Skip to content

Commit 69b3295

Browse files
authored
feat(quote): make settlement time dynamic (#3834)
### 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.
1 parent 5ce0048 commit 69b3295

File tree

12 files changed

+397
-61
lines changed

12 files changed

+397
-61
lines changed

locales/base/translation.json

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1385,7 +1385,10 @@
13851385
"idRequired": "ID Required",
13861386
"numDays": "1-3 Days",
13871387
"oneHour": "Less Than 1 Hour",
1388-
"lessThan24Hours": "Less Than 24 Hours",
1388+
"xHours": "Less Than {{upperBound}} Hours",
1389+
"xToYHours": "{{lowerBound}}-{{upperBound}} Hours",
1390+
"xDays": "Less Than {{upperBound}} Days",
1391+
"xToYDays": "{{lowerBound}}-{{upperBound}} Days",
13891392
"bestRate": "Best Rate",
13901393
"header": "Select Payment Method",
13911394
"newLabel": "NEW",
@@ -1490,8 +1493,12 @@
14901493
"header": "Success",
14911494
"title": "Your funds are on their way!",
14921495
"description": "Your transaction request has been sent & your funds will arrive in {{duration}}.",
1493-
"description1to3Days": "Your transaction request has been sent & your funds will arrive in 1-3 days.",
1494-
"description24Hours": "Your transaction request has been sent & your funds will arrive within 24 hours.",
1496+
"descriptionWithin1Hour": "Your transaction request has been sent & your funds will arrive within 1 hour.",
1497+
"descriptionWithinXHours": "Your transaction request has been sent & your funds will arrive within {{upperBound}} hours.",
1498+
"descriptionInXtoYHours": "Your transaction request has been sent & your funds will arrive in {{lowerBound}}-{{upperBound}} hours.",
1499+
"descriptionWithinXDays": "Your transaction request has been sent & your funds will arrive within {{upperBound}} days.",
1500+
"descriptionXtoYDays": "Your transaction request has been sent & your funds will arrive in {{lowerBound}}-{{upperBound}} days.",
1501+
"baseDescription": "Your transaction request has been sent.",
14951502
"txDetails": "View on CeloExplorer",
14961503
"continue": "Continue"
14971504
},

src/fiatExchanges/PaymentMethodSection.test.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ describe('PaymentMethodSection', () => {
198198
const infoElement = queryByTestId('Bank/provider-0/info')
199199
expect(infoElement).toBeTruthy()
200200
expect(infoElement).toHaveTextContent(
201-
'selectProviderScreen.idRequired | selectProviderScreen.numDays'
201+
'selectProviderScreen.idRequired | selectProviderScreen.xToYDays, {"lowerBound":1,"upperBound":3}'
202202
)
203203
})
204204

@@ -217,7 +217,9 @@ describe('PaymentMethodSection', () => {
217217
)
218218
const infoElement = queryByTestId('Bank/provider-0/info')
219219
expect(infoElement).toBeTruthy()
220-
expect(infoElement).toHaveTextContent('selectProviderScreen.numDays')
220+
expect(infoElement).toHaveTextContent(
221+
'selectProviderScreen.xToYHours, {"lowerBound":1,"upperBound":2}'
222+
)
221223
expect(infoElement).not.toHaveTextContent('selectProviderScreen.idRequired')
222224
})
223225

