The Demo Miniapp backend is available in two implementations - Go and Node.js - both providing the same RESTful API that integrates with the Alipay+ payment gateway. Choose the implementation that best fits your technology stack and preferences.
Both versions demonstrate Super Qi's core capabilities including authentication, payments, refunds, agreement payments, and inbox notifications. The backends are intentionally kept simple and straightforward - they're demo applications designed to show you how to integrate with Super Qi APIs, not production-ready systems.
Technology Version Purpose
─────────────────────────────────────────────────────────────
Go 1.24.2 Programming language
Fiber v2 Fast web framework (Express-inspired)
go-jose v3 JWE token encryption
uuid v1.6 Unique ID generation
godotenv v1.5 Environment variable management
Technology Version Purpose
─────────────────────────────────────────────────────────────
Node.js 18+ JavaScript runtime
Express v5 Web application framework
node-jose v2.2 JWE token encryption
uuid v13 Unique ID generation
dotenv v17 Environment variable management
axios v1.13 HTTP client for API requests
The backend code is organized into focused modules:
backend-go/
├── main.go # Application entry point
├── api/ # HTTP endpoint handlers
│ ├── authEndpoint.go # Auth token exchange & user info
│ ├── paymentEndpoint.go # Online payment creation
│ ├── refundEndpoint.go # Payment refunds with polling
│ ├── agreementEndpoint.go # 3-step agreement payments
│ └── notificationEndpoint.go # Inbox message sending
├── alipay/ # Alipay+ SDK client
│ ├── client.go # HTTP client, RSA signing
│ ├── alipay.go # API method implementations
│ ├── model.go # Request/response types
│ ├── constants.go # Product codes (ONLINE_PURCHASE, AGREEMENT_PAYMENT)
│ └── util.go # RSA key loading utilities
└── jwe/ # Token encryption
└── jwe.go # JWE create/parse with A256GCM
backend-node/
├── src/ # Source code directory
│ ├── index.js # Application entry point
│ ├── api/ # HTTP endpoint handlers
│ │ ├── index.js # Exports all endpoint initializers
│ │ ├── authEndpoint.js # Auth token exchange & user info
│ │ ├── paymentEndpoint.js # Online payment creation
│ │ ├── refundEndpoint.js # Payment refunds with polling
│ │ ├── agreementEndpoint.js # 3-step agreement payments
│ │ └── notificationEndpoint.js # Inbox message sending
│ ├── alipay/ # Alipay+ SDK client
│ │ ├── index.js # Exports alipay functions
│ │ ├── client.js # HTTP client, RSA signing with Axios
│ │ ├── alipay.js # API method implementations
│ │ ├── constants.js # Product codes (ONLINE_PURCHASE, AGREEMENT_PAYMENT)
│ │ └── util.js # RSA key loading utilities
│ └── jwe/ # Token encryption
│ └── jwe.js # JWE create/parse with A256GCM
└── res/ # Resources (crypto keys)
└── crypto/ # RSA key files directory
The application entry point bootstraps the server and sets up routing:
File: main.go (lines 15-42)
• Load environment variables from .env file
• Initialize Alipay+ client with credentials
• Create Fiber app instance
• Configure CORS and recovery middleware
• Register API route handlers under /api prefix
• Start server on port 1999 (default) or PORT env variable
File: src/index.js (lines 13-48)
• Load environment variables from .env file
• Initialize Alipay+ client with credentials (async)
• Create Express app instance
• Configure CORS middleware
• Parse JSON and URL-encoded request bodies
• Register API route handlers under /api prefix
• Start server on port 1999 (default) or PORT env variable
All endpoints are prefixed with /api and organized by functionality:
POST /api/auth/apply-token
Exchanges an authorization code for an access token and retrieves user information.
Purpose: Establishes authenticated session for the user.
// File: api/authEndpoint.go
// Receives auth_code, calls ApplyToken(), then InquiryUserInfo(),
// and returns a JWE token containing user_id and access_token
// File: src/api/authEndpoint.js
// Receives auth_code, calls applyToken(), then inquiryUserInfo(),
// and returns a JWE token containing user_id and access_token
POST /api/payment/create
Creates a one-time payment transaction.
POST /api/payment/refund
Refunds a completed payment transaction.
Agreement payments enable recurring charges without user interaction each time.
POST /api/agreement/prepare
Prepares authorization for agreement payment setup.
POST /api/agreement/apply-token
Exchanges agreement authorization code for agreement access token.
POST /api/agreement/pay
Executes payment using a previously established agreement.
Use Cases: Subscriptions, recurring billing, automated payments
POST /api/notification/send-inbox
Sends inbox messages to user's Super Qi wallet app.
Both backends provide a complete SDK for interacting with Alipay+ APIs with identical functionality.
The client implements:
{METHOD} {PATH}\n{ClientId}.{RequestTime}.{JSONBody}// Package: alipay/client.go
// Generate signature for API request
signContent := fmt.Sprintf("%s %s\n%s.%s.%s",
httpMethod, path, clientID, requestTime, jsonContent)
hash := sha256.Sum256([]byte(signContent))
signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey,
crypto.SHA256, hash[:])
base64Signature := base64.StdEncoding.EncodeToString(signature)
// Module: src/alipay/client.js
// Generate signature for API request
const signContent = `${httpMethod} ${path}\n${clientId}.${requestTime}.${content}`;
const sign = crypto.createSign('SHA256');
sign.update(signContent);
sign.end();
const signature = sign.sign(privateKey, 'base64');
// Package: alipay/
ApplyToken(authCode) → ApplyTokenResponse
InquiryUserInfo(accessToken) → InquiryUserInfoResponse
PrepareAuthorization(contractDesc) → PrepareAuthorizationResponse
InquiryUserCardList(accessToken) → InquiryUserCardListResponse
Pay(PaymentRequest) → PaymentResponse
Refund(RefundRequest) → RefundResponse
InquiryRefund(InquiryRefundRequest) → InquiryRefundResponse
SendInbox(SendInboxRequest) → SendInboxResponse
// Module: src/alipay/
applyToken(authCode) → Promise<ApplyTokenResponse>
inquiryUserInfo(accessToken) → Promise<InquiryUserInfoResponse>
prepareAuthorization(contractDescription) → Promise<PrepareAuthorizationResponse>
inquiryUserCardList(accessToken) → Promise<InquiryUserCardListResponse>
pay(request) → Promise<PaymentResponse>
refund(request) → Promise<RefundResponse>
inquiryRefund(request) → Promise<InquiryRefundResponse>
sendInbox(request) → Promise<SendInboxResponse>
All API responses contain a Result struct with:
Both backends use JWE (JSON Web Encryption) to securely transmit sensitive data:
JWT_KEY environment variable{user_id, access_token}// Package: jwe/jwe.go
// Create encrypted token
claims := jwe.TokenClaims{
UserID: userId,
AccessToken: accessToken,
}
token, err := jwe.CreateJWE(claims)
// Parse encrypted token
claims, err := jwe.ParseAndValidateJWE(token)
userId := claims.UserID
accessToken := claims.AccessToken
// Module: src/jwe/jwe.js
// Create encrypted token
const token = await createJWE({
user_id: userId,
access_token: accessToken
});
// Parse encrypted token
const claims = await parseJWE(token);
const userId = claims.user_id;
const accessToken = claims.access_token;
Create a .env file in the backend directory (backend-go or backend-node) with the following variables:
# Alipay+ Gateway Configuration
ALIPAY_GATEWAY_URL=<API base URL>
ALIPAY_CLIENT_ID=<Your client ID>
# RSA Keys (PEM format)
ALIPAY_MERCHANT_PRIVATE_KEY_PATH=<Path to private key>
ALIPAY_PUBLIC_KEY_PATH=<Path to public key>
# Token Encryption
JWT_KEY=<32-byte key for JWE>
.env file or RSA private keys to version control. Keep these credentials secure and separate from your codebase. Both Go and Node.js implementations use the same .env format.All API requests use unique IDs following the pattern: {PREFIX}-{UUID}-{TIMESTAMP}
Prefixes:
PAY-...AGREEMENT-PAY-...REFUND-...NOTIF-...Purpose: Ensures idempotency and enables request tracing.
import (
"github.com/google/uuid"
"time"
)
requestId := fmt.Sprintf("PAY-%s-%d",
uuid.New().String(),
time.Now().Unix()
)
// Example: PAY-550e8400-e29b-41d4-a716-446655440000-1640000000
import { v4 as uuidv4 } from 'uuid';
const requestId = `PAY-${uuidv4()}-${Date.now()}`;
// Example: PAY-550e8400-e29b-41d4-a716-446655440000-1640000000000
Both backends use consistent error responses:
| HTTP Status | Scenario |
|---|---|
| 400 Bad Request | Invalid request body |
| 401 Unauthorized | Token validation failure |
| 500 Internal Server Error | API call failures |
All errors return JSON with: success, resultStatus, resultCode, resultMessage
// Error response example
c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"success": false,
"resultStatus": "F",
"resultCode": "INVALID_REQUEST",
"resultMessage": "Invalid request body",
})
// Error response example
res.status(400).json({
success: false,
resultStatus: "F",
resultCode: "INVALID_REQUEST",
resultMessage: "Invalid request body"
});
Now that you understand the backend structure in both Go and Node.js, you can:
Both backends provide identical functionality, so you can even build a hybrid approach or migrate between them later!