Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Personal Bank Account via Plaid flow #2746

Merged
merged 13 commits into from
May 17, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ EXPENSIFY_URL_COM=https://www.expensify.com.dev/
EXPENSIFY_PARTNER_NAME=chat-expensify-com
EXPENSIFY_PARTNER_PASSWORD=e21965746fd75f82bb66
PUSHER_APP_KEY=ac6d22b891daae55283a
SECURE_NGROK_URL=https://secure-expensify-user.ngrok.io/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious, can't we reuse the EXPENSIFY_URL_SECURE config for this, and make people enter their secure-ngrok URL there?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could, but this way means if you want to switch from using ngrok to not using it you can just set USE_NGROK to false without having to change the urls often.

NGROK_URL=https://expensify-user.ngrok.io/
USE_NGROK=false
USE_WEB_PROXY=false
5 changes: 5 additions & 0 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,11 @@ dependencies {

// Crashlytics
implementation 'com.google.firebase:firebase-crashlytics:17.2.2'

// Plaid SDK
implementation project(':react-native-plaid-link-sdk')
// This okhttp3 dependency prevents the app from crashing - See https://github.com/plaid/react-native-plaid-link-sdk/issues/74#issuecomment-648435002
implementation "com.squareup.okhttp3:okhttp-urlconnection:4.+"
}

// Run this once to be able to run the application with BUCK
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.soloader.SoLoader;
import com.plaid.PlaidPackage;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Field;
import java.util.List;
Expand All @@ -29,6 +30,7 @@ protected List<ReactPackage> getPackages() {
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
packages.add(new PlaidPackage());
return packages;
}

