Cards & 3-D Secure

One method id — card — accepts every major card scheme, credit and debit, across your presentment currencies. This page covers the two 3-D Secure paths (frictionless and challenge), the AVS/CVV results on the risk object, and how network tokens keep stored cards current.

One method, every scheme

You pass "payment_method": "card" and Finscale routes the transaction across every card-capable provider in your set — you never integrate per scheme or per acquirer. The scheme comes back on the payment, in payment_method_details.card:

FieldTypeMeaning
brandstringCard scheme, e.g. visa, mastercard.
last4stringLast four digits of the card number.
exp_month / exp_yearintegerExpiry (1–12 / four-digit year).
fundingenumcredit, debit, prepaid, or unknown.
countrystringTwo-letter issuing country.

Two things are card-only in the API. Cards are the sole method that supports capture_method: "manual" — authorize now, capture within 7 days, raise the hold with incremental authorization — covered in Authorization flows. And every card-family payment is risk-screened before routing, so the risk object is always populated — see the risk engine.

3-D Secure

3-D Secure (3DS) is the card networks' authentication layer: before authorization, the issuer verifies that the person paying controls the card. Finscale runs 3DS automatically whenever the issuer or local regulation requires it — strong customer authentication in the EEA and UK, for instance — or when authenticating measurably improves approval odds. The issuer then picks one of two paths:

Customer Your server Finscale Issuer (ACS) pays with card POST /v1/payments 3-D Secure authentication A — frictionless (issuer authenticates silently) authenticated — no customer step processing → succeeded B — challenge (issuer requires a customer step) requires_action + next_action.url redirect to next_action.url completes challenge — OTP or bank app authenticated webhook: payment.succeeded returns to return_url
  • Frictionless. The issuer authenticates silently from device and transaction data. No customer step: the payment moves through processing to succeeded in the same call chain, next_action stays null, and you never see requires_action. Most 3DS authentications finish this way.
  • Challenge. The issuer wants proof. The API returns status: "requires_action" with next_action.type: "redirect" — send the customer to next_action.url. Finscale hosts the challenge round-trip (one-time code, banking-app approval) and returns the customer to your return_url. Treat the webhook, not the redirect, as the outcome.
Liability shift When a payment is successfully 3DS-authenticated, liability for fraud-reason chargebacks generally shifts from you to the issuer. Authenticated card payments therefore rarely produce fraudulent disputes you have to defend — see Disputes.

Creating a card payment

Same call as every other method. Always pass return_url on card payments — you can't know in advance whether the issuer will demand a challenge:

POST /v1/payments — card
curl https://api.finscale.dev/v1/payments \
  -H "Authorization: Bearer sk_test_51FinscaleDemo…" \
  -H "Idempotency-Key: idem_ord_9f21_0716" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 4900,
    "currency": "EUR",
    "payment_method": "card",
    "reference": "ord_9f21_0716",
    "customer": { "email": "anna@example.com" },
    "return_url": "https://shop.example.com/checkout/return"
  }'

If the issuer wants a challenge, the response parks the payment in requires_action:

201 Created — issuer wants a challenge
{
  "id": "pay_8Q2mX4nT1cVb",
  "object": "payment",
  "amount": 4900,
  "currency": "EUR",
  "status": "requires_action",
  "payment_method": "card",
  "reference": "ord_9f21_0716",
  "provider": "prov_eu_acq_01",
  "next_action": {
    "type": "redirect",
    "url": "https://pay.finscale.dev/r/8Q2mX4nT"
  },
  "created_at": "2026-07-16T09:24:31Z",
  "livemode": false
}

After the customer completes the challenge, the payment authorizes and (with the default capture_method: "automatic") captures. Fetch it — or wait for payment.succeeded — and the instrument snapshot and risk result are filled in:

GET /v1/payments/pay_8Q2mX4nT1cVb — after the challenge
{
  "id": "pay_8Q2mX4nT1cVb",
  "object": "payment",
  "amount": 4900,
  "currency": "EUR",
  "status": "succeeded",
  "payment_method": "card",
  "payment_method_details": {
    "card": {
      "brand": "visa",
      "last4": "4242",
      "exp_month": 12,
      "exp_year": 2028,
      "funding": "credit",
      "country": "NL"
    }
  },
  "risk": {
    "score": 17,
    "decision": "approved",
    "checks": { "avs_result": "pass", "cvv_result": "pass" }
  },
  "provider": "prov_eu_acq_01",
  "next_action": null,
  "created_at": "2026-07-16T09:24:31Z",
  "livemode": false
}

