Files
sendico/api/payments/methods/internal/service/methods/get_private.go
2026-02-18 01:35:28 +01:00

169 lines
5.2 KiB
Go

package methods
import (
"context"
"github.com/tech/sendico/pkg/merrors"
pkgmodel "github.com/tech/sendico/pkg/model"
methodsv1 "github.com/tech/sendico/pkg/proto/payments/methods/v1"
"go.mongodb.org/mongo-driver/v2/bson"
)
const (
paymentTypeAccount pkgmodel.PaymentType = 8
maxPrivateMethodResolutionDepth = 8
)
func (s *Service) GetPaymentMethodPrivate(ctx context.Context, req *methodsv1.GetPaymentMethodPrivateRequest) (*methodsv1.GetPaymentMethodPrivateResponse, error) {
if req == nil {
return autoError[methodsv1.GetPaymentMethodPrivateResponse](ctx, s.logger, merrors.InvalidArgument("request is required"))
}
if s.pmstore == nil {
return autoError[methodsv1.GetPaymentMethodPrivateResponse](ctx, s.logger, errStoreUnavailable)
}
if req.GetEndpoint() == methodsv1.PrivateEndpoint_PRIVATE_ENDPOINT_UNSPECIFIED {
return autoError[methodsv1.GetPaymentMethodPrivateResponse](ctx, s.logger, merrors.InvalidArgument("endpoint is required", "endpoint"))
}
organizationRef, err := parseObjectID(req.GetOrganizationRef(), "organization_ref")
if err != nil {
return autoError[methodsv1.GetPaymentMethodPrivateResponse](ctx, s.logger, err)
}
var resolved *pkgmodel.PaymentMethod
switch req.GetSelector().(type) {
case *methodsv1.GetPaymentMethodPrivateRequest_PaymentMethodRef:
methodRef, err := parseObjectID(req.GetPaymentMethodRef(), "payment_method_ref")
if err != nil {
return autoError[methodsv1.GetPaymentMethodPrivateResponse](ctx, s.logger, err)
}
resolved, err = s.resolvePrivateByMethodRef(ctx, organizationRef, methodRef, req.GetEndpoint(), 0)
if err != nil {
return autoError[methodsv1.GetPaymentMethodPrivateResponse](ctx, s.logger, err)
}
case *methodsv1.GetPaymentMethodPrivateRequest_PayeeRef:
payeeRef, err := parseObjectID(req.GetPayeeRef(), "payee_ref")
if err != nil {
return autoError[methodsv1.GetPaymentMethodPrivateResponse](ctx, s.logger, err)
}
resolved, err = s.resolvePrivateByRecipientRef(ctx, organizationRef, payeeRef, req.GetEndpoint(), 0)
if err != nil {
return autoError[methodsv1.GetPaymentMethodPrivateResponse](ctx, s.logger, err)
}
default:
return autoError[methodsv1.GetPaymentMethodPrivateResponse](ctx, s.logger, merrors.InvalidArgument(
"selector must include payment_method_ref or payee_ref",
"selector",
))
}
record, err := encodePaymentMethodRecord(resolved)
if err != nil {
return autoError[methodsv1.GetPaymentMethodPrivateResponse](ctx, s.logger, err)
}
return &methodsv1.GetPaymentMethodPrivateResponse{
PaymentMethodRecord: record,
}, nil
}
func (s *Service) resolvePrivateByMethodRef(
ctx context.Context,
organizationRef bson.ObjectID,
methodRef bson.ObjectID,
endpoint methodsv1.PrivateEndpoint,
depth int,
) (*pkgmodel.PaymentMethod, error) {
method, err := s.pmstore.GetPrivate(ctx, methodRef)
if err != nil {
return nil, err
}
return s.resolvePrivateMethod(ctx, organizationRef, method, endpoint, depth)
}
func (s *Service) resolvePrivateByRecipientRef(
ctx context.Context,
organizationRef bson.ObjectID,
recipientRef bson.ObjectID,
endpoint methodsv1.PrivateEndpoint,
depth int,
) (*pkgmodel.PaymentMethod, error) {
items, err := s.pmstore.ListPrivate(ctx, organizationRef, recipientRef, nil)
if err != nil {
return nil, err
}
if len(items) == 0 {
return nil, merrors.InvalidArgument("no payment methods available for recipient")
}
selected := pickPreferredPrivateMethod(items, endpoint)
if selected == nil {
return nil, merrors.InvalidArgument("no routable payment methods available for recipient")
}
return s.resolvePrivateMethod(ctx, organizationRef, selected, endpoint, depth)
}
func (s *Service) resolvePrivateMethod(
ctx context.Context,
organizationRef bson.ObjectID,
method *pkgmodel.PaymentMethod,
endpoint methodsv1.PrivateEndpoint,
depth int,
) (*pkgmodel.PaymentMethod, error) {
if method == nil {
return nil, merrors.InvalidArgument("payment method is required")
}
if depth >= maxPrivateMethodResolutionDepth {
return nil, merrors.InvalidArgument("payment method resolution depth exceeded")
}
if methodIsAccount(method) {
if method.RecipientRef.IsZero() {
return nil, merrors.InvalidArgument("account payment method recipient_ref is required")
}
return s.resolvePrivateByRecipientRef(ctx, organizationRef, method.RecipientRef, endpoint, depth+1)
}
return method, nil
}
func methodIsAccount(method *pkgmodel.PaymentMethod) bool {
if method == nil {
return false
}
return method.Type == paymentTypeAccount
}
func pickPreferredPrivateMethod(items []pkgmodel.PaymentMethod, endpoint methodsv1.PrivateEndpoint) *pkgmodel.PaymentMethod {
switch endpoint {
case methodsv1.PrivateEndpoint_PRIVATE_ENDPOINT_SOURCE:
return pickMainThenAnyNonAccount(items)
case methodsv1.PrivateEndpoint_PRIVATE_ENDPOINT_DESTINATION:
return pickMainThenAnyNonAccount(items)
default:
return nil
}
}
func pickMainThenAnyNonAccount(items []pkgmodel.PaymentMethod) *pkgmodel.PaymentMethod {
for i := range items {
if items[i].IsMain && !methodIsAccount(&items[i]) {
return &items[i]
}
}
for i := range items {
if !methodIsAccount(&items[i]) {
return &items[i]
}
}
for i := range items {
if items[i].IsMain {
return &items[i]
}
}
if len(items) == 0 {
return nil
}
return &items[0]
}