warimcp 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.env.example ADDED
@@ -0,0 +1,19 @@
1
+ # WariMCP — Environment Variables
2
+ # Copy this file to .env and fill in your credentials
3
+
4
+ # CinetPay (Phase 1)
5
+ CINETPAY_API_KEY=
6
+ CINETPAY_SITE_ID=
7
+
8
+ # Wave (Phase 1)
9
+ WAVE_API_KEY=
10
+
11
+ # Hub2 / Ecobank unified gateway (Phase 2)
12
+ HUB2_API_KEY=
13
+
14
+ # PAPSS Pan-African (Phase 3)
15
+ PAPSS_API_KEY=
16
+
17
+ # Server config
18
+ WARIMCP_PORT=3000
19
+ WARIMCP_ENV=development
package/.gitkeep ADDED
File without changes
package/Dockerfile ADDED
@@ -0,0 +1,14 @@
1
+ FROM node:20-alpine
2
+
3
+ WORKDIR /app
4
+
5
+ COPY package*.json ./
6
+ RUN npm install --production
7
+
8
+ COPY . .
9
+
10
+ USER node
11
+
12
+ EXPOSE 3000
13
+
14
+ CMD ["node", "src/index.js"]
package/NPM_SETUP.md ADDED
@@ -0,0 +1,76 @@
1
+ # WariMCP — npm Publish Setup
2
+
3
+ ## One-Time Setup
4
+
5
+ ### Step 1 — Create an npm Account
6
+ 1. Go to https://www.npmjs.com/signup
7
+ 2. Create an account with:
8
+ - Username: (your preferred handle, e.g. `bigabou`)
9
+ - Email: your main email
10
+ - Password: strong, unique
11
+ 3. Verify your email from the confirmation message
12
+
13
+ ### Step 2 — Enable 2FA (Required for Publishing)
14
+ 1. Log into npmjs.com
15
+ 2. Click your avatar → **Account** → **Two-Factor Authentication**
16
+ 3. Choose **Auth and Writes** (required for publish)
17
+ 4. Scan the QR code with Google Authenticator or Authy
18
+ 5. Save your backup codes securely
19
+
20
+ ### Step 3 — Log In from Terminal
21
+
22
+ ```bash
23
+ cd ~/automation/projects/warimcp
24
+ npm login
25
+ ```
26
+
27
+ You'll be prompted for:
28
+ - Username
29
+ - Password
30
+ - Email
31
+ - OTP (from your authenticator app)
32
+
33
+ Verify login:
34
+ ```bash
35
+ npm whoami
36
+ # should return your npm username
37
+ ```
38
+
39
+ ### Step 4 — Publish the Package
40
+
41
+ ```bash
42
+ npm publish --access=public
43
+ ```
44
+
45
+ > **Note:** The package name `warimcp` was confirmed available as of March 1, 2026.
46
+
47
+ ### Step 5 — Verify on npm
48
+
49
+ Visit: https://www.npmjs.com/package/warimcp
50
+
51
+ You should see the package live within ~2 minutes.
52
+
53
+ ---
54
+
55
+ ## Future Publishes
56
+
57
+ Once logged in, Claude Code can handle all future publishes automatically:
58
+
59
+ ```bash
60
+ # Bump version (patch / minor / major)
61
+ npm version patch # 0.0.1 → 0.0.2
62
+ npm version minor # 0.0.1 → 0.1.0
63
+
64
+ # Publish
65
+ npm publish --access=public
66
+ ```
67
+
68
+ Or just tell Claude: "Publish warimcp version X.Y.Z" and it will handle the version bump and publish.
69
+
70
+ ---
71
+
72
+ ## Important Notes
73
+
74
+ - Do NOT run `npm publish` without bumping the version first — npm will reject duplicate versions
75
+ - The `--access=public` flag is required for the first publish of a scoped or new package
76
+ - npm sessions expire — re-run `npm login` if you get auth errors
package/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # WariMCP
2
+
3
+ > **The AI-native payment layer for West Africa**
4
+
5
+ [![npm version](https://img.shields.io/npm/v/warimcp.svg?style=flat-square)](https://www.npmjs.com/package/warimcp)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
7
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://github.com/Bigabou007-dev/warimcp/blob/main/CONTRIBUTING.md)
8
+
9
+ ---
10
+
11
+ ## Overview
12
+
13
+ WariMCP is an **MCP (Model Context Protocol) server** that gives AI agents and LLMs a unified, production-ready interface to all major West African payment rails.
14
+
15
+ Instead of integrating CinetPay, Wave, Hub2/Ecobank, and PAPSS one by one — WariMCP exposes them all through a single, consistent API that any Claude, GPT, or custom AI agent can call natively.
16
+
17
+ **Built for:**
18
+ - AI agents that need to initiate, track, or refund payments in West Africa
19
+ - Developers building fintech products across UEMOA and pan-African corridors
20
+ - Agencies that need a white-label payment middleware layer
21
+
22
+ ---
23
+
24
+ ## Supported Payment Rails
25
+
26
+ | Provider | Region | Coverage | Phase |
27
+ |---|---|---|---|
28
+ | **CinetPay** | UEMOA | Orange Money, MTN, Wave, Moov, Visa/MC | Phase 1 |
29
+ | **Wave** | Côte d'Ivoire, Sénégal | Wave mobile wallet | Phase 1 |
30
+ | **Hub2 / Ecobank** | UEMOA (unified) | 200M+ mobile wallets, single API | Phase 2 |
31
+ | **PAPSS** | Pan-African | CI ↔ Kenya corridor, 160+ banks, local currency | Phase 3 |
32
+
33
+ ---
34
+
35
+ ## Installation
36
+
37
+ ```bash
38
+ npm install warimcp
39
+ ```
40
+
41
+ ### Environment Variables
42
+
43
+ Copy `.env.example` and fill in your credentials:
44
+
45
+ ```bash
46
+ cp .env.example .env
47
+ ```
48
+
49
+ ```env
50
+ CINETPAY_API_KEY=your_cinetpay_api_key
51
+ CINETPAY_SITE_ID=your_cinetpay_site_id
52
+ WAVE_API_KEY=your_wave_api_key
53
+ HUB2_API_KEY=your_hub2_api_key
54
+ PAPSS_API_KEY=your_papss_api_key
55
+ WARIMCP_PORT=3000
56
+ WARIMCP_ENV=development
57
+ ```
58
+
59
+ ---
60
+
61
+ ## Usage
62
+
63
+ ### As an MCP Server (Claude / Cursor / Windsurf)
64
+
65
+ Add to your `.claude.json` or MCP config:
66
+
67
+ ```json
68
+ {
69
+ "mcpServers": {
70
+ "warimcp": {
71
+ "command": "node",
72
+ "args": ["/path/to/warimcp/src/index.js"],
73
+ "env": {
74
+ "CINETPAY_API_KEY": "...",
75
+ "CINETPAY_SITE_ID": "..."
76
+ }
77
+ }
78
+ }
79
+ }
80
+ ```
81
+
82
+ ### As a Node.js Module
83
+
84
+ ```js
85
+ const warimcp = require('warimcp');
86
+
87
+ const result = await warimcp.initiatePayment({
88
+ provider: 'cinetpay',
89
+ amount: 5000,
90
+ currency: 'XOF',
91
+ customer: { name: 'Kofi Atta', phone: '+2250700000000' },
92
+ description: 'Invoice #001',
93
+ return_url: 'https://yourapp.com/payment/callback'
94
+ });
95
+ ```
96
+
97
+ ---
98
+
99
+ ## MCP Tools
100
+
101
+ WariMCP exposes the following tools to AI agents:
102
+
103
+ | Tool | Description |
104
+ |---|---|
105
+ | `initiate_payment` | Start a payment via any supported rail |
106
+ | `check_status` | Poll the status of a transaction |
107
+ | `refund` | Issue a full or partial refund |
108
+ | `list_transactions` | List recent transactions with filters |
109
+ | `generate_payment_link` | Create a shareable payment link |
110
+
111
+ ---
112
+
113
+ ## Roadmap
114
+
115
+ ### Phase 1 — CinetPay + Wave (March 2026)
116
+ - [x] Project scaffold + MCP server
117
+ - [ ] CinetPay provider: `initiate_payment`, `check_status`, `refund`
118
+ - [ ] Wave provider: `initiate_payment`, `check_status`
119
+ - [ ] Webhook handler (payment confirmation)
120
+ - [ ] Master log / queue for Always-On reliability
121
+ - [ ] npm publish (`npm install warimcp`)
122
+
123
+ ### Phase 2 — Hub2 / Ecobank Unified Gateway (60 days)
124
+ - [ ] Hub2 provider integration (single API → full UEMOA)
125
+ - [ ] Unified routing layer (auto-select best rail by country/wallet)
126
+ - [ ] `list_transactions` across providers
127
+ - [ ] Hosted dashboard (transaction monitor)
128
+
129
+ ### Phase 3 — PAPSS Pan-African Corridor (Q3 2026)
130
+ - [ ] PAPSS integration (CI ↔ Kenya + 160 banks)
131
+ - [ ] Multi-currency settlement engine
132
+ - [ ] Enterprise middleware API (AI Bookkeeper, Bulk Payment Agent)
133
+ - [ ] Partnership program (Hub2, CinetPay, Orange Ventures)
134
+
135
+ ---
136
+
137
+ ## Contributing
138
+
139
+ PRs, issues, and discussions are welcome. Please open an issue before submitting major changes.
140
+
141
+ 1. Fork the repo
142
+ 2. Create your branch: `git checkout -b feat/your-feature`
143
+ 3. Commit your changes: `git commit -m 'feat: add your feature'`
144
+ 4. Push: `git push origin feat/your-feature`
145
+ 5. Open a Pull Request
146
+
147
+ ---
148
+
149
+ ## License
150
+
151
+ [MIT](LICENSE) — Built by [Bigabou](https://github.com/Bigabou007-dev) in Abidjan, Côte d'Ivoire.
package/RECOVERY.md ADDED
@@ -0,0 +1,62 @@
1
+ # WariMCP — Recovery Protocol
2
+
3
+ **Target:** Restore WariMCP to a fresh host in under 30 minutes.
4
+
5
+ ## Prerequisites on Fresh Host
6
+ - Ubuntu 22.04+
7
+ - Docker + Docker Compose installed
8
+ - Nginx Proxy Manager running with `npm_proxy` network
9
+
10
+ ## Step-by-Step Restore
11
+
12
+ ### Step 1 — Restore Files (5 min)
13
+ ```bash
14
+ # From backup archive (gdrive:vps-backups)
15
+ rclone copy gdrive:vps-backups/warimcp/latest.tar.gz /tmp/
16
+ cd /tmp && tar -xzf latest.tar.gz
17
+ mv warimcp ~/automation/projects/
18
+ ```
19
+
20
+ ### Step 2 — Restore Environment (2 min)
21
+ ```bash
22
+ cd ~/automation/projects/warimcp
23
+ # .env is NOT in the archive — restore from your password manager or secure vault
24
+ cp /path/to/secure/.env .env
25
+ ```
26
+
27
+ ### Step 3 — Restore Database (10 min)
28
+ ```bash
29
+ # Start only the DB container first
30
+ docker compose up -d warimcp_db
31
+
32
+ # Wait for postgres to be ready
33
+ sleep 10
34
+
35
+ # Restore from backup dump
36
+ docker compose exec -T warimcp_db psql -U warimcp warimcp < /tmp/warimcp_db_backup.sql
37
+ ```
38
+
39
+ ### Step 4 — Start Service (3 min)
40
+ ```bash
41
+ docker compose up -d
42
+ docker compose logs -f warimcp # confirm healthy
43
+ ```
44
+
45
+ ### Step 5 — Verify NPM Route (2 min)
46
+ - Log into Nginx Proxy Manager dashboard
47
+ - Confirm `warimcp` proxy host points to container:3000 on npm_proxy network
48
+ - Test payment status endpoint
49
+
50
+ ### Step 6 — Queue Reconciliation (5 min)
51
+ ```bash
52
+ # Check the master log for any in-flight transactions at time of failure
53
+ cat ~/automation/projects/warimcp/logs/queue.log | tail -50
54
+ # Re-process any PENDING transactions manually if needed
55
+ ```
56
+
57
+ ## Total Estimated Time: ~27 minutes
58
+
59
+ ## Contacts
60
+ - CinetPay support: support@cinetpay.com
61
+ - Wave developer support: developers@wave.com
62
+ - Hub2: developers@hub2.io
package/TECHNICAL.md ADDED
@@ -0,0 +1,82 @@
1
+ # WariMCP — Technical Documentation
2
+
3
+ ## Architecture
4
+
5
+ WariMCP is a Node.js MCP (Model Context Protocol) server that exposes West African payment rails to AI agents. It runs as a containerized service behind Nginx Proxy Manager with an embedded PostgreSQL database for transaction logging.
6
+
7
+ ```
8
+ AI Agent / LLM
9
+
10
+
11
+ WariMCP MCP Server (Express + MCP SDK)
12
+
13
+ ├── router.js → Selects the correct payment provider
14
+ ├── tools.js → MCP tool definitions
15
+ ├── queue.js → Master log / Always-On queue
16
+
17
+ ├── providers/
18
+ │ ├── cinetpay.js → CinetPay UEMOA gateway (Phase 1)
19
+ │ ├── wave.js → Wave mobile wallet (Phase 1)
20
+ │ ├── hub2.js → Hub2/Ecobank unified (Phase 2)
21
+ │ └── papss.js → PAPSS pan-African (Phase 3)
22
+
23
+ └── webhook.js → Inbound payment confirmations
24
+ ```
25
+
26
+ ## Deployment
27
+
28
+ ### Prerequisites
29
+ - Docker + Docker Compose
30
+ - Nginx Proxy Manager (shared `npm_proxy` network)
31
+ - `.env` file with all required keys
32
+
33
+ ### First-Time Setup
34
+
35
+ ```bash
36
+ # 1. Clone the repo
37
+ git clone https://github.com/Bigabou007-dev/warimcp.git
38
+ cd warimcp
39
+
40
+ # 2. Configure environment
41
+ cp .env.example .env
42
+ nano .env # fill in API keys
43
+
44
+ # 3. Add DB password to .env
45
+ echo "DB_PASSWORD=$(openssl rand -hex 16)" >> .env
46
+
47
+ # 4. Start services
48
+ docker compose up -d
49
+
50
+ # 5. Verify
51
+ docker compose logs -f warimcp
52
+ ```
53
+
54
+ ### NPM Proxy Manager Route
55
+ - Container name: `warimcp`
56
+ - Internal port: `3000`
57
+ - Route via NPM — do NOT expose this port publicly
58
+
59
+ ## Maintenance
60
+
61
+ ### Logs
62
+ ```bash
63
+ docker compose logs -f warimcp
64
+ docker compose logs -f warimcp_db
65
+ ```
66
+
67
+ ### Updates
68
+ ```bash
69
+ git pull
70
+ docker compose up -d --build
71
+ ```
72
+
73
+ ### Restart
74
+ ```bash
75
+ docker compose restart warimcp
76
+ ```
77
+
78
+ ## Security
79
+ - Service runs as non-root (`USER node`)
80
+ - Zero public ports — all traffic via NPM internal network
81
+ - API keys stored in `.env` only, never hardcoded
82
+ - DB credentials auto-generated at setup
package/backup.sh ADDED
@@ -0,0 +1,55 @@
1
+ #!/bin/bash
2
+
3
+ # WariMCP — Backup Script
4
+ # Usage: ./backup.sh [--dry-run]
5
+
6
+ set -euo pipefail
7
+
8
+ DRY_RUN=false
9
+ [[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true
10
+
11
+ TIMESTAMP=$(date +%Y%m%d_%H%M%S)
12
+ BACKUP_DIR="/tmp/warimcp_backup_${TIMESTAMP}"
13
+ ARCHIVE="/tmp/warimcp_${TIMESTAMP}.tar.gz"
14
+ REMOTE="gdrive:vps-backups/warimcp"
15
+
16
+ log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
17
+
18
+ log "WariMCP backup starting — dry-run: $DRY_RUN"
19
+
20
+ mkdir -p "$BACKUP_DIR"
21
+
22
+ log "Dumping database..."
23
+ if $DRY_RUN; then
24
+ log "[DRY-RUN] Would run: docker compose exec warimcp_db pg_dump -U warimcp warimcp"
25
+ else
26
+ docker compose -f "$(dirname "$0")/docker-compose.yml" exec -T warimcp_db \
27
+ pg_dump -U warimcp warimcp > "${BACKUP_DIR}/warimcp_db_${TIMESTAMP}.sql"
28
+ fi
29
+
30
+ log "Archiving code..."
31
+ if $DRY_RUN; then
32
+ log "[DRY-RUN] Would archive: $(dirname "$0") -> $ARCHIVE"
33
+ else
34
+ tar -czf "$ARCHIVE" \
35
+ --exclude=".env" \
36
+ --exclude="node_modules" \
37
+ --exclude=".git" \
38
+ -C "$(dirname "$(realpath "$0")")" . \
39
+ -C "$BACKUP_DIR" .
40
+ fi
41
+
42
+ log "Syncing to remote..."
43
+ if $DRY_RUN; then
44
+ log "[DRY-RUN] Would run: rclone copy $ARCHIVE $REMOTE/"
45
+ else
46
+ rclone copy "$ARCHIVE" "$REMOTE/" --progress
47
+ rclone copy "${BACKUP_DIR}/warimcp_db_${TIMESTAMP}.sql" "$REMOTE/" --progress
48
+ fi
49
+
50
+ log "Cleaning up temp files..."
51
+ if ! $DRY_RUN; then
52
+ rm -rf "$BACKUP_DIR" "$ARCHIVE"
53
+ fi
54
+
55
+ log "Backup complete."
@@ -0,0 +1,45 @@
1
+ version: "3.9"
2
+
3
+ services:
4
+ warimcp:
5
+ build: .
6
+ container_name: warimcp
7
+ restart: unless-stopped
8
+ env_file: .env
9
+ networks:
10
+ - npm_proxy
11
+ deploy:
12
+ resources:
13
+ limits:
14
+ cpus: "0.25"
15
+ memory: 256M
16
+ reservations:
17
+ cpus: "0.10"
18
+ memory: 128M
19
+ depends_on:
20
+ - warimcp_db
21
+
22
+ warimcp_db:
23
+ image: postgres:16-alpine
24
+ container_name: warimcp_db
25
+ restart: unless-stopped
26
+ environment:
27
+ POSTGRES_USER: warimcp
28
+ POSTGRES_PASSWORD: ${DB_PASSWORD}
29
+ POSTGRES_DB: warimcp
30
+ volumes:
31
+ - warimcp_db_data:/var/lib/postgresql/data
32
+ networks:
33
+ - npm_proxy
34
+ deploy:
35
+ resources:
36
+ limits:
37
+ cpus: "0.25"
38
+ memory: 256M
39
+
40
+ volumes:
41
+ warimcp_db_data:
42
+
43
+ networks:
44
+ npm_proxy:
45
+ external: true
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "warimcp",
3
+ "version": "0.0.1",
4
+ "description": "WariMCP — AI-native payment MCP server for West Africa. Unified gateway for CinetPay, Wave, Hub2/Ecobank, and PAPSS payment rails.",
5
+ "main": "src/index.js",
6
+ "scripts": {
7
+ "start": "node src/index.js",
8
+ "dev": "node --watch src/index.js",
9
+ "test": "echo \"No tests yet\" && exit 0"
10
+ },
11
+ "keywords": [
12
+ "mcp",
13
+ "payments",
14
+ "west-africa",
15
+ "cinetpay",
16
+ "wave",
17
+ "fintech",
18
+ "africa",
19
+ "nodejs",
20
+ "uemoa",
21
+ "papss"
22
+ ],
23
+ "author": "Bigabou",
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/Bigabou007-dev/warimcp.git"
28
+ },
29
+ "homepage": "https://github.com/Bigabou007-dev/warimcp#readme",
30
+ "bugs": {
31
+ "url": "https://github.com/Bigabou007-dev/warimcp/issues"
32
+ },
33
+ "engines": {
34
+ "node": ">=18.0.0"
35
+ },
36
+ "dependencies": {
37
+ "axios": "^1.7.0",
38
+ "dotenv": "^16.4.0",
39
+ "express": "^4.18.0"
40
+ }
41
+ }
package/src/index.js ADDED
@@ -0,0 +1,33 @@
1
+ require("dotenv").config();
2
+ const express = require("express");
3
+ const { tools } = require("./tools");
4
+ const { processToolCall } = require("./router");
5
+ const { logQueue } = require("./queue");
6
+ const { handleWebhook } = require("./webhook");
7
+
8
+ const app = express();
9
+ app.use(express.json());
10
+
11
+ app.post("/mcp", async (req, res) => {
12
+ const { tool, input } = req.body;
13
+ if (!tool || !input) return res.status(400).json({ error: "tool and input required" });
14
+
15
+ try {
16
+ logQueue("RECEIVED", tool, input);
17
+ const result = await processToolCall(tool, input);
18
+ logQueue("COMPLETED", tool, result);
19
+ res.json({ result });
20
+ } catch (err) {
21
+ logQueue("FAILED", tool, { error: err.message });
22
+ res.status(500).json({ error: err.message });
23
+ }
24
+ });
25
+
26
+ app.get("/tools", (_req, res) => res.json({ tools }));
27
+
28
+ app.post("/webhook/:provider", handleWebhook);
29
+
30
+ app.get("/health", (_req, res) => res.json({ status: "ok", ts: new Date().toISOString() }));
31
+
32
+ const PORT = process.env.WARIMCP_PORT || 3000;
33
+ app.listen(PORT, () => console.log(`WariMCP listening on port ${PORT}`));
@@ -0,0 +1,73 @@
1
+ const axios = require("axios");
2
+
3
+ const BASE_URL = "https://api-checkout.cinetpay.com/v2";
4
+
5
+ function headers() {
6
+ return { "Content-Type": "application/json" };
7
+ }
8
+
9
+ function credentials() {
10
+ return {
11
+ apikey: process.env.CINETPAY_API_KEY,
12
+ site_id: process.env.CINETPAY_SITE_ID
13
+ };
14
+ }
15
+
16
+ async function initiatePayment({ amount, currency, customer, description, return_url, metadata }) {
17
+ const transaction_id = `CP_${Date.now()}_${Math.random().toString(36).slice(2,8)}`;
18
+
19
+ const payload = {
20
+ ...credentials(),
21
+ transaction_id,
22
+ amount,
23
+ currency: currency || "XOF",
24
+ description: description || "WariMCP Payment",
25
+ customer_name: customer.name,
26
+ customer_surname: "",
27
+ customer_email: customer.email || "",
28
+ customer_phone_number: customer.phone,
29
+ customer_address: "",
30
+ customer_city: "",
31
+ customer_country: "CI",
32
+ customer_state: "CI",
33
+ customer_zip_code: "00000",
34
+ return_url: return_url || "",
35
+ notify_url: `${process.env.WARIMCP_WEBHOOK_URL || ""}/webhook/cinetpay`,
36
+ channels: "ALL",
37
+ metadata: JSON.stringify(metadata || {})
38
+ };
39
+
40
+ const { data } = await axios.post(`${BASE_URL}/payment`, payload, { headers: headers() });
41
+ return { transaction_id, ...data };
42
+ }
43
+
44
+ async function checkStatus(transaction_id) {
45
+ const { data } = await axios.post(`${BASE_URL}/payment/check`, {
46
+ ...credentials(),
47
+ transaction_id
48
+ }, { headers: headers() });
49
+
50
+ return data;
51
+ }
52
+
53
+ async function refund(transaction_id, amount, reason) {
54
+ throw new Error("CinetPay refunds require manual processing via CinetPay dashboard.");
55
+ }
56
+
57
+ async function listTransactions({ limit = 20, offset = 0 } = {}) {
58
+ throw new Error("CinetPay list_transactions not yet implemented.");
59
+ }
60
+
61
+ async function generatePaymentLink({ amount, currency, description, expires_at, metadata }) {
62
+ const result = await initiatePayment({
63
+ amount,
64
+ currency,
65
+ customer: { name: "Customer", phone: "" },
66
+ description,
67
+ metadata
68
+ });
69
+
70
+ return { payment_url: result?.data?.payment_url, transaction_id: result.transaction_id };
71
+ }
72
+
73
+ module.exports = { initiatePayment, checkStatus, refund, listTransactions, generatePaymentLink };
@@ -0,0 +1,21 @@
1
+ async function initiatePayment(input) {
2
+ throw new Error("Hub2 provider coming in Phase 2.");
3
+ }
4
+
5
+ async function checkStatus(transaction_id) {
6
+ throw new Error("Hub2 provider coming in Phase 2.");
7
+ }
8
+
9
+ async function refund(transaction_id, amount, reason) {
10
+ throw new Error("Hub2 provider coming in Phase 2.");
11
+ }
12
+
13
+ async function listTransactions(filters) {
14
+ throw new Error("Hub2 provider coming in Phase 2.");
15
+ }
16
+
17
+ async function generatePaymentLink(input) {
18
+ throw new Error("Hub2 provider coming in Phase 2.");
19
+ }
20
+
21
+ module.exports = { initiatePayment, checkStatus, refund, listTransactions, generatePaymentLink };
@@ -0,0 +1,21 @@
1
+ async function initiatePayment(input) {
2
+ throw new Error("PAPSS provider coming in Phase 3.");
3
+ }
4
+
5
+ async function checkStatus(transaction_id) {
6
+ throw new Error("PAPSS provider coming in Phase 3.");
7
+ }
8
+
9
+ async function refund(transaction_id, amount, reason) {
10
+ throw new Error("PAPSS provider coming in Phase 3.");
11
+ }
12
+
13
+ async function listTransactions(filters) {
14
+ throw new Error("PAPSS provider coming in Phase 3.");
15
+ }
16
+
17
+ async function generatePaymentLink(input) {
18
+ throw new Error("PAPSS provider coming in Phase 3.");
19
+ }
20
+
21
+ module.exports = { initiatePayment, checkStatus, refund, listTransactions, generatePaymentLink };
@@ -0,0 +1,42 @@
1
+ const axios = require("axios");
2
+
3
+ const BASE_URL = "https://api.wave.com/v1";
4
+
5
+ function headers() {
6
+ return {
7
+ "Authorization": `Bearer ${process.env.WAVE_API_KEY}`,
8
+ "Content-Type": "application/json"
9
+ };
10
+ }
11
+
12
+ async function initiatePayment({ amount, currency, customer, description, return_url, metadata }) {
13
+ const payload = {
14
+ currency: currency || "XOF",
15
+ amount: String(amount),
16
+ error_url: return_url || "",
17
+ success_url: return_url || "",
18
+ client_reference: `WAVE_${Date.now()}`
19
+ };
20
+
21
+ const { data } = await axios.post(`${BASE_URL}/checkout/sessions`, payload, { headers: headers() });
22
+ return { transaction_id: data.id, payment_url: data.wave_launch_url, ...data };
23
+ }
24
+
25
+ async function checkStatus(transaction_id) {
26
+ const { data } = await axios.get(`${BASE_URL}/checkout/sessions/${transaction_id}`, { headers: headers() });
27
+ return data;
28
+ }
29
+
30
+ async function refund(transaction_id, amount, reason) {
31
+ throw new Error("Wave refunds not yet implemented.");
32
+ }
33
+
34
+ async function listTransactions({ limit = 20, offset = 0 } = {}) {
35
+ throw new Error("Wave list_transactions not yet implemented.");
36
+ }
37
+
38
+ async function generatePaymentLink({ amount, currency, description, metadata }) {
39
+ return initiatePayment({ amount, currency, customer: { name: "Customer", phone: "" }, description, metadata });
40
+ }
41
+
42
+ module.exports = { initiatePayment, checkStatus, refund, listTransactions, generatePaymentLink };
package/src/queue.js ADDED
@@ -0,0 +1,42 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ const LOG_FILE = path.join(__dirname, "../logs/queue.log");
5
+
6
+ function logQueue(status, tool, data) {
7
+ const entry = JSON.stringify({
8
+ ts: new Date().toISOString(),
9
+ status,
10
+ tool,
11
+ data
12
+ });
13
+
14
+ fs.appendFileSync(LOG_FILE, entry + "\n");
15
+ }
16
+
17
+ function readQueue() {
18
+ if (!fs.existsSync(LOG_FILE)) return [];
19
+
20
+ return fs.readFileSync(LOG_FILE, "utf8")
21
+ .split("\n")
22
+ .filter(Boolean)
23
+ .map(line => {
24
+ try { return JSON.parse(line); }
25
+ catch { return null; }
26
+ })
27
+ .filter(Boolean);
28
+ }
29
+
30
+ function getPending() {
31
+ const entries = readQueue();
32
+ const pending = new Map();
33
+
34
+ for (const entry of entries) {
35
+ if (entry.status === "RECEIVED") pending.set(entry.data?.transaction_id, entry);
36
+ if (entry.status === "COMPLETED" || entry.status === "FAILED") pending.delete(entry.data?.transaction_id);
37
+ }
38
+
39
+ return [...pending.values()];
40
+ }
41
+
42
+ module.exports = { logQueue, readQueue, getPending };
package/src/router.js ADDED
@@ -0,0 +1,31 @@
1
+ const cinetpay = require("./providers/cinetpay");
2
+ const wave = require("./providers/wave");
3
+ const hub2 = require("./providers/hub2");
4
+ const papss = require("./providers/papss");
5
+
6
+ const providers = { cinetpay, wave, hub2, papss };
7
+
8
+ async function processToolCall(tool, input) {
9
+ const provider = input.provider || "cinetpay";
10
+
11
+ if (!providers[provider]) {
12
+ throw new Error(`Unknown provider: ${provider}`);
13
+ }
14
+
15
+ switch (tool) {
16
+ case "initiate_payment":
17
+ return providers[provider].initiatePayment(input);
18
+ case "check_status":
19
+ return providers[provider].checkStatus(input.transaction_id);
20
+ case "refund":
21
+ return providers[provider].refund(input.transaction_id, input.amount, input.reason);
22
+ case "list_transactions":
23
+ return providers[provider].listTransactions(input);
24
+ case "generate_payment_link":
25
+ return providers[provider].generatePaymentLink(input);
26
+ default:
27
+ throw new Error(`Unknown tool: ${tool}`);
28
+ }
29
+ }
30
+
31
+ module.exports = { processToolCall };
package/src/tools.js ADDED
@@ -0,0 +1,84 @@
1
+ const tools = [
2
+ {
3
+ name: "initiate_payment",
4
+ description: "Start a payment transaction via a supported West African payment rail.",
5
+ inputSchema: {
6
+ type: "object",
7
+ required: ["provider", "amount", "currency", "customer"],
8
+ properties: {
9
+ provider: { type: "string", enum: ["cinetpay", "wave", "hub2", "papss"] },
10
+ amount: { type: "number", description: "Amount in smallest currency unit" },
11
+ currency: { type: "string", description: "ISO 4217 currency code, e.g. XOF, CFA" },
12
+ customer: {
13
+ type: "object",
14
+ required: ["name", "phone"],
15
+ properties: {
16
+ name: { type: "string" },
17
+ phone: { type: "string" },
18
+ email: { type: "string" }
19
+ }
20
+ },
21
+ description: { type: "string" },
22
+ return_url: { type: "string" },
23
+ metadata: { type: "object" }
24
+ }
25
+ }
26
+ },
27
+ {
28
+ name: "check_status",
29
+ description: "Check the status of an existing payment transaction.",
30
+ inputSchema: {
31
+ type: "object",
32
+ required: ["transaction_id", "provider"],
33
+ properties: {
34
+ transaction_id: { type: "string" },
35
+ provider: { type: "string", enum: ["cinetpay", "wave", "hub2", "papss"] }
36
+ }
37
+ }
38
+ },
39
+ {
40
+ name: "refund",
41
+ description: "Issue a full or partial refund for a completed transaction.",
42
+ inputSchema: {
43
+ type: "object",
44
+ required: ["transaction_id", "provider"],
45
+ properties: {
46
+ transaction_id: { type: "string" },
47
+ provider: { type: "string", enum: ["cinetpay", "wave", "hub2", "papss"] },
48
+ amount: { type: "number", description: "Partial refund amount. Omit for full refund." },
49
+ reason: { type: "string" }
50
+ }
51
+ }
52
+ },
53
+ {
54
+ name: "list_transactions",
55
+ description: "List recent transactions with optional filters.",
56
+ inputSchema: {
57
+ type: "object",
58
+ properties: {
59
+ provider: { type: "string", enum: ["cinetpay", "wave", "hub2", "papss", "all"] },
60
+ status: { type: "string", enum: ["pending", "completed", "failed", "refunded"] },
61
+ limit: { type: "number", default: 20 },
62
+ offset: { type: "number", default: 0 }
63
+ }
64
+ }
65
+ },
66
+ {
67
+ name: "generate_payment_link",
68
+ description: "Generate a shareable payment link for a customer.",
69
+ inputSchema: {
70
+ type: "object",
71
+ required: ["provider", "amount", "currency"],
72
+ properties: {
73
+ provider: { type: "string", enum: ["cinetpay", "wave", "hub2"] },
74
+ amount: { type: "number" },
75
+ currency: { type: "string" },
76
+ description: { type: "string" },
77
+ expires_at: { type: "string", description: "ISO 8601 expiry datetime" },
78
+ metadata: { type: "object" }
79
+ }
80
+ }
81
+ }
82
+ ];
83
+
84
+ module.exports = { tools };
package/src/webhook.js ADDED
@@ -0,0 +1,12 @@
1
+ const { logQueue } = require("./queue");
2
+
3
+ async function handleWebhook(req, res) {
4
+ const { provider } = req.params;
5
+ const payload = req.body;
6
+
7
+ logQueue("WEBHOOK", provider, payload);
8
+
9
+ res.json({ received: true });
10
+ }
11
+
12
+ module.exports = { handleWebhook };