@tagadapay/headless-sdk 1.14.1 → 1.16.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 (81) hide show
  1. package/dist/core/client.d.ts +7 -0
  2. package/dist/core/client.d.ts.map +1 -1
  3. package/dist/core/client.js +10 -3
  4. package/dist/core/client.js.map +1 -1
  5. package/dist/core/index.d.ts +3 -1
  6. package/dist/core/index.d.ts.map +1 -1
  7. package/dist/core/index.js +1 -0
  8. package/dist/core/index.js.map +1 -1
  9. package/dist/core/modules/checkout.d.ts +5 -1
  10. package/dist/core/modules/checkout.d.ts.map +1 -1
  11. package/dist/core/modules/checkout.js +10 -1
  12. package/dist/core/modules/checkout.js.map +1 -1
  13. package/dist/core/modules/payment.d.ts +19 -1
  14. package/dist/core/modules/payment.d.ts.map +1 -1
  15. package/dist/core/modules/payment.js +42 -3
  16. package/dist/core/modules/payment.js.map +1 -1
  17. package/dist/core/modules/rx.d.ts +32 -1
  18. package/dist/core/modules/rx.d.ts.map +1 -1
  19. package/dist/core/modules/rx.js +17 -1
  20. package/dist/core/modules/rx.js.map +1 -1
  21. package/dist/core/modules/track.d.ts +127 -0
  22. package/dist/core/modules/track.d.ts.map +1 -0
  23. package/dist/core/modules/track.js +371 -0
  24. package/dist/core/modules/track.js.map +1 -0
  25. package/dist/core/types.d.ts +2 -0
  26. package/dist/core/types.d.ts.map +1 -1
  27. package/dist/core/version.d.ts +4 -0
  28. package/dist/core/version.d.ts.map +1 -0
  29. package/dist/core/version.js +4 -0
  30. package/dist/core/version.js.map +1 -0
  31. package/dist/pixels/engine.d.ts +56 -0
  32. package/dist/pixels/engine.d.ts.map +1 -0
  33. package/dist/pixels/engine.js +263 -0
  34. package/dist/pixels/engine.js.map +1 -0
  35. package/dist/pixels/index.d.ts +10 -0
  36. package/dist/pixels/index.d.ts.map +1 -0
  37. package/dist/pixels/index.js +5 -0
  38. package/dist/pixels/index.js.map +1 -0
  39. package/dist/pixels/metaEventId.d.ts +15 -0
  40. package/dist/pixels/metaEventId.d.ts.map +1 -0
  41. package/dist/pixels/metaEventId.js +17 -0
  42. package/dist/pixels/metaEventId.js.map +1 -0
  43. package/dist/pixels/pixelIdentity.d.ts +34 -0
  44. package/dist/pixels/pixelIdentity.d.ts.map +1 -0
  45. package/dist/pixels/pixelIdentity.js +65 -0
  46. package/dist/pixels/pixelIdentity.js.map +1 -0
  47. package/dist/pixels/pixelMapping.d.ts +172 -0
  48. package/dist/pixels/pixelMapping.d.ts.map +1 -0
  49. package/dist/pixels/pixelMapping.js +574 -0
  50. package/dist/pixels/pixelMapping.js.map +1 -0
  51. package/dist/pixels/pixelTracker.d.ts +81 -0
  52. package/dist/pixels/pixelTracker.d.ts.map +1 -0
  53. package/dist/pixels/pixelTracker.js +795 -0
  54. package/dist/pixels/pixelTracker.js.map +1 -0
  55. package/dist/pixels/types.d.ts +103 -0
  56. package/dist/pixels/types.d.ts.map +1 -0
  57. package/dist/pixels/types.js +14 -0
  58. package/dist/pixels/types.js.map +1 -0
  59. package/dist/react/hooks/useStep.d.ts +7 -0
  60. package/dist/react/hooks/useStep.d.ts.map +1 -0
  61. package/dist/react/hooks/useStep.js +14 -0
  62. package/dist/react/hooks/useStep.js.map +1 -0
  63. package/dist/react/index.d.ts +1 -0
  64. package/dist/react/index.d.ts.map +1 -1
  65. package/dist/react/index.js +1 -0
  66. package/dist/react/index.js.map +1 -1
  67. package/dist/stripe/index.d.ts +3 -1
  68. package/dist/stripe/index.d.ts.map +1 -1
  69. package/dist/stripe/index.js +13 -6
  70. package/dist/stripe/index.js.map +1 -1
  71. package/dist/stripe/useStripeExpress.d.ts.map +1 -1
  72. package/dist/stripe/useStripeExpress.js +15 -1
  73. package/dist/stripe/useStripeExpress.js.map +1 -1
  74. package/dist/tagada-headless-react.min.js +2 -2
  75. package/dist/tagada-headless-react.min.js.map +4 -4
  76. package/dist/tagada-headless.js +439 -16
  77. package/dist/tagada-headless.min.js +2 -2
  78. package/dist/tagada-headless.min.js.map +4 -4
  79. package/dist/tagada-pixels.min.js +7 -0
  80. package/dist/tagada-pixels.min.js.map +7 -0
  81. package/package.json +8 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @tagadapay/headless-sdk v1.14.1
2
+ * @tagadapay/headless-sdk v1.16.0
3
3
  * Headless checkout, payments, offers, and CRM
4
4
  * @license MIT
5
5
  */
@@ -63,6 +63,7 @@ var TagadaHeadlessBundle = (() => {
63
63
  TagadaPaymentError: () => TagadaPaymentError,
64
64
  TagadaRateLimitError: () => TagadaRateLimitError,
65
65
  TagadaValidationError: () => TagadaValidationError,
66
+ TrackModule: () => TrackModule,
66
67
  createHeadlessClient: () => createHeadlessClient,
67
68
  ensureGeoDataLoaded: () => ensureGeoDataLoaded,
68
69
  formatMoney: () => formatMoney,
@@ -237,10 +238,11 @@ var TagadaHeadlessBundle = (() => {
237
238
 
238
239
  // src/core/modules/checkout.ts
239
240
  var CheckoutModule = class _CheckoutModule {
240
- constructor(http, storeId, onTokenChange) {
241
+ constructor(http, storeId, onTokenChange, onSession) {
241
242
  this.http = http;
242
243
  this.storeId = storeId;
243
244
  this.onTokenChange = onTokenChange;
245
+ this.onSession = onSession;
244
246
  }
245
247
  /**
246
248
  * Returns the most recently loaded `CheckoutSession`, if any.
@@ -264,7 +266,7 @@ var TagadaHeadlessBundle = (() => {
264
266
  * Example URL: `/checkout?checkoutToken=abc123&token=eyJ...`
265
267
  */
266
268
  async loadSession(checkoutToken, sessionToken, opts) {
267
- var _a;
269
+ var _a, _b;
268
270
  if (sessionToken) {
269
271
  this.sessionToken = sessionToken;
270
272
  (_a = this.onTokenChange) == null ? void 0 : _a.call(this, sessionToken);
@@ -282,6 +284,10 @@ var TagadaHeadlessBundle = (() => {
282
284
  );
283
285
  const session = this.normalizeSession(raw);
284
286
  this._currentSession = session;
287
+ try {
288
+ (_b = this.onSession) == null ? void 0 : _b.call(this, session);
289
+ } catch {
290
+ }
285
291
  return session;
286
292
  }
287
293
  /**
@@ -751,6 +757,358 @@ var TagadaHeadlessBundle = (() => {
751
757
  return Boolean(((_a = address.line1) == null ? void 0 : _a.trim()) && ((_b = address.country) == null ? void 0 : _b.trim()));
752
758
  }
753
759
 
760
+ // src/core/version.ts
761
+ var SDK_NAME = "headless";
762
+ var SDK_VERSION = "1.16.0";
763
+
764
+ // src/core/modules/track.ts
765
+ var CDN_URL = false ? "" : "https://cdn.jsdelivr.net/npm/@tagadapay/headless-sdk@1.16.0/dist/tagada-pixels.min.js";
766
+ async function loadPixels() {
767
+ if (typeof window === "undefined")
768
+ return null;
769
+ if (CDN_URL) {
770
+ const w = window;
771
+ if (w.TagadaPixels)
772
+ return w.TagadaPixels;
773
+ await new Promise((resolve, reject) => {
774
+ const s = document.createElement("script");
775
+ s.src = CDN_URL;
776
+ s.async = true;
777
+ s.onload = () => resolve();
778
+ s.onerror = () => reject(new Error("pixel bundle failed to load"));
779
+ document.head.appendChild(s);
780
+ });
781
+ return w.TagadaPixels ?? null;
782
+ }
783
+ return await import("../../pixels/index.js");
784
+ }
785
+ var FLAG_KEY = "__tgd_track";
786
+ var ANON_KEY = "__tgd_aid";
787
+ var SESSION_KEY = "__tgd_sid";
788
+ var QUEUE_LIMIT = 50;
789
+ var FLUSH_MS = 2e3;
790
+ var DEDUP_MS = 1e3;
791
+ var STEP_MEMORY = 64;
792
+ function toContents(items) {
793
+ if (!Array.isArray(items) || items.length === 0)
794
+ return void 0;
795
+ return items.map((item) => ({ id: item.variantId, quantity: item.quantity, item_price: item.unitAmount }));
796
+ }
797
+ function scalars(data) {
798
+ if (!data)
799
+ return void 0;
800
+ const out = {};
801
+ for (const [key, value] of Object.entries(data)) {
802
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean")
803
+ out[key] = value;
804
+ }
805
+ return Object.keys(out).length > 0 ? out : void 0;
806
+ }
807
+ var STANDARD_EVENTS = {
808
+ lead: "Lead",
809
+ product_viewed: "ViewContent",
810
+ added_to_cart: "AddToCart"
811
+ };
812
+ function uuid() {
813
+ try {
814
+ return crypto.randomUUID();
815
+ } catch {
816
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
817
+ }
818
+ }
819
+ function toPixelsConfig(pixels) {
820
+ const out = {};
821
+ for (const pixel of pixels) {
822
+ const { provider, id, idKey, ...rest } = pixel;
823
+ (out[provider] ?? (out[provider] = [])).push({ ...rest, enabled: true, [idKey]: id, events: pixel.events ?? {} });
824
+ }
825
+ return out;
826
+ }
827
+ var TrackModule = class {
828
+ constructor(baseUrl, storeId, funnelKey) {
829
+ this.baseUrl = baseUrl;
830
+ this.storeId = storeId;
831
+ this.funnelKey = funnelKey;
832
+ this.records = [];
833
+ this.lastStep = /* @__PURE__ */ new Map();
834
+ this.signals = [];
835
+ this.currentStepId = null;
836
+ this.manifestHash = null;
837
+ this.loadedProviders = [];
838
+ this.configPromise = null;
839
+ this.enginePromise = null;
840
+ this.flushTimer = null;
841
+ this.listening = false;
842
+ }
843
+ // -- opt-in flag ----------------------------------------------------------
844
+ /**
845
+ * The flag is `<storeId>:<funnelKey>`. Only the storeId half arms it; the funnel half is DATA,
846
+ * read back by `flagFunnelKey()` so automatic events report the funnel the last `step()` named.
847
+ * A funnel-key mismatch never disarms tracking; only another tenant's storeId reads as disabled.
848
+ */
849
+ flag() {
850
+ try {
851
+ return (localStorage.getItem(FLAG_KEY) ?? "").split(":");
852
+ } catch {
853
+ return [];
854
+ }
855
+ }
856
+ enabled() {
857
+ return this.flag()[0] === this.storeId;
858
+ }
859
+ flagFunnelKey() {
860
+ return this.flag()[1] || this.funnelKey;
861
+ }
862
+ /** Every automatic path is off under SSR and off without the flag. */
863
+ off() {
864
+ return typeof window === "undefined" || !this.enabled();
865
+ }
866
+ arm() {
867
+ try {
868
+ localStorage.setItem(FLAG_KEY, `${this.storeId}:${this.funnelKey}`);
869
+ } catch {
870
+ }
871
+ }
872
+ // -- public API -----------------------------------------------------------
873
+ step(stepId, data) {
874
+ if (typeof window === "undefined")
875
+ return;
876
+ this.arm();
877
+ const path = (data == null ? void 0 : data.path) ?? window.location.pathname;
878
+ const key = `${stepId}|${path}`;
879
+ const at = Date.now();
880
+ const previous = this.lastStep.get(key);
881
+ if (previous !== void 0 && at - previous < DEDUP_MS)
882
+ return;
883
+ this.remember(key, at);
884
+ this.currentStepId = stepId;
885
+ this.listen();
886
+ this.push({ kind: "step", stepId, at, path });
887
+ void this.pixels().then((engine) => engine == null ? void 0 : engine.pageView(path, { stepId }));
888
+ }
889
+ event(name, data) {
890
+ if (this.off())
891
+ return;
892
+ const stepId = this.currentStepId;
893
+ if (stepId === null)
894
+ return;
895
+ const standard = STANDARD_EVENTS[name];
896
+ const eventId = `${standard ?? name}_${uuid()}`;
897
+ this.push({ kind: "event", stepId, name, at: Date.now(), eventId, props: scalars(data) });
898
+ void this.pixels().then(
899
+ (engine) => standard ? engine == null ? void 0 : engine.track(standard, data, { stepId, eventId }) : engine == null ? void 0 : engine.trackCustom(name, data, { stepId })
900
+ );
901
+ }
902
+ /** Keep the dedup map to entries still inside the window, and never past `STEP_MEMORY`. */
903
+ remember(key, at) {
904
+ for (const [seen, when] of this.lastStep) {
905
+ if (at - when >= DEDUP_MS)
906
+ this.lastStep.delete(seen);
907
+ }
908
+ while (this.lastStep.size >= STEP_MEMORY) {
909
+ const oldest = this.lastStep.keys().next();
910
+ if (oldest.done === true)
911
+ break;
912
+ this.lastStep.delete(oldest.value);
913
+ }
914
+ this.lastStep.set(key, at);
915
+ }
916
+ identify(who) {
917
+ if (this.off())
918
+ return;
919
+ void this.pixels().then(
920
+ (engine) => engine == null ? void 0 : engine.identify({ email: who.email, phone: who.phone, externalId: who.customerId })
921
+ );
922
+ }
923
+ consent(state) {
924
+ if (this.off())
925
+ return;
926
+ void this.pixels().then((engine) => engine == null ? void 0 : engine.consent(state));
927
+ }
928
+ async health() {
929
+ if (typeof window === "undefined")
930
+ return this.snapshot();
931
+ if (this.enabled())
932
+ await this.config();
933
+ return this.snapshot();
934
+ }
935
+ // -- internal hooks, called by the checkout and payment modules -----------
936
+ /** @internal */
937
+ noteCheckout(input) {
938
+ this.notePixel("InitiateCheckout", input.checkoutSessionId, input);
939
+ }
940
+ /** @internal */
941
+ notePaid(input) {
942
+ this.notePixel("Purchase", input.orderId, input);
943
+ }
944
+ /** The `eventId` formula is the server's `makeMetaEventId`, so Meta and TikTok dedup. */
945
+ notePixel(event, id, input) {
946
+ if (this.off())
947
+ return;
948
+ void this.pixels().then(
949
+ (engine) => {
950
+ var _a;
951
+ return engine == null ? void 0 : engine.track(
952
+ event,
953
+ { value: input.amount, currency: input.currency, contents: input.items, num_items: (_a = input.items) == null ? void 0 : _a.length },
954
+ { eventId: `${event}_${id}` }
955
+ );
956
+ }
957
+ );
958
+ }
959
+ /** @internal */
960
+ noteSignal(signal, value, detail) {
961
+ if (this.off())
962
+ return;
963
+ this.signals.push({ signal, value, detail });
964
+ this.listen();
965
+ this.push({ kind: "signal", signal, value, detail });
966
+ }
967
+ // -- config, engine -------------------------------------------------------
968
+ snapshot() {
969
+ return {
970
+ enabled: typeof window === "undefined" ? false : this.enabled(),
971
+ storeId: this.storeId,
972
+ funnelKey: this.flagFunnelKey(),
973
+ sdk: { name: SDK_NAME, version: SDK_VERSION },
974
+ manifestHash: this.manifestHash,
975
+ pixels: { loaded: this.loadedProviders.length > 0, providers: [...this.loadedProviders] },
976
+ signals: [...this.signals]
977
+ };
978
+ }
979
+ /**
980
+ * R4, once per page. Plain `fetch`, no header, no credentials — never `HttpClient`,
981
+ * which adds `Content-Type` and `x-cms-token` and so preflights against a fixed allow-list.
982
+ */
983
+ config() {
984
+ if (this.configPromise !== null)
985
+ return this.configPromise;
986
+ const url = `${this.baseUrl}/api/public/v1/sdk/config?storeId=${encodeURIComponent(this.storeId)}&funnel=${encodeURIComponent(this.flagFunnelKey())}`;
987
+ this.configPromise = (async () => {
988
+ try {
989
+ const response = await fetch(url);
990
+ if (!response.ok)
991
+ return null;
992
+ const config = await response.json();
993
+ this.manifestHash = config.manifestHash || null;
994
+ return config;
995
+ } catch {
996
+ return null;
997
+ }
998
+ })();
999
+ return this.configPromise;
1000
+ }
1001
+ pixels() {
1002
+ if (!this.enabled())
1003
+ return Promise.resolve(null);
1004
+ this.enginePromise ?? (this.enginePromise = this.startEngine());
1005
+ return this.enginePromise;
1006
+ }
1007
+ async startEngine() {
1008
+ var _a;
1009
+ try {
1010
+ const config = await this.config();
1011
+ if (config === null)
1012
+ return null;
1013
+ const module = await loadPixels();
1014
+ if (module === null)
1015
+ return null;
1016
+ const engine = module.createPixelEngine({
1017
+ pixels: toPixelsConfig(config.pixels ?? []),
1018
+ steps: config.steps,
1019
+ checkout: config.checkout,
1020
+ requireConsent: ((_a = config.consent) == null ? void 0 : _a.required) === true,
1021
+ onSignal: (signal) => this.onPixelSignal(signal)
1022
+ });
1023
+ await engine.init();
1024
+ this.loadedProviders = [...new Set((config.pixels ?? []).map((pixel) => pixel.provider))];
1025
+ return engine;
1026
+ } catch {
1027
+ return null;
1028
+ }
1029
+ }
1030
+ onPixelSignal(signal) {
1031
+ const detail = signal.detail ?? {};
1032
+ this.signals.push({ signal: signal.kind, value: signal.provider, detail });
1033
+ this.push({ kind: "signal", signal: signal.kind, value: signal.provider, detail });
1034
+ }
1035
+ // -- R5 queue -------------------------------------------------------------
1036
+ listen() {
1037
+ if (this.listening || typeof window === "undefined")
1038
+ return;
1039
+ this.listening = true;
1040
+ const onLeave = (event) => {
1041
+ if (event.type === "visibilitychange" && document.visibilityState !== "hidden")
1042
+ return;
1043
+ this.flush();
1044
+ };
1045
+ window.addEventListener("pagehide", onLeave);
1046
+ window.addEventListener("visibilitychange", onLeave);
1047
+ }
1048
+ push(record) {
1049
+ if (!this.enabled())
1050
+ return;
1051
+ this.records.push(record);
1052
+ if (this.records.length >= QUEUE_LIMIT) {
1053
+ this.flush();
1054
+ return;
1055
+ }
1056
+ if (this.flushTimer === null)
1057
+ this.flushTimer = setTimeout(() => this.flush(), FLUSH_MS);
1058
+ }
1059
+ /** `localStorage` is an undeclared identifier in Node, so it is read inside the try. */
1060
+ identifier(scope, key) {
1061
+ try {
1062
+ const store = scope === "local" ? localStorage : sessionStorage;
1063
+ const existing = store.getItem(key);
1064
+ if (existing)
1065
+ return existing;
1066
+ const fresh = uuid();
1067
+ store.setItem(key, fresh);
1068
+ return fresh;
1069
+ } catch {
1070
+ return uuid();
1071
+ }
1072
+ }
1073
+ flush() {
1074
+ if (this.flushTimer !== null) {
1075
+ clearTimeout(this.flushTimer);
1076
+ this.flushTimer = null;
1077
+ }
1078
+ if (this.records.length === 0)
1079
+ return;
1080
+ const records = this.records.splice(0, QUEUE_LIMIT);
1081
+ const payload = {
1082
+ storeId: this.storeId,
1083
+ funnelKey: this.flagFunnelKey(),
1084
+ sdk: { name: SDK_NAME, version: SDK_VERSION },
1085
+ anonymousId: this.identifier("local", ANON_KEY),
1086
+ sessionId: this.identifier("session", SESSION_KEY),
1087
+ records
1088
+ };
1089
+ if (this.manifestHash !== null)
1090
+ payload.manifestHash = this.manifestHash;
1091
+ const body = JSON.stringify(payload);
1092
+ const url = `${this.baseUrl}/api/public/v1/sdk/events`;
1093
+ try {
1094
+ if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
1095
+ if (navigator.sendBeacon(url, new Blob([body], { type: "text/plain;charset=UTF-8" })))
1096
+ return;
1097
+ }
1098
+ } catch {
1099
+ }
1100
+ try {
1101
+ void fetch(url, {
1102
+ method: "POST",
1103
+ body,
1104
+ keepalive: true,
1105
+ headers: { "content-type": "text/plain;charset=UTF-8" }
1106
+ }).catch(() => void 0);
1107
+ } catch {
1108
+ }
1109
+ }
1110
+ };
1111
+
754
1112
  // src/core/modules/payment.ts
755
1113
  var EXPRESS_METHOD_TYPES = /* @__PURE__ */ new Set(["apple_pay", "google_pay", "paypal", "klarna", "klarna_express", "link"]);
756
1114
  function paymentMethodListEntryToConfig(entry) {
@@ -775,11 +1133,13 @@ var TagadaHeadlessBundle = (() => {
775
1133
  };
776
1134
  }
777
1135
  var PaymentModule = class {
778
- constructor(http, storeId, environment, checkout) {
1136
+ constructor(http, storeId, environment, checkout, onPaid, onSignal) {
779
1137
  this.http = http;
780
1138
  this.storeId = storeId;
781
1139
  this.environment = environment;
782
1140
  this.checkout = checkout;
1141
+ this.onPaid = onPaid;
1142
+ this.onSignal = onSignal;
783
1143
  /**
784
1144
  * Last payment setup seen by `getPaymentSetup` — lets `processApplePay`
785
1145
  * resolve the domain's Apple Pay registration (BYOK / platform-integrator)
@@ -847,7 +1207,7 @@ var TagadaHeadlessBundle = (() => {
847
1207
  * filtered to only those available in the current browser.
848
1208
  */
849
1209
  async getExpressMethods(checkoutSessionId, opts) {
850
- var _a, _b, _c, _d, _e, _f, _g, _h;
1210
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
851
1211
  const setup = await this.getPaymentSetup(checkoutSessionId, opts);
852
1212
  const result = {};
853
1213
  for (const [key, config] of Object.entries(setup)) {
@@ -865,12 +1225,15 @@ var TagadaHeadlessBundle = (() => {
865
1225
  result.applePay = { ...base2, type: "apple_pay", available: !!hasApplePay };
866
1226
  }
867
1227
  if ((_d = config.methods.google_pay) == null ? void 0 : _d.enabled) {
1228
+ if (typeof window !== "undefined" && !("PaymentRequest" in window)) {
1229
+ (_e = this.onSignal) == null ? void 0 : _e.call(this, "googlePay", { reason: "no-payment-request-api" });
1230
+ }
868
1231
  result.googlePay = { ...base2, type: "google_pay" };
869
1232
  }
870
- if ((_e = config.methods.link) == null ? void 0 : _e.enabled) {
1233
+ if ((_f = config.methods.link) == null ? void 0 : _f.enabled) {
871
1234
  result.link = { ...base2, type: "link" };
872
1235
  }
873
- if ((_f = config.methods.klarna_express) == null ? void 0 : _f.enabled) {
1236
+ if ((_g = config.methods.klarna_express) == null ? void 0 : _g.enabled) {
874
1237
  result.klarna = { ...base2, type: "klarna_express" };
875
1238
  }
876
1239
  continue;
@@ -886,9 +1249,12 @@ var TagadaHeadlessBundle = (() => {
886
1249
  metadata: config.metadata
887
1250
  };
888
1251
  if (method === "apple_pay" || config.type === "apple_pay") {
889
- const hasApplePay = typeof window !== "undefined" && "ApplePaySession" in window && ((_h = (_g = window.ApplePaySession) == null ? void 0 : _g.canMakePayments) == null ? void 0 : _h.call(_g));
1252
+ const hasApplePay = typeof window !== "undefined" && "ApplePaySession" in window && ((_i = (_h = window.ApplePaySession) == null ? void 0 : _h.canMakePayments) == null ? void 0 : _i.call(_h));
890
1253
  result.applePay = { ...base, available: !!hasApplePay };
891
1254
  } else if (method === "google_pay" || config.type === "google_pay") {
1255
+ if (typeof window !== "undefined" && !("PaymentRequest" in window)) {
1256
+ (_j = this.onSignal) == null ? void 0 : _j.call(this, "googlePay", { reason: "no-payment-request-api" });
1257
+ }
892
1258
  result.googlePay = base;
893
1259
  } else if (method === "paypal") {
894
1260
  result.paypal = base;
@@ -1287,14 +1653,15 @@ var TagadaHeadlessBundle = (() => {
1287
1653
  * don't need to re-pay.
1288
1654
  */
1289
1655
  async finalize(input, opts) {
1290
- var _a, _b;
1656
+ var _a, _b, _c;
1291
1657
  const payment = input.payment;
1292
1658
  const returnUrl = (opts == null ? void 0 : opts.returnUrl) ?? (typeof window !== "undefined" ? window.location.href : void 0);
1293
1659
  if (payment.status === "succeeded") {
1660
+ this.emitPaid((_a = input.order) == null ? void 0 : _a.id, payment);
1294
1661
  return { status: "succeeded", payment, order: input.order };
1295
1662
  }
1296
1663
  if (payment.status === "failed" || payment.status === "declined") {
1297
- const msg = ((_a = payment.requireActionData) == null ? void 0 : _a.message) || ((_b = payment.requireActionData) == null ? void 0 : _b.errorCode) || `Payment ${payment.status}`;
1664
+ const msg = ((_b = payment.requireActionData) == null ? void 0 : _b.message) || ((_c = payment.requireActionData) == null ? void 0 : _c.errorCode) || `Payment ${payment.status}`;
1298
1665
  return { status: "failed", error: msg, payment };
1299
1666
  }
1300
1667
  if (payment.requireAction && payment.requireAction !== "none" && payment.requireActionData) {
@@ -1528,6 +1895,7 @@ var TagadaHeadlessBundle = (() => {
1528
1895
  * Also handles new requireAction that may appear during polling.
1529
1896
  */
1530
1897
  async pollUntilDone(paymentId, opts) {
1898
+ var _a;
1531
1899
  const interval = (opts == null ? void 0 : opts.intervalMs) ?? 1500;
1532
1900
  const timeout = (opts == null ? void 0 : opts.timeoutMs) ?? 6e4;
1533
1901
  const start = Date.now();
@@ -1546,6 +1914,9 @@ var TagadaHeadlessBundle = (() => {
1546
1914
  continue;
1547
1915
  }
1548
1916
  if (status.status === "succeeded" || status.status === "pending" && status.subStatus === "authorized") {
1917
+ if (status.status === "succeeded") {
1918
+ this.emitPaid((_a = status.order) == null ? void 0 : _a.id, status);
1919
+ }
1549
1920
  return { status: "succeeded", payment: status, order: status.order };
1550
1921
  }
1551
1922
  if (status.status === "failed" || status.status === "declined") {
@@ -1581,14 +1952,32 @@ var TagadaHeadlessBundle = (() => {
1581
1952
  return null;
1582
1953
  }
1583
1954
  }
1955
+ /** Tracking hook. Never throws, never awaits: a pixel must not be able to break a payment. */
1956
+ emitPaid(orderId, payment) {
1957
+ var _a;
1958
+ if (!orderId || !this.onPaid)
1959
+ return;
1960
+ try {
1961
+ const session = (_a = this.checkout) == null ? void 0 : _a.getCurrentSession();
1962
+ this.onPaid({
1963
+ orderId,
1964
+ amount: payment.amount,
1965
+ currency: payment.currency,
1966
+ items: toContents(session == null ? void 0 : session.items)
1967
+ });
1968
+ } catch {
1969
+ }
1970
+ }
1584
1971
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1585
1972
  async loadCoreJs() {
1586
1973
  try {
1587
1974
  return await import("@tagadapay/core-js");
1588
- } catch {
1589
- throw new Error(
1975
+ } catch (cause) {
1976
+ const error = new Error(
1590
1977
  "This method requires @tagadapay/core-js. Install it: npm install @tagadapay/core-js"
1591
1978
  );
1979
+ error.cause = cause;
1980
+ throw error;
1592
1981
  }
1593
1982
  }
1594
1983
  };
@@ -2022,8 +2411,24 @@ var TagadaHeadlessBundle = (() => {
2022
2411
 
2023
2412
  // src/core/modules/rx.ts
2024
2413
  var RxModule = class {
2025
- constructor(http) {
2414
+ constructor(http, storeId) {
2026
2415
  this.http = http;
2416
+ this.storeId = storeId;
2417
+ }
2418
+ /**
2419
+ * Activation status of the store: steps, missing items and whether Rx
2420
+ * payments are `setting_up` or `ready`. Storefronts use it to show
2421
+ * "Payments are being set up" instead of letting a checkout fail.
2422
+ *
2423
+ * Read-only / public. Defaults to the client's `storeId`.
2424
+ */
2425
+ async getActivationStatus(storeId) {
2426
+ const id = storeId ?? this.storeId;
2427
+ if (!id)
2428
+ throw new Error("rx.getActivationStatus: storeId is required");
2429
+ return this.http.get("/api/public/v1/rx/activation-status", {
2430
+ params: { storeId: id }
2431
+ });
2027
2432
  }
2028
2433
  /**
2029
2434
  * Check whether a product requires an Rx case. Storefronts call this
@@ -2087,11 +2492,29 @@ var TagadaHeadlessBundle = (() => {
2087
2492
  apiKey: config.apiKey,
2088
2493
  getToken: () => this._sessionToken
2089
2494
  });
2495
+ this.track = new TrackModule(baseUrl, this.config.storeId, this.config.funnel ?? "main");
2090
2496
  const setToken = (token) => {
2091
2497
  this._sessionToken = token;
2092
2498
  };
2093
- this.checkout = new CheckoutModule(this.http, this.config.storeId, setToken);
2094
- this.payment = new PaymentModule(this.http, this.config.storeId, environment, this.checkout);
2499
+ this.checkout = new CheckoutModule(
2500
+ this.http,
2501
+ this.config.storeId,
2502
+ setToken,
2503
+ (s) => this.track.noteCheckout({
2504
+ checkoutSessionId: s.id,
2505
+ amount: s.totals.total,
2506
+ currency: s.totals.currency,
2507
+ items: toContents(s.items)
2508
+ })
2509
+ );
2510
+ this.payment = new PaymentModule(
2511
+ this.http,
2512
+ this.config.storeId,
2513
+ environment,
2514
+ this.checkout,
2515
+ (p) => this.track.notePaid(p),
2516
+ (v, d) => this.track.noteSignal("wallet_unavailable", v, d)
2517
+ );
2095
2518
  this.offers = new OffersModule(this.http, this.config.storeId, this.payment);
2096
2519
  this.customer = new CustomerModule(this.http, this.config.storeId);
2097
2520
  this.catalog = new CatalogModule(this.http, this.config.storeId);
@@ -2101,7 +2524,7 @@ var TagadaHeadlessBundle = (() => {
2101
2524
  this.checkout,
2102
2525
  this.config.accountId
2103
2526
  );
2104
- this.rx = new RxModule(this.http);
2527
+ this.rx = new RxModule(this.http, this.config.storeId);
2105
2528
  }
2106
2529
  /** Manually set the session token (e.g. from URL params on page load). */
2107
2530
  setSessionToken(token) {