From dbc73a7b7f05aa3b796b460af2c9cc3e0af0dab6 Mon Sep 17 00:00:00 2001 From: Victoria Park Date: Thu, 16 Nov 2023 07:55:09 -0800 Subject: [PATCH] Add Authorize/Capture for PayPalWebPayments in Demo App (#216) * add authorize/capture for PayPalWebPayments * add missing pbxproj changes, remove name header * spacing PayPalTransactionButtonView * display sandbox email in demo app * Revert project.pbxproj to match main * Change view names for consistency between PayPalWeb and Card * Jax PR feedback * Update Demo/Demo/ViewModels/PayPalWebViewModel.swift --------- Co-authored-by: Jax DesMarais-Leder --- Demo/Demo.xcodeproj/project.pbxproj | 36 ++++++++----- Demo/Demo/Models/Order.swift | 8 ++- .../CardOrderCompletionResultView.swift | 8 +-- .../CardPaymentViews/CardPaymentView.swift | 2 +- ....swift => OrderCreateCardResultView.swift} | 2 +- .../PayPalOrderActionButton.swift | 39 ++++++++++++++ ...View.swift => PayPalTransactionView.swift} | 13 ++++- ...wift => PayPalWebApprovalResultView.swift} | 2 +- .../PayPalWebOrderCompletionResultView.swift | 53 +++++++++++++++++++ .../PayPalWebOrderCompletionView.swift | 38 +++++++++++++ .../PayPalWebViews/PayPalWebView.swift | 2 +- Demo/Demo/ViewModels/PayPalWebState.swift | 18 +++++++ Demo/Demo/ViewModels/PayPalWebViewModel.swift | 50 +++++++++++++++++ 13 files changed, 248 insertions(+), 23 deletions(-) rename Demo/Demo/SwiftUIComponents/CardPaymentViews/{OrderCreateCardPaymentResultView.swift => OrderCreateCardResultView.swift} (95%) create mode 100644 Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalOrderActionButton.swift rename Demo/Demo/SwiftUIComponents/PayPalWebViews/{PayPalTransactionButtonsView.swift => PayPalTransactionView.swift} (71%) rename Demo/Demo/SwiftUIComponents/PayPalWebViews/{PayPalWebApprovalView.swift => PayPalWebApprovalResultView.swift} (96%) create mode 100644 Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalWebOrderCompletionResultView.swift create mode 100644 Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalWebOrderCompletionView.swift diff --git a/Demo/Demo.xcodeproj/project.pbxproj b/Demo/Demo.xcodeproj/project.pbxproj index 61b633dd8..73a9be1e5 100644 --- a/Demo/Demo.xcodeproj/project.pbxproj +++ b/Demo/Demo.xcodeproj/project.pbxproj @@ -16,13 +16,16 @@ 3B22E8BC2A84397600962E34 /* PaymentTokenResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B22E8BB2A84397600962E34 /* PaymentTokenResponse.swift */; }; 3B4DD9A02A892A7000F4A716 /* CardVaultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B4DD99F2A892A7000F4A716 /* CardVaultView.swift */; }; 3B4DD9A22A8982B000F4A716 /* CardFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B4DD9A12A8982B000F4A716 /* CardFormView.swift */; }; + 3B6472A32AFAE5E3004745C4 /* PayPalWebOrderCompletionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B6472A22AFAE5E3004745C4 /* PayPalWebOrderCompletionView.swift */; }; + 3B6472A52AFAEB1E004745C4 /* PayPalWebOrderCompletionResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B6472A42AFAEB1E004745C4 /* PayPalWebOrderCompletionResultView.swift */; }; + 3B6472A72AFAEB3A004745C4 /* PayPalOrderActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B6472A62AFAEB3A004745C4 /* PayPalOrderActionButton.swift */; }; 3B80D50E2A291C0800D2EAC4 /* ClientIDRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B80D50D2A291C0800D2EAC4 /* ClientIDRequest.swift */; }; 3B80D5102A291CB100D2EAC4 /* ClientIDResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B80D50F2A291CB100D2EAC4 /* ClientIDResponse.swift */; }; 3B8EF4DB2A932DA300A70D0B /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B8EF4DA2A932DA300A70D0B /* ErrorView.swift */; }; 3BA56FE72A9DC9D70081D14F /* CardPaymentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BA56FE62A9DC9D70081D14F /* CardPaymentViewModel.swift */; }; 3BA56FE92A9DCA520081D14F /* CardPaymentState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BA56FE82A9DCA520081D14F /* CardPaymentState.swift */; }; 3BA56FEC2A9DCBF30081D14F /* CreateOrderCardPaymentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BA56FEB2A9DCBF30081D14F /* CreateOrderCardPaymentView.swift */; }; - 3BA56FEE2A9DCC340081D14F /* OrderCreateCardPaymentResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BA56FED2A9DCC340081D14F /* OrderCreateCardPaymentResultView.swift */; }; + 3BA56FEE2A9DCC340081D14F /* OrderCreateCardResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BA56FED2A9DCC340081D14F /* OrderCreateCardResultView.swift */; }; 3BA56FF02A9DCCFD0081D14F /* CardOrderApproveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BA56FEF2A9DCCFD0081D14F /* CardOrderApproveView.swift */; }; 3BA56FF22A9DCD440081D14F /* CardApprovalResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BA56FF12A9DCD440081D14F /* CardApprovalResultView.swift */; }; 3BA56FF42A9DCD790081D14F /* CardPaymentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BA56FF32A9DCD790081D14F /* CardPaymentView.swift */; }; @@ -34,8 +37,8 @@ 3BA570012AA052E80081D14F /* PayPalWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BA570002AA052E80081D14F /* PayPalWebView.swift */; }; 3BA570032AA053AE0081D14F /* CreateOrderPayPalWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BA570022AA053AE0081D14F /* CreateOrderPayPalWebView.swift */; }; 3BA570052AA0BBF10081D14F /* OrderCreatePayPalWebResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BA570042AA0BBF10081D14F /* OrderCreatePayPalWebResultView.swift */; }; - 3BA570072AA0DF330081D14F /* PayPalTransactionButtonsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BA570062AA0DF330081D14F /* PayPalTransactionButtonsView.swift */; }; - 3BA570092AA0E8340081D14F /* PayPalWebApprovalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BA570082AA0E8340081D14F /* PayPalWebApprovalView.swift */; }; + 3BA570072AA0DF330081D14F /* PayPalTransactionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BA570062AA0DF330081D14F /* PayPalTransactionView.swift */; }; + 3BA570092AA0E8340081D14F /* PayPalWebApprovalResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BA570082AA0E8340081D14F /* PayPalWebApprovalResultView.swift */; }; 3BA5700B2AA13C1C0081D14F /* CoreConfigManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BA5700A2AA13C1C0081D14F /* CoreConfigManager.swift */; }; 3BB7A9772A5CA6FD00C05140 /* MerchantIntegration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BB7A9762A5CA6FD00C05140 /* MerchantIntegration.swift */; }; 3BC622072A97115700251B85 /* RoundedBlueButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BC622062A97115700251B85 /* RoundedBlueButtonStyle.swift */; }; @@ -134,13 +137,16 @@ 3B22E8BB2A84397600962E34 /* PaymentTokenResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentTokenResponse.swift; sourceTree = ""; }; 3B4DD99F2A892A7000F4A716 /* CardVaultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardVaultView.swift; sourceTree = ""; }; 3B4DD9A12A8982B000F4A716 /* CardFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardFormView.swift; sourceTree = ""; }; + 3B6472A22AFAE5E3004745C4 /* PayPalWebOrderCompletionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PayPalWebOrderCompletionView.swift; sourceTree = ""; }; + 3B6472A42AFAEB1E004745C4 /* PayPalWebOrderCompletionResultView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PayPalWebOrderCompletionResultView.swift; sourceTree = ""; }; + 3B6472A62AFAEB3A004745C4 /* PayPalOrderActionButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PayPalOrderActionButton.swift; sourceTree = ""; }; 3B80D50D2A291C0800D2EAC4 /* ClientIDRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientIDRequest.swift; sourceTree = ""; }; 3B80D50F2A291CB100D2EAC4 /* ClientIDResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientIDResponse.swift; sourceTree = ""; }; 3B8EF4DA2A932DA300A70D0B /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = ""; }; 3BA56FE62A9DC9D70081D14F /* CardPaymentViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPaymentViewModel.swift; sourceTree = ""; }; 3BA56FE82A9DCA520081D14F /* CardPaymentState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPaymentState.swift; sourceTree = ""; }; 3BA56FEB2A9DCBF30081D14F /* CreateOrderCardPaymentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateOrderCardPaymentView.swift; sourceTree = ""; }; - 3BA56FED2A9DCC340081D14F /* OrderCreateCardPaymentResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderCreateCardPaymentResultView.swift; sourceTree = ""; }; + 3BA56FED2A9DCC340081D14F /* OrderCreateCardResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderCreateCardResultView.swift; sourceTree = ""; }; 3BA56FEF2A9DCCFD0081D14F /* CardOrderApproveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardOrderApproveView.swift; sourceTree = ""; }; 3BA56FF12A9DCD440081D14F /* CardApprovalResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardApprovalResultView.swift; sourceTree = ""; }; 3BA56FF32A9DCD790081D14F /* CardPaymentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPaymentView.swift; sourceTree = ""; }; @@ -152,8 +158,8 @@ 3BA570002AA052E80081D14F /* PayPalWebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalWebView.swift; sourceTree = ""; }; 3BA570022AA053AE0081D14F /* CreateOrderPayPalWebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateOrderPayPalWebView.swift; sourceTree = ""; }; 3BA570042AA0BBF10081D14F /* OrderCreatePayPalWebResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderCreatePayPalWebResultView.swift; sourceTree = ""; }; - 3BA570062AA0DF330081D14F /* PayPalTransactionButtonsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalTransactionButtonsView.swift; sourceTree = ""; }; - 3BA570082AA0E8340081D14F /* PayPalWebApprovalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalWebApprovalView.swift; sourceTree = ""; }; + 3BA570062AA0DF330081D14F /* PayPalTransactionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalTransactionView.swift; sourceTree = ""; }; + 3BA570082AA0E8340081D14F /* PayPalWebApprovalResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalWebApprovalResultView.swift; sourceTree = ""; }; 3BA5700A2AA13C1C0081D14F /* CoreConfigManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreConfigManager.swift; sourceTree = ""; }; 3BB7A9762A5CA6FD00C05140 /* MerchantIntegration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MerchantIntegration.swift; sourceTree = ""; }; 3BC622062A97115700251B85 /* RoundedBlueButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedBlueButtonStyle.swift; sourceTree = ""; }; @@ -256,7 +262,7 @@ isa = PBXGroup; children = ( 3BA56FEB2A9DCBF30081D14F /* CreateOrderCardPaymentView.swift */, - 3BA56FED2A9DCC340081D14F /* OrderCreateCardPaymentResultView.swift */, + 3BA56FED2A9DCC340081D14F /* OrderCreateCardResultView.swift */, 3BA56FEF2A9DCCFD0081D14F /* CardOrderApproveView.swift */, 3BA56FF12A9DCD440081D14F /* CardApprovalResultView.swift */, 3BA56FF32A9DCD790081D14F /* CardPaymentView.swift */, @@ -270,11 +276,14 @@ 3BA56FFF2A9FF6630081D14F /* PayPalWebViews */ = { isa = PBXGroup; children = ( + 3B6472A62AFAEB3A004745C4 /* PayPalOrderActionButton.swift */, + 3B6472A42AFAEB1E004745C4 /* PayPalWebOrderCompletionResultView.swift */, + 3B6472A22AFAE5E3004745C4 /* PayPalWebOrderCompletionView.swift */, 3BA570002AA052E80081D14F /* PayPalWebView.swift */, 3BA570022AA053AE0081D14F /* CreateOrderPayPalWebView.swift */, 3BA570042AA0BBF10081D14F /* OrderCreatePayPalWebResultView.swift */, - 3BA570062AA0DF330081D14F /* PayPalTransactionButtonsView.swift */, - 3BA570082AA0E8340081D14F /* PayPalWebApprovalView.swift */, + 3BA570062AA0DF330081D14F /* PayPalTransactionView.swift */, + 3BA570082AA0E8340081D14F /* PayPalWebApprovalResultView.swift */, ); path = PayPalWebViews; sourceTree = ""; @@ -582,7 +591,7 @@ files = ( 3BA56FE92A9DCA520081D14F /* CardPaymentState.swift in Sources */, 3B2027412A8A72050007907E /* CardVaultState.swift in Sources */, - 3BA570092AA0E8340081D14F /* PayPalWebApprovalView.swift in Sources */, + 3BA570092AA0E8340081D14F /* PayPalWebApprovalResultView.swift in Sources */, 80F33CED26F8E7A9006811B1 /* Order.swift in Sources */, 3B4DD9A02A892A7000F4A716 /* CardVaultView.swift in Sources */, 3BA56FF62A9E9AAB0081D14F /* CardOrderActionButton.swift in Sources */, @@ -618,11 +627,12 @@ BECD84A227036DDB007CCAE4 /* Intent.swift in Sources */, BED041AF270CA0FB00C80954 /* CustomButton.swift in Sources */, 3BB7A9772A5CA6FD00C05140 /* MerchantIntegration.swift in Sources */, - 3BA56FEE2A9DCC340081D14F /* OrderCreateCardPaymentResultView.swift in Sources */, + 3BA56FEE2A9DCC340081D14F /* OrderCreateCardResultView.swift in Sources */, BE1766B326F911A2007EF438 /* URLResponseError.swift in Sources */, 3B2027452A8AA78B0007907E /* UpdateSetupTokenView.swift in Sources */, 3BA56FF82A9FDB5A0081D14F /* CardPaymentOrderCompletionView.swift in Sources */, 3B8EF4DB2A932DA300A70D0B /* ErrorView.swift in Sources */, + 3B6472A32AFAE5E3004745C4 /* PayPalWebOrderCompletionView.swift in Sources */, 3BF9997A2A8AE12C009CBDF2 /* PaymentTokenResultView.swift in Sources */, 3BA56FFC2A9FEFE90081D14F /* PayPalWebViewModel.swift in Sources */, 3BDB34922A7CB5DE008100D7 /* SetupTokenResponse.swift in Sources */, @@ -635,7 +645,8 @@ 3B20273D2A89E3F00007907E /* CreateSetupTokenView.swift in Sources */, 3BA570052AA0BBF10081D14F /* OrderCreatePayPalWebResultView.swift in Sources */, 3B22E8BA2A842D8900962E34 /* PaymentTokenRequest.swift in Sources */, - 3BA570072AA0DF330081D14F /* PayPalTransactionButtonsView.swift in Sources */, + 3B6472A52AFAEB1E004745C4 /* PayPalWebOrderCompletionResultView.swift in Sources */, + 3BA570072AA0DF330081D14F /* PayPalTransactionView.swift in Sources */, 3BCCFE492A9D96CA00C5102F /* DemoApp.swift in Sources */, 3BA56FEC2A9DCBF30081D14F /* CreateOrderCardPaymentView.swift in Sources */, 536A5CA82898AA2A005C053D /* SwiftUINativeCheckoutDemo.swift in Sources */, @@ -643,6 +654,7 @@ BC6460CD2A12A2A0002B974B /* EmptyBodyParams.swift in Sources */, 3BF999782A8AD072009CBDF2 /* CreatePaymentTokenView.swift in Sources */, BED042312710833F00C80954 /* CardType.swift in Sources */, + 3B6472A72AFAEB3A004745C4 /* PayPalOrderActionButton.swift in Sources */, 5301468C28918B4D00184F22 /* ApprovalResult.swift in Sources */, 3BC6220B2A97204E00251B85 /* CircularProgressView.swift in Sources */, ); diff --git a/Demo/Demo/Models/Order.swift b/Demo/Demo/Models/Order.swift index 9f92c2dbd..ff225dbce 100644 --- a/Demo/Demo/Models/Order.swift +++ b/Demo/Demo/Models/Order.swift @@ -6,7 +6,8 @@ struct Order: Codable, Equatable { struct PaymentSource: Codable, Equatable { - let card: Card + let card: Card? + let paypal: PayPal? } init(id: String, status: String, paymentSource: PaymentSource? = nil) { @@ -22,6 +23,11 @@ struct Order: Codable, Equatable { let attributes: Attributes? } + struct PayPal: Codable, Equatable { + + let emailAddress: String + } + struct Attributes: Codable, Equatable { let vault: Vault diff --git a/Demo/Demo/SwiftUIComponents/CardPaymentViews/CardOrderCompletionResultView.swift b/Demo/Demo/SwiftUIComponents/CardPaymentViews/CardOrderCompletionResultView.swift index 5cb45ac2d..8da9bca85 100644 --- a/Demo/Demo/SwiftUIComponents/CardPaymentViews/CardOrderCompletionResultView.swift +++ b/Demo/Demo/SwiftUIComponents/CardPaymentViews/CardOrderCompletionResultView.swift @@ -35,19 +35,19 @@ struct CardOrderCompletionResultView: View { LeadingText("\(orderResponse.id)") LeadingText("Status", weight: .bold) LeadingText("\(orderResponse.status)") - if let lastDigits = orderResponse.paymentSource?.card.lastDigits { + if let lastDigits = orderResponse.paymentSource?.card?.lastDigits { LeadingText("Card Last Digits", weight: .bold) LeadingText("\(lastDigits)") } - if let brand = orderResponse.paymentSource?.card.brand { + if let brand = orderResponse.paymentSource?.card?.brand { LeadingText("Brand", weight: .bold) LeadingText("\(brand)") } - if let vaultID = orderResponse.paymentSource?.card.attributes?.vault.id { + if let vaultID = orderResponse.paymentSource?.card?.attributes?.vault.id { LeadingText("Vault ID / Payment Token", weight: .bold) LeadingText("\(vaultID)") } - if let customerID = orderResponse.paymentSource?.card.attributes?.vault.customer.id { + if let customerID = orderResponse.paymentSource?.card?.attributes?.vault.customer.id { LeadingText("Customer ID", weight: .bold) LeadingText("\(customerID)") } diff --git a/Demo/Demo/SwiftUIComponents/CardPaymentViews/CardPaymentView.swift b/Demo/Demo/SwiftUIComponents/CardPaymentViews/CardPaymentView.swift index f0a589f94..38e933d94 100644 --- a/Demo/Demo/SwiftUIComponents/CardPaymentViews/CardPaymentView.swift +++ b/Demo/Demo/SwiftUIComponents/CardPaymentViews/CardPaymentView.swift @@ -13,7 +13,7 @@ struct CardPaymentView: View { ) if let order = cardPaymentViewModel.state.createOrder { - OrderCreateCardPaymentResultView(cardPaymentViewModel: cardPaymentViewModel) + OrderCreateCardResultView(cardPaymentViewModel: cardPaymentViewModel) NavigationLink { CardOrderApproveView(orderID: order.id, cardPaymentViewModel: cardPaymentViewModel) } label: { diff --git a/Demo/Demo/SwiftUIComponents/CardPaymentViews/OrderCreateCardPaymentResultView.swift b/Demo/Demo/SwiftUIComponents/CardPaymentViews/OrderCreateCardResultView.swift similarity index 95% rename from Demo/Demo/SwiftUIComponents/CardPaymentViews/OrderCreateCardPaymentResultView.swift rename to Demo/Demo/SwiftUIComponents/CardPaymentViews/OrderCreateCardResultView.swift index 9dad6c16d..a6ade99bf 100644 --- a/Demo/Demo/SwiftUIComponents/CardPaymentViews/OrderCreateCardPaymentResultView.swift +++ b/Demo/Demo/SwiftUIComponents/CardPaymentViews/OrderCreateCardResultView.swift @@ -1,6 +1,6 @@ import SwiftUI -struct OrderCreateCardPaymentResultView: View { +struct OrderCreateCardResultView: View { @ObservedObject var cardPaymentViewModel: CardPaymentViewModel diff --git a/Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalOrderActionButton.swift b/Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalOrderActionButton.swift new file mode 100644 index 000000000..8b27a7e83 --- /dev/null +++ b/Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalOrderActionButton.swift @@ -0,0 +1,39 @@ +import SwiftUI + +struct PayPalOrderActionButton: View { + + let intent: Intent + let orderID: String + let selectedMerchantIntegration: MerchantIntegration + + @ObservedObject var paypalWebViewModel: PayPalWebViewModel + + var body: some View { + ZStack { + Button("\(intent.rawValue)") { + completeOrder() + } + .buttonStyle(RoundedBlueButtonStyle()) + .padding() + + if .loading == paypalWebViewModel.state.authorizedOrderResponse || + .loading == paypalWebViewModel.state.capturedOrderResponse { + CircularProgressView() + } + } + } + + private func completeOrder() { + Task { + do { + try await paypalWebViewModel.completeOrder( + with: intent, + orderID: orderID, + selectedMerchantIntegration: selectedMerchantIntegration + ) + } catch { + print("Error capturing order: \(error.localizedDescription)") + } + } + } +} diff --git a/Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalTransactionButtonsView.swift b/Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalTransactionView.swift similarity index 71% rename from Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalTransactionButtonsView.swift rename to Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalTransactionView.swift index b77342938..5bf0c212e 100644 --- a/Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalTransactionButtonsView.swift +++ b/Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalTransactionView.swift @@ -1,7 +1,7 @@ import SwiftUI import PaymentButtons -struct PayPalTransactionButtonsView: View { +struct PayPalTransactionView: View { @ObservedObject var paypalWebViewModel: PayPalWebViewModel let orderID: String @@ -29,7 +29,16 @@ struct PayPalTransactionButtonsView: View { .stroke(.gray, lineWidth: 2) .padding(5) ) - PayPalWebApprovalView(paypalWebViewModel: paypalWebViewModel) + PayPalWebApprovalResultView(paypalWebViewModel: paypalWebViewModel) + if paypalWebViewModel.state.checkoutResult != nil { + NavigationLink { + PayPalWebOrderCompletionView(orderID: orderID, payPalWebViewModel: paypalWebViewModel) + } label: { + Text("Complete Order Transaction") + } + .buttonStyle(RoundedBlueButtonStyle()) + .padding() + } Spacer() } } diff --git a/Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalWebApprovalView.swift b/Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalWebApprovalResultView.swift similarity index 96% rename from Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalWebApprovalView.swift rename to Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalWebApprovalResultView.swift index a0f2e300a..8f692dbbf 100644 --- a/Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalWebApprovalView.swift +++ b/Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalWebApprovalResultView.swift @@ -1,6 +1,6 @@ import SwiftUI -struct PayPalWebApprovalView: View { +struct PayPalWebApprovalResultView: View { @ObservedObject var paypalWebViewModel: PayPalWebViewModel diff --git a/Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalWebOrderCompletionResultView.swift b/Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalWebOrderCompletionResultView.swift new file mode 100644 index 000000000..86431e208 --- /dev/null +++ b/Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalWebOrderCompletionResultView.swift @@ -0,0 +1,53 @@ +import SwiftUI + +struct PayPalWebOrderCompletionResultView: View { + + @ObservedObject var paypalWebViewModel: PayPalWebViewModel + + var body: some View { + switch paypalWebViewModel.state.authorizedOrderResponse { + case .idle, .loading: + EmptyView() + case .loaded(let authorizedOrderResponse): + getOrderSuccessView(orderResponse: authorizedOrderResponse, intent: "Authorized") + case .error(let errorMessage): + ErrorView(errorMessage: errorMessage) + } + + switch paypalWebViewModel.state.capturedOrderResponse { + case .idle, .loading: + EmptyView() + case .loaded(let capturedOrderResponse): + getOrderSuccessView(orderResponse: capturedOrderResponse, intent: "Captured") + case .error(let errorMessage): + ErrorView(errorMessage: errorMessage) + } + } + + func getOrderSuccessView(orderResponse: Order, intent: String) -> some View { + VStack(spacing: 16) { + HStack { + Text("Order \(intent)") + .font(.system(size: 20)) + Spacer() + } + LeadingText("Order ID", weight: .bold) + LeadingText("\(orderResponse.id)") + LeadingText("Status", weight: .bold) + LeadingText("\(orderResponse.status)") + if let email = orderResponse.paymentSource?.paypal?.emailAddress { + LeadingText("Email", weight: .bold) + LeadingText("\(email)") + } + Text("") + .id("bottomView") + } + .frame(maxWidth: .infinity) + .padding() + .background( + RoundedRectangle(cornerRadius: 10) + .stroke(.gray, lineWidth: 2) + .padding(5) + ) + } +} diff --git a/Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalWebOrderCompletionView.swift b/Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalWebOrderCompletionView.swift new file mode 100644 index 000000000..609d8d856 --- /dev/null +++ b/Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalWebOrderCompletionView.swift @@ -0,0 +1,38 @@ +import SwiftUI + +struct PayPalWebOrderCompletionView: View { + + let orderID: String + @ObservedObject var payPalWebViewModel: PayPalWebViewModel + + var body: some View { + let state = payPalWebViewModel.state + ScrollView { + ScrollViewReader { scrollView in + VStack { + PayPalWebApprovalResultView(paypalWebViewModel: payPalWebViewModel) + if state.checkoutResult != nil { + PayPalOrderActionButton( + intent: state.intent, + orderID: orderID, + selectedMerchantIntegration: DemoSettings.merchantIntegration, + paypalWebViewModel: payPalWebViewModel + ) + } + + if state.authorizedOrder != nil || state.capturedOrder != nil { + PayPalWebOrderCompletionResultView(paypalWebViewModel: payPalWebViewModel) + } + Text("") + .id("bottomView") + Spacer() + } + .onChange(of: state) { _ in + withAnimation { + scrollView.scrollTo("bottomView") + } + } + } + } + } +} diff --git a/Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalWebView.swift b/Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalWebView.swift index 03770bd21..c1ae6f2fa 100644 --- a/Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalWebView.swift +++ b/Demo/Demo/SwiftUIComponents/PayPalWebViews/PayPalWebView.swift @@ -15,7 +15,7 @@ struct PayPalWebView: View { if let order = paypalWebViewModel.state.createOrder { OrderCreatePayPalWebResultView(paypalWebViewModel: paypalWebViewModel) NavigationLink { - PayPalTransactionButtonsView(paypalWebViewModel: paypalWebViewModel, orderID: order.id) + PayPalTransactionView(paypalWebViewModel: paypalWebViewModel, orderID: order.id) .navigationTitle("PayPal Transactions") } label: { Text("PayPal Transactions") diff --git a/Demo/Demo/ViewModels/PayPalWebState.swift b/Demo/Demo/ViewModels/PayPalWebState.swift index bd6d29cc2..04f94d0b6 100644 --- a/Demo/Demo/ViewModels/PayPalWebState.swift +++ b/Demo/Demo/ViewModels/PayPalWebState.swift @@ -12,6 +12,8 @@ struct PayPalWebState: Equatable { var createOrder: Order? var intent: Intent = .authorize var checkoutResult: CheckoutResult? + var authorizedOrder: Order? + var capturedOrder: Order? var createdOrderResponse: LoadingState = .idle { didSet { @@ -28,4 +30,20 @@ struct PayPalWebState: Equatable { } } } + + var capturedOrderResponse: LoadingState = .idle { + didSet { + if case .loaded(let value) = capturedOrderResponse { + capturedOrder = value + } + } + } + + var authorizedOrderResponse: LoadingState = .idle { + didSet { + if case .loaded(let value) = authorizedOrderResponse { + authorizedOrder = value + } + } + } } diff --git a/Demo/Demo/ViewModels/PayPalWebViewModel.swift b/Demo/Demo/ViewModels/PayPalWebViewModel.swift index 1bb2ab99a..4019517a1 100644 --- a/Demo/Demo/ViewModels/PayPalWebViewModel.swift +++ b/Demo/Demo/ViewModels/PayPalWebViewModel.swift @@ -82,6 +82,56 @@ class PayPalWebViewModel: ObservableObject, PayPalWebCheckoutDelegate { } } + func completeOrder(with intent: Intent, orderID: String, selectedMerchantIntegration: MerchantIntegration) async throws { + switch intent { + case .capture: + try await captureOrder(orderID: orderID, selectedMerchantIntegration: selectedMerchantIntegration) + print("Order Captured. ID: \(state.capturedOrder?.id ?? "")") + case .authorize: + try await authorizeOrder(orderID: orderID, selectedMerchantIntegration: selectedMerchantIntegration) + print("Order Authorized. ID: \(state.authorizedOrder?.id ?? "")") + } + } + + 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)") + } + } // MARK: - PayPalWeb Checkout Delegate