@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
|
@@ -76,7 +76,509 @@ 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 getApplePayConfig = () => runtimeConfig;
|
|
535
|
+
|
|
536
|
+
// src/utils/recaptcha.ts
|
|
537
|
+
var loadRecaptchaScript = (siteKey) => {
|
|
538
|
+
return new Promise((resolve, reject) => {
|
|
539
|
+
if (typeof grecaptcha !== "undefined") return resolve();
|
|
540
|
+
const existingScript = document.querySelector(
|
|
541
|
+
'script[src*="recaptcha/api.js"]'
|
|
542
|
+
);
|
|
543
|
+
if (existingScript) {
|
|
544
|
+
existingScript.addEventListener("load", () => resolve());
|
|
545
|
+
existingScript.addEventListener(
|
|
546
|
+
"error",
|
|
547
|
+
() => reject(new Error("Failed to load existing reCAPTCHA script"))
|
|
548
|
+
);
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
const script = document.createElement("script");
|
|
552
|
+
script.src = `https://www.google.com/recaptcha/api.js?render=${siteKey}`;
|
|
553
|
+
script.async = true;
|
|
554
|
+
script.defer = true;
|
|
555
|
+
script.onload = () => resolve();
|
|
556
|
+
script.onerror = () => reject(new Error("Failed to load reCAPTCHA script"));
|
|
557
|
+
document.head.appendChild(script);
|
|
558
|
+
});
|
|
559
|
+
};
|
|
560
|
+
var getRecaptchaToken = async (action) => {
|
|
561
|
+
const siteKey = getApplePayConfig()?.recaptchaSiteKey;
|
|
562
|
+
if (!siteKey) {
|
|
563
|
+
throw new Error(
|
|
564
|
+
"[web-apple-pay] Missing recaptchaSiteKey. Call configureApplePay({ recaptchaSiteKey, ... }) before initiating Apple Pay."
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
await loadRecaptchaScript(siteKey);
|
|
568
|
+
return new Promise((resolve, reject) => {
|
|
569
|
+
grecaptcha.ready(() => {
|
|
570
|
+
grecaptcha.execute(siteKey, { action }).then(resolve).catch(reject);
|
|
571
|
+
});
|
|
572
|
+
});
|
|
573
|
+
};
|
|
574
|
+
var getPurchaseRecaptchaTokens = async () => {
|
|
575
|
+
const [checkEligibility, newBasket, registerUser] = await Promise.all([
|
|
576
|
+
getRecaptchaToken("checkEligibility"),
|
|
577
|
+
getRecaptchaToken("newBasket"),
|
|
578
|
+
getRecaptchaToken("registerUser")
|
|
579
|
+
]);
|
|
580
|
+
return { checkEligibility, newBasket, registerUser };
|
|
581
|
+
};
|
|
80
582
|
|
|
81
583
|
// src/apple-pay-modal.ts
|
|
82
584
|
function getRenewalDateText(unit, duration) {
|
|
@@ -128,6 +630,7 @@ function getOfferTypeText(offerType) {
|
|
|
128
630
|
}
|
|
129
631
|
var TERMS_LINK = '<a href="https://www.economistgroup.com/terms-of-use" target="_blank" rel="noopener noreferrer">Terms and Conditions</a>';
|
|
130
632
|
var PRIVACY_LINK = '<a href="https://www.economistgroup.com/privacy-policy" target="_blank" rel="noopener noreferrer">Privacy Policy</a>';
|
|
633
|
+
var MSG_RECAPTCHA_LOAD_ERROR = "Something went wrong. Please close and try again.";
|
|
131
634
|
function getCheckboxTermsHtml(isNYT, isLoggedIn, autoRenewal) {
|
|
132
635
|
const autoRenewalClause = autoRenewal ? " and auto-renewal terms above," : "";
|
|
133
636
|
if (isNYT) {
|
|
@@ -297,6 +800,19 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
297
800
|
#country = "";
|
|
298
801
|
#triggerElement = null;
|
|
299
802
|
#keyHandler = null;
|
|
803
|
+
#recaptchaTokens = null;
|
|
804
|
+
#recaptchaTokensPromise = null;
|
|
805
|
+
/**
|
|
806
|
+
* Synchronous callback invoked directly from the CTA click handler once the
|
|
807
|
+
* user confirms. The caller (ApplePayButton) must call startApplePaySession
|
|
808
|
+
* inside this callback to preserve the browser's user-gesture chain — Safari
|
|
809
|
+
* requires new ApplePaySession() to execute within the original click stack.
|
|
810
|
+
*
|
|
811
|
+
* reCAPTCHA tokens are guaranteed non-null here: the CTA remains disabled
|
|
812
|
+
* until the token pre-fetch resolves, so this callback is never reached
|
|
813
|
+
* without valid tokens.
|
|
814
|
+
*/
|
|
815
|
+
onConfirm = null;
|
|
300
816
|
set offer(value) {
|
|
301
817
|
this.#offer = value;
|
|
302
818
|
}
|
|
@@ -391,27 +907,57 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
391
907
|
}
|
|
392
908
|
});
|
|
393
909
|
}
|
|
910
|
+
if (this.#recaptchaTokensPromise) {
|
|
911
|
+
if (cta) {
|
|
912
|
+
cta.setAttribute("disabled", "");
|
|
913
|
+
cta.setAttribute("aria-disabled", "true");
|
|
914
|
+
cta.setAttribute("aria-busy", "true");
|
|
915
|
+
}
|
|
916
|
+
this.#recaptchaTokensPromise.then((tokens) => {
|
|
917
|
+
this.#recaptchaTokens = tokens;
|
|
918
|
+
if (cta) {
|
|
919
|
+
cta.removeAttribute("disabled");
|
|
920
|
+
cta.removeAttribute("aria-disabled");
|
|
921
|
+
cta.removeAttribute("aria-busy");
|
|
922
|
+
}
|
|
923
|
+
}).catch(() => {
|
|
924
|
+
console.error("[web-apple-pay] apple-pay-modal: failed to pre-fetch reCAPTCHA tokens; CTA remains disabled");
|
|
925
|
+
if (cta) {
|
|
926
|
+
cta.removeAttribute("aria-busy");
|
|
927
|
+
}
|
|
928
|
+
const existingError = this.querySelector("#apple-pay-modal-error");
|
|
929
|
+
if (existingError) {
|
|
930
|
+
const icon = existingError.querySelector("svg");
|
|
931
|
+
existingError.textContent = MSG_RECAPTCHA_LOAD_ERROR;
|
|
932
|
+
if (icon) existingError.prepend(icon);
|
|
933
|
+
existingError.classList.add("apm__error--visible");
|
|
934
|
+
} else {
|
|
935
|
+
const errorNotice = document.createElement("div");
|
|
936
|
+
errorNotice.setAttribute("role", "alert");
|
|
937
|
+
errorNotice.className = "apm__error apm__error--visible";
|
|
938
|
+
errorNotice.textContent = MSG_RECAPTCHA_LOAD_ERROR;
|
|
939
|
+
cta?.insertAdjacentElement("beforebegin", errorNotice);
|
|
940
|
+
}
|
|
941
|
+
});
|
|
942
|
+
}
|
|
394
943
|
cta?.addEventListener("click", () => {
|
|
395
944
|
if (isUS && checkbox && !checkbox.checked) {
|
|
396
945
|
errorElement?.classList.add("apm__error--visible");
|
|
397
946
|
checkboxLabel?.classList.add("apm__checkbox-label--error");
|
|
398
947
|
return;
|
|
399
948
|
}
|
|
400
|
-
this
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
detail: { skuId: offer.sku_id, country }
|
|
405
|
-
})
|
|
406
|
-
);
|
|
407
|
-
this.close();
|
|
949
|
+
if (this.#recaptchaTokens) {
|
|
950
|
+
this.onConfirm?.(this.#recaptchaTokens);
|
|
951
|
+
this.close();
|
|
952
|
+
}
|
|
408
953
|
});
|
|
409
954
|
this.querySelector("#apple-pay-modal-close")?.addEventListener(
|
|
410
955
|
"click",
|
|
411
956
|
() => this.close()
|
|
412
957
|
);
|
|
413
958
|
}
|
|
414
|
-
open() {
|
|
959
|
+
open(recaptchaTokensPromise) {
|
|
960
|
+
this.#recaptchaTokensPromise = recaptchaTokensPromise ?? getPurchaseRecaptchaTokens();
|
|
415
961
|
this.#triggerElement = document.activeElement;
|
|
416
962
|
document.body.appendChild(this);
|
|
417
963
|
this.#keyHandler = (event) => {
|
|
@@ -524,10 +1070,6 @@ var apple_pay_button_template_default = `<style>
|
|
|
524
1070
|
</div>
|
|
525
1071
|
`;
|
|
526
1072
|
|
|
527
|
-
// src/clients/payment-checkout/config.ts
|
|
528
|
-
var runtimeConfig = null;
|
|
529
|
-
var getApplePayConfig = () => runtimeConfig;
|
|
530
|
-
|
|
531
1073
|
// src/clients/payment-checkout/detects.ts
|
|
532
1074
|
var isAppleDeviceOrSafari = () => {
|
|
533
1075
|
const isIOSOrMacTouchDevice = /iPad|iPhone|iPod/.test(navigator.userAgent) || navigator.maxTouchPoints > 1 && /Macintosh/.test(navigator.userAgent);
|
|
@@ -536,27 +1078,21 @@ var isAppleDeviceOrSafari = () => {
|
|
|
536
1078
|
};
|
|
537
1079
|
var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null && getApplePayConfig()?.debugBypassEnabled === true;
|
|
538
1080
|
var isApplePayAvailable = async () => {
|
|
539
|
-
console.log("[web-apple-pay] isApplePayAvailable: checking availability");
|
|
540
1081
|
if (bypassApplePayChecks()) {
|
|
541
|
-
console.log("[web-apple-pay] isApplePayAvailable: bypass active \u2192 true");
|
|
542
1082
|
return true;
|
|
543
1083
|
}
|
|
544
1084
|
if (!isAppleDeviceOrSafari()) {
|
|
545
|
-
console.log("[web-apple-pay] isApplePayAvailable: not Apple device or Safari \u2192 false");
|
|
546
1085
|
return false;
|
|
547
1086
|
}
|
|
548
1087
|
const applePaySession = window.ApplePaySession;
|
|
549
1088
|
if (!applePaySession) {
|
|
550
|
-
console.log("[web-apple-pay] isApplePayAvailable: ApplePaySession not found on window \u2192 false");
|
|
551
1089
|
return false;
|
|
552
1090
|
}
|
|
553
1091
|
try {
|
|
554
1092
|
if (typeof applePaySession.canMakePayments !== "function") {
|
|
555
|
-
console.log("[web-apple-pay] isApplePayAvailable: canMakePayments is not a function \u2192 false");
|
|
556
1093
|
return false;
|
|
557
1094
|
}
|
|
558
1095
|
if (!applePaySession.canMakePayments()) {
|
|
559
|
-
console.log("[web-apple-pay] isApplePayAvailable: canMakePayments() returned false \u2192 false");
|
|
560
1096
|
return false;
|
|
561
1097
|
}
|
|
562
1098
|
const merchantId = getApplePayConfig()?.merchantId;
|
|
@@ -566,10 +1102,8 @@ var isApplePayAvailable = async () => {
|
|
|
566
1102
|
);
|
|
567
1103
|
return false;
|
|
568
1104
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
console.log("[web-apple-pay] isApplePayAvailable: canMakePaymentsWithActiveCard \u2192", result);
|
|
572
|
-
return result;
|
|
1105
|
+
const hasActiveCard = await applePaySession.canMakePaymentsWithActiveCard(merchantId);
|
|
1106
|
+
return hasActiveCard;
|
|
573
1107
|
} catch (error) {
|
|
574
1108
|
console.error("Error checking Apple Pay active card status:", error);
|
|
575
1109
|
return false;
|
|
@@ -617,11 +1151,18 @@ var apiFetch = async (path, options = {}) => {
|
|
|
617
1151
|
});
|
|
618
1152
|
if (!response.ok) {
|
|
619
1153
|
const contentType = response.headers.get("content-type") || "";
|
|
1154
|
+
console.warn("[web-apple-pay] apiFetch: non-2xx response", {
|
|
1155
|
+
path,
|
|
1156
|
+
status: response.status,
|
|
1157
|
+
contentType
|
|
1158
|
+
});
|
|
620
1159
|
if (contentType.includes("application/json")) {
|
|
621
1160
|
const errorBody = await response.json();
|
|
1161
|
+
console.warn("[web-apple-pay] apiFetch: parsed JSON error body", { errorBody });
|
|
622
1162
|
throw new ApiError(response.status, errorBody);
|
|
623
1163
|
}
|
|
624
1164
|
const errorText = await response.text();
|
|
1165
|
+
console.warn("[web-apple-pay] apiFetch: non-JSON error body", { errorText });
|
|
625
1166
|
throw new Error(
|
|
626
1167
|
`API request failed: ${response.status} ${response.statusText} - ${errorText}`
|
|
627
1168
|
);
|
|
@@ -659,6 +1200,22 @@ var getExpressCheckoutSession = () => {
|
|
|
659
1200
|
};
|
|
660
1201
|
|
|
661
1202
|
// src/utils/expressCheckoutRedirect.ts
|
|
1203
|
+
var CHECKOUT_REDIRECT_KEY = "checkout-redirect";
|
|
1204
|
+
var ALLOWED_REDIRECT_HOSTNAME = "economist.com";
|
|
1205
|
+
var writeCheckoutRedirect = (originUrl) => {
|
|
1206
|
+
if (originUrl.startsWith("/")) {
|
|
1207
|
+
localStorage.setItem(CHECKOUT_REDIRECT_KEY, originUrl);
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
try {
|
|
1211
|
+
const { hostname } = new URL(originUrl);
|
|
1212
|
+
const isAllowed = hostname === ALLOWED_REDIRECT_HOSTNAME || hostname.endsWith(`.${ALLOWED_REDIRECT_HOSTNAME}`);
|
|
1213
|
+
if (isAllowed) {
|
|
1214
|
+
localStorage.setItem(CHECKOUT_REDIRECT_KEY, originUrl);
|
|
1215
|
+
}
|
|
1216
|
+
} catch {
|
|
1217
|
+
}
|
|
1218
|
+
};
|
|
662
1219
|
var captureOriginUrl = () => window.location.href;
|
|
663
1220
|
var buildQueryString = () => {
|
|
664
1221
|
const countryCode = new URLSearchParams(window.location.search).get(
|
|
@@ -683,68 +1240,19 @@ var redirectToCheckoutComplete = (params) => {
|
|
|
683
1240
|
url.searchParams.set("lmo_offer", params.skuId);
|
|
684
1241
|
url.searchParams.set("status", params.status);
|
|
685
1242
|
url.searchParams.set("redirectToCheckout", "true");
|
|
686
|
-
url.searchParams.set("returnUrl", params.returnUrl);
|
|
687
1243
|
return url.pathname + url.search;
|
|
688
1244
|
};
|
|
689
1245
|
var redirectToCheckoutFallback = (params) => {
|
|
690
1246
|
const url = new URL(window.location.href);
|
|
691
1247
|
url.pathname = "/checkout";
|
|
692
|
-
url.searchParams.set("lmo_offer", params.skuId);
|
|
1248
|
+
url.searchParams.set("lmo_offer", decodeSku(params.skuId));
|
|
693
1249
|
if (params.inlineError)
|
|
694
1250
|
url.searchParams.set("inlineError", params.inlineError);
|
|
695
1251
|
if (params.email) url.searchParams.set("email", params.email);
|
|
696
1252
|
if (params.emailError) url.searchParams.set("emailError", params.emailError);
|
|
697
|
-
url.searchParams.set("returnUrl", params.returnUrl);
|
|
698
1253
|
return url.pathname + url.search;
|
|
699
1254
|
};
|
|
700
1255
|
|
|
701
|
-
// src/utils/recaptcha.ts
|
|
702
|
-
var loadRecaptchaScript = (siteKey) => {
|
|
703
|
-
return new Promise((resolve, reject) => {
|
|
704
|
-
if (typeof grecaptcha !== "undefined") return resolve();
|
|
705
|
-
const existingScript = document.querySelector(
|
|
706
|
-
'script[src*="recaptcha/api.js"]'
|
|
707
|
-
);
|
|
708
|
-
if (existingScript) {
|
|
709
|
-
existingScript.addEventListener("load", () => resolve());
|
|
710
|
-
existingScript.addEventListener(
|
|
711
|
-
"error",
|
|
712
|
-
() => reject(new Error("Failed to load existing reCAPTCHA script"))
|
|
713
|
-
);
|
|
714
|
-
return;
|
|
715
|
-
}
|
|
716
|
-
const script = document.createElement("script");
|
|
717
|
-
script.src = `https://www.google.com/recaptcha/api.js?render=${siteKey}`;
|
|
718
|
-
script.async = true;
|
|
719
|
-
script.defer = true;
|
|
720
|
-
script.onload = () => resolve();
|
|
721
|
-
script.onerror = () => reject(new Error("Failed to load reCAPTCHA script"));
|
|
722
|
-
document.head.appendChild(script);
|
|
723
|
-
});
|
|
724
|
-
};
|
|
725
|
-
var getRecaptchaToken = async (action) => {
|
|
726
|
-
const siteKey = getApplePayConfig()?.recaptchaSiteKey;
|
|
727
|
-
if (!siteKey) {
|
|
728
|
-
throw new Error(
|
|
729
|
-
"[web-apple-pay] Missing recaptchaSiteKey. Call configureApplePay({ recaptchaSiteKey, ... }) before initiating Apple Pay."
|
|
730
|
-
);
|
|
731
|
-
}
|
|
732
|
-
await loadRecaptchaScript(siteKey);
|
|
733
|
-
return new Promise((resolve, reject) => {
|
|
734
|
-
grecaptcha.ready(() => {
|
|
735
|
-
grecaptcha.execute(siteKey, { action }).then(resolve).catch(reject);
|
|
736
|
-
});
|
|
737
|
-
});
|
|
738
|
-
};
|
|
739
|
-
var getPurchaseRecaptchaTokens = async () => {
|
|
740
|
-
const [checkEligibility, newBasket, registerUser] = await Promise.all([
|
|
741
|
-
getRecaptchaToken("checkEligibility"),
|
|
742
|
-
getRecaptchaToken("newBasket"),
|
|
743
|
-
getRecaptchaToken("registerUser")
|
|
744
|
-
]);
|
|
745
|
-
return { checkEligibility, newBasket, registerUser };
|
|
746
|
-
};
|
|
747
|
-
|
|
748
1256
|
// src/utils/expressCheckoutTracking.ts
|
|
749
1257
|
var EXPRESS_CHECKOUT_EVENTS = {
|
|
750
1258
|
/** Wallet button displayed */
|
|
@@ -831,72 +1339,39 @@ var MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
|
|
|
831
1339
|
|
|
832
1340
|
// src/clients/payment-checkout/apple-session.ts
|
|
833
1341
|
function redirectToFallbackCheckout(config) {
|
|
834
|
-
|
|
835
|
-
skuId: config.skuId,
|
|
836
|
-
returnUrl: config.returnUrl,
|
|
837
|
-
inlineError: MSG_PAYMENT_GENERIC_ERROR
|
|
838
|
-
});
|
|
1342
|
+
writeCheckoutRedirect(config.returnUrl);
|
|
839
1343
|
trackFallbackCheckout(config.skuId);
|
|
840
1344
|
window.location.href = redirectToCheckoutFallback({
|
|
841
1345
|
skuId: config.skuId,
|
|
842
|
-
returnUrl: config.returnUrl,
|
|
843
1346
|
inlineError: MSG_PAYMENT_GENERIC_ERROR
|
|
844
1347
|
});
|
|
845
1348
|
}
|
|
846
1349
|
async function handleMerchantValidation(event, session, config) {
|
|
847
|
-
console.log("[web-apple-pay] handleMerchantValidation: start", {
|
|
848
|
-
skuId: config.skuId,
|
|
849
|
-
validationURL: event.validationURL
|
|
850
|
-
});
|
|
851
1350
|
try {
|
|
852
1351
|
const validationHost = new URL(event.validationURL).host;
|
|
853
|
-
console.log("[web-apple-pay] handleMerchantValidation: host parsed", {
|
|
854
|
-
validationHost
|
|
855
|
-
});
|
|
856
1352
|
if (!validationHost.endsWith("apple.com")) {
|
|
857
|
-
console.warn(
|
|
858
|
-
"[web-apple-pay] handleMerchantValidation: invalid validation host",
|
|
859
|
-
{ validationHost }
|
|
860
|
-
);
|
|
861
1353
|
session.abort();
|
|
862
1354
|
redirectToFallbackCheckout(config);
|
|
863
1355
|
return;
|
|
864
1356
|
}
|
|
865
|
-
console.log("[web-apple-pay] handleMerchantValidation: requesting recaptcha token");
|
|
866
1357
|
const recaptchaToken = await getRecaptchaToken("verifyMerchant");
|
|
867
|
-
console.log("[web-apple-pay] handleMerchantValidation: recaptcha token received");
|
|
868
1358
|
const response = await validateAppleMerchant({
|
|
869
1359
|
validationURL: event.validationURL,
|
|
870
1360
|
recaptchaToken
|
|
871
1361
|
});
|
|
872
1362
|
if (!response?.merchantSession) {
|
|
873
|
-
console.warn(
|
|
874
|
-
"[web-apple-pay] handleMerchantValidation: missing merchantSession in response"
|
|
875
|
-
);
|
|
876
1363
|
session.abort();
|
|
877
1364
|
redirectToFallbackCheckout(config);
|
|
878
1365
|
return;
|
|
879
1366
|
}
|
|
880
|
-
console.log("[web-apple-pay] handleMerchantValidation: completing merchant validation");
|
|
881
1367
|
session.completeMerchantValidation(response.merchantSession);
|
|
882
|
-
console.log("[web-apple-pay] handleMerchantValidation: success");
|
|
883
1368
|
} catch (err) {
|
|
884
|
-
console.error("[web-apple-pay]
|
|
885
|
-
skuId: config.skuId,
|
|
886
|
-
err
|
|
887
|
-
});
|
|
1369
|
+
console.error("[web-apple-pay] onvalidatemerchant: error during validation", err);
|
|
888
1370
|
session.abort();
|
|
889
1371
|
redirectToFallbackCheckout(config);
|
|
890
1372
|
}
|
|
891
1373
|
}
|
|
892
1374
|
function startApplePaySession(config) {
|
|
893
|
-
console.log("[web-apple-pay] startApplePaySession", {
|
|
894
|
-
skuId: config.skuId,
|
|
895
|
-
countryCode: config.countryCode,
|
|
896
|
-
currencyCode: config.currencyCode,
|
|
897
|
-
totalAmount: config.totalAmount,
|
|
898
|
-
userLoggedIn: config.userLoggedIn
|
|
899
|
-
});
|
|
900
1375
|
const paymentRequest = {
|
|
901
1376
|
countryCode: config.countryCode,
|
|
902
1377
|
currencyCode: config.currencyCode,
|
|
@@ -910,43 +1385,22 @@ function startApplePaySession(config) {
|
|
|
910
1385
|
// Only request email from Apple Pay sheet if user is NOT logged in
|
|
911
1386
|
...!config.userLoggedIn && { requiredShippingContactFields: ["email"] }
|
|
912
1387
|
};
|
|
913
|
-
console.log("[web-apple-pay] startApplePaySession: payment request created", {
|
|
914
|
-
skuId: config.skuId,
|
|
915
|
-
requiredShippingContactFields: paymentRequest.requiredShippingContactFields
|
|
916
|
-
});
|
|
917
1388
|
try {
|
|
918
1389
|
const session = new ApplePaySession(3, paymentRequest);
|
|
919
|
-
console.log("[web-apple-pay] startApplePaySession: ApplePaySession created");
|
|
920
1390
|
session.onvalidatemerchant = async (event) => {
|
|
921
|
-
console.log("[web-apple-pay] onvalidatemerchant: invoked", {
|
|
922
|
-
skuId: config.skuId
|
|
923
|
-
});
|
|
924
1391
|
await handleMerchantValidation(event, session, config);
|
|
925
1392
|
};
|
|
926
1393
|
session.onpaymentauthorized = (event) => {
|
|
927
|
-
console.log("[web-apple-pay] onpaymentauthorized: invoked", {
|
|
928
|
-
skuId: config.skuId
|
|
929
|
-
});
|
|
930
1394
|
trackWalletPaymentAuthorised(config.skuId);
|
|
931
1395
|
config.onPaymentAuthorized(event, session);
|
|
932
1396
|
};
|
|
933
1397
|
session.oncancel = () => {
|
|
934
|
-
console.log("[web-apple-pay] oncancel: invoked", {
|
|
935
|
-
skuId: config.skuId
|
|
936
|
-
});
|
|
937
1398
|
config.onCancel?.();
|
|
938
1399
|
};
|
|
939
|
-
console.log("[web-apple-pay] startApplePaySession: opening payment sheet", {
|
|
940
|
-
skuId: config.skuId
|
|
941
|
-
});
|
|
942
1400
|
trackWalletPaymentSheetOpen(config.skuId);
|
|
943
1401
|
session.begin();
|
|
944
|
-
console.log("[web-apple-pay] startApplePaySession: session begin called");
|
|
945
1402
|
} catch (err) {
|
|
946
|
-
console.error("[web-apple-pay] startApplePaySession: failed to
|
|
947
|
-
skuId: config.skuId,
|
|
948
|
-
err
|
|
949
|
-
});
|
|
1403
|
+
console.error("[web-apple-pay] startApplePaySession: failed to create session", err);
|
|
950
1404
|
redirectToFallbackCheckout(config);
|
|
951
1405
|
}
|
|
952
1406
|
}
|
|
@@ -1037,25 +1491,13 @@ var billingContactDetails = (contact, supportedBillingCountries) => {
|
|
|
1037
1491
|
// src/clients/payment-checkout/shipping-contact-details.ts
|
|
1038
1492
|
var EMAIL_FORMAT_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1039
1493
|
var shippingContactDetails = (shippingContact) => {
|
|
1040
|
-
console.log("[web-apple-pay] shippingContactDetails: start", {
|
|
1041
|
-
hasShippingContact: Boolean(shippingContact),
|
|
1042
|
-
hasEmailAddress: shippingContact?.emailAddress !== void 0
|
|
1043
|
-
});
|
|
1044
1494
|
const errors = [];
|
|
1045
1495
|
let emailAddress = "";
|
|
1046
1496
|
if (shippingContact?.emailAddress === void 0 || shippingContact?.emailAddress === "") {
|
|
1047
|
-
console.log(
|
|
1048
|
-
"[web-apple-pay] shippingContactDetails: email missing or empty"
|
|
1049
|
-
);
|
|
1050
1497
|
pushRequiredFieldError(errors, "shipping", "emailAddress");
|
|
1051
1498
|
} else {
|
|
1052
|
-
console.log("[web-apple-pay] shippingContactDetails: email provided");
|
|
1053
1499
|
emailAddress = shippingContact.emailAddress.trim();
|
|
1054
|
-
console.log("[web-apple-pay] shippingContactDetails: email trimmed", {
|
|
1055
|
-
emailLength: emailAddress.length
|
|
1056
|
-
});
|
|
1057
1500
|
if (emailAddress.length > 80) {
|
|
1058
|
-
console.log("[web-apple-pay] shippingContactDetails: email too long");
|
|
1059
1501
|
pushFieldError(
|
|
1060
1502
|
errors,
|
|
1061
1503
|
"shipping",
|
|
@@ -1064,9 +1506,6 @@ var shippingContactDetails = (shippingContact) => {
|
|
|
1064
1506
|
MSG_EMAIL_TOO_LONG
|
|
1065
1507
|
);
|
|
1066
1508
|
} else if (!EMAIL_FORMAT_REGEX.test(emailAddress)) {
|
|
1067
|
-
console.log(
|
|
1068
|
-
"[web-apple-pay] shippingContactDetails: email format invalid"
|
|
1069
|
-
);
|
|
1070
1509
|
pushFieldError(
|
|
1071
1510
|
errors,
|
|
1072
1511
|
"shipping",
|
|
@@ -1074,17 +1513,11 @@ var shippingContactDetails = (shippingContact) => {
|
|
|
1074
1513
|
"format",
|
|
1075
1514
|
MSG_EMAIL_WRONG_FORMAT
|
|
1076
1515
|
);
|
|
1077
|
-
} else {
|
|
1078
|
-
console.log("[web-apple-pay] shippingContactDetails: email valid");
|
|
1079
1516
|
}
|
|
1080
1517
|
}
|
|
1081
1518
|
if (errors.length > 0) {
|
|
1082
|
-
console.log("[web-apple-pay] shippingContactDetails: throwing validation error", {
|
|
1083
|
-
errorCount: errors.length
|
|
1084
|
-
});
|
|
1085
1519
|
throw new ContactValidationError(errors);
|
|
1086
1520
|
}
|
|
1087
|
-
console.log("[web-apple-pay] shippingContactDetails: success");
|
|
1088
1521
|
return { emailAddress };
|
|
1089
1522
|
};
|
|
1090
1523
|
|
|
@@ -1099,9 +1532,15 @@ var failApplePaySession = (session, errors = []) => {
|
|
|
1099
1532
|
};
|
|
1100
1533
|
function parseWalletApiError(err) {
|
|
1101
1534
|
const body = err instanceof ApiError ? err.body : err;
|
|
1535
|
+
console.warn("[web-apple-pay] parseWalletApiError: raw error", {
|
|
1536
|
+
isApiError: err instanceof ApiError,
|
|
1537
|
+
body
|
|
1538
|
+
});
|
|
1102
1539
|
if (body && typeof body === "object" && "errorType" in body && "message" in body) {
|
|
1540
|
+
console.warn("[web-apple-pay] parseWalletApiError: structured BFF error", body);
|
|
1103
1541
|
return body;
|
|
1104
1542
|
}
|
|
1543
|
+
console.warn("[web-apple-pay] parseWalletApiError: unstructured error \u2014 no errorType found");
|
|
1105
1544
|
return null;
|
|
1106
1545
|
}
|
|
1107
1546
|
function validateContacts(event, sessionData, supportedBillingCountries) {
|
|
@@ -1115,10 +1554,10 @@ function validateContacts(event, sessionData, supportedBillingCountries) {
|
|
|
1115
1554
|
return void 0;
|
|
1116
1555
|
}
|
|
1117
1556
|
function redirectFallback(skuId, returnUrl, options) {
|
|
1557
|
+
writeCheckoutRedirect(returnUrl);
|
|
1118
1558
|
trackFallbackCheckout(skuId);
|
|
1119
1559
|
window.location.href = redirectToCheckoutFallback({
|
|
1120
1560
|
skuId,
|
|
1121
|
-
returnUrl,
|
|
1122
1561
|
...options
|
|
1123
1562
|
});
|
|
1124
1563
|
}
|
|
@@ -1146,58 +1585,49 @@ function handleStructuredError(ctx) {
|
|
|
1146
1585
|
async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
1147
1586
|
const { skuId, returnUrl, queryString } = config;
|
|
1148
1587
|
let validatedEmail;
|
|
1149
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: starting", { skuId, userLoggedIn: sessionData.loggedIn });
|
|
1150
1588
|
try {
|
|
1151
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: step 1 \u2014 validating contacts");
|
|
1152
1589
|
validatedEmail = validateContacts(
|
|
1153
1590
|
event,
|
|
1154
1591
|
sessionData,
|
|
1155
1592
|
config.supportedBillingCountries
|
|
1156
1593
|
);
|
|
1157
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: contacts valid", { validatedEmail });
|
|
1158
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: step 2 \u2014 calling performPurchase", { skuId });
|
|
1159
1594
|
const result = await performPurchase({
|
|
1160
1595
|
applePayToken: event.payment.token,
|
|
1161
1596
|
skuId: config.skuId,
|
|
1597
|
+
currency: config.currency,
|
|
1162
1598
|
billingContact: event.payment.billingContact,
|
|
1163
1599
|
...event.payment.shippingContact && {
|
|
1164
1600
|
shippingContact: event.payment.shippingContact
|
|
1165
1601
|
},
|
|
1166
1602
|
recaptchaTokens: config.recaptchaTokens
|
|
1167
1603
|
});
|
|
1168
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: performPurchase succeeded", { basketId: result.basketId });
|
|
1169
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: step 3 \u2014 completing payment and redirecting");
|
|
1170
1604
|
trackWalletPaymentSuccess(skuId, result.basketId);
|
|
1171
1605
|
session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
|
|
1172
1606
|
const userStatus = sessionData.loggedIn ? "lex" : "nex";
|
|
1607
|
+
writeCheckoutRedirect(returnUrl);
|
|
1173
1608
|
window.location.href = redirectToCheckoutComplete({
|
|
1174
1609
|
basketId: result.basketId,
|
|
1175
1610
|
status: userStatus,
|
|
1176
1611
|
skuId: decodeSku(skuId),
|
|
1177
|
-
returnUrl,
|
|
1178
1612
|
queryString
|
|
1179
1613
|
});
|
|
1180
1614
|
} catch (err) {
|
|
1181
1615
|
if (err instanceof ContactValidationError) {
|
|
1182
|
-
console.warn("[web-apple-pay] handlePaymentAuthorized: contact validation failed", err.errors);
|
|
1183
1616
|
failApplePaySession(session, err.errors);
|
|
1184
1617
|
return;
|
|
1185
1618
|
}
|
|
1186
1619
|
console.error("[web-apple-pay] onpaymentauthorized error", { skuId, err });
|
|
1187
1620
|
const walletError = parseWalletApiError(err);
|
|
1188
|
-
console.
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
message: walletError?.message
|
|
1621
|
+
console.warn("[web-apple-pay] handlePaymentAuthorized: walletError result", {
|
|
1622
|
+
walletError,
|
|
1623
|
+
validatedEmail
|
|
1192
1624
|
});
|
|
1193
1625
|
trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
|
|
1194
1626
|
failApplePaySession(session);
|
|
1195
1627
|
if (!walletError) {
|
|
1196
|
-
console.warn("[web-apple-pay] handlePaymentAuthorized: unstructured error, using generic fallback");
|
|
1197
1628
|
redirectFallback(skuId, returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
|
|
1198
1629
|
return;
|
|
1199
1630
|
}
|
|
1200
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: dispatching structured error handler", { errorType: walletError.errorType });
|
|
1201
1631
|
handleStructuredError({ skuId, returnUrl, validatedEmail, walletError });
|
|
1202
1632
|
}
|
|
1203
1633
|
}
|
|
@@ -1519,7 +1949,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1519
1949
|
#returnUrl = "";
|
|
1520
1950
|
#supportedBillingCountries = [];
|
|
1521
1951
|
#entryPoint = "";
|
|
1522
|
-
#
|
|
1952
|
+
#recaptchaTokensPromise = null;
|
|
1523
1953
|
// ─── Public API ─────────────────────────────────────────────────────────────
|
|
1524
1954
|
get offer() {
|
|
1525
1955
|
return this.#offer;
|
|
@@ -1584,6 +2014,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1584
2014
|
this.#paymentOptions = null;
|
|
1585
2015
|
this.#returnUrl = "";
|
|
1586
2016
|
this.#supportedBillingCountries = [];
|
|
2017
|
+
this.#recaptchaTokensPromise = null;
|
|
1587
2018
|
this.querySelector(SEL_CONTAINER)?.remove();
|
|
1588
2019
|
}
|
|
1589
2020
|
// ─── Render pipeline ────────────────────────────────────────────────────────
|
|
@@ -1597,7 +2028,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1597
2028
|
this.#computeBillingCountries();
|
|
1598
2029
|
if (await this.#fetchAndCacheOptions()) return;
|
|
1599
2030
|
this.#finishLoading();
|
|
1600
|
-
} catch {
|
|
2031
|
+
} catch (err) {
|
|
2032
|
+
console.error("[web-apple-pay] #run: unexpected error, hiding button", err);
|
|
1601
2033
|
this.style.display = "none";
|
|
1602
2034
|
this.querySelector(SEL_CONTAINER)?.remove();
|
|
1603
2035
|
} finally {
|
|
@@ -1678,16 +2110,13 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1678
2110
|
if (button.classList.contains("wap-loading")) return;
|
|
1679
2111
|
this.#openModal();
|
|
1680
2112
|
});
|
|
1681
|
-
document.addEventListener("apple-pay-confirmed", (event) => {
|
|
1682
|
-
const { skuId, country } = event.detail;
|
|
1683
|
-
this.#initiateApplePay(skuId, country);
|
|
1684
|
-
}, { once: true });
|
|
1685
2113
|
}
|
|
1686
2114
|
#finishLoading() {
|
|
1687
2115
|
const button = this.querySelector(SEL_BUTTON);
|
|
1688
2116
|
if (button) {
|
|
1689
2117
|
button.classList.remove("wap-loading");
|
|
1690
2118
|
this.#updateDisabledState();
|
|
2119
|
+
this.#scheduleRecaptchaTokens();
|
|
1691
2120
|
trackExpressWalletImpression(this.#offer.sku_id, {
|
|
1692
2121
|
entryPoint: this.#entryPoint,
|
|
1693
2122
|
country: this.#country
|
|
@@ -1715,10 +2144,6 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1715
2144
|
return;
|
|
1716
2145
|
}
|
|
1717
2146
|
trackExpressWalletClick(this.#offer.sku_id);
|
|
1718
|
-
getPurchaseRecaptchaTokens().then((tokens) => {
|
|
1719
|
-
this.#recaptchaTokens = tokens;
|
|
1720
|
-
}).catch(() => {
|
|
1721
|
-
});
|
|
1722
2147
|
ApplePayModal.define();
|
|
1723
2148
|
const modal = document.createElement(ApplePayModal.tag);
|
|
1724
2149
|
modal.offer = this.#offer;
|
|
@@ -1726,7 +2151,30 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1726
2151
|
if (this.#sessionData?.loggedIn) {
|
|
1727
2152
|
modal.dataset.loggedIn = "true";
|
|
1728
2153
|
}
|
|
1729
|
-
|
|
2154
|
+
const skuId = this.#offer.sku_id;
|
|
2155
|
+
const country = this.#country;
|
|
2156
|
+
modal.onConfirm = (recaptchaTokens) => {
|
|
2157
|
+
this.#initiateApplePay(skuId, country, recaptchaTokens);
|
|
2158
|
+
this.#scheduleRecaptchaTokens();
|
|
2159
|
+
};
|
|
2160
|
+
modal.open(this.#recaptchaTokensPromise);
|
|
2161
|
+
}
|
|
2162
|
+
/**
|
|
2163
|
+
* Starts a fresh reCAPTCHA token pre-fetch and stores its promise.
|
|
2164
|
+
* If the fetch rejects, the field is cleared so the next `modal.open()` call
|
|
2165
|
+
* falls back to a new attempt via the modal's own null-fallback path — preventing
|
|
2166
|
+
* a single transient failure from permanently disabling the CTA on all retries.
|
|
2167
|
+
* The identity guard prevents a late rejection from clobbering a newer attempt.
|
|
2168
|
+
*/
|
|
2169
|
+
#scheduleRecaptchaTokens() {
|
|
2170
|
+
const attempt = getPurchaseRecaptchaTokens();
|
|
2171
|
+
attempt.catch((err) => {
|
|
2172
|
+
console.error("[web-apple-pay] reCAPTCHA token pre-fetch failed; will retry on modal open", err);
|
|
2173
|
+
if (this.#recaptchaTokensPromise === attempt) {
|
|
2174
|
+
this.#recaptchaTokensPromise = null;
|
|
2175
|
+
}
|
|
2176
|
+
});
|
|
2177
|
+
this.#recaptchaTokensPromise = attempt;
|
|
1730
2178
|
}
|
|
1731
2179
|
async #fetchSession() {
|
|
1732
2180
|
try {
|
|
@@ -1762,6 +2210,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1762
2210
|
onPaymentAuthorized: (event, session) => handlePaymentAuthorized(event, session, sessionData, {
|
|
1763
2211
|
skuId,
|
|
1764
2212
|
country,
|
|
2213
|
+
currency: offer.price?.currency_code ?? DEFAULT_CURRENCY,
|
|
1765
2214
|
returnUrl: this.#returnUrl,
|
|
1766
2215
|
queryString: buildQueryString(),
|
|
1767
2216
|
supportedBillingCountries: this.#supportedBillingCountries,
|
|
@@ -1769,11 +2218,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1769
2218
|
})
|
|
1770
2219
|
};
|
|
1771
2220
|
}
|
|
1772
|
-
|
|
2221
|
+
#initiateApplePay(skuId, country, recaptchaTokens) {
|
|
1773
2222
|
try {
|
|
1774
2223
|
const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
|
|
1775
2224
|
const paymentOptions = this.#paymentOptions;
|
|
1776
|
-
const recaptchaTokens = this.#recaptchaTokens ?? await getPurchaseRecaptchaTokens();
|
|
1777
2225
|
startApplePaySession(
|
|
1778
2226
|
this.#buildSessionParams(
|
|
1779
2227
|
skuId,
|