Allocation de paiement (payment_allocation)

Une payment_allocation trace l'imputation d'un encaissement (ou d'un décaissement) sur un ou plusieurs documents financiers. Elle est l'objet qui fait le lien entre un psp_payment ou un payment et les factures qu'il solde.

Rôle

Chaque fois qu'un paiement est rapproché d'une ou plusieurs factures, unepayment_allocation est créée pour matérialiser cette imputation. Elle répond à la question : quel montant de quel paiement a été appliqué sur quelle facture ?

L'allocation est un objet à deux niveaux :

  • L'en-tête (pal_) identifie la source et son statut global d'imputation (partial ou complete).
  • Les lignes (pai_) détaillent chaque imputation unitaire — quel montant a été appliqué sur quelle cible (facture, avoir, commande, etc.).

La création d'une allocation produit des effets de bord immédiats : mise à jour dusettlement_status des documents financiers concernés et mise à jour du status du paiement source. Le cycle de vie des factures reste indépendant.

Identifiant et structure

L'en-tête porte le préfixe pal_ ; chaque ligne porte le préfixe pai_.

JSON
"payment_allocation":{12 items
"object":"payment_allocation"
"id":"pal_3c8f1a9b2d4e7f6a"
"merchant_id":"mer_1a2b3c4d5e6f7a8b"
"source_type":"psp_payment"
"source_id":"psp_7e4b2d9f1c3a8e5f"
"source_reference":null
"reverses_allocation_id":null
"reversed_by_allocation_id":null
"status":"complete"
"created_at":"2026-06-17T14:30:00.000Z"
"updated_at":"2026-06-17T14:30:00.000Z"
"items":[1 item
0:{...}8 items
]
}
{
  "object": "payment_allocation",
  "id": "pal_3c8f1a9b2d4e7f6a",
  "merchant_id": "mer_1a2b3c4d5e6f7a8b",
  "source_type": "psp_payment",
  "source_id": "psp_7e4b2d9f1c3a8e5f",
  "source_reference": null,
  "reverses_allocation_id": null,
  "reversed_by_allocation_id": null,
  "status": "complete",
  "created_at": "2026-06-17T14:30:00.000Z",
  "updated_at": "2026-06-17T14:30:00.000Z",
  "items": [
    {
      "object": "payment_allocation_item",
      "id": "pai_1a3f2b9c4d8e5f7a",
      "payment_allocation_id": "pal_3c8f1a9b2d4e7f6a",
      "target_type": "invoice",
      "target_id": "inv_4a7b2e9f1c3d8a5e",
      "amount": 120000,
      "currency": "eur",
      "created_at": "2026-06-17T14:30:00.000Z"
    }
  ]
}

Champs de l'en-tête

ChampTypeDescription
idstringIdentifiant de l'allocation (préfixe pal_).
objectstringToujours "payment_allocation".
merchant_idstringMarchand propriétaire.
source_typeenumType de la source : psp_payment, payment, ou supplier_payment.
source_idstringIdentifiant de l'objet source (psp_…, pay_…, spay_…).
source_referencestring | nullRéférence source optionnelle utilisée notamment pour l’idempotence.
reverses_allocation_idstring | nullAllocation compensée par celle-ci lorsqu’il s’agit d’une inversion.
reversed_by_allocation_idstring | nullAllocation de compensation qui a inversé celle-ci, si elle existe.
statusenumpartial ou complete.
created_atdatetimeDate de création.
updated_atdatetimeDate de dernière mise à jour.

Lignes d'imputation

Chaque ligne (payment_allocation_item) décrit l'imputation d'un sous-montant de la source sur un document cible spécifique. Une allocation peut avoir plusieurs lignes si le paiement couvre plusieurs factures.

ChampTypeDescription
idstringIdentifiant de la ligne (préfixe pai_).
objectstringToujours "payment_allocation_item".
payment_allocation_idstringAllocation parente (pal_…).
target_typeenumType de la cible : invoice, credit_note, order, unallocated (AR) ou supplier_invoice, supplier_credit_note, purchase_order, unallocated (AP).
target_idstringIdentifiant de la cible (inv_…, ord_…, cmp_ si unallocated…).
amountintegerMontant imputé dans l’unité mineure de la devise. Positif pour les factures, négatif pour les avoirs.
currencystringDevise (ISO 4217 minuscules). Doit correspondre à la devise source.
created_atdatetimeDate de création.
Montant des avoirs : négatif Pour une ligne ciblant un credit_note, le champ amount est négatif — l'avoir compense une partie du solde à régler plutôt que de le réduire directement. La somme algébrique des lignes donne le montant net imputé.

Les lignes sont accessibles via GET /v1/payment-allocations/:id (retourné dans l'objet parent) ou via le filtre GET /v1/payment-allocations?source_id=psp_….

Statut

Le statut de l'en-tête reflète si la totalité du montant source a été imputée.

StatutSignification
partialLa somme des lignes est inférieure au montant source — une partie du paiement reste non imputée. Le paiement source passe à partially_matched.
completeLa somme des lignes est égale au montant source — le paiement est entièrement rapproché. Le paiement source passe à matched.
Le statut est calculé à la création Une payment_allocation est immuable après création. Son statut est déterminé au moment du POST selon le total des lignes par rapport au montant source. Pour imputer le reliquat d'un paiement partially_matched, créez une nouvelle allocation sur le même paiement source.

Sources et cibles

Types de source

source_typeContrainteUsage
psp_paymentstatus = succeeded, buyer_id requisPaiement PSP encaissé — imputation automatique sur les factures de l'acheteur.
paymentstatus ∈ {pending, partially_matched}Paiement plateforme en cours de rapprochement — imputation manuelle ou via processus.
supplier_paymentstatus ∈ {pending, scheduled, executed}Décaissement sortant — imputation sur des factures fournisseur (flux AP).

Types de cible

target_typeFluxDescription
invoiceARFacture acheteur ouverte. Le montant réduit le solde dû.
credit_noteARAvoir acheteur. Le montant (négatif) augmente le solde à régler.
orderARAvance sur commande, avant émission de facture.
unallocatedAR / APProvision non allouée — target_id est l'identifiant de la company. Utilisée pour les avances sans cible connue.
supplier_invoiceAPFacture fournisseur. Le montant réduit la dette à payer.
supplier_credit_noteAPAvoir fournisseur.
purchase_orderAPAvance sur bon de commande.

Effets de bord sur les cibles

La création d'une allocation met à jour immédiatement les statuts des objets concernés.

Cible / objet impactéEffet
invoicesettlement_status → partially_paid ou paid selon le total appliqué. Le status du document reste inchangé.
credit_noteÉvénement credit_note.applied émis. Pas de changement de statut.
orderÉvénement order.advance_received émis. Pas de changement de statut.
payment (source)status → matched ou partially_matched selon le montant total imputé.
supplier_invoicesettlement_status → scheduled, partially_paid ou paid selon l’état du décaissement. Le status du document reste inchangé.

Création

Une payment_allocation se crée via POST /v1/payment-allocations. La requête décrit la source et la liste des lignes d'imputation.

HTTP
POST /v1/payment-allocations
{3 items
"source_type":"psp_payment"
"source_id":"psp_7e4b2d9f1c3a8e5f"
"items":[1 item
0:{...}4 items
]
}
{
  "source_type": "psp_payment",
  "source_id": "psp_7e4b2d9f1c3a8e5f",
  "items": [
    {
      "target_type": "invoice",
      "target_id": "inv_4a7b2e9f1c3d8a5e",
      "amount": 120000,
      "currency": "eur"
    }
  ]
}

La plateforme valide que :

  • La source est dans un statut éligible (voir tableau des sources).
  • Toutes les cibles appartiennent au même marchand et ont la même devise que la source.
  • Aucune cible n'est sur-imputée (total appliqué ≤ montant du document).
  • La somme des lignes ne dépasse pas le montant source.
  • Les cibles AR appartiennent au même acheteur que la source lorsque la source porte un buyer_id.

Les endpoints de lecture :

HTTP
GET /v1/payment-allocations/:id
GET /v1/payment-allocations?source_type=psp_payment&source_id=psp_7e4b
GET /v1/payment-allocations?target_type=invoice&target_id=inv_4a7b

Dans les processus

En pratique, les payment_allocation ne sont pas créées manuellement — elles sont produites par les nodes de rapprochement.

Nodesource_type produitRôle
reconcile_paymentpayment ou psp_paymentReçoit un paiement bancaire ou PSP et une proposition d'allocation (sortie de propose_payment_allocation), puis crée lapayment_allocation avec le type de source correspondant.

Le flux habituel pour un encaissement PSP est :psp_payment.succeeded → propose_payment_allocation → reconcile_payment → payment_allocation créée → factures soldées.

Corriger une imputation

Une imputation est un fait immuable : elle ne se modifie pas et ne se supprime pas. Pour la corriger, on la contre-passe.

HTTP
POST /v1/payment-allocations/:id/reverse

L'appel crée une allocation compensatoire — le miroir exact de l'allocation nommée, agrégé par cible, montants opposés. Aucune ventilation n'est choisie. Tous les statuts concernés se recalculent : paiement source, factures, avoirs, rollups de remboursement.

Corriger un montant se fait donc en deux temps : reverse puis une nouvelle allocation. Contre-passer libère la capacité de la source, si bien que réimputer est une opération ordinaire — il n'existe volontairement pas d'annulation d'annulation.

Refus

  • une allocation déjà contre-passée ;
  • une allocation compensatoire, qui ne peut pas l'être à son tour ;
  • le miroir créé par POST /v1/payments/:id/reverse : il est imposé par le paiement qu'il contre-passe. Un prélèvement SEPA contesté et retourné à l'acheteur ne laisse rien à lettrer.

L'appel accepte un source_reference : un rejeu après timeout retourne la compensation existante au lieu d'en créer une seconde.

Identifier la contre-passation. Les montants négatifs ne suffisent pas à savoir quelle imputation est défaite lorsque plusieurs visent le même document. La relation est donc explicite, dans les deux sens :

JSON
{3 items
"id":"pal_compensation"
"reverses_allocation_id":"pal_origine"
"reversed_by_allocation_id":null
}
{
  "id": "pal_compensation",
  "reverses_allocation_id": "pal_origine",
  "reversed_by_allocation_id": null
}

reverses_allocation_id est renseigné sur la compensation et pointe l'allocation défaite ; reversed_by_allocation_id est renseigné sur l'originale et pointe sa compensation. Les deux sont nuls sur une allocation ordinaire, et un connecteur n'a donc jamais à parcourir la liste pour savoir si une imputation a été défaite.

Documents annulés ou passés en perte. Contre-passer une imputation qui les visait fait redescendre leur règlement mais ne rouvre jamais le document : rouvrir un written_offmodifierait la créance en silence, sur une décision qui appartient au marchand. La plateforme émet invoice.terminal_settlement_changed et laisse un processus ou un opérateur reprendre la décision.

Événements

ÉvénementDéclencheur
payment_allocation.partialL'allocation a été créée avec status=partial : le montant source n'est pas entièrement imputé.
payment_allocation.completeL'allocation a été créée avec status=complete : le montant source est entièrement imputé.
invoice.paidUne facture a été entièrement réglée via cette allocation (settlement_status → paid).
invoice.partially_paidUne facture est partiellement réglée (settlement_status → partially_paid).
payment.matchedLe paiement source (pay_) a été entièrement rapproché (status → matched).

Les événements payment_allocation.partial et payment_allocation.complete sont mutuellement exclusifs — un seul est émis par création. Les événements sur les documents cibles (invoice.paid, invoice.partially_paid, payment.matched…) sont émis dans la même transaction.