安装GoPay:

go get -u github.com/go-pay/gopay

各支付方式接入,请仔细查看 xxx_test.go 使用方式

  • gopay/wechat/v3/client_test.go
  • gopay/alipay/client_test.go
  • gopay/qq/client_test.go
  • gopay/paypal/client_test.go
  • gopay/apple/verify_test.go
  • 或 examples

文档

支付宝:

1、初始化支付宝客户端并做配置
​gopay/alipay/client_test.go​
import (
"github.com/go-pay/gopay/alipay"
"github.com/go-pay/gopay/pkg/xlog"
)

// 初始化支付宝客户端
// appid:应用ID
// privateKey:应用私钥,支持PKCS1和PKCS8
// isProd:是否是正式环境
client, err := alipay.NewClient("2016091200494382", privateKey, false)
if err != nil {
xlog.Error(err)
return
}

// 自定义配置http请求接收返回结果body大小,默认 10MB
client.SetBodySize() // 没有特殊需求,可忽略此配置

// 打开Debug开关,输出日志,默认关闭
client.DebugSwitch = gopay.DebugOn

// 设置支付宝请求 公共参数
// 注意:具体设置哪些参数,根据不同的方法而不同,此处列举出所有设置参数
client.SetLocation(alipay.LocationShanghai). // 设置时区,不设置或出错均为默认服务器时间
SetCharset(alipay.UTF8). // 设置字符编码,不设置默认 utf-8
SetSignType(alipay.RSA2). // 设置签名类型,不设置默认 RSA2
SetReturnUrl("https://www.fmm.ink"). // 设置返回URL
SetNotifyUrl("https://www.fmm.ink"). // 设置异步通知URL
SetAppAuthToken() // 设置第三方应用授权

// 自动同步验签(只支持证书模式)
// 传入 alipayCertPublicKey_RSA2.crt 内容
client.AutoVerifySign([]byte("alipayCertPublicKey_RSA2 bytes"))

// 公钥证书模式,需要传入证书,以下两种方式二选一
// 证书路径
err := client.SetCertSnByPath("appCertPublicKey.crt", "alipayRootCert.crt", "alipayCertPublicKey_RSA2.crt")
// 证书内容
err := client.SetCertSnByContent("appCertPublicKey bytes", "alipayRootCert bytes", "alipayCertPublicKey_RSA2 bytes")
2、API 方法调用及入参
​err != nil​​alipay.IsBizError()​​BizError​​version​​return_url​​notify_url​​app_auth_token​
  • 统一收单交易支付接口 - 示例
import (
"github.com/go-pay/gopay"
)

// 初始化 BodyMap
bm := make(gopay.BodyMap)
bm.Set("subject", "条码支付").
Set("scene", "bar_code").
Set("auth_code", "286248566432274952").
Set("out_trade_no", "GZ201909081743431443").
Set("total_amount", "0.01").
Set("timeout_express", "2m")

aliRsp, err := client.TradePay(bm)
if err != nil {
if bizErr, ok := alipay.IsBizError(err); ok {
xlog.Errorf("%+v", bizErr)
// do something
return
}
xlog.Errorf("client.TradePay(%+v),err:%+v", bm, err)
return
}
3、同步返回参数验签Sign、异步通知参数解析和验签Sign、异步通知返回
​http.Request.Body​

注意:APP支付、手机网站支付、电脑网站支付 不支持同步返回验签

支付宝支付后的同步/异步通知验签文档:​​支付结果通知​​

  • 同步返回验签,手动验签(如已开启自动验签,则无需手动验签操作)
import (
"github.com/go-pay/gopay/alipay"
)

aliRsp, err := client.TradePay(bm)
if err != nil {
xlog.Error("err:", err)
return
}

// 公钥模式验签
// 注意:APP支付,手机网站支付,电脑网站支付 不支持同步返回验签
// aliPayPublicKey:支付宝平台获取的支付宝公钥
// signData:待验签参数,aliRsp.SignData
// sign:待验签sign,aliRsp.Sign
ok, err := alipay.VerifySyncSign(aliPayPublicKey, aliRsp.SignData, aliRsp.Sign)

