This guide provides comprehensive documentation for integrating with the FundPay payment platform. Follow the steps below to implement a secure and reliable integration.
The following sequence diagrams illustrate the complete integration flows for deposit and withdrawal operations. These diagrams show the interaction between your application, FundPay APIs, and the banking systems. Click on any diagram to view it in full size.
This diagram shows the complete flow for processing deposit transactions, including signature generation, API calls, and webhook notifications.

This diagram illustrates the withdrawal process, including the additional security measures and bank account verification steps required for fund disbursements.

FundPay provides two methods of authentication for API requests. Choose the method that best fits your integration needs.
Use API keys for simple authentication. Include your API key in the request header.
x-api-key: YOUR_API_KEY
Pros: Simple to implement, minimal setup required.
Cons: Less secure than signature-based authentication.
Generate a cryptographic signature for each request for enhanced security.
{
...
"signature": "GENERATED_SIGNATURE"
}Pros: Highly secure, prevents tampering and replay attacks.
Cons: More complex to implement.
We strongly recommend using signature-based authentication for production environments. API key authentication is suitable for testing and development but provides less security for live transactions.
Your API keys are available in your merchant dashboard. Each API key has specific permissions and can be restricted to certain IP addresses for additional security.
Never share your API keys or include them in client-side code. Always store API keys securely in environment variables or a secure key management system.
// JavaScript Example
const axios = require('axios');
async function makeApiRequest() {
try {
const response = await axios.get('https://api.fundpay24h.com/api/v1/merchant/balance', {
headers: {
'x-api-key': 'YOUR_API_KEY'
}
});
console.log(response.data);
} catch (error) {
console.error('Error:', error);
}
}Generating a valid signature for your API requests involves creating an HMAC-SHA256 hash of your request parameters using your secret key.
Collect all the parameters for your API request (excluding the signature itself).
Sort all parameter keys in alphabetical order (A-Z).
Create a URL-encoded string of key-value pairs in the format: key1=value1&key2=value2
Create an HMAC-SHA256 hash of the URL-encoded string using your merchant secret key.
Include the generated signature in your API request as the signature parameter.
When generating signatures, nested objects in the API request must be flattened into individual fields. For example:
Original nested object:
"source_bank_account": {
"bank_code": "kbank",
"account_name": "TEST HEELO",
"account_number": "1234567"
}Flattened for signature generation:
"source_account_bank_code": "kbank" "source_account_name": "TEST HEELO" "source_account_no": "1234567"
{
"merchant_id": "d0bf1184-c1c9-4101-b034-97e3b44edf4e",
"amount": "1000",
"currency": "THB",
"reference": "ttt",
"payment_method": "withdrawal",
"frontend_return_url": "https://example.com/return",
"backend_return_url": "https://example.com/webhook",
"source_account_bank_code": "kbank",
"source_account_name": "TEST HEELO",
"source_account_no": "1234567",
"timestamp": "2025-07-29T21:23:29+07:00"
}YOUR_SECRET_KEY
amount=3269&backend_return_url=https%3A%2F%2Fexample.com%2Fwebhook¤cy=THB&frontend_return_url=https%3A%2F%2Fexample.com%2Freturn&merchant_id=79703311-ab77-4f93-a8b8-70c21a2c8c79&payment_method=qr_payment&reference=deposit_281753758186604&source_account_bank_code=kbank&source_account_name=TEST+HEELO&source_account_no=1234567×tamp=2025-07-29T22%3A10%3A00%2B07%3A00
7dd83af0dcae52d6f2ae5e8672f41fc067c1d587a9ab932495c38dca2e70e1e5
Below are code samples for generating signatures in different programming languages. You can also use the interactive form to generate a signature for your API request.
// JavaScript Example (Node.js)
const crypto = require('crypto');
function generateSignature(payload, secretKey) {
// Sort keys alphabetically
const sortedKeys = Object.keys(payload).sort();
// Create values object
const values = {};
for (const key of sortedKeys) {
values[key] = String(payload[key]);
}
// Create string to sign (URL-encoded key-value pairs)
const params = new URLSearchParams();
for (const key of sortedKeys) {
params.append(key, values[key]);
}
const stringToSign = params.toString();
// Generate HMAC
const hmac = crypto.createHmac('sha256', secretKey);
hmac.update(stringToSign);
return hmac.digest('hex');
}
// Example usage for withdrawal
const payload = {
merchant_id: '79703311-ab77-4f93-a8b8-70c21a2c8c79',
amount: 1000,
currency: 'THB',
reference: 'ttt',
payment_method: 'withdrawal',
frontend_return_url: 'https://example.com/return',
backend_return_url: 'https://example.com/webhook',
source_account_bank_code: 'kbank',
source_account_name: 'TEST HEELO',
source_account_no: '1234567',
timestamp: '2025-07-29T21:23:29+07:00'
};
const secretKey = 'YOUR_SECRET_KEY';
const signature = generateSignature(payload, secretKey);When generating signatures, nested objects in the API request must be flattened into individual fields. For example:
Original nested object:
"source_bank_account": {
"bank_code": "kbank",
"account_name": "TEST HEELO",
"account_number": "1234567"
}Flattened for signature generation:
"source_account_bank_code": "kbank" "source_account_name": "TEST HEELO" "source_account_no": "1234567"
Add this signature to your request payload in the signature field.
FundPay provides a comprehensive set of API endpoints for managing deposits and withdrawals. Below are the detailed specifications for each endpoint.
Creates a new deposit request for a merchant. Supports both QR code and bank transfer payment methods.
| Field | Type | Description |
|---|---|---|
| merchant_id | string | Your merchant ID provided by FundPay |
| amount | number | Amount in the smallest currency unit |
| currency | string | Currency code (e.g., THB) |
| reference | string | Your unique reference for this transaction |
| payment_method | string | Payment method (qr_payment or bank_transfer) |
| frontend_return_url | string | URL to redirect customer after payment |
| backend_return_url | string | URL for webhook notifications |
| timestamp | string | ISO 8601 timestamp |
| source_bank_account | object | The source bank account details (optional) |
| destination_bank_account | object | The destination bank account details (optional) |
| customer_email | string | Customer Email (optional) |
| customer_phone_number | string | Customer phone number (optional) |
| description | string | Additional details about the deposit (optional) |
| signature | string | HMAC-SHA256 signature of the request |
{
"merchant_id": "MERCHANT_DEV_20250522_ABCDEFGHIJKLMN",
"amount": 123124,
"currency": "THB",
"reference": "ORD1026761",
"payment_method": "bank_transfer",
"frontend_return_url": "https://localhost:3000/frontend",
"backend_return_url": "https://localhost:3000/backend",
"source_bank_account": {
"bank_code": "kbank",
"account_number": "12345678",
"account_name": "John Doe"
},
"timestamp": "2025-05-20T16:10:02.941Z",
"customer_email": "[email protected]",
"customer_phone_number": "+66812345678",
"description": "Payment for order ORD1026761",
"signature": "87df2dcd4fbfe782ba9035635ea98a1fc9a4c74ff741e3fcf526ce3711940414"
}| Field | Type | Description |
|---|---|---|
| code | integer | Response code (2000 for success) |
| message | string | Response message |
| data.id | string | Unique deposit ID |
| data.web_payment_url | string | URL for payment page |
| data.payment_token | string | Token to identify this payment |
| data.total_amount | number | Total amount to be paid including any fees |
| data.destination_bank_account | object | The destination bank account details |
| data.destination_bank | object | Destination bank information |
| data.expires_at | string | Expiration timestamp |
{
"code": 2000,
"message": "Deposit created successfully",
"data": {
"id": "deposit_dev_ERKWB1VFRJ7TjBpHcabGaOpgr6AFpNOn",
"web_payment_url": "https://customer-dev.fundpay.app/payment/EV0ZZMILAKMX5iSMqZKPXjHBeUQoA2k6C4ST7Gn7DEz0CR26KD4gWCADJD5TFK8m",
"payment_token": "EV0ZZMILAKMX5iSMqZKPXjHBeUQoA2k6C4ST7Gn7DEz0CR26KD4gWCADJD5TFK8m",
"total_amount": 123124.37,
"expires_at": "2025-05-21T16:09:32.305746742Z",
"destination_bank_account": {
"bank_code": "kbank",
"account_number": "12345667",
"account_name": "John Doe",
"account_promptpay": "1234567890123"
},
"destination_bank": {
"code": "004",
"name": {
"th": "ธนาคารกสิกรไทย",
"en": "Kasikorn Bank"
},
"logo_url": "https://fundpay24h.com/bank-logo/kbank.png"
}
}
}Retrieves the current status of a deposit by its ID.
| Parameter | Type | Description |
|---|---|---|
| id | string | Deposit ID to retrieve |
| Field | Type | Description |
|---|---|---|
| code | integer | Response code (2000 for success) |
| message | string | Response message |
| data.id | string | Unique deposit ID |
| data.status | string | Deposit status (e.g., pending_approve, completed, failed) |
| data.total_amount | number | Total deposit amount |
| data.currency | string | Currency code |
| data.reference | string | Merchant reference |
| data.created_at | string | Creation timestamp |
| data.updated_at | string | Last update timestamp |
| data.payment_method | string | Payment method used |
| data.source_bank_account | object | Source bank account details |
| data.destination_bank_account | object | Destination bank account details |
| data.source_bank | object | Source bank information |
| data.destination_bank | object | Destination bank information |
| data.merchant | object | Merchant information |
{
"code": 2000,
"message": "Get deposit successfully",
"data": {
"id": "deposit_dev_ERKWB1VFRJ7TjBpHcabGaOpgr6AFpNOn",
"status": "pending_approve",
"total_amount": 123123.1,
"currency": "THB",
"reference": "ORD1026761",
"payment_method": "bank_transfer",
"created_at": "2025-05-20T17:03:40.401157Z",
"updated_at": "2025-05-20T17:03:40.401157Z",
"source_bank_account": {
"bank_code": "kbank",
"account_number": "12345678",
"account_name": "John Doe"
},
"destination_bank_account": {
"bank_code": "kbank",
"account_number": "87654321",
"account_name": "Jane Smith"
},
"source_bank": {
"code": "004",
"name": {
"th": "ธนาคารกสิกรไทย",
"en": "Kasikorn Bank"
},
"logo_url": "https://fundpay24h.com/bank-logo/kbank.png"
},
"destination_bank": {
"code": "004",
"name": {
"th": "ธนาคารกสิกรไทย",
"en": "Kasikorn Bank"
},
"logo_url": "https://fundpay24h.com/bank-logo/kbank.png"
},
"merchant": {
"merchant_code": "MERCHANT_DEV_20250522",
"legal_name": {
"th": "บริษัท เทสต์ จำกัด",
"en": "Test Company Limited"
}
}
}
}Follow these security best practices to ensure your integration with FundPay is secure and reliable:
If you have any questions or need assistance with your integration, please contact our support team at [email protected].
FundPay sends webhook notifications to your backend server when transaction statuses are updated. This guide explains how to properly handle these webhook notifications.
Webhooks are HTTP callbacks that notify your system when events occur in the FundPay platform. When a transaction status changes, FundPay will send a POST request to your specified backend URL with details about the event.
When transaction statuses change, FundPay will send webhook notifications to your backend server. The webhook contains all the necessary information about the transaction.
When a deposit status changes, FundPay will send a webhook notification with the following payload structure.
{
"amount": 1500.23,
"status": "approved",
"signature": "e666fcb596d6213b9807930909b77f37a2733d2e6a0835d97c37837b4e10f01e",
"merchant_id": "c513667a-36c5-4c2a-bbba-e72e632aa906",
"reference_id": "ORD-12345",
"transaction_id": "deposit_dev_EWuWJFgxR0NlrZFoJEm42ZOl3DHVfTL4",
"transaction_date": "2025-06-21T12:42:20Z",
"transaction_type": "deposit"
}| Field | Type | Description |
|---|---|---|
| amount | number | The transaction amount in the smallest currency unit |
| status | string | Transaction status (approved, rejected, pending) |
| signature | string | HMAC-SHA256 signature for verifying the webhook |
| merchant_id | string | Your merchant ID |
| reference_id | string | Your reference ID for this transaction |
| transaction_id | string | FundPay's unique transaction ID |
| transaction_date | string | ISO 8601 timestamp of the transaction |
| transaction_type | string | Type of transaction (deposit) |
To ensure webhook authenticity and security, you must verify the signature included in each webhook payload. This prevents malicious actors from sending fake webhook notifications to your server.
Receive the webhook payload and extract all fields except the signature.
Sort all payload fields (excluding signature) in alphabetical order by key name.
Build a URL-encoded query string from the sorted parameters.
Create HMAC-SHA256 hash of the URL-encoded string using your secret key.
Use a constant-time comparison to verify the generated signature matches the received signature.
// Node.js Example
const crypto = require('crypto');
function verifyWebhookSignature(payload, receivedSignature, secretKey) {
// Extract signature from payload
const { signature, ...payloadWithoutSignature } = payload;
// Sort parameters alphabetically
const sortedKeys = Object.keys(payloadWithoutSignature).sort();
// Create values object
const values = {};
for (const key of sortedKeys) {
values[key] = String(payloadWithoutSignature[key]);
}
// Create string to sign (URL-encoded key-value pairs)
const params = new URLSearchParams();
for (const key of sortedKeys) {
params.append(key, values[key]);
}
const stringToSign = params.toString();
// Generate expected signature
const expectedSignature = crypto
.createHmac('sha256', secretKey)
.update(stringToSign)
.digest('hex');
// Use constant-time comparison for security
return crypto.timingSafeEqual(
Buffer.from(receivedSignature, 'hex'),
Buffer.from(expectedSignature, 'hex')
);
}
// Express.js webhook handler example
const express = require('express');
const app = express();
// Middleware to get raw body for signature verification
app.use('/webhook', express.raw({ type: 'application/json' }));
app.post('/webhook', (req, res) => {
try {
// Parse the JSON payload
const payload = JSON.parse(req.body.toString());
// Verify signature
const secretKey = process.env.FUNDPAY_SECRET_KEY;
if (!secretKey) {
return res.status(500).json({ error: 'Secret key not configured' });
}
const isValid = verifyWebhookSignature(payload, payload.signature, secretKey);
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process webhook payload
console.log('Valid webhook received:', payload);
// Update your database based on transaction status
// await updateTransactionStatus(payload.transaction_id, payload.status);
res.status(200).json({ success: true });
} catch (error) {
console.error('Webhook processing error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
app.listen(3000, () => {
console.log('Webhook server listening on port 3000');
});Follow these steps to properly implement webhook handling in your application.
Contact FundPay support to register your webhook URL. This URL should point to an endpoint on your server that will receive webhook notifications.
Create an endpoint on your server to receive webhook notifications. This endpoint should: