Merchant API
How service providers register, manage, and receive payments through ItPay.
The Merchant API is the set of operations a service provider (seller) uses to onboard onto ItPay, publish their ServiceManifest, manage pricing and channels, receive webhook notifications for completed payments, and reconcile transactions.
Onboarding Flow
Every merchant follows the same onboarding lifecycle:
┌──────────────────────┐
│ 1. Create Account │ → itpay.ai/dashboard or API
│ & Generate API Key │
└──────────┬───────────┘
│
┌──────────▼───────────┐
│ 2. Register │ → POST /v1/services
│ ServiceManifest │
└──────────┬───────────┘
│
┌──────────▼───────────┐
│ 3. Configure │ → accepted_channels, pricing,
│ Channels & Pricing │ settlement_currency, webhook URL
└──────────┬───────────┘
│
┌──────────▼───────────┐
│ 4. Activate │ → PATCH /v1/services/{'{'}id{'}'}/activate
│ & Go Live │
└──────────┬───────────┘
│
┌──────────▼───────────┐
│ 5. Receive Payments │ → Webhook notifications
│ via Webhooks │ + Dashboard analytics
└──────────────────────┘
1. Merchant Account & API Keys
Creating an Account
Register at itpay.ai. After verification, you receive:
| Credential | Format | Purpose |
|---|---|---|
| Merchant ID | m_XXXXXXXX | Identifies your account in API calls |
| Live API Key | sk_liv_XXXXXXXXXXXXXXXX | Production requests |
| Test API Key | sk_test_XXXXXXXXXXXXXXXX | Sandbox testing |
| Webhook Secret | whsec_XXXXXXXXXXXXXXXX | Verifies webhook payloads |
API Key Security
┌─────────────────────────┐
│ API Keys │
│ │
│ ┌─────────────────────┐│
│ │ sk_liv_... ││ ← Full access (production)
│ └─────────────────────┘│
│ │ sk_test_... ││ ← Sandbox only
│ └─────────────────────┘│
│ │ whsec_... ││ ← Webhook verification only
│ └─────────────────────┘│
│ │
│ Actions: create / │
│ revoke / rotate │
│ IP whitelist: ✓ │
└─────────────────────────┘
- Rotate API keys periodically via the dashboard
- Set IP whitelists per key for additional security
- Use separate keys for development and production
Authentication
All API requests include the API key in the Authorization header:
Authorization: Bearer sk_liv_XXXXXXXXXXXXXXXX
2. ServiceManifest Management
Creating a Manifest
POST /v1/services
Authorization: Bearer sk_liv_XXXXXXXXXXXXXXXX
{
"name": "AI Document Summary",
"description": "Summarize any PDF or text in seconds.",
"payment_methods": {
"one_time": true,
"cumulative": false,
"subscription": true
},
"pricing": {
"one_time": [
{ "amount": 99, "currency": "USD", "label": "per summary" }
],
"subscription": [
{
"plan_id": "plan_starter",
"name": "Starter",
"amount": 999,
"currency": "USD",
"interval": "monthly",
"features": ["50 summaries/mo"]
}
]
},
"accepted_channels": ["alipay", "wechat", "promptpay"],
"qr_mode": "dynamic",
"settlement_currency": "USD",
"endpoint": "https://api.myservice.ai/itpay/webhook",
"tags": ["summarization", "ai", "document"]
}
Response: HTTP 201 with the full ServiceManifest object, including the generated id (UUIDv7).
Reading Manifests
# List all manifests
GET /v1/services
Authorization: Bearer sk_liv_XXXXXXXXXXXXXXXX
# Get a specific manifest
GET /v1/services/{'{'}id{'}'}
Authorization: Bearer sk_liv_XXXXXXXXXXXXXXXX
Updating a Manifest
Re-POST to the same name or id with updated fields:
POST /v1/services
Authorization: Bearer sk_liv_XXXXXXXXXXXXXXXX
{
"id": "01J7XYKZ1A2B3C4D5E6F7G8H9I",
"name": "AI Document Summary",
"pricing": {
"one_time": [
{ "amount": 129, "currency": "USD", "label": "per summary" }
]
}
}
Only the fields you include are updated. Omitted fields retain their current values.
Manifest Lifecycle
┌──────────┐
│ draft │ ← Created but not visible in discovery
└────┬─────┘
│ activate
▼
┌──────────┐
│ active │ ← Visible in search, MCP tools/list
└────┬─────┘
│ pause
▼
┌──────────┐
│ paused │ ← Hidden from discovery, active subs continue
└────┬─────┘
│ deprecate
▼
┌─────────────┐
│ deprecated │ ← Still visible, labelled "deprecated"
└──────┬──────┘
│ delete
▼
┌──────────┐
│ deleted │ ← Permanently removed
└──────────┘
| Action | Endpoint | Description |
|---|---|---|
| Activate | PATCH /v1/services/{'{'}id{'}'}/activate | Make manifest visible in discovery |
| Pause | PATCH /v1/services/{'{'}id{'}'}/pause | Temporarily hide from discovery |
| Resume | PATCH /v1/services/{'{'}id{'}'}/resume | Restore visibility |
| Deprecate | PATCH /v1/services/{'{'}id{'}'}/deprecate | Mark as deprecated |
| Delete | DELETE /v1/services/{'{'}id{'}'} | Permanently remove |
3. Webhook Configuration
The endpoint URL in your ServiceManifest is where ItPay delivers all payment events. Each webhook is a signed HTTP POST with an HMAC-SHA256 signature.
Webhook Event Types
| Event Type | Description | Trigger |
|---|---|---|
payment_intent.succeeded | A payment completed successfully | Deliver your service |
payment_intent.failed | A payment attempt failed | Notify the user |
payment_intent.expired | QR code expired before payment | Ask user to retry |
subscription.renewed | A subscription auto-renewed | Extend access |
subscription.cancelled | A subscription was cancelled | Revoke access |
refund.succeeded | A refund was processed | Update your records |
refund.failed | A refund failed | Investigate and retry |
revocation.succeeded | Access was revoked | Confirm access removal |
Webhook Payload Format
{
"id": "evt_01J7XZ1A2B3C4D5E6F7G8H9IK",
"type": "payment_intent.succeeded",
"created": "2026-05-27T09:30:00Z",
"data": {
"id": "pi_01J7XZ1A2B3C4D5E6F7G8H9IK",
"amount": { "value": 699, "currency": "CNY" },
"settlement": { "value": 99, "currency": "USD", "rate": 0.1416 },
"channel": "alipay",
"payer": {
"agent_id": "agent_cli_a1b2c3d4",
"human_id": "user_abc_789"
},
"metadata": {
"session_id": "sess_xyz_456"
}
}
}
Webhook Retry Schedule
If your endpoint doesn't return 200 OK within 5 seconds, ItPay retries:
| Attempt | Delay |
|---|---|
| 1st | 10 seconds |
| 2nd | 1 minute |
| 3rd | 10 minutes |
| 4th | 1 hour |
| 5th | 6 hours |
| 6th | 24 hours (final) |
After the 6th failure, the webhook is dropped permanently. Monitor for dropped events via the dashboard.
Best Practices
- Verify signatures — Always validate the
X-ItPay-Signatureheader before acting on a payload - Respond 200 quickly — Acknowledge receipt within 5s, process asynchronously
- Idempotent handlers — Check
event.idbefore processing; webhooks may be delivered more than once - Log request_id — Include
request_idin all logs for support traceability
See the Security Model page for signature verification details and Error Codes for webhook error handling.
4. Transaction Management
Viewing Transactions
# List all payment intents
GET /v1/payment-intents?limit=20&offset=0
Authorization: Bearer sk_liv_XXXXXXXXXXXXXXXX
# Filter by status
GET /v1/payment-intents?status=succeeded&created_after=2026-05-01
# Filter by channel
GET /v1/payment-intents?channel=alipay
Reconciliation
For each completed payment, the webhook payload includes both the payer amount (what the user paid, in their local currency) and the settlement amount (what you receive, converted to your settlement currency):
{
"amount": { "value": 699, "currency": "CNY" },
"settlement": { "value": 99, "currency": "USD", "rate": 0.1416 }
}
Reconciliation reports are available via:
GET /v1/reconciliation?date=2026-05-27
Authorization: Bearer sk_liv_XXXXXXXXXXXXXXXX
Returns a summary of all transactions settled on the given date, including fees, FX rates, and payout amounts.
5. Dashboard
The merchant dashboard at itpay.ai/dashboard provides:
┌──────────────────────────────────────────┐
│ 📊 Dashboard │
│ │
│ Revenue Today Active Channels │
│ $124.50 3 / 6 │
│ ┌────────────┐ ┌──────────────────┐ │
│ │ ██████████ │ │ Alipay ¥1,420 │ │
│ │ ██████ │ │ WeChat ¥890 │ │
│ │ ████ │ │ PromptPay ฿2,100 │ │
│ └────────────┘ └──────────────────┘ │
│ │
│ Recent Transactions │
│ ┌────────────────────────────────┐ │
│ │ ¥6.99 Alipay ✓ succeeded │ │
│ │ ¥3.50 WeChat ✓ succeeded │ │
│ │ ¥12.00 PromptPay ✕ expired │ │
│ └────────────────────────────────┘ │
│ │
│ [Webhook Logs] [API Keys] [Reports] │
└──────────────────────────────────────────┘
| Dashboard Section | Description |
|---|---|
| Revenue | Real-time earnings by channel (today, this week, this month) |
| Transactions | Live feed of all payment intents with status |
| Channel Health | Per-channel success rates and error counts |
| Webhook Logs | Recent webhook delivery attempts and their HTTP status codes |
| API Key Manager | Generate, revoke, and rotate keys with IP whitelisting |
| Reconciliation | Downloadable CSV reports for accounting |
6. Channel & Settlement Configuration
Adding a Channel
Each channel requires separate credentials (managed via the Channel Adapter). From the dashboard or API:
POST /v1/channels
Authorization: Bearer sk_liv_XXXXXXXXXXXXXXXX
{
"channel": "wechat",
"credentials": {
"mchid": "1230000109",
"appid": "wx1234567890abcdef",
"api_certificate_pem": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----",
"apiv3_key": "32bytestringfortest..."
},
"notify_url": "https://api.myservice.itpay/v1/wechat-callback"
}
Settlement Preferences
Configure per-channel settlement via your merchant profile:
PATCH /v1/merchant/settlement
Authorization: Bearer sk_liv_XXXXXXXXXXXXXXXX
{
"preferred_settlement_currency": "USD",
"auto_withdrawal": true,
"withdrawal_threshold": { "value": 10000, "currency": "USD" }
}
Summary: Merchant API Operations
| Operation | Endpoint | Description |
|---|---|---|
| Register manifest | POST /v1/services | Create a new ServiceManifest |
| List manifests | GET /v1/services | List all your manifests |
| Get manifest | GET /v1/services/{'{'}id{'}'} | Get a single manifest |
| Update manifest | POST /v1/services | Update an existing manifest |
| Activate | PATCH /v1/services/{'{'}id{'}'}/activate | Go live |
| Pause / Resume | PATCH /v1/services/{'{'}id{'}'}/pause | Toggle visibility |
| Deprecate / Delete | `PATCH | DELETE /v1/services/{id}` |
| List transactions | GET /v1/payment-intents | View payment history |
| Get reconciliation | GET /v1/reconciliation | Download settlement report |
| Manage channels | POST /v1/channels | Add/configure payment channels |
| Manage API keys | Dashboard | Generate, rotate, revoke keys |
| Webhook logs | Dashboard | Monitor delivery status |
7. Payment Lifecycle (Swimlane)
The complete journey of a payment — from customer scan to merchant fulfillment — spans four participants:
Merchant ItPay Payment Channel Customer
│ │ │ │
│ 1. Create │ │ │
│ PaymentIntent │ │ │
│ ─────────────────►│ │ │
│ │ 2. Translate │ │
│ │ via Adapter │ │
│ │ ──────────────────►│ │
│ │ │ │
│ │ 3. QR Response │ │
│ │ ◄──────────────────│ │
│ │ │ │
│ 4. Return QR │ │ │
│ ◄─────────────────│ │ │
│ │ │ │
│ │ │ 5. Scan QR │
│ │ │ ◄─────────────────│
│ │ │ │
│ │ │ 6. Pay │
│ │ │ ◄─────────────────│
│ │ │ │
│ │ 7. Callback │ │
│ │ ◄──────────────────│ │
│ │ │ │
│ 8. Webhook │ │ │
│ ◄─────────────────│ │ │
│ │ │ │
│ 9. Verify HMAC │ │ │
│ ──┐ │ │ │
│ │ (async) │ │ │
│ 10. Deliver │ │ │
│ Service │ │ │
│ 11. Reconcile │ │ │
│ ─────────────────►│ │ │
Step-by-Step
| Step | Actor | Action | Details |
|---|---|---|---|
| 1 | Merchant | POST /v1/payment-intents | Creates a payment request with amount, channel, metadata |
| 2 | ItPay | Channel Adapter | Translates to channel-specific API format (WeChat RSA-SHA256 / Alipay RSA2) |
| 3 | Channel | QR Response | Returns code_url (WeChat) or qr_code (Alipay EMVCo TLV) |
| 4 | ItPay → Merchant | Normalized response | Returns { qr_code, id, expires_at } in unified format |
| 5 | Customer | Scans QR | Opens payment app (WeChat/Alipay/Bank app) |
| 6 | Customer | Authorizes payment | Confirms amount and pays within the app |
| 7 | Channel → ItPay | Callback | Sends encrypted/signed notification to ItPay's callback endpoint |
| 8 | ItPay → Merchant | Webhook | Sends payment_intent.succeeded with HMAC-SHA256 signature |
| 9 | Merchant | Verifies HMAC | Validates X-ItPay-Signature header against webhook secret |
| 10 | Merchant | Delivers service | Fulfills the order (async processing recommended) |
| 11 | Merchant | Reconciles | Matches webhook data against settlement reports |
Complete HTTP for Payment Creation
Request:
POST /v1/payment-intents
Authorization: Bearer sk_liv_abc123def456
Content-Type: application/json
Idempotency-Key: idem_01J8ABC123
{
"service_id": "01J7XYKZ1A2B3C4D5E6F7G8H9I",
"amount": { "value": 699, "currency": "CNY" },
"channel": "alipay",
"metadata": {
"session_id": "sess_xyz_456",
"user_id": "user_abc_789"
},
"expires_in": 900
}
Response (201 Created):
{
"id": "pi_01J7XZ1A2B3C4D5E6F7G8H9IK",
"status": "pending",
"amount": { "value": 699, "currency": "CNY" },
"qr_code": "00020101021229370016A000000677010112...",
"expires_at": "2026-05-27T09:45:00Z",
"created": "2026-05-27T09:30:00Z"
}
8. Webhook Event Handling Reference
For each webhook event type, the merchant should follow the recommended action:
| Event Type | Recommended Action | Idempotency Check | Notes |
|---|---|---|---|
payment_intent.succeeded | Deliver the purchased service/goods | Check event.data.id | This is the only trigger to fulfill — see swimlane above |
payment_intent.failed | Notify the user and offer retry | Log for analytics | No settlement occurred; no action on channel side needed |
payment_intent.expired | Ask the user to initiate a fresh payment | Create new PaymentIntent | QR code lifetime has elapsed; old code_url/qr_code is invalid |
subscription.renewed | Extend service access for the next billing period | Check subscription period dates | Contains current_period_end and next_billing_date |
subscription.cancelled | Schedule access revocation at period end | Update user's sub status | Service access continues until the current billing period ends |
refund.succeeded | Mark the original payment as refunded | Update transaction ledger | Original payment_intent.id is in event.data.refunded_payment_intent |
refund.failed | Investigate why refund failed, retry if possible | Check channel error details | Common causes: insufficient balance, expired refund window |
revocation.succeeded | Confirm access has been revoked | Remove service entitlements | Final confirmation in the revocation lifecycle |
Webhook Verification (Complete HTTP)
Incoming webhook request:
POST https://api.myservice.ai/itpay/webhook
Content-Type: application/json
X-ItPay-Signature: t=1715712345,v1=f47ac10b58cc4372a5f1c9c07c2f3d8a
X-ItPay-Request-Id: req_01J7XZ1A2B3C4D5E6F7G8H9IK
User-Agent: ItPay-Webhook/1.0
{
"id": "evt_01J7XZ1A2B3C4D5E6F7G8H9IK",
"type": "payment_intent.succeeded",
"created": "2026-05-27T09:30:00Z",
"data": {
"id": "pi_01J7XZ1A2B3C4D5E6F7G8H9IK",
"amount": { "value": 699, "currency": "CNY" },
"settlement": { "value": 99, "currency": "USD", "rate": 0.1416 },
"channel": "alipay"
}
}
Verification logic:
import hmac, hashlib, time
def verify_webhook(payload: bytes, signature_header: str, secret: str) -> bool:
# Signature format: "t={timestamp},v1={hex}"
parts = dict(p.split("=") for p in signature_header.split(","))
timestamp = parts["t"]
expected_sig = parts["v1"]
# Build signing payload: timestamp.payload
signed_payload = f"{timestamp}.{payload.decode('utf-8')}".encode("utf-8")
# Compute HMAC-SHA256
computed = hmac.new(
secret.encode("utf-8"),
signed_payload,
hashlib.sha256
).hexdigest()
# Constant-time comparison
return hmac.compare_digest(computed, expected_sig)
Acknowledge receipt (respond within 5s):
HTTP/1.1 200 OK
Content-Type: application/json
{ "received": true }
9. Settlement & Reconciliation Flow
After a channel settles funds to the merchant's account, ItPay generates a reconciliation report:
Channel ItPay Merchant
│ │ │
│ 1. Settle funds │ │
│ ───────────────►│ │
│ │ 2. Record │
│ │ settlement │
│ │ ──┐ │
│ │ │ │
│ │ 3. Generate │
│ │ report │
│ │ ◄─┘ │
│ │ │
│ │ 4. Recon Report │
│ │ Ready │
│ │───────────────────►│
│ │ │
│ │ 5. Webhook: │
│ │ settlement │
│ │ .completed │
│ │───────────────────►│
│ │ │
│ │ 6. GET │
│ │ /reconciliation │
│ │◄───────────────────│
│ │ │
│ │ 7. CSV/JSON │
│ │ Report │
│ │───────────────────►│
Complete HTTP for Reconciliation
Get daily reconciliation report:
GET /v1/reconciliation?date=2026-05-27&format=csv
Authorization: Bearer sk_liv_abc123def456
Response (CSV):
date,payment_intent_id,channel,payer_amount,payer_currency,settlement_amount,settlement_currency,fx_rate,fee,payout
2026-05-27,pi_01J7XZ1A2B3C4D5E6F7G8H9IK,alipay,699,CNY,99,USD,0.1416,2.97,96.03
2026-05-27,pi_01J7XZ2B3C4D5E6F7G8H9IJ,wechat,1000,CNY,141.60,USD,0.1416,4.25,137.35
Response (JSON):
GET /v1/reconciliation?date=2026-05-27&format=json
Authorization: Bearer sk_liv_abc123def456
{
"date": "2026-05-27",
"merchant_id": "m_XXXXXXXX",
"summary": {
"total_transactions": 42,
"total_payer_amount": { "value": 42000, "currency": "CNY" },
"total_settlement": { "value": 5947.20, "currency": "USD" },
"total_fees": { "value": 178.42, "currency": "USD" },
"net_payout": { "value": 5768.78, "currency": "USD" }
},
"transactions": [
{
"payment_intent_id": "pi_01J7XZ1A2B3C4D5E6F7G8H9IK",
"channel": "alipay",
"payer_amount": { "value": 699, "currency": "CNY" },
"settlement": { "value": 99, "currency": "USD", "rate": 0.1416 },
"fee": { "value": 2.97, "currency": "USD" },
"payout": { "value": 96.03, "currency": "USD" },
"settled_at": "2026-05-27T14:00:00Z"
}
]
}
10. Error Handling & Recovery
Webhook Delivery Failures
| Failure Scenario | Retry Behavior | Recommended Action |
|---|---|---|
| 5s timeout | Retry on schedule (10s → 1m → 10m → 1h → 6h → 24h) | Ensure endpoint responds quickly; process async |
| Non-200 response | Same retry schedule as timeout | Return 200 OK with { "received": true } immediately |
| Network error | Same retry schedule | Check firewall rules; ensure endpoint is publicly reachable |
| 6th failure | Webhook dropped permanently | Monitor webhook logs in dashboard; manually re-process via API |
# Fetch dropped webhooks for manual reprocessing
GET /v1/webhooks/dropped?date=2026-05-27
Authorization: Bearer sk_liv_abc123def456
# Replay a specific webhook
POST /v1/webhooks/evt_01J7XZ1A2B3C4D5E6F7G8H9IK/replay
Authorization: Bearer sk_liv_abc123def456
Invalid Credentials
| Scenario | HTTP Status | Error Code | Recovery |
|---|---|---|---|
| Missing or malformed Authorization header | 401 Unauthorized | auth_invalid | Include Authorization: Bearer <live_key> |
| API key revoked | 401 Unauthorized | auth_revoked | Generate new key in dashboard |
| API key expired | 401 Unauthorized | auth_expired | Rotate key before expiry (keys valid 365 days) |
| IP not whitelisted | 403 Forbidden | ip_not_allowed | Add caller IP to key's whitelist |
| Invalid webhook signature | 400 Bad Request | signature_mismatch | Verify webhook secret matches whsec_* in dashboard |
Channel Outage Scenarios
Merchant ItPay Channel
│ │ │
│ POST PaymentIntent │
│ ────────────────►│ │
│ │ POST to Channel │
│ │ ──────────────────►│
│ │ ❌ 503/Timeout │
│ │◄───────────────────│
│ │ │
│ │ Retry #1 (1s) │
│ │ ──────────────────►│
│ │ ❌ 503/Timeout │
│ │◄───────────────────│
│ │ │
│ │ Retry #2 (5s) │
│ │ ──────────────────►│
│ │ ❌ 503/Timeout │
│ │◄───────────────────│
│ │ │
│ 503 Service │ │
│ Unavailable │ │
│◄─────────────────│ │
│ │ │
│ Fallback: retry │ │
│ with diff channel│ │
Channel outage response:
HTTP/1.1 503 Service Unavailable
Content-Type: application/json
Retry-After: 30
{
"error": {
"code": "channel_unavailable",
"message": "Alipay is currently unavailable. Please try again later or use an alternative channel.",
"channel": "alipay",
"retry_after": 30,
"alternatives": ["wechat", "promptpay"]
}
}
11. Dashboard Operations — API Equivalents
While the dashboard provides a visual interface, all operations have API equivalents:
Viewing Transactions
# Paginated transaction feed (equivalent to dashboard table)
GET /v1/payment-intents?limit=50&offset=0&sort=created:desc
Authorization: Bearer sk_liv_abc123def456
{
"data": [
{
"id": "pi_01J7XZ1A2B3C4D5E6F7G8H9IK",
"status": "succeeded",
"amount": { "value": 699, "currency": "CNY" },
"channel": "alipay",
"created": "2026-05-27T09:30:00Z",
"payer": { "agent_id": "agent_cli_a1b2c3d4" }
}
],
"pagination": {
"total": 142,
"limit": 50,
"offset": 0,
"next_offset": 50
}
}
Generating Reports
# Download CSV report filtered by date range and channels
GET /v1/reconciliation/export?start=2026-05-01&end=2026-05-27&channels=alipay,wechat&format=csv
Authorization: Bearer sk_liv_abc123def456
Webhook Logs
# View recent webhook delivery attempts
GET /v1/webhooks/logs?limit=20&status=failed
Authorization: Bearer sk_liv_abc123def456
{
"data": [
{
"event_id": "evt_01J7XZ1A2B3C4D5E6F7G8H9IK",
"type": "payment_intent.succeeded",
"endpoint": "https://api.myservice.ai/itpay/webhook",
"attempts": 6,
"last_attempt": "2026-05-27T10:30:00Z",
"last_status_code": 504,
"status": "dropped"
}
]
}
Managing API Keys via API
# List keys
GET /v1/api-keys
Authorization: Bearer sk_liv_abc123def456
# Create new key
POST /v1/api-keys
Authorization: Bearer sk_liv_abc123def456
Content-Type: application/json
{
"label": "CI/CD Pipeline",
"permissions": ["payment_intents:read", "webhooks:read"],
"ip_whitelist": ["203.0.113.0/24"]
}
# Revoke key
DELETE /v1/api-keys/k_01J7XYKZ1A2B3C4D5E6F7G8H9I
Authorization: Bearer sk_liv_abc123def456
Next Steps
- Broadcast & Discovery — Full ServiceManifest reference
- Request Payment — Creating payment intents
- Buyer Agent Flow — How buyer agents interact with your service
- Channel Matrix — Supported channels and integration complexity