Exploring the Code

Backend

Exploring the Demo Miniapp backend code structure.

Overview

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.

Tech Stack

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

Project Structure

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

Application Entry Point

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
Both backend implementations require several environment variables to function. See the Configuration section below for required settings.

API Endpoints

All endpoints are prefixed with /api and organized by functionality:

Authentication

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

Online Payment

POST /api/payment/create

Creates a one-time payment transaction.

Refund

POST /api/payment/refund

Refunds a completed payment transaction.

Agreement Payment

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

Inbox Notification

POST /api/notification/send-inbox

Sends inbox messages to user's Super Qi wallet app.

Alipay+ Client Implementation

Both backends provide a complete SDK for interacting with Alipay+ APIs with identical functionality.

Client Structure

The client implements:

  • RSA256 signature generation for request authentication
  • Signature format: {METHOD} {PATH}\n{ClientId}.{RequestTime}.{JSONBody}
  • Required headers: Client-Id, Request-Time, Signature, Content-Type
// 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)

Available Methods

// 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

Response Handling

All API responses contain a Result struct with:

  • resultStatus: "S" (success), "F" (fail), "U" (unknown), "A" (accepted)
  • resultCode: SUCCESS, PAYMENT_NOT_EXIST, REFUND_NOT_EXIST, etc.
  • resultMessage: Human-readable error message

Security & Token Management

JWE Encryption

Both backends use JWE (JSON Web Encryption) to securely transmit sensitive data:

  • Algorithm: A256GCM (AES-256-GCM)
  • Key encryption: Direct (jose.DIRECT)
  • Key size: 32-byte symmetric key from JWT_KEY environment variable
  • Format: Base64-encoded full serialization
  • Claims: {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
The main purpose behind using JWE here is to send the data to the frontend and store it in the frontend because we are not using a database in this demo to simplify the whole process. In normal scenarios there won't be a need to use JWE and send the data to the frontend again - just store it in the backend with proper database security.

Configuration

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>
Never commit your .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.

Key Implementation Details

Request ID Generation

All API requests use unique IDs following the pattern: {PREFIX}-{UUID}-{TIMESTAMP}

Prefixes:

  • Online payment: PAY-...
  • Agreement payment: AGREEMENT-PAY-...
  • Refund: REFUND-...
  • Notification: 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

Error Handling

Both backends use consistent error responses:

HTTP StatusScenario
400 Bad RequestInvalid request body
401 UnauthorizedToken validation failure
500 Internal Server ErrorAPI 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",
})

Next Steps

Now that you understand the backend structure in both Go and Node.js, you can:

  1. Choose your stack - Pick the implementation that fits your team's expertise
  2. Develop your miniapp - Use these demos as a reference for your own implementation
  3. Test locally - See Testing Locally for deployment instructions
  4. Go to production - Once complete, move on to the "Releasing to Production" section

Both backends provide identical functionality, so you can even build a hybrid approach or migrate between them later!