Payment Gateway - Partner Integration Guide
Complete documentation for integrating multi-gateway payment solution (Cards + Meal Vouchers)
Quick Start
- Download Postman collection and YAML spec
- Configure your notification webhook URL
- Integrate the Wrapper API (4 endpoints only)
PayGlobe.js - JavaScript Library
1. Include the Library
<!-- Include PayGlobe.js in your HTML --> <script src="https://api.payglobe.it/paymentgw/js/payglobe.js"></script>
2. Initialize with Your API Key
// Initialize PayGlobe with your publishable key
const payglobe = PayGlobe('pk_test_PayGlobe123456789TestPublishable');
3. Create a Payment
// Create a new payment
const payment = await payglobe.payments.create({
amount: 50.00,
email: 'customer@example.com', // Customer email for payment receipt
description: 'Order #12345' // optional
});
console.log('Payment created:', payment.id);
4. Redirect to Checkout
// Option A: Full page redirect
payglobe.redirectToCheckout({
paymentId: payment.id
});
// Option B: Load in iframe
payglobe.redirectToCheckout({
paymentId: payment.id,
element: '#payment-container' // CSS selector for iframe container
});
5. Check Payment Status
// Retrieve payment status
const status = await payglobe.payments.retrieve(payment.id);
console.log('Payment status:', status.status);
console.log('Amount:', status.totalAmount);
console.log('Gateway Payment ID:', status.paymentId);
console.log('Voucher Amount:', status.voucherAmount);
Complete Example
<!DOCTYPE html>
<html>
<head>
<title>PayGlobe Checkout</title>
<script src="https://api.payglobe.it/paymentgw/js/payglobe.js"></script>
</head>
<body>
<button id="checkout-button">Pay 50.00 EUR</button>
<div id="payment-container"></div>
<script>
const payglobe = PayGlobe('pk_test_PayGlobe123456789TestPublishable');
document.getElementById('checkout-button').addEventListener('click', async () => {
try {
// Create payment
const payment = await payglobe.payments.create({
amount: 50.00,
email: 'customer@example.com', // Customer email for receipt
description: 'Order #12345' // Your internal order number
});
// Redirect to checkout (in iframe)
await payglobe.redirectToCheckout({
paymentId: payment.id,
element: '#payment-container'
});
} catch (error) {
console.error('Payment error:', error);
alert('Payment failed: ' + error.message);
}
});
</script>
</body>
</html>
pk_test_ or pk_live_).
Try the Live Example
See the above code in action with a working demo:
This example demonstrates a simple payment flow using PayGlobe.js with a working payment button.High-Level Architecture
Payment Flow (5 Steps)
Your System โ Gateway โ Card Processor
Preauthorize full amount on card (hold funds)
Customer decides:
โข Pay with card only
โข Use meal vouchers (partial/full)
Gateway โ Voucher Provider
VALIDATE โ SPEND โ NOTIFY
Gateway โ Card Processor:
โข No vouchers: Capture full amount
โข Partial vouchers: Capture difference
โข Full vouchers: Void preauth
Gateway โ Your Webhook
JSON with card + voucher details
Payment Flow (3 Steps)
Your System โ Gateway โ Card Processor
Single call: Auth + Capture in one step
Not applicable - no voucher option
Not applicable - card only
Already captured in step 1
Gateway โ Your Webhook
JSON with payment details
| Feature | PREAUTH + CAPTURE | SALE DIRECT |
|---|---|---|
| Steps | 5 steps (complex flow) | 2-3 steps (simple flow) |
| Meal Vouchers | โ Supported | โ Not available |
| Card Operation | Preauth โ Capture/Void | Single Sale (Auth+Capture) |
| User Interaction | Voucher choice page | Direct payment |
| Settlement | After capture | Immediate |
| Payment Mode Config | PREAUTH_VOUCHER_CAPTURE |
SALE_DIRECT |
Integration Flow
Step 1: Initialize Payment
POST/api/payment/init
{
"amount": 150.00,
"notifyUrl": "https://yourserver.com/api/igfs-callback",
"errorUrl": "https://yourserver.com/payment/error",
"callbackUrl": "https://yourserver.com/payment/{transactionId}/voucher-choice"
}
Response:
{
"success": true,
"transactionId": "abc-123-def-456",
"totalAmount": 150.00,
"redirectUrl": "https://paymentpage.igfs.payglobe.com?paymentID=XYZ",
"igfs": {
"paymentId": "XYZ",
"status": "PENDING"
}
}
Step 2: Get Transaction Info
GET/api/payment/transaction/{transactionId}
Response:
{
"success": true,
"transactionId": "abc-123-def-456",
"status": "CARD_PREAUTH_SUCCESS",
"cardGateway": {
"paymentId": "XYZ",
"preauthorizedAmount": 150.00,
"status": "SUCCESS"
},
"breakdown": {
"cardAmount": 150.00,
"voucherAmount": 0.00,
"paymentMethod": "CARD_ONLY"
}
}
After Step 1, the system automatically handles:
- Card Preauthorization (via configured payment gateway)
- Voucher selection page for the customer
- Meal voucher processing (if customer enters OTP)
- Final capture on card (total amount or difference)
- Notification to your webhook with complete details
For advanced integrations requiring granular control, use the Direct API endpoints.
See Postman collection for complete Direct API examples.
Payment Links - Send Shareable Payment Links with QR Codes
Create payment links for invoicing, social commerce, QR code payments, or remote sales. No checkout integration required!
Step 1: Create Payment Link
POST/api/payment-links/create
Headers:
Authorization: Bearer sk_test_YourSecretKey
Content-Type: application/json
{
"amount": 50.00,
"currency": "EUR",
"description": "Payment for Order #12345",
"customerEmail": "customer@example.com",
"customerName": "John Doe",
"linkType": "LINK",
"expirationDays": 30
}
Response:
{
"success": true,
"transactionId": "LINK_A1B2C3D4E5F67890",
"linkUrl": "https://testpayf.netsgroup.com/pgw/pay?mailId=...",
"qrcodeUrl": "/api/payment-links/LINK_A1B2C3D4E5F67890/qrcode",
"amount": 50.00,
"status": "PENDING",
"expiresAt": "2025-12-09T12:00:00"
}
Link Types
| Type | Description | Use Case |
|---|---|---|
LINK |
Shareable URL (recommended for QR codes) | QR Code POS, WhatsApp, Email |
MAIL |
Payment GW sends email to customer | Automated invoicing |
SMS |
Payment GW sends SMS to customer (requires phone) | Mobile payments |
Step 2: Display QR Code (No Auth Required)
<!-- Embed QR Code in HTML -->
<img src="https://api.payglobe.it/paymentgw/api/payment-links/LINK_ABC123/qrcode?size=300"
alt="Payment QR Code">
<!-- Or use in email template -->
<h2>Invoice Payment</h2>
<p>Scan QR code or click link to pay โฌ50.00</p>
<img src="{{qrcodeUrl}}" alt="QR Code">
<a href="{{linkUrl}}">Pay Now</a>
Step 3: Check Payment Status
GET/api/payment-links/{transactionId}
Headers:
Authorization: Bearer sk_test_YourSecretKey
Response:
{
"success": true,
"transactionId": "LINK_A1B2C3D4E5F67890",
"status": "PAID",
"paymentId": "IGFS_PAYMENT_ID",
"authCode": "123456",
"paidAt": "2025-11-09T12:30:00",
"amount": 50.00
}
Step 4: Verify Status from Payment Gateway
POST/api/payment-links/{transactionId}/verify
Headers:
Authorization: Bearer sk_test_YourSecretKey
Description: Refreshes payment status from gateway and updates local database
Additional Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/payment-links | List all payment links (filter: ?status=PAID) |
| POST | /api/payment-links/{id}/cancel | Cancel a pending payment link |
Status Values
| Status | Description |
|---|---|
PENDING |
Link created, awaiting payment |
PAID |
Payment completed successfully |
EXPIRED |
Link expired (past expiresAt date) |
CANCELLED |
Link manually cancelled |
Live Demo - Interactive Payment Links
Webhook Notifications (HMAC Signature)
PayGlobe sends server-to-server webhooks to notify you of payment events using HMAC-SHA256 signatures for security.
webhookSecret (e.g., whsec_test_...) for signature verification.
Step 1: Configure Webhook in Merchant Dashboard
- Go to Merchant Login and authenticate with your API key
- In the Webhook Configuration section, enter your webhook URL (e.g.,
https://yourdomain.com/api/payment-webhook) - Copy the Webhook Secret (shown once, format:
whsec_test_abc123...) - Save the secret in your backend environment variables
Step 2: Verify HMAC Signature (Java Example)
// Receive webhook
String signature = request.getHeader("X-PayGlobe-Signature");
String rawBody = request.getBody(); // RAW JSON string
// Verify signature with your webhook secret
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(webhookSecret.getBytes(), "HmacSHA256");
mac.init(secretKey);
byte[] hash = mac.doFinal(rawBody.getBytes());
String expectedSignature = Base64.getEncoder().encodeToString(hash);
if (expectedSignature.equals(signature)) {
// โ
Webhook authentic - process payment
PaymentNotification payment = objectMapper.readValue(rawBody, PaymentNotification.class);
System.out.println("Payment received: " + payment.transactionId);
} else {
// โ Invalid signature - reject
return Response.status(401).entity("Invalid signature").build();
}
Step 3: Verify HMAC Signature (Node.js Example)
const crypto = require('crypto');
app.post('/api/payment-webhook', express.raw({type: 'application/json'}), (req, res) => {
const signature = req.headers['x-payglobe-signature'];
const rawBody = req.body.toString(); // RAW body string
// Verify signature
const hmac = crypto.createHmac('sha256', process.env.PAYGLOBE_WEBHOOK_SECRET);
hmac.update(rawBody);
const expectedSignature = hmac.digest('base64');
if (expectedSignature !== signature) {
return res.status(401).send('Invalid signature');
}
// โ
Webhook verified - process payment
const payment = JSON.parse(rawBody);
console.log('Payment received:', payment.transactionId);
res.status(200).send('OK');
});
Webhook Payload Example:
POSThttps://yourdomain.com/api/payment-webhook
Headers:
X-PayGlobe-Signature: Kx7j9mP4vR2nQ8sT... (HMAC-SHA256 signature)
Content-Type: application/json
{
"transactionId": "TXN_20251107_123456",
"totalAmount": 150.00,
"cardAmount": 100.00,
"voucherAmount": 50.00,
"paymentMethod": "CARD_AND_VOUCHER",
"status": "SUCCESS",
"sessionStart": "2025-11-07T10:00:00Z",
"sessionEnd": "2025-11-07T10:05:23Z",
"sessionDurationSeconds": 323,
"vouchersUsed": 1,
"cardGatewayDetails": {
"paymentId": "PAY_789",
"transactionId": "IGFS_123456789",
"operationType": "CAPTURE",
"preauthorizedAmount": 150.00,
"capturedAmount": 100.00,
"responseCode": "00",
"responseMessage": "Transaction approved"
},
"voucherDetails": {
"serialNumber": "2189CA1L7823",
"operationNumber": "OP123456",
"spentAmount": 50.00,
"responseCode": "000",
"responseMessage": "Transaction OK"
},
"createdAt": "2025-11-07T10:00:00Z",
"completedAt": "2025-11-07T10:05:23Z"
}
- Always verify the signature before processing webhooks
- Use the RAW request body for signature verification (not parsed JSON)
- Store
webhookSecretsecurely (environment variable, secrets manager) - Return
200 OKquickly; process payment asynchronously if needed - Webhooks are sent for: PREAUTH success/fail, CAPTURE success/fail, VOID, Voucher processing
Testing Webhooks:
Use webhook.site or ngrok to test webhook delivery:
- Go to webhook.site and copy unique URL
- Configure this URL as webhook in Admin Panel
- Copy webhook secret shown
- Make a test payment
- Verify webhook received with
X-PayGlobe-Signatureheader
Stripe Webhook Compatibility
PayGlobe webhooks follow Stripe's Event object structure, allowing you to reuse existing Stripe webhook code and libraries without modifications.
Why Stripe-Compatible?
- Standard Format: Same JSON structure as Stripe webhook events
- Event Types: Uses Stripe naming conventions (
payment_intent.succeeded,payment_intent.payment_failed, etc.) - Drop-in Replacement: Works with existing Stripe webhook libraries (Node.js stripe, Python stripe, etc.)
- Enhanced Data: Includes additional PayGlobe features like voucher details and multi-gateway support
Webhook Event Structure:
PayGlobe wraps payment data in a Stripe-compatible Event object:
{
"id": "evt_1a2b3c4d5e6f7g8h9i0j", // Unique event ID (evt_...)
"object": "event", // Always "event"
"api_version": "2023-10-16", // API version
"created": 1699372800, // Unix timestamp
"type": "payment_intent.succeeded", // Event type
"livemode": false, // Test/live mode
"pending_webhooks": 0, // Delivery queue count
"data": {
"object": {
// Your payment data here (PaymentNotificationDto)
"transactionId": "TXN_20251107_123456",
"totalAmount": 150.00,
"cardAmount": 100.00,
"voucherAmount": 50.00,
"paymentMethod": "CARD_AND_VOUCHER",
"status": "SUCCESS",
"cardGatewayDetails": { ... },
"voucherDetails": { ... }
}
},
"request": null // Null for automatic events
}
Event Types:
| Event Type | When Triggered | Status |
|---|---|---|
payment_intent.succeeded |
Payment completed successfully | Active |
payment_intent.payment_failed |
Payment failed (preauth/capture/voucher failed) | Active |
payment_intent.processing |
Payment in progress | Active |
charge.refunded |
Refund processed (full or partial) | Active |
checkout.session.completed |
Checkout session completed (payment page closed) | Active |
Using Stripe Libraries (Node.js Example):
const stripe = require('stripe');
app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
const sig = req.headers['stripe-signature'] || req.headers['x-payglobe-signature'];
let event;
try {
// Works with Stripe's constructEvent method!
event = stripe.webhooks.constructEvent(
req.body,
sig,
process.env.WEBHOOK_SECRET // whsec_test_...
);
} catch (err) {
return res.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle event types
switch (event.type) {
case 'payment_intent.succeeded':
const payment = event.data.object;
console.log('Payment succeeded:', payment.transactionId);
console.log('Card amount:', payment.cardAmount);
console.log('Voucher amount:', payment.voucherAmount);
break;
case 'payment_intent.payment_failed':
console.log('Payment failed:', event.data.object);
break;
case 'charge.refunded':
const refund = event.data.object;
console.log('Refund processed:', refund.transactionId);
console.log('Refund amount:', refund.refundAmount);
break;
case 'checkout.session.completed':
console.log('Checkout completed:', event.data.object);
break;
default:
console.log(`Unhandled event type: ${event.type}`);
}
res.status(200).send('OK');
});
Using Stripe Libraries (Python Example):
import stripe
@app.route('/webhook', methods=['POST'])
def webhook():
payload = request.get_data()
sig_header = request.headers.get('Stripe-Signature') or \
request.headers.get('X-PayGlobe-Signature')
try:
# Works with Stripe's construct_event method!
event = stripe.Webhook.construct_event(
payload, sig_header, webhook_secret
)
except ValueError as e:
return 'Invalid payload', 400
except stripe.error.SignatureVerificationError as e:
return 'Invalid signature', 400
# Handle event
if event['type'] == 'payment_intent.succeeded':
payment = event['data']['object']
print(f"Payment succeeded: {payment['transactionId']}")
print(f"Card: {payment['cardAmount']}, Voucher: {payment['voucherAmount']}")
elif event['type'] == 'payment_intent.payment_failed':
print(f"Payment failed: {event['data']['object']}")
elif event['type'] == 'charge.refunded':
refund = event['data']['object']
print(f"Refund processed: {refund['transactionId']}")
print(f"Refund amount: {refund.get('refundAmount', 0)}")
elif event['type'] == 'checkout.session.completed':
print(f"Checkout completed: {event['data']['object']}")
return '', 200
PayGlobe sends both signature headers for maximum compatibility:
X-PayGlobe-Signature- PayGlobe standard headerStripe-Signature- Stripe-compatible header (same value)
This allows Stripe libraries to work without modifications!
PayGlobe Advantages Over Stripe:
| Feature | Stripe | PayGlobe |
|---|---|---|
| Webhook Format | Event object | Same Event object |
| Signature Verification | HMAC-SHA256 | HMAC-SHA256 (same) |
| Library Compatibility | Stripe libraries only | Works with Stripe libraries |
| Voucher Support | None | Yes (Assiopay/Monni) |
| Multi-Gateway | Stripe only | IGFS, Stripe, Nexi, Worldline, etc. |
| Detailed Gateway Info | Limited | Full details (cardGatewayDetails, voucherDetails) |
| Automatic Retries | Limited attempts | 10 attempts, 5 min intervals |
| Delivery Tracking | Basic dashboard | Full audit trail in database |
If you're migrating from Stripe, your existing webhook code will work with PayGlobe with minimal changes:
- Replace
STRIPE_WEBHOOK_SECRETwith PayGlobe'swebhookSecret - Update webhook URL in your configuration
- That's it! Your code continues to work.
Merchant Configuration & Onboarding
Managed Service
PayGlobe provides API keys and manages all backend configurations for you. You don't need to access any admin panel.
How It Works:
- Contact PayGlobe to open a merchant account
- Provide your business details and integration requirements
- PayGlobe configures all payment gateway credentials (IGFS, Stripe, Assiopay, etc.) on your behalf
- Receive your API keys:
pk_test_...- Publishable key (safe for frontend)sk_test_...- Secret key (backend only)
- Configure your webhook via Merchant Dashboard (self-service)
- Start accepting payments using our APIs
Error Handling
| Error Type | Status | Action |
|---|---|---|
| Card Preauth Failed | CARD_PREAUTH_FAILED | Inform user, retry or cancel |
| Voucher Invalid | VOUCHER_FAILED | Allow retry with different voucher or proceed without |
| Capture Failed | CARD_CAPTURE_FAILED | Contact support, transaction will auto-rollback |
| Transaction Timeout | FAILED | Check status, may need manual intervention |
API Endpoints & Authentication
Authentication with API Keys
All API requests require authentication using API keys. You will receive two keys:
- Publishable Key (
pk_test_xxxorpk_live_xxx): Safe to use in frontend code - Secret Key (
sk_test_xxxorsk_live_xxx): Must be kept secret, use only on your server
How to Authenticate
Include API key in Authorization header:
Authorization: Bearer pk_test_PayGlobe123456789TestPublishable
Example with JavaScript:
const response = await fetch('https://api.payglobe.it/paymentgw/api/payment/initiate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer pk_test_PayGlobe123456789TestPublishable'
},
body: JSON.stringify({
amount: 150.00,
email: 'customer@example.com',
description: 'Order #12345'
})
});
Example with cURL:
curl -X POST https://api.payglobe.it/paymentgw/api/payment/initiate \
-H "Authorization: Bearer pk_test_PayGlobe123456789TestPublishable" \
-H "Content-Type: application/json" \
-d '{"amount": 150.00, "email": "customer@example.com", "description": "Order #12345"}'
pk_test_*) for development. No real charges will be made.
Contact support@payglobe.com to receive your API keys.
Production Environment
- Base URL: https://api.payglobe.it/paymentgw
- Protocol: HTTPS only (TLS 1.2+)
- Content-Type: application/json
Testing
- Test Cards: Use test merchant credentials provided by PayGlobe
- Test Vouchers: Contact support for test voucher codes
- Sandbox: Contact support for test environment access
API Keys: Security & Permissions
๐ Two Types of API Keys
PayGlobe provides two distinct API keys with different permission levels:
โ Safe for client-side (JavaScript)
Allowed Operations:
- Create new payments
- Check payment status
- Get checkout URLs
โ Cannot perform:
- Refunds/Storni
- Capture operations
- Access sensitive data
- Modify configurations
Usage: Embed in your frontend JavaScript code
โ ๏ธ Server-side ONLY - Never expose publicly
Full Permissions:
- All Publishable Key operations
- Refunds & Storni
- Manual captures
- Void preauthorizations
- Webhook signature validation
- Access transaction details
Usage: Store in environment variables on your backend server
Refunds & Storni (Reversals)
1. Full Refund (Storno Totale)
Refund the entire payment amount:
Endpoint: POST /api/payment/refund/{transactionId}
Headers:
Authorization: Bearer sk_test_YourSecretKeyHere โ Secret Key required!
Content-Type: application/json
Request Body:
{
"reason": "Customer requested refund"
}
Example with cURL:
curl -X POST https://api.payglobe.it/paymentgw/api/payment/refund/abc123 \
-H "Authorization: Bearer sk_test_YourSecretKeyHere" \
-H "Content-Type: application/json" \
-d '{"reason": "Customer requested refund"}'
Response (Success):
{
"success": true,
"transactionId": "abc123",
"refundAmount": 150.00,
"status": "REFUNDED",
"refundedAt": "2025-11-05T20:15:00Z"
}
2. Partial Refund (Storno Parziale)
Refund only a portion of the payment:
Endpoint: POST /api/payment/refund/{transactionId}
Headers:
Authorization: Bearer sk_test_YourSecretKeyHere โ Secret Key required!
Content-Type: application/json
Request Body:
{
"amount": 50.00,
"reason": "Partial refund for damaged item"
}
Example with Node.js:
const response = await fetch(
`https://api.payglobe.it/paymentgw/api/payment/refund/${transactionId}`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.PAYGLOBE_SECRET_KEY}`, // sk_test_...
'Content-Type': 'application/json'
},
body: JSON.stringify({
amount: 50.00,
reason: 'Partial refund for damaged item'
})
}
);
const refund = await response.json();
console.log('Refund processed:', refund);
Response (Success):
{
"success": true,
"transactionId": "abc123",
"refundAmount": 50.00,
"remainingAmount": 100.00,
"status": "PARTIALLY_REFUNDED",
"refundedAt": "2025-11-05T20:15:00Z"
}
3. Get Refund History
Retrieve all refunds for a transaction:
Endpoint: GET /api/payment/refunds/{transactionId}
Headers:
Authorization: Bearer sk_test_YourSecretKeyHere โ Secret Key required!
Example:
curl https://api.payglobe.it/paymentgw/api/payment/refunds/abc123 \
-H "Authorization: Bearer sk_test_YourSecretKeyHere"
Response:
{
"transactionId": "abc123",
"originalAmount": 150.00,
"totalRefunded": 50.00,
"remainingAmount": 100.00,
"refunds": [
{
"refundId": "ref_xyz789",
"amount": 50.00,
"reason": "Partial refund for damaged item",
"status": "SUCCESS",
"createdAt": "2025-11-05T20:15:00Z"
}
]
}
Backend Implementation Example (Node.js/Express)
// Store secret key in environment variable
const PAYGLOBE_SECRET_KEY = process.env.PAYGLOBE_SECRET_KEY; // sk_test_...
// Refund endpoint on your server
app.post('/admin/refund/:transactionId', async (req, res) => {
try {
const { transactionId } = req.params;
const { amount, reason } = req.body;
// Call PayGlobe API with SECRET KEY
const response = await fetch(
`https://api.payglobe.it/paymentgw/api/payment/refund/${transactionId}`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${PAYGLOBE_SECRET_KEY}`, // SECRET!
'Content-Type': 'application/json'
},
body: JSON.stringify({ amount, reason })
}
);
const refund = await response.json();
if (refund.success) {
// Log the refund in your database
await logRefund(transactionId, refund);
res.json({ success: true, refund });
} else {
res.status(400).json({ error: refund.message });
}
} catch (error) {
res.status(500).json({ error: error.message });
}
});
โ ๏ธ Security Best Practices
- NEVER expose your Secret Key in client-side code (JavaScript, mobile apps)
- ALWAYS store Secret Keys in environment variables or secure secret managers
- NEVER commit Secret Keys to version control (Git)
- Use Publishable Keys for frontend operations only
- Implement refund operations only on your backend server with proper authentication
- Rotate keys immediately if a Secret Key is compromised
Summary: Which Key to Use?
Frontend (JavaScript - Publishable Key):
const payglobe = PayGlobe('pk_test_...'); โ
Public, safe to expose
await payglobe.payments.create({
amount: 100,
email: 'customer@example.com', // Customer email for receipt
description: 'Order #12345' // Your internal order number
});
Backend (Server - Secret Key):
const secretKey = process.env.PAYGLOBE_SECRET_KEY; โ
Private, server-only
Authorization: Bearer sk_test_...
Operations Requiring Secret Key:
- POST /api/payment/refund/{id} โ Full or partial refund
- GET /api/payment/refunds/{id} โ Get refund history
- POST /api/payment/capture/{id} โ Manual capture
- POST /api/payment/void/{id} โ Void preauthorization
- Webhook signature validation โ Verify HMAC
Dispute & Chargeback Management
How Disputes Work
Customer contacts their bank to dispute a transaction
IGFS, Stripe, or PayPal sends webhook to our
/webhook/psp/dispute endpoint
The dispute is visible in Merchant Dashboard โ Disputes tab (you don't create it manually)
Submit evidence (receipts, delivery proof, etc.) before the deadline
Bank decides: WON (funds returned) or LOST (funds kept by customer)
Dispute API Endpoints
1. List Disputes
GET/api/merchant/disputes
Headers:
Authorization: Bearer sk_test_YourSecretKey
Query Parameters:
?status=OPEN - Filter by status (OPEN, UNDER_REVIEW, WON, LOST, EXPIRED)
?startDate=2025-01-01 - Filter from date
?endDate=2025-12-31 - Filter to date
?page=0 - Page number (0-indexed)
?size=20 - Page size
Response:
{
"disputes": [
{
"disputeId": "dp_1abc123def456",
"originalTransactionId": "TXN_20251107_123456",
"amountDisputed": 50.00,
"currency": "EUR",
"reasonCode": "FRAUDULENT",
"reasonDescription": "Customer claims unauthorized charge",
"status": "OPEN",
"pspSource": "IGFS",
"responseDeadline": "2025-12-15T23:59:59Z",
"daysUntilDeadline": 12,
"isUrgent": false,
"createdAt": "2025-12-01T10:30:00Z"
}
],
"currentPage": 0,
"totalPages": 1,
"totalItems": 1
}
2. Get Dispute Statistics
GET/api/merchant/disputes/stats
Headers:
Authorization: Bearer sk_test_YourSecretKey
Response:
{
"open": 3,
"underReview": 1,
"won": 5,
"lost": 2,
"totalAtRisk": 150.00,
"totalLost": 75.00
}
3. Respond to Dispute
POST/api/merchant/disputes/{disputeId}/respond
Headers:
Authorization: Bearer sk_test_YourSecretKey
Content-Type: application/json
Request Body:
{
"response": "This transaction was legitimate. Customer placed order on 2025-11-01 and received delivery confirmation on 2025-11-03. Attached: order receipt, shipping tracking, delivery signature."
}
Response:
{
"success": true,
"disputeId": "dp_1abc123def456",
"status": "UNDER_REVIEW",
"message": "Response submitted successfully"
}
Dispute Status Values
| Status | Description | Action Required |
|---|---|---|
OPEN |
New dispute requiring response | Submit evidence before deadline |
UNDER_REVIEW |
Evidence submitted, awaiting bank decision | Wait for resolution |
EVIDENCE_REQUIRED |
Additional evidence requested | Submit more documentation |
WON |
Dispute resolved in your favor | None - funds returned |
LOST |
Customer won the dispute | None - funds not recovered |
CANCELLED |
Dispute withdrawn by customer/bank | None |
EXPIRED |
Response deadline passed | Automatically lost |
Dispute Reason Codes
| Reason Code | Description | Recommended Evidence |
|---|---|---|
FRAUDULENT |
Customer claims unauthorized charge | IP address, device info, order history |
PRODUCT_NOT_RECEIVED |
Customer didn't receive goods/services | Shipping tracking, delivery signature |
PRODUCT_UNACCEPTABLE |
Product differs from description | Product photos, description, communications |
DUPLICATE |
Customer charged multiple times | Transaction logs, refund records |
SUBSCRIPTION_CANCELLED |
Customer cancelled but still charged | Cancellation policy, terms of service |
CREDIT_NOT_PROCESSED |
Refund not received | Refund confirmation, bank records |
GENERAL |
Other/unspecified reason | Any relevant documentation |
- Disputes appear automatically - You don't create them; they come from PSP webhooks
- Response deadlines are critical - Missing the deadline usually means automatic loss
- isUrgent flag - Set to
truewhen less than 3 days remain - Secret Key required - All dispute endpoints require
sk_test_*orsk_live_*
Webhook: Receive Dispute Updates
When a dispute status changes, PayGlobe sends a webhook to your configured URL:
POSThttps://your-server.com/webhook/payglobe
Event Type: charge.dispute.created | charge.dispute.updated | charge.dispute.closed
Payload:
{
"id": "evt_dispute_123",
"type": "charge.dispute.created",
"created": 1701432000,
"data": {
"object": {
"disputeId": "dp_1abc123def456",
"originalTransactionId": "TXN_20251107_123456",
"amountDisputed": 50.00,
"status": "OPEN",
"reasonCode": "FRAUDULENT",
"responseDeadline": "2025-12-15T23:59:59Z"
}
}
}
Token Payments (Card on File)
Save customer cards for one-click payments and recurring billing. Supports both IGFS and Shift4 gateways.
PREAUTH_VOUCHER_CAPTURE- Classic flow with meal vouchers (default)SALE_DIRECT- Direct sale without vouchersSALE_WITH_TOKENIZATION- Direct sale + save card for future payments
1. List Saved Cards
Get all saved cards for a customer:
GET/api/token-payment/cards?customerId={customerId}
Headers:
Authorization: Bearer sk_test_YourSecretKey
Response:
{
"success": true,
"customerId": "cust_123",
"cards": [
{
"id": 1,
"alias": "VISA ****4242",
"brand": "VISA",
"last4": "4242",
"expiry": "12/26",
"gateway": "IGFS",
"isDefault": true,
"lastUsedAt": "2025-12-08T10:00:00",
"usageCount": 5
}
],
"count": 1
}
2. Pay with Saved Card (One-Click)
Charge a saved card without requiring customer to re-enter details:
POST/api/token-payment/pay
Headers:
Authorization: Bearer sk_test_YourSecretKey
Content-Type: application/json
Request Body:
{
"cardId": 1,
"amount": 50.00,
"customerId": "cust_123",
"customerEmail": "customer@example.com",
"description": "Subscription renewal"
}
Response (Success):
{
"success": true,
"transactionId": "TKN_ABC123DEF456",
"paymentId": "IGFS_PAY_789",
"gateway": "IGFS",
"status": "COMPLETED",
"cardAlias": "VISA ****4242",
"amount": 50.00
}
Response (Failed):
{
"success": false,
"transactionId": "TKN_ABC123DEF456",
"status": "FAILED",
"error": "Card declined"
}
3. Save Card from Transaction
After a successful payment with tokenization enabled, save the card:
POST/api/token-payment/save-card
Headers:
Authorization: Bearer sk_test_YourSecretKey
Content-Type: application/json
Request Body:
{
"transactionId": "TXN_20251208_123456",
"customerId": "cust_123",
"gateway": "IGFS"
}
Response:
{
"success": true,
"cardId": 1,
"alias": "VISA ****4242",
"brand": "VISA",
"last4": "4242",
"isDefault": true
}
4. Set Default Card
POST/api/token-payment/cards/{cardId}/set-default?customerId={customerId}
Headers:
Authorization: Bearer sk_test_YourSecretKey
Response:
{
"success": true,
"cardId": 1,
"alias": "VISA ****4242",
"isDefault": true
}
5. Delete Saved Card
DELETE/api/token-payment/cards/{cardId}
Headers:
Authorization: Bearer sk_test_YourSecretKey
Response:
{
"success": true,
"message": "Card deleted successfully"
}
Integration Flow
First Payment (with tokenization): 1. Create payment with paymentMode = SALE_WITH_TOKENIZATION 2. Customer completes payment on gateway page 3. Gateway returns card token 4. Call POST /api/token-payment/save-card to store card 5. Card is now available for future payments Subsequent Payments (one-click): 1. Call GET /api/token-payment/cards to get customer's cards 2. Display saved cards to customer 3. Customer selects a card 4. Call POST /api/token-payment/pay with cardId 5. Payment is processed server-to-server (no redirect)
- Card tokens are gateway-specific (IGFS or Shift4)
- Tokens can only be used with the same merchant that created them
- Always obtain customer consent before saving cards (GDPR compliance)
- Implement proper customer authentication before showing saved cards
Support
๐ Documentation
OpenAPI YAML specification with complete API reference
๐งช Postman Collection
Ready-to-use API tests for all integration scenarios
๐ฌ Technical Support
Contact: support@payglobe.com