// 公钥证书模式验签
// aliPayPublicKeyCert:支付宝公钥证书存放路径 alipayCertPublicKey_RSA2.crt 或文件内容[]byte
// signData:待验签参数,aliRsp.SignData
// sign:待验签sign,aliRsp.Sign
ok, err := alipay.VerifySyncSignWithCert(aliPayPublicKeyCert, aliRsp.SignData, aliRsp.Sign)
  • 异步通知验签
import (
"github.com/go-pay/gopay/alipay"
)

// 解析异步通知的参数
// req:*http.Request
notifyReq, err = alipay.ParseNotifyToBodyMap(c.Request) // c.Request 是 gin 框架的写法
if err != nil {
xlog.Error(err)
return
}

// value:url.Values
notifyReq, err = alipay.ParseNotifyByURLValues()
if err != nil {
xlog.Error(err)
return
}

// 支付宝异步通知验签(公钥模式)
ok, err = alipay.VerifySign(aliPayPublicKey, notifyReq)

// 支付宝异步通知验签(公钥证书模式)
ok, err = alipay.VerifySignWithCert("alipayCertPublicKey_RSA2.crt content", notifyReq)

// 如果需要,可将 BodyMap 内数据,Unmarshal 到指定结构体指针 ptr
err = notifyReq.Unmarshal(ptr)

// ====异步通知,返回支付宝平台的信息====
// 文档:https://opendocs.alipay.com/open/203/105286
// 程序执行完后必须打印输出“success”(不包含引号)。如果商户反馈给支付宝的字符不是success这7个字符,支付宝服务器会不断重发通知,直到超过24小时22分钟。一般情况下,25小时以内完成8次通知(通知的间隔频率一般是:4m,10m,10m,1h,2h,6h,15h)

// 此写法是 gin 框架返回支付宝的写法
c.String(http.StatusOK, "%s", "success")

// 此写法是 echo 框架返回支付宝的写法
return c.String(http.StatusOK, "success")
4、支付宝 公共API(仅部分说明)
import (
"github.com/go-pay/gopay/alipay"
"github.com/go-pay/gopay/pkg/xlog"
)

// 换取授权访问令牌(默认使用utf-8,RSA2)
// appId:应用ID
// privateKey:应用私钥,支持PKCS1和PKCS8
// grantType:值为 authorization_code 时,代表用code换取;值为 refresh_token 时,代表用refresh_token换取,传空默认code换取
// codeOrToken:支付宝授权码或refresh_token
rsp, err := alipay.SystemOauthToken(appId, privateKey, grantType, codeOrToken)
if err != nil {
xlog.Error(err)
return
}

// 解密支付宝开放数据带到指定结构体
// 以小程序获取手机号为例
phone := new(alipay.UserPhone)
// 解密支付宝开放数据
// encryptedData:包括敏感数据在内的完整用户信息的加密数据
// secretKey:AES密钥,支付宝管理平台配置
// beanPtr:需要解析到的结构体指针
err := alipay.DecryptOpenDataToStruct(encryptedData, secretKey, phone)
xlog.Infof("%+v", phone)

Wechat

1、初始化微信v3客户端并做配置
​gopay/wechat/v3/client_test.go​
import (
"github.com/go-pay/gopay/pkg/xlog"
"github.com/go-pay/gopay/wechat/v3"
)

// NewClientV3 初始化微信客户端 v3
// mchid:商户ID 或者服务商模式的 sp_mchid
// serialNo:商户证书的证书序列号
// apiV3Key:apiV3Key,商户平台获取
// privateKey:私钥 apiclient_key.pem 读取后的内容
client, err = wechat.NewClientV3(MchId, SerialNo, APIv3Key, PrivateKey)
if err != nil {
xlog.Error(err)
return
}

// 设置微信平台API证书和序列号(推荐开启自动验签,无需手动设置证书公钥等信息)
//client.SetPlatformCert([]byte(""), "")

// 启用自动同步返回验签,并定时更新微信平台API证书(开启自动验签时,无需单独设置微信平台API证书和序列号)
err = client.AutoVerifySign()
if err != nil {
xlog.Error(err)
return
}

// 自定义配置http请求接收返回结果body大小,默认 10MB
client.SetBodySize() // 没有特殊需求,可忽略此配置

// 打开Debug开关,输出日志,默认是关闭的
client.DebugSwitch = gopay.DebugOn
2、API 方法调用及入参

具体参数请根据不同接口查看:​​微信支付V3的API字典概览​​

  • JSAPI下单 示例
