Preloader

Guide complet

Intégration cartes virtuelles HeyQo

Tout ce qu’il faut savoir pour intégrer les cartes Visa/Mastercard HeyQo : comportement de l’API live, pièges courants, coûts, sécurité PCI et webhooks. Basé sur le skill open source heyqo-cards.

Référence endpoints curl : Cartes virtuelles · Customers · Webhooks · Exemples en direct

Modèle HeyQo / Platnova

HeyQo est une couche white-label au-dessus de Platnova. La banque émettrice est en bout de chaîne — le KYC vient de la banque, pas de HeyQo seul.

LIVEhttps://heyqo.cash/business/v1
SANDBOXhttps://heyqo.cash/business/sandbox/v1

Le mode de la clé API doit correspondre à l’URL de base, sinon 403 sans message explicite.

Les 5 pièges les plus coûteux

  1. Enveloppe de réponse — Toute réponse est { message, data, type }. Les erreurs utiles sont dans message.error[]. Un HTTP 200 peut quand même être une erreur — vérifiez type.
  2. Noms de champs — Solde carte = amount (string). Détails = info (last4, expiry_month/year en 2 chiffres, name_on_card…). Pas de champ balance.
  3. Carte créée avant d’exister — POST /cards peut répondre sans numéro ni expiry (statut processing). Enregistrez en pending — ne jamais envoyer 0000 au client.
  4. Sandbox ≠ production — Un customer_id sandbox n’existe pas en production. Stockez l’environnement avec l’ID.
  5. HTTP 402 — « Insufficient merchant balance » = votre float marchand HeyQo est vide, pas le solde du client. Message générique côté utilisateur, incident côté ops.

Titulaire et KYC

  • Impossible de réutiliser un KYC d’un autre provider — la banque exige son propre dossier.
  • Consentement explicite avant envoi des pièces (ID, selfie, adresse, déclarations AML).
  • customer_id — Pour POST /cards, utilisez le local_id du customer. L’id UUID de la liste peut renvoyer « customer was not found ».
  • 409 « Customer with this external_ref already exists » — réutilisez le customer existant.
  • Le paramètre ?external_ref= sur GET /customers est ignoré ; filtrez côté client.

Documentation Customers →

Structure des coûts

Confirmez par écrit avec HeyQo avant de fixer vos tarifs :

PosteÀ clarifier
Émission carteFrais + éventuel pré-dépôt sur la carte (débités ensemble)
Recharge (deposit)Frais fixe par opération, en plus du montant
Retrait (withdraw)Frais fixe — le retrait n’est pas gratuit
Float marchandRecharge de votre wallet partenaire ; un 402 signale l’épuisement

Formule utile : pour un frais fixe c et un plafond p %, montant minimum rentable ≈ c / p.

Règles sécurité (argent & PCI)

  • Load — Débiter le wallet utilisateur, appeler l’issuer ; échec issuer → rembourser dans la même requête.
  • Solde — L’issuer fait foi. Pas de liste transactions fiable ; sur événement card.charged / card.funded, re-lisez GET /cards/{id}.
  • Remboursement annulation — Ne remboursez jamais plus que le solde vérifié chez l’issuer.
  • PAN / CVV — Jamais stockés ni loggés chez vous. Affichage via secure-view (iframe 90 s).
  • Logs — Noms de champs oui ; valeurs PAN/CVV non.

API — champs et endpoints

Authentification

POSThttps://heyqo.cash/business/v1/authentication/token

Body : client_id, secret_id (pas client_secret). Token dans data.access_token.

Créer une carte

POSThttps://heyqo.cash/business/v1/cards
{
  "customer_id": "<local_id>",
  "currency": "usd",
  "brand": "visa",
  "label": "Primary card"
}

brand en minuscules (visa, mastercard). Réponse sans PAN/expiry tant que la carte est en provisioning.

Lire une carte

GEThttps://heyqo.cash/business/v1/cards/{id}
{
  "card": {
    "amount": "15.90",
    "currency": "usd",
    "status": "active",
    "info": {
      "masked_pan": "411111******1111",
      "last4": "1111",
      "expiry_month": "08",
      "expiry_year": "29",
      "name_on_card": "...",
      "brand": "visa"
    }
  }
}

Recharge & retrait

POSThttps://heyqo.cash/business/v1/cards/{id}/deposit
POSThttps://heyqo.cash/business/v1/cards/{id}/withdraw

Montants en unités (pas centimes). Frais issuer débités en plus sur votre float.

État carte

PUT .../freeze · PUT .../unfreeze · PUT .../terminate

Référence complète Virtual Cards (curl) →

Affichage PAN (secure-view)

PCI SAQ A : le numéro complet s’affiche dans une page HeyQo, embed via iframe. Ne jamais exposer PAN/CVV via votre API.

POSThttps://heyqo.cash/business/v1/cards/{id}/secure-view
{
  "layout": "free",
  "align": "left",
  "positions": {
    "pan":        { "top": "57%", "left": "6%" },
    "cardholder": { "top": "71%", "left": "6%" },
    "expiry":     { "top": "71%", "left": "60%" },
    "cvv":        { "top": "71%", "left": "78%" }
  },
  "text_color": "#FFFFFF",
  "show_branding": false
}

→ { "iframe_url": "...", "expires_in": 90 }
  • URL à usage unique, ~90 secondes. Retirez l’iframe avant expiration pour éviter le message « expired ».
  • layout: free seul mode avec positions absolues.
  • ~20 appels / heure — header x-ratelimit-remaining.
  • Pendant l’affichage : masquez votre propre ligne PAN, pas toute la carte (évite le « saut » visuel).

Webhooks

Signature HMAC-SHA256 sur le corps brut, header X-HeyQo-Signature.

ÉvénementAction
customer.approved / customer.rejectedMettre à jour le statut KYC
card.charged / card.fundedRe-sync solde (GET card), pas d’arithmétique sur le montant event
card.declinedInformatif
card.terminatedFermer la carte localement

Configuration webhooks →

Checklist avant mise en prod

  • Clés production + URL https://heyqo.cash/business/v1
  • Customer local_id utilisé pour POST /cards
  • Cartes « processing » gérées sans fausse last4
  • 402 / float marchand monitoré
  • Webhooks vérifiés (HMAC) + idempotence par état
  • PAN uniquement via secure-view
  • Rollback wallet si deposit issuer échoue
Source open source (mises à jour communautaires) : github.com/Christerlin/heyqo-cards