Skip to content

Commit

Permalink
[DTPP-142] PayPalWebPayments show stages of order (#311)
Browse files Browse the repository at this point in the history
* [DTPP-142] PayPalWebPayments show stages of order
  • Loading branch information
GMALKHA authored Dec 6, 2024
1 parent 8194323 commit c7a789d
Show file tree
Hide file tree
Showing 10 changed files with 279 additions and 75 deletions.
32 changes: 26 additions & 6 deletions Demo/Demo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
objects = {

/* Begin PBXBuildFile section */
1001E2C12CFFC72E0023A03C /* PayPalPaymentState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1001E2C02CFFC72E0023A03C /* PayPalPaymentState.swift */; };
1001E2C52CFFD2800023A03C /* PayPalApprovalResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1001E2C42CFFD2800023A03C /* PayPalApprovalResultView.swift */; };
1001E2C72CFFD2A30023A03C /* PayPalOrderCompletionResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1001E2C62CFFD2A20023A03C /* PayPalOrderCompletionResultView.swift */; };
3B20273D2A89E3F00007907E /* CreateSetupTokenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B20273C2A89E3F00007907E /* CreateSetupTokenView.swift */; };
3B20273F2A89F24E0007907E /* CardVaultViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B20273E2A89F24E0007907E /* CardVaultViewModel.swift */; };
3B2027412A8A72050007907E /* VaultState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B2027402A8A72050007907E /* VaultState.swift */; };
Expand Down Expand Up @@ -67,7 +70,7 @@
BE1766B326F911A2007EF438 /* URLResponseError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE1766B226F911A2007EF438 /* URLResponseError.swift */; };
BE1766D926FA7BC8007EF438 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = BE1766D826FA7BC8007EF438 /* Settings.bundle */; };
BE5898952B2B91F800AA196E /* LabelViewText.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE5898942B2B91F800AA196E /* LabelViewText.swift */; };
BE8117642B07E778009867B9 /* PayPalWebResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE8117632B07E778009867B9 /* PayPalWebResultView.swift */; };
BE8117642B07E778009867B9 /* PayPalOrderCreateResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE8117632B07E778009867B9 /* PayPalOrderCreateResultView.swift */; };
BE8117682B080472009867B9 /* CurrentState.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE8117672B080472009867B9 /* CurrentState.swift */; };
BE9F36D82745490400AFC7DA /* FloatingLabelTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE9F36D72745490400AFC7DA /* FloatingLabelTextField.swift */; };
BECD84A027036DC2007CCAE4 /* Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = BECD849F27036DC2007CCAE4 /* Environment.swift */; };
Expand Down Expand Up @@ -118,6 +121,9 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
1001E2C02CFFC72E0023A03C /* PayPalPaymentState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalPaymentState.swift; sourceTree = "<group>"; };
1001E2C42CFFD2800023A03C /* PayPalApprovalResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalApprovalResultView.swift; sourceTree = "<group>"; };
1001E2C62CFFD2A20023A03C /* PayPalOrderCompletionResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalOrderCompletionResultView.swift; sourceTree = "<group>"; };
3B20273C2A89E3F00007907E /* CreateSetupTokenView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateSetupTokenView.swift; sourceTree = "<group>"; };
3B20273E2A89F24E0007907E /* CardVaultViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardVaultViewModel.swift; sourceTree = "<group>"; };
3B2027402A8A72050007907E /* VaultState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VaultState.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -186,7 +192,7 @@
BE1766D826FA7BC8007EF438 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
BE420F3628189A7A00D8D66A /* PayPalUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PayPalUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
BE5898942B2B91F800AA196E /* LabelViewText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelViewText.swift; sourceTree = "<group>"; };
BE8117632B07E778009867B9 /* PayPalWebResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalWebResultView.swift; sourceTree = "<group>"; };
BE8117632B07E778009867B9 /* PayPalOrderCreateResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalOrderCreateResultView.swift; sourceTree = "<group>"; };
BE8117672B080472009867B9 /* CurrentState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentState.swift; sourceTree = "<group>"; };
BE9F36D72745490400AFC7DA /* FloatingLabelTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingLabelTextField.swift; sourceTree = "<group>"; };
BECD849F27036DC2007CCAE4 /* Environment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Environment.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -267,6 +273,7 @@
isa = PBXGroup;
children = (
3BA56FFB2A9FEFE90081D14F /* PayPalWebViewModel.swift */,
1001E2C02CFFC72E0023A03C /* PayPalPaymentState.swift */,
);
path = PayPalWebViewModel;
sourceTree = "<group>";
Expand Down Expand Up @@ -314,6 +321,17 @@
path = Vault;
sourceTree = "<group>";
};
1001E2CB2D03836D0023A03C /* PayPalWebResultViews */ = {
isa = PBXGroup;
children = (
BE8117632B07E778009867B9 /* PayPalOrderCreateResultView.swift */,
1001E2C42CFFD2800023A03C /* PayPalApprovalResultView.swift */,
3B6472A62AFAEB3A004745C4 /* PayPalWebTransactionView.swift */,
1001E2C62CFFD2A20023A03C /* PayPalOrderCompletionResultView.swift */,
);
path = PayPalWebResultViews;
sourceTree = "<group>";
};
3B2501052B2679F000903EAB /* VaultViews */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -353,11 +371,10 @@
3BA56FFF2A9FF6630081D14F /* PayPalWebPaymentsView */ = {
isa = PBXGroup;
children = (
1001E2CB2D03836D0023A03C /* PayPalWebResultViews */,
3BA570002AA052E80081D14F /* PayPalWebPaymentsView.swift */,
3BA570062AA0DF330081D14F /* PayPalWebButtonsView.swift */,
3BA570022AA053AE0081D14F /* PayPalWebCreateOrderView.swift */,
3BA570002AA052E80081D14F /* PayPalWebPaymentsView.swift */,
BE8117632B07E778009867B9 /* PayPalWebResultView.swift */,
3B6472A62AFAEB3A004745C4 /* PayPalWebTransactionView.swift */,
);
path = PayPalWebPaymentsView;
sourceTree = "<group>";
Expand Down Expand Up @@ -642,7 +659,7 @@
3B2027412A8A72050007907E /* VaultState.swift in Sources */,
80F33CED26F8E7A9006811B1 /* Order.swift in Sources */,
3B4DD9A02A892A7000F4A716 /* CardVaultView.swift in Sources */,
BE8117642B07E778009867B9 /* PayPalWebResultView.swift in Sources */,
BE8117642B07E778009867B9 /* PayPalOrderCreateResultView.swift in Sources */,
3BA56FF62A9E9AAB0081D14F /* CardOrderActionButton.swift in Sources */,
3BC622092A97198500251B85 /* LeadingText.swift in Sources */,
3B80D5102A291CB100D2EAC4 /* ClientIDResponse.swift in Sources */,
Expand All @@ -657,17 +674,20 @@
3B2027432A8A95EF0007907E /* SetupTokenResultView.swift in Sources */,
3BF999762A8AC093009CBDF2 /* UpdateSetupTokenResultView.swift in Sources */,
BECD84A027036DC2007CCAE4 /* Environment.swift in Sources */,
1001E2C12CFFC72E0023A03C /* PayPalPaymentState.swift in Sources */,
3BCCFE4B2A9D985F00C5102F /* FeatureSelectionView.swift in Sources */,
CB9ED44C283FDA900081F4DE /* PaymentButtonEnums+Extension.swift in Sources */,
3BB60B552B1FA00C00A298CF /* PayPalVaultViewModel.swift in Sources */,
3BB60B532B1F9F1100A298CF /* PayPalVaultView.swift in Sources */,
3B80D50E2A291C0800D2EAC4 /* ClientIDRequest.swift in Sources */,
3BA56FF02A9DCCFD0081D14F /* CardOrderApproveView.swift in Sources */,
BE8117682B080472009867B9 /* CurrentState.swift in Sources */,
1001E2C72CFFD2A30023A03C /* PayPalOrderCompletionResultView.swift in Sources */,
3BDB348E2A7CB02C008100D7 /* CreateSetupTokenParam.swift in Sources */,
3BA0A58B2B1E240300330681 /* VaultViewModel.swift in Sources */,
80F33CF326F8EA50006811B1 /* DemoSettings.swift in Sources */,
3BA56FE72A9DC9D70081D14F /* CardPaymentViewModel.swift in Sources */,
1001E2C52CFFD2800023A03C /* PayPalApprovalResultView.swift in Sources */,
3BA5700B2AA13C1C0081D14F /* CoreConfigManager.swift in Sources */,
80F33CE826F8DE29006811B1 /* DemoMerchantAPI.swift in Sources */,
80F33CEF26F8E7CC006811B1 /* CreateOrderParams.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ struct PayPalWebButtonsView: View {
payPalWebViewModel.paymentButtonTapped(funding: .paypal)
}
}
if payPalWebViewModel.state == .loading &&
if payPalWebViewModel.state.approveResultResponse == .loading &&
payPalWebViewModel.checkoutResult == nil &&
payPalWebViewModel.orderID != nil {
CircularProgressView()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ struct PayPalWebCreateOrderView: View {
}
}
.buttonStyle(RoundedBlueButtonStyle())
if payPalWebViewModel.state == .loading && payPalWebViewModel.checkoutResult == nil && payPalWebViewModel.orderID == nil {
if case .loading = payPalWebViewModel.state.createdOrderResponse {
CircularProgressView()
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,36 @@
import SwiftUI

struct PayPalWebPaymentsView: View {

@StateObject var payPalWebViewModel = PayPalWebViewModel()

var body: some View {
ScrollView {
ScrollViewReader { scrollView in
VStack(spacing: 16) {

PayPalWebCreateOrderView(payPalWebViewModel: payPalWebViewModel)

if payPalWebViewModel.orderID != nil {

if case .loaded = payPalWebViewModel.state.createdOrderResponse {
PayPalOrderCreateResultView(payPalWebViewModel: payPalWebViewModel)

PayPalWebButtonsView(payPalWebViewModel: payPalWebViewModel)
}

PayPalWebResultView(payPalWebViewModel: payPalWebViewModel)

if payPalWebViewModel.checkoutResult != nil {
if case .loaded = payPalWebViewModel.state.approveResultResponse {
PayPalApprovalResultView(payPalWebViewModel: payPalWebViewModel)
PayPalWebTransactionView(payPalWebViewModel: payPalWebViewModel)
.padding(.bottom, 20)
.id("bottomView")
.onAppear {
withAnimation {
scrollView.scrollTo("bottomView")
}
}
}

if case .loaded = payPalWebViewModel.state.capturedOrderResponse {
PayPalOrderCompletionResultView(payPalWebViewModel: payPalWebViewModel)
} else if case .loaded = payPalWebViewModel.state.authorizedOrderResponse {
PayPalOrderCompletionResultView(payPalWebViewModel: payPalWebViewModel)
}
Text("")
.id("bottomView")
}
.onChange(of: payPalWebViewModel.state) { _ in
withAnimation {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import SwiftUI

struct PayPalApprovalResultView: View {

@ObservedObject var payPalWebViewModel: PayPalWebViewModel

var body: some View {
switch payPalWebViewModel.state.approveResultResponse {
case .idle, .loading:
EmptyView()
case .error(let message):
ErrorView(errorMessage: message)
case .loaded(let approvalResult):
getApprovalSuccessView(approvalResult: approvalResult)
}
}

func getApprovalSuccessView(approvalResult: PayPalPaymentState.ApprovalResult) -> some View {
VStack(alignment: .leading, spacing: 16) {
Text("Approval Result")
.font(.headline)
LeadingText("Approval ID", weight: .bold)
.font(.system(size: 20))

LeadingText(approvalResult.id)
if let status = approvalResult.status {
LeadingText("Status", weight: .bold)
LeadingText(status)
}
}
.frame(maxWidth: .infinity)
.padding()
.background(
RoundedRectangle(cornerRadius: 10)
.stroke(.gray, lineWidth: 2)
.padding(5)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import SwiftUI

struct PayPalOrderCompletionResultView: View {

@ObservedObject var payPalWebViewModel: PayPalWebViewModel

var body: some View {
VStack {
if case .loaded(let authorizedOrder) = payPalWebViewModel.state.authorizedOrderResponse {
getCompletionSuccessView(order: authorizedOrder, intent: "Authorized")
}
if case .loaded(let capturedOrder) = payPalWebViewModel.state.capturedOrderResponse {
getCompletionSuccessView(order: capturedOrder, intent: "Captured")
}
}
}

func getCompletionSuccessView(order: Order, intent: String) -> some View {
VStack(alignment: .leading, spacing: 16) {
Text("Order \(intent) Successfully")
.font(.system(size: 20))

LabelViewText("Order ID:", bodyText: order.id)

LabelViewText("Status:", bodyText: order.status)

if let payerID = payPalWebViewModel.checkoutResult?.payerID {
LabelViewText("Payer ID:", bodyText: payerID)
}

if let emailAddress = order.paymentSource?.paypal?.emailAddress {
LabelViewText("Email:", bodyText: emailAddress)
}

if let vaultID = order.paymentSource?.paypal?.attributes?.vault.id {
LabelViewText("Payment Token:", bodyText: vaultID)
}

if let customerID = order.paymentSource?.paypal?.attributes?.vault.customer?.id {
LabelViewText("Customer ID:", bodyText: customerID)
}
}
.frame(maxWidth: .infinity)
.padding()
.background(
RoundedRectangle(cornerRadius: 10)
.stroke(.gray, lineWidth: 2)
.padding(5)
)
}
}
Original file line number Diff line number Diff line change
@@ -1,48 +1,45 @@
import SwiftUI

struct PayPalWebResultView: View {
struct PayPalOrderCreateResultView: View {

@ObservedObject var payPalWebViewModel: PayPalWebViewModel

var body: some View {
switch payPalWebViewModel.state {
switch payPalWebViewModel.state.createdOrderResponse {
case .idle, .loading:
EmptyView()
case .success:
successView
case .loaded(let createOrderResponse):
getSuccessView(createOrderResponse: createOrderResponse)
case .error(let errorMessage):
ErrorView(errorMessage: errorMessage)
}
}

var successView: some View {
func getSuccessView(createOrderResponse: Order) -> some View {
VStack(alignment: .leading, spacing: 16) {
HStack {
Text("Order Details")
.font(.system(size: 20))
Spacer()
}
if let orderID = payPalWebViewModel.orderID {
LabelViewText("Order ID:", bodyText: orderID)
}

if let status = payPalWebViewModel.order?.status {
LabelViewText("Status:", bodyText: status)
}

LabelViewText("Order ID:", bodyText: createOrderResponse.id)

LabelViewText("Status:", bodyText: createOrderResponse.status)

if let payerID = payPalWebViewModel.checkoutResult?.payerID {
LabelViewText("Payer ID:", bodyText: payerID)
}

if let emailAddress = payPalWebViewModel.order?.paymentSource?.paypal?.emailAddress {
if let emailAddress = createOrderResponse.paymentSource?.paypal?.emailAddress {
LabelViewText("Email:", bodyText: emailAddress)
}

if let vaultID = payPalWebViewModel.order?.paymentSource?.paypal?.attributes?.vault.id {
if let vaultID = createOrderResponse.paymentSource?.paypal?.attributes?.vault.id {
LabelViewText("Payment Token:", bodyText: vaultID)
}

if let customerID = payPalWebViewModel.order?.paymentSource?.paypal?.attributes?.vault.customer?.id {
if let customerID = createOrderResponse.paymentSource?.paypal?.attributes?.vault.customer?.id {
LabelViewText("Customer ID:", bodyText: customerID)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ struct PayPalWebTransactionView: View {
.buttonStyle(RoundedBlueButtonStyle())
.padding()

if payPalWebViewModel.state == .loading {
if payPalWebViewModel.state.capturedOrderResponse == .loading ||
payPalWebViewModel.state.authorizedOrderResponse == .loading {
CircularProgressView()
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import Foundation
import PayPalWebPayments

struct PayPalPaymentState: Equatable {

struct ApprovalResult: Decodable, Equatable {

let id: String
let status: String?
}

var createOrder: Order?
var authorizedOrder: Order?
var capturedOrder: Order?
var intent: Intent = .authorize
var approveResult: ApprovalResult?

var createdOrderResponse: LoadingState<Order> = .idle {
didSet {
if case .loaded(let value) = createdOrderResponse {
createOrder = value
}
}
}

var approveResultResponse: LoadingState<ApprovalResult> = .idle {
didSet {
if case .loaded(let value) = approveResultResponse {
approveResult = value
}
}
}

var capturedOrderResponse: LoadingState<Order> = .idle {
didSet {
if case .loaded(let value) = capturedOrderResponse {
capturedOrder = value
}
}
}

var authorizedOrderResponse: LoadingState<Order> = .idle {
didSet {
if case .loaded(let value) = authorizedOrderResponse {
authorizedOrder = value
}
}
}
}
Loading

0 comments on commit c7a789d

Please sign in to comment.