Skip to content

About

Adaptateur Chariow pour Node : checkout + vérification de signature des webhooks. Zéro dépendance, framework-agnostique.

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

chariow-kit

Adaptateur Chariow pour Node, paiements, licences, webhooks. Zéro dépendance, framework-agnostique (Next.js, Express, Fastify, Hono…).

Encaisse partout : Mobile Money en Afrique (MTN, Moov, Orange, Wave…), cartes et PayPal à l'international. Les moyens de paiement s'adaptent au pays de l'acheteur. Bonsoir

Tu démarres de zéro ? npx github:phileas98/create-chariow-kit mon-app génère un projet Next.js complet.

Installation

npm install chariow-kit
# ou, avant publication npm :
npm install github:phileas98/chariow-kit
CHARIOW_API_KEY=sk_live_...        # dashboard Chariow → API
PULSE_SIGNING_SECRET=whsec_...     # Chariow → ton Pulse → Secret de signature

L'ID produit (prd_...) n'est pas une variable d'env : passe-le en argument. Le produit doit être publié (sinon 404).

1. Encaisser

import { createCheckout } from "chariow-kit";

const { checkoutUrl } = await createCheckout({
  productId: "prd_xxxx",
  email: "client@mail.com",
  firstName: "Jean",
  lastName: "Dupont",
  phoneNumber: "0162016549",
  countryCode: "BJ",
  customerIp: req.ip,                    // → moyens de paiement locaux
  customMetadata: { userId: "u_123" },   // te revient dans le webhook
  redirectUrl: "https://app.com/merci?sale={sale_id}",
});
// redirige le client vers checkoutUrl

step vaut payment (→ checkoutUrl), completed (produit gratuit) ou already_purchased.

2. Paywall par licence

Le modèle : le client paie, reçoit une clé, la colle dans ton app, tu débloques.

import { validateOrActivate } from "chariow-kit";

// Dans TA route API — jamais côté navigateur (la clé sk_live_ y serait exposée)
const { valid, reason, license } = await validateOrActivate(
  licenseKey,
  deviceId          // empreinte machine : limite le nb d'appareils
);

if (!valid) {
  // reason: not_found | expired | revoked | activation_limit_reached | inactive
  return Response.json({ valid: false, reason }, { status: 403 });
}

return Response.json({
  valid: true,
  plan: license.product?.name,
  expiresAt: license.expiresAt,          // null = licence à vie
  devicesLeft: license.activations.remaining,
});

validateOrActivate() lit la licence et l'active si c'est sa première utilisation. Si ton produit est configuré avec requires_activation: false (mode SaaS), les licences arrivent déjà actives — le helper le gère.

Autres fonctions : getLicense, validateLicense, activateLicense, revokeLicense, getLicenseActivations, listLicenses.

Gestion des appareils

const { activations } = await getLicenseActivations(licenseKey);
// chaque entrée : IP + pays, navigateur/plateforme, device, date

3. Contrôle d'accès par achat

Pour un cours ou un contenu, sans licence :

import { hasPurchased } from "chariow-kit";

const { hasAccess } = await hasPurchased(user.email, "prd_cours");
if (!hasAccess) return Response.json({ error: "Achat requis" }, { status: 403 });

4. Webhooks (Pulses)

handlePulse() fait tout : signature, déduplication, dispatch.

Next.js (App Router)

import { handlePulse } from "chariow-kit";

export async function POST(request) {
  const result = await handlePulse({
    rawBody: await request.text(),        // corps BRUT obligatoire
    headers: request.headers,
    isDuplicate: (id) => db.deliveries.exists(id),
    markProcessed: (id) => db.deliveries.insert(id),
    on: {
      "successful.sale": async (p) => {
        await grantAccess(p.email, p.productId, p.sale.metadata);
      },
      "license.issued": async (p) => sendKeyByEmail(p.email, p.license.key),
      "license.nearing_expiry": async (p) => sendRenewal(p.email, p.daysUntilExpiry),
      "license.expired": async (p) => revokeAccess(p.license.key),
    },
  });
  return new Response(result.reason ?? "OK", { status: result.status });
}

Express

// express.raw() sur CETTE route : il faut le corps brut
app.post("/webhook", express.raw({ type: "application/json" }), async (req, res) => {
  const result = await handlePulse({ rawBody: req.body, headers: req.headers, on: {…} });
  res.status(result.status).send("OK");
});

Événements

successful.sale, abandoned.sale, failed.sale, license.issued, license.activated, license.nearing_expiry, license.expired, license.revoked, affiliate.joined.

Idempotence : sur la livraison, pas sur la vente

Déduplique sur l'en-tête x-pulse-delivery-id, pas sur sale.id : une même vente produit légitimement une livraison par Pulse abonné. handlePulse() s'en charge via isDuplicate / markProcessed.

Un pulse de test n'a pas ce header (getDeliveryId() renvoie null) et porte un champ note (pulse.isTest).

Politique de retry

5 tentatives (10 s, 100 s, ~17 min, ~2 h 47), timeout 30 s chacune. Après le 5ᵉ échec, Chariow désactive le Pulse et t'envoie un email : il faut le réactiver dans le dashboard. Réponds vite et en 2xx.

Sécurité

  • Clé API serveur uniquement : jamais dans le navigateur, jamais dans du code client. C'est pourquoi la validation de licence doit être server-side.
  • Toujours vérifier la signature avant de livrer quoi que ce soit : ton endpoint est public, n'importe qui peut y poster.
  • Ne te fie pas à la redirection après paiement : la source de vérité est le Pulse.
  • Sur erreur réseau, validateLicense() lève au lieu de renvoyer valid: false à toi de décider (période de grâce hors ligne plutôt que couper l'accès d'un client légitime).

Limites de l'API Chariow

  • Un seul produit par checkout : pas de panier multi-lignes. Pour plusieurs articles en un paiement : produit Bundle.
  • Page de paiement hébergée : on redirige, on n'embarque pas la saisie.
  • Types Service / Coaching / prix libre non supportés par l'API checkout.
  • Endpoint Pulse en HTTPS obligatoire.

Tests

npm test

MIT.

About

Adaptateur Chariow pour Node : checkout + vérification de signature des webhooks. Zéro dépendance, framework-agnostique.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages