# 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.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.maibmerchants.md/mia-qr-api/en/notifications-on-callback-url.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