import (
"github.com/go-pay/gopay"
)

expire := time.Now().Add(10 * time.Minute).Format(time.RFC3339)
// 初始化 BodyMap
bm := make(gopay.BodyMap)
bm.Set("sp_appid", "sp_appid").
Set("sp_mchid", "sp_mchid").
Set("sub_mchid", "sub_mchid").
Set("description", "测试Jsapi支付商品").
Set("out_trade_no", tradeNo).
Set("time_expire", expire).
Set("notify_url", "https://www.fmm.ink").
SetBodyMap("amount", func(bm gopay.BodyMap) {
bm.Set("total", 1).
Set("currency", "CNY")
}).
SetBodyMap("payer", func(bm gopay.BodyMap) {
bm.Set("sp_openid", "asdas")
})

wxRsp, err := client.V3TransactionJsapi(bm)
if err != nil {
xlog.Error(err)
return
}
if wxRsp.Code == Success {
xlog.Debugf("wxRsp: %#v", wxRsp.Response)
return
}
xlog.Errorf("wxRsp:%s", wxRsp.Error)
3、下单后,获取微信小程序支付、APP支付、JSAPI支付所需要的 pay sign
// 小程序
applet, err := client.PaySignOfApplet("appid", "prepayid")
// app
app, err := client.PaySignOfApp("appid", "prepayid")
// jsapi
jsapi, err := client.PaySignOfJSAPI("appid", "prepayid")
4、同步请求返回验签Sign、异步通知参数解析+验签Sign+回执、敏感信息加/解密
​http.Request.Body​
  • 同步返回验签,手动验签(推荐开启自动验签,则无需手动验签操作)
import (
"github.com/go-pay/gopay/wechat/v3"
"github.com/go-pay/gopay/pkg/xlog"
)

wxRsp, err := client.V3TransactionJsapi(bm)
if err != nil {
xlog.Error(err)
return
}

pkMap := client.WxPublicKeyMap()
// wxPublicKey:微信平台证书公钥内容,通过 client.WxPublicKeyMap() 获取,然后根据 signInfo.HeaderSerial 获取相应的公钥
err = wechat.V3VerifySignByPK(wxRsp.SignInfo.HeaderTimestamp, wxRsp.SignInfo.HeaderNonce, wxRsp.SignInfo.SignBody, wxRsp.SignInfo.HeaderSignature, pkMap[wxRsp.SignInfo.HeaderSerial])
if err != nil {
xlog.Error(err)
return
}
  • 异步通知解析、验签、回执
import (
"github.com/go-pay/gopay/wechat/v3"
"github.com/go-pay/gopay/pkg/xlog"
)

notifyReq, err := wechat.V3ParseNotify()
if err != nil {
xlog.Error(err)
return
}

// 获取微信平台证书
certMap := client.WxPublicKeyMap()
// 验证异步通知的签名
err = notifyReq.VerifySignByPKMap(certMap)
if err != nil {
xlog.Error(err)
return
}

// ====↓↓↓====异步通知应答====↓↓↓====
// 退款通知http应答码为200且返回状态码为SUCCESS才会当做商户接收成功,否则会重试。
// 注意:重试过多会导致微信支付端积压过多通知而堵塞,影响其他正常通知。

// 此写法是 gin 框架返回微信的写法
c.JSON(http.StatusOK, &wechat.V3NotifyRsp{Code: gopay.SUCCESS, Message: "成功"})

// 此写法是 echo 框架返回微信的写法
return c.JSON(http.StatusOK, &wechat.V3NotifyRsp{Code: gopay.SUCCESS, Message: "成功"})
  • 敏感信息加/解密
// ====↓↓↓====入参、出参加解密====↓↓↓====

// 敏感信息加密
client.V3EncryptText()
// 敏感信息解密
client.V3DecryptText()

// ====↓↓↓====异步通知参数解密====↓↓↓====

// 普通支付通知解密
result, err := notifyReq.DecryptCipherText(apiV3Key)
// 合单支付通知解密
result, err := notifyReq.DecryptCombineCipherText(apiV3Key)
// 退款通知解密
result, err := notifyReq.DecryptRefundCipherText(apiV3Key)
5、微信v3 公共API(仅部分说明)
import (
"github.com/go-pay/gopay/wechat/v3"
)

