Click & collect
Accept payment online at checkout, then let the customer collect in store — with optional in-store upsell.
Click & collect (Buy Online, Pick Up In Store — BOPIS) lets customers pay at your online checkout and collect their order at a physical location. VINR handles both legs: the online authorization is held until a staff member confirms the customer has collected, at which point the funds are captured. If the customer wants to add an item while they are in store, VINR can charge the same shopper token without them presenting a card again.
Online paymentAsk
Create the payment exactly as you would for any online order, with two additions: tag the fulfillment type in metadata and set captureMethod: 'manual' so funds are reserved but not moved until the customer collects.
import { Vinr } from '@vinr/sdk';
const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });
const payment = await vinr.payments.create({
amount: 7500,
currency: 'EUR',
captureMethod: 'manual',
description: 'Order #8821 — click & collect',
returnUrl: 'https://yourstore.com/orders/8821/confirmation',
shopperReference: 'cust_abc123',
metadata: {
orderId: '8821',
fulfillment: 'click_and_collect',
pickupLocationId: 'loc_london_oxford_st',
pickupWindowStart: '2026-06-01T10:00:00Z',
pickupWindowEnd: '2026-06-01T18:00:00Z',
},
});After the shopper completes the hosted checkout, the payment moves to authorized and VINR emits a payment.authorized webhook. Use that event to trigger your stock-reservation logic and queue the collection-ready notification — do not poll the payments API.
shopperReference is optional at authorization time, but supplying it stores the payment method against the shopper profile. This enables the in-store upsell flow described later without the customer presenting their card again.
Capture at collectionAsk
When a staff member confirms the customer has collected their order, capture the authorization. Call capture as close to the handover moment as possible — the authorization window is seven days.
const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });
const captured = await vinr.payments.capture('pay_8821auth', {
metadata: { staffId: 'staff_jsmith', collectedAt: new Date().toISOString() },
});const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });
const captured = await vinr.payments.capture('pay_8821auth', {
amount: 5000,
metadata: {
staffId: 'staff_jsmith',
collectedAt: new Date().toISOString(),
partialReason: 'item_unavailable',
},
});Pass a smaller amount when only part of the order is available. The remainder of the hold is released automatically; the customer is never charged for what they did not receive.
A successful capture emits payment.completed. Use this event to update your order management system and close the collection ticket.
In-store upsell at collectionAsk
If the customer wants to add an item while they are at the counter, you can charge the saved token from the original authorization — they do not need to tap or insert their card again.
const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });
const upsell = await vinr.payments.create({
amount: 1200,
currency: 'EUR',
captureMethod: 'automatic',
shopperReference: 'cust_abc123',
paymentMethodType: 'saved_token',
description: 'In-store upsell — Order #8821',
metadata: {
orderId: '8821',
fulfillment: 'click_and_collect_upsell',
pickupLocationId: 'loc_london_oxford_st',
staffId: 'staff_jsmith',
},
});If no token is on file — or the customer prefers to pay differently — accept a new contactless tap on the terminal instead. See Shopper recognition for the full token-reuse lifecycle.
Upsell payments are independent charges, not adjustments to the original authorization. Each appears as a separate line item in reconciliation.
Handling no-showsAsk
If the customer never collects, void the authorization before it expires rather than waiting for the hold to lapse on its own. Voids release the funds to the cardholder immediately and produce no settlement entry or fee.
Monitor your order management system for orders approaching their pickupWindowEnd without a confirmed collection.
Send the customer a reminder at least 24 hours before the window closes.
If there is still no collection, call void before the authorization expires (seven-day window from creation).
const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });
await vinr.payments.void('pay_8821auth', {
metadata: { reason: 'no_show', staffId: 'staff_jsmith' },
});Mark the order as cancelled in your system and release reserved stock. VINR emits payment.voided on success.
Do not cancel the authorization by refunding it or by taking no action and letting it expire. Always call void explicitly — a void produces no fee and releases the hold the same day, whereas an expired authorization may take 3–5 banking days to clear from the cardholder's statement.
For the full authorize-and-void reference, see Authorize & capture.
NotificationsAsk
VINR is responsible for payment events; your backend is responsible for customer communications.
Listen for payment.authorized on your webhook endpoint. This fires as soon as the shopper completes online checkout.
On receipt, reserve stock in your warehouse or store system and enqueue a collection-ready SMS or email to the customer.
Listen for payment.completed (fires on capture) to close the order and trigger any loyalty or receipt logic.
Listen for payment.voided to release reserved stock and send a cancellation confirmation.
Configure webhook endpoints and event subscriptions in your VINR Dashboard or via the Webhooks API. VINR retries failed deliveries with exponential back-off for up to 72 hours.
Metadata fieldsAsk
Prop
Type
Next stepsAsk
Return in store
Let customers bring online purchases back to any physical location.
Shopper recognition
Reuse saved tokens across channels without a card-present tap.
Authorize & capture
Full reference for manual capture, voids, and authorization expiry.
Last updated on