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

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

AttributeTypeDescription
idstringUnique identifier (e.g., qr_xxx)
objectstringAlways "qr_code"
typestringstatic (reusable) or dynamic (single-use/limited)
amountnumberFixed amount in pesewas (null = customer enters amount)
currencystringCurrency code (GHS)
namestringDisplay name for the QR code
descriptionstringDescription shown to customer
referencestringYour own reference ID
qr_code_urlstringURL to the QR code image (PNG)
qr_code_datastringRaw data encoded in the QR code
usage_limitintegerMaximum number of uses (null = unlimited)
usage_countintegerNumber of times the QR code has been used
activebooleanWhether the QR code is active
expires_atstringExpiration timestamp (null = never expires)
livemodebooleanWhether this is a live mode QR code
metadataobjectCustom key-value pairs
createdintegerUnix timestamp of creation

Create a QR Code

Generate a new QR code for accepting payments.

POST /v1/qr_codes

Request Body

ParameterTypeRequiredDescription
typestringNostatic (default) or dynamic
amountnumberNoFixed amount in pesewas (omit for customer-entered amount)
currencystringNoCurrency code (default: GHS)
namestringNoDisplay name
descriptionstringNoDescription shown to customer
referencestringNoYour own reference
usage_limitintegerNoMax uses (for dynamic QR codes)
expires_atstringNoISO 8601 expiration timestamp
metadataobjectNoCustom 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/:id
curl 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_codes

Query Parameters

ParameterTypeDescription
limitintegerNumber of results (1-100, default 10)
starting_afterstringCursor for pagination
typestringFilter by type (static or dynamic)
activebooleanFilter 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/:id

Request Body

ParameterTypeDescription
namestringUpdated display name
descriptionstringUpdated description
activebooleanEnable or disable the QR code
expires_atstringSet or update expiration
metadataobjectCustom 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/:id
curl -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/payments
curl 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/download
curl https://api.shikacreators.com/v1/qr_codes/qr_abc123def456/download \
  -H "Authorization: Bearer sk_test_..." \
  --output qr-code.png

Payment Flow

Static QR Code (Reusable)

Best for: store counters, printed displays, recurring use.

  1. Merchant creates a static QR code (no fixed amount)
  2. Customer scans the QR code
  3. Customer enters the payment amount
  4. Customer confirms with PIN
  5. Payment is settled instantly to merchant balance

Dynamic QR Code (Single-use)

Best for: invoices, specific orders, one-time payments.

  1. Merchant creates a dynamic QR code with a fixed amount
  2. Customer scans the QR code
  3. Amount is pre-filled — customer just confirms with PIN
  4. Payment is settled instantly
  5. QR code is automatically deactivated after use

Shika Wallet Integration

When a consumer scans a merchant QR code with the Shika Creators app:

  1. The app resolves the QR code to identify the merchant
  2. The consumer sees the merchant name, logo, and payment amount
  3. The consumer confirms with their PIN
  4. Funds are debited from the consumer's Shika wallet
  5. Funds are credited to the merchant's balance (instant settlement)
  6. 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/resolve

Request Body

ParameterTypeRequiredDescription
qr_payloadstringYesThe 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 disbursing
import 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 disbursing

Response

{
  "object": "qr_resolve",
  "entity": "consumer",
  "account_id": "acc_****a3jo",
  "display_name": "John Doe",
  "amount": 25.00,
  "currency": "GHS",
  "type": "dynamic",
  "qr_active": true
}
FieldTypeDescription
objectstringAlways qr_resolve
entitystringAlways consumer
account_idstringMasked consumer account ID
display_namestringConsumer's display name
amountnumber | nullFixed amount from QR code (in GHS), or null if no amount set
currencystringCurrency code
typestringQR code type: static or dynamic
qr_activebooleanWhether 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

CodeDescription
invalid_qrQR payload is not valid base64
invalid_consumer_qrQR code is not a consumer QR code
qr_inactiveDynamic QR code has been deactivated
qr_expiredDynamic QR code has expired
qr_limit_reachedDynamic QR code usage limit reached
account_not_foundConsumer 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/qr

Request Body

ParameterTypeRequiredDescription
qr_payloadstringYesThe base64 QR code payload from the consumer's QR code
amountnumberConditionalAmount in GHS. Required if the QR code has no fixed amount
currencystringNoCurrency code (default: GHS)
descriptionstringNoDescription for the disbursement
referencestringNoYour own reference ID
metadataobjectNoCustom 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

  1. Merchant scans a consumer's personal QR code (static or dynamic)
  2. The QR payload is sent to POST /v1/disbursements/qr
  3. The API resolves the consumer's account from the QR code
  4. Merchant balance is debited, consumer wallet is credited instantly
  5. 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

CodeDescription
invalid_qrQR payload is not valid base64
invalid_consumer_qrQR code is not a consumer QR code
qr_inactiveDynamic QR code has been deactivated
qr_expiredDynamic QR code has expired
qr_limit_reachedDynamic QR code usage limit reached
amount_requiredNo amount provided and QR has no fixed amount
account_not_foundConsumer account not found or inactive
wallet_not_activeConsumer wallet is frozen or suspended
insufficient_balanceMerchant balance too low