@economist/web-apple-pay 1.1.0 → 1.3.0

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/README.md CHANGED
@@ -32,6 +32,8 @@ import { configureApplePay, defineApplePayElements } from '@economist/web-apple-
32
32
  configureApplePay({
33
33
  merchantId: 'merchant.com.economist.your-app',
34
34
  debugBypassEnabled: false,
35
+ // required: wallet API base URL for session and checkout calls
36
+ walletApiBaseUrl: 'https://bff-test.economist.com',
35
37
  });
36
38
 
37
39
  defineApplePayElements();
@@ -57,11 +59,13 @@ When clicked, the button creates and opens `teg-apple-pay-modal` automatically.
57
59
  ### `configureApplePay(config)`
58
60
 
59
61
  Sets package-level runtime configuration used by Apple Pay availability checks.
62
+ Call this once during app startup.
60
63
 
61
64
  `config` shape:
62
65
 
63
66
  - `merchantId` (`string`, required): Apple Pay merchant identifier used for active-card checks.
64
67
  - `debugBypassEnabled` (`boolean`, optional): allows `?BYPASS_APPLE_PAY_CHECKS=1` query param to bypass capability checks in non-production-style debugging flows.
68
+ - `walletApiBaseUrl` (`string`, required): base URL for wallet API requests.
65
69
 
66
70
  If `merchantId` is not configured, Apple Pay availability checks return `false` and the button hides itself.
67
71
 
@@ -94,7 +98,7 @@ Consumers should listen for this event and continue payment orchestration.
94
98
  ## Public API
95
99
 
96
100
  - `defineApplePayElements()`: registers both custom elements.
97
- - `configureApplePay(config)`: sets required runtime config (merchant ID and optional debug bypass).
101
+ - `configureApplePay(config)`: sets required runtime config (merchant ID and wallet API base URL, with optional debug bypass).
98
102
  - `ApplePayButton`: button component class.
99
103
  - `ApplePayModal`: modal component class.
100
104
  - `ensureApplePayLogoSymbol()`: logo symbol injection helper.
@@ -106,7 +110,7 @@ Consumers should listen for this event and continue payment orchestration.
106
110
 
107
111
  Check:
108
112
 
109
- - `configureApplePay(...)` was called before rendering (with a valid `merchantId`)
113
+ - `configureApplePay(...)` was called before rendering (with a valid `merchantId` and `walletApiBaseUrl`)
110
114
  - device/browser supports Apple Pay checks (Safari/Apple device logic)
111
115
  - query string/feature flag gating for your page is satisfied
112
116
  - offer variant is not excluded (`bundle`, `insider_print`)
@@ -138,3 +142,24 @@ Then in a consuming app:
138
142
  ```sh
139
143
  yalc add @economist/web-apple-pay
140
144
  ```
145
+
146
+ ## Storybook
147
+
148
+ Run Storybook locally:
149
+
150
+ ```sh
151
+ npm run storybook --workspace @economist/web-apple-pay
152
+ ```
153
+
154
+ Build static Storybook output:
155
+
156
+ ```sh
157
+ npm run build-storybook --workspace @economist/web-apple-pay
158
+ ```
159
+
160
+ Story coverage matrix:
161
+
162
+ - `ApplePayButton` docs page shows the stable default example.
163
+ - `ApplePayButton` behavior and state variants are available as individual canvas stories in Storybook.
164
+ - `ApplePayModal` docs page shows the stable default example.
165
+ - `ApplePayModal` behavior and state variants are available as individual canvas stories in Storybook.
package/dist/index.js CHANGED
@@ -775,6 +775,9 @@ var isApplePayAvailable = async () => {
775
775
  }
776
776
  const merchantId = getApplePayConfig()?.merchantId;
777
777
  if (!merchantId) {
778
+ console.warn(
779
+ "[web-apple-pay] Missing merchantId. Call configureApplePay({ merchantId, ... }) before rendering Apple Pay components."
780
+ );
778
781
  return false;
779
782
  }
