Introduction
This comprehensive guide covers all payment scenarios, API endpoints, and integration patterns for the Precium Payment API.
Authentication
All API requests require Bearer token authentication.
Request Format
curl -X GET "https://gate.reviopay.com/api/v1/endpoint/" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json"API Key Types
| Key Type | Usage |
|---|---|
| Standard API Key | All non-S2S operations |
| S2S API Key | Server-to-server card operations (requires SAQ-D) |
Client Management
Clients represent your customers. Creating client records enables stored payment methods, pre-filled checkouts, and recurring billing.
Create Client
POST https://gate.reviopay.com/api/v1/clients/Request Body:
{
"email": "customer@example.com",
"phone": "+27821234567",
"full_name": "John Doe",
"street_address": "123 Main Street",
"city": "Cape Town",
"state": "Western Cape",
"zip_code": "8001",
"country": "ZA",
"shipping_street_address": "123 Main Street",
"shipping_city": "Cape Town",
"shipping_state": "Western Cape",
"shipping_zip_code": "8001",
"shipping_country": "ZA"
}Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Customer email address |
phone | string | No | Phone number (E.164 format) |
full_name | string | No | Customer full name |
personal_code | string | No | ID number (for DebiCheck) |
street_address | string | No | Billing address |
city | string | No | Billing city |
state | string | No | Billing state/province |
zip_code | string | No | Billing postal code |
country | string | No | Billing country (ISO 3166-1 alpha-2) |
bank_account | string | No | Bank account number |
bank_code | string | No | Bank branch/SWIFT code |
cc | array | No | CC email addresses for receipts |
bcc | array | No | BCC email addresses |
Response:
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"type": "client",
"created_on": 1619740800,
"updated_on": 1619740800,
"email": "customer@example.com",
"phone": "+27821234567",
"full_name": "John Doe"
}List Clients
GET https://gate.reviopay.com/api/v1/clients/Query Parameters:
| Parameter | Description |
|---|---|
q | Fuzzy search across email, phone, name, etc. |
email | Search by email |
phone | Search by phone |
Get Client
GET https://gate.reviopay.com/api/v1/clients/{client_id}/Update Client
PUT https://gate.reviopay.com/api/v1/clients/{client_id}/Partial Update Client
PATCH https://gate.reviopay.com/api/v1/clients/{client_id}/Delete Client
DELETE https://gate.reviopay.com/api/v1/clients/{client_id}/List Client Recurring Tokens
Retrieve stored payment methods for a client:
GET https://gate.reviopay.com/api/v1/clients/{client_id}/recurring_tokens/Response:
{
"results": [
{
"id": "tok_abc123...",
"type": "recurring_token",
"payment_method": "visa",
"description": "Visa ending in 1111",
"created_on": 1619740800
}
]
}Delete Client Token
DELETE https://gate.reviopay.com/api/v1/clients/{client_id}/recurring_tokens/{token_id}/Purchase Management
A Purchase represents a payment request. Create a purchase to generate a checkout URL or process a payment.
Create Purchase
POST https://gate.reviopay.com/api/v1/purchases/Full Request Body:
{
"client_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"purchase": {
"currency": "ZAR",
"language": "en",
"products": [
{
"name": "Product Name",
"price": 10000,
"quantity": "1",
"discount": 0,
"tax_percent": "15",
"category": "electronics"
}
],
"notes": "Special instructions",
"due_strict": false
},
"brand_id": "YOUR_BRAND_ID",
"reference": "INV-2026-0001",
"payment_method_whitelist": ["visa", "mastercard", "ozow"],
"success_redirect": "https://www.example.com/success",
"failure_redirect": "https://www.example.com/failure",
"cancel_redirect": "https://www.example.com/cancel",
"success_callback": "https://www.example.com/callback",
"send_receipt": true,
"force_recurring": false,
"skip_capture": false,
"due": 1735689600
}Core Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
brand_id | uuid | Yes | Your brand identifier |
purchase.currency | string | Yes | ISO 4217 currency code |
purchase.products | array | Yes | Array of product objects |
purchase.products[].name | string | Yes | Product name |
purchase.products[].price | integer | Yes | Price in cents |
Client Parameters (use client_id OR client object):
| Parameter | Type | Description |
|---|---|---|
client_id | uuid | Existing client ID |
client.email | string | Create/find client by email |
client.phone | string | Client phone |
client.full_name | string | Client name |
Optional Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
reference | string | --- | Your order reference |
payment_method_whitelist | array | all | Allowed payment methods |
success_redirect | string | --- | Redirect URL on success |
failure_redirect | string | --- | Redirect URL on failure |
cancel_redirect | string | --- | Redirect URL on cancel |
success_callback | string | --- | Webhook URL for this purchase |
send_receipt | boolean | false | Email receipt on payment |
force_recurring | boolean | false | Force token storage |
skip_capture | boolean | false | Pre-authorization only |
due | integer | --- | Due date (Unix timestamp) |
purchase.due_strict | boolean | false | Block payment after due date |
purchase.language | string | en | Checkout language |
Response:
{
"id": "pur_abc123...",
"type": "purchase",
"status": "created",
"created_on": 1619740800,
"checkout_url": "https://gate.reviopay.com/p/abc123.../",
"direct_post_url": "https://gate.reviopay.com/p/abc123.../post/",
"purchase": {
"currency": "ZAR",
"total": 10000,
"products": [...]
},
"brand_id": "YOUR_BRAND_ID",
"reference": "INV-2026-0001"
}Get Purchase
GET https://gate.reviopay.com/api/v1/purchases/{purchase_id}/List purchases for a client
There is no endpoint that lists every purchase. Purchases are listed per client, through the client’s feed, which returns that client’s purchases and refunds together.
GET https://gate.reviopay.com/api/v1/clients/{client_id}/feed/GET /api/v1/purchases/ does not exist. /purchases/ accepts POST only.
Retrieving a single purchase is
GET /api/v1/purchases/{purchase_id}/, which is a different thing from
listing them.
Hold your own index of purchase ids against your orders. Reconciliation matches
on gateway_transaction_id, which is that same id, so you need it stored on
your side regardless. See Reconciliation files.
Charge Purchase
Charge a purchase using a recurring token:
POST https://gate.reviopay.com/api/v1/purchases/{purchase_id}/charge/Request Body:
{
"recurring_token": "tok_abc123..."
}Capture Pre-Authorization
Capture funds from a pre-authorized purchase:
POST https://gate.reviopay.com/api/v1/purchases/{purchase_id}/capture/Request Body:
{
"amount": 10000
}Release Pre-Authorization
Release held funds without capturing:
POST https://gate.reviopay.com/api/v1/purchases/{purchase_id}/release/Cancel Purchase
POST https://gate.reviopay.com/api/v1/purchases/{purchase_id}/cancel/Resend Invoice
POST https://gate.reviopay.com/api/v1/purchases/{purchase_id}/resend_invoice/Mark as Paid
Manually mark a purchase as paid (for offline payments):
POST https://gate.reviopay.com/api/v1/purchases/{purchase_id}/mark_as_paid/Payment Scenarios
Scenario 1: Payment Link (Minimal Integration)
Generate a payment link to send via email or messaging:
POST https://gate.reviopay.com/api/v1/purchases/{
"client": {
"email": "customer@example.com"
},
"purchase": {
"currency": "ZAR",
"products": [
{
"name": "Invoice #12345",
"price": 150000
}
]
},
"brand_id": "YOUR_BRAND_ID"
}Share the checkout_url from the response with your customer.
Scenario 2: E-commerce Checkout (Redirect Flow)
Standard e-commerce integration with redirect:
Create Purchase:
{
"client_id": "existing_client_id",
"purchase": {
"currency": "ZAR",
"products": [
{"name": "Widget", "price": 5000, "quantity": "2"},
{"name": "Shipping", "price": 5000}
]
},
"brand_id": "YOUR_BRAND_ID",
"reference": "ORD-12345",
"payment_method_whitelist": ["visa", "mastercard", "ozow"],
"success_redirect": "https://www.example.com/order/ORD-12345/success",
"failure_redirect": "https://www.example.com/order/ORD-12345/failure",
"cancel_redirect": "https://www.example.com/cart"
}Scenario 3: Pre-Authorization (Hold Funds)
Reserve funds for later capture (hotels, car rentals):
Step 1: Create Pre-Auth Purchase
{
"client_id": "client_uuid",
"purchase": {
"currency": "ZAR",
"products": [
{"name": "Hotel Deposit", "price": 500000}
]
},
"brand_id": "YOUR_BRAND_ID",
"skip_capture": true,
"success_redirect": "https://www.example.com/booking/success",
"failure_redirect": "https://www.example.com/booking/failure"
}Step 2: Capture When Ready
POST https://gate.reviopay.com/api/v1/purchases/{purchase_id}/capture/{
"amount": 450000
}Or Release if Not Needed:
POST https://gate.reviopay.com/api/v1/purchases/{purchase_id}/release/Scenario 4: Restrict Payment Methods
Only allow specific payment methods:
{
"payment_method_whitelist": ["visa", "mastercard"],
...
}Available Payment Methods:
| Value | Description |
|---|---|
visa | Visa cards |
mastercard | Mastercard |
maestro | Maestro cards |
ozow | Ozow Instant EFT |
capitec_pay | Capitec Pay |
capitec_pay_recurring | Capitec Pay Recurring |
zapper | Zapper |
debicheck | DebiCheck |
eft | Traditional debit order / EFT collection |
debicheck | Authenticated debit order (DebiCheck) |
Scenario 5: Invoice with Due Date
Create an invoice that’s due by a specific date:
{
"client_id": "client_uuid",
"purchase": {
"currency": "ZAR",
"due_strict": true,
"products": [
{"name": "Consulting Services", "price": 2500000}
]
},
"brand_id": "YOUR_BRAND_ID",
"due": 1735689600,
"reference": "INV-2026-0042"
}Setting due_strict: true prevents payment after the due date.
Hosted Checkout
The hosted checkout page is a PCI-compliant payment form hosted by Precium.
Checkout URL
After creating a purchase, redirect customers to the checkout_url:
https://gate.reviopay.com/p/{purchase_id}/Customization
Checkout appearance is configured via your Brand settings in the dashboard:
- Logo
- Colors
- Business name
- Support contact
Checkout Flow
Direct Post Integration
Direct Post allows you to create a custom checkout form while keeping card data out of your servers (SAQ A-EP compliance).
How It Works
- Create a purchase to get a
direct_post_url - Build an HTML form that POSTs directly to the
direct_post_url - Card data goes directly from the browser to Precium
- Customer is redirected based on the result
PCI Compliance Note
Important: Direct Post integration requires PCI DSS SAQ A-EP compliance. Contact your account manager before implementing.
Form Structure
<form method='POST' action='{direct_post_url}'>
<input type='hidden' name='pm' value='visa'>
<input type='text' name='cardholder_name'
maxlength='30' required>
<input type='text' name='card_number'
maxlength='19' required>
<input type='text' name='expires'
placeholder='MM/YY' maxlength='5' required>
<input type='text' name='cvc'
maxlength='4' required>
<input type='checkbox' name='remember_card' value='on'>
<label>Save card for future payments</label>
<button type='submit'>Pay Now</button>
</form>Field Requirements
| Field | Format | Validation |
|---|---|---|
pm | string | Payment method: visa, mastercard, etc. |
cardholder_name | string | Latin letters, space, apostrophe, dot, dash. Max 30 chars |
card_number | string | Digits only, no spaces. Max 19 chars |
expires | string | MM/YY format |
cvc | string | 3-4 digits |
remember_card | string | on to save card |
Direct Post for Other Payment Methods
Capitec Pay
<form method='POST' action='{direct_post_url}'>
<input type='hidden' name='pm' value='capitec_pay'>
<input type='hidden' name='capitec_radio' value='mobile_number'>
<input type='text' name='capitec_mobile_number' required>
<input type='hidden' name='accepted_terms' value='on'>
<button type='submit'>Pay with Capitec Pay</button>
</form>Recurring Payments
Tokenizing Payment Methods
To save a payment method for future charges, set force_recurring: true:
{
"client_id": "client_uuid",
"purchase": {
"currency": "ZAR",
"products": [
{"name": "Initial Payment", "price": 10000}
]
},
"brand_id": "YOUR_BRAND_ID",
"force_recurring": true,
"success_redirect": "https://www.example.com/success"
}After successful payment, the purchase ID becomes a recurring token.
Zero-Amount Tokenization
To tokenize a card without charging:
{
"client_id": "client_uuid",
"purchase": {
"currency": "ZAR",
"products": [
{"name": "Card Authorization", "price": 0}
]
},
"brand_id": "YOUR_BRAND_ID",
"skip_capture": true,
"force_recurring": true,
"success_redirect": "https://www.example.com/success"
}Charging with Recurring Token
Step 1: Create Purchase for Upcoming Charge
POST https://gate.reviopay.com/api/v1/purchases/{
"client_id": "client_uuid",
"purchase": {
"currency": "ZAR",
"products": [
{"name": "Monthly Subscription", "price": 29900}
]
},
"brand_id": "YOUR_BRAND_ID",
"reference": "SUB-2026-01"
}Step 2: Charge Using Token
POST https://gate.reviopay.com/api/v1/purchases/{new_purchase_id}/charge/{
"recurring_token": "{original_purchase_id}"
}Listing Client Tokens
GET https://gate.reviopay.com/api/v1/clients/{client_id}/recurring_tokens/Billing Templates (Subscriptions)
No longer offered. Subscription billing via billing templates is not sold to new integrations. This section is retained for existing implementations only. For recurring charges, use tokenisation and merchant-initiated charges — see Recurring Payments above.
Billing templates automate recurring billing with configurable schedules.
Create Billing Template
POST https://gate.reviopay.com/api/v1/billing_templates/{
"title": "Premium Monthly Subscription",
"purchase": {
"currency": "ZAR",
"products": [
{
"name": "Premium Plan",
"price": 29900
}
]
},
"brand_id": "YOUR_BRAND_ID",
"is_subscription": true,
"subscription_active": true,
"subscription_period": 1,
"subscription_period_units": "months",
"number_of_billing_cycles": 12,
"subscription_due_period": 3,
"subscription_due_period_units": "days",
"subscription_trial_periods": 0,
"force_recurring": true,
"success_redirect": "https://www.example.com/subscribe/success",
"failure_redirect": "https://www.example.com/subscribe/failure"
}Subscription Parameters:
| Parameter | Type | Description |
|---|---|---|
is_subscription | boolean | Enable subscription mode |
subscription_active | boolean | Activate automatic billing |
subscription_period | integer | Billing frequency number |
subscription_period_units | string | days, weeks, months, years |
number_of_billing_cycles | integer | Total billing cycles (0 = infinite) |
subscription_due_period | integer | Days before due date to create invoice |
subscription_trial_periods | integer | Free trial periods |
subscription_charge_period_end | boolean | Charge at end of period |
Add Subscriber
POST https://gate.reviopay.com/api/v1/billing_templates/{template_id}/add_subscriber/{
"client_id": "client_uuid",
"invoice_reference": "SUB-CUST-001",
"subscription_confirm_each_order": false,
"send_invoice_on_add_subscriber": false,
"send_invoice_on_charge_failure": true,
"send_receipt": true
}Response includes:
billing_template_client.id- Subscription IDpurchase.checkout_url- URL for initial tokenizationpurchase.direct_post_url- Direct post URL
Add Subscriber with Override
Customise pricing for specific subscribers:
{
"client_id": "client_uuid",
"invoice_reference": "SUB-VIP-001",
"override": {
"products": [
{
"name": "VIP Plan (Discounted)",
"price": 19900
}
]
}
}List Billing Templates
GET https://gate.reviopay.com/api/v1/billing_templates/Get Billing Template
GET https://gate.reviopay.com/api/v1/billing_templates/{template_id}/List Subscribers
GET https://gate.reviopay.com/api/v1/billing_templates/{template_id}/clients/Update Subscriber
A subscriber is updated with PATCH, not PUT. The update is partial: send
only the fields you are changing.
PATCH https://gate.reviopay.com/api/v1/billing_templates/{template_id}/clients/{subscription_id}/There is no operation to remove a subscriber. The API exposes only GET
and PATCH on this path. To stop billing a subscriber, change the subscription
through PATCH or cancel the billing template.
Refunds
Full Refund
POST https://gate.reviopay.com/api/v1/purchases/{purchase_id}/refund/{}Partial Refund
POST https://gate.reviopay.com/api/v1/purchases/{purchase_id}/refund/{
"amount": 5000
}Refund Response
{
"id": "ref_abc123...",
"type": "refund",
"status": "pending",
"amount": 5000,
"purchase_id": "pur_abc123..."
}Refund Limitations
- Refunds must not exceed the original payment amount
- Partial refunds can be issued multiple times until fully refunded
- Some payment methods may not support refunds (check payment method details)
Webhooks
Webhooks notify your application of payment events in real-time.
Available Events
Subscribe only to the events you act on. Event names are exact — a name that does not appear below will silently never fire.
| Event | Fires when |
|---|---|
purchase.created | A purchase has been created |
purchase.viewed | The payer opened the checkout |
purchase.paid | Payment captured and complete |
purchase.payment_failure | Payment failed |
purchase.cancelled | Purchase cancelled |
purchase.hold | Funds reserved by an authorisation |
purchase.preauthorized | Zero-amount card verification succeeded |
purchase.captured | A hold was captured |
purchase.capture_failure | Capture failed |
purchase.released | A hold was released without capture |
purchase.release_failure | Release failed |
purchase.pending_charge | Authorisation in flight at the acquirer |
purchase.pending_capture | Capture accepted, still processing |
purchase.pending_release | Release accepted, still processing |
purchase.pending_refund | Refund accepted, still processing |
payment.refunded | A refund completed. Treat this as the completion signal |
purchase.refund_failure | Refund failed after acceptance |
payment.charged_back | A chargeback was raised |
payment.chargeback_reversed | A chargeback was reversed in your favour |
purchase.settled | The transaction settled |
purchase.recurring_token_deleted | A stored card token was deleted |
Note that purchase.pending, purchase.authorized, purchase.refunded, subscription.created and subscription.cancelled are not valid event names. Use purchase.pending_charge, purchase.preauthorized and payment.refunded instead.
Create Webhook
POST https://gate.reviopay.com/api/v1/webhooks/{
"url": "https://www.example.com/webhooks/precium",
"events": ["purchase.paid", "purchase.payment_failure", "payment.refunded"]
}Webhook Payload
{
"id": "evt_abc123...",
"event_type": "purchase.paid",
"created_on": 1619740800,
"data": {
"id": "pur_abc123...",
"status": "paid",
"purchase": {
"total": 10000,
"currency": "ZAR"
},
"reference": "ORD-12345",
"payment_method_details": {
"card": {
"brand": "visa",
"last4": "1111"
}
}
}
}Webhook Security
Verify webhook authenticity using the signature header:
import base64
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
# Callbacks are signed with ASYMMETRIC RSA. There is no shared secret.
# X-Signature = base64( RSA PKCS#1 v1.5 over the SHA-256 digest of the raw body )
# webhook subscriptions -> public_key on the webhook object
# per-purchase success_callback -> GET /public_key/
def verify_webhook(raw_body: bytes, signature_b64: str, public_key_pem: str) -> bool:
key = serialization.load_pem_public_key(public_key_pem.encode())
try:
key.verify(
base64.b64decode(signature_b64),
raw_body, # raw bytes; never a re-serialised dict
padding.PKCS1v15(),
hashes.SHA256(),
)
return True
except Exception:
return FalseWebhook Best Practices
- Respond quickly — Return 200 within 5 seconds
- Process asynchronously — Queue for background processing
- Handle duplicates — Use event ID for idempotency
- Verify signatures — Validate webhook authenticity
Payouts
Not currently available. Payouts are not yet released. The endpoints are documented here for reference only — do not build against them, and speak to your account manager before planning a disbursement flow.
Send funds to recipients using stored payment methods.
Create Payout
POST https://gate.reviopay.com/api/v1/payouts/{
"client": {
"email": "recipient@example.com",
"phone": "+27821234567"
},
"payment": {
"amount": 50000,
"currency": "ZAR",
"description": "Commission Payment"
},
"brand_id": "YOUR_BRAND_ID"
}Execute Payout
POST {execution_url}{
"cardholder_name": "John Doe",
"card_number": "4444333322221111",
"expiry_month": "12",
"expiry_year": "28"
}Get Payout Status
GET https://gate.reviopay.com/api/v1/payouts/{payout_id}/API Response Codes
| Code | Description |
|---|---|
200 | Success |
201 | Created |
204 | No content (successful deletion) |
400 | Bad request (validation error) |
401 | Unauthorized (invalid API key) |
404 | Not found |
409 | Conflict (duplicate request) |
429 | Rate limited |
500 | Server error |
Rate Limiting
API requests are rate-limited to ensure fair usage:
- Standard: 10 transactions per second, per IP address
- Burst: 200 requests per minute (short periods)
Rate limit headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1619740860Pagination
List endpoints use cursor-based pagination:
{
"results": [...],
"next": "https://gate.reviopay.com/api/v1/purchases/?cursor=abc123",
"previous": null
}Follow the next URL to retrieve additional pages.