Java微信支付全教程demo【公众号支付】详解编程语言

直入主题:

  1. 注册微信公众号、微信支付商户号,并做好基础配置(不解释配置详情,无非是获取 appid,商户号等)
  2. 微信支付接口代码
  3. 微信支付回调接口代码
  4. 微信h5支付页面唤起字符密码界面完成支付   

1,写代码之前准备工作

(1):利用开源代码 weixin-java-tools来开发效率很高,免去了很多繁琐的代码开发量;

链接 https://github.com/wechat-group/weixin-java-tools

搭建maven工程,引入:

<!-- 微信支付 开始--> 
		<dependency> 
		  <groupId>com.github.binarywang</groupId> 
		  <artifactId>weixin-java-pay</artifactId> 
		  <version>2.8.0</version> 
		</dependency> 
		<dependency> 
		  <groupId>com.github.binarywang</groupId> 
		  <artifactId>weixin-java-pay</artifactId> 
		  <version>2.8.0</version> 
		  <classifier>sources</classifier> 
		</dependency> 
		 
		<dependency> 
            <groupId>com.github.binarywang</groupId> 
            <artifactId>weixin-java-mp</artifactId> 
            <version>2.8.0</version> 
        </dependency> 
         
		<dependency> 
		  <groupId>com.github.binarywang</groupId> 
		  <artifactId>weixin-java-mp</artifactId> 
		  <version>2.8.0</version> 
		  <classifier>sources</classifier> 
		</dependency> 
		 
		<dependency> 
		  <groupId>com.github.binarywang</groupId> 
		  <artifactId>weixin-java-common</artifactId> 
		  <version>2.8.0</version> 
		</dependency> 
		<dependency> 
		  <groupId>com.github.binarywang</groupId> 
		  <artifactId>weixin-java-common</artifactId> 
		  <version>2.8.0</version> 
		  <classifier>sources</classifier> 
		</dependency> 
		 
		<!-- 微信支付 结束 -->

(2):微信支付开发接口需要 用到用户openId参数,至于微信授权获取用户openId这里不做解释;

(3):获得微信支付所要的配置文件,

这里我配置的有参数的都是必须要填写的,其他的可以不写,这里WX_WEB_URL这个是你网站的网址在回调的时候需要用到,我把这个地址配置到了配置文件里了。

Java微信支付全教程demo【公众号支付】详解编程语言Java微信支付全教程demo【公众号支付】详解编程语言

#微信对接配置 
WX_APPID=wx11231231231 
WX_APPSECRET=321321321321321321 
WX_TOKEN= 
WX_AESKEY= 
#微信支付商户号  
WX_mchId=432432432 
#微信支付平台商户API密钥  
WX_mchKey=fgfdfdewrewrwer432432 
#服务商模式下的子商户公众账号ID 
WX_subAppId= 
#服务商模式下的子商户号 
WX_subMchId= 
WX_keyPath= 
WX_WEB_URL=http://7cvyn3.natappfree.cc/zc

(4):这个是我们从微信平台上获取的配置文件,还有两个重要的授权地址是我们要在微信平台上配置的,这个也是微信极为坑的一点

1:在微信公众平台——》权限接口——》网页授权获取用户基本信息

网址: https://mp.weixin.qq.com  微信公众号登录入口

必须填入外网域名并且要下载提示里的.txt文件,放到你网站的跟目录下,可以通过网站直接访问的路径

推荐一个内网穿透工具,调试微信开发非常方便https://natapp.cn/#download

 

Java微信支付全教程demo【公众号支付】详解编程语言

Java微信支付全教程demo【公众号支付】详解编程语言

这一步配置好后,还有一步就是微信支付的路径配置,这里最坑,我已在崩溃的边缘,这就是支付授权目录,注意这里是目录,不是仅仅是域名,后面要加你要掉起支付的html页面的最后一个路径要加/。当初我只写了域名,然后怎么都掉用起不来支付界面。坑了我好几天,

Java微信支付全教程demo【公众号支付】详解编程语言

到这一步,如果都没有问,你基础的配置就完成了,下面就到了撸代码的步骤了,由于我们用的是开源工具,代码量其实非常简单,只需要一点配置就好。

2:撸代码,Java搞起来

