HomeGuidesReference
Log In

Defer EBT payment capture and refund completion to the server

Learn how to use the Forage Payments API and SDKs to defer EBT payment capture and refund completion to the server.

🚧

This feature is in active development.

Documentation is subject to change. We appreciate your patience, feedback, and partnership as we continue building.

In some cases, a merchant might not have all of the information that they need to instantly process a payment or refund. Forage provides endpoints and SDK methods to create a Payment or PaymentRefund while holding off on populating the transaction details or capturing the charge. In this guide you’ll learn how to:

Defer payment capture to the server

  1. Create a placeholder Payment (server-side)
  2. Collect a customer’s EBT Card PIN to defer payment capture to the server (front-end)
  3. Update the Payment (server-side)
  4. Capture the Payment (server-side)

Defer refund completion to the server

  1. Collect a customer's EBT Card PIN (front-end)
  2. Complete the PaymentRefund (server-side)

Defer payment capture to the server

Step 1: Create a placeholder Payment (server-side)

Send a POST to /payments/ to create a placeholder Payment. When you create a placeholder Payment, the only required body parameters are funding_type, payment_method, description, and metadata:

curl --request POST \
     --url https://api.sandbox.joinforage.app/api/payments/ \
     --header 'Authorization: Bearer <authentication-token>' \
     --header 'Idempotency-Key: 123e4567e8' \
     --header 'Merchant-Account: 9000055' \
     --header 'accept: application/json' \
     --header 'content-type: application/json' \
     --data '
{
  "funding_type": "ebt_snap",
  "payment_method": "<payment-method-ref>",
  "description": "This is an EBT payment", 
  "metadata": {} 
}
'

Store the ref returned in the response. You’ll need it to update the Payment in Step 3.

Step 2: Collect a customer’s EBT Card PIN (front-end)

📘

SDK reference docs

After initializing the SDK of your choice and creating a Forage Element, call the method that collects a customer’s EBT Card PIN for future payment capture. The following snippets illustrate that function call for each SDK:

try {
  await forage.deferPaymentCapture(
    deferPaymentCaptureElement,
    paymentRef
  )
} catch (error) {
  const { httpStatusCode, message, code } = error ?? {}
}
@HiltViewModel
class DeferPaymentCaptureViewModel @Inject constructor(
    savedStateHandle: SavedStateHandle,
    private val moshi: Moshi
) : ViewModel() {
    private val args = FlowCapturePaymentFragmentArgs.fromSavedStateHandle(savedStateHandle)

    // internal so that DeferPaymentCaptureFragment can access these values
    val snapPaymentRef = args.snapPaymentRef
    val cashPaymentRef = args.cashPaymentRef
    val merchantID = args.merchantID
    val sessionToken = args.sessionToken

    fun deferPaymentCapture(pinForageEditText: ForagePINEditText, paymentRef: String) =
        viewModelScope.launch {
            val response = ForageSDK().deferPaymentCapture(
                DeferPaymentCaptureParams(
                    foragePinEditText = pinForageEditText,
                    paymentRef = paymentRef
                )
            )

            when (response) {
                is ForageApiResponse.Success -> {
                    // handle successful reponse
                }
                is ForageApiResponse.Failure -> {
                    // handle error response
                }
            }
        }
}
ForageSDK.shared.deferPaymentCapture(
    foragePinTextField: foragePinTextField,
    paymentReference: paymentReference
) { result in
    switch result {
    case .success:
       // handle successful PIN collection
    case let .failure(error):
      if let forageError = error as? ForageError? {
        let firstError = forageError?.errors.first
      }
    }
}

How to programmatically collect pins during sandbox testing

To speed up testing and development, you can send a POST to /payments/{payment_ref}/collect_pin_backend/ to programmatically associate a test PIN with an existing Payment object.

Example request:

curl --request POST \
     --url https://api.sandbox.joinforage.app/api/payments/{payment_ref}/collect_pin_backend/ \
     --header 'Authorization: Bearer <authentication-token>' \
     --header 'Idempotency-Key: 123e4567e8' \
     --header 'Merchant-Account: 9000055' \
     --header 'accept: application/json' \
     --header 'content-type: application/json' \
     --data '
{
  "pin": "1234",
}
'

Example response:

{}

🚧

Only use the /collect_pin_backend/ endpoint during sandbox testing. It can't be used in production. Check out the reference documentation for details.

Step 3: Update the Payment (server-side)

🚧

The PATCH endpoint is in active development.

Documentation is subject to change. We appreciate your patience, feedback, and partnership as we continue building together.

After you have all of the required information to process the payment, send a PATCH to /payments/{payment_ref}/ to update the Payment:

curl --request PATCH \
     --url https://api.sandbox.joinforage.app/api/payments/{payment_ref}/ \
     --header 'Authorization: Bearer <authentication-token>' \
     --header 'Idempotency-Key: 123e4567e8' \
     --header 'Merchant-Account: 9000055' \
     --header 'accept: application/json' \
     --header 'content-type: application/json' \
     --data '
{
  "delivery_address": {
    "city": "San Francisco",
    "country": "US",
    "line1": "1856 Market St.",
    "line2": "Unit 3",
    "zipcode": "94106",
    "state": "CA"
  },
  "is_delivery": false,
  "amount": 20.00
}
'

Step 4: Capture the Payment (server-side)

Send a POST to /payments/{payment_ref}/capture_payment/ to capture the Payment:

curl --request POST \
     --url https://api.sandbox.joinforage.app/payments/{payment_ref}/capture_payment/ \
     --header 'Authorization: Bearer <token>' \
     --header 'Idempotency-Key: <idempotency-key>' \
     --header 'Merchant-Account: <merchant-account>' \
     --header 'accept: application/json'

On success, Forage responds with the Payment object and immediately begins processing the charge.

How to handle errors

If the transaction fails to process and Forage returns an error, then retry the same request with the original payment_ref. There is no need to create a new placeholder Payment.

If the error persists, then inspect the message field of the response for error handling suggestions.

Check out the errors reference docs for more details.

Defer refund completion to the server (POS Terminal only)

Step 1: Collect a customer’s EBT Card PIN (front-end)

After initializing the Terminal SDK and creating a Forage Element, pass PosDeferPaymentRefundParams to the deferPaymentRefund() function.

data class PosDeferPaymentRefundParams(
    val foragePinEditText: ForagePINEditText,
    val paymentRef: String
)

suspend fun deferPaymentRefund(
    params: PosDeferPaymentRefundParams
): ForageApiResponse<String>

PosDeferPaymentRefundParams

  • foragePinEditText: A reference to a ForagePINEditText component.
  • paymentRef: A unique string identifier for the previously created Payment in Forage's database, returned by the Create a Payment endpoint when the payment was first created.

Example deferPaymentRefund request:

// PosDeferPaymentRefundViewModel.kt

class PosDeferPaymentRefundViewModel : ViewModel() {
  var paymentRef: String  = ""

  fun deferPaymentRefund(foragePinEditText: ForagePINEditText) = viewModelScope.launch {
    val deferPaymentRefundParams = PosDeferPaymentRefundParams(
      foragePinEditText,
      paymentRef
    )
    val response = forageTerminalSdk.deferPaymentRefund(deferPaymentRefundParams)

    when (response) {
      is ForageApiResponse.Success -> {
        // do something with response.data
      }
      is ForageApiResponse.Failure -> {
        // do something with response.errors
      }
    }
  }

📘

Terminal SDK reference docs

Step 2: Complete the Refund (server-side)

Send a POST to /payments/{payment_ref}/refunds/ to complete the refund. Pass the amount, reason, metadata, and provider_terminal_id in the body of the 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": "<amount>",
  "reason": "<reason>",
  "metadata": "<metadata>",
  "pos_terminal": {
    "provider_terminal_id": "<provider_terminal_id>"
  }
}
'

📘

Payments API reference docs