Redemption
Let members spend points for rewards.
Redemption is the moment a member spends points for something of value — usually a discount applied at checkout, sometimes a physical item or store credit. A redemption is atomic: VINR deducts points and issues the reward together, or neither happens. This page covers the redemption flow, the two reward shapes, and how reversals keep balances honest.
The redemption flowAsk
A redemption (rdm_) ties a member's loyalty_account to a reward from your catalog. Creating one deducts the reward's point cost, records a negative points_transaction (ptx_), and produces a usable artifact — a discount code, a fulfillment line, or a credit grant.
Check the balance
Redemption fails fast if the member can't afford the reward. You can pre-check, but VINR also enforces this server-side, so a race won't let a balance go negative.
Create the redemption
vinr.loyalty.redemptions.create deducts points and returns the redemption with its artifact. This is a single, idempotent call.
Apply or fulfill
For a discount reward, attach the returned code to the payment. For an item, hand the redemption to your fulfillment pipeline.
Settle
When the linked payment completes, the redemption moves to applied. If the payment never completes, the redemption (and its points) can be released — see Reversals.
Redeeming for a discountAsk
The common case: the member spends points for a discount that reduces what they pay at checkout. Create the redemption, then pass the resulting discount onto the payment.
import { Vinr } from '@vinr/sdk';
const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });
// 1. Spend points for a EUR 5.00 discount reward.
const redemption = await vinr.loyalty.redemptions.create(
{
account: 'loy_8sd2k1',
reward: 'rwd_fivecredit',
},
{ idempotencyKey: 'rdm-cart-9f31' }, // safe to retry
);
// redemption.discount = { amount: 500, currency: 'EUR', code: 'VINR-9F31' }
// 2. Apply the discount when you create the payment.
const payment = await vinr.payments.create({
amount: 2999,
currency: 'EUR',
customer: 'cust_abc123',
discounts: [{ redemption: redemption.id }], // pay_... charges 2499
});Always pass an idempotencyKey. If a checkout retries on a flaky network, the key guarantees the member is debited exactly once and you get the same redemption back.
The redemption starts in pending and links to the payment. When payment.completed fires, VINR transitions it to applied and emits loyalty.redemption.applied. If the payment is canceled or expires, the redemption auto-releases and the points return.
Redeeming for an itemAsk
For physical goods, gift cards, or anything you fulfill yourself, omit the discount and read the redemption's fulfillment block. Points are deducted immediately; fulfillment is your responsibility.
const redemption = await vinr.loyalty.redemptions.create({
account: 'loy_8sd2k1',
reward: 'rwd_totebag',
shipping: {
name: 'Mira Olsen',
line1: 'Storgata 12',
city: 'Oslo',
postalCode: '0155',
country: 'NO',
},
});
// redemption.status === 'fulfillment_pending'Subscribe to loyalty.redemption.created to push the order into your warehouse system, then call vinr.loyalty.redemptions.fulfill(redemption.id, { tracking: '...' }) to mark it shipped and emit loyalty.redemption.fulfilled.
Reversals and clawbacksAsk
Redemptions can be undone, returning points to the member. There are two paths:
| Path | Trigger | Result |
|---|---|---|
| Auto-release | Linked payment is canceled or expires | Points refunded, redemption released |
| Manual reversal | You call redemptions.reverse | Points refunded, artifact voided |
// A member returns the item, or a discount was applied in error.
await vinr.loyalty.redemptions.reverse('rdm_3kd9', {
reason: 'item_returned',
});
// Emits loyalty.redemption.reversed and a positive ptx_ restoring the balance.A clawback differs from a reversal: it happens when the purchase that earned points is refunded, not the redemption itself. That logic lives upstream — see Linking payments & loyalty. A reversed redemption that was already applied cannot un-discount a completed payment; instead, reverse it before the payment settles, or issue a refund for the discounted amount.
Failure handlingAsk
Redemption rejects loudly so you never silently overspend a balance or double-issue a reward.
Test the full loop in sandbox before launch: redeem, pay with 4242 4242 4242 4242 to confirm applied, then pay with 4000 0000 0000 0002 (declined) to confirm the redemption releases and points return.
Next stepsAsk
Rewards catalog
Define the rewards members can redeem.
Linking payments & loyalty
Earn on purchase, claw back on refund.
Loyalty accounts
Enroll and manage members and balances.
Last updated on