Développeurs/Intégrer

Recharge mobile

La recharge mobile crédite directement un numéro de téléphone prépayé. Votre client reçoit du crédit d'appel ou des données sur sa ligne. Il n'y a aucun code à lui remettre. Vous payez avec votre portefeuille CardV, comme pour les autres commandes.

Voir aussi : Authentification · Conventions · Webhooks

#Fonctionnement

Text
GET  /recharge/countries            pays que vous pouvez recharger
GET  /recharge/operators?country=US  opérateurs, types de recharge et montants
POST /recharge/quote                votre prix, et un quote_token valable 300 s
POST /recharge/orders               passer la commande, payée avec votre portefeuille
GET  /recharge/orders/{order_id}    consulter le statut, ou attendre un webhook
  • Tous les chemins commencent par /api/v1. Envoyez les mêmes en-têtes que pour tout autre appel.
  • Les deux appels POST doivent être signés. Signez-les comme POST/orders, mais avec leur propre chemin, par exemple /api/v1/recharge/quote.
  • Les commandes de recharge sont distinctes des commandes de cartes cadeaux. Consultez-les avec les endpoints /recharge.
  • Seules les recharges directes sont proposées. Les produits à PIN (un code que le client saisit) ne le sont pas.

#Pays

GET/api/v1/recharge/countries liste les pays que vous pouvez recharger actuellement.

JSON
{
  "count": 2,
  "results": [
    {"code": "MX", "name": "Mexico", "currency_codes": ["MXN"], "operator_count": 4, "offer_count": 37},
    {"code": "US", "name": "United States", "currency_codes": ["USD"], "operator_count": 6, "offer_count": 52}
  ]
}
  • code est le code pays ISO 3166-1 alpha-2. Envoyez-le dans country lors des appels suivants.
  • currency_codes sont les devises locales dans lesquelles vendent les opérateurs de ce pays.
  • La liste change quand des opérateurs sont ajoutés ou deviennent indisponibles. Rechargez-la toutes les quelques heures.

#Opérateurs

GET/api/v1/recharge/operators?country=US liste les opérateurs d'un pays. Ajoutez search=att pour filtrer sur le nom de l'opérateur.

