145 lines
5.8 KiB
Go
145 lines
5.8 KiB
Go
package payments
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"strings"
|
|
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/metadata"
|
|
"google.golang.org/grpc/status"
|
|
appv1 "stream.api/internal/gen/proto/app/v1"
|
|
"stream.api/internal/modules/common"
|
|
)
|
|
|
|
type Handler struct {
|
|
appv1.UnimplementedPaymentsServiceServer
|
|
module *Module
|
|
}
|
|
|
|
var _ appv1.PaymentsServiceServer = (*Handler)(nil)
|
|
|
|
func NewHandler(module *Module) *Handler { return &Handler{module: module} }
|
|
|
|
func (h *Handler) CreatePayment(ctx context.Context, req *appv1.CreatePaymentRequest) (*appv1.CreatePaymentResponse, error) {
|
|
authResult, err := h.module.runtime.Authenticate(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
planID := strings.TrimSpace(req.GetPlanId())
|
|
if planID == "" {
|
|
return nil, status.Error(codes.InvalidArgument, "Plan ID is required")
|
|
}
|
|
if !common.IsAllowedTermMonths(req.GetTermMonths()) {
|
|
return nil, status.Error(codes.InvalidArgument, "Term months must be one of 1, 3, 6, or 12")
|
|
}
|
|
paymentMethod := common.NormalizePaymentMethod(req.GetPaymentMethod())
|
|
if paymentMethod == "" {
|
|
return nil, status.Error(codes.InvalidArgument, "Payment method must be wallet or topup")
|
|
}
|
|
result, err := h.module.CreatePayment(ctx, CreatePaymentCommand{UserID: authResult.UserID, PlanID: planID, TermMonths: req.GetTermMonths(), PaymentMethod: paymentMethod, TopupAmount: req.TopupAmount})
|
|
if err != nil {
|
|
return nil, h.handleError(ctx, err, "Failed to create payment")
|
|
}
|
|
return presentCreatePaymentResponse(result), nil
|
|
}
|
|
|
|
func (h *Handler) ListPaymentHistory(ctx context.Context, req *appv1.ListPaymentHistoryRequest) (*appv1.ListPaymentHistoryResponse, error) {
|
|
authResult, err := h.module.runtime.Authenticate(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result, err := h.module.ListPaymentHistory(ctx, PaymentHistoryQuery{UserID: authResult.UserID, Page: req.GetPage(), Limit: req.GetLimit()})
|
|
if err != nil {
|
|
return nil, h.handleError(ctx, err, "Failed to fetch payment history")
|
|
}
|
|
return presentPaymentHistoryResponse(result), nil
|
|
}
|
|
|
|
func (h *Handler) TopupWallet(ctx context.Context, req *appv1.TopupWalletRequest) (*appv1.TopupWalletResponse, error) {
|
|
authResult, err := h.module.runtime.Authenticate(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result, err := h.module.TopupWallet(ctx, TopupWalletCommand{UserID: authResult.UserID, Amount: req.GetAmount()})
|
|
if err != nil {
|
|
return nil, h.handleError(ctx, err, "Failed to top up wallet")
|
|
}
|
|
return presentTopupWalletResponse(result), nil
|
|
}
|
|
|
|
func (h *Handler) DownloadInvoice(ctx context.Context, req *appv1.DownloadInvoiceRequest) (*appv1.DownloadInvoiceResponse, error) {
|
|
authResult, err := h.module.runtime.Authenticate(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result, err := h.module.DownloadInvoice(ctx, DownloadInvoiceQuery{UserID: authResult.UserID, ID: strings.TrimSpace(req.GetId())})
|
|
if err != nil {
|
|
return nil, h.handleError(ctx, err, "Failed to download invoice")
|
|
}
|
|
return presentDownloadInvoiceResponse(result), nil
|
|
}
|
|
|
|
func (h *Handler) ListAdminPayments(ctx context.Context, req *appv1.ListAdminPaymentsRequest) (*appv1.ListAdminPaymentsResponse, error) {
|
|
result, err := h.module.ListAdminPayments(ctx, ListAdminPaymentsQuery{Page: req.GetPage(), Limit: req.GetLimit(), UserID: strings.TrimSpace(req.GetUserId()), StatusFilter: strings.TrimSpace(req.GetStatus())})
|
|
if err != nil {
|
|
return nil, h.handleError(ctx, err, "Failed to list payments")
|
|
}
|
|
return presentListAdminPaymentsResponse(result), nil
|
|
}
|
|
|
|
func (h *Handler) GetAdminPayment(ctx context.Context, req *appv1.GetAdminPaymentRequest) (*appv1.GetAdminPaymentResponse, error) {
|
|
result, err := h.module.GetAdminPayment(ctx, GetAdminPaymentQuery{ID: strings.TrimSpace(req.GetId())})
|
|
if err != nil {
|
|
return nil, h.handleError(ctx, err, "Failed to get payment")
|
|
}
|
|
return presentGetAdminPaymentResponse(*result), nil
|
|
}
|
|
|
|
func (h *Handler) CreateAdminPayment(ctx context.Context, req *appv1.CreateAdminPaymentRequest) (*appv1.CreateAdminPaymentResponse, error) {
|
|
userID := strings.TrimSpace(req.GetUserId())
|
|
planID := strings.TrimSpace(req.GetPlanId())
|
|
if userID == "" || planID == "" {
|
|
return nil, status.Error(codes.InvalidArgument, "User ID and plan ID are required")
|
|
}
|
|
if !common.IsAllowedTermMonths(req.GetTermMonths()) {
|
|
return nil, status.Error(codes.InvalidArgument, "Term months must be one of 1, 3, 6, or 12")
|
|
}
|
|
paymentMethod := common.NormalizePaymentMethod(req.GetPaymentMethod())
|
|
if paymentMethod == "" {
|
|
return nil, status.Error(codes.InvalidArgument, "Payment method must be wallet or topup")
|
|
}
|
|
result, err := h.module.CreateAdminPayment(ctx, CreateAdminPaymentCommand{UserID: userID, PlanID: planID, TermMonths: req.GetTermMonths(), PaymentMethod: paymentMethod, TopupAmount: req.TopupAmount})
|
|
if err != nil {
|
|
return nil, h.handleError(ctx, err, "Failed to create payment")
|
|
}
|
|
return presentCreateAdminPaymentResponse(result), nil
|
|
}
|
|
|
|
func (h *Handler) UpdateAdminPayment(ctx context.Context, req *appv1.UpdateAdminPaymentRequest) (*appv1.UpdateAdminPaymentResponse, error) {
|
|
result, err := h.module.UpdateAdminPayment(ctx, UpdateAdminPaymentCommand{ID: strings.TrimSpace(req.GetId()), NewStatus: req.GetStatus()})
|
|
if err != nil {
|
|
return nil, h.handleError(ctx, err, "Failed to update payment")
|
|
}
|
|
return presentUpdateAdminPaymentResponse(*result), nil
|
|
}
|
|
|
|
func (h *Handler) handleError(ctx context.Context, err error, fallback string) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
if validationErr, ok := err.(*PaymentValidationError); ok {
|
|
body := validationErr.apiBody()
|
|
if encoded, marshalErr := json.Marshal(body); marshalErr == nil {
|
|
_ = grpc.SetTrailer(ctx, metadata.Pairs("x-error-body", string(encoded)))
|
|
}
|
|
return status.Error(codes.Code(validationErr.GRPCCode), validationErr.Message)
|
|
}
|
|
if _, ok := status.FromError(err); ok {
|
|
return err
|
|
}
|
|
h.module.runtime.Logger().Error(fallback, "error", err)
|
|
return status.Error(codes.Internal, fallback)
|
|
}
|