# Payment notifications

## Payment Notification

<mark style="color:green;">`POST`</mark>` ``/subscribe`

### Request header

*The request sent to the callback URL will have the following headers*

**key: `irembopay-signature`**

**value: `t=<timestamp>, s=<signature>`**

### Request payload

The payload section represents the request's payload that is sent to the callback URL

**Signature**

```javascript
<signature> = HMAC_SHA256(<Secret_Key>, <Payload_To_Hash>)
```

where:

* HMAC\_SHA256 is a function to Compute an HMAC with the SHA256 hash function.
* `<Secret_Key>` is the merchant secret key configured in the portal.
* `<Payload_To_Hash>` is the concatenation of the timestamp, the character “#” and the request body.

**Example**

```javascript
irembopay-signature: t=1653405045000,s=bfecb20753326e5e8602f4a6e727bcd22b7cb1d00797fe5bd65db8cfaf2f4903        
```

#### Verifying the signature <a href="#verifying-the-signature" id="verifying-the-signature"></a>

Merchants can follow the steps below to verify a signature:

**Step 1:** Extract the timestamp and signatures from the header `irembopay-signature`. Split the header using the `,` character as the separator, to get a list of elements. Then split each element, using the `=` character as the separator, to get a prefix and value pair. The value for the prefix `t` corresponds to the timestamp, and `s` corresponds to the signature

**Step 2:** Prepare the `signed_payload` string. The `signed_payload` string is created by concatenating: The timestamp (as a string), the character `#` and the actual JSON payload (the request body).

**Step 3:** Determine the expected signature. Compute an **HMAC** with the **SHA256** hash function. Use the merchant’s secret key as the key and use the `signed_payload` string as the message.

**Step 4:** Compare the signatures. Compare the signature in the header to the expected signature. Optionally, you can also check if the timestamp is not too far from the current time to prevent replay attacks.

### Request samples

`POST: /subscribe`

{% tabs %}
{% tab title="Payload" %}

```json
{
  "success": true,
  "data": {
    "amount": 100,
    "invoiceNumber": "880519183280",
    "transactionId": "B221024053141FNNX",
    "createdAt": "2023-04-19T11:58:02.895+02",
    "updatedAt": "2023-04-19T11:58:02.895+02",
    "paidAt": "2023-04-20T11:58:02.895+02",
    "expiryAt": "2023-04-21T11:58:02.895+02",
    "paymentMethod": "MOMO_PUSH",
    "paymentReference": "0fMRp7R5KotFDrg4VAO6spSao5CMfj",
    "customer": {
      "email": "example@gmail.com",
      "phoneNumber": "0780000001",
      "name": "Jixle Manzi"
    },
    "paymentItems": [
      {
        "unitAmount": 2000,
        "quantity": 1,
        "code": "PI-3e5fe23f2d"
      }
    ],
    "paymentAccountIdentifier": "TST-RWF",
    "paymentStatus": "PAID",
    "currency": "RWF"
  }
}
```

{% endtab %}

{% tab title="Javascript" %}

```javascript
const crypto = require('crypto');

function verifySignature(secretKey, payload, signatureHeader) {
    // Step 1: Extract the timestamp and signatures from the header
    const elements = signatureHeader.split(',');
    let timestamp = null;
    let signatureHash = null;

    elements.forEach(element => {
        const [prefix, value] = element.split('=');
        if (prefix === 't') {
            timestamp = value;
        } else if (prefix === 's') {
            signatureHash = value;
        }
    });

    if (!timestamp || !signatureHash) {
        return false;
    }

    // Step 2: Prepare the signed_payload string
    const signedPayload = `${timestamp}#${payload}`;

    // Step 3: Determine the expected signature
    const expectedSignature = crypto.createHmac('sha256', secretKey)
        .update(signedPayload)
        .digest('hex');

    // Step 4: Compare the signatures
    const isSignatureValid = crypto.timingSafeEqual(
        Buffer.from(expectedSignature, 'hex'),
        Buffer.from(signatureHash, 'hex')
    );

    // Optionally check if the timestamp is not too far from the current time
    const currentTime = Date.now(); // Current time in milliseconds
    const timestampInt = parseInt(timestamp, 10);

    if (Math.abs(currentTime - timestampInt) > 300 * 1000) { // 5 minutes tolerance in milliseconds
        return false;
    }

    return isSignatureValid;
}

