---
title: Recording Payments, Refunds and Marking Paid
description: "Payments reach RackHelm four different ways: the customer pays online, the automatic charge run takes the money, account credit covers it, or you record an offline payment by hand. This article explains what each path does, how to mark an…"
url: "https://rackhelm.com/documentation/recording-payments-and-refunds/"
updated: "2026-09-24"
category: "Billing & Payments"
---

# Recording Payments, Refunds and Marking Paid

Payments reach RackHelm four different ways: the customer pays online, the automatic charge run takes the money, account credit covers it, or you record an offline payment by hand. This article explains what each path does, how to mark an invoice paid correctly, and how to handle refunds — which, in this build, are not a button in the admin area. It is written for operators and staff.

## Before you start

- The invoice action menu sits behind `edit_invoices`. Individual actions are gated further: *Mark as Paid* needs `mark_invoices_paid`, *Send Reminder* needs `send_invoice_reminders`, and *Delete* needs `delete_invoices`.
- The seeded Billing Manager role holds `mark_invoices_paid` and `refund_invoices`, but **not** `delete_invoices` or `send_invoice_reminders`.
- Authorisation is checked against every selected invoice before anything runs, so a bulk action you are not entitled to perform returns 403 and changes nothing.

## How a payment normally arrives

| Path | What happens |
| --- | --- |
| Customer pays online | From **Invoices → (open invoice) → Pay Now** in the client portal. RackHelm creates a Stripe PaymentIntent for the invoice total, the customer confirms it in the browser, and the return trip settles the invoice. The intent is bound to the invoice by its own metadata, and the amount collected is reconciled against the invoice total before anything is marked paid. |
| Stripe webhook | The backstop for a customer who closes the tab mid-payment. See [Stripe setup](/docs/stripe-setup/). |
| Automatic charge run | The **Process Due Invoices** schedule queues one charge attempt per due invoice against the customer's default (or pinned) saved method. |
| Account credit | Applied automatically at checkout and on renewals when the customer has enabled it. See [Account credit](/docs/account-credit/). |
| Offline / bank transfer | No automatic pull is possible. You record it manually with *Mark as Paid*. |

> **Warning:** A payment that collects less than the invoice total is deliberately *not* accepted as settlement. The invoice stays open and an error is written to the application log naming the invoice, the payment reference and both figures in pence, for a human to reconcile. Search the log for "Short payment on Invoice" if a customer insists they have paid.

## Recording a payment by hand

1. Open **Billing → Invoices**, or open the individual invoice.
2. Tick the invoice (on the list) or use the **Actions…** selector (on the invoice page).
3. Choose **Mark as Paid** and select **Apply**.

Marking paid is not a status flip. It runs the single settlement routine every payment path uses, which:

- sets the status to `paid` and stamps `paid_at`;
- advances the next due date on *every* order the invoice covers — including all of them on a merged invoice;
- queues any domain renewals the invoice paid for, guarded so a repeated signal cannot buy a second term;
- reactivates services that were suspended for non-payment;
- credits the customer's balance if the invoice was a credit deposit;
- pushes upgraded resources live if the invoice was an upgrade difference;
- regenerates the PDF, and emails the customer a "Payment Successful" receipt naming the amount.

> **Tip:** Clicking *Mark as Paid* twice is safe. The routine detects that the invoice is already paid, does nothing further, and sends no second receipt.

![An individual invoice page with the Actions dropdown open, listing Mark as Paid, Mark as Pending, Mark as Void, Process Invoice, Regenerate PDF, Send Reminder, Void and Regenerate from Order, and Delete Invoice](/wp-content/uploads/rackhelm-docs/billing-invoice-actions.png)
Every staff-side invoice operation lives in this one Actions selector, which is the same control as the bulk selector on the list.

## The full action list

| Action | What it does | Permission |
| --- | --- | --- |
| **Mark as Paid** | Full settlement, as above. | `mark_invoices_paid` |
| **Mark as Pending** | Sets status `pending` and clears `paid_at`. Does not reverse due-date advances or refund anything — use it to reopen an invoice you settled by mistake, then correct the order manually. | `edit_invoices` |
| **Mark as Void** | Sets status `void` and clears `paid_at`. Voided invoices are excluded from the duplicate-billing check, so the period becomes billable again. | `edit_invoices` |
| **Process Invoice(s)** | Resets the invoice to `pending` and queues an immediate charge attempt against the customer's saved card. This moves real money. | `edit_invoices` |
| **Regenerate PDF** | Re-renders the document from current data and the currently selected template. | `edit_invoices` |
| **Send Reminder** | Intended to email the customer a reminder for this invoice. | `send_invoice_reminders` |
| **Void & Regenerate from Order** | Voids the invoice and rebuilds it from the underlying order. A confirmation dialog offers **Alert user via email**, ticked by default. Refuses to act on a `paid` invoice. | `edit_invoices` |
| **Delete Invoice(s)** | Removes the invoice record permanently. | `delete_invoices` |

