Process EBT SNAP refunds

Learn how to implement EBT SNAP compliant refunds with the Forage API

A refund is a repayment to a customer after they return item(s) in an order. This guide details the specifics of refunding EBT SNAP benefits. The guide is divided into the following sections:

📘

Refund receipts

If you’re looking for detailed information about refund receipts specifically, then head to the Forage receipts guide.

Refund scenarios

Item substitutions

An item substitution is when a lower-cost alternative replaces an item in the original order. A customer might initiate the substitution, or a retailer or delivery shopper might make the swap when they discover a price discrepancy, an inventory error, or that the item is out of stock. Weighted items can also result in item substitutions. If the fulfilled weight is lower than the ordered weight, then the difference may result in a refund.

Item removals

An item removal is when an item is removed from the order. A customer might remove it while the order is being fulfilled, or a retailer or delivery shopper might opt to remove an item if it’s out of stock and there’s no adequate substitute, or if the retailer doesn’t support substitutions.

Item returns

A customer might initiate a return if an item is expired or otherwise unsatisfactory.

📘

Refunds and promotional offers

If a merchant offers any promotions that depend on the total order charge, like free shipping or delivery after a certain amount, then a refund might affect a customer’s eligibility for the offer. For the best customer experience, it’s recommended to honor the original promotion, despite any reduction in the total order charge.

FNS requirements

Deadlines

  • Customer-requested refunds must be processed within two business days after the item is returned.
  • Refunds due to merchant error, like overestimated weighted products, out of stock items, or substitutions must be processed on the same day that the original order was fulfilled.
  • Merchants must send customers a refund notification, whether via email, confirmation screen, or a customer’s account portal, within 24 hours of when the refund is completed.

📘

Refund notification requirements

For complete details on refund receipt requirements including example notifications, head to the Forage receipts guide.

Payment instruments

Summary: FNS payment instrument requirements for refunds

  • Items that are ineligible for SNAP can never be refunded to SNAP.
  • Items that are ineligible for EBT Cash can never be refunded to EBT Cash.
  • Never refund more to a specific payment method than it was originally charged.

SNAP

Only SNAP eligible purchases can be refunded to a customer's SNAP balance. To protect against fraud, no other payment instrument can receive the SNAP refund. Refunds from SNAP to credit/debit, EBT Cash, or store gift cards are prohibited.

Refunding non-SNAP items, like alcohol, should never result in a SNAP refund.

EBT Cash

Only EBT Cash eligible purchases can be refunded to a customer's EBT Cash balance. To protect against fraud, no other payment instrument can receive the EBT Cash refund. Refunds from EBT Cash to credit/debit, SNAP, or store gift cards are prohibited.

Refunding non-EBT Cash items, like alcohol, should never result in an EBT Cash refund.

SNAP purchases can only be refunded to SNAP. EBT Cash purchases can only be refunded to EBT Cash.

SNAP purchases can only be refunded to SNAP. EBT Cash purchases can only be refunded to EBT Cash.

Refund entry

Every retailer must have a secure method for authorized employees, using password protected user IDs, to manually enter refund requests. There must be functionality for an employee to either issue refunds by item or to directly enter a specific amount to be refunded.

Refund notification

Refer to the EBT SNAP refunds section of the receipts guide for a comprehensive list of information that must be shared with customers after a refund is completed.

How to process online EBT SNAP refunds

Forage recommends splitting refund feature development into two parts:

  1. Design the refunds flow
  2. Query the API to issue refunds

Jump to the example API requests

Design the refunds flow

Either restore the original tender amount or maximize credit/debit return

When an entire order is refunded, the merchant returns the total amount paid to the customer. However, if a refund involves only some of the items purchased, then a merchant returns just the cost of those items.

If you map the exact tender amount paid per item in your database, then the fastest-to-build refunds flow is to issue a refund based on the original payment split.

To maximize the credit/debit return instead, you can redistribute the payment split. Calculate the refund based on whether or not the restored EBT SNAP balance can be applied to other eligible items in the cart. This is not required, but it’s the preferred customer experience.