2.1:先把微信配置文件 通过spring写到bean中;

 <!-- 微信基础配置 --> 
    <bean name="wxConfig" class="me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage"> 
		<property name="appId" value="${WX_APPID}" /> 
		<property name="secret" value="${WX_APPSECRET}" /> 
	</bean> 
	<!-- 微信核心service注入 --> 
	<bean id="wxMpService" class="me.chanjar.weixin.mp.api.impl.WxMpServiceImpl"> 
        <property name="wxMpConfigStorage" ref="wxConfig" /> 
    </bean> 
  
    <!-- 微信支付配置 --> 
    <bean name="wxPayConfig" class="com.github.binarywang.wxpay.config.WxPayConfig"> 
		<property name="appId" value="${WX_APPID}" /> 
		<property name="mchId" value="${WX_mchId}" /> 
		<property name="mchKey" value="${WX_mchKey}" /> 
		<property name="subAppId" value="${WX_subAppId}" /> 
		<property name="subMchId" value="${WX_subMchId}" /> 
		<property name="keyPath" value="${WX_keyPath}" /> 
	</bean> 
	<!-- 微信支付service注入 --> 
	<bean id="wxPayService" class="com.github.binarywang.wxpay.service.impl.WxPayServiceImpl"> 
        <property name="config" ref="wxPayConfig" /> 
    </bean>

2.2:直接进入重点,微信支付控制器,微信支付欲支付接口和回调接口:相关工具类封装在下面。

支付那我已经把业务和微信支付做了分离。

/** 
 * 微信对接控制器,微信支付 
 * Project Name:zc_app_api 
 * File Name:WxInitController.java 
 * Package Name:com.zc.app.api.controller.weixin 
 * Date:2017年9月30日下午3:10:19 
 * @author 吉文剑 
 */ 
@Controller 
@RequestMapping("/wxPay/") 
public class WxPayController extends BaseController { 
	private final Logger logger = LoggerFactory.getLogger("WxPayController"); 
	  
	 @Autowired 
	 private WxPayConfig payConfig; 
	 @Autowired 
	 private WxPayService payService; 
	  
	 /** 
	   *微信公众号支付,业务方法 
	   * @param response 
	   * @param request 
	   */ 
	 @RequestMapping(value = "toPayInfo") 
	  public void getJSSDKPayInfo(HttpServletResponse response, 
	                              HttpServletRequest request) { 
		 DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss"); 
		  
		  String orderSubject = "停车缴费0.01元";//商品描述 
		  String merchantTradeNo = df.format(new Date());//商户订单号 
		  Integer totalAmount = 1;//订单总金额,单位为分 
		  String goodsDesc = "万达停车场下午1点-9点"; 
		  String gooodsCode = "code"+merchantTradeNo; 
		   
		  Map<String, Object> payData = getPayData( merchantTradeNo, 
										  orderSubject, 
										  totalAmount, 
										  goodsDesc, 
										  gooodsCode); 
		   
		  resultPayData(response,payData); 
		  return; 
	  } 
     
	 /** 微信公众号支付接口,通过参数生成网页微信js支付参数,掉起支付界面必须参数 
	 * @param merchantTradeNo		商户订单号(必填) 
	 * @param orderSubject			订单名称(必填) 
	 * @param totalAmount			订单金额,单位分(必填) 
	 * @param goodsDesc				商品描述(可空) 
	 * @param gooodsCode			商品编码(可空) 
	 * Date:2017年12月4日上午11:04:04 
	 * @author 吉文剑 
	 */ 
	 private Map<String, Object> getPayData(String merchantTradeNo, 
											 String orderSubject, 
											 Integer totalAmount, 
											 String goodsDesc, 
											 String gooodsCode){ 
		 Map<String, Object> map = new HashMap<String, Object>(); 
		 ConstantUtils instance = ConstantUtils.getInstance(); 
		  ZuUser sessUser = (ZuUser) session.getAttribute(BaseController.SESSION_USER); 
		  if(null == sessUser || !StringUtils.isValid(sessUser.getOpenId())){ 
			  map.put("result", false); 
			  return map; 
		  } 
	     WxPayUnifiedOrderRequest prepayInfo = WxPayUnifiedOrderRequest.newBuilder() 
	      .openid(sessUser.getOpenId()) 
	      .outTradeNo(merchantTradeNo) 
	      .totalFee(totalAmount) 
	      .body(orderSubject) 
	      .tradeType(WeixinUtils.TRADE_TYPE) 
	      .spbillCreateIp(request.getRemoteAddr()) 
	      .notifyURL(instance.getPropertyValue("WEB_URL")+WeixinUtils.NOTIFY_URL) 
	      .nonceStr(WeixinUtils.getNonceStr()) 
	      .detail(goodsDesc) 
	      .productId(gooodsCode) 
	      .build(); 
	    try { 
		      Map<String, String> payInfo = this.payService.getPayInfo(prepayInfo); 
		      map.put("result", true); 
		      map.put("data", payInfo); 
	    } catch (WxPayException e) { 
	    	 map.put("result", false); 
		     map.put("data", e.getErrCodeDes()); 
		     this.logger.error(e.getErrCodeDes()); 
		     e.printStackTrace(); 
	    } 
	    return map; 
	 } 
	  
