@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.
@@ -76,7 +76,517 @@ 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
+ var resetApplePayConfig = () => {
541
+ runtimeConfig = null;
542
+ };
543
+
544
+ // src/utils/recaptcha.ts
545
+ var loadRecaptchaScript = (siteKey) => {
546
+ return new Promise((resolve, reject) => {
547
+ if (typeof grecaptcha !== "undefined") return resolve();
548
+ const existingScript = document.querySelector(
549
+ 'script[src*="recaptcha/api.js"]'
550
+ );
551
+ if (existingScript) {
552
+ existingScript.addEventListener("load", () => resolve());
553
+ existingScript.addEventListener(
554
+ "error",
555
+ () => reject(new Error("Failed to load existing reCAPTCHA script"))
556
+ );
557
+ return;
558
+ }
559
+ const script = document.createElement("script");
560
+ script.src = `https://www.google.com/recaptcha/api.js?render=${siteKey}`;
561
+ script.async = true;
562
+ script.defer = true;
563
+ script.onload = () => resolve();
564
+ script.onerror = () => reject(new Error("Failed to load reCAPTCHA script"));
565
+ document.head.appendChild(script);
566
+ });
567
+ };
568
+ var getRecaptchaToken = async (action) => {
569
+ const siteKey = getApplePayConfig()?.recaptchaSiteKey;
570
+ if (!siteKey) {
571
+ throw new Error(
572
+ "[web-apple-pay] Missing recaptchaSiteKey. Call configureApplePay({ recaptchaSiteKey, ... }) before initiating Apple Pay."
573
+ );
574
+ }
575
+ await loadRecaptchaScript(siteKey);
576
+ return new Promise((resolve, reject) => {
577
+ grecaptcha.ready(() => {
578
+ grecaptcha.execute(siteKey, { action }).then(resolve).catch(reject);
579
+ });
580
+ });
581
+ };
582
+ var getPurchaseRecaptchaTokens = async () => {
583
+ const [checkEligibility, newBasket, registerUser] = await Promise.all([
584
+ getRecaptchaToken("checkEligibility"),
585
+ getRecaptchaToken("newBasket"),
586
+ getRecaptchaToken("registerUser")
587
+ ]);
588
+ return { checkEligibility, newBasket, registerUser };
589
+ };
80
590
 
81
591
  // src/apple-pay-modal.ts
82
592
  function getRenewalDateText(unit, duration) {
@@ -128,6 +638,7 @@ function getOfferTypeText(offerType) {
128
638
  }
129
639
  var TERMS_LINK = '<a href="https://www.economistgroup.com/terms-of-use" target="_blank" rel="noopener noreferrer">Terms and Conditions</a>';
130
640
  var PRIVACY_LINK = '<a href="https://www.economistgroup.com/privacy-policy" target="_blank" rel="noopener noreferrer">Privacy Policy</a>';
641
+ var MSG_RECAPTCHA_LOAD_ERROR = "Something went wrong. Please close and try again.";
131
642
  function getCheckboxTermsHtml(isNYT, isLoggedIn, autoRenewal) {
132
643
  const autoRenewalClause = autoRenewal ? " and auto-renewal terms above," : "";
133
644
  if (isNYT) {
@@ -297,6 +808,19 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
297
808
  #country = "";
298
809
  #triggerElement = null;
299
810
  #keyHandler = null;
811
+ #recaptchaTokens = null;
812
+ #recaptchaTokensPromise = null;
813
+ /**
814
+ * Synchronous callback invoked directly from the CTA click handler once the
815
+ * user confirms. The caller (ApplePayButton) must call startApplePaySession
816
+ * inside this callback to preserve the browser's user-gesture chain — Safari
817
+ * requires new ApplePaySession() to execute within the original click stack.
818
+ *
819
+ * reCAPTCHA tokens are guaranteed non-null here: the CTA remains disabled
820
+ * until the token pre-fetch resolves, so this callback is never reached
821
+ * without valid tokens.
822
+ */
823
+ onConfirm = null;
300
824
  set offer(value) {
301
825
  this.#offer = value;
302
826
  }
@@ -391,27 +915,57 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
391
915
  }
392
916
  });
393
917
  }
