hookwright 1.12.2 → 2.0.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 +476 -27
  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.12.2",
27
+ version: "2.0.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",
@@ -178,7 +178,9 @@ var DEFAULT_CONFIG = {
178
178
  shopflo: { webhookUrl: "", webhookSecret: "" },
179
179
  flexype: { webhookUrl: "", webhookSecret: "" },
180
180
  "return-prime": { webhookUrl: "", webhookSecret: "" },
181
- fastrr: { webhookUrl: "", webhookSecret: "" }
181
+ fastrr: { webhookUrl: "", webhookSecret: "" },
182
+ gokwik: { webhookUrl: "", webhookSecret: "" },
183
+ ecom360: { webhookUrl: "", webhookSecret: "" }
182
184
  },
183
185
  customer: {
184
186
  firstName: "",
@@ -1805,26 +1807,40 @@ function selectionFor4(eventName) {
1805
1807
  return eventDef3(eventName).selection;
1806
1808
  }
1807
1809
  var COMMON_FIELDS3 = [
1808
- { source: "request_type", target: "\xABgate\xBB + requestType", required: true, note: "return or exchange" },
1809
- { source: "status", target: "\xABgate\xBB + status", required: true, note: "requested, approved, received, inspected or rejected" },
1810
- { source: "customer.phone", target: "phone", required: true, note: "missing phone is rejected on every event" },
1811
- { source: "customer.address.country_code", target: "phone region" },
1812
- { source: "request_number", target: "requestNumber" },
1813
- { source: "order.name", target: "order.name" },
1814
- { source: "line_items[].original_product.title", target: "product.name", required: true, note: "the first PAID line wins; freebies rank last" },
1815
- { source: "line_items[].original_product.image.src", target: "product.image" },
1816
- { source: "line_items[].original_product.price", target: "product.price", note: "a zero price marks the line a freebie" },
1817
- { source: "line_items[].reason", target: "reason" },
1818
- { source: "line_items[].presentment_price.actual_amount", target: "pricing.totalPrice" },
1819
- { source: "line_items[].return_fee.price_set\u2026amount", target: "pricing.returnFee" },
1820
- { source: "line_items[].refund.status", target: "refund.status" },
1821
- { source: "line_items[].refund.meta.discount_code", target: "refund.discountCode", note: "only present once store credit is issued" },
1822
- { source: "line_items[].shipping[0].awb", target: "shipping.awb" },
1823
- { source: "approved/received/inspected/rejected.created_at", target: "timeline.*" }
1810
+ { source: "request.request_type", target: "\xABgate\xBB + requestType", required: true, note: "return or exchange" },
1811
+ { source: "request.status", target: "\xABgate\xBB + status", required: true, note: "requested, approved, received, inspected or rejected" },
1812
+ { source: "request.customer.phone", target: "phone", required: true, note: "missing phone is rejected on every event" },
1813
+ { source: "request.customer.address.country_code", target: "phone region" },
1814
+ { source: "request.customer.name", target: "customer.name" },
1815
+ { source: "request.customer.email", target: "customer.email", note: "null on some real requests" },
1816
+ { source: "request.request_number", target: "requestNumber" },
1817
+ { source: "request.id", target: "requestId" },
1818
+ { source: "request.manual_request", target: "manualRequest" },
1819
+ { source: "request.smart_exchange", target: "smartExchange" },
1820
+ { source: "request.order.name", target: "order.name" },
1821
+ { source: "request.order.id", target: "order.id" },
1822
+ { source: "request.line_items[].original_product.title", target: "product.name + product.details", required: true, note: "the first PAID line wins; freebies rank last" },
1823
+ { source: "request.line_items[].original_product.image.src", target: "product.image" },
1824
+ { source: "request.line_items[].original_product.price", target: "product.price", note: "a zero price marks the line a freebie" },
1825
+ { source: "request.line_items[].original_product.sku", target: "product.sku" },
1826
+ { source: "request.line_items[].reason", target: "reason" },
1827
+ { source: "request.line_items[].quantity", target: "product.quantity" },
1828
+ { source: "request.line_items[].presentment_price.actual_amount", target: "pricing.totalPrice" },
1829
+ { source: "request.line_items[].presentment_price.currency", target: "pricing.currency" },
1830
+ { source: "request.line_items[].return_fee.price_set.presentment_money.amount", target: "pricing.returnFee" },
1831
+ { source: "request.line_items[].refund.status", target: "refund.status" },
1832
+ { source: "request.line_items[].refund.requested_mode", target: "refund.requestedMode" },
1833
+ { source: "request.line_items[].refund.meta.discount_code", target: "refund.discountCode", note: "only present once store credit is issued" },
1834
+ { source: "request.line_items[].shipping[0].awb", target: "shipping.awb", note: "absent until a label is raised" },
1835
+ { source: "request.line_items[].shipping[0].tracking_available", target: "shipping.trackingAvailable" },
1836
+ { source: "request.approved.created_at / request.received.created_at / request.inspected.created_at / request.rejected.created_at", target: "timeline.*", note: "whichever steps have happened" },
1837
+ { source: "request.created_at", target: "createdAt" }
1824
1838
  ];
1825
1839
  var EXCHANGE_FIELDS = [
1826
- { source: "line_items[].exchange_product.title", target: "exchangeProduct.name", note: "exchanges only \u2014 returns never carry it" },
1827
- { source: "line_items[].exchange_product.price", target: "exchangeProduct.price + pricing.priceDifference", note: "difference is signed: positive means the shopper owes more" }
1840
+ { source: "request.line_items[].exchange_product.title", target: "exchangeProduct.name", note: "exchanges only \u2014 returns never carry it" },
1841
+ { source: "request.line_items[].exchange_product.price", target: "exchangeProduct.price + pricing.priceDifference", note: "difference is signed: positive means the shopper owes more" },
1842
+ { source: "request.line_items[].exchange_product.image.src", target: "exchangeProduct.image" },
1843
+ { source: "request.line_items[].exchange_product.sku", target: "exchangeProduct.sku" }
1828
1844
  ];
1829
1845
  var fieldMap6 = COMMON_FIELDS3;
1830
1846
  function fieldMapFor2(eventName) {
@@ -1863,7 +1879,7 @@ function buildPayload6(ctx) {
1863
1879
  const cust = config.customer;
1864
1880
  const at = now.toISOString();
1865
1881
  const first = items?.[0];
1866
- const second = items?.[1] ?? first;
1882
+ const second = ctx.exchange ?? items?.[1] ?? first;
1867
1883
  const originalPrice = money2(first?.variant?.price ?? 0);
1868
1884
  const exchangePrice = money2(second?.variant?.price ?? 0);
1869
1885
  const reachedIndex = STATUSES.indexOf(def.status);
@@ -2258,6 +2274,423 @@ var fastrr_default = {
2258
2274
  summarize: summarize7
2259
2275
  };
2260
2276
 
2277
+ // src/providers/gokwik.mjs
2278
+ var GATE_PATH5 = "abc_url";
2279
+ var GATE_VALUE4 = "";
2280
+ var fieldMap8 = [
2281
+ { source: "abc_url", target: "\xABgate\xBB + checkout_link", required: true, note: "absent and the event is dropped; its origin builds every other link" },
2282
+ { source: "customer.phone / address.phone", target: "customer.phone", required: true },
2283
+ { source: "sa_localization", target: "phone region", required: true, note: 'address.country says "India", which the parser cannot use' },
2284
+ { source: "customer.firstname", target: "customer.firstName" },
2285
+ { source: "customer.lastname", target: "customer.lastName" },
2286
+ { source: "customer.email / address.email", target: "customer.email" },
2287
+ { source: "items[].title", target: "product_details", required: true, note: "the first PAID line wins; freebies rank last" },
2288
+ { source: "items[].product_title", target: "product_name" },
2289
+ { source: "items[].image", target: "product_image_url", note: "read off the featured line only" },
2290
+ { source: "items[].url", target: "product_url", note: "relative \u2014 resolved against the abc_url origin" },
2291
+ { source: "items[].final_price", target: "product_price", note: "PAISE \u2014 the consumer divides by 100" },
2292
+ { source: "items[].sku", target: "product_sku" },
2293
+ { source: "items[].product_type", target: "product_type" },
2294
+ { source: "items[].variant_id", target: "cart_link", note: "joined as variant:quantity" },
2295
+ { source: "items[].quantity", target: "total_quantity", note: "summed across the cart" },
2296
+ { source: "item_count", target: "item_count", note: "gokwik's own count is kept over the line count" },
2297
+ { source: "total_price", target: "total_price", note: "RUPEE string, unlike the line prices" },
2298
+ { source: "original_total_price", target: "original_total_price" },
2299
+ { source: "items_subtotal_price", target: "subtotal_price" },
2300
+ { source: "total_discount", target: "total_discount" },
2301
+ { source: "currency", target: "currency" },
2302
+ { source: "request_id", target: "request_id" },
2303
+ { source: "token", target: "token" },
2304
+ { source: "drop_stage", target: "drop_stage" },
2305
+ { source: "rto_risk_flag", target: "rto_risk_flag" },
2306
+ { source: "brand_order_count", target: "brand_order_count" },
2307
+ { source: "mkt_source", target: "mkt_source" },
2308
+ { source: "mkt_medium", target: "mkt_medium" },
2309
+ { source: "mkt_campaign", target: "mkt_campaign" },
2310
+ { source: "mkt_content", target: "mkt_content" },
2311
+ { source: "mkt_term", target: "mkt_term" },
2312
+ { source: "mkt_fbclid", target: "mkt_fbclid" },
2313
+ { source: "landing_page", target: "landing_page" },
2314
+ { source: "orig_referrer", target: "orig_referrer" },
2315
+ { source: "address.city", target: "address.city" },
2316
+ { source: "address.state", target: "address.state" },
2317
+ { source: "address.pincode", target: "address.pincode" },
2318
+ { source: "address.country", target: "address.country" },
2319
+ { source: "address.address", target: "address.address" },
2320
+ { source: "shipping.price", target: "shipping.price" },
2321
+ { source: "city", target: "city", note: "the ip city, not the delivery city" },
2322
+ { source: "isp", target: "isp" },
2323
+ { source: "user_agent", target: "user_agent" },
2324
+ { source: "updated_at", target: "eventTime" }
2325
+ ];
2326
+ var editableFields8 = [
2327
+ { group: "Checkout", path: "request_id", label: "Request id" },
2328
+ { group: "Checkout", path: "token", label: "Token" },
2329
+ { group: "Checkout", path: "abc_url", label: "Recovery URL", hint: "gates the event; its origin builds the product and cart links" },
2330
+ { group: "Checkout", path: "drop_stage", label: "Drop stage" },
2331
+ { group: "Checkout", path: "rto_risk_flag", label: "RTO risk flag" },
2332
+ { group: "Checkout", path: "gokwik_source_channel", label: "Source channel" },
2333
+ { group: "Pricing", path: "total_price", label: "Total price", hint: "rupee string, unlike the line prices" },
2334
+ { group: "Pricing", path: "total_discount", label: "Discount" },
2335
+ { group: "Pricing", path: "currency", label: "Currency" },
2336
+ { group: "Customer", path: "customer.firstname", label: "First name" },
2337
+ { group: "Customer", path: "customer.lastname", label: "Last name" },
2338
+ { group: "Customer", path: "customer.email", label: "Email" },
2339
+ { group: "Customer", path: "customer.phone", label: "Phone" },
2340
+ { group: "Customer", path: "brand_order_count", label: "Brand order count", type: "number" },
2341
+ { group: "Customer", path: "sa_localization", label: "Localization", hint: "two letter region used to parse the phone" },
2342
+ { group: "Shipping", path: "address.address", label: "Address line" },
2343
+ { group: "Shipping", path: "address.city", label: "City" },
2344
+ { group: "Shipping", path: "address.state", label: "State" },
2345
+ { group: "Shipping", path: "address.pincode", label: "Pincode", type: "number" },
2346
+ { group: "Attribution", path: "mkt_source", label: "UTM source" },
2347
+ { group: "Attribution", path: "mkt_medium", label: "UTM medium" },
2348
+ { group: "Attribution", path: "mkt_campaign", label: "UTM campaign" },
2349
+ { group: "Attribution", path: "landing_page", label: "Landing page" },
2350
+ { group: "Attribution", path: "orig_referrer", label: "Referrer" }
2351
+ ];
2352
+ function paise(rupees) {
2353
+ return Math.round(money2(rupees) * 100);
2354
+ }
2355
+ function buildPayload8(ctx) {
2356
+ const { config, items, phone } = ctx;
2357
+ const now = ctx.now ?? /* @__PURE__ */ new Date();
2358
+ const currency = ctx.currency || config.defaults.currency;
2359
+ const domain = config.shopify.domain;
2360
+ const cust = config.customer;
2361
+ const utm = config.defaults.utm ?? {};
2362
+ const lineItems = (items ?? []).map(({ product, variant, quantity }) => {
2363
+ const unit = money2(variant?.price ?? 0);
2364
+ return {
2365
+ id: Number(variant?.id ?? 0),
2366
+ sku: variant?.sku || "",
2367
+ url: `/products/${product.handle}?variant=${variant?.id ?? ""}`,
2368
+ grams: 500,
2369
+ image: imageForVariant(product, variant),
2370
+ price: paise(unit),
2371
+ title: `${product.title}${variant?.title ? ` - ${variant.title}` : ""}`,
2372
+ vendor: product.vendor ?? "",
2373
+ taxable: true,
2374
+ quantity,
2375
+ gift_card: false,
2376
+ line_price: paise(unit * quantity),
2377
+ product_id: Number(product.id),
2378
+ variant_id: Number(variant?.id ?? 0),
2379
+ final_price: paise(unit),
2380
+ product_type: product.productType ?? "",
2381
+ product_title: product.title,
2382
+ original_price: paise(unit),
2383
+ total_discount: 0,
2384
+ final_line_price: paise(unit * quantity),
2385
+ requires_shipping: true
2386
+ };
2387
+ });
2388
+ const subtotal = money2(lineItems.reduce((sum, li) => sum + li.final_line_price / 100, 0));
2389
+ const discount = money2(Math.min(ctx.discount ?? 0, subtotal));
2390
+ const total = money2(subtotal - discount);
2391
+ const requestId = ctx.requestId ?? `gk-${now.getTime()}`;
2392
+ const abcUrl = `https://${domain}?mrid=${requestId}`;
2393
+ return {
2394
+ request_id: requestId,
2395
+ token: `${requestId}?key=${now.getTime().toString(36)}`,
2396
+ note: null,
2397
+ attributes: "{}",
2398
+ original_total_price: subtotal.toFixed(4),
2399
+ total_price: total.toFixed(4),
2400
+ total_discount: discount.toFixed(4),
2401
+ total_weight: "500.0000",
2402
+ is_abandoned: true,
2403
+ item_count: lineItems.length,
2404
+ items: lineItems,
2405
+ requires_shipping: true,
2406
+ currency,
2407
+ items_subtotal_price: String(subtotal),
2408
+ cart_level_discount_applications: [],
2409
+ discount: null,
2410
+ shipping: {
2411
+ id: 1,
2412
+ name: "Shipping",
2413
+ price: ctx.shippingPrice ?? 0,
2414
+ title: "Shipping",
2415
+ base_price: 0,
2416
+ cod_charges: 0,
2417
+ prepaid_price: 0,
2418
+ payment_options: "all",
2419
+ discounted_price: 0
2420
+ },
2421
+ abc_url: abcUrl,
2422
+ created_at: now.toISOString(),
2423
+ updated_at: now.toISOString(),
2424
+ city: cust.city,
2425
+ isp: "Test ISP",
2426
+ ip: "203.0.113.10",
2427
+ mkt_source: utm.utm_source ?? "direct",
2428
+ mkt_medium: utm.utm_medium ?? null,
2429
+ mkt_campaign: utm.utm_campaign ?? null,
2430
+ mkt_content: utm.utm_content ?? null,
2431
+ mkt_term: utm.utm_term ?? null,
2432
+ mkt_fbclid: utm.fbclid ?? null,
2433
+ gift_card_total: "0",
2434
+ payment_methods: ["upi", "cc", "dc", "netbanking"],
2435
+ drop_stage: "Payment Page",
2436
+ checkout_url: null,
2437
+ note_text: "",
2438
+ landing_page: "/",
2439
+ orig_referrer: `https://${domain}/`,
2440
+ gokwik_source_channel: "shopify_checkout",
2441
+ store_source: "web",
2442
+ store_platform: null,
2443
+ rto_risk_flag: "Control",
2444
+ sa_localization: cust.countryCode,
2445
+ shopifysessionid: `gk-session-${now.getTime()}`,
2446
+ user_agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/151.0.0.0 Safari/537.36",
2447
+ notify_customer: false,
2448
+ brand_order_count: 2,
2449
+ address: {
2450
+ firstname: cust.firstName,
2451
+ lastname: cust.lastName,
2452
+ city: cust.city,
2453
+ pincode: Number(cust.zip) || 0,
2454
+ state: cust.province,
2455
+ country: cust.country,
2456
+ email: cust.email,
2457
+ phone,
2458
+ default_address: 0,
2459
+ addresstype: "home",
2460
+ partial_address: false,
2461
+ total_order_count: 1,
2462
+ deliver_order_count: 0,
2463
+ address: cust.address1
2464
+ },
2465
+ customer: {
2466
+ firstname: cust.firstName,
2467
+ lastname: cust.lastName,
2468
+ email: cust.email,
2469
+ phone
2470
+ }
2471
+ };
2472
+ }
2473
+ function sign8() {
2474
+ return { signature: null, timestamp: null, headers: { "content-type": "application/json" } };
2475
+ }
2476
+ function webhookUrl8(config) {
2477
+ return String(targetFor(config, "gokwik").webhookUrl ?? "").trim();
2478
+ }
2479
+ function webhookSecret8(config) {
2480
+ return String(targetFor(config, "gokwik").webhookSecret ?? "");
2481
+ }
2482
+ function summarize8(payload) {
2483
+ const items = Array.isArray(payload?.items) ? payload.items : [];
2484
+ let store;
2485
+ try {
2486
+ store = payload?.abc_url ? new URL(payload.abc_url).host : void 0;
2487
+ } catch {
2488
+ store = payload?.abc_url;
2489
+ }
2490
+ return {
2491
+ event: "abandoned checkout",
2492
+ store,
2493
+ customer: `${payload?.customer?.firstname ?? ""} ${payload?.customer?.lastname ?? ""} \xB7 ${payload?.customer?.phone ?? ""}`.trim(),
2494
+ items: items.map((i) => `${i.title} x${i.quantity}`).join(", "),
2495
+ total: payload?.total_price,
2496
+ currency: payload?.currency,
2497
+ extra: `stage ${payload?.drop_stage}, rto ${payload?.rto_risk_flag}`,
2498
+ link: payload?.abc_url
2499
+ };
2500
+ }
2501
+ var gokwik_default = {
2502
+ id: "gokwik",
2503
+ urlKey: "gokwik",
2504
+ label: "GoKwik",
2505
+ eventType: "gokwik_abandoned_checkout",
2506
+ gate: { path: GATE_PATH5, value: GATE_VALUE4 },
2507
+ signatureScheme: "none documented \u2014 every request is accepted",
2508
+ signatureHeader: null,
2509
+ fieldMap: fieldMap8,
2510
+ editableFields: editableFields8,
2511
+ buildPayload: buildPayload8,
2512
+ sign: sign8,
2513
+ webhookUrl: webhookUrl8,
2514
+ webhookSecret: webhookSecret8,
2515
+ summarize: summarize8
2516
+ };
2517
+
2518
+ // src/providers/ecom360.mjs
2519
+ import crypto4 from "crypto";
2520
+ var GATE_PATH6 = "event";
2521
+ var GATE_VALUE5 = "ABANDONED_CHECKOUT";
2522
+ var fieldMap9 = [
2523
+ { source: "event", target: "\xABgate\xBB", required: true, note: "must be ABANDONED_CHECKOUT or the event is dropped" },
2524
+ { source: "data.phone / data.customer.shipping_address.phone", target: "customer.phone", required: true },
2525
+ { source: "data.customer.shipping_address.country_code", target: "phone region" },
2526
+ { source: "data.customer.first_name", target: "customer.firstName" },
2527
+ { source: "data.customer.last_name", target: "customer.lastName" },
2528
+ { source: "data.email / data.customer.email", target: "customer.email" },
2529
+ { source: "data.abandoned_checkout_url", target: "checkout_link", required: true },
2530
+ { source: "data.store_url", target: "store_url + cart_link", required: true, note: "the cart link is built from it; no lookup needed" },
2531
+ { source: "data.line_items[].title", target: "product_details", required: true, note: "the first PAID line wins; freebies rank last" },
2532
+ { source: "data.line_items[].sku", target: "product_sku" },
2533
+ { source: "data.line_items[].price", target: "product_price", note: "also ranks the lines; a zero marks a freebie" },
2534
+ { source: "data.line_items[].variant_id", target: "cart_link", note: "joined as variant:quantity" },
2535
+ { source: "data.line_items[].quantity", target: "total_quantity", note: "summed across the cart" },
2536
+ { source: "data.line_items[]", target: "item_count", note: "the line count" },
2537
+ { source: "\u2014 (no handle is sent)", target: "product_url", note: "the consumer builds none, by design" },
2538
+ { source: "data.total_price", target: "total_price" },
2539
+ { source: "data.original_total_price", target: "original_total_price" },
2540
+ { source: "data.total_discount", target: "total_discount" },
2541
+ { source: "data.promotions[0].code", target: "coupon_code" },
2542
+ { source: "data.promotions[0].value", target: "coupon_value" },
2543
+ { source: "data.cart_id", target: "cart_id" },
2544
+ { source: "data.cart_token", target: "cart_token" },
2545
+ { source: "data.platform", target: "platform" },
2546
+ { source: "data.utm_parameters.utm_source", target: "utm_parameters.utm_source" },
2547
+ { source: "data.utm_parameters.utm_medium", target: "utm_parameters.utm_medium" },
2548
+ { source: "data.utm_parameters.utm_campaign", target: "utm_parameters.utm_campaign" },
2549
+ { source: "data.utm_parameters.fbclid", target: "utm_parameters.fbclid" },
2550
+ { source: "data.customer.shipping_address.city", target: "shipping_address.city" },
2551
+ { source: "data.customer.shipping_address.province", target: "shipping_address.province" },
2552
+ { source: "data.customer.shipping_address.zip", target: "shipping_address.zip" },
2553
+ { source: "data.customer.shipping_address.country", target: "shipping_address.country" }
2554
+ ];
2555
+ var editableFields9 = [
2556
+ { group: "Checkout", path: "data.cart_id", label: "Cart id" },
2557
+ { group: "Checkout", path: "data.cart_token", label: "Cart token" },
2558
+ { group: "Checkout", path: "data.abandoned_checkout_url", label: "Checkout URL", hint: "the link the shopper is sent back to" },
2559
+ { group: "Checkout", path: "data.store_url", label: "Store domain", hint: "the cart link is built from this" },
2560
+ { group: "Checkout", path: "data.platform", label: "Platform" },
2561
+ { group: "Pricing", path: "data.total_price", label: "Total price", type: "number" },
2562
+ { group: "Pricing", path: "data.original_total_price", label: "Original total", type: "number" },
2563
+ { group: "Pricing", path: "data.total_discount", label: "Discount", type: "number" },
2564
+ { group: "Customer", path: "data.customer.first_name", label: "First name" },
2565
+ { group: "Customer", path: "data.customer.last_name", label: "Last name" },
2566
+ { group: "Customer", path: "data.email", label: "Email" },
2567
+ { group: "Customer", path: "data.phone", label: "Phone" },
2568
+ { group: "Shipping", path: "data.customer.shipping_address.address1", label: "Address line 1" },
2569
+ { group: "Shipping", path: "data.customer.shipping_address.city", label: "City" },
2570
+ { group: "Shipping", path: "data.customer.shipping_address.province", label: "State" },
2571
+ { group: "Shipping", path: "data.customer.shipping_address.zip", label: "Zip / postcode" },
2572
+ { group: "Shipping", path: "data.customer.shipping_address.country_code", label: "Country code", hint: "region used to parse the phone" },
2573
+ { group: "Attribution", path: "data.utm_parameters.utm_source", label: "UTM source" },
2574
+ { group: "Attribution", path: "data.utm_parameters.utm_campaign", label: "UTM campaign" },
2575
+ { group: "Attribution", path: "data.utm_parameters.fbclid", label: "FBCLID" }
2576
+ ];
2577
+ function buildPayload9(ctx) {
2578
+ const { config, items, phone } = ctx;
2579
+ const now = ctx.now ?? /* @__PURE__ */ new Date();
2580
+ const domain = config.shopify.domain;
2581
+ const cust = config.customer;
2582
+ const utm = config.defaults.utm ?? {};
2583
+ const lineItems = (items ?? []).map(({ product, variant, quantity }) => ({
2584
+ product_id: String(product.id),
2585
+ variant_id: String(variant?.id ?? ""),
2586
+ sku: variant?.sku || "",
2587
+ price: money2(variant?.price ?? 0),
2588
+ quantity,
2589
+ gram: 200,
2590
+ title: `${product.title}${variant?.title ? ` - ${variant.title}` : ""}`
2591
+ }));
2592
+ const subtotal = money2(lineItems.reduce((sum, li) => sum + li.price * li.quantity, 0));
2593
+ const discount = money2(Math.min(ctx.discount ?? 0, subtotal));
2594
+ const total = money2(subtotal - discount);
2595
+ const cartToken = ctx.cartToken ?? `ec-${now.getTime()}`;
2596
+ const shippingAddress = {
2597
+ first_name: cust.firstName,
2598
+ last_name: cust.lastName,
2599
+ address1: cust.address1,
2600
+ address2: "",
2601
+ city: cust.city,
2602
+ province: cust.province,
2603
+ country: cust.country,
2604
+ zip: cust.zip,
2605
+ phone,
2606
+ name: `${cust.firstName} ${cust.lastName}`.trim(),
2607
+ province_code: cust.provinceCode ?? "",
2608
+ country_code: cust.countryCode
2609
+ };
2610
+ return {
2611
+ event: GATE_VALUE5,
2612
+ data: {
2613
+ cart_id: `cart_${now.getTime()}`,
2614
+ store_url: domain,
2615
+ platform: "SHOPIFY",
2616
+ cart_token: cartToken,
2617
+ email: cust.email,
2618
+ phone,
2619
+ abandoned_checkout_url: `https://${domain}/checkouts/${cartToken}`,
2620
+ original_total_price: subtotal,
2621
+ total_price: total,
2622
+ total_discount: discount,
2623
+ utm_parameters: {
2624
+ fbclid: utm.fbclid ?? void 0,
2625
+ utm_source: utm.utm_source ?? void 0,
2626
+ utm_medium: utm.utm_medium ?? void 0,
2627
+ utm_campaign: utm.utm_campaign ?? void 0,
2628
+ utm_content: utm.utm_content ?? void 0,
2629
+ utm_term: utm.utm_term ?? void 0
2630
+ },
2631
+ line_items: lineItems,
2632
+ promotions: ctx.couponCode ? [{ code: ctx.couponCode, value: discount }] : [],
2633
+ customer: {
2634
+ email: cust.email,
2635
+ first_name: cust.firstName,
2636
+ last_name: cust.lastName,
2637
+ shipping_address: shippingAddress
2638
+ }
2639
+ }
2640
+ };
2641
+ }
2642
+ function sign9({ body, secret }) {
2643
+ const signature = crypto4.createHmac("sha256", String(secret)).update(body).digest("base64");
2644
+ return {
2645
+ timestamp: null,
2646
+ signature,
2647
+ headers: {
2648
+ "content-type": "application/json",
2649
+ "x-webhook-signature": signature,
2650
+ "x-webhook-topic": "abandonedCheckout",
2651
+ "x-webhook-version": "v1",
2652
+ "user-agent": "Ecom360-v1"
2653
+ }
2654
+ };
2655
+ }
2656
+ function webhookUrl9(config) {
2657
+ return String(targetFor(config, "ecom360").webhookUrl ?? "").trim();
2658
+ }
2659
+ function webhookSecret9(config) {
2660
+ return String(targetFor(config, "ecom360").webhookSecret ?? "");
2661
+ }
2662
+ function summarize9(payload) {
2663
+ const d = payload?.data ?? {};
2664
+ const items = Array.isArray(d.line_items) ? d.line_items : [];
2665
+ const c2 = d.customer ?? {};
2666
+ return {
2667
+ event: "abandoned checkout",
2668
+ store: d.store_url,
2669
+ customer: `${c2.first_name ?? ""} ${c2.last_name ?? ""} \xB7 ${d.phone ?? ""}`.trim(),
2670
+ items: items.map((i) => `${i.title} x${i.quantity}`).join(", "),
2671
+ total: d.total_price,
2672
+ currency: void 0,
2673
+ extra: `platform ${d.platform}`,
2674
+ link: d.abandoned_checkout_url
2675
+ };
2676
+ }
2677
+ var ecom360_default = {
2678
+ id: "ecom360",
2679
+ urlKey: "ecom360",
2680
+ label: "Ecom 360",
2681
+ eventType: "ecom360_abandoned_checkout",
2682
+ gate: { path: GATE_PATH6, value: GATE_VALUE5 },
2683
+ signatureScheme: "base64 HMAC-SHA256 over the body",
2684
+ signatureHeader: "x-webhook-signature",
2685
+ fieldMap: fieldMap9,
2686
+ editableFields: editableFields9,
2687
+ buildPayload: buildPayload9,
2688
+ sign: sign9,
2689
+ webhookUrl: webhookUrl9,
2690
+ webhookSecret: webhookSecret9,
2691
+ summarize: summarize9
2692
+ };
2693
+
2261
2694
  // src/providers/index.mjs
2262
2695
  var PROVIDER_META = [
2263
2696
  {
@@ -2311,6 +2744,20 @@ var PROVIDER_META = [
2311
2744
  signature: "none documented \u2014 every request is accepted",
2312
2745
  header: null,
2313
2746
  available: true
2747
+ },
2748
+ {
2749
+ id: "gokwik",
2750
+ label: "GoKwik",
2751
+ signature: "none documented \u2014 every request is accepted",
2752
+ header: null,
2753
+ available: true
2754
+ },
2755
+ {
2756
+ id: "ecom360",
2757
+ label: "Ecom 360",
2758
+ signature: "base64 HMAC-SHA256 over the body",
2759
+ header: "x-webhook-signature",
2760
+ available: true
2314
2761
  }
2315
2762
  ];
2316
2763
  var providers = {
@@ -2320,7 +2767,9 @@ var providers = {
2320
2767
  [shopflo_default.id]: shopflo_default,
2321
2768
  [flexype_default.id]: flexype_default,
2322
2769
  [return_prime_default.id]: return_prime_default,
2323
- [fastrr_default.id]: fastrr_default
2770
+ [fastrr_default.id]: fastrr_default,
2771
+ [gokwik_default.id]: gokwik_default,
2772
+ [ecom360_default.id]: ecom360_default
2324
2773
  };
2325
2774
  function getProvider(id) {
2326
2775
  const provider = providers[id];
@@ -2980,8 +3429,8 @@ function resolveSource(payload, source) {
2980
3429
  }
2981
3430
  return void 0;
2982
3431
  }
2983
- function coverage(payload, fieldMap8) {
2984
- return fieldMap8.map((f) => {
3432
+ function coverage(payload, fieldMap10) {
3433
+ return fieldMap10.map((f) => {
2985
3434
  const value = resolveSource(payload, f.source);
2986
3435
  const present = value !== void 0 && value !== null && value !== "";
2987
3436
  return {
@@ -3146,7 +3595,7 @@ function applyEdits(payload, fields, edits) {
3146
3595
  }
3147
3596
  return payload;
3148
3597
  }
3149
- async function buildPayload8({ cfg, providerId = "cashfree-occ", items = [], discount = 0, phone, checkImageUrls = true, payload: prebuilt, eventName, collection, envelope, live, delta }) {
3598
+ async function buildPayload10({ cfg, providerId = "cashfree-occ", items = [], discount = 0, phone, checkImageUrls = true, payload: prebuilt, eventName, collection, envelope, live, delta }) {
3150
3599
  const provider0 = getProvider(providerId);
3151
3600
  const needsItems = !provider0.selectionFor || ["cart", "product"].includes(provider0.selectionFor(eventName ?? provider0.defaultEvent));
3152
3601
  if (needsItems && !items.length) throw new ConfigError("no products selected");
@@ -3693,7 +4142,7 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
3693
4142
  const onDiscount = (raw) => startReview(Math.max(0, Number.parseFloat(raw) || 0));
3694
4143
  const finalise = (payload) => {
3695
4144
  setStep(STEP.BUILDING);
3696
- buildPayload8({ cfg: config, providerId, items, discount: draft.discount, payload, eventName, collection, live: draft.live, delta: draft.delta }).then(async (result) => {
4145
+ buildPayload10({ cfg: config, providerId, items, discount: draft.discount, payload, eventName, collection, live: draft.live, delta: draft.delta }).then(async (result) => {
3697
4146
  setBuilt(result);
3698
4147
  let req = null;
3699
4148
  let command = "";
@@ -4964,7 +5413,7 @@ async function cmdBuild(args, config) {
4964
5413
  else if (live?.kind === "order") log.ok(`order ${live.name} (${live.id})`);
4965
5414
  else log.warn("no live record available \u2014 falling back to a generated one");
4966
5415
  }
4967
- const built = await buildPayload8({
5416
+ const built = await buildPayload10({
4968
5417
  cfg: config,
4969
5418
  providerId,
4970
5419
  eventName,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hookwright",
3
- "version": "1.12.2",
3
+ "version": "2.0.0",
4
4
  "description": "Build real, signed e-commerce webhooks from a live Shopify catalogue — interactive terminal UI, no backend",
5
5
  "keywords": [
6
6
  "webhook",