Skip to main content
Version: 2.0

GLODIPAY REFUND API SPECIFICATION

Ask AI

VERSION 2.0.0

Table of Contents​

Introduction​

This document describes the GLODIPAY Refund API v2, which allows merchants to initiate refunds for completed transactions, query refund status, and receive refund notifications via IPN.

Key points:

  • Refunds can be full or partial (subject to per-provider constraints -- some providers only support full-amount refunds).
  • Refunds may be auto-processed immediately or held for manual approval, depending on merchant configuration.
  • The notificationUrl from the original transaction is used for refund IPN notifications.
  • The maximum refundable amount per refund is: paidAmount minus the total already refunded for the transaction.

Endpoints​

TestGet it from the API Keys page of the Sandbox Merchant Dashboard
ProductionGet it from the API Keys page of the Merchant Dashboard

Signature​

All requests and responses are signed using RSA with MD5 to ensure integrity and authenticity.

Generating a Signature (Merchant -> GLODIPAY)​

Sign request payloads with your RSA Private Key (obtained from the Merchant Dashboard).

Steps:

  • Collect all request parameters except signature as a flat key-value object.
  • Sort the keys in natural ascending order (SORT_NATURAL / localeCompare with numeric: true).
  • Trim whitespace from all string values (recursive).
  • Serialize to JSON string with all non-ASCII Unicode characters escaped to \uXXXX (RFC 8259).
  • Sign with md5WithRSAEncryption using your RSA Private Key.
  • Base64-encode the binary output.

Verifying a Signature (GLODIPAY -> Merchant)​

Verify GLODIPAY responses and webhooks with the RSA Public Key (available in the Portal).

Steps:

  • Separate signature from the payload.
  • Sort remaining keys in natural ascending order.
  • Trim all string values (recursive).
  • Serialize to JSON string with all non-ASCII Unicode characters escaped to \uXXXX (RFC 8259).
  • Verify with md5WithRSAEncryption using your RSA Public Key.
  • Return value 1 = valid.

