Overview
The Modulus Labs QR API uses JWE (JSON Web Encryption) tokens to encrypt request and response payloads. This ensures that sensitive payment data is protected and maintains integrity throughout the entire transaction lifecycle.What is JWE? JSON Web Encryption is a standard for encrypting data in a compact, URL-safe format. It ensures that the payload cannot be read or modified during transit.
Why JWE Encryption?
Data Protection
Sensitive payment information is encrypted end-to-end
Integrity Verification
Any tampering with the token will cause verification to fail
Compliance
Meets security standards for financial transactions
Standardized
Uses industry-standard encryption algorithms
Encryption Specifications
The QR API requires specific encryption algorithms for JWE tokens:| Parameter | Value | Description |
|---|---|---|
| Content Encryption | A256CBC-HS512 | AES-256-CBC with HMAC SHA-512 |
| Key Wrap Algorithm | A256KW | AES-256 Key Wrap |
| Content Type | application/json | JSON payload format |
Your Encryption Key
Along with your Secret Key, Modulus Labs will provide you with an Encryption Key for JWE operations:6eaNv7t5xyCuiBfGCq2q7wu9uHEH3mTQ1Mq0c53XO1c53vfG
Store your Encryption Key securely! Never commit it to version control or expose it in client-side code.
Recommended Libraries
Use these well-tested libraries for creating and decrypting JWE tokens:- Node.js
- .NET
- PHP
Creating Request Payloads
Here’s how to encrypt your request data into a JWE token:Step 1: Prepare Your Data
First, construct the JSON payload with your transaction details:{
"activationCode": "A9X4-B7P2-Q6Z8-M3L5",
"currency": "PHP",
"amount": "98.00",
"merchantReferenceNumber": "5e91bc6c-f7e2-4a39-ae0e-5e93985c94a4"
}
Step 2: Encrypt into JWE Token
const jose = require('jose');
// Your encryption key from Modulus Labs
const encryptionKey = '6eaNv7t5xyCuiBfGCq2q7wu9uHEH3mTQ1Mq0c53XO1c53vfG';
// Convert the key to the proper format
const secretKey = Buffer.from(encryptionKey, 'base64');
// Your payment data
const payload = {
activationCode: 'A9X4-B7P2-Q6Z8-M3L5',
currency: 'PHP',
amount: '98.00',
merchantReferenceNumber: '5e91bc6c-f7e2-4a39-ae0e-5e93985c94a4'
};
// Create the JWE token
const jwe = await new jose.CompactEncrypt(
new TextEncoder().encode(JSON.stringify(payload))
)
.setProtectedHeader({ alg: 'A256KW', enc: 'A256CBC-HS512' })
.encrypt(secretKey);
console.log('JWE Token:', jwe);
// Result: eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0...
from jose import jwe
import json
# Your encryption key from Modulus Labs
encryption_key = '6eaNv7t5xyCuiBfGCq2q7wu9uHEH3mTQ1Mq0c53XO1c53vfG'
# Your payment data
payload = {
'activationCode': 'A9X4-B7P2-Q6Z8-M3L5',
'currency': 'PHP',
'amount': '98.00',
'merchantReferenceNumber': '5e91bc6c-f7e2-4a39-ae0e-5e93985c94a4'
}
# Create the JWE token
jwe_token = jwe.encrypt(
json.dumps(payload),
encryption_key,
algorithm='A256KW',
encryption='A256CBC-HS512'
)
print('JWE Token:', jwe_token)
<?php
use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Core\JWK;
use Jose\Component\Encryption\Algorithm\KeyEncryption\A256KW;
use Jose\Component\Encryption\Algorithm\ContentEncryption\A256CBCHS512;
use Jose\Component\Encryption\Compression\CompressionMethodManager;
use Jose\Component\Encryption\JWEBuilder;
use Jose\Component\Encryption\Serializer\CompactSerializer;
// Your encryption key from Modulus Labs
$encryptionKey = '6eaNv7t5xyCuiBfGCq2q7wu9uHEH3mTQ1Mq0c53XO1c53vfG';
// Create JWK from your key
$jwk = new JWK([
'kty' => 'oct',
'k' => $encryptionKey
]);
// Algorithm managers
$keyEncryptionAlgorithmManager = new AlgorithmManager([
new A256KW(),
]);
$contentEncryptionAlgorithmManager = new AlgorithmManager([
new A256CBCHS512(),
]);
// Your payment data
$payload = json_encode([
'activationCode' => 'A9X4-B7P2-Q6Z8-M3L5',
'currency' => 'PHP',
'amount' => '98.00',
'merchantReferenceNumber' => '5e91bc6c-f7e2-4a39-ae0e-5e93985c94a4'
]);
// Build the JWE
$jweBuilder = new JWEBuilder(
$keyEncryptionAlgorithmManager,
$contentEncryptionAlgorithmManager,
new CompressionMethodManager()
);
$jwe = $jweBuilder
->create()
->withPayload($payload)
->withSharedProtectedHeader([
'alg' => 'A256KW',
'enc' => 'A256CBC-HS512',
])
->addRecipient($jwk)
->build();
// Serialize to compact format
$serializer = new CompactSerializer();
$token = $serializer->serialize($jwe, 0);
echo 'JWE Token: ' . $token;
using Jose;
// Your encryption key from Modulus Labs
var encryptionKey = "6eaNv7t5xyCuiBfGCq2q7wu9uHEH3mTQ1Mq0c53XO1c53vfG";
var key = Base64UrlDecode(encryptionKey);
// Base64URL decoding helper
static byte[] Base64UrlDecode(string base64Url)
{
var base64 = base64Url.Replace('-', '+').Replace('_', '/');
switch (base64.Length % 4)
{
case 2: base64 += "=="; break;
case 3: base64 += "="; break;
}
return Convert.FromBase64String(base64);
}
// Your payment data
var payload = new
{
activationCode = "A9X4-B7P2-Q6Z8-M3L5",
currency = "PHP",
amount = "98.00",
merchantReferenceNumber = "5e91bc6c-f7e2-4a39-ae0e-5e93985c94a4"
};
// Create the JWE token
var jweToken = JWT.Encode(
payload,
key,
JweAlgorithm.A256KW,
JweEncryption.A256CBC_HS512
);
Console.WriteLine($"JWE Token: {jweToken}");
# Note: JWE encryption requires a library/tool
# Use one of the language examples below to encrypt your payload,
# then use the encrypted token with cURL
ENCRYPTED_TOKEN="eyJhbGciOiJBMjU2S1ciL..."
curl -X POST https://qrph.sbx.moduluslabs.io/v1/pay/qr \
-u sk_YOUR_SECRET_KEY: \
-H "Content-Type: application/json" \
-d "{\"Token\": \"$ENCRYPTED_TOKEN\"}"
package main
import (
"encoding/base64"
"encoding/json"
"fmt"
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jwe"
)
func main() {
// Your encryption key from Modulus Labs
encryptionKey := "6eaNv7t5xyCuiBfGCq2q7wu9uHEH3mTQ1Mq0c53XO1c53vfG"
// Decode key from Base64URL
keyBytes, _ := base64.RawURLEncoding.DecodeString(encryptionKey)
// Your payment data
payload, _ := json.Marshal(map[string]string{
"activationCode": "A9X4-B7P2-Q6Z8-M3L5",
"currency": "PHP",
"amount": "98.00",
"merchantReferenceNumber": "5e91bc6c-f7e2-4a39-ae0e-5e93985c94a4",
})
// Create the JWE token
encrypted, _ := jwe.Encrypt(payload,
jwe.WithKey(jwa.A256KW, keyBytes),
jwe.WithContentEncryption(jwa.A256CBC_HS512),
)
fmt.Println("JWE Token:", string(encrypted))
}
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.AESEncrypter;
import com.nimbusds.jose.jwk.OctetSequenceKey;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.*;
public class EncryptExample {
public static void main(String[] args) throws Exception {
// Your encryption key from Modulus Labs
String encryptionKey = "6eaNv7t5xyCuiBfGCq2q7wu9uHEH3mTQ1Mq0c53XO1c53vfG";
// Decode key from Base64URL
byte[] keyBytes = Base64.getUrlDecoder().decode(encryptionKey);
OctetSequenceKey jwk = new OctetSequenceKey.Builder(keyBytes).build();
// Your payment data
Map<String, String> payload = new LinkedHashMap<>();
payload.put("activationCode", "A9X4-B7P2-Q6Z8-M3L5");
payload.put("currency", "PHP");
payload.put("amount", "98.00");
payload.put("merchantReferenceNumber", "5e91bc6c-f7e2-4a39-ae0e-5e93985c94a4");
String payloadJson = new ObjectMapper().writeValueAsString(payload);
// Create the JWE token
JWEHeader header = new JWEHeader(JWEAlgorithm.A256KW, EncryptionMethod.A256CBC_HS512);
JWEObject jwe = new JWEObject(header, new Payload(payloadJson));
jwe.encrypt(new AESEncrypter(jwk));
System.out.println("JWE Token: " + jwe.serialize());
}
}
Step 3: Wrap in Request Payload
Send the JWE token in your API request:{
"Token": "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0.sOoEpD55U9NMZ4JQwgFMsHGGVH0ZJvsc7Gn7LXwc8mwrFk36ordNAwJDU23S1xGQtK7ZJno3hHnvm3rhJPcXTw27ry-Lz0Gi.LDd-bawV4AhjUi9hXttE5Q.TUxl65r-0HnOBQj01ggZDMKT8pH2FDJEAKIQp_SfWXDNYZWizgZD5fmtdNxbPgj37WHFVVAvYlfWcAHYvbH-Q.p4drK620DM3qpkdVjw-xaB473Hu3hQFWpoDn_WEwYdw"
}
Decrypting Response Payloads
The API returns JWE tokens in successful responses. Here’s how to decrypt them:Example Response
{
"Token": "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0.htNErE1kLWIP0dwXcozgxLsS1bpeDIa1Qwvqdsx3MpUmyuag3ebonJbh87NrKgDjMOzzCiDwX_AefkTJtRCReYJ1rvrWmgo.ERm2rmdwPJhUncoLL6ofDA.6zqtciKiDbmx_s2AY_bRrXmedr1jafiD2HZVAxS-pWEcZo8VnpovL6epILjfTNdrITogtvCekeoZMhIuDTN6yqOlcqfl2SsblwZF5NfTMKdTUcqF8Sdx3Cg7XzT4UgLhQxd1l441E3dGNvp4coOyGkAfT5_2yVN3uGXVuz9sU.kkhQrE6cPmkOdJqZPJBa4Xphi-u2xG6y-ETcReT203Q"
}
Decrypt the Token
const jose = require('jose');
// Your encryption key
const encryptionKey = '6eaNv7t5xyCuiBfGCq2q7wu9uHEH3mTQ1Mq0c53XO1c53vfG';
const secretKey = Buffer.from(encryptionKey, 'base64');
// The JWE token from the response
const jweToken = response.Token;
// Decrypt the token
const { plaintext } = await jose.compactDecrypt(jweToken, secretKey);
// Parse the payload
const payload = JSON.parse(new TextDecoder().decode(plaintext));
console.log('Decrypted payload:', payload);
// {
// "id": "0ce32626-08cf-405c-adab-6d384a8871da",
// "qrBody": "LAawP8GKxWCwWA1gf4MVisVgsBrA+wIvFYrFYDGB9gBeLxWKxGMD6AC8Wi8ViMYD1AV4sFovFYgDrA7xYLBaLxQDWB3ixWCwWiwGsD/BisVgsFgNYH+DFYrFYLAawPsCLxWKxWAxgfYAX8VisRjA+gAvFovFYjGA9QFeLBaLxf/fXh0LAAAAAAzytx7GnpKIgYABYCBgABgIGAAGAgaAgYABYCBgABgIGAAGAgaAgYABYBC6Qa/HfkoA6gAAAABJRU5ErkJggg=="
// }
from jose import jwe
import json
# Your encryption key
encryption_key = '6eaNv7t5xyCuiBfGCq2q7wu9uHEH3mTQ1Mq0c53XO1c53vfG'
# The JWE token from the response
jwe_token = response['Token']
# Decrypt the token
decrypted_data = jwe.decrypt(jwe_token, encryption_key)
# Parse the payload
payload = json.loads(decrypted_data)
print('Decrypted payload:', payload)
<?php
// Decrypt using the same setup as encryption
$decrypted = $jweLoader->loadAndDecryptWithKey(
$jweToken,
$jwk,
$recipient
);
// Get the payload
$payload = $decrypted->getPayload();
$data = json_decode($payload, true);
print_r($data);
using Jose;
// Your encryption key
var encryptionKey = "6eaNv7t5xyCuiBfGCq2q7wu9uHEH3mTQ1Mq0c53XO1c53vfG";
var key = Base64UrlDecode(encryptionKey);
// The JWE token from the response
var jweToken = response.Token;
// Decrypt the token
var decryptedJson = JWT.Decode(jweToken, key);
var payload = JsonConvert.DeserializeObject<dynamic>(decryptedJson);
Console.WriteLine($"Transaction ID: {payload.id}");
Console.WriteLine($"QR Body: {payload.qrBody}");
# Decryption requires a library/tool
# Use one of the language examples to decrypt the JWE response token
# The token from the response body:
echo "Use Node.js, Python, PHP, Go, Java, or C# to decrypt the response Token"
package main
import (
"encoding/base64"
"encoding/json"
"fmt"
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jwe"
)
func main() {
encryptionKey := "6eaNv7t5xyCuiBfGCq2q7wu9uHEH3mTQ1Mq0c53XO1c53vfG"
keyBytes, _ := base64.RawURLEncoding.DecodeString(encryptionKey)
// The JWE token from the response
jweToken := "eyJhbGciOiJBMjU2S1ciL..." // response.Token
// Decrypt the token
decrypted, _ := jwe.Decrypt([]byte(jweToken), jwe.WithKey(jwa.A256KW, keyBytes))
// Parse the payload
var payload map[string]interface{}
json.Unmarshal(decrypted, &payload)
fmt.Printf("Transaction ID: %s\n", payload["id"])
fmt.Printf("QR Body: %s\n", payload["qrBody"])
}
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.AESDecrypter;
import com.nimbusds.jose.jwk.OctetSequenceKey;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.*;
public class DecryptExample {
public static void main(String[] args) throws Exception {
String encryptionKey = "6eaNv7t5xyCuiBfGCq2q7wu9uHEH3mTQ1Mq0c53XO1c53vfG";
byte[] keyBytes = Base64.getUrlDecoder().decode(encryptionKey);
OctetSequenceKey jwk = new OctetSequenceKey.Builder(keyBytes).build();
// The JWE token from the response
String jweToken = "eyJhbGciOiJBMjU2S1ciL..."; // response.getToken()
// Decrypt the token
JWEObject jweObject = JWEObject.parse(jweToken);
jweObject.decrypt(new AESDecrypter(jwk));
String decryptedJson = jweObject.getPayload().toString();
Map<String, String> payload = new ObjectMapper().readValue(decryptedJson, Map.class);
System.out.println("Transaction ID: " + payload.get("id"));
System.out.println("QR Body: " + payload.get("qrBody"));
}
}
Response Types
The API returns different response formats based on the request status:- Success
- Declined
- Unauthorized
Successful Requests
Returns a JWE token containing the response data:{
"Token": "eyJhbGciOiJBMjU2S1ciL..."
}
{
"id": "0ce32626-08cf-405c-adab-6d384a8871da",
"qrBody": "LAawP8GKxWCwWA1gf4MVisVgs..."
}
Declined Requests
Returns a JWE token containing the error object:{
"Token": "eyJhbGciOiJBMjU2S1ciL..."
}
{
"code": "10000002",
"error": "Account is disabled. Please contact support for assistance.",
"referenceNumber": "338e2710-8268-4afe-8ef9-9765b0b74688"
}
Unauthorized Requests
Returns the error object directly (not encrypted):{
"code": "10000002",
"error": "Account is disabled. Please contact support for assistance.",
"referenceNumber": "338e2710-8268-4afe-8ef9-9765b0b74688"
}
Authentication errors bypass encryption since the credentials are invalid.
Common Issues & Solutions
Invalid JWE Token Error
Invalid JWE Token Error
Cause: Mismatch in encryption algorithms or incorrect keySolution:
- Verify you’re using
A256KWfor key wrap - Verify you’re using
A256CBC-HS512for content encryption - Check that your encryption key is correct
- Ensure the key is Base64-decoded to raw bytes
Decryption Fails
Decryption Fails
Cause: Token has been modified or key mismatchSolution:
- Ensure the token hasn’t been modified during transit
- Verify you’re using the same encryption key for encryption and decryption
- Check that the token is complete (not truncated)
Library Compatibility Issues
Library Compatibility Issues
Cause: Not all JWT libraries support JWESolution:
- Use the recommended libraries listed above
- Ensure your library supports both
A256KWandA256CBC-HS512 - Update to the latest version of your chosen library
Base64 Encoding Issues
Base64 Encoding Issues
Cause: Incorrect encoding of binary dataSolution:
- Use URL-safe Base64 encoding (no padding)
- Ensure consistent encoding between encryption and decryption
- Use library-provided encoding functions
Security Best Practices
Key Storage
Store encryption keys in secure vaults or environment variables, never in code
Key Rotation
Implement a key rotation strategy for enhanced security
Validate Tokens
Always validate token structure and algorithms before decryption
Monitor Failures
Log and monitor decryption failures for potential security issues
Complete Example
Here’s a complete end-to-end example:const jose = require('jose');
const axios = require('axios');
const SECRET_KEY = process.env.MODULUS_SECRET_KEY;
const ENCRYPTION_KEY = process.env.MODULUS_ENCRYPTION_KEY;
async function createDynamicQR() {
// 1. Prepare payload
const payload = {
activationCode: 'A9X4-B7P2-Q6Z8-M3L5',
currency: 'PHP',
amount: '98.00',
merchantReferenceNumber: crypto.randomUUID()
};
// 2. Encrypt payload
const secretKey = Buffer.from(ENCRYPTION_KEY, 'base64');
const jweToken = await new jose.CompactEncrypt(
new TextEncoder().encode(JSON.stringify(payload))
)
.setProtectedHeader({ alg: 'A256KW', enc: 'A256CBC-HS512' })
.encrypt(secretKey);
// 3. Make API request
const response = await axios.post(
'https://qrph.sbx.moduluslabs.io/v1/pay/qr',
{ Token: jweToken },
{
auth: { username: SECRET_KEY, password: '' },
headers: { 'Content-Type': 'application/json' }
}
);
// 4. Decrypt response
const { plaintext } = await jose.compactDecrypt(
response.data.Token,
secretKey
);
const result = JSON.parse(new TextDecoder().decode(plaintext));
console.log('Transaction ID:', result.id);
console.log('QR Body:', result.qrBody);
return result;
}
createDynamicQR().catch(console.error);
import os
import json
import requests
from jose import jwe
from uuid import uuid4
SECRET_KEY = os.getenv('MODULUS_SECRET_KEY')
ENCRYPTION_KEY = os.getenv('MODULUS_ENCRYPTION_KEY')
def create_dynamic_qr():
# 1. Prepare payload
payload = {
'activationCode': 'A9X4-B7P2-Q6Z8-M3L5',
'currency': 'PHP',
'amount': '98.00',
'merchantReferenceNumber': str(uuid4())
}
# 2. Encrypt payload
jwe_token = jwe.encrypt(
json.dumps(payload),
ENCRYPTION_KEY,
algorithm='A256KW',
encryption='A256CBC-HS512'
)
# 3. Make API request
response = requests.post(
'https://qrph.sbx.moduluslabs.io/v1/pay/qr',
json={'Token': jwe_token},
auth=(SECRET_KEY, ''),
headers={'Content-Type': 'application/json'}
)
# 4. Decrypt response
decrypted_data = jwe.decrypt(response.json()['Token'], ENCRYPTION_KEY)
result = json.loads(decrypted_data)
print(f"Transaction ID: {result['id']}")
print(f"QR Body: {result['qrBody']}")
return result
if __name__ == '__main__':
create_dynamic_qr()
# Complete flow requires a language with JWE support for encryption/decryption
# Use cURL only for the HTTP request step:
ENCRYPTED_TOKEN="eyJhbGciOiJBMjU2S1ciL..."
curl -X POST https://qrph.sbx.moduluslabs.io/v1/pay/qr \
-u $MODULUS_SECRET_KEY: \
-H "Content-Type: application/json" \
-d "{\"Token\": \"$ENCRYPTED_TOKEN\"}"
# Decrypt the response Token using your preferred language
<?php
require 'vendor/autoload.php';
use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Core\JWK;
use Jose\Component\Encryption\Algorithm\KeyEncryption\A256KW;
use Jose\Component\Encryption\Algorithm\ContentEncryption\A256CBCHS512;
use Jose\Component\Encryption\Compression\CompressionMethodManager;
use Jose\Component\Encryption\JWEBuilder;
use Jose\Component\Encryption\JWEDecrypter;
use Jose\Component\Encryption\Serializer\CompactSerializer;
$SECRET_KEY = getenv('MODULUS_SECRET_KEY');
$ENCRYPTION_KEY = getenv('MODULUS_ENCRYPTION_KEY');
function createDynamicQR() {
global $SECRET_KEY, $ENCRYPTION_KEY;
// 1. Prepare payload
$payload = json_encode([
'activationCode' => 'A9X4-B7P2-Q6Z8-M3L5',
'currency' => 'PHP',
'amount' => '98.00',
'merchantReferenceNumber' => uniqid('', true)
]);
// 2. Encrypt payload
$jwk = new JWK(['kty' => 'oct', 'k' => $ENCRYPTION_KEY]);
$keyAlgorithmManager = new AlgorithmManager([new A256KW()]);
$contentAlgorithmManager = new AlgorithmManager([new A256CBCHS512()]);
$jweBuilder = new JWEBuilder(
$keyAlgorithmManager,
$contentAlgorithmManager,
new CompressionMethodManager()
);
$jwe = $jweBuilder->create()
->withPayload($payload)
->withSharedProtectedHeader(['alg' => 'A256KW', 'enc' => 'A256CBC-HS512'])
->addRecipient($jwk)
->build();
$serializer = new CompactSerializer();
$jweToken = $serializer->serialize($jwe, 0);
// 3. Make API request
$ch = curl_init('https://qrph.sbx.moduluslabs.io/v1/pay/qr');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['Token' => $jweToken]));
curl_setopt($ch, CURLOPT_USERPWD, $SECRET_KEY . ':');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
// 4. Decrypt response
$responseData = json_decode($response, true);
$jweDecrypter = new JWEDecrypter(
$keyAlgorithmManager,
$contentAlgorithmManager,
null
);
$loadedJwe = $serializer->unserialize($responseData['Token']);
$jweDecrypter->decryptUsingKey($loadedJwe, $jwk, 0);
$result = json_decode($loadedJwe->getPayload(), true);
echo "Transaction ID: {$result['id']}\n";
echo "QR Body: " . substr($result['qrBody'], 0, 50) . "...\n";
return $result;
}
createDynamicQR();
package main
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jwe"
)
func main() {
secretKey := os.Getenv("MODULUS_SECRET_KEY")
encryptionKey := os.Getenv("MODULUS_ENCRYPTION_KEY")
// 1. Prepare payload
payload, _ := json.Marshal(map[string]string{
"activationCode": "A9X4-B7P2-Q6Z8-M3L5",
"currency": "PHP",
"amount": "98.00",
"merchantReferenceNumber": "5e91bc6c-f7e2-4a39-ae0e-5e93985c94a4",
})
// 2. Encrypt payload
keyBytes, _ := base64.RawURLEncoding.DecodeString(encryptionKey)
encrypted, _ := jwe.Encrypt(payload,
jwe.WithKey(jwa.A256KW, keyBytes),
jwe.WithContentEncryption(jwa.A256CBC_HS512),
)
// 3. Make API request
reqBody, _ := json.Marshal(map[string]string{"Token": string(encrypted)})
req, _ := http.NewRequest("POST", "https://qrph.sbx.moduluslabs.io/v1/pay/qr", bytes.NewBuffer(reqBody))
req.SetBasicAuth(secretKey, "")
req.Header.Set("Content-Type", "application/json")
resp, _ := (&http.Client{}).Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
// 4. Decrypt response
var tokenResp map[string]string
json.Unmarshal(body, &tokenResp)
decrypted, _ := jwe.Decrypt([]byte(tokenResp["Token"]), jwe.WithKey(jwa.A256KW, keyBytes))
var result map[string]string
json.Unmarshal(decrypted, &result)
fmt.Printf("Transaction ID: %s\n", result["id"])
fmt.Printf("QR Body: %s...\n", result["qrBody"][:50])
}
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.*;
import com.nimbusds.jose.jwk.OctetSequenceKey;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.net.http.*;
import java.util.*;
public class CompleteExample {
public static void main(String[] args) throws Exception {
String secretKey = System.getenv("MODULUS_SECRET_KEY");
String encryptionKey = System.getenv("MODULUS_ENCRYPTION_KEY");
ObjectMapper mapper = new ObjectMapper();
// 1. Prepare payload
Map<String, String> payload = new LinkedHashMap<>();
payload.put("activationCode", "A9X4-B7P2-Q6Z8-M3L5");
payload.put("currency", "PHP");
payload.put("amount", "98.00");
payload.put("merchantReferenceNumber", UUID.randomUUID().toString());
// 2. Encrypt payload
byte[] keyBytes = Base64.getUrlDecoder().decode(encryptionKey);
OctetSequenceKey jwk = new OctetSequenceKey.Builder(keyBytes).build();
JWEHeader header = new JWEHeader(JWEAlgorithm.A256KW, EncryptionMethod.A256CBC_HS512);
JWEObject jwe = new JWEObject(header, new Payload(mapper.writeValueAsString(payload)));
jwe.encrypt(new AESEncrypter(jwk));
// 3. Make API request
HttpClient client = HttpClient.newHttpClient();
String auth = Base64.getEncoder().encodeToString((secretKey + ":").getBytes());
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://qrph.sbx.moduluslabs.io/v1/pay/qr"))
.header("Authorization", "Basic " + auth)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(
mapper.writeValueAsString(Map.of("Token", jwe.serialize()))
))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// 4. Decrypt response
Map<String, String> responseData = mapper.readValue(response.body(), Map.class);
JWEObject responseJwe = JWEObject.parse(responseData.get("Token"));
responseJwe.decrypt(new AESDecrypter(jwk));
Map<String, String> result = mapper.readValue(responseJwe.getPayload().toString(), Map.class);
System.out.println("Transaction ID: " + result.get("id"));
System.out.println("QR Body: " + result.get("qrBody").substring(0, 50) + "...");
}
}
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using Jose;
var secretKey = Environment.GetEnvironmentVariable("MODULUS_SECRET_KEY");
var encryptionKey = Environment.GetEnvironmentVariable("MODULUS_ENCRYPTION_KEY");
// Base64URL decode helper
static byte[] Base64UrlDecode(string base64Url)
{
var base64 = base64Url.Replace('-', '+').Replace('_', '/');
switch (base64.Length % 4)
{
case 2: base64 += "=="; break;
case 3: base64 += "="; break;
}
return Convert.FromBase64String(base64);
}
// 1. Prepare payload
var payload = new
{
activationCode = "A9X4-B7P2-Q6Z8-M3L5",
currency = "PHP",
amount = "98.00",
merchantReferenceNumber = Guid.NewGuid().ToString()
};
// 2. Encrypt payload
var key = Base64UrlDecode(encryptionKey);
var jweToken = JWT.Encode(
JsonSerializer.Serialize(payload),
key,
JweAlgorithm.A256KW,
JweEncryption.A256CBC_HS512
);
// 3. Make API request
using var client = new HttpClient();
var authBytes = Encoding.ASCII.GetBytes($"{secretKey}:");
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authBytes));
var content = new StringContent(
JsonSerializer.Serialize(new { Token = jweToken }),
Encoding.UTF8,
"application/json"
);
var response = await client.PostAsync(
"https://qrph.sbx.moduluslabs.io/v1/pay/qr", content
);
var responseBody = await response.Content.ReadAsStringAsync();
// 4. Decrypt response
var responseData = JsonSerializer.Deserialize<JsonElement>(responseBody);
var responseToken = responseData.GetProperty("Token").GetString();
var decryptedJson = JWT.Decode(responseToken, key);
var result = JsonSerializer.Deserialize<JsonElement>(decryptedJson);
Console.WriteLine($"Transaction ID: {result.GetProperty("id")}");
Console.WriteLine($"QR Body: {result.GetProperty("qrBody").GetString()?[..50]}...");
Next Steps
Quickstart Guide
Follow a complete example with encryption
Create QR Endpoint
See the full API reference
Error Handling
Learn how to handle encrypted error responses
Testing
Test encryption in the sandbox environment