Enregistrer une carte et la débiter plus tard
Encaissez un paiement par carte sur votre propre checkout, conservez la carte avec le consentement du client, puis débitez-la à nouveau plus tard en l'absence du client : frais de non-présentation, suppléments ajoutés après le départ, acompte complété. Cette page détaille tout le flux, de la création du premier checkout au débit de la carte enregistrée, avec les requêtes et réponses de chaque étape.
Activée par Yavin. L'enregistrement d'une carte et le débit d'une carte enregistrée nécessitent tous deux que la fonctionnalité de cartes enregistrées soit activée sur votre compte. Demandez son activation à votre contact Yavin ; d'ici là, les requêtes ci-dessous sont refusées.
Fonctionnement
Deux moments distincts, reliés par un seul identifiant. Le premier paiement enregistre la carte et vous donne stored_card.id. Plus tard, à tout moment, votre serveur débite cet identifiant sur un nouveau checkout. Le numéro de carte n'atteint jamais vos serveurs, ni dans un cas ni dans l'autre.
Avant de commencer
| Prérequis | Pourquoi |
|---|---|
| La fonctionnalité activée sur votre compte | Un seul interrupteur couvre l'enregistrement d'une carte via le widget carte et le débit d'une carte enregistrée via l'API. Demandez à Yavin de l'activer. |
| Le widget carte | Le consentement du titulaire de la carte est transmis avec le paiement : la carte est donc enregistrée depuis votre propre page via le widget (submit({ enrolConsent: true })). La page de paiement hébergée par Yavin ne le propose pas. Voir Intégration Drop-in du paiement en ligne. |
| Votre propre mention de consentement | Yavin ne recueille pas le consentement à votre place. Vous affichez votre propre mention d'information et ne transmettez enrolConsent: true que pour un client qui l'a acceptée. |
A webhook_url | L'identifiant de la carte enregistrée vous parvient dans le webhook payment_received. Conservez-le pour chaque checkout du parcours. |
| Un plafond de dépenses adapté | Les débits sur carte enregistrée sont plafonnés par client, sur toute la durée de vie du client, à un montant défini sur votre compte (300 € par défaut). Un débit qui ferait dépasser ce plafond au client est refusé avec amount_over_limit. Demandez à Yavin de le modifier. |
URL de base : production https://api.yavin.com/api/v5/ecommerce, sandbox https://api.sandbox.yavin.com/api/v5/ecommerce. Authentification : votre clé API dans l'en-tête Yavin-Secret.
Étape 1 - Enregistrer la carte
Étape 1 - Créer le checkout qui enregistre la carte
Créez le checkout côté serveur, exactement comme dans l'intégration Drop-in, avec deux ajouts : features.enrol_card et un objet customer.
features.enrol_cardest un booléen et vautfalsepar défaut. Chaque valeur defeaturesdoit être un véritable booléen- Avec
enrol_card: true, l'objetcustomerest obligatoire :first_name,last_nameet au moins l'un des champstelephone(50 caractères maximum) ouemail. Yavin conserve ce contact avec la carte enregistrée, et votre marchand le voit dans MyYavin - Une requête qui enfreint l'une de ces règles, ou qui provient d'un compte sans cette fonctionnalité, est rejetée avant la création de tout checkout :
422sur/checkouts/, avec une erreur désignantfeaturesoucustomer enrol_cardn'est pris en compte que par le widget de carte. Un checkout ouvert sur la page de paiement hébergée de Yavin n'envoie aucun consentement : rien n'y est donc enregistré
POST https://api.yavin.com/api/v5/ecommerce/checkouts/
Yavin-Secret: <your yavin_secret_key>
Content-Type: application/json
{ "checkout_external_id": "booking_88",
"amount": 2900,
"customer": { "first_name": "Ada", "last_name": "Lovelace",
"telephone": "+33612345678", "email": "ada@example.com" },
"features": { "enrol_card": true },
"return_url_success": "https://shop.example.com/return",
"return_url_cancelled": "https://shop.example.com/cancel",
"webhook_url": "https://shop.example.com/hooks/yavin" }// 201 Created
{ "checkout_token": "Ab3f...",
"payment_link": "https://..." }Étape 2 - Envoyer le consentement avec le paiement
Affichez le widget avec checkout_token, présentez votre mention de consentement, puis transmettez le consentement lors du paiement :
enrolConsentest facultatif et vautfalsepar défaut. Il doit être un véritable booléen :"true",1ounullfont quesubmit()rejette la valeur et que rien n'est envoyé- Le consentement ne s'applique qu'à cette tentative. Une nouvelle tentative qui ne le transmet pas n'enregistre rien, même si une tentative précédente l'avait fait
- Après une authentification renforcée 3-D Secure, c'est le consentement donné au début du paiement qui est pris en compte
- L'enregistrement de la carte ne fait jamais échouer le paiement : si la carte ne peut pas être enregistrée, le paiement aboutit quand même et aucune carte n'est renvoyée
const widget = window.YavinPaymentsSDK.mount(
document.getElementById('card'),
{ checkoutToken: 'Ab3f...' }
)
// the customer ticked your "keep my card for later payments" box
const result = await widget.submit({ enrolConsent: customerAgreed })Étape 3 - Lire la carte enregistrée
Une fois le paiement autorisé, le webhook payment_received arrive avec le checkout. Chaque transaction contient stored_card :
| Champ | Signification |
|---|---|
stored_card | null lorsqu'aucune carte enregistrée n'est liée à la transaction, ce qui est le cas dès que rien n'a été enregistré. |
stored_card.id | L'identifiant de la carte (un UUID). Conservez-le avec votre client ou votre réservation : c'est la valeur customer_card_id que vous envoyez pour débiter la carte. |
stored_card.expiration_date | YYYY-MM, le mois d'expiration de la carte, tel qu'imprimé sur la carte. Une carte est valide jusqu'à la fin de ce mois. null lorsque l'expiration est inconnue. |
Vérifiez-la avant de vous y fier. Si le webhook indique stored_card: null, le client a payé mais aucune carte n'est enregistrée, par exemple parce que le consentement n'a pas été envoyé. Gérez ce cas dans votre parcours : demandez un autre moyen de paiement plutôt que de supposer qu'une carte existe.
La carte est enregistrée lorsque le paiement est autorisé, avant toute capture, donc stored_card figure déjà dans le webhook payment_received. Seuls les paiements par carte sont enregistrés : un paiement par portefeuille électronique, comme Apple Pay, ne l'est jamais. Vous pouvez lire ce même objet à tout moment avec /get_cart_information/.
// webhook POST to your webhook_url
{ "action": "payment_received",
"checkout_external_id": "booking_88",
"requested_amount": 2900,
"asked_amount": 2900,
"paid_amount": 2900,
"service_fee": 0,
"status": "ok",
"transactions": [
{ "transaction_id": "trs_7f3a92c5",
"total_amount": 2900,
"gift_amount": 0,
"currency_code": "EUR",
"date_of_payment": "2026-11-03 14:35:15",
"gateway": "NEPTING_ECOMMERCE",
"issuer": "VISA",
"pan": "4970********0001",
"status": "ok",
"stored_card": { "id": "9b2f4c1e-7a3d-4f55-8a61-2d9c3b7e1a10",
"expiration_date": "2028-12" } } ] }Étape 2 - Débiter la carte enregistrée
Lorsque vous devez débiter, créez un nouveau checkout pour le nouveau montant, puis payez-le avec la carte enregistrée. Les deux appels sont effectués par votre serveur, sans client ni navigateur.
Étape 4 - Créer le checkout à débiter
- Attribuez-lui un nouveau
checkout_external_id: un identifiant est réservé définitivement, même par un checkout annulé - Laissez le mode de capture à sa valeur par défaut (instantanée). Un checkout créé avec
is_instant_capture: falsene peut pas être payé avec une carte enregistrée - Vous n'avez pas besoin de
enrol_cardni d'un client ici
POST https://api.yavin.com/api/v5/ecommerce/checkouts/
Yavin-Secret: <your yavin_secret_key>
Content-Type: application/json
{ "checkout_external_id": "booking_88_noshow",
"amount": 1500,
"webhook_url": "https://shop.example.com/hooks/yavin" }// 201 Created
{ "checkout_token": "Zk9q...",
"payment_link": "https://..." }Étape 5 - Payer avec la carte enregistrée
POST https://api.yavin.com/api/v5/ecommerce/checkouts/{checkout_token}/pay/
customer_card_idString (UUID)requis- Le stored_card.id que vous avez conservé à l'étape 3
La carte est débitée du montant total restant à payer sur le checkout. Le corps de la requête ne contient que customer_card_id : n'y joignez aucune donnée de carte.
La réponse est le Paiement en ligne : E-commerce API, renvoyé directement (sans être encapsulé dans data). Le même checkout est ensuite envoyé à votre webhook_url sous la forme payment_received, et la transaction est liée à la carte enregistrée via stored_card.
Définissez un délai d'attente côté client d'au moins 30 secondes et ne relancez jamais un débit à l'aveugle. Si vous n'avez pas obtenu de réponse, lisez d'abord le checkout avec /get_cart_information/ : le paiement a peut-être abouti.
POST https://api.yavin.com/api/v5/ecommerce/checkouts/Zk9q.../pay/
Yavin-Secret: <your yavin_secret_key>
Content-Type: application/json
{ "customer_card_id": "9b2f4c1e-7a3d-4f55-8a61-2d9c3b7e1a10" }// 200 OK, the Checkout object
{ "checkout_external_id": "booking_88_noshow",
"payment_link": "https://...",
"requested_amount": 1500,
"asked_amount": 1500,
"paid_amount": 1500,
"service_fee": 0,
"gift_amount": 0,
"status": "ok",
"transactions": [
{ "transaction_id": "trs_91c4d0aa",
"total_amount": 1500,
"gift_amount": 0,
"currency_code": "EUR",
"date_of_payment": "2026-11-20 09:12:44",
"gateway": "NEPTING_ECOMMERCE",
"issuer": "VISA",
"pan": "4970********0001",
"status": "ok",
"stored_card": { "id": "9b2f4c1e-7a3d-4f55-8a61-2d9c3b7e1a10",
"expiration_date": "2028-12" } } ] }Étape 6 - Confirmer
Considérez la réponse 200 et le webhook payment_received comme la confirmation, comme pour tout autre paiement. Pour réconcilier, lisez le checkout :
POST https://api.yavin.com/api/v5/ecommerce/get_cart_information/
{ "checkout_external_id": "booking_88_noshow" }Erreurs
Un débit refusé renvoie un corps d'erreur, de même forme pour tous les codes :
code est la valeur stable sur laquelle baser votre logique. transaction_id est présent lorsque la tentative a atteint la passerelle de paiement.
La carte ne peut pas être débitée
| code | HTTP | Signification et action à mener |
|---|---|---|
card_not_found | 404 | Aucune carte enregistrée avec cet identifiant sur votre compte. Vérifiez l'identifiant que vous avez conservé : la carte d'un autre marchand a le même aspect. |
card_not_consented | 422 | La carte n'a jamais été enregistrée avec le consentement du titulaire. Elle ne peut donc pas être débitée. |
card_disabled | 422 | La carte a été désactivée, par exemple par votre marchand dans MyYavin. |
card_expired | 422 | Le mois d'expiration de la carte est dépassé. Demandez une nouvelle carte au client. Une carte enregistrée sans date d'expiration n'expire jamais côté Yavin. |
amount_over_limit | 422 | Ce débit ferait dépasser la limite définie sur votre compte pour le total débité au client via des cartes enregistrées. Demandez à Yavin de la relever, ou encaissez autrement. |
amount_invalid | 422 | Le montant à débiter n'est pas supérieur à zéro. |
company_not_configured | 422 | Le compte n'est pas configuré pour débiter des cartes enregistrées : la fonctionnalité est désactivée ou un contrat de paiement est manquant. Contactez Yavin. |
Le checkout ne peut pas être payé
| code | HTTP | Signification et action à mener |
|---|---|---|
invalid_payload | 422 | Le corps de la requête n'est pas un customer_card_id valide (un UUID), ou il le combine avec des données de carte. errors indique le champ concerné. |
checkout_not_found | 404 | Aucun checkout avec ce token sur votre compte. |
checkout_cancelled | 409 | Le checkout a été annulé. Créez-en un nouveau. |
checkout_already_paid | 409 | Le checkout est déjà payé. Ne débitez pas à nouveau. |
checkout_not_payable | 409 | Le checkout n'attend pas de paiement. |
attempt_in_progress | 409 | Une autre tentative de paiement est en cours sur ce checkout. Patientez, puis consultez le checkout avant de réessayer. |
checkout_amount_changed | 409 | Le montant à payer a changé pendant l'appel. Consultez le checkout et décidez de la suite. |
checkout_requires_capture | 422 | Le checkout utilise la capture différée, qu'une carte enregistrée ne peut pas payer. Créez un checkout avec la capture immédiate par défaut. |
checkout_version_incompatible | 422 | Ce checkout ne peut pas être payé de serveur à serveur. Créez-le avec POST /checkouts/. |
Le paiement a été tenté
| code | HTTP | Signification et action à mener |
|---|---|---|
payment_declined | 402 | L'émetteur a refusé le débit. Rien n'a été prélevé. Demandez au client un autre moyen de paiement. |
insufficient_funds | 402 | Le solde de la carte est insuffisant. Rien n'a été prélevé. |
gateway_connection_error | 503 | La passerelle de paiement n'a pas pu être jointe, le débit n'a donc pas été effectué. Vous pouvez réessayer sans risque. |
acquirer_timeout | 504 | L'acquéreur n'a pas répondu dans le délai imparti. Le résultat est inconnu. Consultez le checkout avant de réessayer. |
gateway_error | 502 | La passerelle a renvoyé une réponse dont Yavin n'a pas pu tirer de conclusion. Le résultat n'est pas certain. Consultez le checkout avant de réessayer. |
Les codes invalid_auth, challenge_in_progress et challenge_not_completable sont rares : considérez que le paiement n'a pas été prélevé, et contactez le support s'ils persistent.
Un résultat inconnu n'est pas un échec. Après acquirer_timeout ou gateway_error, ou après tout appel resté sans réponse, consultez le checkout avec /get_cart_information/ et examinez ses champs status et paid_amount avant de débiter à nouveau. Rejouer un débit sans surveillance expose le client à un double débit.
{ "detail": "The payment was declined.",
"code": "payment_declined",
"errors": {},
"transaction_id": "trs_91c4d0aa" }Bon à savoir
- Vous ne manipulez jamais de données de carte. Vous conservez un identifiant et une date d'expiration. Le numéro de carte et le CVC restent dans le champ carte de Yavin et n'atteignent jamais votre page ni vos serveurs
- Conservez la preuve du consentement. Il vous revient d'enregistrer la mention affichée et le moment où le client l'a acceptée
- Votre marchand voit les cartes dans MyYavin. La section des cartes enregistrées liste les cartes enregistrées depuis vos checkouts, avec les coordonnées du client, et permet d'en désactiver une. Une carte désactivée est ensuite refusée avec
card_disabled - Les cartes enregistrées en magasin peuvent être débitées de la même manière. Une carte enregistrée sur un terminal Yavin est débitée avec le même appel et son identifiant, à condition que la fonctionnalité de cartes enregistrées soit activée sur votre compte
- Plusieurs paiements, un seul client. Le plafond de dépenses additionne tous les débits réussis par carte enregistrée d'un même client, toutes cartes enregistrées confondues