// 获取微信平台证书和序列号信息,推荐使用后者
wechat.GetPlatformCerts()

client.GetAndSelectNewestCert()

// 请求参数 敏感信息加密,推荐使用后者
wechat.V3EncryptText() 或 client.V3EncryptText()

// 返回参数 敏感信息解密,推荐使用后者
wechat.V3DecryptText() 或 client.V3DecryptText()

// 回调通知敏感信息解密
wechat.V3DecryptNotifyCipherText()
wechat.V3DecryptRefundNotifyCipherText()
wechat.V3DecryptCombineNotifyCipherText()
wechat.V3DecryptScoreNotifyCipherText()

QQ

​GoPay微信v2文档​
QQ支付 API
​client.MicroPay()​​client.Reverse()​​client.UnifiedOrder()​​client.OrderQuery()​​client.CloseOrder()​​client.Refund()​​client.RefundQuery()​​client.StatementDown()​​client.AccRoll()​​client.SendCashRed()​​client.DownloadRedListFile()​​client.QueryRedInfo()​​client.PostQQAPISelf()​
QQ公共 API
​qq.ParseNotifyToBodyMap()​​qq.ParseNotify()​​qq.VerifySign()​​qq.GetAccessToken()​​qq.GetOpenId()​​qq.GetUserInfo()​

PayPal

1、初始化PayPal客户端并做配置(Init PayPal Client)
import (
"github.com/go-pay/gopay/paypal"
"github.com/go-pay/gopay/pkg/xlog"
)

// 初始化PayPal支付客户端
client, err := paypal.NewClient(Clientid, Secret, false)
if err != nil {
xlog.Error(err)
return
}

// 自定义配置http请求接收返回结果body大小,默认 10MB
client.SetBodySize() // 没有特殊需求,可忽略此配置

// 打开Debug开关,输出日志,默认关闭
client.DebugSwitch = gopay.DebugOn
2、API 方法调用及入参(Call API)
  • Create Orders example
import (
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/paypal"
"github.com/go-pay/gopay/pkg/util"
"github.com/go-pay/gopay/pkg/xlog"
)

// Create Orders example
var pus []*paypal.PurchaseUnit
var item = &paypal.PurchaseUnit{
ReferenceId: util.GetRandomString(16),
Amount: &paypal.Amount{
CurrencyCode: "USD",
Value: "8",
},
}
pus = append(pus, item)

bm := make(gopay.BodyMap)
bm.Set("intent", "CAPTURE").
Set("purchase_units", pus).
SetBodyMap("application_context", func(b gopay.BodyMap) {
b.Set("brand_name", "gopay").
Set("locale", "en-PT").
Set("return_url", "https://example.com/returnUrl").
Set("cancel_url", "https://example.com/cancelUrl")
})
ppRsp, err := client.CreateOrder(ctx, bm)
if err != nil {
xlog.Error(err)
return
}
if ppRsp.Code != paypal.Success {
// do something
return
}
  • Capture payment for order
import (
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/pkg/xlog"
)

// Capture payment for order
//bm := make(gopay.BodyMap)
//bm.SetBodyMap("payment_source", func(b gopay.BodyMap) {
// b.SetBodyMap("token", func(b gopay.BodyMap) {
// b.Set("id", "The PayPal-generated ID for the token").
// Set("type", "BILLING_AGREEMENT")
// })
//})
ppRsp, err := client.OrderCapture(ctx, "4X223967G91314611", nil)
if err != nil {
xlog.Error(err)
return
}
if ppRsp.Code != paypal.Success {
// do something
return
}

Apple

Apple Pay 支付校验收据

url 请选择 apple.UrlSandbox 或 apple.UrlProd

​apple.VerifyReceipt()​

校验示例
import (
"github.com/go-pay/gopay/apple"
"github.com/go-pay/gopay/pkg/xlog"
)

