ZyndPay API Reference
L'API ZyndPay est une passerelle de paiement RESTful permettant d'accepter des paiements sur trois rails : USDT sur la blockchain TRON, carte bancaire (XOF) et mobile money (XOF). Des URL orientées ressources, des corps JSON et une enveloppe de réponse cohérente facilitent l'intégration dans tout environnement.
Tous les montants et frais sont évalués selon le rail de paiement et la configuration du compte marchand. Le dashboard, le checkout, la réponse API et les webhooks sont la source de vérité du frais effectif appliqué.
https://api.zyndpay.io/v1{
"success": true,
"data": { ... },
"meta": { "page": 1, "limit": 20, "total": 100 }
}Authentification
Incluez votre clé API dans l'en-tête X-Api-Key à chaque requête. Vous pouvez aussi la passer comme jeton Bearer dans l'en-tête Authorization.
curl https://api.zyndpay.io/v1/payments \
-H "X-Api-Key: zyp_live_sk_..."| Paramètre | Type | Requis | Description |
|---|---|---|---|
zyp_live_sk_* | string | requis | Clé secrète live — accès API complet. Ne jamais exposer côté client. |
zyp_live_pk_* | string | optionnel | Clé publiable live — accès lecture limité, sûre pour les navigateurs. |
zyp_test_sk_* | string | requis | Clé secrète sandbox — identique au live mais sans transactions réelles. |
zyp_test_pk_* | string | optionnel | Clé publiable sandbox. |
zyp_rk_* | string | optionnel | Clé restreinte — limitée à des opérations spécifiques. |
Démarrage Rapide
Acceptez votre premier paiement USDT en moins de 5 minutes. Sélectionnez votre langage ci-dessous — chaque exemple montre l'installation, la création d'un encaissement, la gestion du webhook et la vérification du statut.
// 1. Install
// npm install @zyndpay/sdk
// 2. Initialize
import { ZyndPay } from '@zyndpay/sdk';
const zyndpay = new ZyndPay({
apiKey: process.env.ZYNDPAY_API_KEY!,
webhookSecret: process.env.ZYNDPAY_WEBHOOK_SECRET,
});
// 3. Create a pay-in and get a payment URL
const payin = await zyndpay.payins.create({
amount: '100',
externalRef: 'order_' + Date.now(), // your order ID
successUrl: 'https://yoursite.com/success',
cancelUrl: 'https://yoursite.com/cancel',
});
console.log('Redirect customer to:', payin.paymentUrl);
// Or show payin.address if you want to display the TRON address directly
// 4. Handle the webhook (Express.js)
import express from 'express';
const app = express();
app.post('/webhooks/zyndpay', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['zyndpay-signature'] as string;
let event;
try {
event = zyndpay.webhooks.verify(req.body, signature);
} catch {
return res.status(400).send('Invalid signature');
}
switch (event.event) {
case 'payin.confirmed':
// Payment received — fulfill the order
console.log('Paid:', event.data.externalRef, event.data.amount, 'USDT');
break;
case 'payin.expired':
console.log('Expired:', event.data.externalRef);
break;
case 'payin.underpaid':
console.log('Underpaid:', event.data.externalRef,
'got', event.data.amount, 'expected', event.data.amountRequested);
break;
}
res.json({ received: true });
});
// 5. Poll / check status programmatically
const tx = await zyndpay.payins.get(payin.transactionId);
console.log('Status:', tx.status); // "CONFIRMED"Mise en Production
Avant d'accepter de vrais paiements, parcourez cette liste. La plupart des éléments prennent moins de 5 minutes chacun.
Créer un Encaissement
/v1/paymentsGénère une adresse de portefeuille TRON unique pour que votre client y envoie des USDT. L'adresse expire après <ic>expiresInSeconds</ic> secondes (par défaut 30 minutes).
| Paramètre | Type | Requis | Description |
|---|---|---|---|
amount | string | requis | Montant en USDT sous forme de chaîne décimale (ex. « 100.00 »). Minimum : « 1.00 ». |
externalRef | string | optionnel | Votre ID de commande ou référence (optionnel). Doit être unique par marchand si fourni. |
expiresInSeconds | number | optionnel | Secondes avant l'expiration de l'adresse. Minimum : 900. Défaut : 1800 (30 minutes). |
successUrl | string | optionnel | URL de redirection du client après un paiement réussi. |
cancelUrl | string | optionnel | URL de redirection du client si le paiement expire ou est annulé. |
metadata | object | optionnel | Paires clé-valeur arbitraires stockées avec le paiement et incluses dans les webhooks. |
paymentMethod | string | optionnel | Rail de paiement. "USDT_TRC20" (défaut), "CARD" ou "MOBILE_MONEY". |
customerName | string | optionnel | Nom complet du client. Obligatoire pour les paiements par carte. |
customerEmail | string | optionnel | Adresse e-mail du client. Obligatoire pour les paiements par carte. |
curl -X POST https://api.zyndpay.io/v1/payments \
-H "X-Api-Key: zyp_live_sk_..." \
-H "Content-Type: application/json" \
-d '{
"amount": "100.00",
"externalRef": "order_123",
"expiresInSeconds": 3600,
"successUrl": "https://yoursite.com/payment/success",
"cancelUrl": "https://yoursite.com/payment/cancel"
}'{
"success": true,
"data": {
"transactionId": "pay_abc123...",
"address": "TRXabc123def456ghi789jkl",
"paymentUrl": "https://checkout.zyndpay.io/pay_abc123...",
"qrCodeUrl": "data:image/png;base64,iVBOR...",
"amount": "100.00",
"amountExpected": "101.00",
"networkFee": "1",
"currency": "USDT_TRC20",
"chain": "TRON",
"status": "AWAITING_PAYMENT",
"expiresAt": "2026-03-06T12:00:00.000Z"
}
}Mobile Money
/v1/paymentsCollectez des paiements mobile money via un checkout ZyndPay hébergé — votre client saisit son OTP (Orange Money) ou approuve la notification push (Moov, MTN, Wave) sur une page aux couleurs de ZyndPay. Disponible en XOF uniquement.
| Paramètre | Type | Requis | Description |
|---|---|---|---|
paymentMethod | string | requis | Doit valoir "MOBILE_MONEY" pour un paiement mobile money. |
amount | string | requis | Montant en XOF sous forme de chaîne (ex. « 1000 »). Le XOF n'a pas de décimales — passez une chaîne entière. Minimum : « 500 ». |
customerPhone | string | requis | Numéro de téléphone du client au format E.164 (ex. « +22670123456 »). Requis pour MOBILE_MONEY. |
operatorCode | string | optionnel | Surcharge l'opérateur mobile money détecté depuis le préfixe téléphonique. Valeurs autorisées : ORANGE_BF, MOOV_BF. Ignoré pour les rails autres que MOBILE_MONEY. |
externalRef | string | optionnel | Votre ID de commande ou référence (optionnel). Doit être unique par marchand si fourni. |
metadata | object | optionnel | Paires clé-valeur arbitraires stockées avec le paiement et incluses dans les webhooks. |
L'opérateur est résolu côté serveur à partir du préfixe téléphonique. Vous pouvez le surcharger avec <ic>operatorCode</ic> quand le client en choisit un explicitement (recommandé). Codes supportés : ORANGE_BF, MOOV_BF.
curl -X POST https://api.zyndpay.io/v1/payments \
-H "X-Api-Key: zyp_live_sk_..." \
-H "Content-Type: application/json" \
-d '{
"amount": "1000",
"paymentMethod": "MOBILE_MONEY",
"customerPhone": "+22670123456",
"customerName": "Aïcha Traoré",
"operatorCode": "ORANGE_BF",
"externalRef": "order_123"
}'La réponse inclut <ic>hostedPaymentUrl</ic> — redirigez votre client vers cette URL pour finaliser le paiement. ZyndPay gère le formulaire OTP, l'instruction opérateur, le polling de statut et la page de résultat. Le statut commence à AWAITING_PAYMENT et passe à CONFIRMED (ou FAILED) via webhook.
Si vous préférez piloter le flux OTP vous-même, la réponse inclut également <ic>nextStep</ic> (<ic>"otp"</ic> ou <ic>"approval"</ic>), <ic>operatorCode</ic> et <ic>instruction</ic>. Affichez votre propre formulaire, appelez Submit OTP pour les opérateurs en mode OTP, et pollez Get Payin. Les nouvelles intégrations devraient privilégier le flux hébergé ci-dessus.
Le champ <ic>nextStep</ic> de la réponse indique l'action suivante : <ic>"otp"</ic> signifie que votre UI doit demander le code que le client vient de recevoir par SMS, puis appeler Submit OTP. <ic>"approval"</ic> signifie que l'opérateur a poussé une demande de confirmation vers le téléphone du client — affichez la chaîne <ic>instruction</ic> pour indiquer au client quoi faire (ex. « Composez *555*6# »), puis pollez Get Payin jusqu'à ce que le statut quitte AWAITING_PAYMENT.
{
"success": true,
"data": {
"transactionId": "pay_momo_abc123",
"paymentMethod": "MOBILE_MONEY",
"hostedPaymentUrl": "https://checkout.zyndpay.io/m/pay_momo_abc123",
"nextStep": "otp",
"operatorCode": "ORANGE_BF",
"instruction": {
"fr": "Vous allez recevoir un SMS avec votre code de validation. Saisissez-le ci-dessous.",
"en": "You will receive an SMS with your validation code. Enter it below."
},
"amount": "1000",
"currency": "XOF",
"status": "AWAITING_PAYMENT",
"expiresAt": "2026-03-06T12:05:00.000Z"
}
}{
"success": true,
"data": {
"transactionId": "pay_momo_xyz456",
"paymentMethod": "MOBILE_MONEY",
"hostedPaymentUrl": "https://checkout.zyndpay.io/m/pay_momo_xyz456",
"nextStep": "approval",
"operatorCode": "MOOV_BF",
"instruction": {
"fr": "Composez *555*6# sur votre téléphone pour valider le paiement.",
"en": "Dial *555*6# on your phone to approve the payment."
},
"amount": "1000",
"currency": "XOF",
"status": "AWAITING_PAYMENT",
"expiresAt": "2026-03-06T12:03:00.000Z"
}
}Valider l'OTP
/v1/payments/:id/submit-otp/v1/payins/:id/submit-otpSoumet l'OTP que votre client a reçu par SMS sur un rail MoMo en mode OTP (Orange BF). Retourne immédiatement avec le statut AWAITING_PAYMENT ; le CONFIRMED final arrive via votre webhook une fois la transaction finalisée. <ic>POST /v1/payments/:id/submit-otp</ic> et <ic>POST /v1/payins/:id/submit-otp</ic> routent vers le même handler — la forme <ic>/payments</ic> est cohérente avec le reste du cycle de vie payin et préférée pour les nouvelles intégrations ; <ic>/payins</ic> est conservé pour les appelants existants.
| Paramètre | Type | Requis | Description |
|---|---|---|---|
otp | string | requis | Le code à 4-8 chiffres que le client a reçu par SMS. Applicable uniquement aux opérateurs en mode OTP. |
curl -X POST https://api.zyndpay.io/v1/payins/pay_momo_abc123/submit-otp \
-H "X-Api-Key: zyp_live_sk_..." \
-H "Content-Type: application/json" \
-d '{
"otp": "123456"
}'{
"success": true,
"data": {
"transactionId": "pay_momo_abc123",
"status": "AWAITING_PAYMENT"
}
}Récupérer un Encaissement
/v1/payments/:id{
"success": true,
"data": {
"id": "f4b2cb0f-08ce-4408-88d4-0a678ca0aae2",
"address": "TRXabc123def456ghi789jkl",
"amount": "100",
"amountReceived": "100.00",
"zyndpayFee": "1.00",
"status": "CONFIRMED",
"txHash": "abc123def456...",
"externalRef": "order_123",
"currency": "USDT_TRC20",
"chain": "TRON",
"paymentUrl": "https://checkout.zyndpay.io/f4b2cb0f...",
"isSandbox": false,
"createdAt": "2026-03-06T11:00:00.000Z",
"updatedAt": "2026-03-06T11:01:02.000Z"
}
}AWAITING_PAYMENTStatut initial — adresse de dépôt active, en attente de fondsPENDINGAdresse générée, aucun fonds reçu pour l'instantCONFIRMINGFonds reçus, en attente de 5 confirmationsCONFIRMED5+ confirmations — solde créditéUNDERPAIDMontant reçu inférieur à l'attenduOVERPAIDMontant reçu supérieur à l'attenduEXPIREDAdresse expirée sans paiementFAILEDPaiement échoué — aucun fonds reçuPaiement par Carte
Acceptez des paiements par carte en XOF en redirigeant votre client vers une page de paiement hébergée. Le client finalise le paiement dans son navigateur ; ZyndPay envoie un webhook payin.confirmed une fois le débit carte confirmé.
/v1/payments| Paramètre | Type | Requis | Description |
|---|---|---|---|
amount | string | requis | Montant en XOF sous forme de chaîne décimale (ex. : "5000"). Le XOF n'a pas de décimales — passez une chaîne entière. Minimum : « 100 ». La devise de règlement est fixée à XOF — ne passez pas de champ `currency` ; l'API rejette les propriétés inconnues. |
paymentMethod | string | requis | Rail de paiement. "USDT_TRC20" (défaut), "CARD" ou "MOBILE_MONEY". |
customerName | string | requis | Nom complet du client. Obligatoire pour les paiements par carte. |
customerEmail | string | requis | Adresse e-mail du client. Obligatoire pour les paiements par carte. |
curl -X POST https://api.zyndpay.io/v1/payments \
-H "X-Api-Key: zyp_live_sk_..." \
-H "Content-Type: application/json" \
-d '{
"amount": "5000",
"paymentMethod": "CARD",
"customerName": "Kofi Mensah",
"customerEmail": "[email protected]",
"externalRef": "order_abc123"
}'const payin = await zyndpay.payins.create({
amount: '5000',
paymentMethod: 'CARD',
customerName: 'Kofi Mensah',
customerEmail: '[email protected]',
externalRef: 'order_abc123',
});
// Redirect customer to complete payment
window.location.href = payin.hostedPaymentUrl;Récupérer un Encaissement
/v1/payments/:idcurl https://api.zyndpay.io/v1/payments/TXN_ID \
-H "X-Api-Key: YOUR_API_KEY"Lister les Encaissements
/v1/payments| Paramètre | Type | Requis | Description |
|---|---|---|---|
page | number | optionnel | Numéro de page. Défaut : 1. |
limit | number | optionnel | Résultats par page. Défaut : 20. Max : 100. |
status | string | optionnel | Filtrer par statut : <ic>AWAITING_PAYMENT</ic> (état par défaut pour un payin MoMo ou CARD fraîchement créé — le plus fréquent à l'ouverture de la liste), <ic>PENDING</ic>, <ic>CONFIRMING</ic>, <ic>CONFIRMED</ic>, <ic>EXPIRED</ic>, <ic>UNDERPAID</ic>, <ic>OVERPAID</ic>, ou <ic>FAILED</ic>. |
currency | string | optionnel | Filtrer par devise (ex. USDT_TRC20). |
curl https://api.zyndpay.io/v1/payments \
-H "X-Api-Key: YOUR_API_KEY"payins = client.payins.list(limit=20, status="CONFIRMED")
for p in payins["items"]:
print(p["transactionId"], p["status"]){
"success": true,
"data": {
"items": [ ... ],
"meta": {
"page": 1,
"limit": 20,
"total": 134,
"totalPages": 7,
"hasNext": true,
"hasPrev": false
}
}
}Tarifs
Les frais dépendent du rail et du compte. La documentation publique explique où les frais apparaissent ; le dashboard authentifié, le checkout, la réponse API et l’accord marchand affichent le taux effectif actuel.
- USDT (TRC20) : taux applicable actuel affiché dans votre compte et les réponses API.
- Mobile Money (XOF) : taux applicable actuel selon opérateur, pays, coûts fournisseur et conditions du compte.
- Carte (XOF — Visa / Mastercard) : taux applicable actuel selon rail carte, coûts fournisseur et conditions du compte.
Les frais sont libellés dans la devise de transaction pertinente. La valeur exacte `zyndpayFee` appliquée est retournée par l’API et les webhooks lorsque applicable.
Créer un Encaissement — SDK
import zyndpay
client = zyndpay.ZyndPay(api_key="YOUR_API_KEY")
payin = client.payins.create(
amount="25.00",
external_ref="order_123",
sandbox=True, # remove in production
)
print(payin["transactionId"])
print(payin["paymentUrl"])<?php
require 'vendor/autoload.php';
use ZyndPay\ZyndPay;
$client = new ZyndPay('YOUR_API_KEY');
$payin = $client->payins->create([
'amount' => '25.00',
'externalRef' => 'order_123',
], sandbox: true); // remove sandbox in production
echo $payin['transactionId'];
echo $payin['paymentUrl'];Créer un Lien
/v1/paylinksCrée un lien de paiement partageable avec un ou plusieurs produits. Partagez le <ic>paymentUrl</ic> retourné avec vos clients. Supporte les types à prix fixe, variable et facturation récurrente.
| Paramètre | Type | Requis | Description |
|---|---|---|---|
name | string | optionnel | Nom interne du lien affiché dans le tableau de bord marchand. Optionnel, jamais visible par le client. |
type | string | requis | Type de lien : FIXED (défaut), VARIABLE ou RECURRING. |
products | array | requis | Tableau de produits. Au moins un produit est requis. |
products[].name | string | requis | Nom du produit affiché au client. |
products[].price | string | requis | Prix en USDT sous forme de chaîne décimale. |
products[].productType | string | requis | PHYSICAL (défaut) ou DIGITAL. |
products[].digitalUrl | string | optionnel | URL externe pour la livraison du produit numérique (Google Drive, Mega, etc.). |
collectEmail | string | optionnel | Collecte d'email client : HIDDEN (défaut), OPTIONAL ou REQUIRED. |
collectName | string | optionnel | Collecte du nom client : HIDDEN, OPTIONAL ou REQUIRED. |
brandColor | string | optionnel | Couleur hexadécimale pour la page de checkout personnalisée (ex. #635BFF). |
successUrl | string | optionnel | URL de redirection du client après un paiement réussi. |
recurringInterval | string | optionnel | Intervalle de facturation pour les liens récurrents : WEEKLY, MONTHLY ou YEARLY. |
curl -X POST https://api.zyndpay.io/v1/paylinks \
-H "X-Api-Key: zyp_live_sk_..." \
-H "Content-Type: application/json" \
-d '{
"type": "FIXED",
"products": [
{
"name": "Premium Course",
"price": "49.99",
"productType": "DIGITAL",
"digitalUrl": "https://drive.google.com/file/d/abc123"
}
],
"collectEmail": "REQUIRED",
"brandColor": "#635BFF"
}'{
"success": true,
"data": {
"id": "pl_cma1xyz8f0001yx5k",
"slug": "aB3dE6fG7hI9",
"name": "Summer collection 2026",
"type": "FIXED",
"status": "ACTIVE",
"currency": "USDT_TRC20",
"paymentUrl": "https://dashboard.zyndpay.io/pay/link/aB3dE6fG7hI9",
"products": [
{
"id": "prod_abc123",
"name": "Premium Course",
"description": "Lifetime access. PDF + video bundle.",
"price": "49.990000000000000000",
"productType": "DIGITAL",
"digitalUrl": "https://drive.google.com/file/d/abc123",
"imageKey": null,
"imageUrl": null,
"stockEnabled": false,
"stockQty": 0,
"lowStockThreshold": 0,
"sortOrder": 0
}
],
"promoCodes": [],
"useCount": 0,
"maxUses": null,
"ordersCount": 0,
"expiresAt": null,
"autoDisableOnDepletion": false,
"collectEmail": "REQUIRED",
"collectName": "OPTIONAL",
"collectPhone": "OPTIONAL",
"collectAddress": "HIDDEN",
"recurringInterval": null,
"recurringIntervalCount": null,
"brandColor": null,
"logoUrl": null,
"coverImageKey": null,
"coverImageUrl": null,
"postPaymentAction": null,
"cancelUrl": null,
"createdAt": "2026-03-08T10:00:00.000Z",
"updatedAt": "2026-03-08T10:00:00.000Z"
}
}Récupérer un Lien
/v1/paylinks/:id{
"success": true,
"data": {
"id": "pl_cma1xyz8f0001yx5k",
"slug": "aB3dE6fG7hI9",
"name": "Summer collection 2026",
"type": "FIXED",
"status": "ACTIVE",
"currency": "USDT_TRC20",
"paymentUrl": "https://dashboard.zyndpay.io/pay/link/aB3dE6fG7hI9",
"products": [
{
"id": "prod_abc123",
"name": "Premium Course",
"description": "Lifetime access. PDF + video bundle.",
"price": "49.990000000000000000",
"productType": "DIGITAL",
"digitalUrl": "https://drive.google.com/file/d/abc123",
"imageKey": null,
"imageUrl": null,
"stockEnabled": false,
"stockQty": 0,
"lowStockThreshold": 0,
"sortOrder": 0
}
],
"promoCodes": [],
"useCount": 0,
"maxUses": null,
"ordersCount": 0,
"expiresAt": null,
"autoDisableOnDepletion": false,
"collectEmail": "REQUIRED",
"collectName": "OPTIONAL",
"collectPhone": "OPTIONAL",
"collectAddress": "HIDDEN",
"recurringInterval": null,
"recurringIntervalCount": null,
"brandColor": null,
"logoUrl": null,
"coverImageKey": null,
"coverImageUrl": null,
"postPaymentAction": null,
"cancelUrl": null,
"createdAt": "2026-03-08T10:00:00.000Z",
"updatedAt": "2026-03-08T10:00:00.000Z"
}
}Lister les Liens
/v1/paylinks| Paramètre | Type | Requis | Description |
|---|---|---|---|
page | number | optionnel | Numéro de page. Défaut : 1. |
limit | number | optionnel | Résultats par page. Défaut : 20. Max : 100. |
{
"success": true,
"data": {
"items": [ ... ],
"meta": {
"page": 1,
"limit": 20,
"total": 134,
"totalPages": 7,
"hasNext": true,
"hasPrev": false
}
}
}Paiement
/v1/pay/link/:slug/checkoutEndpoint public (pas d'auth requise). Crée une commande et un encaissement à partir d'un lien de paiement. Le client reçoit une adresse TRON pour envoyer les USDT.
| Paramètre | Type | Requis | Description |
|---|---|---|---|
items | array | requis | Tableau d'IDs de produits et quantités à acheter. |
customerEmail | string | optionnel | Adresse email du client (requise pour RECURRING et produits numériques). |
customerName | string | optionnel | Nom du client (optionnel). |
curl -X POST https://api.zyndpay.io/v1/pay/link/aB3dE6fG7hI9/checkout \
-H "Content-Type: application/json" \
-d '{
"items": [
{ "productId": "prod_abc123", "quantity": 1 }
],
"customerEmail": "[email protected]",
"customerName": "John Doe"
}'{
"success": true,
"data": {
"orderId": "ord_xyz789",
"transactionId": "cma2abc9g0002yz6l0def",
"address": "TRXabc123def456ghi789jkl",
"amount": "49.99",
"expiresAt": "2026-03-08T11:00:00.000Z"
}
}La devise et les moyens de paiement sont indépendants
La devise d'affichage d'un paylink (USDT ou XOF) est un choix purement visuel. Le checkout client convertit le prix vers le rail choisi par le client au taux du jour. Vous recevez les fonds dans la devise native du rail — USDT pour USDT_TRC20, XOF pour CARD et MOBILE_MONEY — sans aucun risque de change pour ZyndPay.
Créer un Lien — SDK
paylink = client.paylinks.create(
products=[{"name": "Premium Plan", "price": "49.99", "productType": "DIGITAL"}],
type="FIXED",
collect_email="REQUIRED",
sandbox=True, # remove in production
)
print(paylink["id"])
print(paylink["slug"])$paylink = $client->paylinks->create([
'products' => [[
'name' => 'Premium Plan',
'price' => '49.99',
'productType' => 'DIGITAL',
]],
'type' => 'FIXED',
'collectEmail' => 'REQUIRED',
], sandbox: true); // remove in production
echo $paylink['id'];
echo $paylink['slug'];Simuler un Devis de Lien
/v1/paylinks/simulatecurl -X POST "https://api.zyndpay.io/v1/paylinks/simulate?sandbox=true" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"amount": "25.00", "methods": ["USDT_TRC20"], "currency": "USDT_TRC20", "feeBearer": "CLIENT"}'Statistiques du Lien
/v1/paylinks/:id/statscurl https://api.zyndpay.io/v1/paylinks/PAYLINK_ID/stats \
-H "X-Api-Key: YOUR_API_KEY"Commandes du Lien
Récupérez toutes les commandes passées via un lien de paiement. Chaque commande retrace le client, le montant payé, la méthode de paiement et le statut.
/v1/paylinks/:id/orderscurl https://api.zyndpay.io/v1/paylinks/PAYLINK_ID/orders \
-H "X-Api-Key: YOUR_API_KEY"Codes promo
Attachez des codes de réduction à un paylink que les clients peuvent utiliser au paiement. Chaque code a une remise (pourcentage ou montant fixe), une limite d'utilisation optionnelle, et une date d'expiration optionnelle. Les codes sont liés à un seul paylink — la même chaîne sur un autre paylink est un code différent.
/v1/paylinks/:id/promo-codes| Paramètre | Type | Requis | Description |
|---|---|---|---|
code | string | requis | Le code visible par le client, normalisé en majuscules par l'API (ex. `"tabaski"` → `"TABASKI"`). |
discountType | string | requis | `PERCENT` ou `FIXED`. `PERCENT` applique `discountValue`% sur le sous-total ; `FIXED` soustrait un montant fixe dans la devise du paylink. |
discountValue | string | requis | Pour `PERCENT`, le pourcentage en chaîne décimale (ex. `"15"` pour 15 %). Pour `FIXED`, le montant dans la devise du paylink. |
maxUses | integer | optionnel | Nombre total maximal de redemptions pour tous les clients. Omettez pour un nombre illimité. |
expiresAt | string | optionnel | Horodatage ISO 8601 après lequel le code n'est plus utilisable (ex. `"2026-12-31T23:59:59Z"`). Omettez pour aucune limite de temps. |
curl -X POST https://api.zyndpay.io/v1/paylinks/PAYLINK_ID/promo-codes \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"code": "TABASKI",
"discountType": "PERCENT",
"discountValue": "15",
"maxUses": 100,
"expiresAt": "2026-07-07T23:59:59.000Z"
}'/v1/paylinks/:id/promo-codes/v1/paylinks/:id/promo-codes/:codeId/v1/paylinks/:id/promo-codes/:codeIdValider un code promo
Endpoint public que votre interface panier appelle quand un client saisit un code. Renvoie l'aperçu de la remise si le code est utilisable, ou l'un des quatre codes d'erreur (`PROMO_CODE_INVALID`, `PROMO_CODE_INACTIVE`, `PROMO_CODE_EXPIRED`, `PROMO_CODE_USAGE_LIMIT_REACHED`) afin que vous puissiez afficher un message adapté plutôt qu'une erreur générique.
/v1/pay/link/:slug/apply-promo# Public endpoint — call from the cart UI on promo-input blur.
curl -X POST https://api.zyndpay.io/v1/pay/link/PAYLINK_SLUG/apply-promo \
-H "Content-Type: application/json" \
-d '{"code": "TABASKI"}'{
"success": false,
"error": {
"code": "PROMO_CODE_EXPIRED",
"message": "This promo code has expired."
},
"statusCode": 400
}Codes d'erreur de redemption
Effectuez un switch sur `error.code` dans votre interface pour afficher le bon message pour chaque mode d'échec :
| error.code | When | Requis | Description |
|---|---|---|---|
PROMO_CODE_INVALID | lookup | optionnel | Le code n'existe pas sur ce paylink. |
PROMO_CODE_INACTIVE | state | optionnel | Le marchand a désactivé le code via `PATCH .../promo-codes/:codeId` avec `isActive: false`. |
PROMO_CODE_EXPIRED | time | optionnel | Au-delà de `expiresAt`. Rafraîchissez le panier — `expiresAt` est inclus dans la réponse de la liste. |
PROMO_CODE_USAGE_LIMIT_REACHED | count | optionnel | `useCount >= maxUses`. Le code a été utilisé par suffisamment de clients. |
Modèles de Liens
Les modèles vous permettent de sauvegarder une configuration de lien de paiement et de la réutiliser pour créer plusieurs liens rapidement. Les modèles stockent les produits, les paramètres et la marque — mais pas l'historique des commandes.
/v1/paylinks/templates/v1/paylinks/templates/v1/paylinks/templates/:idcurl -X POST https://api.zyndpay.io/v1/paylinks/templates \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "My Template",
"config": {
"type": "FIXED",
"products": [{"name": "Product", "price": "10.00", "productType": "DIGITAL"}],
"collectEmail": "HIDDEN"
}
}'Images de Couverture et Produits
Téléchargez une image de couverture pour la page de paiement, et des images par produit. Les téléchargements utilisent multipart/form-data.
/v1/paylinks/:id/cover-image/v1/paylinks/:id/cover-image/v1/paylinks/:id/products/:productId/image/v1/paylinks/:id/products/import-csvCréer un Paiement Sortant
/v1/payoutLes virements sortants permettent d’envoyer des soldes supportés depuis ZyndPay vers une destination éligible — fournisseurs, remboursements ou commissions. Le frais applicable est affiché ou retourné avant exécution.
| Paramètre | Type | Requis | Description |
|---|---|---|---|
amount | string | requis | Montant à envoyer (par exemple « 50.00 »). Le frais applicable est calculé selon les conditions du compte et retourné avant exécution. |
destinationAddress | string | requis | Adresse de portefeuille TRON (TRC20) du destinataire. Doit correspondre au format T suivi de 33 caractères base58. |
currency | string | optionnel | Devise à envoyer. Défaut : USDT_TRC20. |
chain | string | optionnel | Réseau blockchain. Défaut : TRON. |
externalRef | string | optionnel | Votre ID de référence interne pour ce paiement (ex. numéro de facture fournisseur). |
metadata | object | optionnel | Paires clé-valeur arbitraires stockées avec le paiement. |
curl -X POST https://api.zyndpay.io/v1/payout \
-H "X-Api-Key: zyp_live_sk_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
-d '{
"amount": "50.00",
"destinationAddress": "TXYZabc123def456ghi789jkl012mno345",
"externalRef": "payout_vendor_456"
}'{
"success": true,
"data": {
"transactionId": "cma2abc9g0002yz6l0def5678",
"status": "PROCESSING",
"processingFee": "1.50",
"requiresManualApproval": false,
"currentPayinFee": "1%",
"currentTier": "flat"
}
}Estimer un Virement Sortant
Prévisualisez les frais de traitement et le coût total avant de créer un virement sortant. Renvoie fee, totalDebited, availableBalance et sufficient.
/v1/payout/estimate| Paramètre | Type | Requis | Description |
|---|---|---|---|
amount | string | requis | Montant en USDT à estimer. |
currency | string | optionnel | Devise (défaut : USDT_TRC20). |
chain | string | optionnel | Blockchain (défaut : TRON). |
destinationAddress | string | requis | Adresse TRON de destination. Requis — l'estimation valide l'adresse et exécute un pré-contrôle AML. |
curl -X POST https://api.zyndpay.io/v1/payout/estimate \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"amount": "100.00", "destinationAddress": "TRON_ADDRESS"}'const estimate = await zyndpay.payouts.estimate({
amount: '100.00',
currency: 'USDT_TRC20',
chain: 'TRON',
destinationAddress: 'TXyz1234567890abcdef1234567890abcde',
});
console.log(`Fee: ${estimate.fee} USDT`);
console.log(`Total deducted: ${estimate.totalDebited} USDT`);
console.log(`Sufficient balance: ${estimate.sufficient}`);estimate = client.payouts.estimate(
amount="100.00",
destination_address="TRON_ADDRESS",
)
print(f"Fee: {estimate['fee']} USDT")
print(f"Total debited: {estimate['totalDebited']} USDT")$estimate = $client->payouts->estimate([
'amount' => '100.00',
'destinationAddress' => 'TRON_ADDRESS',
]);
echo "Fee: " . $estimate['fee'] . " USDT\n";
echo "Total: " . $estimate['totalDebited'] . " USDT\n";Créer un Paiement Sortant — SDK
payout = client.payouts.create(
amount="100.00",
destination_address="TRON_ADDRESS",
idempotency_key="payout_vendor_apr_001",
)
print(payout["id"], payout["status"])$payout = $client->payouts->create([
'amount' => '100.00',
'destinationAddress' => 'TRON_ADDRESS',
], idempotencyKey: 'payout_vendor_apr_001');
echo $payout['id'];Récupérer un Paiement Sortant
/v1/payout/:id{
"success": true,
"data": {
"transactionId": "cma2abc9g0002yz6l0def5678",
"status": "PROCESSING",
"processingFee": "1.50",
"requiresManualApproval": false,
"currentPayinFee": "1%",
"currentTier": "flat"
}
}PENDINGEn attente d'approbation manuelle (montant > 50 K $)PROCESSINGApprouvé, préparation de la diffusionBROADCASTSoumis au réseau TRONCONFIRMEDConfirmé on-chainFAILEDDiffusion échouée — solde rembourséCANCELLEDAnnulé avant diffusionLister les Paiements Sortants
/v1/payout{
"success": true,
"data": {
"items": [ ... ],
"meta": {
"page": 1,
"limit": 20,
"total": 42,
"totalPages": 3,
"hasNext": true,
"hasPrev": false
}
}
}Demander un Retrait
/v1/withdrawalsLes retraits permettent aux marchands de déplacer leurs soldes réglés hors de ZyndPay vers une destination enregistrée lorsque supporté. Le frais applicable est calculé selon le compte et affiché avant exécution.
| Paramètre | Type | Requis | Description |
|---|---|---|---|
amount | string | requis | Montant à retirer (par exemple « 500.00 »). Le frais applicable est calculé selon les conditions du compte et affiché avant exécution. |
whitelistAddressId | string | optionnel | ID de l'adresse de retrait enregistrée à utiliser. Utilise votre adresse principale si omis. |
curl -X POST https://api.zyndpay.io/v1/withdrawals \
-H "X-Api-Key: zyp_live_sk_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
-d '{
"amount": "500.00",
"whitelistAddressId": "d2f64e1f-3c3f-4c8b-bc21-1037682d691c"
}'{
"success": true,
"data": {
"id": "wdr_cma1xyz8f0001yx5k",
"amount": "500.00",
"fee": "5.00",
"netAmount": "495.00",
"destinationAddress": "TRXabc123def456ghi789jkl",
"status": "PENDING_REVIEW",
"createdAt": "2026-03-06T11:00:00.000Z"
}
}Récupérer un Retrait — List
/v1/withdrawalscurl https://api.zyndpay.io/v1/withdrawals \
-H "X-Api-Key: YOUR_API_KEY"withdrawal = client.withdrawals.create(
amount="50.00",
idempotency_key="withdrawal_001",
)
print(withdrawal["id"], withdrawal["status"])Récupérer un Retrait
/v1/withdrawals/:idcurl https://api.zyndpay.io/v1/withdrawals/WD_ID \
-H "X-Api-Key: YOUR_API_KEY"{
"success": true,
"data": {
"id": "wdr_cma1xyz8f0001yx5k",
"amount": "500.00",
"fee": "5.00",
"netAmount": "495.00",
"destinationAddress": "TRXabc123def456ghi789jkl",
"status": "PENDING_REVIEW",
"createdAt": "2026-03-06T11:00:00.000Z"
}
}PENDING_REVIEWEn attente de vérification de conformité (PENDING_REVIEW)APPROVEDApprouvé, en file d'attente pour traitementBROADCASTSoumis au réseau TRONCONFIRMEDConfirmé on-chainREJECTEDRejeté par la validation adminFAILEDDiffusion échouée — solde rembourséCANCELLEDAnnulé avant diffusionAnnuler un Retrait
/v1/withdrawals/:idAnnule une demande de retrait encore au statut <ic>PENDING_REVIEW</ic> (avant son approbation et sa diffusion sur le réseau TRON). Le montant est immédiatement retourné à votre solde.
curl -X DELETE https://api.zyndpay.io/v1/withdrawals/WD_ID \
-H "X-Api-Key: YOUR_API_KEY"Paiements en Masse
Les paiements en masse vous permettent d'envoyer des fonds à des centaines de destinataires en un seul lot. Créez un lot en DRAFT, validez-le (aperçu des frais + vérification du solde), puis exécutez — chaque élément est traité indépendamment afin qu'un échec isolé ne bloque pas les autres.
Créer un Lot
Créez un lot vide. Vous pouvez lui attribuer un libellé pour référence interne.
/v1/bulk-payments| Paramètre | Type | Requis | Description |
|---|---|---|---|
label | string | optionnel | Libellé interne pour ce lot (optionnel). |
curl -X POST https://api.zyndpay.io/v1/bulk-payments \
-H "X-Api-Key: zyp_live_sk_..." \
-H "Content-Type: application/json" \
-d '{ "label": "Vendor payouts April 2026" }'Ajouter des Éléments
Ajoutez des destinataires à un lot en statut DRAFT. Chaque élément nécessite une adresse de portefeuille, un montant et des métadonnées optionnelles. Vous pouvez également importer un fichier CSV ou XLSX.
/v1/bulk-payments/:id/items| Paramètre | Type | Requis | Description |
|---|---|---|---|
items | array | requis | Tableau d'éléments de paiement. Chaque élément : walletAddress, amount, recipientName (optionnel), reference (optionnel). |
curl -X POST https://api.zyndpay.io/v1/bulk-payments/batch_abc/items \
-H "X-Api-Key: zyp_live_sk_..." \
-H "Content-Type: application/json" \
-d '{
"items": [
{ "walletAddress": "TXyz1...", "amount": "100.00", "recipientName": "Vendor A", "reference": "inv_001" },
{ "walletAddress": "TXyz2...", "amount": "250.00", "recipientName": "Vendor B", "reference": "inv_002" }
]
}'Valider et Exécuter
La validation vérifie la disponibilité du solde et prévisualise les frais totaux sans exécuter. La réponse inclut <ic>balanceCheck</ic> : sur les lots live, elle renvoie <ic>{ sufficient, required, available }</ic> (et 400 INSUFFICIENT_BALANCE si le grand livre est insuffisant) ; sur les lots sandbox, elle renvoie <ic>{ sandbox: true, skipped: true, required }</ic> — les lots sandbox ne touchent pas le grand livre, donc la vérification du solde est intentionnellement ignorée. Execute relance la vérification du solde live dans une transaction sérialisable (au cas où le solde a changé entre validate et execute) avant de débiter et de mettre en file.
curl -X POST https://api.zyndpay.io/v1/bulk-payments/batch_abc/validate \
-H "X-Api-Key: zyp_live_sk_..."curl -X POST https://api.zyndpay.io/v1/bulk-payments/batch_abc/execute \
-H "X-Api-Key: zyp_live_sk_..."// Full bulk payment workflow
const batch = await zyndpay.bulkPayments.create({ label: 'Vendor payouts April 2026' });
await zyndpay.bulkPayments.addItems(batch.id, {
items: [
{ walletAddress: 'TXyz1...', amount: '100.00', recipientName: 'Vendor A', reference: 'inv_001' },
{ walletAddress: 'TXyz2...', amount: '250.00', recipientName: 'Vendor B', reference: 'inv_002' },
],
});
const validation = await zyndpay.bulkPayments.validate(batch.id);
console.log(`Total fee: ${validation.totalFee} USDT`);
await zyndpay.bulkPayments.execute(batch.id);
// Poll for completion
const status = await zyndpay.bulkPayments.get(batch.id);
console.log(status.status); // PROCESSING | COMPLETED | PARTIALLY_COMPLETED# Create batch
batch = client.bulk_payments.create(label="April payouts")
# Add recipients
client.bulk_payments.add_items(batch["id"], [
{"walletAddress": "TRON_ADDRESS_1", "amount": "50.00", "recipientName": "Vendor A"},
{"walletAddress": "TRON_ADDRESS_2", "amount": "75.00", "recipientName": "Vendor B"},
])
# Validate then execute
client.bulk_payments.validate(batch["id"])
client.bulk_payments.execute(batch["id"])// Create batch
$batch = $client->bulkPayments->create(['label' => 'April payouts']);
// Add recipients
$client->bulkPayments->addItems($batch['id'], [
['walletAddress' => 'TRON_ADDRESS_1', 'amount' => '50.00', 'recipientName' => 'Vendor A'],
['walletAddress' => 'TRON_ADDRESS_2', 'amount' => '75.00', 'recipientName' => 'Vendor B'],
]);
// Validate and execute
$client->bulkPayments->validate($batch['id']);
$client->bulkPayments->execute($batch['id']);Relancer les Éléments Échoués
Après qu'un lot atteint le statut PARTIALLY_COMPLETED, appelez retry pour remettre en file d'attente uniquement les éléments échoués. Les éléments déjà réussis ne sont pas retraités.
/v1/bulk-payments/:id/retryAnnuler un Lot
Annulez un lot encore en statut DRAFT ou VALIDATED. Les fonds ne sont pas réservés avant l'appel à execute(), l'annulation est donc immédiate sans impact sur le solde.
/v1/bulk-payments/:id/cancelSuivre
Interrogez le statut du lot et les résultats par élément. Exportez un rapport CSV à la fin.
/v1/bulk-payments/:id/v1/bulk-payments/:id/exportcurl https://api.zyndpay.io/v1/bulk-payments/batch_abc \
-H "X-Api-Key: zyp_live_sk_..."Portefeuilles & Conversions
ZyndPay maintient un portefeuille USDT (rail TRON, on-chain) ainsi qu'un portefeuille XOF par rail fiat (actuellement <ic>MOMO</ic> et <ic>CARD</ic>) — trois portefeuilles au total. Chaque entrée expose ses propres <ic>currency</ic>, <ic>rail</ic>, solde, flux de règlement et voie de retrait. Identifiez toujours un portefeuille par la paire <ic>(currency, rail)</ic> : filtrer uniquement sur <ic>currency === 'XOF'</ic> ne renvoie que la première entrée XOF et ignore silencieusement l'autre rail. Les conversions permettent de transférer de la valeur entre eux au taux en temps réel.
Liste des Portefeuilles
Renvoie un portefeuille par paire (devise, rail). Utilisez l'id du portefeuille lors des appels de conversion ou de retrait.
/v1/merchants/walletscurl https://api.zyndpay.io/v1/merchants/wallets \
-H "X-Api-Key: YOUR_API_KEY"const wallets = await zyndpay.wallets.list();
// Always identify a wallet by the (currency, rail) pair — XOF is split per rail.
const usdtWallet = wallets.find(w => w.currency === 'USDT_TRC20' && w.rail === 'TRC20');
const xofMomoWallet = wallets.find(w => w.currency === 'XOF' && w.rail === 'MOMO');
const xofCardWallet = wallets.find(w => w.currency === 'XOF' && w.rail === 'CARD');
console.log(usdtWallet.id, usdtWallet.balance);
console.log(xofMomoWallet.balance, xofCardWallet.balance);wallets = client.wallets.list()
for w in wallets:
print(f"{w['currency']} ({w['rail']}): {w['balance']}")$wallets = $client->wallets->list();
foreach ($wallets as $wallet) {
echo $wallet['currency'] . ': ' . $wallet['balance'] . "\n";
}Liste blanche d'adresses
La liste blanche d'adresses regroupe les adresses TRON autorisées à recevoir vos retraits USDT. Chaque destination de retrait — depuis le tableau de bord ou via l'API — doit préalablement figurer dans votre liste blanche. Les adresses peuvent être ajoutées une par une ou en lot (jusqu'à 500).
Ajouter une adresse
Ajoute une adresse TRON à la liste blanche. Si l'adresse existe déjà, l'appel réussit et fusionne optionnellement les nouveaux contextes d'utilisation.
/v1/wallets/whitelistcurl -X POST https://api.zyndpay.io/v1/wallets/whitelist \
-H "Authorization: Bearer zyp_live_sk_..." \
-H "Content-Type: application/json" \
-d '{
"currency": "USDT_TRC20",
"chain": "TRON",
"address": "TRX_ADDRESS_HERE",
"label": "My payout wallet",
"usageContexts": ["WITHDRAWAL", "PAYOUT"]
}'const entry = await zyndpay.whitelist.add({
currency: 'USDT_TRC20',
chain: 'TRON',
address: 'TRX_ADDRESS_HERE',
label: 'My payout wallet',
usageContexts: ['WITHDRAWAL', 'PAYOUT'], // optional, defaults to ['WITHDRAWAL']
});
// entry.availableAt — earliest time this address can receive a withdrawal
console.log('Available for withdrawal at:', entry.availableAt);Ajout en lot
Ajoutez jusqu'à 500 adresses en un seul appel. Passez un tableau <ic>usageContexts</ic> optionnel (par défaut <ic>['WITHDRAWAL']</ic>) — appliqué à chaque adresse du lot. Pour rendre les adresses éligibles aux retraits et aux paiements dès la création, envoyez <ic>['WITHDRAWAL', 'PAYOUT']</ic>. La réponse distingue <ic>added</ic> (nouvellement insérées), <ic>alreadyExists</ic> (ignorées — déjà présentes), et <ic>invalid</ic> (échec de validation d'adresse TRON). Le délai de 24h s'applique individuellement à chaque nouvelle adresse.
/v1/wallets/whitelist/bulkcurl -X POST https://api.zyndpay.io/v1/wallets/whitelist/bulk \
-H "Authorization: Bearer zyp_live_sk_..." \
-H "Content-Type: application/json" \
-d '{
"addresses": [
{ "address": "TRX_ADDRESS_1", "label": "Customer wallet A" },
{ "address": "TRX_ADDRESS_2", "label": "Customer wallet B" }
],
"usageContexts": ["WITHDRAWAL", "PAYOUT"]
}'// Add up to 500 addresses per call
const result = await zyndpay.whitelist.bulkAdd({
addresses: [
{ address: 'TRX_ADDRESS_1', label: 'Customer wallet A' },
{ address: 'TRX_ADDRESS_2', label: 'Customer wallet B' },
],
usageContexts: ['WITHDRAWAL', 'PAYOUT'], // applied to every address in the batch
});
console.log(`Added: ${result.added.length}`);
console.log(`Already existed: ${result.alreadyExists.length}`);
console.log(`Invalid: ${result.invalid.length}`);
// Each added entry includes availableAt (24h from now)Mettre à jour les contextes d'utilisation
Remplace le tableau <ic>usageContexts</ic> sur une entrée existante — par exemple pour étendre une adresse WITHDRAWAL-uniquement vers PAYOUT, sans la ré-ajouter (ce qui redémarrerait le délai de 24h). Le corps doit contenir au moins un contexte valide.
/v1/wallets/whitelist/:id/contextscurl -X PATCH https://api.zyndpay.io/v1/wallets/whitelist/ENTRY_ID/contexts \
-H "Authorization: Bearer zyp_live_sk_..." \
-H "Content-Type: application/json" \
-d '{
"usageContexts": ["WITHDRAWAL", "PAYOUT"]
}'Lister la liste blanche
Retourne toutes les adresses de votre liste blanche, triées par date d'ajout décroissante. Filtrage optionnel par contexte d'utilisation.
/v1/wallets/whitelistcurl https://api.zyndpay.io/v1/wallets/whitelist \
-H "Authorization: Bearer zyp_live_sk_..."Les adresses enregistrées ne peuvent pas être supprimées
Les destinataires crypto enregistrés sont permanents. Ajoutez de nouveaux destinataires si nécessaire ; les destinataires existants restent visibles pour l’historique d’audit et la sécurité des transferts.
Destinations Fiat
Une destination fiat est un numéro mobile money ou un compte bancaire pouvant recevoir des retraits XOF. Au moins une destination doit être enregistrée avant de pouvoir retirer des fonds XOF.
/v1/merchants/fiat-destinations/v1/merchants/fiat-destinations/v1/merchants/fiat-destinations/:id/v1/merchants/fiat-destinations/:idPour MOMO : kind, label, momoOperator (ORANGE_BF, MOOV_BF), momoPhone (format E.164).
Pour BANK : kind, label, bankName, bankAccountName, bankIban ou bankAccountNumber, bankCode.
curl -X POST https://api.zyndpay.io/v1/merchants/fiat-destinations \
-H "X-Api-Key: zyp_live_sk_..." \
-H "Content-Type: application/json" \
-d '{
"kind": "MOMO",
"label": "Orange Money BF",
"momoOperator": "ORANGE_BF",
"momoPhone": "+22670123456",
"isPrimary": true
}'// Register a mobile money destination
const dest = await zyndpay.fiatDestinations.create({
kind: 'MOMO',
label: 'Orange Money BF',
momoOperator: 'ORANGE_BF',
momoPhone: '+22670123456',
isPrimary: true,
});
// Note: 24-hour cooldown before first use (BCEAO compliance)Convertir entre Portefeuilles — Preview
Prévisualisez le montant de destination avant d'engager une conversion entre portefeuilles. Les devises source et destination viennent des portefeuilles sélectionnés ; le contrat n'est pas lié à une seule paire de devises.
/v1/conversions/wallet/previewcurl "https://api.zyndpay.io/v1/conversions/wallet/preview?fromCurrency=XOF&toCurrency=USDT&amount=50000" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json"Convertir entre Portefeuilles — List
/v1/conversionscurl https://api.zyndpay.io/v1/conversions \
-H "X-Api-Key: YOUR_API_KEY"Convertir entre Portefeuilles
Convertissez des fonds d'un portefeuille à un autre au taux ZyndPay en temps réel. Usage typique : convertir des XOF reçus d'encaissements carte ou mobile money en USDT dans votre portefeuille USDT.
/v1/conversions/wallet| Paramètre | Type | Requis | Description |
|---|---|---|---|
fromWalletId | string | requis | Identifiant du portefeuille source (depuis la liste des portefeuilles). |
toWalletId | string | requis | Identifiant du portefeuille de destination. |
fromAmount | string | requis | Montant à convertir depuis le portefeuille source. |
// Convert XOF earnings (MOMO rail) to USDT
const wallets = await zyndpay.wallets.list();
const xof = wallets.find(w => w.currency === 'XOF' && w.rail === 'MOMO')!;
const usdt = wallets.find(w => w.currency === 'USDT_TRC20' && w.rail === 'TRC20')!;
const conversion = await zyndpay.conversions.convertBetweenWallets({
fromWalletId: xof.id,
toWalletId: usdt.id,
fromAmount: '50000',
});
console.log(`Converted: ${conversion.fromAmount} ${conversion.fromCurrency}`);
console.log(`Received: ${conversion.toAmountNet} ${conversion.toCurrency}`);from zyndpay import ZyndPay
client = ZyndPay(api_key="YOUR_API_KEY")
wallets = client.wallets.list()
usdt_wallet = next(w for w in wallets if w["currency"] == "USDT_TRC20")
xof_wallet = next(w for w in wallets if w["currency"] == "XOF")
conversion = client.conversions.convert_between_wallets(
from_wallet_id=usdt_wallet["id"],
to_wallet_id=xof_wallet["id"],
from_amount="100.00",
)
print(conversion["id"], conversion["status"])Récupérer une Transaction
Récupérez une transaction individuelle par son identifiant. L'ID peut être un transactionId d'encaissement, un ID de virement sortant ou un ID de retrait — tous les types partagent le même endpoint.
/v1/transactions/:idcurl https://api.zyndpay.io/v1/transactions/TX_ID \
-H "X-Api-Key: YOUR_API_KEY"Liste des Transactions
Historique unifié des encaissements, virements sortants et retraits. Filtrez par type, statut, devise ou plage de dates.
/v1/transactions| Paramètre | Type | Requis | Description |
|---|---|---|---|
type | string | optionnel | Filtrer par type : PAYIN, PAYOUT, WITHDRAWAL. |
status | string | optionnel | Filtrer par statut. |
currency | string | optionnel | Filtrer par devise (ex. USDT_TRC20). |
from | string | optionnel | Date de début (ISO 8601). |
to | string | optionnel | Date de fin (ISO 8601). |
page | number | optionnel | Numéro de page. Défaut : 1. |
limit | number | optionnel | Résultats par page. Défaut : 20. Max : 100. |
curl "https://api.zyndpay.io/v1/transactions?limit=20&type=PAYIN" \
-H "X-Api-Key: YOUR_API_KEY"const txs = await zyndpay.transactions.list({
type: 'PAYIN',
status: 'CONFIRMED',
from: '2026-01-01',
to: '2026-04-30',
});
console.log(`${txs.total} transactions found`);txns = client.transactions.list(limit=20, type="PAYIN")
for t in txns["items"]:
print(t["id"], t["type"], t["status"], t["amountRequested"])$result = $client->transactions->list([
'limit' => 20,
'type' => 'PAYIN',
]);
foreach ($result['items'] as $tx) {
echo $tx['id'] . ' ' . $tx['status'] . "\n";
}Exporter les Transactions
Générez un CSV ou PDF de votre historique de transactions. Les exports CSV s'exécutent en tâche de fond ; le PDF est renvoyé de manière synchrone.
/v1/transactions/export/v1/transactions/export/pdfcurl "https://api.zyndpay.io/v1/transactions/export?from=2026-01-01&to=2026-12-31" \
-H "X-Api-Key: YOUR_API_KEY" -o transactions.csv// Download CSV export
const csv = await zyndpay.transactions.export({
from: '2026-01-01',
to: '2026-04-30',
});
// csv is a string — write to file or send to browser
// Download PDF report
const pdf = await zyndpay.transactions.exportPdf();Vue d'ensemble
ZyndPay envoie des requêtes HTTP POST à vos URLs d'endpoints enregistrés lorsque des événements de paiement se produisent. Enregistrez des endpoints depuis la page <strong>Webhooks</strong> de votre tableau de bord marchand.
{
"event": "payin.confirmed",
"data": {
"transactionId": "cma1xyz8f0001yx5k9abc1234",
"status": "CONFIRMED",
"currency": "XOF",
"chain": "TRON",
"externalRef": "order_123",
"amount": "9300.00",
"amountRequested": "9300.00",
"txHash": "abc123def456...",
"confirmedAt": "2026-03-06T11:01:02.000Z",
"pricingCurrency": "USDT_TRC20",
"pricingAmount": "15.00",
"fxRate": "620.00000000"
},
"createdAt": "2026-03-06T11:01:03.000Z"
}Vérification de Signature
Chaque livraison de webhook inclut un en-tête <ic>Zyndpay-Signature</ic>. Vérifiez-le toujours avant de traiter l'événement.
t=1680000000,v1=5257a869e7ecebeda32af...La signature est <ic>HMAC-SHA256(timestamp + "." + raw_body, webhook_secret)</ic>. Utilisez toujours le <strong>corps brut de la requête</strong> — analyser le JSON d'abord causera des échecs de vérification.
const crypto = require('crypto');
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['zyndpay-signature'];
const [tPart, v1Part] = sig.split(',');
const timestamp = tPart.split('=')[1];
const received = v1Part.split('=')[1];
// Reject events older than 5 minutes
const age = Math.abs(Date.now() / 1000 - parseInt(timestamp) / 1000);
if (age > 300) return res.status(400).send('Webhook too old');
const expected = crypto
.createHmac('sha256', process.env.WEBHOOK_SECRET)
.update(`${timestamp}.${req.body}`)
.digest('hex');
// Use timing-safe comparison to prevent timing attacks
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received))) {
return res.status(400).send('Invalid signature');
}
const event = JSON.parse(req.body);
// Handle event...
res.json({ received: true });
});Points de Terminaison Webhook
Gérez vos points de terminaison webhook par programmation plutôt que (ou en plus de) l'interface du tableau de bord.
/v1/webhooks/endpoints| Paramètre | Type | Requis | Description |
|---|---|---|---|
url | string | requis | URL HTTPS vers laquelle ZyndPay enverra les événements en POST. |
events | array | requis | Tableau de noms d'événements auxquels s'abonner (ex. : ["payin.confirmed", "payout.failed"]). |
retryConfig | object | optionnel | Réglage des nouvelles tentatives (optionnel). La réponse expose <ic>maxRetries</ic> (défaut 3), <ic>retryBackoff</ic> (énum : <ic>EXPONENTIAL</ic>) et <ic>retryIntervalSeconds</ic> (défaut 60). Le corps de la requête accepte les mêmes champs au niveau racine. |
/v1/webhooks/endpoints/v1/webhooks/endpoints/:id/v1/webhooks/endpoints/:id/v1/webhooks/endpoints/:id/rotate-secret/v1/webhooks/endpoints/:id/reactivateconst endpoint = await zyndpay.webhooks.createEndpoint({
url: 'https://example.com/webhooks/zyndpay',
events: ['payin.confirmed', 'payout.failed', 'withdrawal.confirmed'],
});
// Store endpoint.secret (whs_* prefix) — shown only once!
process.env.ZYNDPAY_WEBHOOK_SECRET = endpoint.secret;Événements
| Événement | Type | Requis | Description |
|---|---|---|---|
payin.created | event | requis | Déclenché quand un nouvel encaissement est créé et une adresse de dépôt est générée. |
payin.confirming | event | requis | Déclenché quand des fonds sont reçus et en attente de 5 confirmations de blocs. |
payin.confirmed | event | requis | Déclenché quand un encaissement est entièrement réglé — pour USDT après 5 confirmations on-chain, pour carte après la capture par la banque émettrice, pour mobile money après confirmation de l'opérateur. Le solde marchand est crédité. |
payin.expired | event | requis | Déclenché quand une adresse d'encaissement expire sans recevoir le montant attendu. |
payin.failed | event | requis | Déclenché quand un encaissement échoue sur l'un des rails — refus carte, échec opérateur mobile money, ou USDT reçu en deçà du seuil de poussière. L'enveloppe `data` inclut un champ `reason` avec la cause de l'échec lisible par l'opérateur. |
payin.underpaid | event | requis | Déclenché quand un encaissement reçoit moins que le montant demandé. |
payin.overpaid | event | requis | Déclenché quand un encaissement reçoit plus que le montant demandé. |
deposit.confirmed | event | requis | Déclenché quand un dépôt de portefeuille atteint 20 confirmations on-chain. Le solde marchand est crédité. |
deposit.failed | event | requis | Déclenché quand un dépôt de portefeuille échoue à se confirmer. |
deposit.overpaid | event | requis | Déclenché quand un dépôt de portefeuille reçoit plus que le montant attendu. |
deposit.underpaid | event | requis | Déclenché quand un dépôt de portefeuille reçoit moins que le montant attendu. |
payout.requested | event | requis | Déclenché lorsqu'un nouveau paiement sortant est demandé et mis en file d'attente pour vérification de conformité. Émis en parallèle de l'événement hérité `withdrawal.requested` pour le même cycle de vie. |
payout.approved | event | requis | Déclenché quand un paiement sortant passe la vérification et est approuvé pour diffusion on-chain. Émis en parallèle de l'événement hérité `withdrawal.approved` pour le même cycle de vie. |
payout.broadcast | event | requis | Déclenché quand une transaction de paiement sortant est signée et diffusée sur le réseau TRON. |
payout.confirmed | event | requis | Déclenché quand un paiement sortant atteint 20 confirmations on-chain. |
payout.failed | event | requis | Déclenché quand la diffusion d'un paiement sortant échoue. Le montant est remboursé au solde marchand. |
withdrawal.requested | event | requis | Déclenché lorsqu'un nouveau retrait est demandé et mis en file d'attente pour vérification de conformité. |
withdrawal.broadcast | event | requis | Déclenché lorsqu'un retrait est signé et diffusé sur TRON. |
withdrawal.approved | event | requis | Déclenché quand un retrait passe la validation et est approuvé pour diffusion on-chain. |
withdrawal.confirmed | event | requis | Déclenché quand un retrait est confirmé on-chain. |
withdrawal.failed | event | requis | Déclenché quand la diffusion d'un retrait échoue. Le montant est remboursé au solde. |
conversion.confirmed | event | requis | Déclenché quand une conversion entre portefeuilles est complétée avec succès. |
conversion.failed | event | requis | Déclenché quand une conversion échoue. Les fonds source sont retournés au portefeuille d'origine. |
subscription.created | event | requis | Déclenché quand un abonnement récurrent est créé pour la première fois lors d'un paiement paylink. L'enveloppe `data` contient subscriptionId, paylinkId, les infos client et la première période de facturation. |
subscription.renewed | event | requis | Déclenché quand un abonnement est renouvelé avec succès pour le cycle suivant. |
subscription.renewal_initiated | event | requis | Déclenché au début du cycle de renouvellement d'abonnement (avant la réussite du débit). |
subscription.failed | event | requis | Déclenché quand le débit de renouvellement d'abonnement échoue. |
subscription.cancelled | event | requis | Déclenché quand un abonnement est annulé (par le marchand ou le client). |
subscription.paused | event | requis | Déclenché quand un abonnement est mis en pause. |
subscription.resumed | event | requis | Déclenché quand un abonnement en pause est repris. |
subscription.updated | event | requis | Déclenché quand les détails d'abonnement (montant, intervalle) sont mis à jour. |
refund.created | event | requis | Déclenché quand une demande de remboursement est créée (en attente d'approbation). |
refund.approved | event | requis | Déclenché quand un remboursement est approuvé par les opérations. |
refund.rejected | event | requis | Déclenché quand un remboursement est rejeté. |
refund.completed | event | requis | Déclenché quand un remboursement est entièrement versé au client. |
refund.failed | event | requis | Déclenché quand un versement de remboursement échoue. |
dispute.opened | event | requis | Déclenché quand un litige est ouvert sur une transaction. |
dispute.resolved | event | requis | Déclenché quand un litige est résolu. |
dispute.rejected | event | requis | Déclenché quand un litige est rejeté. |
dispute.escalated | event | requis | Déclenché quand un litige est escaladé à un niveau supérieur. |
aml.flagged | event | requis | Déclenché quand un paiement sortant est bloqué par le filtrage AML. Abonnez-vous pour réagir aux blocages de conformité. |
splitpayment.created | event | requis | Marketplace / Connect uniquement. Déclenché sur l'endpoint du marchand plateforme après qu'un encaissement a été réparti entre les sous-marchands. Inclut la répartition des allocations pour que la plateforme puisse rapprocher avec son propre grand livre. |
kyb.approved | event | requis | docs.eventKybApprovedDesc |
kyb.rejected | event | requis | docs.eventKybRejectedDesc |
kyb.thread.created | event | requis | A new compliance thread was created on a KYB review. |
kyb.thread.replied | event | requis | A merchant or admin replied to a KYB compliance thread (author field distinguishes). |
kyb.thread.resolved | event | requis | An admin resolved a KYB compliance thread. |
kyb.thread.dismissed | event | requis | An admin dismissed a KYB compliance thread. |
agreement.resign_requested | event | requis | Merchant received re-sign request for MSA v3. Action required within 30 days. |
agreement.resigned | event | requis | Merchant completed re-sign of MSA v3. |
Schémas de Charge Utile
Chaque webhook est un POST JSON avec la même enveloppe : event, data, createdAt. La forme de data dépend du type d'événement. Cliquez sur un événement ci-dessous pour voir sa charge utile exacte.
{
"event": "payin.confirmed",
"data": {
"transactionId": "cma1xyz8f0001yx5k9abc1234",
"status": "CONFIRMED",
"currency": "XOF",
"chain": "TRON",
"externalRef": "order_123",
"amount": "9300.00",
"amountRequested": "9300.00",
"txHash": "abc123def456...",
"confirmedAt": "2026-03-06T11:01:02.000Z",
"pricingCurrency": "USDT_TRC20",
"pricingAmount": "15.00",
"fxRate": "620.00000000"
},
"createdAt": "2026-03-06T11:01:03.000Z"
}{
"event": "payout.confirmed",
"data": {
"transactionId": "tx_sample_payout123",
"status": "CONFIRMED",
"currency": "USDT_TRC20",
"chain": "TRON",
"amount": "50.00",
"fee": "1.50",
"destinationAddress": "TXyz1234567890AbCdEfGhIjKlMnOpQrSt",
"txHash": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
"externalRef": "vendor_invoice_456",
"confirmedAt": "2026-01-15T12:01:00.000Z"
},
"createdAt": "2026-01-15T12:01:05.000Z"
}{
"event": "withdrawal.confirmed",
"data": {
"transactionId": "wdr_sample_abc123",
"status": "CONFIRMED",
"currency": "USDT_TRC20",
"chain": "TRON",
"amount": "500.00",
"fee": "5.00",
"netAmount": "495.00",
"toAddress": "TXyz1234567890AbCdEfGhIjKlMnOpQrSt",
"txHash": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
"confirmedAt": "2026-01-15T12:01:00.000Z"
},
"createdAt": "2026-01-15T12:01:05.000Z"
}Tester un Webhook
Envoyez un événement de test à l'un de vos endpoints webhook enregistrés. Utile pour vérifier que votre endpoint est accessible et que votre code de vérification de signature fonctionne correctement.
/v1/webhooks/test| Paramètre | Type | Requis | Description |
|---|---|---|---|
endpointId | string | requis | L'ID de l'endpoint webhook auquel envoyer l'événement de test. Obtenez-le via GET /webhooks/endpoints. |
eventType | string | requis | Le type d'événement à simuler (ex. payin.confirmed, payout.confirmed). Doit être un nom d'événement valide. |
curl -X POST https://api.zyndpay.io/v1/webhooks/test \
-H "X-Api-Key: zyp_live_sk_..." \
-H "Content-Type: application/json" \
-d '{
"endpointId": "your-endpoint-id",
"eventType": "payin.confirmed"
}'{
"success": true,
"data": {
"message": "Test event sent",
"deliveryId": "9270e4aa-8391-4e3f-a70b-23c431714b84"
}
}Schémas de Charge
Chaque livraison webhook partage la même enveloppe : une chaîne event, un objet data avec des champs spécifiques à l'événement, et un horodatage createdAt. Les exemples ci-dessous montrent les noms et types de champs exacts.
payin.createdDéclenché immédiatement lorsqu'un nouvel encaissement est créé et qu'une adresse TRON est assignée.
{
"event": "payin.created",
"data": {
"transactionId": "cma1xyz8f0001yx5k9abc1234",
"address": "TRXabc123def456ghi789jkl",
"amount": "100.00",
"currency": "USDT_TRC20",
"chain": "TRON",
"externalRef": "order_123",
"status": "AWAITING_PAYMENT",
"expiresAt": "2026-03-06T12:00:00.000Z",
"paymentUrl": "https://checkout.zyndpay.io/cma1xyz8f0001yx5k9abc1234"
},
"createdAt": "2026-03-06T11:00:00.000Z"
}payin.confirmedDéclenché lorsqu'un encaissement atteint 5 confirmations on-chain. Le solde marchand est crédité à ce moment.
{
"event": "payin.confirmed",
"data": {
"transactionId": "cma1xyz8f0001yx5k9abc1234",
"address": "TRXabc123def456ghi789jkl",
"amount": "100.00",
"amountReceived": "100.00",
"currency": "USDT_TRC20",
"chain": "TRON",
"externalRef": "order_123",
"txHash": "abc123def456...",
"status": "CONFIRMED",
"confirmedAt": "2026-03-06T11:01:02.000Z"
},
"createdAt": "2026-03-06T11:01:03.000Z"
}payin.expiredDéclenché lorsqu'une adresse d'encaissement expire sans avoir reçu le montant USDT attendu.
{
"event": "payin.expired",
"data": {
"transactionId": "cma1xyz8f0001yx5k9abc1234",
"address": "TRXabc123def456ghi789jkl",
"amount": "100.00",
"currency": "USDT_TRC20",
"chain": "TRON",
"externalRef": "order_123",
"status": "EXPIRED",
"expiredAt": "2026-03-06T12:00:00.000Z"
},
"createdAt": "2026-03-06T12:00:01.000Z"
}payin.failedDéclenché lorsqu'un encaissement échoue sur l'un des rails — refus carte, échec opérateur mobile money, ou USDT reçu en deçà du seuil de poussière. Le champ `reason` porte la cause de l'échec lisible par l'opérateur. Aucun fonds n'a été capturé.
{
"event": "payin.failed",
"data": {
"transactionId": "cma1xyz8f0001yx5k9abc1234",
"address": "TRXabc123def456ghi789jkl",
"amount": "100.00",
"currency": "USDT_TRC20",
"chain": "TRON",
"externalRef": "order_123",
"status": "FAILED",
"reason": "Network processing error"
},
"createdAt": "2026-03-06T11:05:00.000Z"
}payin.underpaidDéclenché lorsque le client envoie moins d'USDT que le montant demandé. Le champ shortfall indique le manque.
{
"event": "payin.underpaid",
"data": {
"transactionId": "cma1xyz8f0001yx5k9abc1234",
"address": "TRXabc123def456ghi789jkl",
"amount": "100.00",
"amountReceived": "90.00",
"shortfall": "10.00",
"currency": "USDT_TRC20",
"chain": "TRON",
"externalRef": "order_123",
"txHash": "abc123def456...",
"status": "UNDERPAID",
"confirmedAt": "2026-03-06T11:01:02.000Z"
},
"createdAt": "2026-03-06T11:01:03.000Z"
}payin.overpaidDéclenché lorsque le client envoie plus d'USDT que le montant demandé. Le champ surplus indique l'excédent.
{
"event": "payin.overpaid",
"data": {
"transactionId": "cma1xyz8f0001yx5k9abc1234",
"address": "TRXabc123def456ghi789jkl",
"amount": "100.00",
"amountReceived": "110.00",
"surplus": "10.00",
"currency": "USDT_TRC20",
"chain": "TRON",
"externalRef": "order_123",
"txHash": "abc123def456...",
"status": "OVERPAID",
"confirmedAt": "2026-03-06T11:01:02.000Z"
},
"createdAt": "2026-03-06T11:01:03.000Z"
}payout.confirmedDéclenché lorsqu'un paiement sortant est confirmé on-chain. Le txHash est l'ID de transaction on-chain.
{
"event": "payout.confirmed",
"data": {
"transactionId": "cma2abc9g0002yz6l0def5678",
"destinationAddress": "TXYZabc123def456ghi789jkl012mno345",
"amount": "50.00",
"fee": "1.50",
"currency": "USDT_TRC20",
"chain": "TRON",
"externalRef": "payout_vendor_456",
"txHash": "def456abc123...",
"status": "CONFIRMED",
"confirmedAt": "2026-03-06T11:05:00.000Z"
},
"createdAt": "2026-03-06T11:05:01.000Z"
}payout.failedDéclenché lorsque la diffusion d'un paiement sortant échoue. Le montant est automatiquement remboursé sur votre solde marchand.
{
"event": "payout.failed",
"data": {
"transactionId": "cma2abc9g0002yz6l0def5678",
"destinationAddress": "TXYZabc123def456ghi789jkl012mno345",
"amount": "50.00",
"currency": "USDT_TRC20",
"chain": "TRON",
"externalRef": "payout_vendor_456",
"status": "FAILED",
"reason": "Broadcast failed — balance refunded",
"failedAt": "2026-03-06T11:05:00.000Z"
},
"createdAt": "2026-03-06T11:05:01.000Z"
}withdrawal.approvedDéclenché lorsqu'une demande de retrait passe la révision administrateur et est mise en file pour la diffusion on-chain.
{
"event": "withdrawal.approved",
"data": {
"id": "wdr_cma1xyz8f0001yx5k",
"amount": "500.00",
"fee": "5.00",
"netAmount": "495.00",
"destinationAddress": "TRXabc123def456ghi789jkl",
"currency": "USDT_TRC20",
"chain": "TRON",
"status": "APPROVED",
"approvedAt": "2026-03-06T12:00:00.000Z"
},
"createdAt": "2026-03-06T12:00:01.000Z"
}withdrawal.confirmedDéclenché lorsqu'un retrait est confirmé on-chain. netAmount est ce que votre portefeuille a réellement reçu après frais.
{
"event": "withdrawal.confirmed",
"data": {
"id": "wdr_cma1xyz8f0001yx5k",
"amount": "500.00",
"fee": "5.00",
"netAmount": "495.00",
"destinationAddress": "TRXabc123def456ghi789jkl",
"currency": "USDT_TRC20",
"chain": "TRON",
"txHash": "ghi789abc123...",
"status": "CONFIRMED",
"confirmedAt": "2026-03-06T12:30:00.000Z"
},
"createdAt": "2026-03-06T12:30:01.000Z"
}withdrawal.failedDéclenché lorsque la diffusion d'un retrait échoue. Le montant est automatiquement remboursé sur votre solde.
{
"event": "withdrawal.failed",
"data": {
"id": "wdr_cma1xyz8f0001yx5k",
"amount": "500.00",
"destinationAddress": "TRXabc123def456ghi789jkl",
"currency": "USDT_TRC20",
"chain": "TRON",
"status": "FAILED",
"reason": "Broadcast failed — balance refunded",
"failedAt": "2026-03-06T12:30:00.000Z"
},
"createdAt": "2026-03-06T12:30:01.000Z"
}SDK Node.js
Le SDK TypeScript officiel avec typage complet et vérification de webhook intégrée.
npm install @zyndpay/sdkconst { ZyndPay } = require('@zyndpay/sdk');
const client = new ZyndPay({
apiKey: 'zyp_live_sk_...',
webhookSecret: process.env.ZYNDPAY_WEBHOOK_SECRET,
});
// Create a payment
const payment = await client.payins.create({
amount: '100.00',
externalRef: 'order_123',
});
console.log(payment.address);
// Verify a webhook (uses raw body, not parsed JSON)
const event = client.webhooks.verify(
rawBody,
req.headers['zyndpay-signature']
);Démarrage TypeScript
Acceptez votre premier paiement USDT en moins de 5 minutes. Cet exemple utilise Express — adaptez à votre framework.
import { ZyndPay } from '@zyndpay/sdk';
import express from 'express';
// 1. Initialize the SDK
const client = new ZyndPay({
apiKey: process.env.ZYNDPAY_API_KEY, // zyp_live_sk_...
webhookSecret: process.env.WEBHOOK_SECRET,
});
// 2. Create a pay-in (generates a TRON deposit address for your customer)
const payment = await client.payins.create({
amount: '100.00',
externalRef: 'order_123',
expiresInSeconds: 3600,
successUrl: 'https://yoursite.com/payment/success',
});
// Send payment.paymentUrl or payment.address to your customer
console.log('Deposit address:', payment.address);
console.log('Payment page:', payment.paymentUrl);
// 3. Handle webhook events
const app = express();
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
// Always verify using the raw body — never parsed JSON
const event = client.webhooks.verify(
req.body,
req.headers['zyndpay-signature'] as string,
);
switch (event.event) {
case 'payin.confirmed':
console.log(`Payment confirmed: ${event.data.amount} USDT — ${event.data.externalRef}`);
// Fulfill the order, unlock access, send receipt, etc.
break;
case 'payin.expired':
console.log(`Payment expired: ${event.data.externalRef}`);
break;
}
res.json({ received: true });
});
// 4. Check payment status anytime
const status = await client.payins.get(payment.transactionId);
console.log('Status:', status.status); // AWAITING_PAYMENT → CONFIRMING → CONFIRMEDNew Resources (v1.5.0+)
The following resources are available as of SDK v1.5.0: wallets, fiat destinations, conversions, bulk payments, transactions export, and balances.
// Convert XOF earnings (MOMO rail) to USDT
const wallets = await zyndpay.wallets.list();
const xof = wallets.find(w => w.currency === 'XOF' && w.rail === 'MOMO')!;
const usdt = wallets.find(w => w.currency === 'USDT_TRC20' && w.rail === 'TRC20')!;
const conversion = await zyndpay.conversions.convertBetweenWallets({
fromWalletId: xof.id,
toWalletId: usdt.id,
fromAmount: '50000',
});
console.log(`Converted: ${conversion.fromAmount} ${conversion.fromCurrency}`);
console.log(`Received: ${conversion.toAmountNet} ${conversion.toCurrency}`);// Register a mobile money destination
const dest = await zyndpay.fiatDestinations.create({
kind: 'MOMO',
label: 'Orange Money BF',
momoOperator: 'ORANGE_BF',
momoPhone: '+22670123456',
isPrimary: true,
});
// Note: 24-hour cooldown before first use (BCEAO compliance)const wallets = await zyndpay.wallets.list();
// Always identify a wallet by the (currency, rail) pair — XOF is split per rail.
const usdtWallet = wallets.find(w => w.currency === 'USDT_TRC20' && w.rail === 'TRC20');
const xofMomoWallet = wallets.find(w => w.currency === 'XOF' && w.rail === 'MOMO');
const xofCardWallet = wallets.find(w => w.currency === 'XOF' && w.rail === 'CARD');
console.log(usdtWallet.id, usdtWallet.balance);
console.log(xofMomoWallet.balance, xofCardWallet.balance);// Download CSV export
const csv = await zyndpay.transactions.export({
from: '2026-01-01',
to: '2026-04-30',
});
// csv is a string — write to file or send to browser
// Download PDF report
const pdf = await zyndpay.transactions.exportPdf();const balances = await zyndpay.balances.getAll();
// { USDT_TRC20: '245.50', XOF: '150000', USD: '0' }const estimate = await zyndpay.payouts.estimate({
amount: '100.00',
currency: 'USDT_TRC20',
chain: 'TRON',
destinationAddress: 'TXyz1234567890abcdef1234567890abcde',
});
console.log(`Fee: ${estimate.fee} USDT`);
console.log(`Total deducted: ${estimate.totalDebited} USDT`);
console.log(`Sufficient balance: ${estimate.sufficient}`);SDK Python
Le SDK Python officiel basé sur <ic>requests</ic> avec une interface synchrone simple.
pip install zyndpayfrom zyndpay import ZyndPay
client = ZyndPay(
api_key="zyp_live_sk_...",
webhook_secret=os.environ.get("ZYNDPAY_WEBHOOK_SECRET"),
)
payment = client.payins.create(
amount="100.00",
external_ref="order_123"
)
print(payment["address"])Démarrage Python
Acceptez votre premier paiement USDT en moins de 5 minutes. Cet exemple utilise Flask — adaptez à votre framework.
import os
from zyndpay import ZyndPay
from flask import Flask, request, jsonify
# 1. Initialize the SDK
client = ZyndPay(
api_key=os.environ["ZYNDPAY_API_KEY"], # zyp_live_sk_...
webhook_secret=os.environ["WEBHOOK_SECRET"],
)
# 2. Create a pay-in (generates a TRON deposit address for your customer)
payment = client.payins.create(
amount="100.00",
external_ref="order_123",
expires_in_seconds=3600,
success_url="https://yoursite.com/payment/success",
)
# Send payment["paymentUrl"] or payment["address"] to your customer
print("Deposit address:", payment["address"])
print("Payment page:", payment["paymentUrl"])
# 3. Handle webhook events
app = Flask(__name__)
@app.post("/webhook")
def handle_webhook():
# Always verify using the raw body — never parsed JSON
event = client.webhooks.verify(
request.get_data(),
request.headers.get("Zyndpay-Signature"),
)
if event["event"] == "payin.confirmed":
data = event["data"]
print(f"Payment confirmed: {data['amount']} USDT — {data['externalRef']}")
# Fulfill the order, unlock access, send receipt, etc.
elif event["event"] == "payin.expired":
print("Payment expired:", event["data"]["externalRef"])
return jsonify(received=True)
# 4. Check payment status anytime
status = client.payins.get(payment["transactionId"])
print("Status:", status["status"]) # AWAITING_PAYMENT → CONFIRMING → CONFIRMEDNew Resources (v1.5.0+)
As of SDK v1.5.0, the Python SDK supports: wallets.list(), fiat_destinations.create(), conversions.convert_between_wallets(), bulk_payments workflows, transactions.export(), and balances.get_all().
estimate = zyndpay.payouts.estimate(
amount='100.00',
destination_address='TXyz1234567890abcdef1234567890abcde',
)
print(f"Fee: {estimate['fee']} USDT")
print(f"Total deducted: {estimate['totalDebited']} USDT")
print(f"Sufficient: {estimate['sufficient']}")SDK PHP
Le SDK PHP officiel avec client HTTP basé sur cURL et vérification de webhook. Nécessite PHP 8.0+.
composer require zyndpay/zyndpay-php<?php
require_once 'vendor/autoload.php';
$client = new ZyndPay\ZyndPay('zyp_live_sk_...', [
'webhook_secret' => getenv('ZYNDPAY_WEBHOOK_SECRET'),
]);
// Create a payment
$payment = $client->payins->create([
'amount' => '100.00',
'externalRef' => 'order_123',
]);
echo $payment['address'];
// Verify a webhook
$event = $client->webhooks->verify(
$rawBody,
$_SERVER['HTTP_ZYNDPAY_SIGNATURE']
);Démarrage PHP
Acceptez votre premier paiement USDT en moins de 5 minutes. Cet exemple utilise du PHP simple — adaptez le gestionnaire webhook à votre framework.
<?php
require_once 'vendor/autoload.php';
// 1. Initialize the SDK
$client = new ZyndPay\ZyndPay(
getenv('ZYNDPAY_API_KEY'), // zyp_live_sk_...
['webhook_secret' => getenv('WEBHOOK_SECRET')]
);
// 2. Create a pay-in (generates a TRON deposit address for your customer)
$payment = $client->payins->create([
'amount' => '100.00',
'externalRef' => 'order_123',
'expiresInSeconds' => 3600,
'successUrl' => 'https://yoursite.com/payment/success',
]);
// Send $payment['paymentUrl'] or $payment['address'] to your customer
echo "Deposit address: " . $payment['address'] . "\n";
echo "Payment page: " . $payment['paymentUrl'] . "\n";
// 3. Handle webhook events (in your webhook endpoint file)
$rawBody = file_get_contents('php://input');
$sigHeader = $_SERVER['HTTP_ZYNDPAY_SIGNATURE'];
$event = $client->webhooks->verify($rawBody, $sigHeader);
switch ($event['event']) {
case 'payin.confirmed':
$data = $event['data'];
echo "Payment confirmed: {$data['amount']} USDT — {$data['externalRef']}\n";
// Fulfill the order, unlock access, send receipt, etc.
break;
case 'payin.expired':
echo "Payment expired: " . $event['data']['externalRef'] . "\n";
break;
}
http_response_code(200);
echo json_encode(['received' => true]);
// 4. Check payment status anytime
$status = $client->payins->get($payment['transactionId']);
echo "Status: " . $status['status'] . "\n"; // AWAITING_PAYMENT → CONFIRMING → CONFIRMEDImplémentation PHP brute sans le SDK. Produit la même vérification HMAC-SHA256 que les exemples Node.js et Python ci-dessus.
<?php
// IMPORTANT: read raw body before any JSON parsing
$payload = file_get_contents('php://input');
$sigHeader = $_SERVER['HTTP_ZYNDPAY_SIGNATURE'] ?? '';
function verifyWebhook(string $payload, string $sigHeader, string $secret): bool {
$parts = [];
foreach (explode(',', $sigHeader) as $part) {
[$k, $v] = explode('=', $part, 2);
$parts[$k] = $v;
}
if (empty($parts['t']) || empty($parts['v1'])) {
return false;
}
// Reject events older than 5 minutes
if (abs(time() - (int) $parts['t']) > 300) {
return false;
}
$expected = hash_hmac('sha256', $parts['t'] . '.' . $payload, $secret);
// Timing-safe comparison to prevent timing attacks
return hash_equals($expected, $parts['v1']);
}
if (!verifyWebhook($payload, $sigHeader, getenv('ZYNDPAY_WEBHOOK_SECRET'))) {
http_response_code(400);
echo 'Invalid signature';
exit;
}
$event = json_decode($payload, true);
// Handle $event['event'] ...New Resources (v1.5.0+)
As of SDK v1.5.0, the PHP SDK supports: wallets->list(), fiatDestinations->create(), conversions->convertBetweenWallets(), bulkPayments workflows, transactions->export(), and balances->getAll().
$estimate = $zyndpay->payouts->estimate([
'amount' => '100.00',
'destinationAddress' => 'TXyz1234567890abcdef1234567890abcde',
]);
echo "Fee: " . $estimate['fee'] . " USDT\n";
echo "Total: " . $estimate['totalDebited'] . " USDT\n";Plugin WooCommerce
Acceptez des paiements USDT dans votre boutique WordPress / WooCommerce sans écrire une seule ligne de code. Le plugin génère des adresses de dépôt, surveille le statut de confirmation et exécute automatiquement les commandes.
Téléchargez le ZIP du plugin depuis le dépôt GitHub ZyndPay.
Dans l'administration WordPress, allez dans Extensions → Ajouter → Téléverser une extension.
Téléversez le ZIP et cliquez sur Activer.
Naviguez vers WooCommerce → Réglages → Paiements → ZyndPay.
Entrez votre clé API et votre secret webhook, puis sauvegardez.
Vue d'ensemble
Le sandbox vous permet de tester toute votre intégration sans toucher à de vrais fonds ni au réseau TRON. Utilisez votre clé <ic>zyp_test_sk_...</ic> — l'API est identique à la production.
Guide de Test
Suivez ces étapes pour tester votre flux de paiement complet de bout en bout avant de passer en production.
# Using the sandbox API key (zyp_test_sk_...)
curl -X POST "https://api.zyndpay.io/v1/payments?sandbox=true" \
-H "X-Api-Key: zyp_test_sk_..." \
-H "Content-Type: application/json" \
-d '{
"amount": "100",
"externalRef": "test_order_001"
}'import { ZyndPay } from '@zyndpay/sdk';
const zyndpay = new ZyndPay({ apiKey: 'zyp_test_sk_...' });
// 1. Create a sandbox payin
const payin = await zyndpay.payins.create({
amount: '100',
sandbox: true,
externalRef: 'test_order_001',
});
// 2. Instantly simulate confirmation (fires the payin.confirmed webhook)
// simulate() returns an ack { message, transactionId }, not the payin —
// fetch the post-confirmation state with get().
await zyndpay.payins.simulate(payin.transactionId);
// 3. Read the final status
const tx = await zyndpay.payins.get(payin.transactionId);
console.log('Final status:', tx.status); // "CONFIRMED"# Reset all sandbox transactions (useful for clean test runs)
curl -X POST https://api.zyndpay.io/v1/sandbox/reset \
-H "X-Api-Key: zyp_test_sk_..."Simuler un Paiement
/v1/sandbox/payments/:id/simulateConfirme instantanément un encaissement sandbox, déclenche le webhook <ic>payin.confirmed</ic> et crédite le solde sandbox — exactement comme une vraie confirmation on-chain.
curl -X POST \
https://api.zyndpay.io/v1/sandbox/payments/{id}/simulate \
-H "X-Api-Key: zyp_test_sk_..."{
"success": true,
"data": {
"message": "Payin simulation triggered",
"transactionId": "f4b2cb0f-08ce-4408-88d4-0a678ca0aae2"
}
}Codes d'Erreur
Toutes les erreurs retournent la même enveloppe JSON. Utilisez le champ <ic>code</ic> pour gérer les erreurs par programmation — le champ <ic>message</ic> est lisible par l'humain et peut changer entre les versions.
{
"success": false,
"error": {
"code": "AMOUNT_TOO_LOW",
"message": "Minimum payin amount is 1 USDT"
},
"statusCode": 400
}| Code | Statut HTTP | Requis | Description |
|---|---|---|---|
UNAUTHORIZED | 401 | requis | Clé API manquante ou invalide. Vérifiez que vous envoyez l'en-tête X-Api-Key et que la clé existe dans votre tableau de bord. |
FORBIDDEN | 403 | requis | La clé API n'a pas le scope requis pour cette opération (<ic>INSUFFICIENT_SCOPE</ic>). Utilisez une clé secrète (sk) et non une clé publiable (pk), et vérifiez que la clé a été créée avec le scope requis (ex. <ic>wallets_write</ic> pour les endpoints de liste blanche). |
INVALID_API_KEY | 401 | requis | The provided API key is invalid or has been revoked. |
INSUFFICIENT_SCOPE | 403 | requis | The API key does not have permission for this action. |
TOTP_REQUIRED | 403 | requis | Two-factor authentication is required to perform this action. |
TOTP_INVALID | 400 | requis | The provided TOTP code is invalid or expired. |
BACKUP_CODE_INVALID | 400 | requis | The backup code provided is invalid. |
EMAIL_UNVERIFIED | 403 | requis | Account email address has not been verified. |
ACCOUNT_CLOSED | 403 | requis | This merchant account has been closed. |
REGISTRATION_BLOCKED | 403 | requis | New registrations are currently blocked for this account. |
SANDBOX_KEY_LIVE_REQUEST | 400 | requis | Vous avez envoyé une clé sandbox mais la ressource est une transaction live. Utilisez une clé zyp_live_sk_... pour les requêtes de production. |
LIVE_KEY_SANDBOX_REQUEST | 400 | requis | Vous avez envoyé sandbox: true avec une clé API live. Utilisez une clé zyp_test_sk_... pour les requêtes sandbox. |
IP_ALLOWLIST_SELF_LOCKOUT | 400 | requis | This change would lock out your own IP address. |
MERCHANT_NOT_FOUND | 404 | requis | The specified merchant was not found. |
MERCHANT_STATUS_INVALID | 403 | requis | The merchant account is not in a valid state for this action. |
KYB_REQUIRED | 403 | requis | Cette opération nécessite une vérification KYB (Know Your Business) complète. Complétez le KYB dans votre tableau de bord pour débloquer les limites complètes. |
RATE_LIMIT_EXCEEDED | 429 | requis | Trop de requêtes. Consultez l'en-tête de réponse Retry-After pour connaître le nombre de secondes à attendre avant de réessayer. |
RATE_LIMITED | 429 | requis | Too many requests. Please slow down. |
MERCHANT_LIMIT_EXCEEDED | 403 | requis | Merchant-level transaction limit has been exceeded. |
MONTHLY_LIMIT_EXCEEDED | 403 | requis | Limite mensuelle atteinte. Réinitialisation le 1er du mois suivant. |
BALANCE_CAP_REACHED | 403 | requis | Wallet balance cap has been reached. |
DAILY_CAP_EXCEEDED | 403 | requis | Daily transaction cap has been exceeded. |
DAILY_CONVERSION_LIMIT | 403 | requis | Daily conversion volume limit reached. |
DAILY_CONVERSION_COUNT_LIMIT | 403 | requis | Daily conversion count limit reached. |
LIMIT_EXCEEDED_PAYLINKS | 403 | requis | Maximum number of paylinks for this plan has been reached. |
LIMIT_EXCEEDED_WEBHOOKS | 403 | requis | Maximum number of webhook endpoints has been reached. |
LIMIT_EXCEEDED_API_KEYS | 403 | requis | Maximum number of API keys has been reached. |
LIMIT_EXCEEDED_TEAM_MEMBERS | 403 | requis | Maximum number of team members has been reached. |
LIMIT_EXCEEDED_BULK_BATCH | 403 | requis | Maximum number of items in a bulk batch has been reached. |
INVALID_ADDRESS | 400 | requis | L'adresse de portefeuille TRON est mal formée. Les adresses doivent commencer par T et faire 34 caractères au format base58. |
ADDRESS_IN_USE | 409 | requis | This address is already in use by another transaction. |
ADDRESS_NOT_FOUND | 404 | requis | Le whitelistAddressId ne correspond à aucune adresse de retrait enregistrée sur votre compte. Ajoutez des adresses dans votre tableau de bord sous Adresses de Retrait. |
NO_WHITELISTED_ADDRESS | 400 | requis | No whitelisted withdrawal address found for this merchant. |
NO_PAYOUT_ADDRESS | 400 | requis | No payout address configured for this merchant. |
ADDRESS_NOT_WHITELISTED | 403 | requis | L'adresse de destination n'est pas sur votre liste blanche de marchand. Ajoutez-la dans votre tableau de bord avant d'y envoyer des fonds. |
ADDRESS_COOLDOWN | 403 | requis | This address is in a cooldown period and cannot be used yet. |
ADDRESS_REMOVAL_DISABLED | 410 | requis | Saved crypto recipients cannot be deleted. |
WHITELIST_VALIDATION_FAILED | 400 | requis | Address whitelist validation failed. |
INVALID_CONTEXT | 400 | requis | Invalid context provided for this operation. |
WALLET_NOT_FOUND | 404 | requis | The specified wallet was not found. |
WALLET_DIRECT_WITHDRAW_DISABLED | 403 | requis | Direct withdrawal from this wallet type is disabled. |
INSUFFICIENT_BALANCE | 400 | requis | Votre solde USDT est insuffisant pour le montant de paiement sortant ou retrait demandé. Vérifiez votre solde via GET /v1/wallets/balance. |
AMOUNT_TOO_SMALL | 400 | requis | Le montant est inférieur au minimum requis pour cette opération (5 USDT pour les encaissements, 5 USDT pour les paiements sortants et retraits). |
AMOUNT_TOO_LARGE | 400 | requis | Le montant dépasse le maximum autorisé pour cette opération ou votre niveau de conformité actuel. |
INVALID_AMOUNT | 400 | requis | The amount provided is not a valid number. |
INVALID_TRANSACTION_TYPE | 400 | requis | The transaction type specified is not valid. |
INVALID_TRANSACTION_STATUS | 400 | requis | L'opération n'est pas autorisée dans le statut actuel de la transaction (ex. tenter d'annuler un paiement sortant confirmé). |
REFUND_WINDOW_EXPIRED | 400 | requis | The refund window for this transaction has expired. |
REFUND_EXCEEDS_AMOUNT | 400 | requis | Refund amount exceeds the original transaction amount. |
REASON_NOTE_REQUIRED | 400 | requis | A reason note is required for this action. |
CANNOT_CANCEL | 400 | requis | Cette ressource ne peut plus être annulée (elle a déjà été approuvée, diffusée ou complétée). |
DUPLICATE_EXTERNAL_REF | 409 | requis | Une transaction avec cet externalRef existe déjà pour votre compte. Utilisez une référence unique par requête, ou omettez-la. |
MISSING_IDEMPOTENCY_KEY | 400 | requis | Cet endpoint nécessite un en-tête Idempotency-Key. Passez un UUID unique par requête. |
IDEMPOTENCY_KEY_INVALID | 400 | requis | The Idempotency-Key header is malformed. |
IDEMPOTENCY_KEY_MISMATCH | 409 | requis | Une requête existante avec la même Idempotency-Key a des paramètres différents. Utilisez une nouvelle clé pour une requête différente. |
CONFIG_MISSING | 500 | requis | Required system configuration is missing. |
AML_BLOCKED | 403 | requis | Cette transaction a été signalée par notre moteur de filtrage AML et ne peut pas être traitée. Contactez [email protected]. |
AML_SCREENING_UNAVAILABLE | 503 | requis | AML screening service is temporarily unavailable. |
COMPLIANCE_LIMIT_REACHED | 403 | requis | Votre volume de transactions mensuel a atteint la limite de conformité de votre niveau actuel. Complétez le KYB pour augmenter vos limites. |
CONVERSION_NOT_ALLOWED | 403 | requis | Conversion between these currencies is not permitted. |
RATE_LOCK_EXPIRED | 400 | requis | The FX rate lock has expired. Request a new rate. |
RATE_LOCK_NOT_FOUND | 404 | requis | The specified rate lock was not found. |
RATE_LOCK_ALREADY_USED | 409 | requis | This rate lock has already been used. |
RATE_UNAVAILABLE | 503 | requis | Exchange rate is currently unavailable. Try again shortly. |
RATE_STALE | 400 | requis | The exchange rate is stale. Refresh and try again. |
FX_UNAVAILABLE | 503 | requis | FX conversion service is temporarily unavailable. |
INVALID_CONVERSION_PAIR | 400 | requis | The specified currency conversion pair is not supported. |
NEGATIVE_REVENUE | 400 | requis | This conversion would result in negative revenue. |
MISSING_PHONE | 400 | requis | Customer phone number is required for this payment method. |
MISSING_OPERATOR | 400 | requis | Mobile money operator code is required. |
MISSING_BANK_DETAILS | 400 | requis | Bank account details are required. |
MISSING_MOBILE_MONEY_FIELDS | 400 | requis | Required mobile money fields are missing. |
MISSING_BANK_FIELDS | 400 | requis | Required bank transfer fields are missing. |
FIAT_DESTINATION_REQUIRED | 400 | requis | A fiat destination must be configured before withdrawing. |
FIAT_DESTINATION_NOT_FOUND | 404 | requis | The specified fiat destination was not found. |
FIAT_DESTINATION_INVALID | 400 | requis | The fiat destination configuration is invalid. |
REFUND_RAIL_NOT_AVAILABLE | 400 | requis | Refund is not available via the original payment rail. |
WITHDRAWAL_NOT_FIAT | 400 | requis | This operation requires a fiat withdrawal. |
WITHDRAWAL_APPROVE_NOT_SUPPORTED_FOR_FIAT | 400 | requis | Fiat withdrawals cannot be approved via the standard approve endpoint — they would be routed to the on-chain TRON broadcast path. Use the complete-fiat endpoint with a providerRef once the bank wire has been executed. |
WITHDRAWAL_NOT_PENDING_REVIEW | 400 | requis | The withdrawal is not in PENDING_REVIEW status. |
BENEFICIARY_REQUIRED | 400 | requis | A beneficiary must be specified for this payout. |
BENEFICIARY_INVALID | 400 | requis | The beneficiary details are invalid. |
BENEFICIARY_NOT_FOUND | 404 | requis | The specified beneficiary was not found. |
BENEFICIARY_ALREADY_EXISTS | 409 | requis | A beneficiary with these details already exists. |
BENEFICIARY_IN_USE | 409 | requis | This beneficiary is in use by an active transaction. |
BENEFICIARY_REJECTED | 403 | requis | This beneficiary has been rejected by compliance. |
BENEFICIARY_COOLDOWN | 403 | requis | This beneficiary is in a cooldown period. |
BENEFICIARY_NOT_VERIFIED | 403 | requis | This beneficiary has not been verified yet. |
SELF_OWNED_REQUIRED | 400 | requis | A self-owned beneficiary is required for this operation. |
MAX_BENEFICIARIES_REACHED | 403 | requis | Maximum number of beneficiaries has been reached. |
THIRD_PARTY_NOT_ALLOWED | 403 | requis | Third-party beneficiaries are not allowed for this operation. |
CARD_PAYMENTS_DISABLED | 403 | requis | Card payments are not enabled for this merchant. |
MOBILE_MONEY_PAYMENTS_DISABLED | 403 | requis | Mobile money payments are not enabled for this merchant. |
USDT_PAYMENTS_DISABLED | 403 | requis | USDT payments are not enabled for this merchant. |
NO_METHODS_ENABLED | 403 | requis | No payment methods are enabled for this paylink. |
FEE_NOT_CONFIGURED | 500 | requis | Fee configuration is missing for this payment method. |
PAYMENT_METHOD_NOT_ACCEPTED | 400 | requis | The specified payment method is not accepted for this transaction. |
PROVIDER_INITIATE_FAILED | 502 | requis | The payment provider failed to initiate the transaction. |
PROVIDER_MISSING_URL | 502 | requis | The payment provider did not return a redirect URL. |
OPERATOR_NOT_SUPPORTED | 400 | requis | The mobile money operator is not supported in this region. |
INVALID_STATE | 400 | requis | The transaction is not in the correct state for this action. |
OTP_INVALID | 400 | requis | The OTP code provided is incorrect. |
OTP_EXPIRED | 400 | requis | The OTP code has expired. Request a new one. |
OTP_NOT_APPLICABLE | 400 | requis | OTP submission is not applicable for this transaction. |
MOMO_PROVIDER_ERROR | 502 | requis | The mobile money provider returned an error. |
CUSTOMER_PHONE_REQUIRED | 400 | requis | Customer phone number is required for mobile money payments. |
CANNOT_CHANGE_CURRENCY_AFTER_PRODUCTS | 400 | requis | Currency cannot be changed after products have been added. |
PAYLINK_EMPTY | 400 | requis | The paylink has no products configured. |
MARKETPLACE_DISABLED | 403 | requis | Marketplace / Connect features are not enabled for this account. |
NOT_A_PLATFORM_MERCHANT | 403 | requis | This merchant is not a platform merchant. |
SPLIT_RULE_NOT_FOUND | 404 | requis | The specified split rule was not found. |
SPLIT_RULE_IN_USE | 409 | requis | This split rule is in use and cannot be deleted. |
SPLIT_RULE_INVALID_BPS_SUM | 400 | requis | Split rule basis points do not sum to 10000. |
SPLIT_RULE_MISSING_ZYNDPAY_RECIPIENT | 400 | requis | Split rule is missing the ZyndPay fee recipient. |
SPLIT_RULE_MISSING_PLATFORM_RECIPIENT | 400 | requis | Split rule is missing the platform merchant recipient. |
SPLIT_RULE_MISSING_SUB_MERCHANT_RECIPIENT | 400 | requis | Split rule is missing the sub-merchant recipient. |
SPLIT_RULE_INVALID_ZYNDPAY_BPS | 400 | requis | ZyndPay fee basis points in this split rule are below minimum. |
SUB_MERCHANT_NOT_CONNECTED | 403 | requis | This sub-merchant is not connected to your platform. |
SUB_MERCHANT_ALREADY_CONNECTED | 409 | requis | This sub-merchant is already connected to a platform. |
SUB_MERCHANT_KYB_REQUIRED | 403 | requis | The sub-merchant must complete KYB before this action. |
SUB_MERCHANT_SUSPENDED | 403 | requis | The sub-merchant account is suspended. |
SUB_MERCHANT_AML_FLAGGED | 403 | requis | The sub-merchant has been flagged by AML screening. |
SUB_MERCHANT_INVITATION_NOT_FOUND | 404 | requis | The sub-merchant invitation was not found. |
SUB_MERCHANT_INVITATION_EXPIRED | 400 | requis | The sub-merchant invitation has expired. |
SUB_MERCHANT_INVITATION_ALREADY_USED | 409 | requis | This invitation has already been accepted. |
SUB_MERCHANT_HAS_PENDING_BALANCE | 409 | requis | The sub-merchant has a pending balance that must be settled first. |
SPLIT_PAYMENT_NOT_FOUND | 404 | requis | The specified split payment was not found. |
SPLIT_PAYMENT_ALREADY_REVERSED | 409 | requis | This split payment has already been reversed. |
IMPORT_VALIDATION_FAILED | 400 | requis | One or more items in the bulk import failed validation. |
VALIDATION_ERROR | 400 | requis | Le corps de la requête a échoué à la validation. Le champ details liste chaque champ invalide et la raison. |
NOT_FOUND | 404 | requis | La ressource demandée n'existe pas ou n'appartient pas à votre compte marchand. |
DUPLICATE_RESOURCE | 409 | requis | Une ressource avec cette valeur unique existe déjà (ex. email, slug ou champ en doublon). |
CONFLICT | 409 | requis | A conflict occurred with an existing resource. |
BAD_REQUEST | 400 | requis | The request was malformed or contained invalid parameters. |
INVALID_WEBHOOK_SIGNATURE | 400 | requis | La vérification de signature webhook a échoué. Vérifiez votre secret webhook et que vous utilisez le corps brut de la requête. |
INTERNAL_ERROR | 500 | requis | Erreur serveur inattendue. Incluez le requestId de la réponse lorsque vous contactez [email protected]. |
SERVICE_UNAVAILABLE | 503 | requis | The service is temporarily unavailable. Try again shortly. |
Limites de Taux
Les limites sont appliquées par clé API. Les requêtes dépassant les limites reçoivent une réponse <ic>429</ic> avec un en-tête <ic>Retry-After</ic>.
| Point de terminaison | Limite de débit | Requis | Description |
|---|---|---|---|
Default | 100/60s | requis | Limite globale par clé API. |
POST /payments | 30/60s | requis | Création d'encaissement (POST /payments) par marchand. |
POST /payout | 10/60s | requis | Création de paiement sortant (POST /payout) par marchand. |
POST /withdrawals | 5/60s | requis | Demandes de retrait par marchand. |