Skip to content
ABUNA
DocumentationAbonnements

Référence de l'API

Abonnements

Démarrez, listez, lisez, mettez à jour, annulez, conservez et changez la formule de l'abonnement d'un client à un tarif.

Un abonnement facture un client pour un tarif, une période de facturation à la fois. Il démarre pending, devient active quand le client paie la première période, est past_due pendant qu'un renouvellement attend d'être payé, et se termine canceled. Voir Statuts.

L'objet subscription

Attributs

  • idstring

    Identifiant unique de l'abonnement.
  • app_idstring

    L'application à laquelle appartient l'abonnement.
  • customer_idstring

    Le client qui paie.
  • price_idstring

    Le tarif que le client paie.
  • statusstring

    pending jusqu'à ce que la première période soit payée, puis active. past_due quand une période payée est terminée et que son renouvellement n'est pas encore payé, puis de nouveau active une fois qu'il l'est. canceled une fois qu'il se termine. Voir Statuts.
  • starts_attimestamp

    Quand la première période de facturation démarre, en secondes Unix.
  • activated_attimestampnullable

    Quand la première période a été payée.
  • canceled_attimestampnullable

    Quand l'abonnement a été annulé.
  • cancel_attimestampnullable

    Quand l'abonnement prendra fin, si une annulation est programmée pour la fin de la période payée. Null sinon. L'abonnement reste active jusque-là.
  • current_period_starttimestampnullable

    Quand la plus récente période de facturation que le client a payée a démarré, en secondes Unix. Une fois que le client paie un renouvellement en avance, c'est ce renouvellement, qui démarre dans le futur. Null avant le premier paiement. Il reste défini après la fin de l'abonnement.
  • current_period_endtimestampnullable

    Quand la plus récente période de facturation que le client a payée se termine, en secondes Unix : le client a payé jusqu'à cette date. Null avant le premier paiement. Il reste défini après la fin de l'abonnement.
  • ended_reasonstringnullable

    Pourquoi l'abonnement a pris fin. canceled quand vous ou le client l'avez annulé, maintenant ou à la fin de la période payée. unpaid quand une période de facturation n'a pas été payée avant sa date limite de paiement, y compris une première période jamais payée. Null sauf si status est canceled.
  • metadataobject

    Vos propres clés et valeurs. {} quand il n'y en a aucune. Un abonnement démarré par une session de paiement reçoit le sien. Voir Métadonnées.
  • created_attimestamp

    Quand l'abonnement a été créé, en secondes Unix.
  • manage_tokenstring

    Le jeton du lien vers l'espace client. Toute personne qui a le lien peut voir et annuler l'abonnement, alors gardez-le privé.
L'objet subscription
{
  "id": "x9QbL2sK",
  "app_id": "8ddhXCDW",
  "customer_id": "Cu5tM8rA",
  "price_id": "Zt6YbN3q",
  "status": "active",
  "starts_at": 1727600000,
  "activated_at": 1727600100,
  "canceled_at": null,
  "cancel_at": null,
  "current_period_start": 1727600000,
  "current_period_end": 1730192000,
  "ended_reason": null,
  "metadata": {
    "user_id": "42"
  },
  "created_at": 1727600000,
  "manage_token": "8c1f4a7d2e9b3c6f0a5d8e1b4c7f2a9d3e6b"
}

Créer un abonnement

POST/v1/apps/{appID}/subscriptions

Accepte un en-tête Idempotency-Key.

Démarre un abonnement pending et sa première période de facturation, qui démarre maintenant. Abuna émet la facture de la période et envoie au client un avis avec un lien pour la payer. Pour que le client s'inscrive et paie plutôt sur une page de paiement hébergée, utilisez Créer une session de paiement.

Paramètres

  • customer_idstringobligatoire

    Le client à facturer.
  • price_idstringobligatoire

    Le tarif à lui facturer.
  • metadataobject

    Vos propres clés et valeurs. Voir Métadonnées pour les limites.

Renvoie un objet avec l'subscription et sa première période de facturation, period, avec le statut 201. Si l'application n'a aucun client ou tarif avec cet ID, renvoie 404 avec le code not_found. Si le tarif est archivé, renvoie 410 avec le code price_archived. Si une application Réelle ne peut pas encore accepter de paiements, renvoie 422 avec le code not_accepting_payments. Si le client dépassait la limite d'abonnés Réels de votre forfait, renvoie 403 avec le code plan_limit.

