CleonPay

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.

Host-to-Host puts the card number through your systems. Only integrate this way if your own PCI DSS scope covers handling the PAN. If it does not, use the hosted payment page instead: the card never reaches you, and you stay out of scope.

The shape of it

  1. Create a payment with a card method.
  2. Authorize it with the card and the shopper's browser data, server-to-server.
  3. 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.
  4. Send the shopper to the redirect URL. We run the whole 3-D Secure dance and bring them back to your success_url or error_url.
  5. The webhook is the authoritative verdict.

1. Create the payment

POST /v1/payments

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

POST /v1/payments/{id}/authorize

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"
  }
}
Browser data is required. 3-D Secure 2 needs the shopper's real browser characteristics, gathered on your checkout page in the browser (not your server's). Without them the card cannot be authenticated and the authorization is refused.

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.

EventMeaning
payment.settledPaid. Fulfil the order.
payment.failedNot paid. The shopper can try again.

You can also read the state at any time; do not poll it in a tight loop.

GET /v1/payments/{id}

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.