Authentication
One2Pays uses HMAC-based Integration Authentication for all API requests. Each integration has an API key and secret key that are used to generate HMAC signatures for request authentication.
Keep your secret keys secure!
Never share your secret keys in publicly accessible areas such as GitHub, client-side code, or any other public forums. Secret keys are used to generate HMAC signatures and must be kept confidential.
Integration Setup
Before you can make API requests, you need to create an integration in your merchant dashboard:
- Log in to your Merchant Dashboard
- Navigate to Integrations section
- Create a new integration or select an existing one
- Generate API credentials (API key and secret key)
- Copy your API Key and Secret Key
Integration Context
When you authenticate with integration credentials, the API automatically provides your merchantId and integrationId in the request context. You don't need to include these in your request body.
API Credentials
Each integration provides:
- API Key (
pk_test_...orpk_live_...) - Public identifier for your integration - Secret Key (
sk_test_...orsk_live_...) - Used to generate HMAC signatures (keep secret!)
Environments
One2Pays provides two environments:
Test Environment:
- Base URL:
<SandboxApiUrl /> - API keys start with
pk_test_andsk_test_ - No real money is processed
- Perfect for development and testing
Production Environment:
- Base URL:
<ApiUrl /> - API keys start with
pk_live_andsk_live_ - Real money is processed
- Use only when you're ready to accept live payments
Environment Matching
Test keys can only be used in test environment, and production keys can only be used in production environment. Using mismatched keys will result in authentication errors.
HMAC Signature Authentication
All API requests must include three headers:
X-API-Key- Your integration API keyX-Timestamp- Unix timestamp in milliseconds (e.g.,1704067200000)X-Signature- HMAC-SHA256 signature (format:sha256=<hex_signature>)
Signature Generation
The signature is generated using HMAC-SHA256 with the following format:
signature = HMAC-SHA256(timestamp + "." + rawBody, secretKey)Where:
timestampis the Unix timestamp in milliseconds (same asX-Timestampheader)rawBodyis the raw request body as a string (empty string for GET requests)secretKeyis your integration secret key
The signature must be prefixed with sha256= in the X-Signature header.
Signature Requirements
- Timestamp freshness: Timestamps must be within 5 minutes of the current time
- Raw body: Use the exact raw request body string (before JSON parsing)
- Case sensitivity: Header names are case-insensitive, but values are case-sensitive
- Empty body: For GET requests, use an empty string
""as the body
Code Examples
import crypto from 'crypto';
const API_KEY = process.env.PAYMENT_API_KEY!;
const API_SECRET = process.env.PAYMENT_API_SECRET!;
async function makeRequest(method: string, path: string, body?: object) {
const timestamp = Date.now().toString();
const rawBody = body ? JSON.stringify(body) : '';
// Create signature: HMAC-SHA256(timestamp + "." + rawBody, secretKey)
const message = `${timestamp}.${rawBody}`;
const signature = crypto
.createHmac('sha256', API_SECRET)
.update(message)
.digest('hex');
const response = await fetch(`https://api.example.com${path}`, {
method,
headers: {
'X-API-Key': API_KEY,
'X-Timestamp': timestamp,
'X-Signature': `sha256=${signature}`,
'Content-Type': 'application/json',
},
body: rawBody || undefined,
});
return response.json();
}
// Example: Create a payment
const payment = await makeRequest('POST', '/api/v1/payments', {
amount: '1000.00',
currency: 'THB',
referenceId: 'order-12345',
paymentMethod: 'promptpay',
customerBankAccountName: 'John Doe',
customerBankAccountNumber: '1234567890',
customerBankCode: '004',
});Error Responses
If authentication fails, you'll receive one of these error responses:
Invalid API Key
{
"success": false,
"error": {
"code": "INVALID_CREDENTIALS",
"message": "Invalid API key"
}
}Missing Required Headers
{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "API key is required in X-API-Key header"
}
}Invalid Signature
{
"success": false,
"error": {
"code": "INVALID_CREDENTIALS",
"message": "Invalid signature or expired timestamp"
}
}Expired Timestamp
{
"success": false,
"error": {
"code": "INVALID_CREDENTIALS",
"message": "Invalid signature or expired timestamp"
}
}Integration Not Active
{
"success": false,
"error": {
"code": "FORBIDDEN",
"message": "Integration is not active"
}
}Environment Mismatch
{
"success": false,
"error": {
"code": "FORBIDDEN",
"message": "Production keys cannot be used in test environment"
}
}Security Best Practices
Environment Variables
Always store your API credentials in environment variables instead of hardcoding them in your application.
Node.js Example
// .env file
PAYMENT_API_KEY=pk_test_...
PAYMENT_API_SECRET=sk_test_...
// app.ts
import dotenv from 'dotenv';
dotenv.config();
const API_KEY = process.env.PAYMENT_API_KEY!;
const API_SECRET = process.env.PAYMENT_API_SECRET!;Security Checklist
- ✅ Use environment variables for API credentials
- ✅ Never commit API credentials to version control
- ✅ Use different credentials for different environments
- ✅ Regularly rotate your API credentials
- ✅ Monitor API credential usage in your dashboard
- ✅ Use HTTPS for all API requests
- ✅ Validate timestamp freshness (within 5 minutes)
- ✅ Use constant-time comparison for signature verification (if implementing your own)
- ❌ Never use secret keys in client-side code
- ❌ Never share API credentials in public forums or documentation
- ❌ Never log API credentials or signatures
Common Issues
Signature Mismatch
Problem: Signature verification fails even with correct credentials.
Solutions:
- Ensure timestamp is in milliseconds (not seconds)
- Verify raw body matches exactly (no extra whitespace, correct JSON encoding)
- Check that signature format is
sha256=<hex_signature>(withsha256=prefix) - Ensure secret key is correct (not API key)
Timestamp Expired
Problem: Request fails with "expired timestamp" error.
Solutions:
- Ensure system clock is synchronized (use NTP)
- Generate timestamp just before making the request
- Timestamps must be within 5 minutes of current time
Environment Mismatch
Problem: Request fails with "environment mismatch" error.
Solutions:
- Use test keys (
pk_test_...,sk_test_...) in test environment - Use production keys (
pk_live_...,sk_live_...) in production environment - Verify you're using the correct base URL for your environment