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 all 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ build/
.gradle
local.properties
*.iml
android/*.hprof

# Vscode
.vscode
Expand Down
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')
1 change: 1 addition & 0 deletions apple-app-site-association
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"/setpassword/*",
"/details/*",
"/v/*",
"/add-bank-account/*",
]
}
]
Expand Down
4 changes: 4 additions & 0 deletions ios/ExpensifyCash.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -371,10 +371,12 @@
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash-ExpensifyCashTests/Pods-ExpensifyCash-ExpensifyCashTests-frameworks.sh",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL/OpenSSL.framework/OpenSSL",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/LinkKit/LinkKit.framework/LinkKit",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/LinkKit.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
Expand Down Expand Up @@ -549,10 +551,12 @@
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash-frameworks.sh",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL/OpenSSL.framework/OpenSSL",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/LinkKit/LinkKit.framework/LinkKit",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/LinkKit.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
Expand Down
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
17 changes: 14 additions & 3 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@ PODS:
- nanopb/decode (1.30906.0)
- nanopb/encode (1.30906.0)
- OpenSSL-Universal (1.1.180)
- PromisesObjC (1.2.11)
- Plaid (2.1.2)
- PromisesObjC (1.2.12)
- RCTRequired (0.63.3)
- RCTTypeSafety (0.63.3):
- FBLazyVector (= 0.63.3)
Expand Down Expand Up @@ -330,6 +331,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 @@ -459,6 +463,7 @@ DEPENDENCIES:
- FlipperKit/SKIOSNetworkPlugin (= 0.75.1)
- 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 @@ -476,6 +481,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 @@ -529,6 +535,7 @@ SPEC REPOS:
- libevent
- nanopb
- OpenSSL-Universal
- Plaid
- PromisesObjC
- YogaKit

Expand Down Expand Up @@ -573,6 +580,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 @@ -658,7 +667,8 @@ SPEC CHECKSUMS:
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc
OpenSSL-Universal: 1aa4f6a6ee7256b83db99ec1ccdaa80d10f9af9b
PromisesObjC: 8c196f5a328c2cba3e74624585467a557dcb482f
Plaid: c02276ccc630a726a9ed790bf923d29839ff4017
PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97
RCTRequired: 48884c74035a0b5b76dbb7a998bd93bcfc5f2047
RCTTypeSafety: edf4b618033c2f1c5b7bc3d90d8e085ed95ba2ab
React: f36e90f3ceb976546e97df3403e37d226f79d0e3
Expand All @@ -674,6 +684,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 @@ -704,6 +715,6 @@ SPEC CHECKSUMS:
Yoga: 7d13633d129fd179e01b8953d38d47be90db185a
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a

PODFILE CHECKSUM: 3ed7125d9c3d76b4d32742223aa1fd55351b31ac
PODFILE CHECKSUM: 6eb5d43e785faa9b1fe52688d5bf7a328bd1b1cf

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 @@ -82,13 +82,15 @@
"react-native-onyx": "git+https://github.com/Expensify/react-native-onyx.git#586c76e7b90dbbde051d7ec7adbc4d53b2d51cd1",
"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
26 changes: 20 additions & 6 deletions src/CONFIG.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,30 @@ import {addTrailingForwardSlash} from './libs/Url';
import CONST from './CONST';

// Set default values to contributor friendly values to make development work out of the box without an .env file
const ENVIRONMENT = lodashGet(Config, 'ENVIRONMENT', CONST.ENVIRONMENT.DEV);
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 expensifyURLSecure = addTrailingForwardSlash(lodashGet(
Config, 'EXPENSIFY_URL_SECURE', 'https://secure.expensify.com/',
));
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(
Config, 'EXPENSIFY_URL_SECURE', 'https://secure.expensify.com/',
));

// Throw errors on dev if config variables are not set correctly
if (ENVIRONMENT === CONST.ENVIRONMENT.DEV) {
if (!useNgrok && expensifyURL.includes('dev') && !expensifyURLSecure.includes('dev')) {
throw new Error('EXPENSIFY_URL_SECURE must end with .dev when EXPENSIFY_URL_COM ends with .dev');
}

if (useNgrok && !secureNgrokURL) {
throw new Error('SECURE_NGROK_URL must be defined in .env when USE_NGROK=true');
}
}

const secureURLRoot = useNgrok && secureNgrokURL ? secureNgrokURL : expensifyURLSecure;

// Ngrok helps us avoid many of our cross-domain issues with connecting to our API
// and is required for viewing images on mobile and for developing on android
Expand All @@ -25,16 +40,15 @@ 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,
URL_API_ROOT: expensifyURLRoot,
PARTNER_NAME: lodashGet(Config, 'EXPENSIFY_PARTNER_NAME', 'chat-expensify-com'),
PARTNER_PASSWORD: lodashGet(Config, 'EXPENSIFY_PARTNER_PASSWORD', 'e21965746fd75f82bb66'),
EXPENSIFY_CASH_REFERER: 'ecash',
ENVIRONMENT: lodashGet(Config, 'ENVIRONMENT', CONST.ENVIRONMENT.DEV),
ENVIRONMENT,
},
// eslint-disable-next-line no-undef
IS_IN_PRODUCTION: Platform.OS === 'web' ? process.env.NODE_ENV === 'production' : !__DEV__,
Expand Down
7 changes: 7 additions & 0 deletions src/CONST.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,13 @@ const CONST = {
RECONNECT: 1000,
},

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

OS: {
WINDOWS: 'Windows',
MAC_OS: 'Mac OS',
Expand Down
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