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  }