Java服务端支付宝对接(详细)


1、创建连接的公钥私钥

支付宝对接时需要有相应的密钥、网关、APPID,第一步就是获取这些数据:

在支付宝【开发者服务中心】(https://openhome.alipay.com/platform/developerIndex.htm)网页中进行创建(测试时使用沙箱创建就好,不过使用这个需要先入驻平台,很简单,如下操作即可:)
Java服务端支付宝对接(详细)_Java
然后进去后在控制台页面中渣都到【沙箱测试】:
Java服务端支付宝对接(详细)_Java_02
进入沙箱后操作步骤如下:
Java服务端支付宝对接(详细)_Java_03
然后进入支付宝密钥生成器下载页面下载即可:
Java服务端支付宝对接(详细)_Java_04
下载下来后点击运行安装得到如下的一个应用:
Java服务端支付宝对接(详细)_Java_05
点击进入并登录(注意,在【支付宝开发者服务中心】网页中登陆的支付宝账号必须和在此登陆的支付宝账号必须是同一个):
Java服务端支付宝对接(详细)_Java_06
然后获取密钥:
Java服务端支付宝对接(详细)_Java_07
将密钥什么的都保存下来,如果没保存也不用担心,本地会自动保你保存一份在你本地,如下所示:
Java服务端支付宝对接(详细)_Java_08
然后将生成的公钥放到【开发者服务中心】的沙箱设置中,如下:
Java服务端支付宝对接(详细)_Java_09
如此,第一步的操作算是完成!


2、导入jar包、编写代码

导入jar包如下(仅展示的时支付宝所用到的,其他的基础jar包自行安装,毕竟每个框架用的东西都有些微不同,就不再一一列举):

<!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java -->
<dependency>
    <groupId>com.alipay.sdk</groupId>
    <artifactId>alipay-sdk-java</artifactId>
    <version>4.10.111.ALL</version>
</dependency>

Java代码:

/** 
 * @Author qtl 
 * @Description payTest 
 * @Date 16:36 2020/10/23 
 * @Param [] 
 * @return void 
 **/
 @PostMapping("payTest")
 public void aliPay(){    
 	AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipaydev.com/gateway.do","2016102700768586","MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC++ZvFC+XrvFxWoiaRSgqlwhe9o4rsN1IsdoGQBjY935X5Ms46I2dqgK8xZysc+ck/X3W+Sy6I4nLp3igGtUmzqMv+51eJZE1uuj+PBS76jsGyEePXKSYAf8CtJTl/vaRR82NMriVe1WcnvjD/WC1UuCaB1j5evOPSBLAzapzLlryOIAkMB/7+H0x+2BzfMAewPIRDXNUelSEYrAq9OJNKFrJxJn9b85masIfP5nOi3KwIZQKdDRVMlO3+bebZxyorZ1iANs5PyRGOAO3XkVjeywG09LOtzB/9ZNpmehG0/Tyc5XS6qTXctwQq2eHYvrdDitryZ3C42JKy7w3l89oNAgMBAAECggEAC1XhixajtRRfHr1NML6GV0RoCFeWvWKjPARkuFnGCnIlAr+jQgwr7URqIbKP+o5RBbPy1zK7NmDKlVscYRmNoF5ajWzJ41vSxMnFUAXsve6PTLSHI5RLryfmFeDpws1M3Zhmwnxrt29PSiqd2eov9qFfl8FRvmnT0/l3Q/YGDIdxtaNWVbBmYCmn7BSUV9y+TdYkzc4ufHWMGz8zD6YEs161TROHKX6fb8owrA5DWtyhPYES7NUkpJHMhP/ktumI5DF/dNBdpPjvJCqkLR6kAYxNXYQletpxhGdVYd+/98DYHzsPZ2FKMr0v/QmpZPCbkVMPV3KBsepX7a7h1ktk+QKBgQDfi5c7l7otGCO99vG8ZlphI5gvW/f3qALJHjVk+090lpGtPigfbcqDZ2zjZKuVK8DMCwLKO/n9AOo4nym/tA+4LNGY3/mHn/ddEvUxtrEY6mgSE4dLPU8Tp6rQulgVqqLK7QspleTle8BrrFBeTgf6d8hH2kUTw+JCzTB+jaEVjwKBgQDas35oCiuYNkS7TIGo9x4peQBKpBJSCTGQ6tivC/D+fqW/2ZHF3j+p+1m5VIekWNZfhTHBiGyhdkhRKDjaOn7+UZx78/YKL3O4nrqienSCzhCdHfi6PbA5SwJxTyKFe+M0epnCGUUyf817he5OU/W/9ZLibW8FqlTTQIItmxrgowKBgQCVZp20Xjs3WCLLGveEioNs2HUlcbnP8qGp+F6HFs6LhPXnats15hKqr7y+XNGv6IX52IIM1vjEdpATN4yXaqf21z1dhpMmaWrQ8ZKdnxPNKxxSuGp7hoYLBMvMpD2qfVg/dx02tc887/AyBf/QhyYd52AbZW01QH0/WBNJAS7fUQKBgQDWjne5zBh+yObfNaySvnV5zHrrv1E16E0XVj7kZHu2wTsNH37ytxqQQcYBmxtDseAGcB6jNpsUQH2sGSDFf9Ps3tX9iRbdYTZPbnG9SxOY7F7CNZA57qE/ZAZucWt1t1fsuUa1dKM9asKFp5xmfdo/y8WhnCjqTvPPGJ9d7KgOrwKBgFCXt4er4QqCe9gDPiVvdKWhkWwciq6nixoa/ypUNzSVcPNVEnnZJqCYms2qn1jx2qm81RFQIp/pm1KukDvUBH+u4Ev45ysXGsszRyZ9vH3acK95nIO8GpbXhoDlVibHqk9Pe7eApmCynblmyyURxZ3ijeXV6n5MdsnhIFnjxx6k", 
 		"json",
 		"utf-8", 
 		"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvvmbxQvl67xcVqImkUoKpcIXvaOK7DdSLHaBkAY2Pd+V+TLOOiNnaoCvMWcrHPnJP191vksuiOJy6d4oBrVJs6jL/udXiWRNbro/jwUu+o7BshHj1ykmAH/ArSU5f72kUfNjTK4lXtVnJ74w/1gtVLgmgdY+Xrzj0gSwM2qcy5a8jiAJDAf+/h9Mftgc3zAHsDyEQ1zVHpUhGKwKvTiTShaycSZ/W/OZmrCHz+ZzotysCGUCnQ0VTJTt/m3m2ccqK2dYgDbOT8kRjgDt15FY3ssBtPSzrcwf/WTaZnoRtP08nOV0uqk13LcEKtnh2L63Q4ra8mdwuNiSsu8N5fPaDQIDAQAB",
 		"RSA2");    
 		//实例化客户端    
 		//实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay    
 		AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();    
 		//SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。    
 		AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();    
 		model.setBody("我是测试数据");    
 		model.setSubject("购买手机");
 		//展示在支付宝付款界面上的订单信息    
 		model.setOutTradeNo((Long.valueOf(String.valueOf((new Date()).getTime()))).toString());    
 		model.setTimeoutExpress("3m");    
 		model.setTotalAmount("0.01");    
 		model.setProductCode("001-ProductCode");    
 		request.setBizModel(model);    
 		request.setNotifyUrl("http://xxx:8080/pay/aliPayCallback");    
 		try {        
 			//这里和普通的接口调用不同,使用的是sdkExecute        
 			AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);        
 			System.out.println("发起支付 - 生成前端唤起支付宝命令串!");        
 			System.out.println(response.getBody());
 			//就是orderString 可以直接给客户端请求,无需再做处理。    
 		} catch (AlipayApiException e) {
 	        e.printStackTrace();    
 }}

Java服务端支付宝对接(详细)_Java_10
Java服务端支付宝对接(详细)_Java_11


3、使用测试工具测试

测试步骤中需要下载两个东西,第一个是【客户端调试】,另一个是【沙箱版支付宝】(因为是通过沙箱测试,正式的支付宝APP网关和沙箱的APP网关等均不同);

【客户端调试】APP下载地址:https://alipaybbs.oss-cn-hangzhou.aliyuncs.com/1807/thread/60_191_eb31b639a0caf31.zip

【沙箱版支付宝】下载地址:https://sandbox.alipaydev.com/user/downloadApp.htm

Java服务端支付宝对接(详细)_Java_12
下载完成后进行安装,手机上显示图标如下:
Java服务端支付宝对接(详细)_Java_13
需要有一点要注意,这个支付宝只具有极个别的功能,而且登陆的账号必须要用他们提供的,账号信息:https://openhome.alipay.com/platform/appDaily.htm?tab=account
Java服务端支付宝对接(详细)_Java_14
拿到账号后进行登录,然后准备开始测试:

  • 第一步:将代码运行起来并调用代码的接口生成使前端唤起手机上的支付宝软件的命令;
    Java服务端支付宝对接(详细)_Java_15
  • 第二步:将命令放到【客户端调试】APP的沙盒模式中;
    Java服务端支付宝对接(详细)_Java_16
  • 第三步:运行结果
    Java服务端支付宝对接(详细)_Java_17
    到此支付过程完毕了,如果是将沙箱的切换到正式的支付的话,只需要更换代码中的网关、APPID、公钥、私钥即可;

4、支付宝回调返回支付结果

这个没有什么好讲的,主要就这么点东西:

/**
     * @Author qtl
     * @Description 支付宝 - 支付结果回调返回
     * @Date 16:53 2020/10/23
     * @Param [request]
     * @return boolean
     **/@PostMapping("/aliPayCallback")public void aliPayCallback(HttpServletRequest request){logger.info("服务端验证异步通知信息参数");     // 需要注意的是这个公钥是支付宝公钥(沙箱测试环境获取支付宝公钥的地方请看下方截图,正式企业开发),并不是上面的公钥,需要特别注意!!!!!!!!!!!String alipaypublicKey = "xxxxxxxxx";String charset = "utf-8";// 获取支付宝POST过来反馈信息Map<String,String> params = new HashMap<String,String>();Map requestParams = request.getParameterMap();for  (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {String name = (String)iter.next();String[] values = (String[])requestParams.get(name);String valueStr = "";for  (int i = 0; i < values.length; i++) {valueStr = (i == values.length - 1 )?valueStr + values[i] : valueStr + values[i] + "," ;}//乱码解决,这段代码在出现乱码时使用。//valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");params.put(name, valueStr);}String paramsJson = JSON.toJSONString(params);logger.info("支付宝回调,{}", paramsJson);// 切记alipaypublickey是支付宝的公钥,请去open.alipay.com对应应用下查看。// boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)boolean flag = false;try {flag = AlipaySignature.rsaCheckV1(params, alipaypublicKey, charset, "RSA2" );if(flag){ // 检测是否是真实数据   // 在此地处行数据持久化,支付宝返回的具体数据打印上文中处理好的paramsJson对象即可看到;}else{logger.info("返回数据存在风险,非合法的支付宝返回值!");}} catch (AlipayApiException e) {e.printStackTrace();}System.out.println(flag);}

另外,代码中的paramsJson对象中的数据包含有商户订单号、支付宝支付订单号、商品信息、支付金额、发票金额等,需要注意的是没有商户真正收到的金额信息(扣除手续费后的金额);

沙箱测试模式中支付宝公钥获取方式:
Java服务端支付宝对接(详细)_Java_18
企业正式开发(网页&移动开发) 支付宝公钥获取是在应用信息中可以获取:
Java服务端支付宝对接(详细)_Java_19
整个支付流程已经完毕!


pass: 后来发现,超时后的订单支付宝并未回调并返回结果,也就是说只有用户支付成功后才会回调返回结果,因此,在支付状态存在【待支付、已支付、取消支付】三种状态时,就需要对未支付的订单进行处理,我的解决办法是使用定时器,根据订单超时时间来设定定时器执行频率和更新订单支付状态数据获取的时间范围(例如:我设定的订单超时时间是3分钟超时,那我就在定时器中定为每五分钟执行一次更新支付状态的操作,操作中被更新的支付状态的订单时间取值范围定在据当前时间的15分钟前到4分钟前的数据)即可;