@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 +633 -191
- package/dist/src/apple-pay-button.js +628 -180
- package/dist/src/apple-pay-button.stories.js +654 -195
- package/dist/src/apple-pay-modal.d.ts +16 -1
- package/dist/src/apple-pay-modal.js +556 -10
- package/dist/src/apple-pay-modal.stories.js +628 -180
- package/dist/src/clients/payment-checkout/apple-session.js +34 -59
- package/dist/src/clients/payment-checkout/config.js +0 -7
- package/dist/src/clients/payment-checkout/detects.js +2 -10
- package/dist/src/clients/payment-checkout/index.js +7 -0
- package/dist/src/clients/payment-checkout/payment-handler.d.ts +1 -0
- package/dist/src/clients/payment-checkout/payment-handler.js +36 -39
- package/dist/src/clients/payment-checkout/session.js +7 -0
- package/dist/src/clients/payment-checkout/shipping-contact-details.js +0 -21
- package/dist/src/clients/payment-checkout/types.d.ts +1 -0
- package/dist/src/index.js +633 -191
- package/dist/src/stories/utils/story-env.js +18 -7
- package/dist/src/utils/expressCheckoutRedirect.d.ts +27 -6
- package/dist/src/utils/expressCheckoutRedirect.js +19 -4
- package/package.json +1 -1
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
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
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
|
-
|
|
581
|
-
|
|
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
|
-
|
|
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]
|
|
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
|
|
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.
|
|
1200
|
-
|
|
1201
|
-
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
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,
|