Checkout SDK

Checkout flow and screens

Checkout flow, UI phases, mobile app checkout, deep links, and polling.

Checkout flow

The SDK loads the payment session and guides the customer through checkout. On phone (SDK 1.2.0+), checkout auto-starts Pay with Zippy unless you opt out. On desktop, customers use QR and/or manual entry per checkoutPolicy.

Key rules

NameTypeDescription
Method lockbehaviorAfter QR or manual checkout is committed, customers cannot switch methods. Exception: before payment is claimed in the app, failed Pay with Zippy handoff may offer QR/manual fallbacks.
Amount sourcebehaviorDisplay amount from onReady / onSessionUpdate — amount is locked server-side.
Deep link / QR payloadsecurityOnly sessionId — never clientToken.
Payment approvalbehaviorCustomer confirms in the Zippy mobile app (PIN / approval).

Three time boundaries

NameTypeDescription
expiresAtIDLE phaseIdle shelf — customer must start checkout before this time.
paymentExpiryAtPAYING phasePay window — countdown after checkout starts.
absoluteExpiresAtalwaysHard maximum — session and token expire here.

Session statuses

NameTypeDescription
CREATEDactiveSession open — customer can choose method.
AWAITING_PAYMENTactiveCustomer must confirm in Zippy app.
COMPLETEDterminalPayment succeeded.
EXPIREDterminalSession or pay window expired.
CANCELLEDterminalPayment cancelled.
FAILEDterminalPayment failed.

Checkout methods

NameTypeDescription
QRQR codeCustomer scans QR in the Zippy app. Locked when QR flow starts.
ManualZippy IDCustomer enters Zippy ID, verifies, and proceeds. Locked when payment starts.
Pay with ZippyPhone onlyOpens the Zippy Pay app on phone (default in SDK 1.2.0+). Ignored on desktop.

Mobile app checkout (phone)

On phone, the SDK can open the Zippy Pay app so customers complete payment there. Configure with mobileApp.policy in ZippyPay.create() or mobile-app-policy on <zippy-pay-checkout>. Requires SDK 1.1.0+ for mobile app features; 1.2.0+ for the new default.

On desktop, mobile app settings are ignored. Customers always use QR and/or manual entry per checkoutPolicy.

mobileApp.policy

NameTypeDescription
enforcedefault (1.2.0+)Recommended — lands on Pay with Zippy automatically. QR/manual hidden until app handoff fails.
optionphoneFull chooser (QR/manual per checkoutPolicy) plus a Pay with Zippy button.
offphoneWeb-only checkout on phone. QR/manual only, per checkoutPolicy. Restores pre-1.2.0 behavior.

Default behavior (1.2.0+)

If you omit mobileApp, or omit policy inside it, the SDK uses enforce on phone:

Equivalent on phone (SDK 1.2.0+)

typescript
ZippyPay.create({ environment, sessionId, clientToken });
ZippyPay.create({ environment, sessionId, clientToken, mobileApp: {} });
ZippyPay.create({ environment, sessionId, clientToken, mobileApp: { policy: 'enforce' } });

Web-only on phone

typescript
ZippyPay.create({
  environment,
  sessionId,
  clientToken,
  mobileApp: { policy: 'off' },
});

checkoutPolicy × mobileApp.policy (phone)

NameTypeDescription
(omit) + enforcedefaultPay with Zippy auto-start.
(omit) + optionchooserQR + manual + Pay with Zippy.
(omit) + offweb-onlyChooser: QR + manual.
QR + enforceapp firstPay with Zippy auto-start; QR fallback after failed handoff.
QR + offweb-onlyQR only.
MANUAL + enforceapp firstPay with Zippy auto-start; manual fallback after failed handoff.
MANUAL + offweb-onlyManual only.

Migration from 1.1.x

What changed

NameTypeDescription
≤ 1.1.xbeforeOmitting mobileApp → web-only on phone.
1.2.0+afterOmitting mobileApp → Pay with Zippy enforce on phone.
  • Want Pay with Zippy by default? Upgrade to 1.2.1 (or 1.2.0+) — no config change.
  • Want web-only on phone? Add mobileApp: { policy: 'off' }.
  • Already set enforce or option? No change needed.

FAQ

Common questions

NameTypeDescription
Is mobileApp required?No. Omitted mobileApp defaults to enforce on phone (SDK 1.2.0+).
Does enforce affect desktop?No. Desktop never shows Pay with Zippy.
QR/manual with enforce?Yes — after app handoff fails. Customer sees Try again, Or pay another way, and Install Zippy.

Customer flow

  1. Customer sees Pay with Zippy (enforce) or taps it from the chooser (option).
  2. Button shows Opening Zippy… (disabled, with spinner) while the SDK attempts to open the app.
  3. SDK opens the Zippy Pay app when the customer taps (iOS and Android). Checkout stays on your page.
  4. If the app opens: Complete your payment in the Zippy app. (no Try again).
  5. If the app does not open: failed handoff screen (below). The App Store / Play Store is not opened automatically.

UI states

NameTypeDescription
IdlePay with ZippyPay with Zippy button. No inline countdown. Bottom pay progress bar visible during pay window. Footnote: Keep this page open until payment completes.
OpeningHandoffDisabled Opening Zippy… button with spinner. No inline timer.
OpenedIn appComplete your payment in the Zippy app. Inline timer if pay window active. No Try again.
Awaiting paymentClaimedWaiting spinner after app claim. Payer details if available.
Failed handoffFallbackSee failed handoff section below.

