Authenticate

Signing a Request

Quickpay Widget API uses merchant API keys and private keys to authenticate all API calls. Depending on your deployment environment, the base API URL will be one of the following:

  • Development Environment: https://sandbox-api.quickpay.best
  • Production Environment: https://api.quickpay.best

Every API request must contain the following headers:

  • Authorization - This value should be set to Bearer <JWT>. The access token is a JSON Web Token (JWT) signed with the merchant's private key.

JWT Structure

Authorization: Bearer <JWT>

The JWT payload field should contain the following fields:

  • uri - The URI part of the request (e.g., /merchants/profile).
  • nonce - Unique number or string. Each API request needs to have a different nonce.
  • iat - The time at which the JWT was issued, in seconds since Epoch.
  • exp - The expiration time on and after which the JWT must not be accepted for processing, in seconds since Epoch. (Must be less than iat+55sec.)
  • sub - The merchant API Key.
  • bodyHash - Hex-encoded SHA-256 hash of the raw HTTP request body.

The JWT must be signed with the merchant's private key and the RS256 (RSASSA-PKCS1-v1_5 using SHA-256 hash) algorithm.

Authentication Flow

  1. Merchant Registration: Merchants register in the system and receive API Key and private key
  2. Request Signing: Client uses merchant private key to sign the request
  3. Server Verification: Server verifies the signature using the merchant's public key
  4. Merchant Identification: Server identifies the merchant through the merchant_key field

Security Features

  • Replay Attack Prevention: Prevents request replay through nonce and bodyHash
  • Time Validation: Ensures request timeliness through iat and exp
  • Request Integrity: Ensures request body integrity through bodyHash
  • Merchant Isolation: Each merchant uses independent key pairs

Code Examples

quickpay-sdk-js

const jwt = require('jsonwebtoken');
const crypto = require('crypto');
const fs = require('fs');

// Merchant configuration
const apiKey = 'ac55d6fe-cc98-436c-a7f9-9c0e5f0873c6';
const privateKey = fs.readFileSync('./merchant_private_key.pem', 'utf8');

function createAuthToken(uri, body = '{}') {
    const now = Math.floor(Date.now() / 1000);
    const nonce = crypto.randomBytes(16).toString('hex');
    const bodyHash = crypto.createHash('sha256').update(body).digest('hex');
    
    const payload = {
        uri: uri,
        nonce: nonce,
        iat: now,
        exp: now + 55,
        sub: apiKey,
        bodyHash: bodyHash
    };
    
    return jwt.sign(payload, privateKey, { algorithm: 'RS256' });
}

// Usage example
const token = createAuthToken('/merchants/profile');
console.log(`Authorization: Bearer ${token}`);

quickpay-sdk-python

import jwt
import hashlib
import time
import uuid
from cryptography.hazmat.primitives import serialization

# Merchant configuration
api_key = 'ac55d6fe-cc98-436c-a7f9-9c0e5f0873c6'
with open('./merchant_private_key.pem', 'r') as f:
    private_key = f.read()

def create_auth_token(uri, body='{}'):
    now = int(time.time())
    nonce = str(uuid.uuid4())
    body_hash = hashlib.sha256(body.encode()).hexdigest()
    
    payload = {
        'uri': uri,
        'nonce': nonce,
        'iat': now,
        'exp': now + 55,
        'sub': api_key,
        'bodyHash': body_hash
    }
    
    return jwt.encode(payload, private_key, algorithm='RS256')

# Usage example
token = create_auth_token('/merchants/profile')
print(f"Authorization: Bearer {token}")

quickpay-sdk-php

<?php
require_once 'vendor/autoload.php';

use Firebase\JWT\JWT;

// Merchant configuration
$apiKey = 'ac55d6fe-cc98-436c-a7f9-9c0e5f0873c6';
$privateKey = file_get_contents('./merchant_private_key.pem');

function createAuthToken($uri, $body = '{}') {
    global $apiKey, $privateKey;
    
    $now = time();
    $nonce = bin2hex(random_bytes(16));
    $bodyHash = hash('sha256', $body);
    
    $payload = [
        'uri' => $uri,
        'nonce' => $nonce,
        'iat' => $now,
        'exp' => $now + 55,
        'sub' => $apiKey,
        'bodyHash' => $bodyHash
    ];
    
    return JWT::encode($payload, $privateKey, 'RS256');
}

// Usage example
$token = createAuthToken('/merchants/profile');
echo "Authorization: Bearer " . $token;
?>

Error Handling

Common authentication errors include:

  • 401 Unauthorized - Invalid JWT or signature verification failed
  • 401 Token Expired - JWT has expired
  • 401 Invalid Merchant - Merchant does not exist or is not active
  • 401 Body Hash Mismatch - Request body hash does not match

Best Practices

  1. Key Security: Safely store merchant private keys, do not hardcode in code
  2. Time Synchronization: Ensure client time is synchronized with server time
  3. Nonce Management: Ensure each request uses a unique nonce
  4. Error Retry: Implement appropriate error retry mechanisms
  5. Logging: Log authentication failures for debugging

Related Documentation