Skip to main content

Hosted Checkout

Create a checkout session from your trusted backend, then redirect the customer to the returned checkoutUrl. Keep machine credentials off the browser and choose the authorized locationId. Do not build a checkout URL from a guessed host or session ID.

Create a session

Use GatewayPayClient.checkoutSessions.create and the installed SDK's request model. Amounts are integer minor units. An example TypeScript request is:

const session = await pay.checkoutSessions.create({
  amount: 2500,
  currency: 'USD',
  locationId,
  successUrl: 'https://merchant.example/payment/success',
  cancelUrl: 'https://merchant.example/payment/cancel',
  metadata: { orderId },
}, checkoutCreateKey)
// Send session.checkoutUrl to the browser as the redirect destination.

Persist checkoutCreateKey (nonblank, at most 64 characters) and the original request before dispatch. Recover a lost response with authenticated pay.checkoutSessions.getByKey(checkoutCreateKey, locationId). An identical request replays the durable session before mutable validation; changed input under the same identity returns HTTP 409. A lookup 404 does not rule out a delayed original create: any recovery retry must keep the original key and body.

The other hosted creation entry points also require this explicit durable key:

  • Authenticated pay.hostedPayments.createIframeSession(request, key) sends Idempotency-Key to POST /api/v1/hosted/iframe-session. Gateway binds the canonical authenticated organization/location and freezes the request fingerprint. The same input replays before mutable validation; changed input under the same identity returns HTTP 409.
  • Anonymous pay.hostedPayments.createCheckoutFromButton(buttonId, key) sends Idempotency-Key to POST /api/v1/hosted/buttons/{buttonId}/checkout, without a request body or bearer token. Its durable identity and bodyless fingerprint are namespaced to the original merchant/button, not just the caller's key. Preserve the key for the same shopper attempt instead of creating a fresh session on a navigation retry. A new deliberate shopper attempt uses a new persisted key.

To itemize the checkout, send lineItems instead of (or together with) amount. Each line is either a catalog line with priceId, which is charged and stored at the catalog price, or a custom line with name and unitPrice; sending unitPrice with priceId returns 400. Gateway computes each line total from quantity × the charged unit price. When lineItems are present, omit amount to use their sum as the base session amount, or send exactly that sum; a different amount returns 400 before the session is created. Discounts, shipping, and tax may change the final payable amountTotal.

See TypeScript, Kotlin, and Authentication. Use locationId, not a legacy merchant field. Use the API reference for optional customer, expiry, branding, and payment-method fields.

Card billing countries and postal codes

Card checkout supports all 249 ISO 3166-1 alpha-2 billing countries, with the United States selected by default. Each new card, ACH, or wallet checkout charge requires street line 1, city, country, and a valid postal code, except for the trusted issuing-country postal exemption below. Street line 2 is optional; either street line must be at most 100 characters and cannot contain control characters.

US states and Canadian provinces are required and must match the offered codes. Other state/province/region fields remain optional: the existing country/postal data does not define reliable subdivision requirements outside US/CA. ACH checkout is US-only and requires a valid US state and ZIP.

Apple Pay and Google Pay request the billing address from the wallet sheet and use that address for checkout. Wallets must always supply a postal code: opaque wallet tokens provide no trusted issuer evidence before decoding, so missing postal codes are rejected before payment admission rather than after a charge starts. The trusted-BIN exemption below applies to card checkout.

Saved cards and embedded checkout frames also require complete billing information; selecting a saved card does not bypass address validation. Virtual-terminal parents pass the full billing address to prefill the embedded frame. Non-checkout transaction API channels are unchanged. Recovering an already-approved payment does not require re-entering newly required address fields; recovery never initiates a new charge.

US ZIP, Canadian postal-code, and UK postcode formats retain their existing validation. Other supplied postal codes are trimmed and must contain 1–10 ASCII letters, digits, spaces, or hyphens. The following billing countries support omitting postal codes, subject to the trusted issuing-country check below:

AE AG AO AQ AW BF BI BJ BO BQ BS BV BW BZ CD CF CG CI CK CM CW DJ DM ER FJ GA GD GH GM GQ GY HK IE JM KI KM KN LC LY ML MO MR MS MW NR NU PA PS QA RW SB SC SL SR SS ST SX TD TF TG TK TL TO TV UG VU WS YE ZW.

The no-postal-code country set follows Google's libaddressinput country metadata (countries without a zip pattern, consulted October 17, 2026). Ireland (IE) is explicitly eligible for omission even though Eircodes are supported.

The server waives required postal evidence only when both the billing country and the card's issuing country, established by the existing trusted BIN lookup, are in this set. A missing or unknown issuing country requires a postal code; US-, Canadian-, and UK-issued cards always require postal evidence even when the shopper selects a no-postal billing country. Checkout may submit a blank field for the server to decide, because the browser has no trusted issuing-country evidence. Selecting a billing country alone cannot disable the AVS postal gate. Street address remains required and supplied postal codes remain validated. The same trusted decision applies to pre-routing and selected-route numeric fraud assessment: an eligible omission is not missing postal evidence. It does not manufacture an AVS match or change issuer mismatch enforcement. Country support does not imply a processor supports every card, currency, or international transaction.

Customer flow and result

Gateway-hosted checkout collects card details and processes payment. It does not expose a separate browser tokenization API or return reusable tokens from card entry. Eligible saved payment methods require the session/customer context. A hosted payment button is sale-only; it is not a refund or void interface.

See Wallet prerequisites.

A success redirect is not authoritative payment evidence: confirm the persisted transaction or a verified notification before fulfillment. Expired/cancelled sessions and unknown outcomes require their own UI; do not manufacture approval or automatically submit a second payment after a timeout.

Hosted checkout displays “Card declined — try again or use another card” after a definitive decline. Retry stays on the same session and is enabled only when the session GET returns paymentRetryAllowed=true. Card entry remains available for that retry; a payment response alone never grants retry permission.

The daily card-verification mismatch limit is the specific exception: Processing persists the pre-dispatch VERIFY_LIM response code, and checkout maps that code (never processor message text or generic FRAUD_DEC) to “Too many address or security-code mismatches for this card. Try again tomorrow or use another card.” The immediate result, declined webhook/embedded event, and refreshed session retain this safe copy. Other fraud declines retain generic messaging. The web SDK forwards the checkout result/event; Virtual Terminal and dashboard transaction views already display the returned response message. The limit does not grant retry permission or dispatch another processor attempt.

Merchant-configured stricter per-card mismatch limits use the same code and copy: the persisted matched BLOCK-rule evidence must identify a triggered payment-credential VERIFICATION_MISMATCH velocity condition, not just any merchant velocity or fraud rule. Generic FRAUD_BLOCKED results also complete the checkout attempt as a definitive decline (including wallets, webhook and reload), but keep generic copy. Recovery consumes an interrupted non-gift no-dispatch block under the existing sequence/metadata fence before releasing its checkout claim; no processor request is replayed and split-tender compensation keeps its owner.

On reload, a retryable session stays PENDING; paymentAttemptStatus=DECLINED is projected only when the immediately preceding deterministic attempt is a definitive decline matching the session's location, reference, and CNP source. Sequence advancement alone is not decline evidence: no-send recovery also advances sequences. Missing, unavailable, or mismatched prior evidence leaves paymentAttemptStatus=NOT_STARTED without changing retry permission.

For PROCESSING or an unresolved attempt (PENDING/UNKNOWN) without retry permission, the page displays “We're confirming your payment — please don't pay again”. Ending bounded status observation does not establish failure and does not invite a new checkout. A FAILED payment response is also refreshed against the authoritative session before showing a terminal outcome: it can represent checkout completion failing after a processor charge. Actual expired, cancelled, or terminal failed sessions retain their existing terminal guidance.

