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
communityRequestRateKeyfor 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 Supabasetotal_amount: ¥9.90 (990 cents converted to yuan)subject: Community access descriptiontimeout_express: 30 minutes expirationreturn_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
paidboolean 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:
- Parses the form-encoded body using
sanitizeAlipayNotifyParams - Validates the RSA2 signature against Alipay's public key
- Confirms
app_idandseller_idmatch runtime configuration - Checks
trade_statusfor TRADE_SUCCESS or TRADE_FINISHED - Calls
markCommunityOrderPaidto update the order toPAIDstatus - 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 identifierALIPAY_PRIVATE_KEY: RSA2 signing keyALIPAY_PUBLIC_KEY: Alipay's verification keyALIPAY_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 paymentPAID: Payment confirmed, access grantedCLOSED: Expired or manually cancelledREFUNDED: Payment reversed post-completionREVOKED: 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 metadatacommunity_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_orderto maintain idempotency - Order expiration occurs automatically after 30 minutes via
shouldExpireCommunityOrderandcloseCommunityOrderAtAlipay - Key files include
api/community/alipay/checkout.js,query.js,notify.js, andapi/_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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →