Dynamic QR codes encode order-specific metadata so you can present a scannable Raast request at checkout. Each QR is single-use and maps to a specific aggregator merchant.
Prerequisites
{{aggregator_id}} and {{secret_key}}
aggregator_merchant_identifier for the merchant collecting funds
- Optional order metadata and amount
Flow summary
Load merchant identifiers
Retrieve the aggregator_merchant_identifier from the merchant creation response.
Create the dynamic QR
Call POST /v1/aggregators/{{aggregator_id}}/qrs with type: DYNAMIC and the order details.
Render the QR
Render the returned data.code string as a QR image in your checkout UI.
Track the outcome
Listen for payment webhooks or poll the payment endpoint to confirm settlement.
Set {{base_url}} to https://dev.api.getsafepay.com/raastwire in Sandbox or https://api.getsafepay.com/raastwire in Production.
Dynamic QR
Request Fields
API Example
Sample Response
Use DYNAMIC to create a single-use QR code.
aggregator_merchant_identifier
Token returned from merchant creation (data.token).
Recommended to guarantee idempotency. Reuse only when retrying the same payload.
Optional order reference surfaced in webhook payloads.
Amount in paisa to embed in the QR; omit to let the payer choose the amount.
Optional expiry window (defaults to 180) after which the QR is invalid.
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
}'
const baseUrl = '{{base_url}}';
const aggregatorId = '{{aggregator_id}}';
const secretKey = '{{secret_key}}';
const aggregatorMerchantIdentifier = '{{aggregator_merchant_identifier}}';
const qrResponse = 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 (!qrResponse.ok) {
const errorPayload = await qrResponse.json();
throw new Error(`Dynamic QR failed: ${qrResponse.status} ${errorPayload.message}`);
}
type CreateQrPayload = {
api_version: string;
data: {
token: string;
code: string;
type: 'DYNAMIC' | 'STATIC';
aggregator_merchant_id: string;
created_at: string;
};
};
const qrPayload: CreateQrPayload = await qrResponse.json();
console.log('QR code payload', qrPayload.data.code);
{
"api_version": "v1",
"data": {
"token": "qr_4076ad61-75cf-44a3-97a1-b66643ea3bf2",
"aggregator_merchant_id": "am_be454a50-7612-4dc6-a97e-284ebbe7ae93",
"type": "DYNAMIC",
"code": "0002010102122876003285bc899c1dd443e3b3beb24631aef093...",
"created_at": "2025-04-28T10:26:48Z"
}
}
Render the QR
Feed data.code into your preferred QR library (for example, qrcode.react, qr-image, or qrcodejs). The returned string is EMVCo-compliant and ready for Raast banking apps.
Webhook events to expect
payment.created
payment.completed
payment.settled
payment.failed
payment.rejected
When the customer pays, monitor the associated payment token via GET /v1/aggregators/{{aggregator_id}}/payments/{{payment_id}} or your webhook handler to confirm settlement.
See also