> For the complete documentation index, see [llms.txt](https://docs.maibmerchants.md/mia-qr-api/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.maibmerchants.md/mia-qr-api/en/notifications-on-callback-url.md).

# Notifications on Callback URL

The merchant will receive notifications on the Callback URL with the final payment response, which contains the transaction status and relevant details (e.g., `qrStatus`, `payId`, `amount`, etc).

Based on these notifications, the merchant must ensure the delivery of the service or product to the buyer.

A notification is considered successfully received if the merchant’s server responds with HTTP status code **200 OK**.

***

#### Notification structure (Example)

<pre class="language-json"><code class="lang-json"><strong>{
</strong>    "result": {
        "qrId": "789e0123-f456-7890-a123-456789012345",
        "extensionId": "40e6ba44-7dff-48cc-91ec-386a38318c68",
        "qrStatus": "Paid",
        "payId": "123e4567-e89b-12d3-a456-426614174000",
        "referenceId": "QR000123456789",
        "orderId": "789e0123-e89b-45d6-b789-426614174111",
        "amount": 100.50,
        "commission": 2.50,
        "currency": "MDL",
        "payerName": "John D.",
        "payerIban": "MD24AG000225100013104168",
        "executedAt": "2029-10-22T10:32:28+03:00",
        "terminalId": "P011111"
    },
    "signature": "5wHkZvm9lFeXxSeFF0ui2CnAp7pCEFSNmuHYFYJlC0s="
}
</code></pre>

***

**Notification parameters**

| Parameter          | Type             | Description                                                                                                         |
| ------------------ | ---------------- | ------------------------------------------------------------------------------------------------------------------- |
| result             | object           | Response result object.                                                                                             |
| result.qrId        | string(guid)     | QR unique identifier.                                                                                               |
| result.extensionId | string(guid)     | QR extension identifier.                                                                                            |
| result.qrStatus    | string(enum)     | <p>QR status.</p><p>Possible values: Active, Paid.</p>                                                              |
| result.payId       | string(guid)     | Payment unique identifier.                                                                                          |
| result.referenceId | string(15)       | Instant payments servicer RRN.                                                                                      |
| result.orderId     | string(100)      | Merchant-side order identifier.                                                                                     |
| result.amount      | number(decimal)  | Payment amount.                                                                                                     |
| result.commission  | number(decimal)  | Payment commission.                                                                                                 |
| result.currency    | string(enum)     | <p>Payment currency.</p><p>Possible values: MDL.</p><p>Format: ISO 4217.</p>                                        |
| result.payerName   | string(200)      | <p>Payer abbreviated name.</p><p>Eg: "<em>John D.</em>"</p>                                                         |
| result.payerIban   | string(200)      | Payer IBAN (International Bank Account Number).                                                                     |
| result.executedAt  | string(datetime) | <p>Timestamp of the payment execution.</p><p>Format: ISO 8601-1:2019.</p><p>(e.g., "2029-10-22T10:32:28+03:00")</p> |
| result.terminalId  | string(100)      | Terminal id, provided by bank                                                                                       |
| signature          | string           | Notification validation signature                                                                                   |

***

#### Signature validation

To verify the integrity and authenticity of the received data, the Merchant must validate the `signature` field from the response body using the following algorithm:

1. All fields in the `result` object shall be sorted in alphabetical order, excluding the `signature` field. Sorting must be **case-insensitive**.
2. **Ignore fields** that have a `null` value or an empty string (`""`). These fields should be **excluded entirely** from the signature generation process, as if they do not exist.
3. **Format amount fields** (`amount`, `commission`) using exactly **two decimal places** (e.g., `0.50`, `2.31`) before concatenation.
4. **Concatenate** the remaining parameter values using a **colon (`:`)** as a separator, in the sorted order.
5. **Append** the **Signature Key** (available in the project settings in **maibmerchants**) to the end of the concatenated string.
6. **Generate** a **SHA-256 hash** in binary format from the resulting string.
7. **Encode** the binary hash using **Base64** (or another format as specified in the official QR MIA documentation).
8. **Compare** the resulting encoded signature with the `signature` value received in the notification.

***

#### Signature validation example

```php
<?php
$key = "signature-key-from-project-settings"; // Signature Key obtained
// Get the JSON content received on the Callback URL
$json = file_get_contents('php://input');
$data = json_decode($json, true);

if (isset($data['result']['signature'])) {
    $signatureReceived = $data['result']['signature'];
    $dataResult = $data['result'];

    // Remove the signature from the data to be validated
    unset($dataResult['signature']);

    // Alphabetically sort the fields in the result object
    uksort($dataResult, 'strcasecmp');

    // Recursive function to concatenate values with ':' separator
    function implodeRecursive($separator, $array) {
        $result = '';
        foreach ($array as $item) {
            if (is_array($item)) {
                $result .= implodeRecursive($separator, $item) . $separator;
            } else {
                $result .= (string)$item . $separator;
            }
        }
        return substr($result, 0, -1); // remove the last separator
    }

    // Build the signature string
    $signString = implodeRecursive(':', $dataResult) . ':' . $key;

    // Generate binary SHA256 hash
    $hash = hash('sha256', $signString, true);

    // Encode the hash in Base64
    $signatureCalculated = base64_encode($hash);

    // Compare the calculated signature with the received one
    if ($signatureCalculated === $signatureReceived) {
        http_response_code(200);
        echo "Signature is valid.";
        // Process the transaction data here
    } else {
        http_response_code(400);
        echo "Signature is invalid.";
    }
} else {
    http_response_code(400);
    echo "No signature provided.";
}
```

***

**Recommendations**

* Ensure that your server is accessible from maib IPs to receive the notifications.
* Respond with HTTP status 200 OK only after successfully verifying the signature.
* In case of errors or invalid signature, respond with a status code different from 200 to force the notification to be resent.
