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",
"latest_attempt": {
"status": "succeeded",
"provider": "yoco",
"provider_ref": "ch_abc123xyz789",
"created_at": "2025-01-07T12:05:30Z"
}
}
}

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