All blog posts

Architecture · proposed · System Architecture · Financial Tech

How checkout fits into a partner app

Partners need checkout without rebuilding payments. This note traces one checkout from the app through the backend and a completion event, and names who owns each boundary.

3 min read · Updated 2026-05-10

Reader takeaway

  • A step-by-step checkout path from app to payment webhook
  • Who owns device UI, core, payment state, and fulfillment
  • Failure cases to design for, including what “idempotent” means here

The partner’s problem

A partner app wants ads, checkout, or rewards without standing up a full payments stack. The integration fails when ownership is unclear: the app thinks the SDK owns payment state, or the server thinks the webhook is optional, or each platform invents its own client API.

Status: proposed integration shape from commerce SDK design work. Confirm failure handling in your own stack before you promise it to partners. For keeping platforms aligned over time, see Keeping SDKs consistent across iOS, Android, and web.

From the app to the payment event

One checkout, step by step (hypothetical happy path):

  1. Partner app calls the platform pack (Swift / Kotlin / JS) to start checkout and render UI the pack owns.
  2. Platform pack forwards to the shared core for session checks and checkout entry. The pack may shape idiomatic APIs; it must not invent payment rules.
  3. Core calls the cloud API (POST /intents) with an idempotency key so a double tap does not create two charges.
  4. Payment service owns payment state and returns a status the app can show (ready, processing, failed).
  5. User completes payment in the UI the pack presents.
  6. Webhook delivers payment.completed (or equivalent) to the partner’s backend. Fulfillment keys off that event, not only on the device saying “success.”
App → Platform pack → Shared core → POST /intents
                                      ↓
                              Payment service
                                      ↓
Partner backend ← webhook payment.completed
Diagram from partner apps through gateway and services to event consumers
Point at ownership: device packs, gateway, payment service, and consumers of completion events.

Failures you should design for

Idempotent means the same logical request can be sent more than once and still produce one outcome. Example: the user taps Pay twice, or the network retries POST /intents. The API should accept a client-supplied idempotency key and return the same intent, not a second charge.

FailureWho owns the fixWhat “good” looks like
Double tap / retryPayment APISame idempotency key → same intent
Duplicate webhookPartner consumerDedupe on event id before fulfill
Late webhookPartner app + backendUI shows “confirming…” until backend has the event
Platform forkSDK ownersShared rules live in core; packs only bind

A shared core and shared contracts reduce duplicated client logic. They do not eliminate platform bugs or divergent releases. Humans still gate public API breaks.

Questions before you build

Who owns payment state? Who retries? Who must be idempotent? What does the app show if the webhook is late? Which pack is allowed to change shared namespaces? Answer those before you write the first binding.