Verso SDK Documentation

Integrate Verso one-time payment links into your platform

What is the Verso SDK?

The Verso Merchant Payment SDK enables developers to create secure, one-time-use payment links for seamless merchant checkout integration. Users authenticate once, confirm payment details, enter their PIN, and complete transactions without leaving your platform.

Integration Flow

1
Generate Token
→
2
Open SDK Window
→
3
Validate Token
→
4
User Authenticates
→
5
Payment Complete

Key Features

🔐 Secure One-Time Links

Each token is single-use and expires after 24 hours. Tokens are cryptographically random and tied to specific merchants and amounts.

🚀 Quick Integration

Simple REST API with clear endpoints. Generate a token, open the SDK, and handle the callback. No complex setup required.

🌍 Cross-Platform

Works on desktop and mobile. Open SDK in a new window or embed in an iframe for seamless checkout experience.

Getting Started

Follow these steps to integrate the Verso SDK into your application.

Prerequisites

  • A Verso merchant account with API credentials
  • Authentication token from your merchant dashboard
  • Basic knowledge of HTTP requests and JavaScript

Step 1: Get Your Credentials

Log in to your Verso merchant dashboard and navigate to:

Developer Settings → API Keys

You'll find your authToken. Keep this secure and never expose it in client-side code.

Step 2: Test Your Setup

Use the Sandbox / Test panel to verify your credentials work correctly.

Step 3: Integrate into Your App

See the Integration Guide for step-by-step implementation instructions with code examples.

💡 Tip: Start with the sandbox/test panel to generate your first token without writing code. This helps you understand the flow before diving into integration.

Authentication

All API requests require an authentication token to identify your merchant account.

How to Obtain an Auth Token

  1. Log in to your Verso merchant portal
  2. Navigate to Developer Settings → API Keys
  3. Copy your API token (it's a long, cryptographic string)
  4. Use this token in the token parameter when calling the generate endpoint

Token Security

⚠️ WARNING: Never include your auth token in client-side code, browser console, or public repositories. Always call the generate endpoint from your backend server.

Token Rotation

For security, rotate your API tokens regularly. You can generate new tokens and revoke old ones from your merchant dashboard.

Example: Using Your Token

curl --location 'http://localhost:3000/api/sdk-access/generate' \
--header 'Content-Type: application/json' \
--data '{
  "token": "your_auth_token_here",
  "amount": 500
}'

API Reference

Complete documentation of Verso SDK API endpoints.

Generate Payment Token

POST /api/sdk-access/generate

Description

Generate a one-time-use payment link token. Call this from your backend server with your authentication token.

Request Body

{
  "token": "your_merchant_auth_token",
  "amount": 500
}
Parameter Type Description
token string Your merchant authentication token from the API dashboard
amount number Payment amount in CFA. Must be greater than 0

Response

{
  "success": true,
  "token": "a1b2c3d4e5f6...",
  "merchantId": "6982c6da5ee026485586c3b0",
  "userId": "68d0d522ca4fdef054351beb",
  "amount": 500,
  "sdkUrl": "http://localhost:3000/verso-merchant-sdk.html?token=a1b2c3d4&merchantId=6982c6d&amount=500",
  "message": "Payment link generated successfully"
}

Validate Payment Token

GET /api/sdk-access/validate

Description

Validate a payment token and mark it as used. Called automatically by the SDK frontend, but documented here for reference.

Query Parameters

?token=TOKEN&merchantId=MERCHANT_ID&amount=AMOUNT
Parameter Type Description
token string The SDK token generated by the generate endpoint
merchantId string The merchant ID from the token response
amount number The payment amount from the token response

Response

{
  "success": true,
  "message": "Payment link validated successfully",
  "token": "a1b2c3d4e5f6...",
  "merchantId": "6982c6da5ee026485586c3b0",
  "amount": 500,
  "merchantCompanyName": "Test Enterprise"
}

Notify Payment (Webhook Trigger)

POST /api/sdk-access/notify-payment

Description

Called automatically by the SDK after a successful payment. Looks up the merchant's registered webhook URL and fires a payment.success notification. If no webhook is configured the call succeeds silently.

This endpoint is called internally — you do not need to call it yourself. It is documented here for transparency.

Request Body

