Java微信支付全教程demo【公众号支付】

直入主题:

 

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

 

1,写代码之前准备工作

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

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

搭建maven工程,引入:

 

 
  1. <!-- 微信支付 开始-->

  2. <dependency>

  3. <groupId>com.github.binarywang</groupId>

  4. <artifactId>weixin-java-pay</artifactId>

  5. <version>2.8.0</version>

  6. </dependency>

  7. <dependency>

  8. <groupId>com.github.binarywang</groupId>

  9. <artifactId>weixin-java-pay</artifactId>

  10. <version>2.8.0</version>

  11. <classifier>sources</classifier>

  12. </dependency>

  13.  
  14. <dependency>

  15. <groupId>com.github.binarywang</groupId>

  16. <artifactId>weixin-java-mp</artifactId>

  17. <version>2.8.0</version>

  18. </dependency>

  19.  
  20. <dependency>

  21. <groupId>com.github.binarywang</groupId>

  22. <artifactId>weixin-java-mp</artifactId>

  23. <version>2.8.0</version>

  24. <classifier>sources</classifier>

  25. </dependency>

  26.  
  27. <dependency>

  28. <groupId>com.github.binarywang</groupId>

  29. <artifactId>weixin-java-common</artifactId>

  30. <version>2.8.0</version>

  31. </dependency>

  32. <dependency>

  33. <groupId>com.github.binarywang</groupId>

  34. <artifactId>weixin-java-common</artifactId>

  35. <version>2.8.0</version>

  36. <classifier>sources</classifier>

  37. </dependency>

  38.  
  39. <!-- 微信支付 结束 -->


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

 

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

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

 

 
  1. #微信对接配置

  2. WX_APPID=wx11231231231

  3. WX_APPSECRET=321321321321321321

  4. WX_TOKEN=

  5. WX_AESKEY=

  6. #微信支付商户号

  7. WX_mchId=432432432

  8. #微信支付平台商户API密钥

  9. WX_mchKey=fgfdfdewrewrwer432432

  10. #服务商模式下的子商户公众账号ID

  11. WX_subAppId=

  12. #服务商模式下的子商户号

  13. WX_subMchId=

  14. WX_keyPath=

  15. WX_WEB_URL=http://7cvyn3.natappfree.cc/zc


 

 

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

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

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

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

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

 

 

 

 

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

 

 

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

 

2:撸代码,Java搞起来

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

 

 
  1. <!-- 微信基础配置 -->

  2. <bean name="wxConfig" class="me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage">

  3. <property name="appId" value="${WX_APPID}" />

  4. <property name="secret" value="${WX_APPSECRET}" />

  5. </bean>

  6. <!-- 微信核心service注入 -->

  7. <bean id="wxMpService" class="me.chanjar.weixin.mp.api.impl.WxMpServiceImpl">

  8. <property name="wxMpConfigStorage" ref="wxConfig" />

  9. </bean>

  10.  
  11. <!-- 微信支付配置 -->

  12. <bean name="wxPayConfig" class="com.github.binarywang.wxpay.config.WxPayConfig">

  13. <property name="appId" value="${WX_APPID}" />

  14. <property name="mchId" value="${WX_mchId}" />

  15. <property name="mchKey" value="${WX_mchKey}" />

  16. <property name="subAppId" value="${WX_subAppId}" />

  17. <property name="subMchId" value="${WX_subMchId}" />

  18. <property name="keyPath" value="${WX_keyPath}" />

  19. </bean>

  20. <!-- 微信支付service注入 -->

  21. <bean id="wxPayService" class="com.github.binarywang.wxpay.service.impl.WxPayServiceImpl">

  22. <property name="config" ref="wxPayConfig" />

  23. </bean>

 

 

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

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

 

 
  1. /**

  2. * 微信对接控制器,微信支付

  3. * Project Name:zc_app_api

  4. * File Name:WxInitController.java

  5. * Package Name:com.zc.app.api.controller.weixin

  6. * Date:2017年9月30日下午3:10:19

  7. * @author 吉文剑

  8. */

  9. @Controller

  10. @RequestMapping("/wxPay/")

  11. public class WxPayController extends BaseController {

  12. private final Logger logger = LoggerFactory.getLogger("WxPayController");

  13.  
  14. @Autowired

  15. private WxPayConfig payConfig;

  16. @Autowired

  17. private WxPayService payService;

  18.  
  19. /**

  20. *微信公众号支付,业务方法

  21. * @param response

  22. * @param request

  23. */

  24. @RequestMapping(value = "toPayInfo")

  25. public void getJSSDKPayInfo(HttpServletResponse response,

  26. HttpServletRequest request) {

  27. DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");

  28.  
  29. String orderSubject = "停车缴费0.01元";//商品描述

  30. String merchantTradeNo = df.format(new Date());//商户订单号

  31. Integer totalAmount = 1;//订单总金额,单位为分

  32. String goodsDesc = "万达停车场下午1点-9点";

  33. String gooodsCode = "code"+merchantTradeNo;

  34.  
  35. Map<String, Object> payData = getPayData( merchantTradeNo,

  36. orderSubject,

  37. totalAmount,

  38. goodsDesc,

  39. gooodsCode);

  40.  
  41. resultPayData(response,payData);

  42. return;

  43. }

  44.  
  45. /** 微信公众号支付接口,通过参数生成网页微信js支付参数,掉起支付界面必须参数

  46. * @param merchantTradeNo 商户订单号(必填)

  47. * @param orderSubject 订单名称(必填)

  48. * @param totalAmount 订单金额,单位分(必填)

  49. * @param goodsDesc 商品描述(可空)

  50. * @param gooodsCode 商品编码(可空)

  51. * Date:2017年12月4日上午11:04:04

  52. * @author 吉文剑

  53. */

  54. private Map<String, Object> getPayData(String merchantTradeNo,

  55. String orderSubject,

  56. Integer totalAmount,

  57. String goodsDesc,

  58. String gooodsCode){

  59. Map<String, Object> map = new HashMap<String, Object>();

  60. ConstantUtils instance = ConstantUtils.getInstance();

  61. ZuUser sessUser = (ZuUser) session.getAttribute(BaseController.SESSION_USER);

  62. if(null == sessUser || !StringUtils.isValid(sessUser.getOpenId())){

  63. map.put("result", false);

  64. return map;

  65. }

  66. WxPayUnifiedOrderRequest prepayInfo = WxPayUnifiedOrderRequest.newBuilder()

  67. .openid(sessUser.getOpenId())

  68. .outTradeNo(merchantTradeNo)

  69. .totalFee(totalAmount)

  70. .body(orderSubject)

  71. .tradeType(WeixinUtils.TRADE_TYPE)

  72. .spbillCreateIp(request.getRemoteAddr())

  73. .notifyURL(instance.getPropertyValue("WEB_URL")+WeixinUtils.NOTIFY_URL)

  74. .nonceStr(WeixinUtils.getNonceStr())

  75. .detail(goodsDesc)

  76. .productId(gooodsCode)

  77. .build();

  78. try {

  79. Map<String, String> payInfo = this.payService.getPayInfo(prepayInfo);

  80. map.put("result", true);

  81. map.put("data", payInfo);

  82. } catch (WxPayException e) {

  83. map.put("result", false);

  84. map.put("data", e.getErrCodeDes());

  85. this.logger.error(e.getErrCodeDes());

  86. e.printStackTrace();

  87. }

  88. return map;

  89. }

  90.  
  91. /**

  92. * 微信通知支付结果的回调地址,notifyCallback

  93. *

  94. * @param request

  95. * @param response

  96. */

  97. @RequestMapping(value = "notifyCallback")

  98. public void notifyCallback(HttpServletRequest request, HttpServletResponse response) {

  99. try {

  100. synchronized (this) {

  101. Map<String, String> kvm = XMLUtil.parseRequestXmlToMap(request);

  102. String orderCode = null;//回调 支付订单号

  103. String resultCode = null;//回调支付是否成功状态吗

  104. String totalFee = null;//支付金额

  105. System.out.println("微信支付回调参数:");

  106. System.out.println(kvm);

  107. if (SignUtils.checkSign(kvm, this.payConfig.getMchKey())) {

  108. orderCode = kvm.get("out_trade_no");

  109. resultCode = kvm.get("result_code");

  110. totalFee = kvm.get("total_fee");

  111. if ("SUCCESS".equals(resultCode)) {

  112. //TODO(user) 微信服务器通知此回调接口支付成功后,通知给业务系统做处理

  113. logger.info("out_trade_no: " + orderCode + " pay SUCCESS!");

  114. response.getWriter().write(WeixinUtils.WX_PAY_SUCCESS);

  115. } else {

  116. this.logger.error("out_trade_no: " + orderCode + " result_code is FAIL");

  117. response.getWriter().write(WeixinUtils.WX_PAY_FAIL);

  118. }

  119. } else {

  120. this.logger.error("out_trade_no: " + orderCode + " check signature FAIL");

  121. response.getWriter().write(WeixinUtils.WX_PAY_SIGN_FAIL);

  122. }

  123.  
  124. if("SUCCESS".equals(resultCode)){

  125. //支付成功的业务逻辑

  126. //totalFee 要判断支付金额是否等于订单金额!!!

  127. System.out.println("支付成功:订单号:"+orderCode+",支付金额:"+totalFee);

  128.  
  129. }else{

  130. //支付失败的业务逻辑

  131. System.out.println("微信支付 回调 :*-************支付失败");

  132. }

  133.  
  134. }

  135. } catch (Exception e) {

  136. e.printStackTrace();

  137. }

  138. }

  139.  
  140.  
  141. /**

  142. * 客户端返回JSON字符串

  143. * @param response

  144. * @param object

  145. * @return

  146. */

  147. protected String resultPayData(HttpServletResponse response, Object object) {

  148. try {

  149. response.reset();

  150. response.setContentType("application/json");

  151. response.setCharacterEncoding("utf-8");

  152. //解决跨域问题

  153. response.setHeader("Access-Control-Allow-Origin", "*");

  154. response.getWriter().print(new Gson().toJson(object));

  155. return null;

  156. } catch (IOException e) {

  157. return null;

  158. }

  159. }

  160.  
  161. }

 

 

 

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

 

 
  1. /**

  2. * 微信通用工具类

  3. * Project Name:zc_app_api

  4. * File Name:WeixinUtils.java

  5. * Package Name:com.zc.app.api.utils

  6. * Date:2017年10月18日下午1:45:01

  7. * @author 吉文剑

  8. */

  9. public class WeixinUtils {

  10. /** 微信支付回调支付成功,返回成功结果 */

  11. public static final String WX_PAY_SUCCESS = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[ok]]></return_msg></xml>";

  12. /** 微信支付回调支付失败,返回失败结果 */

  13. public static final String WX_PAY_FAIL = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[result_code is FAIL]]></return_msg></xml>";

  14. /** 微信支付回调支付sign验证失败,返回sign验证失败结果 */

  15. public static final String WX_PAY_SIGN_FAIL = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[check signature FAIL]]></return_msg></xml>";

  16. /** 微信支付回调地址路径 */

  17. public static final String NOTIFY_URL = "/wxPay/notifyCallback.do";

  18. /** 微信获取微信用户授权后用户信息 地址路径 */

  19. public static final String OAUTH2_USERINFO_URL = "/wx/getOAuth2UserInfo.do";

  20. /** 微信官方api接口 */

  21. public static final String URL_OAUTH2 = "https://open.weixin.qq.com/connect/oauth2/authorize?";

  22. /** 获取微信用户信息 */

  23. public static final String SCOPE = "snsapi_userinfo";

  24. /** 交易类型 :jsapi代表微信公众号支付 */

  25. public static final String TRADE_TYPE = "JSAPI";

  26.  
  27. /** 获取微信openId URL */

  28. public String getWxOpenIdUrl(String toUrl){

  29. ConstantUtils instance = ConstantUtils.getInstance();

  30. StringBuffer url = new StringBuffer();

  31. url.append(URL_OAUTH2)

  32. .append("appid=").append(instance.getPropertyValue("WX_APPID"))

  33. .append("&redirect_uri=").append(instance.getPropertyValue("WX_WEB_URL") + OAUTH2_USERINFO_URL)

  34. .append("&response_type=code")

  35. .append("&scope=").append(SCOPE)

  36. .append("&state=").append(toUrl)

  37. .append("#wechat_redirect");

  38. return url.toString();

  39. }

  40.  
  41. /**

  42. * 获得微信支付随机码

  43. * @return

  44. * Date:2017年12月4日上午9:50:48

  45. * @author 吉文剑

  46. */

  47. public static String getNonceStr(){

  48. return UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();

  49. }

  50.  
  51. }


