# Prompt d’intégration SYLI Payments — API paiement crypto v1

> **Usage.** Copiez **tout ce fichier** dans Cursor, Claude, ChatGPT, Copilot ou tout agent de code.  
> L’agent doit implémenter une intégration **complète et production** de SYLI Payments, sans inventer d’endpoints ni de champs.

---

## 0. Identité du produit

Tu intègres **SYLI Payments**, passerelle de paiement crypto (marque blanche).

- Site : `https://sylipayments.com`
- API : `https://sylipayments.com/api/v1`
- Checkout hébergé : `https://sylipayments.com/pay/{invoice_id}`
- Docs HTML : `https://sylipayments.com/docs/api`
- Laboratoire (requêtes live) : `https://sylipayments.com/docs/lab`
- Plugins : `https://sylipayments.com/plugins`

**Règles de marque (non négociables) :**

- Présente uniquement **SYLI Payments** comme produit de paiement.
- N’invente pas de sous-traitant, processeur, agrégateur tiers ou marque rail.
- N’expose jamais de clés `syli_live_…`, secrets webhook, ni payloads signés dans le front public, les logs client, ou un dépôt git.

Langue UI marchand par défaut : **français**. Les noms de champs API restent en anglais.

---

## 1. Mission

Intègre SYLI Payments dans **ce projet** de bout en bout :

1. Créer un paiement (checkout hébergé **ou** API directe avec adresse + QR).
2. Rediriger / afficher le paiement au client.
3. Recevoir et **vérifier** les webhooks HMAC.
4. Marquer la commande **payée uniquement** quand `payment_status === "confirmed"` (après signature valide). Accepter aussi `finished` / `sending` par compatibilité (plus renvoyés).
5. Gérer erreurs, idempotence, expirations, paiements partiels, XRP/Monero (`payin_extra_id`). Pour l’API directe : n’afficher que les cryptos de `GET /currencies` (clé API).

Avant de coder : inspecte le repo (stack, panier, commandes, auth). Choisis le chemin d’intégration le plus naturel, puis implémente-le **entièrement**, pas un squelette.

Si une info manque (clé API, URL HTTPS, stack), demande-la. N’invente pas de credentials.

---

## 2. Prérequis marchand (à vérifier avec l’utilisateur)

Sans ces étapes, l’API répond `403` (KYC) ou `400` (wallet) :

1. Compte sur `https://sylipayments.com/signup`
2. KYC validé : Dashboard → Profil (`/dashboard/profile`)
3. Wallets de réception : Dashboard → Réception (`/dashboard/reception`) — une adresse **validée** par crypto
4. Clé API + secret webhook : Dashboard → Configuration → API paiement (`/dashboard/configuration/api`)
5. URL webhook HTTPS publique (pas `localhost` en production)

Variables d’environnement **côté serveur uniquement** :

```
SYLI_API_KEY=syli_live_…
SYLI_IPN_SECRET=…          # secret webhook (64 hex), pas la clé API
SYLI_API_URL=https://sylipayments.com/api/v1
SYLI_SUCCESS_URL=https://VOTRE-SITE/paiement/ok
SYLI_CANCEL_URL=https://VOTRE-SITE/paiement/annuler
SYLI_WEBHOOK_URL=https://VOTRE-SITE/api/webhooks/syli
```

---

## 3. Choisir le mode d’intégration

| Mode | Quand l’utiliser | Flux |
|---|---|---|
| **A. Checkout hébergé** (recommandé) | Boutique, SaaS, facture, plugin | `POST /invoice` → rediriger vers `invoice_url` |
| **B. API directe** | App custom, QR in-app, kiosque | `POST /payment` avec `pay_currency` → afficher `pay_address` (+ QR) et `payin_extra_id` si présent |
| **C. Plugin boutique** | WooCommerce, PrestaShop, Wix, Shopify | ZIP officiel, puis clé + secret |
| **D. Lien dashboard** | Sans code | Dashboard → Liens de paiement |

**Par défaut, implémente le mode A** (checkout) + webhook. Ajoute le mode B si le projet affiche déjà un écran de paiement custom.

Les deux modes peuvent coexister. Les webhooks sont **obligatoires** dans tous les cas : ne te fie pas seulement au retour navigateur (`success_url` n’est pas une preuve de paiement).

---

## 4. Authentification API

Toutes les routes `/api/v1/*` **sauf** `/status`, `/estimate` et `/min-amount` exigent la clé.

`GET /currencies` **exige la clé** : uniquement les cryptos du compte (adresse validée dans Dashboard → Réception). Sans wallet validé, la liste est vide et `POST /payment` / `POST /invoice` répondent 400.

Un de ces en-têtes (le premier trouvé l’emporte) :

```
x-api-key: syli_live_xxxxxxxx
x-syli-api-key: syli_live_xxxxxxxx
Authorization: Bearer syli_live_xxxxxxxx
```

Format : `syli_live_` + secret. Régénérer la clé dans le dashboard **invalide** l’ancienne.

`Content-Type: application/json` sur les POST.

---

## 5. Référence API (ne pas inventer d’autres routes)

Base : `https://sylipayments.com/api/v1`

Succès : **objet JSON à la racine** (pas de wrapper `{ data: … }`).  
Erreur :

```json
{ "status": false, "message": "…", "error": "…" }
```

| HTTP | Cas |
|---|---|
| 400 | Corps invalide, crypto non supportée, pas de wallet, montant sous le minimum |
| 403 | KYC non vérifié, ou compte désactivé |
| 404 | Paiement / facture introuvable |
| 410 | Lien de paiement expiré |
| 503 | Service indisponible — **y compris** clé API absente ou invalide. Message public : `Service indisponible. Réessayez plus tard.` |

Une clé API manquante ou fausse ne doit **jamais** être expliquée à l’acheteur (pas de « clé API », « 401 », « unauthorized »). Logger le détail **côté serveur uniquement**.

### 5.1 `GET /status` (public)

Santé. Réponse : `{ "message": "OK" }`.

### 5.2 `GET /currencies` (auth)

Uniquement les cryptos du compte (adresse **validée** dans Dashboard → Réception). Au moins une crypto doit être configurée pour encaisser.

Réponse :

```json
{
  "currencies": ["usdttrc20"],
  "accepted": [{ "code": "usdttrc20", "label": "Tether USDT (TRC-20)", "ticker": "USDT", "network": "Tron" }],
  "available": [{ "code": "usdttrc20", "label": "Tether USDT (TRC-20)", "ticker": "USDT", "network": "Tron" }]
}
```

`available` = `accepted` (rétrocompat). **Mode B (sans checkout)** : le sélecteur client = `currencies`. Une crypto hors liste est refusée (`Cette crypto n’est pas activée pour ce marchand`). Liste vide : `Aucun wallet de réception validé sur SYLI Payments…`.

### 5.3 `GET /estimate?amount=&currency_from=&currency_to=` (public)

Estimation du montant crypto à envoyer.

```json
{
  "currency_from": "usdt",
  "amount_from": 20,
  "currency_to": "btc",
  "estimated_amount": 0.00018
}
```

### 5.4 `GET /min-amount?currency_from=&currency_to=` (public)

`currency_to` optionnel (défaut = `currency_from`).

```json
{
  "currency_from": "usdt",
  "currency_to": "btc",
  "min_amount": 1,
  "processor_min_amount": 14.5,
  "fee_usd": 0.22,
  "rule": "syli"
}
```

Minimum SYLI Payments : `max(1 USDT, 4 × frais réseau estimés)` pour **toutes** les cryptos (`rule: "syli"`). `processor_min_amount` est informatif. Un payin ≥ 99,5 % du `pay_amount` marchand est traité comme couvert. Avant `createPayment` / `createInvoice`, refusez un montant trop bas.

### 5.5 `POST /invoice` — checkout hébergé (auth)

**Requis :** `price_amount` (number > 0), `price_currency`  
**Optionnel :** `pay_currency`, `order_id`, `order_description`, `ipn_callback_url`, `success_url`, `cancel_url`

Si `pay_currency` est omis, le client choisit parmi les cryptos du compte (wallets validés). S’il est fourni, il doit figurer dans `GET /currencies`. Aucun wallet validé → 400.

Réponse (champs utiles) :

```json
{
  "id": "2d6f88d7-5b40-4d9e-b0a4-0793953d2707",
  "order_id": "CMD-9001",
  "order_description": "Commande boutique",
  "price_amount": 49.9,
  "price_currency": "usdt",
  "pay_currency": null,
  "ipn_callback_url": "https://boutique.example/webhooks/syli",
  "success_url": "https://boutique.example/ok",
  "cancel_url": "https://boutique.example/annuler",
  "created_at": "2026-09-07T12:00:00.000Z",
  "invoice_url": "https://sylipayments.com/pay/2d6f88d7-5b40-4d9e-b0a4-0793953d2707"
}
```

**Action :** `302` / redirect navigateur vers `invoice_url`. Persiste `id` (invoice) + `order_id` en base **avant** la redirection.

### 5.6 `GET /invoice/:id` (auth)

Relit la facture. Même objet.

### 5.7 `POST /payment` — adresse + QR (auth)

**Requis :** `price_amount`, `price_currency`, `pay_currency`  
**Optionnel :** `order_id`, `order_description`, `ipn_callback_url`

`pay_currency` **doit** figurer dans `GET /currencies`. N’utilise pas le catalogue plateforme.

Idempotence : si un paiement **actif** existe déjà pour le même `order_id`, SYLI Payments le **renvoie** (ne crée pas un doublon). Utilise un `order_id` stable et unique par commande.

Réponse (champs) :

```
payment_id, payment_status, pay_address, payin_extra_id,
price_amount, price_currency, pay_amount, actually_paid, pay_currency,
order_id, order_description, invoice_id,
outcome_amount, outcome_currency, payout_address, payout_amount,
payout_status, payout_hash, payin_hash,
created_at, updated_at, expiration_estimate_date, rate_locked_at
```

**UI obligatoire :**

- Afficher `pay_address` (et QR de cette adresse).
- Si `payin_extra_id` n’est pas null : l’afficher **très visiblement**.
  - `xrp` : destination tag
  - `xmr` : payment ID
- Afficher `pay_amount` + `pay_currency`, et `expiration_estimate_date` s’il est présent (taux gelé).
- Polling `GET /payment/:id` toutes les 5–10 s **en plus** du webhook, jusqu’à `confirmed` / `expired` / `failed` / `refunded`.

### 5.8 `GET /payment/:id` (auth)

Resynchronise le statut on-chain, puis renvoie le même objet que POST.

---

## 6. Prix et cryptos

`price_currency` : toujours **`usdt`**. `usd` est accepté et renvoyé comme `usdt`.

`pay_currency` (codes canoniques) :

| Code | Réseau | Notes |
|---|---|---|
| `btc` | Bitcoin | Taux gelé jusqu’à `expiration_estimate_date` |
| `eth` | Ethereum | Taux gelé |
| `usdterc20` | USDT Ethereum | ~1:1 |
| `usdttrc20` | USDT Tron | ~1:1 |
| `usdtbsc` | USDT BNB Smart Chain | ~1:1 |
| `usdcerc20` | USDC Ethereum | ~1:1 |
| `sol` | Solana | Taux gelé |
| `bnbbsc` | BNB Smart Chain | Taux gelé |
| `xrp` | XRP Ledger | `payin_extra_id` = destination tag |
| `xmr` | Monero | `payin_extra_id` = payment ID si présent |

Alias acceptés par l’API : `usdtbep20` / `usdtbep` → `usdtbsc` ; `usdc` / `usdce` → `usdcerc20` ; `bnb` / `bnbsc` → `bnbbsc` ; `solana` → `sol` ; `ripple` → `xrp` ; `monero` → `xmr`.

**Toujours envoyer les codes canoniques** dans ton code.

---

## 7. Webhooks IPN (critique)

SYLI Payments envoie `POST` JSON vers `ipn_callback_url` du paiement/facture, sinon l’URL par défaut du dashboard.

