支付宝app支付java后台流程、原理分析(含nei wang chuan tou)
java版支付宝app支付流程及原理分析
本实例是基于springmvc框架编写
一、流程步骤
1.执行流程
当手机端app(就是你公司开发的app)在支付页面时,调起服务端(后台第1个创建订单接口)接口,后台把需要调起支付宝支付的参数返回给手机端,手机端拿到
这些参数后,拉起支付宝支付环境完成支付,完成支付后会调异步通知(第2个接口),此时需要给支付宝返回成功或者失败信息,成功后会调用同步通知(第3个接口)
返回支付成功页面,完成整个支付流程。
2.支付的配置文件AlipayConfig
1 public class AlipayConfig { 2 // 1.商户appid 3 public static String APPID = "20170812********"; 4 5 // 2.私钥 pkcs8格式的 6 public static String RSA_PRIVATE_KEY =""; 7 8 // 3.支付宝公钥 9 public static String ALIPAY_PUBLIC_KEY = ""; 10 11 // 4.服务器异步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 12 public static String notify_url = "http://www.xxx.com/alipay/notify_url.do"; 13 14 // 5.页面跳转同步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 商户可以自定义同步跳转地址 15 public static String return_url = "http://www.xxx.com/alipay/return_url.do"; 16 17 // 6.请求网关地址 18 public static String URL = "https://openapi.alipay.com/gateway.do"; 19 20 // 7.编码 21 public static String CHARSET = "UTF-8"; 22 23 // 8.返回格式 24 public static String FORMAT = "json"; 25 26 // 9.加密类型 27 public static String SIGNTYPE = "RSA2"; 28 29 }
3.第1个创建订单接口
1 /** 2 *@param userId 充值人 3 *@param tradeMoney 充值money(RMB) 4 *@throws AlipayApiException ModelAndView 5 */ 6 @RequestMapping(value="api/alipay/createOrder",method={RequestMethod.POST,RequestMethod.GET}) 7 @ResponseBody 8 public Model alipay( 9 @RequestParam("userId")String userId, 10 @RequestParam("tradeMoney")String tradeMoney,Model m) throws AlipayApiException{ 11 12 String orderStr = ""; 13 try { 14 15 /****** 1.封装你的交易订单开始 *****/ //自己用 16 17 此处封装你的订单数据,订单状态可以设置为等待支付 18 19 /****** 1.封装你的交易订单结束 *****/ 20 21 Map<String,String> orderMap = new LinkedHashMap<String,String>(); //订单实体 22 Map<String,String> bizModel = new LinkedHashMap<String,String>(); //公共实体 23 24 /****** 2.商品参数封装开始 *****/ //手机端用 25 // 商户订单号,商户网站订单系统中唯一订单号,必填 26 orderMap.put("out_trade_no",trade.getOrderNumber()); 27 // 订单名称,必填 28 orderMap.put("subject","手机网站支付购买游戏币"); 29 // 付款金额,必填 30 orderMap.put("total_amount",tradeMoney); 31 // 商品描述,可空 32 orderMap.put("body","您购买游戏币"+tradeMoney +"元"); 33 // 超时时间 可空 34 orderMap.put("timeout_express","30m"); 35 // 销售产品码 必填 36 orderMap.put("product_code","QUICK_WAP_PAY"); 37 38 /****** 2.商品参数封装结束 *****/ 39 40 /******--------------- 3.公共参数封装 开始 ------------------------*****/ //支付宝用 41 //1.商户appid 42 bizModel.put("app_id",AlipayConfig.APPID); 43 //2.请求网关地址 44 bizModel.put("method",AlipayConfig.URL); 45 //3.请求格式 46 bizModel.put("format",AlipayConfig.FORMAT); 47 //4.回调地址 48 bizModel.put("return_url",AlipayConfig.return_url); 49 //5.私钥 50 bizModel.put("private_key",AlipayConfig.RSA_PRIVATE_KEY); 51 //6.商家id 52 bizModel.put("seller_id","2088102170411333"); 53 //7.加密格式 54 bizModel.put("sign_type",AlipayConfig.SIGNTYPE+""); 55 56 /******--------------- 3.公共参数封装 结束 ------------------------*****/ 57 58 //实例化客户端 59 AlipayClient client = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID, AlipayConfig.RSA_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET, AlipayConfig.ALIPAY_PUBLIC_KEY,AlipayConfig.SIGNTYPE); 60 61 //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay 62 AlipayTradeAppPayRequest ali_request = new AlipayTradeAppPayRequest(); 63 64 //SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。 65 AlipayTradeAppPayModel model = new AlipayTradeAppPayModel(); 66 model.setPassbackParams(URLEncoder.encode((String)orderMap.get("body").toString()));; //描述信息 添加附加数据 67 model.setBody(orderMap.get("body")); //商品信息 68 model.setSubject(orderMap.get("subject")); //商品名称 69 model.setOutTradeNo(orderMap.get("out_trade_no")); //商户订单号(自动生成) 70 model.setTimeoutExpress(orderMap.get("timeout_express")); //交易超时时间 71 model.setTotalAmount(orderMap.get("total_amount")); //支付金额 72 model.setProductCode(orderMap.get("product_code")); //销售产品码 73 model.setSellerId("20881021********"); //商家id 74 ali_request.setBizModel(model); 75 ali_request.setNotifyUrl(AlipayConfig.notify_url); //回调地址 76 77 AlipayTradeAppPayResponse response = client.sdkExecute(ali_request); 78 orderStr = response.getBody(); 79 System.err.println(orderStr); //就是orderString 可以直接给客户端请求,无需再做处理。 80 81 m.addAttribute("result",orderStr); 82 m.addAttribute("status",0); 83 m.addAttribute("msg","订单生成成功"); 84 85 } catch (Exception e) { 86 m.addAttribute("status",1); 87 m.addAttribute("msg","订单生成失败"); 88 } 89 90 return m; 91 }
4.第2个异步回调接口
1 /** 2 * 支付宝支付成功后.回调该接口 3 * @param request 4 * @return 5 * @throws IOException 6 */ 7 @RequestMapping(value="api/alipay/notify_url",method={RequestMethod.POST,RequestMethod.GET}) 8 @ResponseBody 9 public String notify(HttpServletRequest request,HttpServletResponse response) throws IOException { 10 Map<String, String> params = new HashMap<String, String>();
Map<String, String[]> requestParams = request.getParameterMap();
Trade trade =null; 11 //1.从支付宝回调的request域中取值 12 Map<String, String[]> requestParams = request.getParameterMap(); 13 14 for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) { 15 String name = iter.next(); 16 String[] values = requestParams.get(name); 17 String valueStr = ""; 18 for (int i = 0; i < values.length; i++) { 19 valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; 20 } 21 // 乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化 22 // valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk"); 23 params.put(name, valueStr); 24 } 25 //2.封装必须参数 26 String out_trade_no = request.getParameter("out_trade_no"); // 商户订单号 27 String orderType = request.getParameter("body"); // 订单内容 28 String tradeStatus = request.getParameter("trade_status"); //交易状态 29 30 //3.签名验证(对支付宝返回的数据验证,确定是支付宝返回的) 31 boolean signVerified = false; 32 try { 33 //3.1调用SDK验证签名 34 signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.CHARSET, AlipayConfig.SIGNTYPE);35 } catch (AlipayApiException e) { 36 e.printStackTrace(); 37 } 38 //4.对验签进行处理 39 if (signVerified) { //验签通过 40 if(tradeStatus.equals("TRADE_SUCCESS")) { //只处理支付成功的订单: 修改交易表状态,支付成功 41 Trade trade = tradeService.selectByOrderNumber(out_trade_no); 42 trade.setTradeStatus((byte)3); //支付完成 43 int returnResult = tradeService.updateByPrimaryKeySelective(trade); //更新交易表中状态 44 if(returnResult>0){ 45 return "success"; 47 }else{ 48 return "fail"; 50 } 51 }else{ 52 return "fail"; 53 } 54 } else { //验签不通过 55 System.err.println("验签失败"); 57 return "fail"; 58 } 59 }
5.第3个同步通知接口
1 /** 2 * 支付宝支付成功后.通知页面 3 *@author Zhao 4 *@date 2017年11月2日 5 *@param request 6 *@return 7 *@throws UnsupportedEncodingException 8 */ 9 @RequestMapping(value="api/alipay/return_url",method={RequestMethod.POST,RequestMethod.GET}) 10 @ResponseBody 11 public Model returnUrl(@RequestParam("id") String id,HttpServletRequest request,Model model) throws UnsupportedEncodingException { 12 System.err.println("。。。。。。 同步通知 。。。。。。"); 13 System.err.println("。。。。。。 同步通知 。。。。。。"); 14 System.err.println("。。。。。。 同步通知 。。。。。。"); 15 Map returnMap = new HashMap(); 16 try { 17 18 Trade trade = tradeService.selectByOrderNumber(id); 19 // 返回值Map 20 if(trade !=null && trade.getTradeStatus() == 2){ 21 User user = userService.selectByPrimaryKey(trade.gettUserId()); 22 returnMap.put("tradeType", trade.getTradeType()); //支付方式 23 returnMap.put("phoneNum", user.getPhoneNumber()); //支付帐号 24 returnMap.put("tradeMoney", trade.getTradeMoney()+""); //订单金额 25 26 }else{ 27 model.addAttribute("msg", "查询失败"); 28 model.addAttribute("status", 0); 29 } 30 model.addAttribute("returnMap", returnMap); 31 System.err.println(returnMap); 32 model.addAttribute("msg", "查询成功"); 33 model.addAttribute("status", 0); 34 } catch (Exception e) { 35 model.addAttribute("msg", "查询失败"); 36 model.addAttribute("status", 1); 37 } 38 39 return model; 40 }
Trade类
public class Trade { private Long id; private String orderNumber; private String tradeNumber; private Byte tradeType; private BigDecimal tradeMoney; private String orderName; private String goodsDescribe; private Byte tradeStatus; private Byte orderType; private Date createTime; private Long tUserId; private Date payTime; private String bankName; private String payeeName; private String payeeAccount; private Date paymentTime; public Date getPaymentTime() { return paymentTime; } public void setPaymentTime(Date paymentTime) { this.paymentTime = paymentTime; } public String getBankName() { return bankName; } public void setBankName(String bankName) { this.bankName = bankName; } public String getPayeeName() { return payeeName; } public void setPayeeName(String payeeName) { this.payeeName = payeeName; } public String getPayeeAccount() { return payeeAccount; } public void setPayeeAccount(String payeeAccount) { this.payeeAccount = payeeAccount; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getOrderNumber() { return orderNumber; } public void setOrderNumber(String orderNumber) { this.orderNumber = orderNumber; } public String getTradeNumber() { return tradeNumber; } public void setTradeNumber(String tradeNumber) { this.tradeNumber = tradeNumber; } public Byte getTradeType() { return tradeType; } public void setTradeType(Byte tradeType) { this.tradeType = tradeType; } public BigDecimal getTradeMoney() { return tradeMoney; } public void setTradeMoney(BigDecimal tradeMoney) { this.tradeMoney = tradeMoney; } public String getOrderName() { return orderName; } public void setOrderName(String orderName) { this.orderName = orderName; } public String getGoodsDescribe() { return goodsDescribe; } public void setGoodsDescribe(String goodsDescribe) { this.goodsDescribe = goodsDescribe; } public Byte getTradeStatus() { return tradeStatus; } public void setTradeStatus(Byte tradeStatus) { this.tradeStatus = tradeStatus; } public Byte getOrderType() { return orderType; } public void setOrderType(Byte orderType) { this.orderType = orderType; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public Long gettUserId() { return tUserId; } public void settUserId(Long tUserId) { this.tUserId = tUserId; } public Date getPayTime() { return payTime; } public void setPayTime(Date payTime) { this.payTime = payTime; } @Override public String toString() { return "Trade [id=" + id + ", orderNumber=" + orderNumber + ", tradeNumber=" + tradeNumber + ", tradeType=" + tradeType + ", tradeMoney=" + tradeMoney + ", orderName=" + orderName + ", goodsDescribe=" + goodsDescribe + ", tradeStatus=" + tradeStatus + ", orderType=" + orderType + ", createTime=" + createTime + ", tUserId=" + tUserId + ", payTime=" + payTime + ", bankName=" + bankName + ", payeeName=" + payeeName + ", payeeAccount=" + payeeAccount + ", paymentTime=" + paymentTime + "]"; } }
二、测试
支付宝在app端支付成功后,要调用异步通知,因些需要访问的url必须是外网可以访问的,在这里推荐一款内网穿透工具natapp,需要用身份证号验证,测试个支付是没问题的
附:
1. natapp官网: https://natapp.cn
2.natapp 1分钟新手图文教程: https://natapp.cn/article/natapp_newbie
三、流程分析:
1.配置文件AlipayConfig注意事项:
(1)注意沙箱环境和正式环境的不同:
商户appid :
沙箱:进入沙箱环境会自动分配
正式:商户唯一的标识
请求网关地址:
沙箱:https://openapi.alipaydev.com/gateway.do
正式:https://openapi.alipay.com/gateway.do
(2)公钥和私钥
公钥和私钥是自己生成的不要配错,与支付宝的公钥不要混淆了。(详情见:https://docs.open.alipay.com/204/105297)
2. 本案例只是对支付进行说明,退款等功能可进行举一反三编写。
3.本人由于能力水平有限.有不到之处请大家多多指教!!!