Store credit at checkout
Give customers a money valued wallet and apply it to orders with quote, apply, capture, void and refund.
Store credit is a currency whose amounts are money: integer minor units of an ISO currency (cents for USD). Customers earn it like any other currency (rules, manual adjustments, imports) and spend it against an order at checkout.
1. Create the wallet
Create a spendable currency whose precision equals the minor unit, then mark it as a wallet.
curl -X POST "$ORISHARE_API/v1/currencies" -H "Authorization: Bearer $SECRET_KEY" \
-H "Idempotency-Key: credit-currency" -H "Content-Type: application/json" \
-d '{"code":"credit","name":"Store credit","precision":2}'
curl -X POST "$ORISHARE_API/v1/store_credit/wallets" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"currency":"credit","iso_currency":"USD","minor_unit":2,"hold_days":7}'The currency must forbid negative balances, and that setting cannot be changed while it is a
wallet. The ISO currency and minor unit of a registered wallet never change; hold_days can.
2. Issue credit
Any earning path works: a rule with points.award on the credit currency (cashback), or a
manual adjustment:
curl -X POST "$ORISHARE_API/v1/ledger_entries" -H "Authorization: Bearer $SECRET_KEY" \
-H "Idempotency-Key: goodwill-1001" -H "Content-Type: application/json" \
-d '{"customer_id":"user_42","currency":"credit","amount":1500,"reason":"Goodwill credit"}'3. Checkout flow
All checkout calls come from your server with a secret key. Amounts are minor units.
| Step | Call | Effect |
|---|---|---|
| Preview | GET /v1/customers/{id}/store_credit/quote?currency=credit&order_total=4200 | Returns applicable_amount and remaining_to_pay. Changes nothing. |
| Apply | POST /v1/store_credit/applications | Debits the wallet now and holds the credit for the order. |
| Capture | POST /v1/store_credit/applications/{id}/capture | The order was paid. Optional final_amount returns the unused part. |
| Void | POST /v1/store_credit/applications/{id}/void | The order was abandoned. All held credit returns. |
| Refund | POST /v1/store_credit/applications/{id}/refunds | Returns captured credit, once per refund_ref, up to the captured amount. |
curl -X POST "$ORISHARE_API/v1/store_credit/applications" -H "Authorization: Bearer $SECRET_KEY" \
-H "Idempotency-Key: apply-order-1001" -H "Content-Type: application/json" \
-d '{"customer_id":"user_42","currency":"credit","order_ref":"order-1001","order_total":4200,"amount":1500}'Rules that keep the flow safe to retry:
- One application per order and wallet. Applying the same order again with the same customer,
amount and total returns the existing application (
200). Other terms answer409 already_exists. After a void the order can take a new application. - An apply that the balance cannot cover answers
409 insufficient_balanceand holds nothing. - Capture, void and refund can be repeated: the second call returns the same state and moves no
value. A refund with a known
refund_refand another amount answers409 idempotency_key_reused. - A captured application cannot be voided. Refund it instead.
- Holds that are never captured or voided are released after the wallet's
hold_daysbyPOST /v1/store_credit/release_stale(run it on a schedule, daily is enough).
Returned credit (void, unused capture, refund) is a new credit entry. If the currency has an expiry policy, the returned credit follows the same terms as a manual credit.
4. Reconciliation
GET /v1/store_credit/reconciliation returns, per wallet: issued, held, captured,
returned, refunded and outstanding (your liability: the sum of customer balances). It also
lists mismatches: applications whose ledger movements differ from what their state implies.
That list should always be empty.
GET /v1/customers/{id}/store_credit returns one customer's available and held credit per wallet
with a formatted amount such as 15.00 USD.
Usage
Applying credit to an order counts as one operation. Capture, void, refund and the stale release
are included with it. Holds of deleted customers are not released; the release call lists holds
it could not release in skipped and carries on with the rest.
For merged customers the wallet is shared: balances, holds and application lists cover every merged profile.
Scopes
Wallet setup needs currencies:write. Apply, capture, void, refund and release need
ledger:write. Reads need ledger:read.
Game economy
Items, a shop, inventory, energy and lives, timed boosts, chests with published odds and battle passes: the configuration commands, the player and backend routes, the events, the limits, and one complete recipe.
VIP tiers with yearly windows and benefits
Qualify members per calendar or membership year, keep a tier through the following year, add a grace period, declare benefits and gate offers by tier.