| | |
|---|---|
| Header | `x-syli-sig` |
| Algo | HMAC-SHA512, digest **hex** |
| Corps signé | **Objet JSON parsé**, clés triées : `JSON.stringify(payload, Object.keys(payload).sort())` |
| Secret | `SYLI_IPN_SECRET` (dashboard), **pas** la clé API |
| Succès | Répondre **2xx** (idéalement 200) **après** traitement idempotent |
| Relances | Jusqu’à 8 tentatives (1, 5, 15, 60, 180, 360, 720, 1440 min) si tu ne réponds pas 2xx |

**Ne signe jamais le body HTTP brut** (espaces, ordre des clés). Parse le JSON, trie les clés, puis HMAC. Compare en timing-safe.

Champs payload :

```
payment_id, invoice_id, payment_status, pay_address,
price_amount, price_currency, pay_amount, actually_paid, pay_currency,
order_id, order_description,
outcome_amount, outcome_currency,
payout_amount, payout_hash, payin_hash, payout_status
```

### Node.js (SDK — préféré)

```js
import { Syli, SyliError } from "syli-sdk";

const syli = new Syli({ apiKey: process.env.SYLI_API_KEY });

app.post("/api/webhooks/syli", express.json(), (req, res) => {
  try {
    const event = syli.constructEvent(
      req.body,
      req.headers["x-syli-sig"],
      process.env.SYLI_IPN_SECRET,
    );
    if (["confirmed", "finished", "sending"].includes(event.payment_status)) {
      // marquer event.order_id / event.payment_id comme payé — idempotent
      // L’API renvoie `confirmed` dès que le client a payé. `payout_status` / `payout_hash` = versement wallet.
    }
    res.sendStatus(200);
  } catch (err) {
    const status = err instanceof SyliError ? err.status || 400 : 400;
    res.sendStatus(status);
  }
});
```

### Python

```python
import hmac, hashlib, json

def verify_syli_sig(payload: dict, secret: str, signature: str) -> bool:
    canonical = json.dumps(payload, separators=(",", ":"), sort_keys=True)
    expected = hmac.new(secret.encode(), canonical.encode(), hashlib.sha512).hexdigest()
    return hmac.compare_digest(expected, signature.lower())
```

Règles métier webhook :

- Vérifie la signature **en premier**. 401 si invalide.
- Idempotence : un même `payment_id` + `payment_status` peut arriver plusieurs fois. Ne double-livre pas.
- Livrer / activer le service **seulement** si `payment_status === "confirmed"` (signature OK). Traite aussi `finished` / `sending` comme payé (anciens événements / plugins).
- `payment_status` décrit le **payin client**, pas le versement vers le wallet. Le versement = `payout_status` / `payout_hash`. N’attends pas `finished` : l’API ne le renvoie plus.
- `partially_paid` : montant inférieur au montant marchand. Ne livre pas. Affiche un sous-paiement. (SYLI Payments peut traiter comme payé un payin qui couvre ~99,5 % du montant marchand — fie-toi au statut renvoyé, pas à un calcul maison.)
- `expired` / `failed` / `refunded` : annule ou flag la commande, jamais de livraison.
- Réponds 2xx même si la commande est inconnue **après** log (évite une boucle de retries infinie), sauf signature invalide.

---

## 8. Statuts `payment_status`

| Valeur | Sens | Action boutique |
|---|---|---|
| `waiting` | En attente du virement client | Afficher QR / checkout |
| `confirming` | Vu on-chain, confirmations | « Confirmation en cours » |
| `confirmed` | Le client a payé le montant attendu | **Livrer / activer** |
| `partially_paid` | Payé en dessous du montant marchand | Alerter, ne pas livrer |
| `expired` | Délai dépassé | Rouvrir un nouveau paiement |
| `failed` | Échec | Échec commande |
| `refunded` | Remboursé | Annuler livraison si besoin |

`sending` / `finished` ne sont **plus** renvoyés (`payment_status` est mappé en `confirmed`). Le versement wallet = `payout_status` / `payout_hash`.

---

## 9. SDK officiel (Node.js ≥ 18)

```bash
npm install syli-sdk
# ou : npm install github:Nimba-Algo-Trading/syli-sdk
```

