Card acquiring (Host-to-Host)
Capture the card on your own page and hand it to us server-to-server. We acquire it, run 3-D Secure, and settle by webhook.
Most integrations let CleonPay's payment page take the card (see accepting payments) and never touch a card number. Host-to-Host is for a platform that already has its own checkout and its own card fields, such as another aggregator: you collect the card, send it to us over a server-to-server call, and we do the acquiring, the 3-D Secure and the settlement. The merchant-facing shape does not change, and the response never names the bank behind the payment.
The shape of it
- Create a payment with a card method.
- Authorize it with the card and the shopper's browser data, server-to-server.
- We answer with one of three outcomes: a redirect to finish 3-D Secure, an outright decline, or (rarely, when no authentication is needed) a completed payment.
- Send the shopper to the redirect URL. We run the whole 3-D Secure
dance and bring them back to your
success_urlorerror_url. - The webhook is the authoritative verdict.
1. Create the payment
Create it with a card method (visa or mastercard),
the amount in minor units, and your success_url and
error_url. You get back a payment id in
awaiting_customer.
{
"method": "mastercard",
"amount": 4200,
"currency": "GBP",
"success_url": "https://your-platform.example/return/ok",
"error_url": "https://your-platform.example/return/fail",
"reference": "order-10294"
}
2. Authorize the card
Send the card and the shopper's browser data over a server-to-server
call. The card number and CVC live only in this request and the acquiring
call behind it: never stored, never logged. Send an
Idempotency-Key so a retried
submit re-uses the same authorization rather than opening a second one.
{
"card": {
"number": "5555550000000002",
"expiry_month": "09",
"expiry_year": "2029",
"cvc": "123",
"holder_name": "A CARDHOLDER"
},
"customer": { "email": "shopper@example.com", "first_name": "Lex", "last_name": "Clarke" },
"billing": { "address": "1 High St", "city": "London", "postcode": "EC1A 1AA", "country": "GB" },
"browser": {
"accept_header": "text/html,application/xhtml+xml,...",
"user_agent": "Mozilla/5.0 ...",
"language": "en-GB",
"java_enabled": false,
"javascript_enabled": true,
"color_depth": 24,
"screen_width": 1440,
"screen_height": 900,
"time_zone_offset": -60,
"ip": "203.0.113.9"
}
}
3. Handle the outcome
Authorize answers one of three ways.
Requires action, the usual case
The card needs 3-D Secure. Send the shopper's browser to
next_action.url. We render the authentication (the invisible
device check and, if the bank asks for it, the one-time code), keep the
shopper on a CleonPay page throughout, and return them to your
success_url or error_url when it is done.
{
"id": "3743881d-0fce-416c-b3de-8bc3f5d414b1",
"status": "requires_action",
"next_action": { "type": "redirect", "url": "https://pay.cleonpay.com/pay/3743.../challenge" },
"livemode": true
}
Declined
A 402 the shopper can retry with another card. The message is
safe to show; it never names the acquirer.
{
"error": { "type": "card_error", "code": "card_declined", "message": "The card was not accepted. Try another card." }
}
Completed with no authentication
When the issuer needs no authentication at all, authorize returns the payment object directly. Even so, treat the webhook as the final word.
4. The webhook is authoritative
3-D Secure finishes asynchronously: the acquirer confirms the verdict to us on its own channel, so the redirect back to your URL is a UX signal, not proof of payment. Wait for the webhook before you fulfil the order.
| Event | Meaning |
|---|---|
payment.settled | Paid. Fulfil the order. |
payment.failed | Not paid. The shopper can try again. |
You can also read the state at any time; do not poll it in a tight loop.
Money back
Return a settled payment with a refund
(POST /v1/refunds, full or partial), or void one before it
settles with a reversal
(POST /v1/payments/{id}/reverse, full amount, usually free).
Testing
In test mode, Mastercard 5555550000000002 authenticates
frictionlessly and 5555550000000010 forces a challenge; the
sandbox one-time code is 12345. See
testing for the full matrix.