pwd := ""
receipt := ""
rsp, err := apple.VerifyReceipt(UrlSandbox, pwd, receipt)
if err != nil {
xlog.Error(err)
return
}
/**
response body:
{"receipt":{"original_purchase_date_pst":"2021-08-14 05:28:17 America/Los_Angeles", "purchase_date_ms":"1628944097586", "unique_identifier":"13f339a765b706f8775f729723e9b889b0cbb64e", "original_transaction_id":"1000000859439868", "bvrs":"10", "transaction_id":"1000000859439868", "quantity":"1", "in_app_ownership_type":"PURCHASED", "unique_vendor_identifier":"6DFDEA8B-38CE-4710-A1E1-BAEB8B66FEBD", "item_id":"1581250870", "version_external_identifier":"0", "bid":"com.huochai.main", "is_in_intro_offer_period":"false", "product_id":"10002", "purchase_date":"2021-08-14 12:28:17 Etc/GMT", "is_trial_period":"false", "purchase_date_pst":"2021-08-14 05:28:17 America/Los_Angeles", "original_purchase_date":"2021-08-14 12:28:17 Etc/GMT", "original_purchase_date_ms":"1628944097586"}, "status":0}
*/
if rsp.Receipt != nil {
xlog.Infof("receipt:%+v", rsp.Receipt)
}
​[App 信息] --> [App Store 服务器通知]  --> [版本 2]​
示例
​notification_v2_test.go​
import (
"github.com/go-pay/gopay/apple"
"github.com/go-pay/gopay/pkg/xlog"
)

// decode signedPayload
payload, err := apple.DecodeSignedPayload(signedPayload)
if err != nil {
xlog.Error(err)
return
}
xlog.Debugf("payload.NotificationType: %s", payload.NotificationType)
xlog.Debugf("payload.Subtype: %s", payload.Subtype)
xlog.Debugf("payload.NotificationUUID: %s", payload.NotificationUUID)
xlog.Debugf("payload.NotificationVersion: %s", payload.NotificationVersion)
xlog.Debugf("payload.Data: %+v", payload.Data)
bs1, _ := json.Marshal(payload)
xlog.Color(xlog.RedBright).Info(string(bs1))
/*
{
"notificationType":"DID_RENEW",
"subtype":"",
"notificationUUID":"469bf30e-7715-4f9f-aae3-a7bfc12aea77",
"notificationVersion":"",
"data":{
"appAppleId":0,
"bundleId":"com.audaos.audarecorder",
"bundleVersion":"7",
"environment":"Sandbox",
"signedRenewalInfo":"xxxxxxxxxx",
"signedTransactionInfo":"xxxxxxxxxxx"
}
}
*/

// decode renewalInfo
renewalInfo, err := payload.DecodeRenewalInfo()
if err != nil {
xlog.Error(err)
return
}
xlog.Debugf("data.renewalInfo: %+v", renewalInfo)
bs, _ := json.Marshal(renewalInfo)
xlog.Color(xlog.GreenBright).Info(string(bs))
/*
{
"autoRenewProductId":"com.audaos.audarecorder.vip.m2",
"autoRenewStatus":1,
"expirationIntent":0,
"gracePeriodExpiresDate":0,
"isInBillingRetryPeriod":false,
"offerIdentifier":"",
"offerType":0,
"originalTransactionId":"2000000000842607",
"priceIncreaseStatus":0,
"productId":"com.audaos.audarecorder.vip.m2",
"signedDate":1646387008228
}
*/

// decode transactionInfo
transactionInfo, err := payload.DecodeTransactionInfo()
if err != nil {
xlog.Error(err)
return
}
xlog.Debugf("data.transactionInfo: %+v", transactionInfo)
bs2, _ := json.Marshal(transactionInfo)
xlog.Color(xlog.YellowBright).Info(string(bs2))
/*
{
"appAccountToken":"",
"bundleId":"com.audaos.audarecorder",
"expiresDate":1646387196000,
"inAppOwnershipType":"PURCHASED",
"isUpgraded":false,
"offerIdentifier":"",
"offerType":0,
"originalPurchaseDate":1646046037000,
"originalTransactionId":"2000000000842607",
"productId":"com.audaos.audarecorder.vip.m2",
"purchaseDate":1646387016000,
"quantity":1,
"revocationDate":0,
"revocationReason":"",
"signedDate":1646387008254,
"subscriptionGroupIdentifier":"20929536",
"transactionId":"2000000004047119",
"type":"Auto-Renewable Subscription",
"webOrderLineItemId":"2000000000302832"
}
*/
App Store Server API
​apple.GetTransactionHistory()​​apple.GetAllSubscriptionStatuses()​

官方文档:

点击查看不同支付方式的使用文档。方便的话,请给作者留下您认可的小星星,十分感谢!