重构支付接口,以动态支持多种产品类型,整合长短效套餐的购买支付逻辑;引入 decimal 类型的金额计算;
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"platform/web/core"
|
||||
resource2 "platform/web/domains/resource"
|
||||
trade2 "platform/web/domains/trade"
|
||||
g "platform/web/globals"
|
||||
"platform/web/globals/orm"
|
||||
q "platform/web/queries"
|
||||
s "platform/web/services"
|
||||
@@ -143,163 +144,92 @@ func AllResource(c *fiber.Ctx) error {
|
||||
|
||||
// endregion
|
||||
|
||||
// region CompleteResource
|
||||
// region CreateResource
|
||||
|
||||
type CreateResourceReq struct {
|
||||
s.CreateResourceData
|
||||
s.CreateResourceSerializer
|
||||
}
|
||||
|
||||
type CreateResourceResp struct {
|
||||
TradeNo string `json:"trade_no"`
|
||||
PayURL string `json:"pay_url"`
|
||||
}
|
||||
|
||||
type PaidCreateResourceReq struct {
|
||||
TradeNo string `json:"trade_no" validate:"required"`
|
||||
}
|
||||
|
||||
func PrepareResourceByAlipay(c *fiber.Ctx) error {
|
||||
func CreateResource(c *fiber.Ctx) error {
|
||||
|
||||
// 检查权限
|
||||
authContext, err := auth.Protect(c, []auth.PayloadType{auth.PayloadUser}, []string{})
|
||||
authCtx, err := auth.NewProtect(c).Payload(auth.PayloadUser).Do()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 解析请求参数
|
||||
req := new(CreateResourceReq)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 保存交易信息
|
||||
result, err := s.Resource.PrepareResource(
|
||||
c.Context(),
|
||||
&req.CreateResourceData,
|
||||
authContext.Payload.Id,
|
||||
trade2.MethodAlipay,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 返回结果
|
||||
return c.JSON(CreateResourceResp{
|
||||
TradeNo: result.TradeNo,
|
||||
PayURL: result.PayURL,
|
||||
})
|
||||
}
|
||||
|
||||
func PrepareResourceByWechat(c *fiber.Ctx) error {
|
||||
|
||||
// 检查权限
|
||||
authContext, err := auth.Protect(c, []auth.PayloadType{auth.PayloadUser}, []string{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 解析请求参数
|
||||
req := new(CreateResourceReq)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 保存交易信息
|
||||
result, err := s.Resource.PrepareResource(
|
||||
c.Context(),
|
||||
&req.CreateResourceData,
|
||||
authContext.Payload.Id,
|
||||
trade2.MethodWeChat,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 返回结果
|
||||
return c.JSON(CreateResourceResp{
|
||||
TradeNo: result.TradeNo,
|
||||
PayURL: result.PayURL,
|
||||
})
|
||||
}
|
||||
|
||||
func CreateResourceByAlipay(c *fiber.Ctx) error {
|
||||
// 检查权限
|
||||
_, err := auth.Protect(c, []auth.PayloadType{auth.PayloadUser}, []string{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 解析请求参数
|
||||
req := new(PaidCreateResourceReq)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 验证支付结果
|
||||
result, err := s.Transaction.VerifyTransaction(c.Context(), &s.TransactionVerifyData{
|
||||
TradeNo: req.TradeNo,
|
||||
Method: trade2.MethodAlipay,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 完成套餐创建
|
||||
err = s.Resource.CompleteResource(c.Context(), req.TradeNo, result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateResourceByWechat(c *fiber.Ctx) error {
|
||||
// 检查权限
|
||||
_, err := auth.Protect(c, []auth.PayloadType{auth.PayloadUser}, []string{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 解析请求参数
|
||||
req := new(PaidCreateResourceReq)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 验证支付结果
|
||||
result, err := s.Transaction.VerifyTransaction(c.Context(), &s.TransactionVerifyData{
|
||||
TradeNo: req.TradeNo,
|
||||
Method: trade2.MethodWeChat,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 完成套餐创建
|
||||
err = s.Resource.CompleteResource(c.Context(), req.TradeNo, result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateResourceByBalance(c *fiber.Ctx) error {
|
||||
|
||||
// 检查权限
|
||||
authCtx, err := auth.Protect(c, []auth.PayloadType{auth.PayloadUser}, []string{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 解析请求参数
|
||||
req := new(CreateResourceReq)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
var req = new(CreateResourceReq)
|
||||
if err := g.Validator.Validate(c, req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建套餐
|
||||
err = s.Resource.CreateResource(&req.CreateResourceData, authCtx.Payload.Id)
|
||||
err = s.Resource.CreateResource(authCtx.Payload.Id, time.Now(), &req.CreateResourceSerializer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type PrepareResourceReq struct {
|
||||
Method trade2.Method `json:"method" validate:"required"`
|
||||
s.CreateResourceSerializer
|
||||
}
|
||||
|
||||
type PrepareResourceResp struct {
|
||||
TradeNo string `json:"trade_no"`
|
||||
PayURL string `json:"pay_url"`
|
||||
}
|
||||
|
||||
func PrepareCreateResource(c *fiber.Ctx) error {
|
||||
|
||||
// 检查权限
|
||||
authCtx, err := auth.NewProtect(c).Payload(auth.PayloadUser).Do()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 解析请求参数
|
||||
var req = new(PrepareResourceReq)
|
||||
if err := g.Validator.Validate(c, req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 准备创建套餐
|
||||
result, err := s.Resource.PrepareResource(authCtx.Payload.Id, time.Now(), req.Method, &req.CreateResourceSerializer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.JSON(PrepareResourceResp{
|
||||
TradeNo: result.TradeNo,
|
||||
PayURL: result.PayURL,
|
||||
})
|
||||
}
|
||||
|
||||
type CompleteResourceReq struct {
|
||||
TradeNo string `json:"trade_no" validate:"required"`
|
||||
}
|
||||
|
||||
func CompleteCreateResource(c *fiber.Ctx) error {
|
||||
|
||||
// 检查权限
|
||||
_, err := auth.NewProtect(c).Payload(auth.PayloadUser).Do()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 解析请求参数
|
||||
var req = new(CompleteResourceReq)
|
||||
if err := g.Validator.Validate(c, req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 完成创建套餐
|
||||
var now = time.Now()
|
||||
err = s.Resource.CompleteResource(req.TradeNo, now, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -308,3 +238,26 @@ func CreateResourceByBalance(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
func ResourcePrice(c *fiber.Ctx) error {
|
||||
// 检查权限
|
||||
_, err := auth.NewProtect(c).Payload(auth.PayloadInternalServer).Do()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 解析请求参数
|
||||
var req = new(s.CreateResourceSerializer)
|
||||
if err := g.Validator.Validate(c, req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := req.ToData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
"price": data.GetPrice().StringFixed(2),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,13 +2,13 @@ package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/shopspring/decimal"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
trade2 "platform/web/domains/trade"
|
||||
g "platform/web/globals"
|
||||
q "platform/web/queries"
|
||||
s "platform/web/services"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
@@ -53,7 +53,7 @@ func AlipayCallback(c *fiber.Ctx) error {
|
||||
case string(alipay.TradeStatusSuccess):
|
||||
|
||||
// 收集交易状态
|
||||
payment, err := strconv.ParseFloat(notification.TotalAmount, 64)
|
||||
payment, err := decimal.NewFromString(notification.TotalAmount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -70,14 +70,14 @@ func AlipayCallback(c *fiber.Ctx) error {
|
||||
|
||||
// 余额充值
|
||||
case trade2.TypeRecharge:
|
||||
err := s.User.RechargeConfirm(c.Context(), notification.OutTradeNo, verified)
|
||||
err := s.User.RechargeConfirm(notification.OutTradeNo, verified)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 购买产品
|
||||
case trade2.TypePurchase:
|
||||
err = s.Resource.CompleteResource(c.Context(), notification.OutTradeNo, verified)
|
||||
err = s.Resource.CompleteResource(notification.OutTradeNo, time.Now(), verified)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -89,13 +89,7 @@ func AlipayCallback(c *fiber.Ctx) error {
|
||||
|
||||
// 购买产品
|
||||
case trade2.TypePurchase:
|
||||
|
||||
cancelAt, err := time.Parse("2006-01-02 15:04:05", notification.GmtClose)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.Resource.CancelResource(c.Context(), notification.OutTradeNo, cancelAt, trade2.MethodAlipay)
|
||||
err = s.Resource.CancelResource(notification.OutTradeNo, time.Now(), true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -163,7 +157,7 @@ func WechatPayCallback(c *fiber.Ctx) error {
|
||||
case "SUCCESS":
|
||||
|
||||
// 收集交易状态
|
||||
payment := float64(*content.Amount.PayerTotal) / 100
|
||||
payment := decimal.NewFromInt(*content.Amount.PayerTotal).Div(decimal.NewFromInt(100))
|
||||
paidAt, err := time.Parse(time.RFC3339, *content.SuccessTime)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -178,14 +172,14 @@ func WechatPayCallback(c *fiber.Ctx) error {
|
||||
|
||||
// 余额充值
|
||||
case trade.Type == int32(trade2.TypeRecharge):
|
||||
err := s.User.RechargeConfirm(c.Context(), *content.OutTradeNo, verified)
|
||||
err := s.User.RechargeConfirm(*content.OutTradeNo, verified)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 购买产品
|
||||
case trade.Type == int32(trade2.TypePurchase):
|
||||
err = s.Resource.CompleteResource(c.Context(), *content.OutTradeNo, verified)
|
||||
err = s.Resource.CompleteResource(*content.OutTradeNo, time.Now(), verified)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/shopspring/decimal"
|
||||
"platform/web/auth"
|
||||
trade2 "platform/web/domains/trade"
|
||||
m "platform/web/models"
|
||||
q "platform/web/queries"
|
||||
s "platform/web/services"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
@@ -143,7 +143,7 @@ func UpdatePassword(c *fiber.Ctx) error {
|
||||
// region /recharge
|
||||
|
||||
type RechargePrepareReq struct {
|
||||
Amount float64 `json:"amount" validate:"required,min=0.01"`
|
||||
Amount string `json:"amount" validate:"required,numeric"`
|
||||
}
|
||||
|
||||
type RechargePrepareResp struct {
|
||||
@@ -174,11 +174,16 @@ func RechargePrepareAlipay(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// 保存交易信息
|
||||
var now = time.Now()
|
||||
amount, err := decimal.NewFromString(req.Amount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var result *s.TransactionPrepareResult
|
||||
err = q.Q.Transaction(func(tx *q.Query) error {
|
||||
result, err = s.Transaction.PrepareTransaction(c.Context(), tx, authContext.Payload.Id, &s.TransactionPrepareData{
|
||||
Subject: "账户充值 - " + strconv.FormatFloat(req.Amount, 'f', 2, 64) + "元",
|
||||
Amount: req.Amount,
|
||||
result, err = s.Transaction.PrepareTransaction(tx, authContext.Payload.Id, now, &s.TransactionPrepareData{
|
||||
Subject: "账户充值 - " + amount.StringFixed(2) + "元",
|
||||
Amount: amount,
|
||||
ExpireAt: time.Now().Add(30 * time.Minute),
|
||||
Type: trade2.TypeRecharge,
|
||||
Method: trade2.MethodAlipay,
|
||||
@@ -210,7 +215,7 @@ func RechargeConfirmAlipay(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// 验证支付结果
|
||||
result, err := s.Transaction.VerifyTransaction(c.Context(), &s.TransactionVerifyData{
|
||||
result, err := s.Transaction.VerifyTransaction(&s.TransactionVerifyData{
|
||||
TradeNo: req.TradeNo,
|
||||
Method: trade2.MethodAlipay,
|
||||
})
|
||||
@@ -219,7 +224,7 @@ func RechargeConfirmAlipay(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// 更新数据库
|
||||
err = s.User.RechargeConfirm(c.Context(), req.TradeNo, result)
|
||||
err = s.User.RechargeConfirm(req.TradeNo, result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -241,12 +246,17 @@ func RechargePrepareWechat(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// 保存交易信息
|
||||
var now = time.Now()
|
||||
amount, err := decimal.NewFromString(req.Amount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var result *s.TransactionPrepareResult
|
||||
err = q.Q.Transaction(func(tx *q.Query) error {
|
||||
result, err = s.Transaction.PrepareTransaction(c.Context(), tx, authContext.Payload.Id, &s.TransactionPrepareData{
|
||||
Subject: "账户充值 - " + strconv.FormatFloat(req.Amount, 'f', 2, 64) + "元",
|
||||
Amount: req.Amount,
|
||||
ExpireAt: time.Now().Add(30 * time.Minute),
|
||||
result, err = s.Transaction.PrepareTransaction(tx, authContext.Payload.Id, now, &s.TransactionPrepareData{
|
||||
Subject: "账户充值 - " + amount.StringFixed(2) + "元",
|
||||
Amount: amount,
|
||||
ExpireAt: now.Add(30 * time.Minute),
|
||||
Type: trade2.TypeRecharge,
|
||||
Method: trade2.MethodWeChat,
|
||||
})
|
||||
@@ -279,7 +289,7 @@ func RechargeConfirmWechat(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// 验证支付结果
|
||||
result, err := s.Transaction.VerifyTransaction(c.Context(), &s.TransactionVerifyData{
|
||||
result, err := s.Transaction.VerifyTransaction(&s.TransactionVerifyData{
|
||||
TradeNo: req.TradeNo,
|
||||
Method: trade2.MethodWeChat,
|
||||
})
|
||||
@@ -288,7 +298,7 @@ func RechargeConfirmWechat(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// 更新数据库
|
||||
err = s.User.RechargeConfirm(c.Context(), req.TradeNo, result)
|
||||
err = s.User.RechargeConfirm(req.TradeNo, result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user