smartwalle / alipay

支付宝 AliPay SDK for Go, 集成简单,功能完善,持续更新,支持公钥证书和普通公钥进行签名和验签,支持文件上传和接口内容加密。
MIT License
1.87k stars 425 forks source link
alipay alipaysdk go golang pay

AliPay SDK for Golang

鸣谢

jetbrains.svg

安装

启用 Go module

go get github.com/smartwalle/alipay/v3
import github.com/smartwalle/alipay/v3

未启用 Go module

go get github.com/smartwalle/alipay
import github.com/smartwalle/alipay

帮助

在集成的过程中有遇到问题,欢迎加 QQ 群 203357977 讨论。

其它支付

苹果支付 https://github.com/smartwalle/apple

PayPal https://github.com/smartwalle/paypal

银联支付 https://github.com/smartwalle/unionpay

关于各分支(版本)

推荐使用 v3 版本

v3 版本如何初始化

下面用到的 privateKey 需要特别注意一下,如果是通过“支付宝开发平台开发助手”创建的CSR文件,在 CSR 文件所在的目录下会生成相应的私钥文件,我们需要使用该私钥进行签名。

var client, err = alipay.New(appID, privateKey, isProduction)

关于应用私钥 (privateKey)

应用私钥是我们通过工具生成的私钥,调用支付宝接口的时候,我们需要使用该私钥对参数进行签名。

关于 alipay.New() 函数中的最后一个参数 isProduction

支付宝提供了用于开发时测试的 sandbox 环境,对接的时候需要注意相关的 app id 和密钥是 sandbox 环境还是 production 环境的。如果是 sandbox 环境,本参数应该传 false,否则为 true。

公钥证书模式

如果采用公钥证书方式进行验证签名,需要调用以下几个方法加载证书信息,所有证书都是从支付宝创建的应用处下载,参考 https://docs.open.alipay.com/291/105971/https://docs.open.alipay.com/291/105972/

client.LoadAppCertPublicKeyFromFile("/路径/appCertPublicKey_2017011104995404.crt") // 加载应用公钥证书
client.LoadAliPayRootCertFromFile("/路径/alipayRootCert.crt")                // 加载支付宝根证书
client.LoadAlipayCertPublicKeyFromFile("/路径/alipayCertPublicKey_RSA2.crt") // 加载支付宝公钥证书

普通公钥模式

需要注意此处用到的公钥是支付宝公钥,不是我们用工具生成的应用公钥。

如何查看支付宝公钥?

client.LoadAliPayPublicKey("aliPublicKey")

特别注意:公钥证书普通公钥不能同时存在,只能选择其中一种。

接口内容加密

详细内容访问 https://opendocs.alipay.com/common/02mse3 进行了解。

如果需要开启该功能,只需调用一下 SetEncryptKey() 方法。

client.SetEncryptKey("key")

如果不需要开启该功能,则不用调用该方法。

签名验证

内部已实现对支付宝返回的数据进行签名验证,详细信息请参考自行实现验签

需要自行对签名验证的场景有 同步回调(return_url)异步通知(notify_url) 的 HTTP 处理函数。

同步回调(return_url)

发起支付(网页支付)的时候,如果有提供 ReturnURL 参数,那么支付成功之后,支付宝会将浏览器重定向到该 URL,并附带上相关的参数。

var p = alipay.TradeWapPay{}
p.ReturnURL = "http://xxx/return"

对支付宝提供的参数进行签名验证:

http.HandleFunc("/return", func (writer http.ResponseWriter, request *http.Request) {
request.ParseForm()
if err := client.VerifySign(request.Form); err != nil {
// 如果 err 不为空,则表示验签失败
fmt.Println(err)
return
}
// 业务处理
}

异步通知(notify_url)

有支付或者其它动作发生后,支付宝服务器会调用我们提供的 NotifyURL,并向其传递相关的信息。参考手机网站支付结果异步通知

var p = alipay.TradeWapPay{}
p.NotifyURL = "http://xxx/return"

解析通知并验证签名:

http.HandleFunc("/notify", func (writer http.ResponseWriter, request *http.Request) {
request.ParseForm()

// DecodeNotification 内部已调用 VerifySign 方法验证签名
var noti, err = client.DecodeNotification(request.Form)
if err != nil {
// 错误处理
fmt.Println(err)
return
}
// 业务处理
// 如果通知消息没有问题,我们需要确认收到通知消息,不然支付宝后续会继续推送相同的消息
alipay.ACKNotification(writer)
})

支持 RSA2 签名及验证

采用 RSA2 签名,不再提供 RSA 的支持。

特别注意

提供给支付宝的 NotifyURL 和 ReturnURL 最好不要附带任何参数,支付宝在生成签名信息的时候不会包含 URL 中的参数,而 VerifySign() 方法在验证签名的时候会将收到的所有参数一起验证。

如果确实需要附带参数,可以在调用 VerifySign() 方法前,将附带的参数从 request.Form 中删除。

已实现接口

中线(-)后面的名称是该接口在 Client 结构体中对应的方法名。

集成流程

支付宝开放平台申请创建相关的应用,使用自己的支付宝账号登录即可。

沙箱环境

支付宝开放平台为每一个应用提供了沙箱环境,供开发人员开发测试使用。

沙箱环境是独立的,每一个应用都会有一个商家账号和买家账号。

沙箱环境网关地址

沙箱环境目前有两个网关地址:

大家在对接的时候一定要确认清楚是新地址还是老地址。

本 SDK 目前默认使用的是 【新地址】,如果需要使用老地址,只需要在初始化的时候通过 alipay.WithPastSandboxGateway() 指定即可。

alipay.New(appId, privateKey, isProduction, alipay.WithPastSandboxGateway())

应用信息配置

参考官网文档 进行应用的配置。

本 SDK 中的签名方法默认为 RSA2,采用支付宝提供的 RSA签名&验签工具 生成秘钥时,秘钥长度推荐 2048。所以在支付宝管理后台请注意配置 RSA2(SHA256)密钥

生成秘钥对之后,将公钥提供给支付宝(通过支付宝后台上传)对我们请求的数据进行签名验证,我们的代码中将使用私钥对请求数据签名。

请参考 如何生成 RSA 密钥

创建 Wap 支付

var privateKey = "xxx" // 必须,上一步中使用 RSA签名验签工具 生成的私钥
var client, err = alipay.New(appId, privateKey, false)
if err != nil {
fmt.Println(err)
return
}

// 加载应用公钥证书
if err = client.LoadAppCertPublicKeyFromFile("appCertPublicKey_2017011104995404.crt"); err != nil {
// 错误处理
}

// 加载支付宝根证书
if err = client.LoadAliPayRootCertFromFile("alipayRootCert.crt"); err != nil {
// 错误处理
}

// 加载支付宝公钥证书
if err = client.LoadAlipayCertPublicKeyFromFile("alipayCertPublicKey_RSA2.crt"); err != nil {
// 错误处理
}

// 加载内容密钥,可选
if err = client.SetEncryptKey("FtVd5SgrsUzYQRAPBmejHQ=="); err != nil {
// 错误处理
}

var p = alipay.TradeWapPay{}
p.NotifyURL = "http://xxx"
p.ReturnURL = "http://xxx"
p.Subject = "标题"
p.OutTradeNo = "传递一个唯一单号"
p.TotalAmount = "10.00"
p.ProductCode = "QUICK_WAP_WAY"

var url, err = client.TradeWapPay(p)
if err != nil {
fmt.Println(err)
}

// 这个 payURL 即是用于打开支付宝支付页面的 URL,可将输出的内容复制,到浏览器中访问该 URL 即可打开支付页面。
var payURL = url.String()
fmt.Println(payURL)

自定义请求

对于本库还未实现接口,可使用 alipay.Payload 结构体作为参数调用 alipay.Client 结构体的 Request() 方法。

var p = alipay.NewPayload("这里是接口名称,如:alipay.trade.query")
// 添加公共请求参数,如:app_auth_token
p.AddParam("key", "value")
// 添加请求参数(业务相关)
p.AddBizField("key", "value")

// 应用场景一:需要向支付宝服务器发起网络请求,如交易查询接口(alipay.trade.query)
var result map[string]interface{}
// result 也可以为结构体,可参照 alipay.TradeQueryRsp
var err = client.Request(p, &result)
if err != nil {
...
}

// 应用场景二:只需要生成 URL,如网页支付(alipay.trade.page.pay)
var url, err = clietn.BuildURL(p)
...

// 应用场景三:只需要对参数进行签名,如 App 支付(alipay.trade.app.pay)
var s, err = client.EncodeParam(p)
...

更多信息

文件上传

使用 自定义请求 实现 alipay.open.file.upload(支付宝文件上传接口) 功能,需要注意本接口是小程序应用的功能,需要在小程序应用中开启 搜素直达 才能正常使用。

var p = alipay.NewPayload("alipay.open.file.upload")
p.Encrypt = false // 文件上传不支持接口内容加密

// 设置参数
p.AddParam("biz_code", "content_creation")

// 添加文件
p.AddFilePath("file_content", "a.jpg", "path/a.jpg")
// 或者
p.AddFileObject("file_content", "a.jpg", io.Reader)

var result map[string]interface{}
var err = client.Request(p, &result)

链路跟踪

// HTTP客户端
httpClient := &http.Client{
    Timeout: 30 * time.Second,
    Transport: otelhttp.NewTransport(
        http.DefaultTransport,
        otelhttp.WithSpanNameFormatter(func(operation string, r *http.Request) string {
            return r.Method + " " + r.URL.Path
        }),
    ),
}

// 初始化支付宝客户端
client, err := alipay.New(appId, privateKey, false, alipay.WithHTTPClient(httpClient))
if err != nil {
    fmt.Println(err)
}

示例

网页支付

感谢你的支持

如果对你有帮助,请我喝杯咖啡吧!

支付宝 微信

License

This project is licensed under the MIT License.