Dernière mise à jour : 11 avril 2026
Infrastructure de paiement on-chain pour entreprises et institutions sur le XRP Ledger, construite autour du protocole x402 (HTTP 402 Payment Required). Un marchand protège ses API en une ligne, ses clients paient en XRP/IOU/MPT, et les transactions sont loggées en SQLite avec export ISO 20022.
┌─────────────────────────────────────────────────────────────────────┐
│ Client (AI agent / browser) │
└──────────────────────────────────┬──────────────────────────────────┘
│ 1. GET /resource (no header)
▼
┌─────────────────────────────────────────────────────────────────────┐
│ Resource Server (ed4l02-x402 middleware) │
│ Express / Next.js / Hono / Cloudflare Workers │
│ │
│ app.get('/premium', xrpl402Express({ ...opts }), handler) │
└──────────────────────────────────┬──────────────────────────────────┘
2. 402 + X-Payment-Required │ │ 4. POST /settle
◄──────────────────────────── ▼
┌───────────────────────────────────┐
3. sign tx + X-Payment-Signature │ ed4l02-facilitator │
──────────────────────────────► │ │
│ verify → credential gate → submit│
│ → SQLite log → { tx_hash } │
└───────────────────┬───────────────┘
│ 5. submitAndWait
▼
XRPL Ledger (testnet/mainnet)
Ajoute le gate x402 à n'importe quelle route en une ligne.
Fichiers clés
ed4l02-x402/src/
├── middleware.ts # xrpl402() + xrpl402Express()
├── types.ts # PaymentPayload, PaymentDetails, VatBreakdown, Xrpl402Options...
├── vat.ts # computeVat(), VAT_RATES_BPS
└── index.ts # exports publics
Usage Express
import { xrpl402Express } from 'ed4l02-x402';
app.get('/premium',
xrpl402Express({
facilitatorUrl: 'https://ed4l02.onrender.com',
network: 'testnet',
asset: 'XRP',
amount: '1000000', // 1 XRP en drops
payTo: 'rVotreAdresse...',
// Optionnel — credential gate XLS-70 (AND)
requiredCredentials: [{ issuer: 'rIssuer...', credentialType: 'KYC_18' }],
// Optionnel — domain gate XLS-80 (OR)
requiredDomain: { domainId: 'ABCD1234...' },
// Optionnel — TVA auto (XRP uniquement)
vatConfig: { jurisdiction: 'FR', ht_amount: '1000000' }, // 1 XRP HT → 1.2 XRP TTC
}),
handler
);Assets supportés
| Asset | Format amount | Notes |
|---|---|---|
XRP |
"1000000" (drops) |
1 XRP = 1 000 000 drops |
IOU |
{ currency, issuer, value } |
Trust lines, RLUSD... |
MPT |
{ mpt_issuance_id, value } |
XLS-33 Multi-Purpose Token |
Headers x402
X-Payment-Required— 402 → base64(JSON) des conditions de paiementX-Payment-Signature— client → base64(PaymentPayload) avec le tx signéX-Payment-Response— 200 → base64(SettleResult) avec le tx_hash
Bug fix majeur : requiredCredentials et requiredDomain n'étaient pas forwardés au facilitator → corrigé dans buildPaymentDetails() avec conditional spreads.
Settlement backend. Déployé sur Render : https://ed4l02.onrender.com
Fichiers clés
ed4l02-facilitator/src/
├── index.ts # Routes : /health, /verify, /settle, /transactions, /marketplace
├── verify.ts # Vérification offline du tx_blob (sans RPC)
├── settle.ts # Pipeline : verify → TVA gate → credential gate → submit → SQLite
├── credentials.ts # XLS-70 gate + EDEL_AGENT + XLS-80 gate + checkMerchantVatCredential
├── marketplace.ts # CRUD marchands + endpoints : /merchants/listing, /marketplace
├── db.ts # SQLite (better-sqlite3, WAL) : insertTx, getTxsByAddress
├── client.ts # Pool de clients xrpl.js par réseau
└── types.ts # TxLog, PaymentPayload, PaymentDetails, VatBreakdown, SettleResponse...
GET /health
{ "status": "ok", "service": "ed4l02-facilitator", "version": "0.1.0" }POST /verify — vérification offline (pas de RPC)
- Vérifie scheme, network, Account, Destination, DestinationTag, montant exact
- Retourne
{ valid: true }ou{ valid: false, reason: "..." }
POST /settle — pipeline complet
- Déduplication —
sha256(tx_blob)en cache mémoire (TTL 120s) - Vérification offline
- Gate TVA — si
paymentDetails.vatBreakdownprésent : vérifie que le MARCHAND tientEDEL_TVA_FRouEDEL_TVA_CH(émis parEDEL_TRUSTED_ISSUER) - Gate XLS-70 (
requiredCredentials) — AND logic - Gate XLS-80 (
requiredDomain) — OR logic submitAndWait(tx_blob)avec timeout configurable (défaut 60s)- Vérification
meta.TransactionResult === "tesSUCCESS" - Log SQLite (inclut les colonnes TVA)
GET /transactions?address=r...&limit=50&offset=0
{
"total": 5,
"transactions": [
{
"tx_hash": "267B64A5...",
"from_address": "rSender...",
"to_address": "rMarchand...",
"asset": "XRP",
"amount": "1200000",
"network": "testnet",
"settled_at": "2026-04-11T16:44:08.964Z",
"facilitator": "http://localhost:3000",
"vat_jurisdiction": "FR",
"vat_ht_amount": "1000000",
"vat_vat_amount": "200000",
"vat_ttc_amount": "1200000",
"vat_rate_bps": 2000
}
]
}POST /merchants/listing — déclarer/mettre à jour un marchand sur le marketplace
- Header
x-wallet-addressdoit correspondre àmerchant.walletdans le JSON - Upsert par
wallet_address— le fichier JSON est la source de vérité - Body :
{
"merchant": {
"name": "Acme Weather",
"wallet": "rVotreAdresse...",
"description": "Real-time weather data API",
"category": "data"
},
"endpoints": [
{ "url": "https://acme.com/api/weather", "title": "Current Weather", "price": "10000", "asset": "XRP" }
]
}GET /marketplace — liste tous les marchands publics
- Query params optionnels :
category=data|ai|finance|media|tools|other,max_price=<drops>
GET /marketplace/:merchant_id — détail d'un marchand
Le fichier ed4l02.db contient 3 tables. WAL mode activé pour des écritures rapides et lectures non-bloquantes.
Table tx_logs — toutes les tx settlées
| Colonne | Type | Notes |
|---|---|---|
tx_hash |
TEXT PRIMARY KEY | Hash XRPL de la tx confirmée |
from_address |
TEXT | Wallet expéditeur |
to_address |
TEXT | Wallet marchand |
asset |
TEXT | XRP / IOU / MPT |
amount |
TEXT | Drops (XRP) ou value string (IOU/MPT) |
currency |
TEXT | IOU uniquement |
issuer |
TEXT | IOU uniquement |
mpt_id |
TEXT | MPT uniquement |
network |
TEXT | mainnet / testnet / devnet |
settled_at |
TEXT | ISO 8601 |
facilitator |
TEXT | URL du facilitator |
vat_jurisdiction |
TEXT | "FR" / "CH" / NULL |
vat_ht_amount |
TEXT | Montant HT en drops / NULL |
vat_vat_amount |
TEXT | Montant TVA en drops / NULL |
vat_ttc_amount |
TEXT | Montant TTC en drops / NULL |
vat_rate_bps |
INTEGER | 2000 = 20%, 810 = 8.1% / NULL |
Table merchants — profils marchands du marketplace
| Colonne | Type | Notes |
|---|---|---|
id |
TEXT PRIMARY KEY | UUID |
wallet_address |
TEXT UNIQUE | Adresse XRPL |
name |
TEXT | Nom du marchand |
description |
TEXT | Description |
category |
TEXT | data / ai / finance / media / tools / other |
listed |
INTEGER | 1 = visible sur le marketplace |
created_at |
TEXT | ISO 8601 |
updated_at |
TEXT | ISO 8601 |
Table merchant_endpoints — routes x402 listées par marchand
| Colonne | Type | Notes |
|---|---|---|
id |
INTEGER PRIMARY KEY | Auto-increment |
merchant_id |
TEXT | FK → merchants.id |
url |
TEXT | URL de l'endpoint protégé |
title |
TEXT | Titre affiché sur le marketplace |
description |
TEXT | Description |
price_amount |
TEXT | Prix en drops |
price_asset |
TEXT | XRP / IOU / MPT |
| Variable | Défaut | Description |
|---|---|---|
PORT |
3000 |
Port HTTP |
SETTLE_TIMEOUT_MS |
60000 |
Timeout confirmation ledger (ms) |
DATA_DIR |
./data |
Dossier SQLite — le fichier ed4l02.db est créé ici |
EDEL_TRUSTED_ISSUER |
— | Adresse XRPL de l'émetteur des credentials TVA/KYC |
FACILITATOR_URL |
http://localhost:3000 |
URL publique du facilitator (affiché dans /marketplace) |
XRPL_MAINNET_URL |
wss://xrplcluster.com |
Node mainnet |
XRPL_TESTNET_URL |
wss://testnet.xrpl-labs.com |
Node testnet |
XRPL_DEVNET_URL |
wss://s.devnet.rippletest.net:51233 |
Node devnet |
Client Resource Server Facilitator XRPL
│ │ │ │
│── GET /resource ──────▶│ │ │
│◀── 402 + conditions ───│ │ │
│ sign Payment tx │ │ │
│── GET /resource ──────▶│── POST /settle ───────▶│ │
│ X-Payment-Signature │ │── submitAndWait▶│
│ │ │◀── tesSUCCESS ──│
│◀── 200 + tx_hash ──────│◀── { success, hash } ──│ │
Le marchand configure requiredCredentials: [{ issuer, credentialType }].
À la réception du /settle, le facilitator :
- Appelle
account_objects(sender, type: credential)sur le ledger validé - Pour chaque
RequiredCredential, vérifie que le sender possède unCredentialobjet avec :IssuercorrespondantCredentialTypeen hex uppercase correspondantFlags & lsfAccepted (0x00010000)activéExpirationabsent ou dans le futur (epoch Ripple)
- Toutes les credentials doivent être présentes (AND) → sinon rejet avant submit
Le marchand configure requiredDomain: { domainId }.
ledger_entry(domainId)→ récupère lePermissionedDomainet sa listeAcceptedCredentialsaccount_objects(sender, credential)→ récupère les credentials du sender- Le sender doit posséder au moins une credential de la liste du domaine (OR) → sinon rejet
Le marchand configure vatConfig: { jurisdiction: 'FR' | 'CH', ht_amount: '...' } sur sa route.
Ce que fait le middleware (ed4l02-x402)
- Calcule TTC = HT × (1 + taux) en BigInt basis points (pas de flottant)
- FR :
2000nbps → 20% ; CH :810nbps → 8.1%
- FR :
- Override
amountavec le TTC - Embed
vatBreakdown: { jurisdiction, rate_bps, ht_amount, vat_amount, ttc_amount }dansPaymentDetails
Ce que fait le facilitator
- Étape 3 du pipeline (avant gates credential/domain)
- Vérifie que le MARCHAND (
pay_to) tientEDEL_TVA_FRouEDEL_TVA_CHémis parEDEL_TRUSTED_ISSUER - Si OK → poursuit le pipeline et logue les colonnes
vat_*en SQLite - Si KO → rejet 402
"Merchant does not hold a valid EDEL_TVA_FR credential"
Credential types (hex)
EDEL_TVA_FR=4544454C5F5456415F4652EDEL_TVA_CH=4544454C5F5456415F4348
Permet à un AI agent de payer au nom d'un humain KYC'd sans que l'humain expose ses credentials.
Setup (plateforme Ed4l02)
issuer ──CredentialCreate──▶ human (type: KYC_18)
issuer ──CredentialCreate──▶ agent (type: EDEL_AGENT, URI: hex(human_address))
human ──CredentialAccept──▶ ledger
agent ──CredentialAccept──▶ ledger
Flow au moment du paiement
agent.sign(Payment → merchant)
↓
facilitator.POST /settle (from_address = agent)
↓
checkCredentials(agent, [{ issuer, KYC_18 }])
→ agent n'a pas KYC_18 directement
→ fallback: checkAgentDelegation()
1. Trouve EDEL_AGENT sur agent (même issuer, accepté, non expiré)
2. Décode URI hex → UTF-8 → adresse XRPL de l'humain
3. isValidClassicAddress() sur l'adresse décodée
4. account_objects(human) → vérifie que l'humain a KYC_18
5. OK → log "[credentials] delegation accepted: agent X acting for Y"
↓
submit tx → 200 ✓
Credential EDEL_AGENT
CredentialType=4544454C5F4147454E54(hex de "EDEL_AGENT")URI= hex(adresse XRPL de l'humain délégant) — ex:724E4365633137...pourrNCEc17...- Émis par la plateforme EdelPay (même issuer que les credentials KYC)
9 steps (A–I) — fichier : ed4l02-facilitator/test-integration/run-test.ts
npx tsx test-integration/run-test.ts
| Step | Description | Attendu |
|---|---|---|
| A | GET /protected sans header | 402 |
| B | Signature tx (sender → recipient, 1 XRP) | tx signé |
| C | GET /protected avec X-Payment-Signature | 200 + tx_hash |
| D | GET /credential-protected sans header | 402 |
| E | Agent paye /credential-protected (délégation EDEL_AGENT → KYC_18 humain) | 200 + tx_hash |
| F | Sender sans credentials essaie /credential-protected | 402 "missing credential" |
| G | Issuer émet EDEL_TVA_FR sur le marchand (recipient), marchand accepte |
credential accepté |
| H | Sender paye /vat-protected (1 XRP HT → 1.2 XRP TTC, TVA FR 20%) | 200 + tx_hash |
| I | Sender paye /vat-protected-no-cred (marchand sans EDEL_TVA_FR) | 402 "does not hold EDEL_TVA_FR" |
Le test :
- Démarre le facilitator en subprocess (avec
EDEL_TRUSTED_ISSUER= adresse issuer) - Connecte au testnet XRPL, fund 5 wallets (recipient, sender, issuer, human, agent)
- Émet
KYC_18(issuer→human) +EDEL_AGENT(issuer→agent, URI=hex(human)) on-chain - Lance les steps A–F (basic + delegation flows)
- Émet
EDEL_TVA_FR(issuer→recipient/marchand) on-chain — Step G - Lance les steps H et I (TVA flow)
Dashboard Next.js pour les marchands + pages publiques marketplace + flow utilisateur.
Stack : Next.js 16 · React 18 · Tailwind · xrpl-connect · Space Grotesk
Home | Marketplace | Merchant | Transactions | User
- "List my API" n'est plus dans la nav globale — accessible depuis le dashboard marchand uniquement (feature réservée aux marchands onboardés)
- "User" (
/user) remplace l'ancien onglet "KYC" — flow KYC/identité utilisateur
- 5 KPI cards : Total Revenue · This Week · Avg Payment · Unique Senders · TVA Collected
- TVA Collected = somme des
vat_amountsur toutes les tx + nombre de tx avec TVA
- TVA Collected = somme des
- Bar chart SVG — revenus des 7 derniers jours (pur SVG, pas de lib externe)
- Bouton "List my API →" dans le header de page →
/merchant/marketplace-listing - Recent Payments — 8 derniers paiements, montant en XRP, sender raccourci, lien explorer au hover
- 4 KPI cards avec sparklines SVG (tendance haussière/baissière)
- Bar chart SVG — 14 jours
- Filtres :
- Recherche texte (adresse sender ou montant)
- Période : All / 7d / 30d / 90d
- Asset : All / XRP / IOU / MPT
- TVA : All / With TVA / Without TVA
- Montant min/max en XRP
- Table avec checkboxes multi-sélection
- Avatar initiales coloré par adresse
- Sender cliquable → copie dans le presse-papier
- Montant en XRP avec badge TVA sous le montant quand applicable :
+1.2 XRP [TVA FR 20%] HT 1 · TVA 0.2 - Date & heure complètes
- Lien "View ↗" vers testnet.xrpl.org
- Barre de sélection contextuelle avec export des sélectionnés
- Bouton ISO 20022 — export CAMT.053 des tx visibles ou sélectionnées
Formulaire JSON pour déclarer ses endpoints sur le marketplace Ed4l02.
Catalogue des marchands ayant déclaré leurs endpoints. Filtrable par catégorie et prix.
Affiche les endpoints, prix, et description d'un marchand.
Flow d'onboarding KYC en 5 étapes : Wallet → Identity Type → Scan QR → Accept Credential → Complete.
frontend/app/api/merchants/[id]/transactions/route.js
Proxy vers le facilitator SQLite — map les champs TVA :
GET /api/merchants/me/transactions
→ GET http://localhost:3000/transactions?address=<walletAddress>
→ map TxLog → frontend shape :
drops → XRP, vatBreakdown { jurisdiction, rate_bps, ht_amount, vat_amount, ttc_amount }
Variable : FACILITATOR_URL=http://localhost:3000 dans .env.local
frontend/lib/camt053.js
Génère un XML camt.053.001.02 (Bank-to-Customer Statement) conforme ISO 20022 :
<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
<BkToCstmrStmt>
<GrpHdr>
<MsgId>EDELPAY-1744389848000</MsgId>
<CreDtTm>2026-04-11T16:44:08Z</CreDtTm>
</GrpHdr>
<Stmt>
<Acct>
<Id><Othr><Id>rMarchand...</Id></Othr></Id>
<Ccy>XRP</Ccy>
</Acct>
<TxsSummry>
<TtlCdtNtries><NbOfNtries>5</NbOfNtries><Sum>26.000000</Sum></TtlCdtNtries>
</TxsSummry>
<Ntry>
<Amt Ccy="XRP">5.000000</Amt>
<CdtDbtInd>CRDT</CdtDbtInd>
<Sts>BOOK</Sts>
<AcctSvcrRef>267B64A5...</AcctSvcrRef>
<BkTxCd><Domn><Cd>PMNT</Cd>...</Domn><Prtry><Cd>x402-xrpl-v1</Cd></Prtry></BkTxCd>
<NtryDtls><TxDtls>
<RltdPties><Dbtr>...</Dbtr><Cdtr>...</Cdtr></RltdPties>
<RmtInf><Ustrd>x402 payment · network: testnet</Ustrd></RmtInf>
</TxDtls></NtryDtls>
</Ntry>
</Stmt>
</BkToCstmrStmt>
</Document>- Généré client-side dans le navigateur (pas besoin de serveur)
- Nom de fichier :
camt053-2026-04-01-2026-04-11.xml - Compatible SAP, Sage, QuickBooks et tout outil comptable ISO 20022
Ed4l02/
├── ed4l02-x402/ # npm package — middleware x402
│ ├── src/
│ │ ├── middleware.ts # xrpl402() + xrpl402Express() + buildPaymentDetails (vatConfig)
│ │ ├── vat.ts # computeVat(), VAT_RATES_BPS (BigInt basis points)
│ │ ├── types.ts # PaymentPayload, PaymentDetails, VatBreakdown, VatConfig...
│ │ └── index.ts # exports publics
│ ├── package.json
│ └── README.md
│
├── ed4l02-facilitator/ # Express server — settlement backend
│ ├── src/
│ │ ├── index.ts # Routes : /health /verify /settle /transactions /marketplace
│ │ ├── verify.ts # Vérification offline
│ │ ├── settle.ts # Pipeline : verify → TVA gate → cred gate → submit → SQLite
│ │ ├── credentials.ts # XLS-70 + EDEL_AGENT + XLS-80 + checkMerchantVatCredential
│ │ ├── marketplace.ts # CRUD marchands — /merchants/listing + /marketplace
│ │ ├── db.ts # SQLite (WAL) : tx_logs, merchants, merchant_endpoints
│ │ ├── client.ts # Pool xrpl.js par réseau
│ │ └── types.ts # TxLog (avec colonnes vat_*), VatBreakdown...
│ ├── test-integration/
│ │ └── run-test.ts # 9-step integration test (A–I)
│ ├── scripts/
│ │ └── send-txs.ts # Script envoi de tx de test
│ └── data/
│ └── ed4l02.db # SQLite (gitignore en prod)
│
├── frontend/ # Next.js — dashboard marchand + marketplace + user
│ ├── app/
│ │ ├── merchant/
│ │ │ ├── page.js # Dashboard KPI (incl. TVA Collected)
│ │ │ ├── transactions/page.js # Historique + filtres TVA + export
│ │ │ └── marketplace-listing/page.js # Formulaire listing API
│ │ ├── marketplace/
│ │ │ ├── page.js # Annuaire public
│ │ │ └── [id]/page.js # Détail marchand
│ │ ├── user/
│ │ │ └── page.js # Flow KYC utilisateur (ex /onboarding/kyc)
│ │ └── api/merchants/[id]/
│ │ └── transactions/route.js # Proxy → facilitator SQLite (avec champs TVA)
│ ├── components/
│ │ └── Header.js # Nav : Home Marketplace Merchant Transactions User
│ ├── hooks/
│ │ └── useMerchant.js # fetchTransactions, fetchMerchant, saveMerchant
│ ├── lib/
│ │ └── camt053.js # Générateur ISO 20022 CAMT.053
│ └── README.md
│
├── RECAP.md # Ce fichier
├── BRIEFING.md # Briefing condensé pour Claude.ai web
├── Credentialinfos.md # Spec XLS-70 credentials
└── PermissionDomain.md # Spec XLS-80 PermissionedDomain
Instructions pour déployer
ed4l02-facilitatorsur un VPS Linux (Infomaniak, OVH, Hetzner…) avec SQLite persistant, PM2 comme gestionnaire de process, et Nginx en reverse proxy.
# Node.js 20 LTS
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
# PM2 (gestionnaire de process)
sudo npm install -g pm2
# Nginx
sudo apt install -y nginx
# Git (si pas déjà installé)
sudo apt install -y gitVérifier :
node -v # v20.x.x
npm -v # 10.x.x
pm2 -v # 5.x.x
nginx -v# Cloner le repo (ou uploader les fichiers via scp/rsync)
git clone https://github.com/<ton-org>/ed4l02.git /srv/ed4l02
cd /srv/ed4l02/ed4l02-facilitator
# Installer les dépendances
npm install
# Builder TypeScript → dist/
npm run buildSi le repo est privé ou pas encore sur GitHub, utiliser
scp:# Depuis ta machine locale scp -r ./ed4l02-facilitator user@ip-vps:/srv/ed4l02/
nano /srv/ed4l02/ed4l02-facilitator/.envContenu :
PORT=3000
SETTLE_TIMEOUT_MS=60000
# Dossier de données SQLite — le fichier ed4l02.db sera créé ici automatiquement
DATA_DIR=/srv/ed4l02/data
# Adresse XRPL de l'émetteur des credentials TVA (EDEL_TVA_FR, EDEL_TVA_CH) et KYC
EDEL_TRUSTED_ISSUER=rIssuerAddressXRPL...
# URL publique du facilitator (affiché dans /marketplace)
FACILITATOR_URL=https://facilitator.tondomaine.com
# Optionnel — surcharger les nodes XRPL
# XRPL_MAINNET_URL=wss://xrplcluster.com
# XRPL_TESTNET_URL=wss://testnet.xrpl-labs.com
# XRPL_DEVNET_URL=wss://s.devnet.rippletest.net:51233Créer le dossier de données :
mkdir -p /srv/ed4l02/data
# Donner les droits à l'utilisateur qui fera tourner le process
chown -R $USER:$USER /srv/ed4l02/dataPoint clé SQLite persistant :
DATA_DIRdoit pointer vers un dossier hors du code (dist/, repo). Comme ça,git pull+ rebuild ne touche jamais la base./srv/ed4l02/data/est l'emplacement recommandé.
cd /srv/ed4l02/ed4l02-facilitator
# Lancer le process compilé
pm2 start dist/index.js --name "ed4l02-facilitator" --env production
# Sauvegarder la config PM2 pour redémarrage automatique au reboot
pm2 save
pm2 startup # → copier-coller la commande sudo qu'il afficheCommandes utiles PM2 :
pm2 status # voir les process
pm2 logs ed4l02-facilitator # logs en temps réel
pm2 logs ed4l02-facilitator --lines 200 # 200 dernières lignes
pm2 restart ed4l02-facilitator # redémarrer
pm2 stop ed4l02-facilitator # stopper
pm2 delete ed4l02-facilitator # supprimer du registre PM2Vérifier que le facilitator tourne :
curl http://localhost:3000/health
# → {"status":"ok","service":"ed4l02-facilitator","version":"0.1.0"}sudo nano /etc/nginx/sites-available/ed4l02-facilitatorContenu (remplacer facilitator.tondomaine.com par ton vrai domaine ou l'IP) :
server {
listen 80;
server_name facilitator.tondomaine.com;
# Logs
access_log /var/log/nginx/ed4l02-facilitator.access.log;
error_log /var/log/nginx/ed4l02-facilitator.error.log;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
# Headers standard pour proxy
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Timeouts (le settle peut prendre jusqu'à 60s sur le ledger)
proxy_connect_timeout 10s;
proxy_read_timeout 90s;
proxy_send_timeout 90s;
# Taille max du body (les tx_blob ne dépassent pas quelques Ko)
client_max_body_size 1m;
}
}Activer et recharger Nginx :
sudo ln -s /etc/nginx/sites-available/ed4l02-facilitator /etc/nginx/sites-enabled/
sudo nginx -t # vérifier la config — doit afficher "syntax is ok"
sudo systemctl reload nginxTester depuis l'extérieur :
curl http://facilitator.tondomaine.com/healthsudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d facilitator.tondomaine.com
# → suivre les instructions, choisir "redirect HTTP to HTTPS"
# Renouvellement automatique (déjà configuré par certbot, vérifier)
sudo systemctl status certbot.timerAprès ça, le facilitator est accessible en HTTPS :
https://facilitator.tondomaine.com/health
https://facilitator.tondomaine.com/settle
https://facilitator.tondomaine.com/transactions?address=r...
cd /srv/ed4l02/ed4l02-facilitator
# Récupérer les changements
git pull origin main # ou scp si pas de git
# Reinstaller les dépendances si package.json a changé
npm install
# Rebuilder
npm run build
# Redémarrer le process (la DB SQLite dans /srv/ed4l02/data/ n'est PAS touchée)
pm2 restart ed4l02-facilitator
# Vérifier
pm2 logs ed4l02-facilitator --lines 20
curl http://localhost:3000/healthDans frontend/.env.local (ou les variables d'environnement du déploiement frontend) :
FACILITATOR_URL=https://facilitator.tondomaine.comEt dans tous les appels xrpl402Express() du resource server, mettre à jour facilitatorUrl :
xrpl402Express({
facilitatorUrl: 'https://facilitator.tondomaine.com',
// ...
})[ ] Node.js 20 + PM2 + Nginx installés
[ ] Code copié dans /srv/ed4l02/ed4l02-facilitator/
[ ] npm install && npm run build — sans erreur
[ ] .env créé avec DATA_DIR=/srv/ed4l02/data et EDEL_TRUSTED_ISSUER=r...
[ ] Dossier /srv/ed4l02/data/ créé avec les bons droits
[ ] pm2 start dist/index.js --name "ed4l02-facilitator"
[ ] curl localhost:3000/health → {"status":"ok"}
[ ] Config Nginx créée et activée
[ ] nginx -t → syntax ok
[ ] curl http://facilitator.tondomaine.com/health → ok
[ ] HTTPS avec certbot (optionnel mais recommandé)
[ ] pm2 save + pm2 startup (redémarrage auto au reboot)
[ ] FACILITATOR_URL mis à jour dans le frontend (.env.local)
| Symptôme | Cause probable | Solution |
|---|---|---|
pm2 logs : EACCES: permission denied, open '...ed4l02.db' |
Le dossier DATA_DIR n'existe pas ou mauvais owner |
mkdir -p /srv/ed4l02/data && chown $USER /srv/ed4l02/data |
pm2 logs : Cannot find module 'dist/index.js' |
Build pas fait ou échoué | npm run build dans le bon dossier |
Nginx : 502 Bad Gateway |
Le facilitator ne tourne pas | pm2 status + pm2 logs |
Nginx : 504 Gateway Timeout |
proxy_read_timeout trop court |
Vérifier que proxy_read_timeout 90s est bien dans la config Nginx |
/settle timeout |
SETTLE_TIMEOUT_MS trop court ou node XRPL lent |
Augmenter SETTLE_TIMEOUT_MS=90000 dans .env |
| WebSocket XRPL se déconnecte | Idle timeout sur le VPS | Normal, le client.ts se reconnecte automatiquement |
ed4l02.db vide après redéploiement |
DATA_DIR pointe dans dist/ |
Mettre DATA_DIR=/srv/ed4l02/data dans .env |
- Déploiement VPS Infomaniak avec SQLite persistant (
DATA_DIRpersisté hors du code) - Publier
ed4l02-x402sur npm - API key authentication sur le facilitator (header
X-Api-Key) - Test e2e IOU (RLUSD) et MPT
- Vraie implémentation CAMT.053 avec totaux d'ouverture/clôture (opening/closing balance)
- Webhook marchand : POST vers une URL configurée à chaque tx settlée
- Dashboard : pagination sur
/transactionspour les gros volumes - Refresh automatique du dashboard toutes les N secondes
- TVA CH — test e2e
EDEL_TVA_CH(8.1%) - Marketplace : frontend pagination + filtres côté client (
/marketplace) - Onboarding marchand guidé : émettre
EDEL_TVA_FR/EDEL_TVA_CHdepuis l'interface admin
Voir le fichier de seeds dans le répertoire projet (ne pas commiter en prod).
Facilitator local : http://localhost:3000
Frontend local : http://localhost:3001
Facilitator Render (devnet) : https://ed4l02.onrender.com