Merge pull request 'added gateway and operation references' (#635) from bff-634 into main
Reviewed-on: #635
This commit was merged in pull request #635.
This commit is contained in:
@@ -8,8 +8,10 @@ import (
|
||||
|
||||
"github.com/tech/sendico/pkg/api/http/response"
|
||||
"github.com/tech/sendico/pkg/mlogger"
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
paymenttypes "github.com/tech/sendico/pkg/payments/types"
|
||||
feesv1 "github.com/tech/sendico/pkg/proto/billing/fees/v1"
|
||||
gatewayv1 "github.com/tech/sendico/pkg/proto/common/gateway/v1"
|
||||
paginationv1 "github.com/tech/sendico/pkg/proto/common/pagination/v1"
|
||||
oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1"
|
||||
orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2"
|
||||
@@ -81,6 +83,8 @@ type PaymentOperation struct {
|
||||
Code string `json:"code,omitempty"`
|
||||
State string `json:"state,omitempty"`
|
||||
Label string `json:"label,omitempty"`
|
||||
OperationRef string `json:"operationRef,omitempty"`
|
||||
Gateway string `json:"gateway,omitempty"`
|
||||
FailureCode string `json:"failureCode,omitempty"`
|
||||
FailureReason string `json:"failureReason,omitempty"`
|
||||
StartedAt time.Time `json:"startedAt,omitempty"`
|
||||
@@ -326,13 +330,16 @@ func toUserVisibleOperations(steps []*orchestrationv2.StepExecution) []PaymentOp
|
||||
}
|
||||
|
||||
func toPaymentOperation(step *orchestrationv2.StepExecution) PaymentOperation {
|
||||
operationRef, gateway := operationRefAndGateway(step.GetStepCode(), step.GetRefs())
|
||||
op := PaymentOperation{
|
||||
StepRef: step.GetStepRef(),
|
||||
Code: step.GetStepCode(),
|
||||
State: enumJSONName(step.GetState().String()),
|
||||
Label: strings.TrimSpace(step.GetUserLabel()),
|
||||
StartedAt: timestampAsTime(step.GetStartedAt()),
|
||||
CompletedAt: timestampAsTime(step.GetCompletedAt()),
|
||||
StepRef: step.GetStepRef(),
|
||||
Code: step.GetStepCode(),
|
||||
State: enumJSONName(step.GetState().String()),
|
||||
Label: strings.TrimSpace(step.GetUserLabel()),
|
||||
OperationRef: operationRef,
|
||||
Gateway: string(gateway),
|
||||
StartedAt: timestampAsTime(step.GetStartedAt()),
|
||||
CompletedAt: timestampAsTime(step.GetCompletedAt()),
|
||||
}
|
||||
failure := step.GetFailure()
|
||||
if failure == nil {
|
||||
@@ -346,6 +353,117 @@ func toPaymentOperation(step *orchestrationv2.StepExecution) PaymentOperation {
|
||||
return op
|
||||
}
|
||||
|
||||
const (
|
||||
externalRefKindOperation = "operation_ref"
|
||||
)
|
||||
|
||||
func operationRefAndGateway(stepCode string, refs []*orchestrationv2.ExternalReference) (string, mservice.Type) {
|
||||
var (
|
||||
operationRef string
|
||||
gateway mservice.Type
|
||||
)
|
||||
|
||||
for _, ref := range refs {
|
||||
if ref == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
kind := strings.ToLower(strings.TrimSpace(ref.GetKind()))
|
||||
value := strings.TrimSpace(ref.GetRef())
|
||||
candidateGateway := inferGatewayType(ref.GetGatewayInstanceId(), ref.GetRail(), stepCode)
|
||||
|
||||
if kind == externalRefKindOperation && operationRef == "" && value != "" {
|
||||
operationRef = value
|
||||
}
|
||||
if gateway == "" && candidateGateway != "" {
|
||||
gateway = candidateGateway
|
||||
}
|
||||
}
|
||||
if gateway == "" {
|
||||
gateway = inferGatewayType("", gatewayv1.Rail_RAIL_UNSPECIFIED, stepCode)
|
||||
}
|
||||
return operationRef, gateway
|
||||
}
|
||||
|
||||
func inferGatewayType(gatewayInstanceID string, rail gatewayv1.Rail, stepCode string) mservice.Type {
|
||||
if gateway := gatewayTypeFromInstanceID(gatewayInstanceID); gateway != "" {
|
||||
return gateway
|
||||
}
|
||||
if gateway := gatewayTypeFromRail(rail); gateway != "" {
|
||||
return gateway
|
||||
}
|
||||
return gatewayTypeFromStepCode(stepCode)
|
||||
}
|
||||
|
||||
func gatewayTypeFromInstanceID(raw string) mservice.Type {
|
||||
value := strings.ToLower(strings.TrimSpace(raw))
|
||||
if value == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
switch mservice.Type(value) {
|
||||
case mservice.ChainGateway, mservice.TronGateway, mservice.MntxGateway, mservice.PaymentGateway, mservice.TgSettle, mservice.Ledger:
|
||||
return mservice.Type(value)
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.Contains(value, "ledger"):
|
||||
return mservice.Ledger
|
||||
case strings.Contains(value, "tgsettle"):
|
||||
return mservice.TgSettle
|
||||
case strings.Contains(value, "payment_gateway"),
|
||||
strings.Contains(value, "settlement"),
|
||||
strings.Contains(value, "onramp"),
|
||||
strings.Contains(value, "offramp"):
|
||||
return mservice.PaymentGateway
|
||||
case strings.Contains(value, "mntx"), strings.Contains(value, "mcards"):
|
||||
return mservice.MntxGateway
|
||||
case strings.Contains(value, "tron"):
|
||||
return mservice.TronGateway
|
||||
case strings.Contains(value, "chain"), strings.Contains(value, "crypto"):
|
||||
return mservice.ChainGateway
|
||||
case strings.Contains(value, "card"):
|
||||
return mservice.MntxGateway
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func gatewayTypeFromRail(rail gatewayv1.Rail) mservice.Type {
|
||||
switch rail {
|
||||
case gatewayv1.Rail_RAIL_LEDGER:
|
||||
return mservice.Ledger
|
||||
case gatewayv1.Rail_RAIL_CARD:
|
||||
return mservice.MntxGateway
|
||||
case gatewayv1.Rail_RAIL_SETTLEMENT, gatewayv1.Rail_RAIL_ONRAMP, gatewayv1.Rail_RAIL_OFFRAMP:
|
||||
return mservice.PaymentGateway
|
||||
case gatewayv1.Rail_RAIL_CRYPTO:
|
||||
return mservice.ChainGateway
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func gatewayTypeFromStepCode(stepCode string) mservice.Type {
|
||||
code := strings.ToLower(strings.TrimSpace(stepCode))
|
||||
switch {
|
||||
case strings.Contains(code, "ledger"):
|
||||
return mservice.Ledger
|
||||
case strings.Contains(code, "card_payout"), strings.Contains(code, ".card."):
|
||||
return mservice.MntxGateway
|
||||
case strings.Contains(code, "provider_settlement"),
|
||||
strings.Contains(code, "settlement"),
|
||||
strings.Contains(code, "fx_convert"),
|
||||
strings.Contains(code, "onramp"),
|
||||
strings.Contains(code, "offramp"):
|
||||
return mservice.PaymentGateway
|
||||
case strings.Contains(code, "crypto"), strings.Contains(code, "chain"):
|
||||
return mservice.ChainGateway
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func isUserVisibleStep(visibility orchestrationv2.ReportVisibility) bool {
|
||||
switch visibility {
|
||||
case orchestrationv2.ReportVisibility_REPORT_VISIBILITY_HIDDEN,
|
||||
|
||||
@@ -3,6 +3,7 @@ package sresponse
|
||||
import (
|
||||
"testing"
|
||||
|
||||
gatewayv1 "github.com/tech/sendico/pkg/proto/common/gateway/v1"
|
||||
orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2"
|
||||
quotationv2 "github.com/tech/sendico/pkg/proto/payments/quotation/v2"
|
||||
sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1"
|
||||
@@ -134,3 +135,64 @@ func TestToPaymentQuote_MapsIntentRef(t *testing.T) {
|
||||
t.Fatalf("intent_ref mismatch: got=%q want=%q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToPaymentOperation_MapsOperationRefAndGateway(t *testing.T) {
|
||||
op := toPaymentOperation(&orchestrationv2.StepExecution{
|
||||
StepRef: "step-1",
|
||||
StepCode: "hop.4.card_payout.send",
|
||||
State: orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_COMPLETED,
|
||||
Refs: []*orchestrationv2.ExternalReference{
|
||||
{
|
||||
Rail: gatewayv1.Rail_RAIL_CARD,
|
||||
GatewayInstanceId: "mcards",
|
||||
Kind: "operation_ref",
|
||||
Ref: "op-123",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if got, want := op.OperationRef, "op-123"; got != want {
|
||||
t.Fatalf("operation_ref mismatch: got=%q want=%q", got, want)
|
||||
}
|
||||
if got, want := op.Gateway, "mntx_gateway"; got != want {
|
||||
t.Fatalf("gateway mismatch: got=%q want=%q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToPaymentOperation_InfersGatewayFromStepCode(t *testing.T) {
|
||||
op := toPaymentOperation(&orchestrationv2.StepExecution{
|
||||
StepRef: "step-2",
|
||||
StepCode: "edge.1_2.ledger.debit",
|
||||
State: orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_COMPLETED,
|
||||
})
|
||||
|
||||
if got := op.OperationRef; got != "" {
|
||||
t.Fatalf("expected empty operation_ref, got=%q", got)
|
||||
}
|
||||
if got, want := op.Gateway, "ledger"; got != want {
|
||||
t.Fatalf("gateway mismatch: got=%q want=%q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToPaymentOperation_DoesNotFallbackToCardPayoutRef(t *testing.T) {
|
||||
op := toPaymentOperation(&orchestrationv2.StepExecution{
|
||||
StepRef: "step-3",
|
||||
StepCode: "hop.4.card_payout.send",
|
||||
State: orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_COMPLETED,
|
||||
Refs: []*orchestrationv2.ExternalReference{
|
||||
{
|
||||
Rail: gatewayv1.Rail_RAIL_CARD,
|
||||
GatewayInstanceId: "mcards",
|
||||
Kind: "card_payout_ref",
|
||||
Ref: "payout-123",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if got := op.OperationRef; got != "" {
|
||||
t.Fatalf("expected empty operation_ref, got=%q", got)
|
||||
}
|
||||
if got, want := op.Gateway, "mntx_gateway"; got != want {
|
||||
t.Fatalf("gateway mismatch: got=%q want=%q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ mcards:
|
||||
request_timeout_seconds: 15
|
||||
status_success: "success"
|
||||
status_processing: "processing"
|
||||
strict_operation_mode: true
|
||||
strict_operation_mode: false
|
||||
|
||||
gateway:
|
||||
id: "mcards"
|
||||
|
||||
@@ -397,6 +397,12 @@ components:
|
||||
label:
|
||||
description: Human-readable operation label.
|
||||
type: string
|
||||
operationRef:
|
||||
description: Internal operation reference identifier reported by the gateway.
|
||||
type: string
|
||||
gateway:
|
||||
description: Gateway microservice type handling the operation.
|
||||
type: string
|
||||
failureCode:
|
||||
description: Machine-readable failure code when operation fails.
|
||||
type: string
|
||||
|
||||
Reference in New Issue
Block a user