golang AliPay 沙箱环境
参考:
[go]沙盒环境下调用支付宝扫码支付_gin支付宝沙盒_CRAJA的博客-CSDN博客
内网穿透建议使用cpolar 配置很简单参考:
如何使用 cpolar 工具进行内网穿透? - 知乎 (zhihu.com)
main.go
package main
import (
"apay/pay"
"fmt"
"github.com/gin-gonic/gin"
"github.com/smartwalle/xid"
"log"
"net/http"
"strconv"
)
func init() {
log.SetFlags(log.Lshortfile | log.Ltime)
}
const (
kAppID = "9021000126611535"
kPrivateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDE4Otnh53Slcl24lmC1//uNTAuUXEJ6us0qZa7UuT4UCNcXy0kIxd1sL4DlPM4Az6B/gQYBhv/XBot84iz8/bLR9ourkF30Te5oSzpVBbaj33Eni55hAf+y8fEO3sWk0O0gNBpI393lT5NBfeuPMPZFvL6r111s0acbKEXH+tSIqlm1derryC6h8WSTRqzZyiBY9+pR2Mct3xQLr3002ZZFyJ5HmSSjyW7A27bnYi3Kdpu0XHAPM1QeKQkuRW0/pn07uslMtuDJaX743fKjgfCA8PdkvHcS6EPxdg1HEJCDOZIByoSStHWf9XL0X1NO48820BBGlLwAVFq6LkyGQlhAgMBAAECggEAV3X73cA3cmesWi3vydqfDXl/b/QqHVfJKVn1Zj5LDay8KLr3WMhmEZh0sS6oZce7KxBnTSzZnzWRQ7yejCLjDidbW8V5u/qroMFjLDx48pswKfqDduQakSyCEEDES6EcQMHA1BQ6+irIxHrULwk7j+NMCeWBxVnhEniHj7hUV+XZ3xVvu/gsmiE14H7I7YxVVmI9o5wY3KbdSpNCIjKc/9GpPc3p72FkHU0KW5H6RFSdbByqw4Ab9sj5dVwGlAfvI/CYD2jkjzIJ1DLyOBiKyHrDBN1xLfhCRjJXfNE2S+/+iZPZecwuTRHJmW3qCewYFVoFqfHnbiyD/l6NfWuS6QKBgQD0cNZ2UXV3gltPC9qj9x4JmFJGvVetP6xGWn4p/2rFRbFYYAMuGMGEq9Txk2IA3RBYxYlCNrTt4R7DQ3i0/JUBzjTkKy25P0qaymgfSy55GVG1SNu4IzakhVUavY27U1SKnolCUBBab38U6BUTF1ep4LYGz1R/8s8P42WOKB4QZwKBgQDOME0n+XM3ENXaB5Na0tHhLVdp6lsfLf6npAzvAZ/bPyDXxGJZwnUEfBTwmyXUt8OQ0GR91C0Cq2YvwBa3j0qz9LbcnKE6Um3gzxBCt4TQeXsj4YKJjRqupfCF5wvM3LEgQpwt4Jo9N3wUGJYZffTmzo8r3khM7hKigjJTijVa9wKBgGeHRv3zwEmul1eiUKbzafBsaCR32yOQfTmK6DaHbERDITqp547lg0jasiTi5DYzatH+UAUQQI8jsrDG6d/VvpJ2MKaWtxXnfYSTx4fBgtxdYeLjCdQybdk+c24Q7G9zerH/NWk1mceDw5IqPYrJbyKO/ulhdIuAXjx/dirASQNvAoGBAKJo/X1tScmoIyq4KOOzh8pc0qwFA32N1zbpS56/otEp6o7Ki8W0HERKEJxtOOFTVmItI9rRVBr0M2uNXNd9sRQKwDcJ0R2FbDNWJrLGp4EnD032BcKfONM0tj5i/bKCa6rDjQ2+CwAJQlz5+NFfm5ZqQfAGCOE/7cG/2DBhfyGFAoGBAIz5/RLEkxZR6QV5nv/O9qYXQJ/OhHxubzXcvWNcLasT0lRgOT0PQDTg38mYvMVC9lfO1i4MpeKqJhO08lDUXQjJ/CJtWcfN4m+bnEGfrScrmKEe64sZxf05SDQEyan7Co9xwkfAzuAeSVQyEEl1CNp4AYxzd5ZQsKoIi3E2qes2"
kServerDomain = "http://59cf885b.r8.cpolar.top"
AppPublicCertPath = "cert/appPublicCert.crt" // app公钥证书路径
AliPayRootCertPath = "cert/alipayRootCert.crt" // alipay根证书路径
AliPayPublicCertPath = "cert/alipayPublicCert.crt" // alipay公钥证书路径
NotifyURL = kServerDomain + "/notify"
ReturnURL = kServerDomain + "/callback"
IsProduction = false
)
var AliPayClient *pay.AliPayClient
func main() {
AliPayClient = pay.Init(pay.Config{
KAppID: kAppID,
KPrivateKey: kPrivateKey,
IsProduction: IsProduction,
AppPublicCertPath: AppPublicCertPath,
AliPayRootCertPath: AliPayRootCertPath,
AliPayPublicCertPath: AliPayPublicCertPath,
NotifyURL: NotifyURL,
ReturnURL: ReturnURL,
})
var s = gin.Default()
s.GET("/alipay", payUrl)
s.GET("/callback", callback)
s.POST("/notify", notify)
s.Run(":8083")
}
//重定向到支付宝二维码
func payUrl(c *gin.Context) {
orderID := strconv.FormatInt(xid.Next(), 10)
url, err := AliPayClient.Pay(pay.Order{
ID: orderID,
Subject: "ttms:" + orderID,
TotalAmount: 8,
Code: pay.LaptopWebPay,
})
if err != nil {
log.Println(err)
c.JSON(http.StatusOK, "系统错误")
return
}
c.Redirect(http.StatusTemporaryRedirect, url)
}
//支付后页面的重定向界面
func callback(c *gin.Context) {
_ = c.Request.ParseForm() // 解析form
orderID, err := AliPayClient.VerifyForm(c.Request.Form)
if err != nil {
log.Println(err)
c.JSON(http.StatusOK, "校验失败")
return
}
c.JSON(http.StatusOK, "callback支付成功:"+orderID)
}
//支付成功后支付宝异步通知
func notify(c *gin.Context) {
_ = c.Request.ParseForm() // 解析form
orderID, err := AliPayClient.VerifyForm(c.Request.Form)
if err != nil {
log.Println(err)
return
}
log.Println("notify支付成功:" + orderID)
// 做自己的事
fmt.Println("-----------------------------------------")
fmt.Println("-----------------------------------------orderID:", orderID)
}pay.go
package pay
import (
"errors"
"fmt"
"net/url"
"strconv"
"github.com/smartwalle/alipay/v3"
)
type AliPayClient struct {
client *alipay.Client
notifyURL string
returnURL string
}
// Config 初始化配置文件
type Config struct {
KAppID string // 应用ID
KPrivateKey string // 应用私钥
IsProduction bool // 是否是正式环境
AppPublicCertPath string // app公钥证书路径
AliPayRootCertPath string // alipay根证书路径
AliPayPublicCertPath string // alipay公钥证书路径
NotifyURL string // 异步通知地址
ReturnURL string // 支付后回调链接地址
}
// Init 客户端初始化
func Init(config Config) *AliPayClient {
var err error
var aliClient *alipay.Client
doThat := func(f func() error) {
if err = f(); err != nil {
panic(err)
}
}
doThat(func() error {
aliClient, err = alipay.New(config.KAppID, config.KPrivateKey, config.IsProduction)
return err
})
doThat(func() error { return aliClient.LoadAppCertPublicKeyFromFile(config.AppPublicCertPath) })
doThat(func() error { return aliClient.LoadAliPayRootCertFromFile(config.AliPayRootCertPath) })
doThat(func() error { return aliClient.LoadAlipayCertPublicKeyFromFile(config.AliPayPublicCertPath) })
return &AliPayClient{client: aliClient, notifyURL: config.NotifyURL, returnURL: config.ReturnURL}
}
type Order struct {
ID string // 订单ID
Subject string // 订单标题
TotalAmount float32 // 订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000]
Code ProductCode // 销售产品码,与支付宝签约的产品码名称
}
type ProductCode string
const (
AppPay ProductCode = "QUICK_MSECURITY_PAY" // app支付
PhoneWebPay ProductCode = "QUICK_WAP_WAY" // 手机网站支付
LaptopWebPay ProductCode = "FAST_INSTANT_TRADE_PAY" // 电脑网站支付
)
var (
ErrOrderAmountOver = errors.New("订单金额超限")
ErrVerifySign = errors.New("异步通知验证签名未通过")
)
// Pay 订单支付请求,返回支付界面链接及可能出现的错误
func (client *AliPayClient) Pay(order Order) (payUrl string, err error) {
if order.TotalAmount < 0.01 || order.TotalAmount > 100000000 {
return "", ErrOrderAmountOver
}
var p = alipay.TradePagePay{}
p.NotifyURL = client.notifyURL
p.ReturnURL = client.returnURL
p.Subject = order.Subject
p.OutTradeNo = order.ID
p.TotalAmount = strconv.FormatFloat(float64(order.TotalAmount), 'f', 2, 32)
p.ProductCode = string(order.Code)
pay, err := client.client.TradePagePay(p)
if err != nil {
return "", err
}
return pay.String(), nil
}
// VerifyForm 校验form表单并返回对应订单ID(注意: callback为get,notify为post)
func (client *AliPayClient) VerifyForm(form url.Values) (orderID string, err error) {
err = client.client.VerifySign(form)
if err != nil {
return "", err
}
//if !ok {
// return "", ErrVerifySign
//}
orderID = form.Get("out_trade_no")
var p = alipay.TradeQuery{}
p.OutTradeNo = orderID
rsp, err := client.client.TradeQuery(p)
if err != nil {
return "", fmt.Errorf("异步通知验证订单 %s 信息发生错误: %s", orderID, err.Error())
}
if rsp.IsSuccess() == false {
return "", fmt.Errorf("异步通知验证订单 %s 信息发生错误: %s-%s", orderID, "", "")
}
return orderID, nil
}