How a PolyPay Hosted Payment Works
A code-aligned walkthrough of Hosted Checkout provider selection, Stripe Checkout, crypto settlement, and signed webhook delivery.
Last reviewed against the PolyPay implementation: August 31, 2026.
Stripe onboarding and Stripe Checkout require an active paid or Pay-as-you-go subscription in addition to the configured rollout rules. Hosted Checkout shows Stripe only after the subscription, merchant connection, and payment capabilities are ready.
Payment lifecycle at a glance
- The merchant creates an order with a merchant order ID, amount, asset, network, and optional callback URLs.
- Hosted Checkout shows only the provider routes currently available to that order: Stripe, Crypto, or both.
- The merchant first authorizes an existing Stripe Standard account through OAuth. For Stripe payments, PolyPay creates a server-owned Payment Attempt and a Direct Charge Checkout Session scoped to that merchant account; the charge, balance, refunds, and disputes remain in Stripe under the merchant. For Crypto, PolyPay allocates an enabled receiving wallet and exact payable amount and records a separate Payment Attempt.
- PolyPay confirms Stripe through signed provider webhooks, an active connected-account lookup, and scheduled reconciliation of stale attempts; crypto is confirmed independently by the chain listener.
- Only the first verified provider can move the order to paid and trigger fulfilment. A later verified Stripe or Crypto payment is recorded in a deduplicated manual-resolution queue instead of triggering a second fulfilment; the merchant receives one signed paid webhook.
1. Order creation and idempotency
A merchant-provided mch_order_id is scoped to the merchant and the payment isolation selected by the deployment or API Key. Merchants do not choose or submit an environment when creating an order. Hosted Checkout automatically uses the Stripe account connected for the active deployment. Repeating an active order request returns the existing order instead of silently creating another payable order. After an order expires, Hosted Checkout can reuse the same merchant order ID and creates a fresh payment attempt after payment-method selection. The response contains trade_id, actual_amount, address, expiration_time, and payment_url.
Choose Stripe or Crypto in Hosted Checkout
When both Stripe and Crypto are available, the payer chooses a top-level provider before choosing a crypto asset or network. When only Crypto is available, Hosted Checkout skips the provider layer and opens asset and network selection directly. Stripe appears only when the merchant has an active paid or Pay-as-you-go subscription, the merchant connection is active, card payments and payouts are ready, the merchant switch is enabled, and the order meets currency, amount, and rollout rules. If the subscription expires, the saved Stripe connection and switch are retained but frozen: Stripe disappears from Hosted Checkout and new Stripe attempts are rejected until the subscription is restored. Selecting Stripe changes the order from payment-method selection to waiting for payment and creates a pending Payment Attempt. Returning from Stripe expires the unpaid Session, cancels that attempt, and restores payment-method selection. If the original payment URL is reopened while Stripe is pending, Hosted Checkout shows provider selection instead of empty crypto payment details and safely reuses the active Stripe attempt when Stripe is selected again. Stripe Checkout receives the stored order amount; the browser cannot override it. A Stripe return page polls the server-side Payment Attempt and never marks an order paid from URL parameters.
Hosted Checkout reads GET /api/v2/checkout/orders/{trade_id}/payment-providers, creates Stripe attempts with POST /api/v2/checkout/orders/{trade_id}/payment-attempts using provider=stripe, and polls GET /api/v2/checkout/payment-attempts/{attempt_id}. Merchants with an active paid or Pay-as-you-go subscription can authorize their existing Standard account, complete the one-time OAuth callback, refresh status, enable checkout, or revoke access under /api/v1/pay/merchant/payment-providers/stripe. The status endpoint remains readable while frozen, but management endpoints reject free or expired subscriptions. PolyPay stores only the acct_ identifier, never the merchant’s Stripe secret key or OAuth tokens. The platform keeps its restricted payment API key separate from the full secret API key required only for OAuth exchange and revocation. Connect, enable, and disconnect require an authenticated step-up ticket.
2. Crypto receiving wallet and exact amount allocation
PolyPay selects from the merchant’s enabled wallets that support the requested asset and network. It then reserves the wallet-and-amount combination for the order lifetime. actual_amount is therefore the value the payer must send; integrations should not recalculate or round it.
Reference network fees during method selection
When currency and network are not preset, Hosted Checkout selects USDT whenever the merchant supports it. If the default receiving wallet supports USDT, its network is selected and placed first; otherwise TRON is preferred, followed by the first remaining USDT network. For USDC, Ethereum follows the default wallet network. A network appears only once when it is both the default and the common network. If USDT is unavailable, the selector falls back to the default wallet’s preferred currency. Currency cards keep a stable product-defined order and are not reordered by the default wallet. Whenever the payer changes currency, Hosted Checkout automatically selects the first network in that currency’s ordered network list; the payer can still choose another compatible network. The payment-method response includes short-lived fee_quotes for each asset and network. PolyPay refreshes live unit rates from chain RPC or fee endpoints, combines them with a standard transfer profile, and returns standard_fee_usd using the live USD price of the network fee asset. Base and Optimism estimates also include representative L1 data and operator fees reported by the OP Stack GasPriceOracle. available is current, stale is the last successful observation after a refresh failure, and unavailable means no numeric value should be displayed. Hosted Checkout keeps the selector focused and does not show fee estimates; the connected wallet remains the source of the final network fee.
For public comparison without authentication, GET /api/v1/pay/public/network-fees returns low, standard, and fast estimates for every supported transfer profile. PolyPay Tools uses the same data and compares the standard estimate for USDT, USDC, and each network’s native asset side by side. Open the live fee explorer.
The public GET /api/v1/pay/public/platform-stats endpoint supplies the homepage with a privacy-safe current listener snapshot. It reports the canonical payment-network and asset counts, each listener’s highest completed block, scan lag, and operational, degraded, outage, or unknown state. It never returns wallet addresses, transaction hashes, RPC details, or internal error messages. When monitoring is disabled, incomplete, or stale, the website displays that the status is unavailable instead of substituting sample data.
Receiving wallet review
PolyPay silently screens new receiving wallets and wallets whose address or network changes. Intermediate review states are not shown in Wallet Management. If an address is confirmed as belonging to an exchange or custodial platform, the wallet is disabled and cannot be edited or re-enabled. Wallet Management shows a reason link that opens the appeal and support entry points. Name, currency, and monitoring-range edits do not restart screening.
3. Pay with WalletConnect or the displayed address
For supported USDT and USDC orders on TRON and EVM networks, Hosted Checkout can open a compatible wallet through WalletConnect and request the exact token transfer. The regular payment QR code remains visible on desktop; on mobile it is collapsed behind “Scan with another device” so the exact amount and receiving address stay prominent. For a TRON account whose Active Permission requires multiple signatures, checkout collects the first TronLink signature, creates a short-lived cross-device session, and displays a QR code for an authorized co-signer. That multisig QR code remains a required part of the mobile flow. The phone signs the exact same transaction, verifies the accumulated weight with getSignWeight, and broadcasts only after the permission threshold is reached. The browser may display the transaction hash returned by the wallet, but it never submits that hash as payment evidence. PolyPay independently discovers the transfer through its chain listener, matches the reserved network, token contract, recipient, and integer base-unit amount, and waits for the required confirmations before changing the order state. The QR code and manual address flow use the same server-owned confirmation path.
4. Observable order states
5. Webhook delivery and verification
For Stripe, PolyPay first verifies Stripe-Signature against the environment-specific destination secret, stores the provider event idempotently, checks the connected-account scope, and actively retrieves the Checkout Session or Charge before changing state. PolyPay then signs merchant callbacks with Ed25519 v2. Verify the signed merchant ID, environment, timestamp, nonce, and raw request body using the public key selected from the platform JWKS by key ID. A non-2xx response is treated as a failed delivery. Store the event ID and process each event idempotently.
6. Choose the correct source of truth
A browser redirect improves customer experience, but it is not proof of payment. This applies to both Stripe Checkout and crypto wallet redirects. Update the merchant order from a verified PolyPay webhook or an authenticated order-status query. Keep fulfilment idempotent because provider and merchant webhooks can be delivered more than once.
Responsibility boundary
- Merchant
- Own receiving wallets and the connected Stripe account, protect API credentials, handle refunds and disputes in Stripe Dashboard, verify PolyPay webhooks, and fulfil the business order idempotently.
- PolyPay
- Create and track payment orders, initiate merchant-scoped Direct Charge Checkout, verify provider events, allocate crypto payable details, observe configured chains, and deliver signed status events. PolyPay does not receive the merchant’s Stripe funds or collect an application fee.
- Payer
- Use the exact network, address, asset, and amount shown before the order expires.
For implementation details, continue with the API Key guide and Webhook Security guide. API Key · Webhook Security
FAQ
Should I mark an order paid after the success-page redirect?
No. Treat the redirect as navigation only. Fulfil from a verified webhook or authenticated order-status query.
Why can actual_amount differ from amount?
The order creation flow converts the quoted amount when needed and reserves an exact payable amount so concurrent transfers can be matched reliably.
What should happen while an order is confirming?
Show a pending confirmation state and wait. Do not fulfil until the order reaches paid status.