Profil : plateforme intermédiaire qui collecte des paiements pour le compte de tiers (vendeurs) et prélève une commission. Exemples : Etsy-like, food delivery, immobilier.
Architecture
[Client] → paye le total au [Compte plateforme IzichangePay]
↓
Webhook payment_intent.completed
↓
[Votre backend] calcule les splits
↓
Soit: N transferts internes (vendeurs sont aussi sur IzichangePay)
Soit: N payouts unitaires vers les wallets externes des vendeurs
Option A : vendeurs sur IzichangePay (recommandé)
Si vos vendeurs ont aussi un compte IzichangePay (typiquement vous les onboarderiez à l’inscription) :
- Transferts internes : sans frais de gas, sans commission, crédités immédiatement sur la balance du vendeur
- Chaque vendeur gère son auto-settlement vers son compte mobile money
app.post('/webhooks/izipay', express.raw(), async (req, res) => {
const event = IziPayClient.validateWebhook(...);
if (event.type !== 'payment_intent.completed') return res.json({ received: true });
const order = await db.orders.findByIzipayIntentId(event.data.intentId);
// Split : 90% au vendeur, 10% commission plateforme (déjà sur votre compte)
const splits = order.items.map(item => ({
sellerEmail: item.sellerEmail,
amountToSeller: item.priceCrypto.times(0.9),
}));
// Un transfert interne par vendeur (sequence safe — pas de gas)
for (const split of splits) {
await izipay.payouts.createInternal({
assetCode: event.data.assetCode,
amount: split.amountToSeller.toString(),
destinationType: 'internal_email',
destinationEmail: split.sellerEmail,
idempotencyKey: `split-${order.id}-${split.sellerEmail}`,
note: `Order ${order.id}`,
});
}
// Votre commission reste sur votre balance, settle automatique selon votre rule
res.json({ received: true });
});
Option B : vendeurs externes (paiement sur leur propre wallet)
Émettez un payout unitaire par vendeur (payouts.create). Le payout groupé (batch) arrivera en V2.
for (const item of order.items) {
await izipay.payouts.create({
destinationAddress: item.sellerWalletAddress,
assetCode: event.data.assetCode,
amount: item.priceCrypto.times(0.9).toString(),
idempotencyKey: `payout-${order.id}-${item.id}`,
note: `Order ${order.id} — ${item.title}`,
});
}
Pour cette option, activez la whitelist (payoutMode = whitelist_only, défaut allow_all) et pré-enregistrez chaque adresse vendeur. La maturation des adresses (24h) est un réglage plateforme : un payout vers une adresse non mature est rejeté en 403 DESTINATION_NOT_MATURED. Cela évite qu’un compte vendeur compromis ne modifie son adresse pour un vol immédiat.
Tracking des splits
Votre DB doit tracer chaque split pour la compta / reporting vendeur :
CREATE TABLE seller_payouts (
id UUID PRIMARY KEY,
order_id UUID,
seller_id UUID,
izipay_payout_id VARCHAR,
amount_crypto NUMERIC,
status VARCHAR, -- pending | confirmed | failed
created_at TIMESTAMPTZ
);
À chaque webhook payout.confirmed (ou payout.failed), mettez à jour la ligne correspondante via izipay_payout_id.
Gestion des litiges
Si un client conteste un paiement après split :
disputeManager = merchant recommandé sur votre compte plateforme
- À la décision refund : votre balance n’a peut-être plus le montant (déjà splitté)
- Solution : retenez la commission + un buffer pendant N jours (configurable) avant de la débloquer
- Implémenter cette logique côté votre app : IzichangePay ne gère pas l’escrow
KYB cascade
Si vos vendeurs reçoivent leur argent via IzichangePay, ils doivent eux aussi avoir un compte business avec KYB approuvé. Vous pouvez automatiser l’onboarding via le wizard officiel (voir Mise en route).