> **Warning:** *Send Reminder* from this menu does not work in the current build — it hands the reminder job the invoice's order where the job expects the invoice itself, and the request errors. Use the **Invoice Reminders** schedule for reminders (see [How invoicing works](/docs/how-invoicing-works/)), and chase individual customers by ticket or email until this is fixed.

> **Warning:** *Process Invoice(s)* charges the card immediately. Do not use it to "refresh" an invoice's appearance — that is what *Regenerate PDF* is for.

## Refunds

RackHelm has a `refund_invoices` permission and a matching policy, but **there is no admin screen or action that sends a refund to a gateway**. The only automatic refund in the system is a safety net during checkout: if payment succeeds but the order cannot be created (for example two customers race the last use of a limited coupon), RackHelm reverses the charge itself — through Stripe's refund API, PayPal's capture-refund endpoint or Square's refunds endpoint as appropriate — and raises an operator alert. If that automatic reversal also fails, a "Manual refund required" alert is raised naming the gateway, charge reference and amount.

### Refunding a customer in practice

1. Issue the refund in the gateway's own dashboard (Stripe, PayPal or Square), against the transaction reference stored on the invoice.
2. In RackHelm, decide what the invoice should now say. For a full reversal, **Mark as Void**. For a partial reversal there is no partial-payment ledger, so leave the invoice paid and record the adjustment as credit instead.
3. If the customer is to keep the money on account rather than back on their card, add it as account credit from **Users → (customer) → Billing** with a clear reason — see [Account credit](/docs/account-credit/).
4. Note the gateway refund reference in the customer's ticket or in the credit reason so the two sides reconcile later.

> **Note:** A package change or a billing-cycle change already prorates automatically: the unused value is applied to the difference invoice as a negative line, and anything left over is added to the customer's balance as a credit-ledger entry rather than refunded to a card. Do not refund that a second time by hand. A cancellation does not mint credit — it voids or reduces the pending invoice instead.

## Where the transaction reference lives

Once an invoice settles, the gateway's own identifier is written into the invoice's `meta` as `transaction_id`, alongside the `gateway` slug. For a Stripe payment that is the PaymentIntent id; for PayPal, the capture id; for Square, the payment id; for a bank transfer taken at checkout, a generated `BT-…` reference for the customer to quote. An invoice fully covered by account credit records the literal value `Account Credit`.

## Troubleshooting

| Symptom | Cause and fix |
| --- | --- |
| 403 on a bulk action | You lack the specific permission that action maps to. The whole batch is refused before anything runs, so nothing is half-applied — grant the permission and retry. |
| Marked paid, but the service is still suspended | Reactivation is queued. Check that a worker is consuming the queues, then re-check the order item. If the invoice was already `paid`, the settlement routine did nothing at all — mark it pending and paid again to re-run it. |
| Marked paid, but the next due date did not move | The invoice has no resolvable orders — most commonly a custom invoice, whose ghost order has no items. Adjust the service's next due date on the order directly. |
| Customer paid but the invoice is still open | Either the amount collected was short of the total (search the log for "Short payment on Invoice"), or the Stripe webhook is not reaching the portal. See [Stripe setup](/docs/stripe-setup/). |
| A card was charged twice | Very unlikely — the checkout capture is serialised per order with a lock, the gateway call carries an idempotency key, and the automatic charge job is unique per invoice. Check the gateway dashboard for two distinct references before refunding; if there really are two, refund one in the gateway. |
| "Bank transfer awaiting confirmation" alert | The customer's default method is Bank Transfer, so the invoice was set to `unpaid` with a reference rather than charged. Confirm the funds arrived, then **Mark as Paid**. |
| Delete Invoice is missing or 403s | `delete_invoices` is not granted to the Billing Manager role by default. Grant it deliberately — deletion is permanent. |
