Refund a sale
- Sandbox:
POST https://sandbox.payme.io/api/refund-sale - Production:
POST https://live.payme.io/api/refund-sale
Credentials
- Request body field
payme_client_key(optional): Partners: your partner key. You can send it in thePayMe-Partner-Keyheader instead. A seller leaves it out. - Request body field
seller_payme_id(optional): The seller's MPL. Required unless you sendseller_idwithpayme_client_key. You can send it in thePayMe-Merchant-Keyheader instead.
Overview
Refunds a paid sale, in full or in part. The money goes back to the payment method the buyer paid with. On an authorized sale the same call voids the hold instead; see Void an authorization.
Credentials
Name the seller: send seller_payme_id, or seller_id with your partner key (payme_client_key). A seller needs no partner key. A partner key on its own fails with error 1. The sale must belong to the seller you name; otherwise the call returns HTTP 403, error 303.
Rules
- The sale's status must be
completedorpartial-refund. Any other status, includingchargeback, returns error305with the current status instatus_additional_info. While a chargeback is open the dispute returns the money, so no refund is possible. - Leave out
sale_refund_amountto refund everything that is left. Never send0. - You can refund several times, up to the sale price. Each partial refund, including the last one, must be at least the account's minimum sale value (500 by default).
- After a partial refund,
sale_statusstayspartial-refundeven when nothing is left. Readsale_refund_bufferfor the amount that remains. - If the seller's balance doesn't cover the refund, PayMe takes the shortfall from the seller's card on file and returns
refunded_from_creditcard: true. - A refund can't be undone.
- Sales paid by POS, eCheck or a Verifone prepaid card aren't refunded with this call (error
4). - Send
verify_only: trueto check a refund without making it.
Callback
When the refund succeeds, PayMe sends a refund callback (notify_type: refund) to the sale's sale_callback_url and to your partner's sale callback URL. A void sends the same callback. See Callbacks.
With the invoicing service active on the account, a refund issues a credit document; the original document is not cancelled.
Language of errors
Errors about the refund itself (wrong status, amount, nothing left to refund) come back in the language the sale was created with, whatever you send in language.
Parameters
This endpoint takes no path, query or header parameters.
Request body
Content type: application/json
| Field | Type | Required | Description |
|---|---|---|---|
payme_client_key |
string | no | Partners: your partner key. You can send it in the PayMe-Partner-Key header instead. A seller leaves it out. |
seller_payme_id |
string | no | The seller's MPL. Required unless you send seller_id with payme_client_key. You can send it in the PayMe-Merchant-Key header instead. |
seller_id |
string | no | Partners: your own ID for the seller (the seller_id you set when you created it). Send it with payme_client_key instead of seller_payme_id. |
payme_sale_id |
string | yes | The sale to refund: PayMe's sale ID, or your own transaction_id for that sale. |
sale_refund_amount |
integer | no | Amount to refund, in minor units (1000 = 10.00). Leave it out to refund everything that is left. Never send 0. A partial amount must be at least the account's minimum sale value (500 by default). |
verify_only |
boolean | no | true to check the call without making it: PayMe validates the request and returns the amounts, but changes nothing and sends no callback. Default false. |
language |
string | no | Language of error messages: en, or he (the default). es, ru and ar are accepted, but messages then come back mostly in English. Any other value returns error 51. |
Request example
{
"seller_payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-1234567",
"payme_sale_id": "SALEDEMO-XXXXXXXX-XXXXXXXX-XXXXXXXX",
"sale_refund_amount": 1000,
"language": "en"
}
Code samples
curl
curl -X POST 'https://sandbox.payme.io/api/refund-sale' \
-H 'Content-Type: application/json' \
--data-raw '{
"seller_payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-1234567",
"payme_sale_id": "SALEDEMO-XXXXXXXX-XXXXXXXX-XXXXXXXX",
"sale_refund_amount": 1000,
"language": "en"
}'
Node.js
const response = await fetch("https://sandbox.payme.io/api/refund-sale", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
"seller_payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-1234567",
"payme_sale_id": "SALEDEMO-XXXXXXXX-XXXXXXXX-XXXXXXXX",
"sale_refund_amount": 1000,
"language": "en"
}),
});
console.log(await response.text());
Python
import requests
response = requests.post(
"https://sandbox.payme.io/api/refund-sale",
json={
"seller_payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-1234567",
"payme_sale_id": "SALEDEMO-XXXXXXXX-XXXXXXXX-XXXXXXXX",
"sale_refund_amount": 1000,
"language": "en",
},
)
print(response.text)
PHP
<?php
$body = <<<'BODY'
{
"seller_payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-1234567",
"payme_sale_id": "SALEDEMO-XXXXXXXX-XXXXXXXX-XXXXXXXX",
"sale_refund_amount": 1000,
"language": "en"
}
BODY;
$ch = curl_init('https://sandbox.payme.io/api/refund-sale');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Responses
200 — The refund was made (or, with verify_only, would be accepted). (success)
| Field | Type | Description |
|---|---|---|
status_code |
integer | 0: success. |
status_error_code |
integer | 0 on success. |
payme_status |
string | success. |
refunded_from_creditcard |
boolean | true when the seller's balance didn't cover the refund and PayMe took the shortfall from the seller's card on file. |
sale_invoice_number |
string | Number of the credit document, when the invoicing service is active on the account; otherwise null. |
sale_invoice_url |
string | Link to the credit document, when the invoicing service is active on the account; otherwise null. |
sale_refund_buffer |
integer | Amount still left to refund, in minor units. |
sale_status |
string | The sale's status after the refund: refunded, or partial-refund after a partial refund. |
payme_transaction_total |
integer | Amount refunded to the buyer, in minor units. |
payme_transaction_total_aft_deduction |
integer | Amount taken from the seller for this refund, after fees, in minor units. |
payme_transaction_id |
string | PayMe's ID of the refund transaction. null when verify_only is true. |
payme_transaction_card_brand |
string | Card brand. null when verify_only is true. |
payme_transaction_auth_number |
string | Authorization number from the card issuer. null when verify_only is true. |
payme_transaction_voucher |
string | Voucher number. null when verify_only is true. |
payme_signature |
string | PayMe's signature for the transaction. null when verify_only is true. |
payme_transaction_acquirer |
string | The acquirer, or null. |
payme_transaction_arn |
string | Acquirer reference (EMV UID) when the acquirer returns one; usually null. Not returned when verify_only is true. |
session |
string | PayMe's ID for this request. Quote it when you contact PayMe. |
{
"status_code": 0,
"status_error_code": 0,
"payme_status": "success",
"refunded_from_creditcard": false,
"sale_invoice_number": null,
"sale_invoice_url": null,
"sale_refund_buffer": 9000,
"sale_status": "partial-refund",
"payme_transaction_total": 1000,
"payme_transaction_total_aft_deduction": 1000,
"payme_transaction_id": "TRANDEMO-XXXXXXXX-XXXXXXXX-XXXXXXXX",
"payme_transaction_card_brand": "Mastercard",
"payme_transaction_auth_number": "72****4",
"payme_transaction_voucher": "24****9",
"payme_signature": "b71e**************ca5a",
"payme_transaction_acquirer": null,
"payme_transaction_arn": null,
"session": "Ab12Cd34"
}
400 — Error 1: no seller named (a partner key on its own). Error 352: sale_refund_amount isn't a positive number. Error 357: this sale type can't be refunded, for example a customer deposit. Error 20: the seller doesn't belong to the partner key sent. (error)
| Field | Type | Description |
|---|---|---|
status_code |
integer | 1: the request failed. |
status_error_code |
integer | PayMe's error code. See Errors. |
status_error_details |
string | The error message, in the language you asked for. |
status_additional_info |
string | Extra detail: usually the field at fault, the sale's current status, or the masked card number. |
session |
string | PayMe's ID for this request. Quote it when you contact PayMe. |
{
"status_code": 1,
"status_error_details": "Required parameter is missing",
"status_additional_info": "seller_payme_id",
"status_error_code": 1,
"session": "Ab12Cd34"
}
403 — Error 303: the sale belongs to another partner or another seller. (error)
| Field | Type | Description |
|---|---|---|
status_code |
integer | 1: the request failed. |
status_error_code |
integer | PayMe's error code. See Errors. |
status_error_details |
string | The error message, in the language you asked for. |
status_additional_info |
string | Extra detail: usually the field at fault, the sale's current status, or the masked card number. |
session |
string | PayMe's ID for this request. Quote it when you contact PayMe. |
{
"status_code": 1,
"status_error_details": "Could not access sale",
"status_additional_info": "",
"status_error_code": 303,
"session": "Ab12Cd34"
}
429 — Error 998: another call for this sale is still running. Wait, then check the sale before retrying. (error)
| Field | Type | Description |
|---|---|---|
status_code |
integer | 1: the request failed. |
status_error_code |
integer | PayMe's error code. See Errors. |
status_error_details |
string | The error message, in the language you asked for. |
status_additional_info |
string | Extra detail: usually the field at fault, the sale's current status, or the masked card number. |
session |
string | PayMe's ID for this request. Quote it when you contact PayMe. |
{
"status_code": 1,
"status_error_details": "Operation is not permitted",
"status_additional_info": "Duplicated action detected, please retry later",
"status_error_code": 998,
"session": "Ab12Cd34"
}
500 — Request errors. 1: a required field is missing. 200: unknown partner key. 251: unknown seller. 350: no sale has this ID. 305: the sale isn't completed or partial-refund (its status is in status_additional_info). 309: already fully refunded. 352: the amount is outside the account's limits. 503: more than what is left to refund. 517: more than the sale price. 4: POS, eCheck or Verifone prepaid sale. (error)
| Field | Type | Description |
|---|---|---|
status_code |
integer | 1: the request failed. |
status_error_code |
integer | PayMe's error code. See Errors. |
status_error_details |
string | The error message, in the language you asked for. |
status_additional_info |
string | Extra detail: usually the field at fault, the sale's current status, or the masked card number. |
session |
string | PayMe's ID for this request. Quote it when you contact PayMe. |
{
"status_code": 1,
"status_error_details": "Cannot perform action due to an incorrect status",
"status_additional_info": "chargeback",
"status_error_code": 305,
"session": "Ab12Cd34"
}