	  /** 
	   * 微信通知支付结果的回调地址,notifyCallback 
	   * 
	   * @param request 
	   * @param response 
	   */ 
	  @RequestMapping(value = "notifyCallback") 
	  public void notifyCallback(HttpServletRequest request,  HttpServletResponse response) { 
	    try { 
	    	synchronized (this) { 
	        Map<String, String> kvm = XMLUtil.parseRequestXmlToMap(request); 
	        String orderCode = null;//回调 支付订单号 
	        String resultCode = null;//回调支付是否成功状态吗 
	        String totalFee = null;//支付金额 
	        System.out.println("微信支付回调参数:"); 
	        System.out.println(kvm); 
	        if (SignUtils.checkSign(kvm, this.payConfig.getMchKey())) { 
	        	orderCode =  kvm.get("out_trade_no"); 
	        	resultCode = kvm.get("result_code"); 
	        	totalFee = kvm.get("total_fee"); 
	          if ("SUCCESS".equals(resultCode)) { 
	            //TODO(user) 微信服务器通知此回调接口支付成功后,通知给业务系统做处理 
	            logger.info("out_trade_no: " + orderCode + " pay SUCCESS!"); 
	            response.getWriter().write(WeixinUtils.WX_PAY_SUCCESS); 
	          } else { 
	            this.logger.error("out_trade_no: "  + orderCode + " result_code is FAIL"); 
	            response.getWriter().write(WeixinUtils.WX_PAY_FAIL); 
	          } 
	        } else { 
	        	 this.logger.error("out_trade_no: " + orderCode + " check signature FAIL"); 
	        	 response.getWriter().write(WeixinUtils.WX_PAY_SIGN_FAIL); 
	        } 
	         
	        if("SUCCESS".equals(resultCode)){ 
	            //支付成功的业务逻辑 
	        	//totalFee   要判断支付金额是否等于订单金额!!! 
	        	System.out.println("支付成功:订单号:"+orderCode+",支付金额:"+totalFee); 
	        	 
	        }else{ 
	            //支付失败的业务逻辑 
	        	 System.out.println("微信支付 回调 :*-************支付失败"); 
	        } 
	         
	      } 
	    } catch (Exception e) { 
	      e.printStackTrace(); 
	    } 
	  } 
	   
	   
	  /** 
	     * 客户端返回JSON字符串 
	     * @param response 
	     * @param object 
	     * @return 
	     */ 
	  protected String resultPayData(HttpServletResponse response, Object object) { 
		  try { 
	            response.reset(); 
	            response.setContentType("application/json"); 
	            response.setCharacterEncoding("utf-8"); 
	            //解决跨域问题 
	            response.setHeader("Access-Control-Allow-Origin", "*"); 
	            response.getWriter().print(new Gson().toJson(object)); 
	            return null; 
	        } catch (IOException e) { 
	            return null; 
	        } 
	    } 
     
}


2.3:自己整理了一个工具类,你们后面会用的到的,基本都是写配置项,不会变的:

/** 
 * 微信通用工具类 
 * Project Name:zc_app_api 
 * File Name:WeixinUtils.java 
 * Package Name:com.zc.app.api.utils 
 * Date:2017年10月18日下午1:45:01 
 * @author 吉文剑 
 */ 
