Les subscriptions natives ne sont pas encore dans l’API (V2). Pour le moment, gérez le cycle d’abonnement côté votre app et créez un PaymentIntent à chaque renouvellement.
Pattern actuel
[Votre app] tient le calendrier des abonnements
J-3 expiration → notify_email("Renouvellement prochain")
J expiration → izipay.paymentIntents.create({ ... })
→ email avec lien de paiement
→ si payé sous 7j → renew + reset date
→ si pas payé → désactiver compte
Implémentation
1. Modèle DB
CREATE TABLE subscriptions (
id UUID PRIMARY KEY,
user_id UUID NOT NULL,
plan VARCHAR NOT NULL,
amount_xof INT NOT NULL,
period VARCHAR NOT NULL, -- monthly | yearly
current_period_end TIMESTAMPTZ NOT NULL,
status VARCHAR NOT NULL, -- active | past_due | cancelled
last_intent_id VARCHAR
);
2. Cron de renouvellement
// daily — pour chaque subscription expirant aujourd'hui
async function renewSubscriptionsDueToday() {
const subs = await db.subscriptions.findMany({
where: { currentPeriodEnd: { lte: tomorrow() }, status: 'active' },
});
for (const sub of subs) {
const intent = await izipay.paymentIntents.create({
requestedCurrencyType: 'fiat',
currencyRequested: 'XOF',
amountRequested: String(sub.amountXof),
acceptedCoins: ['USDT.TRC20', 'USDT.BEP20'],
merchantReference: `sub-${sub.id}-${sub.currentPeriodEnd.toISOString()}`,
idempotencyKey: `renew-${sub.id}-${sub.currentPeriodEnd.toISOString()}`,
expiresAt: addDays(now(), 7),
metadata: { subscriptionId: sub.id, period: sub.period },
});
await db.subscriptions.update({
id: sub.id, status: 'past_due', lastIntentId: intent.id,
});
await sendRenewalEmail({
to: sub.user.email,
paymentUrl: intent.paymentUrl,
expiresAt: intent.expiresAt,
});
}
}
3. Webhook handler
case 'payment_intent.completed': {
const { intentId, merchantReference, metadata } = event.data;
if (metadata?.subscriptionId) {
const sub = await db.subscriptions.findById(metadata.subscriptionId);
if (sub?.lastIntentId === intentId && sub.status === 'past_due') {
await db.subscriptions.update({
id: sub.id,
status: 'active',
currentPeriodEnd: addPeriod(sub.currentPeriodEnd, sub.period),
lastIntentId: null,
});
}
}
break;
}
case 'payment_intent.expired': {
const { merchantReference, metadata } = event.data;
if (metadata?.subscriptionId) {
const sub = await db.subscriptions.findById(metadata.subscriptionId);
if (sub?.status === 'past_due') {
await db.subscriptions.update({ id: sub.id, status: 'cancelled' });
await deactivateAccount(sub.userId);
}
}
break;
}
Alternative : Products + email manuel
Pour un onboarding rapide d’abonnés sans cron, créez un Product par plan et partagez son lien de paiement public (récupéré à la création du produit côté dashboard ou API). Le client revient chaque mois cliquer. Pas vraiment “subscription” mais ça marche pour les early stages.
Limites du pattern actuel
- Pas de re-essai automatique d’une carte / wallet pour le client (le client doit revenir cliquer le lien)
- Pas de notification “votre abonnement va expirer dans X jours” intégrée (à coder côté votre app)
- Pas de gestion native des promo codes (à gérer côté votre app, créez l’intent avec le montant promo)
Subscriptions V2 : prévu T1 2027 avec API dédiée (POST /v1/subscriptions, gestion native du cycle, promo codes, multi-currency).