201 lines
6.5 KiB
Go
201 lines
6.5 KiB
Go
package test
|
||
|
||
import (
|
||
"backend/Type"
|
||
"backend/client"
|
||
"backend/common"
|
||
"backend/middleware/feishu"
|
||
"backend/msg"
|
||
"backend/sdk/ship/model/base"
|
||
"backend/util"
|
||
"crypto/md5"
|
||
"encoding/json"
|
||
"fmt"
|
||
"log"
|
||
"reflect"
|
||
"sort"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
type TestModel struct {
|
||
}
|
||
|
||
type ChargeRequest struct {
|
||
ApiVer string `json:"apiver" form:"apiver" binding:"required" description:"SDK服务端支付API版本"`
|
||
AppID string `json:"appId" form:"appId" binding:"required" description:"cp应用ID"`
|
||
AppInfo string `json:"appInfo" form:"appInfo" description:"项目透传信息"`
|
||
ChargedDiamonds string `json:"chargedDiamonds" form:"chargedDiamonds" description:"充值钻石数量,一般为充值金额的10倍"`
|
||
ChargedRmbs string `json:"chargedRmbs" form:"chargedRmbs" description:"充值金额(元)"`
|
||
ClientID string `json:"clientId" form:"clientId" binding:"required" description:"途游客户端包id"`
|
||
ConsumeCoin string `json:"consumeCoin" form:"consumeCoin" description:"消耗金币数量,一般为充值金额的10倍"`
|
||
ConsumeID string `json:"consumeId" form:"consumeId" description:"本次消耗事务id"`
|
||
OrderID string `json:"orderId" form:"orderId" description:"CP订单号"`
|
||
PlatformOrder string `json:"platformOrder" form:"platformOrder" description:"SDK订单号"`
|
||
ProdCount string `json:"prodCount" form:"prodCount" description:"商品数量,默认为1"`
|
||
UserID string `json:"userId" form:"userId" binding:"required" description:"SDK用户ID"`
|
||
ProdID string `json:"prodId" form:"prodId" description:"商品ID"`
|
||
ProdPrice string `json:"prodPrice" form:"prodPrice" description:"商品价格"`
|
||
Code string `json:"code" form:"code" binding:"required" description:"回调签名"`
|
||
}
|
||
|
||
const (
|
||
APP_KEY = "8001bd25832553c39d4b443f56480841"
|
||
)
|
||
|
||
// 增加 Sign 方法:按 json tag 名称收集参数,排除 code,按 ASCII 升序拼接 key=value 用 & 连接,最后 MD5 并返回小写十六进制字符串。
|
||
func (r *ChargeRequest) Sign() string {
|
||
if r == nil {
|
||
return ""
|
||
}
|
||
|
||
// 收集参数到 map[key]=string(value)
|
||
params := make(map[string]string)
|
||
val := reflect.ValueOf(*r)
|
||
typ := reflect.TypeOf(*r)
|
||
for i := 0; i < typ.NumField(); i++ {
|
||
field := typ.Field(i)
|
||
// 使用 json tag 作为键名,若为空则 fallback 到字段名
|
||
tag := field.Tag.Get("json")
|
||
key := strings.Split(tag, ",")[0]
|
||
if key == "" {
|
||
key = field.Name
|
||
}
|
||
// 排除 code
|
||
if key == "code" {
|
||
continue
|
||
}
|
||
fv := val.Field(i)
|
||
var s string
|
||
if fv.Kind() == reflect.String {
|
||
s = fv.String()
|
||
} else {
|
||
s = fmt.Sprintf("%v", fv.Interface())
|
||
}
|
||
params[key] = s
|
||
}
|
||
|
||
// 排序键名
|
||
keys := make([]string, 0, len(params))
|
||
for k := range params {
|
||
keys = append(keys, k)
|
||
}
|
||
sort.Strings(keys)
|
||
|
||
// 拼接签名源字符串
|
||
var b strings.Builder
|
||
for i, k := range keys {
|
||
if i > 0 {
|
||
b.WriteByte('&')
|
||
}
|
||
b.WriteString(k)
|
||
b.WriteByte('=')
|
||
b.WriteString(params[k])
|
||
}
|
||
checkstr := b.String()
|
||
checkstr = APP_KEY + checkstr + APP_KEY
|
||
// 计算 MD5 并返回小写十六进制字符串
|
||
sum := md5.Sum([]byte(checkstr))
|
||
return fmt.Sprintf("%x", sum)
|
||
}
|
||
|
||
func Charge(c *gin.Context) {
|
||
var req ChargeRequest
|
||
if err := c.ShouldBind(&req); err != nil {
|
||
log.Print("binding error:", err)
|
||
c.JSON(400, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
expectedSign := req.Sign()
|
||
if req.Code != expectedSign {
|
||
// FeishuNotify(fmt.Sprintf("invalid signature: got %s, expected %s", req.Code, expectedSign))
|
||
// log.Printf("invalid signature: got %s, expected %s", req.Code, expectedSign)
|
||
// c.JSON(401, gin.H{"error": "invalid signature"})
|
||
// return
|
||
}
|
||
var AppInfo base.Param
|
||
if err := json.Unmarshal([]byte(req.AppInfo), &AppInfo); err != nil {
|
||
FeishuNotify(fmt.Sprintf("unmarshal AppInfo error:%v", err))
|
||
log.Print("unmarshal AppInfo error:", err)
|
||
c.JSON(400, gin.H{"error": "invalid AppInfo"})
|
||
return
|
||
}
|
||
if AppInfo.Uid != 100001 {
|
||
log.Printf("test debug error request %v", AppInfo)
|
||
return
|
||
}
|
||
AppInfo.ChannelOrderId = req.PlatformOrder
|
||
err := AppInfo.ChangeOrderStatus("test", req.ProdPrice)
|
||
if err != nil {
|
||
//alibaba.SendStandardMsg("途游充值发货错误-测试", err.Error(), "red")
|
||
log.Print("change order status error:", err)
|
||
c.JSON(500, gin.H{"error": "failed to change order status"})
|
||
return
|
||
}
|
||
go Shipping(AppInfo)
|
||
c.String(200, "success")
|
||
}
|
||
|
||
// http://localhost:5240/api/tuyou/charge?apiver=2&appId=20659&appInfo=%7B%22appId%22%3A0%2C%22serverId%22%3A1%2C%22orderId%22%3A%22order_105372_20260125225337HhpqbU%22%2C%22uid%22%3A105372%7D&chargedDiamonds=20&chargedRmbs=1.99&clientId=Android_5.00_tyGuest%2Cfacebook.googleplay.0-hall20659.googleplay.Meowment&consumeCoin=20&consumeId=d50b32601260117387&orderId=-&platformOrder=e50b3260126045d972&prodCount=1&prodId=TY206590059&prodPrice=1.99&userId=3790944&code=fd7532d651bed4c3041aa3e628bef80b
|
||
func Shipping(AppInfo base.Param) {
|
||
if common.GetRpcSwitch() {
|
||
err := client.OrderShipping(AppInfo.AppId, AppInfo.ServerId, &msg.ReqOrderShipping{
|
||
OrderSn: AppInfo.OrderId,
|
||
Status: 1,
|
||
ChannelOrderSn: AppInfo.ChannelOrderId,
|
||
})
|
||
|
||
if err == nil {
|
||
log.Printf("rpc order shipping success;AppId:%d;ServerId:%d;OrderId:%s", AppInfo.AppId, AppInfo.ServerId, AppInfo.OrderId)
|
||
return
|
||
}
|
||
}
|
||
|
||
Adminreq := &msg.ReqAdminShipping{
|
||
OrderSn: AppInfo.OrderId,
|
||
Status: 1,
|
||
ChannelOrderSn: AppInfo.ChannelOrderId,
|
||
}
|
||
num := 0
|
||
log.Print("charge shipping start;AppId:", AppInfo.AppId, ";ServerId:", AppInfo.ServerId, ";OrderId:", AppInfo.OrderId)
|
||
for {
|
||
num++
|
||
if num > 100 {
|
||
log.Print("charge shipping break infinite loop;AppId:", AppInfo.AppId, ";ServerId:", AppInfo.ServerId, ";OrderId:", AppInfo.OrderId)
|
||
break
|
||
}
|
||
|
||
ws, err := util.GetWebsocket(AppInfo.AppId, AppInfo.ServerId)
|
||
if err != nil {
|
||
time.Sleep(time.Second)
|
||
continue
|
||
}
|
||
|
||
r, err := util.SendAdminMsg(ws, Adminreq)
|
||
// close the websocket immediately to avoid accumulating defers in the loop
|
||
if closeErr := ws.Close(); closeErr != nil {
|
||
log.Printf("failed to close websocket: %v", closeErr)
|
||
}
|
||
if err != nil {
|
||
time.Sleep(time.Second)
|
||
continue
|
||
}
|
||
if r != nil {
|
||
log.Printf("ws charge shipping success:orderSn:%s;res:%v", AppInfo.OrderId, r)
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
func FeishuNotify(EvnetName string) {
|
||
feishu.SendNotifyMsg(&Type.NotifyData{
|
||
NotifyMsg: "支付发货失败",
|
||
Host: "途游",
|
||
EventName: EvnetName,
|
||
Severity: "High",
|
||
AlarmTime: time.Unix(time.Now().Unix(), 0).Format("2006-01-02 15:04:05"),
|
||
})
|
||
}
|