Payment Exceptions

Handle the scenarios that deviate from the happy path — overpayments, underpayments, late arrivals, held payments, rejected payouts, and expiry.


Cryptocurrency payments are push payments, not pull payments — BEEM cannot control exactly when, or exactly how much, an end-user sends. Most exceptions come down to three causes: end-user error, unpredictable fees deducted by the sending exchange, or slow blockchain processing.

This page covers every scenario that deviates from the happy path, how to detect it from webhook data, and how to handle it. Each exception is tagged with the direction it applies to.

Overpayments

Applies to: Payment Link In

An overpayment is when the customer sends more crypto than the link requested — for example, 0.1 ETH against a 0.01 ETH invoice. The payment still moves to COMPLETE; the excess is credited along with the rest.

Detect it on the transaction-confirmed (or status-change → COMPLETE) webhook by comparing two fields:

  • paidCurrency.actual — the amount actually received.
  • paidCurrency.amount — the amount originally requested.

If actual is greater than amount, the customer overpaid.

How to handle it. The full amount sent is credited to the wallet linked to the payment. The cleanest approach is to credit the end-user's account in your own system for the full actual amount, rather than returning the small excess on-chain — returning it would cost a network fee and create more reconciliation work. Use the .actual fields to update the end-user's balance.

Underpayments

Applies to: Payment Link In

An underpayment is when the customer sends less crypto than the link requested. This commonly happens when the sending exchange deducts a network fee the customer did not account for, or the customer simply sends the wrong amount. The payment moves to UNDERPAID — a terminal status — and the funds that were sent are still credited to the wallet.

Detect it the same way: compare paidCurrency.actual against paidCurrency.amount on the transaction-confirmed or status-change → UNDERPAID webhook. actual will be less than amount.

How to handle it. The full amount sent is credited to the wallet linked to the payment. Credit the end-user's account for the actual amount received, rather than the amount you originally requested. Whether you treat the shortfall as a failed order, a partial credit, or chase the customer for the difference is a business decision your system makes.

Late payments

Applies to: Payment Link In

A late payment is when funds arrive for a payment that has already reached a terminal state — EXPIRED, COMPLETE, or UNDERPAID. It happens when:

  • the blockchain confirmation is delayed past the expiry window,
  • the customer sends funds after the link has expired, or
  • the customer sends a second payment to a link that has already completed.

You receive a transaction-late webhook. The link itself does not change status — a COMPLETE link stays COMPLETE, an EXPIRED link stays EXPIRED. The deposit address remains live on-chain, which is why funds can still land.

The webhook payload includes:

  • walletCurrency.actual — the amount credited to your wallet, converted at the current spot rate. The rate originally quoted for the link has long since expired, so late funds settle at whatever the rate is when they arrive.
  • The original exchange rate and amount sent — for reconciliation and reporting.

How to handle it. Late funds are credited to the wallet regardless of the link's status, so reconcile them against the payment uuid and decide what to do on the merchant side — credit the customer, refund, or route to operations review.

Held payments

Applies to: Payment Link In and Payment Link Out

Every payment passes through compliance screening. A payment that triggers a screening rule emits a transaction-held webhook and stays in PROCESSING until the hold is resolved — it does not credit the wallet (IN) or submit on-chain (OUT) while held.

Detect it on the transaction-held webhook. The held transaction inside the transactions array carries isOnHold: true.

How to handle it. A held payment is not a failure — most clear automatically once screening completes, after which the payment resumes its normal sequence. Surface held payments to your operations team so they are visible, but no integration action is required to release them.

Rejected payouts

Applies to: Payment Link Out

If your account has approvals configured, an approver can reject an outbound payout during review. The payout terminates in CANCELLED and the wallet is not debited — the funds never leave.

Detect it on the status-change → CANCELLED webhook.

How to handle it. Treat CANCELLED as a definitive "this payout did not happen." Reverse any provisional state in your own system — for example, un-mark a withdrawal as sent — and, if appropriate, notify the recipient that the payout was not approved.

Expiry

Applies to: Payment Link In and Payment Link Out

A payment that is not completed within its expiry window moves to EXPIRED. You receive a status-change → EXPIRED webhook.

  • For Payment Link In, expiry means no funds (or not enough) were detected in time.
  • For Payment Link Out, expiry means the recipient never completed the Hosted Payments Page.

Expiry is governed by the link expiry window — see the Expiry section on the Payment Links Overview.

📘

An expired link stays expired

Once a payment is EXPIRED it stays EXPIRED, even if funds arrive afterwards. Late funds against an expired — or completed, or underpaid — link are handled through the transaction-late webhook, not by reopening the link.

What's next