An all-in-one "build site + collect payment + customer support + analytics" system on Cloudflare Pages, ready to use out of the box.
RCJ Stack is an integrated system based on Cloudflare Pages for "site building + payment + customer support + data analytics", ready to use out of the box:
/admin dashboard shows revenue, trends, conversations.The whole stack runs on Cloudflare β no ICP filing, global CDN, generous free quota, no server of your own needed.
Prerequisites: Node.js and wrangler installed.
npm i -g wrangler # or: npx wrangler
wrangler login # first time: bind your Cloudflare account (browser auth)
cd project-dir
wrangler pages deploy . --project-name <your-project>
Note: wrangler.toml sets pages_build_output_dir = "." β no build step, just upload static files + Functions.
Cloudflare Console β Workers & Pages β Create β Connect Git repo β pick repo & branch β Save. Every git push then deploys automatically.
Multi-account tip: to deploy to a non-default Cloudflare account, override with env vars:
CLOUDFLARE_API_TOKEN=<token> CLOUDFLARE_ACCOUNT_ID=<id> wrangler pages deploy . --project-name <name>
wrangler d1 create <db-name>
# get database_id from the output
wrangler.toml:
[[d1_databases]]
binding = "DB"
database_name = "<db-name>"
database_id = "<database_id>"
orders / support_threads / support_messages) are created automatically on first run β no manual table creation needed.Edit ITEMS at the top of functions/api/paypal/_lib.js:
export const ITEMS = {
'consulting': {
name: 'Consulting', price: 39,
desc: 'A 1:1 consulting session.',
i18n: { zh: { name: 'ε¨θ―’ζε‘', desc: 'δΈε―ΉδΈε¨θ―’γ' },
ja: { name: 'γ³γ³γ΅γ«γγ£γ³γ°', desc: '1:1 γ³γ³γ΅γ«γ' } },
},
// ...
};
| Field | Meaning |
|---|---|
price | List price (your settlement currency, USD by default) |
deposit | Deposit charged online now; omit to auto-calc by DEPOSIT_RATE |
balance | Balance charged on delivery (auto = price β deposit) |
i18n | Chinese / Japanese display names & descriptions |
Other tunable constants (same file):
DEPOSIT_RATE = 0.10: default deposit ratio.CNY_PER_USD = 7.2: reference exchange rate used to show Β₯ on the frontend.BRAND = 'RCJ Stack': brand name shown in orders / notifications.After editing, re-run wrangler pages deploy . β the storefront and admin read the new list live.
Set via Cloudflare Console (Settings β Environment variables) or command line:
wrangler pages secret put PAYPAL_CLIENT_ID
wrangler pages secret put PAYPAL_CLIENT_SECRET
wrangler pages secret put PAYPAL_MODE # sandbox or live
wrangler pages secret put TG_BOT_TOKEN # optional: Telegram notifications
wrangler pages secret put TG_CHAT_ID
wrangler pages secret put ADMIN_PASSWORD # /admin login password (required)
| Variable | Required | Description |
|---|---|---|
PAYPAL_CLIENT_ID | Yes | PayPal app Client ID |
PAYPAL_CLIENT_SECRET | Yes | PayPal app Secret |
PAYPAL_SANDBOX_CLIENT_ID | No | Sandbox-only ID (falls back to above if blank) |
PAYPAL_SANDBOX_CLIENT_SECRET | No | Sandbox-only Secret |
PAYPAL_MODE | Yes | sandbox (test) / live (production) |
PAYPAL_CURRENCY | No | Force settlement currency, e.g. CNY |
PAYPAL_CNY_ENABLED | No | true to enable RMB (requires PayPal account supporting RMB) |
TG_BOT_TOKEN | No | Telegram Bot Token β new orders / new messages push in real time |
TG_CHAT_ID | No | Telegram chat ID to receive notifications |
ADMIN_PASSWORD | Yes | Admin dashboard login password |
ADMIN_KEY | No | Backup API Key |
RESEND_API_KEY | No | Email notifications (Resend) |
NOTIFY_EMAIL | No | Email to receive notifications |
EMAIL_FROM | No | From address |
To switch test / production, only change the PAYPAL_MODE variable.
PAYPAL_MODE=sandbox, order with PayPal Developer sandbox accounts (virtual buyers) β no real charge.PAYPAL_MODE=live, fill in live Client ID / Secret.PAYPAL_CNY_ENABLED=true (settles in CNY); otherwise USD, and Β₯ on the frontend is reference only.deposit online, balance collected offline via WeChat / Alipay on delivery (system only handles online deposit / full payment).@BotFather to create a Bot, get the Token β fill TG_BOT_TOKEN.@userinfobot, get your chat_id β fill TG_CHAT_ID.Important: use your own Bot, don't mix with the demo Bot; before going live be sure to swap in your own credentials.
/admin<your-domain>/admin, enter ADMIN_PASSWORD to log in.The dashboard (endpoint /api/analytics/data) aggregates from D1:
| Metric | Meaning |
|---|---|
| Revenue | sum of cny_amount for orders with status = paid or deposit |
| Orders | all orders (incl. leads, pending) |
| Pending | count of orders with status=pending |
| Leads | count of rows with source='lead' |
| Open / Closed sessions | open / closed count of support conversations |
| 14-day trend | orders & revenue per day, to see the growth curve |
Traffic analytics (PV / UV) is tracked separately by Cloudflare Web Analytics, not in D1.
widget.js): visitors send messages directly β stored in D1 + pushed to Telegram./admin support panel; replies are written to the same conversation as the "support" role; visitors see them on refresh (polling, no WebSocket needed).open; you can manually close after replying.After setting RESEND_API_KEY + NOTIFY_EMAIL, new orders / leads also send email.
Note: Resend's free quota is limited and easy to land in spam β important notifications should rely on Telegram.
/admin says Unauthorized? β ADMIN_PASSWORD not set, or wrong password.NO_CRED? β PayPal secrets not set, or PAYPAL_MODE doesn't match the credential environment (sandbox / live mixed up).@userinfobot to confirm chat_id first.wrangler d1 execute <db-name> --remote \
--command "DELETE FROM orders; DELETE FROM support_threads; DELETE FROM support_messages;"
Then re-seed sample or real data.
ITEMS in _lib.js, re-run wrangler pages deploy ..git pull latest then redeploy.wrangler d1 export to disk.Demo instance and real customers must each use a separate D1 + separate secrets, to avoid data cross-talk.