Zentra Developer Platform

Hosted Checkout

Send the order from your backend, then move the customer to a secure Zentra checkout. You control the return journey while Zentra handles card entry. Need a fully server-to-server flow? Use the Payments API.

REST API JSON TLS 1.2+ Version 1
01

Server-side access

Create sessions securely with a private key that never reaches the browser.
02

Safe test journey

Exercise successful, declined, and 3DS flows before accepting live payments.
03

Thirty-minute session

Redirect using hosted_payment_url; each one-time session expires after 30 minutes.
01

How the handoff works

Hosted Checkout keeps card collection on a Zentra page. Unlike the Payments API, your application never sends card data. Create a session, redirect to hosted_payment_url, and let Zentra complete the secure payment step.

  1. POST /api/v1/payment/hosted/create (no card fields).
  2. Get hosted_payment_url and expires_at.
  3. Redirect the customer.
  4. Customer enters card only. Amount and billing come from your request.
  5. After pay/3DS, redirect to return_url.
  6. Confirm via /api/v1/payment/status or webhooks.
02

Choose the endpoint

Base
Live:  https://api.zentra.solutions/api/v1/payment/hosted/create
Test:  https://api.zentra.solutions/api/v1/test/payment/hosted/create
03

Live and test routes

Live
POSTRequired

/api/v1/payment/hosted/create · /api/v1/payment/status

Test
POSTRequired

/api/v1/test/payment/hosted/create · /api/v1/test/payment/status

The URL prefix selects the mode. Always redirect to the checkout URL returned by Zentra rather than constructing one in your application.

04

Authorize your server

Generate a private key in Developers. Send it from your backend as a bearer token or X-API-Key header.

Header
Authorization: Bearer zentra_xxxxxxxxxxxxxxxx
05

Open a checkout session

POST /api/v1/payment/hosted/create or test prefix. One-time URL. Expires in 30 minutes.

Request

first_name
stringRequired

Customer first name. Letters, spaces, apostrophes, dots, or hyphens only.

last_name
stringRequired

Customer last name. Letters, spaces, apostrophes, dots, or hyphens only.

email
stringRequired

Valid customer email address.

amount
string | numberRequired

Charge amount with up to 2 decimal places, e.g. 149.95

currency
stringRequired

ISO 4217 currency code, e.g. USD, GBP, EUR

merchant_transaction_id
stringRequired

Your unique order/reference ID (max 128 chars, alphanumeric, underscore, hyphen).

return_url
stringRequired

HTTPS (or HTTP) URL where the customer is redirected after payment completes.

line1
stringOptional

Billing address line 1 (max 200 characters).

city
stringOptional

Billing city (max 100 characters).

state
stringOptional

Billing state or province (max 100 characters).

postal_code
stringOptional

Billing postal or ZIP code (max 20 characters).

country
stringOptional

ISO 3166-1 alpha-2 country code, e.g. US, GB

phone
stringOptional

Customer phone number, e.g. +441614960180

ip_address
stringOptional

Customer IPv4 address, e.g. 198.51.100.24

Forbidden

card_number
forbiddenRequired

Rejected — collect on the hosted page only.

card_expiry_month
forbiddenRequired

Rejected — collect on the hosted page only.

card_expiry_year
forbiddenRequired

Rejected — collect on the hosted page only.

card_cvv
forbiddenRequired

Rejected — collect on the hosted page only.

cURL
curl -X POST https://api.zentra.solutions/api/v1/payment/hosted/create \
  -H "Authorization: Bearer zentra_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "first_name": "Luca",
    "last_name": "Moretti",
    "line1": "42 King Street",
    "country": "GB",
    "state": "Greater Manchester",
    "city": "Manchester",
    "postal_code": "M2 4WQ",
    "ip_address": "198.51.100.24",
    "email": "luca.moretti@atlas-demo.test",
    "phone": "+441614960180",
    "amount": "149.95",
    "currency": "GBP",
    "return_url": "https://shop.atlas-demo.test/order/complete",
    "merchant_transaction_id": "ATLAS-CHECKOUT-4096"
  }'
06

Use the session response

Redirect to hosted_payment_url. Do not build the URL.

status
stringRequired

Always pending when the hosted payment is created successfully.

hosted_payment_url
stringRequired

Full URL to redirect the customer for card entry.

