feat(loyalty): remove paying with EnakPoint
EnakPoint can only be redeemed for vouchers now: it can no longer pay for orders and is never cashed out (docs/enakgame-prd.md §3.2, EG-001, EG-002). No order was ever paid with EnakPoint, so there is no data to move. Removed: - POST /customer/wallet/payment-code, POST /customer/orders/:id/pay-with-points and GET /orders/:id/point-payment/preview, with their processors, repositories, services, handlers and tests. - The point payment method type: paying, splitting and refunding with it, the outlet filter on the method list, and the system-method guard. - points and payment_code on CreatePayment; points_used and point_value on payments; accepts_point_payment on the customer outlets. - The outlet point_payment settings. A PUT that still sends them is rejected as an unknown field. - The EnakPoint split in the payment method analytics. - PAYMENT and PAYMENT_REFUND from the wallet type rules. Tests that used them as a generic EnakPoint debit use REWARD_REDEEM. - The EnakPoint-paid part from the earning basis, which is subtotal − discount again. Migration 000102 drops the trigger, the point methods and their index, the payments columns, and the outlet settings, and restores the method type CHECK without point. payments.payment_method_id is ON DELETE RESTRICT, so it fails rather than lose a payment made with EnakPoint. The integration docs list the removed endpoints and fields, and the EnakPoint & EnakCoin PRD and tasks note what is superseded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
3ebc09f818
commit
2c9753fae7
@@ -1,34 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"apskel-pos-be/internal/contract"
|
||||
"apskel-pos-be/internal/models"
|
||||
"apskel-pos-be/internal/processor"
|
||||
"apskel-pos-be/internal/transformer"
|
||||
)
|
||||
|
||||
// CustomerOrderPaymentService lets customers pay their own orders with EnakPoint in the
|
||||
// app or a self-order (docs/prd-point-coin.md F9).
|
||||
type CustomerOrderPaymentService interface {
|
||||
PayWithPoints(ctx context.Context, customerID, orderID uuid.UUID, req *contract.PayWithPointsRequest, info models.CustomerPinRequestInfo) *contract.Response
|
||||
}
|
||||
|
||||
type CustomerOrderPaymentServiceImpl struct {
|
||||
orders processor.OrderProcessor
|
||||
}
|
||||
|
||||
func NewCustomerOrderPaymentService(orders processor.OrderProcessor) *CustomerOrderPaymentServiceImpl {
|
||||
return &CustomerOrderPaymentServiceImpl{orders: orders}
|
||||
}
|
||||
|
||||
func (s *CustomerOrderPaymentServiceImpl) PayWithPoints(ctx context.Context, customerID, orderID uuid.UUID, req *contract.PayWithPointsRequest, info models.CustomerPinRequestInfo) *contract.Response {
|
||||
payment, err := s.orders.PayWithPointsInApp(ctx, customerID, orderID, req.Points, req.Pin, info)
|
||||
if err != nil {
|
||||
return PointPaymentErrorResponse(err)
|
||||
}
|
||||
return contract.BuildSuccessResponse(transformer.PaymentModelToContract(payment))
|
||||
}
|
||||
@@ -25,18 +25,14 @@ type CustomerPinService interface {
|
||||
|
||||
RemovePin(ctx context.Context, apctx *appcontext.ContextInfo, customerID uuid.UUID, req *contract.RemoveCustomerPinRequest, info models.CustomerPinRequestInfo) *contract.Response
|
||||
ListSecurityEvents(ctx context.Context, apctx *appcontext.ContextInfo, customerID uuid.UUID, page, limit int) *contract.Response
|
||||
|
||||
// IssuePaymentCode checks the PIN and returns a one-time code for the cashier (F9).
|
||||
IssuePaymentCode(ctx context.Context, customerID uuid.UUID, req *contract.IssuePaymentCodeRequest, info models.CustomerPinRequestInfo) *contract.Response
|
||||
}
|
||||
|
||||
type CustomerPinServiceImpl struct {
|
||||
pins *processor.CustomerPinProcessor
|
||||
codes *processor.PaymentCodeProcessor
|
||||
pins *processor.CustomerPinProcessor
|
||||
}
|
||||
|
||||
func NewCustomerPinService(pins *processor.CustomerPinProcessor, codes *processor.PaymentCodeProcessor) *CustomerPinServiceImpl {
|
||||
return &CustomerPinServiceImpl{pins: pins, codes: codes}
|
||||
func NewCustomerPinService(pins *processor.CustomerPinProcessor) *CustomerPinServiceImpl {
|
||||
return &CustomerPinServiceImpl{pins: pins}
|
||||
}
|
||||
|
||||
func (s *CustomerPinServiceImpl) Status(ctx context.Context, customerID uuid.UUID) *contract.Response {
|
||||
@@ -130,11 +126,3 @@ func PinErrorResponse(err error) *contract.Response {
|
||||
contract.NewResponseError(code, constants.CustomerPinServiceEntity, err.Error()),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *CustomerPinServiceImpl) IssuePaymentCode(ctx context.Context, customerID uuid.UUID, req *contract.IssuePaymentCodeRequest, info models.CustomerPinRequestInfo) *contract.Response {
|
||||
code, err := s.codes.Issue(ctx, customerID, req.Pin, info)
|
||||
if err != nil {
|
||||
return PinErrorResponse(err)
|
||||
}
|
||||
return contract.BuildSuccessResponse(code)
|
||||
}
|
||||
|
||||
@@ -557,8 +557,7 @@ func (s *OrderServiceImpl) validateCreatePaymentRequest(req *models.CreatePaymen
|
||||
return fmt.Errorf("payment method ID is required")
|
||||
}
|
||||
|
||||
// A payment with EnakPoint gives points instead; its amount is computed from them.
|
||||
if req.Points == nil && req.Amount <= 0 {
|
||||
if req.Amount <= 0 {
|
||||
return fmt.Errorf("payment amount must be greater than zero")
|
||||
}
|
||||
|
||||
|
||||
@@ -42,14 +42,6 @@ func (m *MockOrderProcessor) UpdateOrder(ctx context.Context, id uuid.UUID, req
|
||||
return args.Get(0).(*models.OrderResponse), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockOrderProcessor) PayWithPointsInApp(ctx context.Context, customerID, orderID uuid.UUID, points int64, pin string, info models.CustomerPinRequestInfo) (*models.PaymentResponse, error) {
|
||||
args := m.Called(ctx, customerID, orderID, points, pin, info)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).(*models.PaymentResponse), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockOrderProcessor) GetOrderByID(ctx context.Context, id uuid.UUID) (*models.OrderResponse, error) {
|
||||
args := m.Called(ctx, id)
|
||||
if args.Get(0) == nil {
|
||||
|
||||
@@ -2,10 +2,8 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"apskel-pos-be/internal/appcontext"
|
||||
"apskel-pos-be/internal/constants"
|
||||
"apskel-pos-be/internal/contract"
|
||||
"apskel-pos-be/internal/mappers"
|
||||
"apskel-pos-be/internal/processor"
|
||||
@@ -41,7 +39,7 @@ func (s *PaymentMethodServiceImpl) CreatePaymentMethod(ctx context.Context, cont
|
||||
response, err := s.paymentMethodProcessor.CreatePaymentMethod(ctx, modelReq)
|
||||
if err != nil {
|
||||
return contract.BuildErrorResponse([]*contract.ResponseError{
|
||||
contract.NewResponseError(paymentMethodErrorCode(err, "PAYMENT_METHOD_CREATE_ERROR"), "payment_method", err.Error()),
|
||||
contract.NewResponseError("PAYMENT_METHOD_CREATE_ERROR", "payment_method", err.Error()),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -86,7 +84,7 @@ func (s *PaymentMethodServiceImpl) UpdatePaymentMethod(ctx context.Context, id u
|
||||
response, err := s.paymentMethodProcessor.UpdatePaymentMethod(ctx, id, modelReq)
|
||||
if err != nil {
|
||||
return contract.BuildErrorResponse([]*contract.ResponseError{
|
||||
contract.NewResponseError(paymentMethodErrorCode(err, "PAYMENT_METHOD_UPDATE_ERROR"), "payment_method", err.Error()),
|
||||
contract.NewResponseError("PAYMENT_METHOD_UPDATE_ERROR", "payment_method", err.Error()),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -99,7 +97,7 @@ func (s *PaymentMethodServiceImpl) DeletePaymentMethod(ctx context.Context, id u
|
||||
err := s.paymentMethodProcessor.DeletePaymentMethod(ctx, id)
|
||||
if err != nil {
|
||||
return contract.BuildErrorResponse([]*contract.ResponseError{
|
||||
contract.NewResponseError(paymentMethodErrorCode(err, "PAYMENT_METHOD_DELETE_ERROR"), "payment_method", err.Error()),
|
||||
contract.NewResponseError("PAYMENT_METHOD_DELETE_ERROR", "payment_method", err.Error()),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -125,12 +123,3 @@ func (s *PaymentMethodServiceImpl) GetActivePaymentMethodsByOrganization(ctx con
|
||||
|
||||
return contract.BuildSuccessResponse(contractResponses)
|
||||
}
|
||||
|
||||
// paymentMethodErrorCode answers a request to create, delete or retype the EnakPoint
|
||||
// method as a bad request instead of a server error.
|
||||
func paymentMethodErrorCode(err error, fallback string) string {
|
||||
if errors.Is(err, processor.ErrSystemPaymentMethod) {
|
||||
return constants.ValidationErrorCode
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"apskel-pos-be/internal/appcontext"
|
||||
"apskel-pos-be/internal/constants"
|
||||
"apskel-pos-be/internal/contract"
|
||||
"apskel-pos-be/internal/processor"
|
||||
"apskel-pos-be/internal/repository"
|
||||
)
|
||||
|
||||
// PointPaymentService serves what the cashier needs before taking EnakPoint
|
||||
// (docs/prd-point-coin.md F9).
|
||||
type PointPaymentService interface {
|
||||
Preview(ctx context.Context, apctx *appcontext.ContextInfo, orderID uuid.UUID) *contract.Response
|
||||
}
|
||||
|
||||
type PointPaymentServiceImpl struct {
|
||||
payments *processor.PointPaymentProcessor
|
||||
}
|
||||
|
||||
func NewPointPaymentService(payments *processor.PointPaymentProcessor) *PointPaymentServiceImpl {
|
||||
return &PointPaymentServiceImpl{payments: payments}
|
||||
}
|
||||
|
||||
func (s *PointPaymentServiceImpl) Preview(ctx context.Context, apctx *appcontext.ContextInfo, orderID uuid.UUID) *contract.Response {
|
||||
preview, err := s.payments.Preview(ctx, apctx.OrganizationID, orderID)
|
||||
if err != nil {
|
||||
return PointPaymentErrorResponse(err)
|
||||
}
|
||||
return contract.BuildSuccessResponse(preview)
|
||||
}
|
||||
|
||||
// PointPaymentErrorResponse answers a refused EnakPoint payment as a bad request, PIN
|
||||
// problems with their own codes, and anything else as a server error.
|
||||
func PointPaymentErrorResponse(err error) *contract.Response {
|
||||
var pinErr *processor.PinError
|
||||
if errors.As(err, &pinErr) {
|
||||
return PinErrorResponse(err)
|
||||
}
|
||||
code := constants.InternalServerErrorCode
|
||||
switch {
|
||||
case errors.Is(err, repository.ErrPointPaymentOrderNotFound):
|
||||
code = constants.NotFoundErrorCode
|
||||
case errors.Is(err, processor.ErrPointPaymentRejected):
|
||||
code = constants.ValidationErrorCode
|
||||
}
|
||||
return contract.BuildErrorResponse([]*contract.ResponseError{
|
||||
contract.NewResponseError(code, constants.WalletServiceEntity, err.Error()),
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user