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