---
updatedAt: 2026-09-03T21:54:27.000Z
agentTools:
  projectIndex: https://docs.joinforage.app/llms.txt
---

# Quickstart

Learn about the Forage JS methods and Payments API endpoints that you need to interact with to accept EBT SNAP online.

> 📘 Multi-payment experiences are not supported in online SDK
>
> Use the hosted solution if you are looking to process EBT and other payment methods in a single integration.

Forage recommends splitting your development process into three phases:

1. Add the core EBT payment flow
2. Implement transaction confirmation and refunds
3. Catalog EBT eligible items

This guide includes sample code and lists the relevant reference documents for each phase.

### Prerequisites

* Sign up for a Forage account ([contact us](https://www.joinforage.com/contact) if you don't yet have one)

# Phase 1: Add the core EBT payment flow

## Set up authentication

1. Create a backend API route that generates session tokens that your front-end can access ([Create a session token](https://docs.joinforage.app/reference/create-session-token)).

```javascript
const axios = require('axios')
const express = require('express')
const bodyParser = require('body-parser')

const app = express()
const port = 5000

// middleware to parse the request body
app.use(bodyParser.urlencoded({ extended: true }))

app.post('/api/session_token', async (req, res) => {
  try {
    const apiUrl = 'https://api.sandbox.joinforage.app/api/session_token/'
    // replace with your authentication token
    const authenticationToken = '<authentication-token>'
    // replace with your Forage merchant ID
    const merchantId = '<merchant-id>'

    const headers = {
      Accept: 'application/json',
      Authorization: `Bearer ${authenticationToken}`,
      'Merchant-Account': merchantId, // replace with your Forage merchant ID
      'Content-Type': 'application/x-www-form-urlencoded'
    }

    const response = await axios.post(apiUrl, null, { headers })

    res.json(response.data)
  } catch (error) {
    res
      .status(error.response ? error.response.status : 500)
      .json({ error: 'Something went wrong' })
  }
})

app.listen(port, () => {
  console.log(`Server is running on http://localhost:${port}`)
})
```

```ruby
# app.rb
require 'sinatra'
require 'net/http'
require 'json'

class ApiController < Sinatra::Base
  post '/session_token' do
    begin
      api_url = 'https://api.sandbox.joinforage.app/api/session_token/'
      # replace with your authentication token
      authentication_token = '<authentication-token>'
      # replace with your Forage merchant ID
      merchant_id = 'mid/<merchant-id>'

      uri = URI(api_url)
      http = Net::HTTP.new(uri.host, uri.port)
      http.use_ssl = true

      headers = {
        'Accept' => 'application/json',
        'Authorization' => "Bearer #{authentication_token}",
        'Merchant-Account' => merchant_id,
        'Content-Type' => 'application/x-www-form-urlencoded',
      }

      response = http.post(uri.path, nil, headers)

      if response.is_a?(Net::HTTPSuccess)
        json_response = JSON.parse(response.body)
        content_type :json
        json_response.to_json
      else
        status response.code
        { error: 'Something went wrong' }.to_json
      end
    rescue StandardError => e
      status e.respond_to?(:status) ? e.status : 500
      { error: 'Something went wrong' }.to_json
    end
  end
end
```

For more information on authenticating with Forage, refer to the [authentication guide](https://docs.joinforage.app/docs/authentication).

## Collect and store an EBT Card

1. Create a parent container for the Forage Element, as in the following example:

```html
<div id="card-tokenization-form-container" style="width: 300px; height: 80px;"></div>
```

2. After [initializing Forage JS](https://docs.joinforage.app/docs/forage-js#step-1-initialize), create and add the `ForageCardElement` component to your front-end checkout flow to collect a customer's EBT Card number ([Create a `ForageCardElement`](https://docs.joinforage.app/reference/store-an-ebt-card#create-a-foragecardelement)).

```javascript
const tokenizeCardElement = forage.create('tokenize_card')

tokenizeCardElement.mount('card-tokenization-form-container')
```

3. Call the `tokenizeCard()` submit method from your front-end to submit a customer's EBT Card number to Forage ([Submit a `ForageCardElement`](https://docs.joinforage.app/reference/store-an-ebt-card#submit-a-foragecardelement)).
   1. `tokenizeCard()` creates a Forage [`PaymentMethod` object](https://docs.joinforage.app/reference/payment-methods#paymentmethod-object) that represents the EBT Card number. It also [returns](https://docs.joinforage.app/reference/store-an-ebt-card#returns) a `ref` for the `PaymentMethod`.
   2. Handle potential errors ([Forage JS errors](https://docs.joinforage.app/reference/forage-js-errors)).

```javascript
async function submitCardNumber() {
  try {
    const tokenizationResult = await forage.tokenizeCard(
      tokenizeCardElement
    )
    return tokenizationResult
  } catch (error) {
    const { httpStatusCode, message, code } = error ?? {}
  }
}
```

4. Create a backend API route to associate the `ref` for the `PaymentMethod` with the customer's account in your database. You need this `ref` later to [check the balance of an EBT Card](https://docs.joinforage.app/docs/forage-js-quickstart#check-and-display-the-balance-of-an-ebt-card) or [Authorize a Payment](https://docs.joinforage.app/docs/forage-js-quickstart#authorize-an-ebt-payment).

```javascript
// replace with your database setup
const database = new Map()

// middleware to parse the request body
app.use(bodyParser.json())

app.post('/api/add_forage_payment_method', (req, res) => {
  try {
    const { ref } = req.body

    // replace with your database logic
    database.set(customerId, ref)

    res.json({
      success: true,
      message: 'Forage PaymentMethod added successfully'
    })
  } catch (error) {
    res.status(500).json({ success: false, error: 'Something went wrong' })
  }
})
```

```ruby
# app.rb
require 'sinatra'
require 'net/http'
require 'json'

class ApiController < Sinatra::Base
  post '/add_forage_payment_method' do
    begin
      payment_method_ref = params[:ref]

      # Replace the following logic with your ORM model or database setup
      forage_payment_method = ForagePaymentMethod.new(customer_id: current_user.id, ref: ref)

      if forage_payment_method.save
        content_type :json
        { success: true, message: 'Forage PaymentMethod added successfully' }.to_json
      else
        status 500
        { success: false, error: 'Something went wrong' }.to_json
      end
    rescue StandardError => e
      status 500
      { success: false, error: 'Something went wrong' }.to_json
    end
  end
end
```

5. Send the response from Step 3 to your backend using the endpoint you created in Step 4.

```javascript
async function addForagePaymentMethod() {
  // tokenize the EBT card number and get the result
  // this is the function you wrote in Step 2
  const tokenizationResult = await handleSubmit()

  if (tokenizationResult) {
    const { ref: ref, type, balance, card } = tokenizationResult

    fetch('http://localhost:5000/api/add_forage_payment_method', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ ref, type, balance, card })
    })
      .then((response) => response.json())
      .then((data) => {
        console.log('Response from the backend:', data)
      })
      .catch((error) => {
        console.error('Error occurred during fetch:', error)
      })
  } else {
    console.error(
      'Failed to add Forage PaymentMethod:',
      tokenizationResult.error
    )
  }
}
```

## Check and display the balance of an EBT Card

> ⚠️ FNS Balance Inquiry Rules
>
> FNS prohibits balance checks on sites with guest checkout. If guest checkout isn't offered, adding a balance check is optional.

1. Add the `ForagePinElement` component to your front-end checkout flow to collect a customer's EBT Card PIN to perform a balance inquiry ([Create a `ForagePinElement`](https://docs.joinforage.app/reference/check-the-balance-of-an-ebt-card#create-a-forageebtbalanceelement)).

```javascript
const balanceElement = forage.create('collect_pin')

