Refunds
A refund is a transfer from your wallet back to the payer. P2Flux works out who and how much, and verifies it afterwards — it never holds the money in between.
A refund is a transfer from the merchant’s wallet to the payer’s. P2Flux works out who and how much from the original settlement, locks those terms so they cannot be edited in a browser, and verifies afterwards that the transfer happened. It never takes custody and never sends the transaction itself.
How a refund works
A completed blockchain payment cannot be reversed — that is true of the chain, not a P2Flux limitation. So a refund is a second, separate payment going the other way, and the endpoints here exist to make that safe rather than to move money.
Neither endpoint accepts a destination address, a merchant or a maximum from the caller. All three are derived from the original settlement on chain. If a caller could name the destination, a refund button would be a withdrawal form.
1. Lock the terms
Work out the refund from the original settlement and lock it.
Exactly one of intent or subscription identifies the settlement, and either way the transaction hash is required: the capability says what was authorized, the receipt says what actually happened.
The response returns refund_token, chain_id, token, merchant, payer, the original_amount and the refund_amount (each with a base-unit twin), and expires_at.
It lives fifteen minutes and exists only to carry the terms to a browser. Reconciliation later uses /v1/refunds/verify with the original settlement, which needs no token at all.
2. The merchant sends it
Open the hosted checkout at <checkout>/#/refund/<refund_token> and connect the merchant wallet. The checkout shows the locked terms and sends the transfer.
It reports back to the opening page by postMessage, twice: p2flux.refund.sent when the transaction is broadcast, and p2flux.refund.confirmed once it has settled. Both carry tx_hash.
You can also skip the checkout entirely. Nothing here is privileged: a refund is a plain token transfer of refund_amount to payer, so a treasury tool or a script that already holds the merchant key can send it and you verify the result the same way.
3. Verify it landed
Confirm the refund happened, and that it is settled.
Takes the original settlement again — tx_hash plus intent or subscription and period_index — with refund_amount in base units and refund_tx_hash. Deliberately not the prepare token, so you can reconcile hours or days later, after a crash or a support ticket, without having kept a fifteen-minute token alive.
A settled refund returns status: "REFUNDED" with refund_tx_hash, refund_amount, original_amount, payer, merchant and block_number.
Refunding a renewal
Refunds are per charge, never per subscription. Identify the renewal with the capability, the transaction that charged it, and its period_index. Refunding one renewal does nothing to the subscription itself: it stays active and the next renewal will still be due.
The refundable amount is what the customer was charged for that period, excluding the network cost they reimbursed. To stop future charges as well, the customer cancels — see Cancel and revoke.
Refunding twice
No refund history is stored — the same statelessness that means we never hold your money means we hold no record of what you sent. Nothing in the API stops a second refund of the same payment, and each one would be a real transfer out of your wallet. One refund per payment is your integration’s job, enforced against your own order record.