diff --git a/RTNWallet/android/build.gradle b/RTNWallet/android/build.gradle new file mode 100644 index 000000000000..7f8a446a3df5 --- /dev/null +++ b/RTNWallet/android/build.gradle @@ -0,0 +1,29 @@ +buildscript { + ext.safeExtGet = {prop, fallback -> + rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback + } + repositories { + google() + gradlePluginPortal() + } + dependencies { + classpath("com.android.tools.build:gradle:7.3.1") + } +} + +apply plugin: 'com.android.library' +apply plugin: 'com.facebook.react' + +android { + compileSdkVersion safeExtGet('compileSdkVersion', 33) + namespace "com.rtnwallet" +} + +repositories { + mavenCentral() + google() +} + +dependencies { + implementation 'com.facebook.react:react-native' +} \ No newline at end of file diff --git a/RTNWallet/android/src/main/java/com/rtnwallet/WalletModule.kt b/RTNWallet/android/src/main/java/com/rtnwallet/WalletModule.kt new file mode 100644 index 000000000000..13dbf5a2c8b9 --- /dev/null +++ b/RTNWallet/android/src/main/java/com/rtnwallet/WalletModule.kt @@ -0,0 +1,148 @@ +package com.rtnwallet + +import com.facebook.react.bridge.Promise +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReadableMap +import com.facebook.react.module.annotations.ReactModule +import com.facebook.react.turbomodule.core.interfaces.TurboModule +import com.google.android.gms.common.api.ApiException +import com.google.android.gms.tapandpay.TapAndPay +import com.google.android.gms.tapandpay.TapAndPayClient +import com.google.android.gms.tapandpay.TapAndPayStatusCodes +import com.google.android.gms.tapandpay.issuer.PushTokenizeRequest +import com.google.android.gms.tapandpay.issuer.UserAddress +import kotlinx.coroutines.* + + +@ReactModule(name = WalletModule.NAME) +class WalletModule(reactContext: ReactApplicationContext) : NativeWalletSpec(reactContext), TurboModule { + private class WalletCreationItem(val model: Map) { + object Keys { + const val tokenServiceProvider = "tokenServiceProvider" + const val network = "network" + const val opaquePaymentCard = "opaquePaymentCard" + const val displayName = "displayName" + const val lastDigits = "lastDigits" + const val name = "name" + const val phone = "phone" + const val userAddress = "userAddress" + const val address1 = "address1" + const val address2 = "address2" + const val city = "city" + const val state = "state" + const val country = "country" + const val postal_code = "postal_code" + } + + private val addressData: Map + get() = model[Keys.userAddress] as? Map ?: HashMap() + + val tokenServiceProvider: Int + get() = when ((model[Keys.tokenServiceProvider] as? String ?: "").toUpperCase()) { + "TOKEN_PROVIDER_AMEX" -> TapAndPay.TOKEN_PROVIDER_AMEX + "TOKEN_PROVIDER_MASTERCARD" -> TapAndPay.TOKEN_PROVIDER_MASTERCARD + "TOKEN_PROVIDER_VISA" -> TapAndPay.TOKEN_PROVIDER_VISA + "TOKEN_PROVIDER_DISCOVER" -> TapAndPay.TOKEN_PROVIDER_DISCOVER + else -> 1000 + } + + val network: Int + get() = when ((model[Keys.network] as? String ?: "").toUpperCase()) { + "AMEX" -> TapAndPay.CARD_NETWORK_AMEX + "DISCOVER" -> TapAndPay.CARD_NETWORK_DISCOVER + "MASTERCARD" -> TapAndPay.CARD_NETWORK_MASTERCARD + "VISA" -> TapAndPay.CARD_NETWORK_VISA + else -> 1000 + } + + val opcBytes: ByteArray + get() = (model[Keys.opaquePaymentCard] as? String ?: "").toByteArray() + + val displayName: String + get() = model[Keys.displayName] as? String ?: "" + + val lastDigits: String + get() = model[Keys.lastDigits] as? String ?: "" + + val userAddress: UserAddress + get() = UserAddress.newBuilder() + .setName(addressData[Keys.name] as? String ?: "") + .setAddress1(addressData[Keys.address1] as? String ?: "") + .setAddress2(addressData[Keys.address2] as? String ?: "") + .setLocality(addressData[Keys.city] as? String ?: "") + .setAdministrativeArea(addressData[Keys.state] as? String ?: "") + .setCountryCode(addressData[Keys.country] as? String ?: "") + .setPostalCode(addressData[Keys.postal_code] as? String ?: "") + .setPhoneNumber(addressData[Keys.phone] as? String ?: "") + .build() + } + + private val tapAndPayClient: TapAndPayClient by lazy { + TapAndPay.getClient(reactContext.currentActivity ?: throw Exception("Current Activity not found")) + } + + private val coroutineScope = CoroutineScope(Dispatchers.Main + Job()) + + override fun getDeviceDetails(promise: Promise) { + coroutineScope.launch { + try { + val walletID = async(Dispatchers.IO) { getWalletID() } + val hardwareID = async(Dispatchers.IO) { getHardwareID() } + val result = WritableNativeMap().apply { + putString("walletID", walletID.await()) + putString("hardwareID", hardwareID.await()) + } + promise.resolve(result) + } catch (e: Exception) { + promise.reject("DEVICE_DETAILS_ERROR", "Failed to retrieve device details", e) + } + } + } + + + override fun handleWalletCreationResponse(data: ReadableMap, promise: Promise) { + try { + val context = reactContext.currentActivity ?: throw Exception("Current Activity not found") + val walletCreationItem = WalletCreationItem(data) + val pushTokenizeRequest = PushTokenizeRequest.Builder() + .setOpaquePaymentCard(walletCreationItem.opcBytes) + .setNetwork(walletCreationItem.network) + .setTokenServiceProvider(walletCreationItem.tokenServiceProvider) + .setDisplayName(walletCreationItem.displayName) + .setLastDigits(walletCreationItem.lastDigits) + .setUserAddress(walletCreationItem.userAddress) + .build() + tapAndPayClient.pushTokenize(context, pushTokenizeRequest, requestPushTokenize) + + // Assuming success handling here. Adjust according to actual process + promise.resolve(null) + } catch (e: Exception) { + promise.reject("WALLET_CREATION_ERROR", "Failed to add card to Google Pay", e) + } + } + + private fun getWalletID(completion: (String?, Exception?) -> Unit) { + tapAndPayClient.activewalletID.addOnCompleteListener { task -> + if (task.isSuccessful && task.result != null) { + completion(task.result, null) + } else { + completion(null, task.exception) + } + } + } + + private fun getHardwareID(completion: (String?, Exception?) -> Unit) { + tapAndPayClient.stablehardwareID.addOnCompleteListener { task -> + if (task.isSuccessful && task.result != null) { + completion(task.result, null) + } else { + completion(null, task.exception) + } + } + } + + + companion object { + const val NAME = "RTNWallet" + } +} diff --git a/RTNWallet/android/src/main/java/com/rtnwallet/WalletPackage.kt b/RTNWallet/android/src/main/java/com/rtnwallet/WalletPackage.kt new file mode 100644 index 000000000000..235c41793950 --- /dev/null +++ b/RTNWallet/android/src/main/java/com/rtnwallet/WalletPackage.kt @@ -0,0 +1,30 @@ +package com.rtnwallet + +import com.facebook.react.TurboReactPackage +import com.facebook.react.bridge.NativeModule +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.module.model.ReactModuleInfo +import com.facebook.react.module.model.ReactModuleInfoProvider + +class WalletPackage : TurboReactPackage() { + override fun getModule(name: String?, reactContext: ReactApplicationContext): NativeModule? = + if (name == WalletModule.NAME) { + WalletModule(reactContext) + } else { + null + } + + override fun getReactModuleInfoProvider(): ReactModuleInfoProvider = ReactModuleInfoProvider { + mapOf( + WalletModule.NAME to ReactModuleInfo( + WalletModule.NAME, + WalletModule.NAME, + false, // canOverrideExistingModule + false, // needsEagerInit + true, // hasConstants + false, // isCxxModule + true // isTurboModule + ) + ) + } +} diff --git a/RTNWallet/ios/RTNWallet.h b/RTNWallet/ios/RTNWallet.h new file mode 100644 index 000000000000..9a70bb74651e --- /dev/null +++ b/RTNWallet/ios/RTNWallet.h @@ -0,0 +1,9 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface RTNWallet : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/RTNWallet/ios/RTNWallet.mm b/RTNWallet/ios/RTNWallet.mm new file mode 100644 index 000000000000..c8b2ec1c07cb --- /dev/null +++ b/RTNWallet/ios/RTNWallet.mm @@ -0,0 +1,34 @@ +#import "RTNWalletSpec.h" +#import "RTNWallet.h" + +@implementation RTNWallet + +RCT_EXPORT_MODULE(); + +RCT_EXPORT_METHOD(handleWalletCreationResponse:(NSDictionary *)data + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { + + NSString *encryptedPassData = data[@"encryptedPassData"]; + NSString *activationData = data[@"activationData"]; + NSString *ephemeralPublicKey = data[@"ephemeralPublicKey"]; + + // Convert strings to NSData + NSData *encryptedPassDataBytes = [[NSData alloc] initWithBase64EncodedString:encryptedPassData options:0]; + NSData *activationDataBytes = [[NSData alloc] initWithBase64EncodedString:activationData options:0]; + NSData *ephemeralPublicKeyBytes = [[NSData alloc] initWithBase64EncodedString:ephemeralPublicKey options:0]; + + if (!encryptedPassDataBytes || !activationDataBytes || !ephemeralPublicKeyBytes) { + reject(@"ERROR", @"Invalid data provided", nil); + return; + } + + +} + +RCT_EXPORT_METHOD(getDeviceDetails:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { + +} + +@end diff --git a/RTNWallet/ios/rtn-wallet.podspec b/RTNWallet/ios/rtn-wallet.podspec new file mode 100644 index 000000000000..283441984665 --- /dev/null +++ b/RTNWallet/ios/rtn-wallet.podspec @@ -0,0 +1,19 @@ +require "json" + +package = JSON.parse(File.read(File.join(__dir__, "package.json"))) + +Pod::Spec.new do |s| + s.name = "rtn-wallet" + s.version = package["version"] + s.summary = package["description"] + s.description = package["description"] + s.homepage = package["homepage"] + s.license = package["license"] + s.platforms = { :ios => "11.0" } + s.author = package["author"] + s.source = { :git => package["repository"], :tag => "#{s.version}" } + + s.source_files = "ios/**/*.{h,m,mm,swift}" + + install_modules_dependencies(s) +end diff --git a/RTNWallet/js/NativeWallet.ts b/RTNWallet/js/NativeWallet.ts new file mode 100644 index 000000000000..f6075cc8abb5 --- /dev/null +++ b/RTNWallet/js/NativeWallet.ts @@ -0,0 +1,29 @@ +import {TurboModule, TurboModuleRegistry} from 'react-native'; + +export interface Spec extends TurboModule { + // Method to get device details for wallet creation + getDeviceDetails(): Promise<{ + walletAccountID?: string; // Android + deviceID?: string; // Android + // Add any iOS-specific details if needed + }>; + + // Method to handle wallet creation response + handleWalletCreationResponse(data: { + // Common return values + cardToken: string; + // Android-specific return values + opaquePaymentCard?: string; + userAddress?: string; + network?: string; + tokenServiceProvider?: string; + displayName?: string; + lastDigits?: string; + // iOS-specific return values + encryptedPassData?: string; + activationData?: string; + ephemeralPublicKey?: string; + }): Promise; +} + +export default TurboModuleRegistry.get('RTNWallet'); diff --git a/RTNWallet/package.json b/RTNWallet/package.json new file mode 100644 index 000000000000..bae47cc19453 --- /dev/null +++ b/RTNWallet/package.json @@ -0,0 +1,71 @@ +{ + "name": "react-native-wallet", + "version": "0.0.1", + "description": "A cross platform native module for React-Native to add payment cards to Apple Pay and Google Pay", + "main": "index.js", + "scripts": { + "build": "echo \"Warning: no build process has been specified yet\"", + "lint": "eslint index.js", + "prettier": "prettier --write .", + "prettier-watch": "onchange \"**/*.js\" -- prettier --write --ignore-unknown {{changed}}", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Expensify/react-native-wallet.git" + }, + "files": [ + "js", + "android", + "ios", + "rtn-wallet.podspec", + "!android/build", + "!ios/build", + "!**/__tests__", + "!**/__fixtures__", + "!**/__mocks__" + ], + "keywords": [ + "React", + "React-Native", + "Passkit", + "Wallet" + ], + "author": "Expensify, Inc.", + "license": "MIT", + "bugs": { + "url": "https://github.com/Expensify/react-native-wallet/issues" + }, + "homepage": "https://github.com/Expensify/react-native-wallet#readme", + "devDependencies": { + "@babel/core": "7.20.12", + "@babel/preset-env": "^7.20.2", + "@babel/preset-react": "^7.18.6", + "babel-eslint": "^10.1.0", + "babel-loader": "^8.2.5", + "eslint": "^7.6.0", + "eslint-config-expensify": "^2.0.24", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-jsx-a11y": "^6.6.1", + "eslint-plugin-react": "^7.31.10", + "metro-react-native-babel-preset": "^0.72.3", + "prettier": "^2.8.8" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + }, + "engines": { + "node": "20.9.0", + "npm": "10.1.0" + }, + "codegenConfig": { + "name": "RTNWalletSpec", + "type": "modules", + "jsSrcsDir": "js", + "android": { + "javaPackageName": "com.rtnwallet" + } + } + } + \ No newline at end of file diff --git a/RTNWallet/rtn-wallet.podspec b/RTNWallet/rtn-wallet.podspec new file mode 100644 index 000000000000..6c1ffdd1b4cf --- /dev/null +++ b/RTNWallet/rtn-wallet.podspec @@ -0,0 +1,19 @@ +require "json" + +package = JSON.parse(File.read(File.join(__dir__, "package.json"))) + +Pod::Spec.new do |s| + s.name = "rtn-wallet" + s.version = package["version"] + s.summary = package["description"] + s.description = package["description"] + s.homepage = package["homepage"] + s.license = package["license"] + s.platforms = { :ios => "11.0" } + s.author = package["author"] + s.source = { :git => package["repository"], :tag => "#{s.version}" } + + s.source_files = "ios/**/*.{h,m,mm,swift}" + + install_modules_dependencies(s) +end \ No newline at end of file diff --git a/android/gradle.properties b/android/gradle.properties index c77d6b16f1b3..e253cf5a0f88 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -37,7 +37,7 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 # your application. You should enable this flag either if you want # to write custom TurboModules/Fabric components OR use libraries that # are providing them. -newArchEnabled=false +newArchEnabled=true # Use this property to enable or disable the Hermes JS engine. # If set to false, you will be using JSC instead. diff --git a/package-lock.json b/package-lock.json index 1dcdff30b536..fefbf5bd2bac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -112,6 +112,7 @@ "react-native-url-polyfill": "^2.0.0", "react-native-view-shot": "3.8.0", "react-native-vision-camera": "^2.16.2", + "react-native-wallet": "file:RTNWallet", "react-native-web": "^0.19.9", "react-native-web-linear-gradient": "^1.1.2", "react-native-webview": "13.6.3", @@ -47327,6 +47328,10 @@ "react-native": "*" } }, + "node_modules/react-native-wallet": { + "resolved": "RTNWallet", + "link": true + }, "node_modules/react-native-web": { "version": "0.19.9", "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.19.9.tgz", @@ -55856,6 +55861,304 @@ "type": "github", "url": "https://github.com/sponsors/wooorm" } + }, + "RTNWallet": { + "name": "react-native-wallet", + "version": "0.0.1", + "license": "MIT", + "devDependencies": { + "@babel/core": "7.20.12", + "@babel/preset-env": "^7.20.2", + "@babel/preset-react": "^7.18.6", + "babel-eslint": "^10.1.0", + "babel-loader": "^8.2.5", + "eslint": "^7.6.0", + "eslint-config-expensify": "^2.0.24", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-jsx-a11y": "^6.6.1", + "eslint-plugin-react": "^7.31.10", + "metro-react-native-babel-preset": "^0.72.3", + "prettier": "^2.8.8" + }, + "engines": { + "node": "20.9.0", + "npm": "10.1.0" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "RTNWallet/node_modules/@babel/core": { + "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", + "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.7", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helpers": "^7.20.7", + "@babel/parser": "^7.20.7", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.12", + "@babel/types": "^7.20.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "RTNWallet/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "RTNWallet/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "RTNWallet/node_modules/babel-loader": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", + "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==", + "dev": true, + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "RTNWallet/node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "RTNWallet/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "RTNWallet/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "RTNWallet/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "RTNWallet/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "RTNWallet/node_modules/metro-react-native-babel-preset": { + "version": "0.72.4", + "resolved": "https://registry.npmjs.org/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.72.4.tgz", + "integrity": "sha512-YGCVaYe1H5fOFktdDdL9IwAyiXjPh1t2eZZFp3KFJak6fxKpN+q5PPhe1kzMa77dbCAqgImv43zkfGa6i27eyA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.14.0", + "@babel/plugin-proposal-async-generator-functions": "^7.0.0", + "@babel/plugin-proposal-class-properties": "^7.0.0", + "@babel/plugin-proposal-export-default-from": "^7.0.0", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0", + "@babel/plugin-proposal-object-rest-spread": "^7.0.0", + "@babel/plugin-proposal-optional-catch-binding": "^7.0.0", + "@babel/plugin-proposal-optional-chaining": "^7.0.0", + "@babel/plugin-syntax-dynamic-import": "^7.0.0", + "@babel/plugin-syntax-export-default-from": "^7.0.0", + "@babel/plugin-syntax-flow": "^7.2.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.0.0", + "@babel/plugin-syntax-optional-chaining": "^7.0.0", + "@babel/plugin-transform-arrow-functions": "^7.0.0", + "@babel/plugin-transform-async-to-generator": "^7.0.0", + "@babel/plugin-transform-block-scoping": "^7.0.0", + "@babel/plugin-transform-classes": "^7.0.0", + "@babel/plugin-transform-computed-properties": "^7.0.0", + "@babel/plugin-transform-destructuring": "^7.0.0", + "@babel/plugin-transform-exponentiation-operator": "^7.0.0", + "@babel/plugin-transform-flow-strip-types": "^7.0.0", + "@babel/plugin-transform-function-name": "^7.0.0", + "@babel/plugin-transform-literals": "^7.0.0", + "@babel/plugin-transform-modules-commonjs": "^7.0.0", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.0.0", + "@babel/plugin-transform-parameters": "^7.0.0", + "@babel/plugin-transform-react-display-name": "^7.0.0", + "@babel/plugin-transform-react-jsx": "^7.0.0", + "@babel/plugin-transform-react-jsx-self": "^7.0.0", + "@babel/plugin-transform-react-jsx-source": "^7.0.0", + "@babel/plugin-transform-runtime": "^7.0.0", + "@babel/plugin-transform-shorthand-properties": "^7.0.0", + "@babel/plugin-transform-spread": "^7.0.0", + "@babel/plugin-transform-sticky-regex": "^7.0.0", + "@babel/plugin-transform-template-literals": "^7.0.0", + "@babel/plugin-transform-typescript": "^7.5.0", + "@babel/plugin-transform-unicode-regex": "^7.0.0", + "@babel/template": "^7.0.0", + "react-refresh": "^0.4.0" + }, + "peerDependencies": { + "@babel/core": "*" + } + }, + "RTNWallet/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "RTNWallet/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "RTNWallet/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "RTNWallet/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "RTNWallet/node_modules/react-refresh": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.4.3.tgz", + "integrity": "sha512-Hwln1VNuGl/6bVwnd0Xdn1e84gT/8T9aYNL+HAKDArLCS7LWjwr7StE30IEYbIkx0Vi3vs+coQxe+SQDbGbbpA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "RTNWallet/node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "RTNWallet/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } } }, "dependencies": { @@ -89884,6 +90187,227 @@ "integrity": "sha512-QIpG33l3QB0AkTfX/ccRknwNRu1APNUkokVKF1lpRO2+tBnkXnGL0UapgXg5u9KIONZtrpupeDeO+J5B2TeQVw==", "requires": {} }, + "react-native-wallet": { + "version": "file:RTNWallet", + "requires": { + "@babel/core": "7.20.12", + "@babel/preset-env": "^7.20.2", + "@babel/preset-react": "^7.18.6", + "babel-eslint": "^10.1.0", + "babel-loader": "^8.2.5", + "eslint": "^7.6.0", + "eslint-config-expensify": "^2.0.24", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-jsx-a11y": "^6.6.1", + "eslint-plugin-react": "^7.31.10", + "metro-react-native-babel-preset": "^0.72.3", + "prettier": "^2.8.8" + }, + "dependencies": { + "@babel/core": { + "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", + "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.7", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helpers": "^7.20.7", + "@babel/parser": "^7.20.7", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.12", + "@babel/types": "^7.20.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "babel-loader": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", + "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==", + "dev": true, + "requires": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + } + }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "metro-react-native-babel-preset": { + "version": "0.72.4", + "resolved": "https://registry.npmjs.org/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.72.4.tgz", + "integrity": "sha512-YGCVaYe1H5fOFktdDdL9IwAyiXjPh1t2eZZFp3KFJak6fxKpN+q5PPhe1kzMa77dbCAqgImv43zkfGa6i27eyA==", + "dev": true, + "requires": { + "@babel/core": "^7.14.0", + "@babel/plugin-proposal-async-generator-functions": "^7.0.0", + "@babel/plugin-proposal-class-properties": "^7.0.0", + "@babel/plugin-proposal-export-default-from": "^7.0.0", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0", + "@babel/plugin-proposal-object-rest-spread": "^7.0.0", + "@babel/plugin-proposal-optional-catch-binding": "^7.0.0", + "@babel/plugin-proposal-optional-chaining": "^7.0.0", + "@babel/plugin-syntax-dynamic-import": "^7.0.0", + "@babel/plugin-syntax-export-default-from": "^7.0.0", + "@babel/plugin-syntax-flow": "^7.2.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.0.0", + "@babel/plugin-syntax-optional-chaining": "^7.0.0", + "@babel/plugin-transform-arrow-functions": "^7.0.0", + "@babel/plugin-transform-async-to-generator": "^7.0.0", + "@babel/plugin-transform-block-scoping": "^7.0.0", + "@babel/plugin-transform-classes": "^7.0.0", + "@babel/plugin-transform-computed-properties": "^7.0.0", + "@babel/plugin-transform-destructuring": "^7.0.0", + "@babel/plugin-transform-exponentiation-operator": "^7.0.0", + "@babel/plugin-transform-flow-strip-types": "^7.0.0", + "@babel/plugin-transform-function-name": "^7.0.0", + "@babel/plugin-transform-literals": "^7.0.0", + "@babel/plugin-transform-modules-commonjs": "^7.0.0", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.0.0", + "@babel/plugin-transform-parameters": "^7.0.0", + "@babel/plugin-transform-react-display-name": "^7.0.0", + "@babel/plugin-transform-react-jsx": "^7.0.0", + "@babel/plugin-transform-react-jsx-self": "^7.0.0", + "@babel/plugin-transform-react-jsx-source": "^7.0.0", + "@babel/plugin-transform-runtime": "^7.0.0", + "@babel/plugin-transform-shorthand-properties": "^7.0.0", + "@babel/plugin-transform-spread": "^7.0.0", + "@babel/plugin-transform-sticky-regex": "^7.0.0", + "@babel/plugin-transform-template-literals": "^7.0.0", + "@babel/plugin-transform-typescript": "^7.5.0", + "@babel/plugin-transform-unicode-regex": "^7.0.0", + "@babel/template": "^7.0.0", + "react-refresh": "^0.4.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "react-refresh": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.4.3.tgz", + "integrity": "sha512-Hwln1VNuGl/6bVwnd0Xdn1e84gT/8T9aYNL+HAKDArLCS7LWjwr7StE30IEYbIkx0Vi3vs+coQxe+SQDbGbbpA==", + "dev": true + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, "react-native-web": { "version": "0.19.9", "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.19.9.tgz", diff --git a/package.json b/package.json index 5664d2326a57..8ca28f18cbc1 100644 --- a/package.json +++ b/package.json @@ -160,6 +160,7 @@ "react-native-url-polyfill": "^2.0.0", "react-native-view-shot": "3.8.0", "react-native-vision-camera": "^2.16.2", + "react-native-wallet": "file:RTNWallet", "react-native-web": "^0.19.9", "react-native-web-linear-gradient": "^1.1.2", "react-native-webview": "13.6.3", diff --git a/tsconfig.json b/tsconfig.json index 837f98dc5516..6529762737ca 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -46,6 +46,6 @@ } }, "exclude": ["**/node_modules/*", "**/dist/*", ".github/actions/**/index.js", "**/docs/*"], - "include": ["src", "desktop", "web", "website", "docs", "assets", "config", "tests", "jest", "__mocks__", ".github/**/*", ".storybook/**/*"], + "include": ["src", "desktop", "web", "website", "docs", "assets", "config", "tests", "jest", "__mocks__", ".github/**/*", ".storybook/**/*", "**/RTNWallet/*"], "extends": "expo/tsconfig.base" }