balanceElement.mount('balance-check-container')
```

2. Call the `checkBalance()` submit method from your front-end to submit a customer's EBT PIN to Forage ([Submit a `ForagePinElement`](https://docs.joinforage.app/reference/check-the-balance-of-an-ebt-card#submit-a-forageebtbalanceelement)).
   1. `checkBalance()` returns the `snap` and `non_snap` (EBT Cash) balance available on the EBT Card, in addition to a timestamp for when the balance was last `updated`.
   2. Handle potential errors ([Forage JS errors](https://docs.joinforage.app/reference/forage-js-errors)).

```javascript
async function handleSubmit() {
  try {
    const balanceResult = await forage.checkBalance(
      balanceElement,
      paymentMethodRef
    )
    const { ebt, updated } = balanceResult
    setSnapBalance(ebt?.snap)
    setCashBalance(ebt?.cash)
  } catch (error) {
    const { httpStatusCode, message, code } = error ?? {}
  }
}
```

3. Display the customer's balance.

```html
<div>
   SNAP Balance: <span id="balance-snap">{snapBalance}</span>
</div>
<div>
   Cash Balance: <span id="balance-cash">{cashBalance}</span>
</div>
```

## Authorize an EBT Payment

1. Prompt the customer to set the SNAP and/or EBT Cash amount to apply to the purchase.
   1. You need to pass these values to the POST `/payments/` request to create the Payment object in the next step.

```jsx
<form onSubmit={handleSubmit} aria-label="SNAP amount form">
  <label htmlFor="snapAmount">Enter SNAP amount to apply:</label>
  <input
    type="number"
    id="snapAmount"
    name="snapAmount"
    aria-required="true"
  />
  <button type="submit">Complete purchase</button>
