@economist/web-apple-pay 0.1.3-beta.2 → 0.1.3-beta.21

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/dist/index.js CHANGED
@@ -76,7 +76,514 @@ function ensureApplePayLogoSymbol() {
76
76
  }
77
77
 
78
78
  // src/templates/apple-pay-modal.template.html
79
- var apple_pay_modal_template_default = '<style>\n .apm-overlay {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 97;\n background: rgba(13, 13, 13, 0.7);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--wall-gutter, 2rem);\n box-sizing: border-box;\n }\n\n .apm {\n position: relative;\n background: #fff;\n border-radius: 0.5rem 0.5rem 0 0;\n border: 0.0625rem solid #d9d9d9;\n padding: var(--mb-spacing-lg, 1.5rem);\n width: 25rem;\n max-width: 25rem;\n box-sizing: border-box;\n font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));\n box-shadow: 0 0.25rem 1rem 0 rgba(13, 13, 13, 0.15);\n }\n\n .apm__close {\n position: absolute;\n top: 1.5rem;\n right: 1.3125rem;\n background: none;\n border: none;\n cursor: pointer;\n padding: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .apm__close:focus {\n outline: solid 0.125rem var(--focus-border-color, #121212);\n border-radius: 0.25rem;\n }\n\n .apm__offer-name {\n font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));\n font-size: var(--mb-typestyle-title-3-size, 1.25rem);\n font-weight: 500;\n color: var(--mb-colour-greyscale-london-5, #0d0d0d);\n line-height: var(--mb-typestyle-title-3-leading, 1.75rem);\n font-style: normal;\n margin: 0 1.5rem 0.5rem 0;\n }\n\n .apm__due-today {\n color: #1a1a1a;\n font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));\n font-size: var(--mb-typestyle-label-2-size, 0.9375rem);\n font-style: normal;\n font-weight: 400;\n line-height: var(--mb-typestyle-label-2-leading, 1.25rem);\n margin: 0 0 0.75rem 0;\n }\n\n .apm__rule {\n border: none;\n border-top: 0.0625rem solid #d9d9d9;\n margin: 0 0 1rem 0;\n }\n\n .apm__terms {\n color: #1a1a1a;\n font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));\n font-size: var(--mb-typestyle-label-2-size, 0.9375rem);\n font-style: normal;\n font-weight: 400;\n line-height: var(--mb-typestyle-label-2-leading, 1.25rem);\n margin: 0 0 1rem 0;\n }\n\n .apm__terms-bullets {\n list-style: disc outside;\n padding-left: 1.25rem;\n margin: 0 0 0.75rem 0;\n }\n\n .apm__terms-bullets li {\n margin-bottom: 0.375rem;\n }\n\n .apm__terms-legal {\n margin: 0;\n }\n\n .apm__passive-terms {\n font-family: var(--wall-type-system-sans);\n font-size: 0.9375rem;\n color: #0d0d0d;\n line-height: 1.4;\n margin: 0 0 8px 0;\n }\n\n .apm__passive-terms a {\n color: #1a1a1a;\n font-weight: 700;\n text-decoration: underline;\n }\n\n .apm__checkbox-label {\n display: flex;\n align-items: flex-start;\n gap: var(--mb-spacing-sm, 0.5rem);\n font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));\n font-size: var(--mb-typestyle-label-2-size, 0.9375rem);\n color: var(--mb-colour-greyscale-london-20, #1a1a1a);\n line-height: var(--mb-typestyle-label-2-leading, 1.25rem);\n cursor: pointer;\n margin: 0 0 1.5rem 0;\n }\n\n .apm__checkbox-label--error {\n margin-bottom: 0.25rem;\n }\n\n .apm__checkbox-wrapper {\n position: relative;\n flex-shrink: 0;\n width: 1.25rem;\n height: 1.25rem;\n margin-top: 0.0625rem;\n }\n\n .apm__checkbox-input {\n position: absolute;\n opacity: 0;\n width: 100%;\n height: 100%;\n cursor: pointer;\n margin: 0;\n z-index: 1;\n }\n\n .apm__checkbox-custom {\n position: absolute;\n top: 0;\n left: 0;\n width: 1.25rem;\n height: 1.25rem;\n pointer-events: none;\n }\n\n .apm__checkbox-custom svg {\n display: none;\n width: 1.25rem;\n height: 1.25rem;\n }\n\n .apm__checkbox-input:not(:checked) ~ .apm__checkbox-custom .svg-unchecked {\n display: block;\n }\n\n .apm__checkbox-input:checked ~ .apm__checkbox-custom .svg-checked {\n display: block;\n }\n\n .apm__checkbox-label--error\n .apm__checkbox-input:not(:checked)\n ~ .apm__checkbox-custom\n .svg-unchecked {\n display: none;\n }\n\n .apm__checkbox-label--error\n .apm__checkbox-input:not(:checked)\n ~ .apm__checkbox-custom\n .svg-error {\n display: block;\n }\n\n .apm__checkbox-input:focus-visible ~ .apm__checkbox-custom {\n outline: solid 0.125rem #c91d42;\n outline-offset: 0.125rem;\n border-radius: 0.25rem;\n }\n\n .apm__checkbox-label a {\n color: #1a1a1a;\n font-weight: 700;\n text-decoration: underline;\n }\n\n .apm__error {\n display: none;\n align-items: center;\n gap: 0.75rem;\n color: var(--mb-colour-greyscale-london-5, #0d0d0d);\n font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));\n font-size: 1.0625rem;\n font-style: normal;\n font-weight: 500;\n line-height: 1.5rem;\n margin-bottom: 1rem;\n }\n\n .apm__error--visible {\n display: flex;\n }\n\n .apm__cta {\n display: flex;\n width: 100%;\n height: 2.75rem;\n padding: 0.375rem 1rem;\n justify-content: center;\n align-items: center;\n gap: 0.375rem;\n border-radius: 0.25rem;\n background: #000;\n border: none;\n cursor: pointer;\n color: #fff;\n text-align: center;\n font-family: var(\n --mb-typeface-sans,\n var(--wall-type-system-sans, system-ui, sans-serif)\n );\n font-size: 1.25rem;\n font-style: normal;\n font-weight: 500;\n line-height: normal;\n }\n\n .apm__cta svg {\n flex-shrink: 0;\n display: block;\n }\n\n @media (min-width: 22.5rem) and (max-width: 37.5rem) {\n .apm {\n width: 24.375rem;\n max-width: 24.375rem;\n }\n }\n\n @media (min-width: 37.5625rem) {\n .apm {\n border-radius: 0.5rem;\n }\n }\n\n @media (max-width: 22.4375rem) {\n .apm {\n width: 20rem;\n max-width: 20rem;\n }\n }\n\n @media (max-width: 26.1875rem) {\n .apm__cta {\n font-size: 1.125rem;\n }\n }\n</style>\n\n<div class="apm-overlay" id="apple-pay-modal-overlay">\n <div\n class="apm"\n role="dialog"\n aria-modal="true"\n aria-labelledby="apple-pay-modal-offer-name"\n id="apple-pay-modal"\n tabindex="-1"\n >\n <button class="apm__close" id="apple-pay-modal-close" aria-label="Close">\n <svg\n aria-hidden="true"\n xmlns="http://www.w3.org/2000/svg"\n width="24"\n height="24"\n viewBox="0 0 24 24"\n fill="none"\n >\n <g clip-path="url(#clip0_15712_93054)">\n <rect width="24" height="24" fill="white" fill-opacity="0.01" />\n <path\n fill-rule="evenodd"\n clip-rule="evenodd"\n d="M0 0H24V24H0V0Z"\n fill="white"\n fill-opacity="0.01"\n />\n <path\n fill-rule="evenodd"\n clip-rule="evenodd"\n 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"\n fill="#333333"\n />\n </g>\n <defs>\n <clipPath id="clip0_15712_93054">\n <rect width="24" height="24" fill="white" />\n </clipPath>\n </defs>\n </svg>\n </button>\n <p class="apm__offer-name" id="apple-pay-modal-offer-name"></p>\n <p class="apm__due-today" id="apple-pay-modal-due-today"></p>\n <hr class="apm__rule" />\n <div class="apm__terms" id="apple-pay-modal-terms"></div>\n <p class="apm__passive-terms" id="apple-pay-modal-passive-terms"></p>\n <label\n class="apm__checkbox-label"\n id="apple-pay-modal-checkbox-label"\n for="apple-pay-modal-checkbox"\n >\n <div class="apm__checkbox-wrapper">\n <input\n class="apm__checkbox-input"\n type="checkbox"\n id="apple-pay-modal-checkbox"\n />\n <span class="apm__checkbox-custom">\n <svg\n aria-hidden="true"\n class="svg-unchecked"\n width="24"\n height="24"\n viewBox="0 0 24 24"\n fill="none"\n xmlns="http://www.w3.org/2000/svg"\n >\n <path\n 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"\n fill="white"\n stroke="#595959"\n />\n </svg>\n <svg\n aria-hidden="true"\n class="svg-checked"\n width="24"\n height="24"\n viewBox="0 0 24 24"\n fill="none"\n xmlns="http://www.w3.org/2000/svg"\n >\n <g clip-path="url(#clip0_20014_9555)">\n <path\n 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"\n fill="#333333"\n />\n <path\n d="M10.2857 17L6 12.6814L7.20857 11.4635L10.2857 14.5557L16.7914 8L18 9.22649L10.2857 17Z"\n fill="white"\n />\n </g>\n <defs>\n <clipPath id="clip0_20014_9555">\n <path\n 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"\n fill="white"\n />\n </clipPath>\n </defs>\n </svg>\n <svg\n aria-hidden="true"\n class="svg-error"\n width="24"\n height="24"\n viewBox="0 0 24 24"\n fill="none"\n xmlns="http://www.w3.org/2000/svg"\n >\n <path\n 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"\n fill="white"\n stroke="#C91D42"\n stroke-width="2"\n />\n </svg>\n </span>\n </div>\n <span id="apple-pay-modal-checkbox-text"></span>\n </label>\n <div class="apm__error" id="apple-pay-modal-error" role="alert">\n <svg\n aria-hidden="true"\n width="20"\n height="20"\n viewBox="0 0 20 20"\n fill="none"\n xmlns="http://www.w3.org/2000/svg"\n >\n <path\n fill-rule="evenodd"\n clip-rule="evenodd"\n 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"\n fill="#C91D42"\n />\n </svg>\n Please accept the terms to continue\n </div>\n <button class="apm__cta" id="apple-pay-modal-cta">\n Continue with\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</div>\n\n';
79
+ var apple_pay_modal_template_default = `<style>
80
+ .apm-overlay {
81
+ position: fixed;
82
+ top: 0;
83
+ left: 0;
84
+ width: 100%;
85
+ height: 100%;
86
+ z-index: 97;
87
+ background: rgba(13, 13, 13, 0.7);
88
+ display: flex;
89
+ align-items: center;
90
+ justify-content: center;
91
+ padding: var(--wall-gutter, 2rem);
92
+ box-sizing: border-box;
93
+ }
94
+
95
+ .apm {
96
+ position: relative;
97
+ background: #fff;
98
+ border-radius: 0.5rem 0.5rem 0 0;
99
+ border: 0.0625rem solid #d9d9d9;
100
+ padding: var(--mb-spacing-lg, 1.5rem);
101
+ width: 25rem;
102
+ max-width: 25rem;
103
+ box-sizing: border-box;
104
+ font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));
105
+ box-shadow: 0 0.25rem 1rem 0 rgba(13, 13, 13, 0.15);
106
+ }
107
+
108
+ .apm__close {
109
+ position: absolute;
110
+ top: 1.5rem;
111
+ right: 1.3125rem;
112
+ background: none;
113
+ border: none;
114
+ cursor: pointer;
115
+ padding: 0;
116
+ display: flex;
117
+ align-items: center;
118
+ justify-content: center;
119
+ }
120
+
121
+ .apm__close:focus {
122
+ outline: solid 0.125rem var(--focus-border-color, #121212);
123
+ border-radius: 0.25rem;
124
+ }
125
+
126
+ .apm__offer-name {
127
+ font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));
128
+ font-size: var(--mb-typestyle-title-3-size, 1.25rem);
129
+ font-weight: 500;
130
+ color: var(--mb-colour-greyscale-london-5, #0d0d0d);
131
+ line-height: var(--mb-typestyle-title-3-leading, 1.75rem);
132
+ font-style: normal;
133
+ margin: 0 1.5rem 0.5rem 0;
134
+ }
135
+
136
+ .apm__due-today {
137
+ color: #1a1a1a;
138
+ font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));
139
+ font-size: var(--mb-typestyle-label-2-size, 0.9375rem);
140
+ font-style: normal;
141
+ font-weight: 400;
142
+ line-height: var(--mb-typestyle-label-2-leading, 1.25rem);
143
+ margin: 0 0 0.75rem 0;
144
+ }
145
+
146
+ .apm__rule {
147
+ border: none;
148
+ border-top: 0.0625rem solid #d9d9d9;
149
+ margin: 0 0 1rem 0;
150
+ }
151
+
152
+ .apm__terms {
153
+ color: #1a1a1a;
154
+ font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));
155
+ font-size: var(--mb-typestyle-label-2-size, 0.9375rem);
156
+ font-style: normal;
157
+ font-weight: 400;
158
+ line-height: var(--mb-typestyle-label-2-leading, 1.25rem);
159
+ margin: 0 0 1rem 0;
160
+ }
161
+
162
+ .apm__terms-bullets {
163
+ list-style: disc outside;
164
+ padding-left: 1.25rem;
165
+ margin: 0 0 0.75rem 0;
166
+ }
167
+
168
+ .apm__terms-bullets li {
169
+ margin-bottom: 0.375rem;
170
+ }
171
+
172
+ .apm__terms-legal {
173
+ margin: 0;
174
+ }
175
+
176
+ .apm__passive-terms {
177
+ font-family: var(--wall-type-system-sans);
178
+ font-size: 0.9375rem;
179
+ color: #0d0d0d;
180
+ line-height: 1.4;
181
+ margin: 0 0 8px 0;
182
+ }
183
+
184
+ .apm__passive-terms a {
185
+ color: #1a1a1a;
186
+ font-weight: 700;
187
+ text-decoration: underline;
188
+ }
189
+
190
+ .apm__checkbox-label {
191
+ display: flex;
192
+ align-items: flex-start;
193
+ gap: var(--mb-spacing-sm, 0.5rem);
194
+ font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));
195
+ font-size: var(--mb-typestyle-label-2-size, 0.9375rem);
196
+ color: var(--mb-colour-greyscale-london-20, #1a1a1a);
197
+ line-height: var(--mb-typestyle-label-2-leading, 1.25rem);
198
+ cursor: pointer;
199
+ margin: 0 0 1.5rem 0;
200
+ }
201
+
202
+ .apm__checkbox-label--error {
203
+ margin-bottom: 0.25rem;
204
+ }
205
+
206
+ .apm__checkbox-wrapper {
207
+ position: relative;
208
+ flex-shrink: 0;
209
+ width: 1.25rem;
210
+ height: 1.25rem;
211
+ margin-top: 0.0625rem;
212
+ }
213
+
214
+ .apm__checkbox-input {
215
+ position: absolute;
216
+ opacity: 0;
217
+ width: 100%;
218
+ height: 100%;
219
+ cursor: pointer;
220
+ margin: 0;
221
+ z-index: 1;
222
+ }
223
+
224
+ .apm__checkbox-custom {
225
+ position: absolute;
226
+ top: 0;
227
+ left: 0;
228
+ width: 1.25rem;
229
+ height: 1.25rem;
230
+ pointer-events: none;
231
+ }
232
+
233
+ .apm__checkbox-custom svg {
234
+ display: none;
235
+ width: 1.25rem;
236
+ height: 1.25rem;
237
+ }
238
+
239
+ .apm__checkbox-input:not(:checked) ~ .apm__checkbox-custom .svg-unchecked {
240
+ display: block;
241
+ }
242
+
243
+ .apm__checkbox-input:checked ~ .apm__checkbox-custom .svg-checked {
244
+ display: block;
245
+ }
246
+
247
+ .apm__checkbox-label--error
248
+ .apm__checkbox-input:not(:checked)
249
+ ~ .apm__checkbox-custom
250
+ .svg-unchecked {
251
+ display: none;
252
+ }
253
+
254
+ .apm__checkbox-label--error
255
+ .apm__checkbox-input:not(:checked)
256
+ ~ .apm__checkbox-custom
257
+ .svg-error {
258
+ display: block;
259
+ }
260
+
261
+ .apm__checkbox-input:focus-visible ~ .apm__checkbox-custom {
262
+ outline: solid 0.125rem #c91d42;
263
+ outline-offset: 0.125rem;
264
+ border-radius: 0.25rem;
265
+ }
266
+
267
+ .apm__checkbox-label a {
268
+ color: #1a1a1a;
269
+ font-weight: 700;
270
+ text-decoration: underline;
271
+ }
272
+
273
+ .apm__error {
274
+ display: none;
275
+ align-items: center;
276
+ gap: 0.75rem;
277
+ color: var(--mb-colour-greyscale-london-5, #0d0d0d);
278
+ font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));
279
+ font-size: 1.0625rem;
280
+ font-style: normal;
281
+ font-weight: 500;
282
+ line-height: 1.5rem;
283
+ margin-bottom: 1rem;
284
+ }
285
+
286
+ .apm__error--visible {
287
+ display: flex;
288
+ }
289
+
290
+ .apm__cta {
291
+ display: flex;
292
+ width: 100%;
293
+ height: 2.75rem;
294
+ padding: 0.375rem 1rem;
295
+ justify-content: center;
296
+ align-items: center;
297
+ gap: 0.375rem;
298
+ border-radius: 0.25rem;
299
+ background: #000;
300
+ border: none;
301
+ cursor: pointer;
302
+ color: #fff;
303
+ text-align: center;
304
+ font-family: var(
305
+ --mb-typeface-sans,
306
+ var(--wall-type-system-sans, system-ui, sans-serif)
307
+ );
308
+ font-size: 1.25rem;
309
+ font-style: normal;
310
+ font-weight: 500;
311
+ line-height: normal;
312
+ }
313
+
314
+ .apm__cta:disabled {
315
+ cursor: not-allowed;
316
+ }
317
+
318
+ .apm__cta[aria-busy="true"] {
319
+ position: relative;
320
+ cursor: not-allowed;
321
+ }
322
+
323
+ .apm__cta[aria-busy="true"] > * {
324
+ visibility: hidden;
325
+ }
326
+
327
+ .apm__cta[aria-busy="true"]::after {
328
+ content: '';
329
+ position: absolute;
330
+ left: 50%;
331
+ top: 50%;
332
+ margin-left: -0.625rem;
333
+ margin-top: -0.625rem;
334
+ width: 1.25rem;
335
+ height: 1.25rem;
336
+ border: 2px solid rgba(255, 255, 255, 0.2);
337
+ border-top-color: #fff;
338
+ border-radius: 50%;
339
+ animation: apm-cta-spinner 0.8s linear infinite;
340
+ }
341
+
342
+ @keyframes apm-cta-spinner {
343
+ to {
344
+ transform: rotate(360deg);
345
+ }
346
+ }
347
+
348
+ .apm__cta svg {
349
+ flex-shrink: 0;
350
+ display: block;
351
+ }
352
+
353
+ @media (min-width: 22.5rem) and (max-width: 37.5rem) {
354
+ .apm {
355
+ width: 24.375rem;
356
+ max-width: 24.375rem;
357
+ }
358
+ }
359
+
360
+ @media (min-width: 37.5625rem) {
361
+ .apm {
362
+ border-radius: 0.5rem;
363
+ }
364
+ }
365
+
366
+ @media (max-width: 22.4375rem) {
367
+ .apm {
368
+ width: 20rem;
369
+ max-width: 20rem;
370
+ }
371
+ }
372
+
373
+ @media (max-width: 26.1875rem) {
374
+ .apm__cta {
375
+ font-size: 1.125rem;
376
+ }
377
+ }
378
+ </style>
379
+
380
+ <div class="apm-overlay" id="apple-pay-modal-overlay">
381
+ <div
382
+ class="apm"
383
+ role="dialog"
384
+ aria-modal="true"
385
+ aria-labelledby="apple-pay-modal-offer-name"
386
+ id="apple-pay-modal"
387
+ tabindex="-1"
388
+ >
389
+ <button class="apm__close" id="apple-pay-modal-close" aria-label="Close">
390
+ <svg
391
+ aria-hidden="true"
392
+ xmlns="http://www.w3.org/2000/svg"
393
+ width="24"
394
+ height="24"
395
+ viewBox="0 0 24 24"
396
+ fill="none"
397
+ >
398
+ <g clip-path="url(#clip0_15712_93054)">
399
+ <rect width="24" height="24" fill="white" fill-opacity="0.01" />
400
+ <path
401
+ fill-rule="evenodd"
402
+ clip-rule="evenodd"
403
+ d="M0 0H24V24H0V0Z"
404
+ fill="white"
405
+ fill-opacity="0.01"
406
+ />
407
+ <path
408
+ fill-rule="evenodd"
409
+ clip-rule="evenodd"
410
+ 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"
411
+ fill="#333333"
412
+ />
413
+ </g>
414
+ <defs>
415
+ <clipPath id="clip0_15712_93054">
416
+ <rect width="24" height="24" fill="white" />
417
+ </clipPath>
418
+ </defs>
419
+ </svg>
420
+ </button>
421
+ <p class="apm__offer-name" id="apple-pay-modal-offer-name"></p>
422
+ <p class="apm__due-today" id="apple-pay-modal-due-today"></p>
423
+ <hr class="apm__rule" />
424
+ <div class="apm__terms" id="apple-pay-modal-terms"></div>
425
+ <p class="apm__passive-terms" id="apple-pay-modal-passive-terms"></p>
426
+ <label
427
+ class="apm__checkbox-label"
428
+ id="apple-pay-modal-checkbox-label"
429
+ for="apple-pay-modal-checkbox"
430
+ >
431
+ <div class="apm__checkbox-wrapper">
432
+ <input
433
+ class="apm__checkbox-input"
434
+ type="checkbox"
435
+ id="apple-pay-modal-checkbox"
436
+ />
437
+ <span class="apm__checkbox-custom">
438
+ <svg
439
+ aria-hidden="true"
440
+ class="svg-unchecked"
441
+ width="24"
442
+ height="24"
443
+ viewBox="0 0 24 24"
444
+ fill="none"
445
+ xmlns="http://www.w3.org/2000/svg"
446
+ >
447
+ <path
448
+ 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"
449
+ fill="white"
450
+ stroke="#595959"
451
+ />
452
+ </svg>
453
+ <svg
454
+ aria-hidden="true"
455
+ class="svg-checked"
456
+ width="24"
457
+ height="24"
458
+ viewBox="0 0 24 24"
459
+ fill="none"
460
+ xmlns="http://www.w3.org/2000/svg"
461
+ >
462
+ <g clip-path="url(#clip0_20014_9555)">
463
+ <path
464
+ 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"
465
+ fill="#333333"
466
+ />
467
+ <path
468
+ d="M10.2857 17L6 12.6814L7.20857 11.4635L10.2857 14.5557L16.7914 8L18 9.22649L10.2857 17Z"
469
+ fill="white"
470
+ />
471
+ </g>
472
+ <defs>
473
+ <clipPath id="clip0_20014_9555">
474
+ <path
475
+ 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"
476
+ fill="white"
477
+ />
478
+ </clipPath>
479
+ </defs>
480
+ </svg>
481
+ <svg
482
+ aria-hidden="true"
483
+ class="svg-error"
484
+ width="24"
485
+ height="24"
486
+ viewBox="0 0 24 24"
487
+ fill="none"
488
+ xmlns="http://www.w3.org/2000/svg"
489
+ >
490
+ <path
491
+ 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"
492
+ fill="white"
493
+ stroke="#C91D42"
494
+ stroke-width="2"
495
+ />
496
+ </svg>
497
+ </span>
498
+ </div>
499
+ <span id="apple-pay-modal-checkbox-text"></span>
500
+ </label>
501
+ <div class="apm__error" id="apple-pay-modal-error" role="alert">
502
+ <svg
503
+ aria-hidden="true"
504
+ width="20"
505
+ height="20"
506
+ viewBox="0 0 20 20"
507
+ fill="none"
508
+ xmlns="http://www.w3.org/2000/svg"
509
+ >
510
+ <path
511
+ fill-rule="evenodd"
512
+ clip-rule="evenodd"
513
+ 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"
514
+ fill="#C91D42"
515
+ />
516
+ </svg>
517
+ Please accept the terms to continue
518
+ </div>
519
+ <button class="apm__cta" id="apple-pay-modal-cta" aria-label="Continue with Apple Pay">
520
+ <span class="apm__cta-content">
521
+ Continue with
522
+ <svg width="51" height="22" aria-hidden="true" role="img">
523
+ <use href="#apple-pay-logo" />
524
+ </svg>
525
+ </span>
526
+ </button>
527
+ </div>
528
+ </div>
529
+
530
+ `;
531
+
532
+ // src/clients/payment-checkout/config.ts
533
+ var runtimeConfig = null;
534
+ var configureApplePay = (config) => {
535
+ runtimeConfig = {
536
+ ...config
537
+ };
538
+ };
539
+ var getApplePayConfig = () => runtimeConfig;
540
+
541
+ // src/utils/recaptcha.ts
542
+ var loadRecaptchaScript = (siteKey) => {
543
+ return new Promise((resolve, reject) => {
544
+ if (typeof grecaptcha !== "undefined") return resolve();
545
+ const existingScript = document.querySelector(
546
+ 'script[src*="recaptcha/api.js"]'
547
+ );
548
+ if (existingScript) {
549
+ existingScript.addEventListener("load", () => resolve());
550
+ existingScript.addEventListener(
551
+ "error",
552
+ () => reject(new Error("Failed to load existing reCAPTCHA script"))
553
+ );
554
+ return;
555
+ }
556
+ const script = document.createElement("script");
557
+ script.src = `https://www.google.com/recaptcha/api.js?render=${siteKey}`;
558
+ script.async = true;
559
+ script.defer = true;
560
+ script.onload = () => resolve();
561
+ script.onerror = () => reject(new Error("Failed to load reCAPTCHA script"));
562
+ document.head.appendChild(script);
563
+ });
564
+ };
565
+ var getRecaptchaToken = async (action) => {
566
+ const siteKey = getApplePayConfig()?.recaptchaSiteKey;
567
+ if (!siteKey) {
568
+ throw new Error(
569
+ "[web-apple-pay] Missing recaptchaSiteKey. Call configureApplePay({ recaptchaSiteKey, ... }) before initiating Apple Pay."
570
+ );
571
+ }
572
+ await loadRecaptchaScript(siteKey);
573
+ return new Promise((resolve, reject) => {
574
+ grecaptcha.ready(() => {
575
+ grecaptcha.execute(siteKey, { action }).then(resolve).catch(reject);
576
+ });
577
+ });
578
+ };
579
+ var getPurchaseRecaptchaTokens = async () => {
580
+ const [checkEligibility, newBasket, registerUser] = await Promise.all([
581
+ getRecaptchaToken("checkEligibility"),
582
+ getRecaptchaToken("newBasket"),
583
+ getRecaptchaToken("registerUser")
584
+ ]);
585
+ return { checkEligibility, newBasket, registerUser };
586
+ };
80
587
 
