@expensify/react-native-wallet 0.1.40 → 0.2.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/src/index.tsx CHANGED
@@ -1,3 +1,4 @@
1
+ /* eslint-disable no-console */
1
2
  /* eslint-disable @lwc/lwc/no-async-await */
2
3
  import {NativeEventEmitter, Platform} from 'react-native';
3
4
  import type {EmitterSubscription} from 'react-native';
@@ -11,8 +12,10 @@ import type {
11
12
  IOSEncryptPayload,
12
13
  AndroidWalletData,
13
14
  onCardActivatedPayload,
15
+ onPaymentCredentialsRequestPayload,
14
16
  IOSAddPaymentPassData,
15
17
  TokenInfo,
18
+ AndroidPaymentCredentialsResponse,
16
19
  } from './NativeWallet';
17
20
  import {getCardState, getTokenizationStatus} from './utils';
18
21
  import AddToWalletButton from './AddToWalletButton';
@@ -22,6 +25,25 @@ function getModuleLinkingRejection() {
22
25
  }
23
26
 
24
27
  let eventEmitter: NativeEventEmitter | undefined;
28
+ let paymentCredentialsHandler: ((data: onPaymentCredentialsRequestPayload) => Promise<AndroidPaymentCredentialsResponse>) | null = null;
29
+
30
+ if (Platform.OS === 'android' && Wallet) {
31
+ eventEmitter ??= new NativeEventEmitter(Wallet);
32
+ eventEmitter.addListener('onPaymentCredentialsRequest', async (data: onPaymentCredentialsRequestPayload) => {
33
+ if (!paymentCredentialsHandler) {
34
+ console.warn(`[${PACKAGE_NAME}] Received payment credentials request but no handler is set`);
35
+ await Wallet?.AndroidRejectPaymentCredentials(data.requestId, 'No payment credentials handler is set').catch(() => undefined);
36
+ return;
37
+ }
38
+ try {
39
+ const responseData = await paymentCredentialsHandler(data);
40
+ await Wallet?.AndroidProvidePaymentCredentials(data.requestId, responseData);
41
+ } catch (error) {
42
+ console.error(`[${PACKAGE_NAME}] Error handling payment credentials request:`, error);
43
+ await Wallet?.AndroidRejectPaymentCredentials(data.requestId, error instanceof Error ? error.message : String(error)).catch(() => undefined);
44
+ }
45
+ });
46
+ }
25
47
 
26
48
  function addListener(event: string, callback: (data: onCardActivatedPayload) => void): EmitterSubscription {
27
49
  if (!Wallet) {
@@ -84,7 +106,30 @@ async function getCardStatusByIdentifier(identifier: string, tsp: string): Promi
84
106
  return getCardState(tokenState);
85
107
  }
86
108
 
87
- async function addCardToGoogleWallet(cardData: AndroidCardData): Promise<TokenizationStatus> {
109
+ /**
110
+ * Checks whether the card can still be added to a Google Wallet target: the phone or a paired wearable.
111
+ * Use it to decide whether to show the "Add to Google Wallet" button when wearables are supported.
112
+ * @param last4Digits - The last 4 digits of the card number (FPAN, not the token)
113
+ * @param network - The card network, e.g. `VISA`, `MASTERCARD`
114
+ * @param issuerName - The issuer name used for matching, as in the token metadata (not the card display name)
115
+ * @returns `true` if at least one target can still receive the card
116
+ */
117
+ async function hasEligibleTokenizationTarget(last4Digits: string, network: string, issuerName: string): Promise<boolean> {
118
+ if (Platform.OS === 'ios') {
119
+ throw new Error('hasEligibleTokenizationTarget is not available on iOS');
120
+ }
121
+
122
+ if (!Wallet) {
123
+ return getModuleLinkingRejection();
124
+ }
125
+
126
+ return Wallet.hasEligibleTokenizationTarget(last4Digits, network.toUpperCase(), issuerName);
127
+ }
128
+
129
+ async function addCardToGoogleWallet(
130
+ cardData: AndroidCardData,
131
+ handlePaymentCredentialsGeneration: (data: onPaymentCredentialsRequestPayload) => Promise<AndroidPaymentCredentialsResponse>,
132
+ ): Promise<TokenizationStatus> {
88
133
  if (Platform.OS === 'ios') {
89
134
  throw new Error('addCardToGoogleWallet is not available on iOS');
90
135
  }
@@ -96,8 +141,14 @@ async function addCardToGoogleWallet(cardData: AndroidCardData): Promise<Tokeniz
96
141
  if (!isWalletInitialized) {
97
142
  throw new Error('Wallet could not be initialized');
98
143
  }
99
- const tokenizationStatus = await Wallet.addCardToGoogleWallet(cardData);
100
- return getTokenizationStatus(tokenizationStatus);
144
+
145
+ paymentCredentialsHandler = handlePaymentCredentialsGeneration;
146
+ try {
147
+ const tokenizationStatus = await Wallet.addCardToGoogleWallet(cardData);
148
+ return getTokenizationStatus(tokenizationStatus);
149
+ } finally {
150
+ paymentCredentialsHandler = null;
151
+ }
101
152
  }
102
153
 
103
154
  async function resumeAddCardToGoogleWallet(cardData: AndroidResumeCardData): Promise<TokenizationStatus> {
@@ -164,13 +215,26 @@ async function addCardToAppleWallet(
164
215
  return getTokenizationStatus(status);
165
216
  }
166
217
 
167
- export type {AndroidCardData, AndroidWalletData, CardStatus, IOSEncryptPayload, IOSCardData, IOSAddPaymentPassData, onCardActivatedPayload, TokenizationStatus, TokenInfo};
218
+ export type {
219
+ AndroidCardData,
220
+ AndroidWalletData,
221
+ CardStatus,
222
+ IOSEncryptPayload,
223
+ IOSCardData,
224
+ IOSAddPaymentPassData,
225
+ onCardActivatedPayload,
226
+ onPaymentCredentialsRequestPayload,
227
+ TokenizationStatus,
228
+ TokenInfo,
229
+ AndroidPaymentCredentialsResponse,
230
+ };
168
231
  export {
169
232
  AddToWalletButton,
170
233
  checkWalletAvailability,
171
234
  getSecureWalletInfo,
172
235
  getCardStatusBySuffix,
173
236
  getCardStatusByIdentifier,
237
+ hasEligibleTokenizationTarget,
174
238
  addCardToGoogleWallet,
175
239
  resumeAddCardToGoogleWallet,
176
240
  listTokens,