Official Documentation

VanturaPay API Integration Guide

Connecting small online stores (e.g., MyMama Shop - mymamashp.com) to securely accept payments, process automated webhooks, and manage store fulfillment.


1. Developer Guide

Integrating VanturaPay allows your storefront to process checkout requests smoothly through an API gateway proxy architecture...

2. API Reference & Authentication

Your store connects to VanturaPay using your merchant API credentials:

CredentialPurpose
api_publicPublic identifier passed via Authorization header during transaction initiation.
api_secretPrivate key used to generate HMAC-SHA256 signatures for secure webhooks.

3. Callback Handling

Once a transaction finishes on VanturaPay, customers return via your specified redirect URL. The gateway appends parameters such as ?reference=... which your receipt view handles safely.

4. Webhook System

Webhooks provide robust server-to-server updates. VanturaPay dispatches a POST request containing transaction payloads validated securely using hash_equals against your database secret.

5. Reports & Exports

Merchants can track reconciliation, review sales volumes, and view historical transaction logs in real time via the orders.php management view.

6. Security & Compliance

All database transactions utilize MySQLi prepared statements to protect against SQL injections. Cryptographic signing prevents forgery and timing attacks.

7. Error Handling

Webhooks respond with HTTP status codes: 200 OK for successful updates, 401 Unauthorized for signature mismatches, and 400 Bad Request for incomplete events.

8. Database Schema

Execute the following SQL queries to provision your store database:

-- 1. Settings Table
CREATE TABLE IF NOT EXISTS `gateway_settings` (
    `id` INT AUTO_INCREMENT PRIMARY KEY,
    `api_public` VARCHAR(255) NOT NULL,
    `api_secret` VARCHAR(255) NOT NULL,
    `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

INSERT INTO `gateway_settings` (`id`, `api_public`, `api_secret`) 
VALUES (1, 'vantura_public_key_xxx', 'vantura_secret_xxx')
ON DUPLICATE KEY UPDATE id=id;

-- 2. Products Table
CREATE TABLE IF NOT EXISTS `products` (
    `id` INT AUTO_INCREMENT PRIMARY KEY,
    `name` VARCHAR(255) NOT NULL,
    `price` DECIMAL(10,2) NOT NULL,
    `image` VARCHAR(255) NOT NULL
);

INSERT INTO `products` (`id`, `name`, `price`, `image`) VALUES
(1, 'Organic Farm Basket', 740.25, 'https://images.unsplash.com/photo-1542838132-92c53300491e?w=300'),
(2, 'Fresh Artisan Bread', 250.00, 'https://images.unsplash.com/photo-1509440159596-0249088772ff?w=300');

-- 3. Orders Table
CREATE TABLE IF NOT EXISTS `orders` (
    `id` INT AUTO_INCREMENT PRIMARY KEY,
    `product_id` INT NOT NULL,
    `reference` VARCHAR(100) UNIQUE NOT NULL,
    `customer_email` VARCHAR(255) NOT NULL,
    `amount` DECIMAL(10,2) NOT NULL,
    `status` VARCHAR(50) DEFAULT 'pending',
    `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

9. Complete Store PHP Scripts

1. database.php
<?php
$host = 'localhost';$db   = 'mymamashop';
$user = 'root';$pass = '';

$mysqli = new mysqli($host,$user, $pass,$db);

if ($mysqli->connect_error) {
    die("Database Connection Failed: " . $mysqli->connect_error);
}
$mysqli->set_charset("utf8mb4");
?>
2. settings.php
<?php
require_once 'database.php';
$message = '';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $api_public = trim($_POST['api_public']);
    $api_secret = trim($_POST['api_secret']);

    $stmt =$mysqli->prepare("UPDATE gateway_settings SET api_public = ?, api_secret = ? WHERE id = 1");
    $stmt->bind_param("ss", $api_public,$api_secret);
    if ($stmt->execute()) {$message = "Settings updated successfully!";
    } else {
        $message = "Error updating settings.";
    }
    $stmt->close();
}

$settings =$mysqli->query("SELECT * FROM gateway_settings WHERE id = 1")->fetch_assoc();
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Gateway Settings - MyMama Shop</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container py-5" style="max-width: 600px;">
    <div class="card shadow-sm border-0 rounded-4 p-4">
        <h3 class="fw-bold mb-3">VanturaPay Gateway Settings</h3>
        <?php if($message): ?>
            <div class="alert alert-success"><?= htmlspecialchars($message) ?></div>
        <?php endif; ?>
        <form method="POST">
            <div class="mb-3">
                <label class="form-label">VanturaPay Public Key</label>
                <input type="text" name="api_public" class="form-control" value="<?= htmlspecialchars($settings['api_public'] ?? '') ?>" required>
            </div>
            <div class="mb-3">
                <label class="form-label">VanturaPay Secret Key</label>
                <input type="text" name="api_secret" class="form-control" value="<?= htmlspecialchars($settings['api_secret'] ?? '') ?>" required>
            </div>
            <button type="submit" class="btn btn-dark w-100">Save Configuration</button>
        </form>
        <div class="mt-3 text-center">
            <a href="index.php" class="text-decoration-none text-muted">&larr; Back to Store</a>
        </div>
    </div>
