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