Table of contents

Integration guide

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

BASH

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 TypeUsage
Standard API KeyAll non-S2S operations
S2S API KeyServer-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

JSON

POST /api/v1/clients/

‍

Request Body:

JSON

{
  "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:

ParameterTypeRequiredDescription
emailstringYesCustomer email address
phonestringNoPhone number (E.164 format)
full_namestringNoCustomer full name
personal_codestringNoID number (for DebiCheck)
street_addressstringNoBilling address
citystringNoBilling city
statestringNoBilling state/province
zip_codestringNoBilling postal code
countrystringNoBilling country (ISO 3166-1 alpha-2)
bank_accountstringNoBank account number
bank_codestringNoBank branch/SWIFT code
ccarrayNoCC email addresses for receipts
bccarrayNoBCC email addresses

‍

Response:

JSON

{
  "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

JSON

GET /api/v1/clients/

‍

Query Parameters:

ParameterDescription
qFuzzy search across email, phone, name, etc.
emailSearch by email
phoneSearch by phone

‍

Get Client

BASH

GET /api/v1/clients/{client_id}/

‍

Update Client

BASH

PUT /api/v1/clients/{client_id}/

‍

Partial Update Client

BASH

PATCH /api/v1/clients/{client_id}/

‍

Delete Client

BASH

DELETE /api/v1/clients/{client_id}/

‍

List Client Recurring Tokens

‍

Retrieve stored payment methods for a client:

‍

BASH

GET /api/v1/clients/{client_id}/recurring_tokens/

‍

Response:

JSON

{
  "results": [
    {
      "id": "tok_abc123...",
      "type": "recurring_token",
      "payment_method": "visa",
      "description": "Visa ending in 1111",
      "created_on": 1619740800
    }
  ]
}

‍

Delete Client Token

BASH

DELETE /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

BASH

POST /api/v1/purchases/

‍

Full Request Body:

JSON

{
  "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:

ParameterTypeRequiredDescription
brand_iduuidYesYour brand identifier
purchase.currencystringYesISO 4217 currency code
purchase.productsarrayYesArray of product objects
purchase.products[].namestringYesProduct name
purchase.products[].priceintegerYesPrice in cents

‍

Client Parameters (use client_id OR client object):

ParameterTypeDescription
client_iduuidExisting client ID
client.emailstringCreate/find client by email
client.phonestringClient phone
client.full_namestringClient name

‍

Optional Parameters:

ParameterTypeDefaultDescription
referencestring---Your order reference
payment_method_whitelistarrayallAllowed payment methods
success_redirectstring---Redirect URL on success
failure_redirectstring---Redirect URL on failure
cancel_redirectstring---Redirect URL on cancel
success_callbackstring---Webhook URL for this purchase
send_receiptbooleanfalseEmail receipt on payment
force_recurringbooleanfalseForce token storage
skip_capturebooleanfalsePre-authorization only
dueinteger---Due date (Unix timestamp)
purchase.due_strictbooleanfalseBlock payment after due date
purchase.languagestringenCheckout language

‍

Response:

JSON

{
  "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

BASH

GET /api/v1/purchases/{purchase_id}/

‍

List Purchases

BASH

GET /api/v1/purchases/

‍

Charge Purchase

‍

Charge a purchase using a recurring token:

‍

BASH

POST /api/v1/purchases/{purchase_id}/charge/

‍

Request Body:

JSON

{
  "recurring_token": "tok_abc123..."
}

‍

Capture Pre-Authorization

‍

Capture funds from a pre-authorized purchase:

‍

BASH

POST /api/v1/purchases/{purchase_id}/capture/

‍

Request Body:

JSON

{
  "amount": 10000
}

‍

Release Pre-Authorization

‍

Release held funds without capturing:

‍

BASH

POST /api/v1/purchases/{purchase_id}/release/

‍

Cancel Purchase

BASH

POST /api/v1/purchases/{purchase_id}/cancel/

‍

Resend Invoice

BASH

POST /api/v1/purchases/{purchase_id}/resend_invoice/

‍

Mark as Paid

‍

Manually mark a purchase as paid (for offline payments):

‍

BASH

POST /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:

‍

BASH

POST /api/v1/purchases/

‍

JSON

{
  "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:

‍

sequenceDiagram
    participant C as Customer
    participant M as Your Site
    participant P as Precium

    C->>M: Click "Pay Now"
    M->>P: POST /purchases/
    P-->>M: checkout_url
    M->>C: Redirect to checkout_url
    C->>P: Complete payment
    P->>C: Redirect to success_redirect
    P->>M: Webhook: purchase.paid
    M->>C: Show confirmation

‍

Create Purchase:

JSON

{
  "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

JSON

{
  "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

BASH

POST /api/v1/purchases/{purchase_id}/capture/

‍

JSON

{
  "amount": 450000
}

‍

Or Release if Not Needed:

BASH

POST /api/v1/purchases/{purchase_id}/release/

‍

Scenario 4: Restrict Payment Methods

Only allow specific payment methods:

‍

JSON

{
  "payment_method_whitelist": ["visa", "mastercard"],
  ...
}

‍

Available Payment Methods:

ValueDescription
visaVisa cards
mastercardMastercard
maestroMaestro cards
ozowOzow Instant EFT
capitec_payCapitec Pay
capitec_pay_recurringCapitec Pay Recurring
zapperZapper
debicheckDebiCheck
eftTraditional debit order / EFT collection
debicheckAuthenticated debit order (DebiCheck)

‍

Scenario 5: Invoice with Due Date

Create an invoice that's due by a specific date:

‍

JSON

{
  "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

flowchart TD
    A[Customer arrives at checkout_url] --> B{Multiple payment methods?}
    B -->|Yes| C[Select payment method]
    B -->|No| D[Show payment form]
    C --> D
    D --> E[Enter payment details]
    E --> F{3DS Required?}
    F -->|Yes| G[Complete 3DS]
    F -->|No| H[Process payment]
    G --> H
    H --> I{Success?}
    I -->|Yes| J[Redirect to success_redirect]
    I -->|No| K[Redirect to failure_redirect]

‍

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

  1. Create a purchase to get a direct_post_url
  2. Build an HTML form that POSTs directly to the direct_post_url
  3. Card data goes directly from the browser to Precium
  4. 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

HTML

<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

FieldFormatValidation
pmstringPayment method: visa, mastercard, etc.
cardholder_namestringLatin letters, space, apostrophe, dot, dash. Max 30 chars
card_numberstringDigits only, no spaces. Max 19 chars
expiresstringMM/YY format
cvcstring3-4 digits
remember_cardstringon to save card

‍

Direct Post for Other Payment Methods

Capitec Pay:

HTML

<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>

‍

DebiCheck:

HTML

<form method='POST' action='https://gate.reviopay.com/p/{purchase_id}/'>
  <input type='hidden' name='pm' value='debicheck'>
  <input type='text' name='PayerFirstName' required>
  <input type='text' name='PayerLastName' required>
  <input type='text' name='PayerPhoneNumber' required>
  <input type='hidden' name='PayerIdType' value='idNumber'>
  <input type='text' name='PayerdNumber' required>
  <input type='text' name='PayerBranchCode' maxlength='6' required>
  <input type='text' name='PayerAccountType' value='Current'>
  <input type='text' name='PayerAccount' required>
  <button type='submit'>Set Up DebiCheck</button>
</form>

‍

Ozow

JSON

s2s:true
pm:ozow
phone:27123456789

‍

Recurring Payments

Tokenizing Payment Methods

To save a payment method for future charges, set force_recurring: true:

‍

JSON

{
  "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:

‍

JSON

{
  "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

BASH

POST /api/v1/purchases/

‍

JSON

{
  "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

BASH

POST /api/v1/purchases/{new_purchase_id}/charge/

‍

JSON

{
  "recurring_token": "{original_purchase_id}"
}

‍

Listing Client Tokens

BASH

GET /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

BASH

POST /api/v1/billing_templates/

‍

JSON

{
  "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:

ParameterTypeDescription
is_subscriptionbooleanEnable subscription mode
subscription_activebooleanActivate automatic billing
subscription_periodintegerBilling frequency number
subscription_period_unitsstringdays, weeks, months, years
number_of_billing_cyclesintegerTotal billing cycles (0 = infinite)
subscription_due_periodintegerDays before due date to create invoice
subscription_trial_periodsintegerFree trial periods
subscription_charge_period_endbooleanCharge at end of period

‍

Add Subscriber

BASH

POST /api/v1/billing_templates/{template_id}/add_subscriber/

‍

JSON

{
  "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 ID
  • purchase.checkout_url - URL for initial tokenization
  • purchase.direct_post_url - Direct post URL

‍

Add Subscriber with Override

Customise pricing for specific subscribers:

‍

JSON

{
  "client_id": "client_uuid",
  "invoice_reference": "SUB-VIP-001",
  "override": {
    "products": [
      {
        "name": "VIP Plan (Discounted)",
        "price": 19900
      }
    ]
  }
}

‍

List Billing Templates

BASH

GET /api/v1/billing_templates/

‍

Get Billing Template

BASH

GET /api/v1/billing_templates/{template_id}/

‍

List Subscribers

BASH

GET /api/v1/billing_templates/{template_id}/clients/

‍

Update Subscriber

BASH

PUT /api/v1/billing_templates/{template_id}/clients/{subscription_id}/

‍

Remove Subscriber

BASH

DELETE /api/v1/billing_templates/{template_id}/clients/{subscription_id}/

‍

Refunds

Full Refund

BASH

POST /api/v1/purchases/{purchase_id}/refund/

‍

JSON

{}

‍

Partial Refund

BASH

POST /api/v1/purchases/{purchase_id}/refund/

‍

JSON

{
  "amount": 5000
}

‍

Refund Response

JSON

{
  "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.

EventFires when
purchase.createdA purchase has been created
purchase.viewedThe payer opened the checkout
purchase.paidPayment captured and complete
purchase.payment_failurePayment failed
purchase.cancelledPurchase cancelled
purchase.holdFunds reserved by an authorisation
purchase.preauthorizedZero-amount card verification succeeded
purchase.capturedA hold was captured
purchase.capture_failureCapture failed
purchase.releasedA hold was released without capture
purchase.release_failureRelease failed
purchase.pending_chargeAuthorisation in flight at the acquirer
purchase.pending_captureCapture accepted, still processing
purchase.pending_releaseRelease accepted, still processing
purchase.pending_refundRefund accepted, still processing
payment.refundedA refund completed. Treat this as the completion signal
purchase.refund_failureRefund failed after acceptance
payment.charged_backA chargeback was raised
payment.chargeback_reversedA chargeback was reversed in your favour
purchase.settledThe transaction settled
purchase.recurring_token_deletedA 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

BASH

POST /api/v1/webhooks/

‍

JSON

{
  "url": "https://www.example.com/webhooks/precium",
  "events": ["purchase.paid", "purchase.payment_failure", "payment.refunded"]
}

‍

Webhook Payload

JSON

{
  "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:

‍

PYTHON

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 False

‍

Webhook Best Practices

  1. Respond quickly — Return 200 within 5 seconds
  2. Process asynchronously — Queue for background processing
  3. Handle duplicates — Use event ID for idempotency
  4. 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

BASH

POST /api/v1/payouts/

‍

JSON

{
  "client": {
    "email": "recipient@example.com",
    "phone": "+27821234567"
  },
  "payment": {
    "amount": 50000,
    "currency": "ZAR",
    "description": "Commission Payment"
  },
  "brand_id": "YOUR_BRAND_ID"
}

‍

Execute Payout

BASH

POST {execution_url}

‍

JSON

{
  "cardholder_name": "John Doe",
  "card_number": "4444333322221111",
  "expiry_month": "12",
  "expiry_year": "28"
}

‍

Get Payout Status

BASH

GET /api/v1/payouts/{payout_id}/

‍

API Response Codes

CodeDescription
200Success
201Created
204No content (successful deletion)
400Bad request (validation error)
401Unauthorized (invalid API key)
404Not found
409Conflict (duplicate request)
429Rate limited
500Server error

‍

Rate Limiting

API requests are rate-limited to ensure fair usage:

  • Standard: 100 requests per minute
  • Burst: 200 requests per minute (short periods)

‍

Rate limit headers:

‍

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1619740860

‍

Pagination

List endpoints use cursor-based pagination:

‍

JSON

{
  "results": [...],
  "next": "https://gate.reviopay.com/api/v1/purchases/?cursor=abc123",
  "previous": null
}

‍

Follow the next URL to retrieve additional pages.