POST /v1/apps/{appID}/subscriptions
curl https://api.abuna.app/v1/apps/8ddhXCDW/subscriptions \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{"customer_id": "Cu5tM8rA", "price_id": "Zt6YbN3q"}'
Réponse
{
  "subscription": {
    "id": "x9QbL2sK",
    "app_id": "8ddhXCDW",
    "customer_id": "Cu5tM8rA",
    "price_id": "Zt6YbN3q",
    "status": "pending",
    "starts_at": 1727600000,
    "activated_at": null,
    "canceled_at": null,
    "cancel_at": null,
    "current_period_start": null,
    "current_period_end": null,
    "ended_reason": null,
    "metadata": {
      "user_id": "42"
    },
    "created_at": 1727600000,
    "manage_token": "8c1f4a7d2e9b3c6f0a5d8e1b4c7f2a9d3e6b"
  },
  "period": {
    "id": "T6yVr3Dp",
    "subscription_id": "x9QbL2sK",
    "price_id": "Zt6YbN3q",
    "pay_token": "3a7e1c9f5b2d8a4e6c0f7b3d9a1e5c8f2b6d",
    "starts_at": 1727600000,
    "ends_at": 1730192000,
    "period_index": 0,
    "grace_period_ends_at": 1727686400,
    "paid_at": null,
    "paused_at": null,
    "in_force": true,
    "created_at": 1727600000
  }
}

Lister les abonnements

GET/v1/apps/{appID}/subscriptions

Liste les abonnements de l'application, du plus récent au plus ancien, une page à la fois. Chaque élément a les champs de l'abonnement, y compris cancel_at, current_period_start, current_period_end, ended_reason et metadata, sans app_id ni manage_token, plus trois autres.

Paramètres de requête

  • statusstring

    Renvoie uniquement les abonnements ayant ce statut : pending, active, past_due ou canceled.
  • customer_idstring

    Renvoie uniquement les abonnements de ce client.
  • emailstring

    Renvoie uniquement les abonnements dont l'e-mail du client contient ce texte. La casse n'a pas d'importance.
  • limitinteger

    Combien d'abonnements renvoyer, de 1 à 100. Par défaut 50.
  • cursorstring

    La valeur next_cursor de la page précédente. Envoyez les mêmes filtres avec.

Attributs supplémentaires

  • customerobject

    Qui paie.
    Afficher les attributsMasquer les attributs
    • namestringnullable

      Le nom du client.
    • emailstring

      L'adresse e-mail du client.
  • priceobject

    Ce qu'il paie.
    Afficher les attributsMasquer les attributs
    • namestring

      Le nom du tarif.
    • amountinteger

      Le montant pour chaque période, dans la plus petite unité de la devise.
    • currencystring

      XAF.
    • intervalstring

      day, week, month ou year.
    • interval_countinteger

      Combien d'intervalles composent une période.
  • next_payment_attimestampnullable

    Quand le prochain paiement est dû. Tant que la dernière période est impayée, c'est le début de cette période, même s'il est dans le passé. Une fois qu'elle est payée, c'est le début de la période suivante. Null une fois annulé, et null tant qu'une annulation est programmée.

Renvoie items et next_cursor, comme décrit dans Listes et filtres. Un status inconnu ou un limit hors de 1 à 100 renvoie 422 avec le code invalid.

GET /v1/apps/{appID}/subscriptions
curl "https://api.abuna.app/v1/apps/8ddhXCDW/subscriptions?status=active&customer_id=Cu5tM8rA" \
  -H "Authorization: Bearer sk_test_..."
Réponse
{
  "items": [
    {
      "id": "x9QbL2sK",
      "customer_id": "Cu5tM8rA",
      "price_id": "Zt6YbN3q",
      "status": "active",
      "starts_at": 1727600000,
      "activated_at": 1727600100,
      "canceled_at": null,
      "cancel_at": null,
      "current_period_start": 1727600000,
      "current_period_end": 1730192000,
      "ended_reason": null,
      "metadata": {
        "user_id": "42"
      },
      "created_at": 1727600000,
      "customer": {
        "name": "Jane Doe",
        "email": "jane@example.com"
      },
      "price": {
        "name": "Monthly",
        "amount": 5000,
        "currency": "XAF",
        "interval": "month",
        "interval_count": 1
      },
      "next_payment_at": 1730192000
    }
  ],
  "next_cursor": null
}

Récupérer un abonnement

GET/v1/apps/{appID}/subscriptions/{id}

Récupère un abonnement avec son client, son tarif, ses périodes de facturation et ses paiements.

Attributs de réponse

  • subscriptionobject

    L'objet subscription.
  • subscription_page_urlstring

    Le lien vers l'espace client, où le client voit et annule l'abonnement.
  • customerobject

    L'objet customer, plus telegram_connected.
  • priceobject

  • periodsarray

    Chaque période de facturation en vigueur, de la plus ancienne à la plus récente. La période d'une mise à niveau en attente de paiement, ou une période échue, n'est pas listée. Celle en attente figure dans pending_change, et sa facture reste dans la liste des factures.
    Afficher les attributsMasquer les attributs
    • idstring

      L'ID de la période de facturation.
    • starts_attimestamp

      Quand la période démarre.
    • ends_attimestamp

      Quand la période se termine.
    • grace_period_ends_attimestamp

      La date limite de paiement.
    • paid_attimestampnullable

      Quand la période a été payée.
    • pay_urlstring

      Le lien de paiement de la période.
    • invoiceobjectnullable

      L'objet invoice de la période.
    • invoice_urlstringnullable

      Un lien vers la page de la facture. Il expire après 7 jours.
  • paymentsarray

    Chaque paiement de l'abonnement, du plus récent au plus ancien.
  • pending_changeobjectnullable

    Le changement de formule en attente : une mise à niveau en attente de paiement, ou une rétrogradation programmée pour la fin de la période payée. Null quand il n'y en a pas. Une annulation programmée figure plutôt dans cancel_at.
    Afficher les attributsMasquer les attributs
    • idstring

      Le changement de formule.
    • typestring

      upgrade ou downgrade.
    • priceobject

      Le nouveau tarif : id, name, amount, currency, interval et interval_count.
    • requested_bystring

      customer quand le client l'a choisi dans son espace client, ou merchant quand il a confirmé un changement que vous lui avez envoyé.
    • created_attimestamp

      Quand le changement a été demandé.
    • amount_dueintegernullable

      Ce que le client paie pour basculer maintenant. Null pour une rétrogradation.
    • creditintegernullable

      Le temps inutilisé de la période en cours déduit du nouveau tarif. Null pour une rétrogradation.
    • expires_attimestampnullable

      Si la mise à niveau n'est pas payée avant, elle expire et la formule actuelle continue. Null pour une rétrogradation.
    • pay_urlstringnullable

      Le lien de paiement de la mise à niveau. Null pour une rétrogradation.
    • effective_attimestampnullable

      Quand la rétrogradation démarre. Null pour une mise à niveau.
  • plan_offerobjectnullable

    Le changement de formule en attente de confirmation du client, ou null. Il est null une fois dépassé son expires_at.
  • plan_changesarray

    Chaque changement de formule de l'abonnement, du plus récent au plus ancien.
    Afficher les attributsMasquer les attributs
    • idstring

      Le changement de formule.
    • directionstring

      upgrade ou downgrade.
    • statusstring

      awaiting_payment (une mise à niveau pas encore payée), scheduled (une rétrogradation en attente de la fin de la période), applied, lapsed (une mise à niveau non payée à temps), undone (une rétrogradation reprise), replaced (un autre changement l'a remplacée), ou canceled (l'abonnement a pris fin avant).
    • from_priceobject

      Le tarif d'avant, dans la même forme que ci-dessus.
    • to_priceobject

      Le nouveau tarif.
    • requested_bystring

      customer ou merchant.
    • amountintegernullable

      Ce que la mise à niveau facture. Null pour une rétrogradation.
    • creditintegernullable

      Le temps inutilisé déduit de la mise à niveau. Null pour une rétrogradation.
    • effective_attimestampnullable

      Quand elle a pris effet, ou pour une rétrogradation, quand elle prendra effet. Null pour une mise à niveau qui n'a pas encore pris effet.
    • created_attimestamp

      Quand elle a été demandée.
    • resolved_attimestampnullable

      Quand elle a quitté l'état en attente.

Si l'application n'a aucun abonnement avec cet ID, renvoie 404 avec le code not_found.

GET /v1/apps/{appID}/subscriptions/{id}
curl https://api.abuna.app/v1/apps/8ddhXCDW/subscriptions/x9QbL2sK \
  -H "Authorization: Bearer sk_test_..."