{
  "token": "sdk_access_token_here",
  "transactionId": "2066618466",
  "amount": 5000,
  "merchantId": "6982c6da5ee026485586c3b0",
  "customerName": "Jean Dupont",
  "customerEmail": "jean@example.com"
}
ParameterTypeRequiredDescription
tokenstringYesThe one-time SDK access token
transactionIdstringYesTransaction ID from the payment API
amountnumberYesPayment amount
merchantIdstringYesTarget merchant ID
customerNamestringNoName of the paying user
customerEmailstringNoEmail of the paying user

Response

// Webhook delivered
{ "success": true, "webhookFired": true, "webhookStatus": 200 }

// No webhook registered (not an error)
{ "success": true, "webhookFired": false, "message": "No active webhook registered" }

// Delivery failed (endpoint unreachable — not an error for the caller)
{ "success": true, "webhookFired": false, "message": "Webhook delivery failed: ..." }

Integration Guide

Step-by-step instructions to integrate the Verso SDK into your application.

Step 1: Backend Setup

Create an endpoint in your backend that calls the Verso generate API. This endpoint should:

  • Accept the payment amount from your frontend
  • Use your secure auth token (stored in environment variables)
  • Call POST /api/sdk-access/generate
  • Return the sdkUrl to your frontend

Example: Node.js/Express

app.post('/api/generate-payment-link', async (req, res) => {
  const { amount } = req.body;

  try {
    const response = await fetch('http://localhost:3000/api/sdk-access/generate', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        token: process.env.VERSO_AUTH_TOKEN,
        amount: amount
      })
    });

    const data = await response.json();
    if (!data.success) throw new Error(data.error);

    res.json({ 
      success: true, 
      sdkUrl: data.sdkUrl,
      token: data.token 
    });
  } catch (error) {
    res.status(500).json({ success: false, error: error.message });
  }
});

Step 2: Frontend Integration

Create a button or form that triggers the SDK window.

Example: Open SDK in New Window

async function initiatePayment(amount) {
  try {
    // Get SDK URL from your backend
    const response = await fetch('/api/generate-payment-link', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ amount: amount })
    });

    const data = await response.json();
    if (!data.success) throw new Error(data.error);

    // Open SDK in new window
    const width = 500;
    const height = 700;
    const left = (screen.width - width) / 2;
    const top = (screen.height - height) / 2;

    window.open(
      data.sdkUrl,
      'verso-payment',
      `width=${width},height=${height},left=${left},top=${top}`
    );

    // Listen for success message from SDK
    window.addEventListener('message', handleSDKMessage);
  } catch (error) {
    console.error('Error initiating payment:', error);
  }
}

function handleSDKMessage(event) {
  if (event.data.type === 'payment-success') {
    console.log('Payment successful!', event.data.transactionId);
    // Handle success - update UI, save transaction ID, etc.
  }
}

Step 3: Handle Payment Success

After the user completes payment in the SDK, your server is notified automatically via webhook (if you've registered a URL). You can also:

  • Listen for window messages from the SDK popup
  • Register a webhook URL via POST /api/merchant/webhook-register to receive a payment.success POST on your server
  • Update your order/cart status to "completed" when the webhook arrives
📝 Note: The SDK validates the token internally and ensures it can only be used once. Your backend should also verify the token on your side for additional security.

Code Samples

Copy-paste ready examples for common integration scenarios.

cURL - Generate Token

curl --location 'http://localhost:3000/api/sdk-access/generate' \
--header 'Content-Type: application/json' \
--data '{
  "token": "your_auth_token_here",
  "amount": 500
}'

Node.js - Generate Token

const fetch = require('node-fetch');

async function generatePaymentToken(authToken, amount) {
  const response = await fetch('http://localhost:3000/api/sdk-access/generate', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      token: authToken,
      amount: amount
    })
  });

  const data = await response.json();
  return data;
}

// Usage
generatePaymentToken('your_token', 500)
  .then(result => console.log('SDK URL:', result.sdkUrl))
  .catch(err => console.error('Error:', err.message));

JavaScript - Handle Payment Completion

// Open SDK and listen for completion
function handlePaymentCompletion() {
  window.addEventListener('message', (event) => {
    if (event.origin !== 'http://localhost:3000') return;
    
    if (event.data.type === 'payment-complete') {
      console.log('Transaction ID:', event.data.transactionId);
      console.log('Amount:', event.data.amount);
      
      // Update UI or redirect user
      alert('Payment successful! Transaction: ' + event.data.transactionId);
    }
  });
}

