API Documentation
Integrate PayCow's non-custodial crypto payment gateway into your platform. Accept USDT on TRC-20 (TRON) and ERC-20 (Ethereum) with real-time blockchain confirmation, instant webhooks, and REST status polling.
0 AI Agent Integration Prompt
Instruct AI coding assistants to integrate PayCow automatically
Download or copy the master prompt file to instruct AI coding assistants (Cursor, Windsurf, Claude Code, GitHub Copilot, ChatGPT) to integrate PayCow cleanly into your codebase.
1 Overview
Understand how PayCow works at a high level
PayCow provides a non-custodial RESTful API and Webhook event system to generate and monitor cryptocurrency payment sessions. Your backend initiates a payment session and redirects the customer to our hosted checkout, which handles real-time blockchain monitoring automatically.
https://paycow.net — All API endpoints enforce HTTPS.
Supported Settlement Networks
| Network | Code | Address Format | Confirmations |
|---|---|---|---|
| TRON (TRC-20) | TRC20 |
Starts with T (34 chars) |
Solidified Block (~20 confirms) |
| Ethereum (ERC-20) | ERC20 |
Starts with 0x (42 chars) |
12 Confirmed Blocks |
Integration Flow
1. Merchant Server --> POST /api/payment/create (amount, userId, network)
2. PayCow Server --> Returns { sessionId, paymentUrl, depositAddress, payableAmount }
3. Customer --> Redirected to paymentUrl
4. PayCow Engine --> Monitors blockchain and confirms transaction
5. State Update --> PUSH: Webhook POST to merchant URL
PULL: Merchant polls GET /payment/data/:id
2 Authentication
Secure your API requests with merchant credentials
Authenticated API requests require your merchant API Key and API Secret passed as custom HTTP headers. Retrieve these from your merchant profile dashboard at /dashboard.
x-api-key: your_api_key_here x-api-secret: your_api_secret_here
3 Error Handling
Understand HTTP status codes and error response format
PayCow uses standard HTTP status codes. Error responses return a JSON object with a descriptive msg or error field.
| Status Code | Meaning |
|---|---|
| 200 / 201 | Success |
| 400 | Bad Request — Invalid or missing parameters |
| 401 | Unauthorized — Missing or invalid API credentials |
| 403 | Forbidden — Account suspended or subscription exceeded |
| 404 | Not Found — Payment session does not exist |
| 500 | Internal Server Error — Retry with Idempotency-Key |
4 Create Payment Session
Generate a non-custodial payment session and hosted checkout URL
HTTP Headers
| Header | Type | Description |
|---|---|---|
| Content-Type required | string | Must be application/json |
| x-api-key required | string | Merchant public API key |
| x-api-secret required | string | Merchant private API secret |
| Idempotency-Key optional | string | Unique request ID to prevent duplicate payments |
Request Body
| Field | Type | Description |
|---|---|---|
| amount required | number | Base order amount in USDT (e.g. 50.00) |
| userId required | string | Your internal order ID or customer reference |
| network required | string | TRC20 or ERC20 |
| currency optional | string | Defaults to USDT |
| addressSerialNo optional | string | Target wallet serial ID (defaults to primary wallet) |
50.0001, 50.0002) to prevent transaction collisions.
Use amountRequested / baseAmount for internal accounting, and payableAmount for the customer-facing on-chain amount.
cURL Example
curl -X POST https://paycow.net/api/payment/create \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-H "x-api-secret: YOUR_API_SECRET" \
-H "Idempotency-Key: order_tron_847291" \
-d '{
"amount": 50.00,
"userId": "user_123456",
"network": "TRC20"
}'
Success Response 200 OK
{
"sessionId": "648f3a8b1c2d3e4f5a6b7c8d",
"paymentUrl": "https://paycow.net/payment/648f3a8b1c2d3e4f5a6b7c8d",
"paymentReference": "a1b2c3d4e5f6a7b8",
"amountRequested": 50.00,
"payableAmount": 50.0001,
"amount": 50.0001,
"depositAddress": "TJY5Pj6xWkP7mNQ7w9u8z1v2y3x4w5e6r7",
"addressSerialNo": "WAL-1002",
"expiresAt": "2026-03-12T22:30:00.000Z"
}
5 Payment Lifecycle
Deterministic state transitions during a payment session
- PENDING Session active. Awaiting customer deposit on-chain.
- DETECTED Deposit transaction detected in blockchain mempool.
- CONFIRMING Accumulating block confirmations (12 for ERC-20, solidified for TRC-20).
- PAID Confirmed and settled directly into merchant wallet. Fulfill order.
- EXPIRED 30-minute window elapsed without a valid deposit.
- REJECTED Rejected by merchant operator or invalidated on-chain.
PENDING so blockchain listener daemons continue tracking incoming deposits without loss. See Awake Mode Documentation.
6 Polling & Status Queries
Pull architecture -- query payment status on demand
curl https://paycow.net/payment/data/648f3a8b1c2d3e4f5a6b7c8d
Response 200 OK
{
"_id": "648f3a8b1c2d3e4f5a6b7c8d",
"userId": "user_123456",
"baseAmount": 50.00,
"payableAmount": 50.0001,
"amountRequested": 50.00,
"amountReceived": 50.0001,
"currencyInfo": "USDT",
"depositNetwork": "TRC20",
"status": "PAID",
"confirmations": 20,
"txHash": "9e1c7f4a2b3d8e5f...",
"isAwake": false,
"awakeExpiresAt": null
}
x-api-key and x-api-secret. Use from backend cron jobs or verification services.
curl -X GET https://paycow.net/api/payment/status/648f3a8b1c2d3e4f5a6b7c8d \ -H "x-api-key: YOUR_API_KEY" \ -H "x-api-secret: YOUR_API_SECRET"
Underpayment & Dispute Resolution ("Awake Mode")
Automated 72-hour session extension and merchant alerting for fractional shortfalls
A frequent pain point in non-custodial crypto payments is customer underpayment caused by centralized exchange (CEX) withdrawal fee deductions (e.g. Binance, OKX, Bybit deducting 0.30 - 0.40 USDT or 1 USDT directly from the principal withdrawal) or minor fractional discrepancies (e.g. 0.0003 USDT short). In standard gateways, the session expires in 15-30 minutes, risking lost customer funds and merchant overhead.
PayCow solves this natively through the 72-Hour "Awake Mode" Engine. When a customer or checkout widget reports a shortfall:
- 72-Hour Lifespan Extension: The payment session expiration (
expiresAt) is extended by 3 full days (72 hours). - On-Chain Worker Reactivation: If the session had previously timed out or entered
EXPIRED/REJECTEDstate, it is immediately revived back toPENDINGso TRC-20 and ERC-20 listener daemons keep polling for incoming blockchain deposits. - Immunity to Auto-Expiry Cron: The background auto-expiry worker ignores the session throughout its 72-hour window.
- Automated Merchant SMTP Alert: A comprehensive notification email is dispatched in English directly to the merchant's registered inbox with shortfall breakdown, blockchain explorer links, and customer proof.
multipart/form-data (with file upload) or standard JSON. To protect merchants from fraudulent claims, PayCow performs strict on-chain validation of the transaction hash and sender address across Ethereum (ERC-20) and TRON (TRC-20) networks before accepting the dispute.
Request Parameters
| Parameter | Type | Description |
|---|---|---|
| sessionId required* | string | Payment session MongoDB ID (or provide paymentReference) |
| paymentReference required* | string | PayCow unique payment reference (or provide sessionId) |
| txHash required | string | Broadcasted blockchain transaction hash (TxID). Verified directly on-chain against the invoice deposit address |
| senderAddress required | string | Sender wallet or exchange account address from which the USDT transfer originated |
| amountPaid required | number | Transferred amount in USDT (cross-verified against on-chain transaction logs) |
| reason required | string | Detailed customer explanation of the shortfall (minimum 10 characters) |
| issueType optional | string | Category: exchange_fee_deduction, network_fee_shortfall, fractional_underpayment, wrong_amount_sent, or other (default: exchange_fee_deduction) |
| contactInfo optional | string | Customer email address or Telegram handle for merchant resolution |
| screenshot optional | file | Receipt screenshot or transfer proof (JPEG, PNG, WEBP, or PDF; max 10MB) |
cURL Example (Form Data with Screenshot)
curl -X POST https://paycow.net/api/dispute/submit \ -F "sessionId=648f3a8b1c2d3e4f5a6b7c8d" \ -F "txHash=0xf6f1e72ae1c1a05b140ea54923eebc28e3bca0438b7f289b34478782ef1468b2" \ -F "senderAddress=0xBdb3ba9ffe392549E1f8658DD2630c141fDF47B6" \ -F "amountPaid=49.65" \ -F "issueType=exchange_fee_deduction" \ -F "[email protected]" \ -F "reason=Exchange deducted withdrawal fee from transfer. Please verify on-chain TxID." \ -F "screenshot=@/path/to/withdrawal_proof.png"
Success Response 201 Created
{
"success": true,
"msg": "Dispute verified on-chain! 72-Hour Awake Mode is now active, and the merchant has been notified via email.",
"isAwake": true,
"awakeExpiresAt": "2026-03-15T22:30:00.000Z",
"verifiedOnChain": true,
"txHash": "0xf6f1e72ae1c1a05b140ea54923eebc28e3bca0438b7f289b34478782ef1468b2",
"network": "ERC20",
"onChainAmount": 49.65,
"onChainSender": "0xBdb3ba9ffe392549E1f8658DD2630c141fDF47B6",
"shortfallAmount": 0.35,
"emailDispatched": true
}
7 Webhooks & Real-Time Notifications
Push architecture -- receive instant HTTP POST notifications on state changes
Setting Up Webhooks
- Log into your PayCow Dashboard (
/dashboard). - Navigate to Developer → Webhooks.
- Enter your HTTPS webhook URL and click Save Changes.
- Copy your HMAC-SHA256 Signing Secret (
whsec_...) to your.env. - Click Send Test Webhook to verify integration.
| Endpoint | Method | Description |
|---|---|---|
| /api/operator/webhook | GET | Fetch current webhook URL and status |
| /api/operator/webhook | POST | Configure webhook URL and return signing secret |
| /api/operator/webhook/rotate-secret | POST | Rotate signing secret |
| /api/operator/webhook/test | POST | Trigger webhook.test event |
| /api/operator/webhook/events | GET | Get delivery logs and attempt history |
8 Event Types & Payload
Webhook events fired on on-chain state transitions
| Event Type | Transition | Description |
|---|---|---|
payment.detected | PENDING → DETECTED | Deposit observed in mempool |
payment.confirming | DETECTED → CONFIRMING | Accumulating block confirmations |
payment.paid | CONFIRMING → PAID | Fulfill order or credit balance |
payment.expired | PENDING → EXPIRED | 30-min window elapsed |
payment.rejected | ANY → REJECTED | Manually rejected or invalidated |
webhook.test | N/A | Test ping from dashboard |
Standard Payload
{
"id": "evt_01k4xyz123abc987",
"type": "payment.paid",
"apiVersion": "2026-09-01",
"createdAt": "2026-09-09T12:45:31.124Z",
"data": {
"sessionId": "648f3a8b1c2d3e4f5a6b7c8d",
"paymentReference": "a1b2c3d4e5f6a7b8",
"userId": "order_84920",
"status": "PAID",
"amountRequested": 50,
"amountReceived": 50.0001,
"currency": "USDT",
"network": "TRC20",
"depositAddress": "TJY5TestDepositAddress12345",
"txHash": "9e1c7f4a5b6c7d8e...",
"confirmations": 20
}
}
9 Signature Verification
Verify webhook authenticity with HMAC-SHA256
| Header | Description |
|---|---|
PayCow-Event-Id | Unique event ID, identical across retries |
PayCow-Timestamp | Unix timestamp (seconds) for replay prevention |
PayCow-Signature | v1=<hex_hash> -- HMAC-SHA256 over timestamp.rawBody |
Verification Implementation
import crypto from 'crypto';
function verifyPayCowWebhook({ rawBody, timestamp, signature, secret, toleranceSeconds = 300 }) {
if (!rawBody || !timestamp || !signature || !secret) return false;
const now = Math.floor(Date.now() / 1000);
if (isNaN(Number(timestamp)) || Math.abs(now - Number(timestamp)) > toleranceSeconds) return false;
const expected = crypto.createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`).digest('hex');
const received = signature.replace('v1=', '');
if (expected.length !== received.length) return false;
return crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(received, 'hex'));
}
import hmac, hashlib, time
def verify_paycow_webhook(raw_body: bytes, timestamp: str, signature: str, secret: str, tolerance=300) -> bool:
try:
if abs(int(time.time()) - int(timestamp)) > tolerance:
return False
signed = f"{timestamp}.".encode() + raw_body
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature.replace("v1=", ""))
except Exception:
return False
<?php
function verifyPayCowWebhook($rawBody, $timestamp, $signature, $secret, $tolerance = 300) {
if (abs(time() - intval($timestamp)) > $tolerance) return false;
$expected = hash_hmac('sha256', $timestamp . '.' . $rawBody, $secret);
return hash_equals($expected, str_replace('v1=', '', $signature));
}
10 Retries & Security
At-least-once delivery, exponential backoff, and SSRF protection
Retry Schedule
| Attempt | Delay | Description |
|---|---|---|
| 1 | Immediate | Initial dispatch |
| 2 | +30s | 1st retry |
| 3 | +2 min | 2nd retry |
| 4 | +10 min | 3rd retry |
| 5 | +30 min | 4th retry |
| 6 | +2 hours | 5th retry |
| 7 | +6 hours | 6th retry |
| 8 | +24 hours | Final retry |
SSRF Protection
- HTTPS Only -- HTTP rejected in production
- Private IP Blocked --
10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.0/8 - DNS Pinning -- No redirects (301/302) followed
11 Best Practices
Architecture recommendations for production integrations
- Webhooks as Primary, Polling as Backup: Process orders via webhooks; run a cron job with
GET /api/payment/status/:idas fallback. - Store Credentials Securely: Keep
x-api-secretand webhook secrets in server-side environment variables only. - Always Pass Idempotency-Key: Use a unique order UUID in every
POST /api/payment/createrequest.
12 Code Examples
Create payment sessions in your preferred language
const response = await fetch('https://paycow.net/api/payment/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': process.env.PAYCOW_API_KEY,
'x-api-secret': process.env.PAYCOW_API_SECRET,
'Idempotency-Key': `order_${order.id}`,
},
body: JSON.stringify({
amount: 50.00,
userId: 'user_123456',
network: 'TRC20',
}),
});
const data = await response.json();
res.redirect(data.paymentUrl);
import requests, os
response = requests.post("https://paycow.net/api/payment/create",
json={"amount": 50.00, "userId": "user_123456", "network": "TRC20"},
headers={
"Content-Type": "application/json",
"x-api-key": os.getenv("PAYCOW_API_KEY"),
"x-api-secret": os.getenv("PAYCOW_API_SECRET"),
"Idempotency-Key": "order_847291"
})
payment_url = response.json().get("paymentUrl")
data := map[string]interface{}{
"amount": 50.00, "userId": "user_123456", "network": "TRC20",
}
jsonData, _ := json.Marshal(data)
req, _ := http.NewRequest("POST", "https://paycow.net/api/payment/create",
bytes.NewBuffer(jsonData))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-api-key", os.Getenv("PAYCOW_API_KEY"))
req.Header.Set("x-api-secret", os.Getenv("PAYCOW_API_SECRET"))
resp, _ := (&http.Client{}).Do(req)
$ch = curl_init('https://paycow.net/api/payment/create');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'amount' => 50.00, 'userId' => $user_id, 'network' => 'TRC20'
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'x-api-key: ' . getenv('PAYCOW_API_KEY'),
'x-api-secret: ' . getenv('PAYCOW_API_SECRET'),
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = json_decode(curl_exec($ch), true);
header('Location: ' . $result['paymentUrl']);
var client = new HttpClient();
var data = new { amount = 50.00, userId = "user_123456", network = "TRC20" };
var content = new StringContent(JsonSerializer.Serialize(data), Encoding.UTF8, "application/json");
client.DefaultRequestHeaders.Add("x-api-key", Environment.GetEnvironmentVariable("PAYCOW_API_KEY"));
client.DefaultRequestHeaders.Add("x-api-secret", Environment.GetEnvironmentVariable("PAYCOW_API_SECRET"));
var response = await client.PostAsync("https://paycow.net/api/payment/create", content);
let client = reqwest::Client::new();
let mut headers = HeaderMap::new();
headers.insert("x-api-key", HeaderValue::from_str(&std::env::var("PAYCOW_API_KEY")?)?);
headers.insert("x-api-secret", HeaderValue::from_str(&std::env::var("PAYCOW_API_SECRET")?)?);
let res = client.post("https://paycow.net/api/payment/create")
.headers(headers)
.json(&json!({"amount": 50.0, "userId": "user_1", "network": "TRC20"}))
.send().await?.json::<serde_json::Value>().await?;
println!("URL: {}", res["paymentUrl"]);
WooCommerce Payment Gateway
Official Non-Custodial USDT Gateway Plugin for WordPress & WooCommerce
Accept direct, non-custodial USDT (TRC-20 & ERC-20) payments on your WooCommerce store with 0% gateway commission. Customer transfers settle directly to your self-custody wallet addresses with real-time on-chain confirmation.
paycow-crypto-payment-gateway.zip
WordPress plugin archive for WooCommerce stores. HPOS and Checkout Blocks compatible.
Installation & Setup Guide
- Download & Upload: Download
paycow-crypto-payment-gateway.zipand upload it in your WordPress admin via Plugins → Add New → Upload Plugin, then click Activate. - Configure Credentials: Navigate to WooCommerce → Settings → Payments → PayCow Non-Custodial Crypto Gateway. Enter your PayCow Merchant API Key and API Secret from your dashboard (Developer → API Keys).
- Setup Webhooks: Copy your store's unique Webhook Delivery URL shown at the bottom of the PayCow settings screen (e.g.
https://example.com/?wc-api=paycow_webhook), paste it into your PayCow Dashboard under Developer → Webhooks, and save your generated HMAC-SHA256 Signing Secret (whsec_...) back into WooCommerce.
| Setting | Required | Description |
|---|---|---|
| Enable/Disable | Yes | Toggle gateway visibility at customer checkout |
| Merchant API Key | Yes | Public API Key (x-api-key header) |
| Merchant API Secret | Yes | Private API Secret (x-api-secret header) |
| Webhook Signing Secret | Yes | HMAC-SHA256 secret (whsec_...) for cryptographic event verification |
| Supported Network Mode | Yes | Customer Choice (TRC-20 / ERC-20), TRC-20 Only, or ERC-20 Only |
| Specific Wallet Serial | Optional | Target specific wallet serial ID (e.g. WAL-1001) or leave empty for primary wallet |
| Debug Logging | Optional | Writes detailed transaction and webhook events to WooCommerce Logs |
WHMCS Payment Gateway
Official Non-Custodial USDT Gateway Module for WHMCS Web Hosts & SaaS Providers
Accept direct, non-custodial USDT (TRC20 & ERC20) payments in WHMCS. Payments settle directly on-chain into your merchant vault address with automatic invoice reconciliation powered by the official PayCow PHP SDK.
paycow-whmcs-payment-gateway.zip
Official WHMCS gateway module with embedded standalone PayCow PHP SDK.
Quick Setup
- Extract Files: Unpack
paycow-whmcs-payment-gateway.zipdirectly into your root WHMCS directory. Module files will automatically populatemodules/gateways/paycow.php,modules/gateways/callback/paycow.php, andmodules/gateways/paycow/sdk/. - Activate in WHMCS: Navigate to Configuration → System Settings → Payment Gateways → All Payment Gateways, locate PayCow Non-Custodial Crypto, and click Activate.
- Configure API Keys: Enter your API Key, API Secret, and Webhook Secret from your PayCow dashboard.
- Set Webhook URL: Copy the callback URL displayed in the gateway instructions (
https://example.com/modules/gateways/callback/paycow.php) and save it in your PayCow Dashboard under Webhook Settings.
Official PHP SDK
Official Zero-Dependency PHP Client for PayCow Non-Custodial Infrastructure
Integrate PayCow directly into custom PHP applications, web frameworks (Laravel, Symfony), and billing systems with paycow/paycow-php-sdk. Features zero external dependencies, pure decimal string arithmetic to eliminate rounding drift, and timing-safe HMAC-SHA256 signature verification.
paycow/paycow-php-sdk
Official client library with dual Composer and standalone PSR-4 autoloader distribution.
Installation Options
Option 1: Composer
Option 2: Standalone PSR-4 Autoloader