Static QR codes are reusable. They identify your aggregator merchant but leave amount and payer details open, making them ideal for countertop displays and printed invoices.
Prerequisites
{{aggregator_id}} and {{secret_key}}
aggregator_merchant_identifier for the merchant collecting funds
Flow summary
Load merchant identifiers
Retrieve the aggregator_merchant_identifier from the merchant creation response.
Create the static QR
Call POST /v1/aggregators/{{aggregator_id}}/qrs with type: STATIC.
Render and share
Render the returned data.code and distribute it digitally or physically.
Reconcile payments
Match webhook payloads or list QR payments to reconcile each payment.
Set {{base_url}} to https://dev.api.getsafepay.com/raastwire in Sandbox or https://api.getsafepay.com/raastwire in Production.
Static QR
Request Fields
API Example
Sample Response
Set to STATIC for reusable codes.
aggregator_merchant_identifier
Token returned from merchant creation (data.token).
Recommended to prevent duplicate QR creation on retries.
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}}"
}'
const baseUrl = '{{base_url}}';
const aggregatorId = '{{aggregator_id}}';
const secretKey = '{{secret_key}}';
const aggregatorMerchantIdentifier = '{{aggregator_merchant_identifier}}';
const staticQrResponse = 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 (!staticQrResponse.ok) {
const errorPayload = await staticQrResponse.json();
throw new Error(`Static QR failed: ${staticQrResponse.status} ${errorPayload.message}`);
}
type CreateQrPayload = {
api_version: string;
data: {
token: string;
code: string;
type: 'STATIC' | 'DYNAMIC';
aggregator_merchant_id: string;
created_at: string;
};
};
const staticQrPayload: CreateQrPayload = await staticQrResponse.json();
console.log('Reusable QR code', staticQrPayload.data.code);
{
"api_version": "v1",
"data": {
"token": "qr_4076ad61-75cf-44a3-97a1-b66643ea3bf2",
"aggregator_merchant_id": "am_be454a50-7612-4dc6-a97e-284ebbe7ae93",
"type": "STATIC",
"code": "000201010211...",
"created_at": "2025-04-28T10:26:48Z"
}
}
Rendering tips
- Feed
data.code to any QR generation library (qrcode.react, qr-image, etc.).
- Print the QR with usage instructions or embed it in digital invoices.
- Use
GET /v1/aggregators/{{aggregator_id}}/qrs/{{qr_id}}/payments to list transactions tied to this QR.
Webhook events to expect
payment.created
payment.completed
payment.settled
payment.failed
payment.rejected
See also