How to Implement Paid Community Features with QR Codes and Refunds
To implement paid community features with QR codes and refunds, configure Alipay Web-Pay for ¥9.90 transactions, persist order states in Supabase, store protected QR images in a dedicated table, and process asynchronous payment notifications via webhooks to dynamically toggle access eligibility.
The freestylefly/awesome-gpt-image-2 repository provides a production-ready implementation of a paid community feature that operates independently from existing membership or credit systems. This architecture uses Alipay for payment processing, Supabase for state management, and atomic database operations to ensure QR codes remain secure while supporting full refund lifecycles. Below is a complete technical breakdown of how to implement paid community features with QR codes and refunds, referencing the actual source code and database schema.
Architecture Overview
The system decouples payment processing from community access through a state-machine-driven flow. According to the source code in docs/paid-community.md, the implementation follows this sequence:
- Frontend Initiation: Users navigate to
/communityto view the QR code page or/community/resultfor payment outcomes. - Alipay Integration: The backend calls
alipay.trade.page.payto create a ¥9.90 (990¢) order. - State Persistence: The
community_orderstable tracks status transitions from PENDING to PAID, and finally to terminal states like REFUNDED, CLOSED, or REVOKED. - Access Control: The
community_qrtable stores the protected image as abyteablob, with access granted only when a user's latest order status equals PAID (or PROCESSING during active refunds). - Asynchronous Updates: Alipay webhooks push final status updates to
POST /api/community/alipay/notify.
Database Schema Design
The community_orders Table
Located in supabase/migrations/20260722090000_paid_community.sql, this table links payments to Supabase user IDs:
statuscolumn manages the lifecycle: PENDING → PAID → REFUNDED/CLOSED/REVOKED- Foreign key relationships ensure each order maps to a specific user
- The table supports idempotent updates to handle duplicate Alipay notifications safely
The community_qr Table
QR images are stored as binary blobs with strict size limits (PNG/JPEG/WebP ≤ 2MB). The schema enforces atomic replacement operations through RPC transactions, ensuring the old image is disabled before the new one becomes live. This prevents race conditions where users might access outdated or revoked community codes.
Implementing the Payment Flow
Checkout Initiation
The frontend initiates payments via POST /api/community/alipay/checkout. In api/_lib/alipay.js, the server generates an Alipay order and returns a redirect URL:
// Start an Alipay checkout (frontend)
async function startCheckout() {
const res = await fetch('/api/community/alipay/checkout', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({amount: 990}) // fixed amount in cents
});
const {checkoutUrl} = await res.json();
window.location = checkoutUrl; // redirects to Alipay
}
Webhook Handling
The POST /api/community/alipay/notify endpoint processes Alipay server-to-server notifications. As implemented in the API layer, this webhook updates the community_orders table status and handles idempotent processing:
// Alipay webhook (server side) – records payment status
import {recordAlipayNotification} from '../_lib/alipay.js';
export default async function handler(req, res) {
if (req.method !== 'POST') return res.status(405).end();
await recordAlipayNotification(req.body); // updates `community_orders`
res.status(200).json({ok: true});
}
Status Polling and Eligibility
The GET /api/community/status endpoint aggregates payment state, QR availability, and user eligibility. The frontend uses this to conditionally render the protected QR code:
// Get community status (frontend)
fetch('/api/community/status')
.then(r => r.json())
.then(({eligible, qrReady, order}) => {
if (!eligible) return alert('Purchase required.');
if (qrReady) showQrImage(); // renders the protected QR
else console.log('QR not yet uploaded.');
});
Behind this endpoint, api/_lib/community.js provides helper functions including canAccessCommunityQr, getCurrentCommunityQr, and getLatestCommunityOrder to encapsulate the eligibility logic.
QR Code Access Control
Storage and Retrieval
Protected QR images are never served directly from static files. Instead, the system queries the community_qr table and streams the bytea blob only to eligible users. The src/community.jsx and src/community.css files handle the frontend rendering once access is verified.
Atomic Replacement
Administrators rotate QR codes through admin-only endpoints (/api/admin/...). The replaceCommunityQr function executes an RPC transaction to swap images atomically:
// Admin: Rotate QR code (server side)
import {replaceCommunityQr} from '../_lib/community.js';
export default async function handler(req, res) {
const {qrFile} = req.body; // binary image uploaded by admin
await replaceCommunityQr(qrFile); // RPC transaction, atomic swap
res.json({ok: true});
}
This ensures zero-downtime rotation and immediate invalidation of previous codes.
Handling Refunds and Revocation
Refund processing follows Alipay's refund API workflow. When an administrator issues a refund through the admin panel, the system calls the createRefund function from api/_lib/alipay.js and immediately updates the corresponding order status to REFUNDED or PROCESSING:
// Admin: Issue a refund (server side)
import {createRefund} from '../_lib/alipay.js';
export default async function handler(req, res) {
const {orderId} = req.body;
const result = await createRefund(orderId); // Alipay refund API call
res.status(200).json(result);
}
Once the status transitions to REFUNDED, the canAccessCommunityQr helper returns false, instantly revoking QR code access without requiring manual intervention.
Environment Configuration and Safety Flags
The implementation uses feature flags to prevent premature activation. The COMMUNITY_PAYMENT_ENABLED environment variable (default false) guards all payment endpoints until production acceptance testing completes. Additionally, COMMUNITY_ALIPAY_NOTIFY_URL defines the webhook endpoint for Alipay asynchronous notifications.
Production Deployment Checklist
According to the paid-community runbook in docs/paid-community.md, deploy the feature using this verification sequence:
- Deploy code with
COMMUNITY_PAYMENT_ENABLED=false - Upload an initial QR image via admin endpoints
- Enable the flag temporarily:
COMMUNITY_PAYMENT_ENABLED=true - Execute a real ¥9.90 payment flow
- Process a full-cycle refund and verify:
- Alipay async notification and query APIs are idempotent
- QR code becomes unavailable after successful refund
- Order status correctly transitions through all states
- If any step fails, immediately revert
COMMUNITY_PAYMENT_ENABLEDtofalse
Summary
- Alipay Web-Pay handles ¥9.90 transactions through
alipay.trade.page.pay, redirecting users to complete payment externally. - Supabase stores order states in
community_ordersand protected images incommunity_qr, linking access to PAID status only. - API Layer provides public endpoints for status checks and checkout initiation, with admin endpoints for QR rotation and refunds.
- Idempotent Webhooks ensure
POST /api/community/alipay/notifysafely processes duplicate notifications without corrupting order state. - Atomic Operations in
replaceCommunityQrprevent race conditions during QR code rotation. - Environment Flags like
COMMUNITY_PAYMENT_ENABLEDprovide circuit-breaker protection during deployment.
Frequently Asked Questions
How does the system prevent users from accessing QR codes after a refund?
When a refund is processed via createRefund in api/_lib/alipay.js, the order status updates to REFUNDED or PROCESSING. The canAccessCommunityQr helper function in api/_lib/community.js checks this status and returns false for any non-PAID state, immediately blocking QR image retrieval.
What happens if Alipay sends duplicate webhook notifications?
The recordAlipayNotification function implements idempotent updates. It checks the existing order state in the community_orders table before writing, ensuring duplicate notifications from Alipay do not create duplicate transactions or corrupt the payment status.
Can administrators change the QR code without downtime?
Yes. The replaceCommunityQr function executes an RPC transaction that atomically disables the old image before enabling the new one. This approach, defined in api/_lib/community.js, ensures users never see a broken or outdated QR code during rotation.
How is the payment amount fixed and protected?
The backend enforces the ¥9.90 (990 cents) amount in the POST /api/community/alipay/checkout handler. While the frontend may display the amount, the server validates or hardcodes the parameter when calling the Alipay SDK, preventing price tampering through client-side requests.
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 →