diff --git a/assets/images/bolt.svg b/assets/images/bolt.svg
new file mode 100644
index 000000000000..2290fbd7ded7
--- /dev/null
+++ b/assets/images/bolt.svg
@@ -0,0 +1,7 @@
+
+
+
diff --git a/assets/images/transfer.svg b/assets/images/transfer.svg
new file mode 100644
index 000000000000..03cb3c5f72b2
--- /dev/null
+++ b/assets/images/transfer.svg
@@ -0,0 +1,11 @@
+
+
+
diff --git a/src/CONST.js b/src/CONST.js
index 9e06129f252f..1fe463dcb01c 100755
--- a/src/CONST.js
+++ b/src/CONST.js
@@ -377,6 +377,11 @@ const CONST = {
},
WALLET: {
+ TRANSFER_BALANCE_FEE: 0.30,
+ TRANSFER_METHOD_TYPE: {
+ INSTANT: 'instant',
+ ACH: 'ach',
+ },
ERROR: {
IDENTITY_NOT_FOUND: 'Identity not found',
INVALID_SSN: 'Invalid SSN',
diff --git a/src/ONYXKEYS.js b/src/ONYXKEYS.js
index 1bfc29e16510..4c7ea591f3ff 100755
--- a/src/ONYXKEYS.js
+++ b/src/ONYXKEYS.js
@@ -168,4 +168,8 @@ export default {
// Is Keyboard shortcuts modal open?
IS_SHORTCUTS_MODAL_OPEN: 'isShortcutsModalOpen',
+
+ // Stores information about active wallet transfer amount, selectedAccountID, status, etc
+ WALLET_TRANSFER: 'walletTransfer',
+
};
diff --git a/src/ROUTES.js b/src/ROUTES.js
index a3a33bcdaa96..5f02858208d6 100644
--- a/src/ROUTES.js
+++ b/src/ROUTES.js
@@ -33,6 +33,8 @@ export default {
SETTINGS_ADD_BANK_ACCOUNT: 'settings/payments/add-bank-account',
SETTINGS_ADD_LOGIN: 'settings/addlogin/:type',
getSettingsAddLoginRoute: type => `settings/addlogin/${type}`,
+ SETTINGS_PAYMENTS_TRANSFER_BALANCE: 'settings/payments/transfer-balance',
+ SETTINGS_PAYMENTS_CHOOSE_TRANSFER_ACCOUNT: 'settings/payments/choose-transfer-account',
NEW_GROUP: 'new/group',
NEW_CHAT: 'new/chat',
REPORT,
diff --git a/src/components/Icon/Expensicons.js b/src/components/Icon/Expensicons.js
index ab8561c91b2e..945c81cda6ff 100644
--- a/src/components/Icon/Expensicons.js
+++ b/src/components/Icon/Expensicons.js
@@ -4,6 +4,7 @@ import ArrowRight from '../../../assets/images/arrow-right.svg';
import BackArrow from '../../../assets/images/back-left.svg';
import Bank from '../../../assets/images/bank.svg';
import Bill from '../../../assets/images/bill.svg';
+import Bolt from '../../../assets/images/bolt.svg';
import Briefcase from '../../../assets/images/briefcase.svg';
import Bug from '../../../assets/images/bug.svg';
import Building from '../../../assets/images/building.svg';
@@ -57,6 +58,7 @@ import RotateLeft from '../../../assets/images/rotate-left.svg';
import Send from '../../../assets/images/send.svg';
import SignOut from '../../../assets/images/sign-out.svg';
import Sync from '../../../assets/images/sync.svg';
+import Transfer from '../../../assets/images/transfer.svg';
import ThreeDots from '../../../assets/images/three-dots.svg';
import Trashcan from '../../../assets/images/trashcan.svg';
import UpArrow from '../../../assets/images/arrow-up.svg';
@@ -73,6 +75,7 @@ export {
BackArrow,
Bank,
Bill,
+ Bolt,
Briefcase,
Building,
Bug,
@@ -126,6 +129,7 @@ export {
Send,
SignOut,
Sync,
+ Transfer,
ThreeDots,
Trashcan,
UpArrow,
diff --git a/src/languages/en.js b/src/languages/en.js
index 231416f6ca17..3bf15aa0e1aa 100755
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -92,6 +92,7 @@ export default {
bankAccount: 'Bank account',
join: 'Join',
decline: 'Decline',
+ transferBalance: 'Transfer Balance',
cantFindAddress: 'Can\'t find your address? ',
enterManually: 'Enter it manually',
},
@@ -335,9 +336,27 @@ export default {
},
paymentsPage: {
paymentMethodsTitle: 'Payment methods',
+ allSet: 'All Set!',
+ transferConfirmText: ({amount}) => `${amount} will hit your account shortly!`,
+ gotIt: 'Got it, Thanks!',
+ },
+ transferAmountPage: {
+ transfer: ({amount}) => `Transfer${amount ? ` ${amount}` : ''}`,
+ instant: 'Instant (Debit Card)',
+ instantSummary: ({amount}) => `1.5% fee (${amount} minimum)`,
+ ach: '1-3 Business Days (Bank Account)',
+ achSummary: 'No fee',
+ whichAccount: 'Which Account?',
+ fee: 'Fee',
+ failedTransfer: 'Failed to transfer balance',
+ },
+ chooseTransferAccountPage: {
+ chooseAccount: 'Choose Account',
},
paymentMethodList: {
addPaymentMethod: 'Add payment method',
+ addDebitCard: 'Add debit card',
+ addBankAccount: 'Add bank account',
accountLastFour: 'Account ending in',
cardLastFour: 'Card ending in',
addFirstPaymentMethod: 'Add a payment method to send and receive payments directly in the app.',
diff --git a/src/languages/es.js b/src/languages/es.js
index e37c269231c9..f936749ca409 100644
--- a/src/languages/es.js
+++ b/src/languages/es.js
@@ -92,6 +92,7 @@ export default {
bankAccount: 'Cuenta bancaria',
join: 'Unirse',
decline: 'Rechazar',
+ transferBalance: 'Transferencia de saldo',
cantFindAddress: '¿No encuentras tu dirección? ',
enterManually: 'Ingresar manualmente',
},
@@ -335,9 +336,27 @@ export default {
},
paymentsPage: {
paymentMethodsTitle: 'Métodos de pago',
+ allSet: 'Todo listo!',
+ transferConfirmText: ({amount}) => `${amount} llegará a tu cuenta en breve!`,
+ gotIt: 'Gracias!',
+ },
+ transferAmountPage: {
+ transfer: ({amount}) => `Transferir${amount ? ` ${amount}` : ''}`,
+ instant: 'Instante',
+ instantSummary: ({amount}) => `Tarifa del 1.5% (${amount} mínimo)`,
+ ach: '1-3 días laborales',
+ achSummary: 'Sin cargo',
+ whichAccount: '¿Que cuenta?',
+ fee: 'Tarifa',
+ failedTransfer: 'No se pudo transferir el saldo',
+ },
+ chooseTransferAccountPage: {
+ chooseAccount: 'Elegir cuenta',
},
paymentMethodList: {
addPaymentMethod: 'Agrega método de pago',
+ addDebitCard: 'Agregar tarjeta de débito',
+ addBankAccount: 'Agregar cuenta de banco',
accountLastFour: 'Cuenta con terminación',
cardLastFour: 'Tarjeta con terminacíon',
addFirstPaymentMethod: 'Añade un método de pago para enviar y recibir pagos directamente desde la aplicación.',
diff --git a/src/libs/API.js b/src/libs/API.js
index 60ebf7334203..9904b7756f18 100644
--- a/src/libs/API.js
+++ b/src/libs/API.js
@@ -1123,6 +1123,21 @@ function CreatePolicyRoom(parameters) {
return Network.post(commandName, parameters);
}
+/**
+ * Transfer Wallet balance and takes either the bankAccoundID or fundID
+ * @param {Object} parameters
+ * @param {String} [parameters.bankAccountID]
+ * @param {String} [parameters.fundID]
+ * @returns {Promise}
+ */
+function Wallet_TransferBalance(parameters) {
+ const commandName = 'TransferWalletBalance';
+ if (!parameters.bankAccountID && !parameters.fundID) {
+ throw new Error('Must pass either bankAccountID or fundID to TransferWalletBalance');
+ }
+ return Network.post(commandName, parameters);
+}
+
export {
Authenticate,
AuthenticateWithAccountID,
@@ -1180,6 +1195,7 @@ export {
ValidateEmail,
Wallet_Activate,
Wallet_GetOnfidoSDKToken,
+ Wallet_TransferBalance,
GetLocalCurrency,
GetCurrencyList,
Policy_Create,
diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js
index 3e2d3c1ddbc2..ccfe29fa6523 100644
--- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js
+++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js
@@ -40,6 +40,8 @@ import WorkspaceMembersPage from '../../../pages/workspace/WorkspaceMembersPage'
import WorkspaceBankAccountPage from '../../../pages/workspace/WorkspaceBankAccountPage';
import WorkspaceNewRoomPage from '../../../pages/workspace/WorkspaceNewRoomPage';
import CONST from '../../../CONST';
+import TransferBalancePage from '../../../pages/settings/Payments/TransferBalancePage';
+import ChooseTransferAccountPage from '../../../pages/settings/Payments/ChooseTransferAccountPage';
const defaultSubRouteOptions = {
cardStyle: styles.navigationScreenCardStyle,
@@ -180,6 +182,14 @@ const SettingsModalStackNavigator = createModalStackNavigator([
Component: SettingsPaymentsPage,
name: 'Settings_Payments',
},
+ {
+ Component: TransferBalancePage,
+ name: 'Settings_Payments_Transfer_Balance',
+ },
+ {
+ Component: ChooseTransferAccountPage,
+ name: 'Settings_Payments_Choose_Transfer_Account',
+ },
{
Component: SettingsAddPayPalMePage,
name: 'Settings_Add_Paypal_Me',
diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js
index 2b61a58a5f40..34946309b308 100644
--- a/src/libs/Navigation/linkingConfig.js
+++ b/src/libs/Navigation/linkingConfig.js
@@ -52,6 +52,14 @@ export default {
path: ROUTES.SETTINGS_PAYMENTS,
exact: true,
},
+ Settings_Payments_Transfer_Balance: {
+ path: ROUTES.SETTINGS_PAYMENTS_TRANSFER_BALANCE,
+ exact: true,
+ },
+ Settings_Payments_Choose_Transfer_Account: {
+ path: ROUTES.SETTINGS_PAYMENTS_CHOOSE_TRANSFER_ACCOUNT,
+ exact: true,
+ },
Settings_Add_Paypal_Me: {
path: ROUTES.SETTINGS_ADD_PAYPAL_ME,
exact: true,
diff --git a/src/libs/PaymentUtils.js b/src/libs/PaymentUtils.js
new file mode 100644
index 000000000000..4e33a165cb3e
--- /dev/null
+++ b/src/libs/PaymentUtils.js
@@ -0,0 +1,69 @@
+import _ from 'underscore';
+import lodashGet from 'lodash/get';
+import * as Expensicons from '../components/Icon/Expensicons';
+import getBankIcon from '../components/Icon/BankIcons';
+import CONST from '../CONST';
+import * as Localize from './Localize';
+
+/**
+ * Get the PaymentMethods list
+ * @param {Array} bankAccountList
+ * @param {Array} cardList
+ * @param {String} [payPalMeUsername='']
+ * @returns {Array}
+ */
+function getPaymentMethods(bankAccountList, cardList, payPalMeUsername = '') {
+ const combinedPaymentMethods = [];
+
+ _.each(bankAccountList, (bankAccount) => {
+ // Add all bank accounts besides the wallet
+ if (bankAccount.type === CONST.BANK_ACCOUNT_TYPES.WALLET) {
+ return;
+ }
+
+ const formattedBankAccountNumber = bankAccount.accountNumber
+ ? `${Localize.translateLocal('paymentMethodList.accountLastFour')} ${bankAccount.accountNumber.slice(-4)
+ }`
+ : null;
+ const {icon, iconSize} = getBankIcon(lodashGet(bankAccount, 'additionalData.bankName', ''));
+ combinedPaymentMethods.push({
+ title: bankAccount.addressName,
+ description: formattedBankAccountNumber,
+ methodID: bankAccount.bankAccountID,
+ icon,
+ iconSize,
+ key: `bankAccount-${bankAccount.bankAccountID}`,
+ });
+ });
+
+ _.each(cardList, (card) => {
+ const formattedCardNumber = card.cardNumber
+ ? `${Localize.translateLocal('paymentMethodList.cardLastFour')} ${card.cardNumber.slice(-4)}`
+ : null;
+ const {icon, iconSize} = getBankIcon(card.bank, true);
+ combinedPaymentMethods.push({
+ title: card.addressName,
+ description: formattedCardNumber,
+ methodID: card.cardNumber,
+ icon,
+ iconSize,
+ key: `card-${card.cardNumber}`,
+ });
+ });
+
+ if (payPalMeUsername) {
+ combinedPaymentMethods.push({
+ title: 'PayPal.me',
+ methodID: CONST.PAYMENT_METHODS.PAYPAL,
+ description: payPalMeUsername,
+ icon: Expensicons.PayPal,
+ key: 'payPalMePaymentMethod',
+ });
+ }
+
+ return combinedPaymentMethods;
+}
+
+export default {
+ getPaymentMethods,
+};
diff --git a/src/pages/settings/Payments/ChooseTransferAccountPage.js b/src/pages/settings/Payments/ChooseTransferAccountPage.js
new file mode 100644
index 000000000000..eb3fec894eca
--- /dev/null
+++ b/src/pages/settings/Payments/ChooseTransferAccountPage.js
@@ -0,0 +1,27 @@
+import React from 'react';
+import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton';
+import ScreenWrapper from '../../../components/ScreenWrapper';
+import Navigation from '../../../libs/Navigation/Navigation';
+import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
+import KeyboardAvoidingView from '../../../components/KeyboardAvoidingView';
+
+const propTypes = {
+ ...withLocalizePropTypes,
+};
+
+const ChooseTransferAccountPage = props => (
+
+
+ Navigation.goBack()}
+ onCloseButtonPress={() => Navigation.dismissModal()}
+ />
+
+
+);
+
+ChooseTransferAccountPage.propTypes = propTypes;
+
+export default withLocalize(ChooseTransferAccountPage);
diff --git a/src/pages/settings/Payments/PaymentMethodList.js b/src/pages/settings/Payments/PaymentMethodList.js
index 15f6548a9cef..54d9a3d9817a 100644
--- a/src/pages/settings/Payments/PaymentMethodList.js
+++ b/src/pages/settings/Payments/PaymentMethodList.js
@@ -3,7 +3,6 @@ import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {FlatList} from 'react-native';
import {withOnyx} from 'react-native-onyx';
-import lodashGet from 'lodash/get';
import styles from '../../../styles/styles';
import * as StyleUtils from '../../../styles/StyleUtils';
import MenuItem from '../../../components/MenuItem';
@@ -13,8 +12,8 @@ import withLocalize, {withLocalizePropTypes} from '../../../components/withLocal
import ONYXKEYS from '../../../ONYXKEYS';
import CONST from '../../../CONST';
import * as Expensicons from '../../../components/Icon/Expensicons';
-import getBankIcon from '../../../components/Icon/BankIcons';
import bankAccountPropTypes from '../../../components/bankAccountPropTypes';
+import PaymentUtils from '../../../libs/PaymentUtils';
const MENU_ITEM = 'menuItem';
@@ -70,60 +69,12 @@ class PaymentMethodList extends Component {
* @returns {Array}
*/
createPaymentMethodList() {
- const combinedPaymentMethods = [];
-
- _.each(this.props.bankAccountList, (bankAccount) => {
- // Add all bank accounts besides the wallet
- if (bankAccount.type === CONST.BANK_ACCOUNT_TYPES.WALLET) {
- return;
- }
-
- const formattedBankAccountNumber = bankAccount.accountNumber
- ? `${this.props.translate('paymentMethodList.accountLastFour')} ${
- bankAccount.accountNumber.slice(-4)
- }`
- : null;
- const {icon, iconSize} = getBankIcon(lodashGet(bankAccount, 'additionalData.bankName', ''));
- combinedPaymentMethods.push({
- type: MENU_ITEM,
- title: bankAccount.addressName,
-
- // eslint-disable-next-line
- description: formattedBankAccountNumber,
- icon,
- iconSize,
- onPress: e => this.props.onPress(e, bankAccount.bankAccountID),
- key: `bankAccount-${bankAccount.bankAccountID}`,
- });
- });
-
- _.each(this.props.cardList, (card) => {
- const formattedCardNumber = card.cardNumber
- ? `${this.props.translate('paymentMethodList.cardLastFour')} ${card.cardNumber.slice(-4)}`
- : null;
- const {icon, iconSize} = getBankIcon(card.bank, true);
- combinedPaymentMethods.push({
- type: MENU_ITEM,
- title: card.addressName,
- // eslint-disable-next-line
- description: formattedCardNumber,
- icon,
- iconSize,
- onPress: e => this.props.onPress(e, card.cardNumber),
- key: `card-${card.cardNumber}`,
- });
- });
-
- if (this.props.payPalMeUsername) {
- combinedPaymentMethods.push({
- type: MENU_ITEM,
- title: 'PayPal.me',
- description: this.props.payPalMeUsername,
- icon: Expensicons.PayPal,
- onPress: e => this.props.onPress(e, 'payPalMe'),
- key: 'payPalMePaymentMethod',
- });
- }
+ let combinedPaymentMethods = PaymentUtils.getPaymentMethods(this.props.bankAccountList, this.props.cardList, this.props.payPalMeUsername);
+ combinedPaymentMethods = _.map(combinedPaymentMethods, paymentMethod => ({
+ ...paymentMethod,
+ type: MENU_ITEM,
+ onPress: e => this.props.onPress(e, paymentMethod.methodID),
+ }));
// If we have not added any payment methods, show a default empty state
if (_.isEmpty(combinedPaymentMethods)) {
diff --git a/src/pages/settings/Payments/TransferBalancePage.js b/src/pages/settings/Payments/TransferBalancePage.js
new file mode 100644
index 000000000000..eb12627c2a3c
--- /dev/null
+++ b/src/pages/settings/Payments/TransferBalancePage.js
@@ -0,0 +1,27 @@
+import React from 'react';
+import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton';
+import ScreenWrapper from '../../../components/ScreenWrapper';
+import Navigation from '../../../libs/Navigation/Navigation';
+import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
+import KeyboardAvoidingView from '../../../components/KeyboardAvoidingView';
+
+const propTypes = {
+ ...withLocalizePropTypes,
+};
+
+const TransferBalancePage = props => (
+
+
+ Navigation.goBack()}
+ onCloseButtonPress={() => Navigation.dismissModal(true)}
+ />
+
+
+);
+
+TransferBalancePage.propTypes = propTypes;
+
+export default withLocalize(TransferBalancePage);