Skip to content
logo
  • Products
    • Amwal Checkout
    • Merchant App
    • Merchant Control Panel
  • Pricing
  • Developers
  • About us
  • Contact Us
Edit Content
  • Products
    • Amwal Checkout
    • Merchant App
    • Merchant Control Panel
  • Pricing
  • Developers
  • About us
  • Contact Us
Login
Get Started
Login
Get Started
BA Booking

BA Booking

2
  • BA Booking overview
  • BA Booking Installation
amwalpay woocommerce

Woocommerce

2
  • Overview
  • Woocommerce Installation
CS-Cart

CS Cart

2
  • CS-Cart Overview
  • CS CART Installation
Shopify

Shopify

2
  • Shopify Overview
  • Shopify Installation
odoo

Odoo

2
  • Odoo Overview
  • Odoo Installation
whmcs

WHMCS

2
  • WHMCS Overview
  • WHMCS Installation
Magento

Magento

2
  • Magento 2 Overview
  • Magento Installation
Opencart

Opencart

2
  • OpenCart Overview
  • Opencart Installtion
ZenCart

ZenCart

2
  • ZenCart Overview
  • Zencart Installation
Drupal

Drupal

2
  • Drupal Overview
  • Drupal Installation
PrestaShop

PrestaShop

2
  • PrestaShop Overview
  • PrestaShop Installation
Contact Form 7

Contact Form 7

2
  • Contact Form 7 Overview
  • Contact Form 7 Installation
Joomla

Joomla

2
  • Joomla Overview
  • Joomla Installation
AMWAL INTEGRATED PAYMENT LINK

Integrated Payment Link

1
  • Implementation
Android SDk

Native Android SDK

2
  • Pre Requisites
  • Implementation
ios SDk

Native iOS SDK

3
  • Apple Pay Specific Configuration
  • Pre Requisites
  • Implementation
React SDk

React Native SDK

1
  • Implementation

Laravel Package

2
  • Installation
  • Configuration

Flutter SDK

2
  • Implementation
  • Flutter SDK Overview

SMARTBOX

5
  • Express ApplePay Implementation
  • Offsite Implementation
  • Pre Requisites
  • Implementation
  • Acquiring Session Token

Merchant Cloud Notification

1
  • Merchant Cloud Notification Integration Guide

Secure Hash Calculation

1
  • Secure Hash Calculation

Wp Travel Engine

1
  • Installation

Webhooks

8
  • Encryption and Decryption
  • Refund Payment
  • Void Payment
  • Get Transaction by ID
  • Transactions with Statistics
  • Transactions Summary
  • Delete Customer Token
  • Pay by Token
View Categories
  • Home
  • Amwal Pay Developer Portal
  • Webhooks
  • Encryption and Decryption

Encryption and Decryption

1. Overview #

AMWAL Pay uses JSON Web Encryption (JWE) to protect webhook request payloads and provide an additional layer of security.

JWE encryption is applied in addition to:

  • TLS for secure transport.
  • HMAC-based payload integrity validation using secureHashValue.
  • Merchant-specific encryption keys managed through the Merchant Portal.

The encrypted request body must use JWE Compact Serialization.


2. Cryptographic Contract #

The following cryptographic parameters must be used exactly as specified.

ParameterValue
SerializationJWE Compact Serialization
Key Management (alg)dir — Direct Encryption
Content Encryption (enc)A128CBC-HS256
Key ID (kid)Merchant Key ID (UUID)
Content Encryption Key (CEK)256-bit / 32-byte key
Payload IntegritysecureHashValue
Transport SecurityTLS

JWE Protected Header #

The JWE protected header must contain the encryption algorithm, encryption method, and Merchant Key ID.

{
  "alg": "dir",
  "enc": "A128CBC-HS256",
  "kid": "put_kid_value_here"
}

Header Parameters #

ParameterDescriptionExample
algKey management algorithmdir
encContent encryption algorithmA128CBC-HS256
kidMerchant encryption key identifier550e8400-e29b-41d4-a716-446655440000

