Go语言对接微信支付与退款全流程指南

目录:

一、准备工作

二、初始化微信支付客户端

三、实现支付功能

  1. 付款时序图

  2. 实现不同场景下的支付

WAP端支付

PC端支付

Android端支付

  1. 解析支付回调

四、实现退款功能

退款时序图

发起退款

解析退款回调

五、总结

在互联网技术日益发展的今天,线上支付已成为不可或缺的一部分。作为一门简洁高效的编程语言,Go(又称Golang)凭借其强大的并发处理能力和高效性能,在后端开发领域越来越受到开发者的青睐。本文将详细介绍如何使用Go语言对接微信支付,并实现支付和退款功能,帮助开发者快速上手。

一、准备工作

在开始编写代码之前,你需要先准备好以下几项工作:

  1. 注册成为微信支付商户 :如果你还没有微信支付商户账号,需要先前往微信支付商户平台完成注册。

  2. 获取必要的配置信息

    • 商户号 (MchId)
    • AppID (Appid)
    • API v3 密钥 (ApiV3Key)
    • 商户证书序列号 (MchSerialNo)
    • 私钥 (PrivateKey)
    • 支付通知地址 (NotifyUrl)
    • 退款通知地址 (RefundUrl)
  3. 安装第三方库 :为了简化微信支付接口的调用,推荐使用github.com/go-pay/gopay这个库。可以通过go get命令安装:

    bash 复制代码
    go get github.com/go-pay/gopay

二、初始化微信支付客户端

首先,我们需要创建一个WechatPayService结构体来封装微信支付的相关操作。该结构体包含上下文、配置信息和微信支付客户端实例。

go 复制代码
type WechatPayService struct {
	ctx       context.Context
	config    WechatPayConfig
	wechatPay *wechat.ClientV3
}

type WechatPayConfig struct {
	Appid       string
	Appid1      string
	MchId       string
	ApiV3Key    string
	MchSerialNo string
	PrivateKey  string
	NotifyUrl   string
	RefundUrl   string
}

接着,我们通过NewWechatPayService函数来初始化WechatPayService实例。

go 复制代码
func NewWechatPayService(ctx context.Context, config WechatPayConfig) *WechatPayService {
	client, err := wechat.NewClientV3(config.MchId, config.MchSerialNo, config.ApiV3Key, config.PrivateKey)
	if err != nil {
		fmt.Println(err)
		return nil
	}
	err = client.AutoVerifySign()
	if err != nil {
		fmt.Println(err)
		return nil
	}
	client.DebugSwitch = gopay.DebugOn

	return &WechatPayService{
		ctx:       ctx,
		wechatPay: client,
		config:    config,
	}
}

此代码段中,我们通过NewClientV3方法初始化了微信支付客户端,传入商户号、证书序列号、API v3密钥和私钥等关键参数。为了保障支付的安全性,开启了自动验签功能。

三、实现支付功能

1. 付款时序图

2. 实现不同场景下的支付

WAP端支付

go 复制代码
func (w *WechatPayService) WapPay(charge *Charge) (result string, err error) {
	amount := decimal.NewFromInt(charge.MoneyFee).DivRound(decimal.NewFromInt(1), 2).IntPart()
	expire := time.Now().Add(10 * time.Minute).Format(time.RFC3339)
	bm := make(gopay.BodyMap)
	bm.Set("appid", w.config.Appid).
		Set("mchid", w.config.MchId).
		Set("description", charge.Describe).
		Set("out_trade_no", charge.TradeNum).
		Set("time_expire", expire).
		Set("notify_url", w.config.NotifyUrl).
		SetBodyMap("amount", func(bm gopay.BodyMap) {
			bm.Set("total", amount).
				Set("currency", "CNY")
		}).
		SetBodyMap("scene_info", func(bm gopay.BodyMap) {
			bm.Set("payer_client_ip", "127.0.0.1").
				SetBodyMap("h5_info", func(bm gopay.BodyMap) {
					bm.Set("type", "Wap")
				})
		})

	rsp, err := w.wechatPay.V3TransactionH5(w.ctx, bm)
	if err != nil {
		return
	}
	result = rsp.Response.H5Url
	return
}

PC端支付

go 复制代码
func (w *WechatPayService) PcPay(charge *Charge) (result string, err error) {
	amount := decimal.NewFromInt(charge.MoneyFee).DivRound(decimal.NewFromInt(1), 2).IntPart()
	expire := time.Now().Add(10 * time.Minute).Format(time.RFC3339)
	bm := make(gopay.BodyMap)
	bm.Set("appid", w.config.Appid).
		Set("mchid", w.config.MchId).
		Set("description", charge.Describe).
		Set("out_trade_no", charge.TradeNum).
		Set("time_expire", expire).
		Set("notify_url", w.config.NotifyUrl).
		SetBodyMap("amount", func(bm gopay.BodyMap) {
			bm.Set("total", amount).
				Set("currency", "CNY")
		})

	rsp, err := w.wechatPay.V3TransactionNative(w.ctx, bm)
	if err != nil {
		return
	}
	result = rsp.Response.CodeUrl
	return
}

