# Callback Notifications

After a **successful payment** in the Bank App, the platform sends a **server-to-server** notification to your **Callback URL**. The payload contains the payment outcome and attributes required for fulfillment and reconciliation, along with a **signature** you must verify.

***

### When it is sent

* **Trigger:** after successful payment execution.
* **Primary signal:** use the callback as the **source of truth** for order fulfillment.
* If a callback is delayed or rejected by your server, you may cross-check with `GET /v2/rtp/{id}`.

> A notification is considered **successfully received** only if your server responds with **HTTP 200 OK**.

***

### Notification structure

**Content-Type:** `application/json`

```json
{
  "result": {
    "rtpId": "string (GUID)",
    "rtpStatus": "Accepted",
    "orderId": "string",
    "payId": "string (GUID)",
    "amount": 0,
    "commission": 0,
    "currency": "MDL",
    "payerName": "string",
    "payerIban": "string",
    "executedAt": "YYYY-MM-DDThh:mm:ss±hh:mm"
  },
  "signature": "string (Base64)"
}
```

#### Fields

| Field               | Type                     | Description                                          |
| ------------------- | ------------------------ | ---------------------------------------------------- |
| `result`            | object                   | Wrapper for notification data.                       |
| `result.rtpId`      | string (GUID)            | RTP unique identifier.                               |
| `result.rtpStatus`  | enum                     | Notification status. Possible values: **Accepted**.  |
| `result.orderId`    | string                   | Merchant order identifier (if provided at create).   |
| `result.payId`      | string (GUID)            | Unique **payment** identifier.                       |
| `result.amount`     | number (decimal)         | Payment amount.                                      |
| `result.commission` | number (decimal)         | Payment commission.                                  |
| `result.currency`   | string (ISO 4217)        | Payment currency. Possible values: `MDL`.            |
| `result.payerName`  | string                   | Payer abbreviated name (e.g., “John D.”).            |
| `result.payerIban`  | string                   | Payer IBAN.                                          |
| `result.executedAt` | string (ISO 8601-1:2019) | Payment execution timestamp.                         |
| `signature`         | string (Base64)          | Validation signature for integrity and authenticity. |

***

### Example notification

```json
{
  "result": {
    "rtpId": "123e4567-e89b-12d3-a456-426614174000",
    "rtpStatus": "Accepted",
    "orderId": "123",
    "payId": "c56a4180-65aa-42ec-a945-5fd21dec0538",
    "amount": 100.00,
    "commission": 1.00,
    "currency": "MDL",
    "payerName": "John D.",
    "payerIban": "MD24AG000225100014156789",
    "executedAt": "2029-10-22T10:32:28+03:00"
  },
  "signature": "r4KwwIUXQGHhcEM7C4um8o9rSrGEriTRcYQuBbmjEec="
}
```

***

### Signature validation

Merchants receive a **Signature Key** (in maibmerchants project settings). Validate every callback as follows:

1. Take all fields from the **`result`** object (do **not** include the top-level `signature`).
2. **Exclude** any field that is `null` or an empty string.
3. Format **`amount`** and **`commission`** with **exactly two decimals** (e.g., `0.50`, `2.31`).
4. **Sort** the remaining field names **alphabetically (case-insensitive)**.
5. **Concatenate** the corresponding field values using `:` as a separator, in the sorted order.
6. **Append** `:` and the **Signature Key** to the end of the string.
7. Compute **SHA-256** over the resulting string (binary).
8. Encode the hash with **Base64** → this must equal the incoming `signature`.

> If the calculated signature does not match the received `signature`, do **not** fulfill the order and respond with a **non-200** status to allow redelivery.

***

{% hint style="info" %}
**Handling recommendations**

* **Verify the signature first**, then update your order/payment state.
* Make the handler **idempotent**: repeated notifications with the same `payId` must not duplicate fulfillment.
* Log `rtpId`, `payId`, `rtpStatus`, `executedAt`, and the verification outcome.
* Respond with **HTTP 200 OK** only after successful processing. If verification fails or processing errors occur, return a **non-200** status.
  {% endhint %}