81
588
  // src/apple-pay-modal.ts
82
589
  function getRenewalDateText(unit, duration) {
@@ -128,6 +635,7 @@ function getOfferTypeText(offerType) {
128
635
  }
129
636
  var TERMS_LINK = '<a href="https://www.economistgroup.com/terms-of-use" target="_blank" rel="noopener noreferrer">Terms and Conditions</a>';
130
637
  var PRIVACY_LINK = '<a href="https://www.economistgroup.com/privacy-policy" target="_blank" rel="noopener noreferrer">Privacy Policy</a>';
638
+ var MSG_RECAPTCHA_LOAD_ERROR = "Something went wrong. Please close and try again.";
131
639
  function getCheckboxTermsHtml(isNYT, isLoggedIn, autoRenewal) {
132
640
  const autoRenewalClause = autoRenewal ? " and auto-renewal terms above," : "";
133
641
  if (isNYT) {
@@ -297,6 +805,19 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
297
805
  #country = "";
298
806
  #triggerElement = null;
299
807
  #keyHandler = null;
808
+ #recaptchaTokens = null;
809
+ #recaptchaTokensPromise = null;
810
+ /**
811
+ * Synchronous callback invoked directly from the CTA click handler once the
812
+ * user confirms. The caller (ApplePayButton) must call startApplePaySession
813
+ * inside this callback to preserve the browser's user-gesture chain — Safari
814
+ * requires new ApplePaySession() to execute within the original click stack.
815
+ *
816
+ * reCAPTCHA tokens are guaranteed non-null here: the CTA remains disabled
817
+ * until the token pre-fetch resolves, so this callback is never reached
818
+ * without valid tokens.
819
+ */
820
+ onConfirm = null;
300
821
  set offer(value) {
301
822
  this.#offer = value;
302
823
  }
@@ -391,27 +912,57 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
391
912
  }
392
913
  });
