支付宝:APP支付接口2.0(alipay.trade.app.pay)

本章是跟支付宝进行签约对接商户服务端(也就是自行开发的JAVA后端),做此记录。

文献基本都来源于支付宝,详情请看支付宝官方文档:APP支付

目录

系统交互图

服务端demo

配置参数

获取APPID

获取公密钥

使用demo

获取签名后的订单信息

验签


系统交互图

先来查看下主要系统交互图,方便后面的流程梳理。

支付宝:APP支付接口2.0(alipay.trade.app.pay)_第1张图片

文档位置:https://docs.open.alipay.com/204/105297/ 的快速接入,第四步:调用接口

看到这里,可以清楚的知道,java后端并不需要进行支付操作,只负责生成签名后的订单信息最终验签,并最后异步接收支付通知

服务端demo

下载 服务端mode 依赖包

文档地址:https://docs.open.alipay.com/54/106370/

我这JAVA MAVEN项目 , 直接引入依赖即可


    
        com.alipay.sdk
        alipay-sdk-java
        4.8.10.ALL
    

服务端demo 文档地址:https://docs.open.alipay.com/54/106370/

代码块文档内有展示,这里就不显示了,(简易固定参数不做说明)主要展示咱们需要自助获取的参数:

  • app_id :应用Id
  • sign_type :签名类型,demo采用了(证书)版本,这里选用推荐的 RSA2,详情可看:RSA 和 RSA2 签名算法区别
  • private_key :应用私钥
  • app_cert_path :应用公钥证书路径
  • alipay_cert_path:支付宝公钥证书路径
  • alipay_root_cert_path:支付宝根证书路径

配置参数

先登录到支付宝开放平台,这里的实名认证,绑定手机号等操作这里就不讲解了。

获取APPID

如下图进行3步进入创建应用界面

支付宝:APP支付接口2.0(alipay.trade.app.pay)_第2张图片

支付宝:APP支付接口2.0(alipay.trade.app.pay)_第3张图片

创建完成之后,就获取到咱们要用的应用APPID

支付宝:APP支付接口2.0(alipay.trade.app.pay)_第4张图片

获取公密钥

阿里文档链接参考:

第一步:生成 RSA 密钥(这里选择公钥证书方式)

第二步:上传应用公钥并获取支付宝公钥

 上传完成后,可在设置页面下载到 应用公钥证书,支付宝公钥证书,支付宝根证书

支付宝:APP支付接口2.0(alipay.trade.app.pay)_第5张图片

使用证书的方式,直接查看demo就可以。

使用demo

查看demo其中的一句

//SDK已经封装掉了公共参数,这里只需要传入业务参数。
//以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();

该业务对象已经帮我们封装好了,也够用了,demo展示的set只是其中几个参数,其他可以继续赋值。

使用demo提供的AlipayTradeAppPayRequest好处是,自行帮我们做好了sign签名处理,我们无需再自己签名。

麻烦的问题在于证书路径的读取。如下标识:

//设置应用公钥证书路径
certAlipayRequest.setCertPath(app_cert_path);
//设置支付宝公钥证书路径
certAlipayRequest.setAlipayPublicCertPath(alipay_cert_path);
//设置支付宝根证书路径
certAlipayRequest.setRootCertPath(alipay_root_cert_path);

如果项目是war,那还好,这三个参数直接搜索项目根路径比较简单,但如果是springboot打包成jar项目的启动方式的话,就比较困难。jar包如果想读取内部配置文件,只能通过流来读取,网上搜索的读取配置文件大多是InputStream,可我们要的是文件的路径而不是内容。在开发测试的时候,Linux环境只能读取绝对路径,Window可以读取相对路径。所以做了两个版本的路径切换。下面展示下效果。

// Linux文件绝对路径 --- ${jar包所在“根”路径}/crt/
// String outpath = System.getProperty("user.dir") + File.separator + "crt" + File.separator;
// window文件相对路径
String outpath = this.getClass().getResource("/").getPath()
// 设置应用公钥证书路径
// linux getPath: ${jar包所在“根”路径}/crt/appCertPublicKey_2019102168481752.crt,其他两个相同
// window getPath: G:\git\leopard-console\target\classes\appCertPublicKey_2019102168481752.crt
File appCertfile = new File(outpath + "appCertPublicKey_2019102168481752.crt");
if (appCertfile == null || appCertfile.getPath() == null) {
	logger.error("------/crt/------找不到应用公钥证书");
	return null;
}
// 设置应用公钥证书路径
certAlipayRequest.setCertPath(appCertfile.getPath());

本地window测试还方便,如果是linux记得切换路径,实例里面有个 “crt“ 文件夹,是我创建后将文件放进去的。

如果是通过dockerfile来构建docker读取配置。相应配置如下:

