@economist/web-apple-pay 0.1.3-beta.46 → 0.1.3-beta.48
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 +123 -110
- package/dist/src/apple-pay-button.d.ts +29 -1
- package/dist/src/apple-pay-button.js +117 -102
- package/dist/src/apple-pay-button.stories.js +117 -102
- package/dist/src/apple-pay-modal.d.ts +2 -6
- package/dist/src/apple-pay-modal.js +9 -27
- package/dist/src/apple-pay-modal.stories.js +117 -102
- package/dist/src/clients/payment-checkout/apple-session.d.ts +1 -0
- package/dist/src/clients/payment-checkout/apple-session.js +6 -19
- package/dist/src/clients/payment-checkout/payment-handler.d.ts +1 -5
- package/dist/src/clients/payment-checkout/payment-handler.js +1 -21
- package/dist/src/clients/payment-checkout/types.d.ts +1 -5
- package/dist/src/constants.d.ts +16 -0
- package/dist/src/constants.js +7 -0
- package/dist/src/index.js +123 -110
- package/dist/src/utils/recaptcha.d.ts +6 -6
- package/dist/src/utils/recaptcha.js +5 -21
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -576,9 +576,7 @@ var loadRecaptchaScript = (siteKey) => {
|
|
|
576
576
|
if (existingScript) {
|
|
577
577
|
existingScript.addEventListener("load", () => resolve());
|
|
578
578
|
existingScript.addEventListener("error", () => {
|
|
579
|
-
|
|
580
|
-
console.error("[web-apple-pay] reCAPTCHA script load failed (existing script)", error);
|
|
581
|
-
reject(error);
|
|
579
|
+
reject(new Error("Failed to load existing reCAPTCHA script"));
|
|
582
580
|
});
|
|
583
581
|
return;
|
|
584
582
|
}
|
|
@@ -588,9 +586,7 @@ var loadRecaptchaScript = (siteKey) => {
|
|
|
588
586
|
script.defer = true;
|
|
589
587
|
script.onload = () => resolve();
|
|
590
588
|
script.onerror = () => {
|
|
591
|
-
|
|
592
|
-
console.error("[web-apple-pay] reCAPTCHA script load failed (new script)", error);
|
|
593
|
-
reject(error);
|
|
589
|
+
reject(new Error("Failed to load reCAPTCHA script"));
|
|
594
590
|
};
|
|
595
591
|
document.head.appendChild(script);
|
|
596
592
|
});
|
|
@@ -609,20 +605,8 @@ var getRecaptchaToken = async (action) => {
|
|
|
609
605
|
});
|
|
610
606
|
});
|
|
611
607
|
};
|
|
612
|
-
var
|
|
613
|
-
|
|
614
|
-
try {
|
|
615
|
-
const [checkEligibility, newBasket, registerUser] = await Promise.all([
|
|
616
|
-
getRecaptchaToken("checkEligibility"),
|
|
617
|
-
getRecaptchaToken("newBasket"),
|
|
618
|
-
getRecaptchaToken("registerUser")
|
|
619
|
-
]);
|
|
620
|
-
console.info("[web-apple-pay] All reCAPTCHA tokens acquired successfully");
|
|
621
|
-
return { checkEligibility, newBasket, registerUser };
|
|
622
|
-
} catch (error) {
|
|
623
|
-
console.error("[web-apple-pay] Failed to acquire one or more reCAPTCHA tokens", error);
|
|
624
|
-
throw error;
|
|
625
|
-
}
|
|
608
|
+
var getPurchaseRecaptchaToken = () => {
|
|
609
|
+
return getRecaptchaToken("walletPurchase");
|
|
626
610
|
};
|
|
627
611
|
|
|
628
612
|
// src/apple-pay-modal.ts
|
|
@@ -850,7 +834,7 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
850
834
|
#isClosed = false;
|
|
851
835
|
/**
|
|
852
836
|
* Callback invoked synchronously from the CTA click handler.
|
|
853
|
-
* Receives a Promise of reCAPTCHA
|
|
837
|
+
* Receives a Promise of a reCAPTCHA token — the promise is already in-flight
|
|
854
838
|
* by the time this is called. The caller (ApplePayButton) must call
|
|
855
839
|
* new ApplePaySession() synchronously inside this callback to stay within
|
|
856
840
|
* Safari's user-gesture window. The token promise resolves before
|
|
@@ -962,15 +946,12 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
962
946
|
this.querySelector('[role="alert"]:not(#apple-pay-modal-error)')?.remove();
|
|
963
947
|
cta.disabled = true;
|
|
964
948
|
cta.setAttribute("aria-busy", "true");
|
|
965
|
-
|
|
966
|
-
const recaptchaTokensPromise = getPurchaseRecaptchaTokens();
|
|
949
|
+
const recaptchaTokenPromise = getPurchaseRecaptchaToken();
|
|
967
950
|
if (!this.#isClosed && this.onConfirm) {
|
|
968
|
-
|
|
969
|
-
this.onConfirm(recaptchaTokensPromise);
|
|
951
|
+
this.onConfirm(recaptchaTokenPromise);
|
|
970
952
|
this.close();
|
|
971
953
|
}
|
|
972
|
-
|
|
973
|
-
console.error("[web-apple-pay] reCAPTCHA token fetch failed", error);
|
|
954
|
+
recaptchaTokenPromise.catch(() => {
|
|
974
955
|
if (this.#isClosed) return;
|
|
975
956
|
cta.disabled = false;
|
|
976
957
|
cta.removeAttribute("aria-busy");
|
|
@@ -996,6 +977,7 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
996
977
|
);
|
|
997
978
|
}
|
|
998
979
|
open() {
|
|
980
|
+
this.#isClosed = false;
|
|
999
981
|
this.#triggerElement = document.activeElement;
|
|
1000
982
|
document.body.appendChild(this);
|
|
1001
983
|
this.#keyHandler = (event) => {
|
|
@@ -1024,7 +1006,10 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
1024
1006
|
|
|
1025
1007
|
// src/templates/apple-pay-button.template.html
|
|
1026
1008
|
var apple_pay_button_template_default = `<style>
|
|
1027
|
-
/*
|
|
1009
|
+
/*
|
|
1010
|
+
* Entry CTA: \u201CSubscribe with\u201D + Apple Pay wordmark (Figma 1118:14088).
|
|
1011
|
+
* \u201CSubscribe with\u201D matches Continue to checkout typography (EconomistSans Regular / 17 / 24 / 400).
|
|
1012
|
+
*/
|
|
1028
1013
|
teg-apple-pay-button {
|
|
1029
1014
|
display: block;
|
|
1030
1015
|
margin: var(--apple-pay-margin, 0);
|
|
@@ -1044,25 +1029,50 @@ var apple_pay_button_template_default = `<style>
|
|
|
1044
1029
|
display: flex;
|
|
1045
1030
|
align-items: center;
|
|
1046
1031
|
justify-content: center;
|
|
1047
|
-
gap: 0.375rem;
|
|
1048
1032
|
width: 100%;
|
|
1049
1033
|
min-width: 0;
|
|
1050
1034
|
height: 100%;
|
|
1051
|
-
min-height: var(--apple-pay-height, 2.
|
|
1052
|
-
|
|
1035
|
+
min-height: var(--apple-pay-height, 2.5rem);
|
|
1036
|
+
padding: 0;
|
|
1037
|
+
box-sizing: border-box;
|
|
1038
|
+
background-color: var(--apple-pay-background-color, #1a1a1a);
|
|
1053
1039
|
color: var(--apple-pay-foreground-color, #fff);
|
|
1054
1040
|
border: none;
|
|
1055
|
-
border-radius: var(--apple-pay-border-radius,
|
|
1041
|
+
border-radius: var(--apple-pay-border-radius, 0.25rem);
|
|
1056
1042
|
cursor: pointer;
|
|
1057
|
-
|
|
1058
|
-
font-
|
|
1059
|
-
|
|
1043
|
+
/* Same as wall Continue to checkout / Marber button large */
|
|
1044
|
+
font-family: var(
|
|
1045
|
+
--mb-typeface-sans,
|
|
1046
|
+
var(--wall-type-system-sans, 'EconomistSans', system-ui, sans-serif)
|
|
1047
|
+
);
|
|
1048
|
+
font-size: var(--mb-button-font-size, 1.0625rem);
|
|
1049
|
+
font-weight: var(--mb-font-weight-regular, 400);
|
|
1050
|
+
line-height: 1;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
teg-apple-pay-button .apple-pay-btn__label {
|
|
1054
|
+
display: inline-flex;
|
|
1055
|
+
align-items: center;
|
|
1056
|
+
/* No flex gap \u2014 spacing after \u201Cwith\u201D uses the same word-space as \u201CSubscribe with\u201D */
|
|
1057
|
+
gap: 0;
|
|
1058
|
+
white-space: nowrap;
|
|
1060
1059
|
}
|
|
1061
1060
|
|
|
1062
|
-
teg-apple-pay-button .apple-pay-
|
|
1061
|
+
teg-apple-pay-button .apple-pay-btn__caption {
|
|
1062
|
+
/* One typographic word space after \u201Cwith\u201D, matching Subscribe\u2194with */
|
|
1063
|
+
padding-right: 0.25em;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
teg-apple-pay-button .apple-pay-btn__logo {
|
|
1063
1067
|
flex-shrink: 0;
|
|
1064
1068
|
display: block;
|
|
1069
|
+
/* Match caption font-size so mark aligns with text metrics */
|
|
1070
|
+
height: 1.05em;
|
|
1071
|
+
width: auto;
|
|
1072
|
+
aspect-ratio: 51 / 22;
|
|
1065
1073
|
color: currentColor;
|
|
1074
|
+
/* Optical nudge \u2014 SVG viewBox sits slightly high vs EconomistSans caps */
|
|
1075
|
+
transform: translateY(0.06em);
|
|
1066
1076
|
}
|
|
1067
1077
|
|
|
1068
1078
|
teg-apple-pay-button .apple-pay-btn.wap-loading {
|
|
@@ -1070,6 +1080,10 @@ var apple_pay_button_template_default = `<style>
|
|
|
1070
1080
|
cursor: default;
|
|
1071
1081
|
}
|
|
1072
1082
|
|
|
1083
|
+
teg-apple-pay-button .apple-pay-btn.wap-loading .apple-pay-btn__label {
|
|
1084
|
+
visibility: hidden;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1073
1087
|
teg-apple-pay-button .apple-pay-btn.wap-loading::before {
|
|
1074
1088
|
content: '';
|
|
1075
1089
|
position: absolute;
|
|
@@ -1083,11 +1097,6 @@ var apple_pay_button_template_default = `<style>
|
|
|
1083
1097
|
animation: wap-button-spinner 0.8s linear infinite;
|
|
1084
1098
|
}
|
|
1085
1099
|
|
|
1086
|
-
teg-apple-pay-button .apple-pay-btn__logo {
|
|
1087
|
-
display: inline-flex;
|
|
1088
|
-
align-items: center;
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
1100
|
teg-apple-pay-button .apple-pay-btn__loading {
|
|
1092
1101
|
display: none;
|
|
1093
1102
|
}
|
|
@@ -1102,18 +1111,26 @@ var apple_pay_button_template_default = `<style>
|
|
|
1102
1111
|
}
|
|
1103
1112
|
</style>
|
|
1104
1113
|
|
|
1105
|
-
<div class="apple-pay-container" aria-label="
|
|
1114
|
+
<div class="apple-pay-container" aria-label="Subscribe with Apple Pay">
|
|
1106
1115
|
<button
|
|
1107
1116
|
class="apple-pay-btn"
|
|
1108
1117
|
type="button"
|
|
1109
|
-
aria-label="
|
|
1118
|
+
aria-label="Subscribe with Apple Pay"
|
|
1110
1119
|
data-analytics="paywall:apple-pay-checkout"
|
|
1111
1120
|
>
|
|
1112
|
-
<span class="apple-pay-
|
|
1113
|
-
|
|
1121
|
+
<span class="apple-pay-btn__label"
|
|
1122
|
+
><span class="apple-pay-btn__caption">Subscribe with</span
|
|
1123
|
+
><svg
|
|
1124
|
+
class="apple-pay-btn__logo"
|
|
1125
|
+
width="42"
|
|
1126
|
+
height="18"
|
|
1127
|
+
viewBox="0 0 51 22"
|
|
1128
|
+
aria-hidden="true"
|
|
1129
|
+
role="img"
|
|
1130
|
+
>
|
|
1114
1131
|
<use href="#apple-pay-logo" />
|
|
1115
|
-
</svg
|
|
1116
|
-
|
|
1132
|
+
</svg
|
|
1133
|
+
></span>
|
|
1117
1134
|
<span class="apple-pay-btn__loading" aria-hidden="true">Loading...</span>
|
|
1118
1135
|
</button>
|
|
1119
1136
|
</div>
|
|
@@ -1382,28 +1399,22 @@ async function handleMerchantValidation(event, session, config) {
|
|
|
1382
1399
|
try {
|
|
1383
1400
|
const validationHost = new URL(event.validationURL).host;
|
|
1384
1401
|
if (!validationHost.endsWith("apple.com")) {
|
|
1385
|
-
console.error("[web-apple-pay] Merchant validation URL is not from apple.com \u2014 aborting session", { validationURL: event.validationURL });
|
|
1386
1402
|
session.abort();
|
|
1387
1403
|
redirectToFallbackCheckout(config);
|
|
1388
1404
|
return;
|
|
1389
1405
|
}
|
|
1390
|
-
console.info("[web-apple-pay] Merchant validation started", { validationHost });
|
|
1391
1406
|
const recaptchaToken = await getRecaptchaToken("verifyMerchant");
|
|
1392
|
-
console.info("[web-apple-pay] verifyMerchant reCAPTCHA token acquired \u2014 calling validateAppleMerchant");
|
|
1393
1407
|
const response = await validateAppleMerchant({
|
|
1394
1408
|
validationURL: event.validationURL,
|
|
1395
1409
|
recaptchaToken
|
|
1396
1410
|
});
|
|
1397
1411
|
if (!response?.merchantSession) {
|
|
1398
|
-
console.error("[web-apple-pay] Merchant validation response missing merchantSession \u2014 aborting session", { response });
|
|
1399
1412
|
session.abort();
|
|
1400
1413
|
redirectToFallbackCheckout(config);
|
|
1401
1414
|
return;
|
|
1402
1415
|
}
|
|
1403
|
-
console.info("[web-apple-pay] Merchant validation succeeded \u2014 completing merchant validation");
|
|
1404
1416
|
session.completeMerchantValidation(response.merchantSession);
|
|
1405
|
-
} catch
|
|
1406
|
-
console.error("[web-apple-pay] Merchant validation threw an error \u2014 aborting session", error);
|
|
1417
|
+
} catch {
|
|
1407
1418
|
session.abort();
|
|
1408
1419
|
redirectToFallbackCheckout(config);
|
|
1409
1420
|
}
|
|
@@ -1424,14 +1435,12 @@ function startApplePaySession(config) {
|
|
|
1424
1435
|
};
|
|
1425
1436
|
try {
|
|
1426
1437
|
const session = new ApplePaySession(3, paymentRequest);
|
|
1427
|
-
console.info("[web-apple-pay] ApplePaySession created successfully");
|
|
1428
1438
|
session.onvalidatemerchant = async (event) => {
|
|
1429
1439
|
await handleMerchantValidation(event, session, config);
|
|
1430
1440
|
};
|
|
1431
1441
|
session.onpaymentauthorized = (event) => {
|
|
1432
1442
|
trackWalletPaymentAuthorised(config.skuId);
|
|
1433
|
-
config.onPaymentAuthorized(event, session).catch((
|
|
1434
|
-
console.error("[web-apple-pay] onPaymentAuthorized threw an unhandled error \u2014 aborting session", err);
|
|
1443
|
+
config.onPaymentAuthorized(event, session).catch(() => {
|
|
1435
1444
|
session.completePayment({ status: ApplePaySession.STATUS_FAILURE });
|
|
1436
1445
|
redirectToFallbackCheckout(config);
|
|
1437
1446
|
});
|
|
@@ -1441,9 +1450,7 @@ function startApplePaySession(config) {
|
|
|
1441
1450
|
};
|
|
1442
1451
|
trackWalletPaymentSheetOpen(config.skuId);
|
|
1443
1452
|
session.begin();
|
|
1444
|
-
|
|
1445
|
-
} catch (error) {
|
|
1446
|
-
console.error("[web-apple-pay] ApplePaySession creation or begin() threw \u2014 redirecting to fallback checkout", error);
|
|
1453
|
+
} catch {
|
|
1447
1454
|
redirectToFallbackCheckout(config);
|
|
1448
1455
|
}
|
|
1449
1456
|
}
|
|
@@ -1625,24 +1632,12 @@ function handleStructuredError(ctx) {
|
|
|
1625
1632
|
async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
1626
1633
|
const { skuId, returnUrl, country } = config;
|
|
1627
1634
|
let validatedEmail;
|
|
1628
|
-
console.info("[web-apple-pay] Payment authorised \u2014 starting purchase flow", {
|
|
1629
|
-
skuId,
|
|
1630
|
-
country,
|
|
1631
|
-
loggedIn: sessionData.loggedIn,
|
|
1632
|
-
userType: sessionData.userType,
|
|
1633
|
-
recaptchaTokensPresent: {
|
|
1634
|
-
checkEligibility: !!config.recaptchaTokens.checkEligibility,
|
|
1635
|
-
newBasket: !!config.recaptchaTokens.newBasket,
|
|
1636
|
-
registerUser: !!config.recaptchaTokens.registerUser
|
|
1637
|
-
}
|
|
1638
|
-
});
|
|
1639
1635
|
try {
|
|
1640
1636
|
validatedEmail = validateContacts(
|
|
1641
1637
|
event,
|
|
1642
1638
|
sessionData,
|
|
1643
1639
|
config.supportedBillingCountries
|
|
1644
1640
|
);
|
|
1645
|
-
console.info("[web-apple-pay] Contact validation passed \u2014 calling purchase API");
|
|
1646
1641
|
const result = await performPurchase({
|
|
1647
1642
|
applePayToken: event.payment.token,
|
|
1648
1643
|
skuId: config.skuId,
|
|
@@ -1651,9 +1646,8 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1651
1646
|
...event.payment.shippingContact && {
|
|
1652
1647
|
shippingContact: event.payment.shippingContact
|
|
1653
1648
|
},
|
|
1654
|
-
|
|
1649
|
+
recaptchaToken: config.recaptchaToken
|
|
1655
1650
|
});
|
|
1656
|
-
console.info("[web-apple-pay] Purchase API succeeded", { skuId, basketId: result.basketId });
|
|
1657
1651
|
trackWalletPaymentSuccess(skuId, result.basketId);
|
|
1658
1652
|
session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
|
|
1659
1653
|
const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
|
|
@@ -1668,17 +1662,10 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1668
1662
|
});
|
|
1669
1663
|
} catch (err) {
|
|
1670
1664
|
if (err instanceof ContactValidationError) {
|
|
1671
|
-
console.warn("[web-apple-pay] Contact validation failed \u2014 keeping Apple Pay sheet open", err);
|
|
1672
1665
|
failApplePaySession(session, err.errors);
|
|
1673
1666
|
return;
|
|
1674
1667
|
}
|
|
1675
1668
|
const walletError = parseWalletApiError(err);
|
|
1676
|
-
console.error("[web-apple-pay] Purchase API failed", {
|
|
1677
|
-
skuId,
|
|
1678
|
-
errorType: walletError?.errorType ?? "UNKNOWN",
|
|
1679
|
-
message: walletError?.message,
|
|
1680
|
-
err
|
|
1681
|
-
});
|
|
1682
1669
|
trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
|
|
1683
1670
|
failApplePaySession(session);
|
|
1684
1671
|
if (!walletError) {
|
|
@@ -1950,6 +1937,19 @@ var getSupportedBillingCountries = (offerCountry, offerVariant) => {
|
|
|
1950
1937
|
return [...zoneCountries];
|
|
1951
1938
|
};
|
|
1952
1939
|
|
|
1940
|
+
// src/constants.ts
|
|
1941
|
+
var ApplePayEntryPoint = /* @__PURE__ */ ((ApplePayEntryPoint3) => {
|
|
1942
|
+
ApplePayEntryPoint3["PAYWALL"] = "article_paywall";
|
|
1943
|
+
ApplePayEntryPoint3["REGWALL"] = "article_regwall";
|
|
1944
|
+
ApplePayEntryPoint3["DUAL_OFFER_BANNER"] = "dual_offer_banner";
|
|
1945
|
+
return ApplePayEntryPoint3;
|
|
1946
|
+
})(ApplePayEntryPoint || {});
|
|
1947
|
+
var APPLE_PAY_EXPRESS_BANNERS_EXPERIMENT_KEY = "subscribe-experiment-calibration";
|
|
1948
|
+
var APPLE_PAY_EXPRESS_BANNERS_VARIANTS = {
|
|
1949
|
+
CONTROL: "control",
|
|
1950
|
+
TREATMENT: "treatment"
|
|
1951
|
+
};
|
|
1952
|
+
|
|
1953
1953
|
// src/apple-pay-button.ts
|
|
1954
1954
|
var FEATURE_APPLE_PAY_EXPRESS_CHECKOUT = "FEATURE_APPLE_PAY_EXPRESS_CHECKOUT";
|
|
1955
1955
|
var TOTAL_LABEL = "The Economist";
|
|
@@ -1967,7 +1967,23 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1967
1967
|
define(_ApplePayButton.tag, _ApplePayButton);
|
|
1968
1968
|
}
|
|
1969
1969
|
/**
|
|
1970
|
-
* Creates and configures an ApplePayButton
|
|
1970
|
+
* Creates and configures an ApplePayButton when the Amplitude experiment is enabled.
|
|
1971
|
+
*
|
|
1972
|
+
* Reads the experiment variant from `window.experimentIntegration.getVariant` using
|
|
1973
|
+
* {@link APPLE_PAY_EXPRESS_BANNERS_EXPERIMENT_KEY}. The host page is responsible
|
|
1974
|
+
* for setting up `window.experimentIntegration` before calling this method.
|
|
1975
|
+
*
|
|
1976
|
+
* - `treatment` → button created and returned
|
|
1977
|
+
* - `control` / missing / anything else → returns null (button suppressed)
|
|
1978
|
+
*
|
|
1979
|
+
* **Transition period:** the legacy `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param
|
|
1980
|
+
* gate is also evaluated as a fallback until all host apps have integrated
|
|
1981
|
+
* `window.experimentIntegration`. Once all consumers are migrated, the URL param
|
|
1982
|
+
* fallback will be removed.
|
|
1983
|
+
*
|
|
1984
|
+
* @param offer - The offer to display in the Apple Pay flow.
|
|
1985
|
+
* @param country - The user's country code.
|
|
1986
|
+
* @param entryPoint - Where the button is being rendered (e.g. paywall, banner).
|
|
1971
1987
|
*/
|
|
1972
1988
|
static createIfEnabled(offer, country, entryPoint) {
|
|
1973
1989
|
if (!offer) {
|
|
@@ -1976,7 +1992,18 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1976
1992
|
if (APPLE_PAY_EXCLUDED_VARIANTS.includes(offer.product?.variant)) {
|
|
1977
1993
|
return null;
|
|
1978
1994
|
}
|
|
1979
|
-
|
|
1995
|
+
const experimentValue = window.experimentIntegration?.getVariant?.(
|
|
1996
|
+
APPLE_PAY_EXPRESS_BANNERS_EXPERIMENT_KEY
|
|
1997
|
+
);
|
|
1998
|
+
const isTreatment = experimentValue === APPLE_PAY_EXPRESS_BANNERS_VARIANTS.TREATMENT;
|
|
1999
|
+
const hasUrlParamFlag = new URLSearchParams(window.location.search).has(
|
|
2000
|
+
FEATURE_APPLE_PAY_EXPRESS_CHECKOUT
|
|
2001
|
+
);
|
|
2002
|
+
console.log(`Apple Pay button experiment: variant="${experimentValue}`);
|
|
2003
|
+
if (!isTreatment && !hasUrlParamFlag) {
|
|
2004
|
+
console.log(
|
|
2005
|
+
`Apple Pay button suppressed: experiment variant is "${experimentValue}" and URL param "${FEATURE_APPLE_PAY_EXPRESS_CHECKOUT}" is not present.`
|
|
2006
|
+
);
|
|
1980
2007
|
return null;
|
|
1981
2008
|
}
|
|
1982
2009
|
_ApplePayButton.define();
|
|
@@ -2209,11 +2236,9 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2209
2236
|
}
|
|
2210
2237
|
const skuId = this.#offer.sku_id;
|
|
2211
2238
|
const country = this.#country;
|
|
2212
|
-
modal.onConfirm = (
|
|
2213
|
-
|
|
2214
|
-
this.#initiateApplePay(skuId, country, recaptchaTokensPromise);
|
|
2239
|
+
modal.onConfirm = (recaptchaTokenPromise) => {
|
|
2240
|
+
this.#initiateApplePay(skuId, country, recaptchaTokenPromise);
|
|
2215
2241
|
};
|
|
2216
|
-
console.info("[web-apple-pay] Opening Apple Pay modal", { skuId, country });
|
|
2217
2242
|
modal.open();
|
|
2218
2243
|
}
|
|
2219
2244
|
async #fetchSession() {
|
|
@@ -2235,7 +2260,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2235
2260
|
return null;
|
|
2236
2261
|
}
|
|
2237
2262
|
}
|
|
2238
|
-
#buildSessionParams(skuId, country, sessionData, paymentOptions,
|
|
2263
|
+
#buildSessionParams(skuId, country, sessionData, paymentOptions, recaptchaTokenPromise) {
|
|
2239
2264
|
const offer = this.#offer;
|
|
2240
2265
|
const totalAmount = offer.price?.purchase ? `${offer.price.purchase}` : DEFAULT_TOTAL;
|
|
2241
2266
|
return {
|
|
@@ -2248,56 +2273,44 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2248
2273
|
returnUrl: this.#returnUrl,
|
|
2249
2274
|
userLoggedIn: sessionData.loggedIn,
|
|
2250
2275
|
onPaymentAuthorized: async (event, session) => {
|
|
2251
|
-
const
|
|
2276
|
+
const recaptchaToken = await recaptchaTokenPromise;
|
|
2252
2277
|
return handlePaymentAuthorized(event, session, sessionData, {
|
|
2253
2278
|
skuId,
|
|
2254
2279
|
country,
|
|
2255
2280
|
currency: offer.price?.currency_code ?? DEFAULT_CURRENCY,
|
|
2256
2281
|
returnUrl: this.#returnUrl,
|
|
2257
2282
|
supportedBillingCountries: this.#supportedBillingCountries,
|
|
2258
|
-
|
|
2283
|
+
recaptchaToken
|
|
2259
2284
|
});
|
|
2260
2285
|
}
|
|
2261
2286
|
};
|
|
2262
2287
|
}
|
|
2263
|
-
#initiateApplePay(skuId, country,
|
|
2288
|
+
#initiateApplePay(skuId, country, recaptchaTokenPromise) {
|
|
2264
2289
|
try {
|
|
2265
2290
|
const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
|
|
2266
2291
|
const paymentOptions = this.#paymentOptions;
|
|
2267
2292
|
if (!paymentOptions) {
|
|
2268
|
-
console.error("[web-apple-pay] #initiateApplePay called before payment options were fetched");
|
|
2269
2293
|
return;
|
|
2270
2294
|
}
|
|
2271
|
-
console.info("[web-apple-pay] Starting Apple Pay session", {
|
|
2272
|
-
skuId,
|
|
2273
|
-
country,
|
|
2274
|
-
loggedIn: sessionData.loggedIn,
|
|
2275
|
-
userType: sessionData.userType,
|
|
2276
|
-
hasPaymentOptions: true
|
|
2277
|
-
});
|
|
2278
2295
|
startApplePaySession(
|
|
2279
2296
|
this.#buildSessionParams(
|
|
2280
2297
|
skuId,
|
|
2281
2298
|
country,
|
|
2282
2299
|
sessionData,
|
|
2283
2300
|
paymentOptions,
|
|
2284
|
-
|
|
2301
|
+
recaptchaTokenPromise
|
|
2285
2302
|
)
|
|
2286
2303
|
);
|
|
2287
|
-
} catch
|
|
2288
|
-
|
|
2304
|
+
} catch {
|
|
2305
|
+
redirectToFallbackCheckout({
|
|
2306
|
+
returnUrl: this.#returnUrl,
|
|
2307
|
+
skuId,
|
|
2308
|
+
countryCode: country
|
|
2309
|
+
});
|
|
2289
2310
|
}
|
|
2290
2311
|
}
|
|
2291
2312
|
};
|
|
2292
2313
|
|
|
2293
|
-
// src/constants.ts
|
|
2294
|
-
var ApplePayEntryPoint = /* @__PURE__ */ ((ApplePayEntryPoint2) => {
|
|
2295
|
-
ApplePayEntryPoint2["PAYWALL"] = "article_paywall";
|
|
2296
|
-
ApplePayEntryPoint2["REGWALL"] = "article_regwall";
|
|
2297
|
-
ApplePayEntryPoint2["DUAL_OFFER_BANNER"] = "dual_offer_banner";
|
|
2298
|
-
return ApplePayEntryPoint2;
|
|
2299
|
-
})(ApplePayEntryPoint || {});
|
|
2300
|
-
|
|
2301
2314
|
// src/index.ts
|
|
2302
2315
|
function defineApplePayElements() {
|
|
2303
2316
|
ApplePayButton.define();
|
|
@@ -1,12 +1,40 @@
|
|
|
1
1
|
import type { Offer, ProductVariant } from './types/offer.types';
|
|
2
2
|
import { ApplePayEntryPoint } from './constants';
|
|
3
|
+
/**
|
|
4
|
+
* The host page is responsible for setting up `window.experimentIntegration`
|
|
5
|
+
* with a `getVariant` method that returns the variant value for a given flag key.
|
|
6
|
+
* web-apple-pay reads the experiment variant from here to decide whether to show or hide the button.
|
|
7
|
+
*/
|
|
8
|
+
declare global {
|
|
9
|
+
interface Window {
|
|
10
|
+
experimentIntegration?: {
|
|
11
|
+
getVariant: (flagKey: string) => string | undefined;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
}
|
|
3
15
|
export declare const APPLE_PAY_EXCLUDED_VARIANTS: readonly ProductVariant[];
|
|
4
16
|
export declare class ApplePayButton extends HTMLElement {
|
|
5
17
|
#private;
|
|
6
18
|
static readonly tag = "teg-apple-pay-button";
|
|
7
19
|
static define(): void;
|
|
8
20
|
/**
|
|
9
|
-
* Creates and configures an ApplePayButton
|
|
21
|
+
* Creates and configures an ApplePayButton when the Amplitude experiment is enabled.
|
|
22
|
+
*
|
|
23
|
+
* Reads the experiment variant from `window.experimentIntegration.getVariant` using
|
|
24
|
+
* {@link APPLE_PAY_EXPRESS_BANNERS_EXPERIMENT_KEY}. The host page is responsible
|
|
25
|
+
* for setting up `window.experimentIntegration` before calling this method.
|
|
26
|
+
*
|
|
27
|
+
* - `treatment` → button created and returned
|
|
28
|
+
* - `control` / missing / anything else → returns null (button suppressed)
|
|
29
|
+
*
|
|
30
|
+
* **Transition period:** the legacy `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param
|
|
31
|
+
* gate is also evaluated as a fallback until all host apps have integrated
|
|
32
|
+
* `window.experimentIntegration`. Once all consumers are migrated, the URL param
|
|
33
|
+
* fallback will be removed.
|
|
34
|
+
*
|
|
35
|
+
* @param offer - The offer to display in the Apple Pay flow.
|
|
36
|
+
* @param country - The user's country code.
|
|
37
|
+
* @param entryPoint - Where the button is being rendered (e.g. paywall, banner).
|
|
10
38
|
*/
|
|
11
39
|
static createIfEnabled(offer: Offer | undefined, country: string, entryPoint?: ApplePayEntryPoint): ApplePayButton | null;
|
|
12
40
|
get offer(): Offer | null;
|