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
- Create a placeholder
Payment
(server-side) - Collect a customer’s EBT Card PIN to defer payment capture to the server (front-end)
- Update the
Payment
(server-side) - Capture the
Payment
(server-side)
Defer refund completion to the server
- Collect a customer's EBT Card PIN (front-end)
- Complete the
PaymentRefund
(server-side)
Defer payment capture to the server
Step 1: Create a placeholder Payment
(server-side)
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)
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)
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 aForagePINEditText
component.paymentRef
: A unique string identifier for the previously createdPayment
in Forage's database, returned by the Create aPayment
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
Updated 25 days ago