docs/developer/core-concepts/store-credits-gift-cards.mdx
import { Since } from '/snippets/since.mdx';
<Since version="5.1" />Spree provides two stored value mechanisms that customers can use at checkout:
| Feature | Store Credit | Gift Card |
|---|---|---|
| Purpose | Refunds, loyalty rewards, compensation | Gifting, promotions, marketing |
| Requires account | Yes (tied to customer account) | No (guests can use at checkout) |
| Transferable | No | Yes (code can be shared) |
| Has code | No | Yes |
| Created by | Admin | Admin |
| Assignment | Directly to customer | Applied to order via code |
| Expiration | Configurable by type | Configurable per card |
Store Credits are monetary values assigned directly to a customer's account. They are commonly used for:
erDiagram
StoreCredit ||--o{ StoreCreditEvent : "has many"
StoreCredit ||--o{ Payment : "source for"
StoreCredit }o--|| User : "belongs to"
StoreCredit }o--|| Store : "belongs to"
StoreCredit }o--o| StoreCreditCategory : "belongs to"
StoreCredit }o--o| StoreCreditType : "belongs to"
StoreCredit {
decimal amount
decimal amount_used
decimal amount_authorized
string currency
string memo
}
StoreCreditCategory {
string name
}
StoreCreditType {
string name
integer priority
}
StoreCreditEvent {
string action
decimal amount
string authorization_code
}
| Attribute | Description | Example |
|---|---|---|
amount | Total credit amount | 100.00 |
amount_used | Amount already spent | 25.00 |
amount_authorized | Amount currently authorized for pending orders | 0.00 |
currency | Currency code | USD |
memo | Optional note about the credit | Refund for order #R123 |
Categories help organize store credits by their purpose:
| Category | Description |
|---|---|
| Default | General purpose store credit (also the default reimbursement category) |
| Expiring | Store credit that may expire |
| Non-expiring | Store credit that does not expire |
Types define the priority in which store credits are applied at checkout:
| Type | Description |
|---|---|
| Expiring | Credits that may expire (applied first) |
| Non-expiring | Credits that don't expire (applied last) |
Every action on a store credit is recorded as an event for audit purposes:
| Action | Description |
|---|---|
allocation | Initial credit was assigned |
authorize | Credit was authorized for an order |
capture | Authorized credit was captured |
void | Authorization was voided |
credit | Credit was returned (e.g., order canceled) |
Store credits are managed in the Admin Panel:
Store credits can also be created via the Admin API, as a nested resource under the customer:
<CodeGroup>import { createAdminClient } from '@spree/admin-sdk'
const client = createAdminClient({
baseUrl: 'https://store.example.com',
secretKey: 'sk_xxx',
})
const credit = await client.customers.storeCredits.create('cus_xxx', {
amount: '25.00',
currency: 'USD',
category_id: 'sccat_xxx',
memo: 'Goodwill credit for delayed shipment',
})
spree api post /customers/cus_xxx/store_credits -d '{
"amount": "25.00",
"currency": "USD",
"category_id": "sccat_xxx",
"memo": "Goodwill credit"
}'
The store credit system publishes lifecycle events:
| Event | Description |
|---|---|
store_credit.created | Store credit was created |
store_credit.updated | Store credit was updated |
Gift Cards are stored value codes created by admins that can be shared and redeemed by customers. When redeemed, they create a Store Credit on the customer's account.
erDiagram
GiftCard }o--|| Store : "belongs to"
GiftCard }o--o| User : "belongs to"
GiftCard }o--o| GiftCardBatch : "belongs to"
GiftCard ||--o{ StoreCredit : "originator for"
GiftCard {
string code
string state
decimal amount
decimal amount_used
decimal amount_authorized
string currency
date expires_at
datetime redeemed_at
}
GiftCardBatch {
string prefix
integer codes_count
decimal amount
string currency
date expires_at
}
| Attribute | Description | Example |
|---|---|---|
code | Unique redemption code | abc1234def |
amount | Total gift card value | 50.00 |
amount_used | Amount already redeemed | 0.00 |
state | Current state | active |
currency | Currency code | USD |
expires_at | Optional expiration date | 2025-12-31 |
stateDiagram-v2
[*] --> active: Created
active --> partially_redeemed: Partial redemption
active --> redeemed: Full redemption
active --> canceled: Admin cancels
partially_redeemed --> redeemed: Remaining redeemed
partially_redeemed --> partially_redeemed: Another partial redemption
| State | Description |
|---|---|
active | Available for redemption |
partially_redeemed | Some value has been redeemed |
redeemed | Fully redeemed |
canceled | Canceled by admin |
flowchart TB
A[Admin creates Gift Card] --> B[Code generated]
B --> C[Admin shares code with recipient]
C --> D[Recipient enters code at checkout]
D --> E[Store Credit created for customer]
E --> F[Gift Card marked as redeemed]
F --> G[Customer uses Store Credit]
Gift cards can also be created via the Admin API. The code is generated automatically if you don't supply one:
<CodeGroup>import { createAdminClient } from '@spree/admin-sdk'
const client = createAdminClient({
baseUrl: 'https://store.example.com',
secretKey: 'sk_xxx',
})
const giftCard = await client.giftCards.create({
amount: '50.00',
currency: 'USD',
expires_at: '2026-12-31',
})
console.log(giftCard.code) // share this with the recipient
spree api post /gift_cards -d '{
"amount": "50.00",
"currency": "USD",
"expires_at": "2026-12-31"
}'
For promotions or bulk distribution, you can create multiple gift cards at once using Gift Card Batches:
HOLIDAY)Batches can also be created via the Admin API:
<CodeGroup>const batch = await client.giftCardBatches.create({
prefix: 'HOLIDAY',
codes_count: 1000,
amount: '25.00',
currency: 'USD',
expires_at: '2026-12-31',
})
spree api post /gift_card_batches -d '{
"prefix": "HOLIDAY",
"codes_count": 1000,
"amount": "25.00",
"currency": "USD",
"expires_at": "2026-12-31"
}'
Gift cards can be redeemed by both registered customers and guest visitors at checkout. This is a key difference from Store Credits, which require a customer account.
<Info> Unlike Store Credits which are tied to a customer account, Gift Cards can be applied directly to an order during checkout - no account required. This makes them ideal for gifting to anyone. </Info>When a gift card is applied to an order:
Gift cards are applied via a dedicated POST /api/v3/store/carts/:cart_id/gift_cards endpoint (separate from the discount_codes endpoint used for promotion codes); the discount-codes endpoint does not handle gift cards. See the cart & checkout SDK guide for the full cart flow these calls belong to.
// Apply gift card code to cart (works for guests and registered customers)
// Gift cards use a dedicated endpoint — they reduce amount_due, not total
const cart = await client.carts.giftCards.apply('cart_abc123', 'abc1234def', {
spreeToken: '<token>',
})
// Remove gift card (ID from cart.gift_card.id)
await client.carts.giftCards.remove('cart_abc123', cart.gift_card.id, {
spreeToken: '<token>',
})
# Apply gift card to cart (works for guests and registered customers)
curl -X POST 'https://api.mystore.com/api/v3/store/carts/cart_abc123/gift_cards' \
-H 'X-Spree-Api-Key: pk_xxx' \
-H 'X-Spree-Token: <token>' \
-H 'Content-Type: application/json' \
-d '{ "code": "abc1234def" }'
The gift card system publishes lifecycle events:
| Event | Description |
|---|---|
gift_card.created | Gift card was created |
gift_card.redeemed | Gift card was fully redeemed |
gift_card.partially_redeemed | Gift card was partially redeemed |
Store Credits and Gift Cards work differently at checkout:
flowchart TB
A[Customer proceeds to payment] --> B{Guest or Registered?}
B -->|Registered| C{Has Store Credit?}
B -->|Guest| D{Has Gift Card code?}
C -->|Yes| E[Display available balance]
C -->|No| D
D -->|Yes| F[Apply Gift Card to order]
D -->|No| G[Show payment methods]
E --> H{Apply Store Credit?}
H -->|Yes| I[Authorize store credit]
H -->|No| D
F --> J{Covers order total?}
I --> J
J -->|Yes| K[Complete order]
J -->|No| G
G --> L[Customer pays remaining balance]
L --> K
When a registered customer has multiple store credits, they are applied in order of priority:
amount, amount_used, and other money fields