AVS & CVV results

During authorization the issuer verifies the billing address (AVS) and the card verification code (CVV). Both results land on risk.checks and feed the payment's risk score:

Checkpassfailunavailable
avs_resultBilling address matched the issuer's record.Address did not match — raises the risk score; rarely a hard decline on its own.The issuer or method doesn't support AVS (common outside the US and UK).
cvv_resultCode matched.Code did not match — issuers usually decline these (incorrect_cvc).Code not checked — e.g. a network-token or wallet charge that carries a cryptogram instead.

A fail on either check never silently downgrades a payment: either the issuer declines (you get a card error) or the payment proceeds and the result simply raises risk.score, which can tip the decision to review or blocked.

Network tokens & vaulting

Card numbers never touch your servers. Hosted checkout — or Finscale-hosted fields in an API integration — collects the PAN inside Finscale's environment, built to PCI DSS Level 1 standards, and hands you a token. Saving a card produces a reusable pm_… instrument you charge with payment_method_id; see the overview for the save-and-charge pattern and Security & PCI for what this does to your compliance scope.

For vaulted cards, Finscale provisions a network token: a scheme-issued token bound to the card and to your merchant, used in place of the PAN on every subsequent charge. You don't opt in per payment — it happens automatically where the scheme supports it. What it buys you:

  • Cards stay current. When the plastic is reissued, expires, or is replaced after loss, the scheme updates the token. Subscriptions keep charging without asking the customer to re-enter anything.
  • Higher approval rates. Issuers treat network-token traffic as lower risk — token charges carry a per-transaction cryptogram.
  • Less credential exposure. Providers see the token, never the PAN. payment_method_details.card still reports the real brand and last4, so receipts and support flows don't change.

Refunds & chargebacks

Refunds are one call — POST /v1/refunds with the payment id, full or partial — and Finscale routes the reversal through whichever provider processed the original. The refund is pending until the scheme processes it; the customer's statement typically updates within a few business days (issuer timing, not yours). Details in Refunds.

Chargebacks are the card-specific dispute rail: the cardholder disputes the charge with their bank and the money is pulled back pending an outcome. Each one surfaces as a Dispute object (dp_…) with a normalized reasonfraudulent, product_not_received, duplicate, credit_not_processed, general — and a dispute.created webhook. Respond with evidence or accept before evidence_due_by; miss the deadline and the dispute is lost. The full state machine, evidence API, and escalation stages are in Disputes.

Settlement timing

Card money moves in two beats. Authorization is instant — the issuer places a hold at approval. Clearing starts at capture (immediately with capture_method: "automatic"; whenever you call capture with manual) and takes the scheme a day or two. Cleared funds then join your unified payout on the standard T+2 schedule, with interchange and scheme fees netted at settlement — the concepts, and the reports that itemize them, are in Settlement & payouts.

Error scenarios

ScenarioWhat the API does
Issuer declines402 with type: "card_error" and a normalized code — card_declined, insufficient_funds, expired_card, incorrect_cvc, … The payment is failed with the same code in failure_code. Final declines never trigger failover — the issuer's no is a no.
Issuer demands authentication mid-flowFinscale re-runs the payment through 3-D Secure automatically. If authentication can't be completed, the payment fails with authentication_required.
Challenge failedThe customer failed or refused the challenge: payment.failed fires with failure_code: "authentication_required".
Challenge abandonedThe payment sits in requires_action until the session expires, then fails. Cancel it earlier with POST /v1/payments/{id}/cancel if the customer restarts checkout.
Blocked by riskNo provider is attempted; the payment is failed with failure_code: "payment_blocked" — see Risk engine.
Provider timeout / outageNot your problem: automatic failover retries the next provider inside the same API call — see Smart routing.

Test cards

In test mode (sk_test_… keys), specific card numbers and amounts script every path above:

InputResult
4242 4242 4242 4242Succeeds, frictionless.
4000 0000 0000 3220Returns requires_action with a simulated 3-D Secure challenge page — approve or fail it there.
4000 0000 0000 0002Declined: card_declined.
4000 0000 0000 9995Declined: insufficient_funds.
"amount": 4999First provider fails retriably → demonstrates failover.
"amount": 4991Risk holds the payment for review.
"amount": 4992Risk blocks the payment (payment_blocked).

The full matrix, including webhook rehearsal, is in Testing.