Record document-only refunds via refundPayment
Summary
refundPayment now records refunds for non-card payments as well as Stripe card payments. Requires the issueRefund right.
This release is additive: existing Stripe refund behaviour is unchanged. No new mutation is introduced.
Behaviour by payment source
- Stripe card payments — same as before: refunds money back to the original card via Stripe, returns
refundExternalIdfor reconciliation. - Non-card payments (cash, cheque, bank transfer, payment-on-account, and similar) — records a refund document on the invoice only. No money is moved and no credit note is created. The practice handles the offline refund.
refundExternalIdis not set. - Payments that still have a Stripe
paymentChargeId(including legacy rows whosepaymentSourceis e.g.UNKNOWN) — refunded via Stripe so funds return to the card. - Terminal payments — still out of scope; the mutation returns an error.
Args are unchanged: invoiceId, paymentId, amount, reason (RefundReason), optional comment (max 500 characters). Partial refunds are supported.
Integration guidance
- Stripe: Continue calling
refundPaymentas today; storerefundExternalIdif you reconcile with Stripe. - Non-card: Call the same
refundPaymentmutation with the payment'sInvoice.payments[].id. Treat success as “refund recorded”; do not expect a PSP refund id. - Querying: Use existing
Invoice.refundsfields to read refund history. - Deprecated nested input:
NewInvoicePaymentDataInput.refundsremains deprecated; userefundPayment.
Example
mutation RefundCashPayment {
refundPayment(
invoiceId: "INVOICE_ID"
paymentId: "PAYMENT_ID"
amount: 50.0
reason: REQUESTED_BY_CUSTOMER
comment: "Cash refund at reception"
) {
data {
id
refunded
refunds {
id
refundAmount
refundReason
relatedPayment
}
}
refundId
refundExternalId
error
}
}
Migration
No breaking schema changes. Existing Stripe integrations keep working. Terminal (in-person reader) payments remain out of scope for public API refunds.