Skip to content

Payments API

Payments API

Accept payments from customers via card and other payment methods.

Payment Flow

Redirect Flow

  1. Initiate payment - Call POST /api/v1/payments/init with order details
  2. Redirect user - Send customer to the payment_url returned
  3. Customer pays - Customer completes payment on QRPay hosted page
  4. Receive webhook - Get notified of payment result via webhook
  5. Fulfill order - Process the order based on payment status

QR Code Flow

  1. Initiate payment - Call POST /api/v1/payments/init with include_qr: true
  2. Display QR code - Show the QR code image to the customer
  3. Customer scans - Customer scans QR with their phone and opens payment page
  4. Customer pays - Customer completes payment on their mobile device
  5. Receive webhook - Get notified of payment result via webhook
  6. Fulfill order - Process the order based on payment status

Payment Intent Types

Intent TypeBehaviorUse Case
purchase_and_redeemUser pays → voucher issued and redeemed → auto-redirectInstant fulfillment, seamless UX
purchaseUser pays → voucher issued → code shownUser saves voucher for later

Initiate Payment

Endpoint: POST /api/v1/payments/init

Headers:

  • X-API-Key: <secret>
  • Content-Type: application/json
  • Idempotency-Key: <unique-id> (optional but recommended)

Request:

{
"intent_type": "purchase_and_redeem",
"amount": 10000,
"currency": "ZAR",
"merchant_order_id": "ORDER-2025-001",
"include_qr": true,
"success_url": "https://merchant.example/orders/001/success",
"cancel_url": "https://merchant.example/orders/001/cancel",
"failure_url": "https://merchant.example/orders/001/failure",
"webhook_url": "https://merchant.example/webhooks/qrpay",
"customer_info": {
"email": "customer@example.com",
"phone": "+27821234567"
}
}

Request Parameters:

ParameterTypeRequiredDescription
intent_typestringNopurchase or purchase_and_redeem (default: purchase_and_redeem)
amountintegerYesAmount in cents (5500-300000)
currencystringYesCurrency code (ZAR only)
merchant_order_idstringYesYour unique order reference (max 255 chars)
include_qrbooleanNoInclude QR code data in response (default: false)
success_urlstringNoRedirect URL after successful payment
cancel_urlstringNoRedirect URL if user cancels
failure_urlstringNoRedirect URL after failed payment
webhook_urlstringNoURL to receive payment notifications
customer_infoobjectNoCustomer email and/or phone for notifications

Validation Rules:

  • amount: 5500 to 300000 cents (R55 to R3000)
  • currency: Only ZAR supported
  • merchant_order_id: Max 255 chars, must be unique per merchant
  • URLs: Must be HTTPS (except in sandbox)
  • customer_info: Optional, used for confirmation emails/SMS

Response (200 OK):

{
"status": "success",
"data": {
"payment_id": "01932e5d-7f8a-7890-b123-456789abcdef",
"status": "created",
"payment_url": "https://pay.qrpay2.com/01932e5d-7f8a-7890-b123-456789abcdef"
},
"metadata": {
"timestamp": "2025-01-07T12:00:00Z",
"api_version": "v1"
}
}

Response with QR Code (when include_qr: true):

{
"status": "success",
"data": {
"payment_id": "01932e5d-7f8a-7890-b123-456789abcdef",
"status": "created",
"payment_url": "https://pay.qrpay2.com/01932e5d-7f8a-7890-b123-456789abcdef",
"qr_code": {
"content": "https://pay.qrpay2.com/01932e5d-7f8a-7890-b123-456789abcdef",
"image_url": "https://pay.qrpay2.com/qr/eyJ1IjoiaHR0cHM6Ly9wYXku...png"
}
},
"metadata": {
"timestamp": "2025-01-07T12:00:00Z",
"api_version": "v1"
}
}

QR Code Response Fields:

FieldDescription
qr_code.contentThe URL encoded in the QR code (same as payment_url)
qr_code.image_urlSigned URL to fetch the QR code image

Next Steps:

  1. Store payment_id in your database
  2. Redirect user to payment_url or display the QR code image
  3. Wait for webhook notification

QR Code Images

When you request a QR code with include_qr: true, the response includes an image_url that serves the QR code image.

Image Formats

The QR code image URL supports two formats:

FormatURL SuffixContent-TypeUse Case
PNG.png (default)image/pngWeb, mobile apps, print
SVG.svgimage/svg+xmlScalable graphics, high-DPI displays

To get an SVG image, replace .png with .svg in the image_url:

# PNG (default)
https://pay.qrpay2.com/qr/eyJ1IjoiaHR0cHM6Ly9wYXku...png
# SVG
https://pay.qrpay2.com/qr/eyJ1IjoiaHR0cHM6Ly9wYXku...svg

Token Expiry

QR code image URLs are signed and expire after 24 hours.

Display Recommendations

  • Minimum size: 128x128 pixels for reliable scanning
  • Recommended size: at least 256x256 pixels for most displays

Get Payment Status

Endpoint: GET /api/v1/payments/{payment_id}

Terminal window
curl -X GET "https://api.qrpay2.com/api/v1/payments/$PAYMENT_ID" \
-H "X-API-Key: $API_KEY"

Response (200 OK):

