> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hub2.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Intégration

> Apprenez à inscrire des clients, gérer des abonnements et suivre les prélèvements récurrents avec l'API HUB2.

## Prérequis

### Permissions de la clé API

Les abonnements utilisent les mêmes clés API HUB2 et les mêmes en-têtes `ApiKey`, `MerchantId` et `Environment` que le reste de l'API. Accordez à la clé les permissions requises par les points de terminaison que vous appelez.

| Permission | Autorise |
| :- | :- |
| `Api.subscription_intent_create` | Créer des liens d'abonnement et démarrer des intentions d'abonnement |
| `Api.subscription_intent_read` | Consulter le statut d'une inscription |
| `Api.subscription_list` | Lister les abonnements et consulter l'historique de leurs débits |
| `Api.subscription_cancel` | Résilier un abonnement |
| `Api.subscription_suspend` | Suspendre un abonnement |
| `Api.subscription_charge` | Débiter un abonnement à la demande |

> **[Clés API](https://docs.hub2.io/integration/fr/getting_started/api_keys.md)**

### Conditions du compte

* Votre marchand doit être enregistré en Côte d'Ivoire. Sinon, une intention d'abonnement est refusée avec une erreur `403` et le code `card_on_file_ineligible_country`.
* Le card-on-file doit être activé sur votre marchand par HUB2. Sinon, une intention d'abonnement est refusée avec une erreur `403` et le code `card_on_file_disabled`.
* Ces deux mêmes conditions s'appliquent à un lien d'abonnement. Le lien peut être créé, mais le client ne peut pas finaliser l'inscription depuis sa page.
* Un marchand fermé ou suspendu ne peut pas utiliser ces points de terminaison (`403`, codes `merchant_closed` et `merchant_suspended`).
* Un marchand parent qui possède des sous-marchands doit appeler les points de terminaison avec le `MerchantId` du sous-marchand. Avec son propre `MerchantId`, il reçoit une erreur `403` avec le code `group_owner_transaction_forbidden`. Un sous-marchand ne voit et ne gère que les abonnements qu'il a inscrits. Consultez la page [sous-marchands](https://docs.hub2.io/integration/fr/submerchants/submerchants_integration.md).

## Inscrire un client

L'inscription enregistre la carte du client et crée l'abonnement. Choisissez l'une des deux méthodes.

| | Lien d'abonnement | Intention d'abonnement |
| :- | :- | :- |
| Point de terminaison | `POST /subscription-links` | `POST /subscription-intents` |
| Consentement et e-mail recueillis par | Une page hébergée par HUB2 | Votre propre interface |
| Types d'abonnement | Planifié | Planifié et à la demande |
| Suivre le résultat avec | Webhook `subscription.created`, liste des abonnements | Webhook `subscription.created`, point de terminaison de statut de l'inscription |

### S'inscrire avec un lien d'abonnement

[Créer un lien d'abonnement](https://docs.hub2.io/api-reference/subscriptions/create-a-subscription-link.md)

Le lien décrit le prélèvement récurrent. Partagez avec votre client l'URL retournée dans la réponse.

```bash Curl theme={null}
curl --location 'https://api.hub2.io/subscription-links' \
--header 'ApiKey: [REDACTED]' \
--header 'MerchantId: [REDACTED]' \
--header 'Environment: sandbox' \
--header 'Content-Type: application/json' \
--data '{
    "purchaseReference": "SUB_2026_001",
    "description": "Forfait mensuel",
    "amount": 5000,
    "currency": "XOF",
    "frequency": "monthly",
    "startDate": "2026-12-01",
    "country": "CI",
    "expirationDate": "2026-11-30T23:59:59.000Z",
    "successUrl": "https://merchant.example.com/subscription/success",
    "failureUrl": "https://merchant.example.com/subscription/failure"
}'
```

| Champ | Obligatoire | Description |
| :- | :- | :- |
| `amount` | Oui | Montant prélevé à chaque échéance, un entier positif exprimé en unités entières de la devise (`5000` XOF). Les devises avec décimales ne sont pas prises en charge pour les prélèvements planifiés : le montant est facturé en unités entières, et non dans la plus petite unité |
| `currency` | Oui | Code de devise ISO 4217 à trois lettres, par exemple `XOF` |
| `purchaseReference` | Oui | Votre référence pour cet abonnement, 100 caractères au maximum. Elle devient le `customerId` de l'abonnement |
| `frequency` | Oui | `weekly` ou `monthly` |
| `startDate` | Oui | Date ISO 8601 du premier prélèvement |
| `country` | Oui | Code pays ISO 3166-1 alpha-2 à deux lettres du pays où l'inscription est traitée |
| `description` | Non | Description du lien, 300 caractères au maximum |
| `expirationDate` | Non | Date d'expiration du lien. Si elle est omise, le lien expire 15 minutes après sa création |
| `successUrl` | Non | URL `https` vers laquelle le client est redirigé après une inscription réussie |
| `failureUrl` | Non | URL `https` vers laquelle le client est redirigé après une inscription échouée |

Si vous omettez `successUrl` ou `failureUrl`, le client arrive sur une page de résultat hébergée par HUB2.

La réponse est un objet lien. Le champ `url` est la page à partager, et `status` vaut `active` tant que le lien n'est pas utilisé ou n'a pas expiré.

```json theme={null}
{
    "id": "pl_Doc7fK2mQx4vN8sT1aB3c",
    "url": "https://pay.hub2.io/pl_Doc7fK2mQx4vN8sT1aB3c",
    "status": "active",
    "purchaseReference": "SUB_2026_001",
    "amount": 5000,
    "currency": "XOF",
    "expirationDate": "2026-11-30T23:59:59.000Z"
}
```

Créer un lien avec la `purchaseReference` d'un lien encore actif retourne ce lien existant au lieu d'en créer un nouveau.

#### Ce que voit le client

**1. La page d'inscription**

Le client ouvre le lien. La page affiche le nom de votre entreprise et le montant récurrent. Le client saisit une adresse e-mail, coche la case de consentement qui autorise les prélèvements récurrents, puis clique sur le bouton d'inscription.

**2. La page de saisie de la carte**

Le client est envoyé vers une page sécurisée pour saisir sa carte et terminer l'authentification 3D Secure. Le client n'est pas débité à cette étape.

**3. Le retour**

Le client revient sur votre `successUrl` ou votre `failureUrl`.

Le lien est à usage unique. Il passe à `completed` dès que le client a été envoyé vers la page de saisie de la carte, quel que soit le résultat final de l'authentification de la carte. Si HUB2 refuse l'inscription avant la page de saisie de la carte, le lien reste `active` et le client peut réessayer.

> [!WARNING]
>
> N'utilisez pas les webhooks `payment_links.completed` ou `payment_links.paid`, ni une redirection vers votre `successUrl`, comme preuve que l'abonnement existe. Attendez le webhook `subscription.created`, ou listez les abonnements avec `customerId` égal à votre `purchaseReference`.

### S'inscrire avec une intention d'abonnement

[Inscrire un client](https://docs.hub2.io/api-reference/subscriptions/enroll-a-customer.md)

Une intention d'abonnement se déroule en trois étapes :

1. Créez l'intention d'abonnement depuis votre serveur.
2. Envoyez le client vers la page de saisie de la carte, en effectuant l'action retournée par l'API.
3. Récupérez le résultat de l'inscription.

#### 1. Créer l'intention d'abonnement

Recueillez le consentement du client dans votre interface avant l'appel. Le consentement est obligatoire : la requête est rejetée s'il est absent ou si `given` n'est pas à `true`.

Un abonnement planifié exige `amount`, `frequency` et `startDate`. Un abonnement à la demande n'accepte ni `frequency` ni `startDate`, car rien n'est planifié. Pour un abonnement à la demande, `amount` est ignoré dans l'environnement `live`, mais reste validé s'il est envoyé ; en `sandbox`, il sélectionne le résultat simulé.

```bash Planifié theme={null}
curl --location 'https://api.hub2.io/subscription-intents' \
--header 'ApiKey: [REDACTED]' \
--header 'MerchantId: [REDACTED]' \
--header 'Environment: sandbox' \
--header 'Content-Type: application/json' \
--data-raw '{
    "customerReference": "cust_01924059",
    "email": "customer@example.com",
    "billingMode": "rbs_scheduled",
    "amount": 5000,
    "currency": "XOF",
    "frequency": "monthly",
    "startDate": "2026-12-01",
    "consent": {
        "given": true,
        "timestamp": "2026-11-03T10:00:00.000Z",
        "reference": "consent-3f6d2c1e-9a47-4b0e-8d52-7c1a5e9b0f34"
    },
    "onSuccessRedirectionUrl": "https://merchant.example.com/subscription/success",
    "onFailedRedirectionUrl": "https://merchant.example.com/subscription/failure"
}'
```

```bash À la demande theme={null}
curl --location 'https://api.hub2.io/subscription-intents' \
--header 'ApiKey: [REDACTED]' \
--header 'MerchantId: [REDACTED]' \
--header 'Environment: sandbox' \
--header 'Content-Type: application/json' \
--data-raw '{
    "customerReference": "cust_01924059",
    "email": "customer@example.com",
    "billingMode": "merchant_initiated",
    "currency": "XOF",
    "consent": {
        "given": true,
        "timestamp": "2026-11-03T10:00:00.000Z",
        "reference": "consent-3f6d2c1e-9a47-4b0e-8d52-7c1a5e9b0f34"
    },
    "onSuccessRedirectionUrl": "https://merchant.example.com/subscription/success",
    "onFailedRedirectionUrl": "https://merchant.example.com/subscription/failure"
}'
```

| Champ | Obligatoire | Description |
| :- | :- | :- |
| `customerReference` | Oui | Votre référence du client, 255 caractères au maximum. Elle devient le `customerId` de l'abonnement |
| `email` | Oui | Adresse e-mail du client. HUB2 y envoie les notifications d'inscription et de prélèvement |
| `currency` | Oui | Code de devise ISO 4217 à trois lettres |
| `consent` | Oui | Le consentement du client, décrit ci-dessous |
| `billingMode` | Non | `rbs_scheduled` (par défaut) ou `merchant_initiated` |
| `amount` | Planifié : obligatoire. À la demande : sélection du résultat en `sandbox` uniquement | Montant récurrent, un entier positif exprimé en unités entières de la devise, jusqu'à 9999999999. Une devise avec décimales n'est pas refusée mais facturée en unités entières : `1500` EUR est facturé 1 500 EUR, contrairement au `amount` d'un débit, exprimé dans la plus petite unité |
| `frequency` | Planifié uniquement | `weekly` ou `monthly`. Refusé pour un abonnement à la demande |
| `startDate` | Planifié uniquement | Date ISO 8601 du premier prélèvement. Refusée pour un abonnement à la demande |
| `onSuccessRedirectionUrl` | Non | URL `https` vers laquelle le client est redirigé après une inscription réussie, 2084 caractères au maximum |
| `onFailedRedirectionUrl` | Non | URL `https` vers laquelle le client est redirigé après une inscription échouée, 2084 caractères au maximum |

L'objet `consent` comporte trois champs :

| Champ | Description |
| :- | :- |
| `given` | Doit valoir `true` |
| `timestamp` | Date et heure ISO 8601 auxquelles le client a donné son consentement |
| `reference` | Votre identifiant de ce consentement, 64 caractères au maximum, composé uniquement de lettres, de chiffres et de tirets |

> [!WARNING]
>
> La `reference` du consentement doit être unique au sein de votre compte marchand, sous-marchands de votre groupe compris. Un autre marchand HUB2 peut utiliser la même valeur. Utilisez un UUID, éventuellement avec un préfixe, comme dans l'exemple. Réutiliser une référence pour réessayer la même inscription du même client est sans risque. Une référence déjà utilisée pour un autre client, ou déjà inscrite pour un autre sous-marchand de votre groupe, fait échouer l'inscription avec la cause d'échec `consent_reference_in_use`, après que le client a terminé la page de saisie de la carte. Une référence déjà inscrite dans l'autre mode (`sandbox` ou `live`) ou, pour un sous-marchand, déjà utilisée par son marchand parent ou un autre sous-marchand, est refusée immédiatement à la création de l'intention d'abonnement, avec une erreur `409` et le code `card_on_file_consent_reference_in_use`.

La réponse contient l'identifiant du paiement d'inscription et son statut. Conservez le `paymentId` : il identifie l'inscription dans les étapes suivantes.

```json theme={null}
{
    "paymentId": "pay_Doc5GBc53h6dHvuuq4vlc",
    "status": "action_required",
    "action": {
        "type": "redirection",
        "data": {
            "url": "https://card-page.example.com/pay",
            "method": "post"
        }
    }
}
```

| `status` | Signification |
| :- | :- |
| `action_required` | Le client doit terminer la page de saisie de la carte. L'objet `action` décrit comment l'y envoyer |
| `pending` | L'inscription est en cours de traitement. Consultez son statut |
| `successful` | L'abonnement a été créé |
| `failed` | L'inscription a été refusée. Consultez son statut pour lire la cause d'échec |

#### 2. Envoyer le client vers la page de saisie de la carte

Lorsque `status` vaut `action_required`, `action` a la même forme que le `nextAction` d'un paiement. Construisez un formulaire à partir de `action.data.url`, `action.data.method` et des champs de `action.data.data`, puis soumettez-le depuis le navigateur du client.

```javascript theme={null}
function openCardPage(action) {
  const form = document.createElement('form');
  form.method = action.data.method;
  form.action = action.data.url;

  for (const [name, value] of Object.entries(action.data.data ?? {})) {
    const input = document.createElement('input');
    input.type = 'hidden';
    input.name = name;
    input.value = String(value);
    form.appendChild(input);
  }

  document.body.appendChild(form);
  form.submit();
}
```

Le client saisit sa carte, termine l'authentification 3D Secure et revient sur `onSuccessRedirectionUrl` ou `onFailedRedirectionUrl`. Si HUB2 n'a pas encore enregistré de résultat lorsque le client revient, celui-ci est envoyé vers l'URL d'échec. Une redirection ne suffit donc pas pour conclure : confirmez toujours le résultat avec le point de terminaison de statut ou le webhook `subscription.created`.

#### 3. Récupérer le résultat de l'inscription

[Récupérer le statut d'une inscription](https://docs.hub2.io/api-reference/subscriptions/retrieve-an-enrollment-status.md)

Abonnez-vous au webhook `subscription.created` pour savoir que l'inscription a réussi. Aucun webhook n'est envoyé lorsqu'une inscription échoue : interrogez donc le statut de l'inscription pour détecter les échecs et les inscriptions abandonnées.

```bash Curl theme={null}
curl --location 'https://api.hub2.io/subscription-intents/pay_Doc5GBc53h6dHvuuq4vlc/status' \
--header 'ApiKey: [REDACTED]' \
--header 'MerchantId: [REDACTED]' \
--header 'Environment: sandbox'
```

```json theme={null}
{
    "paymentId": "pay_Doc5GBc53h6dHvuuq4vlc",
    "status": "successful",
    "subscriptionId": 42,
    "createdAt": "2026-11-03T10:00:02.113Z",
    "updatedAt": "2026-11-03T10:01:45.870Z"
}
```

| `status` | Signification | Étape suivante |
| :- | :- | :- |
| `pending` | L'inscription a été créée et rien n'est encore revenu | Continuez à interroger |
| `action_required` | Le client a été envoyé vers la page de saisie de la carte et l'inscription n'est pas conclue | Continuez à interroger. Un client qui abandonne la page reste dans ce statut et n'est jamais signalé comme `failed` : appliquez votre propre délai d'expiration |
| `successful` | L'abonnement existe. `subscriptionId` est présent | Conservez `subscriptionId`, l'identifiant à utiliser avec les points de terminaison `/subscriptions` |
| `failed` | L'inscription a été refusée ou n'a pas pu être enregistrée. `failureCause` est présent | Lisez `failureCause.code` et laissez le client réessayer |

Une inscription échouée retourne un `failureCause` avec un `code`, un `message` et, pour des paramètres invalides, la liste des paramètres concernés dans `params`.

```json theme={null}
{
    "paymentId": "pay_Doc5GBc53h6dHvuuq4vlc",
    "status": "failed",
    "failureCause": {
        "code": "authentication_failed",
        "message": "Payment failed. The customer failed to authenticate the payment."
    },
    "createdAt": "2026-11-03T10:00:02.113Z",
    "updatedAt": "2026-11-03T10:01:45.870Z"
}
```

Les causes d'échec propres à une inscription sont :

| Code | Description |
| :- | :- |
| `authentication_failed` | Le client n'a pas terminé l'authentification 3D Secure, ou elle a échoué |
| `issuer_authentication_unavailable` | Le service d'authentification de l'émetteur de la carte n'a pas pu être joint |
| `consent_reference_in_use` | La référence de consentement est déjà utilisée par une autre inscription. Utilisez une référence unique |
| `internal_error` | L'inscription n'a pas pu être enregistrée. Réessayez |

Une inscription peut aussi échouer avec les autres causes d'échec liées à la carte, par exemple `card_declined` ou `card_expired`. Consultez la liste complète dans [Causes d'échec des paiements](https://docs.hub2.io/integration/fr/payments/payments_failure_causes.md). Traitez les codes inconnus comme un échec générique.

Le point de terminaison de statut répond `404` lorsqu'aucune inscription portant cet identifiant n'appartient à votre marchand dans l'environnement courant.

> [!WARNING]
>
> Ce point de terminaison est limité à 5 requêtes par tranche de 5 secondes pour une même inscription. Au-delà, l'API renvoie `HTTP 429 - Too Many Requests`. Interrogez-le toutes les quelques secondes et arrêtez dès que le statut est `successful` ou `failed`. Consultez les [limites de flux](https://docs.hub2.io/documentation/fr/limits.md).

## Gérer les abonnements

Utilisez dans les points de terminaison ci-dessous l'`id` de l'abonnement, retourné sous le nom `subscriptionId` par le point de terminaison de statut de l'inscription.

### Statuts d'un abonnement

| Statut | Signification |
| :- | :- |
| `active` | L'abonnement est en cours. Un abonnement planifié est prélevé selon son calendrier, et un abonnement à la demande peut être débité |
| `delinquent` | Un abonnement planifié dont les prélèvements sont en échec de paiement. Il peut encore être suspendu ou résilié |
| `suspended` | Les prélèvements sont en pause, et un abonnement à la demande ne peut pas être débité |
| `cancelled` | Statut final. Plus aucun prélèvement ne sera effectué |
| `completed` | Statut final. Un abonnement planifié arrivé au terme de son calendrier |
| `created`, `pending` | Statuts transitoires. Traitez-les comme non encore actifs |

### Lister les abonnements

[Récupérer une collection d'abonnements](https://docs.hub2.io/api-reference/subscriptions/retrieve-a-subscription-collection.md)

```bash theme={null}
curl --location 'https://api.hub2.io/subscriptions?status=active&billingMode=rbs_scheduled&page=1&perPage=10' \
--header 'ApiKey: [REDACTED]' \
--header 'MerchantId: [REDACTED]' \
--header 'Environment: sandbox'
```

Les filtres suivants sont disponibles : `customerId` (correspondance exacte avec votre référence du client), `billingMode` (`rbs_scheduled` ou `merchant_initiated`) et `status`. Les résultats sont paginés avec `page` (`1` par défaut) et `perPage` (`10` par défaut), et l'en-tête `Content-Range` indique la plage et le total.

La réponse est un tableau d'abonnements. Chaque élément ressemble à ceci :

```json theme={null}
{
    "id": 42,
    "cybersourceSubscriptionId": "6000123456789012345678",
    "status": "active",
    "billingMode": "rbs_scheduled",
    "frequency": "monthly",
    "amount": "5000.00",
    "currency": "XOF",
    "startDate": "2026-12-01",
    "customerId": "cust_01924059",
    "createdAt": "2026-11-03T10:01:45.870Z"
}
```

Pour un abonnement à la demande, `cybersourceSubscriptionId`, `frequency`, `amount` et `startDate` valent `null`.

### Suspendre un abonnement

[Suspendre un abonnement](https://docs.hub2.io/api-reference/subscriptions/suspend-a-subscription.md)

```bash theme={null}
curl --location --request POST 'https://api.hub2.io/subscriptions/42/suspend' \
--header 'ApiKey: [REDACTED]' \
--header 'MerchantId: [REDACTED]' \
--header 'Environment: sandbox'
```

Seul un abonnement `active` ou `delinquent` peut être suspendu. L'appel retourne l'abonnement mis à jour et déclenche le webhook `subscription.suspended`. L'API ne propose pas d'opération pour reprendre un abonnement suspendu.

### Résilier un abonnement

[Résilier un abonnement](https://docs.hub2.io/api-reference/subscriptions/cancel-a-subscription.md)

```bash theme={null}
curl --location --request POST 'https://api.hub2.io/subscriptions/42/cancel' \
--header 'ApiKey: [REDACTED]' \
--header 'MerchantId: [REDACTED]' \
--header 'Environment: sandbox'
```

> [!WARNING]
>
> La résiliation est définitive. Elle arrête uniquement les prélèvements futurs : HUB2 ne rembourse ni ne calcule de prorata sur les montants déjà prélevés. Pour reprendre un client, inscrivez-le à nouveau.

Un abonnement `active`, `suspended` ou `delinquent` peut être résilié. L'appel retourne l'abonnement mis à jour et déclenche le webhook `subscription.cancelled`. Résilier un abonnement déjà résilié ou terminé est refusé avec une erreur `409`.

### Débiter un abonnement à la demande

[Débiter un abonnement](https://docs.hub2.io/api-reference/subscriptions/charge-a-subscription.md)

Seul un abonnement `merchant_initiated` au statut `active` peut être débité.

```bash Curl theme={null}
curl --location 'https://api.hub2.io/subscriptions/42/charge' \
--header 'ApiKey: [REDACTED]' \
--header 'MerchantId: [REDACTED]' \
--header 'Environment: sandbox' \
--header 'Content-Type: application/json' \
--data '{
    "amount": 1500,
    "currency": "XOF",
    "reference": "invoice-2026-11-0042"
}'
```

| Champ | Obligatoire | Description |
| :- | :- | :- |
| `amount` | Oui | Montant à débiter, un entier positif exprimé dans la plus petite unité de la devise. Pour une devise avec décimales, le montant doit correspondre à un nombre entier d'unités de la devise : `1550` pour l'EUR (15,50) est refusé avec une erreur `400` |
| `currency` | Oui | Doit être la devise de l'abonnement |
| `reference` | Oui | Votre clé d'idempotence pour ce débit, 255 caractères au maximum |

La réponse décrit le débit :

```json theme={null}
{
    "id": 318,
    "status": "success",
    "amount": "1500.00",
    "transactionDate": "2026-11-20T08:12:44.020Z",
    "merchantReference": "invoice-2026-11-0042",
    "indicator": "MIT",
    "createdAt": "2026-11-20T08:12:44.020Z"
}
```

Les montants de la réponse, de l'historique et des webhooks sont des chaînes décimales en unités principales : un débit de `1500` XOF renvoie `"1500.00"`, un débit de `1500` centimes d'EUR renvoie `"15.00"`.

| `status` | Signification |
| :- | :- |
| `success` | Le débit a été accepté |
| `failed` | Le débit a été refusé. Le statut HTTP reste `200` |
| `pending` | Le résultat n'est pas encore connu. Renvoyez la même requête pour le résoudre |

`indicator` vaut `MIT` pour un prélèvement que vous ou HUB2 initiez sur la carte enregistrée, et `CIT` pour une opération initiée par le client.

**Réessayer sans risque.** La `reference` est votre clé d'idempotence pour un abonnement donné. Après un délai d'attente dépassé ou une erreur `5xx`, renvoyez la même requête avec la même `reference` et le même `amount` : la carte n'est jamais débitée deux fois, et l'appel retourne le débit une fois son résultat connu. Réutiliser une `reference` avec un `amount` différent est refusé avec une erreur `409`. Utilisez une nouvelle `reference` pour chaque nouveau débit.

Les débits à la demande ne déclenchent pas de webhooks `payment.succeeded` ou `payment.failed`. La réponse de l'appel et l'historique des débits donnent le résultat.

### Consulter l'historique des débits

[Récupérer l'historique des débits d'un abonnement](https://docs.hub2.io/api-reference/subscriptions/retrieve-a-subscription-charge-history.md)

```bash theme={null}
curl --location 'https://api.hub2.io/subscriptions/42/transactions?page=1&perPage=10' \
--header 'ApiKey: [REDACTED]' \
--header 'MerchantId: [REDACTED]' \
--header 'Environment: sandbox'
```

L'historique liste les débits d'un abonnement, du plus récent au plus ancien, avec le même comportement de `page`, `perPage` et `Content-Range` que la liste des abonnements. Chaque élément comporte un `id`, un `amount` (une chaîne décimale comme `"5000.00"`), un `status` (`pending`, `success` ou `failed`), une `transactionDate`, l'`indicator`, et la `merchantReference` que vous avez envoyée pour un débit à la demande (`null` pour un prélèvement planifié).

Un prélèvement planifié apparaît d'abord avec le statut `pending`. Son statut final est défini une fois que HUB2 a confirmé le résultat, ce qui peut prendre un certain temps après la date du prélèvement.

## Webhooks

Les abonnements utilisent le mécanisme habituel des webhooks. Enregistrez un point de terminaison et abonnez-vous aux événements ci-dessous. Consultez le [guide d'intégration des webhooks](https://docs.hub2.io/integration/fr/webhooks/webhooks_overview.md).

| Événement | Envoyé quand |
| :- | :- |
| `subscription.created` | Un client est inscrit avec succès |
| `subscription.suspended` | Un abonnement est suspendu |
| `subscription.cancelled` | Un abonnement est résilié |
| `payment.succeeded` | Un prélèvement planifié est confirmé comme réussi |
| `payment.failed` | Un prélèvement planifié échoue |

L'objet `data` de chaque événement contient les champs ci-dessous. Les montants sont des chaînes décimales en unités principales (`"15.00"` pour 15 EUR).

```json theme={null}
{
    "owner": "1234",
    "type": "subscription.created",
    "data": {
        "subscriptionId": "6000123456789012345678",
        "customerId": "cust_01924059",
        "frequency": "monthly",
        "amount": "5000.00",
        "startDate": "2026-12-01"
    },
    "test": false,
    "id": "evt_Doc432799e0e6bc3187ca",
    "createdAt": "2026-11-03T10:01:46.002Z"
}
```

| Événement | Champs de `data` |
| :- | :- |
| `subscription.created` | `subscriptionId`, `customerId`, `frequency`, `amount`, `startDate` |
| `subscription.suspended`, `subscription.cancelled` | `subscriptionId`, `customerId` |
| `payment.succeeded`, `payment.failed` (prélèvement planifié) | `subscriptionId`, `customerId`, `transactionReference`, `amount` |

> **Important**
>
> Le `data` d'un prélèvement planifié diffère du `data` d'un paiement ponctuel : il ne contient ni `id` de paiement ni `intentId`. Vérifiez la présence de `subscriptionId` avant de lire `data.id` : lorsqu'il est présent, l'événement est un prélèvement planifié, et un gestionnaire qui lit d'abord `data.id` n'obtient aucune valeur.

Pour rapprocher un événement d'un abonnement :

* `subscriptionId` dans les événements est la valeur du champ `cybersourceSubscriptionId` de l'abonnement, et non son `id`.
* `transactionReference` est la valeur du champ `cybersourceReference` du prélèvement dans l'historique des débits.
* `customerId` est la `customerReference` de l'intention, ou la `purchaseReference` du lien.
* Pour un abonnement à la demande, `subscriptionId` vaut `null`, et `frequency`, `amount` et `startDate` valent `null` dans `subscription.created`. Rapprochez l'événement avec `customerId`, et listez les abonnements de ce `customerId` pour obtenir l'`id`.

L'événement `payment.failed` ne contient pas de cause d'échec.

## Erreurs

La référence de l'API liste les codes de statut HTTP de chaque point de terminaison. Le format des erreurs est décrit dans [Gestion des erreurs](https://docs.hub2.io/documentation/fr/error_management.md).

| Statut HTTP | Code | Points de terminaison | Cause |
| :- | :- | :- | :- |
| `400` | | Lien, intention, débit | Requête invalide. Pour une intention d'abonnement : `frequency` ou `startDate` envoyés pour un abonnement à la demande, un consentement absent ou invalide, une référence de consentement avec des caractères interdits, une URL de redirection qui n'est pas en HTTPS. Pour un débit : un montant qui n'est pas un nombre entier d'unités de la devise |
| `401` | | Tous | Identifiants manquants ou incorrects |
| `403` | `card_on_file_disabled` | Intention | Le card-on-file n'est pas activé sur votre marchand. Contactez le support HUB2 |
| `403` | `card_on_file_ineligible_country` | Intention | Votre marchand n'est pas enregistré en Côte d'Ivoire |
| `403` | `merchant_closed`, `merchant_suspended` | Lien, intention, liste, résiliation, suspension, débit, historique | Votre marchand est fermé ou suspendu |
| `403` | `group_owner_transaction_forbidden` | Lien, intention, liste, résiliation, suspension, débit, historique | Le `MerchantId` est celui d'un marchand parent. Utilisez le `MerchantId` d'un sous-marchand |
| `404` | | Statut, résiliation, suspension, débit, historique | L'inscription ou l'abonnement n'existe pas, appartient à un autre marchand, ou existe dans un autre environnement |
| `409` | `card_on_file_consent_reference_in_use` | Intention d'abonnement | La référence de consentement est déjà inscrite dans l'autre mode (`sandbox` ou `live`), ou un sous-marchand a utilisé une référence de consentement déjà utilisée dans son groupe |
| `409` | | Résiliation, suspension | Le statut de l'abonnement n'autorise pas l'opération |
| `409` | | Débit | L'abonnement n'est pas `merchant_initiated`, n'est pas `active`, utilise une autre devise, ou la `reference` a déjà été utilisée avec un `amount` différent |
| `429` | | Tous | Limite de flux dépassée |
| `503` | | Inscription, résiliation, suspension, débit | Le service est temporairement indisponible. Réessayez plus tard |

## Sandbox

Dans l'environnement `sandbox`, aucune vraie page de saisie de carte n'est affichée. Le résultat d'une inscription est simulé, et vous le lisez avec le point de terminaison de statut de l'inscription.

Pour un abonnement planifié, le `amount` de la requête sélectionne le résultat :

| `amount` | Résultat |
| :- | :- |
| `100` | L'inscription réussit et l'abonnement est créé |
| `200` | Échec, `authentication_failed` |
| `201` | Échec, `card_declined` |
| `202` | Échec, `card_expired` |
| `204` | Échec, `customer_insufficient_funds` |
| `302` | Échec, `service_unavailable` |
| Toute autre valeur | Échec, `bad_parameters` |

Pour un abonnement à la demande, omettez `amount` pour simuler une inscription réussie. Les autres valeurs sélectionnent le résultat de la même façon que dans le tableau.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.