hookwright 1.2.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +2 -0
  2. package/dist/cli.js +1096 -100
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -24,7 +24,7 @@ var init_package = __esm({
24
24
  "package.json"() {
25
25
  package_default = {
26
26
  name: "hookwright",
27
- version: "1.2.2",
27
+ version: "1.3.0",
28
28
  description: "Build real, signed e-commerce webhooks from a live Shopify catalogue \u2014 interactive terminal UI, no backend",
29
29
  keywords: [
30
30
  "webhook",
@@ -171,7 +171,11 @@ var DEFAULT_CONFIG = {
171
171
  targets: {
172
172
  "cashfree-occ": { webhookUrl: "", webhookSecret: "" },
173
173
  "razorpay-magic": { webhookUrl: "", webhookSecret: "" },
174
- nitro: { webhookUrl: "", webhookSecret: "" }
174
+ nitro: { webhookUrl: "", webhookSecret: "" },
175
+ shopflo: { webhookUrl: "", webhookSecret: "" },
176
+ flexype: { webhookUrl: "", webhookSecret: "" },
177
+ "return-prime": { webhookUrl: "", webhookSecret: "" },
178
+ fastrr: { webhookUrl: "", webhookSecret: "" }
175
179
  },
176
180
  customer: {
177
181
  firstName: "",
@@ -407,6 +411,9 @@ function cartPermalink(domain, items) {
407
411
  function money2(value) {
408
412
  return Math.round(Number(value) * 100) / 100;
409
413
  }
414
+ function minor(value) {
415
+ return Math.round(Number(value) * 100);
416
+ }
410
417
  function normalizeCollection(collection) {
411
418
  return {
412
419
  id: String(collection.id),
@@ -603,7 +610,7 @@ var fieldMap2 = [
603
610
  { source: "email", target: "email" },
604
611
  { source: "phone", target: "phone", required: true, note: "falls back to Shipping_address.Phone" },
605
612
  { source: "currency", target: "pricing.currency", required: true, note: "read from the ROOT, unlike Cashfree" },
606
- { source: "line_items_total", target: "pricing.totalPrice", required: true, note: "documented as a STRING in paise \u2014 the consumer parseFloats it and divides by 100" },
613
+ { source: "line_items_total", target: "pricing.totalPrice", required: true, note: "a STRING in the smallest currency unit \u2014 the consumer parseFloats it and divides by 100" },
607
614
  { source: "platform", target: "\u2014", note: "documented root field" },
608
615
  { source: "utm_parameters", target: "\u2014", note: "documented root field" },
609
616
  { source: "customer.first_name", target: "customer.firstName" },
@@ -622,14 +629,15 @@ var fieldMap2 = [
622
629
  { source: "customer.Shipping_address.Zip", target: "customer.zip" },
623
630
  { source: "line_items[0].name", target: "cart.productName", required: true },
624
631
  { source: "line_items[0].image_url", target: "cart.image", note: "blank if the product has no image" },
625
- { source: "line_items[0].quantity", target: "cart.quantity" }
632
+ { source: "line_items[0].quantity", target: "cart.quantity" },
633
+ { source: "line_items[].price", target: "\u2014", note: "smallest currency unit, like line_items_total; the consumer reads no item price" }
626
634
  ];
627
635
  var editableFields2 = [
628
636
  { group: "Checkout", path: "abandoned_checkout_url", label: "Abandoned checkout URL", hint: "must be non-empty \u2014 this is the schema gate" },
629
637
  { group: "Checkout", path: "created_at", label: "Created at" },
630
638
  { group: "Checkout", path: "shop_id", label: "Shop domain" },
631
639
  { group: "Checkout", path: "cart_token", label: "Cart token" },
632
- { group: "Pricing", path: "line_items_total", label: "Line items total (paise, string)", hint: "documented as a string; the consumer divides by 100" },
640
+ { group: "Pricing", path: "line_items_total", label: "Line items total (minor units, string)", hint: "paise for INR, cents for USD; the consumer divides by 100" },
633
641
  { group: "Pricing", path: "currency", label: "Currency" },
634
642
  { group: "Customer", path: "customer.first_name", label: "First name" },
635
643
  { group: "Customer", path: "customer.last_name", label: "Last name" },
@@ -652,7 +660,7 @@ function buildPayload2(ctx) {
652
660
  const domain = config.shopify.domain;
653
661
  const cust = config.customer;
654
662
  const lineItems = items.map(({ product, variant, quantity }) => {
655
- const price = money2(variant?.price ?? 0);
663
+ const price = minor(variant?.price ?? 0);
656
664
  return {
657
665
  image_url: imageForVariant(product, variant),
658
666
  name: product.title,
@@ -670,9 +678,9 @@ function buildPayload2(ctx) {
670
678
  gram: "0"
671
679
  };
672
680
  });
673
- const total = money2(lineItems.reduce((sum, li) => sum + li.price * li.quantity, 0));
674
- const discount = money2(Math.min(ctx.discount ?? 0, total));
675
- const net2 = money2(total - discount);
681
+ const total = lineItems.reduce((sum, li) => sum + li.price * li.quantity, 0);
682
+ const discount = Math.min(minor(ctx.discount ?? 0), total);
683
+ const net2 = total - discount;
676
684
  const checkoutUrl = cartPermalink(domain, items.map(({ variant, quantity }) => ({ variantId: variant?.id, quantity })));
677
685
  return {
678
686
  shop_id: domain,
@@ -684,9 +692,9 @@ function buildPayload2(ctx) {
684
692
  phone,
685
693
  abandoned_checkout_url: checkoutUrl,
686
694
  currency,
687
- // Documented as a string, in the smallest currency unit (paise).
695
+ // Documented as a string, already in the smallest currency unit.
688
696
  // The consumer parses it with parseFloat and divides by 100.
689
- line_items_total: String(Math.round(net2 * 100)),
697
+ line_items_total: String(net2),
690
698
  line_items: lineItems,
691
699
  tax_details: {},
692
700
  promotions: [],
@@ -743,7 +751,7 @@ function summarize2(payload) {
743
751
  items: items.map((i) => `${i.name} x${i.quantity}`).join(", "),
744
752
  total: Number.parseFloat(payload?.line_items_total ?? "0") / 100,
745
753
  currency: payload?.currency,
746
- extra: `${payload?.line_items_total} paise`,
754
+ extra: `${payload?.line_items_total} minor units`,
747
755
  link: payload?.abandoned_checkout_url
748
756
  };
749
757
  }
@@ -1072,6 +1080,981 @@ var nitro_default = {
1072
1080
  summarize: summarize3
1073
1081
  };
1074
1082
 
1083
+ // src/providers/shopflo.mjs
1084
+ var EVENTS2 = [
1085
+ { eventName: "store_page_view", type: "shopflo_store_page_view", label: "Store Page View", selection: "none", subject: "a page, no product", envelope: "eventPayload" },
1086
+ { eventName: "collection_page_viewed", type: "shopflo_collection_page_viewed", label: "Collection Page View", selection: "collection", subject: "one collection", envelope: "data" },
1087
+ { eventName: "product_page_viewed", type: "shopflo_product_page_viewed", label: "Product Page View", selection: "product", subject: "one product", envelope: "data" },
1088
+ { eventName: "added_to_cart_ui", type: "shopflo_added_to_cart", label: "Added To Cart", selection: "cart", subject: "cart contents", envelope: "root" },
1089
+ { eventName: "checkout_clicked", type: "shopflo_checkout_clicked", label: "Checkout Clicked", selection: "cart", subject: "cart contents", envelope: "root" },
1090
+ { eventName: "checkout_abandoned", type: "shopflo_checkout_abandoned", label: "Abandoned Checkout", selection: "cart", subject: "cart contents", envelope: "root" },
1091
+ { eventName: "order_completed", type: "shopflo_order_completed", label: "Order Completed", selection: "cart", subject: "ordered items", envelope: "root" }
1092
+ ];
1093
+ var DEFAULT_EVENT2 = "checkout_abandoned";
1094
+ var eventDef = (eventName) => EVENTS2.find((e) => e.eventName === eventName) ?? EVENTS2.find((e) => e.eventName === DEFAULT_EVENT2);
1095
+ function eventFromPayload2(payload) {
1096
+ return payload?.event_name ?? payload?.eventName;
1097
+ }
1098
+ function selectionFor2(eventName) {
1099
+ return eventDef(eventName).selection;
1100
+ }
1101
+ var COMMON_FIELDS2 = [
1102
+ { source: "event_name / eventName", target: "\xABgate\xBB", required: true, note: "the schema matches EITHER spelling" },
1103
+ { source: "customer.phone / user_data.phone", target: "phone", required: true, note: "missing phone is rejected on every event" }
1104
+ ];
1105
+ var BROWSING_FIELDS = [
1106
+ { source: "uiData.pageUrl / data.page_url", target: "page.url" },
1107
+ { source: "uiData.pageTitle / data.page_title", target: "page.title" },
1108
+ { source: "uiData.landingPageUrl", target: "page.landingPage" },
1109
+ { source: "uiData.referrerPageUrl", target: "page.referrer" },
1110
+ { source: "clientData.brandUrl", target: "storeUrl" },
1111
+ { source: "session_id", target: "sessionId" }
1112
+ ];
1113
+ var CART_FIELDS = [
1114
+ { source: "line_items[].title", target: "cart.productName", required: true, note: "the first PAID line wins; freebies rank last" },
1115
+ { source: "line_items[].product_image", target: "cart.image" },
1116
+ { source: "line_items[].price", target: "\u2014 (ranking only)", note: "a zero price marks the line a freebie" },
1117
+ { source: "line_items[].quantity", target: "cart.quantity" },
1118
+ { source: "total_price", target: "pricing.totalPrice" },
1119
+ { source: "subtotal_price", target: "pricing.subtotal" },
1120
+ { source: "total_discount", target: "pricing.totalDiscount" },
1121
+ { source: "currency", target: "pricing.currency" },
1122
+ { source: "note_attributes[shopflo_checkout_url]", target: "checkoutUrl", note: "checkout_abandoned puts the link here, not in token_id" }
1123
+ ];
1124
+ var FIELD_MAPS2 = {
1125
+ store_page_view: [...COMMON_FIELDS2, ...BROWSING_FIELDS],
1126
+ collection_page_viewed: [...COMMON_FIELDS2, ...BROWSING_FIELDS, { source: "data.collection_page_url", target: "collection.url" }],
1127
+ product_page_viewed: [
1128
+ ...COMMON_FIELDS2,
1129
+ ...BROWSING_FIELDS,
1130
+ { source: "data.product_name", target: "product.name" },
1131
+ { source: "data.product_image", target: "product.image" },
1132
+ { source: "data.product_url", target: "product.url" },
1133
+ { source: "data.product_price", target: "product.price" }
1134
+ ],
1135
+ added_to_cart_ui: [...COMMON_FIELDS2, ...CART_FIELDS],
1136
+ checkout_clicked: [...COMMON_FIELDS2, ...CART_FIELDS],
1137
+ checkout_abandoned: [...COMMON_FIELDS2, ...CART_FIELDS],
1138
+ order_completed: [
1139
+ ...COMMON_FIELDS2,
1140
+ ...CART_FIELDS,
1141
+ { source: "order_id", target: "order.id" },
1142
+ { source: "order_name", target: "order.name" },
1143
+ { source: "payment_mode", target: "order.paymentMode" },
1144
+ { source: "total_payable", target: "order.totalPayable" }
1145
+ ]
1146
+ };
1147
+ var fieldMap4 = FIELD_MAPS2[DEFAULT_EVENT2];
1148
+ function editableFieldsFor2(eventName) {
1149
+ const def = eventDef(eventName);
1150
+ const base = [
1151
+ { group: "Event", path: "session_id", label: "Session id" },
1152
+ { group: "Customer", path: "customer.first_name", label: "First name" },
1153
+ { group: "Customer", path: "customer.last_name", label: "Last name" },
1154
+ { group: "Customer", path: "customer.email", label: "Email" },
1155
+ { group: "Customer", path: "customer.phone", label: "Phone", hint: "must parse to E.164" }
1156
+ ];
1157
+ if (def.envelope === "eventPayload") {
1158
+ return [
1159
+ { group: "Event", path: "eventPayload.uiData.pageUrl", label: "Page URL" },
1160
+ { group: "Event", path: "eventPayload.uiData.pageTitle", label: "Page title" },
1161
+ { group: "Event", path: "eventPayload.clientData.brandUrl", label: "Store URL" },
1162
+ { group: "Customer", path: "eventPayload.userData.firstName", label: "First name" },
1163
+ { group: "Customer", path: "eventPayload.userData.email", label: "Email" },
1164
+ { group: "Customer", path: "eventPayload.userData.phone", label: "Phone", hint: "must parse to E.164" }
1165
+ ];
1166
+ }
1167
+ if (def.envelope === "data") {
1168
+ return [
1169
+ { group: "Event", path: "data.page_url", label: "Page URL" },
1170
+ { group: "Event", path: "data.page_title", label: "Page title" },
1171
+ { group: "Customer", path: "data.user_data.firstName", label: "First name" },
1172
+ { group: "Customer", path: "data.user_data.email", label: "Email" },
1173
+ { group: "Customer", path: "data.user_data.phone", label: "Phone", hint: "must parse to E.164" }
1174
+ ];
1175
+ }
1176
+ return [
1177
+ ...base,
1178
+ { group: "Pricing", path: "total_price", label: "Total price", type: "number" },
1179
+ { group: "Pricing", path: "subtotal_price", label: "Subtotal", type: "number" },
1180
+ { group: "Pricing", path: "total_discount", label: "Discount", type: "number" },
1181
+ { group: "Pricing", path: "currency", label: "Currency" },
1182
+ ...def.eventName === "order_completed" ? [
1183
+ { group: "Order", path: "order_name", label: "Order name" },
1184
+ { group: "Order", path: "payment_mode", label: "Payment mode" },
1185
+ { group: "Order", path: "total_payable", label: "Total payable", type: "number" }
1186
+ ] : []
1187
+ ];
1188
+ }
1189
+ var editableFields4 = editableFieldsFor2(DEFAULT_EVENT2);
1190
+ function buildPayload4(ctx) {
1191
+ const { config, items, phone, collection } = ctx;
1192
+ const eventName = ctx.eventName ?? DEFAULT_EVENT2;
1193
+ const def = eventDef(eventName);
1194
+ const now = ctx.now ?? /* @__PURE__ */ new Date();
1195
+ const currency = ctx.currency || config.defaults.currency;
1196
+ const domain = config.shopify.domain;
1197
+ const cust = config.customer;
1198
+ const first = items?.[0];
1199
+ if (def.envelope === "eventPayload") {
1200
+ return {
1201
+ eventName,
1202
+ session_id: ctx.sessionId ?? `sf-${now.getTime()}`,
1203
+ channel: "web",
1204
+ eventPayload: {
1205
+ userData: { firstName: cust.firstName, lastName: cust.lastName, email: cust.email, phone },
1206
+ uiData: {
1207
+ pageUrl: `https://${domain}/`,
1208
+ pageTitle: config.shopify.shop?.name ?? domain,
1209
+ landingPageUrl: `https://${domain}/`,
1210
+ referrerPageUrl: ""
1211
+ },
1212
+ clientData: { brandUrl: `https://${domain}` }
1213
+ },
1214
+ timestamp: now.getTime()
1215
+ };
1216
+ }
1217
+ if (def.envelope === "data") {
1218
+ const isProduct = def.selection === "product";
1219
+ return {
1220
+ event_name: eventName,
1221
+ session_id: ctx.sessionId ?? `sf-${now.getTime()}`,
1222
+ channel: "web",
1223
+ data: {
1224
+ page_url: isProduct ? `https://${domain}/products/${first?.product?.handle ?? ""}` : `https://${domain}/collections/${collection?.handle ?? "all"}`,
1225
+ page_title: isProduct ? first?.product?.title : collection?.title,
1226
+ landing_page_url: `https://${domain}/`,
1227
+ referrer_page_url: "",
1228
+ ...isProduct ? {
1229
+ product_name: first?.product?.title,
1230
+ product_image: imageForVariant(first?.product, first?.variant),
1231
+ product_url: `https://${domain}/products/${first?.product?.handle ?? ""}`,
1232
+ product_price: money2(first?.variant?.price ?? 0),
1233
+ product_compare_at_price: money2(first?.variant?.compareAtPrice ?? first?.variant?.price ?? 0),
1234
+ product_type: first?.product?.productType ?? ""
1235
+ } : { collection_page_url: `https://${domain}/collections/${collection?.handle ?? "all"}` },
1236
+ user_data: { firstName: cust.firstName, lastName: cust.lastName, email: cust.email, phone }
1237
+ },
1238
+ timestamp: now.getTime()
1239
+ };
1240
+ }
1241
+ const lineItems = (items ?? []).map(({ product, variant, quantity }) => ({
1242
+ id: String(variant?.id ?? product.id),
1243
+ product_id: String(product.id),
1244
+ title: product.title,
1245
+ price: money2(variant?.price ?? 0).toFixed(2),
1246
+ quantity,
1247
+ product_image: imageForVariant(product, variant)
1248
+ }));
1249
+ const subtotal = money2(lineItems.reduce((sum, li) => sum + Number(li.price) * li.quantity, 0));
1250
+ const discount = money2(Math.min(ctx.discount ?? 0, subtotal));
1251
+ const total = money2(subtotal - discount);
1252
+ const checkoutUrl = cartPermalink(domain, (items ?? []).map(({ variant, quantity }) => ({ variantId: variant?.id, quantity })));
1253
+ return {
1254
+ event_name: eventName,
1255
+ session_id: ctx.sessionId ?? `sf-${now.getTime()}`,
1256
+ token_id: checkoutUrl,
1257
+ source: "shopflo",
1258
+ channel: "web",
1259
+ email: cust.email,
1260
+ phone,
1261
+ created_at: now.toISOString(),
1262
+ // checkout_abandoned carries the recovery link here; the consumer prefers it
1263
+ note_attributes: [
1264
+ { name: "landing_page", value: `/products/${items?.[0]?.product?.handle ?? ""}` },
1265
+ { name: "shopflo_checkout_url", value: checkoutUrl }
1266
+ ],
1267
+ line_items: lineItems,
1268
+ customer: { uid: "sf-user", first_name: cust.firstName, last_name: cust.lastName, email: cust.email, phone },
1269
+ currency,
1270
+ subtotal_price: subtotal,
1271
+ total_discount: discount,
1272
+ total_price: total,
1273
+ ...eventName === "order_completed" ? {
1274
+ order_id: ctx.orderId ?? Math.floor(now.getTime() / 1e3),
1275
+ order_name: ctx.orderName ?? `#${String(now.getTime()).slice(-5)}`,
1276
+ payment_mode: "prepaid",
1277
+ total_shipping: 0,
1278
+ total_tax: 0,
1279
+ total_payable: total
1280
+ } : {},
1281
+ timestamp: now.getTime()
1282
+ };
1283
+ }
1284
+ function sign4() {
1285
+ return { signature: null, timestamp: null, headers: { "content-type": "application/json" } };
1286
+ }
1287
+ function webhookUrl4(config) {
1288
+ return String(targetFor(config, "shopflo").webhookUrl ?? "").trim();
1289
+ }
1290
+ function webhookSecret4(config) {
1291
+ return String(targetFor(config, "shopflo").webhookSecret ?? "");
1292
+ }
1293
+ function storeOf(payload) {
1294
+ const brandUrl = payload?.eventPayload?.clientData?.brandUrl;
1295
+ const candidate = brandUrl ?? payload?.token_id;
1296
+ if (!candidate) return void 0;
1297
+ try {
1298
+ return new URL(candidate).host;
1299
+ } catch {
1300
+ return candidate;
1301
+ }
1302
+ }
1303
+ function summarize4(payload) {
1304
+ const name = eventFromPayload2(payload);
1305
+ const items = Array.isArray(payload?.line_items) ? payload.line_items : [];
1306
+ const envelope = payload?.eventPayload ?? payload?.data ?? {};
1307
+ const user = envelope?.userData ?? envelope?.user_data ?? payload?.customer ?? {};
1308
+ return {
1309
+ event: eventDef(name).label.toLowerCase(),
1310
+ store: storeOf(payload),
1311
+ customer: `${user.firstName ?? user.first_name ?? ""} ${user.lastName ?? user.last_name ?? ""} \xB7 ${payload?.phone ?? user.phone ?? ""}`.replace(/\s+/g, " ").trim(),
1312
+ items: items.map((i) => `${i.title} x${i.quantity}`).join(", ") || (envelope?.product_name ?? "\u2014"),
1313
+ total: payload?.total_price,
1314
+ currency: payload?.currency,
1315
+ extra: `envelope: ${eventDef(name).envelope}`,
1316
+ link: payload?.note_attributes?.find((n) => n.name === "shopflo_checkout_url")?.value ?? payload?.token_id
1317
+ };
1318
+ }
1319
+ var shopflo_default = {
1320
+ id: "shopflo",
1321
+ urlKey: "shopflo",
1322
+ label: "Shopflo",
1323
+ events: EVENTS2,
1324
+ defaultEvent: DEFAULT_EVENT2,
1325
+ eventType: "shopflo_checkout_abandoned",
1326
+ gate: { path: "event_name", value: DEFAULT_EVENT2 },
1327
+ gateFor: (eventName) => ({ path: eventDef(eventName).envelope === "eventPayload" ? "eventName" : "event_name", value: eventName }),
1328
+ signatureScheme: "none documented \u2014 every request is accepted",
1329
+ signatureHeader: null,
1330
+ fieldMap: fieldMap4,
1331
+ fieldMapFor: (eventName) => FIELD_MAPS2[eventName] ?? FIELD_MAPS2[DEFAULT_EVENT2],
1332
+ editableFields: editableFields4,
1333
+ editableFieldsFor: editableFieldsFor2,
1334
+ selectionFor: selectionFor2,
1335
+ eventFromPayload: eventFromPayload2,
1336
+ buildPayload: buildPayload4,
1337
+ sign: sign4,
1338
+ webhookUrl: webhookUrl4,
1339
+ webhookSecret: webhookSecret4,
1340
+ summarize: summarize4
1341
+ };
1342
+
1343
+ // src/phone.mjs
1344
+ var CALLING_CODES = {
1345
+ IN: "91",
1346
+ US: "1",
1347
+ CA: "1",
1348
+ GB: "44",
1349
+ AE: "971",
1350
+ SG: "65",
1351
+ AU: "61",
1352
+ NZ: "64",
1353
+ DE: "49",
1354
+ FR: "33",
1355
+ IT: "39",
1356
+ ES: "34",
1357
+ NL: "31",
1358
+ SE: "46",
1359
+ RO: "40",
1360
+ PL: "48",
1361
+ ZA: "27",
1362
+ NG: "234",
1363
+ KE: "254",
1364
+ BD: "880",
1365
+ PK: "92",
1366
+ LK: "94",
1367
+ NP: "977",
1368
+ MY: "60",
1369
+ ID: "62",
1370
+ PH: "63",
1371
+ TH: "66",
1372
+ VN: "84",
1373
+ JP: "81",
1374
+ KR: "82",
1375
+ CN: "86",
1376
+ SA: "966",
1377
+ QA: "974",
1378
+ KW: "965",
1379
+ OM: "968",
1380
+ BH: "973",
1381
+ BR: "55",
1382
+ MX: "52"
1383
+ };
1384
+ function callingCode(regionCode) {
1385
+ return CALLING_CODES[String(regionCode || "").toUpperCase()] || null;
1386
+ }
1387
+ function toE164(raw, regionCode) {
1388
+ const cleaned = String(raw ?? "").replace(/\s+/g, "");
1389
+ if (!cleaned) return { ok: false, reason: "phone is empty" };
1390
+ if (cleaned.startsWith("+")) {
1391
+ const digits2 = cleaned.slice(1).replace(/\D/g, "");
1392
+ if (digits2.length < 8 || digits2.length > 15) return { ok: false, reason: `+${digits2} is not a plausible E.164 length` };
1393
+ return { ok: true, value: `+${digits2}` };
1394
+ }
1395
+ const cc = callingCode(regionCode);
1396
+ if (!cc) {
1397
+ return {
1398
+ ok: false,
1399
+ reason: `no country calling code known for region "${regionCode}" \u2014 give the phone in +E.164 form instead`
1400
+ };
1401
+ }
1402
+ let national = cleaned.replace(/\D/g, "").replace(/^0+/, "");
1403
+ if (national.startsWith(cc) && national.length > 10) national = national.slice(cc.length);
1404
+ const value = `+${cc}${national}`;
1405
+ const digits = value.slice(1);
1406
+ if (digits.length < 8 || digits.length > 15) return { ok: false, reason: `${value} is not a plausible E.164 length` };
1407
+ return { ok: true, value };
1408
+ }
1409
+ var knownRegions = Object.keys(CALLING_CODES);
1410
+ function splitPhone(raw, regionCode) {
1411
+ const digits = String(raw ?? "").replace(/[^\d]/g, "");
1412
+ if (!digits) return { dialCode: "", national: "" };
1413
+ const fromRegion = callingCode(regionCode);
1414
+ if (fromRegion && digits.startsWith(fromRegion)) {
1415
+ return { dialCode: fromRegion, national: digits.slice(fromRegion.length) };
1416
+ }
1417
+ const longestFirst = [...new Set(Object.values(CALLING_CODES))].sort((a, b) => b.length - a.length);
1418
+ const matched = longestFirst.find((code) => digits.startsWith(code));
1419
+ if (matched) return { dialCode: matched, national: digits.slice(matched.length) };
1420
+ return { dialCode: "", national: digits };
1421
+ }
1422
+ function nationalNumber(raw, regionCode) {
1423
+ return splitPhone(raw, regionCode).national;
1424
+ }
1425
+
1426
+ // src/providers/flexype.mjs
1427
+ var GATE_PATH3 = "event_type";
1428
+ var GATE_VALUE2 = "ABANDONED_CHECKOUT";
1429
+ var fieldMap5 = [
1430
+ { source: "event_type", target: "\xABgate\xBB", required: true, note: "must be ABANDONED_CHECKOUT or the event is dropped" },
1431
+ { source: "payload.user.phone_with_dial_code", target: "phone", required: true, note: "falls back to dial_code + phone" },
1432
+ { source: "payload.user.first_name", target: "customer.firstName" },
1433
+ { source: "payload.user.last_name", target: "customer.lastName" },
1434
+ { source: "payload.user.full_name", target: "customer.name" },
1435
+ { source: "payload.user.email", target: "customer.email" },
1436
+ { source: "payload.checkout_url", target: "checkoutUrl", required: true },
1437
+ { source: "payload.session_id", target: "sessionId" },
1438
+ { source: "payload.session_state", target: "sessionState" },
1439
+ { source: "payload.cart_items[].title", target: "cart.productName", required: true, note: "the first PAID line wins; freebies rank last" },
1440
+ { source: "payload.cart_items[].image", target: "cart.image" },
1441
+ { source: "payload.cart_items[].total_price", target: "pricing.totalPrice", note: "summed across the cart" },
1442
+ { source: "payload.cart_items[].total_discount", target: "pricing.totalDiscount", note: "summed across the cart" },
1443
+ { source: "payload.cart_items[].quantity", target: "cart.quantity" },
1444
+ { source: "payload.coupons[0].code", target: "pricing.couponCode" },
1445
+ { source: "payload.checkout_context.presentment_currency", target: "pricing.currency" },
1446
+ { source: "payload.checkout_context.presentment_country", target: "phone region" },
1447
+ { source: "payload.metadata.utm[]", target: "utm.*", note: "a list of {name,value} pairs, not an object" },
1448
+ { source: "payload.metadata.landing_page", target: "attribution.landingPage" },
1449
+ { source: "store.shopify_domain", target: "storeUrl" },
1450
+ { source: "store.name", target: "store.name" },
1451
+ { source: "event_id", target: "eventId" },
1452
+ { source: "event_time", target: "eventTime" }
1453
+ ];
1454
+ var editableFields5 = [
1455
+ { group: "Event", path: "event_id", label: "Event id" },
1456
+ { group: "Event", path: "event_time", label: "Event time" },
1457
+ { group: "Checkout", path: "payload.session_id", label: "Session id" },
1458
+ { group: "Checkout", path: "payload.checkout_url", label: "Checkout URL", hint: "the link the shopper is sent back to" },
1459
+ { group: "Checkout", path: "payload.session_state", label: "Session state" },
1460
+ { group: "Customer", path: "payload.user.first_name", label: "First name" },
1461
+ { group: "Customer", path: "payload.user.last_name", label: "Last name" },
1462
+ { group: "Customer", path: "payload.user.email", label: "Email" },
1463
+ { group: "Customer", path: "payload.user.phone_with_dial_code", label: "Phone", hint: "already E.164 shaped" },
1464
+ { group: "Pricing", path: "payload.checkout_context.presentment_currency", label: "Currency" },
1465
+ { group: "Pricing", path: "payload.coupons.0.code", label: "Coupon code", optional: true },
1466
+ { group: "Store", path: "store.name", label: "Store name" },
1467
+ { group: "Store", path: "store.shopify_domain", label: "Shopify domain" },
1468
+ { group: "Attribution", path: "payload.metadata.landing_page", label: "Landing page", optional: true }
1469
+ ];
1470
+ function buildPayload5(ctx) {
1471
+ const { config, items, phone } = ctx;
1472
+ const now = ctx.now ?? /* @__PURE__ */ new Date();
1473
+ const currency = ctx.currency || config.defaults.currency;
1474
+ const domain = config.shopify.domain;
1475
+ const cust = config.customer;
1476
+ const { dialCode, national } = splitPhone(phone, cust.countryCode);
1477
+ const cartItems = (items ?? []).map(({ product, variant, quantity }) => {
1478
+ const unit = money2(variant?.price ?? 0);
1479
+ return {
1480
+ title: product.title,
1481
+ image: imageForVariant(product, variant),
1482
+ price: unit,
1483
+ total_price: money2(unit * quantity),
1484
+ total_discount: 0,
1485
+ quantity,
1486
+ variant_id: String(variant?.id ?? ""),
1487
+ product_id: String(product.id),
1488
+ sku: variant?.sku || null
1489
+ };
1490
+ });
1491
+ const checkoutUrl = cartPermalink(domain, (items ?? []).map(({ variant, quantity }) => ({ variantId: variant?.id, quantity })));
1492
+ const utm = Object.entries(config.defaults.utm ?? {}).map(([name, value]) => ({ name, value }));
1493
+ return {
1494
+ event_id: `fx-${now.getTime()}`,
1495
+ event_type: GATE_VALUE2,
1496
+ event_time: now.toISOString(),
1497
+ store: {
1498
+ id: 1,
1499
+ name: config.shopify.shop?.name ?? domain,
1500
+ shopify_domain: domain,
1501
+ main_domain: `https://${domain}`
1502
+ },
1503
+ payload: {
1504
+ session_id: `fx-sess-${now.getTime()}`,
1505
+ session_state: "ABANDONED",
1506
+ checkout_url: checkoutUrl,
1507
+ user: {
1508
+ first_name: cust.firstName,
1509
+ last_name: cust.lastName,
1510
+ full_name: `${cust.firstName} ${cust.lastName}`.trim(),
1511
+ email: cust.email,
1512
+ phone: national,
1513
+ dial_code: dialCode,
1514
+ phone_with_dial_code: phone
1515
+ },
1516
+ checkout_context: {
1517
+ presentment_currency: currency,
1518
+ store_currency: currency,
1519
+ presentment_country: cust.countryCode,
1520
+ store_country: cust.country
1521
+ },
1522
+ cart_items: cartItems,
1523
+ coupons: ctx.couponCode ? [{ code: ctx.couponCode }] : [],
1524
+ metadata: {
1525
+ landing_page: `https://${domain}/products/${items?.[0]?.product?.handle ?? ""}`,
1526
+ orig_referer: "",
1527
+ referer_host: "",
1528
+ utm
1529
+ }
1530
+ }
1531
+ };
1532
+ }
1533
+ function sign5({ secret }) {
1534
+ return {
1535
+ signature: null,
1536
+ timestamp: null,
1537
+ headers: {
1538
+ "content-type": "application/json",
1539
+ "x-flexype-authorization": String(secret ?? "")
1540
+ }
1541
+ };
1542
+ }
1543
+ function webhookUrl5(config) {
1544
+ return String(targetFor(config, "flexype").webhookUrl ?? "").trim();
1545
+ }
1546
+ function webhookSecret5(config) {
1547
+ return String(targetFor(config, "flexype").webhookSecret ?? "");
1548
+ }
1549
+ function summarize5(payload) {
1550
+ const p = payload?.payload ?? {};
1551
+ const items = Array.isArray(p.cart_items) ? p.cart_items : [];
1552
+ return {
1553
+ event: "abandoned checkout",
1554
+ store: payload?.store?.shopify_domain,
1555
+ customer: `${p.user?.full_name ?? ""} \xB7 ${p.user?.phone_with_dial_code ?? ""}`.trim(),
1556
+ items: items.map((i) => `${i.title} x${i.quantity}`).join(", "),
1557
+ total: items.reduce((sum, i) => sum + Number(i.total_price ?? 0), 0),
1558
+ currency: p.checkout_context?.presentment_currency,
1559
+ extra: p.coupons?.[0]?.code ? `coupon ${p.coupons[0].code}` : "no coupon",
1560
+ link: p.checkout_url
1561
+ };
1562
+ }
1563
+ var flexype_default = {
1564
+ id: "flexype",
1565
+ urlKey: "flexype",
1566
+ label: "FlexyPe",
1567
+ eventType: "flexype_abandoned_checkout",
1568
+ gate: { path: GATE_PATH3, value: GATE_VALUE2 },
1569
+ signatureScheme: "static shared secret in the header (no body signature)",
1570
+ signatureHeader: "x-flexype-authorization",
1571
+ fieldMap: fieldMap5,
1572
+ editableFields: editableFields5,
1573
+ buildPayload: buildPayload5,
1574
+ sign: sign5,
1575
+ webhookUrl: webhookUrl5,
1576
+ webhookSecret: webhookSecret5,
1577
+ summarize: summarize5
1578
+ };
1579
+
1580
+ // src/providers/return-prime.mjs
1581
+ var STATUSES = ["requested", "approved", "received", "inspected", "rejected"];
1582
+ var titleCase = (v) => v.charAt(0).toUpperCase() + v.slice(1);
1583
+ var EVENTS3 = ["return", "exchange"].flatMap(
1584
+ (requestType) => STATUSES.map((status) => ({
1585
+ eventName: `${requestType}_${status}`,
1586
+ requestType,
1587
+ status,
1588
+ type: `return_prime_${requestType}_${status}`,
1589
+ label: `${titleCase(requestType)} ${titleCase(status)}`,
1590
+ selection: requestType === "exchange" ? "exchange" : "product",
1591
+ subject: requestType === "exchange" ? "the returned product and its replacement" : "the returned product"
1592
+ }))
1593
+ );
1594
+ var DEFAULT_EVENT3 = "return_requested";
1595
+ var eventDef2 = (eventName) => EVENTS3.find((e) => e.eventName === eventName) ?? EVENTS3.find((e) => e.eventName === DEFAULT_EVENT3);
1596
+ function eventFromPayload3(payload) {
1597
+ const request = payload?.request ?? payload ?? {};
1598
+ if (!request.request_type || !request.status) return void 0;
1599
+ return `${request.request_type}_${request.status}`;
1600
+ }
1601
+ function selectionFor3(eventName) {
1602
+ return eventDef2(eventName).selection;
1603
+ }
1604
+ var COMMON_FIELDS3 = [
1605
+ { source: "request_type", target: "\xABgate\xBB + requestType", required: true, note: "return or exchange" },
1606
+ { source: "status", target: "\xABgate\xBB + status", required: true, note: "requested, approved, received, inspected or rejected" },
1607
+ { source: "customer.phone", target: "phone", required: true, note: "missing phone is rejected on every event" },
1608
+ { source: "customer.address.country_code", target: "phone region" },
1609
+ { source: "request_number", target: "requestNumber" },
1610
+ { source: "order.name", target: "order.name" },
1611
+ { source: "line_items[].original_product.title", target: "product.name", required: true, note: "the first PAID line wins; freebies rank last" },
1612
+ { source: "line_items[].original_product.image.src", target: "product.image" },
1613
+ { source: "line_items[].original_product.price", target: "product.price", note: "a zero price marks the line a freebie" },
1614
+ { source: "line_items[].reason", target: "reason" },
1615
+ { source: "line_items[].presentment_price.actual_amount", target: "pricing.totalPrice" },
1616
+ { source: "line_items[].return_fee.price_set\u2026amount", target: "pricing.returnFee" },
1617
+ { source: "line_items[].refund.status", target: "refund.status" },
1618
+ { source: "line_items[].refund.meta.discount_code", target: "refund.discountCode", note: "only present once store credit is issued" },
1619
+ { source: "line_items[].shipping[0].awb", target: "shipping.awb" },
1620
+ { source: "approved/received/inspected/rejected.created_at", target: "timeline.*" }
1621
+ ];
1622
+ var EXCHANGE_FIELDS = [
1623
+ { source: "line_items[].exchange_product.title", target: "exchangeProduct.name", note: "exchanges only \u2014 returns never carry it" },
1624
+ { source: "line_items[].exchange_product.price", target: "exchangeProduct.price + pricing.priceDifference", note: "difference is signed: positive means the shopper owes more" }
1625
+ ];
1626
+ var fieldMap6 = COMMON_FIELDS3;
1627
+ function fieldMapFor(eventName) {
1628
+ return eventDef2(eventName).requestType === "exchange" ? [...COMMON_FIELDS3, ...EXCHANGE_FIELDS] : COMMON_FIELDS3;
1629
+ }
1630
+ function editableFieldsFor3(eventName) {
1631
+ const def = eventDef2(eventName);
1632
+ const p = (path6) => def.envelope === "root" ? path6 : `request.${path6}`;
1633
+ return [
1634
+ { group: "Request", path: p("request_number"), label: "Request number" },
1635
+ { group: "Request", path: p("created_at"), label: "Created at" },
1636
+ { group: "Request", path: p("line_items.0.reason"), label: "Reason" },
1637
+ { group: "Order", path: p("order.name"), label: "Order name" },
1638
+ { group: "Customer", path: p("customer.name"), label: "Customer name" },
1639
+ { group: "Customer", path: p("customer.email"), label: "Email" },
1640
+ { group: "Customer", path: p("customer.phone"), label: "Phone", hint: "already E.164 shaped" },
1641
+ { group: "Shipping", path: p("customer.address.address_line_1"), label: "Address line 1" },
1642
+ { group: "Shipping", path: p("customer.address.city"), label: "City" },
1643
+ { group: "Shipping", path: p("customer.address.province"), label: "Province / state" },
1644
+ { group: "Shipping", path: p("customer.address.postal_code"), label: "Postal code" },
1645
+ { group: "Shipping", path: p("customer.address.country_code"), label: "Country code", hint: "region used to parse the phone" },
1646
+ { group: "Pricing", path: p("line_items.0.presentment_price.actual_amount"), label: "Item amount", type: "number" },
1647
+ { group: "Pricing", path: p("line_items.0.presentment_price.currency"), label: "Currency" }
1648
+ ];
1649
+ }
1650
+ var editableFields6 = editableFieldsFor3(DEFAULT_EVENT3);
1651
+ function statusBlock(reached, at, comment = null) {
1652
+ return { status: reached, comment, created_at: reached ? at : null };
1653
+ }
1654
+ function buildPayload6(ctx) {
1655
+ const { config, items, phone } = ctx;
1656
+ const eventName = ctx.eventName ?? DEFAULT_EVENT3;
1657
+ const def = eventDef2(eventName);
1658
+ const now = ctx.now ?? /* @__PURE__ */ new Date();
1659
+ const currency = ctx.currency || config.defaults.currency;
1660
+ const cust = config.customer;
1661
+ const at = now.toISOString();
1662
+ const first = items?.[0];
1663
+ const second = items?.[1] ?? first;
1664
+ const originalPrice = money2(first?.variant?.price ?? 0);
1665
+ const exchangePrice = money2(second?.variant?.price ?? 0);
1666
+ const reachedIndex = STATUSES.indexOf(def.status);
1667
+ const reached = (name) => STATUSES.indexOf(name) <= reachedIndex && def.status !== "requested";
1668
+ const lineItem = {
1669
+ id: Number(first?.variant?.id ?? first?.product?.id ?? 0),
1670
+ refund: {
1671
+ requested_mode: "store_credit",
1672
+ actual_mode: def.status === "rejected" ? "store_credit" : null,
1673
+ meta: def.status === "rejected" && ctx.couponCode ? { discount_code: ctx.couponCode } : null,
1674
+ status: def.status === "rejected" ? "refunded" : "pending",
1675
+ refunded_amount: {
1676
+ shop_money: { ...def.status === "rejected" ? { amount: originalPrice } : {}, currency_code: currency },
1677
+ presentment_money: { ...def.status === "rejected" ? { amount: originalPrice } : {}, currency_code: currency }
1678
+ },
1679
+ refunded_at: def.status === "rejected" ? at : null,
1680
+ comment: null
1681
+ },
1682
+ return_fee: {
1683
+ price_set: {
1684
+ shop_money: { amount: ctx.returnFee ?? 0, currency_code: currency },
1685
+ presentment_money: { amount: ctx.returnFee ?? 0, currency_code: currency }
1686
+ },
1687
+ rule: "order"
1688
+ },
1689
+ exchange_fee: {
1690
+ price_set: {
1691
+ shop_money: { amount: null, currency_code: null },
1692
+ presentment_money: { amount: null, currency_code: null }
1693
+ }
1694
+ },
1695
+ exchange: { order: null },
1696
+ original_product: {
1697
+ title: first?.product?.title,
1698
+ variant_title: first?.variant?.title ?? null,
1699
+ product_id: Number(first?.product?.id ?? 0),
1700
+ variant_id: Number(first?.variant?.id ?? 0),
1701
+ image: { src: imageForVariant(first?.product, first?.variant) },
1702
+ price: originalPrice,
1703
+ sku: first?.variant?.sku || "",
1704
+ variant_deleted: false,
1705
+ product_deleted: false
1706
+ },
1707
+ ...def.requestType === "exchange" ? {
1708
+ exchange_product: {
1709
+ product_id: Number(second?.product?.id ?? 0),
1710
+ title: second?.product?.title,
1711
+ variant_title: second?.variant?.title ?? "Default Title",
1712
+ variant_id: Number(second?.variant?.id ?? 0),
1713
+ image: { src: imageForVariant(second?.product, second?.variant) },
1714
+ price: exchangePrice,
1715
+ sku: second?.variant?.sku || "",
1716
+ variant_deleted: false,
1717
+ product_deleted: false
1718
+ }
1719
+ } : {},
1720
+ shipping: [
1721
+ def.status === "requested" ? { tracking_available: false, labels: [] } : {
1722
+ shipping_company: "bluedartV2",
1723
+ tracking_available: true,
1724
+ awb: String(now.getTime()).slice(-11),
1725
+ tracking_updated_at: null,
1726
+ labels: [],
1727
+ shipment_status: "Requested",
1728
+ tracking_url: `https://www.bluedart.com/trackdartresultthirdparty?trackFor=0&trackNo=${String(now.getTime()).slice(-11)}`
1729
+ }
1730
+ ],
1731
+ quantity: first?.quantity ?? 1,
1732
+ reason: ctx.reason ?? "Size of Fit Issues",
1733
+ shop_price: {
1734
+ actual_amount: originalPrice,
1735
+ total_tax: 0,
1736
+ return_quantity: first?.quantity ?? 1,
1737
+ total_discount: 0,
1738
+ shipping_amount: 0,
1739
+ taxes_included: true,
1740
+ currency,
1741
+ do_not_carry_forward_discount: true
1742
+ },
1743
+ presentment_price: {
1744
+ actual_amount: originalPrice,
1745
+ total_tax: 0,
1746
+ return_quantity: first?.quantity ?? 1,
1747
+ total_discount: 0,
1748
+ shipping_amount: 0,
1749
+ taxes_included: true,
1750
+ currency,
1751
+ do_not_carry_forward_discount: true
1752
+ },
1753
+ shopify_order_fulfillment_location: null,
1754
+ return_location: def.status === "requested" ? null : {
1755
+ id: "rp-location-1",
1756
+ fullName: config.shopify.shop?.name ?? config.shopify.domain,
1757
+ mobileNumber: nationalNumber(phone, cust.countryCode),
1758
+ pinCode: cust.zip,
1759
+ streetAddress1: cust.address1,
1760
+ streetAddress2: cust.address2 ?? "",
1761
+ landmark: "",
1762
+ city: cust.city,
1763
+ state: cust.province,
1764
+ stateCode: cust.provinceCode,
1765
+ countryCode: cust.countryCode,
1766
+ country: cust.country,
1767
+ store: config.shopify.domain,
1768
+ facilityCode: "",
1769
+ isDefault: true,
1770
+ latitude: "",
1771
+ longitude: "",
1772
+ inStoreReturnExchange: false,
1773
+ external: false,
1774
+ createdAt: at,
1775
+ updatedAt: at
1776
+ },
1777
+ notes: null,
1778
+ tags: []
1779
+ };
1780
+ const request = {
1781
+ id: `rp-${now.getTime()}`,
1782
+ request_number: ctx.requestNumber ?? `${def.requestType === "exchange" ? "EXC" : "RET"}${String(now.getTime()).slice(-3)}`,
1783
+ request_type: def.requestType,
1784
+ status: def.status,
1785
+ manual_request: false,
1786
+ channel: 97426,
1787
+ smart_exchange: false,
1788
+ payment_details: null,
1789
+ order: {
1790
+ id: Number(ctx.orderId ?? String(now.getTime()).slice(-10)),
1791
+ name: ctx.orderName ?? `#${String(now.getTime()).slice(-5)}`,
1792
+ order_manual_payment: false,
1793
+ payment_gateways: [],
1794
+ fulfillments: [{ id: Number(String(now.getTime()).slice(-10)), line_items: [lineItem.id], delivery_status: null, delivery_date: null }],
1795
+ created_at: at
1796
+ },
1797
+ customer: {
1798
+ id: Number(String(now.getTime()).slice(-10)),
1799
+ name: `${cust.firstName} ${cust.lastName}`.trim(),
1800
+ email: cust.email,
1801
+ phone,
1802
+ address: {
1803
+ first_name: cust.firstName,
1804
+ last_name: cust.lastName || null,
1805
+ postal_code: cust.zip,
1806
+ city: cust.city,
1807
+ province: cust.province,
1808
+ country_code: cust.countryCode,
1809
+ province_code: cust.provinceCode,
1810
+ address_line_1: cust.address1,
1811
+ address_line_2: cust.address2 ?? "",
1812
+ country: cust.country,
1813
+ address_line_3: ""
1814
+ },
1815
+ bank: { account_holder_name: "", account_number: "", confirm_account_number: "", ifsc_code: "" }
1816
+ },
1817
+ approved: statusBlock(reached("approved"), at),
1818
+ received: statusBlock(reached("received"), at),
1819
+ inspected: statusBlock(reached("inspected"), at, def.status === "inspected" ? "quickreply test" : null),
1820
+ rejected: statusBlock(def.status === "rejected", at),
1821
+ archived: statusBlock(false, at),
1822
+ unarchived: statusBlock(false, at),
1823
+ incentive: null,
1824
+ line_items: [lineItem],
1825
+ created_at: at
1826
+ };
1827
+ return ctx.envelope === "root" ? request : { request };
1828
+ }
1829
+ function sign6() {
1830
+ return { signature: null, timestamp: null, headers: { "content-type": "application/json" } };
1831
+ }
1832
+ function webhookUrl6(config) {
1833
+ return String(targetFor(config, "return-prime").webhookUrl ?? "").trim();
1834
+ }
1835
+ function webhookSecret6(config) {
1836
+ return String(targetFor(config, "return-prime").webhookSecret ?? "");
1837
+ }
1838
+ function summarize6(payload) {
1839
+ const r = payload?.request ?? payload ?? {};
1840
+ const item = (Array.isArray(r.line_items) ? r.line_items : [])[0] ?? {};
1841
+ const original = item.original_product ?? {};
1842
+ const exchange = item.exchange_product;
1843
+ const difference = exchange ? money2(Number(exchange.price ?? 0) - Number(original.price ?? 0)) : void 0;
1844
+ return {
1845
+ event: `${r.request_type ?? "?"} ${r.status ?? "?"}`,
1846
+ store: item.return_location?.store ?? r.order?.name,
1847
+ customer: `${r.customer?.name ?? ""} \xB7 ${r.customer?.phone ?? ""}`.trim(),
1848
+ items: exchange ? `${original.title} \u2192 ${exchange.title}` : original.title,
1849
+ total: item.presentment_price?.actual_amount,
1850
+ currency: item.presentment_price?.currency,
1851
+ extra: difference === void 0 ? `${r.request_number ?? ""}` : `difference ${difference > 0 ? "+" : ""}${difference} (${difference > 0 ? "shopper pays" : "refund due"})`,
1852
+ link: null
1853
+ };
1854
+ }
1855
+ var return_prime_default = {
1856
+ id: "return-prime",
1857
+ urlKey: "return_prime",
1858
+ label: "Return Prime",
1859
+ events: EVENTS3,
1860
+ defaultEvent: DEFAULT_EVENT3,
1861
+ eventType: "return_prime_return_requested",
1862
+ gate: { path: "request.request_type", value: "return" },
1863
+ gateFor: (eventName) => ({ path: "request.status", value: eventDef2(eventName).status }),
1864
+ signatureScheme: "none documented \u2014 every request is accepted",
1865
+ signatureHeader: null,
1866
+ fieldMap: fieldMap6,
1867
+ fieldMapFor,
1868
+ editableFields: editableFields6,
1869
+ editableFieldsFor: editableFieldsFor3,
1870
+ selectionFor: selectionFor3,
1871
+ eventFromPayload: eventFromPayload3,
1872
+ buildPayload: buildPayload6,
1873
+ sign: sign6,
1874
+ webhookUrl: webhookUrl6,
1875
+ webhookSecret: webhookSecret6,
1876
+ summarize: summarize6
1877
+ };
1878
+
1879
+ // src/providers/fastrr.mjs
1880
+ var GATE_PATH4 = "event_name";
1881
+ var GATE_VALUE3 = "ABANDON_CART";
1882
+ var fieldMap7 = [
1883
+ { source: "event_name", target: "\xABgate\xBB", required: true, note: "must be ABANDON_CART or the event is dropped" },
1884
+ { source: "event_id", target: "eventId" },
1885
+ { source: "event_data.phone_number", target: "phone", required: true, note: "BARE national number \u2014 region comes from the address" },
1886
+ { source: "event_data.shipping_address.country_code", target: "phone region", required: true },
1887
+ { source: "event_data.checkout_url", target: "checkoutUrl", required: true },
1888
+ { source: "event_data.items[].name", target: "cart.productName", required: true, note: "the first PAID line wins; freebies rank last" },
1889
+ { source: "event_data.items[].img_url", target: "cart.image" },
1890
+ { source: "event_data.items[].price", target: "\u2014 (ranking only)", note: "a zero price marks the line a freebie" },
1891
+ { source: "event_data.items[].sku", target: "cart.sku" },
1892
+ { source: "event_data.item_count", target: "cart.itemCount" },
1893
+ { source: "event_data.total_price", target: "pricing.totalPrice" },
1894
+ { source: "event_data.total_discount", target: "pricing.totalDiscount" },
1895
+ { source: "event_data.shipping_price", target: "pricing.shippingPrice" },
1896
+ { source: "event_data.tax", target: "pricing.tax" },
1897
+ { source: "event_data.currency", target: "pricing.currency" },
1898
+ { source: "event_data.discount_codes[0]", target: "pricing.discountCode" },
1899
+ { source: "event_data.custom_attributes.landing_page_url", target: "attribution.landingPage + utm.*", note: "utm is PARSED from this query string" },
1900
+ { source: "event_data.latest_stage", target: "latestStage" },
1901
+ { source: "event_data.payment_status", target: "paymentStatus" },
1902
+ { source: "event_data.rtoPredict", target: "rtoPredict" },
1903
+ { source: "event_data.cart_id", target: "cartId" },
1904
+ { source: "event_data.cart_token", target: "cartToken" }
1905
+ ];
1906
+ var editableFields7 = [
1907
+ { group: "Event", path: "event_id", label: "Event id" },
1908
+ { group: "Cart", path: "event_data.cart_id", label: "Cart id" },
1909
+ { group: "Cart", path: "event_data.cart_token", label: "Cart token" },
1910
+ { group: "Cart", path: "event_data.checkout_url", label: "Checkout URL", hint: "the link the shopper is sent back to" },
1911
+ { group: "Cart", path: "event_data.latest_stage", label: "Latest stage" },
1912
+ { group: "Cart", path: "event_data.payment_status", label: "Payment status" },
1913
+ { group: "Cart", path: "event_data.rtoPredict", label: "RTO prediction", optional: true },
1914
+ { group: "Pricing", path: "event_data.total_price", label: "Total price", type: "number" },
1915
+ { group: "Pricing", path: "event_data.total_discount", label: "Discount", type: "number" },
1916
+ { group: "Pricing", path: "event_data.shipping_price", label: "Shipping", type: "number" },
1917
+ { group: "Pricing", path: "event_data.tax", label: "Tax", type: "number" },
1918
+ { group: "Pricing", path: "event_data.currency", label: "Currency" },
1919
+ { group: "Customer", path: "event_data.first_name", label: "First name" },
1920
+ { group: "Customer", path: "event_data.last_name", label: "Last name" },
1921
+ { group: "Customer", path: "event_data.email", label: "Email" },
1922
+ { group: "Customer", path: "event_data.phone_number", label: "Phone", hint: "bare national number, no country code" },
1923
+ { group: "Shipping", path: "event_data.shipping_address.address1", label: "Address line 1" },
1924
+ { group: "Shipping", path: "event_data.shipping_address.city", label: "City" },
1925
+ { group: "Shipping", path: "event_data.shipping_address.state", label: "State" },
1926
+ { group: "Shipping", path: "event_data.shipping_address.zip", label: "Zip / postcode" },
1927
+ { group: "Shipping", path: "event_data.shipping_address.country_code", label: "Country code", hint: "region used to parse the phone" },
1928
+ { group: "Attribution", path: "event_data.custom_attributes.landing_page_url", label: "Landing page URL", hint: "utm parameters are read out of this query string" }
1929
+ ];
1930
+ function buildPayload7(ctx) {
1931
+ const { config, items, phone } = ctx;
1932
+ const now = ctx.now ?? /* @__PURE__ */ new Date();
1933
+ const currency = ctx.currency || config.defaults.currency;
1934
+ const domain = config.shopify.domain;
1935
+ const cust = config.customer;
1936
+ const nationalPhone = nationalNumber(phone, cust.countryCode);
1937
+ const lineItems = (items ?? []).map(({ product, variant, quantity }) => ({
1938
+ sku: variant?.sku || "",
1939
+ name: product.title,
1940
+ title: product.title,
1941
+ price: money2(variant?.price ?? 0),
1942
+ quantity,
1943
+ product_id: Number(product.id),
1944
+ variant_id: Number(variant?.id ?? 0),
1945
+ img_url: imageForVariant(product, variant),
1946
+ custom_attributes: { contentId: String(product.id) }
1947
+ }));
1948
+ const subtotal = money2(lineItems.reduce((sum, li) => sum + li.price * li.quantity, 0));
1949
+ const discount = money2(Math.min(ctx.discount ?? 0, subtotal));
1950
+ const shippingPrice = ctx.shippingPrice ?? 0;
1951
+ const total = money2(subtotal - discount + shippingPrice);
1952
+ const landingPage = new URL(`https://${domain}/products/${items?.[0]?.product?.handle ?? ""}`);
1953
+ for (const [key, value] of Object.entries(config.defaults.utm ?? {})) {
1954
+ if (value) landingPage.searchParams.set(key, String(value));
1955
+ }
1956
+ const address = {
1957
+ zip: cust.zip,
1958
+ city: cust.city,
1959
+ state: cust.province,
1960
+ name: `${cust.firstName} ${cust.lastName}`.trim(),
1961
+ phone: nationalPhone,
1962
+ country: cust.country,
1963
+ address1: cust.address1,
1964
+ last_name: cust.lastName,
1965
+ first_name: cust.firstName,
1966
+ country_code: cust.countryCode,
1967
+ line_1: cust.address1,
1968
+ email: cust.email
1969
+ };
1970
+ const cartToken = `fastrr-${now.getTime()}`;
1971
+ return {
1972
+ event_id: `fr-${now.getTime()}`,
1973
+ event_name: GATE_VALUE3,
1974
+ event_data: {
1975
+ checkout_url: cartPermalink(domain, (items ?? []).map(({ variant, quantity }) => ({ variantId: variant?.id, quantity }))),
1976
+ total_discount: discount,
1977
+ latest_stage: "ORDER_SCREEN",
1978
+ product_id_list: lineItems.map((li) => String(li.product_id)),
1979
+ billing_address: address,
1980
+ custom_attributes: {
1981
+ landing_page_url: landingPage.toString(),
1982
+ ipv4_address: "192.168.1.1",
1983
+ shopifyCartToken: cartToken
1984
+ },
1985
+ cart_id: cartToken,
1986
+ updated_at: now.toISOString().replace("Z", ""),
1987
+ currency,
1988
+ shipping_address: address,
1989
+ first_name: cust.firstName,
1990
+ email: cust.email,
1991
+ source_name: "fastrr",
1992
+ item_count: lineItems.length,
1993
+ shipping_price: shippingPrice,
1994
+ variant_id_list: lineItems.map((li) => String(li.variant_id)),
1995
+ total_price: total,
1996
+ payment_status: "Pending",
1997
+ last_name: cust.lastName,
1998
+ tax: 0,
1999
+ item_title_list: lineItems.map((li) => li.title),
2000
+ item_name_list: lineItems.map((li) => li.name),
2001
+ img_url: lineItems[0]?.img_url,
2002
+ sku_list: lineItems.map((li) => li.sku),
2003
+ item_price_list: lineItems.map((li) => String(li.price)),
2004
+ cart_token: cartToken,
2005
+ phone_number: nationalPhone,
2006
+ items: lineItems,
2007
+ discount_codes: ctx.couponCode ? [ctx.couponCode] : [],
2008
+ rtoPredict: "low"
2009
+ }
2010
+ };
2011
+ }
2012
+ function sign7() {
2013
+ return { signature: null, timestamp: null, headers: { "content-type": "application/json" } };
2014
+ }
2015
+ function webhookUrl7(config) {
2016
+ return String(targetFor(config, "fastrr").webhookUrl ?? "").trim();
2017
+ }
2018
+ function webhookSecret7(config) {
2019
+ return String(targetFor(config, "fastrr").webhookSecret ?? "");
2020
+ }
2021
+ function summarize7(payload) {
2022
+ const d = payload?.event_data ?? {};
2023
+ let store;
2024
+ try {
2025
+ store = d.checkout_url ? new URL(d.checkout_url).host : void 0;
2026
+ } catch {
2027
+ store = d.checkout_url;
2028
+ }
2029
+ const items = Array.isArray(d.items) ? d.items : [];
2030
+ return {
2031
+ event: "abandoned cart",
2032
+ store,
2033
+ customer: `${d.first_name ?? ""} ${d.last_name ?? ""} \xB7 ${d.phone_number ?? ""}`.trim(),
2034
+ items: items.map((i) => `${i.name} x${i.quantity}`).join(", "),
2035
+ total: d.total_price,
2036
+ currency: d.currency,
2037
+ extra: `stage ${d.latest_stage}, rto ${d.rtoPredict}`,
2038
+ link: d.checkout_url
2039
+ };
2040
+ }
2041
+ var fastrr_default = {
2042
+ id: "fastrr",
2043
+ urlKey: "fastrr",
2044
+ label: "Fastrr",
2045
+ eventType: "fastrr_abandoned_cart",
2046
+ gate: { path: GATE_PATH4, value: GATE_VALUE3 },
2047
+ signatureScheme: "none documented \u2014 every request is accepted",
2048
+ signatureHeader: null,
2049
+ fieldMap: fieldMap7,
2050
+ editableFields: editableFields7,
2051
+ buildPayload: buildPayload7,
2052
+ sign: sign7,
2053
+ webhookUrl: webhookUrl7,
2054
+ webhookSecret: webhookSecret7,
2055
+ summarize: summarize7
2056
+ };
2057
+
1075
2058
  // src/providers/index.mjs
1076
2059
  var PROVIDER_META = [
1077
2060
  {
@@ -1095,12 +2078,46 @@ var PROVIDER_META = [
1095
2078
  header: "authorization",
1096
2079
  available: true,
1097
2080
  events: nitro_default.events
2081
+ },
2082
+ {
2083
+ id: "shopflo",
2084
+ label: "Shopflo",
2085
+ signature: "none documented \u2014 every request is accepted",
2086
+ header: null,
2087
+ available: true,
2088
+ events: shopflo_default.events
2089
+ },
2090
+ {
2091
+ id: "flexype",
2092
+ label: "FlexyPe",
2093
+ signature: "static shared secret in the header",
2094
+ header: "x-flexype-authorization",
2095
+ available: true
2096
+ },
2097
+ {
2098
+ id: "return-prime",
2099
+ label: "Return Prime",
2100
+ signature: "none documented \u2014 every request is accepted",
2101
+ header: null,
2102
+ available: true,
2103
+ events: return_prime_default.events
2104
+ },
2105
+ {
2106
+ id: "fastrr",
2107
+ label: "Fastrr",
2108
+ signature: "none documented \u2014 every request is accepted",
2109
+ header: null,
2110
+ available: true
1098
2111
  }
1099
2112
  ];
1100
2113
  var providers = {
1101
2114
  [cashfree_occ_default.id]: cashfree_occ_default,
1102
2115
  [razorpay_magic_default.id]: razorpay_magic_default,
1103
- [nitro_default.id]: nitro_default
2116
+ [nitro_default.id]: nitro_default,
2117
+ [shopflo_default.id]: shopflo_default,
2118
+ [flexype_default.id]: flexype_default,
2119
+ [return_prime_default.id]: return_prime_default,
2120
+ [fastrr_default.id]: fastrr_default
1104
2121
  };
1105
2122
  function getProvider(id) {
1106
2123
  const provider = providers[id];
@@ -1466,74 +2483,6 @@ function autoSelect(products, count = 1) {
1466
2483
  }));
1467
2484
  }
1468
2485
 
1469
- // src/phone.mjs
1470
- var CALLING_CODES = {
1471
- IN: "91",
1472
- US: "1",
1473
- CA: "1",
1474
- GB: "44",
1475
- AE: "971",
1476
- SG: "65",
1477
- AU: "61",
1478
- NZ: "64",
1479
- DE: "49",
1480
- FR: "33",
1481
- IT: "39",
1482
- ES: "34",
1483
- NL: "31",
1484
- SE: "46",
1485
- RO: "40",
1486
- PL: "48",
1487
- ZA: "27",
1488
- NG: "234",
1489
- KE: "254",
1490
- BD: "880",
1491
- PK: "92",
1492
- LK: "94",
1493
- NP: "977",
1494
- MY: "60",
1495
- ID: "62",
1496
- PH: "63",
1497
- TH: "66",
1498
- VN: "84",
1499
- JP: "81",
1500
- KR: "82",
1501
- CN: "86",
1502
- SA: "966",
1503
- QA: "974",
1504
- KW: "965",
1505
- OM: "968",
1506
- BH: "973",
1507
- BR: "55",
1508
- MX: "52"
1509
- };
1510
- function callingCode(regionCode) {
1511
- return CALLING_CODES[String(regionCode || "").toUpperCase()] || null;
1512
- }
1513
- function toE164(raw, regionCode) {
1514
- const cleaned = String(raw ?? "").replace(/\s+/g, "");
1515
- if (!cleaned) return { ok: false, reason: "phone is empty" };
1516
- if (cleaned.startsWith("+")) {
1517
- const digits2 = cleaned.slice(1).replace(/\D/g, "");
1518
- if (digits2.length < 8 || digits2.length > 15) return { ok: false, reason: `+${digits2} is not a plausible E.164 length` };
1519
- return { ok: true, value: `+${digits2}` };
1520
- }
1521
- const cc = callingCode(regionCode);
1522
- if (!cc) {
1523
- return {
1524
- ok: false,
1525
- reason: `no country calling code known for region "${regionCode}" \u2014 give the phone in +E.164 form instead`
1526
- };
1527
- }
1528
- let national = cleaned.replace(/\D/g, "").replace(/^0+/, "");
1529
- if (national.startsWith(cc) && national.length > 10) national = national.slice(cc.length);
1530
- const value = `+${cc}${national}`;
1531
- const digits = value.slice(1);
1532
- if (digits.length < 8 || digits.length > 15) return { ok: false, reason: `${value} is not a plausible E.164 length` };
1533
- return { ok: true, value };
1534
- }
1535
- var knownRegions = Object.keys(CALLING_CODES);
1536
-
1537
2486
  // src/core/paths.mjs
1538
2487
  function getPath(obj, path6) {
1539
2488
  return String(path6).replace(/\[(\d+)\]/g, ".$1").split(".").reduce((acc, key) => acc == null ? void 0 : acc[key], obj);
@@ -1701,7 +2650,19 @@ function locatePhone(payload) {
1701
2650
  ["phone", "customer.Shipping_address.Country_code"],
1702
2651
  ["customer.Shipping_address.Phone", "customer.Shipping_address.Country_code"],
1703
2652
  // nitro
1704
- ["eventVal.customer.phone", "country"]
2653
+ ["eventVal.customer.phone", "country"],
2654
+ // shopflo — one path per envelope, most specific first
2655
+ ["eventPayload.userData.phone", "eventPayload.clientData.country"],
2656
+ ["data.user_data.phone", "data.country_code"],
2657
+ ["customer.phone", "customer.country_code"],
2658
+ // flexype
2659
+ ["payload.user.phone_with_dial_code", "payload.checkout_context.presentment_country"],
2660
+ ["payload.user.phone", "payload.checkout_context.presentment_country"],
2661
+ // return prime — wrapped and root envelopes
2662
+ ["request.customer.phone", "request.customer.address.country_code"],
2663
+ // fastrr — a bare national number, so the region is what makes it parse
2664
+ ["event_data.phone_number", "event_data.shipping_address.country_code"],
2665
+ ["event_data.billing_address.phone", "event_data.billing_address.country_code"]
1705
2666
  ];
1706
2667
  for (const [phonePath, regionPath] of candidates) {
1707
2668
  const phone = getPath(payload, phonePath);
@@ -1736,8 +2697,8 @@ function checkAllowedPhone(payload, config) {
1736
2697
  detail: ok ? `${normalized} is an approved test handset` : `${normalized} is NOT in allowedPhones \u2014 refusing to risk messaging a real shopper`
1737
2698
  };
1738
2699
  }
1739
- function coverage(payload, fieldMap4) {
1740
- return fieldMap4.map((f) => {
2700
+ function coverage(payload, fieldMap8) {
2701
+ return fieldMap8.map((f) => {
1741
2702
  const value = getPath(payload, f.source);
1742
2703
  const present = value !== void 0 && value !== null && value !== "";
1743
2704
  return {
@@ -1864,7 +2825,7 @@ function loadPayload(file) {
1864
2825
  }
1865
2826
 
1866
2827
  // src/core/build.mjs
1867
- function draftPayload({ cfg, providerId = "cashfree-occ", items, discount = 0, phone, eventName, collection }) {
2828
+ function draftPayload({ cfg, providerId = "cashfree-occ", items, discount = 0, phone, eventName, collection, envelope }) {
1868
2829
  const provider = getProvider(providerId);
1869
2830
  const event = eventName ?? provider.defaultEvent;
1870
2831
  const resolvedPhone = phone ?? cfg.customer.phone;
@@ -1875,6 +2836,7 @@ function draftPayload({ cfg, providerId = "cashfree-occ", items, discount = 0, p
1875
2836
  discount,
1876
2837
  eventName: event,
1877
2838
  collection,
2839
+ envelope,
1878
2840
  phone: parsed.ok ? parsed.value : String(resolvedPhone ?? "")
1879
2841
  });
1880
2842
  const editable = provider.editableFieldsFor ? provider.editableFieldsFor(event) : provider.editableFields;
@@ -1890,7 +2852,7 @@ function applyEdits(payload, fields, edits) {
1890
2852
  }
1891
2853
  return payload;
1892
2854
  }
1893
- async function buildPayload4({ cfg, providerId = "cashfree-occ", items = [], discount = 0, phone, checkImageUrls = true, payload: prebuilt, eventName, collection }) {
2855
+ async function buildPayload8({ cfg, providerId = "cashfree-occ", items = [], discount = 0, phone, checkImageUrls = true, payload: prebuilt, eventName, collection, envelope }) {
1894
2856
  const provider0 = getProvider(providerId);
1895
2857
  const needsItems = !provider0.selectionFor || ["cart", "product"].includes(provider0.selectionFor(eventName ?? provider0.defaultEvent));
1896
2858
  if (needsItems && !items.length) throw new ConfigError("no products selected");
@@ -1899,7 +2861,7 @@ async function buildPayload4({ cfg, providerId = "cashfree-occ", items = [], dis
1899
2861
  const parsed = toE164(resolvedPhone, cfg.customer.countryCode);
1900
2862
  if (!parsed.ok) throw new ConfigError(`phone is unusable: ${parsed.reason}`);
1901
2863
  const event = eventName ?? provider.defaultEvent;
1902
- const payload = prebuilt ?? provider.buildPayload({ config: cfg, items, discount, phone: parsed.value, eventName: event, collection });
2864
+ const payload = prebuilt ?? provider.buildPayload({ config: cfg, items, discount, phone: parsed.value, eventName: event, collection, envelope });
1903
2865
  const body = JSON.stringify(payload);
1904
2866
  const report = await runAll({ payload, body, provider, config: cfg, checkImageUrls, eventName: event });
1905
2867
  const summary = provider.summarize ? provider.summarize(payload) : {};
@@ -2323,7 +3285,7 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
2323
3285
  const onDiscount = (raw) => startReview(Math.max(0, Number.parseFloat(raw) || 0));
2324
3286
  const finalise = (payload) => {
2325
3287
  setStep(STEP.BUILDING);
2326
- buildPayload4({ cfg: config, providerId, items, discount: draft.discount, payload, eventName, collection }).then(async (result) => {
3288
+ buildPayload8({ cfg: config, providerId, items, discount: draft.discount, payload, eventName, collection }).then(async (result) => {
2327
3289
  setBuilt(result);
2328
3290
  let req = null;
2329
3291
  let command = "";
@@ -3536,18 +4498,43 @@ async function cmdBuild(args, config) {
3536
4498
  const discount = Number.parseFloat(args.flags.discount ?? "0") || 0;
3537
4499
  const search = typeof args.flags.search === "string" ? args.flags.search : void 0;
3538
4500
  const checkImageUrls = args.flags.image !== false && args.flags["image-check"] !== false;
3539
- log.step(`fetching products from ${config.shopify.domain}\u2026`);
3540
- const products = await fetchCatalogue(config, { search });
3541
- if (!products.length) {
3542
- log.fail("Shopify returned no products");
3543
- return 1;
4501
+ const provider = getProvider(providerId);
4502
+ const eventName = typeof args.flags.event === "string" ? args.flags.event : provider.defaultEvent;
4503
+ const selection = provider.selectionFor ? provider.selectionFor(eventName) : "cart";
4504
+ let items = [];
4505
+ let collection;
4506
+ if (selection === "collection") {
4507
+ log.step(`fetching collections from ${config.shopify.domain}\u2026`);
4508
+ const collections = await fetchCollections(config, { search });
4509
+ if (!collections.length) {
4510
+ log.fail("Shopify returned no collections");
4511
+ log.dim("create one in Shopify, or pick a different event");
4512
+ return 1;
4513
+ }
4514
+ const wanted = typeof args.flags.collection === "string" ? args.flags.collection.toLowerCase() : null;
4515
+ collection = collections.find((c2) => wanted && (c2.id === wanted || c2.handle === wanted || c2.title.toLowerCase() === wanted)) ?? collections[0];
4516
+ if (wanted && collection.title.toLowerCase() !== wanted && collection.id !== wanted && collection.handle !== wanted) {
4517
+ log.warn(`no collection matched "${args.flags.collection}", using ${collection.title}`);
4518
+ }
4519
+ log.ok(`collection: ${collection.title}`);
4520
+ } else if (selection !== "none") {
4521
+ log.step(`fetching products from ${config.shopify.domain}\u2026`);
4522
+ const products = await fetchCatalogue(config, { search });
4523
+ if (!products.length) {
4524
+ log.fail("Shopify returned no products");
4525
+ return 1;
4526
+ }
4527
+ items = autoSelect(products, selection === "product" ? 1 : count);
4528
+ log.ok(`${items.length} product(s): ${items.map((i) => i.product.title).join(", ")}`);
4529
+ } else {
4530
+ log.ok(`${eventName} needs no product or collection`);
3544
4531
  }
3545
- const items = autoSelect(products, count);
3546
- log.ok(`${items.length} product(s): ${items.map((i) => i.product.title).join(", ")}`);
3547
- const built = await buildPayload4({
4532
+ const built = await buildPayload8({
3548
4533
  cfg: config,
3549
4534
  providerId,
3550
- eventName: typeof args.flags.event === "string" ? args.flags.event : void 0,
4535
+ eventName,
4536
+ collection,
4537
+ envelope: typeof args.flags.envelope === "string" ? args.flags.envelope : void 0,
3551
4538
  items,
3552
4539
  discount,
3553
4540
  phone: typeof args.flags.phone === "string" ? args.flags.phone : void 0,
@@ -3750,9 +4737,18 @@ ${c.bold("COMMANDS")}
3750
4737
  help show this message
3751
4738
 
3752
4739
  ${c.bold("BUILD OPTIONS")}
3753
- --provider <id> cashfree-occ | razorpay-magic | nitro (default cashfree-occ)
3754
- --event <name> nitro only: view \xB7 category_view \xB7 product_view \xB7 addtocart
3755
- removefromcart \xB7 checkout \xB7 orders/create \xB7 orders/updated
4740
+ --provider <id> cashfree-occ \xB7 razorpay-magic \xB7 nitro \xB7 shopflo
4741
+ flexype \xB7 return-prime \xB7 fastrr (default cashfree-occ)
4742
+ --event <name> multi-event providers only:
4743
+ nitro view \xB7 category_view \xB7 product_view \xB7 addtocart
4744
+ removefromcart \xB7 checkout \xB7 orders/create \xB7 orders/updated
4745
+ shopflo store_page_view \xB7 collection_page_viewed \xB7 product_page_viewed
4746
+ added_to_cart_ui \xB7 checkout_clicked \xB7 checkout_abandoned
4747
+ order_completed
4748
+ return-prime {return,exchange}_{requested,approved,received,
4749
+ inspected,rejected}
4750
+ --envelope <x> return-prime only: wrapped (default) or root
4751
+ --collection <x> category or collection view: collection id, handle or title
3756
4752
  --items <n> how many products to put in the cart (default 1)
3757
4753
  --search <text> only consider products matching a title
3758
4754
  --discount <n> cart discount in major currency units (default 0)