Skip to content

Invoices and credit

Every bill PaySigna collects for you is an invoice: a subscription’s period bill (see Subscriptions), or a one-off invoice you issue yourself — consulting, a setup fee, a business customer’s purchase order. This page covers what an invoice is made of, one-off invoices, and the two ways to give money back on one: credit notes and customer credit.

Everything here needs a secret key: billing.read to read, billing.write to change. It is under the Billing tag in the API tester.

An invoice is the sum of its lines, less any discount and credit:

amount = subtotal − discount − credit_applied
Field What it is
kind subscription (a period’s bill), one_off (issued by you) or proration.
subtotal The lines, before discount and credit.
discount Taken off by the subscription’s coupon. Always 0 on a one-off invoice.
credit_applied Paid from the customer’s credit balance, plus anything taken off by a credit note while the invoice was open.
amount What is collected. Never below zero.
credited Given back by credit notes after it was paid.
transaction The charge that paid it. Absent when nothing had to be paid — a coupon or credit covered the whole bill.
subscription, period_start, period_end Present on a subscription’s bill; absent on a one-off invoice.
due_on, purchase_order, memo, payment_method A one-off invoice’s terms, and the number it is prompted on (masked).

GET /v1/invoices/{id} adds lines. Each line has a kind (plan, one_off, discount, credit and others), a description, quantity, unit_amount and amount. Discount and credit lines are negative. The list, GET /v1/invoices, leaves the lines out.

An invoice that comes to zero is paid as soon as it opens, with no charge and no prompt. invoice.paid still fires, with amount: 0.

POST /v1/invoices bills a customer you already have. The Idempotency-Key header is required, so a retried request never bills them twice.

Terminal window
curl https://sandbox.paysigna.com/v1/invoices \
-H "Authorization: Bearer psg_test_…" \
-H "Idempotency-Key: 4c1d7e20-8a3b-4f55-9d0e-6b2a1c7f3e98" \
-H "Content-Type: application/json" \
-d '{
"customer": "<customer id>",
"currency": "GHS",
"lines": [
{ "description": "Website build — phase 1", "unit_amount": 250000 },
{ "description": "Hosting setup", "quantity": 2, "unit_amount": 15000 }
],
"due_in_days": 14,
"purchase_order": "PO-2026-118",
"payment_method": { "momo_network": "mtn" }
}'
Field Notes
customer A stored customer’s id. A customer blocked on your account cannot be invoiced.
currency The invoice’s currency.
lines 1 to 50 lines, each a description, a quantity (default 1) and a unit_amount in minor units.
due_in_days Payment terms, 0 to 90. 0, the default, is due on receipt. Sets due_on.
purchase_order Optional. Your customer’s PO number, shown on the invoice and in the notice.
memo Optional note.
payment_method Required. phone defaults to the customer’s phone. Name its network with momo_network (a code such as mtn) or momo_network_id.
apply_credit Spend the customer’s credit balance on it. Defaults to true.

One-off invoices are collected by mobile-money prompt only. All fields: issue a one-off invoice.

The invoice is created open and fires invoice.created and invoice.finalized. The customer is sent the invoice by SMS and email at once. When the first prompt goes out depends on the terms:

due_in_days First prompt Retries counted from
0 (due on receipt) Within a minute. The day it was issued.
1 or more 09:00 UTC on due_on (09:00 in Ghana). Nothing is asked for before the due date, and the notice tells the customer the prompt will come that day. That first prompt.

After that it is collected like a subscription’s bill, with these differences:

  • Failed prompts are retried on the days in your retry schedule. When those run out, nobody is prompted again.
  • collect_until is 30 days after due_on. If the invoice is still unpaid then, it becomes uncollectible. Your schedule’s “end on day” setting does not apply to one-off invoices.
  • A failed attempt fires invoice.payment_failed to you, with attempt_count and failure_message. An attempt refused before it reached a partner reads “The payment was refused before it was sent: {reason}”, where the reason is the same detail the API would return (a limit, a blocked number, a suspended account, a method your account can’t use). The customer is sent an SMS and email saying it was not collected, the date it is still due by, and when the next prompt will come — or that there will be no more prompts.
  • When it is paid, the customer is emailed a receipt.

If the customer’s credit covers the whole invoice, it is paid at once, nobody is prompted, and the customer is emailed a receipt for the amount paid from credit.

Each attempt is an ordinary charge, with the invoice number as its reference. It appears in your transactions and settles like any other payment.

POST /v1/invoices/{id}/void withdraws an open one-off or proration invoice. Collection stops, nobody is prompted again, and invoice.voided fires. It cannot be undone. Any customer credit the invoice had spent is given back, as a credit entry with reason adjustment and the note “Credit returned: invoice … was voided.” This happens however the invoice is voided: by you, or by cancelling, pausing or closing.

You cannot void Instead
A subscription’s period bill Cancel or pause the subscription, which voids its open bill. Otherwise the next bill would simply open again.
A paid invoice Issue a credit note.
An invoice the customer is being prompted for right now Try again in a few minutes.

