26支付宝支付

Posted 无休止符

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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())


  • 访问生成的支付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":""
  

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"`


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(),
	)

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)
	


  • 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")



五、完整源码

  • 完整源码下载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的配置导入文件

接入支付宝支付SDK

参考技术A 接入支付宝支付SDK

可以说支付宝支付接入是所有SDK最好接入的,没有之一。

客户端不用签名,也不用管包名,也不用管签名文件,就接口返回订单,把订单交给支付宝SDK调用就行,成功或者失败都在当前界面返回给你。你再去通知接口。

支付流程图

官方文档地址

!支付宝支付官方文档地址

按照文档说明接入SDK和相关配置,在这就不重复了

客户端支付关键代码===》支付接口的调用(调起支付弹框)

记住支付接口的调用必须在独立的非ui线程中执行,即需新开线程里面调用。可以想官方demo一样用new Thread方式。

下面我给出用Observable方式示例代码

在PayUtils中

/**

* desc:支付宝支付

* Created by congge on 2018/8/27 17:20

* @param orderInfo 接口返回的订单

**/

public static void aliPay(final Activity activity, final String orderInfo, final OrderListener orderListener)

Observable.just(orderInfo)

.map(new Function ()

@Override

public String apply(String orderInfo) throws Exception

//用户在商户app内部点击付款,是否需要一个loading做为在钱包唤起之前的过渡,这个值设置为true

return new PayTask(activity).pay(orderInfo, true);



)

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe(new Consumer ()

@Override

public void accept(String payResult) throws Exception

orderListener.onPayResult(payResult);



);



支付结果返回处理

返回例子:

resultStatus=9000;memo=;result="alipay_trade_app_pay_response":"code":"10000","msg":"Success","app_id":"2016091300503896","auth_app_id":"2016091300503896","charset":"utf-8","timestamp":"2018-08-28 17:51:11","out_trade_no":"nVElbd74TW6WnEyxQwvX8A","total_amount":"0.01","trade_no":"2018082821001004680500208879","seller_id":"2088102175487650","sign":"W0Hg9k4GxL8Oaxymvqk2i65WNDQxYp6HGve32ek6VjSRnymmI3GQTjpQVbZuDzvjcwQ/HIkM97PoBGAVlTmi/wiJcqDgSSDzDY7AFnNN0OcK0ehWGwKQINA4IDGh51A7yY/vYKmR0VW+2OwGhlRPPMMZtQOEqh8a9/aIijzT6ZLwy9Hl4ayG/fVKhdC1VdckF6+C25BFNp3fIxarg5tfEunm7N9iWngKCUsnP+IZz05OHdvynimgYPcBnbBERHG97GVqRT/EdBWTQyIDMc0LemScAYxJixTVgXDkRddQjzWZ7HgLdBfjs0nXY24puHudT76ERxVY+8NkoKle/QI+FA==","sign_type":"RSA2"

也可以自己打log看看

处理示例代码:

//支付宝支付

PayUtils.aliPay(this, result.getSignDataStr(), new PayUtils.OrderListener()

@Override

public void onPayResult(String payResult)

PayResult pr = new PayResult(payResult);

String rs = pr.getResultStatus();

String r = pr.getResult();

switch (rs)

case AliPayResultStatus.PAY_SUCCESS:

ToastUtils.show(R.string.pay_success);

//通知接口支付成功

break;

case AliPayResultStatus.PAY_PROCESSING:

case AliPayResultStatus.PAY_UNKNOWN:

ToastUtils.show(R.string.pay_fail);

//支付可能成功,要接口去查询

break;

default:

ToastUtils.show(R.string.pay_fail);

//通知接口支付失败,取消订单





);

上面方法中:

//通知接口支付成功 //支付可能成功,要接口去查询 //通知接口支付失败,取消订单。根据你产品需求要不要通知你服务器做的操作。正常是要的,用来改变订单状态

PayResult.class

public class PayResult

private String resultStatus;

private String result;

private String memo;

public PayResult(String rawResult)

if (TextUtils.isEmpty(rawResult))

return;

String[] resultParams = rawResult.split(";");

for (String resultParam : resultParams)

if (resultParam.startsWith("resultStatus"))

resultStatus = gatValue(resultParam, "resultStatus");



if (resultParam.startsWith("result"))

result = gatValue(resultParam, "result");



if (resultParam.startsWith("memo"))

memo = gatValue(resultParam, "memo");







@Override

public String toString()

return "resultStatus=" + resultStatus + ";memo=" + memo

+ ";result=" + result + "";



private String gatValue(String content, String key)

String prefix = key + "=";

return content.substring(content.indexOf(prefix) + prefix.length(),

content.lastIndexOf(""));



public String outOrder()

String order = ""out_trade_no"";

if (result.contains(order))

String begin = result.substring(result.indexOf(order));

String ss = begin.split(",")[0];

String newS = ss.replace(""", "")

.replace("", "")

.replace(":", "")

.replace("out_trade_no", "");

try

return newS;

catch (Exception e)

e.printStackTrace();





return "";



/**

* @return the resultStatus

*/

public String getResultStatus()

return resultStatus;



/**

* @return the memo

*/

public String getMemo()

return memo;



/**

* @return the result

*/

public String getResult()

return result;



最后给下支付返回码表

AliPayResultStatus.class

public class AliPayResultStatus

/**

* 订单支付成功,唯一肯定是支付成功的

*/

public static final String PAY_SUCCESS = "9000";

/**

* 正在处理中,支付结果未知(有可能已经支付成功),请查询商户订单列表中订单的支付状态

*/

public static final String PAY_PROCESSING = "8000";

/**

* 订单支付失败

*/

public static final String PAY_FAIL = "4000";

/**

* 重复请求

*/

public static final String PAY_REPEAT = "5000";

/**

* 用户中途取消

*/

public static final String PAY_PROCESS_CANCEL = "6001";

/**

* 网络连接出错

*/

public static final String PAY_NET_ERROR = "6002";

/**

* 支付结果未知(有可能已经支付成功),请查询商户订单列表中订单的支付状态

*/

public static final String PAY_UNKNOWN = "6004";

还有一个直接弃用沙箱调试模式,否则提示支付失败也有可能不知道错在那,怕金额大,和接口商量,测试服务器就用0.01测试。

以上是关于26支付宝支付的主要内容,如果未能解决你的问题,请参考以下文章

支付宝系统架构内部剖析

转 最全最强解析:支付宝钱包系统架构内部剖析(架构图)

Java面试题我的支付宝3面+美团4面+拼多多四面

微服务架构之支付平台服务化流程

微服务分布式事务处理支付相关

最全最强解析:支付宝钱包系统架构内部剖析(架构图)