During approved-payment recovery, the first transaction read that verifies identity and checkout origin retains the authoritative paid amount and explicit partial-approval facts. Failure of a later, optional card-presentation lookup must not replace that evidence with the requested full order amount. If a partial approval is known but its authoritative processed amount is unavailable, recovery stays in doubt on the original bound transaction and attempt; it does not submit another charge. For split gift-card and card payments, the buyer-paid total includes both the gift-card leg and the authoritative card amount, not just the card remainder.

Session amounts and payment presentation

Session amount and amountTotal describe the order, not payment received. processedAmount describes the processor/card leg. For a COMPLETED session, nullable paidAmount is the known gross buyer-paid total, including both gift-card and card funding (or gift-card-only funding); it is not net of later refunds. A real zero is valid. If paidAmount is unavailable, show that it is unavailable: do not substitute the order total, the card leg, or zero. A partiallyApproved flag alone does not establish a balance owed, including when paid and order totals are equal.

An AUTHORIZED session is not paid: display its authoritative processedAmount as the amount authorized, or unavailable if unknown, and do not offer a paid receipt. paidAmount remains null for authorization.

In the embedded peakgateway.checkout v1 protocol, amount deliberately remains the expected order amount used by the retail iframe's session-binding guard, not the actual buyer-paid total. Preserve that binding and deliver known completion status even when paidAmount is unavailable.

Actual tender and ACH lifecycle

allowedPaymentMethods is an offer, never the method actually charged. Generated checkout/payment/transaction responses and completion events expose nullable selectedTender: card, ach, apple_pay, or google_pay, derived from the original scoped processor transaction. Missing evidence stays null; it never defaults to card. Match transaction, session, location and currency before acting.

For ACH, SUCCEEDED, COMPLETED, APPROVED, and capture/batch status establish acceptance only. paymentLifecycleState=PENDING must not mark an invoice paid. paymentOutcomeVersion is semantic precedence for one original debit, not a delivery counter: PENDING=1, SETTLED=2, FAILED/RETURNED=3. UNKNOWN has no version. Only SETTLED is funded evidence, and settlementFinal=false remains explicit: ACH funding is returnable, not irrevocable finality.

The merchant dashboard and admin portal transaction lists and details use this separate lifecycle for ACH primary badges: Pending settlement, Settled, Failed, or Returned (including achReturnCode when present). Transaction details retain the original Authorization status; an approved authorization does not override a later failed or returned debit. Card badges are unchanged. Missing or unrecognized lifecycle evidence is Outcome unknown, not approval or funding; known declined, errored, fraud-blocked or fraud-held authorization evidence remains visible alongside that unknown outcome. The transaction-list Authorization status filter still filters the original authorization outcome, not the ACH lifecycle.

Pending/failed/returned bodies use AchLifecycleEventPayload; payment.settled additively extends the existing PaymentSettledEventPayload, retaining its published aggregate fields without introducing an ambiguous competing shape:

EventEvidenceVersion
payment.pendingDurable approved debit, still awaiting funding1
payment.failedDurable definite authorization decline; no successful debit3
payment.settledOriginal-profile CardPointe Funding txns ECHECK row without return code, exact amount2
payment.returnedOriginal-profile ECHECK row with achreturncode, exact original amount magnitude3

Every delivery carries the persisted immutable envelope eventId. Bodies carry an immutable semantic outcomeEventId, original transactionId, nullable sessionId (non-hosted debits have none), organization/merchant/location, minor-unit amount, currency, lifecycleState, outcomeVersion, effective time/precision, actual ACH tender and optional return code. Returns retain the original debit; they are not fresh charges or refund child transactions. payment.settled also retains published transactionIds, totalAmount and currency aggregate fields. Failure amount describes the attempted debit, not money received.

