Go语言对接微信支付与退款指南(示例详解)

 更新时间:2024年10月09日 08:57:33   作者:tatasix  
在互联网技术日益发展的背景下,Go语言凭借并发处理能力,在后端开发中大放异彩,本文详细介绍如何使用Go语言对接微信支付,完成支付和退款功能,包括准备工作、初始化微信支付客户端、实现支付功能,以及处理支付回调和退款等

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

一、准备工作

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

  • 注册成为微信支付商户:如果你还没有微信支付商户账号,需要先前往微信支付商户平台完成注册。
  • 获取必要的配置信息
    • 商户号 (MchId)
    • AppID (Appid)
    • API v3 密钥 (ApiV3Key)
    • 商户证书序列号 (MchSerialNo)
    • 私钥 (PrivateKey)
    • 支付通知地址 (NotifyUrl)
    • 退款通知地址 (RefundUrl)

安装第三方库:为了简化微信支付接口的调用,推荐使用github.com/go-pay/gopay这个库。可以通过go get命令安装:

go get github.com/go-pay/gopay

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

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

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实例。

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端支付

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端支付

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端支付

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. 解析支付回调

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

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方法。

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
}

解析退款回调

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语言对接微信支付,并实现了支付和退款功能。这些功能不仅能够提升用户体验,还能帮助你在实际项目中更加高效地处理支付相关的业务逻辑。希望本文对你有所帮助!

到此这篇关于Go语言对接微信支付与退款全流程指南的文章就介绍到这了,更多相关Go语言对接微信支付与退款内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解Go语言中获取文件路径的不同方法与应用场景

    详解Go语言中获取文件路径的不同方法与应用场景

    在使用 Go 开发项目时,估计有不少人遇到过无法正确处理文件路径的问题,本文将尝试从简单到复杂,详细介绍 Go 中获取路径的不同方法及应用场景,希望对大家有所帮助
    2024-02-02
  • Golang因Channel未关闭导致内存泄漏的解决方案详解

    Golang因Channel未关闭导致内存泄漏的解决方案详解

    这篇文章主要为大家详细介绍了当Golang因Channel未关闭导致内存泄漏时盖如何解决,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
    2023-07-07
  • Go语言操作Excel的实现示例

    Go语言操作Excel的实现示例

    excelize是一个功能丰富且易于使用的Go语言库,它极大地简化了Excel文件的读写操作,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-12-12
  • 一文掌握Golang的panic和recover实战

    一文掌握Golang的panic和recover实战

    Go语言中,异常处理通常依赖error返回值,本文将通过示例展示如何在Go语言中正确使用recover来处理panic异常,防止程序直接崩溃,感兴趣的朋友跟随小编一起看看吧
    2024-09-09
  • Golang爬虫框架colly使用浅析

    Golang爬虫框架colly使用浅析

    这篇文章主要介绍了Golang爬虫框架colly的使用,colly是Go实现的比较有名的一款爬虫框架,而且Go在高并发和分布式场景的优势也正是爬虫技术所需要的,感兴趣想要详细了解可以参考下文
    2023-05-05
  • Go 协程超时控制的实现

    Go 协程超时控制的实现

    本文主要介绍了Go 协程超时控制的实现,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • go设置多个GOPATH的方式

    go设置多个GOPATH的方式

    这篇文章主要介绍了go设置多个GOPATH的方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-05-05
  • GoFrame基于性能测试得知grpool使用场景

    GoFrame基于性能测试得知grpool使用场景

    这篇文章主要为大家介绍了GoFrame基于性能测试得知grpool使用场景示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • Go语言break跳转语句怎么使用

    Go语言break跳转语句怎么使用

    这篇文章主要介绍了Go语言break跳转语句怎么使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-01-01
  • golang之JWT实现的示例代码

    golang之JWT实现的示例代码

    这篇文章主要介绍了golang之JWT实现的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05

最新评论