> ## Documentation Index
> Fetch the complete documentation index at: https://docs.moduluslabs.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Create Dynamic QR Ph

> Generate a Dynamic QR Ph code for Consumer Scans Business transactions

<Info>
  **How it works:** Generate a unique QR code → Customer scans with banking app → Payment completed → Webhook notification sent
</Info>

## Authentication

This endpoint requires [HTTP Basic Authentication](/docs/qr/authentication) using your Secret Key.

## Encrypted Payload Structure

The `Token` field must contain a JWE-encrypted JSON payload with the following fields:

| Field                     | Type   | Required | Description                                                      |
| ------------------------- | ------ | -------- | ---------------------------------------------------------------- |
| `activationCode`          | string | Yes      | Activation Code from Modulus Labs. Format: `XXXX-XXXX-XXXX-XXXX` |
| `currency`                | string | Yes      | ISO 4217 currency code (3 chars). Currently only `PHP` supported |
| `amount`                  | string | Yes      | Payment amount with 2 decimals. Range: `1.00` to `99999.99`      |
| `merchantReferenceNumber` | string | Yes      | Your unique reference (1-36 chars, alphanumeric and hyphens)     |

<AccordionGroup>
  <Accordion title="activationCode" icon="key">
    Activation Code assigned to your sub-merchant account by Modulus Labs.

    * **Format:** `XXXX-XXXX-XXXX-XXXX` (19 characters including hyphens)
    * **Example:** `A9X4-B7P2-Q6Z8-M3L5`
  </Accordion>

  <Accordion title="currency" icon="coins">
    ISO 4217 currency code.

    * **Length:** Exactly 3 characters
    * **Supported:** `PHP`
    * **Example:** `PHP`
  </Accordion>

  <Accordion title="amount" icon="calculator">
    Payment amount in decimal format.

    * **Min:** `1.00` (₱1.00)
    * **Max:** `99999.99` (₱99,999.99)
    * **Format:** String with exactly 2 decimal places
    * **Example:** `"500.00"`, `"1234.56"`

    <Warning>
      Pass amount as a **string**, not a number, to preserve decimal precision.
    </Warning>
  </Accordion>

  <Accordion title="merchantReferenceNumber" icon="fingerprint">
    Your unique reference number for this transaction.

    * **Min Length:** 1 character
    * **Max Length:** 36 characters
    * **Allowed:** Alphanumeric characters and hyphens
    * **Example:** `"ORDER-12345"`, `"5e91bc6c-f7e2-4a39-ae0e-5e93985c94a4"`

    <Tip>
      Use UUIDs for guaranteed uniqueness across transactions.
    </Tip>
  </Accordion>
</AccordionGroup>

### Example Payload (Before Encryption)

```json theme={null}
{
  "activationCode": "A9X4-B7P2-Q6Z8-M3L5",
  "currency": "PHP",
  "amount": "500.00",
  "merchantReferenceNumber": "ORDER-2025-001"
}
```

## Decrypted Response

When you decrypt the response `Token`, you'll get:

| Field    | Type   | Description                                             |
| -------- | ------ | ------------------------------------------------------- |
| `id`     | string | Unique transaction identifier generated by Modulus Labs |
| `qrBody` | string | Base64-encoded PNG image of the QR code                 |

```json theme={null}
{
  "id": "0ce32626-08cf-405c-adab-6d384a8871da",
  "qrBody": "iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAYAAAB5fY51AAAMTElEQVR4nO..."
}
```