JSON
{
  "count": 1,
  "results": [
    {
      "operator_key": "us-att",
      "name": "AT&T",
      "country": "US",
      "country_name": "United States",
      "logo_url": "https://b2b.cardv.net/api/v1/catalog-assets/3f5c...a1.png",
      "subtypes": ["airtime", "data"],
      "amount_model": "range",
      "currency_codes": ["USD"],
      "amounts": [
        {"min": "5.0000", "max": "100.0000", "currency": "USD", "subtype": "airtime", "label": "5.0000-100.0000 USD"},
        {"min": "15.0000", "max": "15.0000", "currency": "USD", "subtype": "data", "label": "15.0000 USD"}
      ],
      "offer_count": 3
    }
  ]
}
ChampSignification
operator_keyL'identifiant à envoyer pour obtenir le prix et commander, par exemple us-att. Enregistrez-le sous forme de texte.
subtypesCe que vous pouvez acheter : airtime (crédit d'appel), data ou bundle (appels et données).
amount_modelfixed si tous les montants sont des valeurs fixes, range si au moins un montant est une fourchette.
amounts[]Chaque option. Si min est égal à max, c'est un montant fixe. Sinon, tout montant compris entre les deux.
amounts[].currencyLa devise locale de cette option. Envoyez-la dans local_currency.
logo_urlImage hébergée par CardV, ou "".
  • Les montants sont des montants locaux : ce que reçoit la ligne, dans la devise locale.
  • Un country inconnu ou mal formé renvoie HTTP 400.

#Prix

POST/api/v1/recharge/quote vous donne votre prix pour une recharge. Cet appel doit être signé.

JSON
{
  "country": "US",
  "operator_key": "us-att",
  "amount": "10.00",
  "local_currency": "USD",
  "subtype": "airtime"
}
ChampObligatoireSignification
countryOuiCode pays issu de la liste des pays.
operator_keyOuiIssu de la liste des opérateurs.
amountOuiMontant local, sous forme de chaîne. Fixe : une des valeurs listées. Fourchette : entre min et max.
local_currencyRecommandéCode ISO 4217 de amount, issu de amounts[].currency. Envoyez-le quand un opérateur propose plusieurs devises.
subtypeNonairtime (par défaut), data ou bundle.

La réponse :

JSON
{
  "country": "US",
  "country_name": "United States",
  "operator": {"operator_key": "us-att", "name": "AT&T", "logo_url": ""},
  "subtype": "airtime",
  "local_amount": "10.0000",
  "local_currency": "USD",
  "merchant_price": "9.6200",
  "merchant_currency": "USD",
  "expires_at": "2026-09-30T08:20:30.123456+00:00",
  "quote_token": "eyJ2ZXJzaW9uIjox...:1uXyZa:8c1f..."
}
  • merchant_price est le montant débité de votre portefeuille, en merchant_currency.
  • quote_token bloque ce prix pendant 300 secondes, jusqu'à expires_at. Renvoyez-le tel quel avec la commande.
  • Le jeton est lié à votre compte et à ce pays, cet opérateur, ce type et ce montant.
  • Vérifiez que local_currency est bien la devise attendue avant de commander.
  • Une demande de prix ne bloque aucun argent. Vous pouvez redemander un prix à tout moment.

#Passer une commande de recharge

POST/api/v1/recharge/orders recharge le téléphone et paie avec votre portefeuille. Cet appel doit être signé.

JSON
{
  "external_order_id": "SHOP-RC-20260930-0001",
  "country": "US",
  "operator_key": "us-att",
  "amount": "10.00",
  "local_currency": "USD",
  "subtype": "airtime",
  "account": "12125550100",
  "quote_token": "eyJ2ZXJzaW9uIjox...:1uXyZa:8c1f..."
}
ChampObligatoireSignification
external_order_idOuiVotre numéro de commande. Unique parmi toutes vos commandes, y compris les commandes de cartes cadeaux.
country, operator_key, amount, local_currency, subtypeOuiLes mêmes valeurs que celles envoyées pour le prix.
accountOuiLe numéro de téléphone à recharger : chiffres uniquement, avec l'indicatif du pays, sans + ni espaces.
quote_tokenOuiIssu de la demande de prix, avant son expiration.

Exemples de numéros : 12125550100 (États-Unis), 525512345678 (Mexique). Vérifiez que le numéro appartient bien à l'opérateur choisi. Une recharge envoyée au mauvais numéro ne peut pas être annulée.

CardV vérifie le prix, débite immédiatement merchant_price de votre portefeuille et lance la recharge en arrière-plan. Une nouvelle commande renvoie HTTP 201 :

JSON
{
  "idempotent_replay": false,
  "order": {
    "order_id": "O-00005678",
    "external_order_id": "SHOP-RC-20260930-0001",
    "status": "accepted",
    "status_title": "Recharge accepted",
    "poll_after_seconds": 12,
    "account": "12***00",
    "local_amount": "10.0000",
    "local_currency": "USD",
    "merchant_price": "9.6200",
    "merchant_currency": "USD",
    "...": "more fields"
  }
}
  • Enregistrez order.order_id.
  • Le numéro de téléphone est renvoyé masqué, jamais en entier.

#Renvoyer sans risque

external_order_id vous évite de recharger deux fois.

Vous envoyezVous recevez
Un nouveau numéro de commandeHTTP 201. Une nouvelle commande. Votre portefeuille est débité.
Le même numéro et la même rechargeHTTP 200 et "idempotent_replay": true. La commande existante. Aucun débit.
Le même numéro mais une recharge différenteHTTP 400 sur external_order_id. Il ne se passe rien.

« La même recharge » signifie le même pays, le même opérateur, le même type, le même montant et le même numéro de téléphone. Une commande en double est détectée avant la vérification du prix : un quote_token expiré renvoie donc quand même la première commande.

  • Après un délai dépassé, une erreur 5xx ou une connexion perdue, renvoyez le même corps avec le même numéro de commande. Signez-le à nouveau avec un nouvel horodatage et un nouveau nonce.
  • Ne créez jamais un nouveau numéro de commande parce qu'une réponse s'est perdue. Le téléphone pourrait être rechargé deux fois.

#Consulter les commandes de recharge

GET/api/v1/recharge/orders/{order_id} renvoie une commande. Vous pouvez utiliser le numéro de commande CardV (O-00005678).

JSON
{
  "order_id": "O-00005678",
  "external_order_id": "SHOP-RC-20260930-0001",
  "status": "processing",
  "order_status": "processing",
  "status_title": "Recharge processing",
  "status_message": "The recharge request is being processed. Delivery is not confirmed yet.",
  "next_step": "Keep this order open and wait for confirmation before placing another recharge.",
  "poll_after_seconds": 12,
  "country": "US",
  "operator": {"operator_key": "us-att", "name": "AT&T", "logo_url": ""},
  "subtype": "airtime",
  "account": "12***00",
  "local_amount": "10.0000",
  "local_currency": "USD",
  "merchant_price": "9.6200",
  "merchant_currency": "USD",
  "attempts": [{"attempt_no": 1, "status": "processing", "error_message": "", "submitted_at": "2026-09-30T08:16:02.511201Z", "completed_at": null}],
  "created_at": "2026-09-30T08:16:01.004211Z",
  "updated_at": "2026-09-30T08:16:02.611978Z",
  "...": "more fields"
}
  • Un numéro de commande inconnu, ou la commande d'un autre compte, renvoie HTTP 404.
  • status_title, status_message et next_step sont des textes en anglais que vous pouvez montrer à votre équipe.
  • poll_after_seconds indique combien de temps attendre avant la prochaine vérification. 0 signifie que la commande est terminée.

#Lister les commandes de recharge

GET/api/v1/recharge/orders liste vos commandes de recharge, des plus récentes aux plus anciennes.

HTTP
GET /api/v1/recharge/orders?status=processing&limit=50&offset=0
JSON
{"count": 3, "limit": 50, "results": ["..."]}
  • Filtres : status et search (numéro de commande CardV, votre numéro de commande ou nom de l'opérateur).
  • limit vaut 20 par défaut, 100 au maximum. Une valeur plus grande est ramenée à 100.
  • Un limit ou un offset négatif ou non numérique renvoie HTTP 400.

#Statut

Text
accepted ──► processing ──► succeeded
                  │
                  ├──► manual_review ──► succeeded ou refunded
                  │
                  └──► failed ──► refunded   (argent rendu à votre portefeuille)
StatutTerminée ?Que faire
acceptedNonAttendez. Le portefeuille est débité, la recharge n'a pas commencé.
processingNonAttendez. Cela peut prendre plusieurs minutes. Ne repassez pas la commande.
manual_reviewNonCardV vérifie le résultat auprès de l'opérateur. Attendez.
succeededOuiLe téléphone a été rechargé. Prévenez votre client.
failedPas encoreLa recharge n'a pas abouti. Attendez refunded.
refundedOuiL'argent est revenu dans votre portefeuille. Vous pouvez passer une nouvelle commande.
  • Interrogez l'API après poll_after_seconds, puis espacez : 30 s, 60 s, puis toutes les 5 minutes. Restez sous la limite de requêtes.
  • Considérez un statut inconnu comme « pas encore terminée ».
  • Tant qu'une commande n'est pas terminée, n'envoyez pas d'autre recharge vers le même numéro avec un nouveau numéro de commande. Si la première aboutit aussi, le téléphone est rechargé deux fois.

#Webhooks et remboursements

Les commandes de recharge envoient les mêmes webhooks que les autres commandes : order.succeeded, order.failed et order.refunded. Le webhook contient le numéro de commande CardV et votre numéro de commande, avec une liste items vide. Après un webhook, consultez la commande avec GET/api/v1/recharge/orders/{order_id}.

Les remboursements sont automatiques. Quand l'opérateur confirme un échec, CardV rend l'intégralité du merchant_price à votre portefeuille et la commande passe à refunded. Le remboursement apparaît sur la page des transactions du Portail. Une recharge réussie ne peut être ni remboursée ni annulée.

#Erreurs

Les erreurs suivent les Conventions. Une demande de prix ou une commande refusée renvoie HTTP 400 et rien n'est débité.

CléOùQue faire
detailPrix, commandePays, opérateur, type ou montant indisponible. Vérifiez la liste des opérateurs.
amountPrix, commandePas un nombre, zéro ou hors limites.
local_currencyPrix, commandePas un code ISO 4217 à 3 lettres.
accountCommandeNuméro de téléphone manquant.
quote_tokenCommandeManquant, expiré, modifié ou ne correspondant pas. Consultez code, puis redemandez le prix.
balanceCommandeAjoutez des fonds dans le Portail.
riskCommandeVous avez atteint un plafond de commande. Contactez CardV.
external_order_idCommandeDéjà utilisé pour une autre commande. Voir Renvoyer sans risque.
walletCommandeAucun portefeuille actif. Contactez CardV.

Les erreurs quote_token sont accompagnées d'un code :

JSON
{"code": "quote_expired", "quote_token": "This quote expired. Refresh the price and confirm again."}
codeSignification
quote_requiredAucun quote_token n'a été envoyé.
quote_expiredPlus de 300 secondes se sont écoulées. Redemandez le prix.
quote_invalidModifié, ou prévu pour une autre recharge. Redemandez le prix.
price_changedVotre prix a changé depuis la demande. Redemandez le prix et confirmez le nouveau prix.

HTTP 403 signale un problème d'identifiants, de signature, d'IP ou de validation. Voir Authentification.

Une question sur votre intégration ? Écrivez à [email protected] en indiquant votre Merchant ID et l’identifiant de commande ou de requête.