@tagadapay/headless-sdk 1.14.0 → 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 +13 -11
  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 +442 -26
  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.0
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
  /**
@@ -359,16 +365,9 @@ var TagadaHeadlessBundle = (() => {
359
365
  * ```
360
366
  */
361
367
  async createSessionUrl(opts) {
362
- const session = await this.createSession({
363
- items: opts.items,
364
- currency: opts.currency,
365
- returnUrl: opts.returnUrl,
366
- customerEmail: opts.customerEmail,
367
- customerFirstName: opts.customerFirstName,
368
- customerLastName: opts.customerLastName
369
- });
370
- const origin = opts.origin ?? (typeof window !== "undefined" ? window.location.origin : "");
371
- const checkoutPath = opts.checkoutPath ?? "/checkout";
368
+ const { checkoutPath = "/checkout", origin: originOverride, ...sessionOpts } = opts;
369
+ const session = await this.createSession(sessionOpts);
370
+ const origin = originOverride ?? (typeof window !== "undefined" ? window.location.origin : "");
372
371
  const params = new URLSearchParams({
373
372
  checkoutToken: session.checkoutToken,
374
373
  sessionToken: session.sessionToken
@@ -758,6 +757,358 @@ var TagadaHeadlessBundle = (() => {
758
757
  return Boolean(((_a = address.line1) == null ? void 0 : _a.trim()) && ((_b = address.country) == null ? void 0 : _b.trim()));
759
758
  }
760
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
+
761
1112
  // src/core/modules/payment.ts
762
1113
  var EXPRESS_METHOD_TYPES = /* @__PURE__ */ new Set(["apple_pay", "google_pay", "paypal", "klarna", "klarna_express", "link"]);
763
1114
  function paymentMethodListEntryToConfig(entry) {
@@ -782,11 +1133,13 @@ var TagadaHeadlessBundle = (() => {
782
1133
  };
783
1134
  }
784
1135
  var PaymentModule = class {
785
- constructor(http, storeId, environment, checkout) {
1136
+ constructor(http, storeId, environment, checkout, onPaid, onSignal) {
786
1137
  this.http = http;
787
1138
  this.storeId = storeId;
788
1139
  this.environment = environment;
789
1140
  this.checkout = checkout;
1141
+ this.onPaid = onPaid;
1142
+ this.onSignal = onSignal;
790
1143
  /**
791
1144
  * Last payment setup seen by `getPaymentSetup` — lets `processApplePay`
792
1145
  * resolve the domain's Apple Pay registration (BYOK / platform-integrator)
@@ -854,7 +1207,7 @@ var TagadaHeadlessBundle = (() => {
854
1207
  * filtered to only those available in the current browser.
855
1208
  */
856
1209
  async getExpressMethods(checkoutSessionId, opts) {
857
- var _a, _b, _c, _d, _e, _f, _g, _h;
1210
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
858
1211
  const setup = await this.getPaymentSetup(checkoutSessionId, opts);
859
1212
  const result = {};
860
1213
  for (const [key, config] of Object.entries(setup)) {
@@ -872,12 +1225,15 @@ var TagadaHeadlessBundle = (() => {
872
1225
  result.applePay = { ...base2, type: "apple_pay", available: !!hasApplePay };
873
1226
  }
874
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
+ }
875
1231
  result.googlePay = { ...base2, type: "google_pay" };
876
1232
  }
877
- if ((_e = config.methods.link) == null ? void 0 : _e.enabled) {
1233
+ if ((_f = config.methods.link) == null ? void 0 : _f.enabled) {
878
1234
  result.link = { ...base2, type: "link" };
879
1235
  }
880
- if ((_f = config.methods.klarna_express) == null ? void 0 : _f.enabled) {
1236
+ if ((_g = config.methods.klarna_express) == null ? void 0 : _g.enabled) {
881
1237
  result.klarna = { ...base2, type: "klarna_express" };
882
1238
  }
883
1239
  continue;
@@ -893,9 +1249,12 @@ var TagadaHeadlessBundle = (() => {
893
1249
  metadata: config.metadata
894
1250
  };
895
1251
  if (method === "apple_pay" || config.type === "apple_pay") {
896
- 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));
897
1253
  result.applePay = { ...base, available: !!hasApplePay };
898
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
+ }
899
1258
  result.googlePay = base;
900
1259
  } else if (method === "paypal") {
901
1260
  result.paypal = base;
@@ -1294,14 +1653,15 @@ var TagadaHeadlessBundle = (() => {
1294
1653
  * don't need to re-pay.
1295
1654
  */
1296
1655
  async finalize(input, opts) {
1297
- var _a, _b;
1656
+ var _a, _b, _c;
1298
1657
  const payment = input.payment;
1299
1658
  const returnUrl = (opts == null ? void 0 : opts.returnUrl) ?? (typeof window !== "undefined" ? window.location.href : void 0);
1300
1659
  if (payment.status === "succeeded") {
1660
+ this.emitPaid((_a = input.order) == null ? void 0 : _a.id, payment);
1301
1661
  return { status: "succeeded", payment, order: input.order };
1302
1662
  }
1303
1663
  if (payment.status === "failed" || payment.status === "declined") {
1304
- 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}`;
1305
1665
  return { status: "failed", error: msg, payment };
1306
1666
  }
1307
1667
  if (payment.requireAction && payment.requireAction !== "none" && payment.requireActionData) {
@@ -1535,6 +1895,7 @@ var TagadaHeadlessBundle = (() => {
1535
1895
  * Also handles new requireAction that may appear during polling.
1536
1896
  */
1537
1897
  async pollUntilDone(paymentId, opts) {
1898
+ var _a;
1538
1899
  const interval = (opts == null ? void 0 : opts.intervalMs) ?? 1500;
1539
1900
  const timeout = (opts == null ? void 0 : opts.timeoutMs) ?? 6e4;
1540
1901
  const start = Date.now();
@@ -1553,6 +1914,9 @@ var TagadaHeadlessBundle = (() => {
1553
1914
  continue;
1554
1915
  }
1555
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
+ }
1556
1920
  return { status: "succeeded", payment: status, order: status.order };
1557
1921
  }
1558
1922
  if (status.status === "failed" || status.status === "declined") {
@@ -1588,14 +1952,32 @@ var TagadaHeadlessBundle = (() => {
1588
1952
  return null;
1589
1953
  }
1590
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
+ }
1591
1971
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1592
1972
  async loadCoreJs() {
1593
1973
  try {
1594
1974
  return await import("@tagadapay/core-js");
1595
- } catch {
1596
- throw new Error(
1975
+ } catch (cause) {
1976
+ const error = new Error(
1597
1977
  "This method requires @tagadapay/core-js. Install it: npm install @tagadapay/core-js"
1598
1978
  );
1979
+ error.cause = cause;
1980
+ throw error;
1599
1981
  }
1600
1982
  }
1601
1983
  };
@@ -2029,8 +2411,24 @@ var TagadaHeadlessBundle = (() => {
2029
2411
 
2030
2412
  // src/core/modules/rx.ts
2031
2413
  var RxModule = class {
2032
- constructor(http) {
2414
+ constructor(http, storeId) {
2033
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
+ });
2034
2432
  }
2035
2433
  /**
2036
2434
  * Check whether a product requires an Rx case. Storefronts call this
@@ -2094,11 +2492,29 @@ var TagadaHeadlessBundle = (() => {
2094
2492
  apiKey: config.apiKey,
2095
2493
  getToken: () => this._sessionToken
2096
2494
  });
2495
+ this.track = new TrackModule(baseUrl, this.config.storeId, this.config.funnel ?? "main");
2097
2496
  const setToken = (token) => {
2098
2497
  this._sessionToken = token;
2099
2498
  };
2100
- this.checkout = new CheckoutModule(this.http, this.config.storeId, setToken);
2101
- 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
+ );
2102
2518
  this.offers = new OffersModule(this.http, this.config.storeId, this.payment);
2103
2519
  this.customer = new CustomerModule(this.http, this.config.storeId);
2104
2520
  this.catalog = new CatalogModule(this.http, this.config.storeId);
@@ -2108,7 +2524,7 @@ var TagadaHeadlessBundle = (() => {
2108
2524
  this.checkout,
2109
2525
  this.config.accountId
2110
2526
  );
2111
- this.rx = new RxModule(this.http);
2527
+ this.rx = new RxModule(this.http, this.config.storeId);
2112
2528
  }
2113
2529
  /** Manually set the session token (e.g. from URL params on page load). */
2114
2530
  setSessionToken(token) {