-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
Require validateCode when requesting replacement cards #51147
Conversation
@jjcoffee this needs an internal review due to some backend changes that aren't live and we can't make them live until this PR is deployed. |
@thienlnam Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
Yup I found it weird too. I haven't touched this part in my changes. |
🧪🧪 Use the links below to test this adhoc build on Android, iOS, Desktop, and Web. Happy testing! 🧪🧪
|
Fixed. This was using Screen.Recording.2024-10-29.at.21.49.36.mov |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code largely looks good, just a comment about the side effect pattern. One more thing is will this work even without the auth changes?
requestValidateCodeAction(); | ||
}; | ||
|
||
const handleValidateCodeEntered = (validateCode: string) => { | ||
CardActions.requestReplacementExpensifyCard(physicalCard.cardID, reason?.key as ReplacementReason, validateCode).then((newCardID) => { | ||
if (!newCardID) { | ||
return; | ||
} | ||
setIsValidateCodeActionModalVisible(false); | ||
Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(newCardID)); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't look like we did this navigation before, is there a reason we are adding it now?
src/libs/actions/Card.ts
Outdated
// eslint-disable-next-line rulesdir/no-api-side-effects-method | ||
API.makeRequestWithSideEffects(SIDE_EFFECT_REQUEST_COMMANDS.REQUEST_REPLACEMENT_EXPENSIFY_CARD, parameters, {optimisticData, successData, failureData}) | ||
.then((response): string | undefined => { | ||
if (response?.jsonCode !== CONST.JSON_CODE.SUCCESS) { | ||
return; | ||
} | ||
resolve((response as ExpensifyCardID).newCardID.toString()); | ||
}) | ||
.catch(() => reject()); | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Returning promises from action methods is a discouraged pattern - it looks like we can do this without the side effects.
We just want the new cardID here, so we could probably just have it update an onyx key and only re-direct if we have that key present and are done loading. Alternatively, it seems like we didn't have a redirect before so maybe we don't need to have it still
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@thienlnam without the side effects we used to redirect to the old card page and we display a "$0" limit which looks like a bug to the user
Screen_Recording_2024-10-23_at_22.04.03-2024-10-30.17_36_55.334.mov
We also use side effects for reveal virtual card details here
Line 197 in 084b104
function revealVirtualCardDetails(cardID: number, validateCode: string): Promise<ExpensifyCardDetails> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use side effects there because we can't store those details in onyx since they're sensitive - but for this which is just a cardID is fine to store in onyx
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm let me see how I can make this work without side effects and also prevent the bug above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 lmk if you need some ideas - usually the way we get around this is by adding a loading indicator on a loading key on the onyx key for the page
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@thienlnam IMO I think we can keep the side effect call here, you can see in the comment that our use case here is allowed, we are both calling Marqeta and redirecting based on the response
Also given that we display a spinner and make the user wait, I don't see how is using the optimistic pattern is helpful?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup something along those lines would work - you'd need to have the API command send an onyx update for the newCardID if you wanted to redirect to the new card.
Though thinking about this more though, if you only added the redirect so that the card doesn't show with a $0 limit we could also just not do the redirect because then it forces this flow to happen online.
In App, in the optimistic data you could set a new key called isReplacing or something on the cardID and so when you open that cardID you would see some note that it's been replaced and then when the API completes it would remove the card and have the new card in the list. That's probably easier and then allows the user to take other actions offline with the command queued - what do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hear you. However I think that this stuff is out of scope, so it's best if I kept this PR focused on validateCode using the same offline pattern we have now, which means not fixing the $0 bug, I think we can open an issue to fix separately. Sounds good?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that works for me - the main concern for me here is to remove the side effect so if we can do that here and have an external contributor handle the UI update seperately
Good shout Danny, I agree seeing the error below the footer button felt weird - glad we have the option to easily add it above! |
Updated @thienlnam |
Looks like there are some lint failures |
Ah, I need to remove usage of withOnyx. Will do tomorrow. Also, apologies for late update, I was OOO sick. |
Fixed @thienlnam |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, thanks!
Reviewer Checklist
Screenshots/VideosAndroid: NativeAndroid: mWeb ChromeiOS: NativeiOS: mWeb SafariMacOS: Chrome / SafariMacOS: Desktop |
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
🚀 Deployed to staging by https://github.com/thienlnam in version: 9.0.60-0 🚀
|
@youssef-lr @francoisl we don't have physical card and we can verify with virtual card only. |
@youssef-lr is there a way to test with physical cards without actually requesting a replacement on each platform? |
I'm not sure there is, cc @NikkiWines. I think we can check this off for now, nothing has changed once a validateCode is valid, things should work the same as before |
🤔 I don't think we have a flow for that. Except maybe you could request a replacement then cancel the request internally which might be tricky timing wise and I'm not to sure exactly how you'd do that. It's a little dicey to not test the success flow at all on staging or prod, but I'm not sure how to bypass that here |
Maybe we could follow the same / similar steps from when this command was initially added here? |
🚀 Deployed to production by https://github.com/francoisl in version: 9.0.60-3 🚀
|
Details
Fixed Issues
$ https://github.com/Expensify/Expensify/issues/434460
$ https://github.com/Expensify/Expensify/issues/434468
Tests
First checkout this Auth PR, and then hardcode these lines:
https://github.com/Expensify/Web-Expensify/blob/4fccc5d6258fc9d7879d5cea6c0ced5663cd3334/lib/CardAPI.php#L652 =>
$response = Auth::requestReplacementExpensifyCard($authToken, $cardID, $reason, validateCode: $validateCode);
https://github.com/Expensify/Web-Expensify/blob/4fccc5d6258fc9d7879d5cea6c0ced5663cd3334/lib/CardAPI.php#L405 =>
Auth::requestReplacementExpensifyCard($authToken, $existingVirtualCardID, Card::CARD_REPLACEMENT_REASON_STOLEN, validateCode: $validateCode, skipVirtualCardCreation: true);
Have an account with a physical and virtual card.
Report virtual card for fraud, make sure you're asked to enter a validateCode.
Enter a wrong validate code and make sure the request doesn't go through.
Enter the correct magic code and make sure you're redirected to the new card page.
Repeat the same steps by requesting a replacement physical card.
Offline tests
QA Steps
PR Author Checklist
### Fixed Issues
section aboveTests
sectionOffline steps
sectionQA steps
sectiontoggleReport
and notonIconClick
)myBool && <MyComponent />
.src/languages/*
files and using the translation methodSTYLE.md
) were followedAvatar
, I verified the components usingAvatar
are working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG)
)Avatar
is modified, I verified thatAvatar
is working as expected in all cases)ScrollView
component to make it scrollable when more elements are added to the page.main
branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTest
steps.Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari
Screen.Recording.2024-10-29.at.21.18.47.mov
MacOS: Desktop