```js
import { Syli } from "syli-sdk";

const syli = new Syli({
  apiKey: process.env.SYLI_API_KEY,
  // apiUrl: "https://sylipayments.com/api/v1", // défaut
});
```

Méthodes : `createInvoice`, `getInvoice`, `createPayment`, `getPayment`, `getCurrencies`, `getStatus`, `estimate`, `getMinAmount`, `constructEvent`, `verifyWebhook`, `signatureFromHeaders`.

**Serveur uniquement.** Interdit dans un bundle navigateur, une app mobile non protégée, un README public, ou `.env` commité.

PHP : `https://sylipayments.com/downloads/syli-php-sdk.zip` (`createInvoice` + `verifySignature`).

### 9.1 Flutter / Dart

Paquet `syli_sdk` (pur Dart). ZIP : `https://sylipayments.com/downloads/syli-dart-sdk.zip`. Source : `sdk/dart`.

```yaml
dependencies:
  syli_sdk:
    path: packages/syli_sdk
```

```dart
import 'package:syli_sdk/syli_sdk.dart';

final syli = Syli(apiKey: apiKey);
final invoice = await syli.createInvoice(
  priceAmount: 49.9,
  priceCurrency: 'usdt',
  orderId: 'CMD-9001',
  successUrl: 'https://boutique.example/ok',
  cancelUrl: 'https://boutique.example/annuler',
  ipnCallbackUrl: 'https://boutique.example/webhooks/syli',
);
// Ouvrez invoice.invoiceUrl (checkout SYLI Payments). Ne mettez jamais syli_live_… dans l’APK.
```

Méthodes : `createInvoice`, `getInvoice`, `createPayment`, `getPayment`, `getCurrencies`, `getStatus`, `estimate`, `getMinAmount`, `constructEvent`, `verifyWebhook`. Webhook : `constructEvent(body, x-syli-sig, secret)`, livrer si `event.isConfirmed`.

---

## 10. Plugins boutiques (si le projet est une boutique)

Téléchargements :

- WooCommerce : `https://sylipayments.com/downloads/syli-woocommerce.zip`
- PrestaShop 8+ : `https://sylipayments.com/downloads/syli-prestashop.zip`
- Wix (Velo) : `https://sylipayments.com/downloads/syli-wix.zip`
- Shopify (app Node) : `https://sylipayments.com/downloads/syli-shopify.zip`

Configure dans le module : URL API, `syli_live_…`, secret webhook. Ne réécris pas un plugin officiel si un ZIP existe — installe-le, puis branche le webhook / le mapping commandes.

---

## 11. MCP (optionnel, agents IA)

Si l’utilisateur travaille dans Cursor / Claude Desktop :

```json
{
  "mcpServers": {
    "syli": {
      "command": "npx",
      "args": ["-y", "syli-mcp"],
      "env": {
        "SYLI_API_KEY": "syli_live_xxxxxxxx",
        "SYLI_API_URL": "https://sylipayments.com/api/v1",
        "SYLI_IPN_SECRET": "xxxxxxxx",
        "SYLI_IPN_CALLBACK_URL": "https://boutique.example/webhooks/syli"
      }
    }
  }
}
```

Outils : `syli_status`, `syli_list_currencies`, `syli_estimate`, `syli_min_amount`, `syli_create_invoice`, `syli_get_invoice`, `syli_create_payment`, `syli_get_payment`, `syli_verify_webhook`.

---

## 12. Plan d’implémentation (exécute dans l’ordre)

1. **Cartographier** le projet : où naît une commande, où on encaissait avant, où marquer « payé ».
2. **Secrets** : `.env` / `.env.local` (jamais commités) + exemple `.env.example` sans vraies clés.
3. **Module serveur SYLI Payments** : un seul client (SDK ou fetch wrapper) pour invoice/payment/get/webhook.
4. **Création paiement** au clic « Payer » :
   - Mode A : `createInvoice` puis redirect `invoice_url`.
   - Mode B : `createPayment` puis page QR (`pay_address`, `pay_amount`, `payin_extra_id`, expiration).
