flamingo.me/flamingo-commerce/v3@v3.11.0/checkout/application/placeorder/validate_payment.go (about) 1 package placeorder 2 3 import ( 4 "context" 5 "fmt" 6 7 "go.opencensus.io/trace" 8 9 "flamingo.me/flamingo-commerce/v3/checkout/domain/placeorder/process" 10 "flamingo.me/flamingo-commerce/v3/checkout/domain/placeorder/states" 11 "flamingo.me/flamingo-commerce/v3/payment/application" 12 paymentDomain "flamingo.me/flamingo-commerce/v3/payment/domain" 13 ) 14 15 const ( 16 // ValidatePaymentErrorNoActionURL used for errors when the needed URL is missing from the ActionData struct 17 ValidatePaymentErrorNoActionURL = "no url set for action" 18 // ValidatePaymentErrorNoWalletDetails used for errors when the needed URL is missing from the ActionData struct 19 ValidatePaymentErrorNoWalletDetails = "no wallet details set for action" 20 // ValidatePaymentErrorNoActionDisplayData used for errors when the needed DisplayData/HTML is missing from the ActionData struct 21 ValidatePaymentErrorNoActionDisplayData = "no display data / html set for action" 22 ) 23 24 // PaymentValidator to decide over the next state 25 func PaymentValidator(ctx context.Context, p *process.Process, paymentService *application.PaymentService) process.RunResult { 26 ctx, span := trace.StartSpan(ctx, "checkout/PaymentValidator") 27 defer span.End() 28 29 cart := p.Context().Cart 30 gateway, err := paymentService.PaymentGatewayByCart(p.Context().Cart) 31 if err != nil { 32 return process.RunResult{ 33 Failed: process.ErrorOccurredReason{Error: err.Error()}, 34 } 35 } 36 37 flowStatus, err := gateway.FlowStatus(ctx, &cart, p.Context().UUID) 38 if err != nil { 39 return process.RunResult{ 40 Failed: process.ErrorOccurredReason{Error: err.Error()}, 41 } 42 } 43 44 switch flowStatus.Status { 45 case paymentDomain.PaymentFlowStatusUnapproved: 46 switch flowStatus.Action { 47 case paymentDomain.PaymentFlowActionPostRedirect: 48 formFields := make(map[string]states.FormField, len(flowStatus.ActionData.FormParameter)) 49 for k, v := range flowStatus.ActionData.FormParameter { 50 formFields[k] = states.FormField{ 51 Value: v.Value, 52 } 53 } 54 if flowStatus.ActionData.URL == nil { 55 return process.RunResult{ 56 Failed: process.PaymentErrorOccurredReason{Error: ValidatePaymentErrorNoActionURL}, 57 } 58 } 59 p.UpdateState(states.PostRedirect{}.Name(), states.NewPostRedirectStateData(flowStatus.ActionData.URL, formFields)) 60 case paymentDomain.PaymentFlowActionShowWalletPayment: 61 if flowStatus.ActionData.WalletDetails == nil { 62 return process.RunResult{ 63 Failed: process.PaymentErrorOccurredReason{Error: ValidatePaymentErrorNoWalletDetails}, 64 } 65 } 66 p.UpdateState(states.ShowWalletPayment{}.Name(), states.NewShowWalletPaymentStateData(states.ShowWalletPaymentData(*flowStatus.ActionData.WalletDetails))) 67 case paymentDomain.PaymentFlowActionRedirect: 68 if flowStatus.ActionData.URL == nil { 69 return process.RunResult{ 70 Failed: process.PaymentErrorOccurredReason{Error: ValidatePaymentErrorNoActionURL}, 71 } 72 } 73 p.UpdateState(states.Redirect{}.Name(), states.NewRedirectStateData(flowStatus.ActionData.URL)) 74 case paymentDomain.PaymentFlowActionShowHTML: 75 if flowStatus.ActionData.DisplayData == "" { 76 return process.RunResult{ 77 Failed: process.PaymentErrorOccurredReason{Error: ValidatePaymentErrorNoActionDisplayData}, 78 } 79 } 80 p.UpdateState(states.ShowHTML{}.Name(), states.NewShowHTMLStateData(flowStatus.ActionData.DisplayData)) 81 case paymentDomain.PaymentFlowActionShowIframe: 82 if flowStatus.ActionData.URL == nil { 83 return process.RunResult{ 84 Failed: process.PaymentErrorOccurredReason{Error: ValidatePaymentErrorNoActionURL}, 85 } 86 } 87 p.UpdateState(states.ShowIframe{}.Name(), states.NewShowIframeStateData(flowStatus.ActionData.URL)) 88 case paymentDomain.PaymentFlowActionTriggerClientSDK: 89 if flowStatus.ActionData.URL == nil { 90 return process.RunResult{ 91 Failed: process.PaymentErrorOccurredReason{Error: ValidatePaymentErrorNoActionURL}, 92 } 93 } 94 p.UpdateState(states.TriggerClientSDK{}.Name(), states.NewTriggerClientSDKStateData(flowStatus.ActionData.URL, flowStatus.ActionData.DisplayData)) 95 default: 96 return process.RunResult{ 97 Failed: process.PaymentErrorOccurredReason{Error: fmt.Sprintf("Payment action not supported: %q", flowStatus.Action)}, 98 } 99 } 100 case paymentDomain.PaymentFlowStatusApproved: 101 // payment is done but needs confirmation 102 p.UpdateState(states.CompletePayment{}.Name(), nil) 103 case paymentDomain.PaymentFlowStatusCompleted: 104 // payment is done and confirmed, place order if not already placed 105 p.UpdateState(states.Success{}.Name(), nil) 106 case paymentDomain.PaymentFlowStatusFailed, paymentDomain.PaymentFlowStatusCancelled: 107 err := "" 108 if flowStatus.Error != nil { 109 err = flowStatus.Error.Error() 110 } 111 return process.RunResult{ 112 Failed: process.PaymentErrorOccurredReason{Error: err}, 113 } 114 case paymentDomain.PaymentFlowStatusAborted: 115 return process.RunResult{ 116 Failed: process.PaymentCanceledByCustomerReason{}, 117 } 118 case paymentDomain.PaymentFlowWaitingForCustomer: 119 // payment pending, waiting for customer doing async stuff like finishing is payment in mobile app 120 p.UpdateState(states.WaitForCustomer{}.Name(), nil) 121 default: 122 // unknown payment flow status 123 return process.RunResult{ 124 Failed: process.PaymentErrorOccurredReason{Error: fmt.Sprintf("Payment status not supported: %q", flowStatus.Status)}, 125 } 126 } 127 128 return process.RunResult{} 129 }