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)sendsIdempotency-KeytoPOST /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)sendsIdempotency-KeytoPOST /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:
| Event | Evidence | Version |
|---|---|---|
payment.pending | Durable approved debit, still awaiting funding | 1 |
payment.failed | Durable definite authorization decline; no successful debit | 3 |
payment.settled | Original-profile CardPointe Funding txns ECHECK row without return code, exact amount | 2 |
payment.returned | Original-profile ECHECK row with achreturncode, exact original amount magnitude | 3 |
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.