Skip to content

1. Introduction

Welcome to our BNPL integration process. This guide provides comprehensive instructions for merchants to integrate with QPay after signing the agreement.

Important: Before starting, ensure you have received your merchant agreement confirmation email.

2. Integration Process Overview

  1. Agreement Signing: Merchant signs agreement and requests integration.
  2. Documentation: We share this guide and sample certificates.
  3. Certificate Generation: Merchant generates and shares certificates following our security standards.
  4. Environment Setup: We register certificates in UAT and Production environments.
  5. Access Provision: We provide x-merchant-id, verified phone number, service ID, API documentation, Postman collection, and environment URLs.

Note: The entire process typically takes 1-3 business days after certificate submission.

3. Certificate Generation

Generate your security certificates using the following OpenSSL commands:

Terminal
# Step 1: Generate Private Key (2048-bit RSA)
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048

# Step 2: Generate Certificate Signing Request
openssl req -new -key private_key.pem -out request.csr \
  -subj "/C=US/ST=State/L=City/O=YourCompany/CN=yourdomain.com"

# Step 3: Generate Public Certificate (Valid for 2 years)
openssl x509 -req -in request.csr -signkey private_key.pem -out public_cert.pem \
  -days 730 -sha256

Certificate Requirements

  • 2048-bit RSA minimum
  • SHA-256 hashing algorithm
  • 2-year validity period
  • Proper subject fields (Country, Organization, CN)

Important Notes

  • Keep private key secure
  • Do not regenerate unless compromised
  • Notify us before certificate expiration
  • Test certificates in UAT first

4. After Certificate Submission

After we receive your certificates, you'll receive the following within 2 business days:

Merchant Credentials

  • x-merchant-id (e.g. "qpay")
  • Verified mobile number for OTP
  • Merchant Service ID

Development Resources

  • Postman Collection
  • Sample Code in 5 languages
  • Mock API endpoints

Environment Setup

  • UAT access confirmation
  • Production onboarding
  • IP whitelisting instructions

Pro Tip: Begin testing in our UAT environment immediately after receiving credentials. Production access requires additional verification.

5. API Documentation & Endpoints

Download our comprehensive API documentation:

Download API Docs (PDF)
API Description UAT Production
List Plans Retrieve available payment plans /bnpl/mw/list-plans /bnpl/mw/list-plans
List Cards Get customer's saved payment methods /bnpl/mw/list-card /bnpl/mw/list-card
Generate OTP Initiate OTP for transaction /bnpl/mw/generate-otp /bnpl/mw/generate-otp
Confirm Plan Finalize payment plan selection /bnpl/mw/confirm-plan /bnpl/mw/confirm-plan

Base URLs

UAT Environment:

https://bnpl-api-uat.fintec.solutions

Production Environment:

https://bnpl-api.fintec.solutions

6. Request Headers & Authentication

All API requests must include the following headers:

HTTP Headers
Content-Type: application/json
x-Merchant-ID: <your-unique-merchant-identifier>
token: <base64-rsa-sha256-signature>

Header Requirements

  • x-Merchant-ID provided after registration
  • token is regenerated per request (RSA–SHA256, Base64)
  • All headers are case-sensitive

Request Timing

  • Token valid for 5 minutes
  • Clock skew tolerance: ±2 minutes
  • Rate limit: 30 requests/minute
  • Timeout: 30 seconds

7. Signature Generation

Generate request signatures using your private key with the following exact implementation:

Required Headers

Content-Type: application/json
x-Merchant-ID: <your-merchant-id-from-config>
token: <generated-signature>
PHP Implementation
/**
 * Generates a base64-encoded signature for API requests
 * @param array $value Request payload
 * @return string Base64-encoded signature
 * @throws Exception On signing failure
 */
public function sign(array $value): string {
    try {
        $privateKey = $this->loadMerchantPrivateKey();

        // Step 1: Convert JSON to UTF-8 bytes
        $utf8Bytes = json_encode($value, JSON_UNESCAPED_UNICODE);

        // Step 2: Sign using RSA private key with SHA256
        $signature = '';
        $success = openssl_sign($utf8Bytes, $signature, $privateKey, OPENSSL_ALGO_SHA256);

        if (!$success) {
            $error = openssl_error_string();
            throw new Exception('Failed to sign hash: ' . $error);
        }

        openssl_free_key($privateKey);

        // Step 3: Encode result into Base64 string
        $base64Signature = base64_encode($signature);

        Log::info('QPay Security Service - Data signed successfully', [
            'algorithm' => OPENSSL_ALGO_SHA256,
            'data_length' => strlen($utf8Bytes),
            'signature_length' => strlen($signature),
            'base64_length' => strlen($base64Signature)
        ]);

        return $base64Signature;
    } catch (Exception $e) {
        Log::error('QPay Security Service - Sign failed', [
            'error' => $e->getMessage()
        ]);
        throw new Exception('Failed to sign data: ' . $e->getMessage());
    }
}

Request Example (JSON)

Basic
{
  "senderInfo": {
    "lang": 1,
    "sender": "00968********",
    "senderType": "M",
    "msgId": "uniqueMSG",
    "deviceId": "454"
  },
  "amount": "amount"
}

Sign the exact JSON string (same order/whitespace) to avoid signature mismatches.

Request Example with Hashed OTP

Hashed OTP

OTP: 123456 → SHA-256 (Base64): jZae727K08KaOmKSgOaGzww/XVqGr/PKEgIMkjrcbJI=

{
  "senderInfo": {
    "lang": 1,
    "sender": "00968********",
    "senderType": "M",
    "deviceId": "454",
    "msgId": "uniqueMSGid",
    "otp": "jZae727K08KaOmKSgOaGzww/XVqGr/PKEgIMkjrcbJI="
  }
}

Hash the plaintext OTP with SHA-256 and encode in Base64. Use the same string for signing and sending.

Expected Signature (Sample)

j0CZl8+Sj9mDZgTkQZ+MzKNF1o7u4rwiXouxmsYXbUKwYWfWdI/xkiLQkuCO+jWXs+TmgNX1dtulVPvMTJxT5AI0b9vqJAr9iDMYNjv4N1XQPVQvSyTe50eczcdNmLan/6vy371gc32Q+5ERgAI41Is29VKDlyEIkkjZV8lDp6MLrACDo8/N8nvoVdHQpTY0EEbRyMw/X9ri+5JKVu2JejNMeIEewcapsflCcpOFitBiITPXiltRjP4fpRtwajgKIC1vo3nvYaC2xe6Y0Mx+X4WBPpV7t+aJm4NQdMADVaFjuB82BNMV53vNnB28e5vFgbczPkM7swnDfiRxtyf1Uw==

Sample only. Your signature changes with the exact JSON and private key.

8. Testing & Go-Live Checklist

Download our comprehensive Postman Collection:

Postman Collection (JSON)

Quick Signing & Hashing (Base64)

Use these browser tools for ad-hoc validation (for production, sign server-side using your private key):

  1. Prepare your JSON request (see “Request Example” above) as a single string without extra spaces/line breaks.
  2. Open RSA Signing Tool, choose algorithm RSASSA-PKCS1-v1_5 with SHA-256, paste your private key (PEM) locally, paste the JSON string as the message, and ensure the output is Base64.
  3. Copy the Base64 signature and set it as the token header. Ensure x-Merchant-ID is set correctly.
  4. (Optional) Use the SHA-256 Hashing Tool to compute the digest of your JSON string to compare against server-side logs.

cURL Example

curl -X POST "https://bnpl-api-uat.fintec.solutions/bnpl/mw/confirm-plan" \
  -H "Content-Type: application/json" \
  -H "x-Merchant-ID: <your-merchant-id>" \
  -H "token: <base64-rsa-sha256-signature>" \
  -d '{"senderInfo":{"lang":1,"sender":"00968********","senderType":"M","msgId":"uniqueMSG","deviceId":"454"},"amount":"1.000"}'

Replace placeholders with your real values. Keep JSON exactly the same when generating the signature and when sending the request.

Integration Testing Steps

  1. UAT Environment Setup: Configure your system with UAT credentials
  2. Basic Connectivity: Test authentication and token generation
  3. API Validation: Verify all endpoints with sample data
  4. Error Handling: Test with invalid inputs and error conditions
  5. End-to-End Flow: Complete full transaction lifecycle
  6. Performance Testing: Verify under expected load

Go-Live Requirements

Mandatory Items

  • Successful UAT testing report
  • Production certificates submitted
  • IP addresses whitelisted
  • Fallback mechanism implemented

Recommended Checks

  • Load testing completed
  • Monitoring configured
  • Team training conducted
  • Support contacts verified

Common Pitfalls: wrong x-Merchant-ID value, JSON reformatting after signing, expired token, or clock drift > 2 minutes.

9. Error Codes & Troubleshooting

Below is a subset of common errors. Refer to the full API document for the complete list.

Code HTTP Message How to Resolve
INVALID_SIGNATURE 401 Signature verification failed Sign the exact JSON; ensure Base64 output; use the correct private key; check clock skew.
MISSING_HEADER 400 Required header is missing Include x-Merchant-ID, Content-Type, and token headers.
TOKEN_EXPIRED 401 Token is older than allowed window Re-generate signature within 5 minutes of the request.
REQUEST_VALIDATION 400 Payload failed validation Fix field formats/required fields; avoid conflicting parameters.
RATE_LIMIT_EXCEEDED 429 Too many requests Throttle to ≤ 30 requests/minute.
UNAUTHORIZED_MERCHANT 403 Merchant not allowed for this operation Check merchant status, IP whitelist, and environment (UAT vs PROD).
OTP_INVALID 400 Invalid/expired OTP Request a new OTP and retry within the valid time window.
PLAN_NOT_AVAILABLE 409 Selected plan no longer available Re-query List Plans and re-select.
46 (Message Is Duplicated) Message Is Duplicated Ensure msgId is unique for every request (e.g., UUIDv4 or a timestamp+nonce). Do not reuse the same msgId across requests.
49 (Invalid Credentials) Invalid Credentials Verify x-Merchant-ID, regenerate token (RSA-SHA256, Base64) over the exact JSON, check key/cert pairing and environment (UAT vs PROD), and confirm token is within 5 minutes.

Provider Error Payload Examples

Duplicate msgId used in more than one request:

{
  "responseInfo": {
    "errorCd": "46",
    "desc": "Message Is Duplicated",
    "statusCode": "3"
  }
}

Problem with signing or x-Merchant-ID:

{
  "responseInfo": {
    "errorCd": "49",
    "desc": "Invalid Credentials",
    "statusCode": "3"
  }
}


10. Support & Contact Information

Technical Support

  • Email Support

    alwarith.s@fintec.solutions

    mohammed.alajmi@fintec.solutions

    Response time: 2 business hours

  • Emergency Phone

    +968 2416 2616

Integration Assistance

  • Office Hours

    Sunday-Thursday, 9AM-5PM GST

    Dedicated integration support

Support Guidelines

  • Always include your x-merchant-id in communications
  • For API issues, provide request/response samples
  • Business inquiries should use separate channels
Scroll Up