FROM java:7
VOLUME /tmp
ADD leopard-console*.jar /leopard-console.jar
ADD classes/alipayCertPublicKey_RSA2.crt /crt/alipayCertPublicKey_RSA2.crt
ADD classes/alipayRootCert.crt /crt/alipayRootCert.crt
ADD classes/appCertPublicKey_2019102168481752.crt /crt/appCertPublicKey_2019102168481752.crt
RUN bash -c 'touch /leopard-console.jar'
EXPOSE 8080
CMD ["java", "-jar","leopard-console.jar"]

将jar编译的配置文件,创建到 /crt 目录下,代码不变,就可以引用到。

自建的 JAR 项目地址:https://github.com/leopardF/alipay , 有需要参考的可自行下载。

该JAR以嵌入依赖为主,等下测试时候会看到引用。

获取签名后的订单信息

开始测试请求,引入自建项目依赖


	com.pay
	alipay
	0.0.1

编写测试代码:

/**
 * 请求生成签名信息
 * 
 * @return
 */
@RequestMapping(value = "/getOrderInfoSign")
public Message getOrderInfoSign() {
	AliAppPayResponseCode appPayRequest = new AliAppPayRequest().appPayRequest(30, "测试-model", "预约3天,详情如下。。。",
			"0.01", "201910221548751212", "paramUrl;paramUrl2", "http://cwfpx6.natappfree.cc/verifyOrderInfoSign");
	return new Message("200", "aa", appPayRequest.alias());
}

浏览器输入: localhost:8080/getOrderInfoSign , 成功获取签名验证信息。

支付宝:APP支付接口2.0(alipay.trade.app.pay)_第6张图片

data就是移动端需要的签名信息。

我们再重新看下系统流程图:

支付宝:APP支付接口2.0(alipay.trade.app.pay)_第7张图片

第2、3步我们已经完成。

接下来开始我们的验签。

验签

直接上代码:


import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.fastjson.JSONObject;
import com.alipay.AliAppPayRequest;
import com.alipay.config.AlipayConfig;
import com.alipay.enums.AliAppPayResponseCode;
import com.alipay.enums.AliPayPublicCode;
import com.leopard.util.bean.Message;
import com.leopard.util.enums.SystemCodeAndMsg;

/**
 * ali支付
 */
@RestController
@RequestMapping(value = "/alipay")
public class AliPayAction {

	private static final Logger logger = LoggerFactory.getLogger(AliPayAction.class);

	/**
	 * 请求生成订单签名信息
	 * 
	 * @param subject
	 *            标题
	 * @param body
	 *            内容
	 * @param totalAmount
	 *            金额
	 * @param passbackParams
	 *            附加参数
	 * @return
	 */
	@RequestMapping(value = "/getModelOrderInfoSign", method = RequestMethod.POST)
	public Message getModelOrderInfoSign(@RequestParam(value = "subject") String subject,
			@RequestParam(value = "body") String body, @RequestParam(value = "totalAmount") String totalAmount,
			@RequestParam(value = "passbackParams") String passbackParams) {

		AliAppPayResponseCode appPayRequest = new AliAppPayRequest().appPayRequest(30, subject, body, totalAmount,
				"201910221548751212", passbackParams, "http://cwfpx6.natappfree.cc/verifyOrderInfoSign");
		// AliAppPayResponseCode appPayRequest = new
		// AliAppPayRequest().appPayRequest(30, "测试-model", "预约3天,详情如下。。。",
		// "0.01", "201910221548751212", "paramUrl;paramUrl2",
		// "http://cwfpx6.natappfree.cc/verifyOrderInfoSign");
		return new Message("200", "aa", appPayRequest.alias());
	}

	/**
	 * 同步获取前端成功返回结果
	 * 
	 * @param result
	 *            支付成功后的result的json串
	 * @return
	 */
	@RequestMapping(value = "/syncNotifyValidate", method = RequestMethod.POST)
	public Message syncNotifyValidate(@RequestParam(value = "result") String result) {

		JSONObject resultObject = JSONObject.parseObject(result);
		String alipayTradeAppPayResponse = resultObject.getString("alipay_trade_app_pay_response");
		if (StringUtils.isBlank(alipayTradeAppPayResponse)) {
			return new Message(SystemCodeAndMsg.FAIL.code(), "响应参数不存在");
		}
		String sign = resultObject.getString("sign");
		if (StringUtils.isBlank(sign)) {
			return new Message(SystemCodeAndMsg.FAIL.code(), "验签信息不能为空");
		}
		String signType = resultObject.getString("sign_type");
		if (!AlipayConfig.sign_type.equals(signType)) {
			return new Message(SystemCodeAndMsg.FAIL.code(), "签名类型不匹配");
		}

		JSONObject responseObject = JSONObject.parseObject(alipayTradeAppPayResponse);
		if (!AliPayPublicCode.Success.code().equals(responseObject.getString("code"))) {
			logger.info("---syncNotifyValidate-----responseObject:" + responseObject.toString());
			return new Message(responseObject.getString("code"), responseObject.getString("msg"));
		}

		// 以下验证业务逻辑需补充
		Message publicValidateBaseInfo = publicValidateBaseInfo(responseObject);
		if(!SystemCodeAndMsg.SUCCESS.code().equals(publicValidateBaseInfo.getCode())){
			//验证失败
			return publicValidateBaseInfo;
		}

		// 验证通过,买家付款成功,将信息记录到数据库
		// 业务代码

		// 反馈给前端
		return new Message(SystemCodeAndMsg.SUCCESS.code(), SystemCodeAndMsg.SUCCESS.msg());
	}