transaction_id
stringRequired

Zentra transaction ID for this hosted payment.

merchant_transaction_id
stringRequired

Echo of your order reference from the request.

expires_at
stringRequired

Session expiry in UTC, e.g. 2026-10-26 05:30:00 UTC

JSON
{
  "status": "pending",
  "hosted_payment_url": "https://zentra.solutions/hosted-checkout/txn_znt_4Jq8Rc2Ny7Vs",
  "transaction_id": "txn_znt_4Jq8Rc2Ny7Vs",
  "merchant_transaction_id": "ATLAS-CHECKOUT-4096",
  "expires_at": "2026-10-26 05:30:00 UTC"
}

Declined

JSON
{
  "status": "declined",
  "reason": "Duplicate merchant transaction id.",
  "merchant_transaction_id": "ATLAS-CHECKOUT-4096"
}

Card fields sent

JSON
{
  "status": "blocked",
  "reason": "Card details must not be sent to hosted payment create. Collect card on the hosted page."
}

Validation failed

JSON
{
  "status": "blocked",
  "reason": "The return url field is required.",
  "merchant_transaction_id": "ATLAS-CHECKOUT-4096",
  "transaction_id": "txn_znt_4Jq8Rc2Ny7Vs"
}
07

Hand off to Zentra

Customer lands on Zentra's page at hosted_payment_url.

  • Merchant name and charge amount
  • Customer name and email (from your request)
  • Card number, expiry, and CVV only

Zentra submits the card. You only call create from your server.

08

Receive the customer back

After pay or 3DS, redirect to return_url:

status
stringRequired

success, processing, declined, or blocked

transaction_id
stringRequired

Zentra transaction ID

merchant_transaction_id
stringRequired

Your order reference

reason
stringOptional

Human-readable outcome message

return_url
https://shop.atlas-demo.test/order/complete?status=success&transaction_id=txn_znt_9Hd3Wp6Kt1Bx&merchant_transaction_id=ATLAS-CHECKOUT-4096&reason=Payment%20completed

Confirm from your backend via status or webhooks — do not trust the browser redirect alone.

09

Verify server-side

Use the same status endpoint as the Payments API. Match the live or test prefix used when creating the session.

cURL
curl -X POST https://api.zentra.solutions/api/v1/payment/status \
  -H "Authorization: Bearer zentra_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_transaction_id": "ATLAS-CHECKOUT-4096"
  }'
JSON
{
  "success": true,
  "data": {
    "transaction_id": "txn_znt_9Hd3Wp6Kt1Bx",
    "merchant_transaction_id": "ATLAS-CHECKOUT-4096",
    "status": "success",
    "amount": 149.95,
    "currency": "GBP",
    "message": "Payment completed.",
    "customer_info": {
      "phone": "+441614960180",
      "line1": "42 King Street",
      "postal_code": "M2 4WQ",
      "city": "Manchester",
      "state": "Greater Manchester",
      "name": "Luca Moretti",
      "email": "luca.moretti@atlas-demo.test",
      "country": "GB"
    },
    "created_at": "2026-10-26 05:00:00 UTC",
    "processed_at": "2026-10-26 05:01:12 UTC",
    "payment_info": {
      "card_number": "4242",
      "card_expiry_month": 1,
      "card_expiry_year": 2028
    }
  }
}
10

Identify the payment source

Same transactions / test_transactions tables. type is the source:

direct_api
typeRequired

Host-to-host API (card sent from merchant server)

payment_link
typeRequired

Payment link checkout

hosted_payment
typeRequired

Hosted payment API (card collected on Zentra page)

Create inserts a pending row with type: hosted_payment and no card data. Card entry completes the same record.

11

Session lifecycle

  • Sessions expire 30 minutes after creation.
  • Single-use after success, processing, or declined.
  • merchant_transaction_id must be unique per merchant.
  • Expired or used sessions error on the hosted page.
12

Exercise checkout states

Create a test session, then enter these cards on the hosted page:

4242424242424242
cardRequired

Success

4000000000003220
cardRequired

3DS authentication required

4000000000000002
cardRequired

Payment failed.

4000000000009995
cardRequired

Insufficient funds.

13

Process payment events

Hosted payments emit the same events as the Payments API (payment.completed, payment.declined, payment.blocked). See Payments API guide for setup and payloads.