Stripe order identity and duplicate prevention
Use one durable merchant-owned purchase request, order, pending entitlement, or equivalent record for one intended purchase. Create or recover that row before calling Proxy, derive the Proxy session-create idempotency key from its stable non-PII ID, persist the returned Proxy session ID, and share the same handoff URL with every authorized payer.
Two identities, two questions
Section titled “Two identities, two questions”| Value | Question answered | Example |
|---|---|---|
| Stable session-create idempotency key | Which intended purchase/order should reuse one Proxy session? | proxy-session:purchase_request_123 |
buyerReference |
Who receives the merchant-owned benefit? | pending_entitlement_456 |
They may contain the same pending-entitlement ID in a simple data model, but they are separate concepts. Do not invent a new Proxy cross-session identity subsystem.
Correct flow
Section titled “Correct flow”const purchaseRequest = await findOrCreatePurchaseRequest({ clientRequestId, offerId, beneficiaryId,});
const handoff = await proxy.sessions.createHandoff({ amountMinor: purchaseRequest.amountMinor, buyerReference: purchaseRequest.beneficiaryId, cartSnapshot: purchaseRequest.cartSnapshot, currency: purchaseRequest.currency, idempotencyKey: `proxy-session:${purchaseRequest.id}`,});
await purchaseRequest.attachProxySession({ handoffUrl: handoff.handoffUrl, proxySessionId: handoff.id,});Retries must reuse the purchase-request row and reject attempts to change its beneficiary, offer, or immutable identity. A genuinely new purchase gets a new purchase-request ID even when the beneficiary, amount, and offer are identical.
Concurrent payers
Section titled “Concurrent payers”Share one stored handoff URL when a buyer invites two parents or other payers. Do not create a Proxy session per payer, browser, device, message, or link open. Both payers can render and interact with the same current Stripe root concurrently. Neither opening becomes the owner; neither waits for the other to leave or for an expiry timer.
When one qualifying payment completes, every page keeps the cart visible and renders an explicit completed state. The backend returns already_paid and performs zero new Stripe object creation. If an out-of-band second success exists despite the normal helper path, Proxy retains it as a financial fact and fee but refuses a second provisioning winner.
Unsafe identity choices
Section titled “Unsafe identity choices”Do not use any of these as the purchase-request identity:
- email, phone, or other PII;
- payer identity or browser/session identity;
- Stripe Customer, PaymentMethod, PaymentIntent, or Checkout Session ID;
- amount or cart total alone;
buyerReferencealone without a durable purchase-attempt policy;- a random UUID regenerated on every retry.
The Proxy session ID is public correlation, not a claim secret. For pre-account access, keep the beneficiary claim token merchant-owned, store only its hash, and never put it in Proxy/Stripe metadata or the cart snapshot.