QR Codes
Generate and manage QR codes for in-person and scan-to-pay transactions.
QR Codes API
The QR Codes API allows merchants to generate QR codes for accepting payments. Customers can scan these QR codes using the Shika Creators consumer app to pay instantly from their Shika wallet.
The QR Code Object
{
"id": "qr_abc123def456",
"object": "qr_code",
"type": "static",
"amount": null,
"currency": "GHS",
"name": "Store Counter",
"description": "Main checkout counter",
"reference": "counter-1",
"qr_code_url": "https://api.shikacreators.com/uploads/qrcodes/qr_abc123.png",
"qr_code_data": "https://paygate.shikacreators.com/pay/qr/qr_abc123def456",
"usage_limit": null,
"usage_count": 5,
"active": true,
"expires_at": null,
"livemode": true,
"metadata": {},
"created": 1705312200
}Attributes
| Attribute | Type | Description |
|---|---|---|
id | string | Unique identifier (e.g., qr_xxx) |
object | string | Always "qr_code" |
type | string | static (reusable) or dynamic (single-use/limited) |
amount | number | Fixed amount in pesewas (null = customer enters amount) |
currency | string | Currency code (GHS) |
name | string | Display name for the QR code |
description | string | Description shown to customer |
reference | string | Your own reference ID |
qr_code_url | string | URL to the QR code image (PNG) |
qr_code_data | string | Raw data encoded in the QR code |
usage_limit | integer | Maximum number of uses (null = unlimited) |
usage_count | integer | Number of times the QR code has been used |
active | boolean | Whether the QR code is active |
expires_at | string | Expiration timestamp (null = never expires) |
livemode | boolean | Whether this is a live mode QR code |
metadata | object | Custom key-value pairs |
created | integer | Unix timestamp of creation |
Create a QR Code
Generate a new QR code for accepting payments.
POST /v1/qr_codesRequest Body
| Parameter | Type | Required | Description |
|---|---|---|---|
type | string | No | static (default) or dynamic |
amount | number | No | Fixed amount in pesewas (omit for customer-entered amount) |
currency | string | No | Currency code (default: GHS) |
name | string | No | Display name |
description | string | No | Description shown to customer |
reference | string | No | Your own reference |
usage_limit | integer | No | Max uses (for dynamic QR codes) |
expires_at | string | No | ISO 8601 expiration timestamp |
metadata | object | No | Custom metadata |
# Static QR code (reusable, customer enters amount)
curl -X POST https://api.shikacreators.com/v1/qr_codes \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"type": "static",
"name": "Store Counter",
"description": "Pay at checkout"
}'// Static QR code
const qrCode = await shikacreators.qrCodes.create({
type: 'static',
name: 'Store Counter',
description: 'Pay at checkout'
})
// Dynamic QR code with fixed amount
const dynamicQR = await shikacreators.qrCodes.create({
type: 'dynamic',
amount: 5000, // GHS 50.00 in pesewas
name: 'Invoice #1234',
usage_limit: 1
})# Static QR code
qr_code = client.qr_codes.create(
type='static',
name='Store Counter',
description='Pay at checkout'
)Response
{
"id": "qr_abc123def456",
"object": "qr_code",
"type": "static",
"amount": null,
"currency": "GHS",
"name": "Store Counter",
"description": "Pay at checkout",
"qr_code_url": "https://api.shikacreators.com/uploads/qrcodes/qr_abc123.png",
"qr_code_data": "https://paygate.shikacreators.com/pay/qr/qr_abc123def456",
"usage_limit": null,
"usage_count": 0,
"active": true,
"expires_at": null,
"livemode": false,
"metadata": {},
"created": 1705312200
}Retrieve a QR Code
GET /v1/qr_codes/:idcurl https://api.shikacreators.com/v1/qr_codes/qr_abc123def456 \
-H "Authorization: Bearer sk_test_..."const qrCode = await shikacreators.qrCodes.retrieve('qr_abc123def456')qr_code = client.qr_codes.retrieve('qr_abc123def456')List QR Codes
GET /v1/qr_codesQuery Parameters
| Parameter | Type | Description |
|---|---|---|
limit | integer | Number of results (1-100, default 10) |
starting_after | string | Cursor for pagination |
type | string | Filter by type (static or dynamic) |
active | boolean | Filter by active status |
curl "https://api.shikacreators.com/v1/qr_codes?active=true&type=static" \
-H "Authorization: Bearer sk_test_..."const qrCodes = await shikacreators.qrCodes.list({
active: true,
type: 'static'
})Update a QR Code
POST /v1/qr_codes/:idRequest Body
| Parameter | Type | Description |
|---|---|---|
name | string | Updated display name |
description | string | Updated description |
active | boolean | Enable or disable the QR code |
expires_at | string | Set or update expiration |
metadata | object | Custom metadata |
curl -X POST https://api.shikacreators.com/v1/qr_codes/qr_abc123def456 \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{ "name": "Updated Counter Name", "active": false }'const qrCode = await shikacreators.qrCodes.update('qr_abc123def456', {
name: 'Updated Counter Name',
active: false
})Delete a QR Code
DELETE /v1/qr_codes/:idcurl -X DELETE https://api.shikacreators.com/v1/qr_codes/qr_abc123def456 \
-H "Authorization: Bearer sk_test_..."const deleted = await shikacreators.qrCodes.delete('qr_abc123def456')Response
{
"id": "qr_abc123def456",
"object": "qr_code",
"deleted": true
}Get QR Code Payments
Retrieve all payments made through a specific QR code.
GET /v1/qr_codes/:id/paymentscurl https://api.shikacreators.com/v1/qr_codes/qr_abc123def456/payments \
-H "Authorization: Bearer sk_test_..."const payments = await shikacreators.qrCodes.listPayments('qr_abc123def456')Download QR Code Image
Download the QR code as a PNG image.
GET /v1/qr_codes/:id/downloadcurl https://api.shikacreators.com/v1/qr_codes/qr_abc123def456/download \
-H "Authorization: Bearer sk_test_..." \
--output qr-code.pngPayment Flow
Static QR Code (Reusable)
Best for: store counters, printed displays, recurring use.
- Merchant creates a static QR code (no fixed amount)
- Customer scans the QR code
- Customer enters the payment amount
- Customer confirms with PIN
- Payment is settled instantly to merchant balance
Dynamic QR Code (Single-use)
Best for: invoices, specific orders, one-time payments.
- Merchant creates a dynamic QR code with a fixed amount
- Customer scans the QR code
- Amount is pre-filled — customer just confirms with PIN
- Payment is settled instantly
- QR code is automatically deactivated after use
Shika Wallet Integration
When a consumer scans a merchant QR code with the Shika Creators app:
- The app resolves the QR code to identify the merchant
- The consumer sees the merchant name, logo, and payment amount
- The consumer confirms with their PIN
- Funds are debited from the consumer's Shika wallet
- Funds are credited to the merchant's balance (instant settlement)
- Both parties receive notifications
Shika Wallet payments via QR codes have no external provider delay — settlement is instant. The payment appears in your dashboard immediately with status completed.
Resolving a Consumer QR Code
Preview the consumer's details before sending money. This endpoint decodes a consumer's QR payload and returns their name, masked account ID, and amount — without executing any transaction.
POST /v1/qr_codes/resolveRequest Body
| Parameter | Type | Required | Description |
|---|---|---|---|
qr_payload | string | Yes | The base64 QR code payload from the consumer's QR code |
curl -X POST https://api.shikacreators.com/v1/qr_codes/resolve \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"qr_payload": "eyJlbnRpdHkiOiJjb25zdW1lciIsImFjY291bnRJZCI6ImFjY19hYmMxMjMiLCJ0eXBlIjoic3RhdGljIn0="
}'const resolved = await fetch('https://api.shikacreators.com/v1/qr_codes/resolve', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk_test_...',
'Content-Type': 'application/json',
},
body: JSON.stringify({
qr_payload: scannedQrPayload, // base64 string from QR scan
}),
});
const consumer = await resolved.json();
// Show consumer.display_name and consumer.amount to the user before disbursingimport requests
response = requests.post(
'https://api.shikacreators.com/v1/qr_codes/resolve',
headers={
'Authorization': 'Bearer sk_test_...',
'Content-Type': 'application/json',
},
json={
'qr_payload': scanned_qr_payload, # base64 string from QR scan
},
)
consumer = response.json()
# Show consumer['display_name'] and consumer['amount'] to the user before disbursingResponse
{
"object": "qr_resolve",
"entity": "consumer",
"account_id": "acc_****a3jo",
"display_name": "John Doe",
"amount": 25.00,
"currency": "GHS",
"type": "dynamic",
"qr_active": true
}| Field | Type | Description |
|---|---|---|
object | string | Always qr_resolve |
entity | string | Always consumer |
account_id | string | Masked consumer account ID |
display_name | string | Consumer's display name |
amount | number | null | Fixed amount from QR code (in GHS), or null if no amount set |
currency | string | Currency code |
type | string | QR code type: static or dynamic |
qr_active | boolean | Whether the QR code is currently valid |
Use this endpoint to preview consumer details before calling POST /v1/disbursements/qr to send money. This is a read-only operation — no money is moved.
Error Codes
| Code | Description |
|---|---|
invalid_qr | QR payload is not valid base64 |
invalid_consumer_qr | QR code is not a consumer QR code |
qr_inactive | Dynamic QR code has been deactivated |
qr_expired | Dynamic QR code has expired |
qr_limit_reached | Dynamic QR code usage limit reached |
account_not_found | Consumer account not found or inactive |
Disbursing via Consumer QR Code
Send money directly to a consumer's Shika wallet by scanning their QR code. The consumer's account is resolved automatically from the QR payload — no need to know their account ID or phone number.
POST /v1/disbursements/qrRequest Body
| Parameter | Type | Required | Description |
|---|---|---|---|
qr_payload | string | Yes | The base64 QR code payload from the consumer's QR code |
amount | number | Conditional | Amount in GHS. Required if the QR code has no fixed amount |
currency | string | No | Currency code (default: GHS) |
description | string | No | Description for the disbursement |
reference | string | No | Your own reference ID |
metadata | object | No | Custom metadata |
If the consumer's QR code has a fixed amount (dynamic QR), you can omit the amount field and the QR amount will be used. If you provide amount, it takes priority over the QR amount.
curl -X POST https://api.shikacreators.com/v1/disbursements/qr \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"qr_payload": "eyJlbnRpdHkiOiJjb25zdW1lciIsImFjY291bnRJZCI6ImFjY19hYmMxMjMiLCJ0eXBlIjoic3RhdGljIn0=",
"amount": 25,
"description": "Refund for order #5678"
}'const disbursement = await fetch('https://api.shikacreators.com/v1/disbursements/qr', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk_test_...',
'Content-Type': 'application/json',
},
body: JSON.stringify({
qr_payload: scannedQrPayload, // base64 string from QR scan
amount: 25,
description: 'Refund for order #5678',
}),
});import requests
response = requests.post(
'https://api.shikacreators.com/v1/disbursements/qr',
headers={
'Authorization': 'Bearer sk_test_...',
'Content-Type': 'application/json',
},
json={
'qr_payload': scanned_qr_payload, # base64 string from QR scan
'amount': 25,
'description': 'Refund for order #5678',
},
)Response
{
"id": "po_abc123def456",
"object": "disbursement",
"status": "completed",
"amount": 25,
"currency": "GHS",
"fee": 0,
"destination": {
"type": "shika_wallet",
"provider": null,
"number": "acc****456",
"name": "John Doe"
},
"description": "Refund for order #5678",
"reference": null,
"metadata": null,
"provider_reference": null,
"failure_reason": null,
"created_at": "2025-01-15T10:30:00.000Z",
"completed_at": "2025-01-15T10:30:00.000Z"
}Flow
- Merchant scans a consumer's personal QR code (static or dynamic)
- The QR payload is sent to
POST /v1/disbursements/qr - The API resolves the consumer's account from the QR code
- Merchant balance is debited, consumer wallet is credited instantly
- Both parties can see the transaction in their history
QR disbursements settle instantly — no external provider delay. The disbursement is returned with status completed.
Error Codes
| Code | Description |
|---|---|
invalid_qr | QR payload is not valid base64 |
invalid_consumer_qr | QR code is not a consumer QR code |
qr_inactive | Dynamic QR code has been deactivated |
qr_expired | Dynamic QR code has expired |
qr_limit_reached | Dynamic QR code usage limit reached |
amount_required | No amount provided and QR has no fixed amount |
account_not_found | Consumer account not found or inactive |
wallet_not_active | Consumer wallet is frozen or suspended |
insufficient_balance | Merchant balance too low |