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):
- Partner app calls the platform pack (Swift / Kotlin / JS) to start checkout and render UI the pack owns.
- 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.
- Core calls the cloud API (
POST /intents) with an idempotency key so a double tap does not create two charges. - Payment service owns payment state and returns a status the app can show (ready, processing, failed).
- User completes payment in the UI the pack presents.
- 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
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.
| Failure | Who owns the fix | What “good” looks like |
|---|---|---|
| Double tap / retry | Payment API | Same idempotency key → same intent |
| Duplicate webhook | Partner consumer | Dedupe on event id before fulfill |
| Late webhook | Partner app + backend | UI shows “confirming…” until backend has the event |
| Platform fork | SDK owners | Shared 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.
