Skip to content

Refunds

A refund returns money to the customer who paid you. You can start one on the card machine, in the merchant portal, or through the API, and all three end up in the same place: a refund recorded against the original payment, with an audit trail showing who authorised it.

Two things decide how a refund behaves, and it is worth knowing them before you start.

Has the payment settled? A payment that has not yet settled is cancelled rather than refunded. Cancelling voids the whole thing, so nothing ever reaches your bank and nothing reaches your customer’s statement. A settled payment is refunded instead, in full or in part.

Was it card or Bitcoin? Card refunds move money. Bitcoin refunds do not: Musqet records them for your books, and you return the funds yourself. See Bitcoin refunds are recorded, not sent.

From the sale screen, tap the grid menu icon at the top to open the menu. Under the Refunds heading, choose Card Refund, labelled “Return money to customer”. The steps are the same on a PAX and on a Verifone: both run the Musqet app and share these screens.

With password protection on, which is the default, the terminal shows a Refund password prompt and asks the staff member for their staff ID and password before the refund goes anywhere. The refund is then recorded against that person rather than against the device.

If the terminal is running unattended as a kiosk, it shows Refund verification and waits for a staff member. That request cancels itself after five minutes.

With it off, the terminal refunds straight away. No password, nobody named, and nothing written to the refund log, because the log is written by the password check. See The refund log. The setting is Password protect refunds & voids in the portal, per business, and turning it off trades your refund audit trail for a couple of seconds at the till.

Open the payment link, find the payment, and choose Refund. The dialog asks for a refund type:

  • Refund sale, for a settled sale, in full or in part. The amount box opens pre-filled with everything that is still refundable.
  • Cancel, for a payment that has not settled yet. Cancel has no amount, because it voids the payment in full.

Refunds are issued against the payment link that took the payment. Card and Bitcoin have separate endpoints, because one moves money and the other only records that you did.

Terminal window
curl -X POST https://api.musqet.tech/api/v1/payment-links/{id}/refunds/card \
-H "Authorization: Bearer msqt_api_live_..." \
-H "Content-Type: application/json" \
-d '{ "kind": "REFUND_SALE", "amountInCents": 1250 }'

Amounts are always integers in the smallest unit of the payment’s currency, so £12.50 is 1250.

kind takes one of three values, and the third is not like the other two:

kindWhat it doesNeeds
CANCELVoids an unsettled payment in full. No amountTAKE_PAYMENTS_WRITE
REFUND_SALERefunds a settled payment, in full or in part, with amountInCents. Capped at what the payment tookTAKE_PAYMENTS_WRITE
REFUNDAn uncapped refund with amountInCents, not limited by the payment it refunds againstTAKE_PAYMENTS_WRITE and REFUNDS_WRITE

REFUND is the one to be careful with. It is not limited by the original payment, so it can send more than the customer paid, and it is the reason REFUNDS_WRITE exists as a separate permission: an API key without it gets 403 for this kind and works normally for the other two. The portal never issues it, so a refund of this kind always came from the API.

Recording a manual Bitcoin refund has its own endpoint. It moves no funds: see Bitcoin refunds are recorded, not sent.

Terminal window
curl -X POST https://api.musqet.tech/api/v1/payment-links/{id}/refunds/bitcoin \
-H "Authorization: Bearer msqt_api_live_..." \
-H "Content-Type: application/json" \
-d '{ "amount": 25000, "reason": "Returned in store" }'

Three differences from the card endpoint are worth reading twice:

  • amount is in satoshis, not the smallest unit of a fiat currency, and there is no amountInCents field.
  • reason is optional and stored against the refund.
  • There is no kind. The amount is always capped at what the original Bitcoin payment received, so there is no uncapped form and no equivalent of REFUNDS_WRITE. TAKE_PAYMENTS_WRITE is enough.

You need all of these, or the refund will be refused:

  • The right permission. TAKE_PAYMENTS_WRITE covers cancelling and refunding a sale. On the terminal, the staff member also needs the Refund & void permission, which is set per staff member in the portal.
  • A refund password that has been changed at least once, if password protection is on. New staff members are created with a temporary password and must set their own before their first refund. With password protection off, no password is asked for and this does not apply.
  • Headroom on the original payment, for everything except an uncapped REFUND through the API. A cancel or a refund-sale cannot exceed what the payment took, counting refunds already made against it. A REFUND is not capped at all, which is why it needs REFUNDS_WRITE.
  • A payment that reached the gateway. A payment with no gateway reference cannot be refunded, because there is nothing to refund against.

Staff members can also carry a per-transaction limit and a daily limit. Both are optional, and a staff member with neither set has no ceiling of their own.

Cancel and refund sale are mutually exclusive

Section titled “Cancel and refund sale are mutually exclusive”

A payment can be cancelled, or it can be refunded, but never both.

Once a payment is cancelled there is nothing left to refund, and once any part of it has been refunded it can no longer be cancelled. Partial refunds can be repeated until the original amount is used up, and the total is capped at what the payment took.

Recording a manual Bitcoin refund does not move any Bitcoin. It writes the refund into your records so your books balance, and returning the funds is something you arrange yourself, out of band. The portal is explicit about this at the point of use, and the payment is badged afterwards as “Manual refund. Funds returned out of band”.

Like card refunds, the recorded amount is capped at what the original Bitcoin payment received.