	/**
	 * 异步获取支付宝POST通知。 此地址是一开始生成订单传入的异步地址,请保留好路径
	 * 
	 * @param request
	 * @return
	 */
	@RequestMapping(value = "/asynNotifyValidate", method = RequestMethod.POST)
	public String asynNotifyValidate(HttpServletRequest request) {

		// 支付宝提供的验签方法,已经封装到依赖包,请求调用就行
		AliAppPayResponseCode verifyOrderInfoSign = new AliAppPayRequest().verifyOrderInfoSign(request);
		if (!AliAppPayResponseCode.SUCCESS.code().equals(verifyOrderInfoSign.code())) {
			return "failure";
		}
		
		// 以下验证业务逻辑需补充
		// 已经将验证过程中的订单信息和响应信息放入到alias内回传回来
		JSONObject verifyOrderInfoJson = JSONObject.parseObject(verifyOrderInfoSign.alias());
		Message publicValidateBaseInfo = publicValidateBaseInfo(verifyOrderInfoJson);
		if(!SystemCodeAndMsg.SUCCESS.code().equals(publicValidateBaseInfo.getCode())){
			//验证失败
			return "failure";
		}
		
		//验证通过,开始业务代码,并标记订单支付成功,该信息可覆盖同步回调信息,较为准确
		//最好将回调信息都保存在数据库做记录
		//verifyOrderInfoJson已经保存好回调数据,直接放入数据库用保存即可

		return "success";
	}

	/**
	 * 支付宝公用回调信息验签
	 * 
	 * @param jsonObject
	 * @return
	 */
	private Message publicValidateBaseInfo(JSONObject jsonObject) {
		
		// 1、商户需要验证该通知数据中的 out_trade_no 是否为商户系统中创建的订单号;
		String outTradeNo = jsonObject.getString("out_trade_no");
		// 业务查找订单号对应的信息,并进行匹配验证

		// 2、判断 total_amount 是否确实为该订单的实际金额(即商户订单创建时的金额);
		String totalAmount = jsonObject.getString("total_amount");
		// 根据1获取到的信息,进行匹配验证

		// 3、校验通知中的 seller_id(或者 seller_email) 是否为 out_trade_no
		// 这笔单据对应的操作方(有的时候,一个商户可能有多个 seller_id/seller_email);
		String sellerId = jsonObject.getString("seller_id");
		if (!AliAppPayRequest.verifySellerId(sellerId)) {
			logger.info("---publicValidateBaseInfo-----verifySellerId:" + jsonObject.toString());
			return new Message(SystemCodeAndMsg.FAIL.code(), "验证信息有误");
		}

		// 4、验证 app_id
		// 是否为该商户本身。上述1、2、3、4有任何一个验证不通过,则表明同步校验结果是无效的,只有全部验证通过后,才可以认定买家付款成功。
		String appId = jsonObject.getString("app_id");
		if (!AliAppPayRequest.verifyAppId(appId)) {
			logger.info("---publicValidateBaseInfo-----verifyAppId:" + jsonObject.toString());
			return new Message(SystemCodeAndMsg.FAIL.code(), "验证信息有误");
		}
		
		return new Message(SystemCodeAndMsg.SUCCESS.code(), SystemCodeAndMsg.SUCCESS.msg());
	}
}

这是自己写的验签demo,可直接更改使用到自己的业务内,其中的Message是自己封装的相应格式,这里就不罗列了。

验签请求可参考支付宝技术文档:

第四步:使用支付宝公钥验签

App支付服务端 DEMO & SDK

服务端:通知参数说明

 代码已经将系统流程图的,9、10、12、13步完成。

联调测试,请自行跟移动端联调对接。

调用支付宝的联调日志查询地址为:https://openmonitor.alipay.com/acceptance/cloudparse.htm

 

你可能感兴趣的:(alipay,alipay)