alipay callback
This commit is contained in:
parent
f6137a8299
commit
7d5758d999
|
@ -127,3 +127,9 @@ type H5DirectUnlockWechatData struct {
|
|||
AlipayParamStr string `json:"alipay_param_str"` // 支付宝 app支付参数
|
||||
AlipayH5ParamStr string `json:"alipay_h5_param_str"` // 支付宝 h5支付参数
|
||||
}
|
||||
|
||||
// 支付宝回调参数
|
||||
type AlipayCallbackParamIn struct {
|
||||
OrderId string `json:"order_id"` // 我们自己服务的订单id
|
||||
AlipayOrderId string `json:"alipay_order_id"` // 支付宝订单id
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
vasproto "service/api/proto/vas/proto"
|
||||
"service/app/mix/service"
|
||||
"service/library/logger"
|
||||
"service/library/payclients/alipaycli"
|
||||
)
|
||||
|
||||
func AlipayCallback(ctx *gin.Context) {
|
||||
req, _ := ctx.GetRawData()
|
||||
bm, err := alipaycli.GetDefaultAlipayClient().ParseNotify(ctx.Request)
|
||||
if err != nil {
|
||||
logger.Error("ParseNotify fail, req: %v, err: %v", string(req), err)
|
||||
return
|
||||
}
|
||||
|
||||
service.DefaultService.AlipayCallback(ctx, &vasproto.AlipayCallbackParamIn{
|
||||
OrderId: bm.GetString("out_trade_no"),
|
||||
AlipayOrderId: bm.GetString("trade_no"),
|
||||
})
|
||||
ctx.String(200, "success")
|
||||
}
|
|
@ -185,6 +185,9 @@ func Init(r *gin.Engine) {
|
|||
vasPayGroup.POST("h5_direct_unlock_wechat", middleware.JSONParamValidator(vasproto.H5DirectUnlockWechatReq{}), H5DirectUnlockWechat)
|
||||
vasPayGroup.POST("h5_get_unlock_wechat_list", middleware.JSONParamValidator(vasproto.GetUnlockWechatListReq{}), GetUnlockWechatList)
|
||||
|
||||
extVasPayGroup := r.Group("/ext/vas")
|
||||
extVasPayGroup.POST("alipay_callback", AlipayCallback)
|
||||
|
||||
opVasPayGroup := r.Group("/op/vas", PrepareOp())
|
||||
opVasPayGroup.POST("create_order", middleware.JSONParamValidator(vasproto.OpCreateOrderReq{}), OpCreateOrder)
|
||||
|
||||
|
@ -454,6 +457,22 @@ func PrepareToC() gin.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
func PrepareExt() gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
var bodyParam map[string]interface{}
|
||||
buf, err := ioutil.ReadAll(ctx.Request.Body)
|
||||
err = json.Unmarshal(buf, &bodyParam)
|
||||
if err != nil {
|
||||
logger.Error("arg parse fail: %v", err)
|
||||
ReplyJsonError(ctx, http.StatusBadRequest, "参数解析失败")
|
||||
return
|
||||
}
|
||||
|
||||
buf, err = json.Marshal(&bodyParam)
|
||||
ctx.Set(gin.BodyBytesKey, buf)
|
||||
}
|
||||
}
|
||||
|
||||
var upGrader = websocket.Upgrader{
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
|
|
|
@ -132,6 +132,66 @@ func (m *Mysql) CreateOrder(ctx *gin.Context, tx *sqlx.Tx, order *dbstruct.Order
|
|||
return err
|
||||
}
|
||||
|
||||
// 获取订单
|
||||
func (m *Mysql) GetOrderById(ctx *gin.Context, tx *sqlx.Tx, id string) (order *dbstruct.Order, err error) {
|
||||
var tmpOrder dbstruct.Order
|
||||
if tx != nil {
|
||||
err = tx.GetContext(ctx, &tmpOrder, fmt.Sprintf("select * from %s where id = ?", TableOrder), id)
|
||||
} else {
|
||||
db := m.getDBVas()
|
||||
err = db.GetContext(ctx, &tmpOrder, fmt.Sprintf("select * from %s where id = ?", TableOrder), id)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
order = &tmpOrder
|
||||
return
|
||||
}
|
||||
|
||||
// 获取订单
|
||||
func (m *Mysql) GetOrderByOutOrderId(ctx *gin.Context, tx *sqlx.Tx, outOrderId string) (order *dbstruct.Order, err error) {
|
||||
var tmpOrder dbstruct.Order
|
||||
if tx != nil {
|
||||
err = tx.GetContext(ctx, &tmpOrder, fmt.Sprintf("select * from %s where out_order_id = ?", TableOrder), outOrderId)
|
||||
} else {
|
||||
db := m.getDBVas()
|
||||
err = db.GetContext(ctx, &tmpOrder, fmt.Sprintf("select * from %s where out_order_id = ?", TableOrder), outOrderId)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
order = &tmpOrder
|
||||
return
|
||||
}
|
||||
|
||||
// 获取订单for update
|
||||
func (m *Mysql) GetOrderByIdForUpdate(ctx *gin.Context, tx *sqlx.Tx, id string) (order *dbstruct.Order, err error) {
|
||||
var tmpOrder dbstruct.Order
|
||||
err = tx.GetContext(ctx, &tmpOrder, fmt.Sprintf("select * from %s where id = ? for update", TableOrder), id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
order = &tmpOrder
|
||||
return
|
||||
}
|
||||
|
||||
// 更新订单状态
|
||||
func (m *Mysql) UpdateOrderStatus(ctx *gin.Context, tx *sqlx.Tx, orderId string, preStatus, aftStatus int32) error {
|
||||
var err error
|
||||
sqlStr := "update " + TableOrder + " set order_status=? where id=? and order_status=?"
|
||||
if tx != nil {
|
||||
_, err = tx.ExecContext(ctx, sqlStr, aftStatus, orderId, preStatus)
|
||||
} else {
|
||||
db := m.getDBVas()
|
||||
_, err = db.ExecContext(ctx, sqlStr, aftStatus, orderId, preStatus)
|
||||
}
|
||||
if err != nil {
|
||||
logger.Error("UpdateOrderStatus fail, orderId: %v, preStatus: %v, aftStatus: %v, err: %v", orderId, preStatus, aftStatus, err)
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 获取钱包 for update
|
||||
func (m *Mysql) GetWalletForUpdate(ctx *gin.Context, tx *sqlx.Tx, mid int64) (wallet *dbstruct.Wallet, err error) {
|
||||
var tmpWallet dbstruct.Wallet
|
||||
|
@ -271,7 +331,7 @@ func (m *Mysql) CreateCoinOrder(ctx *gin.Context, tx *sqlx.Tx, order *dbstruct.C
|
|||
return err
|
||||
}
|
||||
|
||||
// 获取金币订单 for update
|
||||
// 获取金币订单
|
||||
func (m *Mysql) GetCoinOrderById(ctx *gin.Context, tx *sqlx.Tx, id string) (order *dbstruct.CoinOrder, err error) {
|
||||
var tmpOrder dbstruct.CoinOrder
|
||||
if tx != nil {
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/jmoiron/sqlx"
|
||||
"github.com/samber/lo"
|
||||
goproto "google.golang.org/protobuf/proto"
|
||||
"service/api/base"
|
||||
"service/api/errs"
|
||||
vasproto "service/api/proto/vas/proto"
|
||||
"service/app/mix/dao"
|
||||
|
@ -133,7 +134,7 @@ func (v *Vas) CreateOrder(ctx *gin.Context, req *vasproto.CreateOrderReq) (data
|
|||
product.RealPrice = req.CalcPrice
|
||||
}
|
||||
if req.CustomCoins > 0 {
|
||||
product.RealCoinPrice = req.CustomCoins
|
||||
product.ValueCoins = req.CustomCoins
|
||||
}
|
||||
|
||||
// 支付参数
|
||||
|
@ -649,6 +650,9 @@ func (v *Vas) OneStepUnlockContact(ctx *gin.Context, req *vasproto.OneStepUnlock
|
|||
contactProductId = req.ContactProductId // 要解锁的联系方式
|
||||
)
|
||||
|
||||
// 获取uid的邀请人mid
|
||||
req.InviterMid = 0
|
||||
|
||||
// 检查钱包
|
||||
_, has := v.CheckWalletExist(ctx, mid)
|
||||
if !has {
|
||||
|
@ -691,6 +695,9 @@ func (v *Vas) OneStepUnlockContact(ctx *gin.Context, req *vasproto.OneStepUnlock
|
|||
case dbstruct.ProductIdContactWechat:
|
||||
// 获取uid微信金币价格
|
||||
coinPrice = uVasInfo.WechatCoinPrice
|
||||
case dbstruct.ProductIdH5ContactWechat:
|
||||
contactProductId = dbstruct.ProductIdContactWechat
|
||||
coinPrice = uVasInfo.GetH5WechatCoinPrice()
|
||||
default:
|
||||
err = errs.ErrVasInvalidContactProduct
|
||||
return
|
||||
|
@ -1356,7 +1363,7 @@ func (v *Vas) H5DirectUnlockWechat(ctx *gin.Context, req *vasproto.H5DirectUnloc
|
|||
if wallet.GetCoins() >= uVas.GetH5WechatCoinPrice() {
|
||||
v.OneStepUnlockContact(ctx, &vasproto.OneStepUnlockContactReq{
|
||||
BaseRequest: req.BaseRequest,
|
||||
ContactProductId: dbstruct.ProductIdH5ContactWechat,
|
||||
ContactProductId: dbstruct.ProductIdContactWechat,
|
||||
Uid: req.Uid,
|
||||
InviterMid: req.InviterMid,
|
||||
})
|
||||
|
@ -1405,3 +1412,165 @@ func (v *Vas) GetUserWechatUnlock(ctx *gin.Context, mid, uid int64) (uu *dbstruc
|
|||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 支付宝【支付】回调
|
||||
func (v *Vas) AlipayCallback(ctx *gin.Context, p *vasproto.AlipayCallbackParamIn) {
|
||||
var (
|
||||
orderId = p.OrderId
|
||||
alipayOrderId = p.AlipayOrderId
|
||||
afterStatus int32
|
||||
)
|
||||
|
||||
// 获取订单
|
||||
checkOrder, err := v.store.GetOrderById(ctx, nil, orderId)
|
||||
if err != nil {
|
||||
logger.Error("GetOrderById fail, p: %v, err: %v", util.ToJson(p), err)
|
||||
return
|
||||
}
|
||||
if checkOrder == nil {
|
||||
logger.Warn("GetOrderById nil, p: %v", util.ToJson(p))
|
||||
return
|
||||
}
|
||||
// 是否已处理过的订单
|
||||
if checkOrder.GetOrderStatus() != dbstruct.VasOrderStatusInit {
|
||||
logger.Error("repeat deal, p: %v", util.ToJson(p))
|
||||
return
|
||||
}
|
||||
|
||||
// ali_order_id检查
|
||||
outOrder, err := v.store.GetOrderByOutOrderId(ctx, nil, alipayOrderId)
|
||||
if err != nil {
|
||||
logger.Error("GetOrderByOutOrderId fail, p: %v, err: %v", util.ToJson(p), err)
|
||||
return
|
||||
}
|
||||
if outOrder != nil {
|
||||
logger.Error("out order exists, p: %v", util.ToJson(p))
|
||||
return
|
||||
}
|
||||
|
||||
// 获取商品
|
||||
product, err := v.store.GetProductById(ctx, checkOrder.GetProductId())
|
||||
if err != nil {
|
||||
logger.Error("GetProductById fail, id: %v, err: %v", checkOrder.GetProductId(), err)
|
||||
return
|
||||
}
|
||||
if product == nil {
|
||||
logger.Error("GetProductById nil, id: %v", checkOrder.GetProductId())
|
||||
return
|
||||
}
|
||||
|
||||
// 钱包
|
||||
_, hasWallet := v.CheckWalletExist(ctx, checkOrder.GetMid())
|
||||
if !hasWallet {
|
||||
logger.Error("CheckWalletExist fail, mid: %v", checkOrder.GetMid())
|
||||
return
|
||||
}
|
||||
|
||||
// 开启事务
|
||||
tx, err := v.store.VasBegin(ctx)
|
||||
if err != nil {
|
||||
logger.Error("vas begin fail, err: %v", err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
_ = v.AddOplogOrder(
|
||||
ctx,
|
||||
&dbstruct.OplogOrder{
|
||||
OrderId: orderId,
|
||||
Mid: checkOrder.GetMid(),
|
||||
Action: dbstruct.OrderOpLogActionAdd,
|
||||
Detail: fmt.Sprintf("充值%d金币", checkOrder.GetCoins()),
|
||||
BeforeStatus: checkOrder.GetOrderStatus(),
|
||||
AfterStatus: afterStatus,
|
||||
},
|
||||
err,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
logger.Error("global err, p: %v, order: %v, err: %v", util.ToJson(p), util.ToJson(checkOrder), err)
|
||||
}
|
||||
errTx := v.store.DealTxCR(tx, err)
|
||||
if errTx != nil {
|
||||
logger.Error("DealTxCR fail, err: %v", errTx)
|
||||
return
|
||||
}
|
||||
|
||||
// 后续处理
|
||||
switch {
|
||||
case product.Id == dbstruct.ProductIdH5ContactWechat:
|
||||
// 解锁
|
||||
_, _, _, errIn := v.OneStepUnlockContact(ctx, &vasproto.OneStepUnlockContactReq{
|
||||
BaseRequest: base.BaseRequest{
|
||||
Mid: checkOrder.GetMid(),
|
||||
},
|
||||
ContactProductId: dbstruct.ProductIdH5ContactWechat,
|
||||
Uid: checkOrder.GetUid(),
|
||||
})
|
||||
if errIn != nil {
|
||||
logger.Error("OneStepUnlockContact fail, order: %v", util.ToJson(checkOrder))
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// 锁住订单
|
||||
order, err := v.store.GetOrderByIdForUpdate(ctx, tx, orderId)
|
||||
if err != nil {
|
||||
logger.Error("GetOrderByIdForUpdate fail, p: %v", util.ToJson(p))
|
||||
return
|
||||
}
|
||||
|
||||
// 锁住钱包
|
||||
wallet, _ := v.store.GetWalletForUpdate(ctx, tx, order.GetMid())
|
||||
|
||||
// 消费历史
|
||||
ch := &dbstruct.ConsumeHistory{
|
||||
Mid: goproto.Int64(order.GetMid()),
|
||||
Type: goproto.Int32(dbstruct.CHTypeCharge),
|
||||
SType: goproto.Int32(dbstruct.CHSTypeChargeUser),
|
||||
TypeId: goproto.String(dbstruct.ProductTypeCoins),
|
||||
OrderId: goproto.String(orderId),
|
||||
Change: goproto.Int64(order.GetCoins()),
|
||||
Before: goproto.Int64(wallet.GetCoins()),
|
||||
After: goproto.Int64(wallet.GetCoins() + order.GetCoins()),
|
||||
Count: goproto.Int64(order.GetCoins()),
|
||||
Ct: goproto.Int64(time.Now().Unix()),
|
||||
}
|
||||
err = v.store.CreateConsumeHistory(ctx, tx, ch)
|
||||
if err != nil {
|
||||
logger.Error("CreateConsumeHistory fail, ch: %v, err: %v", util.ToJson(ch), err)
|
||||
return
|
||||
}
|
||||
|
||||
// 更新状态
|
||||
err = v.store.UpdateOrderStatus(ctx, tx, orderId, dbstruct.VasOrderStatusInit, dbstruct.VasOrderStatusPaySuccess)
|
||||
if err != nil {
|
||||
logger.Error("UpdateOrderStatus fail, p: %v", util.ToJson(p))
|
||||
return
|
||||
}
|
||||
afterStatus = dbstruct.VasOrderStatusPaySuccess
|
||||
|
||||
// 商品判断
|
||||
switch {
|
||||
case product.Type == dbstruct.ProductTypeCoins:
|
||||
// 充金币
|
||||
err = v.store.IncCoins(ctx, tx, order.GetMid(), order.GetCoins())
|
||||
if err != nil {
|
||||
logger.Error("IncCoins fail, order: %v", util.ToJson(order))
|
||||
return
|
||||
}
|
||||
err = v.store.UpdateOrderStatus(ctx, tx, orderId, dbstruct.VasOrderStatusPaySuccess, dbstruct.VasOrderStatusFinish)
|
||||
if err != nil {
|
||||
logger.Error("UpdateOrderStatus fail, order: %v", util.ToJson(order))
|
||||
return
|
||||
}
|
||||
afterStatus = dbstruct.VasOrderStatusFinish
|
||||
case product.Id == dbstruct.ProductIdH5ContactWechat:
|
||||
// 充金币
|
||||
err = v.store.IncCoins(ctx, tx, order.GetMid(), order.GetCoins())
|
||||
if err != nil {
|
||||
logger.Error("IncCoins fail, order: %v", util.ToJson(order))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -339,3 +339,9 @@ func (s *Service) H5DirectUnlockWechat(ctx *gin.Context, req *vasproto.H5DirectU
|
|||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 支付宝回调
|
||||
func (s *Service) AlipayCallback(ctx *gin.Context, req *vasproto.AlipayCallbackParamIn) (data *vasproto.H5DirectUnlockWechatData, ec errcode.ErrCode) {
|
||||
_DefaultVas.AlipayCallback(ctx, req)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -6,11 +6,11 @@ import (
|
|||
|
||||
// 订单状态
|
||||
const (
|
||||
VasOrderStatusNone = iota - 1
|
||||
VasOrderStatusInit // 初始化
|
||||
VasOrderStatusPaySuccess // 付款成功(第三方回调成功)
|
||||
VasOrderStatusFinish // 订单完成,不再参与业务
|
||||
VasOrderStatusRefund // 已退款
|
||||
VasOrderStatusNone = -1
|
||||
VasOrderStatusInit = 0 // 初始化
|
||||
VasOrderStatusPaySuccess = 1 // 付款成功(第三方回调成功)
|
||||
VasOrderStatusFinish = 2 // 订单完成,不再参与业务
|
||||
VasOrderStatusRefund = 3 // 已退款
|
||||
)
|
||||
|
||||
var OrderStatusDescMap = map[int32]string{
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"github.com/go-pay/gopay"
|
||||
"github.com/go-pay/gopay/alipay"
|
||||
"net/http"
|
||||
"service/bizcommon/util"
|
||||
"service/library/configcenter"
|
||||
"service/library/logger"
|
||||
|
@ -34,11 +35,33 @@ func Init(cfg *configcenter.AlipayClientConfig) (err error) {
|
|||
alipayCli.SetNotifyUrl(cfg.NotifyUrl)
|
||||
|
||||
defaultAlipayClient = &AlipayClient{
|
||||
alipayCli,
|
||||
Client: alipayCli,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 解析回调参数
|
||||
func (c *AlipayClient) ParseNotify(req *http.Request) (notify gopay.BodyMap, err error) {
|
||||
// 解析参数
|
||||
notifyTmp, err := alipay.ParseNotifyToBodyMap(req)
|
||||
if err != nil {
|
||||
logger.Error("ParseNotifyToBodyMap fail, req: %v, err: %v", util.ToJson(req), err)
|
||||
return
|
||||
}
|
||||
// 验签
|
||||
ok, err := alipay.VerifySign("", notifyTmp)
|
||||
if err != nil {
|
||||
logger.Error("VerifySign fail, bm: %v, err: %v", util.ToJson(notifyTmp), err)
|
||||
return
|
||||
}
|
||||
if !ok {
|
||||
logger.Error("VerifySign fail, not ok, bm: %v", util.ToJson(notifyTmp))
|
||||
return
|
||||
}
|
||||
notify = notifyTmp
|
||||
return
|
||||
}
|
||||
|
||||
// 支付宝 app支付
|
||||
type AppPayParam struct {
|
||||
OutTradeNo string // 商家订单id,我们自己的订单id
|
||||
|
|
Loading…
Reference in New Issue