</form>
```

2. From your backend, call the Forage API to create a `Payment` object ([Create a Payment](https://docs.joinforage.app/reference/create-a-payment)).
   1. Pass the amounts that the customer indicated in Step 1 in the body of the request as the `amount` and `funding_type` params.
   2. The API returns a Forage `Payment` object that represents the EBT transaction and returns a `ref` for the `Payment`. You need to pass the `ref` as one of the parameters to the `capturePayment()` method in Step 4.

> ⚠️ Server-Side Payment Requests
>
> To keep your app secure, requests to create a payment should only be generated server-side.

```javascript
const express = require('express')
const bodyParser = require('body-parser')
const axios = require('axios')

const app = express()
const port = 5000

app.use(bodyParser.json())

app.post('/api/create_payment', async (req, res) => {
  try {
    const { snapAmount } = req.body

    if (isNaN(snapAmount) || snapAmount <= 0) {
      return res.status(400).json({ success: false, error: 'Invalid amount' })
    }

    const paymentPayload = {
      amount: snapAmount,
      funding_type: 'ebt_snap',
      payment_method: '<payment-method-ref>', // replace with the ref for the Payment Method to be charged
      description: 'Test EBT SNAP payment',
      metadata: {},
      is_delivery: true,
      delivery_address: {
        country: 'US',
        city: 'Los Angeles',
        line1: '4121 Santa Monica Blvd',
        line2: 'Unit 513',
        zipcode: '90029',
        state: 'CA'
      }
    }

    const headers = {
      Accept: 'application/json',
      Authorization: `Bearer ${authenticationToken}`,
      'Merchant-Account': '<merchant-id>',
      'Content-Type': 'application/json',
      'IDEMPOTENCY-KEY': Math.random() * 10000 // udpate me in production: ref:idempotency
    }

    const response = await axios.post(
      'https://api.sandbox.joinforage.app/api/payments/',
      paymentPayload,
      { headers }
    )

    const foragePayment = response.data
    console.log('Forage Payment created:', foragePayment)

    res.json({ success: true, payment: foragePayment })
  } catch (error) {
    console.error('Error occurred:', error.message)
    res.status(500).json({ success: false, error: 'Something went wrong' })
  }
})

app.listen(port, () => {
  console.log(`Server is running on http://localhost:${port}`)
})
```

```ruby
# app.rb
require 'sinatra'
require 'net/http'
require 'json'

