业务需求

实现小程序支付功能

注意事项(订单号重复问题)

1.平台订单号

2.微信支付订单号

分开设计,订单失败成功以微信订单号为准 更新(回调时验证)相关产品订单支付状态;

环境

1.后端java

2.jdk1.7

前端代码(小程序)

\"\"

var app = getApp()
Page({
  payAction: function () {
    var openid = \'oAJ1N5Vg-UcltfCj6uGXXXXX\';//自己openID
    wx.request({
      url: \'http://XXXXX/aglie/ten Pay/v1/orderPay\',//调用接口生成 统一下单的参数
      data: {
        openId: openid,//openid  
        orderId:\'20180717154514968\',
        type:\'1\'
        //body: \"尊贵VIP\", //商品信息  
        //totalfee: 100 //金额 
        
      },
      method: \'POST\', // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
      header: {
        \'content-type\': \'application/x-www-form-urlencoded\'
      }, // 设置请求的 header
      success: function (res) {
        console.log(\"status=\" + res.data.status)
        console.log(\"message=\" + res.data.message)
          var obj = res.data //得到参数
          console.log(\'微信支付接口信息\')
          console.log(\'签名:\' + res.data.paySign)
          console.log(\'随机串:\' + res.data.nonceStr)
          console.log(\'时间戳:\' + res.data.timeStamp)
          console.log(\'package:\' + res.data.package)
        wx.requestPayment({
          timeStamp: obj.timeStamp,
          nonceStr: obj.nonceStr,
          package: obj.package,
          signType: \'MD5\',
          paySign: obj.paySign,
          success: function (res) {
            console.log(\'支付成功打印信息\' )
            console.log(\'timeStam=\' + obj.timeStamp)
            console.log(\'nonceStr=\' + obj.nonceStr)
            console.log(\'package=\' + obj.package)
            console.log(\'paySign=\' + obj.paySign)
            console.log(\'成功\')
          }, fail: function (e) {
            console.log(e)
            console.log(\'失败\')
          }
        })
      },
    })
  }
})

后端代码(java)

1. 参数配置

	// AppID(小程序ID) 小程序唯一标识 (在微信小程序管理后台获取)
	private static final String APPID = \"wx8a52b60XXXXXXX\";
	// 小程序的 AppSecret(小程序密钥) (在微信小程序管理后台获取)
	private static final String APPSECRET = \"d3455f3cfb9685c602b60XXXXXXX\";
	// 授权(必填) 填写为 authorization_code (小程序)
	private static final String GRANT_TYPE = \"authorization_code\";
	
	//微信支付的商户id
	public static final String MCHID = \"12760XXXXXXX\";
	// 商户支付密钥Key。审核通过后,在微信发送的邮件中查看
	public static final String KEY = \"2b60XXXXXXX12345\";
    //签名方式,固定值
    public static final String SIGNTYPE = \"MD5\";
    //交易类型,小程序支付的固定值为JSAPI
    public static final String TRADETYPE = \"JSAPI\";
    //微信统一下单接口地址
    public static final String PAY_URL = 	\"https://api.mch.weixin.qq.com/pay/unifiedorder\";
	//支付成功后的服务器回调url,Controller里的回调函数地址
	public static final String NOTIFY_URL = \"http://XXXX/aglie/ten Pay/v1/notify\";

2.统一下单支付

 /**
	     * 
	     * @ : orderPay   
	     * @De ion: TODO(统一下单)   
	     * @param: @param request
	     * @param: @param response
	     * @param: @param orderId
	     * @param: @param openId
	     * @param: @param type 业务类型 {1:咨询(收费&免费) 4.电商 
	     * @param: @return
	     * @param: @throws Exception      
	     * @return: Map<String, >      
	     * @throws
	     */
		@ResponseBody
		@RequestMapping(value = \"/v1/orderPay\" , method = RequestMethod.POST)
		public Map<String,  > orderPay(HttpServletRequest request,HttpServletResponse response
				,@RequestParam(required=true) String orderId
				,@RequestParam(required=true) String openId
				,@RequestParam(required=true) String type) throws Exception{
			 logger.info(\"微信统一下单  接口调用\");
		     Map<String,  > map = Maps.newHashMap();
				//code获取参数
		     if (StringUtils.isBlank(orderId)) {
		    	 map.put(\"status\", \"fail\");
		    	 map.put(\"message\", \"orderId is not null.\");
		    	 return map;
		     }
		     //这里根据orderId获取下单信息 (openId、
		     logger.info(\"【orderPay】-接收商品orderId号码=\"+orderId + \" 业务类型type =\"+ type); 
		     //局部变量
		     String body=\"尊贵VIP\";//订单 商品信息
		     int totalfee=0; //订单 商品金额 微信是按分处理的
		     String wx_out_trade_no=\"\"; //微信支付订单号,只能使用一次,非产品订单号,业务不同前缀不同(规则 咨询(ZX...) 电商(DS...)

	     	switch (type) {
			case \"1\":
				System.out.println(\"========咨询微信统一下单处理 开始=========\");
				//微信支付订单号生成
				 Date  data = new Date();  
				 wx_out_trade_no=\"ZX\" + DateUtil.dateFormatSSS.format(data)+UtilDate.getThree();

		    	//更新微信支付单号(根据自己业务场景处理) 
		    	try {
		    		 logger.info(\"2-- 微信统一下单 订单号更新订单库中\"); 
		        	 update_orderIdWx_date(type,orderId,wx_out_trade_no);
				} catch (Exception e) {
					logger.info(\"ERROR-更新微信支付订单号:orderId号码=\"+orderId + \" 业务类型type =\"+ type +\" orderIdWx=\"+wx_out_trade_no); 
				}
				//根据订单号获取订单详情(根据自己业务场景处理)
		    	 logger.info(\"3-- 微信统一下单 根据orderId 查询订单库商品信息\"); 
				 MyhQuestion myhQuestionInfo =myhQuestionService.findByOrderId(orderId);
				 if(myhQuestionInfo!=null){
					body=\"咨询服务\";
			        //Double不丢失精度,要用BigDecimal处理
			        BigDecimal v1 = myhQuestionInfo.getFinalPrice();
			        BigDecimal v2 = new BigDecimal(\"100\");
			        Double b = v1.multiply(v2).doubleValue();
			        int fee1 = b.intValue();
					totalfee=fee1;
				 }else {
					 logger.info(\"ERROR-查询订单信息:orderId号码=\"+orderId + \" 业务类型type =\"+ type); 
				}
				 System.out.println(\"========咨询微信统一下单处理  结束=========\");
				break;

			default:
				break;
			}
		     try {
		    	//获取客户端请求code
				String        appid =  Config.getAppid();    //微信小程序--》“开发者ID”
				String       mch_id =  Config.getMchid();    //商户号,将该值赋值给partner
				String          key =  Config.getKey();    //微信支付商户平台登录)
			    String       openid = openId;    //openid
			    String           ip = WXUtil.getIpAddr(request);    //ip地址
			    String    body_info = body; //描述
				int       total_fee = totalfee;     //支付金额
			    String   notify_url =  Config.getNotifyUrl();   //回调链接
				//orderId库里订单ID  与  微信out_trade_no不同;避免支付失败,微信平台订单号出现重复,这里必须重新生成
				String out_trade_no = wx_out_trade_no; //只能使用一次,所以不能使用orderId
			    Map< ,  > map_pay= .weixinPlay(mch_id, appid, key, openid, total_fee, out_trade_no, notify_url, body_info, ip);
			    if (\"SUCCESS\".equals(map_pay.get(\"return_code\"))) {      
			     map.put(\"paySign\", map_pay.get(\"paySign\"));
		    	 map.put(\"timeStamp\", map_pay.get(\"timeStamp\"));
		    	 map.put(\"nonceStr\", map_pay.get(\"nonceStr\"));
		    	 map.put(\"package\", map_pay.get(\"package\"));
				 map.put(\"status\", \"success\");
		    	 map.put(\"message\", \"生成订单\");
		    	 map.put(\"return_code\", \"SUCCESS\");
			    }else {
			     logger.info(\"return_code=\"+map.get(\"return_code\")); 
		    	 map.put(\"status\", \"fail\");
		    	 map.put(\"message\", \"生成订单失败\");
		    	 map.put(\"return_code\", \"Fail\");
		    	 map.put(\"return_msg\", map_pay.get(\"return_msg\"));
				}
		    	 return map;
				
			} catch (Exception e) {
				 logger.debug(\"Error-e:\",e);
			     logger.info(\"生成统一下单失败:openId=\"+openId); 
				 map.put(\"status\", \"fail\");
		    	 map.put(\"message\", \"请求失败\");
		    	 return map;
			}
		     
		}

3.回调更新状态

		/**
		 * 回调函数
		 * @param request
		 * @param response
		 * @throws Exception
		 */
		@ResponseBody
		@RequestMapping(value = \"v1/notify\" , method = RequestMethod.POST)
		public  void notify(HttpServletRequest request,HttpServletResponse response) throws Exception{
			logger.info(\"支付成功的回调函数(notify)接口调用\");
			weixinpay_notify(request,response);
		}

4.其它方法

weixinPlay()生成微信订单

/**
		 * 生成微信订单
		 * 
		 * @param openid
		 * @param period_number
		 * @param merchant_id
		 * @param num
		 * @return
		 */
		public static SortedMap< ,  > weixinPlay(String mch_id,String appid,String key,String openid,int total_fee,String out_trade_no,String notify_url,String body,String ip) throws UnsupportedEncodingException, DocumentException {
			logger.info(\"生成微信订单(weixinPlay)接口调用\");
			SortedMap< ,  > paymentPo = new TreeMap< ,  >();
			paymentPo.put(\"appid\",appid);
			paymentPo.put(\"mch_id\",  mch_id);
			paymentPo.put(\"nonce_str\", WXUtil.generate());
			paymentPo.put(\"body\",body);
			paymentPo.put(\"out_trade_no\",out_trade_no);
			paymentPo.put(\"total_fee\",String.valueOf(total_fee));
			paymentPo.put(\"spbill_create_ip\", ip);//这里填你的ip地址
			paymentPo.put(\"notify_url\", notify_url);
			paymentPo.put(\"trade_type\",  Config.getTradetype()); //JSAPI
			paymentPo.put(\"openid\", openid);
			//第一次签名
			String sign = WXUtil.createSign_ChooseWXPay(\"UTF-8\", paymentPo, key);
			paymentPo.put(\"sign\", sign);
			String param = WXUtil.getRequest (paymentPo);;
		
			String request = WXUtil.httpRequest( Config.PAY_URL, \"POST\", param);
			Map<String, String> map = new HashMap<String, String>();         // 将解析结果存储在HashMap中
			InputStream in = new ByteArrayInputStream(request.getBytes());
			SAXReader reader = new SAXReader();                              // 读取输入流
			Document document = reader.read(in);
		
			Element root = document.getRootElement();                        // 得到 根元素
			@SuppressWarnings(\"unchecked\")                                   // 得到根元素的所有子节点
			List<Element> elementList = root.elements();
			for (Element element : elementList) {
				map.put(element.getName(), element.getText());
			}
			SortedMap< ,  > result = new TreeMap< ,  >();
				
			if (map.get(\"return_code\").equals(\"SUCCESS\")) {                  // 业务结果
				String nonceStr  =WXUtil.generate();
				Long   timeStamp = System.currentTimeMillis() / 1000;
				SortedMap< ,  > params = new TreeMap< ,  >();
				
				params.put(\"appId\", appid);
				params.put(\"nonceStr\", nonceStr);
				params.put(\"package\", \"prepay_id=\" + map.get(\"prepay_id\")); 
				params.put(\"signType\",  Config.getSigntype()); //MD5
				params.put(\"timeStamp\", timeStamp); 
				//二次签名  这个签名用于小程序端调用wx.requesetPayment方法
				String paySign = WXUtil.createSign_ChooseWXPay(\"UTF-8\", params, key);
				
				result.put(\"paySign\", paySign);
				result.put(\"timeStamp\", timeStamp + \"\");
				result.put(\"nonceStr\", nonceStr);
				result.put(\"package\", \"prepay_id=\" + map.get(\"prepay_id\"));
				result.put(\"return_code\", \"SUCCESS\");
			}else {
				result.put(\"return_code\", \"Fail\");
				result.put(\"return_msg\", map.get(\"return_msg\"));
			}
			return result;
		}

weixinpay_notify() 回调方

/**
		 * 支付成功的回调函数
		 * @param request
		 * @param response
		 * @throws Exception
		 */
	    public  void weixinpay_notify(HttpServletRequest request,HttpServletResponse response) throws Exception{  
	    	System.out.println(\"=========支付成功,回调函数(weixinpay_notify)接口调用   开始=========\");
			InputStream inputStream ;  
	        StringBuffer sb = new StringBuffer();  
	        inputStream = request.getInputStream();  
	        String s ;  
	        BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, \"UTF-8\"));  
	        while ((s = in.readLine()) != null){  
	            sb.append(s);  
	        }  
	        in.close();  
	        inputStream.close();  
	        //sb为微信返回的 
	        String notity  = sb.toString();
	        Map<String, String> m = new HashMap<String, String>();  
	        m = WXUtil.do Parse(sb.toString());  
	        SortedMap< , > packageParams = new TreeMap< , >();        
	        Iterator it = m.keySet().iterator();  
	        while (it.hasNext()) {  
	            String parameter = (String) it.next();  
	            String parameterValue = m.get(parameter);  
	            String v = \"\";  
	            if(null != parameterValue) {  
	                v = parameterValue.trim();  
	            }  
	            packageParams.put(parameter, v);  
	        }  
	        //logger.info(\"接收到的报文2- packageParams:\" + packageParams);
	        String key =  Config.getKey(); //秘钥 
            String res  = \"\"; 
             //验证签名
	        if(WXUtil.isTenpaySign(\"UTF-8\", packageParams,key)) {   
	            //logger.info(\"接收到的报文3- packageParams:\" + packageParams);
	            System.out.println(\"-------1- 签名验证成功--------\");
	            //验证报文
	            if(\"SUCCESS\".equals((String)packageParams.get(\"return_code\"))){  
//	            	 //logger.info(\"SUCCESS=packageParams:return_code\" );
	            	 System.out.println(\"-------2- 报文验证成功--------\");
	            	//得到返回的参数
	            	String openid = (String)packageParams.get(\"openid\");  
	                String transaction_id = (String)packageParams.get(\"transaction_id\");  
	                String out_trade_no = (String)packageParams.get(\"out_trade_no\");  
	                String total_fee = (String)packageParams.get(\"total_fee\");  
	                Float fee= Float.parseFloat(total_fee)/100;
	                /**此处添加自己的业务逻辑代码start**/
	                System.out.println(\"******处理业务逻辑  开始******\");
                        
                      这里处理业务
			        
	                System.out.println(\"******处理业务逻辑  结束******\");
	                /**此处添加自己的业务逻辑代码end**/
	                
	                //通知微信服务器已经支付成功
	                res  = \"< >\" + \"<return_code><![CDATA[SUCCESS]]></return_code>\"  
	                        + \"<return_msg><![CDATA[OK]]></return_msg>\" + \"</ > \";  
	                System.out.println(\"-------3- 通知微信已支付成功--------\");
	                BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());  
	                out.write(res .getBytes());  
	                out.flush();  
	                out.close();
	            } else {  
	              System.out.println(\"----回调失败,报文为空----\");
		            res  = \"< >\" + \"<return_code><![CDATA[FAIL]]></return_code>\"
		                    + \"<return_msg><![CDATA[报文为空]]></return_msg>\" + \"</ > \";
	            }  
	        } else{  
	        	System.out.println(\"----回调失败,签名失败----\");
	            res  = \"< >\" + \"<return_code><![CDATA[FAIL]]></return_code>\"
	                    + \"<return_msg><![CDATA[签名失败]]></return_msg>\" + \"</ > \";
	        } 
	        System.out.println(\"=========支付成功,回调函数(weixinpay_notify)接口调用   结束=========\");
	    }

相关工具类(签名、秘钥生成等)这里就不写了

 

 

收藏 打印