Skip to content

Commit

Permalink
feat(quote): make settlement time dynamic (#3834)
Browse files Browse the repository at this point in the history
### Description

Currently, the time estimation for a FiatConnect quote is set to fixed
values. This PR makes it so that the time estimation is dynamic and
based on data sent from the quote provider.


![SelectPaymentDynamicTime](https://github.com/valora-inc/wallet/assets/20508929/6e7a3fac-dd1f-4928-b572-3078c29fcf86)


### Test plan

Added unit tests and modified existing unit test

### Related issues

- Fixes ACT-366

### Backwards compatibility

Yes.
  • Loading branch information
anthonydaoud authored Jun 6, 2023
1 parent 5ce0048 commit 69b3295
Show file tree
Hide file tree
Showing 12 changed files with 397 additions and 61 deletions.
13 changes: 10 additions & 3 deletions locales/base/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -1385,7 +1385,10 @@
"idRequired": "ID Required",
"numDays": "1-3 Days",
"oneHour": "Less Than 1 Hour",
"lessThan24Hours": "Less Than 24 Hours",
"xHours": "Less Than {{upperBound}} Hours",
"xToYHours": "{{lowerBound}}-{{upperBound}} Hours",
"xDays": "Less Than {{upperBound}} Days",
"xToYDays": "{{lowerBound}}-{{upperBound}} Days",
"bestRate": "Best Rate",
"header": "Select Payment Method",
"newLabel": "NEW",
Expand Down Expand Up @@ -1490,8 +1493,12 @@
"header": "Success",
"title": "Your funds are on their way!",
"description": "Your transaction request has been sent & your funds will arrive in {{duration}}.",
"description1to3Days": "Your transaction request has been sent & your funds will arrive in 1-3 days.",
"description24Hours": "Your transaction request has been sent & your funds will arrive within 24 hours.",
"descriptionWithin1Hour": "Your transaction request has been sent & your funds will arrive within 1 hour.",
"descriptionWithinXHours": "Your transaction request has been sent & your funds will arrive within {{upperBound}} hours.",
"descriptionInXtoYHours": "Your transaction request has been sent & your funds will arrive in {{lowerBound}}-{{upperBound}} hours.",
"descriptionWithinXDays": "Your transaction request has been sent & your funds will arrive within {{upperBound}} days.",
"descriptionXtoYDays": "Your transaction request has been sent & your funds will arrive in {{lowerBound}}-{{upperBound}} days.",
"baseDescription": "Your transaction request has been sent.",
"txDetails": "View on CeloExplorer",
"continue": "Continue"
},
Expand Down
10 changes: 6 additions & 4 deletions src/fiatExchanges/PaymentMethodSection.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ describe('PaymentMethodSection', () => {
const infoElement = queryByTestId('Bank/provider-0/info')
expect(infoElement).toBeTruthy()
expect(infoElement).toHaveTextContent(
'selectProviderScreen.idRequired | selectProviderScreen.numDays'
'selectProviderScreen.idRequired | selectProviderScreen.xToYDays, {"lowerBound":1,"upperBound":3}'
)
})

Expand All @@ -217,7 +217,9 @@ describe('PaymentMethodSection', () => {
)
const infoElement = queryByTestId('Bank/provider-0/info')
expect(infoElement).toBeTruthy()
expect(infoElement).toHaveTextContent('selectProviderScreen.numDays')
expect(infoElement).toHaveTextContent(
'selectProviderScreen.xToYHours, {"lowerBound":1,"upperBound":2}'
)
expect(infoElement).not.toHaveTextContent('selectProviderScreen.idRequired')
})

