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 }