Payments
POST /v1/payments records money a customer paid you and issues the receipt in one call. There is no draft step: an issued payment is never cancelled, and money going back to the customer is a credit note.
Required fields
Every request sends customerId, branchId, paymentMethodId and amount. amount is above 0 with at most two decimals.
branchId is the branch that received the money. The payment method posts to the treasury set for that branch, or to the method's default treasury when the branch has none. If that treasury serves only another branch, the payment would be recorded there, so it is refused with 422 payment_method_not_in_branch.
date is optional and defaults to today in Saudi time. A past date is accepted unless the merchant has closed that accounting period (422 period_locked), or it is before an invoice the payment settles (400 invalid_parameter on date).
notes takes up to 500 characters and referenceId up to 100.
Allocations
allocations says which invoices the payment settles, up to 100 of them:
{
"customerId": "3f1c2a6e-8b4d-4e2a-9c1f-5d7e8a9b0c12",
"branchId": "9a0b1c2d-3e4f-4a5b-8c6d-7e8f9a0b1c2d",
"paymentMethodId": "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9",
"amount": 150,
"allocations": [{ "invoiceId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e", "amount": 115 }]
}
| Rule | Error |
|---|---|
The allocations add up to no more than amount | 400 invalid_parameter on allocations |
| An invoice appears once | 400 invalid_parameter on allocations[i].invoiceId |
| The invoice exists, is an issued sales invoice of this customer, and is in a branch your key may use | 422 invoice_not_payable on allocations[i].invoiceId |
The payment's date is not before the invoice's | 400 invalid_parameter on date |
| The amount is no more than the invoice still owes | 422 allocation_exceeds_due on allocations[i].amount |
Whatever the allocations leave, or the whole amount when you send none, stays as the customer's credit. The reply shows it as unallocatedAmount. After the call, the invoice's amountPaid and amountDue in GET /v1/sales-invoices/{id} include the allocation.
A key limited to some branches can allocate a payment only to invoices issued in those branches. An invoice from any other branch answers 422 invoice_not_payable, exactly like an invoice id that does not exist, so the key learns nothing about it.
To settle an invoice of another branch, record the payment with a key that may use that branch.
Other refusals
| Error | When |
|---|---|
422 referenced_resource_not_found | the customer, the branch or the payment method does not exist, or the payment method is disabled; param names the field |
422 customer_inactive | the customer is disabled |
422 payment_method_not_in_branch | the payment method's treasury serves another branch |
409 invoice_busy on allocations[i].invoiceId | another request on that invoice held it for more than 30 seconds; send the request again |
409 invoice_busy with no param | other payments were being recorded for more than 30 seconds; send the request again |
409 reference_id_taken | another payment already has this referenceId; existingId names it when your key may read it |
422 payment_not_recorded | a business rule with no code of its own refused the payment; the message names the rule |
A refused payment records nothing.
Reading payments
GET /v1/payments lists every issued payment in the key's branches, whichever channel recorded it. createdVia says which. Filter with customerId, branchId, invoiceId, referenceId, dateFrom and dateTo; invoiceId returns the payments with an allocation to that invoice.
The list includes a payment taken with a sale (POST /v1/sales-invoices with payment) to a company customer. A sale to a person paid at issue keeps the money on the invoice: its amountPaid shows it, and no payment is created.
A payment recorded on the dashboard can carry a settlement discount: debt the merchant forgave on top of the money. The reply shows it as discount, and its allocations can add up to amount plus discount. unallocatedAmount is amount plus discount, less the allocations. A payment recorded through the API always has a discount of 0.
A payment recorded with no branch has branchId: null, and only a key with every branch sees it.