{
"status": "success",
"data": {
"payment_id": "01932e5d-7f8a-7890-b123-456789abcdef",
"status": "captured",
"amount": 10000,
"currency": "ZAR",
"fee_cents": 250,
"net_amount_cents": 9750,
"latest_attempt": {
"status": "succeeded",
"provider": "yoco",
"provider_ref": "ch_abc123xyz789",
"created_at": "2025-01-07T12:05:30Z"
}
}
}

Platform Fees

A platform fee is charged on successful payments and deducted from the payment amount. The net_amount_cents reflects the amount credited to your merchant balance after the fee. Contact your account manager for specific fee rates applicable to your account.

Payment States

StateDescriptionTerminal?
createdPayment initialized, awaiting user actionNo
initiatedUser started checkout with providerNo
awaiting_webhookProvider processing, awaiting confirmationNo
capturedPayment successful, fulfillment completedYes
failedPayment failed, no charge to userYes
cancelledUser cancelled paymentYes

Terminal States

Once a payment reaches captured, failed, or cancelled, it cannot change. These states trigger webhook notifications.

Code Examples

cURL - Basic Payment

#!/bin/bash
API_BASE="https://api.qrpay2.com"
API_KEY="sec1-prd-YOUR_KEY..."
IDEMP_KEY="ORDER-123-$(date +%s)-$(uuidgen)"
curl -X POST "$API_BASE/api/v1/payments/init" \
-H "X-API-Key: $API_KEY" \
-H "Idempotency-Key: $IDEMP_KEY" \
-H "Content-Type: application/json" \
-d '{
"intent_type": "purchase_and_redeem",
"amount": 10000,
"currency": "ZAR",
"merchant_order_id": "ORDER-2025-001",
"success_url": "https://merchant.example/success",
"cancel_url": "https://merchant.example/cancel",
"failure_url": "https://merchant.example/failure",
"webhook_url": "https://merchant.example/webhooks/qrpay"
}'

cURL - Payment with QR Code

#!/bin/bash
API_BASE="https://api.qrpay2.com"
API_KEY="sec1-prd-YOUR_KEY..."
IDEMP_KEY="ORDER-456-$(date +%s)-$(uuidgen)"
# Create payment with QR code
RESPONSE=$(curl -s -X POST "$API_BASE/api/v1/payments/init" \
-H "X-API-Key: $API_KEY" \
-H "Idempotency-Key: $IDEMP_KEY" \
-H "Content-Type: application/json" \
-d '{
"intent_type": "purchase_and_redeem",
"amount": 10000,
"currency": "ZAR",
"merchant_order_id": "ORDER-2025-002",
"include_qr": true,
"webhook_url": "https://merchant.example/webhooks/qrpay"
}')
# Extract QR image URL
QR_IMAGE_URL=$(echo $RESPONSE | jq -r '.data.qr_code.image_url')
echo "QR Code Image: $QR_IMAGE_URL"

Python - Basic Payment

import requests
import os
API_BASE = "https://api.qrpay2.com"
API_KEY = os.environ["QRPAY_API_KEY"]
def create_payment(amount_cents: int, order_id: str) -> dict:
response = requests.post(
f"{API_BASE}/api/v1/payments/init",
headers={
"X-API-Key": API_KEY,
"Content-Type": "application/json",
},
json={
"intent_type": "purchase_and_redeem",
"amount": amount_cents,
"currency": "ZAR",
"merchant_order_id": order_id,
"success_url": f"https://example.com/orders/{order_id}/success",
"cancel_url": f"https://example.com/orders/{order_id}/cancel",
"failure_url": f"https://example.com/orders/{order_id}/failure",
"webhook_url": "https://example.com/webhooks/qrpay",
},
)
response.raise_for_status()
return response.json()["data"]
# Usage
payment = create_payment(10000, "ORDER-2025-001")
print(f"Redirect to: {payment['payment_url']}")

Python - Payment with QR Code

import requests
import os
API_BASE = "https://api.sandbox.qrpay2.com"
API_KEY = os.environ["QRPAY_API_KEY"]
def create_payment_with_qr(amount_cents: int, order_id: str) -> dict:
"""Create a payment and get QR code for display."""
response = requests.post(
f"{API_BASE}/api/v1/payments/init",
headers={
"X-API-Key": API_KEY,
"Content-Type": "application/json",
},
json={
"intent_type": "purchase_and_redeem",
"amount": amount_cents,
"currency": "ZAR",
"merchant_order_id": order_id,
"include_qr": True,
"webhook_url": "https://example.com/webhooks/qrpay",
},
)
response.raise_for_status()
return response.json()["data"]
def download_qr_image(image_url: str, filename: str = "payment_qr.png"):
"""Download QR code image to file."""
response = requests.get(image_url)
response.raise_for_status()
with open(filename, "wb") as f:
f.write(response.content)
return filename
# Usage
payment = create_payment_with_qr(10000, "ORDER-2025-002")
print(f"Payment ID: {payment['payment_id']}")
print(f"Payment URL: {payment['payment_url']}")
print(f"QR Content: {payment['qr_code']['content']}")
print(f"QR Image URL: {payment['qr_code']['image_url']}")

Error Responses

HTTP CodeDescriptionAction
400Validation failedFix request parameters
401Authentication failedCheck API key
409Duplicate merchant_order_idUse unique order ID
500Internal server errorRetry with backoff