重构资源创建逻辑,整合支付宝,优化套餐生成与交易处理
This commit is contained in:
14
README.md
14
README.md
@@ -8,10 +8,8 @@
|
||||
- [ ] jwt 签发
|
||||
- [x] 鉴权
|
||||
- [x] 实名认证
|
||||
- [ ] 对接接口
|
||||
- [x] 充值余额
|
||||
- [ ] 对接接口
|
||||
- [x] 选择套餐
|
||||
- [x] 对接接口
|
||||
- [x] 充值或购买
|
||||
- [ ] 对接接口
|
||||
- [ ] 提取记录
|
||||
- [x] 提取 IP
|
||||
@@ -24,9 +22,11 @@
|
||||
- [ ] Limiter
|
||||
- [ ] Compress
|
||||
|
||||
统一简化包导入别名
|
||||
统一套餐创建逻辑
|
||||
|
||||
迁移 pkg 包下的代码,尽量放置在 web/globals 下,env 和 log 作为全局公共配置保留
|
||||
删除账单的状态字段,状态从关联表中计算获得
|
||||
|
||||
统一简化包导入别名
|
||||
|
||||
更新数据库填充
|
||||
|
||||
@@ -47,8 +47,6 @@ channel 优化:
|
||||
- 端口分配时加锁
|
||||
- 数据存入顺序,数据库 > 缓存 > 外部接口
|
||||
|
||||
remote 令牌问题
|
||||
|
||||
用对称加密处理密钥
|
||||
|
||||
考虑将鉴权逻辑放到 handler 里,统一动静态鉴权以及解耦服务层
|
||||
|
||||
9
pkg/env/env.go
vendored
9
pkg/env/env.go
vendored
@@ -193,25 +193,26 @@ var (
|
||||
)
|
||||
|
||||
func loadAlipay() {
|
||||
AlipayAppId := os.Getenv("ALIPAY_APP_ID")
|
||||
AlipayAppId = os.Getenv("ALIPAY_APP_ID")
|
||||
if AlipayAppId == "" {
|
||||
panic("环境变量 ALIPAY_APP_ID 的值不能为空")
|
||||
}
|
||||
|
||||
AlipayAppPrivateKey := os.Getenv("ALIPAY_APP_PRIVATE_KEY")
|
||||
AlipayAppPrivateKey = os.Getenv("ALIPAY_APP_PRIVATE_KEY")
|
||||
if AlipayAppPrivateKey == "" {
|
||||
panic("环境变量 ALIPAY_APP_PRIVATE_KEY 的值不能为空")
|
||||
}
|
||||
|
||||
AlipayPublicKey := os.Getenv("ALIPAY_PUBLIC_KEY")
|
||||
AlipayPublicKey = os.Getenv("ALIPAY_PUBLIC_KEY")
|
||||
if AlipayPublicKey == "" {
|
||||
panic("环境变量 ALIPAY_PUBLIC_KEY 的值不能为空")
|
||||
}
|
||||
|
||||
AlipayEncryptKey := os.Getenv("ALIPAY_ENCRYPT_KEY")
|
||||
AlipayEncryptKey = os.Getenv("ALIPAY_ENCRYPT_KEY")
|
||||
if AlipayEncryptKey == "" {
|
||||
panic("环境变量 ALIPAY_ENCRYPT_KEY 的值不能为空")
|
||||
}
|
||||
|
||||
_AlipayProduction := os.Getenv("ALIPAY_PRODUCTION")
|
||||
if _AlipayProduction != "" {
|
||||
value, err := strconv.ParseBool(_AlipayProduction)
|
||||
|
||||
@@ -99,13 +99,13 @@ func RemoveChannels(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
auth, ok := c.Locals("auth").(*services.AuthContext)
|
||||
authCtx, ok := c.Locals("auth").(*services.AuthContext)
|
||||
if !ok {
|
||||
return errors.New("user not found")
|
||||
}
|
||||
|
||||
// 删除通道
|
||||
err := services.Channel.RemoveChannels(c.Context(), auth, req.ByIds...)
|
||||
err := services.Channel.RemoveChannels(c.Context(), authCtx, req.ByIds...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -166,7 +166,7 @@ func CreateChannelGet(c *fiber.Ctx) error {
|
||||
return err
|
||||
}
|
||||
|
||||
auth := &services.AuthContext{
|
||||
authCtx := &services.AuthContext{
|
||||
Payload: services.Payload{
|
||||
Id: user.ID,
|
||||
Type: services.PayloadUser,
|
||||
@@ -188,7 +188,7 @@ func CreateChannelGet(c *fiber.Ctx) error {
|
||||
// 建立连接通道
|
||||
result, err := services.Channel.CreateChannel(
|
||||
c.Context(),
|
||||
auth,
|
||||
authCtx,
|
||||
req.ResourceId,
|
||||
req.Protocol,
|
||||
req.AuthType,
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"platform/pkg/rds"
|
||||
"platform/pkg/u"
|
||||
"platform/web/auth"
|
||||
"platform/web/common"
|
||||
g "platform/web/globals"
|
||||
m "platform/web/models"
|
||||
q "platform/web/queries"
|
||||
"platform/web/services"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/smartwalle/alipay/v3"
|
||||
)
|
||||
|
||||
// region ListResourcePss
|
||||
@@ -26,7 +34,7 @@ type ListResourcePssReq struct {
|
||||
ExpireBefore *time.Time `json:"expire_before"`
|
||||
}
|
||||
|
||||
// ListResourcePss 获取资源列表
|
||||
// ListResourcePss 获取套餐列表
|
||||
func ListResourcePss(c *fiber.Ctx) error {
|
||||
// 检查权限
|
||||
authContext, err := auth.Protect(c, []services.PayloadType{services.PayloadUser}, []string{})
|
||||
@@ -40,7 +48,7 @@ func ListResourcePss(c *fiber.Ctx) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 查询资源列表
|
||||
// 查询套餐列表
|
||||
do := q.Resource.
|
||||
Joins(q.Resource.Pss).
|
||||
Where(q.Resource.UserID.Eq(authContext.Payload.Id))
|
||||
@@ -107,7 +115,7 @@ func AllResource(c *fiber.Ctx) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 查询资源列表
|
||||
// 查询套餐列表
|
||||
pss := q.ResourcePss.As(q.Resource.Pss.Name())
|
||||
do := q.Resource.Debug().
|
||||
Joins(q.Resource.Pss).
|
||||
@@ -140,9 +148,9 @@ func AllResource(c *fiber.Ctx) error {
|
||||
|
||||
// endregion
|
||||
|
||||
// region CreateResourceByBalance
|
||||
// region CreateResource
|
||||
|
||||
type CreateResourceByBalanceReq struct {
|
||||
type CreateResourceReq struct {
|
||||
Type int32 `json:"type" validate:"required"`
|
||||
Live int32 `json:"live" validate:"required"`
|
||||
Expire int32 `json:"expire" validate:"required"`
|
||||
@@ -150,8 +158,16 @@ type CreateResourceByBalanceReq struct {
|
||||
DailyLimit int32 `json:"daily_limit" validate:"required"`
|
||||
}
|
||||
|
||||
// CreateResourceByBalance 通过余额创建资源
|
||||
func CreateResourceByBalance(c *fiber.Ctx) error {
|
||||
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 {
|
||||
|
||||
// 检查权限
|
||||
authContext, err := auth.Protect(c, []services.PayloadType{services.PayloadUser}, []string{})
|
||||
@@ -160,66 +176,268 @@ func CreateResourceByBalance(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// 解析请求参数
|
||||
req := new(CreateResourceByBalanceReq)
|
||||
req := new(CreateResourceReq)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 生成订单
|
||||
amount, tradeNo, err := prepareResource(c.Context(), req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 调用外部接口
|
||||
alipayResp, err := g.Alipay.TradePagePay(alipay.TradePagePay{
|
||||
QRPayMode: "4",
|
||||
Trade: alipay.Trade{
|
||||
OutTradeNo: tradeNo,
|
||||
TotalAmount: strconv.FormatFloat(amount, 'f', 2, 64),
|
||||
Subject: "购买套餐",
|
||||
ProductCode: "FAST_INSTANT_TRADE_PAY",
|
||||
TimeExpire: time.Now().Add(30 * time.Minute).Format("2006-01-02 15:04:05"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 保存交易信息
|
||||
err = savePrepareResource(c.Context(), req, amount, tradeNo, authContext.Payload.Id, 1)
|
||||
|
||||
// 返回结果
|
||||
return c.JSON(CreateResourceResp{
|
||||
TradeNo: tradeNo,
|
||||
PayURL: alipayResp.String(),
|
||||
})
|
||||
}
|
||||
|
||||
func PrepareResourceByWechat(c *fiber.Ctx) error {
|
||||
|
||||
// 检查权限
|
||||
authContext, err := auth.Protect(c, []services.PayloadType{services.PayloadUser}, []string{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 解析请求参数
|
||||
req := new(CreateResourceReq)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 生成订单
|
||||
amount, tradeNo, err := prepareResource(c.Context(), req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 调用外部接口
|
||||
alipayResp, err := g.Alipay.TradePagePay(alipay.TradePagePay{
|
||||
QRPayMode: "3",
|
||||
Trade: alipay.Trade{
|
||||
OutTradeNo: tradeNo,
|
||||
TotalAmount: strconv.FormatFloat(amount, 'f', 2, 64),
|
||||
Subject: "购买套餐",
|
||||
ProductCode: "FAST_INSTANT_TRADE_PAY",
|
||||
TimeExpire: time.Now().Add(30 * time.Minute).Format("2006-01-02 15:04:05"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 保存交易信息
|
||||
err = savePrepareResource(c.Context(), req, amount, tradeNo, authContext.Payload.Id, 2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 返回结果
|
||||
return c.JSON(CreateResourceResp{
|
||||
TradeNo: tradeNo,
|
||||
PayURL: alipayResp.String(),
|
||||
})
|
||||
}
|
||||
|
||||
func CreateResourceByAlipay(c *fiber.Ctx) error {
|
||||
// 检查权限
|
||||
authCtx, err := auth.Protect(c, []services.PayloadType{services.PayloadUser}, []string{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 解析请求参数
|
||||
req := new(PaidCreateResourceReq)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 验证支付结果
|
||||
alipayResp, err := g.Alipay.TradeQuery(c.Context(), alipay.TradeQuery{
|
||||
OutTradeNo: req.TradeNo,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if alipayResp.TradeStatus != "TRADE_SUCCESS" {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "支付未完成,请确认后重试")
|
||||
}
|
||||
|
||||
payment, err := strconv.ParseFloat(alipayResp.ReceiptAmount, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
paidAt, err := time.Parse("2006-01-02 15:04:05", alipayResp.SendPayDate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 获取请求缓存
|
||||
reqStr, err := rds.Client.GetDel(c.Context(), req.TradeNo).Result()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
reqCreate := new(CreateResourceReq)
|
||||
if err := json.Unmarshal([]byte(reqStr), reqCreate); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 保存交易信息
|
||||
err = q.Q.Transaction(func(q *q.Query) error {
|
||||
|
||||
// 保存套餐
|
||||
resource, err := saveResourceBalance(reqCreate, authCtx.Payload.Id, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新订单状态
|
||||
_, err = q.Trade.
|
||||
Where(q.Trade.InnerNo).
|
||||
Select(q.Trade.OuterNo, q.Trade.Payment, q.Trade.Status, q.Trade.PaidAt).
|
||||
Updates(&m.Trade{
|
||||
OuterNo: alipayResp.TradeNo,
|
||||
Payment: payment,
|
||||
Status: 1,
|
||||
PaidAt: common.LocalDateTime(paidAt),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新账单状态
|
||||
_, err = q.Bill.
|
||||
Where(q.Bill.TradeID.Eq(resource.ID)).
|
||||
Updates(&m.Bill{
|
||||
ResourceID: resource.ID,
|
||||
Status: 1,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateResourceByWechat(c *fiber.Ctx) error {
|
||||
// 检查权限
|
||||
authCtx, err := auth.Protect(c, []services.PayloadType{services.PayloadUser}, []string{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 解析请求参数
|
||||
req := new(CreateResourceReq)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = q.Q.Transaction(func(q *q.Query) error {
|
||||
// 检查用户
|
||||
user, err := q.User.Where(q.User.ID.Eq(authContext.Payload.Id)).Take()
|
||||
|
||||
// 保存套餐
|
||||
resource, err := saveResourceBalance(req, authCtx.Payload.Id, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 计算价格
|
||||
var amount = 100
|
||||
var payment = 100
|
||||
// 更新订单状态
|
||||
// _, err = q.Trade.
|
||||
// Where(q.Trade.InnerNo).
|
||||
// Select(q.Trade.OuterNo, q.Trade.Payment, q.Trade.Status, q.Trade.PaidAt).
|
||||
// Updates(&m.Trade{
|
||||
// OuterNo: alipayResp.TradeNo,
|
||||
// Payment: payment,
|
||||
// Status: 1,
|
||||
// PaidAt: common.LocalDateTime(paidAt),
|
||||
// })
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// 检查余额
|
||||
if user.Balance < float64(amount)/100 {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "余额不足")
|
||||
}
|
||||
|
||||
// 更新用户余额
|
||||
user.Balance -= float64(payment)
|
||||
_, err = q.User.
|
||||
Where(q.User.ID.Eq(authContext.Payload.Id)).
|
||||
Update(q.User.Balance, user.Balance)
|
||||
// 更新账单状态
|
||||
_, err = q.Bill.
|
||||
Where(q.Bill.TradeID.Eq(resource.ID)).
|
||||
Updates(&m.Bill{
|
||||
ResourceID: resource.ID,
|
||||
Status: 1,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建资源
|
||||
resource := m.Resource{
|
||||
UserID: authContext.Payload.Id,
|
||||
ResourceNo: services.ID.GenReadable("res"),
|
||||
}
|
||||
err = q.Resource.Create(&resource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resourcePss := m.ResourcePss{
|
||||
ResourceID: resource.ID,
|
||||
Type: req.Type,
|
||||
Live: req.Live,
|
||||
Quota: req.Quota,
|
||||
Expire: common.LocalDateTime(time.Now().Add(time.Duration(req.Expire) * time.Second)),
|
||||
DailyLimit: req.DailyLimit,
|
||||
}
|
||||
err = q.ResourcePss.Create(&resourcePss)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateResourceByBalance(c *fiber.Ctx) error {
|
||||
|
||||
// 检查权限
|
||||
authCtx, err := auth.Protect(c, []services.PayloadType{services.PayloadUser}, []string{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 解析请求参数
|
||||
req := new(CreateResourceReq)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 计算价格
|
||||
var amount = calcResourcePrice(req)
|
||||
|
||||
// 保存交易信息
|
||||
err = q.Q.Transaction(func(q *q.Query) error {
|
||||
|
||||
// 保存套餐
|
||||
resource, err := saveResourceBalance(req, authCtx.Payload.Id, amount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 生成账单
|
||||
bill := m.Bill{
|
||||
UserID: authContext.Payload.Id,
|
||||
UserID: authCtx.Payload.Id,
|
||||
ResourceID: resource.ID,
|
||||
BillNo: services.ID.GenReadable("bil"),
|
||||
Info: "购买套餐",
|
||||
Info: "购买套餐 - " + resourceName(req),
|
||||
Type: 1,
|
||||
Status: 1,
|
||||
Amount: amount,
|
||||
}
|
||||
err = q.Bill.
|
||||
Omit(q.Bill.TradeID, q.Bill.RefundID).
|
||||
@@ -237,6 +455,141 @@ func CreateResourceByBalance(c *fiber.Ctx) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func calcResourcePrice(req *CreateResourceReq) float64 {
|
||||
return 100
|
||||
}
|
||||
|
||||
func prepareResource(ctx context.Context, req *CreateResourceReq) (amount float64, tradeNo string, err error) {
|
||||
|
||||
// todo 计算价格
|
||||
amount = calcResourcePrice(req)
|
||||
|
||||
// 生成订单号
|
||||
tradeNoUint, err := services.ID.GenSerial(ctx)
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
tradeNo = strconv.FormatUint(tradeNoUint, 10)
|
||||
|
||||
return amount, tradeNo, nil
|
||||
}
|
||||
|
||||
func savePrepareResource(ctx context.Context, req *CreateResourceReq, amount float64, tradeNo string, uid int32, method int32) error {
|
||||
// 缓存交易信息
|
||||
reqStr, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = rds.Client.Set(ctx, tradeNo, reqStr, 30*time.Minute).Err()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 保存到数据库
|
||||
err = q.Q.Transaction(func(q *q.Query) error {
|
||||
// 创建交易订单
|
||||
var trade = m.Trade{
|
||||
UserID: uid,
|
||||
InnerNo: tradeNo,
|
||||
Subject: "购买套餐 - " + resourceName(req),
|
||||
Method: method,
|
||||
Type: 1,
|
||||
Status: 0,
|
||||
Amount: amount,
|
||||
}
|
||||
err = q.Trade.Create(&trade)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 保存用户帐单
|
||||
bill := m.Bill{
|
||||
UserID: uid,
|
||||
TradeID: trade.ID,
|
||||
BillNo: services.ID.GenReadable("bil"),
|
||||
Info: "购买产品",
|
||||
Type: 1,
|
||||
Status: 0,
|
||||
Amount: -amount,
|
||||
}
|
||||
err = q.Bill.
|
||||
Omit(q.Bill.ResourceID, q.Bill.RefundID).
|
||||
Create(&bill)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func saveResourceBalance(req *CreateResourceReq, uid int32, amount float64) (*m.Resource, error) {
|
||||
// 检查用户
|
||||
user, err := q.User.
|
||||
Where(
|
||||
q.User.ID.Eq(uid),
|
||||
q.User.Status.Eq(1),
|
||||
).
|
||||
Take()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 检查余额
|
||||
if user.Balance < amount {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "余额不足")
|
||||
}
|
||||
|
||||
// 创建套餐
|
||||
resource := &m.Resource{
|
||||
UserID: user.ID,
|
||||
ResourceNo: services.ID.GenReadable("res"),
|
||||
Active: true,
|
||||
Type: 1,
|
||||
Pss: &m.ResourcePss{
|
||||
Type: req.Type,
|
||||
Live: req.Live,
|
||||
Quota: req.Quota,
|
||||
Expire: common.LocalDateTime(time.Now().Add(time.Duration(req.Expire) * time.Second)),
|
||||
DailyLimit: req.DailyLimit,
|
||||
},
|
||||
}
|
||||
err = q.Resource.Create(resource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 更新用户余额
|
||||
user.Balance -= amount
|
||||
_, err = q.User.
|
||||
Where(q.User.ID.Eq(uid)).
|
||||
Update(q.User.Balance, user.Balance)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resource, nil
|
||||
}
|
||||
|
||||
func resourceName(req *CreateResourceReq) string {
|
||||
sb := strings.Builder{}
|
||||
sb.WriteString("短效动态")
|
||||
switch req.Type {
|
||||
case 1:
|
||||
sb.WriteString("包时 ")
|
||||
case 2:
|
||||
sb.WriteString("包量 ")
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf("%d 分钟", req.Live/60))
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region CreateResourceByAlipayCallback
|
||||
@@ -251,7 +604,7 @@ func CreateResourceByAlipayCallback(c *fiber.Ctx) error {
|
||||
// 1. 支付宝或微信(即时支付)
|
||||
// - 更新订单状态
|
||||
// - 生成账单
|
||||
// - 生成资源
|
||||
// - 生成套餐
|
||||
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"platform/web/auth"
|
||||
g "platform/web/globals"
|
||||
m "platform/web/models"
|
||||
q "platform/web/queries"
|
||||
"platform/web/services"
|
||||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/smartwalle/alipay/v3"
|
||||
)
|
||||
|
||||
// region CreateTrade
|
||||
|
||||
type CreateTradeReq struct {
|
||||
Type int `json:"type" validate:"required"` // 交易类型:1.充值,2.购买
|
||||
Subject string `json:"subject" validate:"required"`
|
||||
Remark string `json:"remark"`
|
||||
Amount int `json:"amount" validate:"required"`
|
||||
Method int `json:"method" validate:"required"` // 支付方式:1.支付宝,2.微信
|
||||
}
|
||||
|
||||
func CreateTrade(c *fiber.Ctx) error {
|
||||
// 检查权限
|
||||
authContext, err := auth.Protect(c, []services.PayloadType{services.PayloadUser}, []string{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 解析请求参数
|
||||
req := new(CreateTradeReq)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 调用外部接口
|
||||
|
||||
// 保存交易订单
|
||||
num, err := services.ID.GenSerial(c.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var trade = m.Trade{
|
||||
UserID: authContext.Payload.Id,
|
||||
InnerNo: strconv.FormatUint(num, 10),
|
||||
Subject: req.Subject,
|
||||
Remark: req.Remark,
|
||||
Amount: float64(req.Amount) / 100,
|
||||
Method: int32(req.Method),
|
||||
}
|
||||
err = q.Trade.Create(&trade)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 保存用户帐单
|
||||
var info string
|
||||
var t int32
|
||||
switch req.Type {
|
||||
case 1:
|
||||
info = "充值余额"
|
||||
t = 0
|
||||
case 2:
|
||||
info = "购买产品"
|
||||
t = 1
|
||||
}
|
||||
bill := m.Bill{
|
||||
UserID: authContext.Payload.Id,
|
||||
TradeID: trade.ID,
|
||||
BillNo: services.ID.GenReadable("bil"),
|
||||
Info: info,
|
||||
Type: t,
|
||||
Status: 0,
|
||||
}
|
||||
err = q.Bill.
|
||||
Omit(q.Bill.ResourceID, q.Bill.RefundID).
|
||||
Create(&bill)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 返回结果,外部支付链接
|
||||
return nil
|
||||
}
|
||||
|
||||
func createTradeByAlipay() (*url.URL, error) {
|
||||
|
||||
target, err := g.Alipay.TradePagePay(alipay.TradePagePay{
|
||||
Trade: alipay.Trade{},
|
||||
AuthToken: "",
|
||||
QRPayMode: "",
|
||||
QRCodeWidth: "",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return target, nil
|
||||
}
|
||||
|
||||
func createTradeByWechat() error {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region TradeCallbackAlipay
|
||||
|
||||
// endregion
|
||||
|
||||
// region TradeCallbackWechat
|
||||
|
||||
// endregion
|
||||
@@ -33,6 +33,10 @@ func ApplyRouters(app *fiber.App) {
|
||||
resource.Post("/list/pss", handlers.ListResourcePss)
|
||||
resource.Post("/all", handlers.AllResource)
|
||||
resource.Post("/create/balance", handlers.CreateResourceByBalance)
|
||||
resource.Post("/prepare/alipay", handlers.PrepareResourceByAlipay)
|
||||
resource.Post("/create/alipay", handlers.CreateResourceByAlipay)
|
||||
resource.Post("/prepare/wechat", handlers.PrepareResourceByWechat)
|
||||
resource.Post("/create/wechat", handlers.CreateResourceByWechat)
|
||||
|
||||
// 用户
|
||||
user := api.Group("/user")
|
||||
@@ -40,10 +44,6 @@ func ApplyRouters(app *fiber.App) {
|
||||
user.Post("/identify", handlers.Identify)
|
||||
user.Post("/identify/callback", handlers.IdentifyCallback)
|
||||
|
||||
// 支付
|
||||
trade := api.Group("/trade")
|
||||
trade.Post("/create", handlers.CreateTrade)
|
||||
|
||||
// 账单
|
||||
bill := api.Group("/bill")
|
||||
bill.Post("/list", handlers.ListBill)
|
||||
|
||||
Reference in New Issue
Block a user