▲ Original storefront demo — a single-product storefront with one-time checkout via Antom CKP Embedded. This existing demo is not a deployment of the optional Airtable integration.
Accept global payments via Antom Checkout Page (Embedded), powered by Next.js 14 App Router.
⚠️ For demonstration purposes only. This template is for demonstration purposes only, is not production-ready, and is provided "as-is" without warranty. To the maximum extent permitted under applicable laws, Antom assumes no liability for any losses arising from its use or deployment. By using this template, you are acknowledging and agreeing to this. See LEGAL.md and LICENSE for trademark, AML/KYC, and warranty notices.
- 100+ global payment methods via a single integration
- RSA2 (SHA256withRSA) request signing — no third-party SDK dependency
- CKP Embedded mode — checkout stays in your page
- One-click deploy to Vercel with pre-configured environment variables
- Webhook signature verification + idempotent processing
This repository opens the Airtable order page at /; /airtable remains available for existing order buttons. The original product page and Embedded checkout are available at /store. AIRTABLE_ENABLED=false is the default; Airtable credentials and a database are required only when enabling the connected integration.
The Airtable flow reads an order from a merchant-owned Base, asks an authenticated merchant operator to confirm it, creates an Antom Hosted Checkout session, and saves the resulting link back to the order. Payment review stays manual. It currently supports Sandbox only, one merchant/Base per deployment, a server-side Airtable PAT and a persistent PostgreSQL database.
- Install and verify the Airtable integration
- Rebuild the Airtable Base from CSVs and field instructions
The Base uses ordinary fields and Open URL buttons compatible with Airtable Free. The Next.js application and database run outside Airtable. Hosting and processing may have costs; Vercel Hobby is limited to personal, non-commercial use. The deploy button above clones mo-cha-lauren/antom-nextjs-template; it does not configure the additional Airtable variables, apply the database migration or publish a Universe template.
No real Sandbox payment, independent merchant-copy acceptance, deployment or public Airtable listing is asserted by this source. Complete the linked acceptance steps before distribution. Use separate Sandbox notification configuration for Airtable and the original storefront; the notification setup and recovery limits explain why they must not share an unverified merchant callback configuration.
Browser ─▶ Next.js App Router (server) ─▶ Antom Gateway (Asia)
▲ │
└── CKP Embedded SDK (js.antom.com) ◀────────┘
Webhook: Antom Gateway ─▶ POST /api/webhooks/antom ─▶ verify + update
| Variable | Required | Exposed to Client | Description |
|---|---|---|---|
ANTOM_CLIENT_ID |
✅ | No | Merchant identifier from Antom Dashboard → Developer → Integration Settings |
ANTOM_PRIVATE_KEY |
✅ | No | RSA2048 private key, single-line base64, no PEM headers |
ANTOM_PUBLIC_KEY |
✅ | No | Antom platform public key for API response and webhook verification |
ANTOM_GATEWAY_URL |
✅ | No | Official HTTPS gateway origin for your merchant region; see below |
NEXT_PUBLIC_ANTOM_ENV |
✅ | Yes | sandbox or prod |
NEXT_PUBLIC_SITE_URL |
✅ | Yes | Your deployment origin |
Must be a single-line base64 string, no PEM headers. Convert with:
awk 'NF{printf "%s",$0}' priv.pem | sed 's/-----[^-]*-----//g'| Region | URL |
|---|---|
| Asia (default) | https://open-sea-global.alipay.com (recommended), or https://open-sea.alipay.com |
| Europe | https://open-de-global.alipay.com |
| North America — non-US merchants | https://open-na-global.alipay.com (recommended), or https://open-na.alipay.com |
| North America — US merchants | https://open.antglobal-us.com |
Select the merchant's correct online-payment gateway using Antom's API reference; the server does not guess or silently substitute a region. Older example values open-eu-global.alipay.com, open-us-global.alipay.com and open-global.alipay.com are rejected. ANTOM_GATEWAY_URL must contain only the HTTPS origin. Sandbox online requests use /ams/sandbox/api/…; production uses /ams/api/…. This project's transport resolves and signs the environment-specific path before sending it. Use the matching merchant credentials as well as the correct environment value.
For a local Airtable preview, no credentials, database or .env.local are needed:
pnpm install
npm run devOpen http://127.0.0.1:3000. The default development command enables AIRTABLE_LOCAL_PREVIEW=true and binds to loopback. When AIRTABLE_ENABLED is not true, the page shows a synthetic order and a simulated confirmation. It does not call Airtable or Antom APIs, access a database, create a checkout link or take a payment. Preview mode is unavailable in production. The original storefront remains at /store.
To configure connected checkout, use the steps below for the storefront or the Airtable setup guide for the order integration. npm run dev:connected disables the local preview; real Airtable testing still requires the documented credentials, database and publicly reachable HTTPS callback origin.
# 1. Install
pnpm install
# 2. Configure env
cp .env.example .env.local
# 3. Generate RSA key pair
openssl genpkey -algorithm RSA -out priv.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -in priv.pem -pubout -out pub.pem
# 4. Convert private key to single-line, paste into ANTOM_PRIVATE_KEY
awk 'NF{printf "%s",$0}' priv.pem | sed 's/-----[^-]*-----//g'
# 5. Upload pub.pem to Antom Dashboard; download Antom platform public key
# → paste into ANTOM_PUBLIC_KEY
# 6. Run without the synthetic Airtable preview
npm run dev:connected # http://127.0.0.1:3000/store- User clicks Pay → frontend POSTs to
/api/create-payment-session - Server signs request with RSA2, calls Antom
createPaymentSession - Returns
paymentSessionDatato frontend - Frontend loads
ams-checkout.js→ mounts CKP Embedded - User completes payment inside iframe
- SDK fires
SDK_PAYMENT_*events (UI only, never trust for fulfillment) - Browser redirects to
/result→ server callsinquiryPayment - In parallel: Antom pushes webhook → server verifies RSA → updates order
antom-nextjs-template/
├── app/
│ ├── page.tsx # Default Airtable order page
│ ├── store/page.tsx # Original inline checkout (single product)
│ ├── checkout-embedded/page.tsx # CKP container
│ ├── result/page.tsx # Inquiry + status
│ ├── airtable/page.tsx # Compatible Airtable order entry
│ └── api/
│ ├── create-payment-session/ # POST: create session
│ ├── inquiry-payment/ # GET: query status
│ ├── airtable/ # Optional authenticated order flow + separate webhook
│ └── webhooks/antom/ # POST: verify + update
├── lib/
│ ├── antom/ # config, sign, client, types, errors
│ ├── airtable/ # Optional Base adapter + durable payment attempts
│ └── orders/ # store interface + memory impl
├── airtable-template/ # Synthetic CSVs and Base rebuild guide
├── docs/airtable.md # Airtable configuration and acceptance
├── sql/airtable.sql # Required schema when enabling Airtable
├── components/ # CheckoutFrame, InlineCheckout, Badge
├── config/ # products, env
└── .env.example
- Private key is server-only (
import 'server-only') - Private key never logged, never in client bundle
- Webhook RSA-verified before processing
- Idempotent webhook handling (by
paymentRequestId) - SDK events used for UI only, never for order fulfillment
- Result page uses server-side
inquiryPaymentfor authority
The steps below concern the original storefront and still require production-readiness work, including its persistence. The optional Airtable integration is Sandbox-only and rejects NEXT_PUBLIC_ANTOM_ENV=prod; it has no production-launch procedure in this version.
- Generate separate production RSA key pair
- Upload public key to Antom Dashboard (Production environment)
- Update Vercel env vars (scope: Production only):
ANTOM_CLIENT_ID→ productionANTOM_PRIVATE_KEY→ productionANTOM_PUBLIC_KEY→ production Antom public keyNEXT_PUBLIC_ANTOM_ENV→prod
- Whitelist your custom domain in Antom Dashboard
- Redeploy
The original storefront's default order store is in-memory (lib/orders/memory.ts). The optional Airtable flow separately requires PostgreSQL and its migration; it does not make the storefront's memory store durable. For a production storefront:
- Swap with
@vercel/kvfor serverless KV - Or any Postgres / MySQL via your ORM of choice
Replace the export in lib/orders/memory.ts:
export const orderStore: OrderStore = kvOrderStore; // or postgresOrderStore| Symptom | Cause | Fix |
|---|---|---|
SIGN_VERIFY_FAILURE |
Sign string mismatch | Body used for signing must equal body sent |
| Webhook returns 401 | Antom public key wrong | Re-download from Dashboard, single-line base64 |
| CKP shows blank | Session may be expired | Inspect the saved expiry and existing payment result before considering another attempt. Airtable does not automatically create a replacement session. |
Missing required env |
Env var not set | Check .env.local or Vercel Settings |
MIT — see LICENSE.
For trademark, AML/CFT/KYC, demonstration-only, and warranty notices, see LEGAL.md.