Failed app handoff

If the app does not open, the customer sees:

  • We couldn't open the Zippy app on this device.
  • Try again — retries opening the app (same as Pay with Zippy).
  • Or pay another way — QR and/or manual cards per checkoutPolicy.
  • Install Zippy (bottom) — opens the native store when tapped. iOS: App Store. Android: Play Store.

Timers (mobile app flow)

NameTypeDescription
Bottom pay progress barpay windowShown whenever checkout is in the pay window, including idle Pay with Zippy.
Inline countdown timerconditionalHidden on idle and Opening Zippy…; shown after app open, on failed handoff, and while awaiting payment.
AmountcallbacksAlways from onReady, onSessionUpdate, onComplete.

UI screens and phases

loading

Spinner while fetching session.

choose-method

QR, Zippy ID, and (on phone) Pay with Zippy per policy.

mobile-app

Pay with Zippy, Opening Zippy…, opened, failed handoff.

qr-active

Branded QR + step guide or awaiting confirmation.

manual-entry

Zippy ID input + Verify button.

manual-confirm

Verified identity card + Proceed button.

awaiting

Complete in Zippy app with identity card.

Terminal

success, expired, failed, cancelled, declined, invalid-session, error.

The header on all active screens shows the business name and formatted amount from the session. A pay progress bar appears when the pay window is active (phase === 'PAYING' with valid paymentExpiryAt).

Deep links and QR

Payment QR codes and mobile app handoff use your sessionId only — never the client token. Desktop customers scan the QR; phone customers may open the app via Pay with Zippy.

  • Rendered as a branded QR with centred Zippy logo (desktop).
  • Pay with Zippy opens the installed Zippy Pay app on phone.
  • Error correction level: H (high) for QR.
  • Never include clientToken in QR payloads or share links.

Zippy ID validation

Personal Zippy handles (payment IDs) are validated before verify and attach:

Validation rules

NameTypeDescription
Length3–20 charactersMinimum 3, maximum 20.
Charactersa-z, 0-9Lowercase letters and digits only.
Dot separatorat most onee.g. john.doe12
Leading/trailing dotinvalid.john and john. are rejected.
CasenormalizedInput normalized to lowercase before validation.

Valid: abc, user123, john.doe12. Invalid: ab (too short), a..b (double dot), .john.

Verify vs proceed

NameTypeDescription
VerifyManual flowLookup only — does not start the pay timer.
Proceed & payManual flowConfirms the payer and starts the pay window.

Session polling

The SDK polls session status while the checkout is in a non-terminal state.

Polling settings

NameTypeDescription
Default interval3 secondsWhen the browser tab is visible.
Background interval10 secondsWhen the tab is hidden.
Pollable statusesCREATED, AWAITING_PAYMENTPolling continues while session is active.
Terminal statusesCOMPLETED, EXPIRED, CANCELLED, FAILEDPolling stops when reached.

Polling also stops on unrecoverable errors (401, bootstrap failure) or when destroy() is called.

Error handling

Errors are RFC 7807 application/problem+json, surfaced as ZippyPayError:

Error codes

NameTypeDescription
payment-session.origin-not-allowed403Checkout page origin not registered. Add the domain under Integrations → Allowed browser origins.
payment-session.client-token-invalid401Bad or missing token. Terminal: session unavailable.
payment-session.id-invalid400Bad UUID. Thrown at init.
payment-id.not-found / payer.not-found404Unknown Zippy ID. Inline: Zippy ID not found.
payment-session.not-attachable400Session closed or expired. Terminal: expired.
payment-session.payer-attached409Payer already bound. SDK refreshes session state if the same payer.
payment-session.checkout-method-conflict409Method already locked. Refreshes session, shows error.
payment-session.failedPayment failed. Terminal: failed.
payment-session.declinedPayment declined. Terminal: declined.
payment-session.cancelledPayment cancelled. Terminal: cancelled.

Troubleshooting

Common issues

NameTypeDescription
Session unavailable immediately401 / invalid tokenCreate a new session on your server.
sessionId must be a UUID v4init errorVerify the session ID format.
container not foundinline modeEnsure the container element exists before create().
Checkout blocked on unregistered domainoriginRegister the page origin under Integrations → Allowed browser origins in the portal.
Web component shows nothingclientTokenSet el.clientToken property after mount.
Amount wrong on pagedisplayUse session.amount from onReady, not page props.
QR scan failsenvironment mismatchSession environment must match SDK environment.
Pay with Zippy does nothingmobile appUse SDK 1.2.1 (recommended). On phone, Pay with Zippy is the default since 1.2.0 — set mobileApp.policy to off for web-only.
Modal won't closemodal optionsEnable closeOnBackdropClick / closeOnEscape or call close().

Browser requirements

Required features

NameTypeDescription
ES2020+JavaScriptRequired.
fetchAPIRequired.
Shadow DOMWeb platformRequired.
Custom ElementsWeb platformRequired.
Intl.NumberFormatAPICurrency display.
  • Supported browsers: current Chrome, Firefox, Safari, Edge (evergreen).
  • Serve the host application over HTTPS in production.
  • Node.js ≥ 18 for local development and bundlers.