Simple payment
On this page
Accept a one-off payment: create a checkout, send the customer to the Payout payment page and mark the order as paid when the webhook confirms it.
Steps
-
Get an API key
Generate an API key (client ID and secret) in the backoffice: Sandbox or Production. You need a Payout account first.
Set a notify URL before you generate the key. Payout sends webhooks (POST requests) about checkouts and other events to this URL.
-
Get a Bearer token
Exchange the client ID and secret for a token:
Command Linecurl --location --request POST 'https://sandbox.payout.one/api/v1/authorize' \ --header 'Content-Type: application/json' \ --header 'Accept: application/json' \ --data-raw '{ "client_id": "DC995618-7ED8-4070-9DA0-48B6F86551C3", "client_secret": "q3dpHpYtDrH-KmGD4HMn5OTEx6IsZPBokQ8CqMONWqMSEePWy9bXd3Ua3KvO7f6C" }'Response:
JSON{ "token": "SFMyNTY.EXAMPLE-TOKEN.dGhpcy1pcy1hLWZha2Utc2lnbmF0dXJlLWV4YW1wbGU", "valid_for": 6000 }Send the token in the
Authorization: Bearerheader of every other request. It is valid forvalid_forseconds; then request a new one. -
Create the checkout
Send the details of your order. Give every checkout its own
Idempotency-Key: a request with a key that was already used returns the existing checkout instead of creating a new one.Command Linecurl --location --request POST 'https://sandbox.payout.one/api/v1/checkouts' \ --header 'Content-Type: application/json' \ --header 'Accept: application/json' \ --header 'Authorization: Bearer SFMyNTY.EXAMPLE-TOKEN.dGhpcy1pcy1hLWZha2Utc2lnbmF0dXJlLWV4YW1wbGU' \ --header 'Idempotency-Key: d2c9f095-8e4c-498a-802a-68c220dbe1bd' \ --data-raw '{ "amount": 300, "currency": "EUR", "customer": { "first_name": "John", "last_name": "Doe", "email": "[email protected]", "phone": "+421900000000" }, "billing_address": { "name": "John Doe", "address_line_1": "Billing Address Line 1", "address_line_2": "Billing Address Line 2", "city": "Billington", "postal_code": "BL92883", "country_code": "GB" }, "shipping_address": { "name": "John Doe", "address_line_1": "Shipping Address Line 1", "address_line_2": "Shipping Address Line 2", "city": "Shippington", "postal_code": "W153KF", "country_code": "GB" }, "products": [ { "name": "Product 1", "unit_price": 100, "quantity": 3 } ], "external_id": "order-1001", "nonce": "bzAwd2VzTU5UMEtWZ3IzMA", "metadata": { "source": "eshop" }, "redirect_url": "https://eshop.example.com/payment/redirect", "signature": "7aff78519a0d038ba4960a53a1bc82f0ff35ce7b84d9cbe367360295652f07fb" }'amountis in cents: 300 is 3.00 EUR. The same applies to other currencies, so 30000 is 300 CZK. Onlyamount,currency,customer,external_id,nonce,redirect_urlandsignatureare required; Create checkout lists all fields.To sign the request, join the values with
|, hash the string with SHA-256 and send the hash assignature:TEXTPattern: amount|currency|external_id|nonce|client_secret Input: 300|EUR|order-1001|bzAwd2VzTU5UMEtWZ3IzMA|q3dpHpYtDrH-KmGD4HMn5OTEx6IsZPBokQ8CqMONWqMSEePWy9bXd3Ua3KvO7f6C SHA-256: 7aff78519a0d038ba4960a53a1bc82f0ff35ce7b84d9cbe367360295652f07fbNote
Every signature in the API is the SHA-256 hash in lowercase hex. Some libraries return uppercase hex; convert it to lowercase before you send or compare it.
-
Verify the response
The response contains the new checkout with its own
nonceandsignature. Compute the signature from the response values and compare it withsignatureto make sure the response comes from Payout.Response:
JSON{ "object": "checkout", "id": 141447, "external_id": "order-1001", "amount": 300, "currency": "EUR", "redirect_url": "https://eshop.example.com/payment/redirect", "idempotency_key": "d2c9f095-8e4c-498a-802a-68c220dbe1bd", "customer": { "first_name": "John", "last_name": "Doe", "name": "John Doe", "email": "[email protected]", "phone": "+421900000000", "note": null }, "checkout_url": "https://sandbox.payout.one/checkouts/U0ZNeU5UWS5FWEFNUExFLUNIRUNLT1VULVRPS0VOLm5vdC1hLXJlYWwtc2lnbmF0dXJl/?account_id=RVhBTVBMRS1BQ0NPVU5ULVRPS0VOLTAwMDAwMDAwMDA", "metadata": { "source": "eshop" }, "status": "processing", "nonce": "V2cwZ0U0YU9tNlVoOUlrbA", "signature": "0cce8d83001e51f45278a01a731566cf4805a9470d35cf1d58a1f1bf4fd98c0a", "payment": null, "all_payments": [], "billing_address": { "name": "John Doe", "address_line_1": "Billing Address Line 1", "address_line_2": "Billing Address Line 2", "city": "Billington", "postal_code": "BL92883", "country_code": "GB" }, "shipping_address": { "name": "John Doe", "address_line_1": "Shipping Address Line 1", "address_line_2": "Shipping Address Line 2", "city": "Shippington", "postal_code": "W153KF", "country_code": "GB" }, "products": [ { "name": "Product 1", "quantity": 3, "unit_price": 100 } ], "payment_token": "U0ZNeU5UWS5FWEFNUExFLUNIRUNLT1VULVRPS0VOLm5vdC1hLXJlYWwtc2lnbmF0dXJl", "is_status_final": false }Signature of this response:
TEXTPattern: amount|currency|external_id|nonce|client_secret Input: 300|EUR|order-1001|V2cwZ0U0YU9tNlVoOUlrbA|q3dpHpYtDrH-KmGD4HMn5OTEx6IsZPBokQ8CqMONWqMSEePWy9bXd3Ua3KvO7f6C SHA-256: 0cce8d83001e51f45278a01a731566cf4805a9470d35cf1d58a1f1bf4fd98c0a -
Redirect the customer to the payment page
Send the customer to
checkout_urlfrom the response. -
Handle the customer's return
When the payment succeeds or fails, Payout sends the customer back to the
redirect_urlof the checkout. The redirect doesn't carry the result of the payment; the webhook does. -
Receive the webhook
Payout sends the result as a POST request with a JSON body to your notify URL. Respond with a 2xx status code; otherwise Payout sends the webhook again.
checkout.succeededpayload:JSON{ "external_id": "order-1001", "object": "webhook", "type": "checkout.succeeded", "data": { "object": "checkout", "id": 141447, "external_id": "order-1001", "amount": 300, "currency": "EUR", "redirect_url": "https://eshop.example.com/payment/redirect", "customer": { "first_name": "John", "last_name": "Doe", "email": "[email protected]", "card_number_masked": "" }, "payment": { "object": "payment", "status": "successful", "payment_method": "card", "failure_reason": "", "created_at": 1759744800, "fee": 8, "net": 292 }, "metadata": { "source": "eshop" }, "status": "succeeded", "is_status_final": true }, "nonce": "eE1FNXBhS3ZxVklRZG5JZA", "signature": "3f2c8df4770fd18311f3360d96aacace00842b464a0317a45768a622531dc79d" }Note
is_status_finaltells whether the checkout has reached a final state and will not change any more. Forsucceededcheckouts it is alwaystrue. Forexpiredcheckouts it becomestrueonly 6 days after the expiry, so that late bank transfers can still arrive and be matched. -
Verify the webhook signature
Compute the signature from the top-level
external_id,typeandnonceof the webhook and compare it withsignature. Process the webhook only if they match.TEXTPattern: external_id|type|nonce|client_secret Input: order-1001|checkout.succeeded|eE1FNXBhS3ZxVklRZG5JZA|q3dpHpYtDrH-KmGD4HMn5OTEx6IsZPBokQ8CqMONWqMSEePWy9bXd3Ua3KvO7f6C SHA-256: 3f2c8df4770fd18311f3360d96aacace00842b464a0317a45768a622531dc79d -
Mark the order as paid
Mark the order as paid once you have received and verified
checkout.succeeded. Itsis_status_final: trueconfirms that the checkout will not change any more.You can also listen for the
checkout.finalizedwebhook, which is sent whenis_status_finalchanges totrue. It is useful forexpiredcheckouts, which become final 6 days after they expire.checkout.finalizedis available on request; contact support to enable it for your account.
Test cards
Use these cards in the sandbox.
| Card number | Expiry | CVV | 3-D Secure | Result |
|---|---|---|---|---|
4245757666349685 |
12/ |
123 |
Challenge required | Authorized |
5150030090350186 |
12/ |
123 |
3DS Method required, then frictionless success | Authorized |
4012001037141120 |
12/ |
123 |
3DS Method and challenge required | Authorized |
5100052384536834 |
12/ |
123 |
Challenge parameters if an SDK object is sent in OrderCreateRequest |
Authorized |
5100052384536818 |
02/ |
123 |
Challenge required; without 3-D Secure, soft decline (SSD) | Authorized |
5100052384536826 |
12/ |
123 |
Frictionless, positive authentication | Authorized |
5521455186577727 |
12/ |
123 |
Frictionless, negative authentication | Not authorized (authentication fails) |
Next steps
- Refund a paid checkout.
- Send the customer straight to one payment method: Checkout payment methods.
- Let the customer pay by bank transfer from your own page: Payment instructions.
- Save the customer's card for later payments: Store card.
- Need help? Contact support.
- Questions? Contact sales.
- Service status? status.payout.one.
- LLM? Read llms.txt.