---
title: Store Credits & Gift Cards
description: How Spree models store credits and gift cards — stored value balances, redeemable codes, and checkout usage for refunds, loyalty, and gifting.
---

## Overview

Spree provides two stored value mechanisms that customers can use at checkout:

- **Store Credits** - Value assigned directly to a customer's account by admins (refunds, loyalty rewards, compensation)
- **Gift Cards** - Value with a redeemable code that can be shared and redeemed by anyone

| 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

Store Credits are monetary values assigned directly to a customer's account. They are commonly used for:

- Refunds (instead of returning money to original payment method)
- Loyalty rewards
- Customer compensation
- Promotional credits

### Store Credit Model

```mermaid
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
    }
```

### Store Credit Attributes

| 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` |

### Why a Credit Exists

Store credits carry no category and no type. Two fields cover it instead:

| Field | What it tells you |
|-------|-------------------|
| `originator` | The return, exchange, claim or gift card that issued the credit, or nothing when an admin issued it by hand |
| `memo` | The free-text reason, written by the refund workflows or by the admin |

Store credits do not expire; only gift cards carry an expiry, on their own
record. Which credit is spent first is decided by `Spree::StoreCredit.oldest_first`.

> **NOTE:** `Spree::StoreCreditCategory` and `Spree::StoreCreditType` still exist as
> deprecated shells for compatibility. Nothing writes them.

### Store Credit Events

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) |

### Assigning Store Credits

Store credits are managed in the Admin Panel:

1. Navigate to **Customers** in the Admin Panel
2. Select a customer
3. Go to the **Store Credits** tab
4. Click **Add Store Credit**
5. Enter the amount, currency, and optional memo
6. Click **Create**

Store credits can also be created via the [Admin API](../../api-reference/admin-api/introduction.md), as a nested resource under the customer:


```typescript Admin SDK
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',
  memo: 'Goodwill credit for delayed shipment',
})
```

```bash CLI
spree api post /customers/cus_xxx/store_credits -d '{
  "amount": "25.00",
  "currency": "USD",
  "memo": "Goodwill credit"
}'
```


### Listing Store Credits Across Customers

Credits are written per customer, but read across them. The Admin API exposes a
read-only list so an integration can answer what the store owes without walking
every customer:


```typescript Admin SDK
const { data: credits, meta } = await client.storeCredits.list({
  outstanding: true,
  expand: ['customer', 'created_by'],
})

// One row per currency, summed over the same filter as the page.
meta.totals.forEach((total) => {
  console.log(total.currency, total.display_amount_remaining)
})
```

```bash CLI
spree api get /store_credits -q 'outstanding=true'
```


Filters mirror the questions a merchant asks: `customer_id`,
`customer_email_cont`, `currency`, `created_by_id`, `memo_cont`, a
`created_at` range, and two scopes — `outstanding` (money still owed versus
money spent) and `from_gift_card` (whether a gift card redemption created it).

Each credit's ledger is its own endpoint:


```typescript Admin SDK
const { data: events } = await client.storeCredits.events.list('credit_xxx')
```

```bash CLI
spree api get /store_credits/credit_xxx/events
```


Creating, updating and deleting a credit remain nested under the customer that
holds it — a credit without an owner has nobody to pay.

### Store Credit Events

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

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.

### Gift Card Model

```mermaid
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 status
        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
    }
```

### Gift Card Attributes

| Attribute | Description | Example |
|-----------|-------------|---------|
| `code` | Unique redemption code | `abc1234def` |
| `amount` | Total gift card value | `50.00` |
| `amount_used` | Amount already redeemed | `0.00` |
| `status` | Current status | `active` |
| `currency` | Currency code | `USD` |
| `expires_at` | Optional expiration date | `2025-12-31` |

### Gift Card Statuses

| Status | Description | How it gets there |
|-------|-------------|-------------------|
| `active` | Available for redemption | The status a card is created with |
| `partially_redeemed` | Some value has been redeemed | `Spree::GiftCards::Redeem`, when a balance remains |
| `redeemed` | Fully redeemed | `Spree::GiftCards::Redeem`, when nothing is left |
| `canceled` | Voided, and no longer spendable | `Spree::GiftCards::Cancel` |

Redemption does not need the caller to choose: `Spree::GiftCards::Redeem`
looks at what is left on the card and marks it partially or fully redeemed
accordingly. A card can be partially redeemed more than once, and each spend
publishes its own event.

Cancelling is refused once a card has been spent against, so cancellation can
never take back value a customer has already used.

> **NOTE:** Gift cards can also be `expired` if `expires_at` date has passed and the card hasn't been fully redeemed.

### Gift Card Lifecycle

```mermaid
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]
```

### Creating Gift Cards

#### Single Gift Card

1. Navigate to **Gift Cards** in the Admin Panel
2. Click **Create Gift Card**
3. Enter the amount and optional expiration date
4. Click **Create**
5. Share the generated code with the recipient

Gift cards can also be created via the [Admin API](../../api-reference/admin-api/introduction.md). The code is generated automatically if you don't supply one:


```typescript Admin SDK
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
```

```bash CLI
spree api post /gift_cards -d '{
  "amount": "50.00",
  "currency": "USD",
  "expires_at": "2026-12-31"
}'
```


#### Batch Gift Card Generation

For promotions or bulk distribution, you can create multiple gift cards at once using Gift Card Batches:

1. Navigate to **Gift Cards** in the Admin Panel
2. Click **Create Batch**
3. Enter:
   - **Prefix** - Code prefix for easy identification (e.g., `HOLIDAY`)
   - **Count** - Number of cards to generate
   - **Amount** - Value per card
   - **Expiration** - Optional expiration date
4. Click **Create**

Batches can also be created via the Admin API:


```typescript Admin SDK
const batch = await client.giftCardBatches.create({
  prefix: 'HOLIDAY',
  codes_count: 1000,
  amount: '25.00',
  currency: 'USD',
  expires_at: '2026-12-31',
})
```

```bash CLI
spree api post /gift_card_batches -d '{
  "prefix": "HOLIDAY",
  "codes_count": 1000,
  "amount": "25.00",
  "currency": "USD",
  "expires_at": "2026-12-31"
}'
```


> **INFO:** Large batches are processed in the background to avoid timeout issues.

### Redeeming Gift Cards

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.

When a gift card is applied to an order:
- The gift card value is used to pay for the order
- The gift card is marked as redeemed (or partially redeemed)
- No Store Credit is created for guest checkouts

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](../sdk/store/cart-checkout.md) for the full cart flow these calls belong to.


```typescript Store SDK
// 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>',
})
```

```bash cURL
# 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" }'
```


### Gift Card Events

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 |

## Using at Checkout

Store Credits and Gift Cards work differently at checkout:

- **Store Credits** - Require a customer account; applied from the customer's balance
- **Gift Cards** - Can be used by anyone (guests included); applied directly to the order via code

### Checkout Flow

```mermaid
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
```

### Store Credit Priority

When a registered customer holds several store credits, the oldest is spent
first (`Spree::StoreCredit.oldest_first`). Store credits do not expire — only
gift cards carry an expiry, on their own record.

## Related Documentation

- [Payments](payments.md) — Payment processing and methods
- [Orders](orders.md) — Order management
- [Customers](customers.md) — Customer management
- [Events](events.md) — Event system and subscribers
- [Admin SDK Quickstart](../sdk/admin/quickstart.md) — Set up and authenticate the Admin SDK
- [Monetary Amounts](../../api-reference/store-api/monetary-amounts.md) — How the API represents `amount`, `amount_used`, and other money fields