Réponse
{
  "subscription": {
    "id": "x9QbL2sK",
    "app_id": "8ddhXCDW",
    "customer_id": "Cu5tM8rA",
    "price_id": "Zt6YbN3q",
    "status": "active",
    "starts_at": 1727600000,
    "activated_at": 1727600100,
    "canceled_at": null,
    "cancel_at": null,
    "current_period_start": 1727600000,
    "current_period_end": 1730192000,
    "ended_reason": null,
    "metadata": {
      "user_id": "42"
    },
    "created_at": 1727600000,
    "manage_token": "8c1f4a7d2e9b3c6f0a5d8e1b4c7f2a9d3e6b"
  },
  "subscription_page_url": "https://app.abuna.app/portal/8c1f4a7d2e9b3c6f0a5d8e1b4c7f2a9d3e6b",
  "customer": {
    "id": "Cu5tM8rA",
    "app_id": "8ddhXCDW",
    "name": "Jane Doe",
    "phone_number": "+237671234567",
    "email": "jane@example.com",
    "created_at": 1727600000,
    "telegram_connected": false
  },
  "price": {
    "id": "Zt6YbN3q",
    "product_id": "pR4dWx9K",
    "name": "Monthly",
    "currency": "XAF",
    "amount": 5000,
    "interval": "month",
    "interval_count": 1,
    "created_at": 1727600000,
    "archived_at": null
  },
  "periods": [
    {
      "id": "T6yVr3Dp",
      "starts_at": 1727600000,
      "ends_at": 1730192000,
      "grace_period_ends_at": 1727686400,
      "paid_at": 1727600100,
      "pay_url": "https://app.abuna.app/pay/3a7e1c9f5b2d8a4e6c0f7b3d9a1e5c8f2b6d",
      "invoice": {
        "id": "V2nC8xEi",
        "app_id": "8ddhXCDW",
        "period_id": "T6yVr3Dp",
        "subscription_id": "x9QbL2sK",
        "number": "MYST-000124",
        "status": "paid",
        "kind": "period",
        "amount": 5000,
        "credit": 0,
        "currency": "XAF",
        "item_label": "Monthly",
        "buyer_name": "Jane Doe",
        "buyer_email": "jane@example.com",
        "seller_name": "My store",
        "period_start": 1727600000,
        "period_end": 1730192000,
        "due_at": 1727686400,
        "created_at": 1727600000,
        "paid_at": 1727600100,
        "voided_at": null
      },
      "invoice_url": "https://app.abuna.app/invoice/V2nC8xEi?exp=1728204800&sig=b4e1c8f2a7d3960e5b1f8c4a2d7e3b9f6a0c5e8d1b4f7a2c9e6d3b0f8a5c1e7d"
    }
  ],
  "payments": [
    {
      "id": "m7Yw4NcJ",
      "app_id": "8ddhXCDW",
      "period_id": "T6yVr3Dp",
      "driver": "sandbox",
      "provider": "test",
      "provider_reference": "test_9f3b7d1a5c2e8f4b6d0a",
      "amount": 5000,
      "currency": "XAF",
      "status": "succeeded",
      "message": null,
      "created_at": 1727600090,
      "fee": 100,
      "session_id": null,
      "method_label": "Successful payment (test)"
    }
  ],
  "pending_change": {
    "id": "H3nM8cGb",
    "type": "downgrade",
    "price": {
      "id": "Kw3FgT7e",
      "name": "Basic monthly",
      "amount": 2000,
      "currency": "XAF",
      "interval": "month",
      "interval_count": 1
    },
    "requested_by": "customer",
    "created_at": 1728464000,
    "amount_due": null,
    "credit": null,
    "expires_at": null,
    "pay_url": null,
    "effective_at": 1730192000
  },
  "plan_offer": null,
  "plan_changes": [
    {
      "id": "H3nM8cGb",
      "direction": "downgrade",
      "status": "scheduled",
      "from_price": {
        "id": "Zt6YbN3q",
        "name": "Monthly",
        "amount": 5000,
        "currency": "XAF",
        "interval": "month",
        "interval_count": 1
      },
      "to_price": {
        "id": "Kw3FgT7e",
        "name": "Basic monthly",
        "amount": 2000,
        "currency": "XAF",
        "interval": "month",
        "interval_count": 1
      },
      "requested_by": "customer",
      "amount": null,
      "credit": null,
      "effective_at": 1730192000,
      "created_at": 1728464000,
      "resolved_at": null
    }
  ]
}

Mettre à jour un abonnement

PATCH/v1/apps/{appID}/subscriptions/{id}

Définit le metadata de l'abonnement. La table que vous envoyez remplace toute la table : les clés que vous omettez sont supprimées. Envoyez {} pour l'effacer.

Paramètres

  • metadataobjectobligatoire

    Le nouveau metadata. Voir Métadonnées pour les limites.

Renvoie l'abonnement. Un metadata au-delà de ses limites renvoie 422 avec le code invalid et un élément errors pour metadata. Si l'application n'a aucun abonnement avec cet ID, renvoie 404 avec le code not_found.

PATCH /v1/apps/{appID}/subscriptions/{id}
curl https://api.abuna.app/v1/apps/8ddhXCDW/subscriptions/x9QbL2sK \
  -X PATCH \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{"metadata": {"user_id": "42"}}'
Réponse
{
  "id": "x9QbL2sK",
  "app_id": "8ddhXCDW",
  "customer_id": "Cu5tM8rA",
  "price_id": "Zt6YbN3q",
  "status": "active",
  "starts_at": 1727600000,
  "activated_at": 1727600100,
  "canceled_at": null,
  "cancel_at": null,
  "current_period_start": 1727600000,
  "current_period_end": 1730192000,
  "ended_reason": null,
  "metadata": {
    "user_id": "42"
  },
  "created_at": 1727600000,
  "manage_token": "8c1f4a7d2e9b3c6f0a5d8e1b4c7f2a9d3e6b"
}

Annuler un abonnement

POST/v1/apps/{appID}/subscriptions/{id}/cancel

Accepte un en-tête Idempotency-Key.

Met fin à l'abonnement, maintenant ou à la fin de la période payée. Envoyez at_period_end pour choisir. Un corps vide, ou false, annule maintenant, comme toujours. Pour que le client confirme lui-même l'annulation, utilisez Créer une session d'annulation.

Paramètres du corps

  • at_period_endboolean

    Facultatif, false par défaut. Avec true, l'abonnement reste active jusqu'à la fin de la période payée en cours, puis prend fin tout seul. Une valeur qui n'est pas un booléen renvoie 400 avec le code invalid_json.

Maintenant. L'abonnement est canceled immédiatement, avec canceled_at défini et cancel_at à null. Abuna annule ses factures ouvertes, envoie un événement subscription.canceled avec la raison merchant, et prévient le client. Annuler maintenant un abonnement qui a une annulation programmée le termine maintenant et efface la programmation. Cela annule aussi une mise à niveau en attente de paiement et retire une offre de changement de formule en attente.

À la fin de la période payée. La réponse a status active et cancel_at défini à la fin de la période payée. Abuna envoie un événement subscription.updated et indique au client quand il prend fin et comment le conserver. Jusque-là, il n'émet aucun renouvellement et n'envoie aucun rappel de renouvellement, et next_payment_at dans la liste est null. Quand la date arrive, l'abonnement devient canceled et Abuna envoie subscription.canceled. Envoyer at_period_end de nouveau pendant qu'une annulation est programmée renvoie 200 et ne change rien : aucun nouvel événement et aucun nouvel e-mail. Une annulation programmée remplace une rétrogradation programmée ou une mise à niveau en attente de paiement. Voir Un changement à la fois.

Si la période en cours n'est pas encore payée, ou est déjà terminée, at_period_end: true annule maintenant à la place. Vérifiez status dans la réponse pour voir ce qui s'est passé. Une facture de renouvellement envoyée avant la fin et pas encore payée est annulée, donc le client ne peut pas payer pour une période après la fin. S'il l'a déjà payée, l'abonnement prend fin quand ce renouvellement se termine.

Renvoie l'abonnement. Si l'application n'a aucun abonnement avec cet ID, ou s'il est déjà annulé, renvoie 404 avec le code not_found. Si at_period_end devait remplacer une mise à niveau dont le paiement est en cours, renvoie 409 avec le code payment_in_progress.

POST /v1/apps/{appID}/subscriptions/{id}/cancel
curl https://api.abuna.app/v1/apps/8ddhXCDW/subscriptions/x9QbL2sK/cancel \
  -X POST \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{"at_period_end": true}'
Réponse
{
  "id": "x9QbL2sK",
  "app_id": "8ddhXCDW",
  "customer_id": "Cu5tM8rA",
  "price_id": "Zt6YbN3q",
  "status": "active",
  "starts_at": 1727600000,
  "activated_at": 1727600100,
  "canceled_at": null,
  "cancel_at": 1730192000,
  "current_period_start": 1727600000,
  "current_period_end": 1730192000,
  "ended_reason": null,
  "metadata": {
    "user_id": "42"
  },
  "created_at": 1727600000,
  "manage_token": "8c1f4a7d2e9b3c6f0a5d8e1b4c7f2a9d3e6b"
}

Conserver un abonnement

POST/v1/apps/{appID}/subscriptions/{id}/resume

Accepte un en-tête Idempotency-Key.

Annule une annulation programmée. La facturation continue comme avant : cancel_at est de nouveau null et le prochain renouvellement est émis à temps. Abuna envoie un événement subscription.updated et indique au client que l'abonnement continue.

Cela annule aussi une rétrogradation programmée, donc la période suivante reste au tarif actuel. Abuna envoie subscription.updated avec un changement downgrade_undone, et aucun e-mail.

Si rien n'est programmé sur un abonnement actif, renvoie 200 et ne change rien. Si l'abonnement est déjà annulé, renvoie 409 avec le code subscription_canceled. Si l'application n'a aucun abonnement avec cet ID, renvoie 404 avec le code not_found.

POST /v1/apps/{appID}/subscriptions/{id}/resume
curl https://api.abuna.app/v1/apps/8ddhXCDW/subscriptions/x9QbL2sK/resume \
  -X POST \
  -H "Authorization: Bearer sk_test_..."
Réponse
{
  "id": "x9QbL2sK",
  "app_id": "8ddhXCDW",
  "customer_id": "Cu5tM8rA",
  "price_id": "Zt6YbN3q",
  "status": "active",
  "starts_at": 1727600000,
  "activated_at": 1727600100,
  "canceled_at": null,
  "cancel_at": null,
  "current_period_start": 1727600000,
  "current_period_end": 1730192000,
  "ended_reason": null,
  "metadata": {
    "user_id": "42"
  },
  "created_at": 1727600000,
  "manage_token": "8c1f4a7d2e9b3c6f0a5d8e1b4c7f2a9d3e6b"
}

Lister les options de formule

GET/v1/apps/{appID}/subscriptions/{id}/plan-options

Liste les tarifs vers lesquels l'abonnement peut basculer, chacun chiffré à l'instant présent. Les montants changent à mesure que la période s'écoule, alors chiffrez de nouveau juste avant de les afficher. Voir Changer de formule pour la façon dont chaque nombre est calculé.

Chaque option

  • priceobject

    Le tarif : id, name, amount, currency, interval et interval_count.
  • directionstring

    upgrade ou downgrade.
  • amount_dueinteger

    Pour une mise à niveau, ce que le client paie maintenant. Il peut être 0. Toujours 0 pour une rétrogradation.
  • creditinteger

    Pour une mise à niveau, le temps inutilisé de la période en cours déduit du nouveau tarif. Toujours 0 pour une rétrogradation.
  • new_billing_datetimestampnullable

    Pour une mise à niveau, quand la nouvelle période se terminerait si elle était payée maintenant. Null pour une rétrogradation.
  • effective_attimestampnullable

    Pour une rétrogradation, quand le nouveau tarif démarre : la fin de la période payée. Null pour une mise à niveau.

Renvoie { "data": [...] }. La liste est vide quand aucun autre tarif n'est éligible ou que l'abonnement n'est pas active ou past_due. Si l'application n'a aucun abonnement avec cet ID, renvoie 404 avec le code not_found.

GET /v1/apps/{appID}/subscriptions/{id}/plan-options
curl https://api.abuna.app/v1/apps/8ddhXCDW/subscriptions/x9QbL2sK/plan-options \
  -H "Authorization: Bearer sk_test_..."
Réponse
{
  "data": [
    {
      "price": {
        "id": "Hc8LmV2s",
        "name": "Pro monthly",
        "amount": 10000,
        "currency": "XAF",
        "interval": "month",
        "interval_count": 1
      },
      "direction": "upgrade",
      "amount_due": 6666,
      "credit": 3334,
      "new_billing_date": 1731142400,
      "effective_at": null
    },
    {
      "price": {
        "id": "Kw3FgT7e",
        "name": "Basic monthly",
        "amount": 2000,
        "currency": "XAF",
        "interval": "month",
        "interval_count": 1
      },
      "direction": "downgrade",
      "amount_due": 0,
      "credit": 0,
      "new_billing_date": null,
      "effective_at": 1730192000
    }
  ]
}

Statuts

  • pending : créé, et en attente du premier paiement. Si la première période n'est pas payée avant sa date limite de paiement, l'abonnement est canceled avec ended_reason unpaid.
  • active : chaque période qui a commencé est payée.
  • past_due : un renouvellement a commencé et n'est pas encore payé.
  • canceled : terminé. ended_reason dit pourquoi. Un abonnement annulé ne peut pas être redémarré.

Renouvellements et past_due

Abuna émet la période de facturation suivante et sa facture avant la fin de la période payée, aussi longtemps à l'avance que l'indique le renewal_lead_days de votre application : 0 à 7, 3 par défaut. Abuna envoie subscription.renewed et invoice.created, et envoie au client par e-mail la facture avec son lien de paiement. L'abonnement reste active, et le client peut payer tout de suite. La nouvelle période démarre toujours quand la période payée se termine, même si elle est payée en avance.

Si le renouvellement est encore impayé quand la période payée se termine, l'abonnement devient past_due et Abuna envoie subscription.past_due. Quand la facture est payée, l'abonnement est de nouveau active, et Abuna envoie subscription.recovered. Un renouvellement payé avant la fin de la période payée ne passe jamais par past_due. Avec renewal_lead_days à 0, ou tant qu'une rétrogradation attend la fin de la période, la facture part à la fin de la période, donc l'abonnement est past_due jusqu'à ce que le client paie.

Si la période n'est pas payée avant sa date limite de paiement, l'abonnement est canceled avec ended_reason unpaid, et Abuna envoie subscription.canceled avec la raison non_payment. La date limite de paiement est legrace_period_ends_at de la période de facturation : le début de la période plus le grace_period_days de votre application, et jamais après la fin de la période.

current_period_start et current_period_end continuent de montrer la dernière période payée tant que l'abonnement est past_due. Un abonnement past_due peut encore :

  • Être payé via son lien de paiement, une session de paiement, ou l'espace client.
  • Être annulé. Une annulation à la fin de la période payée le termine maintenant, parce que la période en cours n'est pas payée.
  • Changer de formule. Une mise à niveau démarre tout de suite une période complète au nouveau tarif, et une rétrogradation attend la fin de la période en cours. Voir Mises à niveau.

Paiements qui arrivent en retard

Un paiement par mobile money que le client a approuvé peut être confirmé par le prestataire des heures plus tard. Si la facture a été annulée entre-temps, Abuna enregistre quand même le paiement : il est succeeded, la facture devient paid, Abuna envoie invoice.paid, et le client reçoit un reçu.

  • Si l'abonnement a pris fin uniquement parce que ce paiement n'était pas arrivé (ended_reason unpaid), et que la période qu'il paie n'est pas encore terminée, l'abonnement revient : status est active, canceled_at et ended_reason sont de nouveau null, et Abuna envoie subscription.recovered, ou subscription.paid pour un premier paiement. Un premier paiement a besoin de place dans la limite d'abonnés Réels de votre forfait, comme au paiement.
  • Sinon l'abonnement reste tel quel : annulé par vous ou le client, remplacé, au-delà de la période payée, ou une mise à niveau qui a expiré. Vous voudrez peut-être rembourser le client.

Dans les deux cas, les membres de l'équipe qui reçoivent les e-mails sur les paiements ou les annulations en reçoivent un à ce sujet.

Métadonnées

metadata contient vos propres clés et valeurs sur un abonnement, comme l'ID de votre utilisateur. Définissez-le quand vous créez l'abonnement, transmettez-le à une session de paiement, ou mettez-le à jour plus tard. Chaque lecture le renvoie.

  • Jusqu'à 20 clés.
  • Chaque clé fait 1 à 40 caractères.
  • Chaque valeur est une chaîne de 500 caractères au plus. Les nombres, les booléens, les objets et null ne sont pas autorisés.

Les événements subscription, invoice et plan_offer portent le metadata de cet abonnement. Les événements de session portent plutôt le metadata de la session. Transmettez le metadata sur chaque session que vous créez. Voir Types d'événement.

Annuler un abonnement

Une annulation peut prendre effet maintenant ou à la fin de la période payée. Vous choisissez le moment quand vous annulez dans le tableau de bord ou l'API. Pour les clients qui annulent dans l'espace client, c'est le réglage Quand les clients annulent de l'application qui décide, soit customer_cancel. Les nouvelles applications démarrent avec at_period_end. Une session d'annulation utilise aussi ce réglage, sauf si vous envoyez at_period_end.

  • Maintenant. L'abonnement prend fin immédiatement et toute facture impayée est annulée.
  • Fin de la période payée. L'abonnement reste active et porte cancel_at. Le client garde l'accès qu'il a payé. Abuna n'émet aucun renouvellement et n'envoie aucun rappel de renouvellement, et annule une facture de renouvellement déjà envoyée et qui n'est pas payée. Un renouvellement que le client a déjà payé compte comme période payée, donc l'abonnement prend fin quand elle se termine. Le tableau de bord et l'espace client affichent « Prend fin le » avec la date. À ce moment-là, l'abonnement devient canceled.
  • Période impayée. Si la période en cours n'est pas encore payée, une annulation de fin de période met fin à l'abonnement maintenant et annule la facture impayée. Le tableau de bord et l'espace client le disent avant que la personne confirme.
  • Revenir en arrière. Jusqu'à la date de fin, vous pouvez conserver l'abonnement dans le tableau de bord ou avec l'endpoint de reprise, et le client peut le conserver dans l'espace client. La facturation continue comme avant.

Le client reçoit un e-mail quand une annulation est programmée, avec la date de fin et comment conserver l'abonnement, un quand elle prend fin, et une courte confirmation si elle est annulée. Votre endpoint de webhook reçoit subscription.updated quand une annulation est programmée ou annulée, et subscription.canceled quand l'abonnement prend fin. Voir Notifications aux clients.

Le mode Test fonctionne de la même façon. Abuna enregistre les e-mails pour un client Test au lieu de les envoyer, et les applications Test et Réelles gardent leur propre réglage Quand les clients annulent.

Changer de formule

Un abonnement active ou past_due peut passer à un autre tarif. Le client le fait dans son espace client, ou confirme un changement que vous lui envoyez avec une session de changement de formule. Vous ne pouvez pas changer le tarif directement, parce qu'une mise à niveau a besoin que le client paie.

Quels tarifs

Tout autre tarif du même produit dans la même devise, sauf s'il est archivé. Jamais un tarif d'un autre produit ni dans une autre devise. Si aucun tarif n'est éligible, l'espace client n'affiche aucune option de changement de formule. Un abonnement encore pending ne peut pas changer de formule.

Mise à niveau ou rétrogradation

Abuna compare le coût par jour : le montant du tarif divisé par la durée en jours d'une de ses périodes démarrant maintenant. Un tarif qui coûte plus par jour est une mise à niveau. Un tarif qui coûte autant ou moins est une rétrogradation. Cela fonctionne entre intervalles, donc passer d'un mensuel à un annuel suit la même règle.

Mises à niveau

Si la période en cours est payée, le client paie la différence maintenant et garde l'ancienne formule jusqu'à ce que ce paiement réussisse. Une facture de renouvellement déjà envoyée pour l'ancien tarif et non payée est annulée, et la suivante est émise au nouveau tarif. Si le client a déjà payé la période suivante avant qu'elle commence, une mise à niveau attend que cette période démarre : jusque-là, l'espace client ne propose aucune mise à niveau, et un changement renvoie 409 avec le code renewal_paid_early. À ce moment-là, la nouvelle formule démarre avec une période complète entière au nouveau tarif, et la date de facturation passe à la date de paiement.

