In-store integration
Fuel forecourt
You don't know the amount until the nozzle stops. So authorize a ceiling, then capture what was actually pumped.
A forecourt controller can't take a fixed amount up front. The customer authenticates for a maximum, fuels, and you capture what they really dispensed — BaxPay releases the difference.
Pre-auth → dispense → capture
Pre-authorize a ceiling
Open a session with type: "PREAUTH" and the ceiling as amountMinor, say €150. The shopper
authenticates at the pump terminal and BaxPay holds that amount on their funds.
Release the pump
When the session reads AUTHORIZED (or the payment_session.preauthorized webhook arrives), your
controller unlocks the nozzle. The hold reduces the shopper's available balance but has moved no
money.
Capture the actual
When fuelling ends, capture the real amount (≤ the ceiling). BaxPay takes that and releases the rest of the hold in the same step.
/integration/sessionstype: "PREAUTH"/integration/sessions/{id}/fuel/captureIdempotency-Key required/integration/sessions/{id}/fuel/releaseno fuel takencurl -X POST https://sandbox-api.baxpay.com/v1/integration/sessions \
-H "Authorization: Bearer $BAXPAY_CONNECTION" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"saleReferenceId": "site12-pump7-tx88",
"type": "PREAUTH",
"amountMinor": 15000,
"currency": "EUR"
}'{
"success": true,
"data": { "sessionId": "44e977a5-…", "type": "PREAUTH", "status": "CREATED", "amountMinor": 15000, "final": false }
}In sandbox, play the shopper approving at the pump:
curl -X POST https://sandbox-api.baxpay.com/v1/integration/sessions/44e977a5-…/simulate \
-H "Authorization: Bearer $BAXPAY_CONNECTION" \
-H "Content-Type: application/json" \
-d '{ "outcome": "APPROVED" }'{ "success": true, "data": { "status": "AUTHORIZED", "final": false } }The pump stops at €81.40. Capture that:
curl -X POST https://sandbox-api.baxpay.com/v1/integration/sessions/44e977a5-…/fuel/capture \
-H "Authorization: Bearer $BAXPAY_CONNECTION" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ "finalAmountMinor": 8140 }'// Called by your forecourt controller when the nozzle is hung up.
async function captureFuel(sessionId, dispensedMinor, attemptKey) {
const res = await fetch(`https://sandbox-api.baxpay.com/v1/integration/sessions/${sessionId}/fuel/capture`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.BAXPAY_CONNECTION}`,
// Keep the same key if you retry this capture: it can never be booked twice.
'Idempotency-Key': attemptKey,
'Content-Type': 'application/json',
},
body: JSON.stringify({ finalAmountMinor: dispensedMinor }),
});
const body = await res.json();
if (!res.ok) throw new Error(`${res.status} ${body.message}`);
return body.data;
}{
"success": true,
"data": {
"sessionId": "44e977a5-…",
"type": "PREAUTH",
"status": "CAPTURED",
"amountMinor": 15000,
"capturedAmountMinor": 8140,
"resultCode": "APPROVED",
"final": true
}
}amountMinor stays the ceiling you authorized; capturedAmountMinor is what was charged. The
€68.60 difference went back to the shopper with the capture.
No fuel taken
The shopper authorized but drove off, or the nozzle never moved: release the pre-auth. The whole
hold goes back at once, the session becomes VOIDED and payment_session.voided fires. Voiding an
AUTHORIZED pre-auth (/void) does the same.
# sandbox: a second pre-auth, approved at the pump
SESSION=$(curl -s -X POST https://sandbox-api.baxpay.com/v1/integration/sessions \
-H "Authorization: Bearer $BAXPAY_CONNECTION" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "saleReferenceId": "site12-pump7-tx89", "type": "PREAUTH", "amountMinor": 15000 }' | jq -r .data.sessionId)
curl -X POST "https://sandbox-api.baxpay.com/v1/integration/sessions/$SESSION/simulate" \
-H "Authorization: Bearer $BAXPAY_CONNECTION" -H "Content-Type: application/json" \
-d '{ "outcome": "APPROVED" }'curl -X POST https://sandbox-api.baxpay.com/v1/integration/sessions/44e977a5-…/fuel/release \
-H "Authorization: Bearer $BAXPAY_CONNECTION"{
"success": true,
"data": { "status": "VOIDED", "resultCode": "ABORTED", "resultDetail": "Pre-authorization released", "final": true }
}If you never capture or release, BaxPay releases the hold automatically 2 hours after the
pre-auth was approved, with the same payment_session.voided event.
Prefer a release over capturing 0: a zero capture is accepted, but it books a zero sale instead of
simply cancelling.
IFSF and OPI estates
Forecourts speaking IFSF or OPI connect through the matching adapter with an IFSF or OPI
connection. The adapter maps their messages onto the same pre-auth / capture engine:
| Protocol | Endpoint | Maps to |
|---|---|---|
| IFSF | POST /integration/ifsf/reservation | MaxAmount (major units) → pre-auth |
| IFSF | POST /integration/ifsf/completion | FinalAmount against POITransactionID → capture |
| OPI | POST /integration/opi/cardServiceRequest | Card service requests → sessions |
Edge cases
Pump meter disputeThe captured amount is authoritative for settlement. Reconcile pump totals against the settlement batch, not against the pre-auth ceiling.
Capture retriedRetry with the same Idempotency-Key. A capture on a session that is already CAPTURED
answers 409.