• 26、支付宝支付


    一、支付宝流程

    1 - 正式上线流程

    • 支付宝开放平台https://open.alipay.com/
      • ①.登录 -> 控制台 -> 入驻填写信息
      • ②.创建网页/移动应用

    在这里插入图片描述
    在这里插入图片描述

    2 - 沙箱环境

    • 沙箱环境:沙箱环境是协助开发者进行接口开发及主要功能联调的模拟环境,目前仅支持网页/移动应用和小程序两种应用类型。在沙箱完成接口调试后,请务必在正式环境进行完整的功能验收测试。
      在这里插入图片描述

    二、支付宝公钥私钥

    1 - 对称加密与非对称加密

    在这里插入图片描述

    2 - 公钥私钥在支付中的应用

    在这里插入图片描述

    3 - 公钥私钥配置生成

    • 支付宝密钥生成器:沙箱应用 -> 自定义密钥 -> 公钥模式 -> 设置并查看 -> 支付宝密钥生成器
    • 生成保存的本地默认路径C:\Users\Administrator\Documents\支付宝开放平台开发助手\RSA密钥
      在这里插入图片描述
      在这里插入图片描述
    • 使用工具生成公钥私钥
      • 私钥我们自己保存
      • 公钥配置到支付宝中
        在这里插入图片描述
    • 配置支付宝公钥:沙箱应用 -> 自定义密钥 -> 公钥模式 -> 设置并查看

    在这里插入图片描述
    在这里插入图片描述

    4 - 授权回调地址

    注意这里的授权回调,我们在内网测试的时候,这里需要使用到内网穿透技术,否则是无法接收到回调的
    在这里插入图片描述


    三、生成支付url并支付

    1 - 生成url

    在这里插入图片描述

    • 测试代码
    package main
    
    import (
    	"fmt"
    	"github.com/smartwalle/alipay/v3"
    )
    
    func main() {
    	appID := "开放平台的appID"
    	privateKey := "应用私钥"
    	aliPublicKey := "支付宝公钥"
    
    	var client, err = alipay.New(appID, privateKey, false)
    	if err != nil {
    		panic(err)
    	}
    	err = client.LoadAliPayPublicKey(aliPublicKey)
    	if err != nil {
    		panic(err)
    	}
    
    	var p = alipay.TradePagePay{}         // page支付方式使用
    	p.NotifyURL = "http://xxx"            // 支付结果回调的url,注意内网穿透问题
    	p.ReturnURL = "http://127.0.0.1:8089" // 支付成功后倒计时结束跳转的页面
    	p.Subject = "标题"
    	p.OutTradeNo = "sn12389342479" //传递一个唯一单号
    	p.TotalAmount = "10.00"
    	p.ProductCode = "FAST_INSTANT_TRADE_PAY" // page支付必须使用这个配置
    
    	url, err := client.TradePagePay(p)
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(url.String())
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    在这里插入图片描述

    • 访问生成的支付url地址

    在这里插入图片描述

    2 - 测试支付

    在这里插入图片描述

    • 登录买家账号支付
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述

    四、gin集成支付宝支付

    1 - nacos配置

    • nacos我们需要配置什么:1、appID;2、应用私钥;3、支付宝公钥;4、notify_url;5、return_url
    • order_web.json配置
    {
      "host": "192.168.124.9",
      "name": "order_web",
      "port": 8083,
      "tags": ["mxshop","imooc","bobby","order","web"],
      "goods_srv": {
        "name": "goods_srv"
      },
      "order_srv": {
        "name": "order_srv"
      },
      "inventory_srv": {
        "name": "inventory_srv"
      },
      "jwt": {
        "key": "VYLDYq3&hGWjWqF$K1ih"
      },
      "consul": {
        "host": "192.168.124.51",
        "port": 8500
      },
      "alipay":{
        "app_id":"",
        "private_key":"",
        "ali_public_key":"",
        "notify_url":"",
        "return_url":""
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    2 - config添加alipay

    • order_web/config/config.go
    package config
    
    type SrvConfig struct {
    	Name string `mapstructure:"name" json:"name"`
    }
    
    type JWTConfig struct {
    	SigningKey string `mapstructure:"key" json:"key"`
    }
    
    type ConsulConfig struct {
    	Host string `mapstructure:"host" json:"host"`
    	Port int    `mapstructure:"port" json:"port"`
    }
    
    type AlipayConfig struct {
    	AppID        string `mapstructure:"app_id" json:"app_id"`
    	PrivateKey   string `mapstructure:"private_key" json:"private_key"`
    	AliPublicKey string `mapstructure:"ali_public_key" json:"ali_public_key"`
    	NotifyURL    string `mapstructure:"notify_url" json:"notify_url"`
    	ReturnURL    string `mapstructure:"return_url" json:"return_url"`
    }
    
    type ServerConfig struct {
    	Name             string       `mapstructure:"name" json:"name"`
    	Host             string       `mapstructure:"host" json:"host"`
    	Tags             []string     `mapstructure:"tags" json:"tags"`
    	Port             int          `mapstructure:"port" json:"port"`
    	GoodsSrvInfo     SrvConfig    `mapstructure:"goods_srv" json:"goods_srv"`
    	OrderSrvInfo     SrvConfig    `mapstructure:"order_srv" json:"order_srv"`
    	InventorySrvInfo SrvConfig    `mapstructure:"inventory_srv" json:"inventory_srv"`
    	JWTInfo          JWTConfig    `mapstructure:"jwt" json:"jwt"`
    	ConsulInfo       ConsulConfig `mapstructure:"consul" json:"consul"`
    	AliPayInfo       AlipayConfig `mapstructure:"alipay" json:"alipay"`
    }
    
    type NacosConfig struct {
    	Host      string `mapstructure:"host"`
    	Port      uint64 `mapstructure:"port"`
    	Namespace string `mapstructure:"namespace"`
    	User      string `mapstructure:"user"`
    	Password  string `mapstructure:"password"`
    	DataId    string `mapstructure:"dataid"`
    	Group     string `mapstructure:"group"`
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    3 - api_order添加支付

    • order_web/api/order/api_order.go
    func New(ctx *gin.Context) {
    	orderForm := forms.CreateOrderForm{}
    	if err := ctx.ShouldBindJSON(&orderForm); err != nil {
    		api.HandleValidatorError(ctx, err)
    	}
    	userId, _ := ctx.Get("userId")
    	rsp, err := global.OrderSrvClient.CreateOrder(context.Background(), &proto.OrderRequest{
    		UserId:  int32(userId.(uint)),
    		Name:    orderForm.Name,
    		Mobile:  orderForm.Mobile,
    		Address: orderForm.Address,
    		Post:    orderForm.Post,
    	})
    	if err != nil {
    		zap.S().Errorw("新建订单失败")
    		api.HandleGrpcErrorToHttp(err, ctx)
    		return
    	}
    
    	// 这里跳过了支付宝的支付的url生成和跳转,这个为了测试使用
    	if global.ServerConfig.AliPayInfo.AppID == "2021000121645456" {
    		ctx.JSON(http.StatusOK, gin.H{
    			"id": rsp.Id,
    		})
    	}
    
    	//生成支付宝的支付url
    	client, err := alipay.New(global.ServerConfig.AliPayInfo.AppID, global.ServerConfig.AliPayInfo.PrivateKey, false)
    	if err != nil {
    		zap.S().Errorw("实例化支付宝失败")
    		ctx.JSON(http.StatusInternalServerError, gin.H{
    			"msg": err.Error(),
    		})
    		return
    	}
    	err = client.LoadAliPayPublicKey(global.ServerConfig.AliPayInfo.AliPublicKey)
    	if err != nil {
    		zap.S().Errorw("加载支付宝的公钥失败")
    		ctx.JSON(http.StatusInternalServerError, gin.H{
    			"msg": err.Error(),
    		})
    		return
    	}
    
    	var p = alipay.TradePagePay{}
    	p.NotifyURL = global.ServerConfig.AliPayInfo.NotifyURL
    	p.ReturnURL = global.ServerConfig.AliPayInfo.ReturnURL
    	p.Subject = "订单-" + rsp.OrderSn
    	p.OutTradeNo = rsp.OrderSn
    	p.TotalAmount = strconv.FormatFloat(float64(rsp.Total), 'f', 2, 64)
    	p.ProductCode = "FAST_INSTANT_TRADE_PAY"
    
    	url, err := client.TradePagePay(p)
    	if err != nil {
    		zap.S().Errorw("生成支付url失败")
    		ctx.JSON(http.StatusInternalServerError, gin.H{
    			"msg": err.Error(),
    		})
    		return
    	}
    
    	ctx.JSON(http.StatusOK, gin.H{
    		"id":         rsp.Id,
    		"alipay_url": url.String(),
    	})
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    4 - 处理支付回调通知

    • order_web/router/router_order.go:添加回调通知的router路由
    package router
    
    import (
    	"github.com/gin-gonic/gin"
    	"web_api/order_web/api/order"
    	"web_api/order_web/api/pay"
    	"web_api/order_web/middlewares"
    )
    
    func InitOrderRouter(Router *gin.RouterGroup) {
    	OrderRouter := Router.Group("orders").Use(middlewares.SetUserId())
    	{
    		OrderRouter.GET("", order.List) // 订单列表
    		//BannerRouter.GET("", middlewares.JWTAuth(), middlewares.IsAdminAuth(), order.List) // 订单列表
    		OrderRouter.POST("", order.New)        // 新建订单
    		OrderRouter.GET("/:id/", order.Detail) // 订单详情
    	}
    	PayRouter := Router.Group("pay")
    	{
    		PayRouter.POST("alipay/notify", pay.Notify)
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • order_web/api/pay/api_alipay.go
    package pay
    
    import (
    	"context"
    	"github.com/gin-gonic/gin"
    	"github.com/smartwalle/alipay/v3"
    	"go.uber.org/zap"
    	"net/http"
    	"web_api/order_web/proto"
    
    	"web_api/order_web/global"
    )
    
    func Notify(ctx *gin.Context) {
    	//支付宝回调通知
    	client, err := alipay.New(global.ServerConfig.AliPayInfo.AppID, global.ServerConfig.AliPayInfo.PrivateKey, false)
    	if err != nil {
    		zap.S().Errorw("实例化支付宝失败")
    		ctx.JSON(http.StatusInternalServerError, gin.H{
    			"msg": err.Error(),
    		})
    		return
    	}
    	err = client.LoadAliPayPublicKey(global.ServerConfig.AliPayInfo.AliPublicKey)
    	if err != nil {
    		zap.S().Errorw("加载支付宝的公钥失败")
    		ctx.JSON(http.StatusInternalServerError, gin.H{
    			"msg": err.Error(),
    		})
    		return
    	}
    
    	notify, err := client.GetTradeNotification(ctx.Request) //这个会自动验证签名是否正确
    	if err != nil {
    		ctx.JSON(http.StatusInternalServerError, gin.H{})
    		return
    	}
    
    	_, err = global.OrderSrvClient.UpdateOrderStatus(context.Background(), &proto.OrderStatus{
    		OrderSn: notify.OutTradeNo, // 商户订单号,这个是我们自己生成的订单号
    		Status:  string(notify.TradeStatus),
    	})
    	if err != nil {
    		ctx.JSON(http.StatusInternalServerError, gin.H{})
    		return
    	}
    	ctx.String(http.StatusOK, "success")
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    五、完整源码

    • 完整源码下载mxshop_srvsV9.0rar
    • 源码说明:(nacos的ip配置自行修改,全局变量DEV_CONFIG设置:1=zsz,2=comp,3=home)
      • goods_srv/model/sql/mxshop_goods.sql:包含了建表语句
      • other_import/api.json:YApi的导入文件
      • other_import/nacos_config_export_user.zip:nacos的user配置集导入文件
      • other_import/nacos_config_export_goods.zip:nacos的goods配置集导入文件
      • other_import/nacos_config_export_inventory.zip:nacos的inventory的配置导入文件
      • other_import/nacos_config_export_orders.zip:nacos的orders的配置导入文件
  • 相关阅读:
    做好规划 拿下未来!
    函数:计算数组的元素和
    【并发编程】ThreadPoolExecutor任务提交与停止流程及底层实现【新手探索版】
    JAVA在线小说系统计算机毕业设计Mybatis+系统+数据库+调试部署
    解决husky在mac下不生效的问题
    【mq】从零开始实现 mq-05-实现优雅停机
    苹果MacBook电脑IntelliJ IDEA 2022 for Mac(JAVA IDE编程工具) v2022.1.3
    浅谈自旋锁和JVM对锁的优化
    自动化运维平台-OpManager
    人类小脑内在组织背后的基因图谱
  • 原文地址:https://blog.csdn.net/qq23001186/article/details/126320462