php是最好的语言

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
}


作者:xTao 分类:LNMP 浏览:2229 评论:0