393
914
  }
915
+ if (this.#recaptchaTokensPromise) {
916
+ if (cta) {
917
+ cta.setAttribute("disabled", "");
918
+ cta.setAttribute("aria-disabled", "true");
919
+ cta.setAttribute("aria-busy", "true");
920
+ }
921
+ this.#recaptchaTokensPromise.then((tokens) => {
922
+ this.#recaptchaTokens = tokens;
923
+ if (cta) {
924
+ cta.removeAttribute("disabled");
925
+ cta.removeAttribute("aria-disabled");
926
+ cta.removeAttribute("aria-busy");
927
+ }
928
+ }).catch(() => {
929
+ console.error("[web-apple-pay] apple-pay-modal: failed to pre-fetch reCAPTCHA tokens; CTA remains disabled");
930
+ if (cta) {
931
+ cta.removeAttribute("aria-busy");
932
+ }
933
+ const existingError = this.querySelector("#apple-pay-modal-error");
934
+ if (existingError) {
935
+ const icon = existingError.querySelector("svg");
936
+ existingError.textContent = MSG_RECAPTCHA_LOAD_ERROR;
937
+ if (icon) existingError.prepend(icon);
938
+ existingError.classList.add("apm__error--visible");
939
+ } else {
940
+ const errorNotice = document.createElement("div");
941
+ errorNotice.setAttribute("role", "alert");
942
+ errorNotice.className = "apm__error apm__error--visible";
943
+ errorNotice.textContent = MSG_RECAPTCHA_LOAD_ERROR;
944
+ cta?.insertAdjacentElement("beforebegin", errorNotice);
945
+ }
946
+ });
947
+ }
394
948
  cta?.addEventListener("click", () => {
395
949
  if (isUS && checkbox && !checkbox.checked) {
396
950
  errorElement?.classList.add("apm__error--visible");
397
951
  checkboxLabel?.classList.add("apm__checkbox-label--error");
398
952
  return;
399
953
  }
400
- this.dispatchEvent(
401
- new CustomEvent("apple-pay-confirmed", {
402
- bubbles: true,
403
- composed: true,
404
- detail: { skuId: offer.sku_id, country }
405
- })
406
- );
407
- this.close();
954
+ if (this.#recaptchaTokens) {
955
+ this.onConfirm?.(this.#recaptchaTokens);
956
+ this.close();
957
+ }
408
958
  });
409
959
  this.querySelector("#apple-pay-modal-close")?.addEventListener(
410
960
  "click",
411
961
  () => this.close()
412
962
  );
413
963
  }
