Payment Flow
This page shows Payylo’s group-payment flow: how a single purchase is split into slots and paid by multiple participants, each covering their own slot.
Overview
Payylo coordinates the split between your backend, the checkout, the card provider, and each participant. The diagrams below walk through the end-to-end process from setup to capture.
The Group Payment Lifecycle
One purchase is split into slots; each participant pays one slot. Instead of repeating the same steps for every participant, the diagram shows the three phases — setup, one participant paying (looped), and completion:
The Three Phases in Detail
Multiple participants collaboratively pay for a single purchase, one slot each:
1. Setup (server-side)
- Your backend creates a group payment with
POST /v1/group-payments, passingtotal_amount(in currency units) and adistributionthat defines the slots. - Payylo returns the
group_payment, itspayment_slots, and a shareablecheckout_url. - You share that URL with participants — or embed the flow (see Embedded Checkout).
2. Each participant pays a slot
- A participant opens the checkout, picks an available slot, and enters their card.
- Payylo pre-authorises (holds) the funds with the card provider — it does not capture yet.
- The group moves to
partially_preauthorised(some slots held) and thenpreauthorisedonce every slot is held. No money has been captured at this point — if the group expires here, the holds are simply released. - Webhooks:
payment_intent.preauthorisedfor that slot.group_payment.partially_paidis not sent for holds — it always means captured money.
3. Auto-capture & completion
- Once every slot is authorised, Payylo automatically captures all of them.
- Webhooks:
payment_intent.captured, thengroup_payment.paid— your cue to fulfil the order / confirm the booking.
Group Payment States
While collecting, no money has been captured — only held. partially_paid
appears only in the capture phase, when some captures have confirmed and
the rest are still pending or failed and retrying; it always means real
captured money, never just holds. Expiry only applies while collecting: the
holds are released and nobody is charged.
Error Handling for Group Payments
Payylo provides comprehensive error handling for group payment scenarios:
Group Payment Creation Errors
- Invalid total amount: Cart total doesn’t match sum of slot amounts
- Slot configuration: Invalid item assignments or duplicate amounts
- API validation: Missing required fields or invalid data
Individual Payment Errors
- Payment declined: Individual user’s payment method declined
- Insufficient funds: User doesn’t have enough balance
- Slot already paid: User trying to pay for an already completed slot
- Payment intent failures: PSP communication issues during payment processing
Group Completion Errors
- Session expiry: Group payment session timed out before completion
- Partial payments: Some slots paid but others remain incomplete
- Webhook delivery: Issues sending completion notifications
Best Practices for Group Payments
- Set clear session timeouts to avoid indefinite waiting
- Implement real-time updates so users see slot status changes
- Handle partial failures gracefully with retry mechanisms
- Validate slot amounts match cart totals before creation
- Test group scenarios with multiple concurrent users
Integration Examples
Creating a group payment
Amounts are in currency units (e.g. 300.00), not cents. Slots come from the
distribution — equally across N slots, or custom amounts that sum to the
total.
curl https://api.payylo.com/v1/group-payments \
-H "Authorization: Bearer $PAYYLO_SECRET_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"group_name": "Office gear",
"total_amount": 300.00,
"currency": "EUR",
"expires_at": "2026-06-22T18:00:00Z",
"distribution": {
"type": "custom",
"slots": [
{ "amount": 250.00, "metadata": { "item": "Laptop" } },
{ "amount": 30.00, "metadata": { "item": "Wireless Mouse" } },
{ "amount": 20.00, "metadata": { "item": "Mechanical Keyboard" } }
]
}
}'Webhook handler for group payments
Verify the signature first (see Webhooks), then switch on the real event types:
app.post(
'/webhooks/payylo',
express.raw({ type: 'application/json' }),
(req, res) => {
// Verify req.header('Payylo-Signature') against the raw body — see /webhooks.
const event = JSON.parse(req.body.toString('utf8'));
switch (event.type) {
case 'group_payment.partially_paid':
// Some money has actually been CAPTURED (not just held) — e.g. while
// captures confirm one by one, or after a partial capture failure.
console.log('Partially paid:', event.data.group_payment_id);
break;
case 'group_payment.paid':
// All slots paid — fulfil the order / confirm the booking here.
console.log('Group payment paid:', event.data.group_payment_id);
break;
case 'group_payment.refunded':
case 'group_payment.partially_refunded':
console.log('Refund event:', event.type, event.data.group_payment_id);
break;
case 'payment_intent.failed':
console.log('A payment intent failed:', event.data);
break;
}
res.json({ received: true });
}
);Next Steps
- Authentication - Set up secure API access
- Quick Start - Create your first group payment
- Webhooks - Handle real-time notifications
- API Reference - Interactive Swagger UI with every endpoint (try requests live)