How the Paid Community System Handles Alipay Onboarding in Awesome GPT Image 2

The paid community system uses a four-stage serverless pipeline to onboard users via Alipay, creating pending orders in Supabase, generating signed payment forms, and confirming completion through both synchronous polling and asynchronous webhook notifications.

The paid community feature in the freestylefly/awesome-gpt-image-2 repository provides a complete Alipay integration for monetizing community access. Built on Vercel serverless functions and Supabase, this system orchestrates order creation, payment processing, and access provisioning through a tightly coupled sequence of API handlers and database operations.

The Four-Stage Onboarding Flow

The Alipay onboarding flow is deterministic and server-driven, splitting the payment journey into four distinct stages that ensure idempotency and security.

Stage 1: Creating or Reusing an Order

The process begins when the client sends a POST request to /api/community/alipay/checkout. The handler in api/community/alipay/checkout.js first authenticates the user and applies rate limiting through applyCommunityRateLimit with a strict 10 requests per minute threshold.

The system checks the COMMUNITY_PAYMENT_ENABLED environment variable to ensure payments are currently accepted. It then calls the Supabase RPC function create_or_reuse_community_order to either fetch an existing PENDING order or insert a new row into the community_orders table. This prevents duplicate charges when users refresh the payment page.

Key implementation details:

  • Rate limiting uses communityRequestRateKey for per-user buckets
  • Order status must be PENDING for reuse; paid or closed orders trigger new rows
  • Default price is set via COMMUNITY_PRICE_CENTS = 990 (¥9.90)

Stage 2: Building the Alipay Payment Request

Once an order exists, the checkout.js handler invokes getAlipayClient to instantiate the Alipay SDK. It constructs a FAST_INSTANT_TRADE_PAY request containing:

  • out_trade_no: The order UUID from Supabase
  • total_amount: ¥9.90 (990 cents converted to yuan)
  • subject: Community access description
  • timeout_express: 30 minutes expiration
  • return_url: Front-end result page (/community/result)
  • notify_url: Server webhook endpoint (/api/community/alipay/notify)

The SDK returns an HTML <form> that auto-submits the browser to Alipay's payment gateway. The client receives this HTML in the paymentHtml response field and injects it directly into the DOM to redirect the user.

Stage 3: User Payment and Redirect

After the user completes payment on Alipay's site, Alipay redirects the browser to the return_url with the order_id query parameter appended. The front-end route /community/result extracts this ID and initiates polling by calling the GET endpoint /api/community/alipay/query.

The query.js handler implements synchronous status checking:

  • Validates the order belongs to the authenticated user
  • Calls Alipay's query API to verify current trade_status
  • Returns JSON with paid boolean and full order object
  • Implements 30 requests per minute rate limiting

The client polls every 2 seconds until receiving paid: true or detecting a closed order.

Stage 4: Asynchronous Notification and Confirmation

Independently of the browser flow, Alipay sends a server-to-server POST to the notify_url defined by COMMUNITY_ALIPAY_NOTIFY_URL. The api/community/alipay/notify.js handler processes this callback:

  1. Parses the form-encoded body using sanitizeAlipayNotifyParams
  2. Validates the RSA2 signature against Alipay's public key
  3. Confirms app_id and seller_id match runtime configuration
  4. Checks trade_status for TRADE_SUCCESS or TRADE_FINISHED
  5. Calls markCommunityOrderPaid to update the order to PAID status
  6. For TRADE_CLOSED status on PENDING orders, invokes closeCommunityOrderAtAlipay

This dual-path confirmation (browser polling + server webhook) ensures the order state remains consistent even if the user closes their browser immediately after payment.

Core Implementation Details

Environment Configuration and Security

The system relies on Vercel environment variables for sensitive credentials. Never commit these to the repository:

  • ALIPAY_APP_ID: Application identifier
  • ALIPAY_PRIVATE_KEY: RSA2 signing key
  • ALIPAY_PUBLIC_KEY: Alipay's verification key
  • ALIPAY_SELLER_ID: Merchant account ID

Public configuration (price, support contact, terms version) is exposed through communityPublicConfig() in api/_lib/community.js. The validateSameOrigin helper ensures all requests originate from the configured APP_URL or same host, preventing CSRF attacks on payment endpoints.

Rate Limiting and Request Validation

Every endpoint enforces user-specific rate limiting via applyCommunityRateLimit:

  • Checkout endpoint: 10 requests per minute
  • Query endpoint: 30 requests per minute

The system uses Redis-backed buckets keyed by communityRequestRateKey to track usage. Additionally, validateSameOrigin checks the Origin header against APP_URL before processing payment-sensitive operations.

Order Lifecycle Management

Orders progress through a strict state machine defined in COMMUNITY_ORDER_STATUSES:

  • PENDING: Order created, awaiting payment
  • PAID: Payment confirmed, access granted
  • CLOSED: Expired or manually cancelled
  • REFUNDED: Payment reversed post-completion
  • REVOKED: Access rescinded

The shouldExpireCommunityOrder utility automatically closes PENDING orders after 30 minutes, invoking closeCommunityOrderAtAlipay to call Alipay's native close API and prevent late payments.

Database Schema

Supabase stores community data in two primary tables defined in supabase/migrations/20260722090000_paid_community.sql:

  • community_orders: Tracks payment state, user ID, amount, terms acceptance, and Alipay metadata
  • community_group_qr_assets: Stores protected QR images unlocked upon payment

The create_or_reuse_community_order RPC handles atomic insertion with conflict resolution based on user ID and pending status.

Code Implementation Examples

Initiating Checkout from the Client

async function startAlipayCheckout() {
  const resp = await fetch('/api/community/alipay/checkout', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    credentials: 'same-origin',
    body: JSON.stringify({ 
      acceptedTerms: true, 
      termsVersion: '2026-07-22' 
    })
  });
  
  const data = await resp.json();
  if (!data.ok) throw new Error(data.error);
  
  // Inject auto-submitting form to redirect to Alipay
  document.body.innerHTML = data.paymentHtml;
}

This implementation references the handler logic in api/community/alipay/checkout.js at lines 55-63, where the server validates the terms version before generating the payment form.

Polling for Payment Status

On the return page (/community/result), extract the order ID and poll until completion:

const url = new URL(window.location.href);
const orderId = url.searchParams.get('order_id');

async function pollPaymentStatus() {
  try {
    const response = await fetch(
      `/api/community/alipay/query?orderId=${orderId}`
    );
    const { paid, order } = await response.json();
    
    if (paid) {
      // Render QR code or success message
      displayCommunityAccess(order);
    } else if (order.status === 'CLOSED') {
      showPaymentExpiredMessage();
    } else {
      setTimeout(pollPaymentStatus, 2000);
    }
  } catch (err) {
    console.error('Polling failed:', err);
  }
}

pollPaymentStatus();

The query.js handler validates user ownership of the order before returning status, preventing information leakage between users.

Handling Server-Side Notifications

While the client polls, the server must handle Alipay's asynchronous callback. The notify.js handler processes form-encoded POST data:

// Validation and processing flow in notify.js
const safeParams = sanitizeAlipayNotifyParams(req.body);
const isValid = validateAlipaySignature(safeParams);

if (isValid && 
    safeParams.app_id === process.env.ALIPAY_APP_ID &&
    ['TRADE_SUCCESS', 'TRADE_FINISHED'].includes(safeParams.trade_status)) {
  
  await markCommunityOrderPaid(client, order, safeParams, {
    notifyId: safeParams.notify_id,
    notifyPayload: safeParams
  });
  
  res.status(200).send('success');
}

The code validates RSA2 signatures, matches seller credentials, and only then updates the order status to PAID, ensuring webhook authenticity before granting community access.

Summary

  • The Alipay onboarding flow consists of four stages: order creation, payment form generation, user redirection with polling, and asynchronous webhook confirmation
  • Rate limiting protects endpoints at 10 req/min (checkout) and 30 req/min (query) using Redis-backed buckets
  • Security measures include RSA2 signature validation, same-origin checks, and environment variable isolation of Alipay credentials
  • Database operations use Supabase RPC functions like create_or_reuse_community_order to maintain idempotency
  • Order expiration occurs automatically after 30 minutes via shouldExpireCommunityOrder and closeCommunityOrderAtAlipay
  • Key files include api/community/alipay/checkout.js, query.js, notify.js, and api/_lib/community.js

Frequently Asked Questions

How does the system prevent duplicate orders?

The create_or_reuse_community_order RPC function in Supabase checks for existing PENDING orders for the authenticated user before creating new rows. If a pending order exists from a previous session, the system returns that order ID instead of creating a duplicate charge. This ensures users never pay twice for the same community access, even if they refresh the checkout page multiple times.

What security measures protect the Alipay integration?

The system implements multiple layers: RSA2 signature validation on all incoming notifications, same-origin validation via validateSameOrigin, environment variable isolation (credentials stored in Vercel, never in the repo), and strict rate limiting to prevent brute force attacks. Additionally, sanitizeAlipayNotifyParams removes unexpected fields before persistence, preventing injection attacks through webhook payloads.

How long do users have to complete payment?

Users have 30 minutes to complete payment after initiating checkout. This timeout is enforced through the timeout_express parameter in the Alipay SDK request and the shouldExpireCommunityOrder utility function. After expiration, the system calls closeCommunityOrderAtAlipay to cancel the order at both Supabase and Alipay, requiring the user to restart the checkout process if they wish to purchase access.

Can the system handle payment notifications if the user closes the browser?

Yes. The architecture uses a dual-path confirmation strategy. While the front-end polls the query endpoint for immediate feedback, the notify.js handler receives asynchronous server-to-server notifications from Alipay regardless of browser state. The markCommunityOrderPaid function updates the database when the webhook arrives, ensuring users receive access even if they close the browser immediately after completing the Alipay payment flow.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →