首页 > 技术文章 > 支付宝App支付

lmcc 2017-12-06 15:00 原文

支付宝App支付

第一步:客户端调服务端的统一下单接口,服务器端在支付宝平台生成预支付交易单,返回正确的预支付交易回话标识后再在APP里面调起支付

 1 package com.kpcx.pay.alipay;
 2 
 3 
 4 import java.io.IOException;
 5 import java.net.URLDecoder;
 6 import java.util.HashMap;
 7 import java.util.Map;
 8 import java.util.ResourceBundle;
 9 import javax.servlet.ServletException;
10 import javax.servlet.annotation.WebServlet;
11 import javax.servlet.http.HttpServlet;
12 import javax.servlet.http.HttpServletRequest;
13 import javax.servlet.http.HttpServletResponse;
14 import com.alibaba.fastjson.JSON;
15 import com.alibaba.fastjson.JSONObject;
16 import com.alipay.api.AlipayApiException;
17 import com.alipay.api.AlipayClient;
18 import com.alipay.api.DefaultAlipayClient;
19 import com.alipay.api.domain.AlipayTradeAppPayModel;
20 import com.alipay.api.request.AlipayTradeAppPayRequest;
21 import com.alipay.api.response.AlipayTradeAppPayResponse;
22 import com.kpcx.controller.NotifyController;
23 import com.kpcx.util.JSONSerializer;
24 /**
25  * 支付宝创建订单接口
26  * 
27  * @see get请求
28  * @Title: 支付宝创建订单接口
29  * @author: 孟丛丛
30  * @param: param
31  * @return: Object
32  * @Time :2017-05-23 
33  * 先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易回话标识后再在APP里面调起支付
34  */
35 @WebServlet(name = "ZfbOrderCreate", urlPatterns = "/create/zfbOrderCreate")
36 public class ZfbOrderCreate2  extends HttpServlet {
37 
38     private static final long serialVersionUID = 1L;
39     /**
40      * @see 支付宝创建订单接口
41      */
42     protected void doGet(HttpServletRequest request, HttpServletResponse response)
43             throws ServletException, IOException {
44         String param = URLDecoder.decode(request.getParameter("param"),"UTF-8");
45         
46         AlipayTradeAppPayModel model = JSON.parseObject(param, AlipayTradeAppPayModel.class);
47         NotifyController ncController = new NotifyController();
48         String outTradeNo = model.getOutTradeNo();//订单号
49         String totalAmount = model.getTotalAmount();//支付金额
50         String checkOrders = ncController.checkOrders(outTradeNo, totalAmount);
51         
52         //这是订单支付前校验,校验金额、支付状态(避免已经用别的支付方式已经支付)、是否已取消 
53         JSONObject checkOrdersO = JSONObject.parseObject(checkOrders);
54         checkOrdersO.remove("Result");
55         checkOrdersO.put("Result", outTradeNo);
56         String code = checkOrdersO.getString("Code");
57         
58         if(code!=null&&"0".equals(code)){
59             //获取资源文件
60             ResourceBundle resource = ResourceBundle.getBundle("kpcxPay"); //kpcxPay是一个.properties文件
61             //实例化客户端
62             AlipayClient alipayClient = new DefaultAlipayClient(resource.getString("URL"), resource.getString("APP_ID"),resource.getString("APP_PRIVATE_KEY"), resource.getString("FORMAT"), resource.getString("CHARSET"), resource.getString("ALIPAY_PUBLIC_KEY"), resource.getString("SIGN_TYPE"));
63             
64             //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
65             AlipayTradeAppPayRequest requestPay = new AlipayTradeAppPayRequest();
66             requestPay.setBizModel(model);
67             requestPay.setNotifyUrl(resource.getString("notifyUrl"));
68             try {
69                 //这里和普通的接口调用不同,使用的是sdkExecute
70                 AlipayTradeAppPayResponse responsePay = alipayClient.sdkExecute(requestPay);
71                 String orderInfo=responsePay.getBody();           
72                 Map map = new HashMap();
73                 map.put("Code", 0);
74                 map.put("Result",orderInfo);             
75                 String jsons = JSONSerializer.serialize(map);
76                 response.getWriter().write(jsons);
77                 //结果返回到filter,记录到日志中
78                 request.setAttribute("outStr", jsons);
79                 
80             } catch (AlipayApiException e) {
81                 e.printStackTrace();
82             }
83         }else{
84             response.getWriter().write(checkOrdersO.toString());
85             //结果返回到filter,记录到日志中
86             request.setAttribute("outStr", checkOrdersO.toString());
87         }
88         
89     }
90     /**
91      * @see post请求
92      */
93     protected void doPost(HttpServletRequest request, HttpServletResponse response)
94             throws ServletException, IOException {
95         doGet(request, response);
96     }
97 }
View Code

 

第二步:客户端根据服务端返回的参数,成功调起app支付,并支付成功,服务端等待支付宝的异步推送

 1 package com.kpcx.pay.alipay;
 2 
 3 import java.io.IOException;
 4 import java.util.HashMap;
 5 import java.util.Iterator;
 6 import java.util.Map;
 7 import java.util.ResourceBundle;
 8 
 9 import javax.servlet.ServletException;
10 import javax.servlet.annotation.WebServlet;
11 import javax.servlet.http.HttpServlet;
12 import javax.servlet.http.HttpServletRequest;
13 import javax.servlet.http.HttpServletResponse;
14 
15 import org.slf4j.Logger;
16 import org.slf4j.LoggerFactory;
17 
18 import com.alibaba.fastjson.JSONObject;
19 import com.alipay.api.internal.util.AlipaySignature;
20 import com.kpcx.controller.NotifyController;
21 import com.kpcx.service.PaymentService;
22 import com.kpcx.service.impl.PaymentServiceImpl;
23 
24 /**
25  * 支付宝创建订单回调接口
26  * 
27  * @see get请求
28  * @Title: 支付宝创建订单回调接口
29  * @author: 孟丛丛
30  * @param: param
31  * @return: Object
32  * @Time :2017-05-23 调用支付宝支付接口成功后调用此接口,更新快票出行订单状态及支付方式
33  */
34 @WebServlet(name = "ZfbOrderNotify", urlPatterns = "/zfbOrderNotify")
35 public class ZfbOrderNotifyNew2 extends HttpServlet {
36 
37     /**
38      *     
39      */
40     private static final long serialVersionUID = 1L;
41     protected Logger logger = LoggerFactory.getLogger(getClass());
42     ResourceBundle resource1 = ResourceBundle.getBundle("jkUrl");
43     
44     /**
45      * @see get请求    
46      */
47     protected void doGet(HttpServletRequest request, HttpServletResponse response)
48             throws ServletException, IOException {
49         // 获取支付宝POST过来反馈信息
50         Map<String, String> params = new HashMap<String, String>();
51         Map<String, String[]> requestParams = request.getParameterMap();
52         for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
53             String name = (String) iter.next();
54             String[] values = (String[]) requestParams.get(name);
55             String valueStr = "";
56             for (int i = 0; i < values.length; i++) {
57                 valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
58             }
59             // 乱码解决,这段代码在出现乱码时使用。
60             params.put(name, valueStr);
61         }
62         
63         params.remove("sign_type");// 移除sign_type
64         ResourceBundle resource = ResourceBundle.getBundle("kpcxPay");
65         try {
66             //验签
67             boolean flag = AlipaySignature.rsaCheckV2(params, resource.getString("ALIPAY_PUBLIC_KEY"),resource.getString("CHARSET"), resource.getString("SIGN_TYPE"));
68             //回调及验签成功
69             if (flag==true && params.get("trade_status").equals("TRADE_SUCCESS")==true) {
70                 // 如果回调成功,进行服务器端订单处理
71                 /**
72                  * 一系列的判断处理
73                  * 1、查看该订单是否是自己的单子
74                  * 2、查看该订单的支付状态是否是未支付
75                  * 3、查看该订单的金额和支付宝返回的金额是否一致
76                  * 4、保存支付数据
77                  * 5、更新支付状态
78                  * 6、判断完成、插完支付数据自己想要做的操作
79                  * 注意:4、5部要加同步锁,防止几个回调同时对数据的操作
80                  * 
81                  */
82             }
83                 
84         } catch (Exception e) {
85             e.printStackTrace();
86         }
87 
88     }
89     @Override
90     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
91         doGet(req, resp);
92     }
93     
94 
95 }
View Code
  • 必须保证服务器异步通知页面(notify_url)上无任何字符,如空格、HTML标签、开发系统自带抛出的异常提示信息等;
  • 支付宝是用POST方式发送通知信息,因此该页面中获取参数的方式,如:request.Form(“out_trade_no”)、$_POST[‘out_trade_no’];
  • 支付宝主动发起通知,该方式才会被启用;
  • 只有在支付宝的交易管理中存在该笔交易,且发生了交易状态的改变,支付宝才会通过该方式发起服务器通知(即时到账交易状态为“等待买家付款”的状态默认是不会发送通知的);
  • 服务器间的交互,不像页面跳转同步通知可以在页面上显示出来,这种交互方式是不可见的;
  • 第一次交易状态改变(即时到账中此时交易状态是交易完成)时,不仅会返回同步处理结果,而且服务器异步通知页面也会收到支付宝发来的处理结果通知;
  • 程序执行完后必须打印输出“success”(不包含引号)。如果商户反馈给支付宝的字符不是success这7个字符,支付宝服务器会不断重发通知,直到超过24小时22分钟。一般情况下,25小时以内完成8次通知(通知的间隔频率一般是:4m,10m,10m,1h,2h,6h,15h);
  • 程序执行完成后,该页面不能执行页面跳转。如果执行页面跳转,支付宝会收不到success字符,会被支付宝服务器判定为该页面程序运行出现异常,而重发处理结果通知;
  • cookies、session等在此页面会失效,即无法获取这些数据;
  • 该方式的调试与运行必须在服务器上,即互联网上能访问;
  • 该方式的作用主要防止订单丢失,即页面跳转同步通知没有处理订单更新,它则去处理;
  • 当商户收到服务器异步通知并打印出success时,服务器异步通知参数notify_id才会失效。也就是说在支付宝发送同一条异步通知时(包含商户并未成功打印出success导致支付宝重发数次通知),服务器异步通知参数notify_id是不变的

 

推荐阅读