<RequestExample>
  ```bash cURL theme={null}
  # Note: You need to encrypt the payload first using a script/tool
  # See the Encryption Guide for JWE token creation

  # 1. Create encrypted JWE token (using external tool/script)
  ENCRYPTED_TOKEN="eyJhbGciOiJBMjU2S1ciL..."

  # 2. Make API request
  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\"}"

  # 3. Response will be an encrypted JWE token that needs decryption
  ```

  ```javascript Node.js theme={null}
  const axios = require('axios');
  const jose = require('jose');
  const crypto = require('crypto');

  const SECRET_KEY = process.env.MODULUS_SECRET_KEY;
  const ENCRYPTION_KEY = process.env.MODULUS_ENCRYPTION_KEY;

  async function createDynamicQR(amount, reference) {
    // 1. Prepare the payload
    const payload = {
      activationCode: 'A9X4-B7P2-Q6Z8-M3L5',
      currency: 'PHP',
      amount: amount.toFixed(2), // Convert to string with 2 decimals
      merchantReferenceNumber: reference || crypto.randomUUID()
    };

    // 2. Encrypt payload into JWE token
    const key = Buffer.from(ENCRYPTION_KEY, 'base64');
    const jweToken = await new jose.CompactEncrypt(
      new TextEncoder().encode(JSON.stringify(payload))
    )
      .setProtectedHeader({ alg: 'A256KW', enc: 'A256CBC-HS512' })
      .encrypt(key);

    // 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,
      key
    );
    const result = JSON.parse(new TextDecoder().decode(plaintext));

    console.log('Transaction ID:', result.id);
    console.log('QR Code (Base64):', result.qrBody.substring(0, 50) + '...');

    return result;
  }

  // Usage
  createDynamicQR(500.00, 'ORDER-2025-001')
    .then(result => {
      // Save QR code to file
      const fs = require('fs');
      const buffer = Buffer.from(result.qrBody, 'base64');
      fs.writeFileSync('qr-code.png', buffer);
      console.log('QR code saved to qr-code.png');
    })
    .catch(error => {
      console.error('Error:', error.response?.data || error.message);
    });
  ```

  ```python Python theme={null}
  import os
  import base64
  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(amount, reference=None):
      # 1. Prepare the payload
      payload = {
          'activationCode': 'A9X4-B7P2-Q6Z8-M3L5',
          'currency': 'PHP',
          'amount': f'{amount:.2f}',  # Convert to string with 2 decimals
          'merchantReferenceNumber': reference or str(uuid4())
      }

      # 2. Encrypt payload into JWE token
      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'}
      )

      response.raise_for_status()

      # 4. Decrypt response
      decrypted = jwe.decrypt(response.json()['Token'], ENCRYPTION_KEY)
      result = json.loads(decrypted)

      print(f'Transaction ID: {result["id"]}')
      print(f'QR Code (Base64): {result["qrBody"][:50]}...')

      return result

  # Usage
  if __name__ == '__main__':
      try:
          result = create_dynamic_qr(500.00, 'ORDER-2025-001')

          # Save QR code to file
          image_data = base64.b64decode(result['qrBody'])
          with open('qr-code.png', 'wb') as f:
              f.write(image_data)
          print('QR code saved to qr-code.png')

      except Exception as e:
          print(f'Error: {str(e)}')
  ```

  ```php PHP theme={null}
  <?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\JWEBuilder;
  use Jose\Component\Encryption\Serializer\CompactSerializer;

  $secretKey = getenv('MODULUS_SECRET_KEY');
  $encryptionKey = getenv('MODULUS_ENCRYPTION_KEY');

  function createDynamicQR($amount, $reference = null) {
      global $secretKey, $encryptionKey;

      // 1. Prepare the payload
      $payload = [
          'activationCode' => 'A9X4-B7P2-Q6Z8-M3L5',
          'currency' => 'PHP',
          'amount' => number_format($amount, 2, '.', ''),
          'merchantReferenceNumber' => $reference ?? uniqid('ORDER-', true)
      ];

      // 2. Encrypt payload (simplified - use proper JWE library)
      // See encryption documentation for full implementation
      $jweToken = encryptToJWE(json_encode($payload), $encryptionKey);

      // 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, $secretKey . ':');
      curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

      $response = curl_exec($ch);
      $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
      curl_close($ch);

      if ($httpCode !== 200) {
          throw new Exception("API request failed with HTTP $httpCode");
      }

      // 4. Decrypt response
      $responseData = json_decode($response, true);
      $decrypted = decryptJWE($responseData['Token'], $encryptionKey);
      $result = json_decode($decrypted, true);

      echo "Transaction ID: {$result['id']}\n";

      return $result;
  }

  // Usage
  try {
      $result = createDynamicQR(500.00, 'ORDER-2025-001');

      // Save QR code to file
      file_put_contents('qr-code.png', base64_decode($result['qrBody']));
      echo "QR code saved to qr-code.png\n";

  } catch (Exception $e) {
      echo "Error: " . $e->getMessage() . "\n";
  }
  ```

  ```java Java theme={null}
  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.nio.file.Files;
  import java.nio.file.Path;
  import java.util.*;

  public class CreateDynamicQRExample {
      private static final ObjectMapper mapper = new ObjectMapper();

      public static void main(String[] args) throws Exception {
          createDynamicQR(500.00, "ORDER-2025-001");
      }

      static void createDynamicQR(double amount, String reference) throws Exception {
          String secretKey = System.getenv("MODULUS_SECRET_KEY");
          String encryptionKey = System.getenv("MODULUS_ENCRYPTION_KEY");

          if (secretKey == null || encryptionKey == null) {
              System.out.println("Error: Missing environment variables");
              System.out.println("Please set MODULUS_SECRET_KEY and MODULUS_ENCRYPTION_KEY");
              return;
          }

          // 1. Prepare the payload
          Map<String, Object> payload = new LinkedHashMap<>();
          payload.put("activationCode", "A9X4-B7P2-Q6Z8-M3L5");
          payload.put("currency", "PHP");
          payload.put("amount", String.format("%.2f", amount));
          payload.put("merchantReferenceNumber", reference != null ? reference : UUID.randomUUID().toString());

          String payloadJson = mapper.writeValueAsString(payload);
          System.out.println("Payload: " + payloadJson);

          // 2. Decode encryption key from Base64URL and create JWE
          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(payloadJson));
          jwe.encrypt(new AESEncrypter(jwk));
          String jweToken = jwe.serialize();

          System.out.println("JWE Token: " + jweToken.substring(0, 60) + "...");

          // 3. Make API request with Basic Auth
          HttpClient client = HttpClient.newHttpClient();
          String auth = secretKey + ":";
          String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());

          String requestBody = mapper.writeValueAsString(Map.of("Token", jweToken));

          HttpRequest request = HttpRequest.newBuilder()
              .uri(URI.create("https://qrph.sbx.moduluslabs.io/v1/pay/qr"))
              .header("Authorization", "Basic " + encodedAuth)
              .header("Content-Type", "application/json")
              .POST(HttpRequest.BodyPublishers.ofString(requestBody))
              .build();

          System.out.println("Sending request to API...");

          HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

          if (response.statusCode() != 200) {
              System.out.println("Error: HTTP " + response.statusCode());
              System.out.println("Details: " + response.body());
              return;
          }

          // 4. Parse and decrypt response
          Map<String, String> responseData = mapper.readValue(response.body(), Map.class);
          String responseToken = responseData.get("Token");

          JWEObject responseJwe = JWEObject.parse(responseToken);
          responseJwe.decrypt(new AESDecrypter(jwk));
          String decryptedJson = responseJwe.getPayload().toString();

          Map<String, String> result = mapper.readValue(decryptedJson, Map.class);

          System.out.println("✓ Transaction ID: " + result.get("id"));
          String qrBody = result.get("qrBody");
          System.out.println("✓ QR Code (Base64): " + qrBody.substring(0, Math.min(50, qrBody.length())) + "...");

          // 5. Save QR code to file
          byte[] imageData = Base64.getDecoder().decode(qrBody);
          Files.write(Path.of("qr-code.png"), imageData);
          System.out.println("✓ QR code saved to qr-code.png");
      }
  }
  ```

  ```go Go theme={null}
  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"
  )

  type QRPayload struct {
  	ActivationCode          string `json:"activationCode"`
  	Currency                string `json:"currency"`
  	Amount                  string `json:"amount"`
  	MerchantReferenceNumber string `json:"merchantReferenceNumber"`
  }

  type TokenRequest struct {
  	Token string `json:"Token"`
  }

  type QRResponse struct {
  	ID     string `json:"id"`
  	QRBody string `json:"qrBody"`
  }

  func main() {
  	if err := createDynamicQR(500.00, "ORDER-2025-001"); err != nil {
  		fmt.Printf("Error: %v\n", err)
  	}
  }

  func createDynamicQR(amount float64, reference string) error {
  	secretKey := os.Getenv("MODULUS_SECRET_KEY")
  	encryptionKey := os.Getenv("MODULUS_ENCRYPTION_KEY")

  	if secretKey == "" || encryptionKey == "" {
  		return fmt.Errorf("missing environment variables: MODULUS_SECRET_KEY and MODULUS_ENCRYPTION_KEY required")
  	}

  	// 1. Prepare the payload
  	payload := QRPayload{
  		ActivationCode:          "A9X4-B7P2-Q6Z8-M3L5",
  		Currency:                "PHP",
  		Amount:                  fmt.Sprintf("%.2f", amount),
  		MerchantReferenceNumber: reference,
  	}

  	payloadJSON, err := json.Marshal(payload)
  	if err != nil {
  		return fmt.Errorf("failed to marshal payload: %w", err)
  	}
  	fmt.Printf("Payload: %s\n", payloadJSON)

  	// 2. Decode encryption key from Base64URL
  	keyBytes, err := base64.RawURLEncoding.DecodeString(encryptionKey)
  	if err != nil {
  		return fmt.Errorf("failed to decode encryption key: %w", err)
  	}
  	fmt.Printf("Decoded key length: %d bytes (%d bits)\n", len(keyBytes), len(keyBytes)*8)

  	// 3. Encrypt payload into JWE token
  	encrypted, err := jwe.Encrypt(payloadJSON,
  		jwe.WithKey(jwa.A256KW, keyBytes),
  		jwe.WithContentEncryption(jwa.A256CBC_HS512),
  	)
  	if err != nil {
  		return fmt.Errorf("failed to encrypt payload: %w", err)
  	}
  	jweToken := string(encrypted)
  	fmt.Printf("JWE Token: %s...\n", jweToken[:60])

  	// 4. Make API request with Basic Auth
  	reqBody := TokenRequest{Token: jweToken}
  	jsonData, _ := json.Marshal(reqBody)

  	req, err := http.NewRequest("POST", "https://qrph.sbx.moduluslabs.io/v1/pay/qr", bytes.NewBuffer(jsonData))
  	if err != nil {
  		return fmt.Errorf("failed to create request: %w", err)
  	}

  	req.SetBasicAuth(secretKey, "")
  	req.Header.Set("Content-Type", "application/json")

  	fmt.Println("Sending request to API...")

  	client := &http.Client{}
  	resp, err := client.Do(req)
  	if err != nil {
  		return fmt.Errorf("request failed: %w", err)
  	}
  	defer resp.Body.Close()

  	body, _ := io.ReadAll(resp.Body)

  	if resp.StatusCode != 200 {
  		return fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body))
  	}

  	// 5. Parse and decrypt response
  	var tokenResp TokenRequest
  	if err := json.Unmarshal(body, &tokenResp); err != nil {
  		return fmt.Errorf("failed to parse response: %w", err)
  	}

  	decrypted, err := jwe.Decrypt([]byte(tokenResp.Token), jwe.WithKey(jwa.A256KW, keyBytes))
  	if err != nil {
  		return fmt.Errorf("failed to decrypt response: %w", err)
  	}

  	var result QRResponse
  	if err := json.Unmarshal(decrypted, &result); err != nil {
  		return fmt.Errorf("failed to parse decrypted response: %w", err)
  	}

  	fmt.Printf("✓ Transaction ID: %s\n", result.ID)
  	if len(result.QRBody) > 50 {
  		fmt.Printf("✓ QR Code (Base64): %s...\n", result.QRBody[:50])
  	}

  	// 6. Save QR code to file
  	imageData, err := base64.StdEncoding.DecodeString(result.QRBody)
  	if err != nil {
  		return fmt.Errorf("failed to decode QR image: %w", err)
  	}

  	if err := os.WriteFile("qr-code.png", imageData, 0644); err != nil {
  		return fmt.Errorf("failed to save QR code: %w", err)
  	}
  	fmt.Println("✓ QR code saved to qr-code.png")

  	return nil
  }
  ```

  ```csharp C# / .NET theme={null}
  using System.Net.Http.Headers;
  using System.Text;
  using System.Text.Json;
  using Jose;

  namespace ModulusQRPH;

  class Program
  {
      static async Task Main(string[] args)
      {
          await CreateDynamicQR(500.00m, "ORDER-2025-001");
      }

      /// <summary>
      /// Decodes a Base64URL-encoded string to bytes.
      /// This replicates what Node.js jose's importJWK does with the 'k' parameter.
      /// </summary>
      static byte[] Base64UrlDecode(string base64Url)
      {
          // Convert Base64URL to standard Base64
          var base64 = base64Url
              .Replace('-', '+')  // Character 62
              .Replace('_', '/'); // Character 63

          // Add padding if necessary
          switch (base64.Length % 4)
          {
              case 2: base64 += "=="; break;
              case 3: base64 += "="; break;
          }

          return Convert.FromBase64String(base64);
      }

      static async Task CreateDynamicQR(decimal amount, string reference)
      {
          var secretKey = Environment.GetEnvironmentVariable("MODULUS_SECRET_KEY");
          var encryptionKey = Environment.GetEnvironmentVariable("MODULUS_ENCRYPTION_KEY");

          if (string.IsNullOrEmpty(secretKey) || string.IsNullOrEmpty(encryptionKey))
          {
              Console.WriteLine("Error: Missing environment variables");
              Console.WriteLine("Please set MODULUS_SECRET_KEY and MODULUS_ENCRYPTION_KEY");
              return;
          }

          try
          {
              // 1. Prepare the payload
              var payload = new
              {
                  activationCode = "A9X4-B7P2-Q6Z8-M3L5",
                  currency = "PHP",
                  amount = amount.ToString("F2"),
                  merchantReferenceNumber = reference ?? Guid.NewGuid().ToString()
              };

              var payloadJson = JsonSerializer.Serialize(payload);
              Console.WriteLine($"Payload: {payloadJson}");

              // 2. Decode the encryption key from Base64URL format
              // This replicates: importJWK({ kty: 'oct', k: encryptionKey }, algorithm)
              var key = Base64UrlDecode(encryptionKey);
              Console.WriteLine($"Decoded key length: {key.Length} bytes ({key.Length * 8} bits)");

              // 3. Encrypt payload into JWE token
              var jweToken = JWT.Encode(
                  payloadJson,
                  key,
                  JweAlgorithm.A256KW,
                  JweEncryption.A256CBC_HS512
              );

              Console.WriteLine($"JWE Token: {jweToken[..60]}...");

              // 4. Make API request with Basic Auth
              using var client = new HttpClient();

              var authBytes = Encoding.ASCII.GetBytes($"{secretKey}:");
              client.DefaultRequestHeaders.Authorization =
                  new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authBytes));

              var requestBody = new { Token = jweToken };
              var content = new StringContent(
                  JsonSerializer.Serialize(requestBody),
                  Encoding.UTF8,
                  "application/json"
              );

              Console.WriteLine("Sending request to API...");

              var response = await client.PostAsync(
                  "https://qrph.sbx.moduluslabs.io/v1/pay/qr",
                  content
              );

              var responseBody = await response.Content.ReadAsStringAsync();

              if (!response.IsSuccessStatusCode)
              {
                  Console.WriteLine($"Error: HTTP {(int)response.StatusCode}");
                  Console.WriteLine($"Details: {responseBody}");
                  return;
              }

              // 5. Parse and decrypt response
              var responseData = JsonSerializer.Deserialize<TokenResponse>(
                  responseBody,
                  new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
              );

              if (responseData?.Token is null)
              {
                  Console.WriteLine("Error: Invalid response - missing token");
                  return;
              }

              var decryptedJson = JWT.Decode(responseData.Token, key);
              var result = JsonSerializer.Deserialize<QRResponse>(
                  decryptedJson,
                  new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
              );

              if (result is null)
              {
                  Console.WriteLine("Error: Failed to deserialize QR response");
                  return;
              }

              Console.WriteLine($"✓ Transaction ID: {result.Id}");
              Console.WriteLine($"✓ QR Code (Base64): {result.QrBody?[..Math.Min(50, result.QrBody?.Length ?? 0)]}...");

              // 6. Save QR code to file
              if (!string.IsNullOrEmpty(result.QrBody))
              {
                  var imageData = Convert.FromBase64String(result.QrBody);
                  await File.WriteAllBytesAsync("qr-code.png", imageData);
                  Console.WriteLine("✓ QR code saved to qr-code.png");
              }
          }
          catch (HttpRequestException e)
          {
              Console.WriteLine($"Request failed: {e.Message}");
          }
          catch (Exception e)
          {
              Console.WriteLine($"Error: {e.GetType().Name} - {e.Message}");
          }
      }
  }

  // Response models
  public class TokenResponse
  {
      public string? Token { get; set; }
  }

  public class QRResponse
  {
      public string? Id { get; set; }
      public string? QrBody { get; set; }
  }
  ```
