Refund
Refund a captured payment to return funds to the customer.
Refund
Version: 3.0.0
Prerequisite: Refund a transaction that has already been captured (or SALE). You cannot refund a pure authorization — use Void instead. Refund must also be enabled on your MID (allowRefunds).
When and Why to Use Refund
Use Refund to return money to a customer after capture:
- Customer returns a product.
- Overcharge correction.
- Service cancellation after payment.
- Partial refund for a subset of items.
Refund Limits
| Rule | Detail |
|---|---|
| Maximum | Total refunds cannot exceed the original captured amount |
| Minimum | amount.value must be at least 1 in the order currency (same minimum as order amount) |
| Multiple partials | Allowed until the captured balance is fully refunded |
| Order status | After a partial refund, data.status is PARTIALLY_REFUNDED. When the captured amount is fully refunded, data.status is REFUNDED |
| Settlement time | Issuing banks may take 5–10 business days to show the credit on the customer's statement |
Over-refund attempts: If the requested refund plus any prior refunds would exceed the captured amount, the PSP rejects the request. The API typically returns HTTP 200 with { "success": false, "message": "<PSP-specific error>" }. Edge validation errors (transaction not in a refundable state, refund disabled on MID) return HTTP 400 with { "status": "fail", "code": "E0044", "message": "..." }.
Endpoint
Refund, Capture, and Void share the same Manage Payment endpoint — only action changes.
| Method | URL |
|---|---|
| POST | https://api.payorc.com/orders/v1/transaction |
Please use the test credentials for sandbox testing.
Headers
| Field | Type | Required | Description |
|---|---|---|---|
merchant-key | String | Yes | Your merchant API key |
merchant-secret | String | Yes | Your merchant API secret |
Content-Type | String | Yes | Must be application/json |
Request Body
The request body is flat (not nested under data).
| Field | Type | Required | Description |
|---|---|---|---|
action | String | Yes | Must be REFUND |
transaction_id | String | Yes | PayOrc transaction ID to refund |
amount | Object | Yes | Amount object |
amount.currencyCode | String | Yes | ISO currency code (e.g. AED) |
amount.value | Number | Yes | Refund amount (full or partial) |
reason | String | No | Merchant notes |
Code Examples
Example 1 — Full Refund
curl --location 'https://api.payorc.com/orders/v1/transaction' \
--header 'merchant-key: {merchant-key}' \
--header 'merchant-secret: {merchant-secret}' \
--header 'Content-Type: application/json' \
--data '{
"action": "REFUND",
"transaction_id": "1000010118",
"amount": {
"currencyCode": "AED",
"value": 100.00
},
"reason": "Customer returned item - full refund"
}'Example 2 — Partial Refund
curl --location 'https://api.payorc.com/orders/v1/transaction' \
--header 'merchant-key: {merchant-key}' \
--header 'merchant-secret: {merchant-secret}' \
--header 'Content-Type: application/json' \
--data '{
"action": "REFUND",
"transaction_id": "1000010118",
"amount": {
"currencyCode": "AED",
"value": 35.00
},
"reason": "Partial return - 1 of 3 items returned"
}'Partial refund: You can issue multiple partial refunds against one transaction as long as the total does not exceed the captured amount. Order status becomes PARTIALLY_REFUNDED or REFUNDED.
Example 3 — Node.js
const axios = require('axios');
const config = {
method: 'post',
url: 'https://api.payorc.com/orders/v1/transaction',
headers: {
'merchant-key': '{merchant-key}',
'merchant-secret': '{merchant-secret}',
'Content-Type': 'application/json'
},
data: {
action: 'REFUND',
transaction_id: '1000010118',
amount: {
currencyCode: 'AED',
value: 100.00
},
reason: 'Full refund - order cancelled'
}
};
axios(config)
.then(response => console.log(JSON.stringify(response.data)))
.catch(error => console.log(error.response?.data || error));Example 4 — Python
import requests
import json
url = 'https://api.payorc.com/orders/v1/transaction'
payload = {
"action": "REFUND",
"transaction_id": "1000010118",
"amount": {
"currencyCode": "AED",
"value": 25.00
},
"reason": "Partial refund - shipping fee adjustment"
}
headers = {
'merchant-key': '{merchant-key}',
'merchant-secret': '{merchant-secret}',
'Content-Type': 'application/json'
}
response = requests.post(url, headers=headers, json=payload)
print(json.dumps(response.json(), indent=2))Response — Success (200)
{
"data": {
"status": "REFUNDED",
"m_order_id": "ORD-2024-001",
"p_order_id": 1000010228,
"transaction_id": 1000010223,
"amount": "100.00",
"currency": "AED",
"date": "04/08/2023"
},
"message": "Refunded Successfully.",
"status": "success",
"code": "00"
}Capture, Void, and Refund share the same data shape: status, m_order_id, p_order_id, transaction_id, amount, currency, date. For partial refunds, status is PARTIALLY_REFUNDED.
Response — Error
{
"message": "Transaction can't be refunded, already refunded or not in state of to be refunded",
"status": "fail",
"code": "E0044"
}Full vs Partial Refund
| Aspect | Full Refund | Partial Refund |
|---|---|---|
| Amount | 100% of captured amount | Less than captured amount |
| Order status | REFUNDED | PARTIALLY_REFUNDED |
| Further refunds? | No | Yes, up to remaining amount |