@@ -237,7 +239,7 @@ describe('PaymentMethodSection', () => {
237239
CiCoCurrency.cUSD
238240
),
239241
'bank',
240-
'numDays',
242+
'xToYHours',
241243
],
242244
[
243245
PaymentMethod.FiatConnectMobileMoney as const,
@@ -248,7 +250,7 @@ describe('PaymentMethodSection', () => {
248250
CiCoCurrency.cUSD
249251
),
250252
'mobileMoney',
251-
'lessThan24Hours',
253+
'xHours',
252254
],
253255
])('shows appropriate title and settlement time for %s', (paymentMethod, quotes, title, info) => {
254256
props.normalizedQuotes = quotes

src/fiatExchanges/PaymentMethodSection.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ import Dialog from 'src/components/Dialog'
88
import Expandable from 'src/components/Expandable'
99
import TokenDisplay from 'src/components/TokenDisplay'
1010
import Touchable from 'src/components/Touchable'
11-
import { SettlementTime } from 'src/fiatExchanges/quotes/constants'
11+
import { SettlementEstimation, SettlementTime } from 'src/fiatExchanges/quotes/constants'
1212
import NormalizedQuote from 'src/fiatExchanges/quotes/NormalizedQuote'
13+
import { getSettlementTimeString } from 'src/fiatExchanges/quotes/utils'
1314
import { ProviderSelectionAnalyticsData } from 'src/fiatExchanges/types'
1415
import { CICOFlow, PaymentMethod } from 'src/fiatExchanges/utils'
1516
import InfoIcon from 'src/icons/InfoIcon'
@@ -21,8 +22,10 @@ import { CiCoCurrency } from 'src/utils/currencies'
2122

2223
const SETTLEMENT_TIME_STRINGS: Record<SettlementTime, string> = {
2324
[SettlementTime.LESS_THAN_ONE_HOUR]: 'selectProviderScreen.oneHour',
24-
[SettlementTime.LESS_THAN_24_HOURS]: 'selectProviderScreen.lessThan24Hours',
25-
[SettlementTime.ONE_TO_THREE_DAYS]: 'selectProviderScreen.numDays',
25+
[SettlementTime.LESS_THAN_X_HOURS]: 'selectProviderScreen.xHours',
26+
[SettlementTime.X_TO_Y_HOURS]: 'selectProviderScreen.xToYHours',
27+
[SettlementTime.LESS_THAN_X_DAYS]: 'selectProviderScreen.xDays',
28+
[SettlementTime.X_TO_Y_DAYS]: 'selectProviderScreen.xToYDays',
2629
}
2730

2831
export interface PaymentMethodSectionProps {
@@ -172,10 +175,18 @@ export function PaymentMethodSection({
172175
</>
173176
)
174177

178+
const getPaymentMethodSettlementTimeString = (settlementEstimation: SettlementEstimation) => {
179+
const { timeString, ...args } = getSettlementTimeString(
180+
settlementEstimation,
181+
SETTLEMENT_TIME_STRINGS
182+
)
183+
return timeString ? t(timeString, args) : t('selectProviderScreen.numDays')
184+
}
185+
175186
const renderInfoText = (quote: NormalizedQuote) => {
176187
const kycInfo = quote.getKycInfo()
177188
const kycString = kycInfo ? `${kycInfo} | ` : ''
178-
return `${kycString}${t(SETTLEMENT_TIME_STRINGS[quote.getTimeEstimation()])}`
189+
return `${kycString}${getPaymentMethodSettlementTimeString(quote.getTimeEstimation())}`
179190
}
180191

181192
const renderFeeAmount = (normalizedQuote: NormalizedQuote, postFix: string) => {

src/fiatExchanges/quotes/ExternalQuote.test.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,15 +160,21 @@ describe('ExternalQuote', () => {
160160
provider: mockProviders[1],
161161
flow: CICOFlow.CashIn,
162162
})
163-
expect(quote.getTimeEstimation()).toEqual(SettlementTime.ONE_TO_THREE_DAYS)
163+
expect(quote.getTimeEstimation()).toEqual({
164+
settlementTime: SettlementTime.X_TO_Y_DAYS,
165+
lowerBound: 1,
166+
upperBound: 3,
167+
})
164168
})
165169
it('returns oneHour for Card', () => {
166170
const quote = new ExternalQuote({
167171
quote: mockProviders[0].quote as SimplexQuote,
168172
provider: mockProviders[0],
169173
flow: CICOFlow.CashIn,
170174
})
171-
expect(quote.getTimeEstimation()).toEqual(SettlementTime.LESS_THAN_ONE_HOUR)
175+
expect(quote.getTimeEstimation()).toEqual({
176+
settlementTime: SettlementTime.LESS_THAN_ONE_HOUR,
177+
})
172178
})
173179
})
174180

src/fiatExchanges/quotes/ExternalQuote.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import BigNumber from 'bignumber.js'
2-
import { SettlementTime } from 'src/fiatExchanges/quotes/constants'
2+
import {
3+
DEFAULT_BANK_SETTLEMENT_ESTIMATION,
4+
DEFAULT_CARD_SETTLEMENT_ESTIMATION,
5+
SettlementEstimation,
6+
} from 'src/fiatExchanges/quotes/constants'
37
import NormalizedQuote from 'src/fiatExchanges/quotes/NormalizedQuote'
48
import {
59
CICOFlow,
@@ -96,11 +100,11 @@ export default class ExternalQuote extends NormalizedQuote {
96100
return strings.idRequired
97101
}
98102

99-
getTimeEstimation(): SettlementTime {
103+
getTimeEstimation(): SettlementEstimation {
100104
// payment method can only be bank or card
101105
return this.getPaymentMethod() === PaymentMethod.Bank
102-
? SettlementTime.ONE_TO_THREE_DAYS
103-
: SettlementTime.LESS_THAN_ONE_HOUR
106+
? DEFAULT_BANK_SETTLEMENT_ESTIMATION
107+
: DEFAULT_CARD_SETTLEMENT_ESTIMATION
104108
}
105109

106110
navigate(): void {

src/fiatExchanges/quotes/FiatConnectQuote.test.ts

Lines changed: 143 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ import { FiatExchangeEvents } from 'src/analytics/Events'
1010
import ValoraAnalytics from 'src/analytics/ValoraAnalytics'
1111
import { FiatConnectQuoteSuccess } from 'src/fiatconnect'
1212
import { selectFiatConnectQuote } from 'src/fiatconnect/slice'
13-
import { SettlementTime } from 'src/fiatExchanges/quotes/constants'
13+
import {
14+
DEFAULT_BANK_SETTLEMENT_ESTIMATION,
15+
DEFAULT_MOBILE_MONEY_SETTLEMENT_ESTIMATION,
16+
SettlementTime,
17+
} from 'src/fiatExchanges/quotes/constants'
1418
import FiatConnectQuote from 'src/fiatExchanges/quotes/FiatConnectQuote'
1519
import { CICOFlow, PaymentMethod } from 'src/fiatExchanges/utils'
1620
import { Currency } from 'src/utils/currencies'
@@ -229,22 +233,155 @@ describe('FiatConnectQuote', () => {
229233
})
230234

231235
describe('.getTimeEstimation', () => {
232-
it('returns 1-3 days for bank account', () => {
236+
it('returns default for bank account when no bounds are present', () => {
237+
const quoteData = _.cloneDeep(mockFiatConnectQuotes[1]) as FiatConnectQuoteSuccess
238+
quoteData.fiatAccount.BankAccount = {
239+
...quoteData.fiatAccount.BankAccount!,
240+
settlementTimeLowerBound: undefined,
241+
settlementTimeUpperBound: undefined,
242+
}
233243
const quote = new FiatConnectQuote({
234244
flow: CICOFlow.CashIn,
235-
quote: mockFiatConnectQuotes[1] as FiatConnectQuoteSuccess,
245+
quote: quoteData,
236246
fiatAccountType: FiatAccountType.BankAccount,
237247
})
238-
expect(quote.getTimeEstimation()).toEqual(SettlementTime.ONE_TO_THREE_DAYS)
248+
expect(quote.getTimeEstimation()).toEqual(DEFAULT_BANK_SETTLEMENT_ESTIMATION)
239249
})
240250

241-
it('returns 24 hours for mobile money', () => {
251+
it('returns default for mobile money when no bounds are present', () => {
242252
const quote = new FiatConnectQuote({
243253
flow: CICOFlow.CashIn,
244254
quote: mockFiatConnectQuotes[4] as FiatConnectQuoteSuccess,
245255
fiatAccountType: FiatAccountType.MobileMoney,
246256
})
247-
expect(quote.getTimeEstimation()).toEqual(SettlementTime.LESS_THAN_24_HOURS)
257+
expect(quote.getTimeEstimation()).toEqual(DEFAULT_MOBILE_MONEY_SETTLEMENT_ESTIMATION)
258+
})
259+
260+
it('when upper bound is less than one hour, "less than one hour" is shown', () => {
261+
const quoteData = _.cloneDeep(mockFiatConnectQuotes[1]) as FiatConnectQuoteSuccess
262+
quoteData.fiatAccount.BankAccount = {
263+
...quoteData.fiatAccount.BankAccount!,
264+
settlementTimeLowerBound: '300', // 5 minutes
265+
settlementTimeUpperBound: '600', // 10 minutes
266+
}
267+
const quote = new FiatConnectQuote({
268+
flow: CICOFlow.CashIn,
269+
quote: quoteData,
270+
fiatAccountType: FiatAccountType.BankAccount,
271+
})
272+
expect(quote.getTimeEstimation()).toEqual({
273+
settlementTime: SettlementTime.LESS_THAN_ONE_HOUR,
274+
})
275+
})
276+
277+
it('when lower bound is in minutes and upper bound is greater than one hour, "{lowerBound} to {upperBound} hours" is shown', () => {
278+
const quoteData = _.cloneDeep(mockFiatConnectQuotes[1]) as FiatConnectQuoteSuccess
279+
quoteData.fiatAccount.BankAccount = {
280+
...quoteData.fiatAccount.BankAccount!,
281+
settlementTimeLowerBound: '300', // 5 minutes
282+
settlementTimeUpperBound: '7200', // 2 hours
283+
}
284+
const quote = new FiatConnectQuote({
285+
flow: CICOFlow.CashIn,
286+
quote: quoteData,
287+
fiatAccountType: FiatAccountType.BankAccount,
288+
})
289+
expect(quote.getTimeEstimation()).toEqual({
290+
settlementTime: SettlementTime.X_TO_Y_HOURS,
291+
lowerBound: 1,
292+
upperBound: 2,
293+
})
294+
})
295+
296+
it('when lower bound is not present, "less than {upperBound}" is shown', () => {
297+
const quoteData = _.cloneDeep(mockFiatConnectQuotes[1]) as FiatConnectQuoteSuccess
298+
quoteData.fiatAccount.BankAccount = {
299+
...quoteData.fiatAccount.BankAccount!,
300+
settlementTimeLowerBound: undefined,
301+
settlementTimeUpperBound: '7200', // 2 hours
302+
}
303+
const quote = new FiatConnectQuote({
304+
flow: CICOFlow.CashIn,
305+
quote: quoteData,
306+
fiatAccountType: FiatAccountType.BankAccount,
307+
})
308+
expect(quote.getTimeEstimation()).toEqual({
309+
settlementTime: SettlementTime.LESS_THAN_X_HOURS,
310+
upperBound: 2,
311+
})
312+
})
313+
314+
it('when lower bound equals upper bound, "less than {upperBound}" is shown', () => {
315+
const quoteData = _.cloneDeep(mockFiatConnectQuotes[1]) as FiatConnectQuoteSuccess
316+
quoteData.fiatAccount.BankAccount = {
317+
...quoteData.fiatAccount.BankAccount!,
318+
settlementTimeLowerBound: '7200', // 2 hours
319+
settlementTimeUpperBound: '7200', // 2 hours
320+
}
321+
const quote = new FiatConnectQuote({
322+
flow: CICOFlow.CashIn,
323+
quote: quoteData,
324+
fiatAccountType: FiatAccountType.BankAccount,
325+
})
326+
expect(quote.getTimeEstimation()).toEqual({
327+
settlementTime: SettlementTime.LESS_THAN_X_HOURS,
328+
upperBound: 2,
329+
})
330+
})
331+
332+
it('when upper bound equals 24 hours, "less than 24 hours" is shown', () => {
333+
const quoteData = _.cloneDeep(mockFiatConnectQuotes[1]) as FiatConnectQuoteSuccess
334+
quoteData.fiatAccount.BankAccount = {
335+
...quoteData.fiatAccount.BankAccount!,
336+
settlementTimeLowerBound: undefined,
337+
settlementTimeUpperBound: '86400', // 1 day
338+
}
339+
const quote = new FiatConnectQuote({
340+
flow: CICOFlow.CashIn,
341+
quote: quoteData,
342+
fiatAccountType: FiatAccountType.BankAccount,
343+
})
344+
expect(quote.getTimeEstimation()).toEqual({
345+
settlementTime: SettlementTime.LESS_THAN_X_HOURS,
346+
upperBound: 24,
347+
})
348+
})
349+
350+
it('when upper bound is greater than 24 hours, but lower bound is less than day, "1 to {upperBound} days" shown', () => {
351+
const quoteData = _.cloneDeep(mockFiatConnectQuotes[1]) as FiatConnectQuoteSuccess
352+
quoteData.fiatAccount.BankAccount = {
353+
...quoteData.fiatAccount.BankAccount!,
354+
settlementTimeLowerBound: '300', // 5 minutes
355+
settlementTimeUpperBound: '86401', // over 1 day (rounds up to two days)
356+
}
357+
const quote = new FiatConnectQuote({
358+
flow: CICOFlow.CashIn,
359+
quote: quoteData,
360+
fiatAccountType: FiatAccountType.BankAccount,
361+
})
362+
expect(quote.getTimeEstimation()).toEqual({
363+
settlementTime: SettlementTime.X_TO_Y_DAYS,
364+
lowerBound: 1,
365+
upperBound: 2,
366+
})
367+
})
368+
369+
it('when upper bound is greater than 24 hours and lower bound equals upper, "less than {upperBound} days" shown', () => {
370+
const quoteData = _.cloneDeep(mockFiatConnectQuotes[1]) as FiatConnectQuoteSuccess
371+
quoteData.fiatAccount.BankAccount = {
372+
...quoteData.fiatAccount.BankAccount!,
373+
settlementTimeLowerBound: '86401', // over 1 day (rounds up to two days)
374+
settlementTimeUpperBound: '86401', // over 1 day (rounds up to two days)
375+
}
376+
const quote = new FiatConnectQuote({
377+
flow: CICOFlow.CashIn,
378+
quote: quoteData,
379+
fiatAccountType: FiatAccountType.BankAccount,
380+
})
381+
expect(quote.getTimeEstimation()).toEqual({
382+
settlementTime: SettlementTime.LESS_THAN_X_DAYS,
383+
upperBound: 2,
384+
})
248385
})
249386
})
250387

0 commit comments

Comments
 (0)