Résiliation d'abonnement en ligne
La résiliation en ligne permet à un membre de résilier son abonnement de manière autonome, via votre application ou portail, sans intervention du club.
Retrouvez ce scénario dans notre collection Bruno
Gagnez du temps dans votre implémentation en observant l'enchaînement des appels et en récupérant les requêtes directement depuis Bruno.
Pourquoi la résiliation en ligne est-elle obligatoire ?
Depuis juin 2023, la législation française (loi dite « résiliation en 3 clics ») impose à tout professionnel proposant des abonnements de permettre à ses clients de résilier en ligne, par la même voie que la souscription. Cette obligation s'étend progressivement à d'autres pays européens.
En tant que partenaire, si vous proposez la souscription en ligne via l'API Resamania, vous devez également proposer la résiliation en ligne. Ce document décrit les endpoints disponibles pour l'implémenter.
Configuration côté club
La résiliation en ligne doit être activée par le club dans ses paramètres (Paramètres > Clubs / Juridiques > Onglet Résiliation > "Autoriser la résiliation des abonnements en ligne"). Sans cette activation, vos appels à POST /online_cancellation seront rejetés.
Macro-process
Étape 1 — Récupérer les motifs de résiliation
Les motifs de résiliation sont configurés par le club. Seuls les motifs explicitement inclus dans la configuration onlineCancellation du club sont acceptés pour une résiliation en ligne.
GET /{clientToken}/referentials/cancellation_motives
curl --location 'https://{{baseUrl}}/{{clientToken}}/referentials/cancellation_motives' \
--header 'accept: application/ld+json' \
--header 'authorization: Bearer {{bearer}}' \
--header 'x-user-club-id: {{clubId}}'{
"@context": "/demoapi/contexts/CancellationMotive",
"@id": "/demoapi/referentials/cancellation_motives",
"@type": "hydra:Collection",
"hydra:member": [
{
"@id": "/demoapi/referentials/cancellation_motives/1",
"@type": "CancellationMotive",
"id": 1,
"name": "Déménagement"
},
{
"@id": "/demoapi/referentials/cancellation_motives/2",
"@type": "CancellationMotive",
"id": 2,
"name": "Raisons médicales"
}
],
"hydra:totalItems": 2
}WARNING
Ne présentez à l'utilisateur que les motifs configurés pour la résiliation en ligne par le club (champ includedCancellationMotives de la configuration onlineCancellation du club). L'usage d'un motif non autorisé entraînera une erreur api.error.subscription-cancel-online.motive-id-not-include.
Étape 2 — Récupérer les dates de résiliation possibles
Avant de présenter un sélecteur de date à l'utilisateur, récupérez les dates autorisées pour la résiliation de l'abonnement. En mode résiliation en ligne, la résiliation se fait toujours à une date de renouvellement (fin de mois complet), jamais à une date libre.
GET /{clientToken}/subscriptions/{id}/cancellation_dates
| Paramètre | Type | Description | Exemple |
|---|---|---|---|
receptionDate | string (query, optionnel) | Date de réception de la demande. Par défaut : aujourd'hui. | 2024-06-01 |
restrict | boolean (query, optionnel) | Restreint les dates aux dates de renouvellement. Par défaut : true. | true |
curl --location 'https://{{baseUrl}}/{{clientToken}}/subscriptions/{{subscriptionId}}/cancellation_dates?receptionDate=2024-06-01' \
--header 'accept: application/json' \
--header 'authorization: Bearer {{bearer}}' \
--header 'x-user-club-id: {{clubId}}'{
"dates": [
{ "date": "2024-07-01" },
{ "date": "2024-08-01" },
{ "date": "2024-09-01" }
]
}INFO
Si la réponse est 204 No Content, aucune date de résiliation n'est disponible (ex : abonnement CDD non échu, abonnement déjà résilié).
Étape 3 — Créer la résiliation
Deux endpoints sont disponibles selon le canal d'origine de la demande.
Option A — POST /subscriptions/{id}/online_cancellation (recommandé pour l'espace membre)
Cet endpoint est recommandé pour les résiliations initiées par le membre. Il vérifie l'ensemble des règles configurées par le club (activation de la fonctionnalité, motif autorisé, plafond de dette, tags contact, délai minimum, quota journalier) avant de créer la résiliation.
La résiliation est créée et immédiatement acceptée en une seule opération, avec inputChannel: member.
POST /{clientToken}/subscriptions/{id}/online_cancellation
| Paramètre | Type | Requis | Description |
|---|---|---|---|
cancellationDate | string (date ISO) | ✅ | Date effective de résiliation. Doit correspondre à une date retournée par GET /cancellation_dates. |
cancellationMotiveId | string (IRI) | ✅ | IRI du motif de résiliation. Doit figurer dans les motifs autorisés par le club. |
comment | string | ❌ | Commentaire libre du membre. |
curl --location --request POST 'https://{{baseUrl}}/{{clientToken}}/subscriptions/{{subscriptionId}}/online_cancellation' \
--header 'accept: application/ld+json' \
--header 'authorization: Bearer {{bearer}}' \
--header 'x-user-club-id: {{clubId}}' \
--header 'Content-Type: application/json' \
--data '{
"cancellationDate": "2024-07-01",
"cancellationMotiveId": "/demoapi/referentials/cancellation_motives/1",
"comment": "Je déménage à l'\''étranger."
}'{
"@context": "/demoapi/contexts/Subscription",
"@id": "/demoapi/subscriptions/42",
"@type": "Subscription",
"id": 42,
"cancellations": [
"/demoapi/cancellations/99"
]
}Codes d'erreur
| Code HTTP | Message d'erreur | Cause |
|---|---|---|
400 | api.error.subscription-cancel-online.missing-date | Le champ cancellationDate est absent du body. |
400 | api.error.subscription-cancel-online.missing-motive-id | Le champ cancellationMotiveId est absent du body. |
400 | api.error.subscription-cancel-online.invalid-date | Le format de cancellationDate est invalide. |
400 | api.error.subscription-cancel-online.already-canceled | L'abonnement est déjà résilié. |
400 | api.error.subscription-cancel-online.invalid-cancel-date | La date est dans le passé, antérieure à la fin d'engagement, ou ne respecte pas le préavis configuré. |
400 | api.error.subscription-cancel-online.cancellation-date-invalid | La date ne figure pas parmi les dates autorisées (non retournée par GET /cancellation_dates). |
400 | api.error.subscription-cancel-online.subscription-contact-different-club | L'abonnement et le contact appartiennent à des clubs différents. |
400 | api.error.subscription-cancel-online.not-enabled | La résiliation en ligne n'est pas activée sur ce club pour ce type d'abonnement. |
400 | api.error.subscription-cancel-online.motive-id-not-include | Le motif fourni n'est pas dans la liste des motifs autorisés pour la résiliation en ligne. |
400 | api.error.subscription-cancel-online.contact-tag-not-eligible | Le contact ne possède pas les tags requis pour résilier en ligne. |
400 | api.error.subscription-cancel-online.contact-tag-prohibited | Le contact possède un tag qui l'exclut de la résiliation en ligne. |
400 | api.error.subscription-cancel-online.contact-debt-over-limit | La dette du contact dépasse le plafond autorisé par le club. |
409 | (Conflict) | Une résiliation est déjà en cours de traitement sur cet abonnement (verrou concurrent). |
Option B — POST /cancellations (back-office ou admin)
Cet endpoint crée directement une résiliation sans vérifier les règles de la résiliation en ligne. Il est adapté aux cas back-office (résiliation saisie par un employé du club depuis votre interface) ou aux imports.
Le champ inputChannel distingue l'origine de la saisie :
admin: résiliation saisie par un employé depuis le back-office ou votre application partenaire.member: résiliation initiée par le membre (préférerPOST /online_cancellationdans ce cas).
Workflow à deux étapes
Contrairement à POST /online_cancellation, une résiliation créée ici a le statut submitted. Elle doit ensuite être acceptée via POST /{clientToken}/cancellations/{id}/transitions avec { "transition": "accept" } pour prendre effet sur l'abonnement.
POST /{clientToken}/cancellations
| Paramètre | Type | Requis | Description |
|---|---|---|---|
subscription | string (IRI) | ✅* | IRI de l'abonnement à résilier. Exclusif avec subscriptionOption. |
subscriptionOption | string (IRI) | ✅* | IRI de l'option à résilier. Exclusif avec subscription. |
receptionDate | string (date ISO) | ✅ | Date de réception de la demande de résiliation. |
cancellationDate | string (date ISO) | ✅ | Date effective de la résiliation. |
cancellationMotiveId | string (IRI) | ✅ | IRI du motif de résiliation. |
inputChannel | string | ✅ | Canal de saisie : admin ou member. |
comment | string | ❌ | Commentaire interne. |
refundPolicy | string | ❌ | Politique de remboursement : period_end (défaut), period_start, prorata. |
createCreditNote | boolean | ❌ | Générer un avoir. Par défaut true. |
* L'un des deux est obligatoire, mais pas les deux simultanément.
curl --location --request POST 'https://{{baseUrl}}/{{clientToken}}/cancellations' \
--header 'accept: application/ld+json' \
--header 'authorization: Bearer {{bearer}}' \
--header 'x-user-club-id: {{clubId}}' \
--header 'Content-Type: application/json' \
--data '{
"subscription": "/demoapi/subscriptions/42",
"receptionDate": "2024-06-01",
"cancellationDate": "2024-07-01",
"cancellationMotiveId": "/demoapi/referentials/cancellation_motives/1",
"comment": "Résiliation demandée par le membre",
"inputChannel": "admin"
}'{
"@context": "/demoapi/contexts/Cancellation",
"@id": "/demoapi/cancellations/99",
"@type": "Cancellation",
"subscription": "/demoapi/subscriptions/42",
"subscriptionOption": null,
"receptionDate": "2024-06-01",
"cancellationDate": "2024-07-01",
"cancellationMotiveId": "/demoapi/referentials/cancellation_motives/1",
"comment": "Résiliation demandée par le membre",
"status": "submitted",
"inputChannel": "admin",
"type": "simple",
"contact": {
"@id": "/demoapi/contacts/123",
"familyName": "Doe",
"givenName": "John",
"number": "C123"
}
}Statuts d'une résiliation
| Statut | Description |
|---|---|
submitted | Résiliation créée, en attente de validation. |
accepted | Résiliation acceptée — l'abonnement s'arrêtera à la cancellationDate. |
rejected | Résiliation refusée. |
canceled | Résiliation annulée. |
Points d'attention
Abonnements CDD et comptants
Les abonnements à durée déterminée (CDD) ou comptants ne peuvent pas être résiliés avant leur date de fin via la résiliation en ligne.
Options liées
La résiliation d'un abonnement principal entraîne automatiquement la résiliation de ses options liées. Les options obligatoires et les options appartenant à un package ne sont pas résiliables indépendamment.
Réseau multi-clubs
Dans un réseau, un contact rattaché au club A ne peut pas résilier en ligne un abonnement souscrit dans le club B.