414
- open() {
964
+ open(recaptchaTokensPromise) {
965
+ this.#recaptchaTokensPromise = recaptchaTokensPromise ?? getPurchaseRecaptchaTokens();
415
966
  this.#triggerElement = document.activeElement;
416
967
  document.body.appendChild(this);
417
968
  this.#keyHandler = (event) => {
@@ -524,21 +1075,6 @@ var apple_pay_button_template_default = `<style>
524
1075
  </div>
525
1076
  `;
526
1077
 
527
- // src/clients/payment-checkout/config.ts
528
- var runtimeConfig = null;
529
- var configureApplePay = (config) => {
530
- console.log("[web-apple-pay] configureApplePay called", {
531
- merchantId: config.merchantId,
532
- walletApiBaseUrl: config.walletApiBaseUrl,
533
- debugBypassEnabled: config.debugBypassEnabled,
534
- recaptchaSiteKey: config.recaptchaSiteKey ? "***" : void 0
535
- });
536
- runtimeConfig = {
537
- ...config
538
- };
539
- };
540
- var getApplePayConfig = () => runtimeConfig;
541
-
542
1078
  // src/clients/payment-checkout/detects.ts
543
1079
  var isAppleDeviceOrSafari = () => {
544
1080
  const isIOSOrMacTouchDevice = /iPad|iPhone|iPod/.test(navigator.userAgent) || navigator.maxTouchPoints > 1 && /Macintosh/.test(navigator.userAgent);
@@ -547,27 +1083,21 @@ var isAppleDeviceOrSafari = () => {
547
1083
  };
548
1084
  var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null && getApplePayConfig()?.debugBypassEnabled === true;
549
1085
  var isApplePayAvailable = async () => {
550
- console.log("[web-apple-pay] isApplePayAvailable: checking availability");
551
1086
  if (bypassApplePayChecks()) {
552
- console.log("[web-apple-pay] isApplePayAvailable: bypass active \u2192 true");
553
1087
  return true;
554
1088
  }
555
1089
  if (!isAppleDeviceOrSafari()) {
556
- console.log("[web-apple-pay] isApplePayAvailable: not Apple device or Safari \u2192 false");
557
1090
  return false;
558
1091
  }
559
1092
  const applePaySession = window.ApplePaySession;
560
1093
  if (!applePaySession) {
561
- console.log("[web-apple-pay] isApplePayAvailable: ApplePaySession not found on window \u2192 false");
562
1094
  return false;
563
1095
  }
564
1096
  try {
565
1097
  if (typeof applePaySession.canMakePayments !== "function") {
566
- console.log("[web-apple-pay] isApplePayAvailable: canMakePayments is not a function \u2192 false");
567
1098
  return false;
568
1099
  }
569
1100
  if (!applePaySession.canMakePayments()) {
570
- console.log("[web-apple-pay] isApplePayAvailable: canMakePayments() returned false \u2192 false");
571
1101
  return false;
572
1102
  }
573
1103
  const merchantId = getApplePayConfig()?.merchantId;
@@ -577,10 +1107,8 @@ var isApplePayAvailable = async () => {
577
1107
  );
578
1108
  return false;
579
1109
  }
580
- console.log("[web-apple-pay] isApplePayAvailable : calling canMakePaymentsWithActiveCard", { merchantId });
581
- const result = await applePaySession.canMakePaymentsWithActiveCard(merchantId);
582
- console.log("[web-apple-pay] isApplePayAvailable: canMakePaymentsWithActiveCard \u2192", result);
583
- return result;
1110
+ const hasActiveCard = await applePaySession.canMakePaymentsWithActiveCard(merchantId);
1111
+ return hasActiveCard;
584
1112
  } catch (error) {
585
1113
  console.error("Error checking Apple Pay active card status:", error);
586
1114
  return false;
@@ -628,11 +1156,18 @@ var apiFetch = async (path, options = {}) => {
628
1156
  });
629
1157
  if (!response.ok) {
630
1158
  const contentType = response.headers.get("content-type") || "";
1159
+ console.warn("[web-apple-pay] apiFetch: non-2xx response", {
1160
+ path,
1161
+ status: response.status,
1162
+ contentType
1163
+ });
631
1164
  if (contentType.includes("application/json")) {
632
1165
  const errorBody = await response.json();
1166
+ console.warn("[web-apple-pay] apiFetch: parsed JSON error body", { errorBody });
633
1167
  throw new ApiError(response.status, errorBody);
634
1168
  }
635
1169
  const errorText = await response.text();
1170
+ console.warn("[web-apple-pay] apiFetch: non-JSON error body", { errorText });
636
1171
  throw new Error(
637
1172
  `API request failed: ${response.status} ${response.statusText} - ${errorText}`
638
1173
  );
@@ -670,6 +1205,22 @@ var getExpressCheckoutSession = () => {
670
1205
  };
671
1206
 
672
1207
  // src/utils/expressCheckoutRedirect.ts
1208
+ var CHECKOUT_REDIRECT_KEY = "checkout-redirect";
1209
+ var ALLOWED_REDIRECT_HOSTNAME = "economist.com";
1210
+ var writeCheckoutRedirect = (originUrl) => {
1211
+ if (originUrl.startsWith("/")) {
1212
+ localStorage.setItem(CHECKOUT_REDIRECT_KEY, originUrl);
1213
+ return;
1214
+ }
1215
+ try {
1216
+ const { hostname } = new URL(originUrl);
1217
+ const isAllowed = hostname === ALLOWED_REDIRECT_HOSTNAME || hostname.endsWith(`.${ALLOWED_REDIRECT_HOSTNAME}`);
1218
+ if (isAllowed) {
1219
+ localStorage.setItem(CHECKOUT_REDIRECT_KEY, originUrl);
1220
+ }
1221
+ } catch {
1222
+ }
1223
+ };
673
1224
  var captureOriginUrl = () => window.location.href;
674
1225
  var buildQueryString = () => {
675
1226
  const countryCode = new URLSearchParams(window.location.search).get(
@@ -694,68 +1245,19 @@ var redirectToCheckoutComplete = (params) => {
694
1245
  url.searchParams.set("lmo_offer", params.skuId);
695
1246
  url.searchParams.set("status", params.status);
696
1247
  url.searchParams.set("redirectToCheckout", "true");
697
- url.searchParams.set("returnUrl", params.returnUrl);
698
1248
  return url.pathname + url.search;
699
1249
  };
700
1250
  var redirectToCheckoutFallback = (params) => {
701
1251
  const url = new URL(window.location.href);
702
1252
  url.pathname = "/checkout";
703
- url.searchParams.set("lmo_offer", params.skuId);
1253
+ url.searchParams.set("lmo_offer", decodeSku(params.skuId));
704
1254
  if (params.inlineError)
705
1255
  url.searchParams.set("inlineError", params.inlineError);
706
1256
  if (params.email) url.searchParams.set("email", params.email);
707
1257
  if (params.emailError) url.searchParams.set("emailError", params.emailError);
708
- url.searchParams.set("returnUrl", params.returnUrl);
709
1258
  return url.pathname + url.search;
710
1259
  };
711
1260
 
712
- // src/utils/recaptcha.ts
713
- var loadRecaptchaScript = (siteKey) => {
714
- return new Promise((resolve, reject) => {
715
- if (typeof grecaptcha !== "undefined") return resolve();
716
- const existingScript = document.querySelector(
717
- 'script[src*="recaptcha/api.js"]'
718
- );
719
- if (existingScript) {
720
- existingScript.addEventListener("load", () => resolve());
721
- existingScript.addEventListener(
722
- "error",
723
- () => reject(new Error("Failed to load existing reCAPTCHA script"))
724
- );
725
- return;
726
- }
727
- const script = document.createElement("script");
728
- script.src = `https://www.google.com/recaptcha/api.js?render=${siteKey}`;
729
- script.async = true;
730
- script.defer = true;
731
- script.onload = () => resolve();
732
- script.onerror = () => reject(new Error("Failed to load reCAPTCHA script"));
733
- document.head.appendChild(script);
734
- });
735
- };
736
- var getRecaptchaToken = async (action) => {
737
- const siteKey = getApplePayConfig()?.recaptchaSiteKey;
738
- if (!siteKey) {
739
- throw new Error(
740
- "[web-apple-pay] Missing recaptchaSiteKey. Call configureApplePay({ recaptchaSiteKey, ... }) before initiating Apple Pay."
741
- );
742
- }
743
- await loadRecaptchaScript(siteKey);
744
- return new Promise((resolve, reject) => {
745
- grecaptcha.ready(() => {
746
- grecaptcha.execute(siteKey, { action }).then(resolve).catch(reject);
747
- });
748
- });
749
- };
750
- var getPurchaseRecaptchaTokens = async () => {
751
- const [checkEligibility, newBasket, registerUser] = await Promise.all([
752
- getRecaptchaToken("checkEligibility"),
753
- getRecaptchaToken("newBasket"),
754
- getRecaptchaToken("registerUser")
755
- ]);
756
- return { checkEligibility, newBasket, registerUser };
757
- };
758
-
759
1261
  // src/utils/expressCheckoutTracking.ts
760
1262
  var EXPRESS_CHECKOUT_EVENTS = {
761
1263
  /** Wallet button displayed */
@@ -842,72 +1344,39 @@ var MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
842
1344
 
843
1345
  // src/clients/payment-checkout/apple-session.ts
844
1346
  function redirectToFallbackCheckout(config) {
845
- console.warn("[web-apple-pay] redirectToFallbackCheckout", {
846
- skuId: config.skuId,
847
- returnUrl: config.returnUrl,
848
- inlineError: MSG_PAYMENT_GENERIC_ERROR
849
- });
1347
+ writeCheckoutRedirect(config.returnUrl);
850
1348
  trackFallbackCheckout(config.skuId);
851
1349
  window.location.href = redirectToCheckoutFallback({
852
1350
  skuId: config.skuId,
853
- returnUrl: config.returnUrl,
854
1351
  inlineError: MSG_PAYMENT_GENERIC_ERROR
855
1352
  });
856
1353
  }
857
1354
  async function handleMerchantValidation(event, session, config) {
858
- console.log("[web-apple-pay] handleMerchantValidation: start", {
859
- skuId: config.skuId,
860
- validationURL: event.validationURL
861
- });
862
1355
  try {
863
1356
  const validationHost = new URL(event.validationURL).host;
864
- console.log("[web-apple-pay] handleMerchantValidation: host parsed", {
865
- validationHost
866
- });
867
1357
  if (!validationHost.endsWith("apple.com")) {
868
- console.warn(
869
- "[web-apple-pay] handleMerchantValidation: invalid validation host",
870
- { validationHost }
871
- );
872
1358
  session.abort();
873
1359
  redirectToFallbackCheckout(config);
874
1360
  return;
875
1361
  }
876
- console.log("[web-apple-pay] handleMerchantValidation: requesting recaptcha token");
877
1362
  const recaptchaToken = await getRecaptchaToken("verifyMerchant");
878
- console.log("[web-apple-pay] handleMerchantValidation: recaptcha token received");
879
1363
  const response = await validateAppleMerchant({
880
1364
  validationURL: event.validationURL,
881
1365
  recaptchaToken
882
1366
  });
883
1367
  if (!response?.merchantSession) {
884
- console.warn(
885
- "[web-apple-pay] handleMerchantValidation: missing merchantSession in response"
886
- );
887
1368
  session.abort();
888
1369
  redirectToFallbackCheckout(config);
889
1370
  return;
890
1371
  }
891
- console.log("[web-apple-pay] handleMerchantValidation: completing merchant validation");
892
1372
  session.completeMerchantValidation(response.merchantSession);
893
- console.log("[web-apple-pay] handleMerchantValidation: success");
894
1373
  } catch (err) {
895
- console.error("[web-apple-pay] handleMerchantValidation: failed", {
896
- skuId: config.skuId,
897
- err
898
- });
1374
+ console.error("[web-apple-pay] onvalidatemerchant: error during validation", err);
899
1375
  session.abort();
900
1376
  redirectToFallbackCheckout(config);
901
1377
  }
902
1378
  }
903
1379
  function startApplePaySession(config) {
904
- console.log("[web-apple-pay] startApplePaySession", {
905
- skuId: config.skuId,
906
- countryCode: config.countryCode,
907
- currencyCode: config.currencyCode,
908
- totalAmount: config.totalAmount,
909
- userLoggedIn: config.userLoggedIn
910
- });
911
1380
  const paymentRequest = {
912
1381
  countryCode: config.countryCode,
913
1382
  currencyCode: config.currencyCode,
@@ -921,43 +1390,22 @@ function startApplePaySession(config) {
921
1390
  // Only request email from Apple Pay sheet if user is NOT logged in
922
1391
  ...!config.userLoggedIn && { requiredShippingContactFields: ["email"] }
923
1392
  };
924
- console.log("[web-apple-pay] startApplePaySession: payment request created", {
925
- skuId: config.skuId,
926
- requiredShippingContactFields: paymentRequest.requiredShippingContactFields
927
- });
928
1393
  try {
929
1394
  const session = new ApplePaySession(3, paymentRequest);
930
- console.log("[web-apple-pay] startApplePaySession: ApplePaySession created");
931
1395
  session.onvalidatemerchant = async (event) => {
932
- console.log("[web-apple-pay] onvalidatemerchant: invoked", {
933
- skuId: config.skuId
934
- });
935
1396
  await handleMerchantValidation(event, session, config);
936
1397
  };
937
1398
  session.onpaymentauthorized = (event) => {
938
- console.log("[web-apple-pay] onpaymentauthorized: invoked", {
939
- skuId: config.skuId
940
- });
941
1399
  trackWalletPaymentAuthorised(config.skuId);
942
1400
  config.onPaymentAuthorized(event, session);
943
1401
  };
944
1402
  session.oncancel = () => {
945
- console.log("[web-apple-pay] oncancel: invoked", {
946
- skuId: config.skuId
947
- });
948
1403
  config.onCancel?.();
949
1404
  };
950
- console.log("[web-apple-pay] startApplePaySession: opening payment sheet", {
951
- skuId: config.skuId
952
- });
953
1405
  trackWalletPaymentSheetOpen(config.skuId);
954
1406
  session.begin();
955
- console.log("[web-apple-pay] startApplePaySession: session begin called");
956
1407
  } catch (err) {
957
- console.error("[web-apple-pay] startApplePaySession: failed to start session", {
958
- skuId: config.skuId,
959
- err
960
- });
1408
+ console.error("[web-apple-pay] startApplePaySession: failed to create session", err);
961
1409
  redirectToFallbackCheckout(config);
962
1410
  }
963
1411
  }
@@ -1048,25 +1496,13 @@ var billingContactDetails = (contact, supportedBillingCountries) => {
1048
1496
  // src/clients/payment-checkout/shipping-contact-details.ts
1049
1497
  var EMAIL_FORMAT_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1050
1498
  var shippingContactDetails = (shippingContact) => {
1051
- console.log("[web-apple-pay] shippingContactDetails: start", {
1052
- hasShippingContact: Boolean(shippingContact),
1053
- hasEmailAddress: shippingContact?.emailAddress !== void 0
1054
- });
1055
1499
  const errors = [];
1056
1500
  let emailAddress = "";
1057
1501
  if (shippingContact?.emailAddress === void 0 || shippingContact?.emailAddress === "") {
1058
- console.log(
1059
- "[web-apple-pay] shippingContactDetails: email missing or empty"
1060
- );
1061
1502
  pushRequiredFieldError(errors, "shipping", "emailAddress");
1062
1503
  } else {
1063
- console.log("[web-apple-pay] shippingContactDetails: email provided");
1064
1504
  emailAddress = shippingContact.emailAddress.trim();
1065
- console.log("[web-apple-pay] shippingContactDetails: email trimmed", {
1066
- emailLength: emailAddress.length
1067
- });
1068
1505
  if (emailAddress.length > 80) {
1069
- console.log("[web-apple-pay] shippingContactDetails: email too long");
1070
1506
  pushFieldError(
1071
1507
  errors,
1072
1508
  "shipping",
@@ -1075,9 +1511,6 @@ var shippingContactDetails = (shippingContact) => {
1075
1511
  MSG_EMAIL_TOO_LONG
1076
1512
  );
1077
1513
  } else if (!EMAIL_FORMAT_REGEX.test(emailAddress)) {
1078
- console.log(
1079
- "[web-apple-pay] shippingContactDetails: email format invalid"
1080
- );
1081
1514
  pushFieldError(
1082
1515
  errors,
1083
1516
  "shipping",
@@ -1085,17 +1518,11 @@ var shippingContactDetails = (shippingContact) => {
1085
1518
  "format",
1086
1519
  MSG_EMAIL_WRONG_FORMAT
1087
1520
  );
1088
- } else {
1089
- console.log("[web-apple-pay] shippingContactDetails: email valid");
1090
1521
  }
1091
1522
  }
1092
1523
  if (errors.length > 0) {
1093
- console.log("[web-apple-pay] shippingContactDetails: throwing validation error", {
1094
- errorCount: errors.length
1095
- });
1096
1524
  throw new ContactValidationError(errors);
1097
1525
  }
1098
- console.log("[web-apple-pay] shippingContactDetails: success");
1099
1526
  return { emailAddress };
1100
1527
  };
1101
1528
 
@@ -1110,9 +1537,15 @@ var failApplePaySession = (session, errors = []) => {
1110
1537
  };
1111
1538
  function parseWalletApiError(err) {
1112
1539
  const body = err instanceof ApiError ? err.body : err;
1540
+ console.warn("[web-apple-pay] parseWalletApiError: raw error", {
1541
+ isApiError: err instanceof ApiError,
1542
+ body
1543
+ });
1113
1544
  if (body && typeof body === "object" && "errorType" in body && "message" in body) {
1545
+ console.warn("[web-apple-pay] parseWalletApiError: structured BFF error", body);
1114
1546
  return body;
1115
1547
  }
1548
+ console.warn("[web-apple-pay] parseWalletApiError: unstructured error \u2014 no errorType found");
1116
1549
  return null;
1117
1550
  }
1118
1551
  function validateContacts(event, sessionData, supportedBillingCountries) {
@@ -1126,10 +1559,10 @@ function validateContacts(event, sessionData, supportedBillingCountries) {
1126
1559
  return void 0;
1127
1560
  }
1128
1561
  function redirectFallback(skuId, returnUrl, options) {
1562
+ writeCheckoutRedirect(returnUrl);
1129
1563
  trackFallbackCheckout(skuId);
1130
1564
  window.location.href = redirectToCheckoutFallback({
1131
1565
  skuId,
1132
- returnUrl,
1133
1566
  ...options
1134
1567
  });
1135
1568
  }
@@ -1157,58 +1590,49 @@ function handleStructuredError(ctx) {
1157
1590
  async function handlePaymentAuthorized(event, session, sessionData, config) {
1158
1591
  const { skuId, returnUrl, queryString } = config;
1159
1592
  let validatedEmail;
1160
- console.log("[web-apple-pay] handlePaymentAuthorized: starting", { skuId, userLoggedIn: sessionData.loggedIn });
1161
1593
  try {
1162
- console.log("[web-apple-pay] handlePaymentAuthorized: step 1 \u2014 validating contacts");
1163
1594
  validatedEmail = validateContacts(
1164
1595
  event,
1165
1596
  sessionData,
1166
1597
  config.supportedBillingCountries
1167
1598
  );
1168
- console.log("[web-apple-pay] handlePaymentAuthorized: contacts valid", { validatedEmail });
1169
- console.log("[web-apple-pay] handlePaymentAuthorized: step 2 \u2014 calling performPurchase", { skuId });
1170
1599
  const result = await performPurchase({
1171
1600
  applePayToken: event.payment.token,
1172
1601
  skuId: config.skuId,
1602
+ currency: config.currency,
1173
1603
  billingContact: event.payment.billingContact,
1174
1604
  ...event.payment.shippingContact && {
1175
1605
  shippingContact: event.payment.shippingContact
1176
1606
  },
1177
1607
  recaptchaTokens: config.recaptchaTokens
1178
1608
  });
1179
- console.log("[web-apple-pay] handlePaymentAuthorized: performPurchase succeeded", { basketId: result.basketId });
1180
- console.log("[web-apple-pay] handlePaymentAuthorized: step 3 \u2014 completing payment and redirecting");
1181
1609
  trackWalletPaymentSuccess(skuId, result.basketId);
1182
1610
  session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
1183
1611
  const userStatus = sessionData.loggedIn ? "lex" : "nex";
1612
+ writeCheckoutRedirect(returnUrl);
1184
1613
  window.location.href = redirectToCheckoutComplete({
1185
1614
  basketId: result.basketId,
1186
1615
  status: userStatus,
1187
1616
  skuId: decodeSku(skuId),
1188
- returnUrl,
1189
1617
  queryString
1190
1618
  });
1191
1619
  } catch (err) {
1192
1620
  if (err instanceof ContactValidationError) {
1193
- console.warn("[web-apple-pay] handlePaymentAuthorized: contact validation failed", err.errors);
1194
1621
  failApplePaySession(session, err.errors);
1195
1622
  return;
1196
1623
  }
1197
1624
  console.error("[web-apple-pay] onpaymentauthorized error", { skuId, err });
1198
1625
  const walletError = parseWalletApiError(err);
1199
- console.error("[web-apple-pay] handlePaymentAuthorized: BFF/network error", {
1200
- skuId,
1201
- errorType: walletError?.errorType ?? "UNKNOWN",
1202
- message: walletError?.message
1626
+ console.warn("[web-apple-pay] handlePaymentAuthorized: walletError result", {
1627
+ walletError,
1628
+ validatedEmail
1203
1629
  });
1204
1630
  trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
1205
1631
  failApplePaySession(session);
1206
1632
  if (!walletError) {
1207
- console.warn("[web-apple-pay] handlePaymentAuthorized: unstructured error, using generic fallback");
1208
1633
  redirectFallback(skuId, returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1209
1634
  return;
1210
1635
  }
1211
- console.log("[web-apple-pay] handlePaymentAuthorized: dispatching structured error handler", { errorType: walletError.errorType });
1212
1636
  handleStructuredError({ skuId, returnUrl, validatedEmail, walletError });
1213
1637
  }
1214
1638
  }
@@ -1530,7 +1954,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1530
1954
  #returnUrl = "";
1531
1955
  #supportedBillingCountries = [];
1532
1956
  #entryPoint = "";
1533
- #recaptchaTokens = null;
1957
+ #recaptchaTokensPromise = null;
1534
1958
  // ─── Public API ─────────────────────────────────────────────────────────────
1535
1959
  get offer() {
1536
1960
  return this.#offer;
@@ -1595,6 +2019,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1595
2019
  this.#paymentOptions = null;
1596
2020
  this.#returnUrl = "";
1597
2021
  this.#supportedBillingCountries = [];
2022
+ this.#recaptchaTokensPromise = null;
1598
2023
  this.querySelector(SEL_CONTAINER)?.remove();
1599
2024
  }
1600
2025
  // ─── Render pipeline ────────────────────────────────────────────────────────
@@ -1608,7 +2033,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1608
2033
  this.#computeBillingCountries();
1609
2034
  if (await this.#fetchAndCacheOptions()) return;
1610
2035
  this.#finishLoading();
1611
- } catch {
2036
+ } catch (err) {
2037
+ console.error("[web-apple-pay] #run: unexpected error, hiding button", err);
1612
2038
  this.style.display = "none";
1613
2039
  this.querySelector(SEL_CONTAINER)?.remove();
1614
2040
  } finally {
@@ -1689,16 +2115,13 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1689
2115
  if (button.classList.contains("wap-loading")) return;
1690
2116
  this.#openModal();
1691
2117
  });
1692
- document.addEventListener("apple-pay-confirmed", (event) => {
1693
- const { skuId, country } = event.detail;
1694
- this.#initiateApplePay(skuId, country);
1695
- }, { once: true });
1696
2118
  }
1697
2119
  #finishLoading() {
1698
2120
  const button = this.querySelector(SEL_BUTTON);
1699
2121
  if (button) {
1700
2122
  button.classList.remove("wap-loading");
1701
2123
  this.#updateDisabledState();
2124
+ this.#scheduleRecaptchaTokens();
1702
2125
  trackExpressWalletImpression(this.#offer.sku_id, {
1703
2126
  entryPoint: this.#entryPoint,
1704
2127
  country: this.#country
@@ -1726,10 +2149,6 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1726
2149
  return;
1727
2150
  }
1728
2151
  trackExpressWalletClick(this.#offer.sku_id);
1729
- getPurchaseRecaptchaTokens().then((tokens) => {
1730
- this.#recaptchaTokens = tokens;
1731
- }).catch(() => {
1732
- });
1733
2152
  ApplePayModal.define();
1734
2153
  const modal = document.createElement(ApplePayModal.tag);
1735
2154
  modal.offer = this.#offer;
@@ -1737,7 +2156,30 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1737
2156
  if (this.#sessionData?.loggedIn) {
1738
2157
  modal.dataset.loggedIn = "true";
1739
2158
  }
1740
- modal.open();
2159
+ const skuId = this.#offer.sku_id;
2160
+ const country = this.#country;
2161
+ modal.onConfirm = (recaptchaTokens) => {
2162
+ this.#initiateApplePay(skuId, country, recaptchaTokens);
2163
+ this.#scheduleRecaptchaTokens();
2164
+ };
2165
+ modal.open(this.#recaptchaTokensPromise);
2166
+ }
2167
+ /**
2168
+ * Starts a fresh reCAPTCHA token pre-fetch and stores its promise.
2169
+ * If the fetch rejects, the field is cleared so the next `modal.open()` call
2170
+ * falls back to a new attempt via the modal's own null-fallback path — preventing
2171
+ * a single transient failure from permanently disabling the CTA on all retries.
2172
+ * The identity guard prevents a late rejection from clobbering a newer attempt.
2173
+ */
2174
+ #scheduleRecaptchaTokens() {
2175
+ const attempt = getPurchaseRecaptchaTokens();
2176
+ attempt.catch((err) => {
2177
+ console.error("[web-apple-pay] reCAPTCHA token pre-fetch failed; will retry on modal open", err);
2178
+ if (this.#recaptchaTokensPromise === attempt) {
2179
+ this.#recaptchaTokensPromise = null;
2180
+ }
2181
+ });
2182
+ this.#recaptchaTokensPromise = attempt;
1741
2183
  }
1742
2184
  async #fetchSession() {
1743
2185
  try {
@@ -1773,6 +2215,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1773
2215
  onPaymentAuthorized: (event, session) => handlePaymentAuthorized(event, session, sessionData, {
1774
2216
  skuId,
1775
2217
  country,
2218
+ currency: offer.price?.currency_code ?? DEFAULT_CURRENCY,
1776
2219
  returnUrl: this.#returnUrl,
1777
2220
  queryString: buildQueryString(),
1778
2221
  supportedBillingCountries: this.#supportedBillingCountries,
@@ -1780,11 +2223,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1780
2223
  })
1781
2224
  };
1782
2225
  }
1783
- async #initiateApplePay(skuId, country) {
2226
+ #initiateApplePay(skuId, country, recaptchaTokens) {
1784
2227
  try {
1785
2228
  const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
1786
2229
  const paymentOptions = this.#paymentOptions;
1787
- const recaptchaTokens = this.#recaptchaTokens ?? await getPurchaseRecaptchaTokens();
1788
2230
  startApplePaySession(
1789
2231
  this.#buildSessionParams(
1790
2232
  skuId,