Orishare
Loyalty guides

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.

StepCallEffect
PreviewGET /v1/customers/{id}/store_credit/quote?currency=credit&order_total=4200Returns applicable_amount and remaining_to_pay. Changes nothing.
ApplyPOST /v1/store_credit/applicationsDebits the wallet now and holds the credit for the order.
CapturePOST /v1/store_credit/applications/{id}/captureThe order was paid. Optional final_amount returns the unused part.
VoidPOST /v1/store_credit/applications/{id}/voidThe order was abandoned. All held credit returns.
RefundPOST /v1/store_credit/applications/{id}/refundsReturns 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 answer 409 already_exists. After a void the order can take a new application.
  • An apply that the balance cannot cover answers 409 insufficient_balance and 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_ref and another amount answers 409 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_days by POST /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.

On this page