780
783
  return await applePaySession.canMakePaymentsWithActiveCard(merchantId);
@@ -784,6 +787,67 @@ var isApplePayAvailable = async () => {
784
787
  }
785
788
  };
786
789
 
790
+ // src/clients/payment-checkout/types.ts
791
+ var ApiError = class extends Error {
792
+ status;
793
+ body;
794
+ constructor(status, body) {
795
+ super(`API request failed with status ${status}`);
796
+ this.name = "ApiError";
797
+ this.status = status;
798
+ this.body = body;
799
+ }
800
+ };
801
+
802
+ // src/clients/payment-checkout/index.ts
803
+ var getWalletApiBaseUrl = () => {
804
+ const config = getApplePayConfig();
805
+ if (!config?.walletApiBaseUrl) {
806
+ throw new Error(
807
+ "[web-apple-pay] Missing walletApiBaseUrl. Call configureApplePay({ merchantId, walletApiBaseUrl, ... }) before making wallet API requests."
808
+ );
809
+ }
810
+ return config.walletApiBaseUrl;
811
+ };
812
+ var apiFetch = async (path, options = {}) => {
813
+ const baseUrl = getWalletApiBaseUrl();
814
+ const method = (options.method ?? "GET").toUpperCase();
815
+ const hasBody = options.body !== void 0 && options.body !== null;
816
+ const headers = new Headers(options.headers);
817
+ if (!headers.has("Accept")) {
818
+ headers.set("Accept", "application/json");
819
+ }
820
+ if (!headers.has("Content-Type")) {
821
+ if (hasBody || method !== "GET" && method !== "HEAD") {
822
+ headers.set("Content-Type", "application/json");
823
+ }
824
+ }
825
+ const response = await fetch(`${baseUrl}${path}`, {
826
+ ...options,
827
+ headers
828
+ });
829
+ if (!response.ok) {
830
+ const contentType = response.headers.get("content-type") || "";
831
+ if (contentType.includes("application/json")) {
832
+ const errorBody = await response.json();
833
+ throw new ApiError(response.status, errorBody);
834
+ }
835
+ const errorText = await response.text();
836
+ throw new Error(
837
+ `API request failed: ${response.status} ${response.statusText} - ${errorText}`
838
+ );
839
+ }
840
+ return response.json();
841
+ };
842
+
843
+ // src/clients/payment-checkout/session.ts
844
+ var getExpressCheckoutSession = () => {
845
+ return apiFetch("/v1/wallet/session", {
846
+ method: "GET",
847
+ credentials: "include"
848
+ });
849
+ };
850
+
787
851
  // src/apple-pay-button.ts
788
852
  var FEATURE_APPLE_PAY_EXPRESS_CHECKOUT = "FEATURE_APPLE_PAY_EXPRESS_CHECKOUT";
789
853
  var APPLE_PAY_EXCLUDED_VARIANTS = ["bundle", "insider_print"];
@@ -841,6 +905,11 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
841
905
  this.style.display = "none";
842
906
  return;
843
907
  }
908
+ const sessionData = await this.#fetchSession();
909
+ if (sessionData?.loggedIn && (sessionData.userType === "active-subscription" || sessionData.userType === "b2b-user")) {
910
+ this.style.display = "none";
911
+ return;
912
+ }
844
913
  this.style.removeProperty("display");
845
914
  ensureApplePayLogoSymbol();
846
915
  const template = document.createElement("template");
@@ -883,6 +952,13 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
883
952
  button.setAttribute("disabled", "");
884
953
  button.setAttribute("aria-disabled", "true");
885
954
  }
955
+ async #fetchSession() {
956
+ try {
957
+ return await getExpressCheckoutSession();
958
+ } catch {
959
+ return null;
960
+ }
961
+ }
886
962
  };
887
963
 
888
964
  // src/index.ts
@@ -770,6 +770,9 @@ var isApplePayAvailable = async () => {
770
770
  }
771
771
  const merchantId = getApplePayConfig()?.merchantId;