918
+ if (this.#recaptchaTokensPromise) {
919
+ if (cta) {
920
+ cta.setAttribute("disabled", "");
921
+ cta.setAttribute("aria-disabled", "true");
922
+ cta.setAttribute("aria-busy", "true");
923
+ }
924
+ this.#recaptchaTokensPromise.then((tokens) => {
925
+ this.#recaptchaTokens = tokens;
926
+ if (cta) {
927
+ cta.removeAttribute("disabled");
928
+ cta.removeAttribute("aria-disabled");
929
+ cta.removeAttribute("aria-busy");
930
+ }
931
+ }).catch(() => {
932
+ console.error("[web-apple-pay] apple-pay-modal: failed to pre-fetch reCAPTCHA tokens; CTA remains disabled");
933
+ if (cta) {
934
+ cta.removeAttribute("aria-busy");
935
+ }
936
+ const existingError = this.querySelector("#apple-pay-modal-error");
937
+ if (existingError) {
938
+ const icon = existingError.querySelector("svg");
939
+ existingError.textContent = MSG_RECAPTCHA_LOAD_ERROR;
940
+ if (icon) existingError.prepend(icon);
941
+ existingError.classList.add("apm__error--visible");
942
+ } else {
943
+ const errorNotice = document.createElement("div");
944
+ errorNotice.setAttribute("role", "alert");
945
+ errorNotice.className = "apm__error apm__error--visible";
946
+ errorNotice.textContent = MSG_RECAPTCHA_LOAD_ERROR;
947
+ cta?.insertAdjacentElement("beforebegin", errorNotice);
948
+ }
949
+ });
950
+ }
394
951
  cta?.addEventListener("click", () => {
395
952
  if (isUS && checkbox && !checkbox.checked) {
396
953
  errorElement?.classList.add("apm__error--visible");
397
954
  checkboxLabel?.classList.add("apm__checkbox-label--error");
398
955
  return;
399
956
  }
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();
957
+ if (this.#recaptchaTokens) {
958
+ this.onConfirm?.(this.#recaptchaTokens);
959
+ this.close();
960
+ }
408
961
  });
409
962
  this.querySelector("#apple-pay-modal-close")?.addEventListener(
410
963
  "click",
411
964
  () => this.close()
412
965
  );
413
966
  }