Android端支付

go 复制代码
func (w *WechatPayService) AndroidPay(charge *Charge) (result string, err error) {
	amount := decimal.NewFromInt(charge.MoneyFee).DivRound(decimal.NewFromInt(1), 2).IntPart()
	expire := time.Now().Add(10 * time.Minute).Format(time.RFC3339)
	bm := make(gopay.BodyMap)
	bm.Set("appid", w.config.Appid1).
		Set("mchid", w.config.MchId).
		Set("description", charge.Describe).
		Set("out_trade_no", charge.TradeNum).
		Set("time_expire", expire).
		Set("notify_url", w.config.NotifyUrl).
		SetBodyMap("amount", func(bm gopay.BodyMap) {
			bm.Set("total", amount).
				Set("currency", "CNY")
		})

	rsp, err := w.wechatPay.V3TransactionApp(w.ctx, bm) 
	if err != nil {
		return
	}

	jsapiInfo, err := w.wechatPay.PaySignOfApp(w.config.Appid1, rsp.Response.PrepayId)
	str, _ := json.Marshal(jsapiInfo)
	result = string(str)
	return
}
  • APP支付跟JSAPI支付很像。主要区别在于app与商户服务后台的交互。app会从商户服务后台获取签名信息,根据签名信息,app直接调用微信支付服务下单。

3. 解析支付回调

当用户完成支付后,微信会向我们的服务器发送支付成功的回调通知。我们需要解析这个通知并验证其合法性。

go 复制代码
func (w *WechatPayService) GetNotifyResult(r *http.Request) (res *wechat.V3DecryptResult, err error) {
	notifyReq, err := wechat.V3ParseNotify(r)    // 解析回调参数
	if err != nil {
		fmt.Println(err)
		return
	}
	if notifyReq == nil {
		return
	}
	return notifyReq.DecryptCipherText(w.config.ApiV3Key)  // 解密回调内容
}

通过V3ParseNotify方法,解析支付通知,并使用API v3密钥解密支付结果。

四、实现退款功能

退款时序图

发起退款

除了支付,退款也是微信支付中常用的功能。接下来,我们来看如何使用Go语言实现退款功能。

当需要对已支付的订单进行退款时,可以调用Refund方法。

go 复制代码
func (w *WechatPayService) Refund(charge *RefundCharge) (err error) {
	amount := decimal.NewFromInt(charge.MoneyFee).DivRound(decimal.NewFromInt(1), 2).IntPart()
	bm := make(gopay.BodyMap)
	bm.Set("out_trade_no", charge.TradeNum).
		Set("out_refund_no", charge.OutRefundNo).
		Set("reason", charge.RefundReason).
		Set("notify_url", w.config.RefundUrl).
		SetBodyMap("amount", func(bm gopay.BodyMap) {
			bm.Set("total", amount).
				Set("refund", amount).
				Set("currency", "CNY")
		})

	rsp, err := w.wechatPay.V3Refund(w.ctx, bm)  // 发起退款请求
	if err != nil {
		return
	}

	if rsp == nil || rsp.Response == nil || rsp.Error != "" {
        // 处理退款错误
		err = errors.New(rsp.Error) 
		return
	}
	return
}

解析退款回调

go 复制代码
func (w *WechatPayService) GetRefundNotifyResult(r *http.Request) (res *wechat.V3DecryptRefundResult, err error) {
	notifyReq, err := wechat.V3ParseNotify(r)
	if err != nil {
		return
	}
	return notifyReq.DecryptRefundCipherText(w.config.ApiV3Key)
}

五、总结

通过本文的介绍,相信你已经掌握了如何使用Go语言对接微信支付,并实现了支付和退款功能。这些功能不仅能够提升用户体验,还能帮助你在实际项目中更加高效地处理支付相关的业务逻辑。希望本文对你有所帮助!

如果你有任何问题或建议,欢迎在评论区留言交流。期待你的宝贵意见!

相关推荐
DemonAvenger15 小时前
深入剖析 sync.Once:实现原理、应用场景与实战经验
分布式·架构·go
一个热爱生活的普通人2 天前
Go语言中 Mutex 的实现原理
后端·go
孔令飞2 天前
关于 LLMOPS 的一些粗浅思考
人工智能·云原生·go
小戴同学2 天前
实时系统降低延时的利器
后端·性能优化·go
Golang菜鸟2 天前
golang中的组合多态
后端·go
Serverless社区2 天前
函数计算支持热门 MCP Server 一键部署
go
Wo3Shi4七3 天前
二叉树数组表示
数据结构·后端·go
网络研究院3 天前
您需要了解的有关 Go、Rust 和 Zig 的信息
开发语言·rust·go·功能·发展·zig
27669582923 天前
拼多多 anti-token unidbg 分析
java·python·go·拼多多·pdd·pxx·anti-token
程序员爱钓鱼3 天前
Go 语言邮件发送完全指南:轻松实现邮件通知功能
后端·go·排序算法