Note (Node.js): Convert all numeric values to strings before sorting/serializing. Escape forward slashes in the JSON string: .replace(///g, '/').

Note (Unicode / RFC 8259): The JSON payload must escape all non-ASCII Unicode characters (e.g., "a with accent" must become "\u00e1") before signing -- this is required by RFC 8259. In PHP, json_encode($data) does this by default -- do not use JSON_UNESCAPED_UNICODE. In Node.js, JSON.stringify() does not escape Unicode by default -- apply: .replace(/[^\\x00-\\x7F]/g, c => "\\u" + c.charCodeAt(0).toString(16).padStart(4, "0")) after serializing.

REFUND API​

Initiate a refund for a completed transaction.

Endpoint: POST /v2/refund Method: POST Content-Type: application/json

Request​

transactionIdString (ULID)MThe transactionId received from the checkout IPN or transaction query
amountFloatMRefund amount. Minimum: 0.10. Maximum: remaining refundable amount (paidAmount minus already refunded)
reasonString(max:1000)OShort description of the refund reason
signatureString(max:750)MRSA-MD5 signature

Note: Certain providers (e.g. PayAgency, SmartPay, ClisaPay, FinvyPay, WPay) only support full-amount refunds. The system enforces the minimum refund amount accordingly.

Response​

Content-Type: application/json

statusStringsuccess
messageStringHuman-readable message
dataJSONRefund details. See data object below

data object:

refundIdString (ULID)GLODIPAY refund ID
refundNumberStringGLODIPAY human-readable refund number

Refund created and processed immediately (auto-refund enabled):

{
"status": "success",
"message": null,
"data": {
"refundId": "01jzabk09xc4pbgwe8hyg4cwbf",
"refundNumber": "2507-1751420414"
}
}

Refund created and pending manual approval:

{
"status": "success",
"message": "Refund created and waiting for approval.",
"data": {
"refundId": "01jzabk09xc4pbgwe8hyg4cwbf",
"refundNumber": "2507-1751420414"
}
}

Validation error (HTTP 422):

{
"status": "error",
"message": "Invalid request data.",
"errors": [
{
"field": "amount",
"message": ["The amount must be between 0.1 and 100."]
}
]
}

REFUND QUERY​

Query the latest status of a refund.

Endpoint: POST /v2/refund/query Method: POST Content-Type: application/json

Request​

refundIdString (ULID)MGLODIPAY refund ID (from Refund API response or Refund IPN)
signatureString(max:750)MRSA-MD5 signature

Response​

Content-Type: application/json

statusStringsuccess
messageStringHuman-readable message
dataJSONRefund details object. See fields below

data object fields:

transactionIdStringGLODIPAY original transaction ID
refStringMerchant's orderRef
refundIdStringGLODIPAY refund ID
currencyStringISO 4217 currency code
refundAmountFloatRefund amount
statusStringRefund status. See Refund Status Values
statusCodeNumberNumeric status code. See Status Codes
metadataJSONKey-value pairs from the original checkout session
reasonStringRefund reason
messageStringHuman-readable status message
originalRefundCreatedAtISO 8601 datetimeRefund creation time at the PSP
refundCreatedAtISO 8601 datetimeRefund creation time in GLODIPAY system
transactionCreatedAtISO 8601 datetimeOriginal transaction creation time
signatureStringRSA-MD5 signature -- verify with GLODIPAY public key

Example:

{
"status": "success",
"message": "",
"data": {
"transactionId": "01jza90dy6w82dfrrqvadn5vs4",
"ref": "ORDER-001",
"refundId": "01jzabk09xc4pbgwe8hyg4cwbf",
"currency": "USD",
"refundAmount": 50.00,
"status": "refund_successful",
"statusCode": 11,
"metadata": { "orderId": "12345" },
"reason": "Customer request",
"message": null,
"originalRefundCreatedAt": "2026-04-14T11:00:00+00:00",
"refundCreatedAt": "2026-04-14T11:00:01+00:00",
"transactionCreatedAt": "2026-04-14T10:00:00+00:00",
"signature": "base64-encoded-rsa-signature"
}
}

REFUND NOTIFICATION​

GLODIPAY sends an HTTP POST to the notificationUrl of the original transaction when a refund status changes.

Method: POST Content-Type: application/json

Retry policy: If your server does not return {"returnCode":"100"} within 30 seconds, GLODIPAY will retry delivery.

Payload​

transactionIdStringMGLODIPAY original transaction ID
refStringMMerchant's orderRef
refundIdStringMGLODIPAY refund ID
currencyStringMISO 4217 currency code
refundAmountFloatMRefund amount
statusStringMRefund status. See Refund Status Values
statusCodeNumberMNumeric status code. See Status Codes
metadataJSONOKey-value pairs from the original checkout session
reasonStringORefund reason
messageStringOHuman-readable status message
originalRefundCreatedAtISO 8601 datetimeMRefund creation time at the PSP
refundCreatedAtISO 8601 datetimeMRefund creation time in GLODIPAY system
transactionCreatedAtISO 8601 datetimeMOriginal transaction creation time
signatureStringMRSA-MD5 signature -- verify with GLODIPAY public key

Example IPN Payload:

{
"transactionId": "01jza90dy6w82dfrrqvadn5vs4",
"ref": "ORDER-001",
"refundId": "01jzabk09xc4pbgwe8hyg4cwbf",
"currency": "USD",
"refundAmount": 50.00,
"status": "refund_successful",
"statusCode": 11,
"metadata": { "orderId": "12345" },
"reason": "Customer request",
"message": null,
"originalRefundCreatedAt": "2026-04-14T11:00:00+00:00",
"refundCreatedAt": "2026-04-14T11:00:01+00:00",
"transactionCreatedAt": "2026-04-14T10:00:00+00:00",
"signature": "base64-encoded-rsa-signature"
}

Response (Merchant -> GLODIPAY)​

Your server must respond within 30 seconds:

{
"returnCode": "100",
"description": "Received"
}
returnCodeStringRMust be "100" to acknowledge receipt
descriptionString(1,1500)OOptional description

Appendix​

Refund Status Values​

String values returned in the status field of refund IPN payloads and refund query responses.

refund_initiatedRefund request initiated
refund_under_reviewRefund under review
refund_successfulRefund completed successfully
refund_failedRefund failed
refund_partially_successfulPartial refund completed
refund_partially_failedPartial refund failed
void_initiatedVoid initiated
void_under_reviewVoid under review
void_successfulVoid completed successfully
void_failedVoid failed
void_partially_successfulPartial void completed
void_partially_failedPartial void failed

Status Codes​

Numeric code in the statusCode field of refund IPN payloads and refund query responses.

8refund_initiatedRefund initiated
9refund_failedRefund failed
10refund_under_reviewRefund under review
11refund_successfulRefund successful
12refund_partially_failedPartial refund failed
13refund_partially_successfulPartial refund successful
18void_initiatedVoid initiated
19void_under_reviewVoid under review
20void_successfulVoid successful
21void_failedVoid failed
22void_partially_successfulPartial void successful
23void_partially_failedPartial void failed

Code Examples​

PHP​

<?php

function generateSignature(array $data): string
{
$privateKey = openssl_pkey_get_private("-----BEGIN PRIVATE KEY-----
YOUR_PRIVATE_KEY_HERE
-----END PRIVATE KEY-----
");

foreach ($data as $k => $v) {
if (is_array($v)) $data[$k] = json_encode($v);
}
ksort($data, SORT_NATURAL);
array_walk_recursive(
$data,
static function (&$field) {
$field = trim($field);
}
);

openssl_sign(json_encode($data), $signature, $privateKey, 'md5WithRSAEncryption');

return base64_encode($signature);
}

function verifySignature(array $data): bool
{
$publicKey = openssl_pkey_get_public("-----BEGIN PUBLIC KEY-----
YOUR_GLODIPAY_PUBLIC_KEY_HERE
-----END PUBLIC KEY-----
");

$dataWithoutSignature = array_filter($data, static function ($key) {
return $key !== 'signature';
}, ARRAY_FILTER_USE_KEY);

$signature = $data['signature'];

ksort($dataWithoutSignature, SORT_NATURAL);
array_walk_recursive(
$dataWithoutSignature,
static function (&$field) {
$field = trim($field);
}
);

$result = openssl_verify(
json_encode($dataWithoutSignature),
base64_decode($signature),
$publicKey,
'md5WithRSAEncryption'
);

return $result === 1;
}

// Example: create a refund
$payload = [
'transactionId' => '01jza90dy6w82dfrrqvadn5vs4',
'amount' => '50.00',
'reason' => 'Customer request',
];

$payload['signature'] = generateSignature($payload);

$ch = curl_init('https://payment-sandbox.gpayprocessing.com/v2/refund');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response, true);
echo 'Refund ID: ' . $result['data']['refundId'];

Node.js​

// Save as script.mjs and run: node script.mjs
import { createSign } from 'crypto';
import https from 'https';

const PRIVATE_KEY = `-----BEGIN PRIVATE KEY-----
YOUR_PRIVATE_KEY_HERE
-----END PRIVATE KEY-----`;

function phpCast(v) {
if (typeof v === 'number') return String(v);
if (typeof v === 'boolean') return v ? '1' : '';
if (typeof v === 'string') return v.trim();
if (Array.isArray(v)) return v.map(phpCast);
if (v && typeof v === 'object') return Object.fromEntries(Object.entries(v).map(([k, val]) => [k, phpCast(val)]));
return v;
}

function generateSignature(data) {
const sorted = {};
Object.keys(data)
.filter(k => k !== 'signature')
.sort((a, b) => a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' }))
.forEach(k => { sorted[k] = data[k]; });

const canonical = JSON.stringify(phpCast(sorted))
.replace(/\//g, '\\/')
.replace(/[\u0080-\uffff]/g, c => '\\u' + c.charCodeAt(0).toString(16).padStart(4, '0'));

const sign = createSign('md5WithRSAEncryption');
sign.update(canonical);
return sign.sign(PRIVATE_KEY, 'base64');
}

// Example: create a refund
const payload = {
transactionId: '01jza90dy6w82dfrrqvadn5vs4',
amount: '50.00',
reason: 'Customer request',
};

payload.signature = generateSignature(payload);

const postData = JSON.stringify(payload);
const options = {
hostname: 'payment-sandbox.gpayprocessing.com',
path: '/v2/refund',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(postData),
},
};

const req = https.request(options, (res) => {
let body = '';
res.on('data', chunk => body += chunk);
res.on('end', () => {
const result = JSON.parse(body);
console.log('Refund ID:', result.data.refundId);
});
});

req.on('error', console.error);
req.write(postData);
req.end();

Bookmarks

No bookmarks yet.
Hover over a heading and click to save a section.