To illustrate the different options, consider the following example receipt. "SP" indicates that an item is SNAP eligible. "EBT" indicates EBT Cash eligible:

588

The receipt illustrates an order involving multiple payment instruments.

Pretend that the customer returns Item A, a $10 SNAP eligible item. The refunds flow that restores the original tender amount refunds $10 to the customer’s SNAP balance. The flow that maximizes the credit/debit refund results in a $9.90 credit card refund. Read on for details.

Refunds flow that restores the original tender amount

In this example scenario, the return of Item A restores $10 to the customer’s SNAP balance.

If the customer decides to also return Item B, then $10 is refunded to the credit card balance. Returning Item D results in a $5.05 refund to the customer’s EBT Cash account.

Refunds flow that maximizes the credit/debit return

This isn’t required for FNS compliance, but it makes for a better customer experience.

If a customer pays for EBT SNAP eligible items via a mix of payment methods, then during a refund it’s possible to redistribute the payment split. The final refund amount is determined based on whether or not the restored EBT SNAP balance can be applied to other eligible items in the cart.

Items with higher tax rates can be refunded to SNAP first to minimize a customer’s taxes and maximize their credit/debit refund, as outlined in the steps below.

Step 1: Calculate the updated order total

Subtract the cost of the returned item from the original order total.

In the scenario, you’d deduct the $10.00 value of Item A from the $60.00 order total. This results in an updated order total of $50.00.

Step 2: Sort any remaining SNAP eligible items by highest tax rate

If SNAP-paid items are returned, then the SNAP credit can be redistributed among any remaining SNAP eligible order items. To maximize a customer’s potential credit/debit card return, apply the SNAP credit to the highest-taxed items first.

In the scenario, after removing Item A, two SNAP eligible items are in the cart:

  • Item C: $10.00, Tax 1%
  • Item B: $10.00, Tax 0%

Item C has a higher tax rate, so it’s sorted above Item B.

Step 3: Apply SNAP credit to SNAP eligible items

Apply all of the returned SNAP credit to the remaining SNAP eligible items in the cart, starting with the highest-taxed items. If the credit exceeds the value of the remaining SNAP eligible items, then return the excess SNAP amount to the customer’s SNAP balance.

In the scenario, the return of Item A restores $10.00 in SNAP credit. That $10.00 is applied in full to Item C, because its tax rate is higher than Item B’s.

Step 4: Distribute the original EBT Cash charge

The original EBT Cash payment can be applied to any remaining SNAP eligible and EBT Cash eligible items. The order of the items doesn’t matter: unlike SNAP purchases, EBT Cash purchases are taxed. If the original EBT Cash payment is greater than the cost of the remaining eligible items, then refund the difference to the customer’s EBT Cash account balance.

In the scenario, the customer paid $5.05 in EBT Cash. That $5.05 is applied in full to the original Item D.

Step 5: Apply the original credit/debit card payment to outstanding items and fees

Apply the original credit/debit card payment to any outstanding items and fees. If the credit/debit card payment exceeds the cost of the outstanding items, then refund the difference to the customer’s credit/debit card account balance. If the cost of the outstanding items exceeds the original credit/debit card payment, then create a new order and charge the customer the difference.

In the scenario, the following items remain in the cart:

  • Item B: $10.00, Tax 0%
  • Item E: $25.00
    • Plus 1% tax: $25.25

The original $45.35 credit/debit card payment is applied to the outstanding $35.20 total. The remaining $9.90 is refunded to the credit/debit card.

Query the Forage API

The endpoints that you need to call to issue refunds differ depending on whether you’ve built with an SDK or the Forage Checkout UI.

SDK

SDK integrations call the Payment Refunds endpoints.

To refund a payment, send a POST to /payments/{payment_ref}/refunds/, passing the ref for the relevant Payment to be refunded as the path param. In the request body specify the amount to be refunded, along with a reason , as in the following example.

Request
curl --request POST \
     --url https://api.sandbox.joinforage.app/api/payments/{payment_ref}/refunds/ \
     --header 'Authorization: Bearer <authentication-token>' \
     --header 'Idempotency-Key: <idempotency-key>' \
     --header 'Merchant-Account: <merchant-account>' \
     --header 'accept: application/json' \
     --header 'content-type: application/json' \
     --data '
{
  "amount": 25.99,
  "reason": "Order could not be delivered"
}
'
Response
{
  "ref": "0ddbcda871",
  "payment_ref": "b873fe62dc",
  "merchant": "9000055",
  "funding_type": "ebt_snap",
  "amount": "25.99",
  "reason": "Order could not be delivered.",
  "created": "2023-09-18T11:38:29.279185-07:00",
  "updated": "2023-09-18T11:38:29.279209-07:00",
  "status": "requires_confirmation",
  "last_processing_error": null,
  "receipt": null
}

📘

Reference documentation

Forage Checkout (Fully Hosted and Custom)

Forage Checkout integrations, for both Fully Hosted and Custom, call the Order Refunds endpoints.

Call different endpoints for refunding an entire Order or refunding part of an Order.

Refund an entire Order

To refund an entire Order, send a POST to /orders/{order_ref}/refunds/, passing the ref for the relevant Order to be refunded as the path param. In the request body specify the reason for the refund and a metadata object, as in the following example.

Request
curl --request POST \
     --url https://api.sandbox.joinforage.app/api/orders/{order_ref}/refund_all/ \
     --header 'Authorization: Bearer <authentication-token>' \
     --header 'Idempotency-Key: <idempotency-key>' \
     --header 'Merchant-Account: <fns-number>' \
     --header 'accept: application/json' \
     --header 'content-type: application/json'
     --data '
{
  "reason": "Order could not be delivered",
  "metadata": {}
}
'
Response
{
  "ref": "e0f7607d5f",
  "snap_total": "10.00",
  "ebt_cash_total": "9.00",
  "remaining_total": "0.00",
  "product_list": [],
  "status": "draft",
  "delivery_address": {
    "city": "San Francisco",
    "country": "US",
    "line1": "1856 Market St.",
    "line2": "Apt. 3",
    "state": "CA",
    "zipcode": "94105,"
  },
  "is_delivery": false,
  "success_redirect_url": "",
  "cancel_redirect_url": "",
  "supported_benefits": [
    "snap",
    "ebt_cash",
    "non_ebt"
  ],
  "success_date": null,
  "receipt": null,
  "customer_id": null,
  "is_commercial_shipping": null,
  "expires_at": "2023-09-29T18:33:28.315051Z",
  "psp_customer_id": null,
  "external_order_id": null,
  "payments": [],
  "refunds": []
}

📘

Reference documentation

Refund part of an Order

To refund part of an Order, specifically part or all of a single OrderPayment associated with an Order, send a POST to /orders/{order_ref}/refunds/, passing the ref for the relevant Order to be refunded as the path param. In the request body set the amount to be refunded, the ref for the relevant OrderPayment, the reason for the refund, and a metadata object, as in the following example.

Request
curl --request POST \
     --url https://api.sandbox.joinforage.app/api/orders/{order_ref}/refunds/ \
     --header 'Authorization: Bearer <authentication-token>' \
     --header 'Idempotency-Key: 123e4567e8' \
     --header 'Merchant-Account: <fns-number>' \
     --header 'accept: application/json' \
     --header 'content-type: application/json'
     --data '
{
  "amount": 25.99,
  "payment": <payment-ref>,
  "reason": "Order could not be delivered",
  "metadata": {}
}
'
Response
{
  "ref": "1ccabfg754",
  "order": "e0f7607d5f",
  "payment": "f587edf124",
  "merchant": "9000055",
  "funding_type": "ebt_snap",
  "amount": "20.00",
  "merchant_fixed_settlement": 5.95,
  "platform_fixed_settlement": 5.11,
  "reason": "Order could not be delivered.",
  "metadata": {},
  "created": "2023-09-18T11:38:29.279185-07:00",
  "updated": "2023-09-18T11:38:29.279209-07:00",
  "status": "requires_confirmation",
  "last_processing_error": null,
  "receipt": null,
  "tpp_lookup_id": "re_3JemUSGfBYJeLEva0af6My64",
  "refund_errors": []
}

📘

Reference documentation