Skip to main content

Payins v3 Direct API Integration

CyberPay v3 creates a pay-in with one request to POST /v3/payment. Use it to accept card payments and the alternative payment methods enabled for your brand. The response tells you whether the payment has completed, the customer must visit the cashier, or your integration must display payment instructions.

You do not need to create a session first. CyberPay creates the transaction and session during submission. Store the returned identifiers and use webhooks or transaction queries to follow the payment.

Environments and authentication

EnvironmentBase URL
Testhttps://api.test.cyberpay.link
Productionhttps://api.cyberpay.link

Make requests from your server using your brand's X-API-KEY. For JSON requests, send Content-Type: application/json.

Source-IP restrictions are optional and can be enabled per brand. When enabled, requests must come from an authorized IP address. The customer's IP in customer.ip is separate from your server's source IP.

Card pay-ins also support the Signature and Idempotency-Key headers described below. These card-specific features do not apply automatically to other payment methods.

1. Select a payment option

Call GET /v3/payment-options to list the methods enabled for your brand. This discovery call is optional when you already have the appropriate option ID, and it does not create a session or reserve an amount.

curl 'https://api.test.cyberpay.link/v3/payment-options?country=DE&currency=EUR' \
-H 'X-API-KEY: YOUR_API_KEY'
Query parameterMeaning
countryOptional two-letter country code, such as DE.
currencyOptional ISO 4217 currency code, such as EUR.
excludeCountriesOptional boolean. Set to true to return an empty availableCountries array for each option.

The response is an array. For example, a brand with cards enabled may return:

[
{
"id": "4864ab16-2fa4-464a-b692-a74cab45aaf8",
"name": "Card",
"logo": "",
"type": "CARD",
"suboptions": [],
"availableCountries": [
{
"code": "DE",
"currencies": ["EUR"]
}
]
}
]

Use the returned option id as paymentOptionId. If an option contains banks, wallets, or other suboptions, use the chosen suboptions[].id as paymentOptionId to select it directly. Supplying a parent option can lead to a cashier selection step.

An empty response means no options match. WORLDWIDE and ANY are fallback coverage values when no specific country or currency list is available. Discovery does not return amount limits or a universal payment-data form; final eligibility depends on the amount, method, provider, and brand configuration.

2. Build the payment request

Submit the request to POST /v3/payment.

FieldRequirement and purpose
paymentOptionIdRequired UUID. Use a payment option or suboption ID available to your brand.
currencyRequired ISO 4217 currency code supported by the method.
amountRequired JSON number, at least zero. Fiat amounts allow at most two decimal places; the method's minimum and maximum amounts also apply.
paymentDataRequired for direct card payments. For other methods, contains the method-specific fields needed to process the payment.
countryRecommended two-letter country code. Omitting it can trigger country selection in the cashier.
customerRecommended customer information. Required details vary by method and brand; existing customer profile information may fill missing fields.
merchantReferenceRecommended unique reference for this payment, up to 255 characters. Generated when omitted.
userIdRecommended stable ID for your customer, up to 255 characters. Generated when omitted.
redirectUrlYour customer return URL after the cashier flow, up to 500 characters. A browser return does not confirm payment completion.
notificationUrlHTTPS webhook URL, up to 500 characters. Overrides the brand's configured notification URL for this transaction.
paymentReferenceOptional payment or invoice reference, up to 255 characters. Generated when omitted.
languageOptional two-letter cashier language code, such as EN.
merchantDomainRequired for payment-facilitator brands. Must exactly match a domain configured for the brand.
extra1, extra2, extra3Optional merchant metadata, each up to 255 characters.
shippingAddressOptional shipping address using the same fields as customer.address.
orderDetailsOptional array of items with productName, numeric quantity, dimensions, and description.

For non-card methods, the three fields paymentOptionId, currency, and amount satisfy the basic request contract. Processing can still require additional data. For cards, send a non-empty paymentData object with valid card details or an existing card token.

Customer information

