feat: 下单接口v2
This commit is contained in:
parent
49ce121677
commit
0ae425e4bb
|
@ -5,10 +5,13 @@ const (
|
||||||
TOKEN_Admin = "Admin_token_"
|
TOKEN_Admin = "Admin_token_"
|
||||||
ADMIN_V1 = "/pay/admin/api/v1"
|
ADMIN_V1 = "/pay/admin/api/v1"
|
||||||
FRONT_V1 = "/pay/front/api/v1"
|
FRONT_V1 = "/pay/front/api/v1"
|
||||||
|
FRONT_V2 = "/pay/front/api/v2"
|
||||||
|
|
||||||
WXCodeRedirectAddress = FRONT_V1 + "/wx/payurl" // 微信支付,授权code后 重定向地址
|
WXCodeRedirectAddress = FRONT_V1 + "/wx/payurl" // 微信支付,授权code后 重定向地址
|
||||||
|
PayPageAddress = FRONT_V1 + "/payPage" // 收银台页面地址
|
||||||
|
PayPageChannelList = FRONT_V1 + "/payPage/list" // 收银台支付方式列表地址
|
||||||
|
|
||||||
// 支付渠道枚举,1微信JSAPI,2微信H5,3微信app,4微信Native,5微信小程序,6支付宝网页&移动应用,7支付宝小程序,8支付宝JSAPI
|
// 支付渠道枚举,1微信JSAPI,2微信H5,3微信app,4微信Native,5微信小程序,6支付宝网页&移动应用,7支付宝小程序,8支付宝JSAPI,9支付宝电脑网站支付
|
||||||
PAY_CHANNEL_UNKNOWN = 0
|
PAY_CHANNEL_UNKNOWN = 0
|
||||||
PAY_CHANNEL_WECHAT_JSAPI = 1
|
PAY_CHANNEL_WECHAT_JSAPI = 1
|
||||||
PAY_CHANNEL_WECHAT_H5 = 2
|
PAY_CHANNEL_WECHAT_H5 = 2
|
||||||
|
@ -18,6 +21,7 @@ const (
|
||||||
PAY_CHANNEL_ALIPAY_WEB = 6
|
PAY_CHANNEL_ALIPAY_WEB = 6
|
||||||
PAY_CHANNEL_ALIPAY_MINI = 7
|
PAY_CHANNEL_ALIPAY_MINI = 7
|
||||||
PAY_CHANNEL_ALIPAY_JSAPI = 8
|
PAY_CHANNEL_ALIPAY_JSAPI = 8
|
||||||
|
PAY_CHANNEL_ALIPAY_PC = 9
|
||||||
|
|
||||||
// 统一登陆信息
|
// 统一登陆信息
|
||||||
ADMIN_USER_ID = "User-Id"
|
ADMIN_USER_ID = "User-Id"
|
||||||
|
@ -75,3 +79,18 @@ var OrderStatusMap = map[int]string{
|
||||||
ORDER_STATUS_FAILED: "执行失败",
|
ORDER_STATUS_FAILED: "执行失败",
|
||||||
ORDER_STATUS_CLOSE: "订单关闭",
|
ORDER_STATUS_CLOSE: "订单关闭",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 客户端环境
|
||||||
|
const (
|
||||||
|
OpenInUnknown int = iota // 未知
|
||||||
|
OpenInWeChat // 微信客户端
|
||||||
|
OpenInMobile // 手机客户端
|
||||||
|
OpenInWindows // PC端
|
||||||
|
)
|
||||||
|
|
||||||
|
// 客户端环境和支付渠道的映射关系
|
||||||
|
var OpenInPayChannelMap = map[int][]int{
|
||||||
|
OpenInWeChat: {PAY_CHANNEL_WECHAT_JSAPI},
|
||||||
|
OpenInMobile: {PAY_CHANNEL_WECHAT_H5, PAY_CHANNEL_ALIPAY_WEB},
|
||||||
|
OpenInWindows: {PAY_CHANNEL_WECHAT_NATIVE, PAY_CHANNEL_ALIPAY_PC},
|
||||||
|
}
|
||||||
|
|
|
@ -101,6 +101,8 @@ const (
|
||||||
// 微信授权
|
// 微信授权
|
||||||
WechatAuthFail = 1901
|
WechatAuthFail = 1901
|
||||||
WechatAuthSignFail = 1902
|
WechatAuthSignFail = 1902
|
||||||
|
|
||||||
|
ClientEnvErr = 2000
|
||||||
)
|
)
|
||||||
|
|
||||||
var MsgEN = map[int]string{
|
var MsgEN = map[int]string{
|
||||||
|
@ -187,6 +189,7 @@ var MsgZH = map[int]string{
|
||||||
|
|
||||||
WechatAuthFail: "微信授权失败",
|
WechatAuthFail: "微信授权失败",
|
||||||
WechatAuthSignFail: "微信签名失败",
|
WechatAuthSignFail: "微信签名失败",
|
||||||
|
ClientEnvErr: "支付环境错误",
|
||||||
}
|
}
|
||||||
var MsgMap map[string]map[int]string = map[string]map[int]string{"en": MsgZH}
|
var MsgMap map[string]map[int]string = map[string]map[int]string{"en": MsgZH}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
package front
|
||||||
|
|
||||||
|
import (
|
||||||
|
"PaymentCenter/app/http/controllers"
|
||||||
|
"PaymentCenter/app/http/entities/front"
|
||||||
|
"PaymentCenter/app/models/paychannelmodel"
|
||||||
|
"PaymentCenter/app/services"
|
||||||
|
"PaymentCenter/app/services/thirdpay"
|
||||||
|
"github.com/ahmetb/go-linq/v3"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 预支付接口V2, 返回收银台页面
|
||||||
|
func PayUrlV2(c *gin.Context) {
|
||||||
|
var res front.ApiResponse
|
||||||
|
req := controllers.GetRequest(c).(*front.PayReqsV2)
|
||||||
|
|
||||||
|
appCheckInfo := controllers.GetAppCheckInfo(c).(*services.AppCheck)
|
||||||
|
|
||||||
|
result, code := services.NewPayUrl(*req).
|
||||||
|
WithApp(appCheckInfo).
|
||||||
|
WithClientIp(c.ClientIP()).
|
||||||
|
PayUrlV2Service()
|
||||||
|
|
||||||
|
if result.Order != nil {
|
||||||
|
res.Order = thirdpay.NewOrdersResp(result.Order)
|
||||||
|
res.Url = result.Url
|
||||||
|
}
|
||||||
|
controllers.ApiRes(c, res, code)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// V2 下单接口
|
||||||
|
// 收银台页面
|
||||||
|
func PayPage(c *gin.Context) {
|
||||||
|
c.HTML(http.StatusOK, "payPage.html", gin.H{
|
||||||
|
//"host": config.GetConf().PayService.Host + common.PayPageChannelList,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 收银台获取支付渠道列表
|
||||||
|
func PayChannelList(c *gin.Context) {
|
||||||
|
req, _ := controllers.GetRequest(c).(*front.PayChannelListRequest)
|
||||||
|
req.UserAgent = c.Request.UserAgent()
|
||||||
|
data, code := services.PayPageChannelList(*req)
|
||||||
|
|
||||||
|
result := []front.PayChannelListResponse{}
|
||||||
|
linq.From(data).SelectT(func(payChannel paychannelmodel.PayChannel) front.PayChannelListResponse {
|
||||||
|
return front.PayChannelListResponse{
|
||||||
|
ChannelType: payChannel.ChannelType,
|
||||||
|
PayName: payChannel.PayName,
|
||||||
|
}
|
||||||
|
}).ToSlice(&result)
|
||||||
|
|
||||||
|
controllers.HandCodeRes(c, result, code)
|
||||||
|
}
|
|
@ -1,5 +1,9 @@
|
||||||
package front
|
package front
|
||||||
|
|
||||||
|
import (
|
||||||
|
"PaymentCenter/app/models/ordersmodel"
|
||||||
|
)
|
||||||
|
|
||||||
type ApiCommonBody struct {
|
type ApiCommonBody struct {
|
||||||
AppId int64 `json:"app_id" validate:"required"`
|
AppId int64 `json:"app_id" validate:"required"`
|
||||||
Timestamp int64 `json:"timestamp" validate:"required"`
|
Timestamp int64 `json:"timestamp" validate:"required"`
|
||||||
|
@ -56,3 +60,33 @@ type ApiResponse struct {
|
||||||
Order interface{} `json:"order,omitempty"`
|
Order interface{} `json:"order,omitempty"`
|
||||||
Url string `json:"url,omitempty"`
|
Url string `json:"url,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PayChannelListRequest struct {
|
||||||
|
OrderId string `json:"id" form:"id" validate:"required"`
|
||||||
|
UserAgent string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PayChannelListResponse struct {
|
||||||
|
PayName string `json:"pay_name"`
|
||||||
|
ChannelType int `json:"channel_type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PayReqsV2 struct {
|
||||||
|
AppId int64 `json:"app_id" validate:"required"`
|
||||||
|
Timestamp int64 `json:"timestamp" validate:"required"`
|
||||||
|
Amount int `json:"amount" validate:"required" label:"支付金额,单位分"`
|
||||||
|
OutTradeNo string `json:"out_trade_no" validate:"required" label:"外侧商户订单号"`
|
||||||
|
|
||||||
|
ReturnUrl string `json:"return_url" validate:"max=1024" label:"支付成功后跳转的地址"`
|
||||||
|
Desc string `json:"desc" validate:"max=100" label:"商品描述"`
|
||||||
|
ExtJson string `json:"ext_json" label:"扩展参数"`
|
||||||
|
OpenId string `json:"open_id" label:"用户openid"` // 微信公众号支付时需要,如果没有传,可走支付中心的授权获取openid
|
||||||
|
}
|
||||||
|
|
||||||
|
type PayReqsV2Response struct {
|
||||||
|
RelationOrder *ordersmodel.Orders
|
||||||
|
Order *ordersmodel.Orders
|
||||||
|
PayCode int
|
||||||
|
Url string
|
||||||
|
ThirdMsg string
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
var FrontRequestMap = map[string]func() (validForm interface{}, isSaveLog bool){
|
var FrontRequestMap = map[string]func() (validForm interface{}, isSaveLog bool){
|
||||||
common.FRONT_V1 + "/pay/url": func() (interface{}, bool) { return new(front.PayReqs), true },
|
common.FRONT_V1 + "/pay/url": func() (interface{}, bool) { return new(front.PayReqs), true },
|
||||||
|
common.FRONT_V2 + "/pay/url": func() (interface{}, bool) { return new(front.PayReqsV2), true },
|
||||||
common.FRONT_V1 + "/pay/refund": func() (interface{}, bool) { return new(front.RefundReqs), true },
|
common.FRONT_V1 + "/pay/refund": func() (interface{}, bool) { return new(front.RefundReqs), true },
|
||||||
common.FRONT_V1 + "/pay/query": func() (interface{}, bool) { return new(front.QueryReqs), false },
|
common.FRONT_V1 + "/pay/query": func() (interface{}, bool) { return new(front.QueryReqs), false },
|
||||||
common.FRONT_V1 + "/pay/close": func() (interface{}, bool) { return new(front.CloseReqs), true },
|
common.FRONT_V1 + "/pay/close": func() (interface{}, bool) { return new(front.CloseReqs), true },
|
||||||
|
@ -17,6 +18,7 @@ var FrontRequestMap = map[string]func() (validForm interface{}, isSaveLog bool){
|
||||||
var FrontRequestMapBeforeDecrypt = map[string]func() interface{}{
|
var FrontRequestMapBeforeDecrypt = map[string]func() interface{}{
|
||||||
|
|
||||||
common.FRONT_V1 + "/pay/url": func() interface{} { return new(front.RequestBody) },
|
common.FRONT_V1 + "/pay/url": func() interface{} { return new(front.RequestBody) },
|
||||||
|
common.FRONT_V2 + "/pay/url": func() interface{} { return new(front.RequestBody) },
|
||||||
common.FRONT_V1 + "/pay/refund": func() interface{} { return new(front.RequestBody) },
|
common.FRONT_V1 + "/pay/refund": func() interface{} { return new(front.RequestBody) },
|
||||||
common.FRONT_V1 + "/pay/query": func() interface{} { return new(front.RequestBody) },
|
common.FRONT_V1 + "/pay/query": func() interface{} { return new(front.RequestBody) },
|
||||||
common.FRONT_V1 + "/pay/close": func() interface{} { return new(front.CloseReqs) },
|
common.FRONT_V1 + "/pay/close": func() interface{} { return new(front.CloseReqs) },
|
||||||
|
@ -25,5 +27,7 @@ var FrontRequestMapBeforeDecrypt = map[string]func() interface{}{
|
||||||
common.FRONT_V1 + "/wx/getWxAuth": func() interface{} { return new(front.GetWxAuthRequest) },
|
common.FRONT_V1 + "/wx/getWxAuth": func() interface{} { return new(front.GetWxAuthRequest) },
|
||||||
common.FRONT_V1 + "/wx/getCode": func() interface{} { return new(front.GetWxAuthRequest) },
|
common.FRONT_V1 + "/wx/getCode": func() interface{} { return new(front.GetWxAuthRequest) },
|
||||||
|
|
||||||
|
common.FRONT_V1 + "/payPage/list": func() interface{} { return new(front.PayChannelListRequest) },
|
||||||
|
|
||||||
common.WXCodeRedirectAddress: func() interface{} { return new(front.WxJsApiPayRequest) },
|
common.WXCodeRedirectAddress: func() interface{} { return new(front.WxJsApiPayRequest) },
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,6 +79,20 @@ func RegisterRoute(router *gin.Engine) {
|
||||||
// 微信jsapi支付链接
|
// 微信jsapi支付链接
|
||||||
router.GET(common.WXCodeRedirectAddress, middlewares.ValidateRequest(), front.WxJsApiPay)
|
router.GET(common.WXCodeRedirectAddress, middlewares.ValidateRequest(), front.WxJsApiPay)
|
||||||
|
|
||||||
|
v2 := router.Group(common.FRONT_V2)
|
||||||
|
{
|
||||||
|
pay := v2.Group("/pay", middlewares.ValidatePayRequest())
|
||||||
|
pay.POST("/url", front.PayUrlV2) // 下单
|
||||||
|
}
|
||||||
|
|
||||||
|
//收银台
|
||||||
|
{
|
||||||
|
// 收银台地址
|
||||||
|
router.GET(common.PayPageAddress, front.PayPage)
|
||||||
|
// 收银台支付方式
|
||||||
|
router.POST(common.PayPageChannelList, middlewares.ValidateRequest(), front.PayChannelList)
|
||||||
|
}
|
||||||
|
|
||||||
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,7 +137,7 @@ func OrderFindOne(order *ordersmodel.Orders, conn builder.Cond, col ...string) (
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return nil, errorcode.OrdersNotFound
|
return nil, errorcode.OrdersNotFound
|
||||||
}
|
}
|
||||||
return nil, errorcode.SystemError
|
return nil, handErr(err)
|
||||||
}
|
}
|
||||||
return orderInfo, errorcode.Success
|
return orderInfo, errorcode.Success
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,189 @@
|
||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"PaymentCenter/app/constants/common"
|
||||||
|
"PaymentCenter/app/constants/errorcode"
|
||||||
|
"PaymentCenter/app/http/entities"
|
||||||
|
"PaymentCenter/app/http/entities/backend"
|
||||||
|
"PaymentCenter/app/http/entities/front"
|
||||||
|
"PaymentCenter/app/models/ordersmodel"
|
||||||
|
"PaymentCenter/app/models/paychannelmodel"
|
||||||
|
"PaymentCenter/app/utils"
|
||||||
|
"PaymentCenter/config"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
"xorm.io/builder"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 收银台支付渠道列表
|
||||||
|
func PayPageChannelList(reqParam front.PayChannelListRequest) (resultPayChannelList []paychannelmodel.PayChannel, code int) {
|
||||||
|
|
||||||
|
orderId, err := strconv.ParseInt(reqParam.OrderId, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
code = errorcode.ParamError
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 订单检查
|
||||||
|
orderInfo := &ordersmodel.Orders{Id: orderId}
|
||||||
|
orderInfo, code = OrderFindOne(orderInfo, builder.Eq{"id": orderId})
|
||||||
|
if code != errorcode.Success {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !(orderInfo.Status == common.ORDER_STATUS_PAYING || orderInfo.Status == common.ORDER_STATUS_WAITPAY) {
|
||||||
|
code = errorcode.OrderStatusErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 商户拥有的支付渠道检查
|
||||||
|
req := backend.PayChannelListRequest{
|
||||||
|
MerchantId: orderInfo.MerchantId,
|
||||||
|
PageRequest: entities.PageRequest{},
|
||||||
|
}
|
||||||
|
payList := make([]paychannelmodel.PayChannel, 0)
|
||||||
|
payList, _, code = PayChannelList(req)
|
||||||
|
if code != errorcode.Success {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(payList) == 0 {
|
||||||
|
code = errorcode.PayChannelNotFound
|
||||||
|
return
|
||||||
|
}
|
||||||
|
merchantPayChannelMap := make(map[int]paychannelmodel.PayChannel, 0)
|
||||||
|
for _, pay := range payList {
|
||||||
|
merchantPayChannelMap[pay.ChannelType] = pay
|
||||||
|
}
|
||||||
|
|
||||||
|
// 客户端环境检查
|
||||||
|
openType := ClientEnvCheck(reqParam.UserAgent)
|
||||||
|
if channels, ok := common.OpenInPayChannelMap[openType]; !ok {
|
||||||
|
code = errorcode.ClientEnvErr
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
for _, channel := range channels {
|
||||||
|
if _, ok = merchantPayChannelMap[channel]; ok {
|
||||||
|
resultPayChannelList = append(resultPayChannelList, merchantPayChannelMap[channel])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultPayChannelList, code
|
||||||
|
}
|
||||||
|
|
||||||
|
// 客户端环境检查
|
||||||
|
func ClientEnvCheck(ua string) int {
|
||||||
|
if utils.IsWeChatClient(ua) {
|
||||||
|
return common.OpenInWeChat
|
||||||
|
} else if utils.IsPC(ua) {
|
||||||
|
return common.OpenInWindows
|
||||||
|
} else if utils.IsMobile(ua) {
|
||||||
|
return common.OpenInMobile
|
||||||
|
}
|
||||||
|
|
||||||
|
return common.OpenInUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预支付
|
||||||
|
type payUrl struct {
|
||||||
|
ClientIp string
|
||||||
|
param front.PayReqsV2
|
||||||
|
app *AppCheck
|
||||||
|
result front.PayReqsV2Response
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPayUrl(param front.PayReqsV2) *payUrl {
|
||||||
|
return &payUrl{
|
||||||
|
param: param,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (this *payUrl) WithApp(app *AppCheck) *payUrl {
|
||||||
|
this.app = app
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
func (this *payUrl) WithClientIp(ip string) *payUrl {
|
||||||
|
this.ClientIp = ip
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
// 订单存在
|
||||||
|
func (this *payUrl) orderExist() {
|
||||||
|
switch this.result.Order.Status {
|
||||||
|
case common.ORDER_STATUS_CLOSE:
|
||||||
|
this.result.PayCode = errorcode.OrderClosed
|
||||||
|
case common.ORDER_STATUS_PAYED:
|
||||||
|
this.result.PayCode = errorcode.OrderPayed
|
||||||
|
case common.ORDER_STATUS_FAILED:
|
||||||
|
this.result.PayCode = errorcode.OrderFailed
|
||||||
|
case common.ORDER_STATUS_WAITPAY, common.ORDER_STATUS_PAYING:
|
||||||
|
this.result.PayCode = errorcode.Success
|
||||||
|
default:
|
||||||
|
this.result.PayCode = errorcode.OrderStatusErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *payUrl) saveOrder() {
|
||||||
|
order := &ordersmodel.Orders{
|
||||||
|
MerchantId: this.app.App.MerchantId,
|
||||||
|
//PayChannelId: w.PayParam.Channel.Id,
|
||||||
|
AppId: this.app.App.Id,
|
||||||
|
OutTradeNo: this.param.OutTradeNo,
|
||||||
|
OrderType: common.ORDER_TYPE_PAY,
|
||||||
|
Amount: this.param.Amount,
|
||||||
|
ExtJson: this.param.ExtJson,
|
||||||
|
Desc: this.param.Desc,
|
||||||
|
Status: common.ORDER_STATUS_WAITPAY,
|
||||||
|
}
|
||||||
|
this.result.Order, this.result.PayCode = OrderCreate(order)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *payUrl) PayUrlV2Service() (result front.PayReqsV2Response, code int) {
|
||||||
|
var (
|
||||||
|
channelList []paychannelmodel.PayChannel
|
||||||
|
order = new(ordersmodel.Orders)
|
||||||
|
)
|
||||||
|
|
||||||
|
// redis 分布式锁 订单号幂等
|
||||||
|
key := utils.GetRealKey("payUrl:" + this.param.OutTradeNo)
|
||||||
|
lockValue := fmt.Sprintf("%d", time.Now().UnixNano())
|
||||||
|
ok, err := utils.AcquireLock(key, lockValue, time.Second*time.Duration(3))
|
||||||
|
if ok {
|
||||||
|
defer utils.ReleaseLock(key, lockValue)
|
||||||
|
} else {
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(nil, "", "PayUrlV2Service,获取分布式锁失败", fmt.Sprintf("错误原因:%s", err.Error()))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 商户是否有配置支付渠道
|
||||||
|
channelList, _, code = PayChannelList(backend.PayChannelListRequest{MerchantId: this.app.App.MerchantId})
|
||||||
|
if code != errorcode.Success {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(channelList) == 0 {
|
||||||
|
code = errorcode.PayChannelNotFound
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 订单是否存在
|
||||||
|
conn := builder.NewCond()
|
||||||
|
conn = conn.And(builder.Eq{"out_trade_no": this.param.OutTradeNo})
|
||||||
|
conn = conn.And(builder.Eq{"app_id": this.param.AppId})
|
||||||
|
order, code = OrderFindOne(order, conn)
|
||||||
|
if code == errorcode.Success {
|
||||||
|
// 订单存在
|
||||||
|
this.result.Order = order
|
||||||
|
this.orderExist()
|
||||||
|
} else if code == errorcode.OrdersNotFound {
|
||||||
|
// 订单不存在
|
||||||
|
this.saveOrder()
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code = this.result.PayCode
|
||||||
|
if code == errorcode.Success {
|
||||||
|
this.result.Url = config.GetConf().PayService.Host + common.PayPageAddress + "?id=" + strconv.FormatInt(this.result.Order.Id, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.result, code
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright (c) [2022] [巴拉迪维 BaratSemet]
|
||||||
|
// [ohUrlShortener] is licensed under Mulan PSL v2.
|
||||||
|
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
// You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
// http://license.coscl.org.cn/MulanPSL2
|
||||||
|
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
// See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func IsWeChatClient(ua string) bool {
|
||||||
|
// 检查 User-Agent 中是否包含微信客户端的标识
|
||||||
|
return strings.Contains(ua, "MicroMessenger")
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsMobile(ua string) bool {
|
||||||
|
// 检查 User-Agent 中是否包含移动设备的标识
|
||||||
|
return strings.Contains(ua, "Mobile") && !IsWeChatClient(ua)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsPC(userAgent string) bool {
|
||||||
|
// 检查 User-Agent 中是否包含 PC 浏览器的标识
|
||||||
|
t := strings.Contains(userAgent, "Windows NT") || strings.Contains(userAgent, "Macintosh") || strings.Contains(userAgent, "Linux")
|
||||||
|
return t
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestIsPC(t *testing.T) {
|
||||||
|
userAgent := "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
||||||
|
t.Log(IsPC(userAgent))
|
||||||
|
}
|
|
@ -483,3 +483,28 @@ func KeyExistsOrSet(key string, expire int) bool {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AcquireLock 尝试获取分布式锁,localValue用于检测是否是同一个客户端持有的锁锁
|
||||||
|
func AcquireLock(key, lockValue string, expired time.Duration) (bool, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
rd := redis.GetRedis()
|
||||||
|
result := rd.SetNX(ctx, key, lockValue, expired)
|
||||||
|
return result.Val(), result.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 释放锁
|
||||||
|
func ReleaseLock(key string, lockValue string) (bool, error) {
|
||||||
|
rd := redis.GetRedis()
|
||||||
|
result := rd.Eval(context.Background(), "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end", []string{key}, lockValue)
|
||||||
|
return result.Val() == int64(1), result.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断切片是否包含指定
|
||||||
|
func Contains[T comparable](s T, slice []T) bool {
|
||||||
|
for _, v := range slice {
|
||||||
|
if s == v {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue