added gateway and operation references #635
@@ -8,8 +8,10 @@ import (
|
|||||||
|
|
||||||
"github.com/tech/sendico/pkg/api/http/response"
|
"github.com/tech/sendico/pkg/api/http/response"
|
||||||
"github.com/tech/sendico/pkg/mlogger"
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
|
"github.com/tech/sendico/pkg/mservice"
|
||||||
paymenttypes "github.com/tech/sendico/pkg/payments/types"
|
paymenttypes "github.com/tech/sendico/pkg/payments/types"
|
||||||
feesv1 "github.com/tech/sendico/pkg/proto/billing/fees/v1"
|
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"
|
paginationv1 "github.com/tech/sendico/pkg/proto/common/pagination/v1"
|
||||||
oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1"
|
oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1"
|
||||||
orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2"
|
orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2"
|
||||||
@@ -81,6 +83,8 @@ type PaymentOperation struct {
|
|||||||
Code string `json:"code,omitempty"`
|
Code string `json:"code,omitempty"`
|
||||||
State string `json:"state,omitempty"`
|
State string `json:"state,omitempty"`
|
||||||
Label string `json:"label,omitempty"`
|
Label string `json:"label,omitempty"`
|
||||||
|
OperationRef string `json:"operationRef,omitempty"`
|
||||||
|
Gateway string `json:"gateway,omitempty"`
|
||||||
FailureCode string `json:"failureCode,omitempty"`
|
FailureCode string `json:"failureCode,omitempty"`
|
||||||
FailureReason string `json:"failureReason,omitempty"`
|
FailureReason string `json:"failureReason,omitempty"`
|
||||||
StartedAt time.Time `json:"startedAt,omitempty"`
|
StartedAt time.Time `json:"startedAt,omitempty"`
|
||||||
@@ -326,13 +330,16 @@ func toUserVisibleOperations(steps []*orchestrationv2.StepExecution) []PaymentOp
|
|||||||
}
|
}
|
||||||
|
|
||||||
func toPaymentOperation(step *orchestrationv2.StepExecution) PaymentOperation {
|
func toPaymentOperation(step *orchestrationv2.StepExecution) PaymentOperation {
|
||||||
|
operationRef, gateway := operationRefAndGateway(step.GetStepCode(), step.GetRefs())
|
||||||
op := PaymentOperation{
|
op := PaymentOperation{
|
||||||
StepRef: step.GetStepRef(),
|
StepRef: step.GetStepRef(),
|
||||||
Code: step.GetStepCode(),
|
Code: step.GetStepCode(),
|
||||||
State: enumJSONName(step.GetState().String()),
|
State: enumJSONName(step.GetState().String()),
|
||||||
Label: strings.TrimSpace(step.GetUserLabel()),
|
Label: strings.TrimSpace(step.GetUserLabel()),
|
||||||
StartedAt: timestampAsTime(step.GetStartedAt()),
|
OperationRef: operationRef,
|
||||||
CompletedAt: timestampAsTime(step.GetCompletedAt()),
|
Gateway: string(gateway),
|
||||||
|
StartedAt: timestampAsTime(step.GetStartedAt()),
|
||||||
|
CompletedAt: timestampAsTime(step.GetCompletedAt()),
|
||||||
}
|
}
|
||||||
failure := step.GetFailure()
|
failure := step.GetFailure()
|
||||||
if failure == nil {
|
if failure == nil {
|
||||||
@@ -346,6 +353,117 @@ func toPaymentOperation(step *orchestrationv2.StepExecution) PaymentOperation {
|
|||||||
return op
|
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 {
|
func isUserVisibleStep(visibility orchestrationv2.ReportVisibility) bool {
|
||||||
switch visibility {
|
switch visibility {
|
||||||
case orchestrationv2.ReportVisibility_REPORT_VISIBILITY_HIDDEN,
|
case orchestrationv2.ReportVisibility_REPORT_VISIBILITY_HIDDEN,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package sresponse
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
gatewayv1 "github.com/tech/sendico/pkg/proto/common/gateway/v1"
|
||||||
orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2"
|
orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2"
|
||||||
quotationv2 "github.com/tech/sendico/pkg/proto/payments/quotation/v2"
|
quotationv2 "github.com/tech/sendico/pkg/proto/payments/quotation/v2"
|
||||||
sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1"
|
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)
|
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
|
request_timeout_seconds: 15
|
||||||
status_success: "success"
|
status_success: "success"
|
||||||
status_processing: "processing"
|
status_processing: "processing"
|
||||||
strict_operation_mode: true
|
strict_operation_mode: false
|
||||||
|
|
||||||
gateway:
|
gateway:
|
||||||
id: "mcards"
|
id: "mcards"
|
||||||
|
|||||||
@@ -397,6 +397,12 @@ components:
|
|||||||
label:
|
label:
|
||||||
description: Human-readable operation label.
|
description: Human-readable operation label.
|
||||||
type: string
|
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:
|
failureCode:
|
||||||
description: Machine-readable failure code when operation fails.
|
description: Machine-readable failure code when operation fails.
|
||||||
type: string
|
type: string
|
||||||
|
|||||||
Reference in New Issue
Block a user