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

Add Authorize/Capture for PayPalWebPayments in Demo App #216

Merged
merged 8 commits into from
Nov 16, 2023

Conversation

KunJeongPark
Copy link
Collaborator

@KunJeongPark KunJeongPark commented Nov 7, 2023

Summary of changes

  • Create authorize/capture on order in demo app after successful checkout
PayPalWebAuth.mp4

Checklist

- [ ] Added a changelog entry

Authors

@KunJeongPark KunJeongPark requested a review from a team as a code owner November 7, 2023 19:28
@KunJeongPark KunJeongPark changed the title Add Authorize/apture for PayPalWebPayments Add Authorize/Capture for PayPalWebPayments in Demo App Nov 7, 2023
Copy link
Collaborator

@sshropshire sshropshire left a comment

Choose a reason for hiding this comment

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

🌊

@@ -0,0 +1,53 @@
import SwiftUI

struct PayPalWebOrderCompletionResultView: View {
Copy link
Collaborator

Choose a reason for hiding this comment

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

The names of these classes confuse me a bit. What is the difference b/w PayPalWebOrderCompletionResultView & PayPalWebOrderCompletionView?

Copy link
Collaborator Author

@KunJeongPark KunJeongPark Nov 13, 2023

Choose a reason for hiding this comment

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

I have this view (PayPalOrderCompletionView) where I carry over order approval results to second screen to display authorize/capture results (PayPalWebOrderCompletionResultView)

Copy link
Collaborator

Choose a reason for hiding this comment

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

If the order completion has already taken place maybe PayPalWebResultView is more appropriate?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

There are several results in these flows. For PayPalWeb, I have order creation result, approval result and the order completion result (result of authorize/capture). So I am distinguishing them with the names.

@@ -0,0 +1,38 @@
import SwiftUI

struct PayPalWebOrderCompletionView: View {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This class looks pretty much the exact same as CardPaymentOrderCompletionView.swift. The views should be re-usable across payment method/ view model types.

This way if we want to update this view, we do it in 1 place versus multiple.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

See comment below.

Comment on lines +85 to +123
func captureOrder(orderID: String, selectedMerchantIntegration: MerchantIntegration) async throws {
do {
DispatchQueue.main.async {
self.state.capturedOrderResponse = .loading
}
let order = try await DemoMerchantAPI.sharedService.captureOrder(
orderID: orderID,
selectedMerchantIntegration: selectedMerchantIntegration
)
DispatchQueue.main.async {
self.state.capturedOrderResponse = .loaded(order)
}
} catch {
DispatchQueue.main.async {
self.state.capturedOrderResponse = .error(message: error.localizedDescription)
}
print("Error capturing order: \(error.localizedDescription)")
}
}

func authorizeOrder(orderID: String, selectedMerchantIntegration: MerchantIntegration) async throws {
do {
DispatchQueue.main.async {
self.state.authorizedOrderResponse = .loading
}
let order = try await DemoMerchantAPI.sharedService.authorizeOrder(
orderID: orderID,
selectedMerchantIntegration: selectedMerchantIntegration
)
DispatchQueue.main.async {
self.state.authorizedOrderResponse = .loaded(order)
}
} catch {
DispatchQueue.main.async {
self.state.authorizedOrderResponse = .error(message: error.localizedDescription)
}
print("Error authorizing order: \(error.localizedDescription)")
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same here, this looks like a copy-paste from CardPaymentViewModel (see snippet). That duplication is usually a good sign that we should refactor.

Happy to talk through some refactor options. We could go the route of a parent class or a protocol to represent a generic PaymentMethodViewModel that has a set of default functionality that is re-usable across all payment methods (ex: auth, capture).

Copy link
Collaborator Author

@KunJeongPark KunJeongPark Nov 13, 2023

Choose a reason for hiding this comment

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

That is a great suggestion. I will start with PaymentMethodViewModel protocol and incorporate it into common views.

Copy link
Collaborator Author

@KunJeongPark KunJeongPark Nov 14, 2023

Choose a reason for hiding this comment

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

@scannillo , I looked into making a PaymentMethodViewModel protocol and extension to provide common default authorizeOrder and captureOrder function implementations and I ran into issue of having to change stored properties(states) in the conforming types. Protocol extensions, as I understand, cannot mutate stored properties of conforming types. For instance:

self.state.authorizedOrderResponse = .loaded(order)

I could provide in PaymentMethodViewModel protocol delegate methods like didAuthorizeOrder and didCaptureOrder but it would reduce duplication only slightly and introduce complexity.
I am looking into protocol with associatedType but it doesn't look pretty imo.

I ran into similar issue with factoring out views that looks the same - state in each ViewModel is of different type. I would need to use associated types and also run into issue of observing different properties of states of different types for each viewModels.

I think benefits of reducing a bit of code duplication doesn't justify the complexity introduced.

Copy link
Collaborator

Choose a reason for hiding this comment

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

It seems like there could be a few options in this StackOverflow that may be worth exploring: https://stackoverflow.com/questions/38885622/swift-protocol-extensions-property-default-values. Not sure if Sammy had a different approach in mind as well!

Copy link
Collaborator Author

@KunJeongPark KunJeongPark Nov 14, 2023

Choose a reason for hiding this comment

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

I will continue working w PaymentMethodViewModel protocol. I guess all both flows have views for order creation result, order approval result and order capture/authorize result.

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 created another ticket for refactoring common views and functionalities in the viewModels. https://paypal.atlassian.net/browse/DTPPCPSDK-1437

@@ -1,6 +1,6 @@
import SwiftUI

struct OrderCreateCardPaymentResultView: View {
struct OrderCreateCardResultView: View {
Copy link
Collaborator

Choose a reason for hiding this comment

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

In the refactor ticket can we please include updating some of these names? This name doesn't seem to match up with what the code in this file is doing and it's not clear why it's being changed since there are no other code changes in this file. I don't think the original name was super clear but it's especially confusing we made this change with no other changes here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Basically we have result views for each Orders API calls to check if they are working as expected.
so there are OrderCreateCardResultView, OrderCreatePayPalWebResultView, etc.
And there are also container views since I didn't want them to be all on one scrollable page.

Copy link
Collaborator

Choose a reason for hiding this comment

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

It sounds like this will be a great thing to refactor or can be shared in some way across payment methods since most of the logic is the same. I think that will help with naming as then we can have a singular OrderResultView.

Copy link
Collaborator Author

@KunJeongPark KunJeongPark Nov 15, 2023

Choose a reason for hiding this comment

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

That is what I want to investigate in the refactor ticket. How to organize the different states to minimize views where it makes sense. Most of the result views have different content. There are two that are identical, order create result views and the "action buttons" for authorize/capture. To me, it's easier to follow if there are separate views for each API call result so we can find them more easily but I will explore different options.

That's why I wanted to create a different PR for the refactor as it will be quite a bit of code change in the viewModels and some backtracking and modifications based on feedback. I wanted to be able to track these changes.

@@ -1,7 +1,7 @@
import SwiftUI
import PaymentButtons

struct PayPalTransactionButtonsView: View {
struct PayPalTransactionsView: View {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
struct PayPalTransactionsView: View {
struct PayPalTransactionView: View {

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

There are three options of transactions on that view PayPal, PayPal Credit, Pay Later. That's why I put Transactions. What do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the singular is fine even though it's used in multiple places there will ever only be 1 transaction

Copy link
Collaborator

@jaxdesmarais jaxdesmarais left a comment

Choose a reason for hiding this comment

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

Thanks for addressing all of my questions/suggestions!

Demo/Demo/ViewModels/PayPalWebViewModel.swift Outdated Show resolved Hide resolved
Comment on lines +29 to +33
try await paypalWebViewModel.completeOrder(
with: intent,
orderID: orderID,
selectedMerchantIntegration: selectedMerchantIntegration
)
Copy link
Collaborator

Choose a reason for hiding this comment

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

🎉

Copy link
Collaborator Author

@KunJeongPark KunJeongPark Nov 16, 2023

Choose a reason for hiding this comment

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

Thank you!

@KunJeongPark KunJeongPark merged commit dbc73a7 into main Nov 16, 2023
4 checks passed
@KunJeongPark KunJeongPark deleted the paypalAuthorizeCapture branch November 16, 2023 15:55
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.

4 participants