Référence de l'API
Factures et paiements
Périodes de facturation, leurs factures et les paiements associés, et comment lister et lire les factures.
Un abonnement facture une période de facturation à la fois. Chaque période a une facture, et chaque tentative de paiement de celle-ci est un paiement. Vous lisez les trois en récupérant un abonnement. Pour parcourir les factures de tous les abonnements, listez les factures. Pour envoyer un client vers une page hébergée afin de payer une facture ouverte, utilisez Créer une session de paiement.
L'objet billing period
Créer un abonnement renvoie sa première période de facturation dans period. Abuna crée la suivante quand une période payée se termine, et émet sa facture.
Attributs
idstringIdentifiant unique de la période de facturation.subscription_idstringL'abonnement auquel appartient la période.price_idstringLe tarif facturé pour la période.pay_tokenstringLe jeton du lien de paiement de la période.starts_attimestampQuand la période commence, en secondes Unix.ends_attimestampQuand la période se termine, en secondes Unix.period_indexintegerLa position de la période dans l'abonnement, à partir de0.grace_period_ends_attimestampLa date limite de paiement : le délai de grâce de l'application après le début de la période, mais jamais après sa fin. S'il passe sans paiement, Abuna annule l'abonnement.paid_attimestampnullableQuand la période a été payée.paused_attimestampnullableRéservé aux périodes en pause. Toujours null.in_forcebooleanfalsepour la période d'une mise à niveau pendant qu'elle attend le paiement, et définitivement si la mise à niveau échoue. Une telle période ne compte pas parmi les périodes de l'abonnement, et la période suivante réutilise sonperiod_index.created_attimestampQuand la période a été créée, en secondes Unix.
{
"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
}L'objet invoice
Une facture conserve le montant, l'acheteur et le vendeur tels qu'ils étaient quand Abuna l'a émise. Les changements ultérieurs ne la modifient pas.
Les numéros de facture se présentent comme MYST-000124 : le préfixe invoice_prefix de l'application, puis le nombre de factures de l'application, complété à 6 chiffres et plus long au-delà de 999999. Chaque facture émise par l'application prend le numéro suivant, donc les numéros ne se répètent jamais et aucun n'est sauté. Le mode Réel et le mode Test comptent séparément. Un numéro ne change jamais une fois émis, même si vous modifiez le préfixe.
Attributs
idstringIdentifiant unique de la facture.app_idstringL'application qui a émis la facture.period_idstringLa période de facturation facturée.subscription_idstringL'abonnement auquel appartient la facture.numberstringLe numéro de facture, commeMYST-000124. Voir numéros de facture.statusstringopenjusqu'au paiement, puispaid.voidquand l'abonnement a été annulé avant le paiement, ou qu'un changement de formule l'a remplacée. Une facture annulée devientpaidsi un paiement que le client avait déjà approuvé passe après son annulation. Voir Paiements qui arrivent en retard.kindstringperiodpour une période de facturation, ouplan_changepour la première période d'une mise à niveau. Voir Mises à niveau.amountintegerLe montant dû, dans la plus petite unité de la devise.creditintegerLe temps inutilisé de la période précédente déduit du tarif.0sauf sikindvautplan_change.currencystringXAF.item_labelstringLe nom du tarif.buyer_namestringnullableLe nom du client.buyer_emailstringnullableL'adresse e-mail du client.seller_namestringLe nom de l'application.period_starttimestampQuand la période facturée commence.period_endtimestampQuand la période facturée se termine.due_attimestampLa date limite de paiement.created_attimestampQuand la facture a été émise, en secondes Unix.paid_attimestampnullableQuand la facture a été payée.voided_attimestampnullableQuand la facture a été annulée. Null de nouveau dès qu'un paiement tardif la rend payée.
{
"id": "V2nC8xEi",
"app_id": "8ddhXCDW",
"period_id": "T6yVr3Dp",
"subscription_id": "x9QbL2sK",
"number": "MYST-000124",
"status": "open",
"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": null,
"voided_at": null
}L'objet payment
Attributs
idstringIdentifiant unique du paiement.app_idstringL'application à laquelle appartient le paiement.period_idstringLa période de facturation concernée par le paiement.driverstringLe prestataire de paiement, commepawapay.sandboxpour un paiement Test simulé, etmanualpour un paiement que vous avez marqué payé.providerstringLe réseau que le prestataire a débité, commeMTN_MOMO_CMR.manualpour un paiement que vous avez marqué payé.provider_referencestringnullableL'identifiant propre au prestataire pour le débit.amountintegerLe montant débité, dans la plus petite unité de la devise.currencystringXAF.statusstringpending,succeededoufailed.messagestringnullableLe code d'échec, commeinsufficient_funds, ou null quand le paiement n'a pas échoué.created_attimestampQuand le paiement a commencé, en secondes Unix.feeintegernullableLes frais du prestataire, dans la plus petite unité de la devise. En mode Réel, ils valent null jusqu'à ce que le prestataire les communique. En mode Test, c'est une estimation.session_idstringnullableLa session utilisée pour ce paiement, ou null pour un paiement effectué autrement.notestringnullablePour un paiement que vous avez marqué payé, la note que vous avez donnée sur la façon dont le client a payé. Les clients ne la voient jamais. Null sinon.marked_by_user_idstringnullablePour un paiement marqué payé depuis le tableau de bord, l'identifiant du membre de l'équipe qui l'a marqué. Null sinon.marked_by_api_key_idstringnullablePour un paiement marqué payé avec une clé secrète, l'identifiant de cette clé. Null sinon.method_labelstringLe nom du réseau, en toutes lettres.Paid outside Abunapour un paiement que vous avez marqué payé.
{
"amount": 5000,
"app_id": "VyYjSLf1",
"created_at": 1791143442,
"currency": "XAF",
"driver": "sandbox",
"fee": 100,
"id": "lzI2RSXG",
"marked_by_api_key_id": null,
"marked_by_user_id": null,
"message": null,
"method_label": "Successful payment (test)",
"note": null,
"period_id": "tKhKXYjr",
"provider": "test",
"provider_reference": "test_92070010bbf2fcf61894",
"session_id": null,
"status": "succeeded"
}L'objet invoice summary
Les endpoints de facture renvoient les factures sous cette forme. Elle ajoute le client et indique si la facture est en retard, et omet app_id, period_id et seller_name.
Attributs
idstringIdentifiant unique de la facture.numberstringLe numéro de facture, commeMYST-000124. Voir numéros de facture.statusstringopenjusqu'au paiement, puispaid.voidquand l'abonnement a été annulé avant le paiement, ou qu'un changement de formule l'a remplacée. Une facture annulée devientpaidsi un paiement que le client avait déjà approuvé passe après son annulation. Voir Paiements qui arrivent en retard.overduebooleantruequand la facture estopenet que sadue_atest passée. Abuna le calcule chaque fois que vous le demandez. Ce n'est pas un statut.kindstringamountintegerLe montant dû, dans la plus petite unité de la devise.creditintegerLe temps inutilisé déduit du tarif, comme sur l'objet invoice.currencystringXAF.item_labelstringLe nom du tarif.buyer_namestringnullableLe nom du client.buyer_emailstringnullableL'adresse e-mail du client.subscription_idstringL'abonnement auquel appartient la facture.customer_idstringLe client de l'abonnement.period_starttimestampQuand la période facturée commence.period_endtimestampQuand la période facturée se termine.due_attimestampLa date limite de paiement de la période de facturation (grace_period_ends_at), telle qu'elle est maintenant. Elle peut différer de ladue_atde l'objet invoice, qui est fixée à l'émission de la facture.created_attimestampQuand la facture a été émise, en secondes Unix.paid_attimestampnullableQuand la facture a été payée.voided_attimestampnullableQuand la facture a été annulée. Null de nouveau dès qu'un paiement tardif la rend payée.
{
"id": "V2nC8xEi",
"number": "MYST-000124",
"status": "open",
"overdue": false,
"kind": "period",
"amount": 5000,
"credit": 0,
"currency": "XAF",
"item_label": "Monthly",
"buyer_name": "Jane Doe",
"buyer_email": "jane@example.com",
"subscription_id": "x9QbL2sK",
"customer_id": "Cu5tM8rA",
"period_start": 1727600000,
"period_end": 1730192000,
"due_at": 1727686400,
"created_at": 1727600000,
"paid_at": null,
"voided_at": null
}Lister les factures
GET/v1/apps/{appID}/invoices
Liste les factures de l'application, de la plus récente à la plus ancienne, une page à la fois. Le mode Test et le mode Réel ont des factures distinctes, donc une liste ne montre que le mode de l'application demandée. Tous les paramètres sont facultatifs, et un paramètre vide est ignoré.
Paramètres
statusstringopen,paid,voidouoverdue.openinclut les factures en retard.overduerenvoie les factures ouvertes dont la date limite de paiement est passée.searchstringUne partie du numéro de facture, du nom de l'acheteur ou de son e-mail. Ignore la casse.fromtimestampUniquement les factures émises à cette date ou après, en secondes Unix.totimestampUniquement les factures émises avant cette date, en secondes Unix.customer_idstringUniquement les factures de ce client.limitintegerCombien de factures renvoyer, de 1 à 100. Par défaut : 50.cursorstringLa valeurnext_cursorde la page précédente. Envoyez les mêmes filtres avec.
Attributs de la réponse
itemsarrayLes factures de la page. Vide quand rien ne correspond.next_cursorstringnullablePassez-le en cursor pour obtenir la page suivante. Il vaut null sur la dernière page. Traitez-le comme opaque.
Renvoie 200. Une valeur non autorisée renvoie 422 avec le code invalid et le nom du paramètre dans errors.
curl "https://api.abuna.app/v1/apps/8ddhXCDW/invoices?status=overdue&limit=50" \
-H "Authorization: Bearer sk_test_..."{
"items": [
{
"id": "V2nC8xEi",
"number": "MYST-000124",
"status": "open",
"overdue": false,
"kind": "period",
"amount": 5000,
"credit": 0,
"currency": "XAF",
"item_label": "Monthly",
"buyer_name": "Jane Doe",
"buyer_email": "jane@example.com",
"subscription_id": "x9QbL2sK",
"customer_id": "Cu5tM8rA",
"period_start": 1727600000,
"period_end": 1730192000,
"due_at": 1727686400,
"created_at": 1727600000,
"paid_at": null,
"voided_at": null
}
],
"next_cursor": "MTcyNzYwMDAwMDAwMDAwMC4wMUo5WlE3VjFXMlgzWTRaNUE2QjdDOEQ5RQ"
}Récupérer une facture
GET/v1/apps/{appID}/invoices/{id}
Récupère une facture avec ses liens et son historique, depuis son émission jusqu'à son paiement ou son annulation.
Attributs de la réponse
invoiceobjectpay_urlstringnullableLe lien de paiement de la période de facturation. Il vaut null dès que la facture est payée ou annulée, ou quand l'abonnement est annulé.invoice_urlstringUn lien vers la page de facture du client, qui affiche aussi les factures payées et annulées. Il expire au bout de 7 jours.timelinearrayCe qui est arrivé à la facture, du plus ancien au plus récent. Les événements de la même seconde arrivent dans l'ordre ci-dessous.Afficher les attributsMasquer les attributs
typestringissued: Abuna a émis la facture.reminder: Abuna a mis un rappel en file d'attente.notice: un message au client à propos de la facture.payment: une tentative de paiement, qu'elle ait réussi, échoué ou soit encore en attente.paid: la facture a été payée.voided: la facture a été annulée.
attimestampQuand c'est arrivé, en secondes Unix. Pour une notification, quand elle a été envoyée, ou créée si elle n'est pas encore envoyée.noticeobjectpaymentobjectSur un événementpayment: l'objet payment. Sesstatusetmessageindiquent comment la tentative s'est terminée.reminderobjectSur un événementreminder: un objet aveckind, l'une des valeursrenewal_upcoming,payment_overdueoupayment_final_notice.
Renvoie 200. Si l'application n'a aucune facture avec cet identifiant, renvoie 404 avec le code not_found.
curl https://api.abuna.app/v1/apps/8ddhXCDW/invoices/V2nC8xEi \
-H "Authorization: Bearer sk_test_..."{
"invoice": {
"id": "V2nC8xEi",
"number": "MYST-000124",
"status": "open",
"overdue": false,
"kind": "period",
"amount": 5000,
"credit": 0,
"currency": "XAF",
"item_label": "Monthly",
"buyer_name": "Jane Doe",
"buyer_email": "jane@example.com",
"subscription_id": "x9QbL2sK",
"customer_id": "Cu5tM8rA",
"period_start": 1727600000,
"period_end": 1730192000,
"due_at": 1727686400,
"created_at": 1727600000,
"paid_at": null,
"voided_at": null
},
"pay_url": "https://app.abuna.app/pay/3a7e1c9f5b2d8a4e6c0f7b3d9a1e5c8f2b6d",
"invoice_url": "https://app.abuna.app/invoice/V2nC8xEi?exp=1728204800&sig=b4e1c8f2a7d3960e5b1f8c4a2d7e3b9f6a0c5e8d1b4f7a2c9e6d3b0f8a5c1e7d",
"timeline": [
{ "type": "issued", "at": 1727600000 },
{
"type": "notice",
"at": 1727600002,
"notice": {
"id": "e7JpT4cN",
"customer_id": "Cu5tM8rA",
"subscription_id": "x9QbL2sK",
"kind": "subscription_started",
"channel": "email",
"recipient": "jane@example.com",
"subject": "Pay FCFA 5,000 to start your Pro · Monthly plan with My store",
"status": "sent",
"attempts": 1,
"last_error": null,
"next_attempt_at": 1727600000,
"created_at": 1727600000,
"sent_at": 1727600002
}
},
{
"type": "payment",
"at": 1727600090,
"payment": {
"id": "m7Yw4NcJ",
"app_id": "8ddhXCDW",
"period_id": "T6yVr3Dp",
"driver": "pawapay",
"provider": "MTN_MOMO_CMR",
"provider_reference": "9f3b7d1a-5c2e-8f4b-6d0a-3e7c1b5a9d2f",
"amount": 5000,
"currency": "XAF",
"status": "failed",
"message": "insufficient_funds",
"created_at": 1727600090,
"fee": 100,
"session_id": null,
"method_label": "MTN Mobile Money Cameroon"
}
},
{ "type": "reminder", "at": 1727686400, "reminder": { "kind": "payment_overdue" } }
]
}Débiter une facture
POST/v1/apps/{appID}/invoices/{id}/charge
Accepte un en-tête Idempotency-Key.
Débite le numéro de téléphone enregistré du client pour une facture ouverte. Le client approuve le paiement sur son téléphone. La requête part vers le réseau auquel appartient le numéro, comme Orange Money pour un numéro camerounais commençant par 69. Quand le préfixe n'indique pas le réseau, elle part vers le réseau avec lequel le client a payé en dernier ; avec pawaPay, pawaPay détermine le réseau à partir du numéro. Avec les paiements Test simulés, le débit réussit toujours. Pour que le client paie plutôt sur une page hébergée, utilisez Créer une session de paiement.
Attributs de la réponse
statusstringsucceeded, oupendingpendant que le client approuve le débit sur son téléphone.paidbooleantruequandstatusvautsucceeded.periodobjectLa période de facturation de la facture.
Renvoie 200. Un débit refusé renvoie 402 avec la raison comme code, comme insufficient_funds ou payment_declined. Ces cas échouent aussi :
403plan_limit: le premier paiement du client dépasserait la limite d'abonnés Réels de votre forfait.404not_found: l'application n'a aucune facture avec cet identifiant.409already_paid: la facture est payée.409invoice_not_open: la facture est annulée, parce que l'abonnement a été annulé.409payment_in_progress: un autre paiement pour la facture est encore en attente.422not_accepting_payments: une application Réelle ne peut pas encore encaisser de paiements.422payment_unavailable: votre prestataire n'a aucun réseau capable de débiter le numéro du client dans la devise de la facture.
curl -X POST https://api.abuna.app/v1/apps/8ddhXCDW/invoices/V2nC8xEi/charge \
-H "Authorization: Bearer sk_test_..."{
"status": "succeeded",
"paid": true,
"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": 1727600100,
"paused_at": null,
"in_force": true,
"created_at": 1727600000
}
}Marquer une facture payée
POST/v1/apps/{appID}/invoices/{id}/mark-paid
Accepte un en-tête Idempotency-Key.
Enregistre que le client a payé une facture ouverte en dehors d'Abuna, par exemple en espèces, par virement bancaire, ou directement sur votre propre numéro de mobile money. Abuna ne débite rien. La facture est alors payée comme n'importe quelle autre : l'abonnement démarre, se renouvelle ou revient de past_due, vous recevez invoice.paid et les événements d'abonnement qui suivent, et le client reçoit un reçu indiquant que vous avez enregistré son paiement. Un renouvellement marqué payé avant son démarrage conserve ses dates.
Paramètres
notestringobligatoireComment le client a payé, pour vos propres archives, comme un reçu ou une référence de virement. De 1 à 500 caractères, les espaces étant retirés aux deux extrémités. Les clients ne la voient jamais.
Attributs de la réponse
statusstringToujourssucceeded.paidbooleanToujourstrue.periodobjectLa période de facturation de la facture, désormais payée.paymentobjectLe paiement enregistré : le montant de la facture,driveretprovideràmanual, aucun frais, et votre note.
Renvoie 200. Ces cas échouent :
403plan_limit: le premier paiement du client dépasserait la limite d'abonnés Réels de votre forfait.404not_found: l'application n'a aucune facture avec cet identifiant.409already_paid: la facture est payée.409invoice_not_open: la facture est annulée, par exemple parce que l'abonnement a été annulé.409payment_in_progress: un paiement par mobile money pour la facture attend l'approbation du client. Réessayez une fois qu'il a réussi ou échoué.422invalid: la note est manquante ou vide (required) ou dépasse 500 caractères (too_long), danserrors.
curl https://api.abuna.app/v1/apps/8ddhXCDW/invoices/V2nC8xEi/mark-paid \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: cash-V2nC8xEi" \
-d '{
"note": "Paid cash at the shop, receipt 0042"
}'{
"status": "succeeded",
"paid": true,
"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": 1727600100,
"paused_at": null,
"in_force": true,
"created_at": 1727600000
},
"payment": {
"amount": 5000,
"app_id": "VyYjSLf1",
"created_at": 1791143442,
"currency": "XAF",
"driver": "manual",
"fee": null,
"id": "lzI2RSXG",
"marked_by_api_key_id": "01J9ZQ5K2M3N4P5Q6R7S8T9V0W",
"marked_by_user_id": null,
"message": null,
"method_label": "Paid outside Abuna",
"note": "Paid cash at the shop, receipt 0042",
"period_id": "tKhKXYjr",
"provider": "manual",
"provider_reference": null,
"session_id": null,
"status": "succeeded"
}
}Étapes suivantes
- AbonnementsDémarrez, listez, lisez, mettez à jour, annulez, conservez et changez la formule de l'abonnement d'un client à un tarif.
- NotificationsLes e-mails et messages Telegram qu'Abuna a envoyés à vos clients.
- Listes et filtresCe que renvoient les endpoints de liste, comment les paginer et les filtrer, et comment traiter les identifiants.
- ÉvénementsLe journal de tout ce qui s'est passé dans une application, et chaque type d'événement.