# 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 (or 6 for CREDIT2CARD) | email + PASSWORD + order_id + card first6/last4 |
| CREATE_SCHEDULE | 3 | PASSWORD only |
| Other schedule ops | 4 | schedule_id + PASSWORD |
| Callback verification (all except CREDIT2CARD, VOID) | 2 | email + PASSWORD + trans_id + card first6/last4 |
| Callback verification (CREDIT2CARD only) | 6 | PASSWORD + trans_id + card first6/last4 |
| Callback verification (VOID) | TBC | Source references undefined "Void signature" - verify with Akurateco |

---

## 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:** `action: CREDITVOID`, `result: DECLINED`, `order_id`, `trans_id`, `decline_reason`, `hash` (Formula 2).

**Undefined:** `result: UNDEFINED`, `status: SETTLED`, `order_id`, `trans_id`, `creditvoid_date`, `amount`, `hash` (Formula 2).

---

## 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`, `hash`. Extended data fields if configured.

**Declined:** `action: VOID`, `result: DECLINED`, `status: SETTLED`, `order_id`, `trans_id`, `trans_date`, `decline_reason`, `hash`.

**Undefined:** `action: VOID`, `result: UNDEFINED`, `status: PENDING / SETTLED`, `order_id`, `trans_id`, `trans_date`, `hash`.

> **Hash note:** The source documentation references a "Void signature" for VOID callbacks that is not defined in the S2S CARD appendix. It may use a simplified formula: `md5(strtoupper(strrev(trans_id)) . PASSWORD)` rather than Formula 2. Verify with Akurateco before go-live.

**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_address2` | <=255 chars | N | |
| `payer_country` | 2-letter | N | |
| `payer_state` | <=32 chars | 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` or `currency`.

| Result | Status | Additional Fields |
|--------|--------|-------------------|
| SUCCESS | `SETTLED` | `descriptor` |
| 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` (Formula 6).

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_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`, `order_id`, `trans_id`.

### Callback Parameters

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

**Declined:** `action: RETRY`, `result: DECLINED`, `status: DECLINED`, `order_id`, `trans_id`, `amount`, `currency`, `decline_reason`, `hash` (Formula 2).

---

## 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, `arn`* if configured.

---

## 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), `decline_reason` if declined, `recurring_token`, `schedule_id`, `pan_type`, `digital_wallet`, `arn`* if configured, `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 (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. 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 |
| `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 |
| `hash` | Formula 2 |

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

---

## 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 and VOID: **Formula 2**
- CREDIT2CARD: **Formula 6**
- VOID: See hash note under VOID section (source references undefined "Void signature" - verify with Akurateco)

**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
}
```

| 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` | Previous payment not completed | 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)*
