Payment Flows
This guide covers all payment flows supported by 4pay.online: direct card payments, hosted payments, payouts, refunds, and cascade routing.
Transaction Lifecycle
Every transaction follows this state machine:
created → new → authorizing → charging → charged ✅
│ → failed ❌
└► action_required → rejected ❌
charged → reversed (a settled transaction was returned)
| Status | Final | Description |
|---|---|---|
created | no | Transaction initialized |
new | no | Accepted, routing and validation in progress |
authorizing | no | Being authorised by the provider |
action_required | no | Waiting for the payer (3-D Secure, OTP, signature) |
charging | no | Being processed by the provider |
charged | yes | Payment confirmed, funds received |
failed | yes | Provider error, or cancelled before settlement |
rejected | yes | Validation, routing, or compliance check failed |
reversed | yes | A settled transaction was returned |
refunded | no | Refund completed — finality is rail-dependent |
Act on final statuses only, and treat any status you do not recognise as
non-final: the list is not closed, and rails carry their own intermediate states
(cancelled, payment_suspended and others). The complete model, including
what each final outcome means for the money, is in the
protocol specification.
Direct Card Payment
For PCI DSS certified partners who handle card data directly.
Partner API 4pay.online Provider
│ │ │
├─── POST /transactions ───────►│ │
│ (card data included) │── route + validate ────────►│
│ │ │
│ │◄── 3DS required ───────────┤
│◄── status: action_required ───┤ │
│ │ │
├─── POST /additional_params ──►│── 3DS verification ────────►│
│ (OTP or 3DS data) │ │
│ │◄── charged ────────────────┤
│◄── webhook: charged ──────────┤ │
Create Payment
A payment needs all five card fields — cardnumber, cardholder, exp_month, exp_year,
cvv — and both redirect URLs. A payout needs only cardnumber, and no redirect URLs.
Passing card data through your own server puts you in PCI DSS scope. Unless you hold an attestation, use the hosted flow below.
curl -X POST https://4pay.online/api/v1/transactions \
-H "x-api-key: YOUR_API_KEY" \
-H "x-organization-id: 00000000-1111-2222-3333-444444444444" \
-H "Content-Type: application/json" \
-d '{
"params": {
"txid": "order_123",
"type": "payment",
"amount": "10000",
"currency": "USD",
"env": "prod",
"money_storage": {
"type": "bank_card",
"cardnumber": "4111111111111111",
"cardholder": "IVAN PETROV",
"exp_month": "12",
"exp_year": "2027",
"cvv": "123"
},
"description": "Order #123",
"returnUrl": "https://your-site.com/result",
"failUrl": "https://your-site.com/failed"
}
}'
Handle 3DS Authentication
When the status becomes action_required, the action itself is in
auth_params — its type tells you what to do (redirect or
auth_3ds_form), and url, method and params tell you where to send the
customer. There is no action_required boolean and no auth_url field.
After verification, submit the authentication data:
curl -X POST https://4pay.online/api/v1/transactions/additional_params_for_auth/TX_ID \
-H "x-api-key: YOUR_API_KEY" \
-H "x-organization-id: 00000000-1111-2222-3333-444444444444" \
-H "Content-Type: application/json" \
-d '{"params": {"otp": "123456"}}'
Hosted Payment
For partners without PCI DSS certification. The customer enters card details on a 4pay.online-hosted page.
Partner API 4pay.online Customer
│ │ │
├─── POST /transactions ───────►│ │
│ (NO card data) │ │
│◄── widget_url ────────────────┤ │
│ │ │
│ redirect customer ─────────┼─────────────────────────────►
│ │◄── card data ──────────────┤
│ │── process payment ──► │
│◄── webhook: charged ──────────┤ │
Create Hosted Payment
curl -X POST https://4pay.online/api/v1/transactions \
-H "x-api-key: YOUR_API_KEY" \
-H "x-organization-id: 00000000-1111-2222-3333-444444444444" \
-H "Content-Type: application/json" \
-d '{
"params": {
"txid": "order_456",
"type": "payment",
"amount": "5000",
"currency": "EUR",
"env": "prod",
"hosted": true,
"description": "Order #456",
"returnUrl": "https://your-site.com/result",
"failUrl": "https://your-site.com/failed"
}
}'
Response includes widget_url — redirect the customer there. After payment, the customer returns to returnUrl and you receive a webhook.
Payout
Send funds from your account to a recipient (card, bank account, wallet).
curl -X POST https://4pay.online/api/v1/transactions \
-H "x-api-key: YOUR_API_KEY" \
-H "x-organization-id: 00000000-1111-2222-3333-444444444444" \
-H "Content-Type: application/json" \
-d '{
"params": {
"txid": "payout_789",
"type": "payout",
"amount": "25000",
"currency": "USD",
"env": "prod",
"money_storage": {
"type": "bank_card",
"cardnumber": "4111111111111111"
},
"description": "Withdrawal request #789"
}
}'
Payout follows the same lifecycle: created → new → charging → charged/failed.
Refund
Issue a full or partial refund on a charged transaction.
Full Refund
curl -X PUT https://4pay.online/api/v1/transactions/TX_ID \
-H "x-api-key: YOUR_API_KEY" \
-H "x-organization-id: 00000000-1111-2222-3333-444444444444" \
-H "Content-Type: application/json" \
-d '{"params": {"type": "refund"}}'
Partial Refund
curl -X PUT https://4pay.online/api/v1/transactions/TX_ID \
-H "x-api-key: YOUR_API_KEY" \
-H "x-organization-id: 00000000-1111-2222-3333-444444444444" \
-H "Content-Type: application/json" \
-d '{"params": {"type": "refund", "amount": "5000"}}'
The outcome lands on the transaction as refund_id and refund_msg. A refund
does not rewrite the original status: a fully returned settlement becomes
reversed.
Two-Phase Payment (Authorize + Capture)
Some providers support authorization (hold) followed by capture (charge). The platform holds funds first, then captures when you confirm.
authorize → charging (funds held) → commit → charged
To manually commit an authorized transaction:
curl -X POST https://4pay.online/api/v1/transactions/commit/TX_ID \
-H "x-api-key: YOUR_API_KEY" \
-H "x-organization-id: 00000000-1111-2222-3333-444444444444" \
-H "Content-Type: application/json" \
-d '{"params": {"money_storage_meta": {}}}'
Until you commit, the money is held and not settled. If the authorisation expires the hold is released and the transaction fails — a hold is never captured for more than it reserved.
Cascade Routing
When a provider fails, the platform automatically retries with the next available provider:
Provider A (priority 0) → failed → rollback hold
↓
Provider B (priority 1) → failed → rollback hold
↓
Provider C (priority 2) → charged ✅
Cascade is transparent to the partner. You always receive the final result via webhook. Configure routing priorities in the Admin Console under Terminals & Routing.
Amount Fields
Every transaction response includes three amount fields:
| Field | Description | Example |
|---|---|---|
amount | Original requested amount | "10000" |
amount_with_fee | Amount including all fees | "10500" |
amount_dest | Amount received by destination | "9500" |
For payments: customer pays amount, merchant receives amount_dest.
For payouts: merchant sends amount, recipient receives amount_dest.
All amounts are decimal strings of minor units (cents, kopecks). For example, "10000" = 100.00 USD. They are strings because assets with eighteen decimals do not fit a JSON number safely.
Webhook Notifications
You receive webhooks at each status change. See Webhooks for setup details.
Example webhook payload:
{
"event_id": "4d9d0e6c-1d0a-4a2e-9a2f-0c9a1b0e8f21",
"timestamp": 1772668800,
"type": "transaction.charged",
"data": {
"type": "payment",
"txid": "order_123",
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "charged",
"amount": "10000",
"amount_dest": "9500",
"amount_with_fee": "10500"
}
}
Deduplicate on event_id, not on the transaction id — one transaction
produces several events over its life, and the platform may send the same event
more than once.
Recovery & Resilience
- Transaction state is persisted at every processing stage
- If a node crashes mid-flow, recovery automatically resumes from the last checkpoint
- No manual intervention required — the platform guarantees at-least-once delivery
Next Steps
- Code Examples — Python, Node.js, PHP implementations
- Webhooks — Webhook setup and signature verification
- Error Reference — Error codes and troubleshooting
- Sandbox & Testing — Test cards and sandbox environment