Card 3DS
International card payments with a 3-D-Secure challenge redirect
Card 3DS is a generic, network-agnostic card flow gated by a 3-D-Secure challenge. The payer is redirected to an issuer-hosted page to authenticate; once approved, 505pay receives a payment.completed webhook and the intent flips to paid.
| Field | Value |
|---|---|
allowed_channels value | card_3ds |
| TTL | 24 h |
| Refund support | Programmatic |
| Settlement | T+1 |
| Channel-typed response fields | challenge_url, checkout_url |
Create intent
Pass allowed_channels: ["card_3ds"] (or channel: "card_3ds" for POS-terminal intents). The response includes a challenge_url field that you must redirect the payer to.
curl -X POST https://sandbox.505pay.link/v1/payments/intents \
-H "Authorization: Bearer $PAYOPS_SECRET_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $YOUR_KEY" \
-d '{
"merchant_id": "'"$PAYOPS_MERCHANT_ID"'",
"amount_idr": 250000,
"max_uses": 1,
"allowed_channels": ["card_3ds"]
}'The response body includes:
{
"id": "01HZ...",
"shortcode": "AbCdEf123456",
"status": "open",
"checkout_url": "https://sandbox.505pay.link/pay/AbCdEf123456",
"challenge_url": "https://sandbox.505pay.link/pay/AbCdEf123456/3ds"
}Redirect the payer to challenge_url. After the issuer-hosted 3DS page collects authentication, the simulator (or, in production, the acquirer) posts back to POST /v1/payments/intents/{id}/3ds-callback?auth_code=<value> to advance the intent.
Sandbox 3DS callback
Drive the intent to paid or failed directly without touching a real card form:
# Approve
curl -X POST "https://sandbox.505pay.link/v1/payments/intents/$INTENT_ID/3ds-callback?auth_code=4111111111111111"
# Decline
curl -X POST "https://sandbox.505pay.link/v1/payments/intents/$INTENT_ID/3ds-callback?auth_code=4000000000000002"Response shape:
{
"intent_id": "01HZ...",
"result": "approved",
"message": "3-D-Secure challenge passed; intent advancing to paid"
}Test card (auth_code) | Outcome |
|---|---|
4111111111111111 | Auto-approve — intent flips to paid, payment.completed webhook fires |
4000000000000002 | Auto-decline — intent flips to failed, payment.failed webhook fires |
| anything else | 400 invalid_auth_code — use one of the two canonical PANs |
Webhook events
payment_intent.created— fired on POST intent.payment.completed— fired when the issuer approves the 3DS challenge.payment.failed— fired when the issuer declines or the payer abandons the challenge.payment_intent.expired— fired if the payer never completes the challenge within the 24h TTL.
See Payment lifecycle.
Test cards in production
The 4111111111111111 / 4000000000000002 test PANs work in the sandbox only. The production flow posts back to the same endpoint with an acquirer-generated auth_code instead. Never embed the test PANs in production code paths.
Was this page helpful?