AIOT Payment x402 is in public beta. Install the skill for your AI agent.

Checkout Page

The SDK ships with a self-contained HTML payment page generated by generatePaymentPageHTML. It’s what the Express middleware serves at GET /aiot-pay/pay/:sessionId, and you can render it yourself if you’re building on a different framework.

What the page does

The rendered HTML has no external dependencies and performs the entire browser-side payment flow:

  1. Detects an injected wallet (window.ethereum) and asks the user to connect it.
  2. Switches the wallet to BSC Mainnet (0x38) or BSC Testnet (0x61) based on session.chainId.
  3. Reads on-chain state directly via the wallet’s Ethereum provider — ERC-20 allowance to Permit2 and Permit2 nonce bitmap — to derive signing parameters locally. No server round-trip needed.
  4. If approval is needed, prompts a gasless EIP-7702 authorization or falls back to an on-chain ERC-20 approve() transaction.
  5. Prompts the user to sign an EIP-712 PermitWitnessTransferFrom typed-data message.
  6. Posts { sessionId, payment, eip7702Auth? } to /aiot-pay/complete and shows a countdown timer + status badge while the facilitator settles on-chain.
  7. Redirects to callbackUrl (if configured) on success, or shows the transaction hash and a BscScan link.

All of that happens in a single <script> tag inside the generated HTML — no SDK, no bundler, no runtime dependency beyond the user’s injected wallet.

Using it directly

import { generatePaymentPageHTML } from '@aiot/shopify-x402/checkout/payment-page'
 
app.get('/pay/:sessionId', async (req, res) => {
  const session = await client.getCheckoutStatus(req.params.sessionId)
  if (!session) return res.status(404).send('Session not found')
 
  const html = generatePaymentPageHTML(
    session,
    {
      title: 'Pay with Crypto',
      callbackUrl: 'https://mystore.example/thanks',
      testnet: session.chainId === 97
    }
  )
 
  res.type('html').send(html)
})

PaymentPageOptions

NameTypeDescription
titlestringPage title and heading.Default: 'Pay with Crypto'
callbackUrlstringWhere to redirect after a successful payment. If omitted, the page just shows a success state.
testnetbooleanForce the "BSC Testnet" badge. Defaults to true for chainId 97, false otherwise.

Security: HTML escaping

Every dynamic value injected into the HTML (title, session amounts, cart items, chain name, transaction hash) is routed through an internal escapeHtml helper before interpolation. This makes the page safe to host even when the title or cart item.title strings contain user-supplied data — there is no template engine and no direct string concatenation.

Warning

The page trusts the session data it’s handed. If you’re building a custom route that reads the session from an untrusted store, validate session.cartItems and session.fiatTotal before passing them to generatePaymentPageHTML.

When to customize

The page is intentionally opinionated. For V1 you should only need to override the title and callbackUrl. If you need a completely different look — a white-label brand, localized copy, a multi-step modal — fork the template function and render your own HTML; all the state you need is on the CheckoutSession object.