Payment Flow
Understand where the money moves in a Real-time Catalog purchase, why ConfirmPayment is required rather than optional, and which checks only your backend can enforce.
On a Real-time Catalog integration, your backend owns the catalog: pricing, availability, and per-player limits are all computed by you, at the moment Stash asks. That makes ConfirmPayment required rather than optional. It is the only point in the flow where Stash shows you a purchase that is still in progress and lets you decline it before the payment is captured.
This page covers web shop purchases, both from the cart and from Buy Now. The same ConfirmPayment contract applies when the same backend also powers Stash Pay checkout links; see Stash Pay High-Level Flow for that surface.
RegisterPayment, ConfirmPayment, and the pre-flight checks on this page apply only to the Real-time Catalog. A managed catalog purchase skips them: Stash authorizes and captures the payment, then your backend grants the items from the PURCHASE_SUCCEEDED webhook. See Granting from the webhook.
Where the Money Is at Each Step
Authorize
Stash asks the player's bank to check and reserve the funds. The money is held, not taken.
RegisterPayment
If you implement it, Stash calls your backend to register the purchase intent so you can reserve inventory. This step is optional and it is not the decision point.
ConfirmPayment
Stash calls your backend and waits. The funds are already held, so your response decides whether they are taken or released. This is the decision point.
Capture
Stash requests the capture that draws the held funds. It runs only after your backend returns success.
PURCHASE_SUCCEEDED
Stash queues the webhook as soon as the capture request is accepted.
PURCHASE_SUCCEEDED marks the capture request, not settlement. Stash sends the event as soon as the payment provider accepts the capture. Settlement happens asynchronously after that, and in rare cases a capture fails once the event has already gone out.
ConfirmPayment Is Required
Stash calls ConfirmPayment on every Real-time Catalog purchase. There is no shop configuration that skips it, and no response that means "Stash should decide".
- Success is a non-empty
resultsarray.ConfirmPaymenthas no status field. Returning the granted results in the body is how your backend says yes. - A
200with an empty or absentresultsarray is a failure. It confirmed nothing, so Stash treats it the same as an error response rather than as a silent success. - Every failure looks the same to Stash. A deliberate rejection, an unhandled
500, a timeout, and a malformed body are all handled identically.
When the call does not succeed, Stash does not capture. The authorization is released or left to expire at the payment provider and the player is not charged, so the bad case is "not charged, not granted" rather than "charged, not granted". A payment method that settles at authorization has no hold to release: its funds moved when the payment was authorized, so reversing that purchase takes a refund. If you implement the RegisterPayment and CancelPayment pair, Stash also calls CancelPayment on the cart flow so you can release the reservation you took.
Because a failure and a rejection are indistinguishable to Stash, an outage in your ConfirmPayment handler reads as a shop-wide refusal to sell. Respond 200 OK with your granted results unless you specifically do not want Stash to take the money.
The Catalog Read Before Checkout Is a Snapshot
Stash reads your catalog once more just before checkout. For a cart purchase it reads when the player starts checkout, as it creates the payment. For a Buy Now purchase it reads when the player taps Buy Now, before the checkout opens. Stash validates that the items still resolve and takes the price, the item's expiration, and its maxPurchasable from that read. The read is a snapshot of what your server said, not a reservation:
- Nothing holds stock between the read and the charge. No reservation is taken at catalog-read time on either side. A player can sit on the checkout page, and the same allowance can be spent elsewhere in the meantime.
- The read can be served from cache. It goes through the same catalog path as any other read, so the catalog cache applies to it.
Stash Keeps No Ledger of Your Limits
maxPurchasable is the player's remaining allowance as your server computed it at read time. For the items your catalog returns, Stash keeps no count of its own and never decrements the value: it checks the purchase against what the pre-checkout read returned.
Stash runs two pre-flight checks, both on values from that pre-checkout read rather than on a fresh read when the purchase is confirmed:
| Check | Cart purchase | Buy Now purchase |
|---|---|---|
| Item expiry | Rejects the purchase if the item's expiration has been reached by the time Stash would call ConfirmPayment | Rejects the purchase if the item's expiration has been reached by the time Stash would call ConfirmPayment |
| Remaining allowance | Rejects a quantity greater than maxPurchasable when the payment is created, and checks the same value again before Stash calls ConfirmPayment | Rejects a quantity greater than maxPurchasable before the checkout opens, and does not check it again |
A rejected pre-flight check fails the purchase before Stash calls RegisterPayment or ConfirmPayment, and Stash does not capture the payment.
These checks catch the obvious stale cases cheaply. They are not a substitute for your own check, because each value is only as fresh as the read it came from.
What Only Your Backend Can Enforce
Anything that depends on your live state has to be enforced in ConfirmPayment, because that is the last moment before the charge at which you are consulted:
- Limits shared across several offers. Stash sees a per-item
maxPurchasable, not a budget spanning a bundle, an offer chain, or a campaign. - Stock that is also sellable elsewhere. Anything a player can also buy in-game or on another surface can be consumed between the catalog read and the charge.
- Per-player caps and eligibility. Daily caps, cooldowns, progression gates, and anti-abuse rules all live in your systems.
- Anything priced or gated on live state. If the answer can change between the catalog read and the charge, the catalog snapshot cannot hold it.
Do not treat RegisterPayment as the enforcement point. It is a reservation hint that helps you lock inventory across multiple clients, it is optional, and Stash can be configured to skip it. ConfirmPayment is the call that gates the money.
Next Steps
Real-time Catalog
Endpoints, authentication, caching, and configuration for the Real-time Catalog integration.
ConfirmPayment API Reference
Full request and response schema for the confirm endpoint.
CancelPayment API Reference
Release a reservation after a purchase does not complete.
Stash Pay High-Level Flow
The same payment sequence as it applies to Stash Pay checkout links.
How is this guide?
Real-time Catalog
Learn how to expose REST API endpoints on your game backend to power personalized web shops with real-time offers. This guide covers the Catalog, Player, and Purchase APIs with protobuf-based type-safe integration.
Authentication Methods
The three ways players sign in to your Stash webshop: pre-authenticated links your game server generates, Direct Sign-in (SSO) with providers like Google, Apple, or Facebook, and Account Linking through deep links and QR codes.