169 lines
5.2 KiB
Go
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 int = 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]
|
|
}
|