Handle Mandate Verification
Complete the gateway mandate signing flow after /pay returns awaiting_verification.
curl -X POST "https://api.catalystpay.com/api/v1/payment-sessions/ps_abc123def456/verify/" \
-H "Content-Type: application/json" \
-d '{"verification_id": "vnd_verify_abc123"}'
import requests
result = requests.post(
f"https://api.catalystpay.com/api/v1/payment-sessions/{token}/verify/",
json={"verification_id": verification_id}, # Optional
).json()
if result["status"] == "completed":
print(f"Payment verified. Transaction: {result['transaction']['id']}")
elif result["status"] == "failed":
code = result["error_details"]["code"]
if result["retry_allowed"]:
print(f"Verification failed ({code}) — customer can retry payment")
else:
print(f"Verification failed ({code}) — no retries remaining")
const result = await fetch(
`https://api.catalystpay.com/api/v1/payment-sessions/${token}/verify/`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ verification_id: verificationId }),
}
).then(r => r.json());
if (result.status === 'completed') {
console.log(`Payment verified. Transaction: ${result.transaction.id}`);
} else if (result.status === 'failed') {
const code = result.error_details.code;
if (result.retry_allowed) {
// Redirect back to payment form for retry
showRetryForm(code);
} else {
showTerminalFailure(code);
}
}
/verify/ returns the same enriched envelope as /pay/ — branch on the top-level status.
Response (verified):
{
"object": "payment_session.result",
"session_token": "ps_abc123def456ghi789jkl012mno345",
"status": "completed",
"livemode": true,
"created_at": "2026-04-15T11:01:59.803200Z",
"processed_at": "2026-04-15T11:05:21.441823Z",
"order_number": "ORD-44E833D6",
"verification_url": null,
"error": null,
"retry_allowed": null,
"transaction": {
"id": "be57acb8-2c70-4af0-9ce0-a1e98b615641",
"merchant_transaction_id": "TXN-20260415-BF60E17A",
"gateway_reference": "116567486",
"tenant_reference_id": "merchant-order-42",
"status": "approved",
"type": "sdd_sale"
},
"order": {
"id": "f8a91004-5b21-4712-b40a-1c8da8aa822b",
"order_number": "ORD-44E833D6",
"status": "COMPLETED"
},
"subscription": null,
"lead": { "id": "39efe9b8-29a6-482b-9376-e9501ff3579d" },
"amount": "79.00",
"currency": "EUR",
"payment_method": {
"type": "sepa_debit",
"iban_last4": "3100",
"iban_country": "DE",
"bic": "DEUTDEDBKOE",
"bank_name": "Deutsche Bank",
"holder_name": "Max Mustermann"
},
"sepa": {
"mandate_reference": null,
"sequence_type": "FRST",
"creditor_identifier": null
},
"adapter": { "name": "Vendo Services" },
"payment_gateway": {
"id": "b9e19a9e-4f54-4bec-b100-8995ea904ba4",
"name": "merchant VENDO",
"descriptor": "VendoStore*Merchant EU",
"test_mode": false
},
"error_details": null
}
Response (verification failed):
status is failed and error_details is populated. Branch on error_details.code (stable enum) and retry_allowed to decide whether to show a retry form.
{
"object": "payment_session.result",
"status": "failed",
"error": "Verification failed",
"retry_allowed": true,
"transaction": { "status": "declined", "...": "..." },
"error_details": {
"code": "mandate_rejected",
"message": "SEPA mandate rejected",
"gateway_code": "MD01",
"gateway_message": "Mandate not signed"
}
}
The verification_id field is optional. If omitted, CatalystPay uses the verification reference stored from the original /pay response. Only sessions in AWAITING_VERIFICATION status accept /verify calls. See Adapters for details on which gateways require verification.