FieldFormat
customer.firstName, customer.lastNameStrings, 1–255 characters. Send the customer's actual name.
customer.emailValid email address, 4–255 characters.
customer.phoneString, 3–255 characters. Include the international dialing code where applicable.
customer.ipCustomer's IPv4 or IPv6 address.
customer.userDeviceMOBILE, DESKTOP, or TABLET.
customer.userAgentCustomer browser's user-agent string, up to 512 characters.
customer.addressstreet, streetNumber, zipCode, city, and state as strings; country as a two-letter code.
customer.documentIdentity-document number and type, when required by the selected method. Both are strings, up to 255 characters.

Card example

This example uses a test card. The sandbox accepts any test card PAN; see sandbox test data for example cards and the amount ranges that select each outcome. Use it only in the test environment with the corresponding card method enabled. The example amount of 110 triggers 3DS in the sandbox. Change the amount to select a different outcome using the test-data table.

{
"paymentOptionId": "4864ab16-2fa4-464a-b692-a74cab45aaf8",
"country": "DE",
"currency": "EUR",
"amount": 110,
"merchantReference": "order-2026-0001",
"paymentReference": "Invoice 0001",
"userId": "customer-123",
"redirectUrl": "https://merchant.example/payments/return",
"notificationUrl": "https://merchant.example/webhooks/payins",
"language": "EN",
"paymentData": {
"cardNumber": "4387751111111111",
"expiryYear": "2029",
"expiryMonth": "01",
"cardCvv": "111",
"cardHolder": "Duck Vader"
},
"customer": {
"firstName": "Duck",
"lastName": "Vader",
"email": "[email protected]",
"phone": "+4915123456789",
"ip": "192.0.2.10",
"userDevice": "DESKTOP",
"address": {
"street": "Example Street",
"streetNumber": "10",
"zipCode": "10115",
"city": "Berlin",
"country": "DE"
}
}
}

Send card numbers, expiry values, and CVVs as strings. Use a valid, unexpired month and year; a four-digit year is recommended. The CVV must have the length required by the card type.

If your integration already has a saved card token, send paymentData.token and paymentData.cardCvv instead of the new-card fields. A saved token can still require customer authentication or other cashier steps.

Save the example request as payin.json and submit it from your server:

curl 'https://api.test.cyberpay.link/v3/payment' \
-H 'X-API-KEY: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data-binary '@payin.json'

Include the Signature header if signature enforcement is enabled for your brand. You can also supply an Idempotency-Key for card payments, subject to the retry behavior explained below.

Other payment methods

Choose a bank, wallet, or other enabled method using its option or suboption ID. Send its required fields in paymentData; these fields are specific to the method and provider. Card fields are not a generic APM payload.

For example, the following request selects a bank-transfer method and lets the cashier collect any missing information. Replace the illustrative ID with one returned for your brand:

{
"paymentOptionId": "dfb7a9c4-1e87-4803-9929-091d946999d3",
"country": "DE",
"currency": "EUR",
"amount": 25,
"merchantReference": "order-2026-0002",
"userId": "customer-123",
"redirectUrl": "https://merchant.example/payments/return",
"notificationUrl": "https://merchant.example/webhooks/payins"
}

3. Handle the result

Payment submission returns HTTP 201 when it returns a business result. The numeric statusCode in the body describes that result; it is separate from the HTTP status.

Store result.transactionId, result.sessionId, and result.merchantReference. Use resultType to choose the next action and result.status to determine the transaction's state.

Body statusCoderesultTypeHandling
200successCheck result.status. COMPLETED confirms completion; PROCESSING still requires a final status update.
300redirectRedirect the customer to the exact result.redirectUrl.
900declinedThe payment was declined. Inspect result.errorCode and result.errorReason.
999errorProcessing failed. Inspect the error and reconcile the transaction before retrying.
80formPresent the method's returned formData using the agreed integration for that method.
81qrcodeDisplay base64QRCode or encode qrCodeString as a QR code for the customer.
83receiptShow the payment details and collect the confirmation or receipt required by the method.
84infoDisplay the returned payment instructions and await the payment.
86barcodeDisplay barcodeData and its expirationDate, when supplied.
90fawryDisplay referenceNumber so the customer can complete the payment.
91mobileDisplay sendTo and collect the confirmation identified by confirmBy, if the method requires it.