772
772
  if (!merchantId) {
773
+ console.warn(
774
+ "[web-apple-pay] Missing merchantId. Call configureApplePay({ merchantId, ... }) before rendering Apple Pay components."
775
+ );
773
776
  return false;
774
777
  }
775
778
  return await applePaySession.canMakePaymentsWithActiveCard(merchantId);
@@ -779,6 +782,67 @@ var isApplePayAvailable = async () => {
779
782
  }
780
783
  };
781
784
 
785
+ // src/clients/payment-checkout/types.ts
786
+ var ApiError = class extends Error {
787
+ status;
788
+ body;
789
+ constructor(status, body) {
790
+ super(`API request failed with status ${status}`);
791
+ this.name = "ApiError";
792
+ this.status = status;
793
+ this.body = body;
794
+ }
795
+ };
796
+
797
+ // src/clients/payment-checkout/index.ts
798
+ var getWalletApiBaseUrl = () => {
799
+ const config = getApplePayConfig();
800
+ if (!config?.walletApiBaseUrl) {
801
+ throw new Error(
802
+ "[web-apple-pay] Missing walletApiBaseUrl. Call configureApplePay({ merchantId, walletApiBaseUrl, ... }) before making wallet API requests."
803
+ );
804
+ }
805
+ return config.walletApiBaseUrl;
806
+ };
807
+ var apiFetch = async (path, options = {}) => {
808
+ const baseUrl = getWalletApiBaseUrl();
809
+ const method = (options.method ?? "GET").toUpperCase();
810
+ const hasBody = options.body !== void 0 && options.body !== null;
811
+ const headers = new Headers(options.headers);
812
+ if (!headers.has("Accept")) {
813
+ headers.set("Accept", "application/json");
814
+ }
815
+ if (!headers.has("Content-Type")) {
816
+ if (hasBody || method !== "GET" && method !== "HEAD") {
817
+ headers.set("Content-Type", "application/json");
818
+ }
819
+ }
820
+ const response = await fetch(`${baseUrl}${path}`, {
821
+ ...options,
822
+ headers
823
+ });
824
+ if (!response.ok) {
825
+ const contentType = response.headers.get("content-type") || "";
826
+ if (contentType.includes("application/json")) {
827
+ const errorBody = await response.json();
828
+ throw new ApiError(response.status, errorBody);
829
+ }
830
+ const errorText = await response.text();
831
+ throw new Error(
832
+ `API request failed: ${response.status} ${response.statusText} - ${errorText}`
833
+ );
834
+ }
835
+ return response.json();
836
+ };
837
+
838
+ // src/clients/payment-checkout/session.ts
839
+ var getExpressCheckoutSession = () => {
840
+ return apiFetch("/v1/wallet/session", {
841
+ method: "GET",
842
+ credentials: "include"
843
+ });
844
+ };
845
+
782
846
  // src/apple-pay-button.ts
783
847
  var FEATURE_APPLE_PAY_EXPRESS_CHECKOUT = "FEATURE_APPLE_PAY_EXPRESS_CHECKOUT";
784
848
  var APPLE_PAY_EXCLUDED_VARIANTS = ["bundle", "insider_print"];
@@ -836,6 +900,11 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
836
900
  this.style.display = "none";
837
901
  return;
838
902
  }
903
+ const sessionData = await this.#fetchSession();
904
+ if (sessionData?.loggedIn && (sessionData.userType === "active-subscription" || sessionData.userType === "b2b-user")) {
905
+ this.style.display = "none";
906
+ return;
907
+ }
839
908
  this.style.removeProperty("display");
840
909
  ensureApplePayLogoSymbol();
841
910
  const template = document.createElement("template");
@@ -878,6 +947,13 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
878
947
  button.setAttribute("disabled", "");
879
948
  button.setAttribute("aria-disabled", "true");
880
949
  }
950
+ async #fetchSession() {
951
+ try {
952
+ return await getExpressCheckoutSession();
953
+ } catch {
954
+ return null;
955
+ }
956
+ }
881
957
  };
882
958
  export {
883
959
  ApplePayButton