docs format updated
This commit is contained in:
@@ -16,6 +16,8 @@ import (
|
||||
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||
documentsv1 "github.com/tech/sendico/pkg/proto/billing/documents/v1"
|
||||
connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1"
|
||||
endpointv1 "github.com/tech/sendico/pkg/proto/payments/endpoint/v1"
|
||||
orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2"
|
||||
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||
mutil "github.com/tech/sendico/server/internal/mutil/param"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
@@ -24,7 +26,6 @@ import (
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -61,6 +62,10 @@ func (a *PaymentAPI) getOperationDocument(r *http.Request, account *model.Accoun
|
||||
if operationRef == "" {
|
||||
return response.BadRequest(a.logger, a.Name(), "missing_parameter", "operation_ref is required")
|
||||
}
|
||||
paymentRef := strings.TrimSpace(query.Get("payment_ref"))
|
||||
if paymentRef == "" {
|
||||
return response.BadRequest(a.logger, a.Name(), "missing_parameter", "payment_ref is required")
|
||||
}
|
||||
|
||||
service, gateway, h := a.resolveOperationDocumentDeps(r.Context(), gatewayService)
|
||||
if h != nil {
|
||||
@@ -73,7 +78,18 @@ func (a *PaymentAPI) getOperationDocument(r *http.Request, account *model.Accoun
|
||||
return documentErrorResponse(a.logger, a.Name(), err)
|
||||
}
|
||||
|
||||
req := operationDocumentRequest(orgRef.Hex(), gatewayService, operationRef, op)
|
||||
req := operationDocumentRequest(orgRef.Hex(), gatewayService, operationRef, paymentRef, op)
|
||||
if payment, paymentErr := a.fetchPayment(r.Context(), orgRef.Hex(), paymentRef); paymentErr != nil {
|
||||
a.logger.Warn(
|
||||
"Failed to fetch payment snapshot for operation document",
|
||||
zap.Error(paymentErr),
|
||||
mzap.ObjRef("organization_ref", orgRef),
|
||||
zap.String("payment_ref", paymentRef),
|
||||
zap.String("operation_ref", operationRef),
|
||||
)
|
||||
} else {
|
||||
enrichOperationDocumentRequestFromPayment(req, payment)
|
||||
}
|
||||
|
||||
docResp, err := a.fetchOperationDocument(r.Context(), service.InvokeURI, req)
|
||||
if err != nil {
|
||||
@@ -263,6 +279,28 @@ func (a *PaymentAPI) fetchGatewayOperation(ctx context.Context, invokeURI, opera
|
||||
return op, nil
|
||||
}
|
||||
|
||||
func (a *PaymentAPI) fetchPayment(ctx context.Context, organizationRef, paymentRef string) (*orchestrationv2.Payment, error) {
|
||||
if a.execution == nil {
|
||||
return nil, merrors.Internal("payment execution client is not configured")
|
||||
}
|
||||
|
||||
resp, err := a.execution.GetPayment(ctx, &orchestrationv2.GetPaymentRequest{
|
||||
Meta: requestMeta(organizationRef, ""),
|
||||
PaymentRef: strings.TrimSpace(paymentRef),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp == nil {
|
||||
return nil, merrors.NoData("payment orchestrator returned empty response")
|
||||
}
|
||||
if resp.GetPayment() == nil {
|
||||
return nil, merrors.NoData("payment orchestrator returned empty payment")
|
||||
}
|
||||
|
||||
return resp.GetPayment(), nil
|
||||
}
|
||||
|
||||
func findGatewayForService(gateways []discovery.GatewaySummary, gatewayService mservice.Type) *discovery.GatewaySummary {
|
||||
candidates := make([]discovery.GatewaySummary, 0, len(gateways))
|
||||
for _, gw := range gateways {
|
||||
@@ -313,13 +351,14 @@ func findGatewayForService(gateways []discovery.GatewaySummary, gatewayService m
|
||||
return &best
|
||||
}
|
||||
|
||||
func operationDocumentRequest(organizationRef string, gatewayService mservice.Type, requestedOperationRef string, op *connectorv1.Operation) *documentsv1.GetOperationDocumentRequest {
|
||||
func operationDocumentRequest(organizationRef string, gatewayService mservice.Type, requestedOperationRef string, paymentRef string, op *connectorv1.Operation) *documentsv1.GetOperationDocumentRequest {
|
||||
req := &documentsv1.GetOperationDocumentRequest{
|
||||
OrganizationRef: strings.TrimSpace(organizationRef),
|
||||
GatewayService: gatewayService,
|
||||
OperationRef: firstNonEmpty(strings.TrimSpace(op.GetOperationRef()), strings.TrimSpace(requestedOperationRef)),
|
||||
PaymentRef: strings.TrimSpace(paymentRef),
|
||||
OperationCode: strings.TrimSpace(op.GetType().String()),
|
||||
OperationLabel: operationLabel(op.GetType()),
|
||||
OperationLabel: strings.TrimSpace(op.GetType().String()),
|
||||
OperationState: strings.TrimSpace(op.GetStatus().String()),
|
||||
Amount: strings.TrimSpace(op.GetMoney().GetAmount()),
|
||||
Currency: strings.TrimSpace(op.GetMoney().GetCurrency()),
|
||||
@@ -332,63 +371,94 @@ func operationDocumentRequest(organizationRef string, gatewayService mservice.Ty
|
||||
req.CompletedAtUnixMs = ts.AsTime().UnixMilli()
|
||||
}
|
||||
|
||||
req.PaymentRef = operationParamValue(op.GetParams(), "payment_ref", "parent_payment_ref", "paymentRef", "parentPaymentRef")
|
||||
req.FailureCode = firstNonEmpty(
|
||||
operationParamValue(op.GetParams(), "failure_code", "provider_code", "error_code"),
|
||||
failureCodeFromStatus(op.GetStatus()),
|
||||
)
|
||||
req.FailureReason = operationParamValue(op.GetParams(), "failure_reason", "provider_message", "error", "message")
|
||||
if isFailedOperationStatus(op.GetStatus()) {
|
||||
req.FailureCode = strings.TrimSpace(op.GetStatus().String())
|
||||
}
|
||||
|
||||
return req
|
||||
}
|
||||
|
||||
func operationLabel(opType connectorv1.OperationType) string {
|
||||
switch opType {
|
||||
case connectorv1.OperationType_CREDIT:
|
||||
return "Credit"
|
||||
case connectorv1.OperationType_DEBIT:
|
||||
return "Debit"
|
||||
case connectorv1.OperationType_TRANSFER:
|
||||
return "Transfer"
|
||||
case connectorv1.OperationType_PAYOUT:
|
||||
return "Payout"
|
||||
case connectorv1.OperationType_FEE_ESTIMATE:
|
||||
return "Fee Estimate"
|
||||
case connectorv1.OperationType_FX:
|
||||
return "FX"
|
||||
case connectorv1.OperationType_GAS_TOPUP:
|
||||
return "Gas Top Up"
|
||||
default:
|
||||
return strings.TrimSpace(opType.String())
|
||||
}
|
||||
func isFailedOperationStatus(status connectorv1.OperationStatus) bool {
|
||||
return status == connectorv1.OperationStatus_OPERATION_FAILED || status == connectorv1.OperationStatus_OPERATION_CANCELLED
|
||||
}
|
||||
|
||||
func failureCodeFromStatus(status connectorv1.OperationStatus) string {
|
||||
switch status {
|
||||
case connectorv1.OperationStatus_OPERATION_FAILED, connectorv1.OperationStatus_OPERATION_CANCELLED:
|
||||
return strings.TrimSpace(status.String())
|
||||
default:
|
||||
return ""
|
||||
func enrichOperationDocumentRequestFromPayment(req *documentsv1.GetOperationDocumentRequest, payment *orchestrationv2.Payment) {
|
||||
if req == nil || payment == nil {
|
||||
return
|
||||
}
|
||||
|
||||
req.PaymentRef = firstNonEmpty(strings.TrimSpace(req.GetPaymentRef()), strings.TrimSpace(payment.GetPaymentRef()))
|
||||
req.ClientName = firstNonEmpty(strings.TrimSpace(req.GetClientName()), paymentClientName(payment))
|
||||
}
|
||||
|
||||
func operationParamValue(params *structpb.Struct, keys ...string) string {
|
||||
if params == nil {
|
||||
func paymentClientName(payment *orchestrationv2.Payment) string {
|
||||
if payment == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
values := params.AsMap()
|
||||
for _, key := range keys {
|
||||
raw, ok := values[key]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if text := strings.TrimSpace(fmt.Sprint(raw)); text != "" && text != "<nil>" {
|
||||
return text
|
||||
}
|
||||
intent := payment.GetIntentSnapshot()
|
||||
if intent == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return ""
|
||||
if customerDescription := strings.TrimSpace(intent.GetComment()); customerDescription != "" {
|
||||
return customerDescription
|
||||
}
|
||||
|
||||
return firstNonEmpty(
|
||||
paymentEndpointClientName(intent.GetDestination()),
|
||||
paymentEndpointClientName(intent.GetSource()),
|
||||
)
|
||||
}
|
||||
|
||||
func paymentEndpointClientName(endpoint *endpointv1.PaymentEndpoint) string {
|
||||
if endpoint == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
method := endpoint.GetPaymentMethod()
|
||||
if method == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
switch method.GetType() {
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CARD:
|
||||
type cardMethodData struct {
|
||||
FirstName string `bson:"firstName"`
|
||||
LastName string `bson:"lastName"`
|
||||
}
|
||||
var payload cardMethodData
|
||||
if err := bson.Unmarshal(method.GetData(), &payload); err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(strings.Join([]string{
|
||||
strings.TrimSpace(payload.FirstName),
|
||||
strings.TrimSpace(payload.LastName),
|
||||
}, " "))
|
||||
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_BANK_ACCOUNT:
|
||||
type bankMethodData struct {
|
||||
RecipientName string `bson:"recipientName"`
|
||||
}
|
||||
var payload bankMethodData
|
||||
if err := bson.Unmarshal(method.GetData(), &payload); err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(payload.RecipientName)
|
||||
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_IBAN:
|
||||
type ibanMethodData struct {
|
||||
AccountHolder string `bson:"accountHolder"`
|
||||
}
|
||||
var payload ibanMethodData
|
||||
if err := bson.Unmarshal(method.GetData(), &payload); err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(payload.AccountHolder)
|
||||
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func findDocumentsService(services []discovery.ServiceSummary) *discovery.ServiceSummary {
|
||||
|
||||
Reference in New Issue
Block a user