# CentaPay API Guide

> **Source documentation:** [https://docs.centapay.com](https://docs.centapay.com)
> **Interactive guide:** [https://www.centapay.com/docs](https://www.centapay.com/docs)
> **API version:** Based on platform version 5.6.4
> **Protocol:** S2S CARD (Server-to-Server)
> **Content-Type:** `application/x-www-form-urlencoded` (all requests and callbacks)
> **Response format:** JSON
> **Contact:** info@centapay.com

---

## Overview

CentaPay is a cross-border payment infrastructure provider for Central Asia. This API enables two core capabilities:

- **Accept payments (SALE)** - Process inbound card payments from consumers in Kazakhstan and Uzbekistan to your international business or platform.
- **Send disbursements (CREDIT2CARD)** - Push funds from your CentaPay settlement balance to recipient cards (payouts, refunds to alternate cards, creator payments).

### Supported Currencies and Amount Formats

| Currency          | Code  | Markets                    | Amount Format           | Example  |
| ----------------- | ----- | -------------------------- | ----------------------- | -------- |
| Kazakhstani Tenge | `KZT` | Kazakhstan                 | Integer (zero-exponent) | `5000`   |
| Uzbekistani Som   | `UZS` | Uzbekistan                 | Integer (zero-exponent) | `120000` |
| US Dollar         | `USD` | Settlement / international | Float XX.XX             | `49.99`  |

### Supported Card Schemes

Visa, Mastercard, UZCARD (Uzbekistan), HUMO (Uzbekistan). Apple Pay and Google Pay supported via wallet token flow.

### Key Integration Facts

- **Protocol:** S2S CARD only (server-to-server). PCI DSS compliance required.
- **Settlement:** T+1 (next business day), typically in USD.
- **3DS:** Most KZ/UZ transactions trigger 3D Secure. Your integration must handle the redirect flow.
- **Callbacks:** The authoritative transaction result is always the callback, not the synchronous response.

---

## Credentials & Environments

[View in docs](https://www.centapay.com/docs#credentials)

Before receiving an account, provide to CentaPay:

| Data              | Description                                                                                                                      |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| **IP list**       | IP addresses from which your server will send requests. Requests from un-whitelisted IPs are rejected silently.                  |
| **Callback URL**  | URL that receives transaction result notifications (webhooks). Max 255 characters. Mandatory if your account supports 3D Secure. |
| **Contact email** | Email of the person who will monitor transactions, conduct refunds, and handle operational queries.                              |

You will receive:

| Credential    | Description                                                                     | Where Used                                       |
| ------------- | ------------------------------------------------------------------------------- | ------------------------------------------------ |
| `CLIENT_KEY`  | Unique account identifier (UUID). Corresponds to _Merchant key_ in admin panel. | POST parameter on every request                  |
| `PASSWORD`    | Secret for hash signature generation. Corresponds to _Password_ in admin panel. | Hash calculation only - never sent over the wire |
| `PAYMENT_URL` | Base endpoint URL (different for sandbox and production).                       | All API requests                                 |

### Protocol Mapping

[View in docs](https://www.centapay.com/docs#credentials-protocol)

You cannot process payments until the S2S CARD protocol has been mapped by CentaPay during onboarding.

| Protocol     | Used For                                                                           | Endpoint                     |
| ------------ | ---------------------------------------------------------------------------------- | ---------------------------- |
| **S2S CARD** | Card payments (Visa, MC, UZCARD, HUMO), Apple Pay, Google Pay, CREDIT2CARD payouts | `https://{PAYMENT_URL}/post` |

Alternative endpoint `https://{PAYMENT_URL}/v2/post` returns `redirect_params` as array of `{name, value}` objects instead of key-value object.

### IP Whitelisting

[View in docs](https://www.centapay.com/docs#credentials-ip)

Requests from un-whitelisted IPs are rejected without response.

### Credential Rotation

No self-service PASSWORD rotation. Contact info@centapay.com to rotate credentials.

---

## Authentication

[View in docs](https://www.centapay.com/docs#authentication)

Every API request includes a `hash` parameter - an MD5 signature computed from specific request fields and your PASSWORD. No Bearer tokens or API key headers.

The PASSWORD never leaves your server. Only the resulting hash is transmitted. If a formula references optional parameters you do not send, omit them from the hash calculation.

### Hash Formulas

[View in docs](https://www.centapay.com/docs#auth-formulas)

In all formulas: `strrev()` = reverse string, `strtoupper()` = uppercase, `md5()` = MD5 hex digest.

#### Formula 1 - SALE, RETRY, RECURRING_SALE (with card data)

```
md5(strtoupper(
  strrev(email)
  . PASSWORD
  . strrev(substr(card_number, 0, 6) . substr(card_number, -4))
))
```

```php
function hashFormula1(string $email, string $password, string $cardNumber): string
{
    $cardPart = substr($cardNumber, 0, 6) . substr($cardNumber, -4);
    $raw = strrev($email) . $password . strrev($cardPart);
    return md5(strtoupper($raw));
}
```

```python
import hashlib

def hash_formula_1(email: str, password: str, card_number: str) -> str:
    card_part = card_number[:6] + card_number[-4:]
    raw = email[::-1] + password + card_part[::-1]
    return hashlib.md5(raw.upper().encode()).hexdigest()
```

```javascript
const crypto = require('crypto')
function strrev(s) {
  return s.split('').reverse().join('')
}

function hashFormula1(email, password, cardNumber) {
  const cardPart = cardNumber.slice(0, 6) + cardNumber.slice(-4)
  const raw = strrev(email) + password + strrev(cardPart)
  return crypto.createHash('md5').update(raw.toUpperCase()).digest('hex')
}
```

#### Formula 1 variant - SALE, RECURRING_SALE (with card_token)

```
md5(strtoupper(
  strrev(email) . PASSWORD . strrev(card_token)
))
```

#### Formula 2 - CAPTURE, CREDITVOID, VOID, GET_TRANS_STATUS, Callback verification

```
md5(strtoupper(
  strrev(email)
  . PASSWORD
  . trans_id
  . strrev(substr(card_number, 0, 6) . substr(card_number, -4))
))
```

```php
function hashFormula2(string $email, string $password, string $transId, string $cardNumber): string
{
    $cardPart = substr($cardNumber, 0, 6) . substr($cardNumber, -4);
    $raw = strrev($email) . $password . $transId . strrev($cardPart);
    return md5(strtoupper($raw));
}
```

```python
def hash_formula_2(email: str, password: str, trans_id: str, card_number: str) -> str:
    card_part = card_number[:6] + card_number[-4:]
    raw = email[::-1] + password + trans_id + card_part[::-1]
    return hashlib.md5(raw.upper().encode()).hexdigest()
```

```javascript
function hashFormula2(email, password, transId, cardNumber) {
  const cardPart = cardNumber.slice(0, 6) + cardNumber.slice(-4)
  const raw = strrev(email) + password + transId + strrev(cardPart)
  return crypto.createHash('md5').update(raw.toUpperCase()).digest('hex')
}
```

#### Formula 3 - CREATE_SCHEDULE

```
md5(strtoupper(strrev(PASSWORD)))
```

#### Formula 4 - PAUSE_SCHEDULE, RUN_SCHEDULE, DELETE_SCHEDULE, SCHEDULE_INFO, DESCHEDULE

```
md5(strtoupper(strrev(schedule_id + PASSWORD)))
```

#### Formula 5 - CREDIT2CARD (request)

```
md5(strtoupper(
  PASSWORD . strrev(substr(card_number, 0, 6) . substr(card_number, -4))
))

// With card_token:
md5(strtoupper(PASSWORD . strrev(card_token)))
```

#### Formula 6 - CREDIT2CARD callbacks & GET_TRANS_STATUS for CREDIT2CARD

```
md5(strtoupper(
  PASSWORD . trans_id . strrev(substr(card_number, 0, 6) . substr(card_number, -4))
))
```

#### Formula 7 - GET_TRANS_STATUS_BY_ORDER

```
md5(strtoupper(
  strrev(email) . PASSWORD . order_id
  . strrev(substr(card_number, 0, 6) . substr(card_number, -4))
))
```

#### Formula 8 - Digital Wallets (Apple Pay / Google Pay)

```
md5(strtoupper(
  strrev(email) . PASSWORD
))
```

### Formula Quick Reference

[View in docs](https://www.centapay.com/docs#auth-summary)

| Action                                         | Formula                  | Key Inputs                                      |
| ---------------------------------------------- | ------------------------ | ----------------------------------------------- |
| SALE (card)                                    | 1                        | email + PASSWORD + card first6/last4            |
| SALE (card_token)                              | 1 variant                | email + PASSWORD + card_token                   |
| SALE (digital wallet)                          | 8                        | email + PASSWORD only                           |
| CAPTURE                                        | 2                        | email + PASSWORD + trans_id + card first6/last4 |
| CREDITVOID                                     | 2                        | email + PASSWORD + trans_id + card first6/last4 |
| VOID                                           | 2                        | email + PASSWORD + trans_id + card first6/last4 |
| RECURRING_SALE                                 | 1                        | email + PASSWORD + card first6/last4            |
| RETRY                                          | 1                        | email + PASSWORD + card first6/last4            |
| CREDIT2CARD (request)                          | 5                        | PASSWORD + card first6/last4                    |
| CREDIT2CARD (callback)                         | 6                        | PASSWORD + trans_id + card first6/last4         |
| GET_TRANS_STATUS                               | 2 (or 6 for CREDIT2CARD) | See formula                                     |
| GET_TRANS_DETAILS                              | 2 (or 6 for CREDIT2CARD) | See formula                                     |
| GET_TRANS_STATUS_BY_ORDER                      | 7                        | email + PASSWORD + order_id + card first6/last4 |
| CREATE_SCHEDULE                                | 3                        | PASSWORD only                                   |
| Other schedule ops                             | 4                        | schedule_id + PASSWORD                          |
| Callback verification (all except CREDIT2CARD) | 2                        | email + PASSWORD + trans_id + card first6/last4 |
| Callback verification (CREDIT2CARD only)       | 6                        | PASSWORD + trans_id + card first6/last4         |

---

## Actions & Results Reference

### Possible Results

| Result      | Description                                         |
| ----------- | --------------------------------------------------- |
| `SUCCESS`   | Action completed successfully                       |
| `DECLINED`  | Action unsuccessful                                 |
| `REDIRECT`  | Additional action required (redirect to 3DS)        |
| `ACCEPTED`  | Action accepted, will complete later                |
| `ERROR`     | Request validation failed                           |
| `UNDEFINED` | Status undetermined, final status sent via callback |

### Possible Statuses

| Status       | Description                                    |
| ------------ | ---------------------------------------------- |
| `3DS`        | Awaiting 3D Secure validation                  |
| `REDIRECT`   | Transaction redirected                         |
| `PENDING`    | Awaiting CAPTURE (AUTH hold)                   |
| `PREPARE`    | Status undetermined, final status via callback |
| `SETTLED`    | Successful transaction                         |
| `REVERSAL`   | Reversal completed                             |
| `REFUND`     | Refund completed                               |
| `VOID`       | Void completed                                 |
| `CHARGEBACK` | Chargeback received                            |
| `DECLINED`   | Transaction declined                           |

---

## SALE

[View in docs](https://www.centapay.com/docs#ref-sale)

Creates a SALE (authorise + capture) or AUTH (authorise only) transaction.

**POST** `https://{PAYMENT_URL}/post`

### Request Parameters

| Parameter                  | Description                                                                            | Format                                                         | Required |
| -------------------------- | -------------------------------------------------------------------------------------- | -------------------------------------------------------------- | -------- |
| `action`                   | `SALE`                                                                                 |                                                                | Y        |
| `client_key`               | Account key                                                                            | UUID                                                           | Y        |
| `channel_id`               | Sub-account routing                                                                    | <=16 chars                                                     | N        |
| `order_id`                 | Your unique order ID                                                                   | <=255 chars                                                    | Y        |
| `order_amount`             | Transaction amount. Integer for KZT/UZS. Float XX.XX for USD. 0 allowed with `auth=Y`. | Number                                                         | Y        |
| `order_currency`           | Currency code                                                                          | 3-letter (KZT, UZS, USD)                                       | Y        |
| `order_description`        | Transaction description                                                                | <=1024 chars                                                   | Y        |
| `card_number`              | Card PAN                                                                               | digits                                                         | Y\*      |
| `card_exp_month`           | Expiry month                                                                           | MM                                                             | Y\*      |
| `card_exp_year`            | Expiry year                                                                            | YYYY                                                           | Y\*      |
| `card_cvv2`                | CVV/CVC2                                                                               | 3-4 digits                                                     | Y\*\*    |
| `card_token`               | Stored card token (replaces card fields)                                               | 64 chars                                                       | N        |
| `digital_wallet`           | `googlepay` or `applepay`. Pair with `payment_token`.                                  | String                                                         | N        |
| `payment_token`            | Wallet payment token from Apple/Google Pay                                             | String                                                         | N        |
| `payer_first_name`         | Customer first name                                                                    | <=32 chars                                                     | Y        |
| `payer_last_name`          | Customer last name                                                                     | <=32 chars                                                     | Y        |
| `payer_middle_name`        | Customer middle name                                                                   | <=32 chars                                                     | N        |
| `payer_birth_date`         | Customer DOB                                                                           | yyyy-MM-dd                                                     | N        |
| `payer_address`            | Customer address                                                                       | <=255 chars                                                    | Y        |
| `payer_address2`           | Address line 2                                                                         | <=255 chars                                                    | N        |
| `payer_house_number`       | House/building number                                                                  | <=9 chars                                                      | N        |
| `payer_country`            | Customer country                                                                       | 2-letter ISO 3166-1                                            | Y        |
| `payer_state`              | Customer state                                                                         | <=32 chars                                                     | N        |
| `payer_city`               | Customer city                                                                          | <=40 chars                                                     | Y        |
| `payer_district`           | Customer district                                                                      | <=32 chars                                                     | N        |
| `payer_zip`                | Customer postal code                                                                   | <=10 chars                                                     | Y        |
| `payer_email`              | Customer email                                                                         | <=256 chars                                                    | Y        |
| `payer_phone`              | Customer phone                                                                         | <=32 chars                                                     | Y        |
| `payer_phone_country_code` | Phone country code                                                                     | String                                                         | N        |
| `payer_ip`                 | Customer IP. IPv4 and IPv6 supported.                                                  | IP address                                                     | Y        |
| `term_url_3ds`             | 3DS return URL                                                                         | <=1024 chars                                                   | Y        |
| `term_url_target`          | Browsing context for 3DS return                                                        | `_blank`, `_self`, `_parent`, `_top` (default), or iframe name | N        |
| `auth`                     | AUTH only (DMS)                                                                        | `Y` or `N` (default N)                                         | N        |
| `req_token`                | Request card token                                                                     | `Y` or `N` (default N)                                         | N        |
| `recurring_init`           | Init recurring sequence                                                                | `Y` or `N` (default N)                                         | N        |
| `schedule_id`              | Link to schedule                                                                       | String                                                         | N        |
| `parameters`               | Acquirer-specific extra fields                                                         | `parameters[key]: value`                                       | N        |
| `custom_data`              | Arbitrary data echoed in callback                                                      | `custom_data[key]: value`                                      | N        |
| `hash`                     | Request signature                                                                      | Formula 1 (cards), Formula 8 (wallets)                         | Y        |

\* Optional if `card_token` or `payment_token` provided.
\*\* Optional if `payment_token` provided.

**Parameter precedence rules:**

- If `card_token` AND card data are both specified, `card_token` is ignored.
- If `req_token` AND `card_token` are both specified, `req_token` is ignored.
- If `payment_token` AND card data are both specified, `payment_token` is ignored.
- If `card_token` is specified, `payment_token` is ignored.

### Synchronous Response

**Success:**

| Parameter         | Description                                                      |
| ----------------- | ---------------------------------------------------------------- |
| `action`          | `SALE`                                                           |
| `result`          | `SUCCESS`                                                        |
| `status`          | `SETTLED` / `PENDING` / `PREPARE`. Only `PENDING` when `auth=Y`. |
| `order_id`        | Your order ID                                                    |
| `trans_id`        | CentaPay transaction ID                                          |
| `trans_date`      | Timestamp (YYYY-MM-DD hh:mm:ss)                                  |
| `descriptor`      | Bank descriptor (what cardholder sees on statement)              |
| `amount`          | Order amount                                                     |
| `currency`        | Currency                                                         |
| `recurring_token` | Returned if `recurring_init=Y` and account supports recurring    |
| `schedule_id`     | Returned if schedule used                                        |
| `card_token`      | Returned if `req_token=Y`                                        |
| `digital_wallet`  | `googlepay` or `applepay` if wallet used                         |
| `pan_type`        | `DPAN` or `FPAN` (wallet transactions only)                      |

**Declined:**

| Parameter        | Description                   |
| ---------------- | ----------------------------- |
| `action`         | `SALE`                        |
| `result`         | `DECLINED`                    |
| `status`         | `DECLINED`                    |
| `order_id`       | Your order ID                 |
| `trans_id`       | CentaPay transaction ID       |
| `trans_date`     | Timestamp                     |
| `descriptor`     | Bank descriptor               |
| `amount`         | Order amount                  |
| `currency`       | Currency                      |
| `decline_reason` | Human-readable decline reason |
| `digital_wallet` | Wallet provider if applicable |
| `pan_type`       | DPAN/FPAN if applicable       |

**3DS Redirect:**

| Parameter         | Description                                                                         |
| ----------------- | ----------------------------------------------------------------------------------- |
| `action`          | `SALE`                                                                              |
| `result`          | `REDIRECT`                                                                          |
| `status`          | `3DS` / `REDIRECT`                                                                  |
| `order_id`        | Your order ID                                                                       |
| `trans_id`        | CentaPay transaction ID                                                             |
| `trans_date`      | Timestamp                                                                           |
| `descriptor`      | Bank descriptor                                                                     |
| `amount`          | Order amount                                                                        |
| `currency`        | Currency                                                                            |
| `redirect_url`    | URL to redirect cardholder to                                                       |
| `redirect_params` | Object of 3DS params (key-value). May be empty array or absent. Varies by acquirer. |
| `redirect_method` | `POST` or `GET`                                                                     |
| `digital_wallet`  | Wallet provider if applicable                                                       |
| `pan_type`        | DPAN/FPAN if applicable                                                             |

**Undefined:**

| Parameter        | Description                                          |
| ---------------- | ---------------------------------------------------- |
| `action`         | `SALE`                                               |
| `result`         | `UNDEFINED`                                          |
| `status`         | `PENDING` / `PREPARE`. `PENDING` only when `auth=Y`. |
| `order_id`       | Your order ID                                        |
| `trans_id`       | CentaPay transaction ID                              |
| `trans_date`     | Timestamp                                            |
| `descriptor`     | Bank descriptor                                      |
| `amount`         | Order amount                                         |
| `currency`       | Currency                                             |
| `digital_wallet` | Wallet provider if applicable                        |
| `pan_type`       | DPAN/FPAN if applicable                              |

### SALE Callback Parameters

**Success:**

| Parameter                           | Description                                                           |
| ----------------------------------- | --------------------------------------------------------------------- |
| `action`                            | `SALE`                                                                |
| `result`                            | `SUCCESS`                                                             |
| `status`                            | `PENDING` / `PREPARE` / `SETTLED`                                     |
| `order_id`                          | Your order ID                                                         |
| `trans_id`                          | CentaPay transaction ID                                               |
| `trans_date`                        | Timestamp                                                             |
| `descriptor`                        | Bank descriptor                                                       |
| `amount`                            | Order amount                                                          |
| `currency`                          | Currency                                                              |
| `card`                              | Masked PAN (e.g. `411111****1111`). For wallets: decrypted token PAN. |
| `card_expiration_date`              | Card expiry                                                           |
| `recurring_token`                   | If `recurring_init=Y` was sent                                        |
| `schedule_id`                       | If schedule used                                                      |
| `card_token`                        | If `req_token=Y` was sent                                             |
| `exchange_rate`                     | FX rate if currency conversion applied                                |
| `exchange_rate_base`                | Base rate if double conversion                                        |
| `exchange_currency`                 | Original currency                                                     |
| `exchange_amount`                   | Original amount                                                       |
| `custom_data`                       | Echoed custom data from request                                       |
| `digital_wallet`                    | `googlepay` or `applepay`                                             |
| `pan_type`                          | `DPAN` or `FPAN`                                                      |
| `connector_name`\*                  | Payment gateway name                                                  |
| `rrn`\*                             | Retrieval Reference Number                                            |
| `approval_code`\*                   | Issuer authorisation code                                             |
| `gateway_id`_ / `extra_gateway_id`_ | Gateway transaction identifiers                                       |
| `merchant_name`_ / `mid_name`_      | Merchant and MID names                                                |
| `issuer_country`_ / `issuer_bank`_  | Card issuer details                                                   |
| `brand`\*                           | Payment method brand (VISA, MASTERCARD, UZCARD, HUMO)                 |
| `arn`\*                             | Acquirer Reference Number                                             |
| `extended_data`\*                   | Custom key-value data                                                 |
| `hash`                              | Verify with Formula 2                                                 |

\* Extended data fields - included only if configured in admin panel (Configuration > Protocol Mappings > "Add Extended Data to Callback").

**Declined callback:** Reduced field set compared to success. Includes: `action`, `result: DECLINED`, `status: DECLINED`, `order_id`, `trans_id`, `trans_date`, `decline_reason`, `custom_data`, `digital_wallet`, `pan_type`, `hash`. Does NOT include: `card`, `card_expiration_date`, `descriptor`, `amount`, `currency`, `card_token`, `recurring_token`, or extended data fields.

**3DS/Redirect callback:** `result: REDIRECT`, `status: 3DS/REDIRECT`. Includes: `action`, `result`, `status`, `order_id`, `trans_id`, `trans_date`, `descriptor`, `amount`, `currency`, `redirect_url`, `redirect_params`, `redirect_method`, `custom_data`, `digital_wallet`, `pan_type`, `hash`. Does NOT include: `card`, `card_expiration_date`.

**Undefined callback:** `result: UNDEFINED`, `status: 3DS / REDIRECT / PENDING / PREPARE`. Includes: `action`, `result`, `status`, `order_id`, `trans_id`, `trans_date`, `descriptor`, `amount`, `currency`, `custom_data`, `digital_wallet`, `pan_type`, `hash`. Does NOT include: `card`, `card_expiration_date`.

**Important: Never use the synchronous response alone to fulfil an order. Always wait for the callback.**

### SALE Example (cURL)

```bash
curl -X POST https://{PAYMENT_URL}/post \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "action=SALE" \
  -d "client_key={CLIENT_KEY}" \
  -d "order_id=ORD-001" \
  -d "order_amount=5000" \
  -d "order_currency=KZT" \
  -d "order_description=Product+purchase" \
  -d "card_number=4111111111111111" \
  -d "card_exp_month=01" \
  -d "card_exp_year=2038" \
  -d "card_cvv2=000" \
  -d "payer_first_name=John" \
  -d "payer_last_name=Smith" \
  -d "payer_email=john@example.com" \
  -d "payer_phone=77001234567" \
  -d "payer_country=KZ" \
  -d "payer_city=Astana" \
  -d "payer_address=10+Kunayeva+St" \
  -d "payer_zip=010000" \
  -d "payer_ip=192.168.1.1" \
  -d "term_url_3ds=https://yoursite.com/3ds-return" \
  -d "hash={CALCULATED_HASH}"
```

---

## 3D Secure

[View in docs](https://www.centapay.com/docs#payments-3ds)

Most Kazakhstan and Uzbekistan card transactions trigger 3DS. Your integration must handle the redirect flow.

### Flow

1. Send SALE request.
2. Receive `result: REDIRECT`, `status: 3DS`, with `redirect_url`.
3. Redirect cardholder to issuer ACS using `redirect_method` and `redirect_params`.
4. Cardholder authenticates (SMS, biometric, or frictionless).
5. Customer returns to your `term_url_3ds`.
6. Final result delivered via callback.

### Handling the Redirect

`redirect_params` varies by acquirer. It may contain `PaReq`, `TermUrl`, or other values. It may be empty or absent (commonly when `redirect_method=GET`). Always check for presence before processing.

**Option 1: HTML Form**

```php
$html = '<form id="3ds" method="'
  . $response['redirect_method']
  . '" action="' . $response['redirect_url'] . '">';
if (!empty($response['redirect_params']) && is_array($response['redirect_params'])) {
    foreach ($response['redirect_params'] as $k => $v) {
        $html .= '<input type="hidden" name="' . htmlspecialchars($k)
          . '" value="' . htmlspecialchars($v) . '">';
    }
}
$html .= '</form><script>document.getElementById("3ds").submit();</script>';
echo $html;
```

**Option 2: JavaScript Redirect (GET)**

```javascript
document.location = response.redirect_url
```

### /v2/post Endpoint

Standard `/post` returns `redirect_params` as key-value object:

```json
"redirect_params": { "PaReq": "eJxVUt1...", "TermUrl": "https://..." }
```

Alternative `/v2/post` returns as array of objects:

```json
"redirect_params": [
  {"name": "PaReq", "value": "eJxVUt1..."},
  {"name": "TermUrl", "value": "https://..."}
]
```

### term_url_target

Controls where customer returns after 3DS when checkout is in an iframe. Values: `_blank`, `_self`, `_parent`, `_top` (default), or custom iframe name.

---

## CAPTURE

[View in docs](https://www.centapay.com/docs#ref-capture)

Capture a previously authorised transaction (SALE with `auth=Y`).

**POST** `https://{PAYMENT_URL}/post`

### Request Parameters

| Parameter    | Format    | Required | Notes                                               |
| ------------ | --------- | -------- | --------------------------------------------------- |
| `action`     | `CAPTURE` | Y        |                                                     |
| `client_key` | UUID      | Y        |                                                     |
| `trans_id`   | UUID      | Y        | From AUTH response                                  |
| `amount`     | Number    | N        | Omit for full capture. One partial capture allowed. |
| `hash`       | MD5 hex   | Y        | Formula 2                                           |

### Synchronous Response

All responses include: `action`, `result`, `status`, `order_id`, `trans_id`, `trans_date`, `descriptor`, `amount`, `currency`.

| Result    | Status    | Description                                  |
| --------- | --------- | -------------------------------------------- |
| SUCCESS   | `SETTLED` | Capture successful                           |
| DECLINED  | `PENDING` | Capture rejected. Includes `decline_reason`. |
| UNDEFINED | `PENDING` | Await callback.                              |

### Callback Parameters

**Success:** `action: CAPTURE`, `result: SUCCESS`, `status: SETTLED`, `order_id`, `trans_id`, `amount`, `trans_date`, `descriptor`, `currency`, `hash` (Formula 2). Extended data fields if configured.

**Declined:** `action: CAPTURE`, `result: DECLINED`, `status: PENDING`, `order_id`, `trans_id`, `decline_reason`, `hash`.

**Undefined:** `action: CAPTURE`, `result: UNDEFINED`, `status: PENDING`, `order_id`, `trans_id`, `trans_date`, `descriptor`, `amount`, `currency`, `hash`.

---

## CREDITVOID (Refund / Reversal)

[View in docs](https://www.centapay.com/docs#ref-creditvoid)

Handles both reversals (cancel AUTH hold) and refunds (return settled funds). Full and partial refunds supported. Multiple partial refunds allowed.

**POST** `https://{PAYMENT_URL}/post`

### Request Parameters

| Parameter    | Format       | Required | Notes                                                                     |
| ------------ | ------------ | -------- | ------------------------------------------------------------------------- |
| `action`     | `CREDITVOID` | Y        |                                                                           |
| `client_key` | UUID         | Y        |                                                                           |
| `trans_id`   | UUID         | Y        | Original transaction ID                                                   |
| `amount`     | Number       | N        | Omit for full refund. Partial: specify amount. Multiple partials allowed. |
| `hash`       | MD5 hex      | Y        | Formula 2                                                                 |

### Synchronous Response

| Parameter  | Description             |
| ---------- | ----------------------- |
| `action`   | `CREDITVOID`            |
| `result`   | `ACCEPTED`              |
| `order_id` | Your order ID           |
| `trans_id` | CentaPay transaction ID |

### Callback Parameters

**Success (full):** `action: CREDITVOID`, `result: SUCCESS`, `status: REFUND` or `REVERSAL`, `order_id`, `trans_id`, `creditvoid_date`, `amount`, `hash` (Formula 2). Extended data fields (`connector_name`, `rrn`, `approval_code`, etc.) if configured.

**Success (partial):** Same but `status: SETTLED` (original transaction retains settled status).

**Declined:** `result: DECLINED`, includes `decline_reason`.

---

## VOID

[View in docs](https://www.centapay.com/docs#ref-void)

Cancel a transaction performed the same financial day. Only for SETTLED transactions from SALE, CAPTURE, or RECURRING_SALE. Does not process a refund - cancels entirely.

**POST** `https://{PAYMENT_URL}/post`

### Request Parameters

| Parameter    | Format      | Required | Notes     |
| ------------ | ----------- | -------- | --------- |
| `action`     | `VOID`      | Y        |           |
| `client_key` | UUID        | Y        |           |
| `trans_id`   | <=255 chars | Y        |           |
| `hash`       | MD5 hex     | Y        | Formula 2 |

### Synchronous Response

| Result    | Status                | Notes                                                               |
| --------- | --------------------- | ------------------------------------------------------------------- |
| SUCCESS   | `VOID`                | Transaction voided                                                  |
| DECLINED  | `SETTLED`             | Void rejected. Includes `decline_reason`. Original remains settled. |
| UNDEFINED | `PENDING` / `SETTLED` | Await callback.                                                     |

### Callback Parameters

**Success:** `action: VOID`, `result: SUCCESS`, `status: VOID`, `order_id`, `trans_id`, `trans_date`, `amount`, `currency`, `hash` (Formula 2).

**Declined:** `result: DECLINED`, `status: SETTLED`, includes `decline_reason`.

**VOID vs CREDITVOID:** Use VOID for same-day cancellations (no funds movement). Use CREDITVOID for refunds after settlement day or to reverse an AUTH hold.

---

## CREDIT2CARD (Payouts)

[View in docs](https://www.centapay.com/docs#ref-credit2card)

Push funds to a recipient's card.

**POST** `https://{PAYMENT_URL}/post`

### Request Parameters

| Parameter           | Format        | Required | Notes                                    |
| ------------------- | ------------- | -------- | ---------------------------------------- |
| `action`            | `CREDIT2CARD` | Y        |                                          |
| `client_key`        | UUID          | Y        |                                          |
| `channel_id`        | <=16 chars    | N        | Sub-account                              |
| `order_id`          | <=255 chars   | Y        |                                          |
| `order_amount`      | Number        | Y        | Integer for KZT/UZS, Float XX.XX for USD |
| `order_currency`    | 3-letter      | Y        |                                          |
| `order_description` | <=1024 chars  | Y        |                                          |
| `card_number`       | PAN           | Y        | Recipient card                           |
| `payee_first_name`  | <=32 chars    | N        | Recipient name                           |
| `payee_last_name`   | <=32 chars    | N        |                                          |
| `payee_middle_name` | <=32 chars    | N        |                                          |
| `payee_birth_date`  | yyyy-MM-dd    | N        |                                          |
| `payee_address`     | <=255 chars   | N        |                                          |
| `payee_address2`    | <=255 chars   | N        |                                          |
| `payee_country`     | 2-letter      | N        |                                          |
| `payee_state`       | <=32 chars    | N        |                                          |
| `payee_city`        | <=32 chars    | N        |                                          |
| `payee_zip`         | <=10 chars    | N        |                                          |
| `payee_email`       | <=256 chars   | N        |                                          |
| `payee_phone`       | <=32 chars    | N        |                                          |
| `payer_first_name`  | <=32 chars    | N        | Sender name                              |
| `payer_last_name`   | <=32 chars    | N        |                                          |
| `payer_middle_name` | <=32 chars    | N        |                                          |
| `payer_birth_date`  | yyyy-MM-dd    | N        |                                          |
| `payer_address`     | <=255 chars   | N        |                                          |
| `payer_country`     | 2-letter      | N        |                                          |
| `payer_city`        | <=32 chars    | N        |                                          |
| `payer_zip`         | <=10 chars    | N        |                                          |
| `payer_email`       | <=256 chars   | N        |                                          |
| `payer_phone`       | <=32 chars    | N        |                                          |
| `payer_ip`          | IPv4          | N        |                                          |
| `parameters`        | Object        | N        | Acquirer-specific                        |
| `hash`              | MD5 hex       | Y        | **Formula 5**                            |

### Synchronous Response

All CREDIT2CARD sync responses include: `action`, `result`, `status`, `order_id`, `trans_id`, `trans_date`. Note: unlike SALE, the SUCCESS response does not include `amount`, `currency`, or `descriptor`.

| Result    | Status     | Additional Fields           |
| --------- | ---------- | --------------------------- |
| SUCCESS   | `SETTLED`  | (none beyond common fields) |
| DECLINED  | `DECLINED` | `decline_reason`            |
| UNDEFINED | `PREPARE`  | `descriptor` (if available) |

### Callback Parameters

**Success:** `action: CREDIT2CARD`, `result: SUCCESS`, `status: SETTLED`, `order_id`, `trans_id`, `trans_date`, `hash` (Formula 6). Extended data fields if configured.

**Declined:** `action: CREDIT2CARD`, `result: DECLINED`, `status: DECLINED`, `order_id`, `trans_id`, `trans_date`, `decline_reason`, `hash` (Formula 6).

**Undefined:** `action: CREDIT2CARD`, `result: UNDEFINED`, `status: PREPARE`, `order_id`, `trans_id`, `trans_date`, `hash`.

Uses **Formula 6** for hash (not Formula 2). This is the only action with a different callback hash formula.

---

## RECURRING_SALE

[View in docs](https://www.centapay.com/docs#ref-recurring-sale)

Create a transaction using stored cardholder data from a previous transaction.

**POST** `https://{PAYMENT_URL}/post`

### Setup

Add `recurring_init=Y` to the initial SALE. On success, the response/callback includes `recurring_token`. Store this with the card first 6 + last 4 digits and payer email (needed for hash computation on subsequent charges).

### Request Parameters

| Parameter                  | Format           | Required | Notes                                                            |
| -------------------------- | ---------------- | -------- | ---------------------------------------------------------------- |
| `action`                   | `RECURRING_SALE` | Y        |                                                                  |
| `client_key`               | UUID             | Y        |                                                                  |
| `order_id`                 | <=255 chars      | Y        | New unique order ID                                              |
| `order_amount`             | Number           | Y        |                                                                  |
| `order_currency`           | 3-letter         | Y        |                                                                  |
| `order_description`        | <=1024 chars     | Y        |                                                                  |
| `recurring_first_trans_id` | UUID             | Y        | trans_id of initial transaction                                  |
| `recurring_token`          | UUID             | Y        | Token from initial transaction                                   |
| `schedule_id`              | String           | N        | Link to schedule                                                 |
| `auth`                     | Y/N              | N        | AUTH only                                                        |
| `custom_data`              | Object           | N        | Overrides initial SALE custom_data                               |
| `hash`                     | MD5 hex          | Y        | **Formula 1** (uses stored card first6/last4 and original email) |

Response identical to SALE but `action=RECURRING_SALE`. Bypasses 3DS.

**Important:** Formula 1 requires card first6/last4. Since RECURRING_SALE does not send card fields, you must store the `card` mask from the initial callback alongside the `recurring_token`.

### Callback

Same parameters as SALE callback. Hash verified with Formula 2.

---

## RETRY

[View in docs](https://www.centapay.com/docs#ref-retry)

Retry a soft-declined recurring transaction. Only for soft declines. Hard declines (stolen card, fraud) will not succeed.

**POST** `https://{PAYMENT_URL}/post`

| Parameter    | Format  | Required | Notes                       |
| ------------ | ------- | -------- | --------------------------- |
| `action`     | `RETRY` | Y        |                             |
| `client_key` | UUID    | Y        |                             |
| `trans_id`   | UUID    | Y        | Declined recurring trans_id |
| `hash`       | MD5 hex | Y        | Formula 1                   |

Sync: `result: ACCEPTED`. Final result via callback with `action: RETRY`.

---

## GET_TRANS_STATUS

[View in docs](https://www.centapay.com/docs#ref-get-status)

Query current status of a transaction.

**POST** `https://{PAYMENT_URL}/post`

| Parameter    | Format             | Required | Notes                                 |
| ------------ | ------------------ | -------- | ------------------------------------- |
| `action`     | `GET_TRANS_STATUS` | Y        |                                       |
| `client_key` | UUID               | Y        |                                       |
| `trans_id`   | UUID               | Y        |                                       |
| `hash`       | MD5 hex            | Y        | Formula 2 (Formula 6 for CREDIT2CARD) |

Response: `status` (3DS / REDIRECT / PENDING / PREPARE / DECLINED / SETTLED / REVERSAL / REFUND / VOID / CHARGEBACK), `decline_reason` if declined, `recurring_token`, `schedule_id`, `digital_wallet` if applicable.

---

## GET_TRANS_DETAILS

[View in docs](https://www.centapay.com/docs#ref-get-details)

Full order history including payer details and all sub-transactions.

**POST** `https://{PAYMENT_URL}/post`

| Parameter    | Format              | Required | Notes                                 |
| ------------ | ------------------- | -------- | ------------------------------------- |
| `action`     | `GET_TRANS_DETAILS` | Y        |                                       |
| `client_key` | UUID                | Y        |                                       |
| `trans_id`   | UUID                | Y        |                                       |
| `hash`       | MD5 hex             | Y        | Formula 2 (Formula 6 for CREDIT2CARD) |

Response: `name`, `mail`, `ip`, `amount`, `currency`, `card` (masked), `pan_type`, `digital_wallet`, `transactions[]` array. Each transaction entry: `date`, `type` (sale, 3ds, auth, capture, credit, chargeback, reversal, refund), `status`, `amount`.

---

## GET_TRANS_STATUS_BY_ORDER

[View in docs](https://www.centapay.com/docs#ref-get-by-order)

Look up most recent transaction status by your `order_id`.

**POST** `https://{PAYMENT_URL}/post`

| Parameter    | Format                      | Required | Notes     |
| ------------ | --------------------------- | -------- | --------- |
| `action`     | `GET_TRANS_STATUS_BY_ORDER` | Y        |           |
| `client_key` | UUID                        | Y        |           |
| `order_id`   | <=255 chars                 | Y        |           |
| `hash`       | MD5 hex                     | Y        | Formula 7 |

Returns same fields as GET_TRANS_STATUS. With cascading enabled, returns most recent transaction only.

---

## Schedule Operations

[View in docs](https://www.centapay.com/docs#ref-schedules)

All schedule actions use `POST https://{PAYMENT_URL}/post`.

### CREATE_SCHEDULE

| Parameter         | Format            | Required | Notes                                                                   |
| ----------------- | ----------------- | -------- | ----------------------------------------------------------------------- |
| `action`          | `CREATE_SCHEDULE` | Y        |                                                                         |
| `client_key`      | UUID              | Y        |                                                                         |
| `name`            | <=100 chars       | Y        | Schedule name                                                           |
| `interval_length` | Number >0         | Y        | e.g. 15 for every 15 days                                               |
| `interval_unit`   | `day` / `month`   | Y        |                                                                         |
| `day_of_month`    | 1-31              | N        | Only if `interval_unit=month`. 29/30/31 uses last day if month shorter. |
| `payments_count`  | Number            | Y        | Total payments                                                          |
| `delays`          | Number            | N        | Intervals to skip before starting                                       |
| `hash`            | MD5 hex           | Y        | Formula 3                                                               |

Response: `schedule_id`.

### PAUSE_SCHEDULE / RUN_SCHEDULE / DELETE_SCHEDULE / SCHEDULE_INFO

| Parameter     | Required | Notes                                                                   |
| ------------- | -------- | ----------------------------------------------------------------------- |
| `action`      | Y        | `PAUSE_SCHEDULE`, `RUN_SCHEDULE`, `DELETE_SCHEDULE`, or `SCHEDULE_INFO` |
| `client_key`  | Y        |                                                                         |
| `schedule_id` | Y        |                                                                         |
| `hash`        | Y        | Formula 4                                                               |

PAUSE_SCHEDULE can only be used when the schedule's `paused` parameter is `N`. A paused schedule cannot be used for new recurring payments until released via RUN_SCHEDULE.

SCHEDULE_INFO returns: `name`, `interval_length`, `interval_unit`, `day_of_month`, `payments_count`, `delays`, `paused` (Y/N).

### DESCHEDULE

| Parameter               | Required | Notes                    |
| ----------------------- | -------- | ------------------------ |
| `action` = `DESCHEDULE` | Y        |                          |
| `client_key`            | Y        |                          |
| `recurring_token`       | Y        | From initial transaction |
| `schedule_id`           | Y        |                          |
| `hash`                  | Y        | Formula 4                |

---

## CHARGEBACK

[View in docs](https://www.centapay.com/docs#ref-chargeback)

Callback-only. Not merchant-initiated. Sent when issuing bank initiates a chargeback.

| Parameter         | Description             |
| ----------------- | ----------------------- |
| `action`          | `CHARGEBACK`            |
| `result`          | `SUCCESS`               |
| `status`          | `CHARGEBACK`            |
| `order_id`        | Your order ID           |
| `trans_id`        | CentaPay transaction ID |
| `amount`          | Chargeback amount       |
| `chargeback_date` | System date             |
| `bank_date`       | Bank date               |
| `reason_code`     | Chargeback reason code  |
| `hash`            | Formula 2               |

---

## Digital Wallets (Apple Pay / Google Pay)

[View in docs](https://www.centapay.com/docs#payments-wallets)

Accept Apple Pay and Google Pay via S2S using wallet payment tokens. No card data touches your server. Use **Formula 8** for hash (email + PASSWORD only). Omit all card fields.

### Apple Pay

**Prerequisites:** Create Merchant ID in Apple Developer, register payment domains, create Merchant Identity Certificate and Processing Private Key. Configure in admin panel (Merchants > Wallets > Apple Pay).

**SALE request:** Include `digital_wallet=applepay` and `payment_token` (full Apple Pay token JSON).

### Google Pay

**Prerequisites:** Review Google Pay Web/Android docs, complete integration checklist, verify domains.

**Token parameters:**

- `allowPaymentMethods: CARD`
- `tokenizationSpecification: { "type": "PAYMENT_GATEWAY" }`
- `allowedCardNetworks: ['MASTERCARD', 'VISA', 'AMEX', 'DISCOVER', 'JCB']`
- `allowedCardAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS']`
- `gateway` = value from CentaPay account manager
- `gatewayMerchantId` = your `CLIENT_KEY`

**SALE request:** Include `digital_wallet=googlepay` and `payment_token`.

**Payment flow:** By default, wallet payments are _virtual_ (card details not stored, DMS/recurring limited). To enable _card flow_ (decrypt token, store for recurring): set up Processing Private Key in admin panel and verify provider support.

---

## Callbacks

[View in docs](https://www.centapay.com/docs#callbacks-delivery)

### Delivery Rules

- Callbacks sent as `application/x-www-form-urlencoded` via HTTP POST.
- Your endpoint must return the plain string `OK`. Any other content or timeout = failure.
- **Blocking:** 5 consecutive timeouts within 5 minutes blocks your callback URL for 15 minutes. All merchants sharing that URL are affected. Counter resets on any successful response. Unblock manually via admin panel (Configuration > Merchants > Edit Merchant).

### When Callbacks Are Sent

| Transaction Type                 | Callback On                       |
| -------------------------------- | --------------------------------- |
| SALE, CREDITVOID, RECURRING_SALE | SUCCESS, FAIL, WAITING, UNDEFINED |
| CAPTURE, VOID, CREDIT2CARD       | SUCCESS, FAIL, UNDEFINED          |
| CHARGEBACK                       | Always                            |

### Hash Verification

[View in docs](https://www.centapay.com/docs#callbacks-verification)

Always verify callback `hash` before processing.

- All actions except CREDIT2CARD: **Formula 2**
- CREDIT2CARD: **Formula 6**

**Important:** The payer's `email` is NOT included in callback parameters. You must store it from the original request. The `card` field in SUCCESS callbacks provides the masked PAN (first 6 + last 4 visible) for the hash. DECLINED callbacks do NOT include `card` - use the card mask stored from your original request instead.

```php
$data = $_POST;
// Use card from callback if present (SUCCESS), otherwise use stored mask (DECLINED)
$cardMask = isset($data['card']) ? $data['card'] : $storedCardMask;
$cardPart = substr($cardMask, 0, 6) . substr($cardMask, -4);
$expected = md5(strtoupper(
  strrev($storedEmail) . PASSWORD . $data['trans_id'] . strrev($cardPart)
));
if (!hash_equals($expected, $data['hash'])) {
    http_response_code(400);
    exit('ERROR');
}
if ($data['result'] === 'SUCCESS') {
    fulfillOrder($data['order_id'], $data['trans_id']);
}
echo 'OK';
```

```python
import hashlib, hmac
data = request.form.to_dict()
# Use card from callback if present (SUCCESS), otherwise use stored mask (DECLINED)
card_mask = data.get('card', stored_card_mask)
card_part = card_mask[:6] + card_mask[-4:]
raw = stored_email[::-1] + PASSWORD + data['trans_id'] + card_part[::-1]
expected = hashlib.md5(raw.upper().encode()).hexdigest()
if not hmac.compare_digest(expected, data.get('hash', '')):
    return 'ERROR', 400
if data['result'] == 'SUCCESS':
    fulfill_order(data['order_id'])
return 'OK'
```

```javascript
const rev = (s) => s.split('').reverse().join('')
app.post('/webhook', express.urlencoded({ extended: false }), (req, res) => {
  const d = req.body
  // Use card from callback if present (SUCCESS), otherwise use stored mask (DECLINED)
  const cardMask = d.card || storedCardMask
  const cp = cardMask.slice(0, 6) + cardMask.slice(-4)
  const raw = rev(storedEmail) + PASSWORD + d.trans_id + rev(cp)
  const exp = crypto.createHash('md5').update(raw.toUpperCase()).digest('hex')
  if (!crypto.timingSafeEqual(Buffer.from(exp), Buffer.from(d.hash || '')))
    return res.status(400).send('ERROR')
  if (d.result === 'SUCCESS') fulfillOrder(d.order_id)
  res.send('OK')
})
```

### Cascading Behaviour

[View in docs](https://www.centapay.com/docs#callbacks-cascading)

When cascading is enabled (auto-retry across MIDs on decline):

- **General case:** You receive only a callback for the **last payment attempt** with the final status.
- **Particular case:** If the payment provider requires a redirect (3DS), you also receive a callback for the **first attempt** with redirect data.
- Intermediate attempt callbacks are **not sent**.
- The `trans_id` in the first-attempt callback may differ from the `trans_id` in the final callback.
- Do not assume you will receive a first-attempt callback.

### Extended Callback Data

[View in docs](https://www.centapay.com/docs#callbacks-extended)

Additional fields available if configured in admin panel (Configuration > Protocol Mappings > "Add Extended Data to Callback"):

`connector_name`, `rrn`, `approval_code`, `gateway_id`, `extra_gateway_id`, `merchant_name`, `mid_name`, `issuer_country`, `issuer_bank`, `brand`, `arn`, `extended_data`.

---

## Tokenisation

[View in docs](https://www.centapay.com/docs#payments-tokenisation)

Add `req_token=Y` to SALE. Response/callback returns `card_token`. For subsequent charges, send `card_token` instead of card fields. Hash uses Formula 1 card_token variant.

---

## Test Cards

[View in docs](https://www.centapay.com/docs#testing-cards)

Sandbox only. No real funds moved. Use any 3-digit CVV.

### S2S CARD Scenarios

All use card number `4111111111111111`. Expiry date determines outcome:

| Expiry    | Scenario                                                     | Response                                          |
| --------- | ------------------------------------------------------------ | ------------------------------------------------- |
| `01/2038` | Successful SALE. Also only card returning `recurring_token`. | SUCCESS / SETTLED. AUTH: PENDING.                 |
| `02/2038` | Declined SALE / AUTH                                         | DECLINED                                          |
| `03/2038` | Successful AUTH, declined CAPTURE                            | AUTH: SUCCESS/PENDING. CAPTURE: DECLINED/PENDING. |
| `05/2038` | 3DS > Success                                                | REDIRECT/3DS > SUCCESS/SETTLED                    |
| `06/2038` | 3DS > Decline                                                | REDIRECT/3DS > DECLINED                           |
| `12/2038` | Redirect > Success                                           | REDIRECT/REDIRECT > SUCCESS/SETTLED               |
| `12/2039` | Redirect > Decline                                           | REDIRECT/REDIRECT > DECLINED                      |

### CREDIT2CARD Test Card

| Card Number        | Scenario          | Response          |
| ------------------ | ----------------- | ----------------- |
| `4601541833776519` | Successful payout | SUCCESS / SETTLED |

---

## Error Codes

[View in docs](https://www.centapay.com/docs#testing-errors)

Validation errors return:

```json
{
  "result": "ERROR",
  "error_message": "Description of the error",
  "error_code": 204002
}
```

For field-level validation failures (code `100000`), the response includes an `errors` array with per-field details:

```json
{
  "result": "ERROR",
  "error_code": 100000,
  "error_message": "Request data is invalid.",
  "errors": [
    {
      "error_code": 100000,
      "error_message": "card_number: This value should not be blank."
    },
    {
      "error_code": 100000,
      "error_message": "order_amount: This value should not be blank."
    },
    {
      "error_code": 100000,
      "error_message": "payer_email: This value should not be blank."
    }
  ]
}
```

| Code     | Description                                                            | Category      |
| -------- | ---------------------------------------------------------------------- | ------------- |
| `204002` | Enabled merchant mappings or MIDs not found                            | Configuration |
| `204003` | Payment type not supported                                             | Configuration |
| `204004` | Payment method not supported                                           | Configuration |
| `204005` | Payment action not supported                                           | Configuration |
| `204006` | Payment system/brand not supported                                     | Configuration |
| `204007` | Day MID limit not set or exceeded                                      | Limits        |
| `204008` | Day merchant mapping limit not set or exceeded                         | Limits        |
| `204009` | Payment type not found                                                 | Configuration |
| `204010` | Payment method not found                                               | Configuration |
| `204011` | Payment system/brand not found                                         | Configuration |
| `204012` | Payment currency not found                                             | Configuration |
| `204013` | Payment action not found                                               | Configuration |
| `204014` | Month MID limit exceeded                                               | Limits        |
| `204015` | Week merchant mapping limit exceeded                                   | Limits        |
| `208001` | Payment not found                                                      | Transaction   |
| `208002` | Cannot request 3DS for payment not in 3DS status                       | Transaction   |
| `208003` | Cannot capture payment not in PENDING status                           | Transaction   |
| `208004` | Capture amount exceeds auth amount                                     | Transaction   |
| `208005` | Cannot refund payment not in SETTLED or PENDING status                 | Transaction   |
| `208006` | Refund amount exceeds payment amount                                   | Transaction   |
| `208008` | Reversal amount exceeds payment amount                                 | Transaction   |
| `208009` | Partial reversal not allowed                                           | Transaction   |
| `208010` | Chargeback amount exceeds payment amount                               | Transaction   |
| `205005` | Card token invalid or not found                                        | Token         |
| `205006` | Card token expired                                                     | Token         |
| `205007` | Card token not accessible                                              | Token         |
| `400`    | Duplicate request                                                      | Validation    |
| `100000` | Request data is invalid. Check `errors[]` array for per-field details. | Validation    |

Decline reasons are returned in `decline_reason` as human-readable strings (e.g. "Insufficient funds", "Card expired", "Do not honor").

---

## Go-Live Checklist

[View in docs](https://www.centapay.com/docs#testing-golive)

### Core

- SALE success tested (card `4111111111111111`, exp `01/2038`)
- SALE decline tested (exp `02/2038`)
- 3DS redirect handled (exp `05/2038` success, `06/2038` decline)
- Callback handler returns `OK`, hash verified with Formula 2, tested all result types
- CREDITVOID tested (full and partial)
- VOID tested if used (same-day on SETTLED)
- GET_TRANS_STATUS tested as fallback
- Error codes handled gracefully
- Unique `order_id` per payment attempt
- Production IPs provided for whitelisting
- Production callback URL registered
- HTTPS on all endpoints

### If Using Recurring

- RECURRING_SALE tested (`recurring_init=Y` on initial, then token charge)
- RETRY tested (soft decline > retry > callback)
- Schedule operations tested if used

### If Using Payouts

- CREDIT2CARD tested (card `4601541833776519`)
- Formula 5 for request hash, Formula 6 for callback hash

### If Using Digital Wallets

- Apple Pay: Merchant ID, certificates, domains configured
- Google Pay: Integration checklist, domains verified
- Formula 8 hash used

---

_CentaPay Limited - info@centapay.com - [centapay.com](https://www.centapay.com)_
_Source documentation: [docs.centapay.com](https://docs.centapay.com)_