Important: The kid value must match the Merchant Key ID configured for the merchant in the AMWAL Pay Merchant Portal.


3. Encryption Flow #

The merchant should encrypt every applicable request using the following sequence:

Prepare JSON Payload
       ↓
Calculate secureHashValue
       ↓
Add secureHashValue to JSON
       ↓
Serialize JSON
       ↓
Encrypt JSON using JWE
       ↓
Include kid in JWE Protected Header
       ↓
Send JWE Compact Serialization
       ↓
AMWAL Pay decrypts and validates the request

Step 1 — Prepare the Request #

Create the normal JSON request payload according to the API specification.

Example:

{
  "merchantId": 74417,
  "transactionId": "7d8fcf50-f075-11ed-b792-9d1241b63248",
  "amount": 100,
  "currencyCode": "512"
}

Step 2 — Generate secureHashValue #

Generate the secureHashValue according to the applicable AMWAL Pay API specification.

The secure hash must be generated before encryption.

The resulting payload should contain the secure hash:

{
  "merchantId": 74417,
  "transactionId": "7d8fcf50-f075-11ed-b792-9d1241b63248",
  "amount": 100,
  "currencyCode": "512",
  "secureHashValue": "84EB3BF8F62EF25717D1E9E13C3CFB719A890980BBF2631AFD8965182ADE1754"
}

Step 3 — Encrypt the Complete JSON Payload #

The complete JSON string, including secureHashValue, must be encrypted using:

alg = dir
enc = A128CBC-HS256
kid = Merchant Key ID

Step 4 — Send the Encrypted Request #

The resulting JWE Compact Serialization string is sent as the HTTP request body.

The request body is therefore not plain JSON when JWE encryption is enabled.


4. JWE Compact Serialization #

A JWE Compact Serialization value consists of five Base64URL-encoded components separated by periods:

Protected Header
.
Encrypted Key
.
Initialization Vector
.
Ciphertext
.
Authentication Tag

For direct encryption (dir), the encrypted key component is empty.

Example structure:

eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoicHV0X2tpZF92YWx1ZV9oZXJlIn0
.
.
<initialization-vector>
<ciphertext>
<authentication-tag>

5. Key Requirements #

The encryption key is generated and managed through the Merchant Keys section of the AMWAL Pay Merchant Portal.

The merchant secret is provided as a Base64-encoded value.

Key Normalization #

After Base64 decoding:

  • A 32-byte key can be used directly.
  • If the decoded material is not 32 bytes, it must be normalized using SHA-256 to produce a 32-byte key.
Base64 Secret
     ↓
Base64 Decode
     ↓
32 bytes?
   ↙     ↘
 Yes      No
 ↓         ↓
Use      SHA-256
          ↓
       32 bytes

For A128CBC-HS256, the resulting 32-byte key is used as the Content Encryption Key.


6. Programming Example #

The following example demonstrates the JWE encryption and decryption process.

PHP Example #

<?php