For a cancel or a refund-sale, Musqet reserves the refund before asking the gateway for it, then settles the reservation to whatever the gateway says. The reservation is what stops two people refunding the same payment at the same moment and taking it past its limit between them.

An uncapped REFUND through the API is not reserved. It goes straight to the gateway and is recorded afterwards, because there is no limit for a reservation to protect. Two of them issued at once will both go through.

sequenceDiagram
    accTitle: How a card refund travels
    accDescr: The merchant asks Musqet for a refund. Musqet checks the permission and the remaining headroom, reserves the refund as pending, sends it to the card gateway, and settles the reservation to whatever the gateway returns before reporting the final status back.
    participant Merchant
    participant Musqet
    participant Gateway
    Merchant->>Musqet: Refund this payment
    Musqet->>Musqet: Check permission and remaining headroom
    Musqet->>Musqet: Reserve the refund as pending
    Musqet->>Gateway: Send the refund
    Gateway-->>Musqet: Approved or declined
    Musqet->>Musqet: Confirm the reservation to that outcome
    Musqet-->>Merchant: Final refund status

If the gateway cannot be reached at all, the reservation is released and the refund is marked failed, so you can try again. A timeout does not prove the refund failed at the card scheme, so check the refund against the payment before retrying a large one.

Match what the person in front of you is seeing.

What they seeWhat it meansWhat to do
Incorrect passwordThe refund password did not matchRe-enter it. Repeated failures lock the staff member out
Incorrect staff IDThe staff number entered does not exist at this businessCheck the number in the portal under staff members
Too large transaction!Over that staff member’s per-transaction limitA staff member with a higher limit must authorise it, or raise the limit in the portal
Daily limit reached!Their refunds today already add up to their daily limitAs above, or wait until tomorrow
Locked out! or Locked out until …Too many failed password attemptsWait for the lockout to expire. It lengthens with each round of failures
Too many attempts, try again later!Same lockout, hit during verificationAs above
Could not reach the server!The terminal has no route to MusqetCheck the terminal’s connection, then retry
Payment has already been cancelledThe payment was voided, so there is nothing to refundNothing to do. The customer was never charged
Payment has already been refundedA partial refund exists, so the payment cannot be cancelledRefund the remaining amount instead of cancelling
Refund exceeds original sale amountThe amount asked for is more than the headroom leftRefund the remaining amount. The portal pre-fills it for you

The refund log is a record of password checks, not of refunds. Every terminal refund attempt that goes through the password check is logged against the staff member who tried, whether it succeeded or not, which is what makes rejections visible as well as approvals.

So if Password protect refunds & voids is off for a business, terminal refunds write nothing here at all. The log is not merely missing a name: there is no entry. If you are relying on this log to account for refunds, check that setting first.

The log is in the portal, and available through the API for anyone building their own reporting.

Entries carry one of four statuses. Note the American spelling, which is what the API returns:

StatusMeaning
authorizedThe staff member was authorised and the refund went ahead
failedThe attempt was rejected
limit_exceededRejected for exceeding the per-transaction limit
daily_limit_exceededRejected for exceeding the daily limit

A rejection also records why: invalid_signature, permission_denied, limit_exceeded or daily_limit_exceeded. invalid_signature means the request was not signed by the staff member it claimed to be from, and is worth investigating rather than retrying.

The void log is the void equivalent of the refund log, and it lives in the portal under Staff members > Void log. It lists every void taken on the terminal, newest first, and you can filter it by the staff member who took them.

Each entry records who voided the sale and what it voided:

  • Who, by name and staff number.
  • The original sale, by invoice number, amount and currency.
  • When that sale was taken, and which terminal the void was taken on.

It also records the outcome:

StatusMeaning
authorizedThe staff member was authorised and the void went ahead
failedThe attempt was rejected: either the void password did not match (invalid_signature), or the staff member does not have the refund & void permission (permission_denied)

Unlike a refund, a void has no amount of its own to weigh against a limit, so the per-transaction and daily limits that apply to refunds do not apply to voids.

Like the refund log, the void log is written by the void password check, so it follows the same Password protect refunds & voids setting. With password protection off, a terminal void is not recorded here, for the same reason a refund is not. See The refund log.

Musqet sends a card refund on to the gateway straight away, but when it lands in the customer’s account is not something Musqet controls. A card refund travels back through the card scheme, the acquirer and the customer’s own bank, and each of those adds its own time. In practice a refund usually shows on the customer’s statement within one to five working days, and it can be longer over a weekend or a bank holiday.

So a customer who has been refunded but cannot yet see the money has not lost it: it is in transit between the banks. There is nothing to re-send from Musqet, and issuing the refund again would return the money twice. If it still has not arrived after several working days, the customer’s own bank is the place to chase it.

A chargeback is not the same as a refund. A refund is money you choose to return. A chargeback is the customer asking their own bank to reverse a card payment, for example because they do not recognise it or believe something went wrong, and the card scheme, not Musqet, decides the outcome.

If a dispute is upheld, the payment is reversed and the money goes back to the customer. The process runs on the card scheme’s own timetable and can take weeks. It sits outside anything you do on the terminal or in the portal, so it is not something you approve or decline there.

If you receive a chargeback, or a request for information about a payment, contact Musqet support so we can help you work through it.

  • The v1 API reference for every field on the refund endpoints, generated from the same code that serves them