</div>
</body>
</html>
3. index.php
<?php
require_once 'database.php';

$stmt =$mysqli->prepare("SELECT api_public FROM gateway_settings WHERE id = 1 LIMIT 1");
$stmt->execute();$settings = $stmt->get_result()->fetch_assoc();$stmt->close();

$merchantPublicKey =$settings['api_public'] ?? '';

$message = "";
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $productId = intval($_POST['product_id']);
    $email = trim($_POST['customer_email']);

    $stmt =$mysqli->prepare("SELECT * FROM products WHERE id = ? LIMIT 1");
    $stmt->bind_param("i", $productId);
    $stmt->execute();$product = $stmt->get_result()->fetch_assoc();$stmt->close();

    if ($product && !empty($email)) {$reference = 'MM_' . strtoupper(uniqid());
        $amount =$product['price'];

        $ordStmt =$mysqli->prepare("INSERT INTO orders (reference, product_id, amount, customer_email) VALUES (?, ?, ?, ?)");
        $ordStmt->bind_param("sidd", $reference,$productId, $amount,$email);
        $ordStmt->execute();$ordStmt->close();

        $payload = json_encode([
            'amount' => $amount,
            'customer_email' => $email,
            'reference' => $reference
        ]);

        $ch = curl_init('https://sandbox.vanturapay.com/api/v1/transaction/initiate.php');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS,$payload);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'Authorization: ' . $merchantPublicKey
        ]);

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

        if ($curlError) {$message = "cURL Connection Error: " . $curlError;
        } else {
            $result = json_decode($response, true);

            if (isset($result['ok']) &&$result['ok']) {
                header('Location: ' . $result['data']['checkout_url']);
                exit;
            } else {
                $apiMessage = $result['message'] ?? 'Could not reach VanturaPay API.';$message = "Gateway Error (HTTP $httpCode): " . $apiMessage;
            }
        }
    }
}

$productsResult =$mysqli->query("SELECT * FROM products");
$products = $productsResult ? $productsResult->fetch_all(MYSQLI_ASSOC) : [];
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>MyMama Shop - Secure Storefront</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<nav class="navbar navbar-expand-lg navbar-dark bg-dark mb-4">
    <div class="container">
        <a class="navbar-brand fw-bold" href="index.php">MyMama Shop</a>
        <div>
            <a href="orders.php" class="btn btn-outline-light btn-sm me-2">View Orders</a>
            <a href="settings.php" class="btn btn-outline-secondary btn-sm">Settings</a>
        </div>
    </div>
</nav>
<div class="container">
    <h2 class="fw-bold mb-4">Featured Products</h2>
    <?php if ($message): ?>
        <div class="alert alert-danger shadow-sm"><?= htmlspecialchars($message) ?></div>
    <?php endif; ?>
    <div class="row">
        <?php foreach($products as$p): ?>
            <div class="col-md-4 mb-4">
                <div class="card shadow-sm border-0 rounded-4 p-3 h-100">
                    <img src="<?= htmlspecialchars($p['image']) ?>" class="rounded-3 mb-3" style="height: 180px; object-fit: cover;">
                    <h5 class="fw-bold"><?= htmlspecialchars($p['name']) ?></h5>
                    <p class="text-success fw-bold fs-5">&#8358;<?= number_format($p['price'], 2) ?></p>
                    <form method="POST" class="mt-auto">
                        <input type="hidden" name="product_id" value="<?= $p['id'] ?>">
                        <div class="mb-2">
                            <input type="email" name="customer_email" class="form-control form-control-sm" placeholder="Your email address" required>
                        </div>
                        <button type="submit" class="btn btn-dark w-100 btn-sm py-2">Pay with VanturaPay</button>
                    </form>
                </div>
            </div>
        <?php endforeach; ?>
    </div>
</div>
</body>
</html>
4. success.php
<?php
require_once 'database.php';
$reference = $_GET['reference'] ?? '';$order = null;

