move api/server to api/edge/bff

This commit is contained in:
Stephan D
2026-02-28 00:39:20 +01:00
parent 34182af3b8
commit 98db0e4e9e
248 changed files with 406 additions and 18 deletions

View File

@@ -0,0 +1,199 @@
package paymentapiimp
import (
"net/http"
"strconv"
"strings"
"time"
"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"
paginationv1 "github.com/tech/sendico/pkg/proto/common/pagination/v1"
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"
"google.golang.org/protobuf/types/known/timestamppb"
)
const maxInt32 = int64(1<<31 - 1)
func (a *PaymentAPI) listPayments(r *http.Request, account *model.Account, token *sresponse.TokenData) http.HandlerFunc {
orgRef, err := a.oph.GetRef(r)
if err != nil {
a.logger.Warn("Failed to parse organization reference for payments list", zap.Error(err), mutil.PLog(a.oph, r))
return response.BadReference(a.logger, a.Name(), a.oph.Name(), a.oph.GetID(r), err)
}
ctx := r.Context()
allowed, err := a.enf.Enforce(ctx, a.permissionRef, account.ID, orgRef, bson.NilObjectID, model.ActionRead)
if err != nil {
a.logger.Warn("Failed to check payments access permissions", zap.Error(err), mutil.PLog(a.oph, r))
return response.Auto(a.logger, a.Name(), err)
}
if !allowed {
a.logger.Debug("Access denied when listing payments", mutil.PLog(a.oph, r))
return response.AccessDenied(a.logger, a.Name(), "payments read permission denied")
}
req := &orchestrationv2.ListPaymentsRequest{Meta: requestMeta(orgRef.Hex(), "")}
if page, err := listPaymentsPage(r); err != nil {
return response.Auto(a.logger, a.Name(), err)
} else if page != nil {
req.Page = page
}
query := r.URL.Query()
if quotationRef := firstNonEmpty(query.Get("quotation_ref"), query.Get("quote_ref")); quotationRef != "" {
req.QuotationRef = quotationRef
}
createdFrom, err := parseRFC3339Timestamp(firstNonEmpty(query.Get("created_from"), query.Get("createdFrom")), "created_from")
if err != nil {
return response.Auto(a.logger, a.Name(), err)
}
if createdFrom != nil {
req.CreatedFrom = createdFrom
}
createdTo, err := parseRFC3339Timestamp(firstNonEmpty(query.Get("created_to"), query.Get("createdTo")), "created_to")
if err != nil {
return response.Auto(a.logger, a.Name(), err)
}
if createdTo != nil {
req.CreatedTo = createdTo
}
if req.GetCreatedFrom() != nil && req.GetCreatedTo() != nil {
if !req.GetCreatedTo().AsTime().After(req.GetCreatedFrom().AsTime()) {
return response.Auto(a.logger, a.Name(), merrors.InvalidArgument("created_to must be after created_from", "created_to"))
}
}
if states, err := parsePaymentStateFilters(r); err != nil {
return response.Auto(a.logger, a.Name(), err)
} else if len(states) > 0 {
req.States = states
}
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 grpcErrorResponse(a.logger, a.Name(), err)
}
return sresponse.PaymentsListResponse(a.logger, resp, token)
}
func listPaymentsPage(r *http.Request) (*paginationv1.CursorPageRequest, error) {
query := r.URL.Query()
cursor := strings.TrimSpace(query.Get("cursor"))
limitRaw := strings.TrimSpace(query.Get("limit"))
var limit int64
hasLimit := false
if limitRaw != "" {
parsed, err := strconv.ParseInt(limitRaw, 10, 32)
if err != nil {
return nil, merrors.InvalidArgument("invalid limit", "limit")
}
limit = parsed
hasLimit = true
}
if cursor == "" && !hasLimit {
return nil, nil
}
page := &paginationv1.CursorPageRequest{
Cursor: cursor,
}
if hasLimit {
if limit < 0 {
limit = 0
} else if limit > maxInt32 {
limit = maxInt32
}
page.Limit = int32(limit)
}
return page, nil
}
func parsePaymentStateFilters(r *http.Request) ([]orchestrationv2.OrchestrationState, error) {
query := r.URL.Query()
values := append([]string{}, query["state"]...)
values = append(values, query["states"]...)
values = append(values, query["filter_states"]...)
if len(values) == 0 {
return nil, nil
}
states := make([]orchestrationv2.OrchestrationState, 0, len(values))
for _, raw := range values {
for _, part := range strings.Split(raw, ",") {
trimmed := strings.TrimSpace(part)
if trimmed == "" {
continue
}
state, ok := orchestrationStateFromString(trimmed)
if !ok {
return nil, merrors.InvalidArgument("unsupported payment state: "+trimmed, "state")
}
states = append(states, state)
}
}
if len(states) == 0 {
return nil, nil
}
return states, nil
}
func orchestrationStateFromString(value string) (orchestrationv2.OrchestrationState, bool) {
upper := strings.ToUpper(strings.TrimSpace(value))
if upper == "" {
return 0, false
}
switch upper {
case "PAYMENT_STATE_ACCEPTED", "ACCEPTED":
return orchestrationv2.OrchestrationState_ORCHESTRATION_STATE_CREATED, true
case "PAYMENT_STATE_FUNDS_RESERVED", "FUNDS_RESERVED", "PAYMENT_STATE_SUBMITTED", "SUBMITTED":
return orchestrationv2.OrchestrationState_ORCHESTRATION_STATE_EXECUTING, true
case "PAYMENT_STATE_SETTLED", "SETTLED":
return orchestrationv2.OrchestrationState_ORCHESTRATION_STATE_SETTLED, true
case "PAYMENT_STATE_FAILED", "FAILED", "PAYMENT_STATE_CANCELLED", "CANCELLED":
return orchestrationv2.OrchestrationState_ORCHESTRATION_STATE_FAILED, true
}
if !strings.HasPrefix(upper, "ORCHESTRATION_STATE_") {
upper = "ORCHESTRATION_STATE_" + upper
}
enumValue, ok := orchestrationv2.OrchestrationState_value[upper]
if !ok {
return 0, false
}
return orchestrationv2.OrchestrationState(enumValue), true
}
func firstNonEmpty(values ...string) string {
for _, value := range values {
trimmed := strings.TrimSpace(value)
if trimmed != "" {
return trimmed
}
}
return ""
}
func parseRFC3339Timestamp(raw string, field string) (*timestamppb.Timestamp, error) {
trimmed := strings.TrimSpace(raw)
if trimmed == "" {
return nil, nil
}
parsed, err := time.Parse(time.RFC3339, trimmed)
if err != nil {
return nil, merrors.InvalidArgument("invalid "+field+", expected RFC3339", field)
}
return timestamppb.New(parsed), nil
}