TRAPAY API v2
Flexible payment infrastructure software for modern online businesses.
Route payments, automate workflows, manage provider integrations, monitor transactions and reconcile payment data through one secure API.
Introduction
Welcome to the Trapay API documentation. Our REST API allows you to process transactions, and manage payments programmatically.
RESTful Design
Predictable resource-oriented URLs and standard HTTP methods
JSON Responses
All responses are returned in JSON format with clear schemas
Webhooks
Real-time notifications for transaction events
Authentication Flow
Merchants authenticate by including their public API key in the request body.
Request Structure
POST /api/endpoint
Content-Type: application/json
{
"public_key": "pk_...",
"other_params": "..."
}
Where to Get API Key
Navigate to Integration
Dashboard → Integration → API Keys section
app.trapay.uk/dashboard/integrationKey Revoke
Navigate to Settings
Dashboard → Settings → Security → API Keys section
app.trapay.uk/dashboard/settings?tab=securityRevoke Keys
Press on Revoke all API keys button to invalidate all existing keys.
Key Types
pk_...
For API authentication
sk_...
For webhook signature validation
Create Payment
/api/payments/create
Public endpoint for creating payment requests.
Authentication
Validates merchant via public_key in request body.
Code Examples
const response = await fetch('https://api.trapay.uk/api/payments/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
public_key: 'pk_your_public_key',
gateway: '0000',
amount: 100.00,
currency: 'USD',
order_id: 'ORDER-123'
})
});
const data = await response.json();
console.log(data);
import requests
url = 'https://api.trapay.uk/api/payments/create'
payload = {
'public_key': 'pk_your_public_key',
'gateway': '0000',
'amount': 100.00,
'currency': 'USD',
'order_id': 'ORDER-123'
}
response = requests.post(url, json=payload)
data = response.json()
print(data)
<?php
$url = 'https://api.trapay.uk/api/payments/create';
$payload = [
'public_key' => 'pk_your_public_key',
'gateway' => '0000',
'amount' => 100.00,
'currency' => 'USD',
'order_id' => 'ORDER-123'
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json'
]);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
print_r($data);
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
func main() {
url := "https://api.trapay.uk/api/payments/create"
payload := map[string]interface{}{
"public_key": "pk_your_public_key",
"gateway": "0000",
"amount": 100.00,
"currency": "USD",
"order_id": "ORDER-123",
}
jsonData, _ := json.Marshal(payload)
resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonData))
if err != nil {
panic(err)
}
defer resp.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
fmt.Println(result)
}
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
public_key |
string | Required | Merchant's public key from database |
gateway |
string | Required | Gateway ID (e.g. "0000") |
amount |
number | Required | Payment amount (positive) |
currency |
string | Required | Payment currency (USD, EUR, etc.) |
order_id |
string | Required | Merchant's order reference |
Optional Fields
| Parameter | Type | Description |
|---|---|---|
success_url |
string | Redirect URL on success |
fail_url |
string | Redirect URL on failure |
pending_url |
string | Redirect URL on pending |
customer_email |
string | Customer email address |
customer_name |
string | Customer full name |
Success Response
{
"success": true,
"message": "Payment created successfully",
"result": {
"id": "pre_payment_uuid",
"order_id": "ORDER-123",
"proxy_url": "https://app.trapay.uk/p/{pre_payment_id}",
"status": "PENDING_CUSTOMER_DETAILS"
}
}
Error Response
{
"success": false,
"message": "Invalid public key"
}
Validation errors include a details array:
{
"success": false,
"message": "Validation error",
"details": [
"\"gateway\" is required"
]
}
Common Error Messages
Invalid public key
Public key not found
Shop is not active
Shop status is not ACTIVE
Gateway "…" is not enabled for your shop
Gateway not enabled for shop
Validation error
Request body failed validation (see details)
Payment Statuses
Main Flow
Alternative Outcomes
Post-Payment Actions
Webhooks
Webhooks are HTTP callbacks sent to merchant's configured URL when payment status changes.
Configuration
Merchants configure webhooks in Dashboard → Integration → Webhooks:
{
"webhookUrl": "https://yoursite.com/webhook",
"webhookEvents": [
"payment.success",
"payment.failed",
"payment.pending"
]
}
Event Mapping
Each webhook includes an event field. Enable the events you need in Dashboard → Integration → Webhooks.
| Event | Payment status |
|---|---|
payment.success |
PAID |
payment.failed |
FAILED, EXPIRED, CHARGEBACK, REFUND |
payment.pending |
PENDING, PROCESSING |
Webhook Payload
Request Format
POST {merchant.webhookUrl}
Content-Type: application/json
X-Webhook-Signature: {hmac_signature}
{
"event": "payment.success",
"payment": {
"id": "cmr0v4rjr00kvg0vfx5nywo14",
"orderId": "ORDER-123",
"status": "PAID",
"amount": 100.00,
"currency": "EUR",
"createdAt": "2024-01-15T10:25:00.000Z",
"paidAt": "2024-01-15T10:30:00.000Z"
}
}
Payment Object Fields
| Field | Type | Description |
|---|---|---|
id |
string | Trapay payment ID |
orderId |
string | Your order reference from the create request |
status |
string | Payment status: PAID, FAILED, PENDING, etc. |
amount |
number | Payment amount |
currency |
string | Payment currency (USD, EUR, etc.) |
createdAt |
string | ISO 8601 creation timestamp |
paidAt |
string | null | ISO 8601 paid timestamp, or null if not paid yet |
Test Webhook
Use Dashboard → Integration → Send Test Webhook to verify your endpoint. The test payload uses event: "test":
{
"event": "test",
"payment": {
"id": "test_payment_123",
"orderId": "test_order_123",
"status": "PAID",
"amount": 100,
"currency": "USD",
"createdAt": "2024-01-15T10:25:00.000Z",
"paidAt": "2024-01-15T10:30:00.000Z"
}
}
Signature Verification
Webhook signature uses HMAC-SHA256 with your secret key (sk_...) to ensure authenticity.
Signature Algorithm
Sign the raw request body exactly as received — do not re-serialize parsed JSON, or the signature will not match.
const signature = crypto
.createHmac('sha256', secret_key)
.update(rawRequestBody)
.digest('hex')
Verification Examples
import crypto from 'crypto'
import express from 'express'
const app = express()
// Keep raw body for signature verification
app.use('/webhook', express.raw({ type: 'application/json' }))
export const verifyWebhookSignature = (
payload: Buffer | string,
signature: string,
secret: string
): boolean => {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
)
}
app.post('/webhook', (req, res) => {
const signature = req.headers['x-webhook-signature'] as string
const secretKey = 'sk_your_secret_key'
if (!verifyWebhookSignature(req.body, signature, secretKey)) {
return res.status(401).json({ error: 'Invalid signature' })
}
const data = JSON.parse(req.body.toString())
console.log('Webhook verified:', data)
res.status(200).json({ received: true })
})
import hmac
import hashlib
from flask import Flask, request, jsonify
app = Flask(__name__)
def verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool:
expected_signature = hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected_signature)
@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('X-Webhook-Signature', '')
payload = request.get_data()
secret_key = 'sk_your_secret_key'
if not verify_webhook_signature(payload, signature, secret_key):
return jsonify({'error': 'Invalid signature'}), 401
data = request.get_json()
print('Webhook verified:', data)
return jsonify({'received': True}), 200
<?php
function verifyWebhookSignature($payload, $signature, $secret) {
$expectedSignature = hash_hmac('sha256', $payload, $secret);
return hash_equals($signature, $expectedSignature);
}
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$secretKey = 'sk_your_secret_key';
if (!verifyWebhookSignature($payload, $signature, $secretKey)) {
http_response_code(401);
echo json_encode(['error' => 'Invalid signature']);
exit;
}
$data = json_decode($payload, true);
error_log('Webhook verified: ' . print_r($data, true));
http_response_code(200);
echo json_encode(['received' => true]);
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"io"
"net/http"
)
func verifyWebhookSignature(payload, signature, secret string) bool {
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(payload))
expectedSignature := hex.EncodeToString(h.Sum(nil))
return hmac.Equal([]byte(signature), []byte(expectedSignature))
}
func webhookHandler(w http.ResponseWriter, r *http.Request) {
signature := r.Header.Get("X-Webhook-Signature")
secretKey := "sk_your_secret_key"
body, _ := io.ReadAll(r.Body)
payload := string(body)
if !verifyWebhookSignature(payload, signature, secretKey) {
http.Error(w, `{"error":"Invalid signature"}`, http.StatusUnauthorized)
return
}
var data map[string]interface{}
json.Unmarshal(body, &data)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]bool{"received": true})
}
func main() {
http.HandleFunc("/webhook", webhookHandler)
http.ListenAndServe(":8080", nil)
}
Delivery
Trapay sends one HTTP POST when a payment status changes. If delivery fails (network error or non-2xx response), the attempt is logged but not automatically retried.
Single Attempt
One delivery per status change event
Success Criteria
HTTP 200–299 response
Webhook Logs
View delivery history in Dashboard → Integration → Webhook Logs
Failure Handling
timeout
Your endpoint did not respond in time
connection_refused
Unable to connect to webhook URL
invalid_status
HTTP status code outside 200–299 range
Failed Webhooks
If a webhook fails, check Dashboard → Integration → Webhook Logs for the HTTP status and response. Use Send Test Webhook to verify your endpoint and signature handling before going live.