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

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:

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

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:

|Parameter|Description| |---|---| |`q`|Fuzzy search across email, phone, name, etc.| |`email`|Search by email| |`phone`|Search 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://yoursite.com/success",
  "failure_redirect": "https://yoursite.com/failure",
  "cancel_redirect": "https://yoursite.com/cancel",
  "success_callback": "https://yoursite.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:

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://yoursite.com/order/ORD-12345/success",
  "failure_redirect": "https://yoursite.com/order/ORD-12345/failure",
  "cancel_redirect": "https://yoursite.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://yoursite.com/booking/success",
  "failure_redirect": "https://yoursite.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:

|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| |`debit_order`|Traditional debit order|

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

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

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://yoursite.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://yoursite.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)

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://yoursite.com/subscribe/success",
  "failure_redirect": "https://yoursite.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

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

|Event|Description| |---|---| |`purchase.paid`|Payment completed successfully| |`purchase.payment_failure`|Payment failed| |`purchase.pending`|Payment is processing| |`purchase.authorized`|Pre-authorization successful| |`purchase.refunded`|Refund processed| |`purchase.cancelled`|Purchase cancelled| |`subscription.created`|New subscription created| |`subscription.cancelled`|Subscription cancelled|

Create Webhook

BASH

POST /api/v1/webhooks/

JSON

{
  "url": "https://yoursite.com/webhooks/precium",
  "events": ["purchase.paid", "purchase.payment_failure", "purchase.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 hmac
import hashlib

def verify_webhook(payload, signature, secret):
    expected = hmac.new(
        secret.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

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

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

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