订单支付后的处理【使用github.com/smartwalle/alipay/v3包】
使用gin修改订单状态,处理超时未支付…
初始化客户端
func InitAlipay() {
//使用配置信息获取
global.AliClient, err = alipay.New(global.ServerConfig.Alipay.Appid, global.ServerConfig.Alipay.PrivateKey, false)
if err != nil {
zap.S().Info("实例化支付宝失败", err)
return
}
err = global.AliClient.LoadAliPayPublicKey(global.ServerConfig.Alipay.PublicKey)
if err != nil {
zap.S().Info("载入支付宝公钥失败", err)
return
}
}
生成支付链接
func GenerateAlipayUrl(c *gin.Context) {
order_sn := c.PostForm("order_sn")
if len(order_sn) == 0 {
api.ReturnErrorJson(c, err)
return
}
//根据订单编号进行查询订单详情
detail, err := global.OrderClient.OrderDetail(c, &orderPb.OrderReq{
OrderSn: order_sn,
})
if err != nil {
api.HandleGrpcErrorToHttp(c, err)
return
}
var p = alipay.TradeWapPay{}
total := strconv.FormatFloat(float64(detail.OrderInfo.Total), 'f', 2, 64)
p.NotifyURL = "http://6a6d884f.r20.cpolar.top/o/v1/alipay/notify" //异步回调地址 是我们用来更改订单信息的 需要内网穿透
p.ReturnURL = "http://192.168.1.114:8891/o/v1/alipay/return" //同步回调地址 用来展示给用户的界面 一般是订单详情
p.Subject = "生鲜" + detail.OrderInfo.OrderSn
p.OutTradeNo = detail.OrderInfo.OrderSn
p.TotalAmount = total
p.ProductCode = "QUICK_WAP_WAY"
url, err := global.AliClient.TradeWapPay(p)
if err != nil {
fmt.Println(err)
}
// 这个 payURL 即是用于打开支付宝支付页面的 URL,可将输出的内容复制,到浏览器中访问该 URL 即可打开支付页面。
var payURL = url.String()
c.JSON(http.StatusOK, gin.H{
"code": 0,
"message": "生成支付链接",
"data": map[string]string{
"pay_url": payURL,
},
})
}
同步回调
// ReturnUrl 同步回调
func ReturnUrl(c *gin.Context) {
//通过订单编号查询订单详情
out_trade_no := c.Query("out_trade_no")
if len(out_trade_no) == 0 {
c.JSON(http.StatusOK, gin.H{
"code": 0,
"message": "订单号不能为空",
})
return
}
detail, err := global.OrderClient.OrderDetail(c, &orderPb.OrderReq{
OrderSn: out_trade_no,
})
if err != nil {
api.HandleGrpcErrorToHttp(c, err)
return
}
c.JSON(http.StatusOK, gin.H{
"code": 0,
"message": "订单详情",
"data": detail,
})
}
异步回调
//NotifyUrl 使用rocketMQ实现异步回调
func NotifyUrl(c *gin.Context) {
trade_no := c.PostForm("trade_no")
trade_status := c.PostForm("trade_status")
out_trade_no := c.PostForm("out_trade_no")
//除了交易成功 还有几种状态 如交易关闭 等待交易 交易完成
// 定义订单信息变量
var orderInfo model.OrderInfo
// 根据交易状态设置订单信息
//1(待支付),2(成功),3(超时关闭),4(交易失败),5(交易结束)
switch trade_status {
case "TRADE_SUCCESS":
//交易成功
orderInfo.Status = 2
case "TRADE_PAY_FAIL":
//交易失败
orderInfo.Status = 4
case "TRADE_CLOSED":
//交易关闭
orderInfo.Status = 3
case "WAIT_BUYER_PAY":
//等待买家付款
orderInfo.Status = 1
case "TRADE_FINISHED":
//交易结束 不可退款
orderInfo.Status = 5
default:
// 可以根据需要处理其他交易状态
zap.S().Info("未处理交易状态: " + trade_status)
c.String(http.StatusBadRequest, "未处理交易状态")
return
}
//1代表支付类型是支付宝
order := model.OrderInfo{
PayType: 1,
Status: orderInfo.Status,
TradeNo: trade_no,
OrderSn: out_trade_no,
}
// 序列化订单信息
msg, err := json.Marshal(order)
if err != nil {
zap.S().Info("序列化失败: " + err.Error())
c.String(http.StatusInternalServerError, "序列化失败")
return
}
// 将修改订单放到异步队列里面,使用唯一的队列键
RocketMqProducer(msg)
c.String(http.StatusOK, "success")
}
RocketMQ生产者
func RocketMqProducer(msgs []byte) {
intn := rand.Intn(100)
name := GenerateOrderSn(int32(intn))
p, err := rocketmq.NewProducer(
producer.WithGroupName(name),
producer.WithNameServer([]string{fmt.Sprintf("%s:%d", global.ServerConfig.Rocketmq.Host, global.ServerConfig.Rocketmq.Port)}),
)
if err != nil {
zap.S().Info("生产者失败", err)
return
}
err = p.Start()
if err != nil {
fmt.Printf("start producer error: %s", err.Error())
os.Exit(1)
}
msg := &primitive.Message{
Topic: "alipay",
Body: msgs,
}
_, err = p.SendSync(context.Background(), msg)
if err != nil {
zap.S().Info("支付发送消息失败", err)
return
}
}
RocketMq消费者
func RocketMqConsumer() {
// 创建消费者
c, err := rocketmq.NewPushConsumer(
consumer.WithGroupName("testGroup1"),
consumer.WithNsResolver(primitive.NewPassthroughResolver([]string{"192.168.1.114:9876"})),
consumer.WithRetry(2),
)
//如果订阅的主题不存在,进行每2秒重试一次,直至订阅成功为止
if err != nil {
zap.S().Info("初始化消费者失败", err)
return
}
// 订阅主题`delay_order`,并设置回调函数
err = c.Subscribe("alipay", consumer.MessageSelector{}, UpdateOrder)
if err != nil {
zap.S().Info("delay_order_consumer error", err.Error())
return
}
err = c.Start()
if err != nil {
zap.S().Info("delay_order_consumer error", err.Error())
return
}
}
更改订单状态
func UpdateOrder(ctx context.Context, messages ...*primitive.MessageExt) (consumer.ConsumeResult, error) {
//把messages的信息反解到订单结构体
for _, msg := range messages {
var orderInfo model.OrderInfo
//将延迟队列里的消息反解到orderInfo结构体
err := json.Unmarshal(msg.Body, &orderInfo)
if err != nil {
zap.S().Info("反序列化失败")
return 0, err
}
now := time.Now().Format(time.DateTime)
// 更新订单状态
_, err = global.OrderClient.UpdateOrder(context.Background(), &orderPb.UpdateOrderInfo{
PayType: orderInfo.PayType,
Status: orderInfo.Status,
TradeNo: orderInfo.TradeNo,
OrderSn: orderInfo.OrderSn,
PayTime: now,
})
if err != nil {
zap.S().Info("订单更新失败: " + err.Error())
return consumer.ConsumeRetryLater, nil
}
log.Println("修改订单状态成功")
}
return consumer.ConsumeSuccess, nil
}
退款
// Refund 支付宝退款
// 退款之前要查询订单信息 如果是交易状态是交易完成 就不能进行退款
func Refund(c *gin.Context) {
// 从请求中获取退款参数,例如:outTradeNo(商户订单号), refundAmount(退款金额)等
outTradeNo := c.PostForm("trade_no")
refundAmount := c.PostForm("refund_amount")
refundReason := c.PostForm("refund_reason")
tradeNo := c.PostForm("trade_no")
if len(outTradeNo) == 0 || len(refundAmount) == 0 {
c.JSON(http.StatusBadRequest, gin.H{
"code": -1,
"msg": "参数不能为空",
})
return
}
//查询订单状态
detail, err := global.OrderClient.OrderDetail(c, &orderPb.OrderReq{
OrderSn: outTradeNo,
})
if err != nil {
api.HandleGrpcErrorToHttp(c, err)
return
}
//1(待支付),2(成功),3(超时关闭),4(交易创建),5(交易结束)
if detail.OrderInfo.Status == 5 {
//代表交易结束 不能够退款
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "交易已完成,无法退款",
})
return
} else if detail.OrderInfo.Status == 1 {
//代表交易创建 也不能退款
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "订单未支付 无法退款",
})
return
}
//查询退款记录表
//1 未退款 2 已退款 3 无法退款
// 创建退款请求
req := alipay.TradeRefund{
OutTradeNo: outTradeNo,
TradeNo: tradeNo,
RefundAmount: refundAmount,
RefundReason: refundReason,
}
// 发起退款请求
resp, err := global.AliClient.TradeRefund(c, req)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "退款失败", "message": err.Error()})
return
}
// 检查退款结果
if resp.IsSuccess() {
c.JSON(http.StatusOK, gin.H{"success": "退款成功"})
} else {
c.JSON(http.StatusOK, gin.H{"error": "退款失败", "message": resp.Msg + ": " + resp.SubMsg})
}
}
日常更新中,有问题可留言…