Skip to main content

Refunds and credit notes

Beta

Being rolled out. The interface described here is the one of the next release: it may still change, and may not be enabled on your tenant yet.

What it is for​

Refund a customer, fully or partially. The refund goes through the provider that collected the payment (Stripe or PayPal), from your account, and the platform issues the credit note that corrects the original invoice.

Prerequisites​

  • A token with the refunds scope.
  • The payment identifier (pay_…): in the payment.succeeded webhook, in the paid cart (payment_id), or with GET /v1/payments?customer_id=….

1. Refund​

curl -s -X POST https://billing-api.dev.stackngo.fr/v1/refunds \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-H "Idempotency-Key: refund-order-10042-1" \
-d '{ "payment_id": "pay_01J9ZQ8T…", "amount": "18.00", "reason": "Support de cours non livré" }'
FieldRole
payment_idthe payment to refund
amountamount to refund, tax included; without amount, full refund of what remains refundable
reasonreason (500 characters at most), shown on the credit note

Idempotency-Key is required: a refund sent again with the same key is not executed twice.

What you get​

{
"id": "ref_01J9ZT4F5G6H7J8K9L0M",
"payment_id": "pay_01J9ZQ8T…",
"status": "pending",
"amount": "18.00",
"currency": "EUR",
"reason": "Support de cours non livré",
"created_at": "2026-10-10T09:00:00Z"
}

When the provider confirms, you receive refund.succeeded; the refund then carries credit_note_id, the credit note issued. The payment becomes partially_refunded or refunded, and amount_refunded adds up the refunds.

The credit note reads like an invoice:

curl -s "https://billing-api.dev.stackngo.fr/v1/invoices?customer_id=cus_01J9ZR1A2B3C4D5E6F7G&kind=credit_note" \
-H "Authorization: Bearer $TOKEN"

It has kind: "credit_note", its own number and corrects_invoice_id, the invoice it corrects. Its Factur-X PDF is downloaded like an invoice.

What you can expect​

  • One credit note per successful refund, numbered in its own series, without gaps.
  • The original invoice is never changed: the credit note corrects it.
  • Several partial refunds are possible, up to the payment amount.
  • The time for the money to reach your customer's account is that of Stripe or PayPal.
  • Refunds appear in the refunded column of the payments report.

If it does not work​

What you seeCauseFix
422 on amountamount above what remains refundable, or not decimalcheck amount and amount_refunded of the payment
409payment already fully refunded, or same Idempotency-Key with a different bodyread the payment again; one key per refund
refund.failedthe provider declined (insufficient balance on your account, payment too old…)see the details in your Stripe or PayPal dashboard, then retry with a new key
403refunds scope missingadd it to the application in the BillingAccount

See also Payments troubleshooting.

See also​