Expand Down
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
buildscript {
ext {
buildToolsVersion = "29.0.2"
minSdkVersion = 16
minSdkVersion = 21
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to be a requirement in order to use the Plaid SDK on Android

https://github.com/plaid/react-native-plaid-link-sdk#3-configure-gradle

I'm hoping it's OK ? @Jag96

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We talked about doing this a while back but this is definitely fine, the issue is here: https://github.com/Expensify/Expensify/issues/149395

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh nice!

compileSdkVersion = 30
targetSdkVersion = 30
androidXCore = "1.0.2"
Expand Down
2 changes: 1 addition & 1 deletion android/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems necessary after adding the Plaid SDK there's some more information here about this:

https://medium.com/google-developers/faster-android-studio-builds-with-dex-in-process-5988ed8aa37e

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TL;DR is that without this enabled the android app wouldn't build at all.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What version of the JDK do we target? MaxPermSize was removed in JDK 8 so that param might not do anything

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JDK 8 I believe, I don't know much about Java though I just uncommented this line out based on the advice in that blog post.


# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
Expand Down
2 changes: 2 additions & 0 deletions android/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ include ':@react-native-community_async-storage'
project(':@react-native-community_async-storage').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/async-storage/android')
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
include ':app'
include ':react-native-plaid-link-sdk'
project(':react-native-plaid-link-sdk').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-plaid-link-sdk/android')
52 changes: 47 additions & 5 deletions ios/ExpensifyCash.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ require_relative '../node_modules/@react-native-community/cli-platform-ios/nativ
platform :ios, '11.0'

target 'ExpensifyCash' do
pod 'Plaid', '~> 2.1.2'
config = use_native_modules!

use_react_native!(:path => config["reactNativePath"])
Expand Down
48 changes: 32 additions & 16 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ PODS:
- Airship/Core
- boost-for-react-native (1.63.0)
- CocoaAsyncSocket (7.6.5)
- CocoaLibEvent (1.0.0)
- DoubleConversion (1.1.6)
- FBLazyVector (0.63.3)
- FBReactNativeSpec (0.63.3):
Expand Down Expand Up @@ -65,17 +64,20 @@ PODS:
- Flipper (0.54.0):
- Flipper-Folly (~> 2.2)
- Flipper-RSocket (~> 1.1)
- Flipper-Boost-iOSX (1.76.0.1.13)
- Flipper-DoubleConversion (1.1.7)
- Flipper-Folly (2.3.0):
- boost-for-react-native
- CocoaLibEvent (~> 1.0)
- Flipper-Fmt (7.1.7)
- Flipper-Folly (2.6.7):
- Flipper-Boost-iOSX
- Flipper-DoubleConversion
- Flipper-Fmt (= 7.1.7)
- Flipper-Glog
- OpenSSL-Universal (= 1.0.2.20)
- libevent (~> 2.1.12)
- OpenSSL-Universal (= 1.1.180)
- Flipper-Glog (0.3.6)
- Flipper-PeerTalk (0.0.4)
- Flipper-RSocket (1.1.0):
- Flipper-Folly (~> 2.2)
- Flipper-RSocket (1.4.3):
- Flipper-Folly (~> 2.6)
- FlipperKit (0.54.0):
- FlipperKit/Core (= 0.54.0)
- FlipperKit/Core (0.54.0):
Expand Down Expand Up @@ -145,14 +147,14 @@ PODS:
- GoogleUtilities/Logger
- GoogleUtilities/UserDefaults (6.7.2):
- GoogleUtilities/Logger
- libevent (2.1.12)
- nanopb (1.30906.0):
- nanopb/decode (= 1.30906.0)
- nanopb/encode (= 1.30906.0)
- nanopb/decode (1.30906.0)
- nanopb/encode (1.30906.0)
- OpenSSL-Universal (1.0.2.20):
- OpenSSL-Universal/Static (= 1.0.2.20)
- OpenSSL-Universal/Static (1.0.2.20)
- OpenSSL-Universal (1.1.180)
- Plaid (2.1.2)
- PromisesObjC (1.2.11)
- RCTRequired (0.63.3)
- RCTTypeSafety (0.63.3):
Expand Down Expand Up @@ -332,6 +334,9 @@ PODS:
- React-Core
- react-native-pdf (6.2.2):
- React-Core
- react-native-plaid-link-sdk (7.0.5):
- Plaid (~> 2.1.2)
- React-Core
- react-native-progress-bar-android (1.0.4):
- React
- react-native-progress-view (1.2.3):
Expand Down Expand Up @@ -461,6 +466,7 @@ DEPENDENCIES:
- FlipperKit/SKIOSNetworkPlugin (~> 0.54.0)
- Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- Plaid (~> 2.1.2)
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
- RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
- React (from `../node_modules/react-native/`)
Expand All @@ -478,6 +484,7 @@ DEPENDENCIES:
- react-native-image-picker (from `../node_modules/react-native-image-picker`)
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
- react-native-pdf (from `../node_modules/react-native-pdf`)
- react-native-plaid-link-sdk (from `../node_modules/react-native-plaid-link-sdk`)
- "react-native-progress-bar-android (from `../node_modules/@react-native-community/progress-bar-android`)"
- "react-native-progress-view (from `../node_modules/@react-native-community/progress-view`)"
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
Expand Down Expand Up @@ -512,15 +519,16 @@ SPEC REPOS:
- Airship
- boost-for-react-native
- CocoaAsyncSocket
- CocoaLibEvent
- Firebase
- FirebaseAnalytics
- FirebaseCore
- FirebaseCoreDiagnostics
- FirebaseCrashlytics
- FirebaseInstallations
- Flipper
- Flipper-Boost-iOSX
- Flipper-DoubleConversion
- Flipper-Fmt
- Flipper-Folly
- Flipper-Glog
- Flipper-PeerTalk
Expand All @@ -529,8 +537,10 @@ SPEC REPOS:
- GoogleAppMeasurement
- GoogleDataTransport
- GoogleUtilities
- libevent
- nanopb
- OpenSSL-Universal
- Plaid
- PromisesObjC
- YogaKit

Expand Down Expand Up @@ -575,6 +585,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/@react-native-community/netinfo"
react-native-pdf:
:path: "../node_modules/react-native-pdf"
react-native-plaid-link-sdk:
:path: "../node_modules/react-native-plaid-link-sdk"
react-native-progress-bar-android:
:path: "../node_modules/@react-native-community/progress-bar-android"
react-native-progress-view:
Expand Down Expand Up @@ -636,7 +648,6 @@ SPEC CHECKSUMS:
Airship: 02ad73780f9eed21870e36b0aaab327acda6a102
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f
DoubleConversion: cde416483dac037923206447da6e1454df403714
FBLazyVector: 878b59e31113e289e275165efbe4b54fa614d43d
FBReactNativeSpec: 7da9338acfb98d4ef9e5536805a0704572d33c2f
Expand All @@ -647,19 +658,23 @@ SPEC CHECKSUMS:
FirebaseCrashlytics: 1a747c9cc084a24dc6d9511c991db1cd078154eb
FirebaseInstallations: 466c7b4d1f58fe16707693091da253726a731ed2
Flipper: be611d4b742d8c87fbae2ca5f44603a02539e365
Flipper-Boost-iOSX: a30adb43d16a4ca0a503723360d90cca6f58836c
Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41
Flipper-Folly: e4493b013c02d9347d5e0cb4d128680239f6c78a
Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b
Flipper-Folly: 83af37379faa69497529e414bd43fbfc7cae259a
Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6
Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
Flipper-RSocket: 64e7431a55835eb953b0bf984ef3b90ae9fdddd7
Flipper-RSocket: d9d9ade67cbecf6ac10730304bf5607266dd2541
FlipperKit: ab353d41aea8aae2ea6daaf813e67496642f3d7d
Folly: b73c3869541e86821df3c387eb0af5f65addfab4
glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3
GoogleAppMeasurement: a6a3a066369828db64eda428cb2856dc1cdc7c4e
GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833
GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc
OpenSSL-Universal: ff34003318d5e1163e9529b08470708e389ffcdd
OpenSSL-Universal: 1aa4f6a6ee7256b83db99ec1ccdaa80d10f9af9b
Plaid: c02276ccc630a726a9ed790bf923d29839ff4017
PromisesObjC: 8c196f5a328c2cba3e74624585467a557dcb482f
RCTRequired: 48884c74035a0b5b76dbb7a998bd93bcfc5f2047
RCTTypeSafety: edf4b618033c2f1c5b7bc3d90d8e085ed95ba2ab
Expand All @@ -676,6 +691,7 @@ SPEC CHECKSUMS:
react-native-image-picker: 32d1ad2c0024ca36161ae0d5c2117e2d6c441f11
react-native-netinfo: 52cf0ee8342548a485e28f4b09e56b477567244d
react-native-pdf: 4b5a9e4465a6a3b399e91dc4838eb44ddf716d1f
react-native-plaid-link-sdk: 1a6593e2d3d790e8113c29178d883eb883f8c032
react-native-progress-bar-android: ce95a69f11ac580799021633071368d08aaf9ad8
react-native-progress-view: 5816e8a6be812c2b122c6225a2a3db82d9008640
react-native-safe-area-context: 01158a92c300895d79dee447e980672dc3fb85a6
Expand Down Expand Up @@ -706,6 +722,6 @@ SPEC CHECKSUMS:
Yoga: 7d13633d129fd179e01b8953d38d47be90db185a
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a

PODFILE CHECKSUM: 41b806c7f131f87b716be1f1f9377532d6c9e43a
PODFILE CHECKSUM: a9d30b7ba6e6413d43aaf7c2a5a52b733317b379

COCOAPODS: 1.10.1
27 changes: 27 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,15 @@
"react-native-onyx": "git+https://github.com/Expensify/react-native-onyx.git#accabbd24b5d9a4556b9619a47ba081325622c46",
"react-native-pdf": "^6.2.2",
"react-native-picker-select": "8.0.4",
"react-native-plaid-link-sdk": "^7.0.5",
"react-native-reanimated": "1.13.2",
"react-native-render-html": "^6.0.0-alpha.10",
"react-native-safe-area-context": "^3.1.4",
"react-native-screens": "2.17.1",
"react-native-svg": "^12.1.0",
"react-native-web": "^0.14.1",
"react-pdf": "^5.2.0",
"react-plaid-link": "^3.1.0",
"react-web-config": "^1.0.0",
"rn-fetch-blob": "^0.12.0",
"save": "^2.4.0",
Expand Down
6 changes: 3 additions & 3 deletions src/CONFIG.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import CONST from './CONST';
const expensifyCashURL = addTrailingForwardSlash(lodashGet(Config, 'EXPENSIFY_URL_CASH', 'https://expensify.cash/'));
const expensifyURL = addTrailingForwardSlash(lodashGet(Config, 'EXPENSIFY_URL_COM', 'https://www.expensify.com/'));
const ngrokURL = addTrailingForwardSlash(lodashGet(Config, 'NGROK_URL', ''));
const secureNgrokURL = addTrailingForwardSlash(lodashGet(Config, 'SECURE_NGROK_URL', ''));
const useNgrok = lodashGet(Config, 'USE_NGROK', 'false') === 'true';
const useWebProxy = lodashGet(Config, 'USE_WEB_PROXY', 'true') === 'true';
const expensifyComWithProxy = getPlatform() === 'web' && useWebProxy ? '/' : expensifyURL;
const secureURLRoot = addTrailingForwardSlash(lodashGet(
const secureURLRoot = useNgrok && secureNgrokURL ? secureNgrokURL : addTrailingForwardSlash(lodashGet(
Config, 'EXPENSIFY_URL_SECURE', 'https://secure.expensify.com/',
));

Expand All @@ -25,8 +26,7 @@ export default {
APP_NAME: 'ExpensifyCash',
AUTH_TOKEN_EXPIRATION_TIME: 1000 * 60 * 90,
EXPENSIFY: {
// Note: This will be EXACTLY what is set for EXPENSIFY_URL_COM and EXPENSIFY_URL_SECURE whether the proxy is
// enabled or not.
// Note: This will be EXACTLY what is set for EXPENSIFY_URL_COM whether the proxy is enabled or not.
URL_EXPENSIFY_COM: expensifyURL,
URL_EXPENSIFY_SECURE: secureURLRoot,
URL_EXPENSIFY_CASH: expensifyCashURL,
Expand Down
7 changes: 7 additions & 0 deletions src/CONST.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,13 @@ const CONST = {
STARTUP: 8000,
RECONNECT: 1000,
},

PLAID: {
EVENT: {
ERROR: 'ERROR',
EXIT: 'EXIT',
},
},
};

export default CONST;
6 changes: 6 additions & 0 deletions src/ONYXKEYS.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ export default {
// Contains the user preference for the LHN priority mode
NVP_PRIORITY_MODE: 'nvp_priorityMode',

// SDK token used to communicate with Plaid API
PLAID_LINK_TOKEN: 'plaidLinkToken',

// List of bank accounts returned by Plaid
PLAID_BANK_ACCOUNTS: 'plaidBankAccounts',

// Collection Keys
COLLECTION: {
REPORT: 'report_',
Expand Down
1 change: 1 addition & 0 deletions src/ROUTES.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {addTrailingForwardSlash} from './libs/Url';
const REPORT = 'r';

export default {
ADD_BANK_ACCOUNT: 'add-bank-account',
HOME: '',
SETTINGS: 'settings',
SETTINGS_PROFILE: 'settings/profile',
Expand Down
35 changes: 35 additions & 0 deletions src/components/PlaidLink/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {useCallback, useEffect} from 'react';
import {usePlaidLink} from 'react-plaid-link';
import {plaidLinkPropTypes, plaidLinkDefaultProps} from './plaidLinkPropTypes';

const PlaidLink = (props) => {
const onSuccess = useCallback((publicToken, metadata) => {
props.onSuccess({publicToken, metadata});
}, []);

const {open, ready, error} = usePlaidLink({
Copy link
Contributor Author

@marcaaron marcaaron May 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We normally should not need to use hooks (and kind of discourage it), but the Plaid library we're using for React web here really only offers this option to launch the flow programmatically.

token: props.token,
onSuccess,
onExit: props.onExit,
});

useEffect(() => {
if (error) {
props.onError(error);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This usage leads to #38069. This happened due to a change in onError on each re-render as it wasn't memoized.

return;
}

if (!ready) {
return;
}

open();
}, [ready, error]);

return null;
};

PlaidLink.propTypes = plaidLinkPropTypes;
PlaidLink.defaultProps = plaidLinkDefaultProps;
PlaidLink.displayName = 'PlaidLink';
export default PlaidLink;
Loading