HomeGuidesReference
Log In
Guides

iOS HSA/FSA Quickstart

Use the Forage iOS SDK to accept HSA/FSA payments in your iOS app.

Looking for the Forage mobile app?

Get instant EBT balance checks and access to exclusive grocery deals—download Forage on iOS or Android and start saving today.


Get up and running with Forage’s iOS SDK to accept HSA/FSA and EBT payments in your iOS app. This guide walks you through the installation, setup, styling, and execution of key payment operations.

In this guide, you’ll learn how to:

  1. Set up a Forage iOS app
  2. Add and style a Forage Payment Sheet
  3. Perform payment operations
    1. Tokenize and store a payment method
    2. Authorize and Capture a payment

Prerequisites

Before we get started, make sure you have the following ready to go:

  • A Forage account
    (Contact us if you don’t have one yet.)

  • Installed tools:

    • Xcode v14.1 or newer
    • iOS SDK v13 or higher
    • Swift v5.5 or higher
  • Required third-party libraries:

  • (Optional) CocoaPods
    Use CocoaPods to install the SDK and manage dependencies.

For more information, see:

Step 1: Set up a Forage iOS app

1.1 Add an endpoint to your server

A POST to the Forage /session_token/ endpoint creates a session token.

⚠️

Environment Variable Best Practices

These examples assume you’re using a .env file. Update them as needed, but never expose sensitive data, such as tokens, in client-side code.

// app.js 

const axios = require('axios')
const express = require('express')
const bodyParser = require('body-parser')
require('dotenv').config();

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 the below with your authentication token
    const authenticationToken = process.env.AUTHENTICATION_TOKEN
    // Replace the below with your Forage merchant ID
    const merchantId = process.env.MERCHANT_ID

    const headers = {
      Accept: 'application/json',
      Authorization: `Bearer ${authenticationToken}`,
      'Merchant-Account': merchantId,
      '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}`)
})
# app.rb

require 'sinatra'
require 'net/http'
require 'json'
require 'dotenv/load'

class ApiController < Sinatra::Base
  post '/session_token' do
    begin
      api_url = 'https://api.sandbox.joinforage.app/api/session_token/'
      # Replace the below with your authentication token
      authentication_token = ENV['AUTHENTICATION_TOKEN']
      # Replace the below with your Forage merchant ID
      merchant_id = ENV['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

1.2 Install the iOS SDK.

You can either use CocoaPods or XCode’s built-in Swift Package Manager (SPM) to install forage-ios-sdk.

CocoaPods

If you haven’t already, install the latest version of CocoaPods. Run the following command if you don’t have an existing Podfile:

pod init

Add the below line to your Podfile:

pod 'ForageSDK', '~> 4.4.4'

Run the installation command:

pod install

To update to the latest version of the SDK, run:

pod repo update
pod update ForageSDK

Xcode

Navigate to Add Packages...

Add the https://github.com/teamforage/forage-ios-sdk dependency. Set the Dependency Rule to Branch and type main for the branch name. Finally, click Add Package.

On the next screen, select the ForageSDK package and click Add Package again.

For more detailed instructions, consult the official Apple SPM guide.

1.3 Import the SDK

Add the following import to the top of every file that uses the library.

import ForageSDK

// your code here

1.4 Initialize the SDK

Set up a request from your front-end to your backend that asks the server to generate a session token.

In your app’s primary ViewController.swift, call ForageSDK.setup to initialize the SDK. Pass a ForageSDK.Config object that specifies the merchantID and the server-generated sessionToken.

// ViewController.swift

ForageSDK.setup(
  ForageSDK.Config(
    merchantID: "d5685d56ed",
    sessionToken: "sandbox_eyJ0eXAiOiJKV1Qi..."
  )
)

Step 2: Add and style a Forage Payment Sheet

A Forage Payment Sheet is a secure, client-side entity that accepts and submits customer input for an HSA/FSA transaction.

The iOS SDK includes a ForagePaymentSheet Element for collecting all of the required HSA/FSA card data, pictured below.

The ForagePaymentSheet element

The ForagePaymentSheet element

2.1 Initialize the ForagePaymentSheet

To add a Payment Sheet, call the method to initialize the Element in a View.swift for a specific payment operation. You can set styling properties after calling the method.

The below example initializes a ForagePaymentSheet Element via a call to ForagePaymentSheet() in a HSAFSAView.swift and specifies some styling properties:

// HSAFSAView.swift

private let foragePaymentSheet: ForagePaymentSheet = {
  let ps = ForagePaymentSheet()

  // set paymentType on the sheet
  ps.paymentType = .HSAFSA

  // set default styles on payment sheet that will cascade to all fields
  ps.borderWidth = 2.5
  ps.borderColor = .black
  ps.cornerRadius = 4

  // Set TextField specific settings and accessibility settings
  ps.cardHolderNameTextField.placeholder = "Card holder name"
  ps.cardHolderNameTextField.accessibilityIdentifier = "tf_paymentsheet_cardholderName"
  ps.cardHolderNameTextField.isAccessibilityElement = true

  return ps
}()

For more information, see How to Style Forage iOS Elements.

2.2 Set up text input validation

Forage validates a Payment Sheet’s input as a customer types. To notify customers of input validation errors, you'll need to conform to the ForagePaymentSheetElementDelegate protocol and listen for the Payment Sheet’s PaymentSheetObservableState.

The sheetFocusDidChange method is triggered when a text field in the Payment Sheet gains or loses focus.

foragePaymentSheet.delegate = self
// signature
public protocol ForagePaymentSheetElementDelegate {
    func sheetTextFieldDidChange(_ state: PaymentSheetObservableState)
    func sheetFocusDidChange(_ state: PaymentSheetObservableState)
}

// usage
extension HSAFSAView: ForagePaymentSheetElementDelegate {
    func sheetFocusDidChange(_ state: any PaymentSheetObservableState) {
	if state.currentFirstResponder != nil {} // Payment Sheet gained focus
	else {} // Payment Sheet lost focus (blurred)
	
// view the currently focused field
      	print(state.currentFirstResponder?.name ?? "No focus")
    }
    
    func sheetTextFieldDidChange(_ state: any PaymentSheetObservableState) {
	// you can view all payment sheet completion errors
       print(state.completionErrors)

	// you can highlight fields with errors
        for var field in foragePaymentSheet.fields {
            if !field.isValid && !field.isFirstResponder && field.isDirty {
                field.borderColor = .red
            } else {
                field.borderColor = .black
            }
        }
    }
}

The PaymentSheetObservableState protocol defines properties reflecting the state of a Payment Sheet.

public protocol PaymentSheetObservableState {
    /// isComplete is true when all inputs on the sheet are valid and the sheet ready to be submitted.
    var isComplete: Bool { get }
    var completionErrors: [String: any Error] { get }
    var currentFirstResponder: (any ForagePaymentSheetField)? { get }
}

In the View.swift for the payment operation, indicate the delegate in the primary render(), as in the following example for the foragePaymentSheet in the HSAFSAView.swift :

// HSAFSAView.swift

public func render() {
  foragePaymentSheet.delegate = self
  setupView()
  setupConstraints()
}

Then, specify the ForagePaymentSheetElementDelegate as an extension in the file. Set the state to PaymentSheetObservableState. Define functions to handle state changes in the extension as well.

The below example sets up an extension for the ForagePaymentSheet in the HSAFSAView.swift :

// HSAFSAView.swift

extension HSAFSAView: ForagePaymentSheetElementDelegate {
  func sheetFocusDidChange(_ state: PaymentSheetObservableState) {
    updateState(state: state)
  }

  func sheetTextFieldDidChange(_ state: PaymentSheetObservableState) {
    updateState(state: state)
  }
}

2.3 Establish a ForagePaymentSheet TextField as the First Responder

Finally, in the Controller.swift for the payment operation, call becomeFirstResponder() to set focus on the Forage Element TextField.

The following example calls the method as part of the lifecycle methods in a HSAFSAViewController.swift :

class HSAFSAViewController: UIViewController {
  override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    // You can change which subfield becomes first responder
    customView.foragePaymentSheet.cardHolderNameTextField.becomeFirstResponder()
  }
}

Step 3: Perform payment operations

3.1 Tokenize and store a payment method

📘 How To Tokenize With a Payment Sheet

To tokenize an HSA/FSA Card number, use the ForagePaymentSheet Element.

Building on the example HSAFSAView.swift, call the ForageSDK.shared.tokenizeCreditDebitCard method.

// HSAFSAView.swift

ForageSDK.shared.tokenizeCreditDebitCard(
  foragePaymentSheet: foragePaymentSheet,
  // NOTE: The following line is for testing purposes only and should not be used in production.
  // Please replace this line with a hashed customer ID value.
  customerID: UUID.init().uuidString,
  reusable: true
) { result in
  // Handle result and error here
  switch result {
  case .success(let paymentMethod):
    // Handle the PaymentMethod here (paymentMethodIdentifier, card, type, etc.)
  case .failure(let error):
    if let forageError = error as? ForageError {
      // handle forageError.code, forageError.httpStatusCode and forageError.message
    }
  }
}

3.2 Authorize and Capture a payment

Follow the Authorize and Capture an HSA/FSA payment guide from this point to complete the payment.