Crypto Refunds

This page is for advanced / cryptocurrency-native companies. If that’s not you, head back to the Quickstart for the standard flows.

Once a crypto payment has successfully settled, you can programmatically issue a refund to your customer’s wallet. Refunds are processed on-chain, and you’ll receive a transaction hash that can be shared with the end user as proof of refund.

Before issuing a refund:

  • The original payment must be settled
  • You must have the paymentId
  • You must provide a valid destination wallet address

Step 1: Initiate a Refund

Send a PUT request to the refund endpoint with the desired refund details.

Request

curl --location --request PUT 'https://api-sandbox.coinflow.cash/api/merchant/payments/{paymentId}/refund' \
--header 'Authorization: coinflow_sandbox_11111111111111111111111111111111_11111111111111111111111111111111' \
--header 'Content-Type: application/json' \
--data '{
"refundReason": "userCancellation",
"destinationWallet": "123abcd",
"partialAmount": {
"cents": 200
}
}'

Parameters

FieldTypeDescription
refundReasonstringReason for refund (e.g. userCancellation)
destinationWalletstringWallet address where funds will be sent
partialAmountobjectAmount to refund in cents (optional; omit for full refund)
allowSubMinimumRemainderbooleanOptional. Accept a partial refund that leaves a remainder below the refund minimum. See Refund minimums.

Response

"RefundPipeline{paymentId}"

This indicates the refund has been initiated and is being processed.


Step 2: Retrieve Payment Refund Details

To confirm the refund and retrieve on-chain details, fetch the payment by ID.

Request

curl --location 'https://api-sandbox.coinflow.cash/api/merchant/payments/{paymentId}' \
--header 'Authorization: coinflow_sandbox_11111111111111111111111111111111_11111111111111111111111111111111'

Step 3: Inspect Refund Data

Within the response, reference the following fields:

Full Example Response

{
"_id": "69fa33f366fad13675d6c897",
"paymentId": "51e5ac49-d3ad-412c-afdb-acb1a5b50b3d",
"totals": {
"subtotal": { "cents": 300, "currency": "USD" },
"creditCardFees": { "cents": 2, "currency": "USD" },
"chargebackProtectionFees": { "cents": 0, "currency": "USD" },
"gasFees": { "cents": 0, "currency": "USD" },
"fxFees": { "cents": 0, "currency": "USD" },
"total": { "cents": 302, "currency": "USD" },
"merchantPaidCreditCardFees": { "cents": 0, "currency": "USD" },
"merchantPaidGasFees": { "cents": 0, "currency": "USD" },
"merchantPaidFxFees": { "cents": 0, "currency": "USD" }
},
"customer": "69fa33f366fad13675d6c894",
"merchant": "68dae987ec61c5361b320e50",
"webhookInfo": {
"item": "sword",
"email": ""
},
"chargebackProtectionDecision": "Not Enabled",
"cryptoInfo": {
"status": "REFUNDED",
"sessionId": "0f495375-1796-4d12-a3a2-07cc6563a23d",
"actualPaymentAmountUSD": "4.01196",
"blockchainLogoUrl": "https://static.logo.xyz/logos/solana-7228edcd.png",
"originatingTransactionHash": "5xWjaoesF1SyRaz4vgX2jKmz6whsxWBAWqeKuvj46dPn9idbYTi7PvGzTa2iMUpY4poyK7G4CS6FSr1Zm1e1234",
"originatingWallet": "9rpv2W6qyShwcwTgZXpiFuC5kFGYpzhYugmpKK5L1234",
"paymentCurrency": "solana:103/token:111111111111111111111111111",
"sponsoredTransactionHash": "4JpcdKKjMi1MzM5fayUwncyfaN8CKEJgPzdGmoU2SF8UfKyn4N75HmhoxRmia8WZn7W5dk91T6ttcy9MM73TPKS9",
"tier": "stables",
"token": "USDC",
"tokenAmount": "4.01196",
"tokenLogoUrl": "https://static.logo.com/logos/usdc-8aaf5df7.png",
"refundAddress": "91eEM5xHu8tY82tiNkjzJNXGLM7NhyCEVaRhvs9p1234",
"refundTransactionHash": "2cboz1RP9H9nY5yhcSz3xCBfZhMXrkfWf4rZbKnmGAK42xWyuP6R66iTDc2AwFPSonKDGqycR9ZtpGnmy4qQ1234"
},
"settlementType": "USDC",
"blockchain": "solana",
"createdAt": "2026-05-05T18:16:19.342Z",
"updatedAt": "2026-05-05T18:19:44.138Z",
"refundInfo": {
"amount": { "cents": 200, "currency": "USD" },
"refundedAt": "2026-05-05T18:19:30.297Z",
"count": 1
}
}

Step 4: Share Refund Receipt

Provide the cryptoInfo.refundTransactionHash to your customer.

  • This is the on-chain transaction hash
  • It can be used to verify the refund on a blockchain explorer
  • Acts as a proof of refund


Refund minimums

Crypto refunds have a minimum amount that depends on the asset and network being refunded — it’s driven mainly by the on-chain network fee to deliver the refund, so higher-fee networks carry a higher floor. A refund below the minimum for its asset is rejected. Reach out to your Coinflow integrations contact for the exact minimum that applies to the assets you support.

Partial refunds and stranded remainders

When you issue a partial refund, the amount left un-refunded (the remainder) must be either zero or at or above the refund minimum. If a partial refund would leave a remainder below the minimum, that remainder could never be refunded afterward — it would fail the same minimum — so the request is rejected:

{
"msg": "Refunding this amount leaves $0.50 unrefundable — below the $1.00 minimum, so it can never be refunded later. Refund the full remaining $2.00, or accept this.",
"code": "refund_strands_remainder"
}
FieldValue
HTTP status409
coderefund_strands_remainder

You have three options:

  1. Refund the full remaining amount instead of a partial amount.
  2. Choose a smaller partial amount that leaves the remainder at or above the minimum.
  3. Accept the stranded remainder — knowingly leave it un-refundable — by resubmitting the same request with allowSubMinimumRemainder: true.

From the merchant dashboard this surfaces as a warning with an “Accept & refund anyway” button that performs option 3.

Refunding below the asset minimum (the refund amount itself, not just the remainder) is always rejected and cannot be overridden — only the sub-minimum remainder case above is acceptable via allowSubMinimumRemainder.


Notes & Best Practices

  • Refunds are irreversible once submitted on-chain
  • Always validate the destinationWallet before initiating
  • Use partial refunds (partialAmount) when issuing refunds for a portion of the original payment
  • Monitor cryptoInfo.status to confirm refund completion (REFUNDED)