|
|
package pay_service
import ( core2 "github.com/chanxuehong/wechat/mch/core" "github.com/chanxuehong/wechat/mch/pay" "github.com/pkg/errors" "go.uber.org/zap" "hudongzhuanjia/logger" "hudongzhuanjia/models" "hudongzhuanjia/utils" "math" "strconv" "time" )
func init() { go loopUnifiedOrder() }
var orderDelayQueue = make(chan *orderDelayQueueParam, math.MaxInt8)
type orderDelayQueueParam struct { First bool `json:"first"` Expires int64 `json:"expires"` Delay int `json:"delay"` OutTradeNo string `json:"out_trade_no"` Body string `json:"body"` Amount int `json:"amount"` // 金额
Status int `json:"status"` // 0 订单 3 退款
OpenId string `json:"open_id"` // 被操作人
}
func PutOrderDelayQueue(body, outTradeNo, openId string, amount, status int, expires int64, delay int) { if expires == 0 { expires = time.Now().Add(2 * time.Hour).Unix() // 2 个小时
}
orderDelayQueue <- &orderDelayQueueParam{ First: true, Expires: expires, Delay: delay, OutTradeNo: outTradeNo, Body: body, Amount: amount, Status: status, OpenId: openId, } }
func loopUnifiedOrder() { orders, err := models.GetUserOrdersByStatus(0, 3) if err != nil { panic(err) }
for _, order := range orders { PutOrderDelayQueue(order.Body, order.OutTradeNo, order.OpenId, int(order.TotalFee), order.Status, 0, 0) }
defer func() { if err := recover(); err != nil { logger.Error("订单轮询查询", zap.Any("panic 恢复错误", err)) } // 重启
time.Sleep(5 * time.Second) loopUnifiedOrder() }() for { select { case param, ok := <-orderDelayQueue: if !ok { panic("通道异常关闭") } //userOrder := new(models.UserOrder)
//exist, err := userOrder.GetByOutTradeNo(outTradeNo)
//if err != nil || !exist {
// logger.Error("通过订单查询UserOrder", zap.String("错误原因", err.Error()),
// zap.Bool("是否存在", exist), zap.String("交易订单号", outTradeNo))
// continue
//}
if param.Expires <= time.Now().Unix() { Close(param.OutTradeNo) // 超时关闭订单
continue // 超时
}
// 首次进入不延迟
if !param.First { time.Sleep(time.Duration(param.Delay) * time.Second) } param.First = false
if param.Status == 0 { res, err := OrderQuery(param.OutTradeNo)
// 出现错误
if err != nil { logger.Error("查询订单出现错误", zap.String("错误原因", err.Error()), zap.String("交易订单号", param.OutTradeNo)) orderDelayQueue <- param // 重新进入队列
continue }
if res.TradeState == CODE_TRADE_REFUND { param.Status = 3 orderDelayQueue <- param continue } else if res.TradeState == CODE_TRADE_NOTPAY || res.TradeState == CODE_TRADE_USERPAYING { orderDelayQueue <- param continue } } else if param.Status == 3 { _, err = QueryRefund(param.OutTradeNo) if err != nil { logger.Error("退款订单查询错误", zap.String("错误原因", err.Error()), zap.String("交易订单号", param.OutTradeNo)) continue } } else { continue } } } }
const CallbackOrderUrl = "https://api.ouxuanhudong.com/PcClient/common/WeChatOauthCtl/callbackOrder"
func UnifiedOrder(body, ip, openid string, fee, goodType, userId, activityId int64) (map[string]interface{}, error) { client, err := Client() if err != nil { return nil, err }
now := time.Now() timeStart := core2.FormatTime(now) timeExpire := core2.FormatTime(now.Add(2 * time.Hour)) outTradeNo := utils.RandomStr(32) nonceStr := utils.RandomStr(32) resp, err := pay.UnifiedOrder2(client, &pay.UnifiedOrderRequest{ Body: body, OutTradeNo: outTradeNo, TotalFee: fee, SpbillCreateIP: ip, NotifyURL: CallbackOrderUrl, TradeType: "JSAPI", DeviceInfo: "WEB", NonceStr: nonceStr, SignType: core2.SignType_MD5, TimeStart: now, OpenId: openid, }) if err != nil { return nil, err }
// 记录这次订单
userOrder := new(models.UserOrder) userOrder.DeviceInfo = "WEB" userOrder.Body = body userOrder.UserId = userId userOrder.ActivityId = activityId userOrder.FeeType = "CNY" userOrder.GoodType = goodType userOrder.OpenId = openid userOrder.OutTradeNo = outTradeNo userOrder.TimeStart = timeStart userOrder.TimeExpire = timeExpire userOrder.TotalFee = fee userOrder.TradeType = "JSAPI" userOrder.PrepayId = resp.PrepayId userOrder.Status = 0 userOrder.IsDelete = false userOrder.CreatedAt = time.Now() userOrder.UpdatedAt = time.Now() if _, err = userOrder.AddUserOrder(); err != nil { return nil, err }
timestamp := strconv.FormatInt(time.Now().Unix(), 10) //获取H5支付需要的paySign
pac := "prepay_id=" + resp.PrepayId paySign := core2.JsapiSign(client.AppId(), nonceStr, pac, core2.SignType_MD5, timestamp, ApiKey)
go PutOrderDelayQueue(userOrder.Body, userOrder.OutTradeNo, userOrder.OpenId, int(userOrder.TotalFee), userOrder.Status, 5, 0) return map[string]interface{}{ "appid": Appid, "timestamp": timestamp, "nonce_str": nonceStr, "package": pac, "sign_type": core2.SignType_MD5, "pay_sign": paySign, "out_trade_no": outTradeNo, "user_order_id": userOrder.Id, }, nil }
// Notify
type NotifyRequest struct { ReturnCode string `xml:"return_code,omitempty" json:"return_code,omitempty"` ReturnMsg string `xml:"return_msg,omitempty" json:"return_msg,omitempty"` ResultCode string `xml:"result_code,omitempty" json:"result_code,omitempty"` ErrCode string `xml:"err_code,omitempty" json:"err_code,omitempty"` ErrCodeDes string `xml:"err_code_des,omitempty" json:"err_code_des,omitempty"` Appid string `xml:"appid,omitempty" json:"appid,omitempty"` MchId string `xml:"mch_id,omitempty" json:"mch_id,omitempty"` DeviceInfo string `xml:"device_info,omitempty" json:"device_info,omitempty"` NonceStr string `xml:"nonce_str,omitempty" json:"nonce_str,omitempty"` Sign string `xml:"sign,omitempty" json:"sign,omitempty"` SignType string `xml:"sign_type,omitempty" json:"sign_type,omitempty"` Openid string `xml:"openid,omitempty" json:"openid,omitempty"` IsSubscribe string `xml:"is_subscribe,omitempty" json:"is_subscribe,omitempty"` TradeType string `xml:"trade_type,omitempty" json:"trade_type,omitempty"` BankType string `xml:"bank_type,omitempty" json:"bank_type,omitempty"` TotalFee int `xml:"total_fee,omitempty" json:"total_fee,omitempty"` SettlementTotalFee int `xml:"settlement_total_fee,omitempty" json:"settlement_total_fee,omitempty"` FeeType string `xml:"fee_type,omitempty" json:"fee_type,omitempty"` CashFee int `xml:"cash_fee,omitempty" json:"cash_fee,omitempty"` CashFeeType string `xml:"cash_fee_type,omitempty" json:"cash_fee_type,omitempty"` CouponFee int `xml:"coupon_fee,omitempty" json:"coupon_fee,omitempty"` CouponCount int `xml:"coupon_count,omitempty" json:"coupon_count,omitempty"` CouponType0 string `xml:"coupon_type_0,omitempty" json:"coupon_type_0,omitempty"` CouponType1 string `xml:"coupon_type_1,omitempty" json:"coupon_type_1,omitempty"` CouponId0 string `xml:"coupon_id_0,omitempty" json:"coupon_id_0,omitempty"` CouponId1 string `xml:"coupon_id_1,omitempty" json:"coupon_id_1,omitempty"` CouponFee0 int `xml:"coupon_fee_0,omitempty" json:"coupon_fee_0,omitempty"` CouponFee1 int `xml:"coupon_fee_1,omitempty" json:"coupon_fee_1,omitempty"` TransactionId string `xml:"transaction_id,omitempty" json:"transaction_id,omitempty"` OutTradeNo string `xml:"out_trade_no,omitempty" json:"out_trade_no,omitempty"` Attach string `xml:"attach,omitempty" json:"attach,omitempty"` TimeEnd string `xml:"time_end,omitempty" json:"time_end,omitempty"` }
//func NotifyOrder(req *http.Request) error {
// res := new(NotifyRequest)
// if err := xml.NewDecoder(req.Body).Decode(res); err != nil {
// return fmt.Errorf("xml.NewDecoder.Decode:%w", err)
// }
//if res.ReturnCode != CODE_SUCCESS {
// return fmt.Errorf("network error, retrun_code: %v and return_msg: %v", res.ReturnCode, res.ReturnMsg)
//}
//
//if res.ResultCode != CODE_FAIL && res.ErrCode == CODE_SYSTEMERROR {
// return fmt.Errorf("trade error, err_code: %v and err_code_des: %v", res.ErrCode, res.ErrCodeDes)
//}
//
//if res.ResultCode == CODE_SUCCESS {
// userOrder := new(models.UserOrder)
// userOrder.TimeEnd = res.TimeEnd
// userOrder.TransactionId = res.TransactionId
// if res.TradeType == CODE_TRADE_SUCCESS {
// userOrder.Status = 1
// } else if _, err := userOrder.UpdateStatusByOutTradeNo(res.OutTradeNo, userOrder.TradeType); err != nil {
// return err
// }
//
//} else {
//}
//// 设置一下
//if userOrder.GoodType == 1 { // 霸屏
// _, err = new(models.BullyScreenHistory).UpdateStatusByUserOrderId(userOrder.Id, 0)
// if err != nil {
// return err
// }
//} else if userOrder.GoodType == 2 {
// _, err = new(models.RewardHistory).UpdateStatusByUserOrderId(userOrder.Id, 0)
// if err != nil {
// return err
// }
//}
// return nil
//}
func OrderQuery(outTradeNo string) (*pay.OrderQueryResponse, error) { client, err := Client() if err != nil { return nil, err } // 请求订单查询,成功后得到结果
userOrder := new(models.UserOrder) exist, err := userOrder.GetByOutTradeNo(outTradeNo) if err != nil { return nil, err } if !exist { return nil, errors.New("订单不存在") }
res, err := pay.OrderQuery2(client, &pay.OrderQueryRequest{ OutTradeNo: outTradeNo, NonceStr: utils.RandomStr(32), SignType: core2.SignType_MD5, })
if err != nil { userOrder.ErrMsg = err.Error() userOrder.UpdateErrByOutTradeNo(outTradeNo) return nil, err }
userOrder.TransactionId = res.TransactionId userOrder.TimeEnd = core2.FormatTime(res.TimeEnd) switch res.TradeState { case CODE_TRADE_SUCCESS: userOrder.Status = 1 case CODE_TRADE_REVOKED: userOrder.Status = 2 case CODE_TRADE_REFUND: userOrder.Status = 3 case CODE_TRADE_PAYERROR: userOrder.Status = 5 case CODE_TRADE_CLOSED: userOrder.Status = 6 case CODE_TRADE_NOTPAY: // 超过期限,就关闭
userOrder.Status = 0 }
if _, err = userOrder.UpdateStatusByOutTradeNo(outTradeNo, userOrder.Status); err != nil { return nil, err } return res, nil }
func Close(outTradeNo string) error { client, err := Client() if err != nil { return err } err = pay.CloseOrder2(client, &pay.CloseOrderRequest{ OutTradeNo: outTradeNo, NonceStr: utils.RandomStr(32), SignType: core2.SignType_MD5, }) // 请求关闭订单,成功后得到结果
if err != nil { return err } return nil }
//const CALLBACK_REFUND_URL = "https://api.ouxuanhudong.com/PcClient/common/WeChatOauthCtl/callbackRefund"
func Refund(reason, outTradeNo string) (*pay.RefundResponse, error) { userOrder := new(models.UserOrder) exist, err := userOrder.GetByOutTradeNo(outTradeNo) if err != nil { return nil, err } if !exist { return nil, errors.New("订单不存在") } client, err := Client()
outRefundNo := utils.RandomStr(64) nonceStr := utils.RandomStr(32) res, err := pay.Refund2(client, &pay.RefundRequest{ TransactionId: "", OutTradeNo: outTradeNo, OutRefundNo: outRefundNo, TotalFee: userOrder.TotalFee, RefundFee: userOrder.TotalFee, NonceStr: nonceStr, SignType: core2.SignType_MD5, RefundFeeType: "CNY", RefundDesc: reason, }) //
if err != nil { userOrder.ErrMsg = err.Error() userOrder.UpdateErrByOutTradeNo(outTradeNo) return nil, err } userOrder.Status = 3 _, err = userOrder.UpdateStatusByOutTradeNo(outTradeNo, userOrder.Status) if err != nil { return nil, err }
PutOrderDelayQueue(userOrder.Body, userOrder.OutTradeNo, userOrder.OpenId, int(userOrder.TotalFee), userOrder.Status, 5, 0) // 退款查询
return res, nil }
func QueryRefund(outTradeNo string) (*pay.RefundQueryResponse, error) { userOrder := new(models.UserOrder) //exist, err := userOrder.GetByOutTradeNo(outTradeNo)
//if err != nil {
// return nil, err
//}
//if !exist {
// return nil, errors.New("不存在改笔退款")
//}
client, err := Client() res, err := pay.RefundQuery2(client, &pay.RefundQueryRequest{ OutTradeNo: outTradeNo, NonceStr: utils.RandomStr(32), SignType: core2.SignType_MD5, }) //请求申请退款
if err != nil { userOrder.ErrMsg = err.Error() userOrder.UpdateErrByOutTradeNo(outTradeNo) return nil, err }
userOrder.RefundAccount = res.RefundList[0].RefundAccount userOrder.RefundRecvAccount = res.RefundList[0].RefundRecvAccout userOrder.SuccessTime = res.RefundList[0].RefundSuccessTime userOrder.Status = 4 _, err = userOrder.UpdateRefundByOutTradeNo(outTradeNo) if err != nil { return nil, err }
return res, nil }
|