Python - Generate Token

import requests
import json

def generate_payment_token(auth_token, amount):
    url = 'http://localhost:3000/api/sdk-access/generate'
    headers = {'Content-Type': 'application/json'}
    payload = {
        'token': auth_token,
        'amount': amount
    }
    
    response = requests.post(url, headers=headers, json=payload)
    return response.json()

# Usage
result = generate_payment_token('your_token', 500)
print(f"SDK URL: {result['sdkUrl']}")

Webhooks & Callbacks

Receive real-time payment notifications on your server

What are Webhooks?

Webhooks allow Verso to send your server real-time HTTP callbacks when a payment succeeds. Instead of polling for status, your endpoint is called automatically the moment a transaction completes.

Supported Events

Event Type Trigger Description
payment.success After PIN confirmation Payment was processed successfully and transaction ID is confirmed

Webhook Payload

Webhooks are delivered as POST requests with a JSON body:

{
  "event": "payment.success",
  "timestamp": "2026-07-29T21:15:54.959Z",
  "data": {
    "transactionId": "2066618466",
    "amount": 5000,
    "currency": "XAF",
    "status": "success",
    "merchantId": "6982c6da5ee026485586c3b0",
    "customerName": "Jean Dupont",
    "customerEmail": "jean@example.com",
    "sdkToken": "a1b2c3d4e5f6g7h8..."
  }
}

Payload Fields

FieldTypeDescription
eventstringAlways payment.success
timestampISO 8601UTC time the notification was sent
data.transactionIdstringVerso transaction ID
data.amountnumberAmount paid in XAF
data.currencystringAlways XAF
data.statusstringAlways success
data.merchantIdstringYour merchant ID
data.customerNamestring | nullFull name of the paying user
data.customerEmailstring | nullEmail of the paying user
data.sdkTokenstringTruncated SDK token (first 16 chars + …)

Request Headers

{
  "Content-Type": "application/json",
  "X-Verso-Event": "payment.success",
  "X-Verso-Signature": "<hmac-sha256-hex>"
}

The X-Verso-Signature is an HMAC-SHA256 hex digest of the full JSON payload, keyed with your SDK token. Use it to verify the request is genuinely from Verso.

Registering Your Webhook URL

Register your endpoint directly via the API — no need to contact support.

POST /api/merchant/webhook-register
curl -X POST https://your-verso-host/api/merchant/webhook-register \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <merchant_token>" \
  -d '{ "webhookUrl": "https://yourapp.com/webhooks/verso" }'

You can also test your URL first (and save it only if the test succeeds) using:

POST /api/merchant/webhook-test-and-save
curl -X POST https://your-verso-host/api/merchant/webhook-test-and-save \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <merchant_token>" \
  -d '{ "webhookUrl": "https://yourapp.com/webhooks/verso" }'

Setup Checklist

  1. Create your handler endpoint — an HTTPS route that accepts POST requests
  2. Register the URL — call POST /api/merchant/webhook-register with your URL
  3. Verify the signature — compute HMAC-SHA256 on the raw body and compare with X-Verso-Signature
  4. Return HTTP 200 — respond promptly; Verso does not retry failed deliveries
⚠️ No retries: Verso makes a single delivery attempt per payment. If your server is down or returns a non-200 status, the notification is not re-sent. Ensure your endpoint is reliable.

Signature Verification

Recompute the HMAC using the raw request body and your SDK token as the key, then compare with the header value.

Node.js / Express

const crypto = require('crypto');

app.post('/webhooks/verso', express.json(), (req, res) => {
    const signature = req.headers['x-verso-signature'];
    const payload   = req.body;

    // Re-compute expected signature using your SDK token as the HMAC key
    const expected = crypto
        .createHmac('sha256', process.env.VERSO_SDK_TOKEN)
        .update(JSON.stringify(payload))
        .digest('hex');

    if (expected !== signature) {
        return res.status(401).send('Invalid signature');
    }

    if (payload.event === 'payment.success') {
        const { transactionId, amount, customerName } = payload.data;
        console.log(`Payment received: ${transactionId} — ${amount} XAF from ${customerName}`);
        // Update your order status, notify customer, etc.
    }

    res.sendStatus(200);
});

