Overview
Refund APIs enable you to return cryptocurrency payments to customers. Whether handling order cancellations, customer dissatisfaction, or error corrections, refunds ensure smooth financial operations and maintain customer trust.
What is a Refund?
A refund returns cryptocurrency funds from a completed payment back to the customer's wallet. Each refund:
- 💰 Returns funds in the original payment cryptocurrency (USDC/USDT)
- 🔗 Links to the original Payment Intent and Charge
- 📊 Tracks status through the refund lifecycle
- 🏷️ Supports metadata for order tracking
- 🔄 Processes through blockchain transactions
🔗 Related APIs: Refunds are created against successful Payment Intents. You must have a completed payment before issuing a refund.
Key Features
| Feature | Description |
|---|---|
| Partial Refunds | Issue partial refunds for any amount up to the full payment |
| Full Refunds | Return the entire payment amount to the customer |
| Automatic Currency | Refunds use the original transaction's cryptocurrency |
| Blockchain Tracking | Each refund links to blockchain transactions |
| Status Tracking | Monitor refund progress through lifecycle statuses |
| Metadata Support | Attach custom data for internal tracking |
| Network Fees | System handles blockchain network fees automatically |
How Refunds Work
Create Refund → Processing → Blockchain Transaction → Customer Receives Funds
↓ ↓ ↓ ↓
Pending → Processing → Blockchain → Succeeded
Refund Lifecycle
1. Refund Creation
- Merchant initiates refund via API
- Specifies amount (partial or full)
- Provides reason and metadata
- Status:
Pending
2. Processing
- System validates refund request
- Creates blockchain transaction
- Calculates network fees
- Status:
Processing
3. Blockchain Execution
- Transaction submitted to blockchain
- Funds transferred from merchant wallet to customer
- Transaction hash recorded
- Status:
Processing→Succeeded
4. Completion
- Customer receives funds in their wallet
- Refund marked as complete
- Webhook
refund.succeededsent - Status:
Success
Refund Status
| Status | Meaning | Next Steps |
|---|---|---|
Pending | Refund created, awaiting processing | Wait for processing to begin |
Processing | Blockchain transaction in progress | Monitor transaction status |
Success | Funds successfully returned to customer | No action needed |
Failed | Refund failed to process | Check failure_reason, retry if needed |
Refund Reasons
Specify a reason when creating refunds for better tracking:
| Reason | When to Use |
|---|---|
requested_by_customer | Customer requested a refund |
duplicate | Duplicate payment processed |
fraudulent | Suspected or confirmed fraud |
order_cancelled | Order was cancelled |
product_not_received | Customer didn't receive product |
product_defective | Product was defective or damaged |
other | Other reason not listed above |
Best Practices
✅ DO:
- Issue refunds promptly to maintain customer satisfaction
- Include clear metadata for internal tracking
- Specify accurate refund reasons for analytics
- Monitor refund status via webhooks
- Keep customers informed about refund progress
❌ DON'T:
- Issue refunds for more than the original payment amount
- Create duplicate refunds for the same payment
- Skip including refund reasons (important for records)
- Ignore failed refunds (investigate and resolve)
Important Notes
- Partial or Full Refunds: Merchants can issue either partial or full refunds depending on the situation.
- Refund Status: Refunds go through different statuses (e.g., pending, succeeded, failed) as they are processed.
- Associated Transactions: Each refund is linked to one or more blockchain transactions that facilitate the return of funds.
- Currency and Amount: Refunds are processed in the original transaction's currency and can be for any amount up to the full payment.
- Metadata: Additional information can be attached to refunds for internal tracking and reconciliation purposes.
- Network Fees: The merchant pays blockchain network fees for refund transactions.
- Processing Time: Refund processing time depends on blockchain network confirmation times.
Understanding and effectively managing refunds is crucial for maintaining customer satisfaction and ensuring smooth financial operations in your MartianPay integration.
API Reference
1. Create Refund
For detailed API reference, see: Create Refund API
Request:
curl --location --request POST 'https://api.martianpay.com/v1/refunds' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}' \
--data-raw '{
"amount": "2",
"metadata": {
"order_id": "12345"
},
"payment_intent_id": "pi_vfk7mLTHPs7Cf0UbU38tGiKq",
"reason": "requested_by_customer"
}'
Response:
{
"error_code": "success",
"msg": "success",
"data": {
"refunds": [
{
"id": "rf_fUlic9qfON8m9yinN4kw6Bqh",
"object": "refund",
"amount": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "2"
},
"network_fee": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "0.01"
},
"net_amount": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "2"
},
"created": 1748784087,
"description": "",
"transactions": [],
"failure_reason": "",
"metadata": {
"order_id": "12345"
},
"charge": "ch_c9tR5aQRdtmkKwCQDji9Z6ST",
"payment_intent": "pi_vfk7mLTHPs7Cf0UbU38tGiKq",
"refund_address": "0x36279Ac046498bF0cb742622cCe22F3cE3c2AfD9",
"reason": "requested_by_customer",
"status": "Pending"
}
]
}
}
Understanding the Response
How to Check if the Request Was Successful:
| Field | Value | Meaning |
|---|---|---|
error_code | "success" | ✅ Refund created successfully |
error_code | Other value | ❌ Error occurred (e.g., "insufficient_funds", "invalid_payment_intent") |
Key Fields:
| Field | Description |
|---|---|
id | Unique refund identifier (use to track refund status) |
status | Current refund status: Pending, Processing, Success, Failed |
amount | Refund amount in original cryptocurrency |
net_amount | Amount customer receives (amount - network_fee) |
network_fee | Blockchain transaction fee (paid by merchant) |
payment_intent | Original payment intent ID being refunded |
refund_address | Customer's wallet address receiving the refund |
reason | Refund reason for tracking and analytics |
Next Steps:
- Save the refund
idfor tracking and customer service - Monitor refund status via Get Refund API
- Subscribe to webhook events to receive real-time updates
- Inform customer that refund is being processed
Webhook Events for Refunds
MartianPay sends webhook notifications for refund events, enabling you to track refund processing in real-time and keep customers informed.
Refund Events
| Event Type | Description | Triggered When |
|---|---|---|
refund.created | Refund request created | A new refund is initially created and recorded |
refund.updated | Refund status updated | Status or details change (may occur multiple times during processing) |
refund.succeeded | Refund completed successfully | Funds successfully returned to customer's wallet |
refund.failed | Refund processing failed | Processing failed due to network issues, invalid address, or insufficient funds |
Webhook Payload Example
Here's an example of a refund webhook event payload:
{
"id": "evt_056qw9fr9PndljykFUqSIf6t",
"object": "event",
"api_version": "2025-01-22",
"created": 1748580845,
"data": {
"object": {
"id": "refund_4HbjPuW72pLq5f8yXPpTn7fv",
"object": "refund",
"amount": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "5"
},
"created": 1748580000,
"updated": 1748580845,
"charge": "ch_c9tR5aQRdtmkKwCQDji9Z6ST",
"payment_intent": "pi_vfk7mLTHPs7Cf0UbU38tGiKq",
"reason": "customer_request",
"status": "Success",
"metadata": {
"order_id": "order_123",
"reason_detail": "Customer changed mind"
},
"transactions": [
{
"tx_hash": "0x85b9b9b2fcd8b149a79efa6eee0d94f0d81c6d3172d1335fed897a374e81210c",
"status": "confirmed",
"amount": "5000000"
}
]
},
"previous_attributes": {
"status": "Processing",
"updated": 1748580500
}
},
"livemode": false,
"pending_webhooks": 0,
"type": "refund.succeeded"
}
Webhook Event Flow
Typical Refund Event Sequence:
refund.created- Refund request submitted (status:Pending)refund.updated- Status changes (may occur multiple times as status progresses:Processing,In Transit, etc.)refund.succeeded- Funds returned successfully (status:Success)- OR
refund.failed- Processing failed (status:Failed)
- OR
Status Transitions:
created → updated* → succeeded (SUCCESS)
↘ failed (FAILURE)
* refund.updated may fire multiple times as status changes
Best Practices
-
Monitor refund.succeeded events - Confirm successful refunds and:
- Update order status to "refunded"
- Send refund confirmation emails to customers
- Update accounting records
- Close support tickets related to refund requests
-
Alert on refund.failed events - Take immediate action when refunds fail:
- Notify customer service team
- Investigate failure reason (
failure_reasonfield) - Retry refund if issue is transient
- Provide alternative refund methods if blockchain issues persist
-
Track refund progress - Use
refund.updatedevents to:- Show real-time status to customers ("Your refund is being processed...")
- Track time from request to completion
- Identify slow refunds for investigation
- Update support dashboards
-
Handle idempotency - Store the event ID (
evt_*) to prevent duplicate processing -
Reconcile with payment intent - Use the
payment_intentandchargefields to:- Match refunds to original payments
- Update payment records in your system
- Track refund amounts against original payment amounts
-
Extract transaction details - When
refund.succeededis received:- Store transaction hashes from
transactions[]array - Provide blockchain explorer links to customers for verification
- Keep records for accounting and dispute resolution
- Store transaction hashes from
-
Customer communication - Use webhook events to trigger automated messages:
refund.created: "Your refund request has been received"refund.updated(Processing): "Your refund is being processed"refund.succeeded: "Your refund of $X has been completed"refund.failed: "There was an issue with your refund, our team is investigating"
For more details on webhook setup, signature verification, and complete event schemas, see the Webhook Events documentation.
2. Retrieve a Single Refund
For detailed API reference, see: Get Refund API
Request:
curl --location --request GET 'https://api.martianpay.com/v1/refunds/\{id\}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}'
Response:
{
"error_code": "success",
"msg": "success",
"data": {
"id": "rf_fUlic9qfON8m9yinN4kw6Bqh",
"object": "refund",
"amount": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "2"
},
"network_fee": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "0.01"
},
"net_amount": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "2"
},
"created": 1748784087,
"description": "",
"transactions": [
{
"tx_id": "a80fa424-ffcb-45cb-a3a5-7391662272f7",
"source_address": "0x89Dbc88f04dc48572a2C7AfD609cdA144fc794D8",
"destination_address": "0x36279Ac046498bF0cb742622cCe22F3cE3c2AfD9",
"tx_hash": "0x59de6b933c6ded9ef95e80238951454b3e53c51cec463ac8ded4b07d9a19dc0e",
"amount": "2000000",
"decimals": 0,
"asset_id": "",
"token": "USDC",
"network": "Ethereum Sepolia",
"type": "refund",
"created_at": 1748784101,
"status": "confirmed",
"aml_status": "approved",
"aml_info": "",
"charge_id": "ch_c9tR5aQRdtmkKwCQDji9Z6ST",
"refund_id": "rf_fUlic9qfON8m9yinN4kw6Bqh",
"fee_info": "network_fee:\"0.000045059020501845\"",
"fee_currency": "ETH_TEST5"
}
],
"failure_reason": "",
"metadata": {
"order_id": "12345"
},
"charge": "ch_c9tR5aQRdtmkKwCQDji9Z6ST",
"payment_intent": "pi_vfk7mLTHPs7Cf0UbU38tGiKq",
"refund_address": "0x36279Ac046498bF0cb742622cCe22F3cE3c2AfD9",
"reason": "requested_by_customer",
"status": "Success"
}
}
Understanding the Response
How to Check if the Request Was Successful:
| Field | Value | Meaning |
|---|---|---|
error_code | "success" | ✅ Refund retrieved successfully |
error_code | "refund_not_found" | ❌ No refund exists with this ID |
error_code | Other value | ❌ Other error occurred |
Key Status Fields:
| Status | Meaning | Next Steps |
|---|---|---|
Pending | ⏳ Refund created, awaiting processing | Wait for processing to begin |
Processing | 🔄 Blockchain transaction in progress | Monitor transaction via webhook events |
Success | ✅ Complete - Funds returned to customer | No action needed, customer has received funds |
Failed | ❌ Failed - Refund could not be processed | Check failure_reason field, retry if needed |
Transaction Details:
The transactions array contains blockchain transaction information:
- tx_hash: Blockchain transaction hash for verification
- source_address: Your merchant wallet address
- destination_address: Customer's refund wallet address
- amount: Amount transferred (in smallest token unit)
- status: Transaction confirmation status (confirmed, pending, failed)
- network: Blockchain network used (e.g., "Ethereum Sepolia")
- fee_info: Network fee details for the transaction
Use Cases:
- Verify refund completion and share tx_hash with customer
- Check transaction status on blockchain explorer
- Track network fees for accounting purposes
- Investigate failed refunds using
failure_reason
Next Steps:
- If status is
Success, inform customer funds have been returned - If status is
Failed, checkfailure_reasonand determine if retry is needed - Share blockchain transaction hash (tx_hash) with customer for transparency
3. List Refunds
Fetches a list of refunds, with options for filtering and pagination. The payment_intent parameter is optional and can be used to filter refunds by a specific payment intent.
For detailed API reference, see: List Refunds API
Request:
curl --location --request GET 'https://api.martianpay.com/v1/refunds?page=0&page_size=10&payment_intent=pi_vfk7mLTHPs7Cf0UbU38tGiKq' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}'
Query Parameters:
page(required): Page number, starting from 0page_size(required): Items per pagepayment_intent(optional): Filter by payment intent ID
Response:
{
"error_code": "success",
"msg": "success",
"data": {
"refunds": [
{
"id": "rf_fUlic9qfON8m9yinN4kw6Bqh",
"object": "refund",
"amount": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "2"
},
"network_fee": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "0.01"
},
"net_amount": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "2"
},
"created": 1748784087,
"description": "",
"transactions": [
{
"tx_id": "a80fa424-ffcb-45cb-a3a5-7391662272f7",
"source_address": "0x89Dbc88f04dc48572a2C7AfD609cdA144fc794D8",
"destination_address": "0x36279Ac046498bF0cb742622cCe22F3cE3c2AfD9",
"tx_hash": "0x59de6b933c6ded9ef95e80238951454b3e53c51cec463ac8ded4b07d9a19dc0e",
"amount": "2000000",
"decimals": 0,
"asset_id": "",
"token": "USDC",
"network": "Ethereum Sepolia",
"type": "refund",
"created_at": 1748784101,
"status": "confirmed",
"aml_status": "approved",
"aml_info": "",
"charge_id": "ch_c9tR5aQRdtmkKwCQDji9Z6ST",
"refund_id": "rf_fUlic9qfON8m9yinN4kw6Bqh",
"fee_info": "network_fee:\"0.000045059020501845\"",
"fee_currency": "ETH_TEST5"
}
],
"failure_reason": "",
"metadata": {
"order_id": "12345"
},
"charge": "ch_c9tR5aQRdtmkKwCQDji9Z6ST",
"payment_intent": "pi_vfk7mLTHPs7Cf0UbU38tGiKq",
"refund_address": "0x36279Ac046498bF0cb742622cCe22F3cE3c2AfD9",
"reason": "requested_by_customer",
"status": "Success"
}
],
"total": 1,
"page": 0,
"page_size": 10
}
}
Understanding the Response
How to Check if the Request Was Successful:
| Field | Value | Meaning |
|---|---|---|
error_code | "success" | ✅ Refunds retrieved successfully |
error_code | Other value | ❌ Error occurred |
Key Fields:
- total: Total number of refunds matching your query
- page: Current page number (0-based)
- page_size: Number of items per page
- refunds: Array of refund objects
Pagination:
- Use
pageandpage_sizeto navigate through results - If
total>page_size, there are more pages available - Next page:
page=1, previous page:page=0
Filtering:
- Use
payment_intentparameter to find refunds for a specific payment - Empty
refundsarray means no matches found
Refund Status Summary:
| Status | Meaning |
|---|---|
Pending | ⏳ Refund created, awaiting processing |
Processing | 🔄 Blockchain transaction in progress |
Success | ✅ Funds returned to customer |
Failed | ❌ Refund failed, check failure_reason |
Next Steps:
- Loop through
refundsarray to process each refund - Use refund
idto fetch more details if needed - Monitor
statusfield to track refund progress - Filter by
payment_intentto reconcile specific orders - Adjust
pageandpage_sizefor pagination