hookwright 1.2.3 → 1.4.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 (2) hide show
  1. package/dist/cli.js +1128 -158
  2. 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.3",
27
+ version: "1.4.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
  }
@@ -801,7 +809,7 @@ var FIELD_MAPS = {
801
809
  ...PAGE_FIELDS,
802
810
  { source: "eventVal.resource_id", target: "eventVal.resource_id", required: true, note: "the collection id" },
803
811
  { source: "eventVal.resource", target: "eventVal.resource" },
804
- { source: "eventVal.title", target: "collection name", required: true },
812
+ { source: "eventVal.resource_id", target: "collection lookup", required: true, note: "the consumer fetches the collection by this id" },
805
813
  { source: "eventVal.domain", target: "eventVal.domain" }
806
814
  ],
807
815
  product_view: [
@@ -820,7 +828,7 @@ var FIELD_MAPS = {
820
828
  { source: "eventVal.init_cart", target: "eventVal.init_cart" },
821
829
  { source: "eventVal.empty_cart", target: "eventVal.empty_cart" },
822
830
  { source: "eventVal.bik_customer_id", target: "eventVal.bik_customer_id" },
823
- { source: "eventVal.line_items[0].name", target: "cart enrichment", required: true }
831
+ { source: "eventVal.line_items[0].title", target: "cart enrichment", required: true }
824
832
  ],
825
833
  removefromcart: [
826
834
  ...COMMON_FIELDS,
@@ -828,7 +836,7 @@ var FIELD_MAPS = {
828
836
  { source: "eventVal.init_cart", target: "eventVal.init_cart" },
829
837
  { source: "eventVal.empty_cart", target: "eventVal.empty_cart" },
830
838
  { source: "eventVal.bik_customer_id", target: "eventVal.bik_customer_id" },
831
- { source: "eventVal.line_items[0].name", target: "cart enrichment" }
839
+ { source: "eventVal.line_items[0].title", target: "cart enrichment" }
832
840
  ],
833
841
  checkout: [
834
842
  ...COMMON_FIELDS,
@@ -867,7 +875,7 @@ var EDITABLE = {
867
875
  ],
868
876
  collection: [
869
877
  { group: "Collection", path: "eventVal.resource_id", label: "Collection id" },
870
- { group: "Collection", path: "eventVal.title", label: "Collection name" },
878
+ { group: "Collection", path: "eventVal.resource_id", label: "Collection id", type: "number" },
871
879
  { group: "Collection", path: "eventVal.l", label: "Collection URL" }
872
880
  ],
873
881
  product: [
@@ -912,104 +920,102 @@ function buildPayload3(ctx) {
912
920
  const session = crypto3.randomBytes(8).toString("hex");
913
921
  const first = items[0];
914
922
  const lineItems = items.map(({ product, variant, quantity }) => ({
915
- id: variant ? String(variant.id) : null,
916
- product_id: String(product.id),
917
- name: product.title,
918
- title: product.title,
919
923
  quantity,
920
- price: money2(variant?.price ?? 0),
921
- image: imageForVariant(product, variant),
922
- url: `https://${domain}/products/${product.handle ?? ""}`
924
+ title: product.title,
925
+ line_price: money2((variant?.price ?? 0) * quantity).toFixed(2),
926
+ id: Number(variant?.id ?? product.id),
927
+ product_id: Number(product.id),
928
+ image_url: imageForVariant(product, variant)
923
929
  }));
924
- const cartValue = money2(lineItems.reduce((sum, li) => sum + li.price * li.quantity, 0));
925
- const productUrl = first ? `https://${domain}/products/${first.product.handle ?? ""}` : `https://${domain}`;
930
+ const cartValue = money2(items.reduce((sum, i) => sum + money2(i.variant?.price ?? 0) * i.quantity, 0));
926
931
  const customer = {
927
- phone,
928
932
  email: cust.email,
929
- name: `${cust.firstName} ${cust.lastName}`.trim(),
930
- first_name: cust.firstName,
931
- last_name: cust.lastName
933
+ phone,
934
+ name: `${cust.firstName} ${cust.lastName}`.trim()
932
935
  };
936
+ const pageUrl = eventName === "product_view" ? `https://${domain}/products/${first?.product?.handle ?? ""}` : eventName === "category_view" ? `https://${domain}/collections/${ctx.collection?.handle ?? "all"}` : `https://${domain}/`;
937
+ const isBrowsing = ["view", "category_view", "product_view"].includes(eventName);
933
938
  const base = {
934
- org_token: config.defaults.orgToken ?? crypto3.randomBytes(12).toString("hex"),
939
+ org_token: config.defaults.orgToken ?? crypto3.randomUUID(),
935
940
  eventName,
941
+ u: isBrowsing ? pageUrl : "",
942
+ lang: config.defaults.lang ?? "en",
936
943
  userId: crypto3.randomUUID(),
937
944
  timestamp: now.toISOString(),
938
945
  country: cust.countryCode,
939
- domain
940
- };
941
- const page = {
942
- page: eventName === "product_view" ? "product" : eventName === "category_view" ? "collection" : "home",
943
- h: domain,
944
- l: productUrl,
945
- _ss: session,
946
- domain
946
+ state: isBrowsing ? cust.province : null,
947
+ city: isBrowsing ? cust.city : null,
948
+ pincode: isBrowsing ? cust.zip : null
947
949
  };
948
950
  let eventVal;
949
951
  switch (eventName) {
950
952
  case "view":
951
- eventVal = { page: "home", h: domain, l: `https://${domain}`, _ss: session, domain, customer };
953
+ eventVal = { page: pageUrl, h: domain, l: pageUrl, _ss: session, customer };
952
954
  break;
953
- case "category_view": {
954
- const collection = ctx.collection;
955
+ case "category_view":
955
956
  eventVal = {
956
- ...page,
957
- page: "collection",
958
- resource: "collection",
959
- resource_id: String(collection?.id ?? ""),
960
- title: collection?.title,
961
- image: collection?.image || void 0,
962
- l: collection?.handle ? `https://${domain}/collections/${collection.handle}` : `https://${domain}/collections/all`,
957
+ page: pageUrl,
958
+ resource_id: ctx.collection?.id ? Number(ctx.collection.id) : null,
959
+ resource: ctx.collection?.id ? "collection" : null,
960
+ domain,
961
+ h: domain,
962
+ l: pageUrl,
963
+ _ss: session,
963
964
  customer
964
965
  };
965
966
  break;
966
- }
967
967
  case "product_view":
968
968
  eventVal = {
969
- ...page,
969
+ page: pageUrl,
970
+ resource_id: Number(first?.product?.id ?? 0),
970
971
  resource: "product",
971
- resource_id: String(first?.product?.id ?? ""),
972
- title: first?.product?.title,
973
- image: first ? imageForVariant(first.product, first.variant) : void 0,
972
+ domain,
974
973
  price: money2(first?.variant?.price ?? 0),
974
+ h: domain,
975
+ l: pageUrl,
976
+ _ss: session,
977
+ image: first ? imageForVariant(first.product, first.variant) : void 0,
978
+ title: first?.product?.title,
975
979
  customer
976
980
  };
977
981
  break;
978
982
  case "addtocart":
979
- case "removefromcart":
983
+ case "removefromcart": {
984
+ const added = eventName === "addtocart";
980
985
  eventVal = {
981
- cart_value: cartValue,
982
- init_cart: eventName === "addtocart",
983
- empty_cart: eventName === "removefromcart" && items.length === 0,
984
- bik_customer_id: crypto3.randomUUID(),
985
- line_items: lineItems,
986
- [eventName === "addtocart" ? "items_added" : "items_removed"]: lineItems,
987
- customer
986
+ line_items: added ? lineItems : [],
987
+ cart_value: added ? cartValue : 0,
988
+ recent_product_image: added ? lineItems[0]?.image_url ?? null : null,
989
+ init_cart: false,
990
+ empty_cart: !added,
991
+ items_added: added ? lineItems : [],
992
+ items_removed: added ? [] : lineItems,
993
+ customer,
994
+ bik_customer_id: null
988
995
  };
989
996
  break;
997
+ }
990
998
  case "checkout":
991
999
  eventVal = {
992
1000
  checkout: cartPermalink(domain, items.map(({ variant, quantity }) => ({ variantId: variant?.id, quantity }))),
993
- cart_value: cartValue,
994
- bik_customer_id: crypto3.randomUUID(),
995
- line_items: lineItems,
996
- customer
1001
+ cart_value: cartValue.toFixed(2),
1002
+ customer,
1003
+ bik_customer_id: null
997
1004
  };
998
1005
  break;
999
1006
  case "orders/create":
1000
1007
  case "orders/updated": {
1001
1008
  const orderNumber = 1e3 + Math.floor(Math.random() * 9e3);
1002
1009
  eventVal = {
1003
- url: `https://${domain}/admin/orders/${orderNumber}`,
1004
- order_id: String(Date.now()),
1010
+ url: `https://${domain}/${crypto3.randomBytes(5).toString("hex")}/orders/${crypto3.randomBytes(16).toString("hex")}/authenticate?key=${crypto3.randomBytes(16).toString("hex")}`,
1011
+ order_id: Number(`${Date.now()}`.slice(-13)),
1005
1012
  order_number: orderNumber,
1006
- order_name: `#${orderNumber}`,
1007
- price: cartValue,
1013
+ order_name: crypto3.randomBytes(4).toString("hex").toUpperCase(),
1014
+ price: cartValue.toFixed(2),
1008
1015
  currency,
1009
1016
  order_created_at: now.toISOString(),
1010
- bik_customer_id: crypto3.randomUUID(),
1011
- line_items: lineItems,
1012
- customer
1017
+ customer,
1018
+ bik_customer_id: null
1013
1019
  };
1014
1020
  break;
1015
1021
  }
@@ -1039,9 +1045,10 @@ function summarize3(payload) {
1039
1045
  const items = Array.isArray(ev.line_items) ? ev.line_items : [];
1040
1046
  return {
1041
1047
  event: payload?.eventName,
1042
- store: payload?.domain,
1048
+ // the schema keeps the domain inside eventVal, not at the root
1049
+ store: payload?.eventVal?.h ?? payload?.eventVal?.domain,
1043
1050
  customer: `${ev.customer?.name ?? ""} \xB7 ${ev.customer?.phone ?? ""}`.trim(),
1044
- items: items.map((i) => `${i.name} x${i.quantity}`).join(", ") || "\u2014",
1051
+ items: items.map((i) => `${i.title} x${i.quantity}`).join(", ") || payload?.eventVal?.title || "\u2014",
1045
1052
  total: ev.cart_value ?? ev.price ?? void 0,
1046
1053
  currency: ev.currency,
1047
1054
  extra: ev.order_name ? `order ${ev.order_name}` : ev.checkout ? "checkout started" : "",
@@ -1072,6 +1079,981 @@ var nitro_default = {
1072
1079
  summarize: summarize3
1073
1080
  };
1074
1081
 
1082
+ // src/providers/shopflo.mjs
1083
+ var EVENTS2 = [
1084
+ { eventName: "store_page_view", type: "shopflo_store_page_view", label: "Store Page View", selection: "none", subject: "a page, no product", envelope: "eventPayload" },
1085
+ { eventName: "collection_page_viewed", type: "shopflo_collection_page_viewed", label: "Collection Page View", selection: "collection", subject: "one collection", envelope: "data" },
1086
+ { eventName: "product_page_viewed", type: "shopflo_product_page_viewed", label: "Product Page View", selection: "product", subject: "one product", envelope: "data" },
1087
+ { eventName: "added_to_cart_ui", type: "shopflo_added_to_cart", label: "Added To Cart", selection: "cart", subject: "cart contents", envelope: "root" },
1088
+ { eventName: "checkout_clicked", type: "shopflo_checkout_clicked", label: "Checkout Clicked", selection: "cart", subject: "cart contents", envelope: "root" },
1089
+ { eventName: "checkout_abandoned", type: "shopflo_checkout_abandoned", label: "Abandoned Checkout", selection: "cart", subject: "cart contents", envelope: "root" },
1090
+ { eventName: "order_completed", type: "shopflo_order_completed", label: "Order Completed", selection: "cart", subject: "ordered items", envelope: "root" }
1091
+ ];
1092
+ var DEFAULT_EVENT2 = "checkout_abandoned";
1093
+ var eventDef = (eventName) => EVENTS2.find((e) => e.eventName === eventName) ?? EVENTS2.find((e) => e.eventName === DEFAULT_EVENT2);
1094
+ function eventFromPayload2(payload) {
1095
+ return payload?.event_name ?? payload?.eventName;
1096
+ }
1097
+ function selectionFor2(eventName) {
1098
+ return eventDef(eventName).selection;
1099
+ }
1100
+ var COMMON_FIELDS2 = [
1101
+ { source: "event_name / eventName", target: "\xABgate\xBB", required: true, note: "the schema matches EITHER spelling" },
1102
+ { source: "customer.phone / user_data.phone", target: "phone", required: true, note: "missing phone is rejected on every event" }
1103
+ ];
1104
+ var BROWSING_FIELDS = [
1105
+ { source: "uiData.pageUrl / data.page_url", target: "page.url" },
1106
+ { source: "uiData.pageTitle / data.page_title", target: "page.title" },
1107
+ { source: "uiData.landingPageUrl", target: "page.landingPage" },
1108
+ { source: "uiData.referrerPageUrl", target: "page.referrer" },
1109
+ { source: "clientData.brandUrl", target: "storeUrl" },
1110
+ { source: "session_id", target: "sessionId" }
1111
+ ];
1112
+ var CART_FIELDS = [
1113
+ { source: "line_items[].title", target: "cart.productName", required: true, note: "the first PAID line wins; freebies rank last" },
1114
+ { source: "line_items[].product_image", target: "cart.image" },
1115
+ { source: "line_items[].price", target: "\u2014 (ranking only)", note: "a zero price marks the line a freebie" },
1116
+ { source: "line_items[].quantity", target: "cart.quantity" },
1117
+ { source: "total_price", target: "pricing.totalPrice" },
1118
+ { source: "subtotal_price", target: "pricing.subtotal" },
1119
+ { source: "total_discount", target: "pricing.totalDiscount" },
1120
+ { source: "currency", target: "pricing.currency" },
1121
+ { source: "note_attributes[shopflo_checkout_url]", target: "checkoutUrl", note: "checkout_abandoned puts the link here, not in token_id" }
1122
+ ];
1123
+ var FIELD_MAPS2 = {
1124
+ store_page_view: [...COMMON_FIELDS2, ...BROWSING_FIELDS],
1125
+ collection_page_viewed: [...COMMON_FIELDS2, ...BROWSING_FIELDS, { source: "data.collection_page_url", target: "collection.url" }],
1126
+ product_page_viewed: [
1127
+ ...COMMON_FIELDS2,
1128
+ ...BROWSING_FIELDS,
1129
+ { source: "data.product_name", target: "product.name" },
1130
+ { source: "data.product_image", target: "product.image" },
1131
+ { source: "data.product_url", target: "product.url" },
1132
+ { source: "data.product_price", target: "product.price" }
1133
+ ],
1134
+ added_to_cart_ui: [...COMMON_FIELDS2, ...CART_FIELDS],
1135
+ checkout_clicked: [...COMMON_FIELDS2, ...CART_FIELDS],
1136
+ checkout_abandoned: [...COMMON_FIELDS2, ...CART_FIELDS],
1137
+ order_completed: [
1138
+ ...COMMON_FIELDS2,
1139
+ ...CART_FIELDS,
1140
+ { source: "order_id", target: "order.id" },
1141
+ { source: "order_name", target: "order.name" },
1142
+ { source: "payment_mode", target: "order.paymentMode" },
1143
+ { source: "total_payable", target: "order.totalPayable" }
1144
+ ]
1145
+ };
1146
+ var fieldMap4 = FIELD_MAPS2[DEFAULT_EVENT2];
1147
+ function editableFieldsFor2(eventName) {
1148
+ const def = eventDef(eventName);
1149
+ const base = [
1150
+ { group: "Event", path: "session_id", label: "Session id" },
1151
+ { group: "Customer", path: "customer.first_name", label: "First name" },
1152
+ { group: "Customer", path: "customer.last_name", label: "Last name" },
1153
+ { group: "Customer", path: "customer.email", label: "Email" },
1154
+ { group: "Customer", path: "customer.phone", label: "Phone", hint: "must parse to E.164" }
1155
+ ];
1156
+ if (def.envelope === "eventPayload") {
1157
+ return [
1158
+ { group: "Event", path: "eventPayload.uiData.pageUrl", label: "Page URL" },
1159
+ { group: "Event", path: "eventPayload.uiData.pageTitle", label: "Page title" },
1160
+ { group: "Event", path: "eventPayload.clientData.brandUrl", label: "Store URL" },
1161
+ { group: "Customer", path: "eventPayload.userData.firstName", label: "First name" },
1162
+ { group: "Customer", path: "eventPayload.userData.email", label: "Email" },
1163
+ { group: "Customer", path: "eventPayload.userData.phone", label: "Phone", hint: "must parse to E.164" }
1164
+ ];
1165
+ }
1166
+ if (def.envelope === "data") {
1167
+ return [
1168
+ { group: "Event", path: "data.page_url", label: "Page URL" },
1169
+ { group: "Event", path: "data.page_title", label: "Page title" },
1170
+ { group: "Customer", path: "data.user_data.firstName", label: "First name" },
1171
+ { group: "Customer", path: "data.user_data.email", label: "Email" },
1172
+ { group: "Customer", path: "data.user_data.phone", label: "Phone", hint: "must parse to E.164" }
1173
+ ];
1174
+ }
1175
+ return [
1176
+ ...base,
1177
+ { group: "Pricing", path: "total_price", label: "Total price", type: "number" },
1178
+ { group: "Pricing", path: "subtotal_price", label: "Subtotal", type: "number" },
1179
+ { group: "Pricing", path: "total_discount", label: "Discount", type: "number" },
1180
+ { group: "Pricing", path: "currency", label: "Currency" },
1181
+ ...def.eventName === "order_completed" ? [
1182
+ { group: "Order", path: "order_name", label: "Order name" },
1183
+ { group: "Order", path: "payment_mode", label: "Payment mode" },
1184
+ { group: "Order", path: "total_payable", label: "Total payable", type: "number" }
1185
+ ] : []
1186
+ ];
1187
+ }
1188
+ var editableFields4 = editableFieldsFor2(DEFAULT_EVENT2);
1189
+ function buildPayload4(ctx) {
1190
+ const { config, items, phone, collection } = ctx;
1191
+ const eventName = ctx.eventName ?? DEFAULT_EVENT2;
1192
+ const def = eventDef(eventName);
1193
+ const now = ctx.now ?? /* @__PURE__ */ new Date();
1194
+ const currency = ctx.currency || config.defaults.currency;
1195
+ const domain = config.shopify.domain;
1196
+ const cust = config.customer;
1197
+ const first = items?.[0];
1198
+ if (def.envelope === "eventPayload") {
1199
+ return {
1200
+ eventName,
1201
+ session_id: ctx.sessionId ?? `sf-${now.getTime()}`,
1202
+ channel: "web",
1203
+ eventPayload: {
1204
+ userData: { firstName: cust.firstName, lastName: cust.lastName, email: cust.email, phone },
1205
+ uiData: {
1206
+ pageUrl: `https://${domain}/`,
1207
+ pageTitle: config.shopify.shop?.name ?? domain,
1208
+ landingPageUrl: `https://${domain}/`,
1209
+ referrerPageUrl: ""
1210
+ },
1211
+ clientData: { brandUrl: `https://${domain}` }
1212
+ },
1213
+ timestamp: now.getTime()
1214
+ };
1215
+ }
1216
+ if (def.envelope === "data") {
1217
+ const isProduct = def.selection === "product";
1218
+ return {
1219
+ event_name: eventName,
1220
+ session_id: ctx.sessionId ?? `sf-${now.getTime()}`,
1221
+ channel: "web",
1222
+ data: {
1223
+ page_url: isProduct ? `https://${domain}/products/${first?.product?.handle ?? ""}` : `https://${domain}/collections/${collection?.handle ?? "all"}`,
1224
+ page_title: isProduct ? first?.product?.title : collection?.title,
1225
+ landing_page_url: `https://${domain}/`,
1226
+ referrer_page_url: "",
1227
+ ...isProduct ? {
1228
+ product_name: first?.product?.title,
1229
+ product_image: imageForVariant(first?.product, first?.variant),
1230
+ product_url: `https://${domain}/products/${first?.product?.handle ?? ""}`,
1231
+ product_price: money2(first?.variant?.price ?? 0),
1232
+ product_compare_at_price: money2(first?.variant?.compareAtPrice ?? first?.variant?.price ?? 0),
1233
+ product_type: first?.product?.productType ?? ""
1234
+ } : { collection_page_url: `https://${domain}/collections/${collection?.handle ?? "all"}` },
1235
+ user_data: { firstName: cust.firstName, lastName: cust.lastName, email: cust.email, phone }
1236
+ },
1237
+ timestamp: now.getTime()
1238
+ };
1239
+ }
1240
+ const lineItems = (items ?? []).map(({ product, variant, quantity }) => ({
1241
+ id: String(variant?.id ?? product.id),
1242
+ product_id: String(product.id),
1243
+ title: product.title,
1244
+ price: money2(variant?.price ?? 0).toFixed(2),
1245
+ quantity,
1246
+ product_image: imageForVariant(product, variant)
1247
+ }));
1248
+ const subtotal = money2(lineItems.reduce((sum, li) => sum + Number(li.price) * li.quantity, 0));
1249
+ const discount = money2(Math.min(ctx.discount ?? 0, subtotal));
1250
+ const total = money2(subtotal - discount);
1251
+ const checkoutUrl = cartPermalink(domain, (items ?? []).map(({ variant, quantity }) => ({ variantId: variant?.id, quantity })));
1252
+ return {
1253
+ event_name: eventName,
1254
+ session_id: ctx.sessionId ?? `sf-${now.getTime()}`,
1255
+ token_id: checkoutUrl,
1256
+ source: "shopflo",
1257
+ channel: "web",
1258
+ email: cust.email,
1259
+ phone,
1260
+ created_at: now.toISOString(),
1261
+ // checkout_abandoned carries the recovery link here; the consumer prefers it
1262
+ note_attributes: [
1263
+ { name: "landing_page", value: `/products/${items?.[0]?.product?.handle ?? ""}` },
1264
+ { name: "shopflo_checkout_url", value: checkoutUrl }
1265
+ ],
1266
+ line_items: lineItems,
1267
+ customer: { uid: "sf-user", first_name: cust.firstName, last_name: cust.lastName, email: cust.email, phone },
1268
+ currency,
1269
+ subtotal_price: subtotal,
1270
+ total_discount: discount,
1271
+ total_price: total,
1272
+ ...eventName === "order_completed" ? {
1273
+ order_id: ctx.orderId ?? Math.floor(now.getTime() / 1e3),
1274
+ order_name: ctx.orderName ?? `#${String(now.getTime()).slice(-5)}`,
1275
+ payment_mode: "prepaid",
1276
+ total_shipping: 0,
1277
+ total_tax: 0,
1278
+ total_payable: total
1279
+ } : {},
1280
+ timestamp: now.getTime()
1281
+ };
1282
+ }
1283
+ function sign4() {
1284
+ return { signature: null, timestamp: null, headers: { "content-type": "application/json" } };
1285
+ }
1286
+ function webhookUrl4(config) {
1287
+ return String(targetFor(config, "shopflo").webhookUrl ?? "").trim();
1288
+ }
1289
+ function webhookSecret4(config) {
1290
+ return String(targetFor(config, "shopflo").webhookSecret ?? "");
1291
+ }
1292
+ function storeOf(payload) {
1293
+ const brandUrl = payload?.eventPayload?.clientData?.brandUrl;
1294
+ const candidate = brandUrl ?? payload?.token_id;
1295
+ if (!candidate) return void 0;
1296
+ try {
1297
+ return new URL(candidate).host;
1298
+ } catch {
1299
+ return candidate;
1300
+ }
1301
+ }
1302
+ function summarize4(payload) {
1303
+ const name = eventFromPayload2(payload);
1304
+ const items = Array.isArray(payload?.line_items) ? payload.line_items : [];
1305
+ const envelope = payload?.eventPayload ?? payload?.data ?? {};
1306
+ const user = envelope?.userData ?? envelope?.user_data ?? payload?.customer ?? {};
1307
+ return {
1308
+ event: eventDef(name).label.toLowerCase(),
1309
+ store: storeOf(payload),
1310
+ customer: `${user.firstName ?? user.first_name ?? ""} ${user.lastName ?? user.last_name ?? ""} \xB7 ${payload?.phone ?? user.phone ?? ""}`.replace(/\s+/g, " ").trim(),
1311
+ items: items.map((i) => `${i.title} x${i.quantity}`).join(", ") || (envelope?.product_name ?? "\u2014"),
1312
+ total: payload?.total_price,
1313
+ currency: payload?.currency,
1314
+ extra: `envelope: ${eventDef(name).envelope}`,
1315
+ link: payload?.note_attributes?.find((n) => n.name === "shopflo_checkout_url")?.value ?? payload?.token_id
1316
+ };
1317
+ }
1318
+ var shopflo_default = {
1319
+ id: "shopflo",
1320
+ urlKey: "shopflo",
1321
+ label: "Shopflo",
1322
+ events: EVENTS2,
1323
+ defaultEvent: DEFAULT_EVENT2,
1324
+ eventType: "shopflo_checkout_abandoned",
1325
+ gate: { path: "event_name", value: DEFAULT_EVENT2 },
1326
+ gateFor: (eventName) => ({ path: eventDef(eventName).envelope === "eventPayload" ? "eventName" : "event_name", value: eventName }),
1327
+ signatureScheme: "none documented \u2014 every request is accepted",
1328
+ signatureHeader: null,
1329
+ fieldMap: fieldMap4,
1330
+ fieldMapFor: (eventName) => FIELD_MAPS2[eventName] ?? FIELD_MAPS2[DEFAULT_EVENT2],
1331
+ editableFields: editableFields4,
1332
+ editableFieldsFor: editableFieldsFor2,
1333
+ selectionFor: selectionFor2,
1334
+ eventFromPayload: eventFromPayload2,
1335
+ buildPayload: buildPayload4,
1336
+ sign: sign4,
1337
+ webhookUrl: webhookUrl4,
1338
+ webhookSecret: webhookSecret4,
1339
+ summarize: summarize4
1340
+ };
1341
+
1342
+ // src/phone.mjs
1343
+ var CALLING_CODES = {
1344
+ IN: "91",
1345
+ US: "1",
1346
+ CA: "1",
1347
+ GB: "44",
1348
+ AE: "971",
1349
+ SG: "65",
1350
+ AU: "61",
1351
+ NZ: "64",
1352
+ DE: "49",
1353
+ FR: "33",
1354
+ IT: "39",
1355
+ ES: "34",
1356
+ NL: "31",
1357
+ SE: "46",
1358
+ RO: "40",
1359
+ PL: "48",
1360
+ ZA: "27",
1361
+ NG: "234",
1362
+ KE: "254",
1363
+ BD: "880",
1364
+ PK: "92",
1365
+ LK: "94",
1366
+ NP: "977",
1367
+ MY: "60",
1368
+ ID: "62",
1369
+ PH: "63",
1370
+ TH: "66",
1371
+ VN: "84",
1372
+ JP: "81",
1373
+ KR: "82",
1374
+ CN: "86",
1375
+ SA: "966",
1376
+ QA: "974",
1377
+ KW: "965",
1378
+ OM: "968",
1379
+ BH: "973",
1380
+ BR: "55",
1381
+ MX: "52"
1382
+ };
1383
+ function callingCode(regionCode) {
1384
+ return CALLING_CODES[String(regionCode || "").toUpperCase()] || null;
1385
+ }
1386
+ function toE164(raw, regionCode) {
1387
+ const cleaned = String(raw ?? "").replace(/\s+/g, "");
1388
+ if (!cleaned) return { ok: false, reason: "phone is empty" };
1389
+ if (cleaned.startsWith("+")) {
1390
+ const digits2 = cleaned.slice(1).replace(/\D/g, "");
1391
+ if (digits2.length < 8 || digits2.length > 15) return { ok: false, reason: `+${digits2} is not a plausible E.164 length` };
1392
+ return { ok: true, value: `+${digits2}` };
1393
+ }
1394
+ const cc = callingCode(regionCode);
1395
+ if (!cc) {
1396
+ return {
1397
+ ok: false,
1398
+ reason: `no country calling code known for region "${regionCode}" \u2014 give the phone in +E.164 form instead`
1399
+ };
1400
+ }
1401
+ let national = cleaned.replace(/\D/g, "").replace(/^0+/, "");
1402
+ if (national.startsWith(cc) && national.length > 10) national = national.slice(cc.length);
1403
+ const value = `+${cc}${national}`;
1404
+ const digits = value.slice(1);
1405
+ if (digits.length < 8 || digits.length > 15) return { ok: false, reason: `${value} is not a plausible E.164 length` };
1406
+ return { ok: true, value };
1407
+ }
1408
+ var knownRegions = Object.keys(CALLING_CODES);
1409
+ function splitPhone(raw, regionCode) {
1410
+ const digits = String(raw ?? "").replace(/[^\d]/g, "");
1411
+ if (!digits) return { dialCode: "", national: "" };
1412
+ const fromRegion = callingCode(regionCode);
1413
+ if (fromRegion && digits.startsWith(fromRegion)) {
1414
+ return { dialCode: fromRegion, national: digits.slice(fromRegion.length) };
1415
+ }
1416
+ const longestFirst = [...new Set(Object.values(CALLING_CODES))].sort((a, b) => b.length - a.length);
1417
+ const matched = longestFirst.find((code) => digits.startsWith(code));
1418
+ if (matched) return { dialCode: matched, national: digits.slice(matched.length) };
1419
+ return { dialCode: "", national: digits };
1420
+ }
1421
+ function nationalNumber(raw, regionCode) {
1422
+ return splitPhone(raw, regionCode).national;
1423
+ }
1424
+
1425
+ // src/providers/flexype.mjs
1426
+ var GATE_PATH3 = "event_type";
1427
+ var GATE_VALUE2 = "ABANDONED_CHECKOUT";
1428
+ var fieldMap5 = [
1429
+ { source: "event_type", target: "\xABgate\xBB", required: true, note: "must be ABANDONED_CHECKOUT or the event is dropped" },
1430
+ { source: "payload.user.phone_with_dial_code", target: "phone", required: true, note: "falls back to dial_code + phone" },
1431
+ { source: "payload.user.first_name", target: "customer.firstName" },
1432
+ { source: "payload.user.last_name", target: "customer.lastName" },
1433
+ { source: "payload.user.full_name", target: "customer.name" },
1434
+ { source: "payload.user.email", target: "customer.email" },
1435
+ { source: "payload.checkout_url", target: "checkoutUrl", required: true },
1436
+ { source: "payload.session_id", target: "sessionId" },
1437
+ { source: "payload.session_state", target: "sessionState" },
1438
+ { source: "payload.cart_items[].title", target: "cart.productName", required: true, note: "the first PAID line wins; freebies rank last" },
1439
+ { source: "payload.cart_items[].image", target: "cart.image" },
1440
+ { source: "payload.cart_items[].total_price", target: "pricing.totalPrice", note: "summed across the cart" },
1441
+ { source: "payload.cart_items[].total_discount", target: "pricing.totalDiscount", note: "summed across the cart" },
1442
+ { source: "payload.cart_items[].quantity", target: "cart.quantity" },
1443
+ { source: "payload.coupons[0].code", target: "pricing.couponCode" },
1444
+ { source: "payload.checkout_context.presentment_currency", target: "pricing.currency" },
1445
+ { source: "payload.checkout_context.presentment_country", target: "phone region" },
1446
+ { source: "payload.metadata.utm[]", target: "utm.*", note: "a list of {name,value} pairs, not an object" },
1447
+ { source: "payload.metadata.landing_page", target: "attribution.landingPage" },
1448
+ { source: "store.shopify_domain", target: "storeUrl" },
1449
+ { source: "store.name", target: "store.name" },
1450
+ { source: "event_id", target: "eventId" },
1451
+ { source: "event_time", target: "eventTime" }
1452
+ ];
1453
+ var editableFields5 = [
1454
+ { group: "Event", path: "event_id", label: "Event id" },
1455
+ { group: "Event", path: "event_time", label: "Event time" },
1456
+ { group: "Checkout", path: "payload.session_id", label: "Session id" },
1457
+ { group: "Checkout", path: "payload.checkout_url", label: "Checkout URL", hint: "the link the shopper is sent back to" },
1458
+ { group: "Checkout", path: "payload.session_state", label: "Session state" },
1459
+ { group: "Customer", path: "payload.user.first_name", label: "First name" },
1460
+ { group: "Customer", path: "payload.user.last_name", label: "Last name" },
1461
+ { group: "Customer", path: "payload.user.email", label: "Email" },
1462
+ { group: "Customer", path: "payload.user.phone_with_dial_code", label: "Phone", hint: "already E.164 shaped" },
1463
+ { group: "Pricing", path: "payload.checkout_context.presentment_currency", label: "Currency" },
1464
+ { group: "Pricing", path: "payload.coupons.0.code", label: "Coupon code", optional: true },
1465
+ { group: "Store", path: "store.name", label: "Store name" },
1466
+ { group: "Store", path: "store.shopify_domain", label: "Shopify domain" },
1467
+ { group: "Attribution", path: "payload.metadata.landing_page", label: "Landing page", optional: true }
1468
+ ];
1469
+ function buildPayload5(ctx) {
1470
+ const { config, items, phone } = ctx;
1471
+ const now = ctx.now ?? /* @__PURE__ */ new Date();
1472
+ const currency = ctx.currency || config.defaults.currency;
1473
+ const domain = config.shopify.domain;
1474
+ const cust = config.customer;
1475
+ const { dialCode, national } = splitPhone(phone, cust.countryCode);
1476
+ const cartItems = (items ?? []).map(({ product, variant, quantity }) => {
1477
+ const unit = money2(variant?.price ?? 0);
1478
+ return {
1479
+ title: product.title,
1480
+ image: imageForVariant(product, variant),
1481
+ price: unit,
1482
+ total_price: money2(unit * quantity),
1483
+ total_discount: 0,
1484
+ quantity,
1485
+ variant_id: String(variant?.id ?? ""),
1486
+ product_id: String(product.id),
1487
+ sku: variant?.sku || null
1488
+ };
1489
+ });
1490
+ const checkoutUrl = cartPermalink(domain, (items ?? []).map(({ variant, quantity }) => ({ variantId: variant?.id, quantity })));
1491
+ const utm = Object.entries(config.defaults.utm ?? {}).map(([name, value]) => ({ name, value }));
1492
+ return {
1493
+ event_id: `fx-${now.getTime()}`,
1494
+ event_type: GATE_VALUE2,
1495
+ event_time: now.toISOString(),
1496
+ store: {
1497
+ id: 1,
1498
+ name: config.shopify.shop?.name ?? domain,
1499
+ shopify_domain: domain,
1500
+ main_domain: `https://${domain}`
1501
+ },
1502
+ payload: {
1503
+ session_id: `fx-sess-${now.getTime()}`,
1504
+ session_state: "ABANDONED",
1505
+ checkout_url: checkoutUrl,
1506
+ user: {
1507
+ first_name: cust.firstName,
1508
+ last_name: cust.lastName,
1509
+ full_name: `${cust.firstName} ${cust.lastName}`.trim(),
1510
+ email: cust.email,
1511
+ phone: national,
1512
+ dial_code: dialCode,
1513
+ phone_with_dial_code: phone
1514
+ },
1515
+ checkout_context: {
1516
+ presentment_currency: currency,
1517
+ store_currency: currency,
1518
+ presentment_country: cust.countryCode,
1519
+ store_country: cust.country
1520
+ },
1521
+ cart_items: cartItems,
1522
+ coupons: ctx.couponCode ? [{ code: ctx.couponCode }] : [],
1523
+ metadata: {
1524
+ landing_page: `https://${domain}/products/${items?.[0]?.product?.handle ?? ""}`,
1525
+ orig_referer: "",
1526
+ referer_host: "",
1527
+ utm
1528
+ }
1529
+ }
1530
+ };
1531
+ }
1532
+ function sign5({ secret }) {
1533
+ return {
1534
+ signature: null,
1535
+ timestamp: null,
1536
+ headers: {
1537
+ "content-type": "application/json",
1538
+ "x-flexype-authorization": String(secret ?? "")
1539
+ }
1540
+ };
1541
+ }
1542
+ function webhookUrl5(config) {
1543
+ return String(targetFor(config, "flexype").webhookUrl ?? "").trim();
1544
+ }
1545
+ function webhookSecret5(config) {
1546
+ return String(targetFor(config, "flexype").webhookSecret ?? "");
1547
+ }
1548
+ function summarize5(payload) {
1549
+ const p = payload?.payload ?? {};
1550
+ const items = Array.isArray(p.cart_items) ? p.cart_items : [];
1551
+ return {
1552
+ event: "abandoned checkout",
1553
+ store: payload?.store?.shopify_domain,
1554
+ customer: `${p.user?.full_name ?? ""} \xB7 ${p.user?.phone_with_dial_code ?? ""}`.trim(),
1555
+ items: items.map((i) => `${i.title} x${i.quantity}`).join(", "),
1556
+ total: items.reduce((sum, i) => sum + Number(i.total_price ?? 0), 0),
1557
+ currency: p.checkout_context?.presentment_currency,
1558
+ extra: p.coupons?.[0]?.code ? `coupon ${p.coupons[0].code}` : "no coupon",
1559
+ link: p.checkout_url
1560
+ };
1561
+ }
1562
+ var flexype_default = {
1563
+ id: "flexype",
1564
+ urlKey: "flexype",
1565
+ label: "FlexyPe",
1566
+ eventType: "flexype_abandoned_checkout",
1567
+ gate: { path: GATE_PATH3, value: GATE_VALUE2 },
1568
+ signatureScheme: "static shared secret in the header (no body signature)",
1569
+ signatureHeader: "x-flexype-authorization",
1570
+ fieldMap: fieldMap5,
1571
+ editableFields: editableFields5,
1572
+ buildPayload: buildPayload5,
1573
+ sign: sign5,
1574
+ webhookUrl: webhookUrl5,
1575
+ webhookSecret: webhookSecret5,
1576
+ summarize: summarize5
1577
+ };
1578
+
1579
+ // src/providers/return-prime.mjs
1580
+ var STATUSES = ["requested", "approved", "received", "inspected", "rejected"];
1581
+ var titleCase = (v) => v.charAt(0).toUpperCase() + v.slice(1);
1582
+ var EVENTS3 = ["return", "exchange"].flatMap(
1583
+ (requestType) => STATUSES.map((status) => ({
1584
+ eventName: `${requestType}_${status}`,
1585
+ requestType,
1586
+ status,
1587
+ type: `return_prime_${requestType}_${status}`,
1588
+ label: `${titleCase(requestType)} ${titleCase(status)}`,
1589
+ selection: requestType === "exchange" ? "exchange" : "product",
1590
+ subject: requestType === "exchange" ? "the returned product and its replacement" : "the returned product"
1591
+ }))
1592
+ );
1593
+ var DEFAULT_EVENT3 = "return_requested";
1594
+ var eventDef2 = (eventName) => EVENTS3.find((e) => e.eventName === eventName) ?? EVENTS3.find((e) => e.eventName === DEFAULT_EVENT3);
1595
+ function eventFromPayload3(payload) {
1596
+ const request = payload?.request ?? payload ?? {};
1597
+ if (!request.request_type || !request.status) return void 0;
1598
+ return `${request.request_type}_${request.status}`;
1599
+ }
1600
+ function selectionFor3(eventName) {
1601
+ return eventDef2(eventName).selection;
1602
+ }
1603
+ var COMMON_FIELDS3 = [
1604
+ { source: "request_type", target: "\xABgate\xBB + requestType", required: true, note: "return or exchange" },
1605
+ { source: "status", target: "\xABgate\xBB + status", required: true, note: "requested, approved, received, inspected or rejected" },
1606
+ { source: "customer.phone", target: "phone", required: true, note: "missing phone is rejected on every event" },
1607
+ { source: "customer.address.country_code", target: "phone region" },
1608
+ { source: "request_number", target: "requestNumber" },
1609
+ { source: "order.name", target: "order.name" },
1610
+ { source: "line_items[].original_product.title", target: "product.name", required: true, note: "the first PAID line wins; freebies rank last" },
1611
+ { source: "line_items[].original_product.image.src", target: "product.image" },
1612
+ { source: "line_items[].original_product.price", target: "product.price", note: "a zero price marks the line a freebie" },
1613
+ { source: "line_items[].reason", target: "reason" },
1614
+ { source: "line_items[].presentment_price.actual_amount", target: "pricing.totalPrice" },
1615
+ { source: "line_items[].return_fee.price_set\u2026amount", target: "pricing.returnFee" },
1616
+ { source: "line_items[].refund.status", target: "refund.status" },
1617
+ { source: "line_items[].refund.meta.discount_code", target: "refund.discountCode", note: "only present once store credit is issued" },
1618
+ { source: "line_items[].shipping[0].awb", target: "shipping.awb" },
1619
+ { source: "approved/received/inspected/rejected.created_at", target: "timeline.*" }
1620
+ ];
1621
+ var EXCHANGE_FIELDS = [
1622
+ { source: "line_items[].exchange_product.title", target: "exchangeProduct.name", note: "exchanges only \u2014 returns never carry it" },
1623
+ { source: "line_items[].exchange_product.price", target: "exchangeProduct.price + pricing.priceDifference", note: "difference is signed: positive means the shopper owes more" }
1624
+ ];
1625
+ var fieldMap6 = COMMON_FIELDS3;
1626
+ function fieldMapFor(eventName) {
1627
+ return eventDef2(eventName).requestType === "exchange" ? [...COMMON_FIELDS3, ...EXCHANGE_FIELDS] : COMMON_FIELDS3;
1628
+ }
1629
+ function editableFieldsFor3(eventName) {
1630
+ const def = eventDef2(eventName);
1631
+ const p = (path6) => def.envelope === "root" ? path6 : `request.${path6}`;
1632
+ return [
1633
+ { group: "Request", path: p("request_number"), label: "Request number" },
1634
+ { group: "Request", path: p("created_at"), label: "Created at" },
1635
+ { group: "Request", path: p("line_items.0.reason"), label: "Reason" },
1636
+ { group: "Order", path: p("order.name"), label: "Order name" },
1637
+ { group: "Customer", path: p("customer.name"), label: "Customer name" },
1638
+ { group: "Customer", path: p("customer.email"), label: "Email" },
1639
+ { group: "Customer", path: p("customer.phone"), label: "Phone", hint: "already E.164 shaped" },
1640
+ { group: "Shipping", path: p("customer.address.address_line_1"), label: "Address line 1" },
1641
+ { group: "Shipping", path: p("customer.address.city"), label: "City" },
1642
+ { group: "Shipping", path: p("customer.address.province"), label: "Province / state" },
1643
+ { group: "Shipping", path: p("customer.address.postal_code"), label: "Postal code" },
1644
+ { group: "Shipping", path: p("customer.address.country_code"), label: "Country code", hint: "region used to parse the phone" },
1645
+ { group: "Pricing", path: p("line_items.0.presentment_price.actual_amount"), label: "Item amount", type: "number" },
1646
+ { group: "Pricing", path: p("line_items.0.presentment_price.currency"), label: "Currency" }
1647
+ ];
1648
+ }
1649
+ var editableFields6 = editableFieldsFor3(DEFAULT_EVENT3);
1650
+ function statusBlock(reached, at, comment = null) {
1651
+ return { status: reached, comment, created_at: reached ? at : null };
1652
+ }
1653
+ function buildPayload6(ctx) {
1654
+ const { config, items, phone } = ctx;
1655
+ const eventName = ctx.eventName ?? DEFAULT_EVENT3;
1656
+ const def = eventDef2(eventName);
1657
+ const now = ctx.now ?? /* @__PURE__ */ new Date();
1658
+ const currency = ctx.currency || config.defaults.currency;
1659
+ const cust = config.customer;
1660
+ const at = now.toISOString();
1661
+ const first = items?.[0];
1662
+ const second = items?.[1] ?? first;
1663
+ const originalPrice = money2(first?.variant?.price ?? 0);
1664
+ const exchangePrice = money2(second?.variant?.price ?? 0);
1665
+ const reachedIndex = STATUSES.indexOf(def.status);
1666
+ const reached = (name) => STATUSES.indexOf(name) <= reachedIndex && def.status !== "requested";
1667
+ const lineItem = {
1668
+ id: Number(first?.variant?.id ?? first?.product?.id ?? 0),
1669
+ refund: {
1670
+ requested_mode: "store_credit",
1671
+ actual_mode: def.status === "rejected" ? "store_credit" : null,
1672
+ meta: def.status === "rejected" && ctx.couponCode ? { discount_code: ctx.couponCode } : null,
1673
+ status: def.status === "rejected" ? "refunded" : "pending",
1674
+ refunded_amount: {
1675
+ shop_money: { ...def.status === "rejected" ? { amount: originalPrice } : {}, currency_code: currency },
1676
+ presentment_money: { ...def.status === "rejected" ? { amount: originalPrice } : {}, currency_code: currency }
1677
+ },
1678
+ refunded_at: def.status === "rejected" ? at : null,
1679
+ comment: null
1680
+ },
1681
+ return_fee: {
1682
+ price_set: {
1683
+ shop_money: { amount: ctx.returnFee ?? 0, currency_code: currency },
1684
+ presentment_money: { amount: ctx.returnFee ?? 0, currency_code: currency }
1685
+ },
1686
+ rule: "order"
1687
+ },
1688
+ exchange_fee: {
1689
+ price_set: {
1690
+ shop_money: { amount: null, currency_code: null },
1691
+ presentment_money: { amount: null, currency_code: null }
1692
+ }
1693
+ },
1694
+ exchange: { order: null },
1695
+ original_product: {
1696
+ title: first?.product?.title,
1697
+ variant_title: first?.variant?.title ?? null,
1698
+ product_id: Number(first?.product?.id ?? 0),
1699
+ variant_id: Number(first?.variant?.id ?? 0),
1700
+ image: { src: imageForVariant(first?.product, first?.variant) },
1701
+ price: originalPrice,
1702
+ sku: first?.variant?.sku || "",
1703
+ variant_deleted: false,
1704
+ product_deleted: false
1705
+ },
1706
+ ...def.requestType === "exchange" ? {
1707
+ exchange_product: {
1708
+ product_id: Number(second?.product?.id ?? 0),
1709
+ title: second?.product?.title,
1710
+ variant_title: second?.variant?.title ?? "Default Title",
1711
+ variant_id: Number(second?.variant?.id ?? 0),
1712
+ image: { src: imageForVariant(second?.product, second?.variant) },
1713
+ price: exchangePrice,
1714
+ sku: second?.variant?.sku || "",
1715
+ variant_deleted: false,
1716
+ product_deleted: false
1717
+ }
1718
+ } : {},
1719
+ shipping: [
1720
+ def.status === "requested" ? { tracking_available: false, labels: [] } : {
1721
+ shipping_company: "bluedartV2",
1722
+ tracking_available: true,
1723
+ awb: String(now.getTime()).slice(-11),
1724
+ tracking_updated_at: null,
1725
+ labels: [],
1726
+ shipment_status: "Requested",
1727
+ tracking_url: `https://www.bluedart.com/trackdartresultthirdparty?trackFor=0&trackNo=${String(now.getTime()).slice(-11)}`
1728
+ }
1729
+ ],
1730
+ quantity: first?.quantity ?? 1,
1731
+ reason: ctx.reason ?? "Size of Fit Issues",
1732
+ shop_price: {
1733
+ actual_amount: originalPrice,
1734
+ total_tax: 0,
1735
+ return_quantity: first?.quantity ?? 1,
1736
+ total_discount: 0,
1737
+ shipping_amount: 0,
1738
+ taxes_included: true,
1739
+ currency,
1740
+ do_not_carry_forward_discount: true
1741
+ },
1742
+ presentment_price: {
1743
+ actual_amount: originalPrice,
1744
+ total_tax: 0,
1745
+ return_quantity: first?.quantity ?? 1,
1746
+ total_discount: 0,
1747
+ shipping_amount: 0,
1748
+ taxes_included: true,
1749
+ currency,
1750
+ do_not_carry_forward_discount: true
1751
+ },
1752
+ shopify_order_fulfillment_location: null,
1753
+ return_location: def.status === "requested" ? null : {
1754
+ id: "rp-location-1",
1755
+ fullName: config.shopify.shop?.name ?? config.shopify.domain,
1756
+ mobileNumber: nationalNumber(phone, cust.countryCode),
1757
+ pinCode: cust.zip,
1758
+ streetAddress1: cust.address1,
1759
+ streetAddress2: cust.address2 ?? "",
1760
+ landmark: "",
1761
+ city: cust.city,
1762
+ state: cust.province,
1763
+ stateCode: cust.provinceCode,
1764
+ countryCode: cust.countryCode,
1765
+ country: cust.country,
1766
+ store: config.shopify.domain,
1767
+ facilityCode: "",
1768
+ isDefault: true,
1769
+ latitude: "",
1770
+ longitude: "",
1771
+ inStoreReturnExchange: false,
1772
+ external: false,
1773
+ createdAt: at,
1774
+ updatedAt: at
1775
+ },
1776
+ notes: null,
1777
+ tags: []
1778
+ };
1779
+ const request = {
1780
+ id: `rp-${now.getTime()}`,
1781
+ request_number: ctx.requestNumber ?? `${def.requestType === "exchange" ? "EXC" : "RET"}${String(now.getTime()).slice(-3)}`,
1782
+ request_type: def.requestType,
1783
+ status: def.status,
1784
+ manual_request: false,
1785
+ channel: 97426,
1786
+ smart_exchange: false,
1787
+ payment_details: null,
1788
+ order: {
1789
+ id: Number(ctx.orderId ?? String(now.getTime()).slice(-10)),
1790
+ name: ctx.orderName ?? `#${String(now.getTime()).slice(-5)}`,
1791
+ order_manual_payment: false,
1792
+ payment_gateways: [],
1793
+ fulfillments: [{ id: Number(String(now.getTime()).slice(-10)), line_items: [lineItem.id], delivery_status: null, delivery_date: null }],
1794
+ created_at: at
1795
+ },
1796
+ customer: {
1797
+ id: Number(String(now.getTime()).slice(-10)),
1798
+ name: `${cust.firstName} ${cust.lastName}`.trim(),
1799
+ email: cust.email,
1800
+ phone,
1801
+ address: {
1802
+ first_name: cust.firstName,
1803
+ last_name: cust.lastName || null,
1804
+ postal_code: cust.zip,
1805
+ city: cust.city,
1806
+ province: cust.province,
1807
+ country_code: cust.countryCode,
1808
+ province_code: cust.provinceCode,
1809
+ address_line_1: cust.address1,
1810
+ address_line_2: cust.address2 ?? "",
1811
+ country: cust.country,
1812
+ address_line_3: ""
1813
+ },
1814
+ bank: { account_holder_name: "", account_number: "", confirm_account_number: "", ifsc_code: "" }
1815
+ },
1816
+ approved: statusBlock(reached("approved"), at),
1817
+ received: statusBlock(reached("received"), at),
1818
+ inspected: statusBlock(reached("inspected"), at, def.status === "inspected" ? "quickreply test" : null),
1819
+ rejected: statusBlock(def.status === "rejected", at),
1820
+ archived: statusBlock(false, at),
1821
+ unarchived: statusBlock(false, at),
1822
+ incentive: null,
1823
+ line_items: [lineItem],
1824
+ created_at: at
1825
+ };
1826
+ return ctx.envelope === "root" ? request : { request };
1827
+ }
1828
+ function sign6() {
1829
+ return { signature: null, timestamp: null, headers: { "content-type": "application/json" } };
1830
+ }
1831
+ function webhookUrl6(config) {
1832
+ return String(targetFor(config, "return-prime").webhookUrl ?? "").trim();
1833
+ }
1834
+ function webhookSecret6(config) {
1835
+ return String(targetFor(config, "return-prime").webhookSecret ?? "");
1836
+ }
1837
+ function summarize6(payload) {
1838
+ const r = payload?.request ?? payload ?? {};
1839
+ const item = (Array.isArray(r.line_items) ? r.line_items : [])[0] ?? {};
1840
+ const original = item.original_product ?? {};
1841
+ const exchange = item.exchange_product;
1842
+ const difference = exchange ? money2(Number(exchange.price ?? 0) - Number(original.price ?? 0)) : void 0;
1843
+ return {
1844
+ event: `${r.request_type ?? "?"} ${r.status ?? "?"}`,
1845
+ store: item.return_location?.store ?? r.order?.name,
1846
+ customer: `${r.customer?.name ?? ""} \xB7 ${r.customer?.phone ?? ""}`.trim(),
1847
+ items: exchange ? `${original.title} \u2192 ${exchange.title}` : original.title,
1848
+ total: item.presentment_price?.actual_amount,
1849
+ currency: item.presentment_price?.currency,
1850
+ extra: difference === void 0 ? `${r.request_number ?? ""}` : `difference ${difference > 0 ? "+" : ""}${difference} (${difference > 0 ? "shopper pays" : "refund due"})`,
1851
+ link: null
1852
+ };
1853
+ }
1854
+ var return_prime_default = {
1855
+ id: "return-prime",
1856
+ urlKey: "return_prime",
1857
+ label: "Return Prime",
1858
+ events: EVENTS3,
1859
+ defaultEvent: DEFAULT_EVENT3,
1860
+ eventType: "return_prime_return_requested",
1861
+ gate: { path: "request.request_type", value: "return" },
1862
+ gateFor: (eventName) => ({ path: "request.status", value: eventDef2(eventName).status }),
1863
+ signatureScheme: "none documented \u2014 every request is accepted",
1864
+ signatureHeader: null,
1865
+ fieldMap: fieldMap6,
1866
+ fieldMapFor,
1867
+ editableFields: editableFields6,
1868
+ editableFieldsFor: editableFieldsFor3,
1869
+ selectionFor: selectionFor3,
1870
+ eventFromPayload: eventFromPayload3,
1871
+ buildPayload: buildPayload6,
1872
+ sign: sign6,
1873
+ webhookUrl: webhookUrl6,
1874
+ webhookSecret: webhookSecret6,
1875
+ summarize: summarize6
1876
+ };
1877
+
1878
+ // src/providers/fastrr.mjs
1879
+ var GATE_PATH4 = "event_name";
1880
+ var GATE_VALUE3 = "ABANDON_CART";
1881
+ var fieldMap7 = [
1882
+ { source: "event_name", target: "\xABgate\xBB", required: true, note: "must be ABANDON_CART or the event is dropped" },
1883
+ { source: "event_id", target: "eventId" },
1884
+ { source: "event_data.phone_number", target: "phone", required: true, note: "BARE national number \u2014 region comes from the address" },
1885
+ { source: "event_data.shipping_address.country_code", target: "phone region", required: true },
1886
+ { source: "event_data.checkout_url", target: "checkoutUrl", required: true },
1887
+ { source: "event_data.items[].name", target: "cart.productName", required: true, note: "the first PAID line wins; freebies rank last" },
1888
+ { source: "event_data.items[].img_url", target: "cart.image" },
1889
+ { source: "event_data.items[].price", target: "\u2014 (ranking only)", note: "a zero price marks the line a freebie" },
1890
+ { source: "event_data.items[].sku", target: "cart.sku" },
1891
+ { source: "event_data.item_count", target: "cart.itemCount" },
1892
+ { source: "event_data.total_price", target: "pricing.totalPrice" },
1893
+ { source: "event_data.total_discount", target: "pricing.totalDiscount" },
1894
+ { source: "event_data.shipping_price", target: "pricing.shippingPrice" },
1895
+ { source: "event_data.tax", target: "pricing.tax" },
1896
+ { source: "event_data.currency", target: "pricing.currency" },
1897
+ { source: "event_data.discount_codes[0]", target: "pricing.discountCode" },
1898
+ { source: "event_data.custom_attributes.landing_page_url", target: "attribution.landingPage + utm.*", note: "utm is PARSED from this query string" },
1899
+ { source: "event_data.latest_stage", target: "latestStage" },
1900
+ { source: "event_data.payment_status", target: "paymentStatus" },
1901
+ { source: "event_data.rtoPredict", target: "rtoPredict" },
1902
+ { source: "event_data.cart_id", target: "cartId" },
1903
+ { source: "event_data.cart_token", target: "cartToken" }
1904
+ ];
1905
+ var editableFields7 = [
1906
+ { group: "Event", path: "event_id", label: "Event id" },
1907
+ { group: "Cart", path: "event_data.cart_id", label: "Cart id" },
1908
+ { group: "Cart", path: "event_data.cart_token", label: "Cart token" },
1909
+ { group: "Cart", path: "event_data.checkout_url", label: "Checkout URL", hint: "the link the shopper is sent back to" },
1910
+ { group: "Cart", path: "event_data.latest_stage", label: "Latest stage" },
1911
+ { group: "Cart", path: "event_data.payment_status", label: "Payment status" },
1912
+ { group: "Cart", path: "event_data.rtoPredict", label: "RTO prediction", optional: true },
1913
+ { group: "Pricing", path: "event_data.total_price", label: "Total price", type: "number" },
1914
+ { group: "Pricing", path: "event_data.total_discount", label: "Discount", type: "number" },
1915
+ { group: "Pricing", path: "event_data.shipping_price", label: "Shipping", type: "number" },
1916
+ { group: "Pricing", path: "event_data.tax", label: "Tax", type: "number" },
1917
+ { group: "Pricing", path: "event_data.currency", label: "Currency" },
1918
+ { group: "Customer", path: "event_data.first_name", label: "First name" },
1919
+ { group: "Customer", path: "event_data.last_name", label: "Last name" },
1920
+ { group: "Customer", path: "event_data.email", label: "Email" },
1921
+ { group: "Customer", path: "event_data.phone_number", label: "Phone", hint: "bare national number, no country code" },
1922
+ { group: "Shipping", path: "event_data.shipping_address.address1", label: "Address line 1" },
1923
+ { group: "Shipping", path: "event_data.shipping_address.city", label: "City" },
1924
+ { group: "Shipping", path: "event_data.shipping_address.state", label: "State" },
1925
+ { group: "Shipping", path: "event_data.shipping_address.zip", label: "Zip / postcode" },
1926
+ { group: "Shipping", path: "event_data.shipping_address.country_code", label: "Country code", hint: "region used to parse the phone" },
1927
+ { group: "Attribution", path: "event_data.custom_attributes.landing_page_url", label: "Landing page URL", hint: "utm parameters are read out of this query string" }
1928
+ ];
1929
+ function buildPayload7(ctx) {
1930
+ const { config, items, phone } = ctx;
1931
+ const now = ctx.now ?? /* @__PURE__ */ new Date();
1932
+ const currency = ctx.currency || config.defaults.currency;
1933
+ const domain = config.shopify.domain;
1934
+ const cust = config.customer;
1935
+ const nationalPhone = nationalNumber(phone, cust.countryCode);
1936
+ const lineItems = (items ?? []).map(({ product, variant, quantity }) => ({
1937
+ sku: variant?.sku || "",
1938
+ name: product.title,
1939
+ title: product.title,
1940
+ price: money2(variant?.price ?? 0),
1941
+ quantity,
1942
+ product_id: Number(product.id),
1943
+ variant_id: Number(variant?.id ?? 0),
1944
+ img_url: imageForVariant(product, variant),
1945
+ custom_attributes: { contentId: String(product.id) }
1946
+ }));
1947
+ const subtotal = money2(lineItems.reduce((sum, li) => sum + li.price * li.quantity, 0));
1948
+ const discount = money2(Math.min(ctx.discount ?? 0, subtotal));
1949
+ const shippingPrice = ctx.shippingPrice ?? 0;
1950
+ const total = money2(subtotal - discount + shippingPrice);
1951
+ const landingPage = new URL(`https://${domain}/products/${items?.[0]?.product?.handle ?? ""}`);
1952
+ for (const [key, value] of Object.entries(config.defaults.utm ?? {})) {
1953
+ if (value) landingPage.searchParams.set(key, String(value));
1954
+ }
1955
+ const address = {
1956
+ zip: cust.zip,
1957
+ city: cust.city,
1958
+ state: cust.province,
1959
+ name: `${cust.firstName} ${cust.lastName}`.trim(),
1960
+ phone: nationalPhone,
1961
+ country: cust.country,
1962
+ address1: cust.address1,
1963
+ last_name: cust.lastName,
1964
+ first_name: cust.firstName,
1965
+ country_code: cust.countryCode,
1966
+ line_1: cust.address1,
1967
+ email: cust.email
1968
+ };
1969
+ const cartToken = `fastrr-${now.getTime()}`;
1970
+ return {
1971
+ event_id: `fr-${now.getTime()}`,
1972
+ event_name: GATE_VALUE3,
1973
+ event_data: {
1974
+ checkout_url: cartPermalink(domain, (items ?? []).map(({ variant, quantity }) => ({ variantId: variant?.id, quantity }))),
1975
+ total_discount: discount,
1976
+ latest_stage: "ORDER_SCREEN",
1977
+ product_id_list: lineItems.map((li) => String(li.product_id)),
1978
+ billing_address: address,
1979
+ custom_attributes: {
1980
+ landing_page_url: landingPage.toString(),
1981
+ ipv4_address: "192.168.1.1",
1982
+ shopifyCartToken: cartToken
1983
+ },
1984
+ cart_id: cartToken,
1985
+ updated_at: now.toISOString().replace("Z", ""),
1986
+ currency,
1987
+ shipping_address: address,
1988
+ first_name: cust.firstName,
1989
+ email: cust.email,
1990
+ source_name: "fastrr",
1991
+ item_count: lineItems.length,
1992
+ shipping_price: shippingPrice,
1993
+ variant_id_list: lineItems.map((li) => String(li.variant_id)),
1994
+ total_price: total,
1995
+ payment_status: "Pending",
1996
+ last_name: cust.lastName,
1997
+ tax: 0,
1998
+ item_title_list: lineItems.map((li) => li.title),
1999
+ item_name_list: lineItems.map((li) => li.name),
2000
+ img_url: lineItems[0]?.img_url,
2001
+ sku_list: lineItems.map((li) => li.sku),
2002
+ item_price_list: lineItems.map((li) => String(li.price)),
2003
+ cart_token: cartToken,
2004
+ phone_number: nationalPhone,
2005
+ items: lineItems,
2006
+ discount_codes: ctx.couponCode ? [ctx.couponCode] : [],
2007
+ rtoPredict: "low"
2008
+ }
2009
+ };
2010
+ }
2011
+ function sign7() {
2012
+ return { signature: null, timestamp: null, headers: { "content-type": "application/json" } };
2013
+ }
2014
+ function webhookUrl7(config) {
2015
+ return String(targetFor(config, "fastrr").webhookUrl ?? "").trim();
2016
+ }
2017
+ function webhookSecret7(config) {
2018
+ return String(targetFor(config, "fastrr").webhookSecret ?? "");
2019
+ }
2020
+ function summarize7(payload) {
2021
+ const d = payload?.event_data ?? {};
2022
+ let store;
2023
+ try {
2024
+ store = d.checkout_url ? new URL(d.checkout_url).host : void 0;
2025
+ } catch {
2026
+ store = d.checkout_url;
2027
+ }
2028
+ const items = Array.isArray(d.items) ? d.items : [];
2029
+ return {
2030
+ event: "abandoned cart",
2031
+ store,
2032
+ customer: `${d.first_name ?? ""} ${d.last_name ?? ""} \xB7 ${d.phone_number ?? ""}`.trim(),
2033
+ items: items.map((i) => `${i.name} x${i.quantity}`).join(", "),
2034
+ total: d.total_price,
2035
+ currency: d.currency,
2036
+ extra: `stage ${d.latest_stage}, rto ${d.rtoPredict}`,
2037
+ link: d.checkout_url
2038
+ };
2039
+ }
2040
+ var fastrr_default = {
2041
+ id: "fastrr",
2042
+ urlKey: "fastrr",
2043
+ label: "Fastrr",
2044
+ eventType: "fastrr_abandoned_cart",
2045
+ gate: { path: GATE_PATH4, value: GATE_VALUE3 },
2046
+ signatureScheme: "none documented \u2014 every request is accepted",
2047
+ signatureHeader: null,
2048
+ fieldMap: fieldMap7,
2049
+ editableFields: editableFields7,
2050
+ buildPayload: buildPayload7,
2051
+ sign: sign7,
2052
+ webhookUrl: webhookUrl7,
2053
+ webhookSecret: webhookSecret7,
2054
+ summarize: summarize7
2055
+ };
2056
+
1075
2057
  // src/providers/index.mjs
1076
2058
  var PROVIDER_META = [
1077
2059
  {
@@ -1095,12 +2077,46 @@ var PROVIDER_META = [
1095
2077
  header: "authorization",
1096
2078
  available: true,
1097
2079
  events: nitro_default.events
2080
+ },
2081
+ {
2082
+ id: "shopflo",
2083
+ label: "Shopflo",
2084
+ signature: "none documented \u2014 every request is accepted",
2085
+ header: null,
2086
+ available: true,
2087
+ events: shopflo_default.events
2088
+ },
2089
+ {
2090
+ id: "flexype",
2091
+ label: "FlexyPe",
2092
+ signature: "static shared secret in the header",
2093
+ header: "x-flexype-authorization",
2094
+ available: true
2095
+ },
2096
+ {
2097
+ id: "return-prime",
2098
+ label: "Return Prime",
2099
+ signature: "none documented \u2014 every request is accepted",
2100
+ header: null,
2101
+ available: true,
2102
+ events: return_prime_default.events
2103
+ },
2104
+ {
2105
+ id: "fastrr",
2106
+ label: "Fastrr",
2107
+ signature: "none documented \u2014 every request is accepted",
2108
+ header: null,
2109
+ available: true
1098
2110
  }
1099
2111
  ];
1100
2112
  var providers = {
1101
2113
  [cashfree_occ_default.id]: cashfree_occ_default,
1102
2114
  [razorpay_magic_default.id]: razorpay_magic_default,
1103
- [nitro_default.id]: nitro_default
2115
+ [nitro_default.id]: nitro_default,
2116
+ [shopflo_default.id]: shopflo_default,
2117
+ [flexype_default.id]: flexype_default,
2118
+ [return_prime_default.id]: return_prime_default,
2119
+ [fastrr_default.id]: fastrr_default
1104
2120
  };
1105
2121
  function getProvider(id) {
1106
2122
  const provider = providers[id];
@@ -1466,74 +2482,6 @@ function autoSelect(products, count = 1) {
1466
2482
  }));
1467
2483
  }
1468
2484
 
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
2485
  // src/core/paths.mjs
1538
2486
  function getPath(obj, path6) {
1539
2487
  return String(path6).replace(/\[(\d+)\]/g, ".$1").split(".").reduce((acc, key) => acc == null ? void 0 : acc[key], obj);
@@ -1701,7 +2649,19 @@ function locatePhone(payload) {
1701
2649
  ["phone", "customer.Shipping_address.Country_code"],
1702
2650
  ["customer.Shipping_address.Phone", "customer.Shipping_address.Country_code"],
1703
2651
  // nitro
1704
- ["eventVal.customer.phone", "country"]
2652
+ ["eventVal.customer.phone", "country"],
2653
+ // shopflo — one path per envelope, most specific first
2654
+ ["eventPayload.userData.phone", "eventPayload.clientData.country"],
2655
+ ["data.user_data.phone", "data.country_code"],
2656
+ ["customer.phone", "customer.country_code"],
2657
+ // flexype
2658
+ ["payload.user.phone_with_dial_code", "payload.checkout_context.presentment_country"],
2659
+ ["payload.user.phone", "payload.checkout_context.presentment_country"],
2660
+ // return prime — wrapped and root envelopes
2661
+ ["request.customer.phone", "request.customer.address.country_code"],
2662
+ // fastrr — a bare national number, so the region is what makes it parse
2663
+ ["event_data.phone_number", "event_data.shipping_address.country_code"],
2664
+ ["event_data.billing_address.phone", "event_data.billing_address.country_code"]
1705
2665
  ];
1706
2666
  for (const [phonePath, regionPath] of candidates) {
1707
2667
  const phone = getPath(payload, phonePath);
@@ -1736,8 +2696,8 @@ function checkAllowedPhone(payload, config) {
1736
2696
  detail: ok ? `${normalized} is an approved test handset` : `${normalized} is NOT in allowedPhones \u2014 refusing to risk messaging a real shopper`
1737
2697
  };
1738
2698
  }
1739
- function coverage(payload, fieldMap4) {
1740
- return fieldMap4.map((f) => {
2699
+ function coverage(payload, fieldMap8) {
2700
+ return fieldMap8.map((f) => {
1741
2701
  const value = getPath(payload, f.source);
1742
2702
  const present = value !== void 0 && value !== null && value !== "";
1743
2703
  return {
@@ -1864,7 +2824,7 @@ function loadPayload(file) {
1864
2824
  }
1865
2825
 
1866
2826
  // src/core/build.mjs
1867
- function draftPayload({ cfg, providerId = "cashfree-occ", items, discount = 0, phone, eventName, collection }) {
2827
+ function draftPayload({ cfg, providerId = "cashfree-occ", items, discount = 0, phone, eventName, collection, envelope }) {
1868
2828
  const provider = getProvider(providerId);
1869
2829
  const event = eventName ?? provider.defaultEvent;
1870
2830
  const resolvedPhone = phone ?? cfg.customer.phone;
@@ -1875,6 +2835,7 @@ function draftPayload({ cfg, providerId = "cashfree-occ", items, discount = 0, p
1875
2835
  discount,
1876
2836
  eventName: event,
1877
2837
  collection,
2838
+ envelope,
1878
2839
  phone: parsed.ok ? parsed.value : String(resolvedPhone ?? "")
1879
2840
  });
1880
2841
  const editable = provider.editableFieldsFor ? provider.editableFieldsFor(event) : provider.editableFields;
@@ -1890,7 +2851,7 @@ function applyEdits(payload, fields, edits) {
1890
2851
  }
1891
2852
  return payload;
1892
2853
  }
1893
- async function buildPayload4({ cfg, providerId = "cashfree-occ", items = [], discount = 0, phone, checkImageUrls = true, payload: prebuilt, eventName, collection }) {
2854
+ async function buildPayload8({ cfg, providerId = "cashfree-occ", items = [], discount = 0, phone, checkImageUrls = true, payload: prebuilt, eventName, collection, envelope }) {
1894
2855
  const provider0 = getProvider(providerId);
1895
2856
  const needsItems = !provider0.selectionFor || ["cart", "product"].includes(provider0.selectionFor(eventName ?? provider0.defaultEvent));
1896
2857
  if (needsItems && !items.length) throw new ConfigError("no products selected");
@@ -1899,7 +2860,7 @@ async function buildPayload4({ cfg, providerId = "cashfree-occ", items = [], dis
1899
2860
  const parsed = toE164(resolvedPhone, cfg.customer.countryCode);
1900
2861
  if (!parsed.ok) throw new ConfigError(`phone is unusable: ${parsed.reason}`);
1901
2862
  const event = eventName ?? provider.defaultEvent;
1902
- const payload = prebuilt ?? provider.buildPayload({ config: cfg, items, discount, phone: parsed.value, eventName: event, collection });
2863
+ const payload = prebuilt ?? provider.buildPayload({ config: cfg, items, discount, phone: parsed.value, eventName: event, collection, envelope });
1903
2864
  const body = JSON.stringify(payload);
1904
2865
  const report = await runAll({ payload, body, provider, config: cfg, checkImageUrls, eventName: event });
1905
2866
  const summary = provider.summarize ? provider.summarize(payload) : {};
@@ -2169,7 +3130,7 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
2169
3130
  return setStep(STEP.MODE);
2170
3131
  case STEP.RESULT:
2171
3132
  case STEP.ERROR:
2172
- return onDone();
3133
+ return built ? setStep(STEP.REPORT) : onDone();
2173
3134
  default:
2174
3135
  return onDone();
2175
3136
  }
@@ -2323,7 +3284,7 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
2323
3284
  const onDiscount = (raw) => startReview(Math.max(0, Number.parseFloat(raw) || 0));
2324
3285
  const finalise = (payload) => {
2325
3286
  setStep(STEP.BUILDING);
2326
- buildPayload4({ cfg: config, providerId, items, discount: draft.discount, payload, eventName, collection }).then(async (result) => {
3287
+ buildPayload8({ cfg: config, providerId, items, discount: draft.discount, payload, eventName, collection }).then(async (result) => {
2327
3288
  setBuilt(result);
2328
3289
  let req = null;
2329
3290
  let command = "";
@@ -2596,7 +3557,7 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
2596
3557
  /* @__PURE__ */ jsx8(Text8, { color: palette.dim, children: "reproduce:" }),
2597
3558
  /* @__PURE__ */ jsx8(Text8, { color: palette.dim, children: sendResult.curl })
2598
3559
  ] }),
2599
- /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text8, { color: palette.dim, children: "press enter to return to the menu" }) })
3560
+ /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text8, { color: palette.dim, children: "enter returns to the menu \xB7 esc goes back to the report to retry" }) })
2600
3561
  ] }),
2601
3562
  step === STEP.ERROR && error && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
2602
3563
  /* @__PURE__ */ jsx8(Alert, { variant: "error", children: error.message }),
@@ -2606,7 +3567,7 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
2606
3567
  /* @__PURE__ */ jsx8(Text8, { color: palette.dim, children: f.detail })
2607
3568
  ] }, i)) }) : null,
2608
3569
  error.hint ? /* @__PURE__ */ jsx8(Text8, { color: palette.warn, children: ` ${error.hint}` }) : null,
2609
- /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text8, { color: palette.dim, children: "press enter to return to the menu" }) })
3570
+ /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text8, { color: palette.dim, children: "enter returns to the menu \xB7 esc goes back to the report to retry" }) })
2610
3571
  ] })
2611
3572
  ] });
2612
3573
  }
@@ -3567,11 +4528,12 @@ async function cmdBuild(args, config) {
3567
4528
  } else {
3568
4529
  log.ok(`${eventName} needs no product or collection`);
3569
4530
  }
3570
- const built = await buildPayload4({
4531
+ const built = await buildPayload8({
3571
4532
  cfg: config,
3572
4533
  providerId,
3573
4534
  eventName,
3574
4535
  collection,
4536
+ envelope: typeof args.flags.envelope === "string" ? args.flags.envelope : void 0,
3575
4537
  items,
3576
4538
  discount,
3577
4539
  phone: typeof args.flags.phone === "string" ? args.flags.phone : void 0,
@@ -3774,10 +4736,18 @@ ${c.bold("COMMANDS")}
3774
4736
  help show this message
3775
4737
 
3776
4738
  ${c.bold("BUILD OPTIONS")}
3777
- --provider <id> cashfree-occ | razorpay-magic | nitro (default cashfree-occ)
3778
- --event <name> nitro only: view \xB7 category_view \xB7 product_view \xB7 addtocart
3779
- removefromcart \xB7 checkout \xB7 orders/create \xB7 orders/updated
3780
- --collection <x> nitro category_view: collection id, handle or title
4739
+ --provider <id> cashfree-occ \xB7 razorpay-magic \xB7 nitro \xB7 shopflo
4740
+ flexype \xB7 return-prime \xB7 fastrr (default cashfree-occ)
4741
+ --event <name> multi-event providers only:
4742
+ nitro view \xB7 category_view \xB7 product_view \xB7 addtocart
4743
+ removefromcart \xB7 checkout \xB7 orders/create \xB7 orders/updated
4744
+ shopflo store_page_view \xB7 collection_page_viewed \xB7 product_page_viewed
4745
+ added_to_cart_ui \xB7 checkout_clicked \xB7 checkout_abandoned
4746
+ order_completed
4747
+ return-prime {return,exchange}_{requested,approved,received,
4748
+ inspected,rejected}
4749
+ --envelope <x> return-prime only: wrapped (default) or root
4750
+ --collection <x> category or collection view: collection id, handle or title
3781
4751
  --items <n> how many products to put in the cart (default 1)
3782
4752
  --search <text> only consider products matching a title
3783
4753
  --discount <n> cart discount in major currency units (default 0)