← back to Gmc Titlefix

apply-canary.mjs

42 lines

// Push the ~400-row FRESH canary price overrides onto the supplemental datasource.
// Reversible: same offerId override; removing the supplement reverts to $4.25.
import fs from 'fs';
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const { token, MERCHANT } = require('./_auth.js');

// CURRENCY BUG FIX 2026-09-11 (TK-11450). These writers honour row.feedLabel (which can be 'CA')
// but hardcoded currencyCode:'USD' (literally). Result, measured live: all 1,077 Merchant Center CA offers are
// DW-written, 996 correctly CAD and 81 carrying a CA-market AMOUNT stamped USD. Google requires the
// offer currency to match an available shipping service's currency; the only CA service is CAD, so
// every one of those 81 is disapproved — 81 of 81, 100%, versus 1.3% for the CAD ones.
// Verified before fixing: 77 of the 81 feed amounts match the product's Shopify CA contextual price
// and ZERO match its USD base price, so the amounts were always CAD and only the label was wrong.
// FAILS CLOSED: an unknown feedLabel throws rather than silently defaulting to USD, because
// defaulting to USD is precisely what caused this.
const FEED_CURRENCY = { US: 'USD', CA: 'CAD', GB: 'GBP' };
function currencyForFeed(feedLabel) {
  const c = FEED_CURRENCY[String(feedLabel || '').toUpperCase()];
  if (!c) throw new Error(`refusing to write: no currency mapping for feedLabel "${feedLabel}" (add it to FEED_CURRENCY rather than defaulting to USD)`);
  return c;
}

const DS = 'accounts/146735262/dataSources/10693978453';
const CANARY = '/Users/macstudio3/.claude/yolo-queue/gmc-fresh-override-canary.json';
const sleep = ms => new Promise(r=>setTimeout(r,ms));
const list = JSON.parse(fs.readFileSync(CANARY,'utf8')).overrides;
let tok = await token(), tokAt = Date.now(), ok=0, fail=0; const fails=[];
console.log(`Pushing ${list.length} canary overrides -> ${DS}`);
for (let i=0;i<list.length;i++){
  if (Date.now()-tokAt > 50*60*1000){ tok=await token(); tokAt=Date.now(); }
  const row=list[i];
  const url=`https://merchantapi.googleapis.com/products/v1/accounts/${MERCHANT}/productInputs:insert?dataSource=${encodeURIComponent(DS)}`;
  const body={ offerId:row.offerId, contentLanguage:row.contentLanguage, feedLabel:row.feedLabel, productAttributes:{ price:{ amountMicros:String(Math.round(row.realPrice*1e6)), currencyCode:currencyForFeed(row.feedLabel) } } };
  const r=await fetch(url,{method:'POST',headers:{Authorization:'Bearer '+tok,'Content-Type':'application/json'},body:JSON.stringify(body)});
  if (r.ok) ok++; else { fail++; const t=await r.text(); if(fails.length<12) fails.push(`${row.offerId} ${r.status} ${t.slice(0,110)}`); if(r.status===429) await sleep(3000); }
  if (i%50===0) process.stdout.write(`  ${i}/${list.length} ok ${ok} fail ${fail}\n`);
}
console.log(`\nCANARY DONE: ok ${ok} / fail ${fail} of ${list.length}`);
if (fails.length){ console.log('--- first failures ---'); fails.forEach(f=>console.log('  '+f)); }
fs.writeFileSync('/Users/macstudio3/.claude/yolo-queue/gmc-canary-apply-result.json', JSON.stringify({ when:'today', ds:DS, pushed:list.length, ok, fail },null,2));