Overview
Payment methods in Expensify include bank accounts and debit cards. Users can manage multiple payment methods and set defaults for different contexts.Getting Payment Methods
Retrieve all payment methods for the current user.function getPaymentMethods(includePartiallySetupBankAccounts?: boolean) {
const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.IS_LOADING_PAYMENT_METHODS>> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.IS_LOADING_PAYMENT_METHODS,
value: true,
},
];
const successData: Array<OnyxUpdate<typeof ONYXKEYS.IS_LOADING_PAYMENT_METHODS>> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.IS_LOADING_PAYMENT_METHODS,
value: false,
},
];
const failureData: Array<OnyxUpdate<typeof ONYXKEYS.IS_LOADING_PAYMENT_METHODS>> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.IS_LOADING_PAYMENT_METHODS,
value: false,
},
];
return API.read(
READ_COMMANDS.OPEN_PAYMENTS_PAGE,
{includePartiallySetupBankAccounts},
{
optimisticData,
successData,
failureData,
},
);
}
Setting Default Payment Method
Making a Payment Method Default
function makeDefaultPaymentMethod(
bankAccountID: number,
fundID: number,
previousPaymentMethod?: PaymentMethod,
currentPaymentMethod?: PaymentMethod
) {
const parameters: MakeDefaultPaymentMethodParams = {
bankAccountID,
fundID,
};
API.write(WRITE_COMMANDS.MAKE_DEFAULT_PAYMENT_METHOD, parameters, {
optimisticData: getMakeDefaultPaymentOnyxData(bankAccountID, fundID, previousPaymentMethod, currentPaymentMethod, true),
failureData: getMakeDefaultPaymentOnyxData(bankAccountID, fundID, previousPaymentMethod, currentPaymentMethod, false),
});
}
Generating Onyx Data for Default Payment Method
This helper function generates the appropriate Onyx updates when changing default payment methods.function getMakeDefaultPaymentOnyxData(
bankAccountID: number,
fundID?: number,
previousPaymentMethod?: PaymentMethod,
currentPaymentMethod?: PaymentMethod,
isOptimisticData = true,
): Array<OnyxUpdate<typeof ONYXKEYS.USER_WALLET | typeof ONYXKEYS.BANK_ACCOUNT_LIST | typeof ONYXKEYS.FUND_LIST>> {
const onyxData: Array<OnyxUpdate<typeof ONYXKEYS.USER_WALLET | typeof ONYXKEYS.BANK_ACCOUNT_LIST | typeof ONYXKEYS.FUND_LIST>> = [
isOptimisticData
? {
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.USER_WALLET,
value: {
walletLinkedAccountID: bankAccountID || fundID,
walletLinkedAccountType: bankAccountID
? CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT
: CONST.PAYMENT_METHODS.DEBIT_CARD,
errors: null,
},
}
: {
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.USER_WALLET,
value: {
walletLinkedAccountID: bankAccountID || fundID,
walletLinkedAccountType: bankAccountID
? CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT
: CONST.PAYMENT_METHODS.DEBIT_CARD,
},
},
];
if (previousPaymentMethod?.methodID) {
onyxData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: previousPaymentMethod.accountType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT
? ONYXKEYS.BANK_ACCOUNT_LIST
: ONYXKEYS.FUND_LIST,
value: {
[previousPaymentMethod.methodID]: {
isDefault: !isOptimisticData,
},
},
});
}
if (currentPaymentMethod?.methodID) {
onyxData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: currentPaymentMethod.accountType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT
? ONYXKEYS.BANK_ACCOUNT_LIST
: ONYXKEYS.FUND_LIST,
value: {
[currentPaymentMethod.methodID]: {
isDefault: isOptimisticData,
},
},
});
}
return onyxData;
}
Payment Cards
Adding a Payment Card
Add a new debit card for payments.function addPaymentCard(accountID: number, params: PaymentCardParams) {
const cardMonth = CardUtils.getMonthFromExpirationDateString(params.expirationDate);
const cardYear = CardUtils.getYearFromExpirationDateString(params.expirationDate);
const parameters: AddPaymentCardParams = {
cardNumber: CardUtils.getMCardNumberString(params.cardNumber),
cardYear,
cardMonth,
cardCVV: params.securityCode,
addressName: params.nameOnCard,
addressZip: params.addressZipCode,
currency: CONST.PAYMENT_CARD_CURRENCY.USD,
isP2PDebitCard: true,
};
const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.FORMS.ADD_PAYMENT_CARD_FORM>> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.FORMS.ADD_PAYMENT_CARD_FORM,
value: {isLoading: true},
},
];
API.write(WRITE_COMMANDS.ADD_PAYMENT_CARD, parameters, {
optimisticData,
successData,
failureData,
});
GoogleTagManager.publishEvent(CONST.ANALYTICS.EVENT.PAID_ADOPTION, accountID);
}
Adding Subscription Payment Card
Add a card specifically for subscription billing, with SCA support.function addSubscriptionPaymentCard(
accountID: number,
cardData: {
cardNumber: string;
cardYear: string;
cardMonth: string;
cardCVV: string;
addressName: string;
addressZip: string;
currency: ValueOf<typeof CONST.PAYMENT_CARD_CURRENCY>;
},
fundList: OnyxEntry<FundList>,
) {
const {cardNumber, cardYear, cardMonth, cardCVV, addressName, addressZip, currency} = cardData;
const parameters: AddPaymentCardParams = {
cardNumber,
cardYear,
cardMonth,
cardCVV,
addressName,
addressZip,
currency,
isP2PDebitCard: false,
shouldClaimEarlyDiscountOffer: true,
};
const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.FORMS.ADD_PAYMENT_CARD_FORM>> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.FORMS.ADD_PAYMENT_CARD_FORM,
value: {isLoading: true},
},
];
if (CONST.SCA_CURRENCIES.has(currency)) {
addPaymentCardSCA(parameters, {optimisticData, successData, failureData});
} else {
API.write(WRITE_COMMANDS.ADD_PAYMENT_CARD, parameters, {
optimisticData,
successData,
failureData,
});
}
if (getCardForSubscriptionBilling(fundList)) {
Log.info(`[GTM] Not logging ${CONST.ANALYTICS.EVENT.PAID_ADOPTION} because a card was already added`);
} else {
GoogleTagManager.publishEvent(CONST.ANALYTICS.EVENT.PAID_ADOPTION, accountID);
}
}
SCA (Strong Customer Authentication) Cards
For European cards requiring 3DS authentication.function addPaymentCardSCA(
params: AddPaymentCardParams,
onyxData: OnyxData<typeof ONYXKEYS.FORMS.ADD_PAYMENT_CARD_FORM> = {}
) {
API.write(WRITE_COMMANDS.ADD_PAYMENT_CARD_SCA, params, onyxData);
}
function verifySetupIntent(accountID: number, isVerifying = true) {
API.write(WRITE_COMMANDS.VERIFY_SETUP_INTENT, {accountID, isVerifying});
}
Deleting Payment Cards
function deletePaymentCard(fundID: number) {
const parameters: DeletePaymentCardParams = {
fundID,
};
const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.FUND_LIST>> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.FUND_LIST}`,
value: {[fundID]: {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}},
},
];
API.write(WRITE_COMMANDS.DELETE_PAYMENT_CARD, parameters, {
optimisticData,
});
}
Wallet Transfer
Transferring Wallet Balance
Transfer funds from the Expensify Wallet to a connected bank account or debit card.function transferWalletBalance(paymentMethod: PaymentMethod) {
const paymentMethodIDKey =
paymentMethod.accountType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT
? CONST.PAYMENT_METHOD_ID_KEYS.BANK_ACCOUNT
: CONST.PAYMENT_METHOD_ID_KEYS.DEBIT_CARD;
const parameters: TransferWalletBalanceParams = {
[paymentMethodIDKey]: paymentMethod.methodID,
};
const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.WALLET_TRANSFER>> = [
{
onyxMethod: 'merge',
key: ONYXKEYS.WALLET_TRANSFER,
value: {
loading: true,
errors: null,
},
},
];
const successData: Array<OnyxUpdate<typeof ONYXKEYS.WALLET_TRANSFER>> = [
{
onyxMethod: 'merge',
key: ONYXKEYS.WALLET_TRANSFER,
value: {
loading: false,
shouldShowSuccess: true,
paymentMethodType: paymentMethod.accountType,
},
},
];
const failureData: Array<OnyxUpdate<typeof ONYXKEYS.WALLET_TRANSFER>> = [
{
onyxMethod: 'merge',
key: ONYXKEYS.WALLET_TRANSFER,
value: {
loading: false,
shouldShowSuccess: false,
},
},
];
API.write(WRITE_COMMANDS.TRANSFER_WALLET_BALANCE, parameters, {
optimisticData,
successData,
failureData,
});
}
Managing Transfer State
function resetWalletTransferData() {
Onyx.merge(ONYXKEYS.WALLET_TRANSFER, {
selectedAccountType: '',
selectedAccountID: null,
filterPaymentMethodType: null,
loading: false,
shouldShowSuccess: false,
});
}
function saveWalletTransferAccountTypeAndID(
selectedAccountType: string | undefined,
selectedAccountID: string | undefined
) {
Onyx.merge(ONYXKEYS.WALLET_TRANSFER, {selectedAccountType, selectedAccountID});
}
function saveWalletTransferMethodType(filterPaymentMethodType?: FilterMethodPaymentType) {
Onyx.merge(ONYXKEYS.WALLET_TRANSFER, {filterPaymentMethodType});
}
Billing Currency
Update the currency used for subscription billing.function updateBillingCurrency(
currency: ValueOf<typeof CONST.PAYMENT_CARD_CURRENCY>,
cardCVV: string
) {
const parameters: UpdateBillingCurrencyParams = {
cardCVV,
currency,
};
const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.FORMS.CHANGE_BILLING_CURRENCY_FORM>> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.FORMS.CHANGE_BILLING_CURRENCY_FORM,
value: {
isLoading: true,
errors: null,
},
},
];
API.write(WRITE_COMMANDS.UPDATE_BILLING_CARD_CURRENCY, parameters, {
optimisticData,
successData,
failureData,
});
}
Invoicing Transfer Account
Set the default bank account for receiving invoice payments.function setInvoicingTransferBankAccount(
bankAccountID: number,
policyID: string,
previousBankAccountID: number
) {
const parameters: SetInvoicingTransferBankAccountParams = {
bankAccountID,
policyID,
};
const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.POLICY>> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
invoice: {
bankAccount: {
transferBankAccountID: bankAccountID,
},
},
},
},
];
const failureData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.POLICY>> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
invoice: {
bankAccount: {
transferBankAccountID: previousBankAccountID,
},
},
},
},
];
API.write(WRITE_COMMANDS.SET_INVOICING_TRANSFER_BANK_ACCOUNT, parameters, {
optimisticData,
failureData,
});
}
Error Handling
Checking for Payment Method Errors
function hasPaymentMethodError(
bankList: OnyxEntry<BankAccountList>,
fundList: OnyxEntry<FundList>,
cardList: OnyxEntry<CardList>
): boolean {
const combinedPaymentMethods = {...bankList, ...fundList, ...cardList};
return Object.values(combinedPaymentMethods).some((item) =>
Object.keys(item.errors ?? {}).length
);
}
Clearing Errors
function clearDeletePaymentMethodError(paymentListKey: PaymentListKey, paymentMethodID: number) {
Onyx.merge(paymentListKey, {
[paymentMethodID]: {
pendingAction: null,
errors: null,
},
});
}
function clearAddPaymentMethodError(paymentListKey: PaymentListKey, paymentMethodID: number) {
Onyx.merge(paymentListKey, {
[paymentMethodID]: null,
});
}
function clearWalletError() {
Onyx.merge(ONYXKEYS.USER_WALLET, {errors: null});
}
Related Resources
Connecting Accounts
Learn about connecting bank accounts
Wallet
Managing your Expensify Wallet
