Refunds
This guide covers how to handle refunds in ToffeePay, including initiating refunds and handling refund webhooks.
Refund Flow
Create a Refund
Send a server-side request to initiate a refund for a paid session.
POST /pay.v1.RefundService/CreateRefund
Authorization: Bearer <your_api_key>
Content-Type: application/json
{
"payment_id": "pay_xyz789",
"reason": "Customer request"
}
Parameters:
payment_id
: The original payment ID to refundreason
: Optional reason for the refund
Response
{
"id": "ref_xyz789"
}
Refund Statuses
Refunds can have the following statuses:
pending
: Refund created but not yet processedsucceeded
: Refund has been successfully processed and funds returnedfailed
: Refund failed (e.g., insufficient funds, payment method issues)cancelled
: Refund was cancelled before completion
Status Flow:
- Refund starts as
pending
- Refund resolves to either
succeeded
,failed
, orcancelled
Timestamp Fields
Refunds include relevant timestamp fields based on their final status:
created_at
: When the refund was initiatedsucceeded_at
: When the refund completed successfully (if applicable)failed_at
: When the refund failed (if applicable)cancelled_at
: When the refund was cancelled (if applicable)
Webhook Events
ToffeePay sends webhooks for the following refund-related events:
Refund Events
refund.created
: When a refund is created and processing beginsrefund.succeeded
: When a refund has been processed successfullyrefund.failed
: When a refund attempt failsrefund.cancelled
: When a refund is cancelled
See the Webhooks page for implementation details and signature verification.
Handle Refund Notifications
When a refund is processed, ToffeePay sends a signed webhook to your backend.
Sample Payload:
{
"id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"event": "refund.succeeded",
"timestamp": "2023-06-01T12:10:00Z",
"data": {
"refund_id": "ref_xyz789"
}
}
For webhook signature verification, see the Webhooks page.
Check Refund Status
You can check the status of a refund at any time:
POST /pay.v1.RefundService/GetRefund
Authorization: Bearer <your_api_key>
Content-Type: application/json
{
"id": "ref_xyz789"
}
Response:
{
"id": "ref_xyz789",
"payment_id": "pay_xyz789",
"status": "succeeded",
"reason": "Customer request",
"created_at": "2023-06-01T12:00:00Z",
"succeeded_at": "2023-06-01T12:05:00Z"
}
List Refunds
Retrieve historical refunds for audit and analytics:
POST /pay.v1.RefundService/ListRefunds
Authorization: Bearer <your_api_key>
Content-Type: application/json
{
"payment_id": "pay_xyz789",
"status": "succeeded",
"limit": 50,
"offset": 0
}
Parameters:
payment_id
(optional): Filter refunds for a specific paymentstatus
(optional): Filter by refund status (pending
,succeeded
,failed
,cancelled
)limit
(optional): Number of refunds to return (default 50)offset
(optional): Number of refunds to skip for pagination
Response:
{
"refunds": [
{
"id": "ref_xyz789",
"payment_id": "pay_xyz789",
"status": "succeeded",
"reason": "Customer request",
"created_at": "2023-06-01T12:00:00Z",
"succeeded_at": "2023-06-01T12:05:00Z"
}
],
"total": 1,
"has_more": false
}
Best Practices
- Idempotency: Use Idempotency-Key header to prevent duplicate refunds
- Revoke Items: When processing a refund webhook, ensure you revoke the purchased items from the user's account
- User Communication: Keep users informed about refund status through your game's UI
- Audit Trail: Log all refund requests and completions for auditing purposes
- Timeout Handling: Refunds may take several business days depending on the payment method
Error Handling
Common refund errors:
payment_not_found
: The payment doesn't existalready_refunded
: Payment has already been refundedrefund_not_allowed
: Payment method doesn't support refundsinsufficient_funds
: Merchant account has insufficient funds for the refund