增加支付功能
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"server/models"
|
||||
"server/pkg/jwtutil"
|
||||
"server/services/payment"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
beego "github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
|
||||
// BackendPaymentController 租户端支付接口(下单页 / 收银台)
|
||||
// 租户端身份为 jwt UserType=backend,TenantId 即租户ID。
|
||||
type BackendPaymentController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
func (c *BackendPaymentController) backendClaims() (*jwtutil.Claims, error) {
|
||||
auth := c.Ctx.Request.Header.Get("Authorization")
|
||||
if auth == "" {
|
||||
return nil, fmt.Errorf("未登录")
|
||||
}
|
||||
parts := strings.SplitN(auth, " ", 2)
|
||||
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||
return nil, fmt.Errorf("认证信息格式错误")
|
||||
}
|
||||
claims, err := jwtutil.ParseToken(parts[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("无效的token")
|
||||
}
|
||||
if claims.UserType != "backend" {
|
||||
return nil, fmt.Errorf("无权访问")
|
||||
}
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
func (c *BackendPaymentController) jsonErr(httpStatus, bizCode int, msg string) {
|
||||
c.Ctx.Output.SetStatus(httpStatus)
|
||||
c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg}
|
||||
_ = c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *BackendPaymentController) ok(data interface{}) {
|
||||
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": data}
|
||||
_ = c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *BackendPaymentController) tenantID() string {
|
||||
claims, _ := c.backendClaims()
|
||||
if claims == nil {
|
||||
return ""
|
||||
}
|
||||
return strconv.Itoa(claims.TenantId)
|
||||
}
|
||||
|
||||
// tenantName 查询租户名称(下单快照用)
|
||||
func (c *BackendPaymentController) tenantName() string {
|
||||
claims, err := c.backendClaims()
|
||||
if err != nil || claims.TenantId <= 0 {
|
||||
return ""
|
||||
}
|
||||
tenant := &models.SystemTenant{}
|
||||
if err := models.Orm.QueryTable(new(models.SystemTenant)).
|
||||
Filter("id", uint64(claims.TenantId)).
|
||||
Filter("delete_time__isnull", true).
|
||||
One(tenant); err != nil {
|
||||
return ""
|
||||
}
|
||||
return tenant.TenantName
|
||||
}
|
||||
|
||||
// Channels GET /backend/payment/channels 收银台可用渠道(已启用且参数完整)
|
||||
func (c *BackendPaymentController) Channels() {
|
||||
if _, err := c.backendClaims(); err != nil {
|
||||
c.jsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
var rows []models.PlatformPaymentChannel
|
||||
_, err := models.Orm.QueryTable(new(models.PlatformPaymentChannel)).
|
||||
Filter("enabled", 1).
|
||||
Filter("delete_time__isnull", true).
|
||||
OrderBy("id").
|
||||
All(&rows)
|
||||
if err != nil && err != orm.ErrNoRows {
|
||||
c.jsonErr(500, 500, "查询渠道失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
list := make([]map[string]interface{}, 0, len(rows))
|
||||
for i := range rows {
|
||||
cfg, err := payment.LoadChannelConfig(rows[i].Channel)
|
||||
if err != nil || len(cfg.Params) == 0 {
|
||||
continue // 参数不完整的渠道不下发
|
||||
}
|
||||
extra := map[string]interface{}{}
|
||||
for k, v := range cfg.Extra {
|
||||
extra[k] = v
|
||||
}
|
||||
list = append(list, map[string]interface{}{
|
||||
"channel": rows[i].Channel, "name": rows[i].Name, "extra": extra,
|
||||
})
|
||||
}
|
||||
c.ok(map[string]interface{}{"list": list})
|
||||
}
|
||||
|
||||
// Create POST /backend/payment/create 创建支付单并返回渠道支付参数
|
||||
// 入参:{ out_trade_no, order_type, amount(分), channel, subject, return_url, pay_type, openid,
|
||||
//
|
||||
// promoter_id?, promoter_name?, promoter_type?, expire_minutes? }
|
||||
//
|
||||
// 出参:{ pay_no, channel, pay_params }
|
||||
func (c *BackendPaymentController) Create() {
|
||||
claims, err := c.backendClaims()
|
||||
if err != nil {
|
||||
c.jsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
body, _ := io.ReadAll(c.Ctx.Request.Body)
|
||||
var p struct {
|
||||
OutTradeNo string `json:"out_trade_no"`
|
||||
OrderType string `json:"order_type"`
|
||||
Amount int64 `json:"amount"`
|
||||
Channel string `json:"channel"`
|
||||
Subject string `json:"subject"`
|
||||
ReturnURL string `json:"return_url"`
|
||||
PayType string `json:"pay_type"`
|
||||
OpenID string `json:"openid"`
|
||||
PromoterID string `json:"promoter_id"`
|
||||
PromoterName string `json:"promoter_name"`
|
||||
PromoterType string `json:"promoter_type"`
|
||||
ExpireMinutes int `json:"expire_minutes"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &p); err != nil {
|
||||
c.jsonErr(400, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
if strings.TrimSpace(p.OutTradeNo) == "" {
|
||||
c.jsonErr(400, 400, "业务订单号不能为空")
|
||||
return
|
||||
}
|
||||
if p.Amount <= 0 {
|
||||
c.jsonErr(400, 400, "支付金额必须大于 0")
|
||||
return
|
||||
}
|
||||
if strings.TrimSpace(p.Channel) == "" {
|
||||
c.jsonErr(400, 400, "请选择支付渠道")
|
||||
return
|
||||
}
|
||||
if p.OrderType == "" {
|
||||
p.OrderType = models.CommissionOrderTypePlatformUsage
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
order, params, err := payment.CreatePayment(ctx, payment.CreateInput{
|
||||
OutTradeNo: strings.TrimSpace(p.OutTradeNo),
|
||||
OrderType: p.OrderType,
|
||||
TenantID: strconv.Itoa(claims.TenantId),
|
||||
TenantName: c.tenantName(),
|
||||
Amount: p.Amount,
|
||||
Channel: strings.TrimSpace(p.Channel),
|
||||
Subject: strings.TrimSpace(p.Subject),
|
||||
ReturnURL: strings.TrimSpace(p.ReturnURL),
|
||||
ClientIP: c.Ctx.Input.IP(),
|
||||
PayType: p.PayType,
|
||||
OpenID: p.OpenID,
|
||||
PromoterID: strings.TrimSpace(p.PromoterID),
|
||||
PromoterName: strings.TrimSpace(p.PromoterName),
|
||||
PromoterType: strings.TrimSpace(p.PromoterType),
|
||||
ExpireMinutes: p.ExpireMinutes,
|
||||
})
|
||||
if err != nil {
|
||||
c.jsonErr(400, 400, err.Error())
|
||||
return
|
||||
}
|
||||
c.ok(map[string]interface{}{
|
||||
"pay_no": order.PayNo,
|
||||
"channel": order.Channel,
|
||||
"status": order.Status,
|
||||
"pay_params": params,
|
||||
})
|
||||
}
|
||||
|
||||
// Query GET /backend/payment/query/:payNo 查询支付单(仅本租户)
|
||||
func (c *BackendPaymentController) Query() {
|
||||
if _, err := c.backendClaims(); err != nil {
|
||||
c.jsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
payNo := c.Ctx.Input.Param(":payNo")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
row, err := payment.QueryPayment(ctx, payNo, true)
|
||||
if err != nil {
|
||||
c.jsonErr(404, 404, "支付单不存在")
|
||||
return
|
||||
}
|
||||
if row.TenantID != c.tenantID() {
|
||||
c.jsonErr(403, 403, "无权访问该支付单")
|
||||
return
|
||||
}
|
||||
c.ok(map[string]interface{}{
|
||||
"pay_no": row.PayNo, "out_trade_no": row.OutTradeNo,
|
||||
"amount": row.Amount, "channel": row.Channel, "status": row.Status,
|
||||
"paid_at": row.PaidAt, "create_time": row.CreateTime,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user