A credit note lowers what an invoice is worth. POST /v1/credit_notes, with an Idempotency-Key:

{
"invoice": "<invoice id>",
"amount": 5000,
"reason": "order_change",
"memo": "One hosting seat cancelled",
"outcome": "customer_credit"
}

reason is duplicate, fraudulent, order_change, product_unsatisfactory or other (all fields). What outcome you may use depends on the invoice:

Invoice outcome What happens
Open reduce_due The amount due goes down. If it reaches zero, the invoice is paid. A subscription waiting on its first bill then becomes active.
Paid customer_credit The amount goes on the customer’s credit balance. Their next invoice spends it.
Paid refund The payment that paid the invoice is refunded by that amount. Also needs the refund.create scope. Follow the refund through refund on the credit note and GET /v1/refunds/{id}.

Rules:

  • On an open invoice, the amount cannot be more than the invoice still asks for.
  • On a paid invoice, the total credited cannot be more than the invoice amount. credited shows how much has been given back so far.
  • A paid invoice that cost nothing (no transaction) cannot be refunded. Give credit instead.
  • Void and uncollectible invoices cannot be credited.
  • An open invoice cannot be reduced while the customer is being prompted for it, because they would be approving the old amount. Try again in a few minutes.

If the partner doesn’t answer the refund in time, the call fails with 504 partner_timeout, but the refund is kept against your Idempotency-Key. Retry with the same key and you get the credit note for that refund; never send it with a new key.

A refund credit note is refused before any money moves if it breaks a rule. Each credit note fires credit_note.created. GET /v1/credit_notes?invoice=<id> lists an invoice’s credit notes.

A customer’s credit balance is held per currency. It is spent automatically by their next invoice in that currency, whether a subscription bill or a one-off invoice, until it runs out. The spend shows on the invoice as credit_applied and a negative credit line. A one-off invoice can skip it with "apply_credit": false.

Credit comes from credit notes with outcome: customer_credit, from a payment that arrived after its bill closed (below), or from a grant:

Terminal window
curl https://sandbox.paysigna.com/v1/customers/<customer id>/credit \
-H "Authorization: Bearer psg_test_…" \
-H "Content-Type: application/json" \
-d '{ "amount": 2000, "currency": "GHS", "note": "Sorry about the outage on 3 Oct" }'

Grants are positive only. Credit goes down only by being spent, so a balance can never fall below zero, and it cannot be paid out as money.

GET /v1/customers/{id}/credit returns balances (one per currency) and entries, the history. Each entry is positive (credit given) or negative (credit spent), with a reason: credit_note, adjustment (a grant), applied_to_invoice, proration, overpayment, overpayment_refund (credit used up by refunding the payment it came from), or overpayment_refund_failed (given back because that refund failed).

A prompt can expire, the attempt be marked failed, and the customer’s payment then be confirmed later by the partner (see late outcomes). The billing cycle picks these up:

  • The bill is still open: that payment pays it, with invoice.paid and the receipt or subscription activation as usual.
  • The bill is already closed (paid by another attempt, voided, or uncollectible): the amount becomes the customer’s credit, reason overpayment, with a note such as “Payment received after invoice INV-… was closed (paid).” Their next bill in that currency spends it, or you can refund the payment.

The same applies to a payment that completes after its bill was voided, for example a subscription cancelled while a charge was in flight. Each payment is handled once. It is never dropped.

Refunding the late payment uses up its credit. The refund spends the credit kept from that payment in the same step, so the customer never gets both. You can refund only what is still unspent: the smaller of what that payment left as credit (less anything already refunded) and the customer’s whole credit balance, since credit already spent on a later bill can’t be refunded again. Beyond that the refund is refused with 409 conflict: “This payment arrived after its bill was closed and was kept as the customer’s account credit. Refunding it spends that credit, and {amount} of it is unspent, so at most that can be refunded.” If the refund then fails, the credit is given back.

Event When
invoice.created Any invoice opened, including a one-off.
invoice.finalized A one-off invoice was issued.
invoice.paid An invoice was paid. amount is 0 when a coupon or credit covered it, and there is no transaction_id.
invoice.payment_failed An attempt failed. More may follow.
invoice.uncollectible Collection ended unpaid. For a one-off invoice nothing else happens.
invoice.voided An open invoice was voided.
credit_note.created A credit note was issued. Carries the outcome, and refund_id for a refund.

Invoice events for a one-off invoice carry no subscription_id. See Webhooks for signatures and retries.

When How
A one-off invoice is issued SMS and email The amount, invoice number, due date and any PO number. Due on receipt, a prompt follows at once; on terms, it says a prompt will come on the due date.
An attempt to collect it failed SMS and email The date it is still due by, and when the next prompt will come, or that no more will.
It is paid Email A receipt. Also sent when credit covered the whole invoice.

They are sent in your name. In the sandbox they are prefixed [TEST].