Supported outcomes depend on the payment method. A QR-code or provider-request flow can also be handled by the cashier and returned as redirect. Follow the returned resultType instead of assuming a particular response from the method name. Treat any unrecognized result type as unresolved and check the transaction status.

Redirect example

{
"statusCode": 300,
"resultType": "redirect",
"result": {
"transactionId": "00e34a0d-4f3d-474a-9ea8-e05f1414e57b",
"sessionId": "dfa27e08-e6e4-4d87-8ca3-216445f11864",
"merchantReference": "order-2026-0001",
"currency": "EUR",
"amount": 110,
"status": "PROCESSING",
"redirectUrl": "https://cashier.example/3ds/00e34a0d-4f3d-474a-9ea8-e05f1414e57b"
}
}

The URL above is illustrative. Use the exact URL returned by CyberPay, including its path and query parameters.

Completed example

{
"statusCode": 200,
"resultType": "success",
"result": {
"transactionId": "00e34a0d-4f3d-474a-9ea8-e05f1414e57b",
"sessionId": "dfa27e08-e6e4-4d87-8ca3-216445f11864",
"merchantReference": "order-2026-0001",
"currency": "EUR",
"amount": 110,
"status": "COMPLETED"
}
}

Declined example

{
"statusCode": 900,
"resultType": "declined",
"result": {
"transactionId": "00e34a0d-4f3d-474a-9ea8-e05f1414e57b",
"sessionId": "dfa27e08-e6e4-4d87-8ca3-216445f11864",
"merchantReference": "order-2026-0001",
"currency": "EUR",
"amount": 110,
"status": "DECLINED",
"errorCode": "1010",
"errorReason": "Payment option is not allowed."
}
}

Validation and authentication failures return HTTP errors such as 400, 401, 403, or 404, with statusCode and a message string or array. For example, malformed card details can fail with HTTP 400 before a transaction is created. These error bodies do not use the resultType envelope.

4. Complete customer actions

Cashier redirects and 3D Secure

Redirects can request a country, bank selection, missing customer data, a verification step, or card authentication. Complete card details do not guarantee an immediate final result.

For card payments, open the returned URL as a top-level browser navigation. The cashier may host a provider or 3D Secure frame internally, so nesting it in your own iframe can interfere with authentication.

CyberPay continues the same transaction through the cashier. Do not create a second payment to supply missing data or restart authentication. After the customer returns to your redirectUrl, retrieve or await the final status before fulfilling the order.

APM instructions and confirmation

For info and receipt responses, display bankName and the returned paymentDetails. An info response may also include paymentLocations, htmlInstructions, or a redirectUrl. Use the returned amount, currency, reference, and expiry information to guide the customer.

Some supported methods require POST /confirm-payment after the customer has paid. Use this endpoint only when the method calls for a receipt or additional confirmation; it is not a general approval step for pay-ins.

Confirmation fieldPurpose
sessionIdRequired UUID from the payment response.
essentialKeyMethod-specific confirmation value, such as the sender identifier requested by the method. String, up to 255 characters.
additionalEssentialKeyAdditional reference where required. For the supported UPI confirmation flow without a receipt, the UTR/RRN must be 12 digits.
documentReceipt image or PDF, when required. Send as multipart form data with a file smaller than 2 MB.

For a method requiring a sender identifier, a JSON confirmation can look like:

{
"sessionId": "dfa27e08-e6e4-4d87-8ca3-216445f11864",
"essentialKey": "+201001234565"
}

For a receipt upload, send the file as multipart form data:

