@expensify/react-native-wallet 0.1.41 → 0.2.1
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 +60 -8
- package/android/src/main/java/com/expensify/wallet/Utils.kt +6 -1
- package/android/src/main/java/com/expensify/wallet/WalletModule.kt +273 -50
- package/android/src/main/java/com/expensify/wallet/model/CardData.kt +3 -1
- package/android/src/paper/java/com/expensify/wallet/NativeWalletSpec.java +8 -0
- package/ios/RNWallet.mm +30 -0
- package/lib/commonjs/NativeWallet.js.map +1 -1
- package/lib/commonjs/index.js +46 -3
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/NativeWallet.js.map +1 -1
- package/lib/module/index.js +46 -4
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/NativeWallet.d.ts +18 -2
- package/lib/typescript/src/NativeWallet.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +13 -4
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/NativeWallet.ts +21 -1
- package/src/index.tsx +68 -4
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
- ♻️ Universal solution (Apple Wallet and Google Wallet)
|
|
6
6
|
- 💳 Easy and secure addition of payment cards from your app directly
|
|
7
7
|
- 💰 Supports Visa, MasterCard, Amex and Discover payment cards
|
|
8
|
+
- 🔐 UAPP (Unified Android Push Provisioning) support for enhanced security
|
|
8
9
|
- 🎉 Expo support (expo-plugin)
|
|
9
10
|
|
|
10
11
|
## Getting started
|
|
@@ -21,7 +22,7 @@ To be able to interact with the Google Wallet on the Android please make sure to
|
|
|
21
22
|
1. Visit [the Google Pay Android Push Provisioning API documentation](https://developers.google.com/pay/issuers/apis/push-provisioning/android/) and request access to it.
|
|
22
23
|
|
|
23
24
|
2. Once getting an approval from the Google team
|
|
24
|
-
1. Download the [TapAndPay SDK](https://developers.google.com/pay/issuers/apis/push-provisioning/android/releases).
|
|
25
|
+
1. Download the [TapAndPay SDK](https://developers.google.com/pay/issuers/apis/push-provisioning/android/releases). The library uses the UAPP APIs (`PaymentCredentialsGenerator`, `PushTokenizeResult`), so older SDK versions without them will not compile. Tested with 18.10.1.
|
|
25
26
|
2. Unzip it and extract the SDK into the `/android/libs` folder in your React Native project (if there is no `libs` folder, create one).
|
|
26
27
|
3. Add `/android/libs` to `.gitignore`.
|
|
27
28
|
|
|
@@ -134,7 +135,7 @@ In your `app.json` file, add the following configuration:
|
|
|
134
135
|
"@expensify/react-native-wallet",
|
|
135
136
|
{
|
|
136
137
|
enableApplePayProvisioning: true,
|
|
137
|
-
googleTapAndPaySdkPath: "./libs/tapandpay-v18.
|
|
138
|
+
googleTapAndPaySdkPath: "./libs/tapandpay-v18.10.1.zip", // path to the Google Tap & Pay SDK zip file
|
|
138
139
|
},
|
|
139
140
|
],
|
|
140
141
|
]
|
|
@@ -157,6 +158,7 @@ Here you can find data elements used in the library, essential to work with Goog
|
|
|
157
158
|
|
|
158
159
|
### Android
|
|
159
160
|
- **Opaque Payment Card** (OPC) - a binary blob of information Google Pay receives from the issuer app that could be presented to TSP to receive a token.
|
|
161
|
+
- **Google Opaque Payment Card** (Google OPC) - an encrypted version of the OPC required for UAPP (Unified Android Push Provisioning) flow. This is generated by your backend using Google's encryption keys during the provisioning process. [Learn more about UAPP →](./docs/UAPP.md)
|
|
160
162
|
- **Token Service Provider** (TSP) - a service that enhances payment security by replacing a credit card number during transactions with a unique digital identifier - token. The TSP specifies the tokenization service used to create a given token e.g. Visa, MasterCard, American Express.
|
|
161
163
|
- **Token Reference ID** - a unique identifying number to refer to a DPAN (Dynamic Personal Account Number). Token Providers will assign each DPAN an issuer token ID at the time of tokenization.
|
|
162
164
|
|
|
@@ -168,7 +170,7 @@ Here you can find data elements used in the library, essential to work with Goog
|
|
|
168
170
|
- **Ephemeral Public Key** - a key used by elliptic curve cryptography (ECC) (Base 64 encoded).
|
|
169
171
|
|
|
170
172
|
# API Reference
|
|
171
|
-
The library offers
|
|
173
|
+
The library offers functions for seamless integration and use of the Apple Wallet and Google Wallet APIs. Additionally, it includes listeners and handlers for managing provisioning events. Below, these functions are described along with the data types involved.
|
|
172
174
|
|
|
173
175
|
## Functions
|
|
174
176
|
|
|
@@ -178,26 +180,49 @@ The library offers seven functions for seamless integration and use of the Apple
|
|
|
178
180
|
| **checkWalletAvailability** | Checks if the wallet is ready and initializes it if possible. | None | `boolean` | ✅ | ✅ |
|
|
179
181
|
| **getCardStatusBySuffix** | Retrieves the current status of a card in the wallet. | `lastDigits: string`<br>(The last few digits of the card number) | `CardStatus` | ✅ | ✅ |
|
|
180
182
|
| **getCardStatusByIdentifier** | Returns the state of a card based on a platform-specific identifier. On Android, it's `Token Reference ID` and on iOS, it's `Primary Account Identifier`. | `identifier: string`,<br>`tsp: string` | `CardStatus` | ✅ | ✅ |
|
|
181
|
-
| **
|
|
183
|
+
| **hasEligibleTokenizationTarget** | Checks whether the card can still be added to a Google Wallet target (the phone or a paired wearable). Google recommends it for the "Add to Google Wallet" button when wearables are supported, because a card already on the phone can still be added to a watch. | `last4Digits: string`,<br>`network: string`,<br>`issuerName: string` | `boolean` | ❌ | ✅ |
|
|
184
|
+
| **addCardToGoogleWallet** | Initiates native Push Provisioning flow for adding a card to the Google Wallet. Requires a handler that returns the payment credentials for the UAPP flow. [Learn more →](./docs/UAPP.md) | `data`: `AndroidCardData`,<br>`handlePaymentCredentials`: `(data: onPaymentCredentialsRequestPayload) => Promise<AndroidPaymentCredentialsResponse>` | `TokenizationStatus` | ❌ | ✅ |
|
|
182
185
|
| **resumeAddCardToGoogleWallet** | Resumes the Push Provisioning flow for adding a card to the Google Wallet using existing token reference ID. | `data`: `AndroidResumeCardData` | `TokenizationStatus` | ❌ | ✅ |
|
|
183
186
|
| **listTokens** | Lists all tokens currently stored in the Google Wallet. | None | `TokenInfo[]` | ❌ | ✅ |
|
|
184
187
|
| **addCardToAppleWallet** | Initiates native Push Provisioning flow for adding a card to the Apple Wallet. | `data`: `IOSCardData`,<br>`issuerEncrypt-`<br>`PayloadCallback: IOSIssuerCallback` | `void` | ✅ | ❌ |
|
|
185
188
|
|
|
186
|
-
|
|
187
189
|
## Data Types
|
|
188
190
|
|
|
189
191
|
| Type | Description | Fields |
|
|
190
192
|
|------|-------------|--------|
|
|
191
193
|
| **AndroidWalletData** | Specific information for Android devices required for wallet transactions. | `deviceID: string`,<br>`walletAccountID: string` |
|
|
192
|
-
| **AndroidCardData** | Data related to a card that is to be added on Android platform wallets. | `network: string`,<br>`opaquePaymentCard: string`,<br>`cardHolderName: string`,<br>`lastDigits: string`,<br>`userAddress: UserAddress` |
|
|
194
|
+
| **AndroidCardData** | Data related to a card that is to be added on Android platform wallets. | `network: string`,<br>`opaquePaymentCard: string`,<br>`cardHolderName: string`,<br>`lastDigits: string`,<br>`userAddress: UserAddress`,<br>`isVirtualCard?: boolean`,<br>`isBounceProvisioned?: boolean` |
|
|
193
195
|
| **AndroidResumeCardData** | Simplified data structure for resuming card addition to Google Wallet using existing token reference ID. | `network: string`,<br>`tokenReferenceID: string`,<br>`cardHolderName?: string`,<br>`lastDigits?: string` |
|
|
196
|
+
| **AndroidPaymentCredentialsResponse** | Response object containing encrypted payment credentials for UAPP flow. [Learn more →](./docs/UAPP.md) | `opaquePaymentCard: string`,<br>`googleOpaquePaymentCard: string` |
|
|
197
|
+
| **onPaymentCredentialsRequestPayload** | Payload received when Google Wallet requests payment credentials during UAPP provisioning. [Learn more →](./docs/UAPP.md) | `requestId: string`,<br>`serverSessionId: string`,<br>`walletId: string`,<br>`tokenRequestorId: string \| null`,<br>`opaquePaymentCard: string` |
|
|
194
198
|
| **UserAddress** | Structured address used for cardholder verification. | `name: string`,<br>`addressOne: string`,<br>`addressTwo: string`,<br>`city: string`,<br>`administrativeArea: string`,<br>`countryCode: string`,<br>`postalCode: string`,<br>`phoneNumber: string` |
|
|
195
199
|
| **IOSCardData** | Data related to a card that is to be added on iOS platform. | `network: string`,<br>`activationData: string`,<br>`encryptedPassData: string`,<br>`ephemeralPublicKey: string`,<br>`cardHolderTitle: string`,<br>`cardHolderName: string`,<br>`lastDigits: string`,<br>`cardDescription: string`,<br>`cardDescriptionComment: string` |
|
|
196
|
-
| **onCardActivatedPayload** | Data used by listener to notice when a card's status changes. | `tokenId
|
|
200
|
+
| **onCardActivatedPayload** | Data used by listener to notice when a card's status changes. | `tokenId?: string \| null`,<br> `status: 'activated' \| 'canceled'`<br> |
|
|
197
201
|
| **IOSIssuerCallback** | This callback is invoked with a nonce, its signature, and a certificate array obtained from Apple. It is expected that you will forward these details to your server or the card issuer's API to securely encrypt the payload required for adding cards to the Apple Wallet. | `(nonce: string, nonceSignature: string, certificate: string[]) => IOSEncryptPayload` |
|
|
198
202
|
| **IOSEncryptPayload** | An object containing the necessary elements to complete the addition of a card to Apple Wallet. | `encryptedPassData: string`,<br>`activationData: string`,<br>`ephemeralPublicKey: string` |
|
|
199
203
|
| **TokenInfo** | Information about a token stored in Google Wallet. | `identifier: string`,<br>`lastDigits: string`,<br>`tokenState: number` |
|
|
200
204
|
|
|
205
|
+
### AndroidCardData Optional Fields
|
|
206
|
+
|
|
207
|
+
The `AndroidCardData` type includes two optional boolean fields that provide additional information about the card being provisioned:
|
|
208
|
+
|
|
209
|
+
#### `isVirtualCard?: boolean`
|
|
210
|
+
|
|
211
|
+
Indicates whether the card being provisioned is a virtual card. Virtual cards are digitally-generated card numbers that are linked to a physical card account. They provide enhanced security by:
|
|
212
|
+
- Allowing users to make online purchases without exposing their physical card number
|
|
213
|
+
- Enabling easy cancellation and regeneration without affecting the physical card
|
|
214
|
+
- Providing transaction-specific or merchant-specific card numbers
|
|
215
|
+
|
|
216
|
+
When set to `true`, this flag informs Google Wallet that the card being provisioned is a virtual card, which may affect how the card is displayed and managed in the wallet interface.
|
|
217
|
+
|
|
218
|
+
#### `isBounceProvisioned?: boolean`
|
|
219
|
+
|
|
220
|
+
Indicates whether the card has been provisioned through "bounce provisioning". Bounce provisioning is a two-step provisioning flow where:
|
|
221
|
+
1. The user initially provisions a card directly through Google Wallet (outside your app)
|
|
222
|
+
2. Your app later "bounces" or redirects the user back to complete additional verification or setup steps
|
|
223
|
+
|
|
224
|
+
This flag helps Google Wallet track cards that were initially added through the Google Wallet app but require additional app-specific configuration or verification. When set to `true`, it signals that this card was previously provisioned through the Google Wallet interface and is now being completed or updated through your application.
|
|
225
|
+
|
|
201
226
|
## Card Status
|
|
202
227
|
|
|
203
228
|
| Type | Possible Values |
|
|
@@ -283,17 +308,44 @@ const base64ToHex = (base64: string): string => Buffer.from(base64, "base64").to
|
|
|
283
308
|
|
|
284
309
|
This will depend on the implementation of your backend/issuer so make sure to check with them otherwise IOS might fail to provision the card.
|
|
285
310
|
|
|
311
|
+
# UAPP (Unified Android Push Provisioning)
|
|
312
|
+
|
|
313
|
+
For Android applications that require enhanced security for card provisioning, this library supports UAPP (Unified Android Push Provisioning) flow with Google Wallet.
|
|
314
|
+
|
|
315
|
+
📖 **[Read the complete UAPP documentation →](./docs/UAPP.md)**
|
|
316
|
+
|
|
317
|
+
The UAPP guide covers:
|
|
318
|
+
- What UAPP is and how it works
|
|
319
|
+
- Step-by-step setup instructions
|
|
320
|
+
- Backend implementation requirements
|
|
321
|
+
- Error handling and troubleshooting
|
|
322
|
+
- Complete code examples
|
|
323
|
+
|
|
286
324
|
# Publishing your app
|
|
287
325
|
|
|
288
326
|
To successfully publish your app, you will need to navigate through a series of mandatory test cases on both platforms.
|
|
289
327
|
|
|
290
328
|
### Android
|
|
291
329
|
|
|
330
|
+
> [!WARNING]
|
|
331
|
+
> **Testing is mandatory before production release.** Your access to the Google Wallet production environment may be revoked if you do not provide the required test evidence for launch approval. Complete all testing requirements before deploying your app.
|
|
332
|
+
|
|
292
333
|
Before deploying your app to the Google Play Store, make sure you have taken care of the security when implementing Push Provisioning and properly tested your code. To accomplish it, please familiarize yourself with [the Google's App Push Provisioning best practices](https://developers.google.com/pay/issuers/apis/push-provisioning/android/best-practices).
|
|
293
334
|
|
|
294
335
|
The latest information about deploying apps with Google TapAndPay SDK can be found in the [pre-launch process](https://developers.google.com/pay/issuers/apis/push-provisioning/android/launch-process#step_3_issuer_app_product_review) and [beta tests](https://developers.google.com/pay/issuers/apis/push-provisioning/android/beta-testing) sections in Google documentation. Make sure to complete all of the steps specified by Google connected to the __Google's branding__, __API safety__, and __app stability__.
|
|
295
336
|
|
|
296
|
-
|
|
337
|
+
#### Testing Requirements
|
|
338
|
+
|
|
339
|
+
The app will need to be reviewed by Google. During this process, it will need to pass 4 mandatory test cases that are specified in the [official Google testing documentation](https://developers.google.com/pay/issuers/apis/push-provisioning/android/test-cases). They verify how your app handles card state tracking in different scenarios.
|
|
340
|
+
|
|
341
|
+
**Key testing criteria include:**
|
|
342
|
+
- Proper handling of card states (active, pending, suspended, etc.)
|
|
343
|
+
- Correct UI behavior when cards are already provisioned
|
|
344
|
+
- Secure handling of sensitive payment data
|
|
345
|
+
- Compliance with Google's branding guidelines
|
|
346
|
+
- Proper error handling and user feedback
|
|
347
|
+
|
|
348
|
+
Please refer to the [complete testing criteria documentation](https://developers.google.com/pay/issuers/apis/push-provisioning/android/test-cases) before submitting your app for review.
|
|
297
349
|
|
|
298
350
|
> [!NOTE]
|
|
299
351
|
>Please make sure to hide the `Add to Google Wallet` buttons when cards are already added to the wallet.
|
|
@@ -29,12 +29,17 @@ object Utils {
|
|
|
29
29
|
.setPhoneNumber(addressMap.getString("phoneNumber") ?: "")
|
|
30
30
|
.build()
|
|
31
31
|
|
|
32
|
+
val isVirtual = if (hasKey("isVirtualCard")) this.getBoolean("isVirtualCard") else false
|
|
33
|
+
val isBounce = if (hasKey("isBounceProvisioned")) this.getBoolean("isBounceProvisioned") else false
|
|
34
|
+
|
|
32
35
|
return CardData(
|
|
33
36
|
network = this.getString("network") ?: "",
|
|
34
37
|
opaquePaymentCard = this.getString("opaquePaymentCard") ?: "",
|
|
35
38
|
cardHolderName = this.getString("cardHolderName") ?: "",
|
|
36
39
|
lastDigits = this.getString("lastDigits") ?: "",
|
|
37
|
-
userAddress = userAddress
|
|
40
|
+
userAddress = userAddress,
|
|
41
|
+
isVirtualCard = isVirtual,
|
|
42
|
+
isBounceProvisioned = isBounce,
|
|
38
43
|
)
|
|
39
44
|
}
|
|
40
45
|
|
|
@@ -5,6 +5,15 @@ import android.app.Activity.RESULT_CANCELED
|
|
|
5
5
|
import android.app.Activity.RESULT_OK
|
|
6
6
|
import android.content.ActivityNotFoundException
|
|
7
7
|
import android.content.Intent
|
|
8
|
+
import androidx.core.content.IntentCompat
|
|
9
|
+
import com.expensify.wallet.Utils.getAsyncResult
|
|
10
|
+
import com.expensify.wallet.Utils.toCardData
|
|
11
|
+
import com.expensify.wallet.error.InvalidNetworkError
|
|
12
|
+
import com.expensify.wallet.event.OnCardActivatedEvent
|
|
13
|
+
import com.expensify.wallet.model.CardData
|
|
14
|
+
import com.expensify.wallet.model.CardStatus
|
|
15
|
+
import com.expensify.wallet.model.TokenizationStatus
|
|
16
|
+
import com.expensify.wallet.model.WalletData
|
|
8
17
|
import com.facebook.react.bridge.ActivityEventListener
|
|
9
18
|
import com.facebook.react.bridge.Arguments
|
|
10
19
|
import com.facebook.react.bridge.Promise
|
|
@@ -15,25 +24,35 @@ import com.facebook.react.bridge.ReactMethod
|
|
|
15
24
|
import com.facebook.react.bridge.ReadableMap
|
|
16
25
|
import com.facebook.react.bridge.WritableMap
|
|
17
26
|
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
27
|
+
import com.google.android.gms.common.api.ApiException
|
|
18
28
|
import com.google.android.gms.tapandpay.TapAndPay
|
|
19
29
|
import com.google.android.gms.tapandpay.TapAndPayClient
|
|
30
|
+
import com.google.android.gms.tapandpay.issuer.GeneratePaymentCredentialsException
|
|
31
|
+
import com.google.android.gms.tapandpay.issuer.GeneratePaymentCredentialsRequest
|
|
32
|
+
import com.google.android.gms.tapandpay.issuer.GeneratePaymentCredentialsResponse
|
|
33
|
+
import com.google.android.gms.tapandpay.issuer.GetTokenStatusRequest
|
|
34
|
+
import com.google.android.gms.tapandpay.issuer.HasEligibleTokenizationTargetRequest
|
|
35
|
+
import com.google.android.gms.tapandpay.issuer.ListTokensRequest
|
|
36
|
+
import com.google.android.gms.tapandpay.issuer.PaymentCredentialsGenerator
|
|
37
|
+
import com.google.android.gms.tapandpay.issuer.PushTokenizeExtraOptions
|
|
20
38
|
import com.google.android.gms.tapandpay.issuer.PushTokenizeRequest
|
|
39
|
+
import com.google.android.gms.tapandpay.issuer.PushTokenizeResult
|
|
40
|
+
import com.google.android.gms.tapandpay.issuer.TokenizeRequest
|
|
41
|
+
import kotlinx.coroutines.CompletableDeferred
|
|
21
42
|
import kotlinx.coroutines.CoroutineScope
|
|
43
|
+
import kotlinx.coroutines.CoroutineStart
|
|
44
|
+
import kotlinx.coroutines.Deferred
|
|
22
45
|
import kotlinx.coroutines.Dispatchers
|
|
23
46
|
import kotlinx.coroutines.launch
|
|
24
47
|
import kotlinx.coroutines.SupervisorJob
|
|
25
48
|
import kotlinx.coroutines.cancel
|
|
26
|
-
import
|
|
27
|
-
import com.expensify.wallet.Utils.toCardData
|
|
28
|
-
import com.expensify.wallet.error.InvalidNetworkError
|
|
29
|
-
import com.expensify.wallet.event.OnCardActivatedEvent
|
|
30
|
-
import com.expensify.wallet.model.CardStatus
|
|
31
|
-
import com.expensify.wallet.model.TokenizationStatus
|
|
32
|
-
import com.expensify.wallet.model.WalletData
|
|
33
|
-
import com.google.android.gms.common.api.ApiException
|
|
34
|
-
import kotlinx.coroutines.Deferred
|
|
49
|
+
import kotlinx.coroutines.withTimeoutOrNull
|
|
35
50
|
import java.nio.charset.Charset
|
|
36
51
|
import java.util.Locale
|
|
52
|
+
import java.util.UUID
|
|
53
|
+
import java.util.concurrent.CompletableFuture
|
|
54
|
+
import java.util.concurrent.ConcurrentHashMap
|
|
55
|
+
import java.util.concurrent.Future
|
|
37
56
|
|
|
38
57
|
|
|
39
58
|
class WalletModule internal constructor(context: ReactApplicationContext) :
|
|
@@ -41,11 +60,14 @@ class WalletModule internal constructor(context: ReactApplicationContext) :
|
|
|
41
60
|
companion object {
|
|
42
61
|
const val NAME = "RNWallet"
|
|
43
62
|
const val REQUEST_CODE_PUSH_TOKENIZE: Int = 0xA001
|
|
63
|
+
const val REQUEST_CODE_RESUME_TOKENIZE: Int = 0xA003
|
|
44
64
|
const val REQUEST_CREATE_WALLET: Int = 0xA002
|
|
45
65
|
|
|
46
66
|
const val E_SDK_API = "SDK API Error"
|
|
47
67
|
const val E_OPERATION_FAILED = "E_OPERATION_FAILED"
|
|
48
68
|
const val E_INVALID_DATA = "E_INVALID_DATA"
|
|
69
|
+
|
|
70
|
+
const val PAYMENT_CREDENTIALS_TIMEOUT_MS = 60_000L
|
|
49
71
|
}
|
|
50
72
|
|
|
51
73
|
private val activity: Activity
|
|
@@ -57,6 +79,8 @@ class WalletModule internal constructor(context: ReactApplicationContext) :
|
|
|
57
79
|
private var pendingPushTokenizePromise: Promise? = null
|
|
58
80
|
private val moduleScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
|
|
59
81
|
|
|
82
|
+
private val pendingPaymentCredentials = ConcurrentHashMap<String, CompletableDeferred<GeneratePaymentCredentialsResponse>>()
|
|
83
|
+
|
|
60
84
|
override fun initialize() {
|
|
61
85
|
super.initialize()
|
|
62
86
|
reactApplicationContext.addActivityEventListener(cardListener)
|
|
@@ -66,42 +90,100 @@ class WalletModule internal constructor(context: ReactApplicationContext) :
|
|
|
66
90
|
super.invalidate()
|
|
67
91
|
moduleScope.cancel()
|
|
68
92
|
reactApplicationContext.removeActivityEventListener(cardListener)
|
|
93
|
+
pendingPaymentCredentials.clear()
|
|
69
94
|
}
|
|
70
95
|
|
|
71
96
|
private val cardListener = object : ActivityEventListener {
|
|
72
97
|
override fun onActivityResult(
|
|
73
98
|
activity: Activity, requestCode: Int, resultCode: Int, data: Intent?
|
|
74
99
|
) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
val localPromise = pendingPushTokenizePromise
|
|
80
|
-
pendingPushTokenizePromise = null
|
|
81
|
-
if (resultCode == RESULT_OK) {
|
|
82
|
-
val tokenId = data?.getStringExtra(TapAndPay.EXTRA_ISSUER_TOKEN_ID)
|
|
83
|
-
sendEvent(
|
|
84
|
-
context,
|
|
85
|
-
OnCardActivatedEvent.NAME,
|
|
86
|
-
OnCardActivatedEvent("activated", tokenId).toMap()
|
|
87
|
-
)
|
|
88
|
-
localPromise?.resolve(TokenizationStatus.SUCCESS.code)
|
|
89
|
-
} else if (resultCode == RESULT_CANCELED) {
|
|
90
|
-
sendEvent(
|
|
91
|
-
context,
|
|
92
|
-
OnCardActivatedEvent.NAME,
|
|
93
|
-
OnCardActivatedEvent("canceled", null).toMap()
|
|
94
|
-
)
|
|
95
|
-
localPromise?.resolve(TokenizationStatus.CANCELED.code)
|
|
96
|
-
} else {
|
|
97
|
-
localPromise?.resolve(TokenizationStatus.ERROR.code)
|
|
100
|
+
when (requestCode) {
|
|
101
|
+
REQUEST_CREATE_WALLET -> {
|
|
102
|
+
pendingCreateWalletPromise?.resolve(resultCode == RESULT_OK)
|
|
103
|
+
pendingCreateWalletPromise = null
|
|
98
104
|
}
|
|
105
|
+
REQUEST_CODE_RESUME_TOKENIZE -> handleResumeTokenizeResult(resultCode, data)
|
|
106
|
+
REQUEST_CODE_PUSH_TOKENIZE -> handlePushTokenizeResult(resultCode, data)
|
|
99
107
|
}
|
|
100
108
|
}
|
|
101
109
|
|
|
102
110
|
override fun onNewIntent(intent: Intent) {}
|
|
103
111
|
}
|
|
104
112
|
|
|
113
|
+
private fun takePendingTokenizePromise(): Promise? =
|
|
114
|
+
pendingPushTokenizePromise.also { pendingPushTokenizePromise = null }
|
|
115
|
+
|
|
116
|
+
private fun sendCardActivatedEvent(status: String, tokenId: String?) {
|
|
117
|
+
sendEvent(reactApplicationContext, OnCardActivatedEvent.NAME, OnCardActivatedEvent(status, tokenId).toMap())
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private fun handleResumeTokenizeResult(resultCode: Int, data: Intent?) {
|
|
121
|
+
val promise = takePendingTokenizePromise()
|
|
122
|
+
when (resultCode) {
|
|
123
|
+
RESULT_OK -> {
|
|
124
|
+
sendCardActivatedEvent("activated", data?.getStringExtra(TapAndPay.EXTRA_ISSUER_TOKEN_ID))
|
|
125
|
+
promise?.resolve(TokenizationStatus.SUCCESS.code)
|
|
126
|
+
}
|
|
127
|
+
RESULT_CANCELED -> {
|
|
128
|
+
sendCardActivatedEvent("canceled", null)
|
|
129
|
+
promise?.resolve(TokenizationStatus.CANCELED.code)
|
|
130
|
+
}
|
|
131
|
+
else -> promise?.resolve(TokenizationStatus.ERROR.code)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private fun handlePushTokenizeResult(resultCode: Int, data: Intent?) {
|
|
136
|
+
val promise = takePendingTokenizePromise()
|
|
137
|
+
if (resultCode == RESULT_CANCELED) {
|
|
138
|
+
sendCardActivatedEvent("canceled", null)
|
|
139
|
+
promise?.resolve(TokenizationStatus.CANCELED.code)
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
val result = data?.let {
|
|
144
|
+
IntentCompat.getParcelableExtra(it, TapAndPay.EXTRA_PUSH_TOKENIZE_RESULT, PushTokenizeResult::class.java)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (result == null) {
|
|
148
|
+
handlePushTokenizeResultExtras(resultCode, data, promise)
|
|
149
|
+
return
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
val successfulOutcome = result.tokenizationOutcomes.firstOrNull { it.tokenResult }
|
|
153
|
+
if (result.cardResult || successfulOutcome != null) {
|
|
154
|
+
sendCardActivatedEvent("activated", successfulOutcome?.issuerTokenId)
|
|
155
|
+
promise?.resolve(TokenizationStatus.SUCCESS.code)
|
|
156
|
+
} else {
|
|
157
|
+
promise?.reject(E_OPERATION_FAILED, "Card not saved. Status: ${result.cardStatus}")
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Google documents the result both as PushTokenizeResult and as separate extras, so read the extras when the object is missing
|
|
162
|
+
private fun handlePushTokenizeResultExtras(resultCode: Int, data: Intent?, promise: Promise?) {
|
|
163
|
+
val hasTokenResult = data?.hasExtra(TapAndPay.EXTRA_TOKEN_RESULT) == true
|
|
164
|
+
val hasCardResult = data?.hasExtra(TapAndPay.EXTRA_CARD_RESULT) == true
|
|
165
|
+
|
|
166
|
+
if (!hasTokenResult && !hasCardResult) {
|
|
167
|
+
if (resultCode == RESULT_OK) {
|
|
168
|
+
sendCardActivatedEvent("activated", data?.getStringExtra(TapAndPay.EXTRA_ISSUER_TOKEN_ID))
|
|
169
|
+
promise?.resolve(TokenizationStatus.SUCCESS.code)
|
|
170
|
+
} else {
|
|
171
|
+
promise?.resolve(TokenizationStatus.ERROR.code)
|
|
172
|
+
}
|
|
173
|
+
return
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
val tokenResult = data?.getBooleanExtra(TapAndPay.EXTRA_TOKEN_RESULT, false) == true
|
|
177
|
+
val cardResult = data?.getBooleanExtra(TapAndPay.EXTRA_CARD_RESULT, false) == true
|
|
178
|
+
if (tokenResult || cardResult) {
|
|
179
|
+
val tokenId = if (tokenResult) data?.getStringExtra(TapAndPay.EXTRA_ISSUER_TOKEN_ID) else null
|
|
180
|
+
sendCardActivatedEvent("activated", tokenId)
|
|
181
|
+
promise?.resolve(TokenizationStatus.SUCCESS.code)
|
|
182
|
+
} else {
|
|
183
|
+
promise?.reject(E_OPERATION_FAILED, "Card not saved")
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
105
187
|
@ReactMethod
|
|
106
188
|
override fun ensureGoogleWalletInitialized(promise: Promise) {
|
|
107
189
|
val localPromise = PromiseImpl({ _ ->
|
|
@@ -147,7 +229,7 @@ class WalletModule internal constructor(context: ReactApplicationContext) :
|
|
|
147
229
|
|
|
148
230
|
@ReactMethod
|
|
149
231
|
override fun getCardStatusBySuffix(last4Digits: String, promise: Promise) {
|
|
150
|
-
tapAndPayClient.listTokens()
|
|
232
|
+
tapAndPayClient.listTokens(ListTokensRequest.Builder().build())
|
|
151
233
|
.addOnCompleteListener { task ->
|
|
152
234
|
if (!task.isSuccessful || task.result == null) {
|
|
153
235
|
promise.resolve(CardStatus.NOT_FOUND_IN_WALLET.code)
|
|
@@ -166,7 +248,12 @@ class WalletModule internal constructor(context: ReactApplicationContext) :
|
|
|
166
248
|
|
|
167
249
|
@ReactMethod
|
|
168
250
|
override fun getCardStatusByIdentifier(identifier: String, tsp: String, promise: Promise) {
|
|
169
|
-
tapAndPayClient.getTokenStatus(
|
|
251
|
+
tapAndPayClient.getTokenStatus(
|
|
252
|
+
GetTokenStatusRequest.Builder()
|
|
253
|
+
.setTokenServiceProvider(getTokenServiceProvider(tsp))
|
|
254
|
+
.setIssuerTokenId(identifier)
|
|
255
|
+
.build()
|
|
256
|
+
)
|
|
170
257
|
.addOnCompleteListener { task ->
|
|
171
258
|
if (!task.isSuccessful || task.result == null) {
|
|
172
259
|
promise.resolve(CardStatus.NOT_FOUND_IN_WALLET.code)
|
|
@@ -183,6 +270,25 @@ class WalletModule internal constructor(context: ReactApplicationContext) :
|
|
|
183
270
|
}
|
|
184
271
|
}
|
|
185
272
|
|
|
273
|
+
@ReactMethod
|
|
274
|
+
override fun hasEligibleTokenizationTarget(last4Digits: String, network: String, issuerName: String, promise: Promise) {
|
|
275
|
+
try {
|
|
276
|
+
val request = HasEligibleTokenizationTargetRequest.Builder()
|
|
277
|
+
.setIdentifier(last4Digits)
|
|
278
|
+
.setNetwork(getCardNetwork(network))
|
|
279
|
+
.setTokenServiceProvider(getTokenServiceProvider(network))
|
|
280
|
+
.setIssuerName(issuerName)
|
|
281
|
+
.build()
|
|
282
|
+
tapAndPayClient.hasEligibleTokenizationTarget(request)
|
|
283
|
+
.addOnSuccessListener { promise.resolve(it) }
|
|
284
|
+
.addOnFailureListener { e ->
|
|
285
|
+
promise.reject(E_OPERATION_FAILED, "hasEligibleTokenizationTarget: ${e.localizedMessage}")
|
|
286
|
+
}
|
|
287
|
+
} catch (e: InvalidNetworkError) {
|
|
288
|
+
promise.reject(E_INVALID_DATA, e.message)
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
186
292
|
@ReactMethod
|
|
187
293
|
override fun addCardToGoogleWallet(
|
|
188
294
|
data: ReadableMap, promise: Promise
|
|
@@ -197,6 +303,11 @@ class WalletModule internal constructor(context: ReactApplicationContext) :
|
|
|
197
303
|
val displayName = getDisplayName(data, cardData.network)
|
|
198
304
|
pendingPushTokenizePromise = promise
|
|
199
305
|
|
|
306
|
+
val pushTokenizeExtraOptions = PushTokenizeExtraOptions.newBuilder()
|
|
307
|
+
.setIsBounceProvisioned(cardData.isBounceProvisioned)
|
|
308
|
+
.setEnrollForVirtualCards(cardData.isVirtualCard)
|
|
309
|
+
.build()
|
|
310
|
+
|
|
200
311
|
val pushTokenizeRequest = PushTokenizeRequest.Builder()
|
|
201
312
|
.setOpaquePaymentCard(cardData.opaquePaymentCard.toByteArray(Charset.forName("UTF-8")))
|
|
202
313
|
.setNetwork(cardNetwork)
|
|
@@ -204,17 +315,130 @@ class WalletModule internal constructor(context: ReactApplicationContext) :
|
|
|
204
315
|
.setDisplayName(displayName)
|
|
205
316
|
.setLastDigits(cardData.lastDigits)
|
|
206
317
|
.setUserAddress(cardData.userAddress)
|
|
318
|
+
.setPaymentCredentialsGenerator(createPaymentCredentialsGenerator(cardData))
|
|
319
|
+
.setPushTokenizeExtraOptions(pushTokenizeExtraOptions)
|
|
207
320
|
.build()
|
|
208
321
|
|
|
209
|
-
tapAndPayClient.pushTokenize(
|
|
210
|
-
|
|
211
|
-
|
|
322
|
+
tapAndPayClient.pushTokenize(pushTokenizeRequest)
|
|
323
|
+
.addOnSuccessListener { pendingIntent ->
|
|
324
|
+
try {
|
|
325
|
+
activity.startIntentSenderForResult(
|
|
326
|
+
pendingIntent.intentSender,
|
|
327
|
+
REQUEST_CODE_PUSH_TOKENIZE,
|
|
328
|
+
null, 0, 0, 0
|
|
329
|
+
)
|
|
330
|
+
} catch (e: Exception) {
|
|
331
|
+
pendingPushTokenizePromise = null
|
|
332
|
+
promise.reject(E_OPERATION_FAILED, "Failed to launch Google Pay: ${e.message}")
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
.addOnFailureListener { e ->
|
|
336
|
+
pendingPushTokenizePromise = null
|
|
337
|
+
promise.reject(E_OPERATION_FAILED, "Google Pay API Error: ${e.message}")
|
|
338
|
+
}
|
|
212
339
|
} catch (e: java.lang.Exception) {
|
|
213
340
|
pendingPushTokenizePromise = null
|
|
214
341
|
promise.reject(e)
|
|
215
342
|
}
|
|
216
343
|
}
|
|
217
344
|
|
|
345
|
+
// Google Wallet only understands GeneratePaymentCredentialsException; UNKNOWN_ERROR covers backend failures and timeouts
|
|
346
|
+
private fun paymentCredentialsError() =
|
|
347
|
+
GeneratePaymentCredentialsException(GeneratePaymentCredentialsException.UNKNOWN_ERROR)
|
|
348
|
+
|
|
349
|
+
private fun createPaymentCredentialsGenerator(
|
|
350
|
+
cardData: CardData
|
|
351
|
+
): PaymentCredentialsGenerator {
|
|
352
|
+
return object : PaymentCredentialsGenerator {
|
|
353
|
+
override fun generate(request: GeneratePaymentCredentialsRequest): Future<GeneratePaymentCredentialsResponse> {
|
|
354
|
+
if (!request.googleOpaquePaymentCardRequested) {
|
|
355
|
+
val response = GeneratePaymentCredentialsResponse.Builder()
|
|
356
|
+
.setOpaquePaymentCard(cardData.opaquePaymentCard.toByteArray(Charsets.UTF_8))
|
|
357
|
+
.build()
|
|
358
|
+
|
|
359
|
+
return CompletableFuture.completedFuture(response)
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
val future = CompletableFuture<GeneratePaymentCredentialsResponse>()
|
|
363
|
+
val credentials = CompletableDeferred<GeneratePaymentCredentialsResponse>()
|
|
364
|
+
val requestId = UUID.randomUUID().toString()
|
|
365
|
+
pendingPaymentCredentials[requestId] = credentials
|
|
366
|
+
moduleScope.launch(start = CoroutineStart.UNDISPATCHED) {
|
|
367
|
+
// null on timeout, on reject, or when invalidate() cancels the scope
|
|
368
|
+
val response = runCatching {
|
|
369
|
+
withTimeoutOrNull(PAYMENT_CREDENTIALS_TIMEOUT_MS) { credentials.await() }
|
|
370
|
+
}.getOrNull()
|
|
371
|
+
pendingPaymentCredentials.remove(requestId)
|
|
372
|
+
if (response != null) {
|
|
373
|
+
future.complete(response)
|
|
374
|
+
} else {
|
|
375
|
+
future.completeExceptionally(paymentCredentialsError())
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
val params = Arguments.createMap().apply {
|
|
380
|
+
putString("requestId", requestId)
|
|
381
|
+
putString("serverSessionId", request.serverSessionId)
|
|
382
|
+
putString("walletId", request.walletId)
|
|
383
|
+
putString("tokenRequestorId", request.tokenRequestorId)
|
|
384
|
+
putString("opaquePaymentCard", cardData.opaquePaymentCard)
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// send event to the RN so the Google OPC can be generated on the backend
|
|
388
|
+
sendEvent(reactApplicationContext, "onPaymentCredentialsRequest", params)
|
|
389
|
+
return future
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// This switch enables the UAPP flow
|
|
393
|
+
override fun getGoogleOpaquePaymentCardSupported(): Boolean {
|
|
394
|
+
return true
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
@ReactMethod
|
|
400
|
+
override fun AndroidProvidePaymentCredentials(requestId: String, responseData: ReadableMap, promise: Promise) {
|
|
401
|
+
val credentials = pendingPaymentCredentials.remove(requestId)
|
|
402
|
+
if (credentials == null) {
|
|
403
|
+
promise.reject(E_OPERATION_FAILED, "Request ID not found or timed out")
|
|
404
|
+
return
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
try {
|
|
408
|
+
val tspOpc = responseData.getString("opaquePaymentCard")
|
|
409
|
+
val googleOpc = responseData.getString("googleOpaquePaymentCard")
|
|
410
|
+
|
|
411
|
+
if (tspOpc == null) {
|
|
412
|
+
throw Exception("opaquePaymentCard is required")
|
|
413
|
+
}
|
|
414
|
+
if (googleOpc == null) {
|
|
415
|
+
throw Exception("googleOpaquePaymentCard is required")
|
|
416
|
+
}
|
|
417
|
+
val response = GeneratePaymentCredentialsResponse.Builder()
|
|
418
|
+
.setGoogleOpaquePaymentCard(googleOpc.toByteArray(Charsets.UTF_8))
|
|
419
|
+
.setOpaquePaymentCard(tspOpc.toByteArray(Charsets.UTF_8))
|
|
420
|
+
.build()
|
|
421
|
+
|
|
422
|
+
credentials.complete(response)
|
|
423
|
+
promise.resolve(true)
|
|
424
|
+
|
|
425
|
+
} catch (e: Exception) {
|
|
426
|
+
credentials.completeExceptionally(paymentCredentialsError())
|
|
427
|
+
promise.reject(E_OPERATION_FAILED, e.message)
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
@ReactMethod
|
|
432
|
+
override fun AndroidRejectPaymentCredentials(requestId: String, errorMessage: String, promise: Promise) {
|
|
433
|
+
val credentials = pendingPaymentCredentials.remove(requestId)
|
|
434
|
+
if (credentials == null) {
|
|
435
|
+
promise.reject(E_OPERATION_FAILED, "Request ID not found or timed out")
|
|
436
|
+
return
|
|
437
|
+
}
|
|
438
|
+
credentials.completeExceptionally(paymentCredentialsError())
|
|
439
|
+
promise.resolve(true)
|
|
440
|
+
}
|
|
441
|
+
|
|
218
442
|
@ReactMethod
|
|
219
443
|
override fun resumeAddCardToGoogleWallet(data: ReadableMap, promise: Promise) {
|
|
220
444
|
if (pendingPushTokenizePromise != null) {
|
|
@@ -232,14 +456,13 @@ class WalletModule internal constructor(context: ReactApplicationContext) :
|
|
|
232
456
|
val displayName = getDisplayName(data, network)
|
|
233
457
|
pendingPushTokenizePromise = promise
|
|
234
458
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
)
|
|
459
|
+
val tokenizeRequest = TokenizeRequest.Builder()
|
|
460
|
+
.setIssuerTokenId(tokenReferenceID)
|
|
461
|
+
.setTokenServiceProvider(tokenServiceProvider)
|
|
462
|
+
.setCardDisplayName(displayName)
|
|
463
|
+
.setCardNetwork(cardNetwork)
|
|
464
|
+
.build()
|
|
465
|
+
tapAndPayClient.tokenize(activity, tokenizeRequest, REQUEST_CODE_RESUME_TOKENIZE)
|
|
243
466
|
} catch (e: java.lang.Exception) {
|
|
244
467
|
pendingPushTokenizePromise = null
|
|
245
468
|
promise.reject(e)
|
|
@@ -248,13 +471,13 @@ class WalletModule internal constructor(context: ReactApplicationContext) :
|
|
|
248
471
|
|
|
249
472
|
@ReactMethod
|
|
250
473
|
override fun listTokens(promise: Promise) {
|
|
251
|
-
tapAndPayClient.listTokens()
|
|
474
|
+
tapAndPayClient.listTokens(ListTokensRequest.Builder().build())
|
|
252
475
|
.addOnCompleteListener { task ->
|
|
253
476
|
if (!task.isSuccessful || task.result == null) {
|
|
254
477
|
promise.resolve(Arguments.createArray())
|
|
255
478
|
return@addOnCompleteListener
|
|
256
479
|
}
|
|
257
|
-
|
|
480
|
+
|
|
258
481
|
val tokensArray = Arguments.createArray()
|
|
259
482
|
task.result.forEach { tokenInfo ->
|
|
260
483
|
val tokenData = Arguments.createMap().apply {
|
|
@@ -264,7 +487,7 @@ class WalletModule internal constructor(context: ReactApplicationContext) :
|
|
|
264
487
|
}
|
|
265
488
|
tokensArray.pushMap(tokenData)
|
|
266
489
|
}
|
|
267
|
-
|
|
490
|
+
|
|
268
491
|
promise.resolve(tokensArray)
|
|
269
492
|
}
|
|
270
493
|
.addOnFailureListener { e ->
|
|
@@ -341,13 +564,13 @@ class WalletModule internal constructor(context: ReactApplicationContext) :
|
|
|
341
564
|
data.getString("cardHolderName")?.let { name ->
|
|
342
565
|
if (name.isNotEmpty()) return name
|
|
343
566
|
}
|
|
344
|
-
|
|
567
|
+
|
|
345
568
|
data.getString("lastDigits")?.let { digits ->
|
|
346
569
|
if (digits.isNotEmpty()) {
|
|
347
570
|
return "${network.uppercase(Locale.getDefault())} Card *$digits"
|
|
348
571
|
}
|
|
349
572
|
}
|
|
350
|
-
|
|
573
|
+
|
|
351
574
|
return "${network.uppercase(Locale.getDefault())} Card"
|
|
352
575
|
}
|
|
353
576
|
|
|
@@ -5,8 +5,10 @@ import com.google.android.gms.tapandpay.issuer.UserAddress
|
|
|
5
5
|
data class CardData(
|
|
6
6
|
val platform: String = "android",
|
|
7
7
|
val network: String,
|
|
8
|
-
val opaquePaymentCard: String,
|
|
8
|
+
val opaquePaymentCard: String, // TSP OPC
|
|
9
9
|
val cardHolderName: String,
|
|
10
10
|
val lastDigits: String,
|
|
11
11
|
val userAddress: UserAddress,
|
|
12
|
+
val isVirtualCard: Boolean = false,
|
|
13
|
+
val isBounceProvisioned: Boolean = false,
|
|
12
14
|
)
|
|
@@ -57,6 +57,14 @@ public abstract class NativeWalletSpec extends ReactContextBaseJavaModule implem
|
|
|
57
57
|
@DoNotStrip
|
|
58
58
|
public abstract void addCardToGoogleWallet(ReadableMap cardData, Promise promise);
|
|
59
59
|
|
|
60
|
+
@ReactMethod
|
|
61
|
+
@DoNotStrip
|
|
62
|
+
public abstract void AndroidProvidePaymentCredentials(String requestId, ReadableMap responseData, Promise promise);
|
|
63
|
+
|
|
64
|
+
@ReactMethod
|
|
65
|
+
@DoNotStrip
|
|
66
|
+
public abstract void AndroidRejectPaymentCredentials(String requestId, String errorMessage, Promise promise);
|
|
67
|
+
|
|
60
68
|
@ReactMethod
|
|
61
69
|
@DoNotStrip
|
|
62
70
|
public abstract void resumeAddCardToGoogleWallet(ReadableMap cardData, Promise promise);
|
package/ios/RNWallet.mm
CHANGED
|
@@ -135,6 +135,36 @@ RCT_REMAP_METHOD(getCardStatusByIdentifier,
|
|
|
135
135
|
// no-op
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
- (void)hasEligibleTokenizationTarget:(NSString *)last4Digits
|
|
139
|
+
network:(NSString *)network
|
|
140
|
+
issuerName:(NSString *)issuerName
|
|
141
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
142
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
143
|
+
{
|
|
144
|
+
// no-op
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
- (void)AndroidProvidePaymentCredentials:(NSString *)requestId
|
|
148
|
+
responseData:
|
|
149
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
150
|
+
(JS::NativeWallet::AndroidPaymentCredentialsResponse &)responseData
|
|
151
|
+
#else
|
|
152
|
+
(NSDictionary *)responseData
|
|
153
|
+
#endif
|
|
154
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
155
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
156
|
+
{
|
|
157
|
+
// no-op
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
- (void)AndroidRejectPaymentCredentials:(NSString *)requestId
|
|
161
|
+
errorMessage:(NSString *)errorMessage
|
|
162
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
163
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
164
|
+
{
|
|
165
|
+
// no-op
|
|
166
|
+
}
|
|
167
|
+
|
|
138
168
|
- (void)getSecureWalletInfo:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
139
169
|
// no-op
|
|
140
170
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_reactNative","require","PACKAGE_NAME","exports","Wallet","TurboModuleRegistry","getEnforcing","error","Error","console","warn","message","_default","default"],"sourceRoot":"../../src","sources":["NativeWallet.ts"],"mappings":";;;;;;AACA,IAAAA,YAAA,GAAAC,OAAA;
|
|
1
|
+
{"version":3,"names":["_reactNative","require","PACKAGE_NAME","exports","Wallet","TurboModuleRegistry","getEnforcing","error","Error","console","warn","message","_default","default"],"sourceRoot":"../../src","sources":["NativeWallet.ts"],"mappings":";;;;;;AACA,IAAAA,YAAA,GAAAC,OAAA;AAuGA,MAAMC,YAAY,GAAAC,OAAA,CAAAD,YAAA,GAAG,gCAAgC;AACrD;AACA;AACA;AACA,IAAIE,MAAwB;AAC5B,IAAI;EACFA,MAAM,GAAGC,gCAAmB,CAACC,YAAY,CAAO,UAAU,CAAC;AAC7D,CAAC,CAAC,OAAOC,KAAK,EAAE;EACd,IAAIA,KAAK,YAAYC,KAAK,EAAE;IAC1B;IACAC,OAAO,CAACC,IAAI,CAAC,IAAIR,YAAY,mCAAmCK,KAAK,CAACI,OAAO,EAAE,CAAC;EAClF;AACF;AAAC,IAAAC,QAAA,GAAAT,OAAA,CAAAU,OAAA,GACcT,MAAM","ignoreList":[]}
|
package/lib/commonjs/index.js
CHANGED
|
@@ -16,6 +16,7 @@ exports.checkWalletAvailability = checkWalletAvailability;
|
|
|
16
16
|
exports.getCardStatusByIdentifier = getCardStatusByIdentifier;
|
|
17
17
|
exports.getCardStatusBySuffix = getCardStatusBySuffix;
|
|
18
18
|
exports.getSecureWalletInfo = getSecureWalletInfo;
|
|
19
|
+
exports.hasEligibleTokenizationTarget = hasEligibleTokenizationTarget;
|
|
19
20
|
exports.listTokens = listTokens;
|
|
20
21
|
exports.removeListener = removeListener;
|
|
21
22
|
exports.resumeAddCardToGoogleWallet = resumeAddCardToGoogleWallet;
|
|
@@ -25,12 +26,31 @@ var _utils = require("./utils");
|
|
|
25
26
|
var _AddToWalletButton = _interopRequireDefault(require("./AddToWalletButton"));
|
|
26
27
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
27
28
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
29
|
+
/* eslint-disable no-console */
|
|
28
30
|
/* eslint-disable @lwc/lwc/no-async-await */
|
|
29
31
|
|
|
30
32
|
function getModuleLinkingRejection() {
|
|
31
33
|
return Promise.reject(new Error(`Failed to load Wallet module, make sure to link ${_NativeWallet.PACKAGE_NAME} correctly`));
|
|
32
34
|
}
|
|
33
35
|
let eventEmitter;
|
|
36
|
+
let paymentCredentialsHandler = null;
|
|
37
|
+
if (_reactNative.Platform.OS === 'android' && _NativeWallet.default) {
|
|
38
|
+
eventEmitter ??= new _reactNative.NativeEventEmitter(_NativeWallet.default);
|
|
39
|
+
eventEmitter.addListener('onPaymentCredentialsRequest', async data => {
|
|
40
|
+
if (!paymentCredentialsHandler) {
|
|
41
|
+
console.warn(`[${_NativeWallet.PACKAGE_NAME}] Received payment credentials request but no handler is set`);
|
|
42
|
+
await (_NativeWallet.default === null || _NativeWallet.default === void 0 ? void 0 : _NativeWallet.default.AndroidRejectPaymentCredentials(data.requestId, 'No payment credentials handler is set').catch(() => undefined));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const responseData = await paymentCredentialsHandler(data);
|
|
47
|
+
await (_NativeWallet.default === null || _NativeWallet.default === void 0 ? void 0 : _NativeWallet.default.AndroidProvidePaymentCredentials(data.requestId, responseData));
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error(`[${_NativeWallet.PACKAGE_NAME}] Error handling payment credentials request:`, error);
|
|
50
|
+
await (_NativeWallet.default === null || _NativeWallet.default === void 0 ? void 0 : _NativeWallet.default.AndroidRejectPaymentCredentials(data.requestId, error instanceof Error ? error.message : String(error)).catch(() => undefined));
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
34
54
|
function addListener(event, callback) {
|
|
35
55
|
if (!_NativeWallet.default) {
|
|
36
56
|
// eslint-disable-next-line no-console
|
|
@@ -85,7 +105,25 @@ async function getCardStatusByIdentifier(identifier, tsp) {
|
|
|
85
105
|
const tokenState = await _NativeWallet.default.getCardStatusByIdentifier(identifier, tsp.toUpperCase());
|
|
86
106
|
return (0, _utils.getCardState)(tokenState);
|
|
87
107
|
}
|
|
88
|
-
|
|
108
|
+
|
|
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, network, issuerName) {
|
|
118
|
+
if (_reactNative.Platform.OS === 'ios') {
|
|
119
|
+
throw new Error('hasEligibleTokenizationTarget is not available on iOS');
|
|
120
|
+
}
|
|
121
|
+
if (!_NativeWallet.default) {
|
|
122
|
+
return getModuleLinkingRejection();
|
|
123
|
+
}
|
|
124
|
+
return _NativeWallet.default.hasEligibleTokenizationTarget(last4Digits, network.toUpperCase(), issuerName);
|
|
125
|
+
}
|
|
126
|
+
async function addCardToGoogleWallet(cardData, handlePaymentCredentialsGeneration) {
|
|
89
127
|
if (_reactNative.Platform.OS === 'ios') {
|
|
90
128
|
throw new Error('addCardToGoogleWallet is not available on iOS');
|
|
91
129
|
}
|
|
@@ -96,8 +134,13 @@ async function addCardToGoogleWallet(cardData) {
|
|
|
96
134
|
if (!isWalletInitialized) {
|
|
97
135
|
throw new Error('Wallet could not be initialized');
|
|
98
136
|
}
|
|
99
|
-
|
|
100
|
-
|
|
137
|
+
paymentCredentialsHandler = handlePaymentCredentialsGeneration;
|
|
138
|
+
try {
|
|
139
|
+
const tokenizationStatus = await _NativeWallet.default.addCardToGoogleWallet(cardData);
|
|
140
|
+
return (0, _utils.getTokenizationStatus)(tokenizationStatus);
|
|
141
|
+
} finally {
|
|
142
|
+
paymentCredentialsHandler = null;
|
|
143
|
+
}
|
|
101
144
|
}
|
|
102
145
|
async function resumeAddCardToGoogleWallet(cardData) {
|
|
103
146
|
if (_reactNative.Platform.OS === 'ios') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_reactNative","require","_NativeWallet","_interopRequireWildcard","_utils","_AddToWalletButton","_interopRequireDefault","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","getModuleLinkingRejection","Promise","reject","Error","PACKAGE_NAME","eventEmitter","
|
|
1
|
+
{"version":3,"names":["_reactNative","require","_NativeWallet","_interopRequireWildcard","_utils","_AddToWalletButton","_interopRequireDefault","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","getModuleLinkingRejection","Promise","reject","Error","PACKAGE_NAME","eventEmitter","paymentCredentialsHandler","Platform","OS","Wallet","NativeEventEmitter","addListener","data","console","warn","AndroidRejectPaymentCredentials","requestId","catch","undefined","responseData","AndroidProvidePaymentCredentials","error","message","String","event","callback","remove","removeListener","subscription","checkWalletAvailability","getSecureWalletInfo","isWalletInitialized","ensureGoogleWalletInitialized","getCardStatusBySuffix","last4Digits","cardState","getCardState","getCardStatusByIdentifier","identifier","tsp","tokenState","toUpperCase","hasEligibleTokenizationTarget","network","issuerName","addCardToGoogleWallet","cardData","handlePaymentCredentialsGeneration","tokenizationStatus","getTokenizationStatus","resumeAddCardToGoogleWallet","listTokens","resolve","addCardToAppleWallet","issuerEncryptPayloadCallback","wallet","passData","IOSPresentAddPaymentPassView","status","addPaymentPassToWallet","paymentPassData","nonce","nonceSignature","certificates","response","IOSHandleAddPaymentPassResponse"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["TurboModuleRegistry","PACKAGE_NAME","Wallet","getEnforcing","error","Error","console","warn","message"],"sourceRoot":"../../src","sources":["NativeWallet.ts"],"mappings":"AACA,SAAQA,mBAAmB,QAAO,cAAc;
|
|
1
|
+
{"version":3,"names":["TurboModuleRegistry","PACKAGE_NAME","Wallet","getEnforcing","error","Error","console","warn","message"],"sourceRoot":"../../src","sources":["NativeWallet.ts"],"mappings":"AACA,SAAQA,mBAAmB,QAAO,cAAc;AAuGhD,MAAMC,YAAY,GAAG,gCAAgC;AACrD;AACA;AACA;AACA,IAAIC,MAAwB;AAC5B,IAAI;EACFA,MAAM,GAAGF,mBAAmB,CAACG,YAAY,CAAO,UAAU,CAAC;AAC7D,CAAC,CAAC,OAAOC,KAAK,EAAE;EACd,IAAIA,KAAK,YAAYC,KAAK,EAAE;IAC1B;IACAC,OAAO,CAACC,IAAI,CAAC,IAAIN,YAAY,mCAAmCG,KAAK,CAACI,OAAO,EAAE,CAAC;EAClF;AACF;AACA,eAAeN,MAAM;AACrB,SAAQD,YAAY","ignoreList":[]}
|
package/lib/module/index.js
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 Wallet, { PACKAGE_NAME } from './NativeWallet';
|
|
@@ -7,6 +8,24 @@ function getModuleLinkingRejection() {
|
|
|
7
8
|
return Promise.reject(new Error(`Failed to load Wallet module, make sure to link ${PACKAGE_NAME} correctly`));
|
|
8
9
|
}
|
|
9
10
|
let eventEmitter;
|
|
11
|
+
let paymentCredentialsHandler = null;
|
|
12
|
+
if (Platform.OS === 'android' && Wallet) {
|
|
13
|
+
eventEmitter ??= new NativeEventEmitter(Wallet);
|
|
14
|
+
eventEmitter.addListener('onPaymentCredentialsRequest', async data => {
|
|
15
|
+
if (!paymentCredentialsHandler) {
|
|
16
|
+
console.warn(`[${PACKAGE_NAME}] Received payment credentials request but no handler is set`);
|
|
17
|
+
await (Wallet === null || Wallet === void 0 ? void 0 : Wallet.AndroidRejectPaymentCredentials(data.requestId, 'No payment credentials handler is set').catch(() => undefined));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
const responseData = await paymentCredentialsHandler(data);
|
|
22
|
+
await (Wallet === null || Wallet === void 0 ? void 0 : Wallet.AndroidProvidePaymentCredentials(data.requestId, responseData));
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error(`[${PACKAGE_NAME}] Error handling payment credentials request:`, error);
|
|
25
|
+
await (Wallet === null || Wallet === void 0 ? void 0 : Wallet.AndroidRejectPaymentCredentials(data.requestId, error instanceof Error ? error.message : String(error)).catch(() => undefined));
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
10
29
|
function addListener(event, callback) {
|
|
11
30
|
if (!Wallet) {
|
|
12
31
|
// eslint-disable-next-line no-console
|
|
@@ -61,7 +80,25 @@ async function getCardStatusByIdentifier(identifier, tsp) {
|
|
|
61
80
|
const tokenState = await Wallet.getCardStatusByIdentifier(identifier, tsp.toUpperCase());
|
|
62
81
|
return getCardState(tokenState);
|
|
63
82
|
}
|
|
64
|
-
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Checks whether the card can still be added to a Google Wallet target: the phone or a paired wearable.
|
|
86
|
+
* Use it to decide whether to show the "Add to Google Wallet" button when wearables are supported.
|
|
87
|
+
* @param last4Digits - The last 4 digits of the card number (FPAN, not the token)
|
|
88
|
+
* @param network - The card network, e.g. `VISA`, `MASTERCARD`
|
|
89
|
+
* @param issuerName - The issuer name used for matching, as in the token metadata (not the card display name)
|
|
90
|
+
* @returns `true` if at least one target can still receive the card
|
|
91
|
+
*/
|
|
92
|
+
async function hasEligibleTokenizationTarget(last4Digits, network, issuerName) {
|
|
93
|
+
if (Platform.OS === 'ios') {
|
|
94
|
+
throw new Error('hasEligibleTokenizationTarget is not available on iOS');
|
|
95
|
+
}
|
|
96
|
+
if (!Wallet) {
|
|
97
|
+
return getModuleLinkingRejection();
|
|
98
|
+
}
|
|
99
|
+
return Wallet.hasEligibleTokenizationTarget(last4Digits, network.toUpperCase(), issuerName);
|
|
100
|
+
}
|
|
101
|
+
async function addCardToGoogleWallet(cardData, handlePaymentCredentialsGeneration) {
|
|
65
102
|
if (Platform.OS === 'ios') {
|
|
66
103
|
throw new Error('addCardToGoogleWallet is not available on iOS');
|
|
67
104
|
}
|
|
@@ -72,8 +109,13 @@ async function addCardToGoogleWallet(cardData) {
|
|
|
72
109
|
if (!isWalletInitialized) {
|
|
73
110
|
throw new Error('Wallet could not be initialized');
|
|
74
111
|
}
|
|
75
|
-
|
|
76
|
-
|
|
112
|
+
paymentCredentialsHandler = handlePaymentCredentialsGeneration;
|
|
113
|
+
try {
|
|
114
|
+
const tokenizationStatus = await Wallet.addCardToGoogleWallet(cardData);
|
|
115
|
+
return getTokenizationStatus(tokenizationStatus);
|
|
116
|
+
} finally {
|
|
117
|
+
paymentCredentialsHandler = null;
|
|
118
|
+
}
|
|
77
119
|
}
|
|
78
120
|
async function resumeAddCardToGoogleWallet(cardData) {
|
|
79
121
|
if (Platform.OS === 'ios') {
|
|
@@ -127,5 +169,5 @@ async function addCardToAppleWallet(cardData, issuerEncryptPayloadCallback) {
|
|
|
127
169
|
const status = await addPaymentPassToWallet(passData);
|
|
128
170
|
return getTokenizationStatus(status);
|
|
129
171
|
}
|
|
130
|
-
export { AddToWalletButton, checkWalletAvailability, getSecureWalletInfo, getCardStatusBySuffix, getCardStatusByIdentifier, addCardToGoogleWallet, resumeAddCardToGoogleWallet, listTokens, addCardToAppleWallet, addListener, removeListener };
|
|
172
|
+
export { AddToWalletButton, checkWalletAvailability, getSecureWalletInfo, getCardStatusBySuffix, getCardStatusByIdentifier, hasEligibleTokenizationTarget, addCardToGoogleWallet, resumeAddCardToGoogleWallet, listTokens, addCardToAppleWallet, addListener, removeListener };
|
|
131
173
|
//# sourceMappingURL=index.js.map
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["NativeEventEmitter","Platform","Wallet","PACKAGE_NAME","getCardState","getTokenizationStatus","AddToWalletButton","getModuleLinkingRejection","Promise","reject","Error","eventEmitter","
|
|
1
|
+
{"version":3,"names":["NativeEventEmitter","Platform","Wallet","PACKAGE_NAME","getCardState","getTokenizationStatus","AddToWalletButton","getModuleLinkingRejection","Promise","reject","Error","eventEmitter","paymentCredentialsHandler","OS","addListener","data","console","warn","AndroidRejectPaymentCredentials","requestId","catch","undefined","responseData","AndroidProvidePaymentCredentials","error","message","String","event","callback","remove","removeListener","subscription","checkWalletAvailability","getSecureWalletInfo","isWalletInitialized","ensureGoogleWalletInitialized","getCardStatusBySuffix","last4Digits","cardState","getCardStatusByIdentifier","identifier","tsp","tokenState","toUpperCase","hasEligibleTokenizationTarget","network","issuerName","addCardToGoogleWallet","cardData","handlePaymentCredentialsGeneration","tokenizationStatus","resumeAddCardToGoogleWallet","listTokens","resolve","addCardToAppleWallet","issuerEncryptPayloadCallback","wallet","passData","IOSPresentAddPaymentPassView","status","addPaymentPassToWallet","paymentPassData","nonce","nonceSignature","certificates","response","IOSHandleAddPaymentPassResponse"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"AAAA;AACA;AACA,SAAQA,kBAAkB,EAAEC,QAAQ,QAAO,cAAc;AAEzD,OAAOC,MAAM,IAAGC,YAAY,QAAO,gBAAgB;AAenD,SAAQC,YAAY,EAAEC,qBAAqB,QAAO,SAAS;AAC3D,OAAOC,iBAAiB,MAAM,qBAAqB;AAEnD,SAASC,yBAAyBA,CAAA,EAAG;EACnC,OAAOC,OAAO,CAACC,MAAM,CAAC,IAAIC,KAAK,CAAC,mDAAmDP,YAAY,YAAY,CAAC,CAAC;AAC/G;AAEA,IAAIQ,YAA4C;AAChD,IAAIC,yBAA4H,GAAG,IAAI;AAEvI,IAAIX,QAAQ,CAACY,EAAE,KAAK,SAAS,IAAIX,MAAM,EAAE;EACvCS,YAAY,KAAK,IAAIX,kBAAkB,CAACE,MAAM,CAAC;EAC/CS,YAAY,CAACG,WAAW,CAAC,6BAA6B,EAAE,MAAOC,IAAwC,IAAK;IAC1G,IAAI,CAACH,yBAAyB,EAAE;MAC9BI,OAAO,CAACC,IAAI,CAAC,IAAId,YAAY,8DAA8D,CAAC;MAC5F,OAAMD,MAAM,aAANA,MAAM,uBAANA,MAAM,CAAEgB,+BAA+B,CAACH,IAAI,CAACI,SAAS,EAAE,uCAAuC,CAAC,CAACC,KAAK,CAAC,MAAMC,SAAS,CAAC;MAC7H;IACF;IACA,IAAI;MACF,MAAMC,YAAY,GAAG,MAAMV,yBAAyB,CAACG,IAAI,CAAC;MAC1D,OAAMb,MAAM,aAANA,MAAM,uBAANA,MAAM,CAAEqB,gCAAgC,CAACR,IAAI,CAACI,SAAS,EAAEG,YAAY,CAAC;IAC9E,CAAC,CAAC,OAAOE,KAAK,EAAE;MACdR,OAAO,CAACQ,KAAK,CAAC,IAAIrB,YAAY,+CAA+C,EAAEqB,KAAK,CAAC;MACrF,OAAMtB,MAAM,aAANA,MAAM,uBAANA,MAAM,CAAEgB,+BAA+B,CAACH,IAAI,CAACI,SAAS,EAAEK,KAAK,YAAYd,KAAK,GAAGc,KAAK,CAACC,OAAO,GAAGC,MAAM,CAACF,KAAK,CAAC,CAAC,CAACJ,KAAK,CAAC,MAAMC,SAAS,CAAC;IAC9I;EACF,CAAC,CAAC;AACJ;AAEA,SAASP,WAAWA,CAACa,KAAa,EAAEC,QAAgD,EAAuB;EACzG,IAAI,CAAC1B,MAAM,EAAE;IACX;IACAc,OAAO,CAACC,IAAI,CAAC,IAAId,YAAY,0DAA0D,CAAC;IACxF,OAAO;MAAC0B,MAAM,EAAEA,CAAA,KAAMR;IAAS,CAAC;EAClC;EACAV,YAAY,KAAK,IAAIX,kBAAkB,CAACE,MAAM,CAAC;EAC/C,OAAOS,YAAY,CAACG,WAAW,CAACa,KAAK,EAAEC,QAAQ,CAAC;AAClD;AAEA,SAASE,cAAcA,CAACC,YAAiC,EAAQ;EAC/DA,YAAY,CAACF,MAAM,CAAC,CAAC;AACvB;AAEA,SAASG,uBAAuBA,CAAA,EAAqB;EACnD,IAAI,CAAC9B,MAAM,EAAE;IACX,OAAOK,yBAAyB,CAAC,CAAC;EACpC;EACA,OAAOL,MAAM,CAAC8B,uBAAuB,CAAC,CAAC;AACzC;AAEA,eAAeC,mBAAmBA,CAAA,EAA+B;EAC/D,IAAIhC,QAAQ,CAACY,EAAE,KAAK,KAAK,EAAE;IACzB,MAAM,IAAIH,KAAK,CAAC,6CAA6C,CAAC;EAChE;EAEA,IAAI,CAACR,MAAM,EAAE;IACX,OAAOK,yBAAyB,CAAC,CAAC;EACpC;EACA,MAAM2B,mBAAmB,GAAG,MAAMhC,MAAM,CAACiC,6BAA6B,CAAC,CAAC;EACxE,IAAI,CAACD,mBAAmB,EAAE;IACxB,MAAM,IAAIxB,KAAK,CAAC,iCAAiC,CAAC;EACpD;EAEA,OAAOR,MAAM,CAAC+B,mBAAmB,CAAC,CAAC;AACrC;AAEA,eAAeG,qBAAqBA,CAACC,WAAmB,EAAuB;EAC7E,IAAI,CAACnC,MAAM,EAAE;IACX,OAAOK,yBAAyB,CAAC,CAAC;EACpC;EAEA,MAAM+B,SAAS,GAAG,MAAMpC,MAAM,CAACkC,qBAAqB,CAACC,WAAW,CAAC;EACjE,OAAOjC,YAAY,CAACkC,SAAS,CAAC;AAChC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,eAAeC,yBAAyBA,CAACC,UAAkB,EAAEC,GAAW,EAAuB;EAC7F,IAAI,CAACvC,MAAM,EAAE;IACX,OAAOK,yBAAyB,CAAC,CAAC;EACpC;EAEA,MAAMmC,UAAU,GAAG,MAAMxC,MAAM,CAACqC,yBAAyB,CAACC,UAAU,EAAEC,GAAG,CAACE,WAAW,CAAC,CAAC,CAAC;EACxF,OAAOvC,YAAY,CAACsC,UAAU,CAAC;AACjC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,eAAeE,6BAA6BA,CAACP,WAAmB,EAAEQ,OAAe,EAAEC,UAAkB,EAAoB;EACvH,IAAI7C,QAAQ,CAACY,EAAE,KAAK,KAAK,EAAE;IACzB,MAAM,IAAIH,KAAK,CAAC,uDAAuD,CAAC;EAC1E;EAEA,IAAI,CAACR,MAAM,EAAE;IACX,OAAOK,yBAAyB,CAAC,CAAC;EACpC;EAEA,OAAOL,MAAM,CAAC0C,6BAA6B,CAACP,WAAW,EAAEQ,OAAO,CAACF,WAAW,CAAC,CAAC,EAAEG,UAAU,CAAC;AAC7F;AAEA,eAAeC,qBAAqBA,CAClCC,QAAyB,EACzBC,kCAA4H,EAC/F;EAC7B,IAAIhD,QAAQ,CAACY,EAAE,KAAK,KAAK,EAAE;IACzB,MAAM,IAAIH,KAAK,CAAC,+CAA+C,CAAC;EAClE;EAEA,IAAI,CAACR,MAAM,EAAE;IACX,OAAOK,yBAAyB,CAAC,CAAC;EACpC;EACA,MAAM2B,mBAAmB,GAAG,MAAMhC,MAAM,CAACiC,6BAA6B,CAAC,CAAC;EACxE,IAAI,CAACD,mBAAmB,EAAE;IACxB,MAAM,IAAIxB,KAAK,CAAC,iCAAiC,CAAC;EACpD;EAEAE,yBAAyB,GAAGqC,kCAAkC;EAC9D,IAAI;IACF,MAAMC,kBAAkB,GAAG,MAAMhD,MAAM,CAAC6C,qBAAqB,CAACC,QAAQ,CAAC;IACvE,OAAO3C,qBAAqB,CAAC6C,kBAAkB,CAAC;EAClD,CAAC,SAAS;IACRtC,yBAAyB,GAAG,IAAI;EAClC;AACF;AAEA,eAAeuC,2BAA2BA,CAACH,QAA+B,EAA+B;EACvG,IAAI/C,QAAQ,CAACY,EAAE,KAAK,KAAK,EAAE;IACzB,MAAM,IAAIH,KAAK,CAAC,qDAAqD,CAAC;EACxE;EAEA,IAAI,CAACR,MAAM,EAAE;IACX,OAAOK,yBAAyB,CAAC,CAAC;EACpC;EACA,MAAM2B,mBAAmB,GAAG,MAAMhC,MAAM,CAACiC,6BAA6B,CAAC,CAAC;EACxE,IAAI,CAACD,mBAAmB,EAAE;IACxB,MAAM,IAAIxB,KAAK,CAAC,iCAAiC,CAAC;EACpD;EACA,MAAMwC,kBAAkB,GAAG,MAAMhD,MAAM,CAACiD,2BAA2B,CAACH,QAAQ,CAAC;EAC7E,OAAO3C,qBAAqB,CAAC6C,kBAAkB,CAAC;AAClD;AAEA,eAAeE,UAAUA,CAAA,EAAyB;EAChD,IAAInD,QAAQ,CAACY,EAAE,KAAK,KAAK,EAAE;IACzB,OAAOL,OAAO,CAAC6C,OAAO,CAAC,EAAE,CAAC;EAC5B;EAEA,IAAI,CAACnD,MAAM,EAAE;IACX,OAAOK,yBAAyB,CAAC,CAAC;EACpC;EACA,MAAM2B,mBAAmB,GAAG,MAAMhC,MAAM,CAACiC,6BAA6B,CAAC,CAAC;EACxE,IAAI,CAACD,mBAAmB,EAAE;IACxB,MAAM,IAAIxB,KAAK,CAAC,iCAAiC,CAAC;EACpD;EACA,OAAOR,MAAM,CAACkD,UAAU,CAAC,CAAC;AAC5B;AAEA,eAAeE,oBAAoBA,CACjCN,QAAqB,EACrBO,4BAA0H,EAC7F;EAC7B,IAAItD,QAAQ,CAACY,EAAE,KAAK,SAAS,EAAE;IAC7B,MAAM,IAAIH,KAAK,CAAC,kDAAkD,CAAC;EACrE;EAEA,IAAI,CAACR,MAAM,EAAE;IACX,OAAOK,yBAAyB,CAAC,CAAC;EACpC;EACA,MAAMiD,MAAM,GAAGtD,MAAM;EAErB,MAAMuD,QAAQ,GAAG,MAAMD,MAAM,CAACE,4BAA4B,CAACV,QAAQ,CAAC;EAEpE,IAAI,CAACS,QAAQ,IAAIA,QAAQ,CAACE,MAAM,KAAK,CAAC,EAAE;IACtC,OAAOtD,qBAAqB,CAAC,CAAAoD,QAAQ,aAARA,QAAQ,uBAARA,QAAQ,CAAEE,MAAM,KAAI,CAAC,CAAC,CAAC;EACtD;EAEA,eAAeC,sBAAsBA,CAACC,eAAsC,EAAmB;IAC7F,MAAMvC,YAAY,GAAG,MAAMiC,4BAA4B,CAACM,eAAe,CAACC,KAAK,EAAED,eAAe,CAACE,cAAc,EAAEF,eAAe,CAACG,YAAY,CAAC;IAC5I,MAAMC,QAAQ,GAAG,MAAMT,MAAM,CAACU,+BAA+B,CAAC5C,YAAY,CAAC;IAC3E;IACA;IACA,IAAI2C,QAAQ,EAAE;MACZ,OAAOL,sBAAsB,CAACK,QAAQ,CAAC;IACzC;IACA,OAAO,CAAC;EACV;EACA,MAAMN,MAAM,GAAG,MAAMC,sBAAsB,CAACH,QAAQ,CAAC;EACrD,OAAOpD,qBAAqB,CAACsD,MAAM,CAAC;AACtC;AAeA,SACErD,iBAAiB,EACjB0B,uBAAuB,EACvBC,mBAAmB,EACnBG,qBAAqB,EACrBG,yBAAyB,EACzBK,6BAA6B,EAC7BG,qBAAqB,EACrBI,2BAA2B,EAC3BC,UAAU,EACVE,oBAAoB,EACpBxC,WAAW,EACXgB,cAAc","ignoreList":[]}
|
|
@@ -21,6 +21,8 @@ type AndroidCardData = {
|
|
|
21
21
|
cardHolderName: string;
|
|
22
22
|
lastDigits: string;
|
|
23
23
|
userAddress: UserAddress;
|
|
24
|
+
isVirtualCard?: boolean;
|
|
25
|
+
isBounceProvisioned?: boolean;
|
|
24
26
|
};
|
|
25
27
|
type AndroidResumeCardData = {
|
|
26
28
|
network: string;
|
|
@@ -35,9 +37,16 @@ type IOSCardData = {
|
|
|
35
37
|
cardDescription: string;
|
|
36
38
|
};
|
|
37
39
|
type onCardActivatedPayload = {
|
|
38
|
-
tokenId
|
|
40
|
+
tokenId?: string | null;
|
|
39
41
|
status: 'activated' | 'canceled';
|
|
40
42
|
};
|
|
43
|
+
type onPaymentCredentialsRequestPayload = {
|
|
44
|
+
requestId: string;
|
|
45
|
+
serverSessionId: string;
|
|
46
|
+
walletId: string;
|
|
47
|
+
tokenRequestorId: string | null;
|
|
48
|
+
opaquePaymentCard: string;
|
|
49
|
+
};
|
|
41
50
|
type IOSAddPaymentPassData = {
|
|
42
51
|
status: number;
|
|
43
52
|
nonce: string;
|
|
@@ -55,13 +64,20 @@ type TokenInfo = {
|
|
|
55
64
|
lastDigits: string;
|
|
56
65
|
tokenState: number;
|
|
57
66
|
};
|
|
67
|
+
type AndroidPaymentCredentialsResponse = {
|
|
68
|
+
opaquePaymentCard: string;
|
|
69
|
+
googleOpaquePaymentCard: string;
|
|
70
|
+
};
|
|
58
71
|
export interface Spec extends TurboModule {
|
|
59
72
|
checkWalletAvailability(): Promise<boolean>;
|
|
60
73
|
ensureGoogleWalletInitialized(): Promise<boolean>;
|
|
61
74
|
getSecureWalletInfo(): Promise<AndroidWalletData>;
|
|
62
75
|
getCardStatusBySuffix(last4Digits: string): Promise<number>;
|
|
63
76
|
getCardStatusByIdentifier(identifier: string, tsp: string): Promise<number>;
|
|
77
|
+
hasEligibleTokenizationTarget(last4Digits: string, network: string, issuerName: string): Promise<boolean>;
|
|
64
78
|
addCardToGoogleWallet(cardData: AndroidCardData): Promise<number>;
|
|
79
|
+
AndroidProvidePaymentCredentials(requestId: string, responseData: AndroidPaymentCredentialsResponse): Promise<boolean>;
|
|
80
|
+
AndroidRejectPaymentCredentials(requestId: string, errorMessage: string): Promise<boolean>;
|
|
65
81
|
resumeAddCardToGoogleWallet(cardData: AndroidResumeCardData): Promise<number>;
|
|
66
82
|
listTokens(): Promise<TokenInfo[]>;
|
|
67
83
|
IOSPresentAddPaymentPassView(cardData: IOSCardData): Promise<IOSAddPaymentPassData>;
|
|
@@ -73,5 +89,5 @@ declare const PACKAGE_NAME = "@expensify/react-native-wallet";
|
|
|
73
89
|
declare let Wallet: Spec | undefined;
|
|
74
90
|
export default Wallet;
|
|
75
91
|
export { PACKAGE_NAME };
|
|
76
|
-
export type { AndroidCardData, AndroidResumeCardData, IOSCardData, AndroidWalletData, CardStatus, UserAddress, onCardActivatedPayload, Platform, IOSAddPaymentPassData, IOSEncryptPayload, TokenizationStatus, TokenInfo, };
|
|
92
|
+
export type { AndroidCardData, AndroidResumeCardData, IOSCardData, AndroidWalletData, CardStatus, UserAddress, onCardActivatedPayload, onPaymentCredentialsRequestPayload, Platform, IOSAddPaymentPassData, IOSEncryptPayload, TokenizationStatus, TokenInfo, AndroidPaymentCredentialsResponse, };
|
|
77
93
|
//# sourceMappingURL=NativeWallet.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativeWallet.d.ts","sourceRoot":"","sources":["../../../src/NativeWallet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,cAAc,CAAC;AAG9C,KAAK,iBAAiB,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,KAAK,UAAU,GAAG,WAAW,GAAG,mBAAmB,GAAG,SAAS,GAAG,QAAQ,GAAG,WAAW,GAAG,aAAa,CAAC;AAEzG,KAAK,QAAQ,GAAG,SAAS,GAAG,KAAK,CAAC;AAElC,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"NativeWallet.d.ts","sourceRoot":"","sources":["../../../src/NativeWallet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,cAAc,CAAC;AAG9C,KAAK,iBAAiB,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,KAAK,UAAU,GAAG,WAAW,GAAG,mBAAmB,GAAG,SAAS,GAAG,QAAQ,GAAG,WAAW,GAAG,aAAa,CAAC;AAEzG,KAAK,QAAQ,GAAG,SAAS,GAAG,KAAK,CAAC;AAElC,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,CAAC;AAEF,KAAK,qBAAqB,GAAG;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,WAAW,GAAG,UAAU,CAAC;CAClC,CAAC;AAEF,KAAK,kCAAkC,GAAG;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,KAAK,qBAAqB,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAEF,KAAK,iBAAiB,GAAG;IACvB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,KAAK,kBAAkB,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC;AAE3D,KAAK,SAAS,GAAG;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,KAAK,iCAAiC,GAAG;IACvC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,uBAAuB,EAAE,MAAM,CAAC;CACjC,CAAC;AAEF,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,uBAAuB,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C,6BAA6B,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAClD,mBAAmB,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAClD,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5D,yBAAyB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5E,6BAA6B,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1G,qBAAqB,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAClE,gCAAgC,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,iCAAiC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACvH,+BAA+B,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3F,2BAA2B,CAAC,QAAQ,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9E,UAAU,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IACnC,4BAA4B,CAAC,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACpF,+BAA+B,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAC;IACnG,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C;AAED,QAAA,MAAM,YAAY,mCAAmC,CAAC;AAItD,QAAA,IAAI,MAAM,EAAE,IAAI,GAAG,SAAS,CAAC;AAS7B,eAAe,MAAM,CAAC;AACtB,OAAO,EAAC,YAAY,EAAC,CAAC;AACtB,YAAY,EACV,eAAe,EACf,qBAAqB,EACrB,WAAW,EACX,iBAAiB,EACjB,UAAU,EACV,WAAW,EACX,sBAAsB,EACtB,kCAAkC,EAClC,QAAQ,EACR,qBAAqB,EACrB,iBAAiB,EACjB,kBAAkB,EAClB,SAAS,EACT,iCAAiC,GAClC,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { EmitterSubscription } from 'react-native';
|
|
2
|
-
import type { TokenizationStatus, AndroidCardData, AndroidResumeCardData, CardStatus, IOSCardData, IOSEncryptPayload, AndroidWalletData, onCardActivatedPayload, IOSAddPaymentPassData, TokenInfo } from './NativeWallet';
|
|
2
|
+
import type { TokenizationStatus, AndroidCardData, AndroidResumeCardData, CardStatus, IOSCardData, IOSEncryptPayload, AndroidWalletData, onCardActivatedPayload, onPaymentCredentialsRequestPayload, IOSAddPaymentPassData, TokenInfo, AndroidPaymentCredentialsResponse } from './NativeWallet';
|
|
3
3
|
import AddToWalletButton from './AddToWalletButton';
|
|
4
4
|
declare function addListener(event: string, callback: (data: onCardActivatedPayload) => void): EmitterSubscription;
|
|
5
5
|
declare function removeListener(subscription: EmitterSubscription): void;
|
|
@@ -13,10 +13,19 @@ declare function getCardStatusBySuffix(last4Digits: string): Promise<CardStatus>
|
|
|
13
13
|
* @returns CardStatus - The card status
|
|
14
14
|
*/
|
|
15
15
|
declare function getCardStatusByIdentifier(identifier: string, tsp: string): Promise<CardStatus>;
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Checks whether the card can still be added to a Google Wallet target: the phone or a paired wearable.
|
|
18
|
+
* Use it to decide whether to show the "Add to Google Wallet" button when wearables are supported.
|
|
19
|
+
* @param last4Digits - The last 4 digits of the card number (FPAN, not the token)
|
|
20
|
+
* @param network - The card network, e.g. `VISA`, `MASTERCARD`
|
|
21
|
+
* @param issuerName - The issuer name used for matching, as in the token metadata (not the card display name)
|
|
22
|
+
* @returns `true` if at least one target can still receive the card
|
|
23
|
+
*/
|
|
24
|
+
declare function hasEligibleTokenizationTarget(last4Digits: string, network: string, issuerName: string): Promise<boolean>;
|
|
25
|
+
declare function addCardToGoogleWallet(cardData: AndroidCardData, handlePaymentCredentialsGeneration: (data: onPaymentCredentialsRequestPayload) => Promise<AndroidPaymentCredentialsResponse>): Promise<TokenizationStatus>;
|
|
17
26
|
declare function resumeAddCardToGoogleWallet(cardData: AndroidResumeCardData): Promise<TokenizationStatus>;
|
|
18
27
|
declare function listTokens(): Promise<TokenInfo[]>;
|
|
19
28
|
declare function addCardToAppleWallet(cardData: IOSCardData, issuerEncryptPayloadCallback: (nonce: string, nonceSignature: string, certificate: string[]) => Promise<IOSEncryptPayload>): Promise<TokenizationStatus>;
|
|
20
|
-
export type { AndroidCardData, AndroidWalletData, CardStatus, IOSEncryptPayload, IOSCardData, IOSAddPaymentPassData, onCardActivatedPayload, TokenizationStatus, TokenInfo };
|
|
21
|
-
export { AddToWalletButton, checkWalletAvailability, getSecureWalletInfo, getCardStatusBySuffix, getCardStatusByIdentifier, addCardToGoogleWallet, resumeAddCardToGoogleWallet, listTokens, addCardToAppleWallet, addListener, removeListener, };
|
|
29
|
+
export type { AndroidCardData, AndroidWalletData, CardStatus, IOSEncryptPayload, IOSCardData, IOSAddPaymentPassData, onCardActivatedPayload, onPaymentCredentialsRequestPayload, TokenizationStatus, TokenInfo, AndroidPaymentCredentialsResponse, };
|
|
30
|
+
export { AddToWalletButton, checkWalletAvailability, getSecureWalletInfo, getCardStatusBySuffix, getCardStatusByIdentifier, hasEligibleTokenizationTarget, addCardToGoogleWallet, resumeAddCardToGoogleWallet, listTokens, addCardToAppleWallet, addListener, removeListener, };
|
|
22
31
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,cAAc,CAAC;AAEtD,OAAO,KAAK,EACV,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,UAAU,EACV,WAAW,EACX,iBAAiB,EACjB,iBAAiB,EACjB,sBAAsB,EACtB,kCAAkC,EAClC,qBAAqB,EACrB,SAAS,EACT,iCAAiC,EAClC,MAAM,gBAAgB,CAAC;AAExB,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AA2BpD,iBAAS,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,sBAAsB,KAAK,IAAI,GAAG,mBAAmB,CAQzG;AAED,iBAAS,cAAc,CAAC,YAAY,EAAE,mBAAmB,GAAG,IAAI,CAE/D;AAED,iBAAS,uBAAuB,IAAI,OAAO,CAAC,OAAO,CAAC,CAKnD;AAED,iBAAe,mBAAmB,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAc/D;AAED,iBAAe,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAO7E;AAED;;;;;GAKG;AACH,iBAAe,yBAAyB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAO7F;AAED;;;;;;;GAOG;AACH,iBAAe,6BAA6B,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAUvH;AAED,iBAAe,qBAAqB,CAClC,QAAQ,EAAE,eAAe,EACzB,kCAAkC,EAAE,CAAC,IAAI,EAAE,kCAAkC,KAAK,OAAO,CAAC,iCAAiC,CAAC,GAC3H,OAAO,CAAC,kBAAkB,CAAC,CAoB7B;AAED,iBAAe,2BAA2B,CAAC,QAAQ,EAAE,qBAAqB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAcvG;AAED,iBAAe,UAAU,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC,CAahD;AAED,iBAAe,oBAAoB,CACjC,QAAQ,EAAE,WAAW,EACrB,4BAA4B,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,iBAAiB,CAAC,GACzH,OAAO,CAAC,kBAAkB,CAAC,CA4B7B;AAED,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,UAAU,EACV,iBAAiB,EACjB,WAAW,EACX,qBAAqB,EACrB,sBAAsB,EACtB,kCAAkC,EAClC,kBAAkB,EAClB,SAAS,EACT,iCAAiC,GAClC,CAAC;AACF,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,mBAAmB,EACnB,qBAAqB,EACrB,yBAAyB,EACzB,6BAA6B,EAC7B,qBAAqB,EACrB,2BAA2B,EAC3B,UAAU,EACV,oBAAoB,EACpB,WAAW,EACX,cAAc,GACf,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expensify/react-native-wallet",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.",
|
|
5
5
|
"source": "./src/index.tsx",
|
|
6
6
|
"main": "lib/commonjs/index",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"devEngines": {
|
|
67
67
|
"runtime": {
|
|
68
68
|
"name": "node",
|
|
69
|
-
"version": ">=24.
|
|
69
|
+
"version": ">=24.22.1",
|
|
70
70
|
"onFail": "error"
|
|
71
71
|
},
|
|
72
72
|
"packageManager": {
|
package/src/NativeWallet.ts
CHANGED
|
@@ -27,6 +27,8 @@ type AndroidCardData = {
|
|
|
27
27
|
cardHolderName: string;
|
|
28
28
|
lastDigits: string;
|
|
29
29
|
userAddress: UserAddress;
|
|
30
|
+
isVirtualCard?: boolean;
|
|
31
|
+
isBounceProvisioned?: boolean;
|
|
30
32
|
};
|
|
31
33
|
|
|
32
34
|
type AndroidResumeCardData = {
|
|
@@ -44,10 +46,18 @@ type IOSCardData = {
|
|
|
44
46
|
};
|
|
45
47
|
|
|
46
48
|
type onCardActivatedPayload = {
|
|
47
|
-
tokenId
|
|
49
|
+
tokenId?: string | null;
|
|
48
50
|
status: 'activated' | 'canceled';
|
|
49
51
|
};
|
|
50
52
|
|
|
53
|
+
type onPaymentCredentialsRequestPayload = {
|
|
54
|
+
requestId: string;
|
|
55
|
+
serverSessionId: string;
|
|
56
|
+
walletId: string;
|
|
57
|
+
tokenRequestorId: string | null;
|
|
58
|
+
opaquePaymentCard: string;
|
|
59
|
+
};
|
|
60
|
+
|
|
51
61
|
type IOSAddPaymentPassData = {
|
|
52
62
|
status: number;
|
|
53
63
|
nonce: string;
|
|
@@ -69,13 +79,21 @@ type TokenInfo = {
|
|
|
69
79
|
tokenState: number;
|
|
70
80
|
};
|
|
71
81
|
|
|
82
|
+
type AndroidPaymentCredentialsResponse = {
|
|
83
|
+
opaquePaymentCard: string;
|
|
84
|
+
googleOpaquePaymentCard: string;
|
|
85
|
+
};
|
|
86
|
+
|
|
72
87
|
export interface Spec extends TurboModule {
|
|
73
88
|
checkWalletAvailability(): Promise<boolean>;
|
|
74
89
|
ensureGoogleWalletInitialized(): Promise<boolean>;
|
|
75
90
|
getSecureWalletInfo(): Promise<AndroidWalletData>;
|
|
76
91
|
getCardStatusBySuffix(last4Digits: string): Promise<number>;
|
|
77
92
|
getCardStatusByIdentifier(identifier: string, tsp: string): Promise<number>;
|
|
93
|
+
hasEligibleTokenizationTarget(last4Digits: string, network: string, issuerName: string): Promise<boolean>;
|
|
78
94
|
addCardToGoogleWallet(cardData: AndroidCardData): Promise<number>;
|
|
95
|
+
AndroidProvidePaymentCredentials(requestId: string, responseData: AndroidPaymentCredentialsResponse): Promise<boolean>;
|
|
96
|
+
AndroidRejectPaymentCredentials(requestId: string, errorMessage: string): Promise<boolean>;
|
|
79
97
|
resumeAddCardToGoogleWallet(cardData: AndroidResumeCardData): Promise<number>;
|
|
80
98
|
listTokens(): Promise<TokenInfo[]>;
|
|
81
99
|
IOSPresentAddPaymentPassView(cardData: IOSCardData): Promise<IOSAddPaymentPassData>;
|
|
@@ -107,9 +125,11 @@ export type {
|
|
|
107
125
|
CardStatus,
|
|
108
126
|
UserAddress,
|
|
109
127
|
onCardActivatedPayload,
|
|
128
|
+
onPaymentCredentialsRequestPayload,
|
|
110
129
|
Platform,
|
|
111
130
|
IOSAddPaymentPassData,
|
|
112
131
|
IOSEncryptPayload,
|
|
113
132
|
TokenizationStatus,
|
|
114
133
|
TokenInfo,
|
|
134
|
+
AndroidPaymentCredentialsResponse,
|
|
115
135
|
};
|
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
|
-
|
|
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
|
-
|
|
100
|
-
|
|
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 {
|
|
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,
|