outbox for gateways
This commit is contained in:
168
api/payments/methods/internal/service/methods/get_private.go
Normal file
168
api/payments/methods/internal/service/methods/get_private.go
Normal file
@@ -0,0 +1,168 @@
|
||||
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]
|
||||
}
|
||||
Reference in New Issue
Block a user