@economist/web-apple-pay 0.1.3-beta.1 → 0.1.3-beta.11
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 +659 -130
- package/dist/src/apple-pay-button.js +654 -125
- package/dist/src/apple-pay-button.stories.js +662 -133
- package/dist/src/apple-pay-modal.d.ts +16 -1
- package/dist/src/apple-pay-modal.js +536 -10
- package/dist/src/apple-pay-modal.stories.js +654 -125
- package/dist/src/clients/payment-checkout/apple-session.js +66 -49
- package/dist/src/clients/payment-checkout/detects.js +8 -1
- package/dist/src/clients/payment-checkout/index.js +1 -0
- package/dist/src/clients/payment-checkout/payment-handler.d.ts +1 -0
- package/dist/src/clients/payment-checkout/payment-handler.js +3 -0
- package/dist/src/clients/payment-checkout/types.d.ts +7 -4
- package/dist/src/index.js +659 -130
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -76,7 +76,512 @@ 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">
|
|
520
|
+
Continue with
|
|
521
|
+
<svg width="51" height="22" aria-label="Apple Pay" role="img">
|
|
522
|
+
<use href="#apple-pay-logo" />
|
|
523
|
+
</svg>
|
|
524
|
+
</button>
|
|
525
|
+
</div>
|
|
526
|
+
</div>
|
|
527
|
+
|
|
528
|
+
`;
|
|
529
|
+
|
|
530
|
+
// src/clients/payment-checkout/config.ts
|
|
531
|
+
var runtimeConfig = null;
|
|
532
|
+
var configureApplePay = (config) => {
|
|
533
|
+
runtimeConfig = {
|
|
534
|
+
...config
|
|
535
|
+
};
|
|
536
|
+
};
|
|
537
|
+
var getApplePayConfig = () => runtimeConfig;
|
|
538
|
+
|
|
539
|
+
// src/utils/recaptcha.ts
|
|
540
|
+
var loadRecaptchaScript = (siteKey) => {
|
|
541
|
+
return new Promise((resolve, reject) => {
|
|
542
|
+
if (typeof grecaptcha !== "undefined") return resolve();
|
|
543
|
+
const existingScript = document.querySelector(
|
|
544
|
+
'script[src*="recaptcha/api.js"]'
|
|
545
|
+
);
|
|
546
|
+
if (existingScript) {
|
|
547
|
+
existingScript.addEventListener("load", () => resolve());
|
|
548
|
+
existingScript.addEventListener(
|
|
549
|
+
"error",
|
|
550
|
+
() => reject(new Error("Failed to load existing reCAPTCHA script"))
|
|
551
|
+
);
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
const script = document.createElement("script");
|
|
555
|
+
script.src = `https://www.google.com/recaptcha/api.js?render=${siteKey}`;
|
|
556
|
+
script.async = true;
|
|
557
|
+
script.defer = true;
|
|
558
|
+
script.onload = () => resolve();
|
|
559
|
+
script.onerror = () => reject(new Error("Failed to load reCAPTCHA script"));
|
|
560
|
+
document.head.appendChild(script);
|
|
561
|
+
});
|
|
562
|
+
};
|
|
563
|
+
var getRecaptchaToken = async (action) => {
|
|
564
|
+
const siteKey = getApplePayConfig()?.recaptchaSiteKey;
|
|
565
|
+
if (!siteKey) {
|
|
566
|
+
throw new Error(
|
|
567
|
+
"[web-apple-pay] Missing recaptchaSiteKey. Call configureApplePay({ recaptchaSiteKey, ... }) before initiating Apple Pay."
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
await loadRecaptchaScript(siteKey);
|
|
571
|
+
return new Promise((resolve, reject) => {
|
|
572
|
+
grecaptcha.ready(() => {
|
|
573
|
+
grecaptcha.execute(siteKey, { action }).then(resolve).catch(reject);
|
|
574
|
+
});
|
|
575
|
+
});
|
|
576
|
+
};
|
|
577
|
+
var getPurchaseRecaptchaTokens = async () => {
|
|
578
|
+
const [checkEligibility, newBasket, registerUser] = await Promise.all([
|
|
579
|
+
getRecaptchaToken("checkEligibility"),
|
|
580
|
+
getRecaptchaToken("newBasket"),
|
|
581
|
+
getRecaptchaToken("registerUser")
|
|
582
|
+
]);
|
|
583
|
+
return { checkEligibility, newBasket, registerUser };
|
|
584
|
+
};
|
|
80
585
|
|
|
81
586
|
// src/apple-pay-modal.ts
|
|
82
587
|
function getRenewalDateText(unit, duration) {
|
|
@@ -297,6 +802,19 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
297
802
|
#country = "";
|
|
298
803
|
#triggerElement = null;
|
|
299
804
|
#keyHandler = null;
|
|
805
|
+
#recaptchaTokens = null;
|
|
806
|
+
#recaptchaTokensPromise = null;
|
|
807
|
+
/**
|
|
808
|
+
* Synchronous callback invoked directly from the CTA click handler once the
|
|
809
|
+
* user confirms. The caller (ApplePayButton) must call startApplePaySession
|
|
810
|
+
* inside this callback to preserve the browser's user-gesture chain — Safari
|
|
811
|
+
* requires new ApplePaySession() to execute within the original click stack.
|
|
812
|
+
*
|
|
813
|
+
* reCAPTCHA tokens are guaranteed non-null here: the CTA remains disabled
|
|
814
|
+
* until the token pre-fetch resolves, so this callback is never reached
|
|
815
|
+
* without valid tokens.
|
|
816
|
+
*/
|
|
817
|
+
onConfirm = null;
|
|
300
818
|
set offer(value) {
|
|
301
819
|
this.#offer = value;
|
|
302
820
|
}
|
|
@@ -317,7 +835,6 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
317
835
|
const offer = this.#offer;
|
|
318
836
|
const country = this.#country;
|
|
319
837
|
if (!offer) {
|
|
320
|
-
console.error("ApplePayModal: Missing or invalid offer data");
|
|
321
838
|
this.remove();
|
|
322
839
|
return;
|
|
323
840
|
}
|
|
@@ -392,19 +909,32 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
392
909
|
}
|
|
393
910
|
});
|
|
394
911
|
}
|
|
912
|
+
if (this.#recaptchaTokensPromise) {
|
|
913
|
+
if (cta) {
|
|
914
|
+
cta.setAttribute("disabled", "");
|
|
915
|
+
cta.setAttribute("aria-disabled", "true");
|
|
916
|
+
cta.setAttribute("aria-busy", "true");
|
|
917
|
+
}
|
|
918
|
+
this.#recaptchaTokensPromise.then((tokens) => {
|
|
919
|
+
this.#recaptchaTokens = tokens;
|
|
920
|
+
if (cta) {
|
|
921
|
+
cta.removeAttribute("disabled");
|
|
922
|
+
cta.removeAttribute("aria-disabled");
|
|
923
|
+
cta.removeAttribute("aria-busy");
|
|
924
|
+
}
|
|
925
|
+
}).catch(() => {
|
|
926
|
+
console.error("[web-apple-pay] apple-pay-modal: failed to pre-fetch reCAPTCHA tokens; CTA remains disabled");
|
|
927
|
+
});
|
|
928
|
+
}
|
|
395
929
|
cta?.addEventListener("click", () => {
|
|
396
930
|
if (isUS && checkbox && !checkbox.checked) {
|
|
397
931
|
errorElement?.classList.add("apm__error--visible");
|
|
398
932
|
checkboxLabel?.classList.add("apm__checkbox-label--error");
|
|
399
933
|
return;
|
|
400
934
|
}
|
|
401
|
-
this
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
composed: true,
|
|
405
|
-
detail: { skuId: offer.sku_id, country }
|
|
406
|
-
})
|
|
407
|
-
);
|
|
935
|
+
if (this.#recaptchaTokens) {
|
|
936
|
+
this.onConfirm?.(this.#recaptchaTokens);
|
|
937
|
+
}
|
|
408
938
|
this.close();
|
|
409
939
|
});
|
|
410
940
|
this.querySelector("#apple-pay-modal-close")?.addEventListener(
|
|
@@ -412,7 +942,8 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
412
942
|
() => this.close()
|
|
413
943
|
);
|
|
414
944
|
}
|
|
415
|
-
open() {
|
|
945
|
+
open(recaptchaTokensPromise) {
|
|
946
|
+
this.#recaptchaTokensPromise = recaptchaTokensPromise ?? getPurchaseRecaptchaTokens();
|
|
416
947
|
this.#triggerElement = document.activeElement;
|
|
417
948
|
document.body.appendChild(this);
|
|
418
949
|
this.#keyHandler = (event) => {
|
|
@@ -525,15 +1056,6 @@ var apple_pay_button_template_default = `<style>
|
|
|
525
1056
|
</div>
|
|
526
1057
|
`;
|
|
527
1058
|
|
|
528
|
-
// src/clients/payment-checkout/config.ts
|
|
529
|
-
var runtimeConfig = null;
|
|
530
|
-
var configureApplePay = (config) => {
|
|
531
|
-
runtimeConfig = {
|
|
532
|
-
...config
|
|
533
|
-
};
|
|
534
|
-
};
|
|
535
|
-
var getApplePayConfig = () => runtimeConfig;
|
|
536
|
-
|
|
537
1059
|
// src/clients/payment-checkout/detects.ts
|
|
538
1060
|
var isAppleDeviceOrSafari = () => {
|
|
539
1061
|
const isIOSOrMacTouchDevice = /iPad|iPhone|iPod/.test(navigator.userAgent) || navigator.maxTouchPoints > 1 && /Macintosh/.test(navigator.userAgent);
|
|
@@ -543,20 +1065,25 @@ var isAppleDeviceOrSafari = () => {
|
|
|
543
1065
|
var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null && getApplePayConfig()?.debugBypassEnabled === true;
|
|
544
1066
|
var isApplePayAvailable = async () => {
|
|
545
1067
|
if (bypassApplePayChecks()) {
|
|
1068
|
+
console.debug("[web-apple-pay] isApplePayAvailable: bypass enabled, returning true");
|
|
546
1069
|
return true;
|
|
547
1070
|
}
|
|
548
1071
|
if (!isAppleDeviceOrSafari()) {
|
|
1072
|
+
console.debug("[web-apple-pay] isApplePayAvailable: not an Apple device or Safari", { userAgent: navigator.userAgent });
|
|
549
1073
|
return false;
|
|
550
1074
|
}
|
|
551
1075
|
const applePaySession = window.ApplePaySession;
|
|
552
1076
|
if (!applePaySession) {
|
|
1077
|
+
console.debug("[web-apple-pay] isApplePayAvailable: ApplePaySession not present on window");
|
|
553
1078
|
return false;
|
|
554
1079
|
}
|
|
555
1080
|
try {
|
|
556
1081
|
if (typeof applePaySession.canMakePayments !== "function") {
|
|
1082
|
+
console.debug("[web-apple-pay] isApplePayAvailable: canMakePayments is not a function");
|
|
557
1083
|
return false;
|
|
558
1084
|
}
|
|
559
1085
|
if (!applePaySession.canMakePayments()) {
|
|
1086
|
+
console.debug("[web-apple-pay] isApplePayAvailable: canMakePayments() returned false");
|
|
560
1087
|
return false;
|
|
561
1088
|
}
|
|
562
1089
|
const merchantId = getApplePayConfig()?.merchantId;
|
|
@@ -566,7 +1093,9 @@ var isApplePayAvailable = async () => {
|
|
|
566
1093
|
);
|
|
567
1094
|
return false;
|
|
568
1095
|
}
|
|
569
|
-
|
|
1096
|
+
const hasActiveCard = await applePaySession.canMakePaymentsWithActiveCard(merchantId);
|
|
1097
|
+
console.debug("[web-apple-pay] isApplePayAvailable: canMakePaymentsWithActiveCard =", hasActiveCard, { merchantId });
|
|
1098
|
+
return hasActiveCard;
|
|
570
1099
|
} catch (error) {
|
|
571
1100
|
console.error("Error checking Apple Pay active card status:", error);
|
|
572
1101
|
return false;
|
|
@@ -640,6 +1169,7 @@ var validateAppleMerchant = (data) => {
|
|
|
640
1169
|
});
|
|
641
1170
|
};
|
|
642
1171
|
var performPurchase = (data) => {
|
|
1172
|
+
console.log("[web-apple-pay] performPurchase request", JSON.stringify(data, null, 2));
|
|
643
1173
|
return apiFetch("/v1/wallet/purchase", {
|
|
644
1174
|
method: "POST",
|
|
645
1175
|
body: JSON.stringify(data),
|
|
@@ -695,53 +1225,6 @@ var redirectToCheckoutFallback = (params) => {
|
|
|
695
1225
|
return url.pathname + url.search;
|
|
696
1226
|
};
|
|
697
1227
|
|
|
698
|
-
// src/utils/recaptcha.ts
|
|
699
|
-
var loadRecaptchaScript = (siteKey) => {
|
|
700
|
-
return new Promise((resolve, reject) => {
|
|
701
|
-
if (typeof grecaptcha !== "undefined") return resolve();
|
|
702
|
-
const existingScript = document.querySelector(
|
|
703
|
-
'script[src*="recaptcha/api.js"]'
|
|
704
|
-
);
|
|
705
|
-
if (existingScript) {
|
|
706
|
-
existingScript.addEventListener("load", () => resolve());
|
|
707
|
-
existingScript.addEventListener(
|
|
708
|
-
"error",
|
|
709
|
-
() => reject(new Error("Failed to load existing reCAPTCHA script"))
|
|
710
|
-
);
|
|
711
|
-
return;
|
|
712
|
-
}
|
|
713
|
-
const script = document.createElement("script");
|
|
714
|
-
script.src = `https://www.google.com/recaptcha/api.js?render=${siteKey}`;
|
|
715
|
-
script.async = true;
|
|
716
|
-
script.defer = true;
|
|
717
|
-
script.onload = () => resolve();
|
|
718
|
-
script.onerror = () => reject(new Error("Failed to load reCAPTCHA script"));
|
|
719
|
-
document.head.appendChild(script);
|
|
720
|
-
});
|
|
721
|
-
};
|
|
722
|
-
var getRecaptchaToken = async (action) => {
|
|
723
|
-
const siteKey = getApplePayConfig()?.recaptchaSiteKey;
|
|
724
|
-
if (!siteKey) {
|
|
725
|
-
throw new Error(
|
|
726
|
-
"[web-apple-pay] Missing recaptchaSiteKey. Call configureApplePay({ recaptchaSiteKey, ... }) before initiating Apple Pay."
|
|
727
|
-
);
|
|
728
|
-
}
|
|
729
|
-
await loadRecaptchaScript(siteKey);
|
|
730
|
-
return new Promise((resolve, reject) => {
|
|
731
|
-
grecaptcha.ready(() => {
|
|
732
|
-
grecaptcha.execute(siteKey, { action }).then(resolve).catch(reject);
|
|
733
|
-
});
|
|
734
|
-
});
|
|
735
|
-
};
|
|
736
|
-
var getPurchaseRecaptchaTokens = async () => {
|
|
737
|
-
const [checkEligibility, newBasket, registerUser] = await Promise.all([
|
|
738
|
-
getRecaptchaToken("checkEligibility"),
|
|
739
|
-
getRecaptchaToken("newBasket"),
|
|
740
|
-
getRecaptchaToken("registerUser")
|
|
741
|
-
]);
|
|
742
|
-
return { checkEligibility, newBasket, registerUser };
|
|
743
|
-
};
|
|
744
|
-
|
|
745
1228
|
// src/utils/expressCheckoutTracking.ts
|
|
746
1229
|
var EXPRESS_CHECKOUT_EVENTS = {
|
|
747
1230
|
/** Wallet button displayed */
|
|
@@ -827,7 +1310,52 @@ var MSG_EMAIL_COLLISION = "This email address is already linked to an account. P
|
|
|
827
1310
|
var MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
|
|
828
1311
|
|
|
829
1312
|
// src/clients/payment-checkout/apple-session.ts
|
|
1313
|
+
function redirectToFallbackCheckout(config) {
|
|
1314
|
+
trackFallbackCheckout(config.skuId);
|
|
1315
|
+
window.location.href = redirectToCheckoutFallback({
|
|
1316
|
+
skuId: config.skuId,
|
|
1317
|
+
returnUrl: config.returnUrl,
|
|
1318
|
+
inlineError: MSG_PAYMENT_GENERIC_ERROR
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1321
|
+
async function handleMerchantValidation(event, session, config) {
|
|
1322
|
+
console.debug("[web-apple-pay] onvalidatemerchant: validating", { validationURL: event.validationURL });
|
|
1323
|
+
try {
|
|
1324
|
+
const validationHost = new URL(event.validationURL).host;
|
|
1325
|
+
if (!validationHost.endsWith("apple.com")) {
|
|
1326
|
+
console.debug("[web-apple-pay] onvalidatemerchant: invalid host, aborting", { validationHost });
|
|
1327
|
+
session.abort();
|
|
1328
|
+
redirectToFallbackCheckout(config);
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
const recaptchaToken = await getRecaptchaToken("verifyMerchant");
|
|
1332
|
+
const response = await validateAppleMerchant({
|
|
1333
|
+
validationURL: event.validationURL,
|
|
1334
|
+
recaptchaToken
|
|
1335
|
+
});
|
|
1336
|
+
if (!response?.merchantSession) {
|
|
1337
|
+
console.debug("[web-apple-pay] onvalidatemerchant: no merchantSession in response, aborting");
|
|
1338
|
+
session.abort();
|
|
1339
|
+
redirectToFallbackCheckout(config);
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
console.debug("[web-apple-pay] onvalidatemerchant: merchant validated successfully");
|
|
1343
|
+
session.completeMerchantValidation(response.merchantSession);
|
|
1344
|
+
} catch (err) {
|
|
1345
|
+
console.error("[web-apple-pay] onvalidatemerchant: error during validation", err);
|
|
1346
|
+
session.abort();
|
|
1347
|
+
redirectToFallbackCheckout(config);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
830
1350
|
function startApplePaySession(config) {
|
|
1351
|
+
console.debug("[web-apple-pay] startApplePaySession: starting session", {
|
|
1352
|
+
skuId: config.skuId,
|
|
1353
|
+
countryCode: config.countryCode,
|
|
1354
|
+
currencyCode: config.currencyCode,
|
|
1355
|
+
supportedNetworks: config.supportedNetworks,
|
|
1356
|
+
totalAmount: config.totalAmount,
|
|
1357
|
+
userLoggedIn: config.userLoggedIn
|
|
1358
|
+
});
|
|
831
1359
|
const paymentRequest = {
|
|
832
1360
|
countryCode: config.countryCode,
|
|
833
1361
|
currencyCode: config.currencyCode,
|
|
@@ -841,55 +1369,27 @@ function startApplePaySession(config) {
|
|
|
841
1369
|
// Only request email from Apple Pay sheet if user is NOT logged in
|
|
842
1370
|
...!config.userLoggedIn && { requiredShippingContactFields: ["email"] }
|
|
843
1371
|
};
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
session.abort();
|
|
866
|
-
window.location.href = redirectToCheckoutFallback({
|
|
867
|
-
skuId: config.skuId,
|
|
868
|
-
returnUrl: config.returnUrl,
|
|
869
|
-
inlineError: MSG_PAYMENT_GENERIC_ERROR
|
|
870
|
-
});
|
|
871
|
-
return;
|
|
872
|
-
}
|
|
873
|
-
session.completeMerchantValidation(response.merchantSession);
|
|
874
|
-
} catch {
|
|
875
|
-
trackFallbackCheckout(config.skuId);
|
|
876
|
-
session.abort();
|
|
877
|
-
window.location.href = redirectToCheckoutFallback({
|
|
878
|
-
skuId: config.skuId,
|
|
879
|
-
returnUrl: config.returnUrl,
|
|
880
|
-
inlineError: MSG_PAYMENT_GENERIC_ERROR
|
|
881
|
-
});
|
|
882
|
-
}
|
|
883
|
-
};
|
|
884
|
-
session.onpaymentauthorized = (event) => {
|
|
885
|
-
trackWalletPaymentAuthorised(config.skuId);
|
|
886
|
-
config.onPaymentAuthorized(event, session);
|
|
887
|
-
};
|
|
888
|
-
session.oncancel = () => {
|
|
889
|
-
config.onCancel?.();
|
|
890
|
-
};
|
|
891
|
-
trackWalletPaymentSheetOpen(config.skuId);
|
|
892
|
-
session.begin();
|
|
1372
|
+
try {
|
|
1373
|
+
const session = new ApplePaySession(3, paymentRequest);
|
|
1374
|
+
session.onvalidatemerchant = async (event) => {
|
|
1375
|
+
await handleMerchantValidation(event, session, config);
|
|
1376
|
+
};
|
|
1377
|
+
session.onpaymentauthorized = (event) => {
|
|
1378
|
+
console.debug("[web-apple-pay] onpaymentauthorized: payment authorised");
|
|
1379
|
+
trackWalletPaymentAuthorised(config.skuId);
|
|
1380
|
+
config.onPaymentAuthorized(event, session);
|
|
1381
|
+
};
|
|
1382
|
+
session.oncancel = () => {
|
|
1383
|
+
console.debug("[web-apple-pay] oncancel: user cancelled Apple Pay sheet");
|
|
1384
|
+
config.onCancel?.();
|
|
1385
|
+
};
|
|
1386
|
+
console.debug("[web-apple-pay] startApplePaySession: session.begin()");
|
|
1387
|
+
trackWalletPaymentSheetOpen(config.skuId);
|
|
1388
|
+
session.begin();
|
|
1389
|
+
} catch (err) {
|
|
1390
|
+
console.error("[web-apple-pay] startApplePaySession: failed to create session", err);
|
|
1391
|
+
redirectToFallbackCheckout(config);
|
|
1392
|
+
}
|
|
893
1393
|
}
|
|
894
1394
|
|
|
895
1395
|
// src/clients/payment-checkout/contact-validation.ts
|
|
@@ -1072,9 +1572,11 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1072
1572
|
sessionData,
|
|
1073
1573
|
config.supportedBillingCountries
|
|
1074
1574
|
);
|
|
1575
|
+
console.log("[web-apple-pay] performPurchase currency", { skuId, currency: config.currency });
|
|
1075
1576
|
const result = await performPurchase({
|
|
1076
1577
|
applePayToken: event.payment.token,
|
|
1077
1578
|
skuId: config.skuId,
|
|
1579
|
+
currency: config.currency,
|
|
1078
1580
|
billingContact: event.payment.billingContact,
|
|
1079
1581
|
...event.payment.shippingContact && {
|
|
1080
1582
|
shippingContact: event.payment.shippingContact
|
|
@@ -1390,11 +1892,15 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1390
1892
|
* Creates and configures an ApplePayButton if the feature flag is present.
|
|
1391
1893
|
*/
|
|
1392
1894
|
static createIfEnabled(offer, country, entryPoint) {
|
|
1393
|
-
if (!offer)
|
|
1394
|
-
|
|
1895
|
+
if (!offer) {
|
|
1896
|
+
return null;
|
|
1897
|
+
}
|
|
1898
|
+
if (APPLE_PAY_EXCLUDED_VARIANTS.includes(offer.product?.variant)) {
|
|
1395
1899
|
return null;
|
|
1396
|
-
|
|
1900
|
+
}
|
|
1901
|
+
if (!new URLSearchParams(window.location.search).has(FEATURE_APPLE_PAY_EXPRESS_CHECKOUT)) {
|
|
1397
1902
|
return null;
|
|
1903
|
+
}
|
|
1398
1904
|
_ApplePayButton.define();
|
|
1399
1905
|
const button = document.createElement(_ApplePayButton.tag);
|
|
1400
1906
|
button.offer = offer;
|
|
@@ -1421,6 +1927,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1421
1927
|
#returnUrl = "";
|
|
1422
1928
|
#supportedBillingCountries = [];
|
|
1423
1929
|
#entryPoint = "";
|
|
1930
|
+
#recaptchaTokensPromise = null;
|
|
1424
1931
|
// ─── Public API ─────────────────────────────────────────────────────────────
|
|
1425
1932
|
get offer() {
|
|
1426
1933
|
return this.#offer;
|
|
@@ -1485,10 +1992,12 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1485
1992
|
this.#paymentOptions = null;
|
|
1486
1993
|
this.#returnUrl = "";
|
|
1487
1994
|
this.#supportedBillingCountries = [];
|
|
1995
|
+
this.#recaptchaTokensPromise = null;
|
|
1488
1996
|
this.querySelector(SEL_CONTAINER)?.remove();
|
|
1489
1997
|
}
|
|
1490
1998
|
// ─── Render pipeline ────────────────────────────────────────────────────────
|
|
1491
1999
|
async #run() {
|
|
2000
|
+
console.debug("[web-apple-pay] #run: start");
|
|
1492
2001
|
try {
|
|
1493
2002
|
if (await this.#checkAvailability()) return;
|
|
1494
2003
|
if (this.#checkOffer()) return;
|
|
@@ -1498,7 +2007,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1498
2007
|
this.#computeBillingCountries();
|
|
1499
2008
|
if (await this.#fetchAndCacheOptions()) return;
|
|
1500
2009
|
this.#finishLoading();
|
|
1501
|
-
} catch {
|
|
2010
|
+
} catch (err) {
|
|
2011
|
+
console.error("[web-apple-pay] #run: unexpected error, hiding button", err);
|
|
1502
2012
|
this.style.display = "none";
|
|
1503
2013
|
this.querySelector(SEL_CONTAINER)?.remove();
|
|
1504
2014
|
} finally {
|
|
@@ -1509,9 +2019,11 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1509
2019
|
async #checkAvailability() {
|
|
1510
2020
|
const available = await isApplePayAvailable();
|
|
1511
2021
|
if (!available) {
|
|
2022
|
+
console.debug("[web-apple-pay] #checkAvailability: not available on this device/browser");
|
|
1512
2023
|
this.style.display = "none";
|
|
1513
2024
|
return true;
|
|
1514
2025
|
}
|
|
2026
|
+
console.debug("[web-apple-pay] #checkAvailability: ok");
|
|
1515
2027
|
return false;
|
|
1516
2028
|
}
|
|
1517
2029
|
/**
|
|
@@ -1520,19 +2032,24 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1520
2032
|
*/
|
|
1521
2033
|
async #checkSession() {
|
|
1522
2034
|
this.#sessionData = await this.#fetchSession();
|
|
2035
|
+
console.debug("[web-apple-pay] #checkSession: session data", { loggedIn: this.#sessionData?.loggedIn, userType: this.#sessionData?.userType });
|
|
1523
2036
|
if (this.#sessionData?.loggedIn && (this.#sessionData.userType === "active-subscription" || this.#sessionData.userType === "b2b-user")) {
|
|
2037
|
+
console.debug("[web-apple-pay] #checkSession: ineligible user, hiding button", { userType: this.#sessionData.userType });
|
|
1524
2038
|
this.style.display = "none";
|
|
1525
2039
|
this.querySelector(SEL_CONTAINER)?.remove();
|
|
1526
2040
|
return true;
|
|
1527
2041
|
}
|
|
2042
|
+
console.debug("[web-apple-pay] #checkSession: ok");
|
|
1528
2043
|
return false;
|
|
1529
2044
|
}
|
|
1530
2045
|
/** Returns `true` (hide) when no offer has been set before mount. */
|
|
1531
2046
|
#checkOffer() {
|
|
1532
2047
|
if (!this.#offer) {
|
|
2048
|
+
console.debug("[web-apple-pay] #checkOffer: no offer set, hiding button");
|
|
1533
2049
|
this.style.display = "none";
|
|
1534
2050
|
return true;
|
|
1535
2051
|
}
|
|
2052
|
+
console.debug("[web-apple-pay] #checkOffer: ok", { skuId: this.#offer.sku_id });
|
|
1536
2053
|
return false;
|
|
1537
2054
|
}
|
|
1538
2055
|
/** Captures the current page URL once at mount for reuse across the session lifetime. */
|
|
@@ -1556,15 +2073,19 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1556
2073
|
*/
|
|
1557
2074
|
async #fetchAndCacheOptions() {
|
|
1558
2075
|
this.#paymentOptions = await this.#fetchPaymentOptions();
|
|
1559
|
-
|
|
2076
|
+
console.debug("[web-apple-pay] #fetchAndCacheOptions: payment options", this.#paymentOptions);
|
|
2077
|
+
if (!this.#paymentOptions?.options?.apple_pay) {
|
|
2078
|
+
console.debug("[web-apple-pay] #fetchAndCacheOptions: apple_pay absent in options, hiding button");
|
|
1560
2079
|
this.style.display = "none";
|
|
1561
2080
|
this.querySelector(SEL_CONTAINER)?.remove();
|
|
1562
2081
|
return true;
|
|
1563
2082
|
}
|
|
2083
|
+
console.debug("[web-apple-pay] #fetchAndCacheOptions: ok", this.#paymentOptions.options.apple_pay);
|
|
1564
2084
|
return false;
|
|
1565
2085
|
}
|
|
1566
2086
|
/** Injects the button template and wires click and confirm event listeners. */
|
|
1567
2087
|
#renderTemplate() {
|
|
2088
|
+
console.debug("[web-apple-pay] #renderTemplate: injecting button template");
|
|
1568
2089
|
this.style.removeProperty("display");
|
|
1569
2090
|
ensureApplePayLogoSymbol();
|
|
1570
2091
|
const template = document.createElement("template");
|
|
@@ -1579,16 +2100,14 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1579
2100
|
if (button.classList.contains("wap-loading")) return;
|
|
1580
2101
|
this.#openModal();
|
|
1581
2102
|
});
|
|
1582
|
-
this.addEventListener("apple-pay-confirmed", (event) => {
|
|
1583
|
-
const { skuId, country } = event.detail;
|
|
1584
|
-
this.#initiateApplePay(skuId, country);
|
|
1585
|
-
});
|
|
1586
2103
|
}
|
|
1587
2104
|
#finishLoading() {
|
|
2105
|
+
console.debug("[web-apple-pay] #finishLoading: all checks passed, enabling button");
|
|
1588
2106
|
const button = this.querySelector(SEL_BUTTON);
|
|
1589
2107
|
if (button) {
|
|
1590
2108
|
button.classList.remove("wap-loading");
|
|
1591
2109
|
this.#updateDisabledState();
|
|
2110
|
+
this.#recaptchaTokensPromise = getPurchaseRecaptchaTokens();
|
|
1592
2111
|
trackExpressWalletImpression(this.#offer.sku_id, {
|
|
1593
2112
|
entryPoint: this.#entryPoint,
|
|
1594
2113
|
country: this.#country
|
|
@@ -1609,7 +2128,12 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1609
2128
|
button.setAttribute("aria-disabled", "true");
|
|
1610
2129
|
}
|
|
1611
2130
|
#openModal() {
|
|
1612
|
-
if (!this.#offer
|
|
2131
|
+
if (!this.#offer) {
|
|
2132
|
+
return;
|
|
2133
|
+
}
|
|
2134
|
+
if (document.querySelector(ApplePayModal.tag)) {
|
|
2135
|
+
return;
|
|
2136
|
+
}
|
|
1613
2137
|
trackExpressWalletClick(this.#offer.sku_id);
|
|
1614
2138
|
ApplePayModal.define();
|
|
1615
2139
|
const modal = document.createElement(ApplePayModal.tag);
|
|
@@ -1618,7 +2142,12 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1618
2142
|
if (this.#sessionData?.loggedIn) {
|
|
1619
2143
|
modal.dataset.loggedIn = "true";
|
|
1620
2144
|
}
|
|
1621
|
-
|
|
2145
|
+
const { skuId, country } = { skuId: this.#offer.sku_id, country: this.#country };
|
|
2146
|
+
modal.onConfirm = (recaptchaTokens) => {
|
|
2147
|
+
this.#initiateApplePay(skuId, country, recaptchaTokens);
|
|
2148
|
+
};
|
|
2149
|
+
modal.open(this.#recaptchaTokensPromise ?? void 0);
|
|
2150
|
+
this.#recaptchaTokensPromise = getPurchaseRecaptchaTokens();
|
|
1622
2151
|
}
|
|
1623
2152
|
async #fetchSession() {
|
|
1624
2153
|
try {
|
|
@@ -1645,7 +2174,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1645
2174
|
return {
|
|
1646
2175
|
countryCode: country,
|
|
1647
2176
|
currencyCode: offer.price?.currency_code ?? DEFAULT_CURRENCY,
|
|
1648
|
-
supportedNetworks: paymentOptions.apple_pay.settings.supported_networks,
|
|
2177
|
+
supportedNetworks: paymentOptions.options.apple_pay.settings.supported_networks,
|
|
1649
2178
|
totalAmount,
|
|
1650
2179
|
totalLabel: TOTAL_LABEL,
|
|
1651
2180
|
skuId,
|
|
@@ -1654,6 +2183,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1654
2183
|
onPaymentAuthorized: (event, session) => handlePaymentAuthorized(event, session, sessionData, {
|
|
1655
2184
|
skuId,
|
|
1656
2185
|
country,
|
|
2186
|
+
currency: offer.price?.currency_code ?? DEFAULT_CURRENCY,
|
|
1657
2187
|
returnUrl: this.#returnUrl,
|
|
1658
2188
|
queryString: buildQueryString(),
|
|
1659
2189
|
supportedBillingCountries: this.#supportedBillingCountries,
|
|
@@ -1661,11 +2191,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1661
2191
|
})
|
|
1662
2192
|
};
|
|
1663
2193
|
}
|
|
1664
|
-
|
|
2194
|
+
#initiateApplePay(skuId, country, recaptchaTokens) {
|
|
1665
2195
|
try {
|
|
1666
2196
|
const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
|
|
1667
2197
|
const paymentOptions = this.#paymentOptions;
|
|
1668
|
-
const recaptchaTokens = await getPurchaseRecaptchaTokens();
|
|
1669
2198
|
startApplePaySession(
|
|
1670
2199
|
this.#buildSessionParams(
|
|
1671
2200
|
skuId,
|