Money
Refunds
A refund is a new transaction, not a reversal — and what follows from that.
A refund is a new transaction #
A blockchain transfer cannot be reversed. There is no chargeback, no reversal and no clawback of a confirmed payment. A refund in Animica Pay is therefore a new ANM transfer from you to the customer, recorded against the original intent.
Consequences worth stating plainly:
- You need the funds. A refund spends from your address; it does not unwind the original payment.
- It costs network fees. The refund transaction pays gas like any other.
- The protocol fee is not returned. The original payment settled and the 2% was earned. Model it the way you model a card processing fee on a refunded order — see The 2% fee.
- The destination is an address, not an account. If the payer address is not spendable by the customer (an exchange deposit address, for example), refunding to it can lose the money. Confirm the destination with the customer for anything material.
States #
On the payment side the intent moves to PARTIALLY_REFUNDED or REFUNDED, and REFUNDED is final:
| From | To | When |
|---|---|---|
PAID | PARTIALLY_REFUNDED | part of the gross was refunded |
PAID | REFUNDED | the whole gross was refunded |
PARTIALLY_REFUNDED | REFUNDED | the remainder was refunded |
OVERPAID | PARTIALLY_REFUNDED or REFUNDED | returning an overpayment |
Each refund is also its own record with its own to_address, amount_nanm and tx_hash — a refund is never a mutation of the payment row.
Who signs #
Merchant-signed by default. We build the transfer and show you the destination, the amount and the network fee before anything is signed. Nothing in Animica Pay holds a key that can spend your funds — unless you opted into a forwarding address, in which case refunds can be signed by the forwarding key. See The splitting limitation for what that mode means.
Doing it over the API #
curl -sS https://pay.animica.dev/api/v1/refunds \
-H "Authorization: Bearer $ANIMICA_PAY_SECRET_KEY" \
-H "Idempotency-Key: refund_19482_10.00" \
-H "Content-Type: application/json" \
-d '{"payment_intent":"pay_X9m2ABC","amount":"10.00","reason":"damaged item"}'
Send an Idempotency-Key that identifies the refund, not just the order. Without it a retried request is a second refund, and unlike a card refund there is nothing to reverse. Full parameters and errors: API reference.
In WooCommerce #
The normal Refund button works. The plugin asks pay.animica.dev to create the refund — WordPress never holds a spending key — and the order screen shows the destination, the amount and the network fee before you confirm. Partial refunds are supported. See WooCommerce.
Reconciliation #
A refund is only complete when its transaction has a successful receipt. Until then it is prepared or pending, not done, and the reconciliation report will flag a refund that was recorded without on-chain support. As everywhere else here: inclusion is not execution.