if (!empty($reference)) {
    $stmt =$mysqli->prepare("SELECT o.*, p.name as product_name, p.image as product_image FROM orders o JOIN products p ON o.product_id = p.id WHERE o.reference = ?");
    $stmt->bind_param("s", $reference);
    $stmt->execute();$order = $stmt->get_result()->fetch_assoc();$stmt->close();
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Payment Receipt - MyMama Shop</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container py-5" style="max-width: 600px;">
    <div class="card shadow-sm border-0 rounded-4 p-4 text-center">
        <?php if ($order): ?>
            <h2 class="fw-bold mb-1 text-success">Payment Successful!</h2>
            <p class="text-muted mb-4">Your transaction has been securely processed.</p>
            <div class="card bg-light border-0 rounded-3 p-3 text-start mb-4">
                <div class="d-flex justify-content-between mb-2"><span class="text-muted">Item:</span><span class="fw-semibold"><?= htmlspecialchars($order['product_name']) ?></span></div>
                <div class="d-flex justify-content-between mb-2"><span class="text-muted">Reference:</span><span class="fw-semibold font-monospace"><?= htmlspecialchars($order['reference']) ?></span></div>
                <div class="d-flex justify-content-between mb-2"><span class="text-muted">Status:</span><span class="badge bg-<?= $order['status'] === 'paid' ? 'success' : 'warning' ?>"><?= strtoupper($order['status']) ?></span></div>
                <div class="d-flex justify-content-between border-top pt-2 mt-2"><span class="fw-bold">Total Paid:</span><span class="fw-bold text-success">&#8358;<?= number_format($order['amount'], 2) ?></span></div>
            </div>
            <a href="index.php" class="btn btn-dark w-100 py-2">Return to Store</a>
        <?php else: ?>
            <h2 class="fw-bold mb-1">Order Not Found</h2>
            <p class="text-muted mb-4">We couldn't locate a transaction matching this reference.</p>
            <a href="index.php" class="btn btn-dark w-100 py-2">Back to Store</a>
        <?php endif; ?>
    </div>
</div>
</body>
</html>
5. orders.php
<?php
require_once 'database.php';
$orders =$mysqli->query("SELECT o.*, p.name as product_name FROM orders o JOIN products p ON o.product_id = p.id ORDER BY o.id DESC");
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Customer Orders - MyMama Shop</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container py-5">
    <div class="d-flex justify-content-between align-items-center mb-4">
        <h2 class="fw-bold">Store Orders</h2>
        <a href="index.php" class="btn btn-dark btn-sm">&larr; Back to Store</a>
    </div>
    <div class="card shadow-sm border-0 rounded-4 p-4">
        <table class="table align-middle mb-0">
            <thead>
                <tr><th>Reference</th><th>Product</th><th>Email</th><th>Amount</th><th>Status</th><th>Date</th></tr>
            </thead>
            <tbody>
                <?php while($row =$orders->fetch_assoc()): ?>
                <tr>
                    <td class="font-monospace"><?= htmlspecialchars($row['reference']) ?></td>
                    <td><?= htmlspecialchars($row['product_name']) ?></td>
                    <td><?= htmlspecialchars($row['customer_email']) ?></td>
                    <td>&#8358;<?= number_format($row['amount'], 2) ?></td>
                    <td><span class="badge bg-<?= $row['status'] === 'paid' ? 'success' : 'warning' ?>"><?= ucfirst($row['status']) ?></span></td>
                    <td><?= $row['created_at'] ?></td>
                </tr>
                <?php endwhile; ?>
            </tbody>
        </table>
    </div>
</div>
</body>
</html>
6. webhook.php
<?php
require_once 'database.php';
header('Content-Type: application/json');

$payload = file_get_contents('php://input');
$receivedSignature =$_GET['signature'] ?? '';

$settings =$mysqli->query("SELECT api_secret FROM gateway_settings WHERE id = 1")->fetch_assoc();
$mySecretKey =$settings['api_secret'] ?? '';

$expectedSignature = hash_hmac('sha256', $payload,$mySecretKey);

if (!hash_equals($expectedSignature,$receivedSignature)) {
    http_response_code(401);
    echo json_encode(['status' => 'error', 'message' => 'Invalid webhook signature']);
    exit;
}

$event = json_decode($payload, true);

if ($event && isset($event['event']) &&$event['event'] === 'charge.completed') {
    $reference =$event['data']['reference'] ?? '';

    if (!empty($reference)) {
        $stmt =$mysqli->prepare("UPDATE orders SET status = 'paid' WHERE reference = ?");
        if ($stmt) {
            $stmt->bind_param("s", $reference);
            $stmt->execute();$stmt->close();

            http_response_code(200);
            echo json_encode(['status' => 'success', 'message' => 'Order marked as paid']);
            exit;
        }
    }
}

http_response_code(400);
echo json_encode(['status' => 'ignored', 'message' => 'Event not handled or reference missing']);
exit;
?>