Skip to content
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

Card: Remove Approve Order Listener Pattern #305

Merged
merged 18 commits into from
Dec 9, 2024

Conversation

sshropshire
Copy link
Collaborator

@sshropshire sshropshire commented Dec 5, 2024

Summary of changes

  • This PR migrates the CardClient approve order flow away from the listener pattern.
  • Migration Guide updates will come in a later PR
  • Users must now balance calls to CardClient.presentAuthChallenge() with calls to CardClient.finishApproveOrder() to complete an order flow

Checklist

  • Added a changelog entry

Authors

List GitHub usernames for everyone who contributed to this pull request.

@sshropshire sshropshire marked this pull request as ready for review December 6, 2024 17:59
@sshropshire sshropshire requested a review from a team as a code owner December 6, 2024 17:59
* @param result [CardApproveOrderResult] result with details
*/
@MainThread
fun onCardApproveOrderResult(result: CardApproveOrderResult)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's interesting. I don't think it's possible to do this in iOS with protocols.
Do you feel like this is something merchant would want us to handle?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if we need to deliver results on the main thread, but it is convenient when updating the UI after executing a transaction.

It's up to y'all if you want to do the same. I feel like the closest equivalent to this annotation in Swift would be @MainActor.

@@ -69,22 +68,27 @@ class CardClient internal constructor(
is ConfirmPaymentSourceResult.Success -> {
if (response.payerActionHref == null) {
Copy link
Collaborator

@KunJeongPark KunJeongPark Dec 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this syntax looks really similar to completion handler. So callback in place of listener pattern is safer for memory management?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's similar. The main difference now is there's a callback for every async method. With the listener pattern, there is only one listener, and it needs to be set by the merchant before starting any of the payment flows.


val url = Uri.parse(response.payerActionHref)
val authChallenge = CardAuthChallenge.ApproveOrder(url, cardRequest)
approveOrderListener?.onApproveOrderAuthorizationRequired(authChallenge)
val result = CardApproveOrderResult.AuthorizationRequired(authChallenge)
Copy link
Collaborator

@KunJeongPark KunJeongPark Dec 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's interesting that you have this intermediate state on the same result type.
So there is result type for API call and final result type for the merchant.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method does have three possible results. Alternatively, I thought about bundling the payer_action_required status into the Failure case but this felt more natural.

Technically it isn't a failure. PPaaS just needs more information before the buyer can continue.

cardClient?.presentAuthChallenge(activity, authChallenge)?.let { presentAuthResult ->
when (presentAuthResult) {
is CardPresentAuthChallengeResult.Success -> {
authState = presentAuthResult.authState
Copy link
Collaborator

@KunJeongPark KunJeongPark Dec 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit lost on how this authState gets translated to success CardApproveOrderResult back to the merchant.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So authState is a JSON string that allows us to continue the flow once the merchant app comes back into the foreground after browser switching. We created this pattern in BT v5 to allow Android merchants to recover from a process kill. In BT it's called a pendingRequest.

We can always come up with a new name, authState for me makes sense because it's similar to the state parameter used in OAuth2.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also this example should be a lot clearer in our revised sample merchant app we have planned.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that viewModel's completeAuthChallenge gets called in ApproveOrderView.

@sshropshire sshropshire merged commit ab03611 into main Dec 9, 2024
6 checks passed
@sshropshire sshropshire deleted the card_approve_order_remove_listener branch December 9, 2024 17:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants