@economist/web-apple-pay 1.0.0 → 1.1.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.
package/README.md ADDED
@@ -0,0 +1,140 @@
1
+ # @economist/web-apple-pay
2
+
3
+ Framework-agnostic Apple Pay UI components implemented as Web Components.
4
+
5
+ This package provides:
6
+
7
+ - `teg-apple-pay-button`
8
+ - `teg-apple-pay-modal`
9
+
10
+ It is intentionally UI-focused. Consumer applications own backend payment orchestration, merchant validation, and secret handling.
11
+
12
+ ## Requirements
13
+
14
+ - Node.js `20.x` or later
15
+ - npm `10.x` or later
16
+
17
+ ## Installation
18
+
19
+ ```sh
20
+ npm install --save @economist/web-apple-pay
21
+ ```
22
+
23
+ ## Quick start
24
+
25
+ ### 1) Configure runtime values
26
+
27
+ Call `configureApplePay(...)` once during app startup, before rendering Apple Pay UI:
28
+
29
+ ```ts
30
+ import { configureApplePay, defineApplePayElements } from '@economist/web-apple-pay';
31
+
32
+ configureApplePay({
33
+ merchantId: 'merchant.com.economist.your-app',
34
+ debugBypassEnabled: false,
35
+ });
36
+
37
+ defineApplePayElements();
38
+ ```
39
+
40
+ ### 2) Create and mount a button
41
+
42
+ ```ts
43
+ import type { Offer, ApplePayButton } from '@economist/web-apple-pay';
44
+
45
+ const button = document.createElement('teg-apple-pay-button') as ApplePayButton;
46
+ button.offer = offer as Offer;
47
+ button.country = 'US';
48
+ button.dataset.loggedIn = 'true';
49
+
50
+ container.appendChild(button);
51
+ ```
52
+
53
+ When clicked, the button creates and opens `teg-apple-pay-modal` automatically.
54
+
55
+ ## Runtime configuration
56
+
57
+ ### `configureApplePay(config)`
58
+
59
+ Sets package-level runtime configuration used by Apple Pay availability checks.
60
+
61
+ `config` shape:
62
+
63
+ - `merchantId` (`string`, required): Apple Pay merchant identifier used for active-card checks.
64
+ - `debugBypassEnabled` (`boolean`, optional): allows `?BYPASS_APPLE_PAY_CHECKS=1` query param to bypass capability checks in non-production-style debugging flows.
65
+
66
+ If `merchantId` is not configured, Apple Pay availability checks return `false` and the button hides itself.
67
+
68
+ ## Component behavior
69
+
70
+ ### `ApplePayButton`
71
+
72
+ - Requires `offer` and `country` before click flow is meaningful.
73
+ - Checks Apple Pay availability on connect; if unavailable, it hides.
74
+ - Skips rendering for excluded variants (`bundle`, `insider_print`).
75
+ - Forwards `data-logged-in` from button to modal (`modal.dataset.loggedIn`).
76
+
77
+ ### `ApplePayModal`
78
+
79
+ - Opened by `ApplePayButton` click.
80
+ - Emits `apple-pay-confirmed` when CTA is accepted.
81
+ - `Escape` closes the modal and prevents propagation to other global handlers.
82
+
83
+ ## Events
84
+
85
+ ### `apple-pay-confirmed`
86
+
87
+ Dispatched from `ApplePayModal` as a `CustomEvent` with:
88
+
89
+ - `detail.skuId` (`string`)
90
+ - `detail.country` (`string`)
91
+
92
+ Consumers should listen for this event and continue payment orchestration.
93
+
94
+ ## Public API
95
+
96
+ - `defineApplePayElements()`: registers both custom elements.
97
+ - `configureApplePay(config)`: sets required runtime config (merchant ID and optional debug bypass).
98
+ - `ApplePayButton`: button component class.
99
+ - `ApplePayModal`: modal component class.
100
+ - `ensureApplePayLogoSymbol()`: logo symbol injection helper.
101
+ - `Offer`: offer contract type used by component props.
102
+
103
+ ## Troubleshooting
104
+
105
+ ### Button does not appear
106
+
107
+ Check:
108
+
109
+ - `configureApplePay(...)` was called before rendering (with a valid `merchantId`)
110
+ - device/browser supports Apple Pay checks (Safari/Apple device logic)
111
+ - query string/feature flag gating for your page is satisfied
112
+ - offer variant is not excluded (`bundle`, `insider_print`)
113
+
114
+ ### Logged-in checkbox copy looks wrong
115
+
116
+ Set `button.dataset.loggedIn = 'true'` before appending the button. The button forwards this flag to the modal automatically.
117
+
118
+ ## Security and responsibilities
119
+
120
+ Merchant setup must be handled by the consuming application:
121
+
122
+ - inject merchant config at runtime
123
+ - perform merchant validation on trusted backend services
124
+ - keep certificates/secrets outside this package
125
+
126
+ ## Local development
127
+
128
+ From `shared-packages`:
129
+
130
+ ```sh
131
+ cd packages/web-apple-pay
132
+ npm run build
133
+ yalc publish --sig
134
+ ```
135
+
136
+ Then in a consuming app:
137
+
138
+ ```sh
139
+ yalc add @economist/web-apple-pay
140
+ ```
package/dist/index.js CHANGED
@@ -36,14 +36,10 @@ function formatPrice(price, currencyCode, shouldFixDecimalPlaces = true, roundUp
36
36
  const points = numberOfDecimalPoints(price);
37
37
  const calculatedPrice = () => {
38
38
  if (roundUp) {
39
- return (Math.ceil(price * PRICE_CONSTANTS.CENTS_MULTIPLIER) / PRICE_CONSTANTS.CENTS_MULTIPLIER).toFixed(
40
- PRICE_CONSTANTS.DECIMAL_PLACES
41
- );
39
+ return (Math.ceil(price * PRICE_CONSTANTS.CENTS_MULTIPLIER) / PRICE_CONSTANTS.CENTS_MULTIPLIER).toFixed(PRICE_CONSTANTS.DECIMAL_PLACES);
42
40
  }
43
41
  if (shouldFixDecimalPlaces) {
44
- return (Math.round(price * PRICE_CONSTANTS.CENTS_MULTIPLIER) / PRICE_CONSTANTS.CENTS_MULTIPLIER).toFixed(
45
- PRICE_CONSTANTS.DECIMAL_PLACES
46
- );
42
+ return (Math.round(price * PRICE_CONSTANTS.CENTS_MULTIPLIER) / PRICE_CONSTANTS.CENTS_MULTIPLIER).toFixed(PRICE_CONSTANTS.DECIMAL_PLACES);
47
43
  }
48
44
  return price;
49
45
  };
@@ -222,23 +218,23 @@ var apple_pay_modal_template_default = `<style>
222
218
  height: 20px;
223
219
  }
224
220
 
225
- .apm__checkbox-input:not(:checked) ~ .apm__checkbox-custom .svg-unchecked {
221
+ .apm__checkbox-input:not(:checked)~.apm__checkbox-custom .svg-unchecked {
226
222
  display: block;
227
223
  }
228
224
 
229
- .apm__checkbox-input:checked ~ .apm__checkbox-custom .svg-checked {
225
+ .apm__checkbox-input:checked~.apm__checkbox-custom .svg-checked {
230
226
  display: block;
231
227
  }
232
228
 
233
- .apm__checkbox-label--error .apm__checkbox-input:not(:checked) ~ .apm__checkbox-custom .svg-unchecked {
229
+ .apm__checkbox-label--error .apm__checkbox-input:not(:checked)~.apm__checkbox-custom .svg-unchecked {
234
230
  display: none;
235
231
  }
236
232
 
237
- .apm__checkbox-label--error .apm__checkbox-input:not(:checked) ~ .apm__checkbox-custom .svg-error {
233
+ .apm__checkbox-label--error .apm__checkbox-input:not(:checked)~.apm__checkbox-custom .svg-error {
238
234
  display: block;
239
235
  }
240
236
 
241
- .apm__checkbox-input:focus-visible ~ .apm__checkbox-custom {
237
+ .apm__checkbox-input:focus-visible~.apm__checkbox-custom {
242
238
  outline: solid 2px #c91d42;
243
239
  outline-offset: 2px;
244
240
  border-radius: 4px;
@@ -321,13 +317,16 @@ var apple_pay_modal_template_default = `<style>
321
317
  </style>
322
318
 
323
319
  <div class="apm-overlay" id="apple-pay-modal-overlay">
324
- <div class="apm" role="dialog" aria-modal="true" aria-labelledby="apple-pay-modal-offer-name" id="apple-pay-modal" tabindex="-1">
320
+ <div class="apm" role="dialog" aria-modal="true" aria-labelledby="apple-pay-modal-offer-name" id="apple-pay-modal"
321
+ tabindex="-1">
325
322
  <button class="apm__close" id="apple-pay-modal-close" aria-label="Close">
326
323
  <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
327
324
  <g clip-path="url(#clip0_15712_93054)">
328
325
  <rect width="24" height="24" fill="white" fill-opacity="0.01" />
329
326
  <path fill-rule="evenodd" clip-rule="evenodd" d="M0 0H24V24H0V0Z" fill="white" fill-opacity="0.01" />
330
- <path fill-rule="evenodd" clip-rule="evenodd" d="M18.75 6.60964L17.3904 5.25L12 10.6404L6.60964 5.25L5.25 6.60964L10.6404 12L5.25 17.3904L6.60964 18.75L12 13.3596L17.3904 18.75L18.75 17.3904L13.3596 12L18.75 6.60964Z" fill="#333333" />
327
+ <path fill-rule="evenodd" clip-rule="evenodd"
328
+ d="M18.75 6.60964L17.3904 5.25L12 10.6404L6.60964 5.25L5.25 6.60964L10.6404 12L5.25 17.3904L6.60964 18.75L12 13.3596L17.3904 18.75L18.75 17.3904L13.3596 12L18.75 6.60964Z"
329
+ fill="#333333" />
331
330
  </g>
332
331
  <defs>
333
332
  <clipPath id="clip0_15712_93054">
@@ -344,22 +343,34 @@ var apple_pay_modal_template_default = `<style>
344
343
  <div class="apm__checkbox-wrapper">
345
344
  <input class="apm__checkbox-input" type="checkbox" id="apple-pay-modal-checkbox" />
346
345
  <span class="apm__checkbox-custom">
347
- <svg class="svg-unchecked" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
348
- <path d="M6 2.5H18C19.933 2.5 21.5 4.067 21.5 6V18C21.5 19.933 19.933 21.5 18 21.5H6C4.067 21.5 2.5 19.933 2.5 18V6C2.5 4.067 4.067 2.5 6 2.5Z" fill="white" stroke="#595959" />
346
+ <svg class="svg-unchecked" width="24" height="24" viewBox="0 0 24 24" fill="none"
347
+ xmlns="http://www.w3.org/2000/svg">
348
+ <path
349
+ d="M6 2.5H18C19.933 2.5 21.5 4.067 21.5 6V18C21.5 19.933 19.933 21.5 18 21.5H6C4.067 21.5 2.5 19.933 2.5 18V6C2.5 4.067 4.067 2.5 6 2.5Z"
350
+ fill="white" stroke="#595959" />
349
351
  </svg>
350
- <svg class="svg-checked" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
352
+ <svg class="svg-checked" width="24" height="24" viewBox="0 0 24 24" fill="none"
353
+ xmlns="http://www.w3.org/2000/svg">
351
354
  <g clip-path="url(#clip0_20014_9555)">
352
- <path d="M2 6C2 3.79086 3.79086 2 6 2H18C20.2091 2 22 3.79086 22 6V18C22 20.2091 20.2091 22 18 22H6C3.79086 22 2 20.2091 2 18V6Z" fill="#333333" />
353
- <path d="M10.2857 17L6 12.6814L7.20857 11.4635L10.2857 14.5557L16.7914 8L18 9.22649L10.2857 17Z" fill="white" />
355
+ <path
356
+ d="M2 6C2 3.79086 3.79086 2 6 2H18C20.2091 2 22 3.79086 22 6V18C22 20.2091 20.2091 22 18 22H6C3.79086 22 2 20.2091 2 18V6Z"
357
+ fill="#333333" />
358
+ <path d="M10.2857 17L6 12.6814L7.20857 11.4635L10.2857 14.5557L16.7914 8L18 9.22649L10.2857 17Z"
359
+ fill="white" />
354
360
  </g>
355
361
  <defs>
356
362
  <clipPath id="clip0_20014_9555">
357
- <path d="M2 6C2 3.79086 3.79086 2 6 2H18C20.2091 2 22 3.79086 22 6V18C22 20.2091 20.2091 22 18 22H6C3.79086 22 2 20.2091 2 18V6Z" fill="white" />
363
+ <path
364
+ d="M2 6C2 3.79086 3.79086 2 6 2H18C20.2091 2 22 3.79086 22 6V18C22 20.2091 20.2091 22 18 22H6C3.79086 22 2 20.2091 2 18V6Z"
365
+ fill="white" />
358
366
  </clipPath>
359
367
  </defs>
360
368
  </svg>
361
- <svg class="svg-error" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
362
- <path d="M6 2.5H18C19.933 2.5 21.5 4.067 21.5 6V18C21.5 19.933 19.933 21.5 18 21.5H6C4.067 21.5 2.5 19.933 2.5 18V6C2.5 4.067 4.067 2.5 6 2.5Z" fill="white" stroke="#C91D42" stroke-width="2" />
369
+ <svg class="svg-error" width="24" height="24" viewBox="0 0 24 24" fill="none"
370
+ xmlns="http://www.w3.org/2000/svg">
371
+ <path
372
+ d="M6 2.5H18C19.933 2.5 21.5 4.067 21.5 6V18C21.5 19.933 19.933 21.5 18 21.5H6C4.067 21.5 2.5 19.933 2.5 18V6C2.5 4.067 4.067 2.5 6 2.5Z"
373
+ fill="white" stroke="#C91D42" stroke-width="2" />
363
374
  </svg>
364
375
  </span>
365
376
  </div>
@@ -367,7 +378,9 @@ var apple_pay_modal_template_default = `<style>
367
378
  </label>
368
379
  <div class="apm__error" id="apple-pay-modal-error" role="alert">
369
380
  <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
370
- <path fill-rule="evenodd" clip-rule="evenodd" d="M10 2C5.58172 2 2 5.58172 2 10C2 14.4183 5.58172 18 10 18C14.4183 18 18 14.4183 18 10C18 5.58172 14.4183 2 10 2ZM0 10C0 4.47715 4.47715 0 10 0C15.5228 0 20 4.47715 20 10C20 15.5228 15.5228 20 10 20C4.47715 20 0 15.5228 0 10ZM11 13C11 13.5523 10.5523 14 10 14C9.44772 14 9 13.5523 9 13C9 12.4477 9.44772 12 10 12C10.5523 12 11 12.4477 11 13ZM10 6C10.5523 6 11 6.44772 11 7V10C11 10.5523 10.5523 11 10 11C9.44772 11 9 10.5523 9 10V7C9 6.44772 9.44772 6 10 6Z" fill="#C91D42" />
381
+ <path fill-rule="evenodd" clip-rule="evenodd"
382
+ d="M10 2C5.58172 2 2 5.58172 2 10C2 14.4183 5.58172 18 10 18C14.4183 18 18 14.4183 18 10C18 5.58172 14.4183 2 10 2ZM0 10C0 4.47715 4.47715 0 10 0C15.5228 0 20 4.47715 20 10C20 15.5228 15.5228 20 10 20C4.47715 20 0 15.5228 0 10ZM11 13C11 13.5523 10.5523 14 10 14C9.44772 14 9 13.5523 9 13C9 12.4477 9.44772 12 10 12C10.5523 12 11 12.4477 11 13ZM10 6C10.5523 6 11 6.44772 11 7V10C11 10.5523 10.5523 11 10 11C9.44772 11 9 10.5523 9 10V7C9 6.44772 9.44772 6 10 6Z"
383
+ fill="#C91D42" />
371
384
  </svg>
372
385
  Please accept the terms to continue
373
386
  </div>
@@ -378,8 +391,7 @@ var apple_pay_modal_template_default = `<style>
378
391
  </svg>
379
392
  </button>
380
393
  </div>
381
- </div>
382
- `;
394
+ </div>`;
383
395
 
384
396
  // src/apple-pay-modal.ts
385
397
  function getRenewalDateText(unit, duration) {
@@ -404,7 +416,11 @@ function getRenewalDateText(unit, duration) {
404
416
  default:
405
417
  break;
406
418
  }
407
- return date.toLocaleString("en-US", { year: "numeric", month: "long", day: "numeric" });
419
+ return date.toLocaleString("en-US", {
420
+ year: "numeric",
421
+ month: "long",
422
+ day: "numeric"
423
+ });
408
424
  }
409
425
  var MIN_TERM_DURATION_PATTERN = /^(\d+)\s+(\w+)s?$/;
410
426
  function getMinTermDurationUnits(minTermContractLength) {
@@ -614,32 +630,55 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
614
630
  this.remove();
615
631
  return;
616
632
  }
617
- const offerNameElement = this.querySelector("#apple-pay-modal-offer-name");
618
- const dueTodayElement = this.querySelector("#apple-pay-modal-due-today");
619
- const termsElement = this.querySelector("#apple-pay-modal-terms");
633
+ const offerNameElement = this.querySelector(
634
+ "#apple-pay-modal-offer-name"
635
+ );
636
+ const dueTodayElement = this.querySelector(
637
+ "#apple-pay-modal-due-today"
638
+ );
639
+ const termsElement = this.querySelector(
640
+ "#apple-pay-modal-terms"
641
+ );
620
642
  if (offerNameElement) {
621
643
  offerNameElement.textContent = getApplePayProductName(offer);
622
644
  }
623
645
  if (dueTodayElement) {
624
646
  const taxSuffix = offer.price.purchase === 0 || offer.price.tax?.inclusive ? "" : "*";
625
- const duePrice = formatPrice(offer.price.purchase, offer.price.currency_code);
647
+ const duePrice = formatPrice(
648
+ offer.price.purchase,
649
+ offer.price.currency_code
650
+ );
626
651
  dueTodayElement.innerHTML = duePrice ? `Due today: <strong>${duePrice}${taxSuffix}</strong>` : "";
627
652
  }
628
653
  if (termsElement) {
629
- termsElement.innerHTML = renderTermsHtml(generateApplePayModalTerms(offer, country));
654
+ termsElement.innerHTML = renderTermsHtml(
655
+ generateApplePayModalTerms(offer, country)
656
+ );
630
657
  }
631
658
  const isUS = country === "US";
632
659
  const isLoggedIn = this.dataset.loggedIn === "true";
633
660
  const isOfferNYT = offer.product?.offer_type === "bogof_nyt";
634
661
  const isAutoRenewal = offer.subscription_options?.auto_renewal ?? true;
635
- const checkboxTextElement = this.querySelector("#apple-pay-modal-checkbox-text");
662
+ const checkboxTextElement = this.querySelector(
663
+ "#apple-pay-modal-checkbox-text"
664
+ );
636
665
  if (checkboxTextElement) {
637
- checkboxTextElement.innerHTML = getCheckboxTermsHtml(isOfferNYT, isLoggedIn, isAutoRenewal);
666
+ checkboxTextElement.innerHTML = getCheckboxTermsHtml(
667
+ isOfferNYT,
668
+ isLoggedIn,
669
+ isAutoRenewal
670
+ );
638
671
  }
639
- const checkboxLabel = this.querySelector("#apple-pay-modal-checkbox-label");
640
- const checkbox = this.querySelector("#apple-pay-modal-checkbox");
672
+ const checkboxLabel = this.querySelector(
673
+ "#apple-pay-modal-checkbox-label"
674
+ );
675
+ const checkbox = this.querySelector(
676
+ "#apple-pay-modal-checkbox"
677
+ );
641
678
  const cta = this.querySelector("#apple-pay-modal-cta");
642
- const errorElement = this.querySelector("#apple-pay-modal-error");
679
+ const errorElement = this.querySelector(
680
+ "#apple-pay-modal-error"
681
+ );
643
682
  if (!isUS) {
644
683
  checkboxLabel?.remove();
645
684
  errorElement?.remove();
@@ -666,7 +705,10 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
666
705
  );
667
706
  this.close();
668
707
  });
669
- this.querySelector("#apple-pay-modal-close")?.addEventListener("click", () => this.close());
708
+ this.querySelector("#apple-pay-modal-close")?.addEventListener(
709
+ "click",
710
+ () => this.close()
711
+ );
670
712
  }
671
713
  open() {
672
714
  this.#triggerElement = document.activeElement;
@@ -684,7 +726,9 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
684
726
  }
685
727
  close() {
686
728
  if (this.#keyHandler) {
687
- document.removeEventListener("keydown", this.#keyHandler, { capture: true });
729
+ document.removeEventListener("keydown", this.#keyHandler, {
730
+ capture: true
731
+ });
688
732
  this.#keyHandler = null;
689
733
  }
690
734
  this.remove();
@@ -693,10 +737,56 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
693
737
  };
694
738
 
695
739
  // src/templates/apple-pay-button.template.html
696
- var apple_pay_button_template_default = '<style>\n /* Use CSS variables for layout so parent walls can easily override placement and sizing */\n teg-apple-pay-button {\n display: block;\n margin: var(--apple-pay-margin, 0);\n width: 100%;\n height: 100%;\n }\n\n teg-apple-pay-button .apple-pay-container {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n width: 100%;\n height: 100%;\n }\n\n teg-apple-pay-button .apple-pay-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 0.375rem;\n width: 100%;\n min-width: 0;\n height: 100%;\n min-height: var(--apple-pay-height, 2.75rem);\n background-color: #000;\n color: #fff;\n border: none;\n border-radius: var(--apple-pay-border-radius, 1.375rem);\n cursor: pointer;\n font-family: var(--wall-type-system-sans, system-ui, sans-serif);\n font-size: 17px;\n font-weight: 500;\n }\n\n teg-apple-pay-button .apple-pay-btn svg {\n flex-shrink: 0;\n display: block;\n }\n</style>\n\n<div class="apple-pay-container" aria-label="Express checkout with Apple Pay">\n <button class="apple-pay-btn" type="button" aria-label="Pay with Apple Pay" data-analytics="paywall:apple-pay-checkout">\n <svg width="51" height="22" aria-label="Apple Pay" role="img">\n <use href="#apple-pay-logo" />\n </svg>\n </button>\n</div>\n';
740
+ var apple_pay_button_template_default = '<style>\n /* Use CSS variables for layout so parent walls can easily override placement and sizing */\n teg-apple-pay-button {\n display: block;\n margin: var(--apple-pay-margin, 0);\n width: 100%;\n height: 100%;\n }\n\n teg-apple-pay-button .apple-pay-container {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n width: 100%;\n height: 100%;\n }\n\n teg-apple-pay-button .apple-pay-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 0.375rem;\n width: 100%;\n min-width: 0;\n height: 100%;\n min-height: var(--apple-pay-height, 2.75rem);\n background-color: #000;\n color: #fff;\n border: none;\n border-radius: var(--apple-pay-border-radius, 1.375rem);\n cursor: pointer;\n font-family: var(--wall-type-system-sans, system-ui, sans-serif);\n font-size: 17px;\n font-weight: 500;\n }\n\n teg-apple-pay-button .apple-pay-btn svg {\n flex-shrink: 0;\n display: block;\n }\n</style>\n\n<div class="apple-pay-container" aria-label="Express checkout with Apple Pay">\n <button\n class="apple-pay-btn"\n type="button"\n aria-label="Pay with Apple Pay"\n data-analytics="paywall:apple-pay-checkout"\n >\n <svg width="51" height="22" aria-label="Apple Pay" role="img">\n <use href="#apple-pay-logo" />\n </svg>\n </button>\n</div>\n';
741
+
742
+ // src/clients/payment-checkout/config.ts
743
+ var runtimeConfig = null;
744
+ var configureApplePay = (config) => {
745
+ runtimeConfig = {
746
+ ...config
747
+ };
748
+ };
749
+ var getApplePayConfig = () => runtimeConfig;
750
+
751
+ // src/clients/payment-checkout/detects.ts
752
+ var isAppleDeviceOrSafari = () => {
753
+ const isIOSOrMacTouchDevice = /iPad|iPhone|iPod/.test(navigator.userAgent) || navigator.maxTouchPoints > 1 && /Macintosh/.test(navigator.userAgent);
754
+ const isSafariBrowser = navigator.userAgent.indexOf("Safari") > -1 && navigator.userAgent.indexOf("Chrome") === -1;
755
+ return isIOSOrMacTouchDevice || isSafariBrowser;
756
+ };
757
+ var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null && getApplePayConfig()?.debugBypassEnabled === true;
758
+ var isApplePayAvailable = async () => {
759
+ if (bypassApplePayChecks()) {
760
+ return true;
761
+ }
762
+ if (!isAppleDeviceOrSafari()) {
763
+ return false;
764
+ }
765
+ const applePaySession = window.ApplePaySession;
766
+ if (!applePaySession) {
767
+ return false;
768
+ }
769
+ try {
770
+ if (typeof applePaySession.canMakePayments !== "function") {
771
+ return false;
772
+ }
773
+ if (!applePaySession.canMakePayments()) {
774
+ return false;
775
+ }
776
+ const merchantId = getApplePayConfig()?.merchantId;
777
+ if (!merchantId) {
778
+ return false;
779
+ }
780
+ return await applePaySession.canMakePaymentsWithActiveCard(merchantId);
781
+ } catch (error) {
782
+ console.error("Error checking Apple Pay active card status:", error);
783
+ return false;
784
+ }
785
+ };
697
786
 
698
787
  // src/apple-pay-button.ts
699
788
  var FEATURE_APPLE_PAY_EXPRESS_CHECKOUT = "FEATURE_APPLE_PAY_EXPRESS_CHECKOUT";
789
+ var APPLE_PAY_EXCLUDED_VARIANTS = ["bundle", "insider_print"];
700
790
  var ApplePayButton = class _ApplePayButton extends HTMLElement {
701
791
  static tag = "teg-apple-pay-button";
702
792
  static define() {
@@ -709,7 +799,13 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
709
799
  if (!offer) {
710
800
  return null;
711
801
  }
712
- if (!new URLSearchParams(window.location.search).has(FEATURE_APPLE_PAY_EXPRESS_CHECKOUT)) {
802
+ const isExcludedVariant = APPLE_PAY_EXCLUDED_VARIANTS.includes(offer.product?.variant);
803
+ if (isExcludedVariant) {
804
+ return null;
805
+ }
806
+ if (!new URLSearchParams(window.location.search).has(
807
+ FEATURE_APPLE_PAY_EXPRESS_CHECKOUT
808
+ )) {
713
809
  return null;
714
810
  }
715
811
  _ApplePayButton.define();
@@ -720,12 +816,13 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
720
816
  }
721
817
  #offer = null;
722
818
  #country = "";
819
+ #rendering = false;
723
820
  get offer() {
724
821
  return this.#offer;
725
822
  }
726
823
  set offer(value) {
727
824
  this.#offer = value;
728
- this.updateDisabledState();
825
+ this.#updateDisabledState();
729
826
  }
730
827
  get country() {
731
828
  return this.#country;
@@ -733,34 +830,47 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
733
830
  set country(value) {
734
831
  this.#country = value;
735
832
  }
736
- connectedCallback() {
737
- if (this.querySelector(".apple-pay-container")) {
833
+ async connectedCallback() {
834
+ if (this.#rendering || this.querySelector(".apple-pay-container")) {
738
835
  return;
739
836
  }
740
- ensureApplePayLogoSymbol();
741
- const template = document.createElement("template");
742
- template.innerHTML = apple_pay_button_template_default;
743
- this.appendChild(template.content.cloneNode(true));
744
- this.updateDisabledState();
745
- const button = this.querySelector(".apple-pay-btn");
746
- button.addEventListener("click", () => {
747
- if (!this.#offer) {
837
+ this.#rendering = true;
838
+ try {
839
+ const available = await isApplePayAvailable();
840
+ if (!available) {
841
+ this.style.display = "none";
748
842
  return;
749
843
  }
750
- if (document.querySelector(ApplePayModal.tag)) {
751
- return;
752
- }
753
- ApplePayModal.define();
754
- const modal = document.createElement(ApplePayModal.tag);
755
- modal.offer = this.#offer;
756
- modal.country = this.#country;
757
- if (this.dataset.loggedIn !== void 0) {
758
- modal.dataset.loggedIn = this.dataset.loggedIn;
759
- }
760
- modal.open();
761
- });
844
+ this.style.removeProperty("display");
845
+ ensureApplePayLogoSymbol();
846
+ const template = document.createElement("template");
847
+ template.innerHTML = apple_pay_button_template_default;
848
+ this.appendChild(template.content.cloneNode(true));
849
+ this.#updateDisabledState();
850
+ const button = this.querySelector(".apple-pay-btn");
851
+ button.addEventListener("click", () => {
852
+ if (!this.#offer) {
853
+ return;
854
+ }
855
+ if (document.querySelector(ApplePayModal.tag)) {
856
+ return;
857
+ }
858
+ ApplePayModal.define();
859
+ const modal = document.createElement(ApplePayModal.tag);
860
+ modal.offer = this.#offer;
861
+ modal.country = this.#country;
862
+ if (this.dataset.loggedIn !== void 0) {
863
+ modal.dataset.loggedIn = this.dataset.loggedIn;
864
+ }
865
+ modal.open();
866
+ });
867
+ } catch {
868
+ this.style.display = "none";
869
+ } finally {
870
+ this.#rendering = false;
871
+ }
762
872
  }
763
- updateDisabledState() {
873
+ #updateDisabledState() {
764
874
  const button = this.querySelector(".apple-pay-btn");
765
875
  if (!button) {
766
876
  return;
@@ -783,6 +893,7 @@ function defineApplePayElements() {
783
893
  export {
784
894
  ApplePayButton,
785
895
  ApplePayModal,
896
+ configureApplePay,
786
897
  defineApplePayElements,
787
898
  ensureApplePayLogoSymbol
788
899
  };
@@ -11,6 +11,5 @@ export declare class ApplePayButton extends HTMLElement {
11
11
  set offer(value: Offer | null);
12
12
  get country(): string;
13
13
  set country(value: string);
14
- connectedCallback(): void;
15
- private updateDisabledState;
14
+ connectedCallback(): Promise<void>;
16
15
  }