Buy online, return in store

Process refunds for online purchases at the physical counter — BORIS / cross-channel returns.

View as MarkdownInstall skills

BORIS (Buy Online, Return In Store) lets a customer complete a purchase on your website or app and then walk into any physical location to return it. VINR links the original online payment to your in-store POS via shopperReference or order ID — so the refund credits the original payment method (card, wallet, or bank account) without the customer needing to produce the physical card. No duplicate customer records, no manual cross-referencing: one lookup surfaces the order and one API call closes it.

Look up the original paymentAsk

Staff begin a return by searching for the order at the POS counter. You can locate the original payment by shopperReference (your customer's stable ID) or by the VINR payment ID directly.

Customer arrives at the counter and provides their order number, email address, or the name on the order.

Staff enters the identifier into your POS. Your POS calls vinr.payments.list with shopperReference, or vinr.payments.retrieve if the pay_ ID is embedded in the order barcode.

The POS displays the matching order — amount, items, date, and payment method — for staff to confirm with the customer before issuing the refund.

import { Vinr } from '@vinr/sdk';

const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

const payments = await vinr.payments.list({
  shopperReference: 'shopper_ada_lovelace',
  limit: 10,
});

const original = payments.data.find(
  (p) => p.metadata?.orderId === 'ORD-8821',
);

// original.id     → "pay_3Nf8x2a..."
// original.amount → 4900
// original.status → "captured"
import { Vinr } from '@vinr/sdk';

const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

const payment = await vinr.payments.retrieve('pay_3Nf8x2a');

// payment.shopperReference → "shopper_ada_lovelace"
// payment.amount           → 4900
// payment.status           → "captured"

Issue the refundAsk

Once the original payment is confirmed, create the refund against its pay_ ID. VINR routes the credit back to the payment method on file — card, digital wallet, or bank account — without requiring the customer to present the original card at the terminal.

import { Vinr } from '@vinr/sdk';

const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

const refund = await vinr.refunds.create({
  payment: 'pay_3Nf8x2a',
  amount: 4900,
  reason: 'requested_by_customer',
  metadata: {
    shopperReference: 'shopper_ada_lovelace',
    originalOrderId: 'ORD-8821',
    returnLocationId: 'store_london_oxford_st',
  },
});

// refund.id     → "re_7Qp2m1c..."
// refund.status → "pending"

The customer does not need to present the original card. The refund travels back to the stored payment token that was used at checkout — the same card or wallet, regardless of which terminal or location processes the return.

Refund to a different methodAsk

If the customer prefers store credit or a gift card instead of a refund to their original payment method, pass refundMethod: 'store_credit' in the request. VINR records the refund disposition and marks the original payment as refunded in reconciliation, but the credit is disbursed by your own gift-card or loyalty system.

import { Vinr } from '@vinr/sdk';

const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

const refund = await vinr.refunds.create({
  payment: 'pay_3Nf8x2a',
  amount: 4900,
  refundMethod: 'store_credit',
  reason: 'requested_by_customer',
  metadata: {
    shopperReference: 'shopper_ada_lovelace',
    originalOrderId: 'ORD-8821',
  },
});

// refund.refundMethod → "store_credit"
// refund.status       → "succeeded"

Store credit refunds require your own gift-card or loyalty system to receive and honour the credit. VINR does not manage gift-card balances. Your system must listen for the refund.succeeded webhook with refundMethod: store_credit and issue the credit to the customer's account.

ReceiptAsk

Issue a return receipt at the counter — on paper from a receipt printer or digitally by email or SMS — using the VINR terminal or your own POS printer. Configure delivery channels per location under Settings → Receipts in the VINR Dashboard, or see Receipts and engagement for the full configuration reference.

The refund receipt automatically includes:

  • The original order reference (metadata.originalOrderId)
  • The original transaction date and payment method
  • The refund amount and the re_ refund ID
  • The store location and staff identifier if passed in metadata

Customers can match the re_ ID against their bank or wallet statement to confirm the credit has been applied.

Exchange flowAsk

An exchange — where the customer returns one item and immediately purchases a replacement — requires two operations: a refund on the original payment and a new payment for the replacement item. Link them with metadata.originalOrderId so both transactions appear together in reconciliation and reporting.

Create a refund against the original pay_ ID for the item being returned (full or partial amount).

Create a new payment for the replacement item at the terminal using vinr.payments.create or your POS payment flow. Include the same shopperReference and set metadata.originalOrderId to the original order ID.

Both the refund and the new payment share the same shopperReference, so they appear together in the shopper's payment history and in your reconciliation export under the same customer record.

import { Vinr } from '@vinr/sdk';

const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

const refund = await vinr.refunds.create({
  payment: 'pay_3Nf8x2a',
  amount: 4900,
  reason: 'requested_by_customer',
  metadata: {
    shopperReference: 'shopper_ada_lovelace',
    originalOrderId: 'ORD-8821',
  },
});

const replacement = await vinr.payments.create({
  amount: 5500,
  currency: 'gbp',
  shopperReference: 'shopper_ada_lovelace',
  metadata: {
    originalOrderId: 'ORD-8821',
    exchangeRefundId: refund.id,
  },
});

Refund field referenceAsk

Prop

Type

Next stepsAsk

Was this page helpful?
Edit on GitHub

Last updated on

On this page