</RequestExample>

## Using the QR Code

After receiving the `qrBody`, you need to convert it to an image that customers can scan:

### Display in Web Application

```html theme={null}
<img src="data:image/png;base64,${qrBody}" alt="Scan to Pay" />
```

### Save as File

<CodeGroup>
  ```javascript Node.js theme={null}
  const fs = require('fs');

  function saveQRCode(qrBody, filename = 'qr-code.png') {
    const buffer = Buffer.from(qrBody, 'base64');
    fs.writeFileSync(filename, buffer);
  }
  ```

  ```python Python theme={null}
  import base64

  def save_qr_code(qr_body, filename='qr-code.png'):
      image_data = base64.b64decode(qr_body)
      with open(filename, 'wb') as f:
          f.write(image_data)
  ```

  ```csharp C# / .NET theme={null}
  using System;
  using System.IO;

  void SaveQRCode(string qrBody, string filename = "qr-code.png")
  {
      var imageData = Convert.FromBase64String(qrBody);
      File.WriteAllBytes(filename, imageData);
  }
  ```
</CodeGroup>

## Best Practices

<CardGroup cols={2}>
  <Card title="Use UUIDs" icon="fingerprint">
    Use UUIDs for `merchantReferenceNumber` to ensure uniqueness
  </Card>

  <Card title="Validate Amount" icon="calculator">
    Validate amount format and range before sending to API
  </Card>

  <Card title="Handle Errors" icon="shield-exclamation">
    Implement proper error handling for all possible scenarios
  </Card>

  <Card title="Store Transaction ID" icon="database">
    Save the returned `id` for reconciliation and support
  </Card>
</CardGroup>

## Common Issues

<AccordionGroup>
  <Accordion title="JWE Token Decryption Fails">
    **Symptoms:**

    * Cannot decrypt response token
    * "Invalid token" errors

    **Solutions:**

    * Verify you're using the correct Encryption Key
    * Check encryption algorithms match (A256KW, A256CBC-HS512)
    * Ensure no whitespace in keys
  </Accordion>

  <Accordion title="Amount Validation Error">
    **Symptoms:**

    * API returns "Invalid amount" error
    * Request rejected with 400 status

    **Solutions:**

    * Ensure amount is a string, not a number: `"500.00"` not `500`
    * Always include 2 decimal places: `"5.00"` not `"5"`
    * Stay within min/max range (1.00 to 99999.99)
  </Accordion>

  <Accordion title="QR Code Won't Display">
    **Symptoms:**

    * Base64 decode fails
    * Invalid image format

    **Solutions:**

    * Ensure full Base64 string was received (not truncated)
    * Check for proper Base64 padding
    * Verify you're decoding as PNG format
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Testing Guide" icon="flask" href="/docs/qr/testing">
    Learn how to test QR code generation and payment simulation
  </Card>

  <Card title="Webhooks" icon="webhook" href="/docs/webhooks">
    Set up webhooks to receive payment notifications
  </Card>

  <Card title="Error Handling" icon="triangle-exclamation" href="/docs/errors">
    Handle errors gracefully in your application
  </Card>

  <Card title="Go Live" icon="rocket" href="/docs/production">
    Deploy your integration to production
  </Card>