curl 'https://api.test.cyberpay.link/confirm-payment' \
-H 'X-API-KEY: YOUR_API_KEY' \
-F 'sessionId=dfa27e08-e6e4-4d87-8ca3-216445f11864' \
-F '[email protected]'

If body-signature enforcement is enabled, also send X-Signature for the JSON-serialized confirmation fields. For multipart requests, the signature covers the parsed text fields, excluding the uploaded file bytes. This is the body-signature format, separate from the card v3 signature below.

A confirmation response such as { "message": "Your payment has been sent." } acknowledges submission of the confirmation. Await the final transaction status; it does not prove the funds were received.

5. Verify the final status

Use payment webhooks to receive status changes. Match notifications by transaction ID and merchant reference, validate the webhook signature, and process duplicate notifications without fulfilling the order more than once.

Use GET /payments/{transactionId} to query the current status when needed. COMPLETED confirms a successful pay-in. INIT, PENDING, and PROCESSING are not proof of completion; DECLINED and ERROR are unsuccessful outcomes.

Keep the browser return flow separate from fulfillment. A customer can close the browser before returning, and an asynchronous payment can complete after the browser flow ends.

Card signatures

For card POST /v3/payment requests, send the Signature header when signature enforcement is enabled for the brand. Compute a Base64-encoded HMAC-SHA256 using your signature key and this exact string:

paymentOptionId:merchantReference:currency:amount

Use an empty segment when merchantReference is omitted. Use the numeric amount's string representation, without currency formatting or added trailing zeros. For the card example above, the input is:

4864ab16-2fa4-464a-b692-a74cab45aaf8:order-2026-0001:EUR:110

Card v3 business responses use the X-Signature header and a different input built from the nested result:

transactionId:merchantReference:currency:amount:status

For the completed example:

00e34a0d-4f3d-474a-9ea8-e05f1414e57b:order-2026-0001:EUR:110:COMPLETED

Webhook signatures cover the JSON-serialized webhook payload, as described in Signatures. They do not use the card response's colon-separated string.

References and retries

Use a unique merchantReference for each intended payment. A duplicate reference for the same merchant and country is rejected; the reference is not a mechanism for replaying a previous response.

For card pay-ins, Idempotency-Key supports replay of a stored response:

  • Use a unique key per intended payment, up to 64 characters. A UUID is suitable.
  • Reuse that key and the same request data when retrying that payment. Never reuse it for a different amount, customer, or order.
  • Replay is scoped to the merchant and brand. Stored response records expire after 48 hours.
  • Replay is available after the response has been stored. An early cashier redirect or an in-flight request may not have a stored response yet; do not submit concurrent attempts or assume every timeout can be retried safely.

Other payment methods do not implement this card-specific replay contract. After a timeout or a lost response, reconcile through webhooks, the transaction query if you have its ID, or the merchant portal using your reference before submitting again.

Migrating from v2

Previous flowv3 flow
Create a session, then retrieve payment options and submit against that session.Optionally discover methods with GET /v3/payment-options, then create the payment with POST /v3/payment.
Retrieve suboptions in a separate step.Read suboptions from the options response and submit the chosen ID as paymentOptionId.
Pass a session ID when creating the direct payment.Receive transactionId and sessionId in the v3 response.
Legacy card field names such as cardnum and cardcvv.Use paymentData.cardNumber, cardCvv, cardHolder, expiryMonth, and expiryYear.
Legacy string result codes such as "000".Numeric body codes such as 200, lowercase resultType, and a nested result object.
Confirm a supported method using its session.POST /confirm-payment remains available when the method requires it, with its own confirmation response format.

Test the integration

In the test environment, cover an immediate completion, a cashier or 3D Secure redirect, a missing-data redirect, a decline, and an HTTP validation error. For your enabled APMs, also cover the returned instructions and any required confirmation.

Verify that an asynchronous webhook can complete an order even when the browser never returns, that duplicate notifications do not duplicate fulfillment, and that a lost submission response is reconciled before another payment is attempted.