public class WeixinUtils { 
	/** 微信支付回调支付成功,返回成功结果 */ 
	public static final String WX_PAY_SUCCESS = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[ok]]></return_msg></xml>"; 
	/** 微信支付回调支付失败,返回失败结果 */ 
	public static final String WX_PAY_FAIL = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[result_code is FAIL]]></return_msg></xml>"; 
	/** 微信支付回调支付sign验证失败,返回sign验证失败结果 */ 
	public static final String WX_PAY_SIGN_FAIL = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[check signature FAIL]]></return_msg></xml>"; 
	/** 微信支付回调地址路径 */ 
	public static final String NOTIFY_URL = "/wxPay/notifyCallback.do"; 
	/** 微信获取微信用户授权后用户信息 地址路径 */ 
	public static final String OAUTH2_USERINFO_URL = "/wx/getOAuth2UserInfo.do"; 
	/** 微信官方api接口 */ 
	public static final String URL_OAUTH2 = "https://open.weixin.qq.com/connect/oauth2/authorize?"; 
	/** 获取微信用户信息 */ 
	public static final String SCOPE = "snsapi_userinfo"; 
	/** 交易类型 :jsapi代表微信公众号支付 */ 
	public static final String TRADE_TYPE = "JSAPI"; 
	 
	/** 获取微信openId URL  */ 
	public String getWxOpenIdUrl(String toUrl){ 
		ConstantUtils instance = ConstantUtils.getInstance(); 
		StringBuffer url = new StringBuffer(); 
		url.append(URL_OAUTH2) 
		.append("appid=").append(instance.getPropertyValue("WX_APPID")) 
		.append("&redirect_uri=").append(instance.getPropertyValue("WX_WEB_URL") + OAUTH2_USERINFO_URL) 
		.append("&response_type=code") 
		.append("&scope=").append(SCOPE) 
		.append("&state=").append(toUrl) 
		.append("#wechat_redirect"); 
		return url.toString(); 
	} 
	 
	 /** 
	  * 获得微信支付随机码 
	  * @return 
	  * Date:2017年12月4日上午9:50:48 
	  * @author 吉文剑 
	  */ 
	public static String getNonceStr(){ 
		return UUID.randomUUID().toString().replaceAll("-", "").toUpperCase(); 
	} 
	 
}

2.4:其他相关工具类:

/**  
 * @ClassName: ConstantUtils  
 * @Description: (读取配置文件的信息,一些公共的属性,参数配置在)  
 * @author JiWenJian 
 */ 
public class ConstantUtils { 
	private static final String FILEPATH = "/zc_app_api.properties"; 
	 
	private static ConstantUtils instance; 
	 
	private ConstantUtils(){ 
	} 
	 
	public static ConstantUtils getInstance(){ 
		if(null == instance ){ 
			instance  = new ConstantUtils(); 
		} 
		return instance; 
	} 
	 
	/** 
	 * @Description: (读取文件信息) 
	 * @author JiWenJian 
	 * @date 2012-11-22 下午01:42:08 
	 * @param key 
	 * @return 
	 */ 
	public String getPropertyValue(String key) { 
		Properties props = new Properties(); 
		try { 
			InputStream in = getClass().getResourceAsStream(FILEPATH); 
			props.load(in); 
			return props.getProperty(key); 
		} catch (Exception e) { 
			e.printStackTrace(); 
			return null; 
		} 
	} 
	 
	/** 
	 * @Description: (读取配置Integer数值信息) 
	 * @author JiWenJian 
	 * @date 2012-11-22 下午01:42:08 
	 * @param key 
	 * @return 
	 */ 
	public Integer getPropertyIntegerValue(String key) { 
		Properties props = new Properties(); 
		try { 
			InputStream in = getClass().getResourceAsStream(FILEPATH); 
			props.load(in); 
			return Integer.parseInt(props.getProperty(key)); 
		} catch (Exception e) { 
			e.printStackTrace(); 
			return null; 
		} 
	} 
 
}

/** 
 *  xml解析成map对象 
 */ 
public class XMLUtil { 
 
    /** 
     * 将微信服务器发送的Request请求中Body的XML解析为Map 
     * 
     * @param request 
     * @return 
     * @throws Exception 
     */ 
    public static Map<String, String> parseRequestXmlToMap(HttpServletRequest request) throws Exception { 
        // 解析结果存储在HashMap中 
        Map<String, String> resultMap; 
        InputStream inputStream = request.getInputStream(); 
        resultMap = parseInputStreamToMap(inputStream); 
        return resultMap; 
    } 
 
    /** 
     * 将输入流中的XML解析为Map 
     * 
     * @param inputStream 
     * @return 
     * @throws DocumentException 
     * @throws IOException 
     */ 
    public static Map<String, String> parseInputStreamToMap(InputStream inputStream) throws DocumentException, IOException { 
        // 解析结果存储在HashMap中 
        Map<String, String> map = new HashMap<String, String>(); 
        // 读取输入流 
        SAXReader reader = new SAXReader(); 
        Document document = reader.read(inputStream); 
        //得到xml根元素 
        Element root = document.getRootElement(); 
        // 得到根元素的所有子节点 
        List<Element> elementList = root.elements(); 
        //遍历所有子节点 
        for (Element e : elementList) { 
            map.put(e.getName(), e.getText()); 
        } 
        //释放资源 
        inputStream.close(); 
        return map; 
    } 
 
