Introduction
This document provides production-ready code examples in multiple programming languages for integrating with Precium’s Server-to-Server API.
Python
Installation
pip install requestsComplete Integration Class
"""
Precium S2S Integration - Python
Requires: requests
"""
import requests
from typing import Optional, Dict, Any
from dataclasses import dataclass
from enum import Enum
import json
class PreciumEnvironment(Enum):
SANDBOX = "https://gate.reviopay.com/api/v1"
PRODUCTION = "https://gate.reviopay.com/api/v1"
@dataclass
class CardData:
cardholder_name: str
card_number: str
expires: str # MM/YY format
cvc: str
remote_ip: str
@dataclass
class BrowserFingerprint:
user_agent: str
accept_header: str
language: str
java_enabled: bool
javascript_enabled: bool
color_depth: int
utc_offset: int
screen_width: int
screen_height: int
class PreciumS2SClient:
"""Precium Server-to-Server API Client"""
def __init__(
self,
standard_api_key: str,
s2s_api_key: str,
brand_id: str,
environment: PreciumEnvironment = PreciumEnvironment.SANDBOX
):
self.standard_api_key = standard_api_key
self.s2s_api_key = s2s_api_key
self.brand_id = brand_id
self.base_url = environment.value
def _headers(self, use_s2s: bool = False) -> Dict[str, str]:
"""Generate request headers with appropriate API key."""
api_key = self.s2s_api_key if use_s2s else self.standard_api_key
return {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
"Accept": "application/json"
}
def _request(
self,
method: str,
endpoint: str,
data: Optional[Dict] = None,
use_s2s: bool = False
) -> Dict[str, Any]:
"""Make an API request."""
url = f"{self.base_url}{endpoint}"
response = requests.request(
method=method,
url=url,
headers=self._headers(use_s2s),
json=data
)
response.raise_for_status()
return response.json()
# =========================================================================
# Client Management
# =========================================================================
def create_client(self, email: str, phone: Optional[str] = None) -> Dict:
"""
Create a new client record.
Args:
email: Customer's email address
phone: Optional phone number in international format
Returns:
Client object with ID
"""
payload = {"email": email}
if phone:
payload["phone"] = phone
return self._request("POST", "/clients/", payload)
def get_client(self, client_id: str) -> Dict:
"""Retrieve a client by ID."""
return self._request("GET", f"/clients/{client_id}/")
# =========================================================================
# Purchase Management
# =========================================================================
def create_purchase(
self,
client_id: str,
amount_cents: int,
currency: str,
product_name: str,
success_redirect: str,
failure_redirect: str,
force_recurring: bool = False,
metadata: Optional[Dict] = None
) -> Dict:
"""
Create a new purchase.
Args:
client_id: The client's ID
amount_cents: Amount in cents (e.g., 29900 for R299.00)
currency: ISO 4217 currency code (e.g., "ZAR")
product_name: Description of the product/service
success_redirect: URL for successful payment redirect
failure_redirect: URL for failed payment redirect
force_recurring: Set True to generate token for future MIT
metadata: Optional custom key-value data
Returns:
Purchase object with direct_post_url
"""
payload = {
"client_id": client_id,
"brand_id": self.brand_id,
"currency": currency,
"products": [{"name": product_name, "price": amount_cents}],
"success_redirect": success_redirect,
"failure_redirect": failure_redirect,
}
if force_recurring:
payload["force_recurring"] = True
if metadata:
payload["metadata"] = metadata
return self._request("POST", "/purchases/", payload)
def create_mit_purchase(
self,
client_id: str,
amount_cents: int,
currency: str,
product_name: str,
card_token: str,
network_transaction_id: str,
original_amount_cents: int
) -> Dict:
"""
Create a purchase for Merchant-Initiated Transaction (recurring).
Args:
client_id: The client's ID
amount_cents: Amount in cents
currency: ISO 4217 currency code
product_name: Description of the charge
card_token: Token from original CIT transaction
network_transaction_id: Network transaction ID from original
original_amount_cents: Amount from original tokenization
Returns:
Purchase object ready for MIT charge
"""
payload = {
"client_id": client_id,
"brand_id": self.brand_id,
"currency": currency,
"products": [{"name": product_name, "price": amount_cents}],
"payment_method_details": {
"card_token": card_token,
"previous_network_transaction_id": network_transaction_id,
"original_amount_cents": original_amount_cents
},
"is_recurring": True
}
return self._request("POST", "/purchases/", payload)
def get_purchase(self, purchase_id: str) -> Dict:
"""Retrieve purchase details by ID."""
return self._request("GET", f"/purchases/{purchase_id}/")
# =========================================================================
# Card Data Submission (Direct Post)
# =========================================================================
def submit_card_data(
self,
direct_post_url: str,
card: CardData,
fingerprint: BrowserFingerprint,
remember_card: bool = False
) -> Dict:
"""
Submit card data to the direct post URL.
This method handles the S2S card submission for CIT with internal 3DS.
Args:
direct_post_url: The URL from purchase creation
card: Card details
fingerprint: Browser fingerprint data for 3DS
remember_card: Whether to tokenize the card
Returns:
Response containing 3DS parameters or ready status
"""
payload = {
"cardholder_name": card.cardholder_name,
"card_number": card.card_number,
"expires": card.expires,
"cvc": card.cvc,
"remember_card": str(remember_card).lower(),
"remote_ip": card.remote_ip,
"user_agent": fingerprint.user_agent,
"accept_header": fingerprint.accept_header,
"language": fingerprint.language,
"java_enabled": str(fingerprint.java_enabled).lower(),
"javascript_enabled": str(fingerprint.javascript_enabled).lower(),
"color_depth": str(fingerprint.color_depth),
"utc_offset": str(fingerprint.utc_offset),
"screen_width": str(fingerprint.screen_width),
"screen_height": str(fingerprint.screen_height),
}
headers = {
"Authorization": f"Bearer {self.s2s_api_key}",
"Content-Type": "application/x-www-form-urlencoded"
}
response = requests.post(
direct_post_url,
headers=headers,
data=payload
)
response.raise_for_status()
return response.json()
def submit_token_for_mit(
self,
direct_post_url: str,
card_token: str,
server_ip: str
) -> Dict:
"""
Submit token for MIT transaction.
Args:
direct_post_url: The URL from MIT purchase creation
card_token: The stored card token
server_ip: Your server's IP address
Returns:
Response indicating ready for charge
"""
payload = {
"card_token": card_token,
"remote_ip": server_ip
}
headers = {
"Authorization": f"Bearer {self.s2s_api_key}",
"Content-Type": "application/x-www-form-urlencoded"
}
response = requests.post(
direct_post_url,
headers=headers,
data=payload
)
response.raise_for_status()
return response.json()
# =========================================================================
# Charge and Refund
# =========================================================================
def charge_purchase(self, purchase_id: str) -> Dict:
"""
Finalize the transaction and charge the card.
Call this after 3DS completion or for MIT transactions.
Args:
purchase_id: The purchase ID to charge
Returns:
Transaction result with payment details and token
"""
return self._request("POST", f"/purchases/{purchase_id}/charge/")
def refund_purchase(
self,
purchase_id: str,
amount_cents: Optional[int] = None
) -> Dict:
"""
Process a refund for a purchase.
Args:
purchase_id: The purchase ID to refund
amount_cents: Amount to refund in cents (None for full refund)
Returns:
Refund confirmation
"""
payload = {}
if amount_cents:
payload["amount"] = amount_cents
return self._request("POST", f"/purchases/{purchase_id}/refund/", payload)
# =========================================================================
# Pre-Authorization Operations
# =========================================================================
def capture_purchase(
self,
purchase_id: str,
amount_cents: Optional[int] = None
) -> Dict:
"""
Capture a pre-authorized transaction.
Args:
purchase_id: The purchase ID to capture
amount_cents: Amount to capture in cents (None for full capture)
Returns:
Capture confirmation
"""
payload = {}
if amount_cents:
payload["amount"] = amount_cents
return self._request("POST", f"/purchases/{purchase_id}/capture/", payload)
def void_purchase(self, purchase_id: str) -> Dict:
"""
Void/cancel a pre-authorized or captured transaction (before settlement).
Args:
purchase_id: The purchase ID to void
Returns:
Void confirmation
"""
return self._request("POST", f"/purchases/{purchase_id}/cancel/")
def void_refund(self, purchase_id: str, refund_id: str) -> Dict:
"""
Void a refund before settlement.
Args:
purchase_id: The original purchase ID
refund_id: The refund ID to void
Returns:
Void confirmation
"""
return self._request("POST", f"/purchases/{purchase_id}/refund/{refund_id}/cancel/")
# =========================================================================
# Zero Authorization (Card Validation)
# =========================================================================
def create_zero_auth_purchase(
self,
client_id: str,
currency: str,
success_redirect: str,
failure_redirect: str,
force_recurring: bool = True
) -> Dict:
"""
Create a zero-amount authorization for card validation.
Args:
client_id: The client's ID
currency: ISO 4217 currency code
success_redirect: URL for successful validation
failure_redirect: URL for failed validation
force_recurring: Set True to tokenize the card
Returns:
Purchase object for zero authorization
"""
payload = {
"client_id": client_id,
"brand_id": self.brand_id,
"currency": currency,
"products": [{"name": "Card Validation", "price": 0}],
"success_redirect": success_redirect,
"failure_redirect": failure_redirect,
}
if force_recurring:
payload["force_recurring"] = True
return self._request("POST", "/purchases/", payload)
# =========================================================================
# External 3DS (MPI)
# =========================================================================
def create_external_3ds_purchase(
self,
client_id: str,
amount_cents: int,
currency: str,
product_name: str,
success_redirect: str,
failure_redirect: str,
authentication_transaction_id: str,
cavv: str,
eci: str,
xid: Optional[str] = None,
ds_trans_id: Optional[str] = None
) -> Dict:
"""
Create a purchase with external 3DS authentication.
Args:
client_id: The client's ID
amount_cents: Amount in cents
currency: ISO 4217 currency code
product_name: Description of product/service
success_redirect: URL for successful payment
failure_redirect: URL for failed payment
authentication_transaction_id: Your MPI's transaction ID
cavv: Cardholder Authentication Verification Value
eci: Electronic Commerce Indicator
xid: Transaction ID (3DS1 only)
ds_trans_id: Directory Server Transaction ID (3DS2 only)
Returns:
Purchase object with direct_post_url
"""
payment_details = {
"card": {
"is_external_3DS": True,
"authentication_transaction_id": authentication_transaction_id,
"cavv": cavv,
"eci_raw": eci
}
}
if xid:
payment_details["card"]["xid"] = xid
if ds_trans_id:
payment_details["card"]["ds_trans_id"] = ds_trans_id
payload = {
"client_id": client_id,
"brand_id": self.brand_id,
"currency": currency,
"products": [{"name": product_name, "price": amount_cents}],
"payment_method_details": payment_details,
"success_redirect": success_redirect,
"failure_redirect": failure_redirect,
}
return self._request("POST", "/purchases/", payload)
# =============================================================================
# Usage Examples
# =============================================================================
def example_cit_with_internal_3ds():
"""Example: Customer-Initiated Transaction with Internal 3DS"""
client = PreciumS2SClient(
standard_api_key='your_standard_api_key',
s2s_api_key='your_s2s_api_key',
brand_id='your_brand_id'
)
# Step 1: Create client
customer = client.create_client(email='customer@example.com')
print(f"Created client: {customer['id']}")
# Step 2: Create purchase
purchase = client.create_purchase(
client_id=customer['id'],
amount_cents=29900,
currency='ZAR',
product_name='Premium Subscription',
success_redirect='https://yoursite.com/success',
failure_redirect='https://yoursite.com/failure',
force_recurring=True # Generate token for future recurring
)
print(f"Created purchase: {purchase['id']}")
print(f"Direct post URL: {purchase['direct_post_url']}")
# Step 3: Submit card data
card = CardData(
cardholder_name='John Smith',
card_number='4000000000001091',
expires='12/28',
cvc='123',
remote_ip='196.21.45.123'
)
fingerprint = BrowserFingerprint(
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
accept_header='text/html,application/xhtml+xml',
language='en-ZA',
java_enabled=False,
javascript_enabled=True,
color_depth=24,
utc_offset=-120,
screen_width=1920,
screen_height=1080
)
result = client.submit_card_data(
direct_post_url=purchase['direct_post_url'],
card=card,
fingerprint=fingerprint,
remember_card=True
)
# Step 4: Handle 3DS (in production, redirect customer)
if '3ds' in result:
print("3DS authentication required")
print(f"3DS URL: {result['3ds']['3ds_url']}")
print(f"MD: {result['3ds']['MD']}")
# Redirect customer to 3DS URL...
# Step 5: After 3DS completion, charge
charge_result = client.charge_purchase(purchase['id'])
print(f"Charge result: {charge_result['status']}")
if charge_result['status'] == 'paid':
# Store token for future MIT
token = charge_result['payment_method_details']['card']['token']
network_txn_id = charge_result['payment_method_details']['card']['network_transaction_id']
print(f"Token for recurring: {token}")
print(f"Network Transaction ID: {network_txn_id}")
def example_mit_recurring():
"""Example: Merchant-Initiated Recurring Transaction"""
client = PreciumS2SClient(
standard_api_key='your_standard_api_key',
s2s_api_key='your_s2s_api_key',
brand_id='your_brand_id'
)
# Stored from original CIT transaction
stored_token = "tok_abc123def456..."
stored_network_txn_id = "MCC123456789012345"
original_amount = 29900
# Create MIT purchase
purchase = client.create_mit_purchase(
client_id='existing_client_id',
amount_cents=29900,
currency='ZAR',
product_name='Monthly Subscription - February 2026',
card_token=stored_token,
network_transaction_id=stored_network_txn_id,
original_amount_cents=original_amount
)
# Submit token
client.submit_token_for_mit(
direct_post_url=purchase['direct_post_url'],
card_token=stored_token,
server_ip='10.0.0.1' # Your server IP
)
# Charge
result = client.charge_purchase(purchase['id'])
print(f"MIT charge result: {result['status']}")
def example_zero_authorization():
"""Example: Zero Authorization for Card Validation"""
client = PreciumS2SClient(
standard_api_key='your_standard_api_key',
s2s_api_key='your_s2s_api_key',
brand_id='your_brand_id'
)
# Create client
customer = client.create_client(email='customer@example.com')
# Create zero-amount purchase for card validation
purchase = client.create_zero_auth_purchase(
client_id=customer['id'],
currency='ZAR',
success_redirect='https://yoursite.com/validation/success',
failure_redirect='https://yoursite.com/validation/failure',
force_recurring=True # Tokenize for future use
)
print(f"Zero auth purchase: {purchase['id']}")
# Submit card data (same as standard flow)
card = CardData(
cardholder_name='John Smith',
card_number='4000000000001091',
expires='12/28',
cvc='123',
remote_ip='196.21.45.123'
)
fingerprint = BrowserFingerprint(
user_agent='Mozilla/5.0',
accept_header='text/html',
language='en-ZA',
java_enabled=False,
javascript_enabled=True,
color_depth=24,
utc_offset=-120,
screen_width=1920,
screen_height=1080
)
result = client.submit_card_data(
direct_post_url=purchase['direct_post_url'],
card=card,
fingerprint=fingerprint,
remember_card=True
)
# Handle 3DS if required, then charge to validate
# Card is validated without any charge
def example_pre_authorization():
"""Example: Pre-Authorization with Later Capture"""
client = PreciumS2SClient(
standard_api_key='your_standard_api_key',
s2s_api_key='your_s2s_api_key',
brand_id='your_brand_id'
)
# Create client
customer = client.create_client(email='guest@hotel.com')
# Pre-authorize for hotel stay (estimated amount)
purchase = client.create_purchase(
client_id=customer['id'],
amount_cents=500000, # R5,000 estimated
currency='ZAR',
product_name='Hotel Stay - 3 nights (estimated)',
success_redirect='https://hotel.com/booking/confirmed',
failure_redirect='https://hotel.com/booking/failed',
force_recurring=False
)
# Submit card data and complete 3DS (same as standard flow)
# ... card submission code ...
# Later: Guest checks out, actual bill is R4,500
# Partial capture
capture_result = client.capture_purchase(
purchase_id=purchase['id'],
amount_cents=450000 # Capture only R4,500
)
print(f"Captured: {capture_result['status']}")
# Alternative: Guest cancels before checkout
# Void the pre-authorization
# void_result = client.void_purchase(purchase['id'])
# print(f"Voided: {void_result['status']}")
def example_partial_refund():
"""Example: Partial Refund Flow"""
client = PreciumS2SClient(
standard_api_key='your_standard_api_key',
s2s_api_key='your_s2s_api_key',
brand_id='your_brand_id'
)
# Assume we have a completed purchase
purchase_id = "existing_paid_purchase_id"
# Customer returns one item worth R100
refund_result = client.refund_purchase(
purchase_id=purchase_id,
amount_cents=10000 # R100 partial refund
)
print(f"Partial refund: {refund_result['status']}")
print(f"Refund ID: {refund_result.get('refund_id')}")
# Can issue multiple partial refunds up to original amount
def example_external_3ds():
"""Example: External 3DS (MPI) Flow"""
client = PreciumS2SClient(
standard_api_key='your_standard_api_key',
s2s_api_key='your_s2s_api_key',
brand_id='your_brand_id' # Must be Non-3DS brand
)
# Create client
customer = client.create_client(email='customer@example.com')
# After completing 3DS with your MPI, create purchase with auth data
purchase = client.create_external_3ds_purchase(
client_id=customer['id'],
amount_cents=50000,
currency='ZAR',
product_name='External 3DS Purchase',
success_redirect='https://yoursite.com/success',
failure_redirect='https://yoursite.com/failure',
authentication_transaction_id='GAux5DosxbhI35RL8nc0',
cavv='AAABBBCCCdddEEEfff111222333=',
eci='05',
ds_trans_id='a1b2c3d4-e5f6-7890-abcd-ef1234567890' # 3DS2
)
# Submit card data (no browser fingerprint needed)
card = CardData(
cardholder_name='John Smith',
card_number='4000000000001091',
expires='12/28',
cvc='000', # Can use placeholder for external 3DS
remote_ip='8.8.8.8'
)
# ... submit card and charge ...
if __name__ == "__main__":
example_cit_with_internal_3ds()Browser Fingerprint Collection (JavaScript)
Use this code on your frontend to collect browser fingerprint data:
function collectBrowserFingerprint() {
return {
userAgent: navigator.userAgent,
acceptHeader: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
language: navigator.language || navigator.userLanguage,
javaEnabled: navigator.javaEnabled ? navigator.javaEnabled() : false,
javascriptEnabled: true,
colorDepth: screen.colorDepth,
utcOffset: new Date().getTimezoneOffset(),
screenWidth: screen.width,
screenHeight: screen.height
};
}
// Usage
const fingerprint = collectBrowserFingerprint();
// Send fingerprint to your backend with the payment request3DS Challenge Form (HTML/JavaScript)
<!DOCTYPE html>
<html>
<head>
<title>Bank Verification</title>
</head>
<body>
<div id='loading'>Redirecting to your bank for verification...</div>
<form id='3ds-form' method='POST' style='display: none;'>
<input type='hidden' name='MD' id='md' />
<input type='hidden' name='PaReq' id='pareq' />
<noscript>
<p>JavaScript is required for bank verification.</p>
<button type='submit'>Continue to Bank</button>
</noscript>
</form>
<script>
// These values come from your backend after card submission
function initiate3DS(threeDSData) {
const form = document.getElementById('3ds-form');
form.action = threeDSData['3ds_url'];
document.getElementById('md').value = threeDSData['MD'];
document.getElementById('pareq').value = threeDSData['PaReq'];
// Add TermUrl for the callback
const termUrl = document.createElement('input');
termUrl.type = 'hidden';
termUrl.name = 'TermUrl';
termUrl.value = threeDSData['callback_url'];
form.appendChild(termUrl);
form.submit();
}
// Call with data from your backend
// initiate3DS(threeDSResponseFromBackend);
</script>
</body>
</html>