Expand All @@ -237,7 +239,7 @@ describe('PaymentMethodSection', () => {
CiCoCurrency.cUSD
),
'bank',
'numDays',
'xToYHours',
],
[
PaymentMethod.FiatConnectMobileMoney as const,
Expand All @@ -248,7 +250,7 @@ describe('PaymentMethodSection', () => {
CiCoCurrency.cUSD
),
'mobileMoney',
'lessThan24Hours',
'xHours',
],
])('shows appropriate title and settlement time for %s', (paymentMethod, quotes, title, info) => {
props.normalizedQuotes = quotes
Expand Down
19 changes: 15 additions & 4 deletions src/fiatExchanges/PaymentMethodSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import Dialog from 'src/components/Dialog'
import Expandable from 'src/components/Expandable'
import TokenDisplay from 'src/components/TokenDisplay'
import Touchable from 'src/components/Touchable'
import { SettlementTime } from 'src/fiatExchanges/quotes/constants'
import { SettlementEstimation, SettlementTime } from 'src/fiatExchanges/quotes/constants'
import NormalizedQuote from 'src/fiatExchanges/quotes/NormalizedQuote'
import { getSettlementTimeString } from 'src/fiatExchanges/quotes/utils'
import { ProviderSelectionAnalyticsData } from 'src/fiatExchanges/types'
import { CICOFlow, PaymentMethod } from 'src/fiatExchanges/utils'
import InfoIcon from 'src/icons/InfoIcon'
Expand All @@ -21,8 +22,10 @@ import { CiCoCurrency } from 'src/utils/currencies'

const SETTLEMENT_TIME_STRINGS: Record<SettlementTime, string> = {
[SettlementTime.LESS_THAN_ONE_HOUR]: 'selectProviderScreen.oneHour',
[SettlementTime.LESS_THAN_24_HOURS]: 'selectProviderScreen.lessThan24Hours',
[SettlementTime.ONE_TO_THREE_DAYS]: 'selectProviderScreen.numDays',
[SettlementTime.LESS_THAN_X_HOURS]: 'selectProviderScreen.xHours',
[SettlementTime.X_TO_Y_HOURS]: 'selectProviderScreen.xToYHours',
[SettlementTime.LESS_THAN_X_DAYS]: 'selectProviderScreen.xDays',
[SettlementTime.X_TO_Y_DAYS]: 'selectProviderScreen.xToYDays',
}

export interface PaymentMethodSectionProps {
Expand Down Expand Up @@ -172,10 +175,18 @@ export function PaymentMethodSection({
</>
)

const getPaymentMethodSettlementTimeString = (settlementEstimation: SettlementEstimation) => {
const { timeString, ...args } = getSettlementTimeString(
settlementEstimation,
SETTLEMENT_TIME_STRINGS
)
return timeString ? t(timeString, args) : t('selectProviderScreen.numDays')
}

const renderInfoText = (quote: NormalizedQuote) => {
const kycInfo = quote.getKycInfo()
const kycString = kycInfo ? `${kycInfo} | ` : ''
return `${kycString}${t(SETTLEMENT_TIME_STRINGS[quote.getTimeEstimation()])}`
return `${kycString}${getPaymentMethodSettlementTimeString(quote.getTimeEstimation())}`
}

const renderFeeAmount = (normalizedQuote: NormalizedQuote, postFix: string) => {
Expand Down
10 changes: 8 additions & 2 deletions src/fiatExchanges/quotes/ExternalQuote.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,15 +160,21 @@ describe('ExternalQuote', () => {
provider: mockProviders[1],
flow: CICOFlow.CashIn,
})
expect(quote.getTimeEstimation()).toEqual(SettlementTime.ONE_TO_THREE_DAYS)
expect(quote.getTimeEstimation()).toEqual({
settlementTime: SettlementTime.X_TO_Y_DAYS,
lowerBound: 1,
upperBound: 3,
})
})
it('returns oneHour for Card', () => {
const quote = new ExternalQuote({
quote: mockProviders[0].quote as SimplexQuote,
provider: mockProviders[0],
flow: CICOFlow.CashIn,
})
expect(quote.getTimeEstimation()).toEqual(SettlementTime.LESS_THAN_ONE_HOUR)
expect(quote.getTimeEstimation()).toEqual({
settlementTime: SettlementTime.LESS_THAN_ONE_HOUR,
})
})
})

Expand Down
12 changes: 8 additions & 4 deletions src/fiatExchanges/quotes/ExternalQuote.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import BigNumber from 'bignumber.js'
import { SettlementTime } from 'src/fiatExchanges/quotes/constants'
import {
DEFAULT_BANK_SETTLEMENT_ESTIMATION,
DEFAULT_CARD_SETTLEMENT_ESTIMATION,
SettlementEstimation,
} from 'src/fiatExchanges/quotes/constants'
import NormalizedQuote from 'src/fiatExchanges/quotes/NormalizedQuote'
import {
CICOFlow,
Expand Down Expand Up @@ -96,11 +100,11 @@ export default class ExternalQuote extends NormalizedQuote {
return strings.idRequired
}

getTimeEstimation(): SettlementTime {
getTimeEstimation(): SettlementEstimation {
// payment method can only be bank or card
return this.getPaymentMethod() === PaymentMethod.Bank
? SettlementTime.ONE_TO_THREE_DAYS
: SettlementTime.LESS_THAN_ONE_HOUR
? DEFAULT_BANK_SETTLEMENT_ESTIMATION
: DEFAULT_CARD_SETTLEMENT_ESTIMATION
}

navigate(): void {
Expand Down
149 changes: 143 additions & 6 deletions src/fiatExchanges/quotes/FiatConnectQuote.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import { FiatExchangeEvents } from 'src/analytics/Events'
import ValoraAnalytics from 'src/analytics/ValoraAnalytics'
import { FiatConnectQuoteSuccess } from 'src/fiatconnect'
import { selectFiatConnectQuote } from 'src/fiatconnect/slice'
import { SettlementTime } from 'src/fiatExchanges/quotes/constants'
import {
DEFAULT_BANK_SETTLEMENT_ESTIMATION,
DEFAULT_MOBILE_MONEY_SETTLEMENT_ESTIMATION,
SettlementTime,
} from 'src/fiatExchanges/quotes/constants'
import FiatConnectQuote from 'src/fiatExchanges/quotes/FiatConnectQuote'
import { CICOFlow, PaymentMethod } from 'src/fiatExchanges/utils'
import { Currency } from 'src/utils/currencies'
Expand Down Expand Up @@ -229,22 +233,155 @@ describe('FiatConnectQuote', () => {
})

describe('.getTimeEstimation', () => {
it('returns 1-3 days for bank account', () => {
it('returns default for bank account when no bounds are present', () => {
const quoteData = _.cloneDeep(mockFiatConnectQuotes[1]) as FiatConnectQuoteSuccess
quoteData.fiatAccount.BankAccount = {
...quoteData.fiatAccount.BankAccount!,
settlementTimeLowerBound: undefined,
settlementTimeUpperBound: undefined,
}
const quote = new FiatConnectQuote({
flow: CICOFlow.CashIn,
quote: mockFiatConnectQuotes[1] as FiatConnectQuoteSuccess,
quote: quoteData,
fiatAccountType: FiatAccountType.BankAccount,
})
expect(quote.getTimeEstimation()).toEqual(SettlementTime.ONE_TO_THREE_DAYS)
expect(quote.getTimeEstimation()).toEqual(DEFAULT_BANK_SETTLEMENT_ESTIMATION)
})

it('returns 24 hours for mobile money', () => {
it('returns default for mobile money when no bounds are present', () => {
const quote = new FiatConnectQuote({
flow: CICOFlow.CashIn,
quote: mockFiatConnectQuotes[4] as FiatConnectQuoteSuccess,
fiatAccountType: FiatAccountType.MobileMoney,
})
expect(quote.getTimeEstimation()).toEqual(SettlementTime.LESS_THAN_24_HOURS)
expect(quote.getTimeEstimation()).toEqual(DEFAULT_MOBILE_MONEY_SETTLEMENT_ESTIMATION)
})

it('when upper bound is less than one hour, "less than one hour" is shown', () => {
const quoteData = _.cloneDeep(mockFiatConnectQuotes[1]) as FiatConnectQuoteSuccess
quoteData.fiatAccount.BankAccount = {
...quoteData.fiatAccount.BankAccount!,
settlementTimeLowerBound: '300', // 5 minutes
settlementTimeUpperBound: '600', // 10 minutes
}
const quote = new FiatConnectQuote({
flow: CICOFlow.CashIn,
quote: quoteData,
fiatAccountType: FiatAccountType.BankAccount,
})
expect(quote.getTimeEstimation()).toEqual({
settlementTime: SettlementTime.LESS_THAN_ONE_HOUR,
})
})

it('when lower bound is in minutes and upper bound is greater than one hour, "{lowerBound} to {upperBound} hours" is shown', () => {
const quoteData = _.cloneDeep(mockFiatConnectQuotes[1]) as FiatConnectQuoteSuccess
quoteData.fiatAccount.BankAccount = {
...quoteData.fiatAccount.BankAccount!,
settlementTimeLowerBound: '300', // 5 minutes
settlementTimeUpperBound: '7200', // 2 hours
}
const quote = new FiatConnectQuote({
flow: CICOFlow.CashIn,
quote: quoteData,
fiatAccountType: FiatAccountType.BankAccount,
})
expect(quote.getTimeEstimation()).toEqual({
settlementTime: SettlementTime.X_TO_Y_HOURS,
lowerBound: 1,
upperBound: 2,
})
})

it('when lower bound is not present, "less than {upperBound}" is shown', () => {
const quoteData = _.cloneDeep(mockFiatConnectQuotes[1]) as FiatConnectQuoteSuccess
quoteData.fiatAccount.BankAccount = {
...quoteData.fiatAccount.BankAccount!,
settlementTimeLowerBound: undefined,
settlementTimeUpperBound: '7200', // 2 hours
}
const quote = new FiatConnectQuote({
flow: CICOFlow.CashIn,
quote: quoteData,
fiatAccountType: FiatAccountType.BankAccount,
})
expect(quote.getTimeEstimation()).toEqual({
settlementTime: SettlementTime.LESS_THAN_X_HOURS,
upperBound: 2,
})
})

it('when lower bound equals upper bound, "less than {upperBound}" is shown', () => {
const quoteData = _.cloneDeep(mockFiatConnectQuotes[1]) as FiatConnectQuoteSuccess
quoteData.fiatAccount.BankAccount = {
...quoteData.fiatAccount.BankAccount!,
settlementTimeLowerBound: '7200', // 2 hours
settlementTimeUpperBound: '7200', // 2 hours
}
const quote = new FiatConnectQuote({
flow: CICOFlow.CashIn,
quote: quoteData,
fiatAccountType: FiatAccountType.BankAccount,
})
expect(quote.getTimeEstimation()).toEqual({
settlementTime: SettlementTime.LESS_THAN_X_HOURS,
upperBound: 2,
})
})

it('when upper bound equals 24 hours, "less than 24 hours" is shown', () => {
const quoteData = _.cloneDeep(mockFiatConnectQuotes[1]) as FiatConnectQuoteSuccess
quoteData.fiatAccount.BankAccount = {
...quoteData.fiatAccount.BankAccount!,
settlementTimeLowerBound: undefined,
settlementTimeUpperBound: '86400', // 1 day
}
const quote = new FiatConnectQuote({
flow: CICOFlow.CashIn,
quote: quoteData,
fiatAccountType: FiatAccountType.BankAccount,
})
expect(quote.getTimeEstimation()).toEqual({
settlementTime: SettlementTime.LESS_THAN_X_HOURS,
upperBound: 24,
})
})

it('when upper bound is greater than 24 hours, but lower bound is less than day, "1 to {upperBound} days" shown', () => {
const quoteData = _.cloneDeep(mockFiatConnectQuotes[1]) as FiatConnectQuoteSuccess
quoteData.fiatAccount.BankAccount = {
...quoteData.fiatAccount.BankAccount!,
settlementTimeLowerBound: '300', // 5 minutes
settlementTimeUpperBound: '86401', // over 1 day (rounds up to two days)
}
const quote = new FiatConnectQuote({
flow: CICOFlow.CashIn,
quote: quoteData,
fiatAccountType: FiatAccountType.BankAccount,
})
expect(quote.getTimeEstimation()).toEqual({
settlementTime: SettlementTime.X_TO_Y_DAYS,
lowerBound: 1,
upperBound: 2,
})
})

it('when upper bound is greater than 24 hours and lower bound equals upper, "less than {upperBound} days" shown', () => {
const quoteData = _.cloneDeep(mockFiatConnectQuotes[1]) as FiatConnectQuoteSuccess
quoteData.fiatAccount.BankAccount = {
...quoteData.fiatAccount.BankAccount!,
settlementTimeLowerBound: '86401', // over 1 day (rounds up to two days)
settlementTimeUpperBound: '86401', // over 1 day (rounds up to two days)
}
const quote = new FiatConnectQuote({
flow: CICOFlow.CashIn,
quote: quoteData,
fiatAccountType: FiatAccountType.BankAccount,
})
expect(quote.getTimeEstimation()).toEqual({
settlementTime: SettlementTime.LESS_THAN_X_DAYS,
upperBound: 2,
})
})
})

Expand Down
Loading

0 comments on commit 69b3295

Please sign in to comment.