    /** 
     * 将String类型的XML解析为Map 
     * 
     * @param str 
     * @return 
     * @throws Exception 
     */ 
    public static Map<String, String> parseXmlStringToMap(String str) throws Exception { 
        Map<String, String> resultMap; 
        InputStream inputStream = new ByteArrayInputStream(str.getBytes("UTF-8")); 
        resultMap = parseInputStreamToMap(inputStream); 
        return resultMap; 
    } 
 
}

3:下面到了前台html页面,只剩下最后一小步了,只要掉起微信支付密码界面就大功告成了;

<head> 
 	  
    <script type="text/javascript" src="http://g.alicdn.com/sj/lib/jquery/dist/jquery.min.js"></script> 
    <script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script> 
	<script src="../js/common.js"></script> 
    <script type="text/javascript"> 
     
    function toPay(){ 
        if (typeof WeixinJSBridge == "undefined"){ 
           if( document.addEventListener ){ 
               document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false); 
           }else if (document.attachEvent){ 
               document.attachEvent('WeixinJSBridgeReady', onBridgeReady);  
               document.attachEvent('onWeixinJSBridgeReady', onBridgeReady); 
           } 
        }else{ 
           onBridgeReady(); 
        } 
    } 
     
    function onBridgeReady(){ 
    	$.ajax({ 
    		   type: "POST", 
    		   url: DEF_GLOBAL_DOMAIN+"/wxPay/toPayInfo.do", 
    		   success: function(data){ 
    	 			  console.log(data); 
    	 			   
    	 			   
    	 			 WeixinJSBridge.invoke( 
    	 			           'getBrandWCPayRequest', { 
    	 			               "appId" : data.data.appId,  
    	 			               "timeStamp": data.data.timeStamp,     
    	 			               "nonceStr" : data.data.nonceStr, 
    	 			               "package" : data.data.package, 
    	 			               "signType" :  data.data.signType,  
    	 			               "paySign" : data.data.paySign 
    	 			           },function(res){ 
    	 			        	   $("#msgId").html(res.err_msg); 
    	 			            if(res.err_msg == "get_brand_wcpay_request:ok"){ 
    	 			            	$("#resId").html("支付成功"); 
    	 			            //	location.href="weixinPayResult.html";//支付成功跳转到指定页面 
    	 			            }else if(res.err_msg == "get_brand_wcpay_request:cancel"){   
    	 			            	$("#resId").html("支付取消"); 
    	 			            }else{   
    	 			            	$("#resId").html("支付失败"); 
    	 			            }   
    	 			        });  
    	 		   } 
    		}); 
    	 
    } 
      
    </script> 
</head> 
<body> 
  
 
<div class="content"> 
    <div class="form-area"> 
    	 <div class="inp"> 
            		支付0.01元 
        </div> 
        <button class="em-submit-st2" onclick="toPay()" > 
           	确定支付 
        </button> 
    </div> 
   结果: 
    <p/> 
    <div id="resId"></div> 
    参数: 
    <p/> 
    <div id="invokeId"></div> 
    <br/><p/> 
    返回: 
    <p/> 
    <div id="msgId"></div> 
</div> 
  
</body> 
</html>

到这里整个微信支付就完成了,总结:难点在于微信平台繁琐的配置,很繁琐,错一个整个都跑不通,还以为是自己代码有问题,坑爹。代码层,感觉没有什么复杂量,就做一些配置就好了,至于微信老是喜欢用各种 签名    sign  

这个工具已经给我们解决了,只要各个地方的配置没有问题,其实就很简单了。

整个执行流程 是 :

微信点击支付按钮——》

       发送ajax到支付请求控制器——》

       返回支付参数——》

       用支付参数,调用微信内嵌的掉起支付js方法,发起支付——》

      支付结果同步返回结果——》

      支付结果异步发送到后台回调控制器做结果处理


我在本文章中只做了代码层和简单的讲解,至于整个微信支付的细节,可以访问下面的作者:写的比较详细:https://www.cnblogs.com/yimiyan/p/5603657.html

感谢同是天涯代码人,花这么长时间来参读本文章,加油把。。。

Java微信支付全教程demo【公众号支付】详解编程语言Java微信支付全教程demo【公众号支付】详解编程语言

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/13974.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论