Apply only a higher version to the same scoped debit. Same-version duplicates are no-ops; conflicting same-version identities/amounts require review. A return received before pending or funding still wins. Allowed transitions are PENDING→SETTLED→RETURNED, PENDING→RETURNED, or initial FAILED; missing funding is not failure. Never reinterpret a bank return as permission to debit again. CardPointe does not document a separate post-acceptance/pre-funding failure feed: its Rxx bank rejection is represented as RETURNED even if funding was never seen, not inferred as FAILED from a missing report.

Recover missed events with the original authorized transaction GET (or hosted session GET when it retains that transaction). These reads project the durable funding/return evidence using the original credential profile, original MID and provider reference, not the merchant's current default route. A retained exact approved sale attempt can supply original account/organization and response observation for older/lost-response records; multiple or inconsistent candidates remain unavailable. Legacy rows without immutable original MID evidence leave bank funding unavailable rather than borrow the current profile's account. Unknown/missing inquiry is not permission for a fresh debit. Request-path and recovery completion events use the same idempotency key; funded/returned events deduplicate by original debit and outcome version. Funding fanout failures fail the date sweep before cursor advancement, and explicit-date backfill can replay fanout without money effects. The durable original transaction is also the pending/failed event repair intent. The existing scheduled CardPointe funding sweep first scans at most 1,000 unpublished authorization outcomes without provider contact, even when funding reporting is disabled. Event insert failure or process death before publication leaves the source eligible; repair reuses the original outcome time/identity and never dispatches a debit. A processing-owned ach_lifecycle_event_receipts tombstone is recorded only after event insertion or exact-key deduplication, survives webhook retention purge, and prevents historical acceptance replay. Partial repair failure fails the scheduled operation instead of claiming complete delivery. Already persisted event fanout remains management's outbox responsibility.

CardPointe's exact provider boundary

The Fiserv ACH Developer Guide documents USD only, cardbrand=ECHECK, achreturncode (Rxx), and original retref reuse for a return, including funding and return on the same day. It explicitly says a processed transaction may still be returned by the receiving bank. Gateway preserves separate same-day funding/return journal rows; chargebacks or free-text adjustment descriptions are not substitutes for ACH return codes.

The Funding API reports funding by requested date, may return partial data while processing, and supports paymenttype=ach|card filtering (Gateway reads the combined feed). ACH data is typically available within 72 hours; bank weekends/holidays delay it. Effective funding/return timestamps here are UTC date anchors with precision DATE, not fabricated bank-event instants. Acceptance/decline uses the first durable Gateway response observation with SECOND precision, not intent createdAt or a claimed provider event time. Legacy/lost-response records without that observation leave the time null; they do not manufacture a current timestamp. Neither absence of a report nor setlstat=Settled proves bank finality.

Funding requires processor enrollment and is documented as production-only. This change performs no provider calls and cannot claim UAT funded/returned proofs or a bounded maximum return window. The existing sweep/backfill must cover late provider postings; no irrevocable-final state is advertised. POS invoice ACH enablement remains separately blocked until deployed feed/enrollment, late-return coverage, and consumer sandbox/concurrency acceptance have been authorized and proven. Local fixtures prove mapping and ordering only, not processor acceptance.

Notification verification

Checkout-session callbacks use X-Pinpoint-Signature, a hex HMAC-SHA256 over the raw body, with no scheme prefix. Their event is payment.completed or payment.declined; this callback format is distinct from the general X-Gateway-Signature webhook event envelope. Verify the configured callback contract before parsing or acting; do not confuse the two verifiers. Amounts are minor units. Match the session, organization, transaction, amount, and currency to your order and handle duplicate notifications idempotently.

Do not log card details, ACH routing/account numbers, gift-card numbers/PINs, client secrets, session tokens or raw callback bodies. Hosted checkout excludes payment-entry and payment-value surfaces from session replay and autocapture; replay does not record network request bodies. These source-level safeguards are not proof of a particular deployed recording or a blanket PCI attestation. For reusable card setup see Elements.