2.4:其他相关工具类:

 

 

 
  1. /**

  2. * @ClassName: ConstantUtils

  3. * @Description: (读取配置文件的信息,一些公共的属性,参数配置在)

  4. * @author JiWenJian

  5. */

  6. public class ConstantUtils {

  7. private static final String FILEPATH = "/zc_app_api.properties";

  8.  
  9. private static ConstantUtils instance;

  10.  
  11. private ConstantUtils(){

  12. }

  13.  
  14. public static ConstantUtils getInstance(){

  15. if(null == instance ){

  16. instance = new ConstantUtils();

  17. }

  18. return instance;

  19. }

  20.  
  21. /**

  22. * @Description: (读取文件信息)

  23. * @author JiWenJian

  24. * @date 2012-11-22 下午01:42:08

  25. * @param key

  26. * @return

  27. */

  28. public String getPropertyValue(String key) {

  29. Properties props = new Properties();

  30. try {

  31. InputStream in = getClass().getResourceAsStream(FILEPATH);

  32. props.load(in);

  33. return props.getProperty(key);

  34. } catch (Exception e) {

  35. e.printStackTrace();

  36. return null;

  37. }

  38. }

  39.  
  40. /**

  41. * @Description: (读取配置Integer数值信息)

  42. * @author JiWenJian

  43. * @date 2012-11-22 下午01:42:08

  44. * @param key

  45. * @return

  46. */

  47. public Integer getPropertyIntegerValue(String key) {

  48. Properties props = new Properties();

  49. try {

  50. InputStream in = getClass().getResourceAsStream(FILEPATH);

  51. props.load(in);

  52. return Integer.parseInt(props.getProperty(key));

  53. } catch (Exception e) {

  54. e.printStackTrace();

  55. return null;

  56. }

  57. }

  58.  
  59. }


 

 

 

 
  1. /**

  2. * xml解析成map对象

  3. */

  4. public class XMLUtil {

  5.  
  6. /**

  7. * 将微信服务器发送的Request请求中Body的XML解析为Map

  8. *

  9. * @param request

  10. * @return

  11. * @throws Exception

  12. */

  13. public static Map<String, String> parseRequestXmlToMap(HttpServletRequest request) throws Exception {

  14. // 解析结果存储在HashMap中

  15. Map<String, String> resultMap;

  16. InputStream inputStream = request.getInputStream();

  17. resultMap = parseInputStreamToMap(inputStream);

  18. return resultMap;

  19. }

  20.  
  21. /**

  22. * 将输入流中的XML解析为Map

  23. *

  24. * @param inputStream

  25. * @return

  26. * @throws DocumentException

  27. * @throws IOException

  28. */

  29. public static Map<String, String> parseInputStreamToMap(InputStream inputStream) throws DocumentException, IOException {

  30. // 解析结果存储在HashMap中

  31. Map<String, String> map = new HashMap<String, String>();

  32. // 读取输入流

  33. SAXReader reader = new SAXReader();

  34. Document document = reader.read(inputStream);

  35. //得到xml根元素

  36. Element root = document.getRootElement();

  37. // 得到根元素的所有子节点

  38. List<Element> elementList = root.elements();

  39. //遍历所有子节点

  40. for (Element e : elementList) {

  41. map.put(e.getName(), e.getText());

  42. }

  43. //释放资源

  44. inputStream.close();

  45. return map;

  46. }

  47.  
  48. /**

  49. * 将String类型的XML解析为Map

  50. *

  51. * @param str

  52. * @return

  53. * @throws Exception

  54. */

  55. public static Map<String, String> parseXmlStringToMap(String str) throws Exception {

  56. Map<String, String> resultMap;

  57. InputStream inputStream = new ByteArrayInputStream(str.getBytes("UTF-8"));

  58. resultMap = parseInputStreamToMap(inputStream);

  59. return resultMap;

  60. }

  61.  
  62. }

 

 

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

 

 

 
  1. <head>

  2.  
  3. <script type="text/javascript" src="http://g.alicdn.com/sj/lib/jquery/dist/jquery.min.js"></script>

  4. <script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>

  5. <script src="../js/common.js"></script>

  6. <script type="text/javascript">

  7.  
  8. function toPay(){

  9. if (typeof WeixinJSBridge == "undefined"){

  10. if( document.addEventListener ){

  11. document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);

  12. }else if (document.attachEvent){

  13. document.attachEvent('WeixinJSBridgeReady', onBridgeReady);

  14. document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);

  15. }

  16. }else{

  17. onBridgeReady();

  18. }

  19. }

  20.  
  21. function onBridgeReady(){

  22. $.ajax({

  23. type: "POST",

  24. url: DEF_GLOBAL_DOMAIN+"/wxPay/toPayInfo.do",

  25. success: function(data){

  26. console.log(data);

  27.  
  28.  
  29. WeixinJSBridge.invoke(

  30. 'getBrandWCPayRequest', {

  31. "appId" : data.data.appId,

  32. "timeStamp": data.data.timeStamp,

  33. "nonceStr" : data.data.nonceStr,

  34. "package" : data.data.package,

  35. "signType" : data.data.signType,

  36. "paySign" : data.data.paySign

  37. },function(res){

  38. $("#msgId").html(res.err_msg);

  39. if(res.err_msg == "get_brand_wcpay_request:ok"){

  40. $("#resId").html("支付成功");

  41. // location.href="weixinPayResult.html";//支付成功跳转到指定页面

  42. }else if(res.err_msg == "get_brand_wcpay_request:cancel"){

  43. $("#resId").html("支付取消");

  44. }else{

  45. $("#resId").html("支付失败");

  46. }

  47. });

  48. }

  49. });

  50.  
  51. }

  52.  
  53. </script>

  54. </head>

  55. <body>

  56.  
  57.  
  58. <div class="content">

  59. <div class="form-area">

  60. <div class="inp">

  61. 支付0.01元

  62. </div>

  63. <button class="em-submit-st2" onclick="toPay()" >

  64. 确定支付

  65. </button>

  66. </div>

  67. 结果:

  68. <p/>

  69. <div id="resId"></div>

  70. 参数:

  71. <p/>

  72. <div id="invokeId"></div>

  73. <br/><p/>

  74. 返回:

  75. <p/>

  76. <div id="msgId"></div>

  77. </div>

  78.  
  79. </body>

  80. </html>


 

 

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

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

 

整个执行流程 是 :

微信点击支付按钮——》

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

       返回支付参数——》

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

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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值