Refunds
A refund returns money from a successful charge back to the shopper. Each refund is one attempt,
with its own id and its own status, and it always points at the chargeId it belongs to.
You can create refunds through the API, or from your merchant account in Lighthouse . Both produce the same refund object.
When a refund succeeds, Beam records a transaction for it, so the money going out appears
in your accounting records the same way the money coming in does. That transaction’s transactionType is either
REFUND or VOID. Both mean the refund succeeded, and the refund itself does not tell you which one you will get, so
treat them the same. See Transaction types.
A refund never changes the charge it points at. The charge stays SUCCEEDED after a successful refund.
What can be refunded
- The charge must have
statusSUCCEEDED. APENDINGorFAILEDcharge cannot be refunded. - Refunds are supported on these payment method types:
CARD,CARD_INSTALLMENTS,QR_PROMPT_PAY,ALIPAY, andWECHAT_PAY. - Partial refunds are only supported on
CARDcharges. For every other refundable payment method, one refund has to cover the full amount of the charge. - Refunds use your available Beam balance. If your balance is not enough, the request can be rejected.
Refund flow
-
Shopper asks for a refund: through your support flow, your app, or your own admin tool.
-
Create the refund: your backend calls
POST /api/v1/refundswith thechargeIdof the successful charge, and optionally anamountand areason. -
Beam returns the refund id: the refund is created with
statusPENDING. Store therefundIdagainst your order. -
Tell the shopper it is in progress: the refund is not final yet, so do not promise the money has arrived.
-
Beam submits the refund: this is not instant, and it is not the same as the shopper being paid. How long the money takes to appear depends on the shopper’s bank or wallet provider.
-
Beam notifies your server: when the refund reaches a final status, Beam sends a webhook to your endpoint.
-
Your server acknowledges: respond with HTTP 2xx.
-
Tell the shopper the outcome: on
refund.succeeded, tell the shopper the refund is on its way, not that it has arrived. Onrefund.failed, tell the shopper and create a new refund to retry.
Refund lifecycle
A new refund starts as PENDING. Beam then submits it to the payment provider and moves it to SUCCEEDED or
FAILED.
| Status | Meaning |
|---|---|
PENDING | Beam is still processing the refund and the outcome is not known yet. |
SUCCEEDED | The payment provider accepted the refund. Beam creates a transaction for it. |
FAILED | The refund could not be submitted. See Refund failure codes. |
SUCCEEDED and FAILED are final. A refund never changes status again after it reaches one of them.
SUCCEEDED does not mean the shopper has their money back. It means Beam submitted the refund and the payment
provider accepted it. The money then moves on the provider’s own schedule, which can take several days depending on
the shopper’s bank or wallet.
Beam has no way to confirm that the shopper received the money, and neither the API nor the webhooks report it. Tell the shopper the refund is on its way, not that it has arrived. If they ask when it will arrive, tell them to check with their bank or wallet provider.
Refund failure codes
When a refund is FAILED, the failureCode field says why. There is one code:
failureCode | Meaning | What to do |
|---|---|---|
RE_PROCESSING_FAILED | The refund could not be processed. | Create a new refund and try again. |
A failure code is not an API error. A refund that fails still returns HTTP 2xx on creation, because Beam accepted your
request. HTTP 4xx and 5xx responses mean the request itself was rejected: for example 422 with REFUND_PENDING when
a refund on that charge is still being processed. See Error Handling.
Track the refund
The create call only tells you the refund exists. To learn the outcome, use webhooks.
Recommended: listen for webhooks. Subscribe to refund.succeeded and refund.failed. Beam calls your endpoint as
soon as the refund reaches a final status, so you do not have to ask for it. Record the refund when
refund.succeeded arrives, not when the create call returns, because the create call only tells you the refund was
accepted.
Fallback: poll the refund. If you cannot receive webhooks yet, call GET /api/v1/refunds/{refundId} until the
status is SUCCEEDED or FAILED. Polling costs you requests and delays the outcome, so use it only until your
webhook endpoint is ready. Poll with exponential backoff and jitter, because polling too fast gets 429 responses.
See Rate limits and retries.
Remember that a final status means the payment provider accepted the refund, not that the shopper has been paid. See Refund lifecycle.
Webhook events for refunds
refund.succeeded: the payment provider accepted the refund. It does not confirm the shopper has been paid.refund.failed: the refund could not be submitted.transaction.created: Beam recorded the transaction for a successful refund.
See Event Types for each payload and the Webhook guide for how to set your endpoint up. For which event to act on, see Track the refund.
Next steps
- Refunds API: the endpoints, with request and response examples.
- Transactions: how a successful refund appears in your accounting records.