CironetPay
The unified payment layer for the Cironet ecosystem. One gateway, one merchant identity, powering every product and client platform.
What is CironetPay?
CironetPay is a centrally hosted payment gateway developed and operated by Cironet Technologies Limited. It sits between the applications that need to accept payments (Nodes) and the payment service providers that process them. It is the single, unified layer through which all payment activity in the Cironet ecosystem flows.
Nodes know CironetPay. CironetPay knows the payment provider. The provider knows Cironet Technologies as the merchant. No node ever interacts with a payment provider directly.
In one sentence: CironetPay is the payment backbone of the Cironet ecosystem — one gateway, one merchant account, powering every product Cironet builds and every client platform Cironet delivers.
The Problem
Accepting digital payments in Uganda is complex. Every provider has its own API, authentication mechanism, and webhook format. For a company operating multiple products and building platforms for various clients, this creates significant overhead:
- Integration Duplication: Rebuilding the same mobile money integration in every product.
- Credential Sprawl: Managing separate API keys across many products is a security risk.
- Onboarding Friction: Clients find PSP registration processes slow and inaccessible.
- Fragmented Records: Payment data lives in separate silos with no unified overview.
The Solution
The "Node" Principle
Any application connects to CironetPay as a node. Nodes POST a checkout request to CironetPay and receive a signed webhook when the payment completes. The node never interacts with a payment provider directly — it does not even know which provider processed the payment.
Powers payments inside Messaging, Smart ERP, and Marketplace through a single internal checkout — no provider logic in each product.
A drop-in payment layer for platforms Cironet builds for clients. Clients accept payments instantly without separate PSP onboarding.
How It Works
Node Initiates Checkout
The Node builds a signed POST form and redirects the user to pay.cironetug.com/checkout.php with amount, reference, and callback URL.
CironetPay Collects Payment Details
CironetPay verifies the signature, records a Pending transaction, and shows the user a phone number prompt.
Payment Processed
CironetPay sends a Mobile Money push prompt to the user's phone. The user enters their PIN to authorize.
Provider Callback
The payment provider notifies CironetPay of the outcome. CironetPay updates the transaction status to Completed or Failed.
Node Notification
CironetPay sends a signed webhook to the Node's callback_url so the Node can fulfill the order (credit wallet, activate service, etc.).
Merchant Identity Model
CironetPay operates on a single-merchant, multi-source model. Payment providers see only Cironet Technologies Limited as the merchant.
| Perspective | What they see | Why |
|---|---|---|
| Payment Providers | One merchant: Cironet Technologies Limited | Simplifies compliance. Providers deal with one trusted entity; clients don't need their own PSP accounts. |
| Inside CironetPay | Every transaction is tagged with its node_source and merchant_id |
Enables granular per-product and per-client reporting, billing, and reconciliation. |
Who It Serves
Cironet Products
Internal platforms owned by Cironet Technologies.
- CironetMessaging — Wallet top-ups & billing
- CironetSmart ERP — Invoice & payroll payments
- CironetMarketplace — Storefront checkout
- cironetug.com — Domains & hosting fees
Client Platforms
Solutions Cironet develops for external businesses.
- SACCO & Microfinance systems
- School Fees platforms
- NGO Donor management
- E-commerce custom builds
Core Capabilities
Mobile Money
STK push to the payer's phone. Supports all major Ugandan mobile money networks.
Node Isolation
Every node has its own API key and isolated transaction history. Keys rotate independently.
Signed Callbacks
HMAC-SHA256 signatures on every outbound webhook. Nodes verify before fulfilling.
Idempotency
Duplicate checkout requests for the same reference are detected and returned safely.
Central Ledger
All payments across the ecosystem recorded in one place with full audit trail.
Merchant Portal
Dashboard at /portal for merchants to view their transaction history and stats.
Checkout Flow (Node Integration)
Nodes integrate via a redirect checkout. The Node builds a form with the required fields, signs it with a HMAC hash, and POSTs the user to CironetPay.
Step 1 — Build and POST the form
// PHP example — Node side (e.g. messaging.cironetug.com)
$app_key = 'cp_live_msg_...'; // Your node's API key
$app_secret = 'cp_sec_msg_...'; // Your node's secret (kept private)
$reference = 'CTR' . bin2hex(random_bytes(6)); // Unique per transaction
$amount = 10000; // UGX
$hash = hash('sha256', $app_key . $reference . $amount . $app_secret);
// POST these fields to https://pay.cironetug.com/checkout.php
$payload = [
'app_key' => $app_key,
'reference' => $reference,
'amount' => $amount,
'currency' => 'UGX',
'customer_email' => $user_email,
'callback_url' => 'https://yourapp.com/api/payment_callback.php',
'return_url' => 'https://yourapp.com/user/history.php',
'hash' => $hash,
];
Step 2 — CironetPay handles the rest
The user is shown a phone number input on the CironetPay checkout page, enters their Mobile Money number, and authorizes the payment on their phone. The Node does not handle any of this.
Authentication
Nodes are authenticated via two values that must both be correct for a checkout to proceed:
| Field | Where | Purpose |
|---|---|---|
app_key |
POST body | Identifies the node. Matched against config/config.php api_keys. |
hash |
POST body | Integrity check. sha256(app_key + reference + amount + app_secret). Prevents tampering with the amount. |
Never expose app_secret in client-side code. Hash generation must happen server-side only.
Request Fields
| Field | Required | Description |
|---|---|---|
app_key | Required | Node API key issued by Cironet. |
reference | Required | Unique transaction reference from the Node. Used to match the webhook callback. |
amount | Required | Payment amount in UGX (integer, no decimals). |
currency | Required | Must be UGX. |
customer_email | Required | Payer's email address for records. |
callback_url | Required | HTTPS endpoint on the Node that receives the signed payment result webhook. |
return_url | Required | Where to send the user after they authorize (or cancel) payment. |
hash | Required | sha256(app_key + reference + amount + app_secret). |
metadata | Optional | JSON string. Passed through and logged. Useful for channel, units, order details. |
Webhook Callback
When a payment completes, CironetPay sends a POST request to the Node's
callback_url with a JSON body and a signature header.
Webhook Payload
{
"reference": "CTR3f8a92b1c4d5",
"amount": 10000,
"status": "completed",
"transaction_id": "a4b2c8d1e6f0..."
}
Webhook Headers
Content-Type: application/json X-CironetPay-Signature: <hmac-sha256-hex>
Node-side handling
// PHP — verify the webhook before processing
$raw_body = file_get_contents('php://input');
$provided_sig = $_SERVER['HTTP_X_CIRONETPAY_SIGNATURE'] ?? '';
$webhook_secret = 'whs_...'; // your cironetpay_webhook_secret
$expected_sig = hash_hmac('sha256', $raw_body, $webhook_secret);
if (!hash_equals($expected_sig, $provided_sig)) {
http_response_code(401);
exit('Invalid signature');
}
$data = json_decode($raw_body, true);
if ($data['status'] === 'completed') {
// Credit wallet, activate service, fulfill order...
}
http_response_code(200);
echo json_encode(['status' => 'success']);
Always return HTTP 200 after successful processing. CironetPay logs non-200 responses as delivery failures.
Signature Security
CironetPay uses two separate secrets — do not confuse them:
| Secret | Used for | Held by |
|---|---|---|
app_secret |
Signing the hash field on the outgoing checkout request |
Node (in system_settings.cironetpay_app_secret) and CironetPay (config.api_secrets) |
webhook_secret |
Signing the X-CironetPay-Signature header on inbound webhook callbacks |
Node (in system_settings.cironetpay_webhook_secret) and CironetPay (WEBHOOK_OUTGOING_SECRET env var) |
Technical Architecture
Built with PHP 8.1+ on a standard LAMP stack. Hosted at pay.cironetug.com.
Component Overview
| Component | File | Responsibility |
|---|---|---|
| Checkout | checkout.php | Authenticates node, collects phone, initiates charge. |
| CironetPay Engine | core/CironetPay.php | Idempotency check, DB write, gateway delegation. |
| Payment Gateway | gateways/ | One class per payment provider, handling the provider-specific protocol. Internal to CironetPay — never exposed to nodes or merchants. |
| Webhook Receiver | api/webhook.php | Receives provider callbacks, updates transaction status. |
| Notifier | core/Notifier.php | Sends signed webhook to the Node's callback_url. |
| Merchant Portal | portal/ | Dashboard for merchants to view their transactions. |
Database Schema
transactions
The central ledger. Every payment attempt is recorded here.
CREATE TABLE transactions (
id INT AUTO_INCREMENT PRIMARY KEY,
transaction_ref VARCHAR(100) UNIQUE NOT NULL,
external_id VARCHAR(100), -- the Node's reference
node_source VARCHAR(50), -- e.g. 'messaging_node'
merchant_id INT,
provider VARCHAR(50), -- internal only
amount DECIMAL(20,2),
currency VARCHAR(10),
payer_phone VARCHAR(30),
payer_email VARCHAR(150),
callback_url VARCHAR(500), -- Node's callback endpoint
status ENUM('Pending','Completed','Failed','Cancelled'),
provider_reference VARCHAR(255),
raw_log JSON,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
merchant_webhooks
Registered webhook URLs for merchant portal accounts.
CREATE TABLE merchant_webhooks (
id INT AUTO_INCREMENT PRIMARY KEY,
merchant_id INT NOT NULL,
url VARCHAR(500) NOT NULL,
secret VARCHAR(255),
status ENUM('active','inactive') DEFAULT 'active'
);
Configuration
All credentials live in .env. Never commit this file to version control.
| Variable | Description |
|---|---|
DB_HOST / DB_NAME / DB_USER / DB_PASS | MySQL connection |
CIRONET_API_KEY_{NODE} | Public API key for each connected node |
CIRONET_API_SECRET_{NODE} | Secret used to verify the checkout hash from that node |
WEBHOOK_OUTGOING_SECRET | Secret used to sign all outbound webhook callbacks to nodes |
Security: If a node's API key is compromised, update CIRONET_API_KEY_{NODE} in .env and cironetpay_app_key in the node's database simultaneously.
Node Onboarding
Generate Keys
Create an API key and a separate secret: bin2hex(random_bytes(16)) for each.
Configure CironetPay
Add CIRONET_API_KEY_{NODE} and CIRONET_API_SECRET_{NODE} to the .env file, and register the node slug in config/config.php.
Configure the Node
Add cironetpay_app_key, cironetpay_app_secret, and cironetpay_webhook_secret to the node's system_settings table.
Implement Callback
Build a /api/payment_callback.php endpoint on the node that verifies the HMAC signature and fulfills the order.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| 401 Unauthorized Node | app_key not found in config |
Verify CIRONET_API_KEY_{NODE} in .env matches what the node is sending. |
| 400 Invalid Signature Hash | hash mismatch on checkout |
Ensure node's cironetpay_app_secret matches CIRONET_API_SECRET_{NODE} in CironetPay's .env. |
| Transaction stuck Pending | Provider webhook not received, or user didn't authorize | Check logs/webhooks_raw.log for provider callback. Verify webhook URL registered with provider. |
| Callback returns 401 on Node | Signature mismatch on webhook | Confirm node's cironetpay_webhook_secret equals WEBHOOK_OUTGOING_SECRET in CironetPay's .env. |
| Callback returns 404 on Node | Transaction reference not found or already completed | Check if the transaction was previously fulfilled (idempotency). Review node's transaction log. |
| No webhook delivered | callback_url was not stored on the transaction |
Ensure callback_url is included in the checkout POST payload. Check logs/notifier.log. |
Roadmap
- Admin Dashboard: Visual interface for cross-node transaction management and reporting.
- Per-Node Billing: Automated commission statements for client nodes.
- Refunds API: Unified endpoint for initiating refunds.
- Card Payments: Visa/Mastercard acceptance for international payers.
- Bank Direct: Direct Stanbic and Equity bank transfer integrations.