1'use strict';23const { isBlank } = require('./value-utils.js');4const { WIX_GIFT_CARDS_APP_ID } = require('./gift-card-build.js');56// rp-target-wix — given the bridge-plugin activity rows7// for one card, netted per source order, build the deterministic8// POST /ecom/v1/gift-cards/redeem request body: { code, amount, orderId, appId }.9//10// The mechanism itself is LIVE-VERIFIED IN ISOLATION 2026-08-17: a real Redeem Gift Card call against a real imported
90 if (!Number.isFinite(face) || !Number.isFinite(bridgeBalance)) {
91 return {
92 safe: false,
93 gap: {
94 code: 'policy-c-verification-missing-input',
95 summary: `faceValue (${JSON.stringify(faceValue)}) and bridgeCurrentBalance (${JSON.stringify(bridgeCurrentBalance)}) must both be finite numbers to verify Policy C is safe for this card.`,
121 summary: `replaying only this card's order-linked activity would leave it at ${predictedFinalBalance}, but the bridge reports a current balance of ${roundedBridgeBalance} (drift ${drift}) — some activity (e.g. a manual, non-order-linked adjustment) is not accounted for by order-linked replay. Policy C is not safe for this card; either gap it for manual review, or use Policy B (create directly at the bridge's verified current balance) instead.`,
179 summary: `options.balancePolicy must be "C" to build a redemption — got ${JSON.stringify(balancePolicy)}. Policy A means "face value, no replay"; replaying a redemption on an 'A' card contradicts that policy instead of implementing it. Policy B already creates the card at its verified current balance; replaying the same redemption on a 'B' card would double-decrement it. Only a card explicitly declared Policy C — face value now, corrected later by replaying real order-linked redemptions — may be redeemed here.`,
193 gaps: [{ code: 'missing-wix-code', summary: 'options.wixCode is required — resolve it from the crosswalk row this card was issued under (sourceStableKey woocommerce:giftCard:<number>) before building a redemption; never re-derive it from the raw source number, which may have been normalized at creation.' }],
194 notes,
195 };
196 }
197 if (isBlank(wixOrderId)) {
198 return {
199 payload: null,
200 gaps: [{ code: 'missing-wix-order-id', summary: 'options.wixOrderId is required — the order this redemption is linked to must already be imported before its redemptions can be recorded.' }],
201 notes,
202 };
203 }
204
205 // Derived from the SAME activityRows the verification above just ran against — never a
206 // separately caller-supplied value (see the fifth-pass fix note above).
215 summary: `net activity for order ${sourceOrderId} is "${netActivity && netActivity.amount}" — not a negative net amount once every activity row for this order is netted together, so there is nothing to redeem (either no debit occurred, or a later same-order reversal already restored it).`,
216 }],
217 notes,
218 };
219 }
220
221 const amount = Math.abs(netAmount);
222 if (amount <= 0) {
223 return {
224 payload: null,
225 gaps: [{ code: 'invalid-redemption-amount', summary: `net activity for order ${netActivity.orderId} has amount "${netActivity.amount}", not a positive value once its sign is taken.` }],
226 notes,
227 };
228 }
229
230 notes.push(`redemption sourced from netted bridge-plugin activity for order_id ${netActivity.orderId} (activity rows: ${JSON.stringify(netActivity.activityIds)}) — net amount after any same-order reversal is ${netActivity.amount}.`);