From 3c0d709a82bf9062b2f110da2e9f181034f5a1a0 Mon Sep 17 00:00:00 2001 From: Stephan D Date: Wed, 11 Feb 2026 18:53:27 +0100 Subject: [PATCH] fixed bff compilation --- api/server/interface/api/sresponse/payment.go | 9 +- .../internal/server/paymentapiimp/list.go | 2 +- .../internal/server/paymentapiimp/pay.go | 2 +- .../internal/server/paymentapiimp/paybatch.go | 2 +- .../internal/server/paymentapiimp/quote.go | 7 +- .../internal/server/paymentapiimp/service.go | 161 +++++++++++++++--- ci/dev/.air.toml | 2 +- 7 files changed, 155 insertions(+), 30 deletions(-) diff --git a/api/server/interface/api/sresponse/payment.go b/api/server/interface/api/sresponse/payment.go index c545150f..6c0ed8a6 100644 --- a/api/server/interface/api/sresponse/payment.go +++ b/api/server/interface/api/sresponse/payment.go @@ -11,6 +11,7 @@ import ( paginationv1 "github.com/tech/sendico/pkg/proto/common/pagination/v1" oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1" orchestratorv1 "github.com/tech/sendico/pkg/proto/payments/orchestration/v1" + quotationv1 "github.com/tech/sendico/pkg/proto/payments/quotation/v1" sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1" ) @@ -110,7 +111,7 @@ func PaymentQuotesResponse(logger mlogger.Logger, resp *quotationv1.QuotePayment } // Payments wraps a list of payments with refreshed access token. -func PaymentsResponse(logger mlogger.Logger, payments []*orchestratorv1.Payment, token *TokenData) http.HandlerFunc { +func PaymentsResponse(logger mlogger.Logger, payments []*sharedv1.Payment, token *TokenData) http.HandlerFunc { return response.Ok(logger, paymentsResponse{ Payments: toPayments(payments), authResponse: authResponse{AccessToken: *token}, @@ -127,7 +128,7 @@ func PaymentsListResponse(logger mlogger.Logger, resp *orchestratorv1.ListPaymen } // Payment wraps a payment with refreshed access token. -func PaymentResponse(logger mlogger.Logger, payment *orchestratorv1.Payment, token *TokenData) http.HandlerFunc { +func PaymentResponse(logger mlogger.Logger, payment *sharedv1.Payment, token *TokenData) http.HandlerFunc { return response.Ok(logger, paymentResponse{ Payment: toPayment(payment), authResponse: authResponse{AccessToken: *token}, @@ -230,7 +231,7 @@ func toPaymentQuotes(resp *quotationv1.QuotePaymentsResponse) *PaymentQuotes { } } -func toPayments(items []*orchestratorv1.Payment) []Payment { +func toPayments(items []*sharedv1.Payment) []Payment { if len(items) == 0 { return nil } @@ -246,7 +247,7 @@ func toPayments(items []*orchestratorv1.Payment) []Payment { return result } -func toPayment(p *orchestratorv1.Payment) *Payment { +func toPayment(p *sharedv1.Payment) *Payment { if p == nil { return nil } diff --git a/api/server/internal/server/paymentapiimp/list.go b/api/server/internal/server/paymentapiimp/list.go index d31e6b71..d6a471cc 100644 --- a/api/server/internal/server/paymentapiimp/list.go +++ b/api/server/internal/server/paymentapiimp/list.go @@ -62,7 +62,7 @@ func (a *PaymentAPI) listPayments(r *http.Request, account *model.Account, token req.FilterStates = states } - resp, err := a.client.ListPayments(ctx, req) + resp, err := a.execution.ListPayments(ctx, req) if err != nil { a.logger.Warn("Failed to list payments", zap.Error(err), mzap.ObjRef("organization_ref", orgRef)) return response.Auto(a.logger, a.Name(), err) diff --git a/api/server/internal/server/paymentapiimp/pay.go b/api/server/internal/server/paymentapiimp/pay.go index e9c15532..762be860 100644 --- a/api/server/internal/server/paymentapiimp/pay.go +++ b/api/server/internal/server/paymentapiimp/pay.go @@ -77,7 +77,7 @@ func (a *PaymentAPI) initiatePayment(r *http.Request, account *model.Account, to Metadata: payload.Metadata, } - resp, err := a.client.InitiatePayment(ctx, req) + resp, err := a.execution.InitiatePayment(ctx, req) if err != nil { a.logger.Warn("Failed to initiate payment", zap.Error(err), mzap.ObjRef("organization_ref", orgRef)) return response.Auto(a.logger, a.Name(), err) diff --git a/api/server/internal/server/paymentapiimp/paybatch.go b/api/server/internal/server/paymentapiimp/paybatch.go index eeb36b80..dcd8cf14 100644 --- a/api/server/internal/server/paymentapiimp/paybatch.go +++ b/api/server/internal/server/paymentapiimp/paybatch.go @@ -49,7 +49,7 @@ func (a *PaymentAPI) initiatePaymentsByQuote(r *http.Request, account *model.Acc Metadata: payload.Metadata, } - resp, err := a.client.InitiatePayments(ctx, req) + resp, err := a.execution.InitiatePayments(ctx, req) if err != nil { a.logger.Warn("Failed to initiate batch payments", zap.Error(err), zap.String("organization_ref", orgRef.Hex())) return response.Auto(a.logger, a.Name(), err) diff --git a/api/server/internal/server/paymentapiimp/quote.go b/api/server/internal/server/paymentapiimp/quote.go index 88e196b7..80c59660 100644 --- a/api/server/internal/server/paymentapiimp/quote.go +++ b/api/server/internal/server/paymentapiimp/quote.go @@ -8,6 +8,7 @@ import ( "github.com/tech/sendico/pkg/api/http/response" "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/model" + quotationv1 "github.com/tech/sendico/pkg/proto/payments/quotation/v1" sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1" "github.com/tech/sendico/server/interface/api/srequest" "github.com/tech/sendico/server/interface/api/sresponse" @@ -59,7 +60,7 @@ func (a *PaymentAPI) quotePayment(r *http.Request, account *model.Account, token Intent: intent, } - resp, err := a.client.QuotePayment(ctx, req) + resp, err := a.quotation.QuotePayment(ctx, req) if err != nil { a.logger.Warn("Failed to quote payment", zap.Error(err), zap.String("organization_ref", orgRef.Hex())) return response.Auto(a.logger, a.Name(), err) @@ -107,7 +108,7 @@ func (a *PaymentAPI) quotePayments(r *http.Request, account *model.Account, toke intents = append(intents, intent) } - req := "ationv1.QuotePaymentResponse{ + req := "ationv1.QuotePaymentsRequest{ Meta: &sharedv1.RequestMeta{ OrganizationRef: orgRef.Hex(), }, @@ -116,7 +117,7 @@ func (a *PaymentAPI) quotePayments(r *http.Request, account *model.Account, toke PreviewOnly: payload.PreviewOnly, } - resp, err := a.client.QuotePayments(ctx, req) + resp, err := a.quotation.QuotePayments(ctx, req) if err != nil { a.logger.Warn("Failed to quote payments", zap.Error(err), zap.String("organization_ref", orgRef.Hex())) return response.Auto(a.logger, a.Name(), err) diff --git a/api/server/internal/server/paymentapiimp/service.go b/api/server/internal/server/paymentapiimp/service.go index b87c9344..7edab491 100644 --- a/api/server/internal/server/paymentapiimp/service.go +++ b/api/server/internal/server/paymentapiimp/service.go @@ -2,6 +2,7 @@ package paymentapiimp import ( "context" + "crypto/tls" "fmt" "os" "strings" @@ -18,24 +19,33 @@ import ( "github.com/tech/sendico/pkg/mlogger" "github.com/tech/sendico/pkg/mservice" orchestratorv1 "github.com/tech/sendico/pkg/proto/payments/orchestration/v1" + quotationv1 "github.com/tech/sendico/pkg/proto/payments/quotation/v1" eapi "github.com/tech/sendico/server/interface/api" mutil "github.com/tech/sendico/server/internal/mutil/param" "go.mongodb.org/mongo-driver/v2/bson" "go.uber.org/zap" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" ) -type paymentClient interface { - QuotePayment(ctx context.Context, req *quotationv1.QuotePaymentRequest) (*quotationv1.QuotePaymentResponse, error) - QuotePayments(ctx context.Context, req *quotationv1.QuotePaymentRequest) (*quotationv1.QuotePaymentsResponse, error) +type executionClient interface { InitiatePayments(ctx context.Context, req *orchestratorv1.InitiatePaymentsRequest) (*orchestratorv1.InitiatePaymentsResponse, error) InitiatePayment(ctx context.Context, req *orchestratorv1.InitiatePaymentRequest) (*orchestratorv1.InitiatePaymentResponse, error) ListPayments(ctx context.Context, req *orchestratorv1.ListPaymentsRequest) (*orchestratorv1.ListPaymentsResponse, error) Close() error } +type quotationClient interface { + QuotePayment(ctx context.Context, req *quotationv1.QuotePaymentRequest) (*quotationv1.QuotePaymentResponse, error) + QuotePayments(ctx context.Context, req *quotationv1.QuotePaymentsRequest) (*quotationv1.QuotePaymentsResponse, error) + Close() error +} + type PaymentAPI struct { logger mlogger.Logger - client paymentClient + execution executionClient + quotation quotationClient enf auth.Enforcer oph mutil.ParamHelper discovery *discovery.Client @@ -49,11 +59,16 @@ type PaymentAPI struct { func (a *PaymentAPI) Name() mservice.Type { return mservice.Payments } func (a *PaymentAPI) Finish(ctx context.Context) error { - if a.client != nil { - if err := a.client.Close(); err != nil { + if a.execution != nil { + if err := a.execution.Close(); err != nil { a.logger.Warn("Failed to close payment orchestrator client", zap.Error(err)) } } + if a.quotation != nil { + if err := a.quotation.Close(); err != nil { + a.logger.Warn("Failed to close payment quotation client", zap.Error(err)) + } + } if a.discovery != nil { a.discovery.Close() } @@ -103,15 +118,15 @@ func (a *PaymentAPI) initPaymentClient(cfg *eapi.PaymentOrchestratorConfig, quot return merrors.InvalidArgument("payment orchestrator configuration is not provided") } - address := strings.TrimSpace(cfg.Address) - if address == "" { - address = strings.TrimSpace(os.Getenv(cfg.AddressEnv)) - } - if address == "" { - return merrors.InvalidArgument(fmt.Sprintf("payment orchestrator address is not specified and address env %s is empty", cfg.AddressEnv)) + address, err := resolveClientAddress("payment orchestrator", cfg) + if err != nil { + return err } quoteAddress := address + quoteInsecure := cfg.Insecure + quoteDialTimeout := cfg.DialTimeoutSeconds + quoteCallTimeout := cfg.CallTimeoutSeconds if quoteCfg != nil { if addr := strings.TrimSpace(quoteCfg.Address); addr != "" { quoteAddress = addr @@ -120,25 +135,133 @@ func (a *PaymentAPI) initPaymentClient(cfg *eapi.PaymentOrchestratorConfig, quot quoteAddress = resolved } } + quoteInsecure = quoteCfg.Insecure + quoteDialTimeout = quoteCfg.DialTimeoutSeconds + quoteCallTimeout = quoteCfg.CallTimeoutSeconds } clientCfg := orchestratorclient.Config{ - Address: address, - QuoteAddress: quoteAddress, - DialTimeout: time.Duration(cfg.DialTimeoutSeconds) * time.Second, - CallTimeout: time.Duration(cfg.CallTimeoutSeconds) * time.Second, - Insecure: cfg.Insecure, + Address: address, + DialTimeout: time.Duration(cfg.DialTimeoutSeconds) * time.Second, + CallTimeout: time.Duration(cfg.CallTimeoutSeconds) * time.Second, + Insecure: cfg.Insecure, } - client, err := orchestratorclient.New(context.Background(), clientCfg) + execution, err := orchestratorclient.New(context.Background(), clientCfg) if err != nil { return err } - a.client = client + quotation, err := newQuotationClient(context.Background(), quotationClientConfig{ + Address: quoteAddress, + DialTimeout: time.Duration(quoteDialTimeout) * time.Second, + CallTimeout: time.Duration(quoteCallTimeout) * time.Second, + Insecure: quoteInsecure, + }) + if err != nil { + _ = execution.Close() + return err + } + + a.execution = execution + a.quotation = quotation return nil } +func resolveClientAddress(service string, cfg *eapi.PaymentOrchestratorConfig) (string, error) { + if cfg == nil { + return "", merrors.InvalidArgument(strings.TrimSpace(service) + " configuration is not provided") + } + address := strings.TrimSpace(cfg.Address) + if address != "" { + return address, nil + } + if env := strings.TrimSpace(cfg.AddressEnv); env != "" { + if resolved := strings.TrimSpace(os.Getenv(env)); resolved != "" { + return resolved, nil + } + return "", merrors.InvalidArgument(fmt.Sprintf("%s address is not specified and address env %s is empty", strings.TrimSpace(service), env)) + } + return "", merrors.InvalidArgument(strings.TrimSpace(service) + " address is not specified") +} + +type quotationClientConfig struct { + Address string + DialTimeout time.Duration + CallTimeout time.Duration + Insecure bool +} + +func (c *quotationClientConfig) setDefaults() { + if c.DialTimeout <= 0 { + c.DialTimeout = 5 * time.Second + } + if c.CallTimeout <= 0 { + c.CallTimeout = 3 * time.Second + } +} + +type grpcQuotationClient struct { + conn *grpc.ClientConn + client quotationv1.QuotationServiceClient + callTimeout time.Duration +} + +func newQuotationClient(ctx context.Context, cfg quotationClientConfig, opts ...grpc.DialOption) (quotationClient, error) { + cfg.setDefaults() + if strings.TrimSpace(cfg.Address) == "" { + return nil, merrors.InvalidArgument("payment quotation: address is required") + } + + dialCtx, cancel := context.WithTimeout(ctx, cfg.DialTimeout) + defer cancel() + + dialOpts := make([]grpc.DialOption, 0, len(opts)+1) + dialOpts = append(dialOpts, opts...) + if cfg.Insecure { + dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials())) + } else { + dialOpts = append(dialOpts, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{}))) + } + + conn, err := grpc.DialContext(dialCtx, cfg.Address, dialOpts...) + if err != nil { + return nil, merrors.InternalWrap(err, fmt.Sprintf("payment-quotation: dial %s", cfg.Address)) + } + return &grpcQuotationClient{ + conn: conn, + client: quotationv1.NewQuotationServiceClient(conn), + callTimeout: cfg.CallTimeout, + }, nil +} + +func (c *grpcQuotationClient) Close() error { + if c == nil || c.conn == nil { + return nil + } + return c.conn.Close() +} + +func (c *grpcQuotationClient) QuotePayment(ctx context.Context, req *quotationv1.QuotePaymentRequest) (*quotationv1.QuotePaymentResponse, error) { + callCtx, cancel := c.callContext(ctx) + defer cancel() + return c.client.QuotePayment(callCtx, req) +} + +func (c *grpcQuotationClient) QuotePayments(ctx context.Context, req *quotationv1.QuotePaymentsRequest) (*quotationv1.QuotePaymentsResponse, error) { + callCtx, cancel := c.callContext(ctx) + defer cancel() + return c.client.QuotePayments(callCtx, req) +} + +func (c *grpcQuotationClient) callContext(ctx context.Context) (context.Context, context.CancelFunc) { + timeout := c.callTimeout + if timeout <= 0 { + timeout = 3 * time.Second + } + return context.WithTimeout(ctx, timeout) +} + func (a *PaymentAPI) initDiscoveryClient(cfg *eapi.Config) error { if cfg == nil || cfg.Mw == nil { return nil diff --git a/ci/dev/.air.toml b/ci/dev/.air.toml index c607b2c3..51751621 100644 --- a/ci/dev/.air.toml +++ b/ci/dev/.air.toml @@ -25,7 +25,7 @@ tmp_dir = "tmp" rerun = false rerun_delay = 500 send_interrupt = false - stop_on_error = false + stop_on_error = true [color] app = ""