class PaymentsController < Sinatra::Base
  post '/create_payment' do
    begin
      snap_amount = params[:snapAmount]

      if snap_amount.to_i <= 0
        status :bad_request
        return { success: false, error: 'Invalid amount' }.to_json
      end

      payment_payload = {
        amount: snap_amount,
        funding_type: 'ebt_snap',
        payment_method: '<payment-method-ref>', # replace with the ref for the Payment Method to be charged
        description: 'Test EBT SNAP payment',
        metadata: {},
        is_delivery: true,
        delivery_address: {
          country: 'US',
          city: 'Los Angeles',
          line1: '4121 Santa Monica Blvd',
          line2: 'Unit 513',
          zipcode: '90029',
          state: 'CA'
        }
      }

      api_url = 'https://api.sandbox.joinforage.app/api/payments/'
      authentication_token = '<authentication-token>' # replace with your authentication token

      uri = URI(api_url)
      http = Net::HTTP.new(uri.host, uri.port)
      http.use_ssl = true

      headers = {
        'Accept' => 'application/json',
        'Authorization' => "Bearer #{authentication_token}",
        'Merchant-Account' => 'mid/<merchant-id>', # replace with your Forage merchant ID
        'Content-Type' => 'application/json',
        'IDEMPOTENCY-KEY' => rand(10000).to_s # update me in production: ref:idempotency
      }

     response = http.post(uri.path, payment_payload.to_json, headers)

      if response.is_a?(Net::HTTPSuccess)
        forage_payment = JSON.parse(response.body)
        puts 'Forage Payment created:', forage_payment

        content_type :json
        { success: true, payment: forage_payment }.to_json
      else
        status :internal_server_error
        { success: false, error: 'Something went wrong' }.to_json
      end
    rescue StandardError => e
      status :internal_server_error
      { success: false, error: 'Something went wrong' }.to_json
    end
  end
end
```

3. Add the `ForagePinElement` to your front-end checkout flow ([Create a `ForagePinElement`](https://docs.joinforage.app/reference/capture-ebt-payment#create-a-foragepinelement) ).
   1. This element collects a customer's EBT Card PIN. You need to pass the `ForagePinElement` as one of the parameters to the `capturePayment()` method in the next step.

```javascript
const snapPaymentCaptureElement = forage.create('collect_pin')

snapPaymentCaptureElement.mount('payment-capture-container')
```

4. Pass the `ForagePinElement` and a `ref` to the `Payment` object returned in Step 2 to the `capturePayment()` method ([Submit a `ForagePinElement`](https://docs.joinforage.app/reference/capture-ebt-payment#submit-a-forageebtpaymentelement)).
   1. This method authorizes and captures the EBT payment.

```javascript
async function handleSubmit() {
  try {
    const paymentCaptureResult = await forage.capturePayment(
      snapPaymentCaptureElement,
      paymentRef
    )
    // handle successful paymentCaptureResult
  } catch (error) {
    const { code, httpStatusCode, message } = error ?? {}
  }
}
```

> 📘 Additional Phase 1 Features
>
> During Phase 1, consider adding [Tax handling](https://docs.joinforage.app/docs/about-taxes), split tender support with a [PSP](https://docs.joinforage.app/docs/ebt-online-101#creditdebit-payment-service-provider-psp), and error/refund handling for non-EBT payments.

# Phase 2: Implement transaction confirmation and refunds

1. Add transaction confirmation and receipts to your integration flow.
2. Handle EBT refunds ([Create a refund for a payment](https://docs.joinforage.app/reference/create-payment-refund), [partial refunds guide](https://docs.joinforage.app/docs/ebt-refunds)).

# Phase 3: Catalog EBT eligible items

1. Tag EBT eligible products in your database and reflect eligibility in the front-end UI.
2. Increase discoverability of EBT eligible products: make sure that customers can search and filter for EBT eligible items.

# Next steps

## Add input validation

Listen for the [`change` event](https://docs.joinforage.app/reference/change) on a front-end Forage Element to validate the customer's input and to enable the submit function, as in the below example.

```javascript
balanceElement.on('change', (event) => {
  if (event.error) {
    // show validation error to customer
    setValidationMessage(event.error.message);
  } else {
    // hide validation error
    setValidationMessage('');
  }

  if (event.complete) {
    // enable submit button
    setDisabled(false);
  }
});
```

## Style Forage Elements

The width and height must be set on an Forage Element's parent container in order to adjust the size of the element, as in the following example.

```html
<div id="card-tokenization-form-container" style="width: 300px; height: 80px;"></div>
```

## Explore more resources

* [Forage JS reference documentation](https://docs.joinforage.app/reference/js-sdk-reference)
* [Forage API reference documentation](https://docs.joinforage.app/reference/introduction)

# Sibling pages

* [Introduction](https://docs.joinforage.app/docs/forage-js.md)