5. **Persistance** : table ou champs `syli_invoice_id`, `syli_payment_id`, `syli_status`, `order_id`.
6. **Route webhook HTTPS** : parse JSON, `constructEvent` / HMAC, idempotence, maj commande, 2xx.
7. **Page succès / échec** : `success_url` / `cancel_url` = UX seulement. La source de vérité reste le webhook (+ GET payment si besoin).
8. **UX acheteur** : montants en USDT, cryptos du `GET /currencies` authentifié. Erreurs 400 métier (montant, crypto) : message clair. **Jamais** d’info technique (clé API, 401, secret, stack, nom d’endpoint). Si le paiement ne peut pas démarrer (503, clé, config), afficher uniquement : « Service indisponible. Réessayez plus tard. »
9. **Logs serveur** : `payment_id`, `order_id`, statut — jamais la clé API ni le secret IPN.
10. **Tests** : laboratoire `/docs/lab`, puis un paiement test petit montant si le compte est live.

---

## 13. Exemples cURL

Checkout :

```bash
curl -X POST https://sylipayments.com/api/v1/invoice \
  -H "x-api-key: $SYLI_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "price_amount": 49.9,
    "price_currency": "usdt",
    "order_id": "CMD-9001",
    "order_description": "Commande boutique",
    "success_url": "https://boutique.example/ok",
    "cancel_url": "https://boutique.example/annuler",
    "ipn_callback_url": "https://boutique.example/api/webhooks/syli"
  }'
```

Paiement API :

```bash
curl -X POST https://sylipayments.com/api/v1/payment \
  -H "x-api-key: $SYLI_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "price_amount": 20,
    "price_currency": "usdt",
    "pay_currency": "usdttrc20",
    "order_id": "CMD-4821",
    "order_description": "Abonnement mensuel"
  }'
```

---

## 14. Critères d’acceptation

L’intégration n’est **pas finie** tant que tout ceci n’est pas vrai :

- [ ] Aucune clé `syli_live_…` ni secret IPN dans le client, le git, ou les logs publics
- [ ] Un `order_id` unique est envoyé à SYLI Payments et stocké localement
- [ ] Checkout : redirection vers `invoice_url` **SYLI Payments** (domaine `sylipayments.com` ou l’URL renvoyée)
- [ ] API directe : `pay_address` + QR + `payin_extra_id` si présent
- [ ] Webhook vérifie `x-syli-sig` (HMAC-SHA512, clés JSON triées)
- [ ] Commande livrée **seulement** sur `confirmed` + signature OK (`finished` / `sending` acceptés en rétrocompat)
- [ ] API directe : sélecteur = `GET /currencies` (clé) ; au moins une crypto configurée
- [ ] Retries webhook : pas de double livraison
- [ ] Erreurs 403 KYC / 400 wallet affichées clairement au marchand
- [ ] UI et commentaires parlent de **SYLI Payments**, pas d’un autre prestataire
- [ ] `.env.example` documente `SYLI_API_KEY`, `SYLI_IPN_SECRET`, `SYLI_API_URL`

---

## 15. Ce que tu ne dois pas faire

- Inventer des endpoints (`/api/v2`, `/checkout/session`, etc.).
- Confondre clé API et secret webhook.
- Marquer payé sur `success_url` ou sur `waiting`.
- Omettre `payin_extra_id` pour XRP / Monero.
- Hardcoder une liste de cryptos : `GET /currencies` (avec clé) est la source. Les autres codes sont refusés.
- Marquer payé sur `payout_status` au lieu de `payment_status`.
- Committer des secrets.
- Citer un processeur de paiement tiers dans l’UI, les README, ou les commits.

---

## 16. Ressources

- Référence : `https://sylipayments.com/docs/api`
- Lab : `https://sylipayments.com/docs/lab`
- SDK JS : `https://github.com/Nimba-Algo-Trading/syli-sdk`
- SDK Dart : `https://sylipayments.com/downloads/syli-dart-sdk.zip`
- Ce prompt : `https://sylipayments.com/downloads/syli-prompt-integration.md`

Quand tu as compris le repo, **implémente**. Ne t’arrête pas à un README : code le client serveur, la route webhook, le branchement checkout, et les mises à jour de commande.
