new payment by id method + shell scripts update + mntx removed

This commit is contained in:
Stephan D
2026-03-16 15:43:36 +01:00
parent 23ae8a8089
commit 2fcb150fbf
36 changed files with 348 additions and 26 deletions

View File

@@ -293,7 +293,7 @@ func TestToPaymentOperation_MapsOperationRefAndGateway(t *testing.T) {
if got, want := op.OperationRef, "op-123"; got != want {
t.Fatalf("operation_ref mismatch: got=%q want=%q", got, want)
}
if got, want := op.Gateway, "mntx_gateway"; got != want {
if got, want := op.Gateway, "mcards_gateway"; got != want {
t.Fatalf("gateway mismatch: got=%q want=%q", got, want)
}
}
@@ -331,7 +331,7 @@ func TestToPaymentOperation_DoesNotFallbackToCardPayoutRef(t *testing.T) {
if got := op.OperationRef; got != "" {
t.Fatalf("expected empty operation_ref, got=%q", got)
}
if got, want := op.Gateway, "mntx_gateway"; got != want {
if got, want := op.Gateway, "mcards_gateway"; got != want {
t.Fatalf("gateway mismatch: got=%q want=%q", got, want)
}
}

View File

@@ -0,0 +1,52 @@
package paymentapiimp
import (
"net/http"
"strings"
"github.com/tech/sendico/pkg/api/http/response"
"github.com/tech/sendico/pkg/merrors"
"github.com/tech/sendico/pkg/model"
"github.com/tech/sendico/pkg/mutil/mzap"
orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2"
"github.com/tech/sendico/server/interface/api/sresponse"
"go.mongodb.org/mongo-driver/v2/bson"
"go.uber.org/zap"
)
func (a *PaymentAPI) getPayment(r *http.Request, account *model.Account, token *sresponse.TokenData) http.HandlerFunc {
paymentRef := strings.TrimSpace(a.pph.GetID(r))
if paymentRef == "" {
return response.BadReference(a.logger, a.Name(), a.pph.Name(), a.pph.GetID(r), merrors.InvalidArgument("payment reference is required"))
}
resp, err := a.execution.GetPayment(r.Context(), &orchestrationv2.GetPaymentRequest{
PaymentRef: paymentRef,
})
if err != nil {
a.logger.Warn("Failed to fetch payment", zap.Error(err), zap.String("payment_ref", paymentRef))
return grpcErrorResponse(a.logger, a.Name(), err)
}
if resp == nil || resp.GetPayment() == nil {
return response.Auto(a.logger, a.Name(), merrors.NoData("payment not found"))
}
orgRefRaw := strings.TrimSpace(resp.GetOrganizationRef())
orgRef, err := bson.ObjectIDFromHex(orgRefRaw)
if err != nil {
a.logger.Warn("Payment lookup returned invalid organization reference", zap.Error(err), zap.String("organization_ref", orgRefRaw), zap.String("payment_ref", paymentRef))
return response.Internal(a.logger, a.Name(), merrors.DataConflict("payment lookup returned invalid organization reference"))
}
allowed, err := a.enf.Enforce(r.Context(), a.permissionRef, account.ID, orgRef, bson.NilObjectID, model.ActionRead)
if err != nil {
a.logger.Warn("Failed to check payment access permissions", zap.Error(err), mzap.ObjRef("organization_ref", orgRef), zap.String("payment_ref", paymentRef))
return response.Auto(a.logger, a.Name(), err)
}
if !allowed {
a.logger.Debug("Payment access denied, hiding existence", mzap.ObjRef("organization_ref", orgRef), zap.String("payment_ref", paymentRef))
return response.NotFound(a.logger, a.Name(), "payment not found")
}
return sresponse.PaymentResponse(a.logger, resp.GetPayment(), token)
}

View File

@@ -0,0 +1,119 @@
package paymentapiimp
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/go-chi/chi/v5"
"github.com/tech/sendico/pkg/auth"
"github.com/tech/sendico/pkg/model"
"github.com/tech/sendico/pkg/mservice"
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"
"go.uber.org/zap"
)
func TestGetPayment_FetchesByPaymentRefWithoutOrganizationPath(t *testing.T) {
orgRef := bson.NewObjectID()
paymentRef := "payment-123"
exec := &fakeExecutionClientForGet{
getPaymentResp: &orchestrationv2.GetPaymentResponse{
OrganizationRef: orgRef.Hex(),
Payment: &orchestrationv2.Payment{
PaymentRef: paymentRef,
},
},
}
enf := &capturingPaymentEnforcer{allowed: true}
api := &PaymentAPI{
logger: zap.NewNop(),
execution: exec,
enf: enf,
oph: mutil.CreatePH(mservice.Organizations),
pph: mutil.CreatePH(mservice.Payments),
permissionRef: bson.NewObjectID(),
}
req := httptest.NewRequestWithContext(context.Background(), http.MethodGet, "/object/"+paymentRef, nil)
routeCtx := chi.NewRouteContext()
routeCtx.URLParams.Add("payments_ref", paymentRef)
req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, routeCtx))
account := &model.Account{}
account.ID = bson.NewObjectID()
rr := httptest.NewRecorder()
handler := api.getPayment(req, account, &sresponse.TokenData{
Token: "token",
Expiration: time.Now().UTC().Add(time.Hour),
})
handler.ServeHTTP(rr, req)
if got, want := rr.Code, http.StatusOK; got != want {
t.Fatalf("status mismatch: got=%d want=%d body=%s", got, want, rr.Body.String())
}
if exec.getPaymentReq == nil {
t.Fatal("expected GetPayment request")
}
if got, want := exec.getPaymentReq.GetPaymentRef(), paymentRef; got != want {
t.Fatalf("payment_ref mismatch: got=%q want=%q", got, want)
}
if exec.getPaymentReq.GetMeta() != nil {
t.Fatalf("expected no organization metadata in request, got=%+v", exec.getPaymentReq.GetMeta())
}
if got, want := enf.organizationRef, orgRef; got != want {
t.Fatalf("permission organization_ref mismatch: got=%s want=%s", got.Hex(), want.Hex())
}
}
type fakeExecutionClientForGet struct {
getPaymentReq *orchestrationv2.GetPaymentRequest
getPaymentResp *orchestrationv2.GetPaymentResponse
}
func (*fakeExecutionClientForGet) ExecutePayment(context.Context, *orchestrationv2.ExecutePaymentRequest) (*orchestrationv2.ExecutePaymentResponse, error) {
return &orchestrationv2.ExecutePaymentResponse{}, nil
}
func (*fakeExecutionClientForGet) ExecuteBatchPayment(context.Context, *orchestrationv2.ExecuteBatchPaymentRequest) (*orchestrationv2.ExecuteBatchPaymentResponse, error) {
return &orchestrationv2.ExecuteBatchPaymentResponse{}, nil
}
func (f *fakeExecutionClientForGet) GetPayment(_ context.Context, req *orchestrationv2.GetPaymentRequest) (*orchestrationv2.GetPaymentResponse, error) {
f.getPaymentReq = req
return f.getPaymentResp, nil
}
func (*fakeExecutionClientForGet) ListPayments(context.Context, *orchestrationv2.ListPaymentsRequest) (*orchestrationv2.ListPaymentsResponse, error) {
return &orchestrationv2.ListPaymentsResponse{}, nil
}
func (*fakeExecutionClientForGet) Close() error { return nil }
type capturingPaymentEnforcer struct {
allowed bool
organizationRef bson.ObjectID
}
func (f *capturingPaymentEnforcer) Enforce(_ context.Context, _ bson.ObjectID, _ bson.ObjectID, organizationRef bson.ObjectID, _ bson.ObjectID, _ model.Action) (bool, error) {
f.organizationRef = organizationRef
return f.allowed, nil
}
func (*capturingPaymentEnforcer) EnforceBatch(context.Context, []model.PermissionBoundStorable, bson.ObjectID, model.Action) (map[bson.ObjectID]bool, error) {
return nil, nil
}
func (*capturingPaymentEnforcer) GetRoles(context.Context, bson.ObjectID, bson.ObjectID) ([]model.Role, error) {
return nil, nil
}
func (*capturingPaymentEnforcer) GetPermissions(context.Context, bson.ObjectID, bson.ObjectID) ([]model.Role, []model.Permission, error) {
return nil, nil, nil
}
var _ auth.Enforcer = (*capturingPaymentEnforcer)(nil)

