Checkout
Checkout adds payment to your website or application's purchase flow. Use Hosted Checkout for GOAT Flow's ready-made payment page or components, or Custom Integration to build your own payment interface and interactions using GOAT Flow APIs and SDKs.
Choose an Integration
| Path | Best for | Primary component |
|---|---|---|
| Hosted Checkout | Merchants that want a complete hosted payment page, an embedded checkout, or a payment element with minimal payment UI development | PayKit React SDK; direct session URL or optional goatflow-checkout for Hosted Page |
| Custom Integration | Merchants that build their own payment interface, wallet interactions, and payment-state handling | goatflow-sdk-server and payment APIs; optional Browser SDK for Crypto, Stripe.js for custom Card forms |
In both paths, your application owns its products, cart, pricing, and fulfillment. The choice is who builds the payment interface, not who owns the cart or order. Confirm payment on your backend before fulfillment.
For merchant-configured products and shareable payment links, see QuickPay.
Integration Setup
Server SDK Setup
Create sessions and payment orders on your backend. Both integration paths can use the Server SDK to authenticate merchant API requests.
Use the API URL and credentials for the selected environment. Keep merchant keys and secrets on the server; never reuse them across environments.
Crypto Prerequisites
The Crypto examples on this page use DIRECT receiving: the buyer pays the
merchant's configured receiving address.
- The merchant account must be enabled and configured for
DIRECTreceiving. - Configure at least one supported receiving chain and Token. The price must be representable at that Token's precision and satisfy the supported minimum and merchant amount limits; the chain must have a fee configuration.
- Where an order fee applies, keep enough merchant fee balance.
- For
DIRECTCheckout Sessions offering Crypto, the current backend also requires the merchant'squickpay_enabledsetting to be on. This requirement applies to session-based Crypto checkout, not ordinarycreateOrder()calls.
See Merchant Portal for account and receiving setup.
Card Prerequisites
- Card payments must be enabled for the merchant account.
- The merchant's connected Stripe account must be authorized, not disabled, and ready to accept charges.
- The selected GOAT Flow deployment must have its Stripe integration configured.
A Card-only Checkout Session created through the authenticated merchant API
(paymentRails: ['fiat']) does not require quickpay_enabled or Crypto receiving
Tokens. These account prerequisites apply to both Hosted Checkout and custom
Card forms.
Checkout Sessions
A Checkout Session stores the payment terms for a purchase. Create it on your backend for all three Hosted Checkout UI options and for custom Card forms. Custom Crypto payments can instead create a payment order directly, as shown under Custom Integration.
Create a Checkout Session
Use the Server SDK configured above and derive the amount and line items from your own backend order. Select the payment methods explicitly.
Crypto only
The Crypto payment order and its fee are created when the buyer binds a payment option, not when the session is created. A created session does not guarantee that later order creation will pass the account, Token, limit, and fee checks.
Card only
paymentRails, fiatCurrency, and fiatAmount are GOAT Flow Server SDK parameters,
not Stripe API parameters. The SDK value for Card is fiat, not
card. fiatAmount is a decimal amount in fiatCurrency, not a Token amount in
wei. Connecting Stripe does not automatically add Card to a session; omitting
paymentRails defaults to Crypto only.
Card and Crypto
Set paymentRails: ['crypto', 'fiat'] and supply both the Crypto price and the
Card fiatCurrency and fiatAmount. For example, use price: '19.95' alongside
fiatCurrency: 'USD' and fiatAmount: '19.95'. Both quotes are set on your
backend; GOAT Flow does not automatically convert one into the other. The buyer
chooses one payment method for the purchase.
Return the session's checkoutId, url, and expiresAt to the browser. Keep API
keys and secrets on the server, and avoid logging the Checkout Session URL.
Optional Redirect URLs
The starter example intentionally omits successUrl and cancelUrl. Add them
only after the permitted return origins or URL prefixes have been configured in
your merchant's redirect allowlist. Arrange this with your GOAT Flow integration
contact; the current Merchant Portal does not provide a self-service allowlist
editor.
A non-empty redirect URL must match an allowed origin and any configured path prefix. Production URLs must use HTTPS. If no allowlist is configured, sending either URL causes session creation to fail; do not assume your website is allowed automatically. If your integration does not need automatic return navigation, omit both fields. A successful return is a UX signal, not proof of payment.
Session References and Recovery
clientReferenceId is optional, but a non-empty value must be unique for that
merchant while the existing session record is retained. Repeating the same
reference returns HTTP 409, even with identical request parameters; it does
not return the original session. This is a duplicate-creation guard, not an
idempotent replay API. Expiration alone does not release the reference.
Persist the returned checkoutId, url, and expiresAt together with your
clientReferenceId and internal order before presenting the payment entry point. Reuse
that session while it remains valid and payable instead of creating one on
every button click or page refresh.
If creation times out or its outcome is unknown, first check your saved session and reconcile the original attempt. A 409 response does not recover the original ID or URL: the platform stores a hash of the session handle, not the raw handle. Do not automatically change the reference and create another payable session. Use your integration contact for unresolved cases. Create a replacement only after confirming the previous attempt is no longer payable and reconciling any payment; give the replacement its own reference and keep fulfillment idempotent for the same internal order.
Session Expiration
expiresIn is a requested lifetime in seconds, not a guaranteed exact deadline.
Crypto-only sessions default to 30 minutes and are bounded to 60 seconds through
24 hours. Any session that offers Card (fiat), including a Card-and-Crypto
session, has a minimum lifetime of 60 minutes: the backend raises a shorter
request to that minimum. Always use the returned Unix-seconds expiresAt for
display and reconciliation rather than calculating a deadline from expiresIn.
The session expiry and the deadline for starting a Card payment are different.
A new Card payment attempt currently requires more than 32 minutes remaining
on the session: a 30-minute provider requirement plus a 2-minute safety buffer.
If that window is too short, initialization returns HTTP 410, even if the
session itself has not expired. Do not promise that a buyer can start a new Card
payment at any point before expiresAt. Recovery of an already-created payment
attempt follows its existing state; do not replace it blindly.
Hosted Checkout
Hosted Checkout provides ready-made payment UI for the server-created session. Choose the surface that fits your application's layout:
| UI option | What it provides | How it fits into your site |
|---|---|---|
| Hosted Page | A complete, standalone GOAT Flow-hosted payment page | Open the session URL directly, use the PayKit React SDK's Hosted Page link, or use optional goatflow-checkout. |
| Embedded Checkout | A complete checkout block with merchant information and payment UI | Embed QuickPayEmbeddedCheckout from the PayKit React SDK. |
| Payment Element | The GOAT Flow payment section without the surrounding product or cart UI | Embed QuickPayPaymentElement from the PayKit React SDK alongside your own content. |
The available payment methods depend on the session's payment rails, merchant configuration, and the selected component's Card support.
The React examples use the current source exports from the draft package
@goatx402/quickpay-react. It is not currently published to npm; obtain a
release-approved package artifact before using those examples. This applies
to the Hosted Page link, Embedded Checkout, and Payment Element. Opening the
returned session URL directly does not require the React package.
Hosted Page
Send the buyer to the complete GOAT Flow payment page using the url returned
when your backend creates the session. This works for Crypto, Card, or both,
according to the session's payment methods.
You can also use a normal link to open that URL in another browser tab.
PayKit React SDK Hosted Page Link
The React link component opens the Checkout Session created by your backend; it does not create the session or set the price.
basePath uses the hosted origin from the backend response, not your storefront
origin or a guessed merchant URL. The helper builds the standard hosted session
route. If your deployment uses a custom route, URL prefix, or additional URL
parameters, navigate to the complete session.url instead.
The component renders a link, so it does not need QuickPayProvider or apiBase.
Omit target="_blank" to navigate in the current tab. This link does not provide
a payment-success callback; confirm the payment and fulfillment conditions on
your backend.
Drop-in Checkout SDK
This is an optional alternative to the React link or direct navigation, not a prerequisite for Hosted Page.
Install the framework-free browser package:
Use the checkoutId and url returned by your backend to open that Checkout
Session. The returned URL supplies the hosted page's origin.
Load the session from your backend before enabling the payment button, then call
openCheckout(session) synchronously from its click or tap handler. Do not wait
for a network request inside that handler before opening a new tab or window,
because the browser may block the new window.
Choose how to open the complete Hosted Page:
display: 'redirect'navigates the current browser tab to the full payment page.display: 'tab'opens the full payment page in a new browser tab.
See SDK Reference
for the supported display options.
For a full-page redirect without the browser SDK, navigate directly to the
session.url returned by your backend.
Embedded Checkout
Use the PayKit React SDK's QuickPayEmbeddedCheckout when you want a complete
GOAT Flow checkout block inside your application's page. Pass the server-created
Checkout Session; the component loads its merchant details and payment terms.
For embedded components on a third-party merchant site, explicitly pass the
GOAT Flow API base for the selected environment as apiBase, as in the example
below. Use the API address
provided for your integration, not an assumed storefront or payment-page origin.
The API must allow your site's origin through CORS. This is public connection
configuration; never pass merchant API keys or secrets to this provider.
If apiBase is omitted and no build-time API base is configured, requests are
same-origin relative. Your site must then reverse-proxy /quickpay/v1,
/checkout/v1, and /mpp/v1 to the GOAT Flow backend. Without an API base or
those proxies, requests will go to your storefront instead of GOAT Flow. This
requirement applies to both Crypto and Card embeds.
The Card presentation depends on the deployed component and backend: an enabled Stripe Elements integration can render the Card form in-page, while a hosted-only deployment hands the buyer off to the Hosted Page. Verify the Card behavior of the package and deployment you use rather than assuming that every embedded package supports in-page Card entry.
Payment Element
Use QuickPayPaymentElement when your application already displays the product,
cart, and order summary and only needs GOAT Flow's ready-made payment section.
It uses the same session, provider, API-base configuration, and Card availability
requirements as Embedded Checkout.
Custom Integration
Use Custom Integration when you build the payment interface and coordinate its actions and state yourself. The Server SDK is the primary backend integration, but Crypto and Card use different buyer-side integrations:
- Crypto: create a payment order on your backend, then let the buyer pay through their wallet. The Browser SDK is optional and handles wallet transfers.
- Card: create a Card Checkout Session through GOAT Flow, initialize its Elements payment attempt, and build your form around Stripe's secure input components. Your application owns the layout, submit action, and payment-state handling; Stripe handles card details and authentication.
PaymentHelper.pay() is for Crypto, not Card. Do not add Card fields to
createOrder() or collect card details through the Browser SDK.
Crypto Payments
This example is for merchants configured with DIRECT receiving: the buyer
transfers directly to the merchant's configured receiving address. It handles
ERC20_DIRECT orders only and is not a generic handler for every payment flow.
Create an Authoritative Order
Derive price and product data from your backend. Do not accept the amount, receiving address, or token contract directly from an untrusted request.
Use a unique dappOrderId for each payment order and persist the returned GOAT
Flow orderId against your own order. Ordinary createOrder() requests do not
accept idempotencyKey; repeating a dappOrderId is rejected as a duplicate,
not returned as an existing order. If creation times out, reconcile the original
request before issuing another payable order.
The Server SDK returns fromChainId, whereas the Browser SDK expects chainId
and fromAddress. Preserve the payer used at creation and send the mapped
browserOrder above through your own backend endpoint. Replace the sample
addresses with the validated payer and configured token for the selected chain.
Pay with a Browser Wallet
Pass the connected wallet's EIP-1193 provider and the payment data returned by your backend. Verify payer, chain, token, recipient, amount, and expiry before payment. A successful wallet transfer is not a substitute for GOAT Flow's backend confirmation.
Card Payments
For a merchant-built Card form, use Stripe Elements with a GOAT Flow-created
payment attempt. This is separate from embedding GOAT Flow's
QuickPayPaymentElement: here you build and maintain the form and its
interaction logic yourself.
In addition to the Card prerequisites, the deployment must
expose the Elements initialization endpoint, have its Stripe publishable key
configured, and allow your site's origin through CORS when using a different
API origin. Use a Stripe.js version compatible with that deployment. The
current frontend implementation uses @stripe/stripe-js 8.7.0.
Initialize the Card Payment Attempt
First create and save a Card Checkout Session on your backend. Then initialize its payment attempt through the GOAT Flow public Checkout API:
The request has no amount or card-details body; the amount comes from the
server-created session. Use the GOAT Flow API base supplied for your integration,
not api.stripe.com. This buyer endpoint uses the opaque Checkout Session
handle and does not require merchant API credentials in the browser.
GOAT Flow creates the associated order and Stripe session. Do not create an
unrelated Stripe session or PaymentIntent yourself: it would not belong to this
GOAT Flow payment attempt. Keep client_secret and the session handle out of
logs, analytics, and unrelated clients.
Build and Submit Your Card Form
- Load the compatible Stripe.js client using the returned
publishable_keyandstripe_account_idas its connected-account context. - Initialize Stripe Checkout with
client_secret. In the current frontend's Stripe.js version, this isstripe.initCheckout({ clientSecret }). - Mount Stripe's Payment Element inside your own form. Build the surrounding layout, order summary, customer-email field, pay button, and error/pending states yourself. The sensitive card fields remain inside Stripe's component.
- Load Checkout actions, read and display the session total, and update the customer email. Confirm through those same actions from your submit handler. Handle authentication and immediate errors through Stripe.js; disable repeated submits while confirmation is pending.
- Use GOAT Flow's session and order status to confirm the result on your backend before fulfillment, as described in Confirm and Fulfill.
See Stripe's custom checkout documentation for the secure input components and form lifecycle. GOAT Flow, rather than your application's Stripe server code, creates and associates the payment attempt; use the integration contract above for that step.
The current backend configures Stripe authentication returns through the GOAT Flow return page. Do not assume every authentication flow stays inside your form or that Stripe returns directly to your storefront.
An Elements attempt and a hosted Card attempt cannot be opened independently
for the same active payment attempt. A different UI-mode claim can return
409 with code: "fiat_ui_mode_locked". If initialization times out, retry or
reconcile the same session and mode rather than switching to a second payment
flow. A missing Elements configuration can return 503 with
code: "fiat_elements_unavailable"; do not treat an arbitrary failure as
permission to create a replacement charge. The shared
session expiry and
recovery rules also apply.
Confirm and Fulfill
For the DIRECT receiving examples on this page, both payment methods require
backend confirmation before fulfillment. Persist your internal order reference,
the Checkout Session ID when used, and the GOAT Flow order ID once available.
Creating a Card Checkout Session returns a checkoutId, not a payment orderId.
The payment order is created when Card payment is initialized. Your backend can
read the session's status at
GET /checkout/v1/sessions/{checkoutId}/status to obtain its bound order_id
and order_status when present. Bind that result to the session recorded for
your internal order; do not accept an unrelated order ID from the browser.
Once the payment order ID is known, read its current state from your backend and repeat safe reads or process completion webhooks as needed:
Pass a GOAT Flow payment orderId, not a Checkout Session checkoutId. Check the
order's ownership, expected payment terms, and association with your internal
order before delivering goods, services, or access. Do not fulfill from a wallet
result, a Card return URL, or a browser callback alone.
PAYMENT_CONFIRMED is not a universal permission to fulfill. A Card charge can
be real while the platform withholds fulfillment because of duplicate payment,
payment conflict, or refund/dispute handling. Wait for the associated order to
reach INVOICED or its Checkout Session to report COMPLETED, then apply the
merchant purchase checks above. Keep platform completion and your own delivery
status separate and reconcile any hold instead of taking another payment.
waitForConfirmation() also returns at PAYMENT_CONFIRMED; it is not a helper
that waits through such a hold until the order is invoiced. A signed
fiat.payment.succeeded notification alone does not authorize delivery either.
See the two status contracts
and Webhook event meanings.
Webhooks can be used with Checkout, QuickPay, or API Payments whenever the merchant account and environment expose the corresponding event. They are not limited to one integration path. Make fulfillment idempotent because polling and webhook delivery can observe the same completion.
Security and Launch Checklist
- Keep merchant credentials on a trusted server.
- Bind price and product data to a server-authoritative record.
- Validate the connected wallet, chain, token, recipient, amount, and expiry.
- Fulfill only from verified backend status or an authenticated webhook.
- Reconcile a lost response or known transaction before retrying payment.
- Test failure, cancellation, expiration, late payment, and duplicate delivery.
Continue with Merchant Portal, SDK Reference, or API Reference.