Le montant est le nouveau tarif moins la valeur inutilisée de la période en cours. La valeur inutilisée est la valeur de la période multipliée par les jours entiers restants, divisée par les jours de la période. La valeur de la période est ce que sa facture a facturé plus tout crédit qu'elle porte, donc une deuxième mise à niveau dans la même période ne perd rien. Le résultat est arrondi à l'entier inférieur et ne descend jamais sous 0. Avec 20 jours restants sur 30 d'une période de 5 000 FCFA, passer à un tarif de 10 000 FCFA coûte 10000 − 3334 = 6666, et credit vaut 3334.

  • Rien à payer. Quand le montant est 0, la mise à niveau prend effet immédiatement. Sa facture est créée déjà payée.
  • Pas payée à temps. La mise à niveau expire à son expires_at : les jours de paiement de votre application à partir de la demande, ou la fin de la période en cours si elle arrive avant. Sa facture est annulée et l'ancienne formule continue. Il n'y a aucune pénalité. Si la mise à niveau a remplacé une annulation programmée, l'annulation est reprogrammée à sa date d'origine, et subscription.updated porte son cancel_at.
  • Période en cours impayée. Si la période en cours n'est pas encore payée, comme tant que l'abonnement est past_due, Abuna annule sa facture et démarre une période complète au nouveau tarif maintenant. La nouvelle formule est en vigueur immédiatement et le client la paie via son lien de paiement, avec les jours de paiement habituels. Il n'y a aucun crédit. Tant qu'un paiement sur l'ancienne facture est en cours, le changement renvoie 409 avec le code payment_in_progress.

La facture d'une mise à niveau a kind plan_change et le temps inutilisé dans credit. Son amount est ce que le client paie.

Une mise à niveau avec quelque chose à payer exige que votre application accepte les paiements. Jusque-là, le changement renvoie 422 avec le code not_accepting_payments. Une mise à niveau sans rien à payer, et une rétrogradation, fonctionnent dans les deux cas.

Rétrogradations

Une rétrogradation ne coûte rien maintenant et ne rembourse rien. Le client garde la formule actuelle jusqu'à la fin de la période payée, et la période suivante est émise au nouveau tarif quand la période payée se termine, pas avant. Une facture de renouvellement déjà envoyée pour l'ancien tarif et non payée est annulée. Si le client a déjà payé la période suivante, la rétrogradation démarre quand cette période se termine. Jusque-là, le client peut l'annuler dans son espace client, et vous pouvez le faire avec Conserver un abonnement. Une rétrogradation programmée figure dans pending_change et dans scheduled_price_change sur subscription.updated.

Une rétrogradation peut être programmée tant que la période en cours est encore impayée. Elle démarre tout de même à la fin de cette période, et si la période reste impayée, l'abonnement prend fin pour non-paiement comme d'habitude. Tant qu'une rétrogradation est programmée, le rappel de renouvellement indique le nouveau tarif.

Un changement à la fois

Un abonnement contient au plus l'un de ces éléments : une annulation programmée, une rétrogradation programmée, ou une mise à niveau en attente de paiement. En démarrer un remplace l'autre. Une mise à niveau remplacée a sa facture annulée, et le changement remplacé reçoit le statut replaced. Tant que le paiement de cette mise à niveau est en cours, elle ne peut pas être remplacée : la requête renvoie 409 avec le code payment_in_progress.

Redemander le changement déjà en attente, vers le même tarif, ne change rien et le renvoie tel quel.

Un changement que vous envoyez au client avec une session de changement de formule attend qu'il le confirme. Un abonnement a au plus un changement en attente d'être confirmé. Un nouveau, ou un changement de formule que le client fait dans l'espace client, le remplace. Le confirmer remplace une annulation programmée, une rétrogradation programmée ou une mise à niveau en attente de paiement, et la page de confirmation le dit d'abord. Annuler l'abonnement maintenant annule une mise à niveau en attente de paiement et retire le changement en attente d'être confirmé.

Dans l'espace client

L'espace client liste chaque tarif éligible avec ce qu'il coûte maintenant : le montant à payer et la nouvelle date de facturation pour une mise à niveau, ou la date à laquelle elle démarre pour une rétrogradation. Une mise à niveau envoie le client vers son lien de paiement, sauf s'il n'y a rien à payer. Tant qu'une mise à niveau attend le paiement, l'espace client l'affiche avec son lien de paiement. Conserver l'abonnement dans l'espace client annule aussi une rétrogradation programmée. Pour envoyer un client vers l'espace client avec un lien de retour vers votre application, utilisez Créer une session de portail.

L'url d'une session de changement de formule ouvre une page avec la formule actuelle, la nouvelle, et ce que coûte le changement, calculé au chargement de la page. Le client confirme là, et le changement suit les mêmes règles que dans l'espace client. La page cesse de fonctionner quand la session expire (7 jours, ou la fin de la période en cours si elle arrive avant), quand un changement plus récent la remplace, ou une fois qu'elle est utilisée.

Abuna n'envoie pas d'e-mail au client quand vous créez une session de changement de formule, alors envoyez-le vous-même vers son url. Le client reçoit un e-mail quand une mise à niveau prend effet et quand une rétrogradation est programmée. Rien n'est envoyé quand une mise à niveau commence à attendre le paiement, quand elle expire, ou quand une rétrogradation est annulée. Voir Notifications aux clients. En mode Test, Abuna enregistre ces e-mails au lieu de les envoyer.

Étapes suivantes