</CardGroup>


## OpenAPI

````yaml POST /v1/pay/qr
openapi: 3.1.0
info:
  title: Modulus Labs QR API
  description: >-
    API for generating Dynamic QR Ph codes for Consumer Scans Business
    transactions
  version: 1.0.0
servers:
  - url: https://qrph.sbx.moduluslabs.io
    description: Sandbox
security: []
paths:
  /v1/pay/qr:
    post:
      tags:
        - QR
      summary: Create Dynamic QR Ph
      description: >-
        Generate a Dynamic QR Ph code for Consumer Scans Business transactions.
        The request and response payloads are encrypted using JWE.
      operationId: createDynamicQR
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - Token
              properties:
                Token:
                  type: string
                  description: >-
                    JWE-encrypted token containing the payment payload.
                    Algorithm: A256KW | Encryption: A256CBC-HS512. See the
                    Encryption Guide for how to create this token.
            example:
              Token: eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0...
      responses:
        '200':
          description: >-
            QR code generated successfully. Response contains an encrypted JWE
            token.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QRResponse'
              example:
                Token: eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0...
        '400':
          description: Bad Request - Invalid parameters in encrypted payload
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QRResponse'
        '401':
          description: Unauthorized - Invalid API Key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                code: '10000013'
                error: Invalid API Key
                referenceNumber: 097bf60a-bdab-40bf-b615-3c0eae693b86
        '403':
          description: Forbidden - Insufficient permissions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                code: '10000015'
                error: Insufficient permissions
                referenceNumber: 097bf60a-bdab-40bf-b615-3c0eae693b86
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                code: '10000001'
                error: An unexpected error occurred. Please try again later.
                referenceNumber: 097bf60a-bdab-40bf-b615-3c0eae693b86
      security:
        - basicAuth: []
components:
  schemas:
    QRResponse:
      type: object
      properties:
        Token:
          type: string
          description: >-
            JWE-encrypted response token. When decrypted, contains the
            transaction ID and QR code image.
    ErrorResponse:
      type: object
      properties:
        code:
          type: string
          description: Error code
        error:
          type: string
          description: Error message
        referenceNumber:
          type: string
          description: Reference number for support inquiries
  securitySchemes:
    basicAuth:
      type: http
      scheme: basic
      description: >-
        HTTP Basic Authentication using your Secret Key as the username and an
        empty password

````