# Accept gift card payments

Use Terminal to accept third-party closed-loop gift cards.

> Request access to the Gift card private preview by contacting your Account Executive or by sending an email to [terminal-gift-cards@stripe.com](mailto:terminal-gift-cards@stripe.com) with the following information:
> 
> - Use case for in-person and/or online gift card acceptance
- Terminal device and integration type
- Gift card provider
- Country/countries where gift cards will be accepted
- Estimated annual gift card transaction volume

Onboard your gift card provider to Stripe and use the Terminal reader to accept and manage third-party closed-loop magnetic stripe gift cards.

Closed-loop gift cards are stored-value instruments that can only be redeemed at a single business or family of businesses. Unlike open-loop gift cards (such as Visa or Amex gift cards), settlement happens off-Stripe, meaning no funds move on Stripe at redemption time.

## How it works

Gift card operations on Terminal fall into two categories:

- **Redemption**: Use the [PaymentIntent](https://docs.stripe.com/api/payment_intents.md) flow with `gift_card` as a payment method type
- **Non-payment operations** (activate, check balance, reload, cashout): Send a request to the reader, prompt the cardholder to swipe, and receive a `gift_card_operation` with the result

## Availability

- **Readers with swipe capability**: [Stripe Reader S700/S710](https://docs.stripe.com/terminal/readers/stripe-reader-s700-s710.md), [BBPOS WisePOS E](https://docs.stripe.com/terminal/readers/bbpos-wisepos-e.md), and [Verifone](https://docs.stripe.com/terminal/payments/setup-reader/verifone.md) devices
- **Gift card provider**: [SVS](https://www.storedvalue.com/) (Stored Value Solutions)
- **Gift card format**: Magnetic stripe
- **Availability**: US only

## Before you begin

- Onboard your gift card provider to Stripe. Contact your Sales team to onboard your gift card provider
- All API requests require a preview API version header. You must include `Stripe-Version: 2026-02-25.preview` in every request

## Limitations

- You can’t attach gift card payment methods to a `Customer` object or save them as stored payment methods
- Calling the [Refunds](https://docs.stripe.com/api/refunds.md) endpoint on a gift card PaymentIntent returns an error. You need to refund manually by reloading the original gift card or activating a new one
- You can’t list gift card operations. Use the `Retrieve a gift card operation` endpoint to look up individual operations by ID

## Gift card program constraints

Your gift card provider and program define the rules that govern each card, including:

- **Load amounts**: The minimum and maximum amount you can add in a single activation or reload.
- **Maximum balance**: The highest balance a card can hold.
- **Denominations**: Any fixed or incremental amounts the program allows.
- **Expiration**: Whether cards expire and when. The provider enforces expiration. Stripe doesn’t enforce it on the provider’s behalf, so a card with a past expiration date can remain usable if the provider still honors it.

Stripe doesn’t define the rules for each card. Stripe forwards each operation to the provider, which validates it against these rules, so an operation with an unsupported amount or other invalid input can fail when the provider processes it. To avoid surprises, make sure your integration only sends values that your program supports.

## Gift card provider constraints

### SVS (Stored Value Solutions)

- Balance checks on inactive cards return a successful operation with a zero balance. All other operations on inactive cards fail with the `card_not_activated` decline code.
- Payments will always authorize for the available balance on the card if the amount of the payment is greater than the remaining balance. Always set [payment_method_options.gift_card.request_partial_authorization](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-gift_card-request_partial_authorization) to `if_available` when accepting SVS gift cards.
- In a *sandbox* (A sandbox is an isolated test environment that allows you to test Stripe functionality in your account without affecting your live integration. Use sandboxes to safely experiment with new features and changes), the [payment_reference](https://docs.stripe.com/api/payment-record/object.md#payment_record_object-processor_details-svs-payment_reference) on the `PaymentRecord` isn’t unique per transaction.

## Non-payment operations

For non-payment gift card operations, the flow is:

1. Send the operation request to the reader.
2. The reader prompts the cardholder to swipe a gift card.
3. Poll the reader or listen for the [terminal.reader.action_succeeded](https://docs.stripe.com/api/events/types.md#event_types-terminal.reader.action_succeeded) webhook.
4. Retrieve the completed action and expand the `gift_card_operation` to get the result.

### Activate a gift card

Activate a new gift card with an initial load amount. You must activate a gift card before the cardholder can use it for any other operations, including balance checks and redemptions.

```curl
curl https://api.stripe.com/v1/terminal/readers/{{TERMINALREADER_ID}}/activate_gift_card \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d brand=svs \
  -d "balance[amount]=5000" \
  -d "balance[currency]=usd"
```

> Omitting `balance` doesn’t cause an immediate validation error. Balance requirements vary by gift card brand, so validation happens after the cardholder swipes.

The response returns the reader with `action.status` set to `in_progress`:

```json
{
  "id": "{{READER_ID}}",
  "object": "terminal.reader",
  "action": {
    "failure_code": null,
    "failure_message": null,
    "status": "in_progress",
    "type": "activate_gift_card"
  }
}
```

After the cardholder swipes, [retrieve the Reader](https://docs.stripe.com/api/terminal/readers/retrieve.md) to inspect the result. Use the `expand` parameter to include the full `gift_card_operation` object:

```curl
curl -G https://api.stripe.com/v1/terminal/readers/{{TERMINALREADER_ID}} \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "expand[]=action.activate_gift_card.gift_card_operation"
```

```json
{
  "id": "{{READER_ID}}",
  "object": "terminal.reader",
  "action": {
    "status": "succeeded",
    "type": "activate_gift_card",
    "activate_gift_card": {
      "gift_card": "{{GIFT_CARD_ID}}",
      "gift_card_operation": {
        "id": "{{GIFT_CARD_OPERATION_ID}}",
        "object": "gift_card_operation",
        "gift_card": "{{GIFT_CARD_ID}}",
        "livemode": false,
        "type": "activation",
        "balance": {
          "amount": 5000,
          "currency": "usd"
        },
        "created": 1234567890,
        "completed_at": 1234567891,
        "status": "succeeded"
      }
    }
  }
}
```

### Check the gift card balance

Check the remaining balance on an active gift card. You can use this API before a transaction to verify available funds, or to display the balance to the cardholder at their request. When you call the `check_gift_card_balance` endpoint on the [Reader](https://docs.stripe.com/api/terminal/readers/object.md), the reader prompts the cardholder to swipe their card.

```curl
curl https://api.stripe.com/v1/terminal/readers/{{TERMINALREADER_ID}}/check_gift_card_balance \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d brand=svs
```

The response returns the reader with `action.status` set to `in_progress`:

```json
{
  "id": "{{READER_ID}}",
  "object": "terminal.reader",
  "action": {
    "failure_code": null,
    "failure_message": null,
    "status": "in_progress",
    "type": "check_gift_card_balance"
  }
}
```

After the cardholder swipes, [retrieve the Reader](https://docs.stripe.com/api/terminal/readers/retrieve.md) to inspect the result. Use the `expand` parameter to include the full `gift_card_operation` object:

```curl
curl -G https://api.stripe.com/v1/terminal/readers/{{TERMINALREADER_ID}} \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "expand[]=action.check_gift_card_balance.gift_card_operation"
```

```json
{
  "id": "{{READER_ID}}",
  "object": "terminal.reader",
  "action": {
    "status": "succeeded",
    "type": "check_gift_card_balance",
    "check_gift_card_balance": {
      "gift_card": "{{GIFT_CARD_ID}}",
      "gift_card_operation": {
        "id": "{{GIFT_CARD_OPERATION_ID}}",
        "object": "gift_card_operation",
        "gift_card": "{{GIFT_CARD_ID}}",
        "livemode": false,
        "type": "balance_check",
        "balance": {
          "amount": 5000,
          "currency": "usd"
        },
        "created": 1234567890,
        "completed_at": 1234567891,
        "status": "succeeded"
      }
    }
  }
}
```

### Reload a gift card

Add funds to an existing gift card. Use this when a cardholder wants to increase the balance on a previously activated card.

```curl
curl https://api.stripe.com/v1/terminal/readers/{{TERMINALREADER_ID}}/reload_gift_card \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d brand=svs \
  -d amount=5000 \
  -d currency=usd
```

The response returns the reader with `action.status` set to `in_progress`:

```json
{
  "id": "{{READER_ID}}",
  "object": "terminal.reader",
  "action": {
    "failure_code": null,
    "failure_message": null,
    "status": "in_progress",
    "type": "reload_gift_card"
  }
}
```

After the cardholder swipes, [retrieve the Reader](https://docs.stripe.com/api/terminal/readers/retrieve.md) to inspect the result. Use the `expand` parameter to include the full `gift_card_operation` object:

```curl
curl -G https://api.stripe.com/v1/terminal/readers/{{TERMINALREADER_ID}} \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "expand[]=action.reload_gift_card.gift_card_operation"
```

The completed action returns `previous_balance` and the updated `balance`:

```json
{
  "id": "{{READER_ID}}",
  "object": "terminal.reader",
  "action": {
    "status": "succeeded",
    "type": "reload_gift_card",
    "reload_gift_card": {
      "gift_card": "{{GIFT_CARD_ID}}",
      "gift_card_operation": {
        "id": "{{GIFT_CARD_OPERATION_ID}}",
        "object": "gift_card_operation",
        "gift_card": "{{GIFT_CARD_ID}}",
        "type": "reload",
        "balance": {
          "amount": 5000,
          "currency": "usd"
        },
        "previous_balance": {
          "amount": 0,
          "currency": "usd"
        },
        "created": 1234567890,
        "completed_at": 1234567891,
        "status": "succeeded"
      }
    }
  }
}
```

### Cash out a gift card

Cash out the full remaining balance on a gift card, setting it to zero. Use this when a cardholder wants to redeem the remaining value as cash, typically to comply with local cash-equivalent laws or store policy.

```curl
curl https://api.stripe.com/v1/terminal/readers/{{TERMINALREADER_ID}}/cashout_gift_card \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d brand=svs
```

The response returns the reader with `action.status` set to `in_progress`:

```json
{
  "id": "{{READER_ID}}",
  "object": "terminal.reader",
  "action": {
    "failure_code": null,
    "failure_message": null,
    "status": "in_progress",
    "type": "cashout_gift_card"
  }
}
```

After the cardholder swipes, [retrieve the Reader](https://docs.stripe.com/api/terminal/readers/retrieve.md) to inspect the result. Use the `expand` parameter to include the full `gift_card_operation` object:

```curl
curl -G https://api.stripe.com/v1/terminal/readers/{{TERMINALREADER_ID}} \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "expand[]=action.cashout_gift_card.gift_card_operation"
```

The completed action returns `previous_balance` showing the balance before cashout:

```json
{
  "id": "{{READER_ID}}",
  "object": "terminal.reader",
  "action": {
    "status": "succeeded",
    "type": "cashout_gift_card",
    "cashout_gift_card": {
      "gift_card": "{{GIFT_CARD_ID}}",
      "gift_card_operation": {
        "id": "{{GIFT_CARD_OPERATION_ID}}",
        "object": "gift_card_operation",
        "gift_card": "{{GIFT_CARD_ID}}",
        "type": "cashout",
        "balance": {
          "amount": 0,
          "currency": "usd"
        },
        "previous_balance": {
          "amount": 5000,
          "currency": "usd"
        },
        "created": 1234567890,
        "completed_at": 1234567891,
        "status": "succeeded"
      }
    }
  }
}
```

### Void an operation

Void an operation to undo a non-payment operation you performed by mistake—for example, if a cashier accidentally loads 500 USD onto a card instead of 50 USD. You can void activate, reload, and cashout operations within 24 hours. Make void requests from your back end using the Stripe API:

```curl
curl https://api.stripe.com/v1/gift_cards/{{GIFT_CARD_ID}}/void_operation \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d operation={{GIFT_CARD_OPERATION_ID}}
```

The gift card provider can reject a void if other activity on the card depends on the operation you’re voiding—for example, voiding an activation after the card has already been reloaded. If the provider rejects the void, the original operation stands.

## Redeem a gift card

To accept a gift card as payment, use a [PaymentIntent](https://docs.stripe.com/api/payment_intents.md). The key differences from a card payment are:

- Include `gift_card` in the `payment_method_types` array when creating the PaymentIntent
- Specify the gift card brand (for example, `svs`) in the process configuration
- The confirmed PaymentIntent returns a [PaymentRecord](https://docs.stripe.com/api/payment-record.md) instead of a [Charge](https://docs.stripe.com/api/charges.md). The PaymentRecord tracks the transaction details from the gift card provider, including the amount authorized and processor-specific references

> Gift card redemption transactions appear as normal payments in the Stripe Dashboard. You can identify them by the `gift_card` payment method type on the PaymentIntent.

### Create a PaymentIntent

To accept a gift card as payment, [create a PaymentIntent](https://docs.stripe.com/api/payment_intents/create.md) with `gift_card` in the `payment_method_types` array. Include `card_present` if you want to allow the cardholder to pay with either a gift card or a standard card.

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d amount=2000 \
  -d currency=usd \
  -d capture_method=automatic \
  -d "payment_method_types[]=gift_card" \
  -d "payment_method_options[gift_card][request_partial_authorization]=if_available"
```

Setting `request_partial_authorization` to `if_available` allows the payment to succeed with a partial amount if the gift card balance is insufficient. If you set it to `never` (the default), the payment fails when funds are insufficient.

### Process the payment

Use [process_payment_intent](https://docs.stripe.com/api/terminal/readers/process_payment_intent.md) to collect the payment method and confirm in a single step. Specify the gift card brand in the process configuration.

```curl
curl https://api.stripe.com/v1/terminal/readers/{{TERMINALREADER_ID}}/process_payment_intent \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "payment_intent={{PAYMENTINTENT_ID}}" \
  -d "process_config[gift_card_brand]=svs"
```

The reader prompts the cardholder to swipe their gift card. After the payment is processed, the PaymentIntent transitions to `succeeded` and includes a PaymentRecord:

```json
{
  "id": "{{PAYMENT_INTENT_ID}}",
  "object": "payment_intent",
  "amount": 2000,
  "currency": "usd",
  "status": "succeeded",
  "payment_method_types": ["card_present", "gift_card"],
  "payment_record": {
    "id": "{{PAYMENT_RECORD_ID}}",
    "object": "payment_record",
    "amount": {
      "currency": "usd",
      "value": 2000
    },
    "amount_requested": {
      "currency": "usd",
      "value": 2000
    },
    "processor_details": {
      "type": "svs",
      "svs": {
        "payment_reference": "svs_transaction_id"
      }
    }
  }
}
```

### Partial authorization

If the gift card balance is insufficient for the full amount, a partial authorization can occur. Handle partial authorizations by collecting the remaining balance through a second payment method.

To enable partial authorization, set `payment_method_options[gift_card][request_partial_authorization]` to `if_available` when [creating the PaymentIntent](https://docs.stripe.com/terminal/features/gift-cards.md#create-a-paymentintent). If the gift card has insufficient funds and partial authorization isn’t enabled (or set to `never`), the payment fails.

In a partial authorization, `amount_authorized` on the PaymentRecord is less than `amount_requested`:

```json
{
  "id": "{{PAYMENT_INTENT_ID}}",
  "object": "payment_intent",
  "amount": 2000,
  "amount_received": 500,
  "currency": "usd",
  "status": "succeeded",
  "payment_method_types": ["card_present", "gift_card"],
  "payment_record": {
    "id": "{{PAYMENT_RECORD_ID}}",
    "object": "payment_record",
    "amount_authorized": {
      "currency": "usd",
      "value": 500
    },
    "amount_canceled": {
      "currency": "usd",
      "value": 1500
    },
    "amount_requested": {
      "currency": "usd",
      "value": 2000
    },
    "processor_details": {
      "type": "svs",
      "svs": {
        "payment_reference": null
      }
    }
  }
}
```

## Connect platforms

For [Connect](https://docs.stripe.com/connect.md) platforms where the connected account has the relationship with the gift card provider, pass `on_behalf_of` with the connected account ID on all non-payment operations.

```curl
curl https://api.stripe.com/v1/terminal/readers/{{TERMINALREADER_ID}}/check_gift_card_balance \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d brand=svs \
  -d "on_behalf_of={{CONNECTEDACCOUNT_ID}}"
```

For redemption flows, use `on_behalf_of` on the PaymentIntent as you would with any other payment method.

Because a gift card redemption doesn’t move money on Stripe, move funds separately when your platform needs to settle with the connected account. If you hold the underlying funds on Stripe, create a [transfer](https://docs.stripe.com/connect/separate-charges-and-transfers.md) to the connected account. If the funds are held outside Stripe, the movement happens out of band and your platform manages it. To collect fees for gift card usage from the connected account, use [Account Debits](https://docs.stripe.com/connect/account-debits.md).

## Test your integration

You can test your integration with a [simulated reader](https://docs.stripe.com/terminal/payments/connect-reader.md?terminal-sdk-platform=server-driven&reader-type=simulated#create-a-simulated-reader) instead of physical hardware. After creating a simulated reader, use the [present_payment_method](https://docs.stripe.com/api/terminal/readers/present_payment_method.md) test helper to simulate the cardholder swiping a gift card.

> You need to use your own test track2 number received from your gift card provider (for example, SVS) to test with the simulated reader. The simulation API makes an actual API request to your gift card provider’s sandbox environment. Contact your gift card provider to obtain test credentials for their sandbox.

### Simulate a non-payment operation

To test non-payment gift card operations (activate, check balance, reload, cashout) with the simulated reader, start the operation, then call the `present_payment_method` test helper to simulate the swipe.

Start the gift card operation on the simulated reader. This example uses activation, but the same approach works for `check_gift_card_balance`, `reload_gift_card`, and `cashout_gift_card`:

```curl
curl https://api.stripe.com/v1/terminal/readers/{{TERMINALREADER_ID}}/activate_gift_card \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d brand=svs \
  -d "balance[amount]=5000" \
  -d "balance[currency]=usd"
```

Next, call `present_payment_method` with `type=gift_card` to simulate the swipe. Pass the test track2 data from your gift card provider and the gift card brand:

```curl
curl https://api.stripe.com/v1/test_helpers/terminal/readers/{{TERMINALREADER_ID}}/present_payment_method \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d type=gift_card \
  -d "gift_card[track_2]={{GIFT_CARD_PROVIDER_TEST_TRACK_2}}" \
  -d "gift_card[brand]=svs"
```

After the simulated swipe, retrieve the reader to inspect the result. Use the `expand` parameter to include the full `gift_card_operation` object. For example, after the activation operation:

```curl
curl -G https://api.stripe.com/v1/terminal/readers/{{TERMINALREADER_ID}} \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "expand[]=action.activate_gift_card.gift_card_operation"
```

### Simulate a gift card redemption

To test the redemption (payment) flow with the simulated reader, create a PaymentIntent, process it on the reader, then simulate the swipe.

Create a PaymentIntent with `gift_card` in the `payment_method_types`:

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d amount=2000 \
  -d currency=usd \
  -d capture_method=automatic \
  -d "payment_method_types[]=gift_card" \
  -d "payment_method_options[gift_card][request_partial_authorization]=if_available"
```

Process the PaymentIntent on the simulated reader with the gift card brand:

```curl
curl https://api.stripe.com/v1/terminal/readers/{{TERMINALREADER_ID}}/process_payment_intent \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "payment_intent={{PAYMENTINTENT_ID}}" \
  -d "process_config[gift_card_brand]=svs"
```

Simulate the gift card swipe using `present_payment_method`:

```curl
curl https://api.stripe.com/v1/test_helpers/terminal/readers/{{TERMINALREADER_ID}}/present_payment_method \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d type=gift_card \
  -d "gift_card[track_2]={{GIFT_CARD_PROVIDER_TEST_TRACK_2}}" \
  -d "gift_card[brand]=svs"
```

After the simulated swipe, poll the reader or listen for the `terminal.reader.action_succeeded` webhook to verify the operation completed.

## Handle errors

### Webhooks

Subscribe to these [webhook](https://docs.stripe.com/webhooks.md) events to track operation results:

- [terminal.reader.action_succeeded](https://docs.stripe.com/api/events/types.md#event_types-terminal.reader.action_succeeded): A gift card operation completed. The reader object in the payload contains the result
- [terminal.reader.action_failed](https://docs.stripe.com/api/events/types.md#event_types-terminal.reader.action_failed): A gift card operation failed. Check `action.failure_code` and `action.failure_message` for details

> The `gift_card` and `gift_card_operation` fields only appear in the `terminal.reader` payload when you register your webhook endpoint with the preview version header `Stripe-Version: 2026-02-25.preview`.

### Timeout errors

If the reader can’t reach Stripe, it displays a failure screen and sends a `terminal.reader.action_failed` webhook with a `failure_code` of `connection_error`. For more details on handling timeouts, see [Payment timeout](https://docs.stripe.com/terminal/payments/collect-card-payment.md?terminal-sdk-platform=server-driven#payment-timeout).

### Maximum balance constraints

Gift card providers enforce maximum balance limits on activation and reload. Stripe doesn’t impose its own balance cap. If an activation or reload amount exceeds the provider’s maximum, the operation fails with a `failure_code` of `invalid_amount`. Contact your gift card provider to confirm their balance limits.

### Network decline codes

When a [PaymentIntent](https://docs.stripe.com/api/payment_intents.md) for a gift card redemption fails, the error response might include a `network_decline_code` containing the raw decline reason from the gift card provider. This code is provider-specific and not normalized by Stripe. Check with your gift card provider for the meaning of specific network decline codes.

For non-payment operations (activate, reload, check balance, cashout), the reader action uses `failure_code` with a Stripe-normalized reason instead of a provider-specific network decline code.

## See also

- [Collect card payments](https://docs.stripe.com/terminal/payments/collect-card-payment.md)
- [Terminal webhooks](https://docs.stripe.com/terminal/payments/collect-card-payment.md?terminal-sdk-platform=server-driven#webhooks)