414
- open() {
967
+ open(recaptchaTokensPromise) {
968
+ this.#recaptchaTokensPromise = recaptchaTokensPromise ?? getPurchaseRecaptchaTokens();
415
969
  this.#triggerElement = document.activeElement;
416
970
  document.body.appendChild(this);
417
971
  this.#keyHandler = (event) => {
@@ -524,25 +1078,6 @@ var apple_pay_button_template_default = `<style>
524
1078
  </div>
525
1079
  `;
526
1080
 
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
- var resetApplePayConfig = () => {
542
- console.log("[web-apple-pay] resetApplePayConfig called");
543
- runtimeConfig = null;
544
- };
545
-
546
1081
  // src/clients/payment-checkout/detects.ts
547
1082
  var isAppleDeviceOrSafari = () => {
548
1083
  const isIOSOrMacTouchDevice = /iPad|iPhone|iPod/.test(navigator.userAgent) || navigator.maxTouchPoints > 1 && /Macintosh/.test(navigator.userAgent);
@@ -551,27 +1086,21 @@ var isAppleDeviceOrSafari = () => {
551
1086
  };
552
1087
  var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null && getApplePayConfig()?.debugBypassEnabled === true;
553
1088
  var isApplePayAvailable = async () => {
554
- console.log("[web-apple-pay] isApplePayAvailable: checking availability");
555
1089
  if (bypassApplePayChecks()) {
556
- console.log("[web-apple-pay] isApplePayAvailable: bypass active \u2192 true");
557
1090
  return true;
558
1091
  }
559
1092
  if (!isAppleDeviceOrSafari()) {
560
- console.log("[web-apple-pay] isApplePayAvailable: not Apple device or Safari \u2192 false");
561
1093
  return false;
562
1094
  }
563
1095
  const applePaySession = window.ApplePaySession;
564
1096
  if (!applePaySession) {
565
- console.log("[web-apple-pay] isApplePayAvailable: ApplePaySession not found on window \u2192 false");
566
1097
  return false;
567
1098
  }
568
1099
  try {
569
1100
  if (typeof applePaySession.canMakePayments !== "function") {
570
- console.log("[web-apple-pay] isApplePayAvailable: canMakePayments is not a function \u2192 false");
571
1101
  return false;
572
1102
  }
573
1103
  if (!applePaySession.canMakePayments()) {
574
- console.log("[web-apple-pay] isApplePayAvailable: canMakePayments() returned false \u2192 false");
575
1104
  return false;
576
1105
  }
577
1106
  const merchantId = getApplePayConfig()?.merchantId;
@@ -581,10 +1110,8 @@ var isApplePayAvailable = async () => {
581
1110
  );
582
1111
  return false;
583
1112
  }
584
- console.log("[web-apple-pay] isApplePayAvailable : calling canMakePaymentsWithActiveCard", { merchantId });
585
- const result = await applePaySession.canMakePaymentsWithActiveCard(merchantId);
586
- console.log("[web-apple-pay] isApplePayAvailable: canMakePaymentsWithActiveCard \u2192", result);
587
- return result;
1113
+ const hasActiveCard = await applePaySession.canMakePaymentsWithActiveCard(merchantId);
1114
+ return hasActiveCard;
588
1115
  } catch (error) {
589
1116
  console.error("Error checking Apple Pay active card status:", error);
590
1117
  return false;
@@ -632,11 +1159,18 @@ var apiFetch = async (path, options = {}) => {
632
1159
  });
633
1160
  if (!response.ok) {
634
1161
  const contentType = response.headers.get("content-type") || "";
1162
+ console.warn("[web-apple-pay] apiFetch: non-2xx response", {
1163
+ path,
1164
+ status: response.status,
1165
+ contentType
1166
+ });
635
1167
  if (contentType.includes("application/json")) {
636
1168
  const errorBody = await response.json();
1169
+ console.warn("[web-apple-pay] apiFetch: parsed JSON error body", { errorBody });
637
1170
  throw new ApiError(response.status, errorBody);
638
1171
  }
639
1172
  const errorText = await response.text();
1173
+ console.warn("[web-apple-pay] apiFetch: non-JSON error body", { errorText });
640
1174
  throw new Error(
641
1175
  `API request failed: ${response.status} ${response.statusText} - ${errorText}`
642
1176
  );
@@ -674,6 +1208,22 @@ var getExpressCheckoutSession = () => {
674
1208
  };
675
1209
 
676
1210
  // src/utils/expressCheckoutRedirect.ts
1211
+ var CHECKOUT_REDIRECT_KEY = "checkout-redirect";
1212
+ var ALLOWED_REDIRECT_HOSTNAME = "economist.com";
1213
+ var writeCheckoutRedirect = (originUrl) => {
1214
+ if (originUrl.startsWith("/")) {
1215
+ localStorage.setItem(CHECKOUT_REDIRECT_KEY, originUrl);
1216
+ return;
1217
+ }
1218
+ try {
1219
+ const { hostname } = new URL(originUrl);
1220
+ const isAllowed = hostname === ALLOWED_REDIRECT_HOSTNAME || hostname.endsWith(`.${ALLOWED_REDIRECT_HOSTNAME}`);
1221
+ if (isAllowed) {
1222
+ localStorage.setItem(CHECKOUT_REDIRECT_KEY, originUrl);
1223
+ }
1224
+ } catch {
1225
+ }
1226
+ };
677
1227
  var captureOriginUrl = () => window.location.href;
678
1228
  var buildQueryString = () => {
679
1229
  const countryCode = new URLSearchParams(window.location.search).get(
@@ -698,68 +1248,19 @@ var redirectToCheckoutComplete = (params) => {
698
1248
  url.searchParams.set("lmo_offer", params.skuId);
699
1249
  url.searchParams.set("status", params.status);
700
1250
  url.searchParams.set("redirectToCheckout", "true");
701
- url.searchParams.set("returnUrl", params.returnUrl);
702
1251
  return url.pathname + url.search;
703
1252
  };
704
1253
  var redirectToCheckoutFallback = (params) => {
705
1254
  const url = new URL(window.location.href);
706
1255
  url.pathname = "/checkout";
707
- url.searchParams.set("lmo_offer", params.skuId);
1256
+ url.searchParams.set("lmo_offer", decodeSku(params.skuId));
708
1257
  if (params.inlineError)
709
1258
  url.searchParams.set("inlineError", params.inlineError);
710
1259
  if (params.email) url.searchParams.set("email", params.email);
711
1260
  if (params.emailError) url.searchParams.set("emailError", params.emailError);
712
- url.searchParams.set("returnUrl", params.returnUrl);
713
1261
  return url.pathname + url.search;
714
1262
  };
715
1263
 
716
- // src/utils/recaptcha.ts
717
- var loadRecaptchaScript = (siteKey) => {
718
- return new Promise((resolve, reject) => {
719
- if (typeof grecaptcha !== "undefined") return resolve();
720
- const existingScript = document.querySelector(
721
- 'script[src*="recaptcha/api.js"]'
722
- );
723
- if (existingScript) {
724
- existingScript.addEventListener("load", () => resolve());
725
- existingScript.addEventListener(
726
- "error",
727
- () => reject(new Error("Failed to load existing reCAPTCHA script"))
728
- );
729
- return;
730
- }
731
- const script = document.createElement("script");
732
- script.src = `https://www.google.com/recaptcha/api.js?render=${siteKey}`;
733
- script.async = true;
734
- script.defer = true;
735
- script.onload = () => resolve();
736
- script.onerror = () => reject(new Error("Failed to load reCAPTCHA script"));
737
- document.head.appendChild(script);
738
- });
739
- };
740
- var getRecaptchaToken = async (action) => {
741
- const siteKey = getApplePayConfig()?.recaptchaSiteKey;
742
- if (!siteKey) {
743
- throw new Error(
744
- "[web-apple-pay] Missing recaptchaSiteKey. Call configureApplePay({ recaptchaSiteKey, ... }) before initiating Apple Pay."
745
- );
746
- }
747
- await loadRecaptchaScript(siteKey);
748
- return new Promise((resolve, reject) => {
749
- grecaptcha.ready(() => {
750
- grecaptcha.execute(siteKey, { action }).then(resolve).catch(reject);
751
- });
752
- });
753
- };
754
- var getPurchaseRecaptchaTokens = async () => {
755
- const [checkEligibility, newBasket, registerUser] = await Promise.all([
756
- getRecaptchaToken("checkEligibility"),
757
- getRecaptchaToken("newBasket"),
758
- getRecaptchaToken("registerUser")
759
- ]);
760
- return { checkEligibility, newBasket, registerUser };
761
- };
762
-
763
1264
  // src/utils/expressCheckoutTracking.ts
764
1265
  var EXPRESS_CHECKOUT_EVENTS = {
765
1266
  /** Wallet button displayed */
@@ -846,72 +1347,39 @@ var MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
846
1347
 
847
1348
  // src/clients/payment-checkout/apple-session.ts
848
1349
  function redirectToFallbackCheckout(config) {
849
- console.warn("[web-apple-pay] redirectToFallbackCheckout", {
850
- skuId: config.skuId,
851
- returnUrl: config.returnUrl,
852
- inlineError: MSG_PAYMENT_GENERIC_ERROR
853
- });
1350
+ writeCheckoutRedirect(config.returnUrl);
854
1351
  trackFallbackCheckout(config.skuId);
855
1352
  window.location.href = redirectToCheckoutFallback({
856
1353
  skuId: config.skuId,
857
- returnUrl: config.returnUrl,
858
1354
  inlineError: MSG_PAYMENT_GENERIC_ERROR
859
1355
  });
860
1356
  }
861
1357
  async function handleMerchantValidation(event, session, config) {
862
- console.log("[web-apple-pay] handleMerchantValidation: start", {
863
- skuId: config.skuId,
864
- validationURL: event.validationURL
865
- });
866
1358
  try {
867
1359
  const validationHost = new URL(event.validationURL).host;
868
- console.log("[web-apple-pay] handleMerchantValidation: host parsed", {
869
- validationHost
870
- });
871
1360
  if (!validationHost.endsWith("apple.com")) {
872
- console.warn(
873
- "[web-apple-pay] handleMerchantValidation: invalid validation host",
874
- { validationHost }
875
- );
876
1361
  session.abort();
877
1362
  redirectToFallbackCheckout(config);
878
1363
  return;
879
1364
  }
880
- console.log("[web-apple-pay] handleMerchantValidation: requesting recaptcha token");
881
1365
  const recaptchaToken = await getRecaptchaToken("verifyMerchant");
882
- console.log("[web-apple-pay] handleMerchantValidation: recaptcha token received");
883
1366
  const response = await validateAppleMerchant({
884
1367
  validationURL: event.validationURL,
885
1368
  recaptchaToken
886
1369
  });
887
1370
  if (!response?.merchantSession) {
888
- console.warn(
889
- "[web-apple-pay] handleMerchantValidation: missing merchantSession in response"
890
- );
891
1371
  session.abort();
892
1372
  redirectToFallbackCheckout(config);
893
1373
  return;
894
1374
  }
895
- console.log("[web-apple-pay] handleMerchantValidation: completing merchant validation");
896
1375
  session.completeMerchantValidation(response.merchantSession);
897
- console.log("[web-apple-pay] handleMerchantValidation: success");
898
1376
  } catch (err) {
899
- console.error("[web-apple-pay] handleMerchantValidation: failed", {
900
- skuId: config.skuId,
901
- err
902
- });
1377
+ console.error("[web-apple-pay] onvalidatemerchant: error during validation", err);
903
1378
  session.abort();
904
1379
  redirectToFallbackCheckout(config);
905
1380
  }
906
1381
  }
907
1382
  function startApplePaySession(config) {
908
- console.log("[web-apple-pay] startApplePaySession", {
909
- skuId: config.skuId,
910
- countryCode: config.countryCode,
911
- currencyCode: config.currencyCode,
912
- totalAmount: config.totalAmount,
913
- userLoggedIn: config.userLoggedIn
914
- });
915
1383
  const paymentRequest = {
916
1384
  countryCode: config.countryCode,
917
1385
  currencyCode: config.currencyCode,
@@ -925,43 +1393,22 @@ function startApplePaySession(config) {
925
1393
  // Only request email from Apple Pay sheet if user is NOT logged in
926
1394
  ...!config.userLoggedIn && { requiredShippingContactFields: ["email"] }
927
1395
  };
928
- console.log("[web-apple-pay] startApplePaySession: payment request created", {
929
- skuId: config.skuId,
930
- requiredShippingContactFields: paymentRequest.requiredShippingContactFields
931
- });
932
1396
  try {
933
1397
  const session = new ApplePaySession(3, paymentRequest);
934
- console.log("[web-apple-pay] startApplePaySession: ApplePaySession created");
935
1398
  session.onvalidatemerchant = async (event) => {
936
- console.log("[web-apple-pay] onvalidatemerchant: invoked", {
937
- skuId: config.skuId
938
- });
939
1399
  await handleMerchantValidation(event, session, config);
940
1400
  };
941
1401
  session.onpaymentauthorized = (event) => {
942
- console.log("[web-apple-pay] onpaymentauthorized: invoked", {
943
- skuId: config.skuId
944
- });
945
1402
  trackWalletPaymentAuthorised(config.skuId);
946
1403
  config.onPaymentAuthorized(event, session);
947
1404
  };
948
1405
  session.oncancel = () => {
949
- console.log("[web-apple-pay] oncancel: invoked", {
950
- skuId: config.skuId
951
- });
952
1406
  config.onCancel?.();
953
1407
  };
954
- console.log("[web-apple-pay] startApplePaySession: opening payment sheet", {
955
- skuId: config.skuId
956
- });
957
1408
  trackWalletPaymentSheetOpen(config.skuId);
958
1409
  session.begin();
959
- console.log("[web-apple-pay] startApplePaySession: session begin called");
960
1410
  } catch (err) {
961
- console.error("[web-apple-pay] startApplePaySession: failed to start session", {
962
- skuId: config.skuId,
963
- err
964
- });
1411
+ console.error("[web-apple-pay] startApplePaySession: failed to create session", err);
965
1412
  redirectToFallbackCheckout(config);
966
1413
  }
967
1414
  }
@@ -1052,25 +1499,13 @@ var billingContactDetails = (contact, supportedBillingCountries) => {
1052
1499
  // src/clients/payment-checkout/shipping-contact-details.ts
1053
1500
  var EMAIL_FORMAT_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1054
1501
  var shippingContactDetails = (shippingContact) => {
1055
- console.log("[web-apple-pay] shippingContactDetails: start", {
1056
- hasShippingContact: Boolean(shippingContact),
1057
- hasEmailAddress: shippingContact?.emailAddress !== void 0
1058
- });
1059
1502
  const errors = [];
1060
1503
  let emailAddress = "";
1061
1504
  if (shippingContact?.emailAddress === void 0 || shippingContact?.emailAddress === "") {
1062
- console.log(
1063
- "[web-apple-pay] shippingContactDetails: email missing or empty"
1064
- );
1065
1505
  pushRequiredFieldError(errors, "shipping", "emailAddress");
1066
1506
  } else {
1067
- console.log("[web-apple-pay] shippingContactDetails: email provided");
1068
1507
  emailAddress = shippingContact.emailAddress.trim();
1069
- console.log("[web-apple-pay] shippingContactDetails: email trimmed", {
1070
- emailLength: emailAddress.length
1071
- });
1072
1508
  if (emailAddress.length > 80) {
1073
- console.log("[web-apple-pay] shippingContactDetails: email too long");
1074
1509
  pushFieldError(
1075
1510
  errors,
1076
1511
  "shipping",
@@ -1079,9 +1514,6 @@ var shippingContactDetails = (shippingContact) => {
1079
1514
  MSG_EMAIL_TOO_LONG
1080
1515
  );
1081
1516
  } else if (!EMAIL_FORMAT_REGEX.test(emailAddress)) {
1082
- console.log(
1083
- "[web-apple-pay] shippingContactDetails: email format invalid"
1084
- );
1085
1517
  pushFieldError(
1086
1518
  errors,
1087
1519
  "shipping",
@@ -1089,17 +1521,11 @@ var shippingContactDetails = (shippingContact) => {
1089
1521
  "format",
1090
1522
  MSG_EMAIL_WRONG_FORMAT
1091
1523
  );
1092
- } else {
1093
- console.log("[web-apple-pay] shippingContactDetails: email valid");
1094
1524
  }
1095
1525
  }
1096
1526
  if (errors.length > 0) {
1097
- console.log("[web-apple-pay] shippingContactDetails: throwing validation error", {
1098
- errorCount: errors.length
1099
- });
1100
1527
  throw new ContactValidationError(errors);
1101
1528
  }
1102
- console.log("[web-apple-pay] shippingContactDetails: success");
1103
1529
  return { emailAddress };
1104
1530
  };
1105
1531
 
@@ -1114,9 +1540,15 @@ var failApplePaySession = (session, errors = []) => {
1114
1540
  };
1115
1541
  function parseWalletApiError(err) {
1116
1542
  const body = err instanceof ApiError ? err.body : err;
1543
+ console.warn("[web-apple-pay] parseWalletApiError: raw error", {
1544
+ isApiError: err instanceof ApiError,
1545
+ body
1546
+ });
1117
1547
  if (body && typeof body === "object" && "errorType" in body && "message" in body) {
1548
+ console.warn("[web-apple-pay] parseWalletApiError: structured BFF error", body);
1118
1549
  return body;
1119
1550
  }
1551
+ console.warn("[web-apple-pay] parseWalletApiError: unstructured error \u2014 no errorType found");
1120
1552
  return null;
1121
1553
  }
1122
1554
  function validateContacts(event, sessionData, supportedBillingCountries) {
@@ -1130,10 +1562,10 @@ function validateContacts(event, sessionData, supportedBillingCountries) {
1130
1562
  return void 0;
1131
1563
  }
1132
1564
  function redirectFallback(skuId, returnUrl, options) {
1565
+ writeCheckoutRedirect(returnUrl);
1133
1566
  trackFallbackCheckout(skuId);
1134
1567
  window.location.href = redirectToCheckoutFallback({
1135
1568
  skuId,
1136
- returnUrl,
1137
1569
  ...options
1138
1570
  });
1139
1571
  }
@@ -1161,58 +1593,49 @@ function handleStructuredError(ctx) {
1161
1593
  async function handlePaymentAuthorized(event, session, sessionData, config) {
1162
1594
  const { skuId, returnUrl, queryString } = config;
1163
1595
  let validatedEmail;
1164
- console.log("[web-apple-pay] handlePaymentAuthorized: starting", { skuId, userLoggedIn: sessionData.loggedIn });
1165
1596
  try {
1166
- console.log("[web-apple-pay] handlePaymentAuthorized: step 1 \u2014 validating contacts");
1167
1597
  validatedEmail = validateContacts(
1168
1598
  event,
1169
1599
  sessionData,
1170
1600
  config.supportedBillingCountries
1171
1601
  );
1172
- console.log("[web-apple-pay] handlePaymentAuthorized: contacts valid", { validatedEmail });
1173
- console.log("[web-apple-pay] handlePaymentAuthorized: step 2 \u2014 calling performPurchase", { skuId });
1174
1602
  const result = await performPurchase({
1175
1603
  applePayToken: event.payment.token,
1176
1604
  skuId: config.skuId,
1605
+ currency: config.currency,
1177
1606
  billingContact: event.payment.billingContact,
1178
1607
  ...event.payment.shippingContact && {
1179
1608
  shippingContact: event.payment.shippingContact
1180
1609
  },
1181
1610
  recaptchaTokens: config.recaptchaTokens
1182
1611
  });
1183
- console.log("[web-apple-pay] handlePaymentAuthorized: performPurchase succeeded", { basketId: result.basketId });
1184
- console.log("[web-apple-pay] handlePaymentAuthorized: step 3 \u2014 completing payment and redirecting");
1185
1612
  trackWalletPaymentSuccess(skuId, result.basketId);
1186
1613
  session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
1187
1614
  const userStatus = sessionData.loggedIn ? "lex" : "nex";
1615
+ writeCheckoutRedirect(returnUrl);
1188
1616
  window.location.href = redirectToCheckoutComplete({
1189
1617
  basketId: result.basketId,
1190
1618
  status: userStatus,
1191
1619
  skuId: decodeSku(skuId),
1192
- returnUrl,
1193
1620
  queryString
1194
1621
  });
1195
1622
  } catch (err) {
1196
1623
  if (err instanceof ContactValidationError) {
1197
- console.warn("[web-apple-pay] handlePaymentAuthorized: contact validation failed", err.errors);
1198
1624
  failApplePaySession(session, err.errors);
1199
1625
  return;
1200
1626
  }
1201
1627
  console.error("[web-apple-pay] onpaymentauthorized error", { skuId, err });
1202
1628
  const walletError = parseWalletApiError(err);
1203
- console.error("[web-apple-pay] handlePaymentAuthorized: BFF/network error", {
1204
- skuId,
1205
- errorType: walletError?.errorType ?? "UNKNOWN",
1206
- message: walletError?.message
1629
+ console.warn("[web-apple-pay] handlePaymentAuthorized: walletError result", {
1630
+ walletError,
1631
+ validatedEmail
1207
1632
  });
1208
1633
  trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
1209
1634
  failApplePaySession(session);
1210
1635
  if (!walletError) {
1211
- console.warn("[web-apple-pay] handlePaymentAuthorized: unstructured error, using generic fallback");
1212
1636
  redirectFallback(skuId, returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1213
1637
  return;
1214
1638
  }
1215
- console.log("[web-apple-pay] handlePaymentAuthorized: dispatching structured error handler", { errorType: walletError.errorType });
1216
1639
  handleStructuredError({ skuId, returnUrl, validatedEmail, walletError });
1217
1640
  }
1218
1641
  }
@@ -1534,7 +1957,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1534
1957
  #returnUrl = "";
1535
1958
  #supportedBillingCountries = [];
1536
1959
  #entryPoint = "";
1537
- #recaptchaTokens = null;
1960
+ #recaptchaTokensPromise = null;
1538
1961
  // ─── Public API ─────────────────────────────────────────────────────────────
1539
1962
  get offer() {
1540
1963
  return this.#offer;
@@ -1599,6 +2022,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1599
2022
  this.#paymentOptions = null;
1600
2023
  this.#returnUrl = "";
1601
2024
  this.#supportedBillingCountries = [];
2025
+ this.#recaptchaTokensPromise = null;
1602
2026
  this.querySelector(SEL_CONTAINER)?.remove();
1603
2027
  }
1604
2028
  // ─── Render pipeline ────────────────────────────────────────────────────────
@@ -1612,7 +2036,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1612
2036
  this.#computeBillingCountries();
1613
2037
  if (await this.#fetchAndCacheOptions()) return;
1614
2038
  this.#finishLoading();
1615
- } catch {
2039
+ } catch (err) {
2040
+ console.error("[web-apple-pay] #run: unexpected error, hiding button", err);
1616
2041
  this.style.display = "none";
1617
2042
  this.querySelector(SEL_CONTAINER)?.remove();
1618
2043
  } finally {
@@ -1693,16 +2118,13 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1693
2118
  if (button.classList.contains("wap-loading")) return;
1694
2119
  this.#openModal();
1695
2120
  });
1696
- document.addEventListener("apple-pay-confirmed", (event) => {
1697
- const { skuId, country } = event.detail;
1698
- this.#initiateApplePay(skuId, country);
1699
- }, { once: true });
1700
2121
  }
1701
2122
  #finishLoading() {
1702
2123
  const button = this.querySelector(SEL_BUTTON);
1703
2124
  if (button) {
1704
2125
  button.classList.remove("wap-loading");
1705
2126
  this.#updateDisabledState();
2127
+ this.#scheduleRecaptchaTokens();
1706
2128
  trackExpressWalletImpression(this.#offer.sku_id, {
1707
2129
  entryPoint: this.#entryPoint,
1708
2130
  country: this.#country
@@ -1730,10 +2152,6 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1730
2152
  return;
1731
2153
  }
1732
2154
  trackExpressWalletClick(this.#offer.sku_id);
1733
- getPurchaseRecaptchaTokens().then((tokens) => {
1734
- this.#recaptchaTokens = tokens;
1735
- }).catch(() => {
1736
- });
1737
2155
  ApplePayModal.define();
1738
2156
  const modal = document.createElement(ApplePayModal.tag);
1739
2157
  modal.offer = this.#offer;
@@ -1741,7 +2159,30 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1741
2159
  if (this.#sessionData?.loggedIn) {
1742
2160
  modal.dataset.loggedIn = "true";
1743
2161
  }
1744
- modal.open();
2162
+ const skuId = this.#offer.sku_id;
2163
+ const country = this.#country;
2164
+ modal.onConfirm = (recaptchaTokens) => {
2165
+ this.#initiateApplePay(skuId, country, recaptchaTokens);
2166
+ this.#scheduleRecaptchaTokens();
2167
+ };
2168
+ modal.open(this.#recaptchaTokensPromise);
2169
+ }
2170
+ /**
2171
+ * Starts a fresh reCAPTCHA token pre-fetch and stores its promise.
2172
+ * If the fetch rejects, the field is cleared so the next `modal.open()` call
2173
+ * falls back to a new attempt via the modal's own null-fallback path — preventing
2174
+ * a single transient failure from permanently disabling the CTA on all retries.
2175
+ * The identity guard prevents a late rejection from clobbering a newer attempt.
2176
+ */
2177
+ #scheduleRecaptchaTokens() {
2178
+ const attempt = getPurchaseRecaptchaTokens();
2179
+ attempt.catch((err) => {
2180
+ console.error("[web-apple-pay] reCAPTCHA token pre-fetch failed; will retry on modal open", err);
2181
+ if (this.#recaptchaTokensPromise === attempt) {
2182
+ this.#recaptchaTokensPromise = null;
2183
+ }
2184
+ });
2185
+ this.#recaptchaTokensPromise = attempt;
1745
2186
  }
1746
2187
  async #fetchSession() {
1747
2188
  try {
@@ -1777,6 +2218,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1777
2218
  onPaymentAuthorized: (event, session) => handlePaymentAuthorized(event, session, sessionData, {
1778
2219
  skuId,
1779
2220
  country,
2221
+ currency: offer.price?.currency_code ?? DEFAULT_CURRENCY,
1780
2222
  returnUrl: this.#returnUrl,
1781
2223
  queryString: buildQueryString(),
1782
2224
  supportedBillingCountries: this.#supportedBillingCountries,
@@ -1784,11 +2226,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1784
2226
  })
1785
2227
  };
1786
2228
  }
1787
- async #initiateApplePay(skuId, country) {
2229
+ #initiateApplePay(skuId, country, recaptchaTokens) {
1788
2230
  try {
1789
2231
  const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
1790
2232
  const paymentOptions = this.#paymentOptions;
1791
- const recaptchaTokens = this.#recaptchaTokens ?? await getPurchaseRecaptchaTokens();
1792
2233
  startApplePaySession(
1793
2234
  this.#buildSessionParams(
1794
2235
  skuId,
@@ -1859,6 +2300,24 @@ var createFetchMock = (sessionResponse, sessionStatus = 200) => {
1859
2300
  headers: { "content-type": "application/json" }
1860
2301
  });
1861
2302
  }
2303
+ if (requestUrl.includes("/v1/wallet/payment-options/sku/")) {
2304
+ return new Response(
2305
+ JSON.stringify({
2306
+ options: {
2307
+ apple_pay: {
2308
+ provider: "apple-pay",
2309
+ settings: {
2310
+ supported_networks: ["visa", "masterCard", "amex"]
2311
+ }
2312
+ }
2313
+ }
2314
+ }),
2315
+ {
2316
+ status: 200,
2317
+ headers: { "content-type": "application/json" }
2318
+ }
2319
+ );
2320
+ }
1862
2321
  return new Response("Not Found", {
1863
2322
  status: 404,
1864
2323
  statusText: "Not Found"