View File

@@ -49,6 +49,7 @@ type PaymentAPI struct {
quotation quotationClient
enf auth.Enforcer
oph mutil.ParamHelper
pph mutil.ParamHelper
discovery *discovery.Client
refreshConsumer msg.Consumer
refreshMu sync.RWMutex
@@ -84,6 +85,7 @@ func CreateAPI(apiCtx eapi.API) (*PaymentAPI, error) {
logger: apiCtx.Logger().Named(mservice.Payments),
enf: apiCtx.Permissions().Enforcer(),
oph: mutil.CreatePH(mservice.Organizations),
pph: mutil.CreatePH(mservice.Payments),
}
desc, err := apiCtx.Permissions().GetPolicyDescription(context.Background(), mservice.Payments)
@@ -106,6 +108,7 @@ func CreateAPI(apiCtx eapi.API) (*PaymentAPI, error) {
apiCtx.Register().AccountHandler(p.Name(), p.oph.AddRef("/immediate"), api.Post, p.initiateImmediate)
apiCtx.Register().AccountHandler(p.Name(), p.oph.AddRef("/by-quote"), api.Post, p.initiateByQuote)
apiCtx.Register().AccountHandler(p.Name(), p.oph.AddRef("/by-multiquote"), api.Post, p.initiatePaymentsByQuote)
apiCtx.Register().AccountHandler(p.Name(), p.pph.AddRef("/by-ref"), api.Get, p.getPayment)
apiCtx.Register().AccountHandler(p.Name(), p.oph.AddRef("/"), api.Get, p.listPayments)
apiCtx.Register().AccountHandler(p.Name(), p.oph.AddRef("/documents/operation"), api.Get, p.getOperationDocument)
apiCtx.Register().AccountHandler(p.Name(), p.oph.AddRef("/registry"), api.Get, p.listDiscoveryRegistry)