// Example usage
const headerValue = "t=1653405045000,s=bfecb20753326e5e8602f4a6e727bcd22b7cb1d00797fe5bd65db8cfaf2f4903";
const secretKey = "your_secret_key_here";
const requestBody = '{"key": "value"}';

const isValid = verifySignature(secretKey, requestBody,headerValue);
console.log("Signature is valid:", isValid);

```

{% endtab %}

{% tab title="Python" %}

```python
import hmac
import hashlib
import time

def verify_signature(secret_key, payload, signature_header):
    # Step 1: Extract the timestamp and signatures from the header
    elements = signature_header.split(',')
    timestamp = None
    signature_hash = None
    
    for element in elements:
        prefix, value = element.split('=')
        if prefix == 't':
            timestamp = value
        elif prefix == 's':
            signature_hash = value
    
    if timestamp is None or signature_hash is None:
        return False

    # Step 2: Prepare the signed_payload string
    signed_payload = f'{timestamp}#{payload}'
    
    # Step 3: Determine the expected signature
    expected_signature = hmac.new(
        key=secret_key.encode(),
        msg=signed_payload.encode(),
        digestmod=hashlib.sha256
    ).hexdigest()
    
    # Step 4: Compare the signatures
    is_signature_valid = hmac.compare_digest(expected_signature, signature_hash)
    
    # Optionally check if the timestamp is not too far from the current time
    current_time = int(time.time() * 1000)  # Convert current time to milliseconds
    timestamp_int = int(timestamp)

    if abs(current_time - timestamp_int) > 300 * 1000:  # 5 minutes tolerance in milliseconds
        return False
    
    return is_signature_valid

# Example usage
secret_key = 'secret_key'
payload = 'request_payload'
signature_header = 'signature_header'

is_valid = verify_signature(secret_key, payload, signature_header)
print(f'Signature valid: {is_valid}')

```

{% endtab %}

{% tab title="Java" %}

```java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

public class SignatureVerifier {

    public static boolean verifySignature(String secretKey, String payload, String signatureHeader) {
        // Step 1: Extract the timestamp and signatures from the header
        String[] elements = signatureHeader.split(",");
        String timestamp = null;
        String signatureHash = null;

        for (String element : elements) {
            String[] parts = element.split("=");
            String prefix = parts[0];
            String value = parts[1];
            if (prefix.equals("t")) {
                timestamp = value;
            } else if (prefix.equals("s")) {
                signatureHash = value;
            }
        }

        if (timestamp == null || signatureHash == null) {
            return false;
        }

        // Step 2: Prepare the signed_payload string
        String signedPayload = timestamp + "#" + payload;

        // Step 3: Determine the expected signature
        String expectedSignature = null;
        try {
            Mac mac = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
            mac.init(secretKeySpec);
            byte[] rawHmac = mac.doFinal(signedPayload.getBytes(StandardCharsets.UTF_8));
            expectedSignature = bytesToHex(rawHmac);
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            e.printStackTrace();
            return false;
        }

        // Step 4: Compare the signatures
        boolean isSignatureValid = expectedSignature.equals(signatureHash);

        // Optionally check if the timestamp is not too far from the current time
        long currentTime = System.currentTimeMillis(); // Current time in milliseconds
        long timestampInt = Long.parseLong(timestamp);

        if (Math.abs(currentTime - timestampInt) > 300 * 1000) { // 5 minutes tolerance in milliseconds
            return false;
        }

        return isSignatureValid;
    }

    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder(bytes.length * 2);
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }

    // Example usage
    public static void main(String[] args) {
        String secretKey = "your_secret_key_here";
        String payload = "{\"key\": \"value\"}";
        String signatureHeader = "signature_header_here";

        boolean isValid = verifySignature(secretKey, payload, signatureHeader);
        System.out.println("Signature valid: " + isValid);
    }
}

```

{% endtab %}
{% endtabs %}


---

# 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://irembopay.gitbook.io/irembopay-api-docs/apis/payment/payment-notifications.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.
