Table of contents

Code examples

Introduction

This document provides production-ready code examples in multiple programming languages for integrating with Precium's Server-to-Server API.

Python

Installation

BASH

pip install requests

Complete Integration Class

PYTHON

"""
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()

Node.js

Installation

BASH

npm install axios

Complete Integration Module

JSX

/**
* Precium S2S Integration - Node.js
* Requires: axios
*/

const axios = require('axios');

const ENVIRONMENTS = {
 SANDBOX: 'https://gate.reviopay.com/api/v1',
 PRODUCTION: 'https://gate.reviopay.com/api/v1'
};

class PreciumS2SClient {
 /**
  * Initialize the Precium S2S client
  * @param {Object} config Configuration object
  * @param {string} config.standardApiKey Standard API key for general requests
  * @param {string} config.s2sApiKey S2S API key for card data submission
  * @param {string} config.brandId Your brand ID
  * @param {string} [config.environment='SANDBOX'] Environment to use
  */
 constructor({ standardApiKey, s2sApiKey, brandId, environment = 'SANDBOX' }) {
   this.standardApiKey = standardApiKey;
   this.s2sApiKey = s2sApiKey;
   this.brandId = brandId;
   this.baseUrl = ENVIRONMENTS[environment];
 }

 /**
  * Get headers for API requests
  * @private
  */
 _getHeaders(useS2S = false) {
   const apiKey = useS2S ? this.s2sApiKey : this.standardApiKey;
   return {
     'Authorization': `Bearer ${apiKey}`,
     'Content-Type': 'application/json',
     'Accept': 'application/json'
   };
 }

 /**
  * Make an API request
  * @private
  */
 async _request(method, endpoint, data = null, useS2S = false) {
   const url = `${this.baseUrl}${endpoint}`;
   const response = await axios({
     method,
     url,
     headers: this._getHeaders(useS2S),
     data
   });
   return response.data;
 }

 // ===========================================================================
 // Client Management
 // ===========================================================================

 /**
  * Create a new client record
  * @param {string} email Customer's email address
  * @param {string} [phone] Optional phone number
  * @returns {Promise<Object>} Client object with ID
  */
 async createClient(email, phone = null) {
   const payload = { email };
   if (phone) payload.phone = phone;
   return this._request('POST', '/clients/', payload);
 }

 /**
  * Get client by ID
  * @param {string} clientId Client ID
  * @returns {Promise<Object>} Client object
  */
 async getClient(clientId) {
   return this._request('GET', `/clients/${clientId}/`);
 }

 // ===========================================================================
 // Purchase Management
 // ===========================================================================

 /**
  * Create a new purchase
  * @param {Object} options Purchase options
  * @returns {Promise<Object>} Purchase object with direct_post_url
  */
 async createPurchase({
   clientId,
   amountCents,
   currency,
   productName,
   successRedirect,
   failureRedirect,
   forceRecurring = false,
   metadata = null
 }) {
   const payload = {
     client_id: clientId,
     brand_id: this.brandId,
     currency,
     products: [{ name: productName, price: amountCents }],
     success_redirect: successRedirect,
     failure_redirect: failureRedirect
   };

   if (forceRecurring) payload.force_recurring = true;
   if (metadata) payload.metadata = metadata;

   return this._request('POST', '/purchases/', payload);
 }

 /**
  * Create MIT (recurring) purchase
  * @param {Object} options MIT purchase options
  * @returns {Promise<Object>} Purchase object
  */
 async createMITPurchase({
   clientId,
   amountCents,
   currency,
   productName,
   cardToken,
   networkTransactionId,
   originalAmountCents
 }) {
   const payload = {
     client_id: clientId,
     brand_id: this.brandId,
     currency,
     products: [{ name: productName, price: amountCents }],
     payment_method_details: {
       card_token: cardToken,
       previous_network_transaction_id: networkTransactionId,
       original_amount_cents: originalAmountCents
     },
     is_recurring: true
   };

   return this._request('POST', '/purchases/', payload);
 }

 /**
  * Get purchase by ID
  * @param {string} purchaseId Purchase ID
  * @returns {Promise<Object>} Purchase object
  */
 async getPurchase(purchaseId) {
   return this._request('GET', `/purchases/${purchaseId}/`);
 }

 // ===========================================================================
 // Card Data Submission
 // ===========================================================================

 /**
  * Submit card data to direct post URL
  * @param {string} directPostUrl URL from purchase creation
  * @param {Object} cardData Card details
  * @param {Object} fingerprint Browser fingerprint
  * @param {boolean} [rememberCard=false] Whether to tokenize
  * @returns {Promise<Object>} 3DS parameters or ready status
  */
 async submitCardData(directPostUrl, cardData, fingerprint, rememberCard = false) {
   const formData = new URLSearchParams({
     cardholder_name: cardData.cardholderName,
     card_number: cardData.cardNumber,
     expires: cardData.expires,
     cvc: cardData.cvc,
     remember_card: rememberCard.toString(),
     remote_ip: cardData.remoteIp,
     user_agent: fingerprint.userAgent,
     accept_header: fingerprint.acceptHeader,
     language: fingerprint.language,
     java_enabled: fingerprint.javaEnabled.toString(),
     javascript_enabled: fingerprint.javascriptEnabled.toString(),
     color_depth: fingerprint.colorDepth.toString(),
     utc_offset: fingerprint.utcOffset.toString(),
     screen_width: fingerprint.screenWidth.toString(),
     screen_height: fingerprint.screenHeight.toString()
   });

   const response = await axios.post(directPostUrl, formData.toString(), {
     headers: {
       'Authorization': `Bearer ${this.s2sApiKey}`,
       'Content-Type': 'application/x-www-form-urlencoded'
     }
   });

   return response.data;
 }

 /**
  * Submit token for MIT transaction
  * @param {string} directPostUrl URL from MIT purchase
  * @param {string} cardToken Stored card token
  * @param {string} serverIp Your server's IP
  * @returns {Promise<Object>} Ready status
  */
 async submitTokenForMIT(directPostUrl, cardToken, serverIp) {
   const formData = new URLSearchParams({
     card_token: cardToken,
     remote_ip: serverIp
   });

   const response = await axios.post(directPostUrl, formData.toString(), {
     headers: {
       'Authorization': `Bearer ${this.s2sApiKey}`,
       'Content-Type': 'application/x-www-form-urlencoded'
     }
   });

   return response.data;
 }

 // ===========================================================================
 // Charge and Refund
 // ===========================================================================

 /**
  * Charge a purchase
  * @param {string} purchaseId Purchase ID to charge
  * @returns {Promise<Object>} Transaction result
  */
 async chargePurchase(purchaseId) {
   return this._request('POST', `/purchases/${purchaseId}/charge/`);
 }

 /**
  * Refund a purchase
  * @param {string} purchaseId Purchase ID to refund
  * @param {number} [amountCents] Amount in cents (null for full refund)
  * @returns {Promise<Object>} Refund confirmation
  */
 async refundPurchase(purchaseId, amountCents = null) {
   const payload = amountCents ? { amount: amountCents } : {};
   return this._request('POST', `/purchases/${purchaseId}/refund/`, payload);
 }
}

// =============================================================================
// Usage Examples
// =============================================================================

async function exampleCITWithInternal3DS() {
 const client = new PreciumS2SClient({
   standardApiKey: 'your_standard_api_key',
   s2sApiKey: 'your_s2s_api_key',
   brandId: 'your_brand_id'
 });

 try {
   // Step 1: Create client
   const customer = await client.createClient('customer@example.com');
   console.log('Created client:', customer.id);

   // Step 2: Create purchase
   const purchase = await client.createPurchase({
     clientId: customer.id,
     amountCents: 29900,
     currency: 'ZAR',
     productName: 'Premium Subscription',
     successRedirect: 'https://yoursite.com/success',
     failureRedirect: 'https://yoursite.com/failure',
     forceRecurring: true
   });
   console.log('Created purchase:', purchase.id);

   // Step 3: Submit card data
   const cardData = {
     cardholderName: 'John Smith',
     cardNumber: '4000000000001091',
     expires: '12/28',
     cvc: '123',
     remoteIp: '196.21.45.123'
   };

   const fingerprint = {
     userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
     acceptHeader: 'text/html,application/xhtml+xml',
     language: 'en-ZA',
     javaEnabled: false,
     javascriptEnabled: true,
     colorDepth: 24,
     utcOffset: -120,
     screenWidth: 1920,
     screenHeight: 1080
   };

   const submitResult = await client.submitCardData(
     purchase.direct_post_url,
     cardData,
     fingerprint,
     true
   );

   // Step 4: Handle 3DS if required
   if (submitResult['3ds']) {
     console.log('3DS required - redirect customer to:', submitResult['3ds']['3ds_url']);
     // In production: redirect customer to 3DS URL
   }

   // Step 5: Charge after 3DS
   const chargeResult = await client.chargePurchase(purchase.id);
   console.log('Charge status:', chargeResult.status);

   if (chargeResult.status === 'paid') {
     console.log('Token:', chargeResult.payment_method_details.card.token);
   }

 } catch (error) {
   console.error('Error:', error.response?.data || error.message);
 }
}

module.exports = { PreciumS2SClient, ENVIRONMENTS };

PHP

Complete Integration Class

PHP

<?php
/**
* Precium S2S Integration - PHP
* Requires: PHP 7.4+ with cURL
*/

declare(strict_types=1);

class PreciumS2SClient
{
   private const ENVIRONMENTS = [
       'SANDBOX' => 'https://gate.reviopay.com/api/v1',
       'PRODUCTION' => 'https://gate.reviopay.com/api/v1'
   ];

   private string $standardApiKey;
   private string $s2sApiKey;
   private string $brandId;
   private string $baseUrl;

   public function __construct(
       string $standardApiKey,
       string $s2sApiKey,
       string $brandId,
       string $environment = 'SANDBOX'
   ) {
       $this->standardApiKey = $standardApiKey;
       $this->s2sApiKey = $s2sApiKey;
       $this->brandId = $brandId;
       $this->baseUrl = self::ENVIRONMENTS[$environment];
   }

   /**
    * Make an API request
    */
   private function request(
       string $method,
       string $endpoint,
       ?array $data = null,
       bool $useS2S = false
   ): array {
       $apiKey = $useS2S ? $this->s2sApiKey : $this->standardApiKey;
       $url = $this->baseUrl . $endpoint;

       $ch = curl_init();
       curl_setopt_array($ch, [
           CURLOPT_URL => $url,
           CURLOPT_RETURNTRANSFER => true,
           CURLOPT_CUSTOMREQUEST => $method,
           CURLOPT_HTTPHEADER => [
               "Authorization: Bearer {$apiKey}",
               'Content-Type: application/json',
               'Accept: application/json'
           ]
       ]);

       if ($data !== null) {
           curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
       }

       $response = curl_exec($ch);
       $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
       curl_close($ch);

       if ($httpCode >= 400) {
           throw new Exception("API Error: HTTP {$httpCode} - {$response}");
       }

       return json_decode($response, true);
   }

   // =========================================================================
   // Client Management
   // =========================================================================

   /**
    * Create a new client
    */
   public function createClient(string $email, ?string $phone = null): array
   {
       $payload = ['email' => $email];
       if ($phone !== null) {
           $payload['phone'] = $phone;
       }
       return $this->request('POST', '/clients/', $payload);
   }

   /**
    * Get client by ID
    */
   public function getClient(string $clientId): array
   {
       return $this->request('GET', "/clients/{$clientId}/");
   }

   // =========================================================================
   // Purchase Management
   // =========================================================================

   /**
    * Create a new purchase
    */
   public function createPurchase(
       string $clientId,
       int $amountCents,
       string $currency,
       string $productName,
       string $successRedirect,
       string $failureRedirect,
       bool $forceRecurring = false,
       ?array $metadata = null
   ): array {
       $payload = [
           'client_id' => $clientId,
           'brand_id' => $this->brandId,
           'currency' => $currency,
           'products' => [['name' => $productName, 'price' => $amountCents]],
           'success_redirect' => $successRedirect,
           'failure_redirect' => $failureRedirect
       ];

       if ($forceRecurring) {
           $payload['force_recurring'] = true;
       }
       if ($metadata !== null) {
           $payload['metadata'] = $metadata;
       }

       return $this->request('POST', '/purchases/', $payload);
   }

   /**
    * Create MIT (recurring) purchase
    */
   public function createMITPurchase(
       string $clientId,
       int $amountCents,
       string $currency,
       string $productName,
       string $cardToken,
       string $networkTransactionId,
       int $originalAmountCents
   ): array {
       $payload = [
           'client_id' => $clientId,
           'brand_id' => $this->brandId,
           'currency' => $currency,
           'products' => [['name' => $productName, 'price' => $amountCents]],
           'payment_method_details' => [
               'card_token' => $cardToken,
               'previous_network_transaction_id' => $networkTransactionId,
               'original_amount_cents' => $originalAmountCents
           ],
           'is_recurring' => true
       ];

       return $this->request('POST', '/purchases/', $payload);
   }

   // =========================================================================
   // Card Data Submission
   // =========================================================================

   /**
    * Submit card data to direct post URL
    */
   public function submitCardData(
       string $directPostUrl,
       array $cardData,
       array $fingerprint,
       bool $rememberCard = false
   ): array {
       $postData = http_build_query([
           'cardholder_name' => $cardData['cardholder_name'],
           'card_number' => $cardData['card_number'],
           'expires' => $cardData['expires'],
           'cvc' => $cardData['cvc'],
           'remember_card' => $rememberCard ? 'true' : 'false',
           'remote_ip' => $cardData['remote_ip'],
           'user_agent' => $fingerprint['user_agent'],
           'accept_header' => $fingerprint['accept_header'],
           'language' => $fingerprint['language'],
           'java_enabled' => $fingerprint['java_enabled'] ? 'true' : 'false',
           'javascript_enabled' => $fingerprint['javascript_enabled'] ? 'true' : 'false',
           'color_depth' => (string)$fingerprint['color_depth'],
           'utc_offset' => (string)$fingerprint['utc_offset'],
           'screen_width' => (string)$fingerprint['screen_width'],
           'screen_height' => (string)$fingerprint['screen_height']
       ]);

       $ch = curl_init();
       curl_setopt_array($ch, [
           CURLOPT_URL => $directPostUrl,
           CURLOPT_RETURNTRANSFER => true,
           CURLOPT_POST => true,
           CURLOPT_POSTFIELDS => $postData,
           CURLOPT_HTTPHEADER => [
               "Authorization: Bearer {$this->s2sApiKey}",
               'Content-Type: application/x-www-form-urlencoded'
           ]
       ]);

       $response = curl_exec($ch);
       curl_close($ch);

       return json_decode($response, true);
   }

   /**
    * Submit token for MIT
    */
   public function submitTokenForMIT(
       string $directPostUrl,
       string $cardToken,
       string $serverIp
   ): array {
       $postData = http_build_query([
           'card_token' => $cardToken,
           'remote_ip' => $serverIp
       ]);

       $ch = curl_init();
       curl_setopt_array($ch, [
           CURLOPT_URL => $directPostUrl,
           CURLOPT_RETURNTRANSFER => true,
           CURLOPT_POST => true,
           CURLOPT_POSTFIELDS => $postData,
           CURLOPT_HTTPHEADER => [
               "Authorization: Bearer {$this->s2sApiKey}",
               'Content-Type: application/x-www-form-urlencoded'
           ]
       ]);

       $response = curl_exec($ch);
       curl_close($ch);

       return json_decode($response, true);
   }

   // =========================================================================
   // Charge and Refund
   // =========================================================================

   /**
    * Charge a purchase
    */
   public function chargePurchase(string $purchaseId): array
   {
       return $this->request('POST', "/purchases/{$purchaseId}/charge/");
   }

   /**
    * Refund a purchase
    */
   public function refundPurchase(string $purchaseId, ?int $amountCents = null): array
   {
       $payload = $amountCents !== null ? ['amount' => $amountCents] : [];
       return $this->request('POST', "/purchases/{$purchaseId}/refund/", $payload);
   }
}

// =============================================================================
// Usage Example
// =============================================================================

/*
$client = new PreciumS2SClient(
   'your_standard_api_key',
   'your_s2s_api_key',
   'your_brand_id'
);

// Create client
$customer = $client->createClient('customer@example.com');

// Create purchase
$purchase = $client->createPurchase(
   $customer['id'],
   29900,
   'ZAR',
   'Premium Subscription',
   'https://yoursite.com/success',
   'https://yoursite.com/failure',
   true
);

// Submit card data
$cardData = [
   'cardholder_name' => 'John Smith',
   'card_number' => '4000000000001091',
   'expires' => '12/28',
   'cvc' => '123',
   'remote_ip' => '196.21.45.123'
];

$fingerprint = [
   '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->submitCardData(
   $purchase['direct_post_url'],
   $cardData,
   $fingerprint,
   true
);

// Charge
$chargeResult = $client->chargePurchase($purchase['id']);
*/

Ruby

RUBY

# Precium S2S Integration - Ruby
# Requires: httparty gem

require 'httparty'
require 'json'

class PreciumS2SClient
 ENVIRONMENTS = {
   sandbox: 'https://gate.reviopay.com/api/v1',
   production: 'https://gate.reviopay.com/api/v1'
 }.freeze

 def initialize(standard_api_key:, s2s_api_key:, brand_id:, environment: :sandbox)
   @standard_api_key = standard_api_key
   @s2s_api_key = s2s_api_key
   @brand_id = brand_id
   @base_url = ENVIRONMENTS[environment]
 end

 # Client Management
 def create_client(email:, phone: nil)
   payload = { email: email }
   payload[:phone] = phone if phone
   request(:post, '/clients/', payload)
 end

 def get_client(client_id)
   request(:get, "/clients/#{client_id}/")
 end

 # Purchase Management
 def create_purchase(client_id:, amount_cents:, currency:, product_name:,
                     success_redirect:, failure_redirect:, force_recurring: false)
   payload = {
     client_id: client_id,
     brand_id: @brand_id,
     currency: currency,
     products: [{ name: product_name, price: amount_cents }],
     success_redirect: success_redirect,
     failure_redirect: failure_redirect
   }
   payload[:force_recurring] = true if force_recurring
   request(:post, '/purchases/', payload)
 end

 def create_mit_purchase(client_id:, amount_cents:, currency:, product_name:,
                         card_token:, network_transaction_id:, original_amount_cents:)
   payload = {
     client_id: client_id,
     brand_id: @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
   }
   request(:post, '/purchases/', payload)
 end

 # Card Data Submission
 def submit_card_data(direct_post_url:, card_data:, fingerprint:, remember_card: false)
   form_data = {
     cardholder_name: card_data[:cardholder_name],
     card_number: card_data[:card_number],
     expires: card_data[:expires],
     cvc: card_data[:cvc],
     remember_card: remember_card.to_s,
     remote_ip: card_data[:remote_ip],
     user_agent: fingerprint[:user_agent],
     accept_header: fingerprint[:accept_header],
     language: fingerprint[:language],
     java_enabled: fingerprint[:java_enabled].to_s,
     javascript_enabled: fingerprint[:javascript_enabled].to_s,
     color_depth: fingerprint[:color_depth].to_s,
     utc_offset: fingerprint[:utc_offset].to_s,
     screen_width: fingerprint[:screen_width].to_s,
     screen_height: fingerprint[:screen_height].to_s
   }

   response = HTTParty.post(
     direct_post_url,
     headers: {
       'Authorization' => "Bearer #{@s2s_api_key}",
       'Content-Type' => 'application/x-www-form-urlencoded'
     },
     body: URI.encode_www_form(form_data)
   )
   JSON.parse(response.body)
 end

 # Charge and Refund
 def charge_purchase(purchase_id)
   request(:post, "/purchases/#{purchase_id}/charge/")
 end

 def refund_purchase(purchase_id, amount_cents: nil)
   payload = amount_cents ? { amount: amount_cents } : {}
   request(:post, "/purchases/#{purchase_id}/refund/", payload)
 end

 private

 def request(method, endpoint, payload = nil, use_s2s: false)
   api_key = use_s2s ? @s2s_api_key : @standard_api_key
   url = "#{@base_url}#{endpoint}"

   options = {
     headers: {
       'Authorization' => "Bearer #{api_key}",
       'Content-Type' => 'application/json',
       'Accept' => 'application/json'
     }
   }
   options[:body] = payload.to_json if payload

   response = HTTParty.send(method, url, options)
   JSON.parse(response.body)
 end
end

Java

JAVA

/**
* Precium S2S Integration - Java
* Requires: java.net.http (Java 11+), org.json
*/

package com.precium.s2s;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.HashMap;
import org.json.JSONObject;

public class PreciumS2SClient {

   private static final String BASE_URL = "https://gate.reviopay.com/api/v1";

   private final String standardApiKey;
   private final String s2sApiKey;
   private final String brandId;
   private final HttpClient httpClient;

   public PreciumS2SClient(String standardApiKey, String s2sApiKey, String brandId) {
       this.standardApiKey = standardApiKey;
       this.s2sApiKey = s2sApiKey;
       this.brandId = brandId;
       this.httpClient = HttpClient.newHttpClient();
   }

   // =========================================================================
   // Client Management
   // =========================================================================

   public JSONObject createClient(String email) throws Exception {
       JSONObject payload = new JSONObject();
       payload.put("email", email);
       return request("POST", "/clients/", payload, false);
   }

   // =========================================================================
   // Purchase Management
   // =========================================================================

   public JSONObject createPurchase(
           String clientId,
           int amountCents,
           String currency,
           String productName,
           String successRedirect,
           String failureRedirect,
           boolean forceRecurring
   ) throws Exception {
       JSONObject product = new JSONObject();
       product.put("name", productName);
       product.put("price", amountCents);

       JSONObject payload = new JSONObject();
       payload.put("client_id", clientId);
       payload.put("brand_id", brandId);
       payload.put("currency", currency);
       payload.put("products", new org.json.JSONArray().put(product));
       payload.put("success_redirect", successRedirect);
       payload.put("failure_redirect", failureRedirect);

       if (forceRecurring) {
           payload.put("force_recurring", true);
       }

       return request("POST", "/purchases/", payload, false);
   }

   // =========================================================================
   // Card Data Submission
   // =========================================================================

   public JSONObject submitCardData(
           String directPostUrl,
           Map<String, String> cardData,
           Map<String, Object> fingerprint,
           boolean rememberCard
   ) throws Exception {
       Map<String, String> formData = new HashMap<>();
       formData.put("cardholder_name", cardData.get("cardholderName"));
       formData.put("card_number", cardData.get("cardNumber"));
       formData.put("expires", cardData.get("expires"));
       formData.put("cvc", cardData.get("cvc"));
       formData.put("remember_card", String.valueOf(rememberCard));
       formData.put("remote_ip", cardData.get("remoteIp"));
       formData.put("user_agent", (String) fingerprint.get("userAgent"));
       formData.put("accept_header", (String) fingerprint.get("acceptHeader"));
       formData.put("language", (String) fingerprint.get("language"));
       formData.put("java_enabled", String.valueOf(fingerprint.get("javaEnabled")));
       formData.put("javascript_enabled", String.valueOf(fingerprint.get("javascriptEnabled")));
       formData.put("color_depth", String.valueOf(fingerprint.get("colorDepth")));
       formData.put("utc_offset", String.valueOf(fingerprint.get("utcOffset")));
       formData.put("screen_width", String.valueOf(fingerprint.get("screenWidth")));
       formData.put("screen_height", String.valueOf(fingerprint.get("screenHeight")));

       String formBody = buildFormData(formData);

       HttpRequest request = HttpRequest.newBuilder()
           .uri(URI.create(directPostUrl))
           .header("Authorization", "Bearer " + s2sApiKey)
           .header("Content-Type", "application/x-www-form-urlencoded")
           .POST(HttpRequest.BodyPublishers.ofString(formBody))
           .build();

       HttpResponse<String> response = httpClient.send(request,
           HttpResponse.BodyHandlers.ofString());

       return new JSONObject(response.body());
   }

   // =========================================================================
   // Charge and Refund
   // =========================================================================

   public JSONObject chargePurchase(String purchaseId) throws Exception {
       return request("POST", "/purchases/" + purchaseId + "/charge/", null, false);
   }

   public JSONObject refundPurchase(String purchaseId, Integer amountCents) throws Exception {
       JSONObject payload = new JSONObject();
       if (amountCents != null) {
           payload.put("amount", amountCents);
       }
       return request("POST", "/purchases/" + purchaseId + "/refund/", payload, false);
   }

   // =========================================================================
   // Private Helpers
   // =========================================================================

   private JSONObject request(String method, String endpoint, JSONObject payload, boolean useS2S)
           throws Exception {
       String apiKey = useS2S ? s2sApiKey : standardApiKey;
       String url = BASE_URL + endpoint;

       HttpRequest.Builder builder = HttpRequest.newBuilder()
           .uri(URI.create(url))
           .header("Authorization", "Bearer " + apiKey)
           .header("Content-Type", "application/json")
           .header("Accept", "application/json");

       if (method.equals("POST")) {
           String body = payload != null ? payload.toString() : "";
           builder.POST(HttpRequest.BodyPublishers.ofString(body));
       } else {
           builder.GET();
       }

       HttpResponse<String> response = httpClient.send(builder.build(),
           HttpResponse.BodyHandlers.ofString());

       return new JSONObject(response.body());
   }

   private String buildFormData(Map<String, String> data) {
       StringBuilder sb = new StringBuilder();
       for (Map.Entry<String, String> entry : data.entrySet()) {
           if (sb.length() > 0) sb.append("&");
           sb.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8));
           sb.append("=");
           sb.append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8));
       }
       return sb.toString();
   }
}

C#

CSHARP

/**
* Precium S2S Integration - C#
* Requires: .NET 6+
*/

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

namespace Precium.S2S
{
   public class PreciumS2SClient
   {
       private const string BaseUrl = "https://gate.reviopay.com/api/v1";

       private readonly string _standardApiKey;
       private readonly string _s2sApiKey;
       private readonly string _brandId;
       private readonly HttpClient _httpClient;

       public PreciumS2SClient(string standardApiKey, string s2sApiKey, string brandId)
       {
           _standardApiKey = standardApiKey;
           _s2sApiKey = s2sApiKey;
           _brandId = brandId;
           _httpClient = new HttpClient();
       }

       // Client Management
       public async Task<JsonDocument> CreateClientAsync(string email, string? phone = null)
       {
           var payload = new Dictionary<string, object> { { "email", email } };
           if (phone != null) payload["phone"] = phone;
           return await RequestAsync("POST", "/clients/", payload);
       }

       // Purchase Management
       public async Task<JsonDocument> CreatePurchaseAsync(
           string clientId,
           int amountCents,
           string currency,
           string productName,
           string successRedirect,
           string failureRedirect,
           bool forceRecurring = false)
       {
           var payload = new Dictionary<string, object>
           {
               { "client_id", clientId },
               { "brand_id", _brandId },
               { "currency", currency },
               { "products", new[] { new { name = productName, price = amountCents } } },
               { "success_redirect", successRedirect },
               { "failure_redirect", failureRedirect }
           };

           if (forceRecurring) payload["force_recurring"] = true;

           return await RequestAsync("POST", "/purchases/", payload);
       }

       // Card Data Submission
       public async Task<JsonDocument> SubmitCardDataAsync(
           string directPostUrl,
           CardData card,
           BrowserFingerprint fingerprint,
           bool rememberCard = false)
       {
           var formData = new Dictionary<string, string>
           {
               { "cardholder_name", card.CardholderName },
               { "card_number", card.CardNumber },
               { "expires", card.Expires },
               { "cvc", card.Cvc },
               { "remember_card", rememberCard.ToString().ToLower() },
               { "remote_ip", card.RemoteIp },
               { "user_agent", fingerprint.UserAgent },
               { "accept_header", fingerprint.AcceptHeader },
               { "language", fingerprint.Language },
               { "java_enabled", fingerprint.JavaEnabled.ToString().ToLower() },
               { "javascript_enabled", fingerprint.JavascriptEnabled.ToString().ToLower() },
               { "color_depth", fingerprint.ColorDepth.ToString() },
               { "utc_offset", fingerprint.UtcOffset.ToString() },
               { "screen_width", fingerprint.ScreenWidth.ToString() },
               { "screen_height", fingerprint.ScreenHeight.ToString() }
           };

           var request = new HttpRequestMessage(HttpMethod.Post, directPostUrl)
           {
               Content = new FormUrlEncodedContent(formData)
           };
           request.Headers.Add("Authorization", $"Bearer {_s2sApiKey}");

           var response = await _httpClient.SendAsync(request);
           var content = await response.Content.ReadAsStringAsync();
           return JsonDocument.Parse(content);
       }

       // Charge and Refund
       public async Task<JsonDocument> ChargePurchaseAsync(string purchaseId)
       {
           return await RequestAsync("POST", $"/purchases/{purchaseId}/charge/", null);
       }

       public async Task<JsonDocument> RefundPurchaseAsync(string purchaseId, int? amountCents = null)
       {
           var payload = amountCents.HasValue
               ? new Dictionary<string, object> { { "amount", amountCents.Value } }
               : null;
           return await RequestAsync("POST", $"/purchases/{purchaseId}/refund/", payload);
       }

       private async Task<JsonDocument> RequestAsync(
           string method,
           string endpoint,
           Dictionary<string, object>? payload,
           bool useS2S = false)
       {
           var apiKey = useS2S ? _s2sApiKey : _standardApiKey;
           var url = $"{BaseUrl}{endpoint}";

           var request = new HttpRequestMessage(
               method == "POST" ? HttpMethod.Post : HttpMethod.Get,
               url);
           request.Headers.Add("Authorization", $"Bearer {apiKey}");
           request.Headers.Add("Accept", "application/json");

           if (payload != null)
           {
               var json = JsonSerializer.Serialize(payload);
               request.Content = new StringContent(json, Encoding.UTF8, "application/json");
           }

           var response = await _httpClient.SendAsync(request);
           var content = await response.Content.ReadAsStringAsync();
           return JsonDocument.Parse(content);
       }
   }

   public record CardData(
       string CardholderName,
       string CardNumber,
       string Expires,
       string Cvc,
       string RemoteIp);

   public record BrowserFingerprint(
       string UserAgent,
       string AcceptHeader,
       string Language,
       bool JavaEnabled,
       bool JavascriptEnabled,
       int ColorDepth,
       int UtcOffset,
       int ScreenWidth,
       int ScreenHeight);
}

Go

GO

// Precium S2S Integration - Go
// Requires: Go 1.18+

package precium

import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"strings"
)

const baseURL = "https://gate.reviopay.com/api/v1"

type Client struct {
StandardAPIKey string
S2SAPIKey      string
BrandID        string
HTTPClient     *http.Client
}

type CardData struct {
CardholderName string
CardNumber     string
Expires        string
CVC            string
RemoteIP       string
}

type BrowserFingerprint struct {
UserAgent         string
AcceptHeader      string
Language          string
JavaEnabled       bool
JavascriptEnabled bool
ColorDepth        int
UTCOffset         int
ScreenWidth       int
ScreenHeight      int
}

func NewClient(standardAPIKey, s2sAPIKey, brandID string) *Client {
return &Client{
StandardAPIKey: standardAPIKey,
S2SAPIKey:      s2sAPIKey,
BrandID:        brandID,
HTTPClient:     &http.Client{},
}
}

// CreateClient creates a new client record
func (c *Client) CreateClient(email string, phone *string) (map[string]interface{}, error) {
payload := map[string]interface{}{"email": email}
if phone != nil {
payload["phone"] = *phone
}
return c.request("POST", "/clients/", payload, false)
}

// CreatePurchase creates a new purchase
func (c *Client) CreatePurchase(
clientID string,
amountCents int,
currency string,
productName string,
successRedirect string,
failureRedirect string,
forceRecurring bool,
) (map[string]interface{}, error) {
payload := map[string]interface{}{
"client_id":        clientID,
"brand_id":         c.BrandID,
"currency":         currency,
"products":         []map[string]interface{}{{"name": productName, "price": amountCents}},
"success_redirect": successRedirect,
"failure_redirect": failureRedirect,
}
if forceRecurring {
payload["force_recurring"] = true
}
return c.request("POST", "/purchases/", payload, false)
}

// SubmitCardData submits card data to the direct post URL
func (c *Client) SubmitCardData(
directPostURL string,
card CardData,
fingerprint BrowserFingerprint,
rememberCard bool,
) (map[string]interface{}, error) {
form := url.Values{}
form.Set("cardholder_name", card.CardholderName)
form.Set("card_number", card.CardNumber)
form.Set("expires", card.Expires)
form.Set("cvc", card.CVC)
form.Set("remember_card", strconv.FormatBool(rememberCard))
form.Set("remote_ip", card.RemoteIP)
form.Set("user_agent", fingerprint.UserAgent)
form.Set("accept_header", fingerprint.AcceptHeader)
form.Set("language", fingerprint.Language)
form.Set("java_enabled", strconv.FormatBool(fingerprint.JavaEnabled))
form.Set("javascript_enabled", strconv.FormatBool(fingerprint.JavascriptEnabled))
form.Set("color_depth", strconv.Itoa(fingerprint.ColorDepth))
form.Set("utc_offset", strconv.Itoa(fingerprint.UTCOffset))
form.Set("screen_width", strconv.Itoa(fingerprint.ScreenWidth))
form.Set("screen_height", strconv.Itoa(fingerprint.ScreenHeight))

req, err := http.NewRequest("POST", directPostURL, strings.NewReader(form.Encode()))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+c.S2SAPIKey)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

resp, err := c.HTTPClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}

var result map[string]interface{}
if err := json.Unmarshal(body, &result); err != nil {
return nil, err
}
return result, nil
}

// ChargePurchase charges a purchase
func (c *Client) ChargePurchase(purchaseID string) (map[string]interface{}, error) {
return c.request("POST", fmt.Sprintf("/purchases/%s/charge/", purchaseID), nil, false)
}

// RefundPurchase refunds a purchase
func (c *Client) RefundPurchase(purchaseID string, amountCents *int) (map[string]interface{}, error) {
var payload map[string]interface{}
if amountCents != nil {
payload = map[string]interface{}{"amount": *amountCents}
}
return c.request("POST", fmt.Sprintf("/purchases/%s/refund/", purchaseID), payload, false)
}

func (c *Client) request(method, endpoint string, payload map[string]interface{}, useS2S bool) (map[string]interface{}, error) {
apiKey := c.StandardAPIKey
if useS2S {
apiKey = c.S2SAPIKey
}

url := baseURL + endpoint

var body io.Reader
if payload != nil {
jsonBytes, err := json.Marshal(payload)
if err != nil {
return nil, err
}
body = bytes.NewReader(jsonBytes)
}

req, err := http.NewRequest(method, url, body)
if err != nil {
return nil, err
}

req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")

resp, err := c.HTTPClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()

respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}

var result map[string]interface{}
if err := json.Unmarshal(respBody, &result); err != nil {
return nil, err
}
return result, nil
}

Browser Fingerprint Collection (JavaScript)

Use this code on your frontend to collect browser fingerprint data:

JSX

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 request

3DS Challenge Form (HTML/JavaScript)

HTML

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