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

# QR codes

> How Safepay Raast creates static and dynamic QR codes.

Safepay lets you issue QR codes that customers can scan with their banking apps. Static codes are reusable, while dynamic codes embed order-level metadata.

## Static vs dynamic

| Attribute              | Static QR                            | Dynamic QR                          |
| ---------------------- | ------------------------------------ | ----------------------------------- |
| Reusability            | Unlimited                            | Single use                          |
| Encoded amount         | Optional (usually omitted)           | Optional (per order)                |
| Encoded order metadata | No                                   | Yes (order ID, request ID)          |
| Typical usage          | Printed signage, countertop displays | POS terminals, e-commerce checkouts |

## Create a static QR

Use this when you want a reusable QR tied to a single aggregator merchant.

<Steps>
  <Step title="Create the QR">
    Call `POST /v1/aggregators/{{aggregator_id}}/qrs` with `type: STATIC` and your `aggregator_merchant_identifier`.
  </Step>

  <Step title="Render and share">
    Render `data.code` using your QR library and display it where customers pay.
  </Step>
</Steps>

<CodeGroup>
  ```bash cURL theme={null}
  curl --request POST "{{base_url}}/v1/aggregators/{{aggregator_id}}/qrs" \
    --header "X-SFPY-AGGREGATOR-SECRET-KEY: {{secret_key}}" \
    --header "Content-Type: application/json" \
    --data '{
      "type": "STATIC",
      "aggregator_merchant_identifier": "{{aggregator_merchant_identifier}}",
      "request_id": "{{request_id}}"
    }'
  ```

  ```typescript TypeScript theme={null}
  const baseUrl = '{{base_url}}';
  const aggregatorId = '{{aggregator_id}}';
  const secretKey = '{{secret_key}}';
  const aggregatorMerchantIdentifier = '{{aggregator_merchant_identifier}}';

  const res = await fetch(`${baseUrl}/v1/aggregators/${aggregatorId}/qrs`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-SFPY-AGGREGATOR-SECRET-KEY': secretKey,
    },
    body: JSON.stringify({
      type: 'STATIC',
      aggregator_merchant_identifier: aggregatorMerchantIdentifier,
      request_id: crypto.randomUUID(),
    }),
  });

  if (!res.ok) {
    const error = await res.json();
    throw new Error(`Static QR failed: ${res.status} ${error.message}`);
  }

  const payload = await res.json();
  console.log('QR code', payload.data.code);
  ```
</CodeGroup>

<Callout type="info">Static QRs can receive multiple payments. Reconcile each payment by webhook or by listing QR payments.</Callout>

## Create a dynamic QR

Dynamic codes embed transaction metadata so each scan maps to a unique order.

<Steps>
  <Step title="Create the QR">
    Call `POST /v1/aggregators/{{aggregator_id}}/qrs` with `type: DYNAMIC`, order details, and amount.
  </Step>

  <Step title="Render and collect">
    Display the QR to the customer and wait for the payment webhook.
  </Step>
</Steps>

<CodeGroup>
  ```bash cURL theme={null}
  curl --request POST "{{base_url}}/v1/aggregators/{{aggregator_id}}/qrs" \
    --header "X-SFPY-AGGREGATOR-SECRET-KEY: {{secret_key}}" \
    --header "Content-Type: application/json" \
    --data '{
      "type": "DYNAMIC",
      "aggregator_merchant_identifier": "{{aggregator_merchant_identifier}}",
      "order_id": "ORDER-4921",
      "request_id": "{{request_id}}",
      "amount": 7500,
      "expiry_in_minutes": 180
    }'
  ```

  ```typescript TypeScript theme={null}
  const baseUrl = '{{base_url}}';
  const aggregatorId = '{{aggregator_id}}';
  const secretKey = '{{secret_key}}';
  const aggregatorMerchantIdentifier = '{{aggregator_merchant_identifier}}';

  const res = await fetch(`${baseUrl}/v1/aggregators/${aggregatorId}/qrs`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-SFPY-AGGREGATOR-SECRET-KEY': secretKey,
    },
    body: JSON.stringify({
      type: 'DYNAMIC',
      aggregator_merchant_identifier: aggregatorMerchantIdentifier,
      order_id: 'ORDER-4921',
      request_id: crypto.randomUUID(),
      amount: 7500,
      expiry_in_minutes: 180,
    }),
  });

  if (!res.ok) {
    const error = await res.json();
    throw new Error(`Dynamic QR failed: ${res.status} ${error.message}`);
  }

  const payload = await res.json();
  console.log('QR code', payload.data.code);
  ```
</CodeGroup>

## Key endpoints

| Endpoint                                                       | Purpose                                    |
| -------------------------------------------------------------- | ------------------------------------------ |
| `POST /v1/aggregators/{{aggregator_id}}/qrs`                   | Create static or dynamic QR codes.         |
| `GET /v1/aggregators/{{aggregator_id}}/qrs`                    | List QR codes with filters and pagination. |
| `GET /v1/aggregators/{{aggregator_id}}/qrs/{{qr_id}}`          | Read a single QR code.                     |
| `GET /v1/aggregators/{{aggregator_id}}/qrs/{{qr_id}}/payments` | List payments made against a QR.           |
| `DELETE /v1/aggregators/{{aggregator_id}}/qrs/{{qr_id}}`       | Delete a QR code.                          |

## See also

* [Dynamic QR use case](/use-cases/dynamic_qr)
* [Static QR use case](/use-cases/static_qr)
* [Payment lifecycle guide](/guides/payment-lifecycle)
* [Webhooks](/concepts/webhooks)
