CartDna Api Documentation
Supported Payment Methods
CartDna supports a variety of payment methods to accommodate customers worldwide. Each payment method has a unique code that you'll use when initiating payment requests.
Available Payment Methods
| Payment Method | Method Code | Supported Currencies | Region |
| iDEAL | cartdna_pix | BRL | Brazil |
| Boleto | cartdna_boleto | BRL | Brazil |
| Bancontact | cartdna_bancontact | EUR | Belgium |
| Klarna | cartdna_klarna | EUR, USD, GBP, SEK, NOK, DKK | Europe, US |
| Credit/Debit Card | cartdna_card | USD, EUR, GBP, LKR, AUD, CAD, JPY | Global |
| BLIK | cartdna_blik | PLN | Germany, Austria |
| Przelewy24 | cartdna_p24 | PLN | Poland |
Currency Codes
CartDna supports the following currency codes in accordance with ISO 4217 standards:
| Currency | Code |
| US Dollar | USD |
| Euro | EUR |
| British Pound | GBP |
| Sri Lankan Rupee | LKR |
| Brazilian Real | BRL |
| Australian Dollar | AUD |
| Canadian Dollar | CAD |
| Japanese Yen | JPY |
| Swedish Krona | SEK |
| Norwegian Krone | NOK |
| Danish Krone | DKK |
| Polish Złoty | PLN |
Specifying Payment Method
To use a specific payment method, include the payment_method parameter in your payment request with the corresponding method code:
{
"merchant_id": "MERCHANT_12345",
"order_id": "ORDER_67890",
"amount": "250.00",
"currency": "BRL",
"payment_method": "cartdna_pix",
"signature": "A1B2C3D4E5F6...",
"webhook_url": "https://yourdomain.com/webhook",
"response_url": "https://yourdomain.com/payment-success",
"cancel_url": "https://yourdomain.com/payment-cancel"
}
| Environment | URL |
|---|---|
| Live URL | https://api.fiatdna.com/v1 |
| Sandbox URL | https://sandbox-api.fiatdna.com/v1 |
STEP 01 : Authentication
All API endpoints require authentication using a Bearer token. Before accessing any other API endpoints, you must first obtain an access token through the authentication process.
Getting Your Credentials
To authenticate, you'll need your API credentials from CartDna:
- Public_key(Client ID) - Your unique client identifier
- private_key (Client Secret) - Your secret key (keep this secure and never share publicly)
- Merchant ID - Merchant identifier
- Signature Key - generate and verify signature using this key
You can obtain these credentials from your CartDna (Contact us - info@cartdna.com).
Authentication Request
Use HTTP Basic Authentication to obtain an access token. Encode your client_id and client_secret in the format client_id:client_secret and base64 encode the string.
curl -X POST https://api.fiatdna.com/v1/auth/token \
-H "Authorization: Basic BASE64_ENCODED_CREDENTIALS" \
-H "Content-Type: application/json" \
-d '{
"grant_type": "client_credentials"
}'Example in PHP
<?php
$client_id = 'YOUR_CLIENT_ID';
$client_secret = 'YOUR_CLIENT_SECRET';
// Create base64 encoded credentials
$credentials = base64_encode("$client_id:$client_secret");
$url = "https://api.fiatdna.com/v1/auth/token";
$data = json_encode([
"grant_type" => "client_credentials"
]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Basic $credentials",
"Content-Type: application/json"
],
CURLOPT_POSTFIELDS => $data
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
echo "cURL Error: " . curl_error($ch);
curl_close($ch);
exit;
}
curl_close($ch);
if ($httpCode === 200) {
$result = json_decode($response, true);
echo "Access Token: " . $result['access_token'];
} else {
echo "Authentication failed. HTTP Code: $httpCode\n";
echo "Response: $response";
}
Authentication Response
A successful authentication returns an access token:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "payment"
}Using the Access Token
Include the access token as a Bearer token in the Authorization header for all subsequent API requests:
curl -X GET https://api.fiatdna.com/v1/some-endpoint \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json"Important: Access tokens expire after the time specified in expires_in (in seconds). You'll need to request a new token when the current one expires.
Token Expiration
When your token expires, you'll receive a 401 Unauthorized response. Simply request a new token using the same authentication process.
STEP 02 : Create Payment Request
The Payment Request API allows you to initiate payment transactions. All payment requests require a secure signature generated using SHA256 hashing algorithm.
🔐 Required Security Headers
Every payment request must include these headers:
| Header | Required | Description | Example |
|---|---|---|---|
| Authorization | Yes | OAuth Bearer token | Bearer YOUR_TOKEN |
| Content-Type | Yes | JSON format | application/json |
| X-Timestamp | Yes | UTC Unix timestamp (seconds) | 1707300000 |
| X-Idempotency-Key | Yes | Unique key per request (UUID recommended) | 550e8400-e29b-41d4-a716-446655440000 |
| X-Signature | Yes | SHA-256 signature | B7C8D9E0F1A2… |
Timestamp rules
- Must be UTC Unix timestamp (seconds)
- Must be integer
- Must be within ± 5 minutes of CartDNA server time
Payment Request Parameters
Include the following parameters in your payment request:
Primary Details
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
| order_id | String | Yes | Unique order reference from merchant system | ORD-100234 |
| amount | Decimal | Yes | Total payment amount | 2500.00 |
| currency | String | Yes | ISO currency code | EUR |
| transaction_type | String | Yes | Support types - Payment / preauth | |
| payment_method | String | Yes | Payment method code | cartdna_boleto |
| customer.name | String | Yes | Customer full name | John Micheal |
| customer.email | String | Yes | Customer email address | info@example.com |
| customer.phone | String | No | Customer mobile number | 1234567890 |
| return_url | URL | Yes | Redirect after successful payment | https://clientsite.com/success |
| cancel_url | URL | Yes | Redirect if user cancels | https://clientsite.com/cancel |
| callback_url | URL | Yes | Webhook URL for notifications | https://clientsite.com/webhook |
| metadata | Object | No | Additional merchant data | { "cart_id":"CART-5566" } |
| Signature | String | Yes | Generated Signature | B7C8D9E0F1A2…. |
Billing Address Details
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
| billing_address.address_line | String | Yes (if provided) | Billing street line 1 | 123 Main Street |
| billing_address.city | String | Yes (if provided) | Billing city | London |
| billing_address.state | String | No | Billing state/province | Western |
| billing_address.postal_code | String | No | Billing postal code | 00500 |
| billing_address.country | String | Yes (if provided) | ISO country code | GB |
Shipping Address Details
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
| shipping_address.address_line | String | Yes (if provided) | Delivery street line 1 | 123 Main Street |
| shipping_address.city | String | Yes (if provided) | Delivery city | London |
| shipping_address.state | String | No | Delivery state/province | Western |
| shipping_address.postal_code | String | No | Delivery postal code | 00500 |
| shipping_address.country | String | Yes (if provided) | ISO country code | GB |
Example Payment Request
curl -X POST https://api.fiatdna.com/v1/payments \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Timestamp: 1707300000" \
-H "X-Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
-H "X-Signature: B7C8D9E0F1A2...." \
-d '{
"order_id": "ORD-100234",
"amount": 2500.00,
"currency": "LKR",
"payment_method": "cartdna_boleto",
"transaction_type": "payment",
"customer": {
"name": "John Robert",
"email": "info@example.com",
"phone": "1234567890"
},
"return_url": "https://yourdomain.com/payment-success",
"cancel_url": "https://yourdomain.com/payment-cancel",
"callback_url": "https://yourdomain.com/webhook",
"metadata": {
"cart_id": "CART-5566"
},
"billing_address": {
"address_line": "123 Main Street",
"city": "London",
"state": "Western",
"postal_code": "00500",
"country": "GB"
},
"shipping_address": {
"address_line": "123 Main Street",
"city": "London",
"state": "Western",
"postal_code": "00500",
"country": "GB"
}
}'
Response
Successful payment initiation returns:
{
"status": "success",
"payment_id": "PAY_ABC123XYZ",
"order_id": "ORD-100234",
"payment_url": "https://payment.fiatdna.com/checkout/PAY_ABC123XYZ",
"expires_at": "2026-02-07T05:43:00Z"
}
Common Errors you may see
| Error | Cause |
|---|---|
| EXPIRED_REQUEST | Timestamp too old/new |
| INVALID_SIGNATURE | Wrong signature rules |
| DUPLICATE_REQUEST | Same Idempotency-Key used twice |
| UNAUTHORIZED | Invalid token |
Creating a Payment Signature
To ensure secure and consistent payment processing, you must generate a signature using SHA-256 hashing. The signature prevents tampering and guarantees data integrity of your request.
Fields used in the signature
You will use the following fields:
merchant_id— Your unique merchant identifier
order_id— Unique order reference number
amount— Payment amount in decimal format (e.g.,100.00)
currency— ISO currency code (e.g.,USD,EUR,LKR)
timestamp— UTC Unix timestamp (seconds) sent inX-Timestampheader
signature_key— Your private signature key (must never be sent in the request)
Step 01 : Mandatory rule — sort parameters A → Z
Before creating the signature, you must sort the parameters alphabetically (a–z) by parameter name.
Correct sorted order
After sorting alphabetically by name, your order MUST be:
amount
currency
merchant_id
order_id
timestamp
❗ If parameters are not sorted correctly, the signature will be rejected.
Step 02 : Convert amount to integer (MANDATORY)
Multiply amount by 100 and remove decimals.
| Your amount | Value you MUST use |
|---|---|
| 100.00 | 10000 |
| 2500.50 | 250050 |
| 1.75 | 175 |
👉 Use this value for signature generation.
Step 03 : MD5 the Signature Key
Do NOT sort signature_key.
Instead:
- Generate MD5 hash
- Append it at the end
Example:
md5("signature_key_999") =5d41402abc4b2a76b9719d911017c592
Step 04 : Build the final string
Concatenate in this exact format:
amount_integer + currency + merchant_id + order_id + timestamp + md5(signature_key)Example result:
10000usdmerchant_abcorder_12317073000005d41402abc4b2a76b9719d911017c592
Apply SHA-256
Hash the string using SHA-256 and convert to UPPERCASE hex:
SHA256(10000usdmerchant_abcorder_12317073000005d41402abc4b2a76b9719d911017c592)
→ A1B2C3D4E5F6...This is your final payment signature
Signature Generation Process
Concatenate the above fields and hash them using SHA256:
<?php
$merchant_id = "MERCHANT_ABC";
$order_id = "ORDER_123";
$amount_decimal = "100.00";
$currency = "USD";
$signature_key = "Signature_KEY_999";
// STEP 1: Convert amount to integer
$amount_integer = (int) round($amount_decimal * 100);
// STEP 2: Lowercase values
$merchant_id = strtolower($merchant_id);
$order_id = strtolower($order_id);
$currency = strtolower($currency);
// STEP 3: MD5 the secret (must be last)
$md5_signature_key = md5(strtolower($signature_key));
// STEP 4: Concatenate in sorted order
$signatureString =
$amount_integer .
$currency .
$merchant_id .
$order_id .
$md5_signature_key;
// STEP 5: SHA-256 + uppercase
$signature = strtoupper(hash("sha256", $signatureString));
echo "Signature: " . $signature;
Common mistakes to avoid
| Mistake | Result |
|---|---|
| Forgetting to include timestamp | Invalid signature |
| Using local time instead of UTC | Invalid signature |
| Not multiplying amount by 100 | Invalid signature |
| Not sorting parameters A → Z | Invalid signature |
| Forgetting MD5 of merchant_secret | Invalid signature |
| Sending uppercase values | Invalid signature |
Using 100 instead of 10000 | Invalid signature |
Webhook Notifications
CartDna will send payment notifications to your webhook URL whenever a payment status changes. The webhook payload includes all transaction details and a signature that you must verify to ensure the authenticity of the notification.
Webhook Payload Structure
The webhook will send a POST request to your webhook_url with the following payload:
{
"merchant_id": "MERCHANT_12345",
"order_id": "ORDER_67890",
"payment_id": "PAY_ABC123XYZ",
"amount": "250.00",
"currency": "USD",
"status": "success",
"transaction_id": "TXN_987654321",
"payment_method": "cartdna_card",
"timestamp": "2026-02-07T05:45:00Z",
"metadata": {
"cart_id": "CART-5566"
},
"acquirer": {
"acquirer_reference": "PH-55667788",
"acquirer_status": "APPROVED",
"acquirer_status_code": "00",
"acquirer_reason_code": "000",
"acquirer_reason_message": "Transaction Approved",
},
"signature": "B7C8D9E0F1A2..."
}
| Field | Type | Description | Example |
|---|---|---|---|
| acquirer.acquirer_reference | String | Gateway/processor reference number | PH-55667788 |
| acquirer.acquirer_status | String | Final status from the gateway | APPROVED / DECLINED |
| acquirer.acquirer_status_code | String | Numeric code from gateway | 00 |
| acquirer.acquirer_reason_code | String | Gateway-specific reason code | 000 |
| acquirer.acquirer_reason_message | String | Human-readable gateway message | Transaction Approved |
Verifying Webhook Signature
To ensure the webhook notification truly came from CartDNA and was not altered in transit, you must recreate the signature using the same rules as the payment request and compare it with the signature you received in the webhook payload.
If the two signatures do not match, the notification must be rejected.
Fields used for verification
You will use these fields from the webhook payload:
merchant_id
order_id
payment_id
amount
currency
signature_key(your signature key — NOT in the payload)
Mandatory normalization rules (must follow exactly)
Before building the verification string:
- Multiply amount by 100 and convert to integer
| Amount in payload | Value you must use |
|---|---|
| 250.00 | 25000 |
| 99.95 | 9995 |
After sorting, the order becomes:
amount
currency
merchant_id
order_id
payment_id- Do NOT include signature_key in sorting
Instead, convert it to MD5 and append it LAST.
Final concatenation format
Build the string exactly like this:
amount_integer+ currency+ merchant_id+ order_id+ payment_id+ status+ md5(signature_key)Then:
- Apply SHA-256
- Convert result to UPPERCASE hexadecimal
Step-by-step verification process
- Extract
signaturefrom the webhook payload
- Sort parameters A → Z
- Append
md5(merchant_secret)at the end
- SHA-256 hash the concatenated string
- Convert hash to uppercase
- Compare with received signature
If they match → accept the webhook
If they do not match → reject the webhook
Common mistakes to avoid in Signature verification
| Mistake | Result |
|---|---|
| Not multiplying amount by 100 | Invalid signature |
| Not sorting A→Z | Invalid signature |
| Including merchant_secret in sorting | Invalid signature |
| Forgetting MD5 on merchant_secret | Invalid signature |
| Comparing lowercase hash | Invalid signature |
Example Verification Code (PHP)
<?php
// --------------------------------------------------
// 1) Your merchant secret (NEVER read this from payload)
// --------------------------------------------------
$signature_key = "SIGNATURE_KEY_999";
// --------------------------------------------------
// 2) Webhook payload you received (example)
// In real life this comes from: json_decode(file_get_contents("php://input"), true);
// --------------------------------------------------
$payload = [
"merchant_id" => "MERCHANT_12345",
"order_id" => "ORDER_67890",
"payment_id" => "PAY_ABC123XYZ",
"amount" => "250.00",
"currency" => "USD",
"status" => "success",
"signature" => "B7C8D9E0F1A2..." // signature sent by CartDNA
];
// --------------------------------------------------
// 3) Extract received signature
// --------------------------------------------------
$receivedSignature = $payload['signature'];
// --------------------------------------------------
// 4) Normalize values
// --------------------------------------------------
// Convert amount to integer (× 100)
$amount_integer = (int) round($payload['amount'] * 100);
// Lowercase text fields
$merchant_id = strtolower($payload['merchant_id']);
$order_id = strtolower($payload['order_id']);
$payment_id = strtolower($payload['payment_id']);
$currency = strtolower($payload['currency']);
$status = strtolower($payload['status']);
// --------------------------------------------------
// 5) Sort parameters A → Z by name
// Final order must be:
// amount, currency, merchant_id, order_id, payment_id, status
// --------------------------------------------------
$sortedValues = [
"amount" => $amount_integer,
"currency" => $currency,
"merchant_id" => $merchant_id,
"order_id" => $order_id,
"payment_id" => $payment_id
];
ksort($sortedValues); // ensures A → Z order
// --------------------------------------------------
// 6) MD5 the merchant secret (must be LAST)
// --------------------------------------------------
$md5_secret = md5(strtolower($merchant_secret));
// --------------------------------------------------
// 7) Build the signature string
// --------------------------------------------------
$signatureString =
$sortedValues['amount'] .
$sortedValues['currency'] .
$sortedValues['merchant_id'] .
$sortedValues['order_id'] .
$sortedValues['payment_id'] .
$sortedValues['status'] .
$md5_secret;
// --------------------------------------------------
// 8) Generate SHA-256 and uppercase
// --------------------------------------------------
$calculatedSignature = strtoupper(hash("sha256", $signatureString));
// --------------------------------------------------
// 9) Compare signatures
// --------------------------------------------------
if (hash_equals($calculatedSignature, $receivedSignature)) {
echo "✅ Webhook VERIFIED — process the payment update.";
} else {
echo " INVALID SIGNATURE — reject webhook.";
}
Webhook Response
Your webhook endpoint should respond with HTTP status code 200 to acknowledge receipt. If CartDna doesn't receive a 200 response, it will retry sending the webhook notification up to 3 times with exponential backoff.
Payment Status Values
The status field in the webhook payload can have the following values:
success- Payment completed successfully
pending- Payment is being processed
failed- Payment failed
cancelled- Payment was cancelled by the customer
refunded- Payment has been refunded
Security Best Practices
- Always verify the signature - Never process a webhook without signature verification
- Use HTTPS - Ensure your webhook URL uses HTTPS for secure communication
- Keep your merchant_secret secure - Never expose this in client-side code or public repositories
- Log webhook attempts - Keep logs of all webhook notifications for debugging and auditing
- Implement idempotency - Handle duplicate webhook notifications gracefully by checking if the order has already been processed
Capture Payment (For transaction_type = auth)
If the original payment request was created with:
transaction_type = auththen the funds are only authorized (reserved) and not charged yet.
To complete the payment, you must call the Capture Payment API.
Endpoint
POST /v1/payments/{payment_id}/capture{payment_id}→ The payment ID returned in the authorization response.
Required Headers
| Header | Required | Description | Example |
|---|---|---|---|
| Authorization | Yes | OAuth Bearer token | Bearer YOUR_TOKEN |
| Content-Type | Yes | JSON format | application/json |
| X-Timestamp | Yes | UTC Unix timestamp (seconds) | 1707300000 |
| X-Idempotency-Key | Yes | Unique key per request | 550e8400-e29b-41d4-a716-446655440000 |
| X-Signature | Yes | SHA-256 signature | C9D0E1F2A3B4… |
Request Body Parameters
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
| amount | Decimal | No | Amount to capture (allows partial capture) | 1500.00 |
If amount is not provided, CartDNA will capture the full authorized amount.
Signature Rule for Capture Request
To generate the signature, follow the same security rules as the payment request, but use these fields:
amount_integer+ currency+ merchant_id+ payment_id+ timestamp+ md5(signature_key)Steps
- Multiply
amountby 100 and convert to integer
- Lowercase all text values
- Sort parameters A → Z
- Append md5(signature_key) last
- Apply SHA-256 and convert to UPPERCASE
Example cURL Request
curl -X POST https://api.fiatdna.com/v1/payments/PAY_ABC123XYZ/capture \
-H"Authorization: Bearer YOUR_TOKEN" \
-H"Content-Type: application/json" \
-H"X-Timestamp: 1707300000" \
-H"X-Idempotency-Key: 660e8400-e29b-41d4-a716-446655440111" \
-H"X-Signature: C9D0E1F2A3B4...." \
-d '{
"amount": 1500.00
}'
Success Response (201 Created)
{
"status": "success",
"payment_id": "PAY_ABC123XYZ",
"captured_amount": 1500.00,
"currency": "USD",
"capture_status": "CAPTURED",
"captured_at": "2026-02-07T06:10:00Z"
}
Capture Status Values
| Status | Meaning |
|---|---|
| CAPTURED | Full authorized amount captured |
| PARTIALLY_CAPTURED | Only part of the amount captured |
| FAILED | Capture attempt failed |
| VOIDED | Authorization was cancelled before capture |
| EXPIRED | Authorization window expired |
Important Rules
- You cannot capture if status is not AUTHORIZED
- You can capture multiple times (partial capture) until the full amount is used
- Capture must happen within the authorization window defined by CartDNA
- Each capture request must use a new X-Idempotency-Key
Get Payment Status API
Overview
This endpoint allows merchants to retrieve the latest status of a payment created through CartDNA. It returns the current payment state, amount, currency, gateway details, and timestamps. This API is useful when a webhook was missed, delayed, or when manual verification is required.
GET /payments/{payment_id}
Required Headers
| Header | Required | Description | Example |
|---|---|---|---|
| Authorization | Yes | Bearer Token | Bearer YOUR_TOKEN |
| Content-Type | Yes | Request format | application/json |
Path Parameter
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
| payment_id | String | Yes | Unique payment identifier | PAY_ABC123XYZ |
cURL Example
curl -X GET"https://api.fiatdna.com/v1/payments/PAY_ABC123XYZ" \
-H"Authorization: Bearer YOUR_API_KEY" \
-H"Content-Type: application/json"Success Response (200 OK)
{
"merchant_id": "MERCHANT_12345",
"order_id": "ORDER_67890",
"payment_id": "PAY_ABC123XYZ",
"amount": "250.00",
"currency": "USD",
"status": "success",
"transaction_id": "TXN_987654321",
"payment_method": "cartdna_card",
"timestamp": "2026-02-07T05:45:00Z",
"acquirer": {
"acquirer_reference": "PH-55667788",
"acquirer_status": "APPROVED",
"acquirer_status_code": "00",
"acquirer_reason_code": "000",
"acquirer_reason_message": "Transaction Approved",
}
}
Possible Payment Status Values
| Status | Meaning |
|---|---|
| PENDING | Payment initiated but not completed |
| AUTHORIZED | Funds reserved (preauth) but not captured yet |
| CAPTURED | Authorized payment fully captured |
| SUCCESS | Immediate payment completed successfully (when transaction_type = payment) |
| FAILED | Payment attempted but failed |
| CANCELLED | User or merchant cancelled the payment/authorization |
| REFUNDED | Full amount was refunded |
| PARTIALLY_REFUNDED | Part of the amount was refunded |
| EXPIRED | Authorization window expired before capture |
Error Responses
404 Not Found
{
"status":"error",
"error_code":"NOT_FOUND",
"message":"Payment ID not found"
}401 Unauthorized
{
"status":"error",
"error_code":"UNAUTHORIZED",
"message":"Invalid or missing API key"
}
Void Authorization (For transaction_type = auth)
If the original payment was created with:
transaction_type = authand you no longer want to capture the funds, you can void (cancel) the authorization before any capture happens.
Once voided, no funds can be captured from that authorization.
Endpoint
POST /v1/payments/{payment_id}/void{payment_id}→ The authorized payment ID returned in the original response.
Required Headers
| Header | Required | Description | Example |
|---|---|---|---|
| Authorization | Yes | OAuth Bearer token | Bearer YOUR_TOKEN |
| Content-Type | Yes | JSON format | application/json |
| X-Timestamp | Yes | UTC Unix timestamp (seconds) | 1707300000 |
| X-Idempotency-Key | Yes | Unique key per request | 770e8400-e29b-41d4-a716-446655440222 |
| X-Signature | Yes | SHA-256 signature | D1E2F3A4B5C6… |
Request Body Parameters
No body is required.
(You can optionally send metadata if you want an audit reason.)
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
| reason | String | No | Business reason for void | “Customer cancelled order” |
Example body (optional):
{"reason":"Customer cancelled order"}
Signature Rule for Void Request
Use the same normalization rules as before, but the fields are:
currency+ merchant_id+ payment_id+ timestamp+ md5(signature_key)Steps
- Lowercase all text values
- Sort parameters A → Z
- Append md5(merchant_secret) last
- Apply SHA-256 and convert to UPPERCASE
Note: No amount is used for void because nothing is being captured or refunded.
Example cURL Request
curl -X POST https://api.fiatdna.com/v1/payments/PAY_ABC123XYZ/void \
-H"Authorization: Bearer YOUR_TOKEN" \
-H"Content-Type: application/json" \
-H"X-Timestamp: 1707300000" \
-H"X-Idempotency-Key: 770e8400-e29b-41d4-a716-446655440222" \
-H"X-Signature: D1E2F3A4B5C6...." \
-d '{
"reason": "Customer cancelled order"
}'Success Response (200 OK)
{
"status":"success",
"payment_id":"PAY_ABC123XYZ",
"void_status":"VOIDED",
"voided_at":"2026-02-07T06:20:00Z",
"message":"Authorization successfully voided"
}
Void Status Values
| Status | Meaning |
|---|---|
| VOIDED | Authorization successfully cancelled |
| FAILED | Void attempt failed |
| ALREADY_CAPTURED | Cannot void because funds were already captured |
| EXPIRED | Authorization already expired |
Refund Payment API — Detailed Specification
Overview
This endpoint allows merchants to request a full or partial refund for a completed payment.
Refunds are processed asynchronously — the final outcome should be checked via the Refund Status API or received through a refund webhook.
Endpoint
POST /payments/{payment_id}/refund
Required Headers
| Header | Required | Description | Example |
|---|---|---|---|
| Authorization | Yes | Bearer access token | Bearer eyJhbGciOiJIUzI1Ni... |
| Content-Type | Yes | Request format | application/json |
Path Parameter
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
| payment_id | String | Yes | CartDNA payment identifier | PAY_ABC123XYZ |
Request Body Parameters
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
| amount | Decimal | Yes | Refund amount (can be partial) | 500.00 |
CURL Example
curl -X POST"https://api.fiatdna.com/v1/payments/PAY_ABC123XYZ/refund" \
-H"Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H"Content-Type: application/json" \
-d '{
"amount": 500.00,
"reason": "Customer requested cancellation"
}'
Success Response (201 Created)
{
"status": "success",
"refund_id": "RF_998877",
"payment_id": "PAY_ABC123XYZ",
"requested_amount": 500.00,
"currency": "USD",
"refund_status": "PROCESSING",
"requested_at": "2026-02-07T06:00:00Z"
}Response Fields
| Field | Type | Description | Example |
|---|---|---|---|
| status | String | Overall request result | success |
| refund_id | String | CartDNA refund identifier | RF_998877 |
| payment_id | String | Original payment reference | PAY_ABC123XYZ |
| requested_amount | Decimal | Amount requested for refund | 500.00 |
| currency | String | Currency of refund | LKR |
| refund_status | String | Current refund state | PROCESSING |
| requested_at | DateTime | Time refund was requested | 2026-02-07T06:00:00Z |
Refund Status Values
| Status | Meaning |
|---|---|
| PROCESSING | Refund is being handled by gateway |
| REFUNDED | Full refund completed |
| PARTIALLY_REFUNDED | Partial refund completed |
| FAILED | Refund rejected or failed |
| CANCELLED | Refund request cancelled |
How CartDNA determines final status
- If
amount = original payment amount→ REFUNDED
- If
amount < original payment amount→ PARTIALLY_REFUNDED
Error Responses
400 Bad Request
{
"status": "error",
"error_code": "INVALID_REQUEST",
"message": "Refund amount exceeds original payment amount"
}
404 Not Found
{
"status": "error",
"error_code": "NOT_FOUND",
"message": "Payment ID not found"
}
401 Unauthorized
{
"status": "error",
"error_code": "UNAUTHORIZED",
"message": "Invalid or missing access token"
}
Gateway Error Codes — Reference Table (with Internal Code)
| Public Error Code | Internal Code | HTTP Status | Category | Meaning | When it occurs | Suggested Action |
|---|---|---|---|---|---|---|
| INVALID_REQUEST | CDN-400-001 | 400 | Validation | Request format is invalid | Missing fields, wrong data type, or invalid values | Fix request payload and retry |
| MISSING_REQUIRED_FIELD | CDN-400-002 | 400 | Validation | Required parameter not provided | e.g., missing order_id, amount, or currency | Add required field and retry |
| INVALID_AMOUNT | CDN-400-003 | 400 | Validation | Amount is zero or negative | amount ≤ 0 | Send valid amount |
| INVALID_CURRENCY | CDN-400-004 | 400 | Validation | Unsupported currency | Currency not allowed by gateway | Use supported currency |
| INVALID_PAYMENT_METHOD | CDN-400-005 | 400 | Validation | Payment method not supported | Wrong payment_method code | Use valid method |
| UNAUTHORIZED | CDN-401-001 | 401 | Authentication | API key is missing or invalid | No Bearer token or wrong key | Provide valid API key |
| INVALID_SIGNATURE | CDN-401-002 | 401 | Security | Request signature mismatch | Tampered payload or wrong secret | Regenerate correct signature |
| FORBIDDEN | CDN-403-001 | 403 | Authorization | Merchant not allowed | Inactive merchant or feature blocked | Contact CartDNA support |
| NOT_FOUND | CDN-404-001 | 404 | Resource | Payment ID does not exist | Wrong payment_id used | Verify payment ID |
| PAYMENT_NOT_FOUND | CDN-404-002 | 404 | Resource | No matching payment | Deleted or never created | Create payment first |
| REFUND_NOT_ALLOWED | CDN-400-010 | 400 | Refund | Refund not permitted | Payment not completed or already refunded | Check payment status |
| REFUND_AMOUNT_EXCEEDED | CDN-400-011 | 400 | Refund | Refund > original amount | Trying to refund more than paid | Reduce refund amount |
| GATEWAY_TIMEOUT | CDN-504-001 | 504 | Gateway | Payment provider did not respond | PayHere/Stripe/Bank delay | Retry later |
| GATEWAY_ERROR | CDN-502-001 | 502 | Gateway | Provider rejected payment | Card declined, insufficient funds, etc. | Ask customer to retry |
| DUPLICATE_ORDER | CDN-409-001 | 409 | Business | Order already processed | Same order_id reused | Use unique order_id |
| SESSION_EXPIRED | CDN-410-001 | 410 | Session | Payment link expired | Checkout URL used after expiry | Create new payment |
| INTERNAL_ERROR | CDN-500-001 | 500 | System | CartDNA system failure | Unexpected server error | Retry or contact support |
| SERVICE_UNAVAILABLE | CDN-503-001 | 503 | System | System temporarily down | Maintenance or overload | Try again later |