Pay at table
Split a bill, pay by item, or settle a tab from a handheld terminal at the table.
Pay at table brings a handheld terminal to the customer instead of the customer going to a fixed payment counter. It supports full bill payment, split-by-amount, split-by-item, and running-tab settlement — all driven from your POS system via the Terminal API.
Typical flowAsk
POS sends the order to VINR
When the customer requests the bill, your POS pushes the order details (items, subtotal, tax, any discounts) to VINR via vinr.terminal.orders.create. VINR stores the order and returns an orderId.
Staff selects a terminal
The server selects a free handheld from the terminal pool. Your POS calls vinr.terminal.payments.create targeting that terminal with the orderId.
Terminal displays the bill
The terminal shows a itemised bill screen. The customer reviews it. If split payment is enabled, the customer selects how to split (equal halves, by item, custom amount).
Card presented
Each party pays their share via contactless, chip+PIN, or mobile wallet. VINR creates a separate terminal_payment for each split.
Order marked as settled
Once all splits sum to the order total, VINR fires terminal_order.completed and your POS marks the table as cleared.
Create an orderAsk
const order = await vinr.terminal.orders.create({
reference: 'table_12_20260602',
locationId: 'loc_01HZ9RSTORE',
currency: 'USD',
lineItems: [
{ description: 'Grilled salmon', quantity: 2, unitAmount: 2800 },
{ description: 'House wine (glass)', quantity: 3, unitAmount: 1200 },
{ description: 'Sparkling water', quantity: 2, unitAmount: 600 },
],
tax: 1020,
serviceCharge: { rate: 0.125, label: '12.5% service charge' },
});
// order.total → 10220 (subtotal 9000 + tax 1020 + service charge 1125 = 10145 — check rounding)Create a full-bill paymentAsk
const tp = await vinr.terminal.payments.create({
terminalId: 'term_01HZ5QXYZ',
amount: order.total,
currency: 'USD',
reference: order.reference,
orderId: order.id,
tipConfig: { mode: 'percentage', percentages: [10, 12, 15] },
});Create a split paymentAsk
To split equally between N parties:
const splitPayment = await vinr.terminal.payments.create({
terminalId: 'term_01HZ5QXYZ',
orderId: order.id,
currency: 'USD',
reference: order.reference,
split: {
mode: 'equal',
parts: 3,
partyIndex: 1, // this payment covers party 1 of 3
},
});
// splitPayment.amount → Math.ceil(order.total / 3)To split by custom amount:
const splitPayment = await vinr.terminal.payments.create({
terminalId: 'term_01HZ5QXYZ',
orderId: order.id,
currency: 'USD',
reference: order.reference,
split: {
mode: 'custom',
amount: 4000, // this party pays $40.00
},
});Order and payment eventsAsk
| Event | When it fires |
|---|---|
terminal_order.created | Order registered |
terminal_order.partially_paid | At least one split payment completed; balance remains |
terminal_order.completed | All splits sum to the order total |
terminal_order.expired | Order not fully settled within the configured window (default 2 hours) |
terminal_payment.completed | Individual split payment completed |
if (event.type === 'terminal_order.completed') {
const order = event.data.object;
await pos.markTableCleared(order.reference);
}
if (event.type === 'terminal_order.partially_paid') {
const order = event.data.object;
const remaining = order.total - order.amountPaid;
console.log(`$${remaining / 100} remaining on table`);
}Last updated on