Python / Flask

import hmac, hashlib, json, os
from flask import Flask, request

app = Flask(__name__)

@app.route('/webhooks/verso', methods=['POST'])
def verso_webhook():
    signature   = request.headers.get('X-Verso-Signature', '')
    payload_raw = request.get_data()          # raw bytes — important!
    payload     = request.get_json()

    expected = hmac.new(
        os.environ['VERSO_SDK_TOKEN'].encode(),
        payload_raw,
        hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(expected, signature):
        return 'Invalid signature', 401

    if payload['event'] == 'payment.success':
        data = payload['data']
        print(f"Payment {data['transactionId']} — {data['amount']} XAF")

    return {'status': 'ok'}, 200

Testing Webhooks Locally

Use a tunneling service like ngrok to expose your local server:

# Terminal 1: Start your local server
npm start

# Terminal 2: Expose it publicly
ngrok http 3000

# Register the public URL
curl -X POST http://localhost:3000/api/merchant/webhook-register \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <your_merchant_token>" \
  -d '{ "webhookUrl": "https://1234-56-78-90-12.ngrok-free.app/webhooks/verso" }'

Best Practices

  • ✅ Use HTTPS — always register an https:// URL in production
  • ✅ Verify the signature — reject requests with a mismatched or missing X-Verso-Signature
  • ✅ Respond quickly — return HTTP 200 immediately; run heavy processing asynchronously
  • ✅ Deduplicate by transactionId — store received IDs to avoid processing the same payment twice if you re-register and trigger a test
  • ✅ Log everything — keep webhook logs for auditing and debugging
  • ❌ Don't block on external calls — avoid slow DB writes or third-party API calls in the handler's critical path

Common Issues

❓ Not receiving webhooks?
  • Check GET /api/merchant/webhook to confirm your URL is registered and active: true
  • Verify your server is reachable from the internet (use webhook-test-and-save to test)
  • Ensure your handler returns HTTP 200, not a redirect or error
❓ Signature verification failing?
  • Use the raw request body bytes — do not parse to JSON before hashing
  • Confirm you're using the correct SDK token as the HMAC key

Error Reference

Common errors and how to resolve them.

Generate Token Errors

Error Code HTTP Status Meaning Solution
Invalid or missing auth token 400 The token parameter is missing or not a string Ensure you're passing a valid auth token in the request body
Invalid auth token 401 The token doesn't match any user in the database Check that the token is correct. Tokens are case-sensitive
No merchant account found 404 The user exists but has no associated merchant account Set up a merchant account in the merchant dashboard
Invalid amount 400 Amount is missing, not a number, or ≤ 0 Ensure amount is a positive number (e.g., 500 for 500 CFA)

Token Validation Errors

Error Code Meaning Solution
TOKEN_NOT_FOUND Token doesn't exist or parameters don't match Verify token, merchantId, and amount are correct. Generate a new token
TOKEN_ALREADY_USED Token has already been used Tokens are single-use. Generate a new token for another payment
NO_TOKEN SDK accessed without a token parameter Always generate a token first and pass it in the URL

Payment Errors

Error Meaning Solution
Invalid or expired auth token User token in login request is invalid User should log in again with correct credentials
Code PIN incorrect User entered wrong PIN User can retry PIN entry
HTTP 422 Payment request validation failed Check merchantId exists and amount is valid

Debugging Tips

🔍 Enable Logging

Open browser console (F12) and check the Network tab to see all API requests and responses. Look for request/response bodies to identify parameter issues.

🔐 Verify Credentials

Double-check your auth token in the dashboard. Tokens are long and case-sensitive. Never copy extra spaces.

⏰ Check Token Expiry

Generated tokens expire after 24 hours. If you see TOKEN_NOT_FOUND, generate a fresh token.

Sandbox / Test Panel

Test the API without writing code. Generate tokens, view responses, and try the SDK flow.

Generate Test Token

Found in: Merchant Dashboard → Developer Settings → API Keys

💡 How to use:
1. Paste your auth token above
2. Enter a test amount
3. Click "Generate Token"
4. Review the API response
5. Click "Open SDK Window" to test the full flow