Shopper insights
Understand how shoppers buy across online, mobile, and in-store channels with VINR's unified transaction data.
Because VINR links every payment to a shopperReference regardless of which channel it originates from, you can analyse purchase behaviour across the full customer journey — not just within one channel. A shopper who browses online, buys in store, then requests a refund via pay-by-link appears as a single identity in your data, giving you a coherent picture of value, preferences, and conversion that per-channel analytics cannot provide.
What data is availableAsk
VINR's unified transaction data exposes the following dimensions per shopper:
- Per-shopper spend by channel — lifetime and period spend broken down by
online,in_person, andpay_by_link. - Channel preference over time — how the mix of channels a shopper uses shifts across quarters or seasons.
- First-channel attribution — which channel the shopper first transacted on, so you know where acquisition actually happened.
- Cross-channel conversion — shoppers who interacted on one channel (e.g. browsed via an online session) and completed a purchase on another (e.g. in store).
- Return rates by channel — refund and return frequency broken down by the originating channel.
- Average order value by channel — whether a shopper spends more in store, online, or via link.
All dimensions are available from the date the shopperReference was first used on your account.
Query shopper historyAsk
vinr.payments.list accepts a shopperReference alongside date filters and returns every payment for that shopper across all channels. Each payment object includes a channel field ('online' | 'in_person' | 'pay_by_link'), amount, currency, and createdAt.
import { Vinr } from '@vinr/sdk';
const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });
const payments = await vinr.payments.list({
shopperReference: 'shopper_8A2KX',
fromDate: '2025-01-01',
toDate: '2026-01-01',
});
type ChannelKey = 'online' | 'in_person' | 'pay_by_link';
const spendByChannel = payments.data.reduce<Record<ChannelKey, number>>(
(acc, payment) => {
const channel = payment.channel as ChannelKey;
acc[channel] = (acc[channel] ?? 0) + payment.amount;
return acc;
},
{ online: 0, in_person: 0, pay_by_link: 0 },
);
console.log(spendByChannel);The payments.data array is ordered by createdAt descending. Paginate using the cursor field on the response if the shopper has a large transaction history.
Payment object field reference
Prop
Type
Dashboard reportsAsk
The VINR Dashboard exposes pre-built shopper analytics under Reporting → Shoppers:
- Top spenders by channel — ranked list filterable by date range and channel.
- Cohort analysis — groups shoppers by their first-purchase month; tracks how many transact again in subsequent months and on which channels.
- Channel mix chart — stacked bar chart showing what percentage of revenue each channel contributed per week or month.
All views support CSV export. Use the export to feed downstream tools such as your CRM, marketing platform, or BI suite.
Data in the Shoppers tab is available from the date a shopperReference was first used on your VINR account. Payments made before you started passing shopperReference are not retroactively attributed to a shopper profile.
SegmentationAsk
The payments list endpoint accepts several filter parameters that let you target specific shopper segments.
Filter for shoppers who have only ever bought in store and have never transacted online — a useful segment for targeted online acquisition campaigns.
const inStoreOnly = await vinr.payments.list({
channel: 'in_person',
fromDate: '2025-01-01',
});
const shopperEmails = inStoreOnly.data
.filter((p) => p.shopperEmail)
.map((p) => p.shopperEmail as string);Identify high-value shoppers (lifetime spend above a threshold) across any channel for a VIP re-engagement flow.
const payments = await vinr.payments.list({ fromDate: '2024-01-01' });
const spendByRef = new Map<string, number>();
for (const p of payments.data) {
spendByRef.set(
p.shopperReference,
(spendByRef.get(p.shopperReference) ?? 0) + p.amount,
);
}
const highValue = [...spendByRef.entries()]
.filter(([, total]) => total >= 100000)
.map(([ref]) => ref);Scope a segment to shoppers who transacted at a specific physical location using the terminal's locationId stored in payment metadata.
const locationPayments = await vinr.payments.list({
channel: 'in_person',
'metadata.locationId': 'loc_MANHATTAN_5TH',
fromDate: '2025-06-01',
});Once you have a list of shopperReference values or shopperEmail addresses, export them and upload to your email platform to run targeted campaigns — for example, inviting high-value in-store shoppers to your online loyalty programme.
GDPR and equivalent privacy laws restrict using personal data (including purchase history and email addresses) to purposes covered by your privacy notice and a valid legal basis. Ensure your notice explicitly describes cross-channel analytics and targeted marketing before exporting shopper data for campaign use.
AttributionAsk
VINR supports both first-touch and last-touch attribution using fields on the payment object and its metadata.
Set acquisitionChannel at first purchase. When your OMS or marketing layer knows how a shopper was acquired (e.g. from a paid search click or an affiliate referral), write it into metadata.acquisitionChannel on the payment request.
const payment = await vinr.payments.create({
amount: 4900,
currency: 'GBP',
shopperReference: 'shopper_8A2KX',
metadata: {
acquisitionChannel: 'paid_search',
campaignId: 'gs_spring_2026',
},
});VINR preserves metadata across the shopper's lifetime. Every subsequent payment for shopper_8A2KX — regardless of channel — retains metadata.acquisitionChannel as set on the first payment, giving you a consistent first-touch signal across the full history.
Read attribution on any payment. When you query vinr.payments.list({ shopperReference }), each payment object includes the metadata blob. The first record (oldest createdAt) carries the original acquisition channel; the most recent carries the last-touch channel.
const history = await vinr.payments.list({
shopperReference: 'shopper_8A2KX',
fromDate: '2024-01-01',
});
const sorted = history.data.sort(
(a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(),
);
const firstTouch = sorted.at(0)?.metadata?.acquisitionChannel;
const lastTouch = sorted.at(-1)?.channel;
console.log({ firstTouch, lastTouch });If your OMS does not set acquisitionChannel, you can backfill it on existing payments using vinr.payments.update({ id, metadata: { acquisitionChannel: '…' } }). Backfilling does not affect the settled payment; it only updates the metadata record.
Next stepsAsk
Shopper recognition
Set up shopperReference so loyalty, saved cards, and purchase history follow each customer across channels.
Reporting
Explore the full Reporting dashboard — revenue breakdowns, settlement reports, and custom date ranges.
Reconciliation
Match VINR settlement data against your ledger and understand how cross-channel payments are batched.
Last updated on