class AmwalPayCrypto
{
    /**
     * Encrypt JWE
     *
     * alg = dir
     * enc = A128CBC-HS256
     */
    public static function encryptJWE($payload, $secret, $kid)
    {
        try {
            $plaintext = is_array($payload)
                ? json_encode($payload, JSON_UNESCAPED_SLASHES)
                : $payload;

            $key = base64_decode($secret, true);

            if ($key === false) {
                throw new Exception('Invalid base64 secret');
            }

            // Normalize key to 32 bytes
            if (strlen($key) !== 32) {
                $key = hash('sha256', $key, true);
            }

            // Split the 32-byte CEK
            $macKey = substr($key, 0, 16);
            $encKey = substr($key, 16, 16);

            // JWE Protected Header
            $header = [
                'alg' => 'dir',
                'enc' => 'A128CBC-HS256',
                'kid' => $kid
            ];

            $encodedHeader = self::base64urlEncode(
                json_encode($header)
            );

            // Generate random IV
            $iv = random_bytes(16);

            // Encrypt using AES-128-CBC
            $ciphertext = openssl_encrypt(
                $plaintext,
                'AES-128-CBC',
                $encKey,
                OPENSSL_RAW_DATA,
                $iv
            );

            if ($ciphertext === false) {
                throw new Exception('Encryption failed');
            }

            // Additional Authenticated Data
            $aad = $encodedHeader;

            // Length of AAD in bits
            $al = pack('N2', 0, strlen($aad) * 8);

            // Authentication input
            $macInput = $aad . $iv . $ciphertext . $al;

            // Generate authentication tag
            $fullTag = hash_hmac(
                'sha256',
                $macInput,
                $macKey,
                true
            );

            $tag = substr($fullTag, 0, 16);

            // Build JWE Compact Serialization
            return implode('.', [
                $encodedHeader,
                '',
                self::base64urlEncode($iv),
                self::base64urlEncode($ciphertext),
                self::base64urlEncode($tag)
            ]);

        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }

    /**
     * Decrypt JWE
     */
    public static function decryptJWE($jwe, $secret)
    {
        try {
            if (!is_string($jwe)) {
                throw new Exception('JWE must be a string');
            }

            $parts = explode('.', trim($jwe));

            if (count($parts) !== 5) {
                throw new Exception('Invalid JWE format');
            }

            list(
                $header,
                $encryptedKey,
                $iv,
                $ciphertext,
                $tag
            ) = $parts;

            $iv = self::base64urlDecode($iv);
            $ciphertext = self::base64urlDecode($ciphertext);
            $tag = self::base64urlDecode($tag);

            $key = base64_decode($secret, true);

            if ($key === false) {
                throw new Exception('Invalid base64 secret');
            }

            // Normalize key to 32 bytes
            if (strlen($key) !== 32) {
                $key = hash('sha256', $key, true);
            }

            // Split CEK
            $macKey = substr($key, 0, 16);
            $encKey = substr($key, 16, 16);

            // Verify authentication tag
            $aad = $header;
            $al = pack('N2', 0, strlen($aad) * 8);

            $macInput = $aad . $iv . $ciphertext . $al;

            $calculatedTag = substr(
                hash_hmac(
                    'sha256',
                    $macInput,
                    $macKey,
                    true
                ),
                0,
                16
            );

            if (!hash_equals($calculatedTag, $tag)) {
                throw new Exception('Authentication failed');
            }

            // Decrypt payload
            $plaintext = openssl_decrypt(
                $ciphertext,
                'AES-128-CBC',
                $encKey,
                OPENSSL_RAW_DATA,
                $iv
            );

            if ($plaintext === false) {
                throw new Exception('Decryption failed');
            }

            return json_decode($plaintext, true);

        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }

    /**
     * Base64URL Encode
     */
    public static function base64urlEncode($data)
    {
        return rtrim(
            strtr(
                base64_encode($data),
                '+/',
                '-_'
            ),
            '='
        );
    }

    /**
     * Base64URL Decode
     */
    public static function base64urlDecode($data)
    {
        $remainder = strlen($data) % 4;

        if ($remainder) {
            $data .= str_repeat(
                '=',
                4 - $remainder
            );
        }

        return base64_decode(
            strtr($data, '-_', '+/')
        );
    }
}

C# (.NET) – using jose-jwt #

public static string EncryptPayload(string json, string secretBase64, Guid kid) { 
 var material = Convert.FromBase64String(secretBase64.Trim()); 
 var cek = material.Length == 32 ? material : SHA256.HashData(material); 
 var extraHeaders = new Dictionary<string, object> { ["kid"] = kid.ToString() }; 
 return JWT.Encode(json, cek, JweAlgorithm.DIR, JweEncryption.A128CBC_HS256, extraHeaders: extraHeaders); 
}

Node.js (TypeScript) – using jose #

export async function encryptBody(plaintext: string, secretBase64: 
string, kid: string) { 
const material = Buffer.from(secretBase64, 'base64'); 
const cek = material.length === 32 ? material : 
crypto.createHash('sha256').update(material).digest(); 
return await new CompactEncrypt(new TextEncoder().encode(plaintext)) 
.setProtectedHeader({ alg: 'dir', enc: 'A128CBC-HS256', kid }) 
.encrypt(new Uint8Array(cek)); 
}

7. Decryption and Response Handling #

When receiving an encrypted JWE payload from AMWAL Pay:

  1. Receive the JWE Compact Serialization body.
  2. Parse the five JWE components.
  3. Read the protected JWE header.
  4. Identify the kid.
  5. Retrieve the corresponding merchant encryption key.
  6. Normalize the key to 32 bytes.
  7. Validate the authentication tag.
  8. Decrypt the ciphertext.
  9. Parse the resulting JSON payload.
  10. Validate the secureHashValue.

Decrypted Payload #

After successful decryption, the payload returns to its original JSON representation:

{
  "merchantId": 74417,
  "transactionId": "7d8fcf50-f075-11ed-b792-9d1241b63248",
  "amount": 100,
  "currencyCode": "512",
  "secureHashValue": "84EB3BF8F62EF25717D1E9E13C3CFB719A890980BBF2631AFD8965182ADE1754"
}

8. Error Handling #

When JWE encryption is enabled, AMWAL Pay validates both the encryption layer and the payload integrity.

HTTP CodeErrorDescription
400WebhookEncryptedBodyRequiredEncryption is enabled, but the request body is not provided in JWE format.
401WebhookEncryptionKeyMerchantMismatchThe kid supplied in the JWE header does not match the merchant’s configured encryption key.
401InvalidHashingJWE decryption succeeded, but the secureHashValue is invalid.

Recommended Error Handling #

Applications should:

  • Log the HTTP status and API error response.
  • Do not log the encryption secret.
  • Do not log decrypted sensitive payment data unnecessarily.
  • Verify the configured kid.
  • Verify that the correct merchant secret is being used.
  • Recalculate and validate secureHashValue.
  • Retry only when the failure is known to be transient.

9. Security Requirements #

The following requirements must be followed for production integrations:

Merchant Key #

Use the encryption key generated specifically for the merchant.

kid #

The kid in the JWE protected header must correspond to the active Merchant Key ID.

Secret Protection #

The secret key must never be:

  • Exposed in frontend code.
  • Included in client-side JavaScript.
  • Committed to source control.
  • Written to application logs.
  • Returned in API responses.

HTTPS #

All communication with AMWAL Pay must use HTTPS/TLS.

Secure Hash #

The secureHashValue must be generated before JWE encryption.

Full Payload Encryption #

The complete JSON payload, including secureHashValue, must be encrypted.


10. Operational Checklist #

Before sending a request, verify:

  • JWE Compact Serialization is being used.
  • alg is set to dir.
  • enc is set to A128CBC-HS256.
  • kid matches the Merchant Key ID.
  • The merchant secret is Base64 decoded correctly.
  • The resulting encryption key is normalized to 32 bytes.
  • secureHashValue is calculated before encryption.
  • The secureHashValue is included inside the encrypted JSON payload.
  • The complete JSON payload is encrypted.
  • The JWE contains five compact-serialization components.
  • Requests are sent over HTTPS.
  • The application can decrypt JWE responses when applicable.
  • Encryption secrets are stored securely and are not logged.

11. Obtaining a Merchant Encryption Key #

Merchant encryption keys are managed through the Merchant Keys section of the AMWAL Pay Merchant Portal.

Step 1 — Open Merchant Keys #

From the Merchant Portal, navigate to:

Merchant Keys → Add Key

The Merchant Keys page provides the option to create and manage encryption keys.

Step 2 — Add a New Encryption Key #

Select Add Key.

A dialog will appear allowing you to enter or generate the encryption secret.

The portal provides a Generate option for creating key material.

Step 3 — Generate the Secret #

Select Generate to generate a Base64 key suitable for use as the merchant encryption secret.

The generated secret must be treated as confidential.

Step 4 — Save the Key #

Select Add to save the encryption key.

The newly created key will appear under Saved Keys.

The saved key contains:

FieldDescription
IDInternal key identifier
KID (JWE)Merchant Key ID used in the JWE kid header
SecretMerchant encryption secret
ActiveIndicates whether the key is active
ActionsAvailable key management operations

12. Using the Generated Key #

Once the encryption key has been created:

  1. Copy the Merchant Key ID (kid).
  2. Securely store the generated secret.
  3. Configure the secret in the server-side application.
  4. Include the kid in every JWE protected header.
  5. Use the corresponding secret to encrypt the request payload.

Example:

{
  "alg": "dir",
  "enc": "A128CBC-HS256",
  "kid": "YOUR_MERCHANT_KEY_ID"
}

Important: The kid and secret work together. Always use the secret associated with the kid specified in the JWE header.


13. End-to-End Request Structure #

The complete integration can be represented as:

Merchant Application
       │
       │ 1. Prepare API JSON
       ▼
┌──────────────────────────┐
│ JSON Request             │
│ + secureHashValue        │
└────────────┬─────────────┘
             │
             │ 2. JWE Encryption
             │
             │ alg = dir
             │ enc = A128CBC-HS256
             │ kid = Merchant Key ID
             ▼
┌──────────────────────────┐
│ JWE Compact Serialization│
└────────────┬─────────────┘
             │
             │ 3. HTTPS POST
             ▼
┌──────────────────────────┐
│ AMWAL Pay                │
│                          │
│ Decrypt JWE              │
│ Validate kid              │
│ Validate secureHashValue │
│ Process API request      │
└────────────┬─────────────┘
             │
             │ 4. Response
             ▼
┌──────────────────────────┐
│ Merchant Application     │
│                          │
│ Decrypt if encrypted     │
│ Validate response        │
└──────────────────────────┘
Updated on August 10, 2026

What are your Feelings

  • Happy
  • Normal
  • Sad

Share This Article :

  • Facebook
  • X
  • LinkedIn
  • Pinterest
Refund Payment
Table of Contents
  • 1. Overview
  • 2. Cryptographic Contract
    • JWE Protected Header
    • Header Parameters
  • 3. Encryption Flow
    • Step 1 — Prepare the Request
    • Step 2 — Generate secureHashValue
    • Step 3 — Encrypt the Complete JSON Payload
    • Step 4 — Send the Encrypted Request
  • 4. JWE Compact Serialization
  • 5. Key Requirements
    • Key Normalization
  • 6. Programming Example
    • PHP Example
    • C# (.NET) - using jose-jwt
    • Node.js (TypeScript) – using jose
  • 7. Decryption and Response Handling
    • Decrypted Payload
  • 8. Error Handling
    • Recommended Error Handling
  • 9. Security Requirements
    • Merchant Key
    • kid
    • Secret Protection
    • HTTPS
    • Secure Hash
    • Full Payload Encryption
  • 10. Operational Checklist
  • 11. Obtaining a Merchant Encryption Key
    • Step 1 — Open Merchant Keys
    • Step 2 — Add a New Encryption Key
    • Step 3 — Generate the Secret
    • Step 4 — Save the Key
  • 12. Using the Generated Key
  • 13. End-to-End Request Structure

Secure. Seamless – Powering Payments for Every Business.

Sign Up
Support

4th Floor, Majan Tower Building
North Al Ghubrah, P.O. Box 233, P.C 118
Muscat, Sultanate of Oman

: support@amwal-pay.com

📞 : +96824121845

Resources
  • Developers
  • Careers
Company
  • About us
  • Contact Us
  • Contact Sales
  • Partners

2026 © AmwalPay. All Rights Reserved.

  • Terms & Conditions
  • Privacy Policy