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.
- POST
/api/v1/payment/hosted/create(no card fields). - Get
hosted_payment_urlandexpires_at. - Redirect the customer.
- Customer enters card only. Amount and billing come from your request.
- After pay/3DS, redirect to
return_url. - Confirm via
/api/v1/payment/statusor webhooks.
Choose the endpoint
Live: https://api.zentra.solutions/api/v1/payment/hosted/create
Test: https://api.zentra.solutions/api/v1/test/payment/hosted/createLive and test routes
Live/api/v1/payment/hosted/create · /api/v1/payment/status
Test/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.
Authorize your server
Generate a private key in Developers. Send it from your backend as a bearer token or X-API-Key header.
Authorization: Bearer zentra_xxxxxxxxxxxxxxxxOpen a checkout session
POST /api/v1/payment/hosted/create or test prefix. One-time URL. Expires in 30 minutes.
Request
first_nameCustomer first name. Letters, spaces, apostrophes, dots, or hyphens only.
last_nameCustomer last name. Letters, spaces, apostrophes, dots, or hyphens only.
emailValid customer email address.
amountCharge amount with up to 2 decimal places, e.g. 149.95
currencyISO 4217 currency code, e.g. USD, GBP, EUR
merchant_transaction_idYour unique order/reference ID (max 128 chars, alphanumeric, underscore, hyphen).
return_urlHTTPS (or HTTP) URL where the customer is redirected after payment completes.
line1Billing address line 1 (max 200 characters).
cityBilling city (max 100 characters).
stateBilling state or province (max 100 characters).
postal_codeBilling postal or ZIP code (max 20 characters).
countryISO 3166-1 alpha-2 country code, e.g. US, GB
phoneCustomer phone number, e.g. +441614960180
ip_addressCustomer IPv4 address, e.g. 198.51.100.24
Forbidden
card_numberRejected — collect on the hosted page only.
card_expiry_monthRejected — collect on the hosted page only.
card_expiry_yearRejected — collect on the hosted page only.
card_cvvRejected — collect on the hosted page only.
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"
}'Use the session response
Redirect to hosted_payment_url. Do not build the URL.
statusAlways pending when the hosted payment is created successfully.
hosted_payment_urlFull URL to redirect the customer for card entry.
transaction_idZentra transaction ID for this hosted payment.
merchant_transaction_idEcho of your order reference from the request.
expires_atSession expiry in UTC, e.g. 2026-10-26 05:30:00 UTC
{
"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
{
"status": "declined",
"reason": "Duplicate merchant transaction id.",
"merchant_transaction_id": "ATLAS-CHECKOUT-4096"
}Card fields sent
{
"status": "blocked",
"reason": "Card details must not be sent to hosted payment create. Collect card on the hosted page."
}Validation failed
{
"status": "blocked",
"reason": "The return url field is required.",
"merchant_transaction_id": "ATLAS-CHECKOUT-4096",
"transaction_id": "txn_znt_4Jq8Rc2Ny7Vs"
}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.
Receive the customer back
After pay or 3DS, redirect to return_url:
statussuccess, processing, declined, or blocked
transaction_idZentra transaction ID
merchant_transaction_idYour order reference
reasonHuman-readable outcome message
https://shop.atlas-demo.test/order/complete?status=success&transaction_id=txn_znt_9Hd3Wp6Kt1Bx&merchant_transaction_id=ATLAS-CHECKOUT-4096&reason=Payment%20completedConfirm from your backend via status or webhooks — do not trust the browser redirect alone.
Verify server-side
Use the same status endpoint as the Payments API. Match the live or test prefix used when creating the session.
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"
}'{
"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
}
}
}Identify the payment source
Same transactions / test_transactions tables. type is the source:
direct_apiHost-to-host API (card sent from merchant server)
payment_linkPayment link checkout
hosted_paymentHosted 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.
Session lifecycle
- Sessions expire 30 minutes after creation.
- Single-use after success, processing, or declined.
merchant_transaction_idmust be unique per merchant.- Expired or used sessions error on the hosted page.
Exercise checkout states
Create a test session, then enter these cards on the hosted page:
4242424242424242Success
40000000000032203DS authentication required
4000000000000002Payment failed.
4000000000009995Insufficient funds.
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.

