证书模式支付宝支付接口demo 沙箱

  1. go语言推荐使用第三方支付库 github.com/go-pay/gopay
  2. 说明
    1. 支付宝和应用都有公私钥,即2套,为什么是2套?
      1. 用户通过应用私钥加密发送消息给支付宝,支付宝通过应用公钥解密;
      2. 支付宝通过支付宝私钥加密发送消息给用户,用户通过支付宝公钥解密。
    2. 加签和验签
      1. sign生成规则:
        1. 发送给支付宝的所有参数、剔除sign、sign_type
        2. 按照参数字母顺序排序,且格式为key=value,参数之间使用&连接
        3. 使用对应的sign_type转换,如MD5
      2. 用户发送给支付宝的消息需要加签,即按照规则生成sign然后以参数sign传递过去,因为支付宝需要校验请求的正确性
      3. 支付宝回调的通知请求需要验签,因为用户也需要验证支付宝请求的正确性
  3. 工具
    1. 沙箱工具及使用 https://opendocs.alipay.com/common/02kkv7
    2. 支付应用文档可参考请求和响应参数 https://opendocs.alipay.com/open/02e7gq?scene=20
    3. 支付宝开放平台
    4. 异步通知URL,需要能被外网访问的网络,可以使用免费内网穿透工具生成域名ngrok 官网甚至有使用介绍,方便快捷
  4. 代码(手机网页支付,手机上提前下载好沙箱版支付宝)
    1. package main
      
      import (
      	"context"
      	"errors"
      	"fmt"
      	"github.com/go-pay/gopay"
      	"github.com/go-pay/gopay/alipay"
      	"github.com/go-pay/gopay/pkg/xlog"
      	"github.com/kataras/iris/v12"
      	"net/url"
      )
      
      //应用私钥
      var privateKey = ``
      
      func main() {
      	// 1初始化支付宝客户端
      	client, err := alipay.NewClient("appid", privateKey, false)
      	if err != nil {
      		xlog.Error(err)
      		return
      	}
      	// 打开Debug开关,输出日志,默认关闭
      	//client.DebugSwitch = gopay.DebugOn
      	// 设置支付宝请求 公共参数
      	client.SetLocation(alipay.LocationShanghai). // 设置时区,不设置或出错均为默认服务器时间
      							SetCharset(alipay.UTF8).                                    // 设置字符编码,不设置默认 utf-8
      							SetSignType(alipay.RSA2).                                   // 设置签名类型,不设置默认 RSA2
      							SetReturnUrl("https://127.0.0.1:80/return").          //跳转页面
      							SetNotifyUrl("https://本地地址80端口映射的域名/notify") // 异步通知URL 可被外网访问 post请求//.SetAppAuthToken() // 设置第三方应用授权
      
      	// 自动同步验签(只支持证书模式)
      	// 传入 alipayCertPublicKey_RSA2.crt 内容
      	//	client.AutoVerifySign([]byte(`-----BEGIN CERTIFICATE-----
      	//-----END CERTIFICATE-----
      	//`))
      
      	// 公钥证书模式,需要传入证书,证书路径
      	err = client.SetCertSnByPath("appCertPublicKey.crt", "alipayRootCert.crt", "alipayCertPublicKey_RSA2.crt")
      	if err != nil {
      		xlog.Error(err)
      		return
      	}
      
      	// 2web服务器
      	app := iris.New()
      	app.Get("/pay", func(ctx iris.Context) { //支付
      		Pay(context.Background(), client)
      	})
      	app.Get("/return", func(ctx iris.Context) { //返回页
      		ReturnUrl(ctx)
      	})
      	app.Post("/notify", func(ctx iris.Context) { //通知页
      		err = NotifyUrl(ctx)
      		if err != nil {
      			fmt.Println("通知页", err)
      		}
      	})
      	err = app.Run(iris.Addr(":80"))
      	if err != nil {
      		fmt.Println(err)
      		return
      	}
      }
      
      // Pay 手机网站支付
      func Pay(ctx context.Context, client *alipay.Client) {
      	//请求参数
      	bm := make(gopay.BodyMap)
      	bm.Set("subject", "手机网站支付")
      	bm.Set("out_trade_no", "LP20220628") //一个订单号只能支付一次
      	bm.Set("total_amount", "0.01")       //1分钱
      	fmt.Println("body:", bm)
      	//发送请求
      	payUrl, err := client.TradeWapPay(ctx, bm) //内部已经处理了签名
      	if err != nil {
      		xlog.Error(err)
      		return
      	}
      	//支付界面链接,直接粘贴到手机浏览器里即可支付
      	fmt.Println(payUrl)
      }
      
      // ReturnUrl 返回页 同步get,可在本机上测试
      func ReturnUrl(ctx iris.Context) {
      	ctx.WriteString("ReturnUrl success")
      }
      
      // NotifyUrl 通知页 异步post,必须外网可访问,必须返回success否则会一直通知
      func NotifyUrl(ctx iris.Context) error {
      	body, err := ctx.GetBody()
      	if err != nil {
      		fmt.Errorf("%v", err)
      		return err
      	}
      	fmt.Println("body", string(body))
      	values, err := url.ParseQuery(string(body))
      	if err != nil {
      		fmt.Errorf("%v", err)
      		return err
      	}
      	datas, err := alipay.ParseNotifyByURLValues(values) //证书异步验签
      	if err != nil {
      		fmt.Errorf("%v", err)
      		return err
      	}
      	//验签
      	ok, err := alipay.VerifySignWithCert("alipayCertPublicKey_RSA2.crt", datas)
      	if ok == false || err != nil {
      		fmt.Errorf("%v", err)
      		return errors.New("校验失败")
      	}
      	fmt.Println(datas)
      	tradeStatus := datas.Get("trade_status")
      	fmt.Printf(tradeStatus)
      	//todo 处理业务逻辑
      	//交易状态
      	if tradeStatus == "TRADE_SUCCESS" {
      		fmt.Println("交易成功")
      	} else {
      		fmt.Errorf("交易异常")
      	}
      	//返回success
      	ctx.WriteString("success")
      	return nil
      }
      

猜你喜欢

转载自blog.csdn.net/qq_37575994/article/details/125513319