SDK & exemples de code
Code prêt à copier dans 6 langages : PHP, Node.js, Python, Java, Go, cURL.
Client PHP minimal
PHP<?php
class NekaPayClient {
public function __construct(
private string $apiKey,
private string $apiSecret,
private string $baseUrl = 'https://nekapaie.com/api/v1'
) {}
private function sign(string $body): array {
$ts = (string) time();
return [
'ts' => $ts,
'sig' => hash_hmac('sha256', $ts . $body, $this->apiSecret),
];
}
public function request(string $method, string $path, array $body = null): array {
$bodyStr = $body ? json_encode($body) : '';
$auth = $this->sign($bodyStr);
$ch = curl_init($this->baseUrl . $path);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_POSTFIELDS => $bodyStr,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-NekaPay-Key: ' . $this->apiKey,
'X-NekaPay-Timestamp: ' . $auth['ts'],
'X-NekaPay-Signature: ' . $auth['sig'],
],
]);
$response = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return ['status' => $code, 'data' => json_decode($response, true)];
}
public function cashIn(array $payload): array {
return $this->request('POST', '/payments/cashin', $payload);
}
public function cashOut(array $payload): array {
return $this->request('POST', '/payments/cashout', $payload);
}
public function getStatus(string $txId): array {
return $this->request('GET', '/payments/' . $txId);
}
public function getBalances(): array {
return $this->request('GET', '/balances');
}
}
// Utilisation
$client = new NekaPayClient(
getenv('NEKAPAY_API_KEY'),
getenv('NEKAPAY_API_SECRET')
);
$result = $client->cashIn([
'merchant_order_id' => 'order_' . uniqid(),
'amount' => 50000,
'currency' => 'XOF',
'customer_msisdn' => '+22376123456',
'country_code' => 'ML',
]);
if ($result['status'] === 202) {
echo "Payment URL: " . $result['data']['data']['payment_url'];
}
Vérification de webhook (PHP)
PHP<?php
$secret = getenv('NEKAPAY_API_SECRET');
$ts = $_SERVER['HTTP_X_NEKAPAY_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_NEKAPAY_SIGNATURE'] ?? '';
$body = file_get_contents('php://input');
if (!$ts || abs(time() - (int)$ts) > 300) { http_response_code(401); exit('replay'); }
if (!hash_equals(hash_hmac('sha256', $ts . $body, $secret), $sig)) {
http_response_code(401); exit('signature');
}
$event = json_decode($body, true);
match ($event['status']) {
'SUCCESS' => markPaid($event['merchant_order_id']),
'FAILED', 'EXPIRED' => markFailed($event['merchant_order_id']),
'REVERSED' => markRefunded($event['merchant_order_id']),
default => null,
};
http_response_code(200);
Client Node.js minimal
JSconst crypto = require('crypto');
const axios = require('axios');
class NekaPayClient {
constructor(apiKey, apiSecret, baseUrl = 'https://nekapaie.com/api/v1') {
this.apiKey = apiKey;
this.apiSecret = apiSecret;
this.baseUrl = baseUrl;
}
sign(body) {
const ts = Math.floor(Date.now() / 1000).toString();
const sig = crypto.createHmac('sha256', this.apiSecret)
.update(ts + body).digest('hex');
return { ts, sig };
}
async request(method, path, body = null) {
const bodyStr = body ? JSON.stringify(body) : '';
const { ts, sig } = this.sign(bodyStr);
const res = await axios({
method, url: this.baseUrl + path,
data: bodyStr,
headers: {
'Content-Type': 'application/json',
'X-NekaPay-Key': this.apiKey,
'X-NekaPay-Timestamp': ts,
'X-NekaPay-Signature': sig,
},
validateStatus: () => true,
});
return { status: res.status, data: res.data };
}
cashIn(payload) { return this.request('POST', '/payments/cashin', payload); }
cashOut(payload) { return this.request('POST', '/payments/cashout', payload); }
getStatus(txId) { return this.request('GET', `/payments/${txId}`); }
getBalances() { return this.request('GET', '/balances'); }
}
// Utilisation
const client = new NekaPayClient(
process.env.NEKAPAY_API_KEY,
process.env.NEKAPAY_API_SECRET
);
(async () => {
const result = await client.cashIn({
merchant_order_id: 'order_' + Date.now(),
amount: 50000,
currency: 'XOF',
customer_msisdn: '+22376123456',
country_code: 'ML',
});
if (result.status === 202) {
console.log('Payment URL:', result.data.data.payment_url);
}
})();
Webhook handler (Express)
JSconst express = require('express');
const crypto = require('crypto');
const app = express();
app.post('/webhooks/nekapay', express.raw({type: 'application/json'}), (req, res) => {
const secret = process.env.NEKAPAY_API_SECRET;
const ts = req.header('X-NekaPay-Timestamp');
const sig = req.header('X-NekaPay-Signature');
const body = req.body.toString();
if (!ts || Math.abs(Date.now()/1000 - parseInt(ts)) > 300)
return res.status(401).send('replay');
const expected = crypto.createHmac('sha256', secret)
.update(ts + body).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig)))
return res.status(401).send('signature');
const event = JSON.parse(body);
switch (event.status) {
case 'SUCCESS': markPaid(event.merchant_order_id); break;
case 'FAILED': case 'EXPIRED': markFailed(event.merchant_order_id); break;
case 'REVERSED': markRefunded(event.merchant_order_id); break;
}
res.status(200).json({received: true});
});
app.listen(3000);
Client Python minimal
PYTHONimport os
import hmac
import hashlib
import json
import time
import requests
class NekaPayClient:
def __init__(self, api_key, api_secret, base_url='https://nekapaie.com/api/v1'):
self.api_key = api_key
self.api_secret = api_secret
self.base_url = base_url
def _sign(self, body):
ts = str(int(time.time()))
sig = hmac.new(
self.api_secret.encode(),
(ts + body).encode(),
hashlib.sha256
).hexdigest()
return ts, sig
def request(self, method, path, body=None):
body_str = json.dumps(body, separators=(',', ':')) if body else ''
ts, sig = self._sign(body_str)
r = requests.request(method, self.base_url + path,
data=body_str,
headers={
'Content-Type': 'application/json',
'X-NekaPay-Key': self.api_key,
'X-NekaPay-Timestamp': ts,
'X-NekaPay-Signature': sig,
}
)
return r.status_code, r.json()
def cash_in(self, **payload): return self.request('POST', '/payments/cashin', payload)
def cash_out(self, **payload): return self.request('POST', '/payments/cashout', payload)
def get_status(self, tx_id): return self.request('GET', f'/payments/{tx_id}')
def get_balances(self): return self.request('GET', '/balances')
# Utilisation
client = NekaPayClient(
os.environ['NEKAPAY_API_KEY'],
os.environ['NEKAPAY_API_SECRET']
)
status, data = client.cash_in(
merchant_order_id='order_' + str(int(time.time())),
amount=50000,
currency='XOF',
customer_msisdn='+22376123456',
country_code='ML',
)
if status == 202:
print('Payment URL:', data['data']['payment_url'])
Webhook handler (Flask)
PYTHONfrom flask import Flask, request
import os, hmac, hashlib, time, json
app = Flask(__name__)
SECRET = os.environ['NEKAPAY_API_SECRET']
@app.route('/webhooks/nekapay', methods=['POST'])
def webhook():
ts = request.headers.get('X-NekaPay-Timestamp', '')
sig = request.headers.get('X-NekaPay-Signature', '')
body = request.get_data(as_text=True)
if not ts or abs(time.time() - int(ts)) > 300:
return 'replay', 401
expected = hmac.new(SECRET.encode(), (ts + body).encode(), hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, sig):
return 'signature', 401
event = json.loads(body)
if event['status'] == 'SUCCESS':
mark_paid(event['merchant_order_id'])
elif event['status'] in ('FAILED', 'EXPIRED'):
mark_failed(event['merchant_order_id'])
return {'received': True}, 200
Client Java (HttpClient)
JAVAimport java.net.URI;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.HexFormat;
public class NekaPayClient {
private final String apiKey, apiSecret, baseUrl;
private final HttpClient http = HttpClient.newHttpClient();
public NekaPayClient(String apiKey, String apiSecret, String baseUrl) {
this.apiKey = apiKey;
this.apiSecret = apiSecret;
this.baseUrl = baseUrl;
}
private String sign(String ts, String body) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(apiSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] hash = mac.doFinal((ts + body).getBytes(StandardCharsets.UTF_8));
return HexFormat.of().formatHex(hash);
}
public HttpResponse<String> request(String method, String path, String body) throws Exception {
String ts = String.valueOf(System.currentTimeMillis() / 1000);
String sig = sign(ts, body == null ? "" : body);
var builder = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + path))
.header("Content-Type", "application/json")
.header("X-NekaPay-Key", apiKey)
.header("X-NekaPay-Timestamp", ts)
.header("X-NekaPay-Signature", sig);
if (body != null) {
builder.method(method, HttpRequest.BodyPublishers.ofString(body));
} else {
builder.method(method, HttpRequest.BodyPublishers.noBody());
}
return http.send(builder.build(), HttpResponse.BodyHandlers.ofString());
}
public static void main(String[] args) throws Exception {
var client = new NekaPayClient(
System.getenv("NEKAPAY_API_KEY"),
System.getenv("NEKAPAY_API_SECRET"),
"https://nekapaie.com/api/v1"
);
String body = """
{"merchant_order_id":"order_001","amount":50000,
"currency":"XOF","customer_msisdn":"+22376123456","country_code":"ML"}""";
var response = client.request("POST", "/payments/cashin", body);
System.out.println("Status: " + response.statusCode());
System.out.println("Body: " + response.body());
}
}
Client Go minimal
GOpackage main
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
type NekaPay struct {
APIKey string
APISecret string
BaseURL string
}
func (c *NekaPay) sign(ts, body string) string {
h := hmac.New(sha256.New, []byte(c.APISecret))
h.Write([]byte(ts + body))
return hex.EncodeToString(h.Sum(nil))
}
func (c *NekaPay) Request(method, path string, payload interface{}) (int, map[string]interface{}, error) {
var bodyBytes []byte
if payload != nil {
bodyBytes, _ = json.Marshal(payload)
}
bodyStr := string(bodyBytes)
ts := strconv.FormatInt(time.Now().Unix(), 10)
sig := c.sign(ts, bodyStr)
req, _ := http.NewRequest(method, c.BaseURL+path, bytes.NewReader(bodyBytes))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-NekaPay-Key", c.APIKey)
req.Header.Set("X-NekaPay-Timestamp", ts)
req.Header.Set("X-NekaPay-Signature", sig)
resp, err := http.DefaultClient.Do(req)
if err != nil { return 0, nil, err }
defer resp.Body.Close()
raw, _ := io.ReadAll(resp.Body)
var data map[string]interface{}
json.Unmarshal(raw, &data)
return resp.StatusCode, data, nil
}
func main() {
client := &NekaPay{
APIKey: os.Getenv("NEKAPAY_API_KEY"),
APISecret: os.Getenv("NEKAPAY_API_SECRET"),
BaseURL: "https://nekapaie.com/api/v1",
}
status, data, err := client.Request("POST", "/payments/cashin", map[string]interface{}{
"merchant_order_id": "order_001",
"amount": 50000,
"currency": "XOF",
"customer_msisdn": "+22376123456",
"country_code": "ML",
})
if err != nil { panic(err) }
fmt.Printf("Status: %d\nResponse: %+v\n", status, data)
}
Bash + cURL (script complet)
BASH#!/usr/bin/env bash
set -euo pipefail
API_KEY="${NEKAPAY_API_KEY:?Missing NEKAPAY_API_KEY}"
API_SECRET="${NEKAPAY_API_SECRET:?Missing NEKAPAY_API_SECRET}"
BASE_URL="${NEKAPAY_BASE_URL:-https://nekapaie.com/api/v1}"
call_api() {
local method="$1" path="$2" body="${3:-}"
local ts sig
ts=$(date +%s)
sig=$(printf '%s%s' "$ts" "$body" | openssl dgst -sha256 -hmac "$API_SECRET" | cut -d' ' -f2)
curl -s -X "$method" "$BASE_URL$path" \
-H "Content-Type: application/json" \
-H "X-NekaPay-Key: $API_KEY" \
-H "X-NekaPay-Timestamp: $ts" \
-H "X-NekaPay-Signature: $sig" \
--data "$body"
}
# 1. Cash-In
BODY='{"merchant_order_id":"order_001","amount":50000,"currency":"XOF","customer_msisdn":"+22376123456","country_code":"ML"}'
TX=$(call_api POST "/payments/cashin" "$BODY" | jq -r '.data.transaction_id')
echo "Transaction créée : $TX"
# 2. Statut
sleep 2
call_api GET "/payments/$TX" | jq
# 3. Soldes
call_api GET "/balances" | jq
SDK officiels : les clients ci-dessus sont des exemples self-contained. Des SDK officiels packagés (Composer, npm, PyPI, Maven, Go modules) sont prévus dans la prochaine release. Pour l'instant, copiez ces snippets dans votre projet.