在之前记录了一下做微信公众号支付的过程,但是有些混乱,之前做的内个也不是直接接的微信官方,而是转接的别人在接的微信官方,他们赚个手续费,在这之后因为app停用了一段时间,上游公司把我们的appid给关掉了,所以打算从新接,直接接微信官方,好了这是背景。

我们做的是公众号支付,也就是在微信网页内部进行调取支付插件进行支付的一个过程

所以需要到微信官方开通公众号支付 微信官网:https://pay.weixin.qq.com

1、登录后点击产品中心, 点击公众号支付

进入后就会看到这个页面

因为我的已经开通所以就不需要了

这是官方文档 : https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_3

点击开发配置

进行配置支付授权目录:也就是你的支付页面所在的目录

一定是生产环境的,微信不支持 ip +端口 形式的地址   异步通知也不支持,

所以测试都需要线上真实环境的域名+支付页面所在目录

登录公众号平台进行配置

https://mp.weixin.qq.com

公众号的按钮在下面

其次设置你的JS接口安全域名:也就是完整域名如:www.baidu.com

配置到这里基本就算完成了

现在我们需要获取几个必须的参数

appid,mch_id ,加密key

基本配置按钮也在下面

显示appid  点击基本配置就会看到了 如:wxf8xxxxxxxxxfca

mch_id 就是你登录微信商户后台的内个号,如:1594xxxxxxxxx98

key 获取,也是在微信商户后台

这个是自己设置的,看你自己设置了,

1.        appid APPID (已有)

wxfxxxxxxxxxxxxxca

2.        mch_id 商户ID (已有)

147xxxxxxxxxxx54

3.        nonce_str 随机字符串 , 生成UUID就可以了;

 UUID.randomUUID().toString().trim().replaceAll("-", "");

4.        sign 签名 用WXPayUtil中的generateSignature(finalMap<String, String> data, String key)方法,data是将除了sign外,其他10个参数放到map中,key是四大配置参数中的API秘钥(paternerKey)(这里不要着急管它,最后处理它);

5.        body 所支付的名称

6.        out_trade_no 自己后台生成的订单号,只要保证唯一就好:如“pay2018062521331”

7.        total_fee 支付金额 单位:分,为了测试此值给1,表示支付1分钱

8.        spbill_create_ip IP地址 网上很多ip的方法,自己找,此处测试给“127.0.0.1”

9.        notify_url 回调地址:这是微信支付成功后,微信那边会带着一大堆参数(XML格式)请求这个地址多次,这个地址做我们业务处理如:修改订单状态,赠送积分等。Ps:支付还没成功还想这么远干嘛,最后再说。地址要公网可以访问。

10.    trade_type 支付类型 咱们是公众号支付此处给“JSAPI”

11.    openid 支付人的微信公众号对应的唯一标识,每个人的openid在不同的公众号是不一样的,这11个参数里,最费劲的就是他了,其他的几乎都已经解决,现在开发得到这个参数。

获得openid的部分内容应该不属于微信支付的范畴,属于微信公众号网页授权的东西,详情请参考微信网页授权:

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842

获得openid步骤:

第一步:用户同意授权,获取code

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

注意:1. redirect_uri参数:授权后重定向的回调链接地址, 请使用 urlEncode 对链接进行处理。

2. scope:用snsapi_base 。

通过此链接可以获取code,可以在一个空页面设置一个a标签,链接至其redirect_uri的地址。点击a标签,即可链接到redirect_uri的地址,并携带code。

[html] view plaincopy
  1. <a href="https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx15c*********&redirect_uri=http%3a%2f%2fwww.***.com%2fpay.jsp&response_type=code&cope=snsapi_base#wechat_redirect">去支付页面pay.jsp并携带code</a>

第二步:通过code换取网页授权access_token(其实微信支付就没有必要获取access_token了,咱们只要其中openid,不是要用户信息,此步结果已经就含有咱们需要的openid了)

获取code后,请求以下链接获取access_token: https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

上一步的code有了,对于此链接的参数就容易了。可是在页面上如何处理是个问题,我是在pay.jsp页面加载完成后将获取code当做参数传异步到后台,在后台中用http相关类发送get请求(可以自行网上查找)。返回的JSON结果为:

[html] view plaincopy
  1. { "access_token":"ACCESS_TOKEN",
  2. "expires_in":7200,
  3. "refresh_token":"REFRESH_TOKEN",
  4. "openid":"OPENID",//就是它,只要这个值
  5. "scope":"SCOPE" }

好了都有了,我们就可以开始写拼装参数了, 参数填写修改成你自己的就可以了

   HashMap<String, Object> reqMap = new HashMap<String, Object>()reqMap.put("appid", appid);reqMap.put("mch_id", mchid);reqMap.put("nonce_str", getUUid());reqMap.put("out_trade_no", "订单号");reqMap.put("total_fee", 金额);reqMap.put("body", "所支付的名称");reqMap.put("spbill_create_ip", getIp);reqMap.put("notify_url", notifyUrl);// reqMap.put("device_info", "WEB");reqMap.put("trade_type", "JSAPI");reqMap.put("openid", openId);

下面是工具类

/** 生成32位编码* @return string*/public static String getUUid() {String uuid = UUID.randomUUID().toString().trim().replaceAll("-", "");return uuid;}
/*** 获取IP* @param request* @return*/public String getRemortIP(HttpServletRequest request) {String remoteAddr = request.getRemoteAddr();String forwarded = request.getHeader("X-Forwarded-For");String realIp = request.getHeader("X-Real-IP");String ip = null;if (realIp == null) {if (forwarded == null) {ip = remoteAddr;} else {ip = remoteAddr + "/" + forwarded.split(",")[0];}} else {if (realIp.equals(forwarded)) {ip = realIp;} else {if (forwarded != null) {forwarded = forwarded.split(",")[0];}ip = realIp + "/" + forwarded;}}return ip}

下面进行签名,MD5工具类会贴出了的

 def md5str = createLinkString(reqMap);//排序System.out.println("---> md5str:" + md5str + "&key=" + wxapiKey);def sign = MD5Tool.md5(md5str + "&key=" + apiKey, charset).toUpperCase();签名并转大写reqMap.put("sign", sign)
/***  SignStr 待签名字符串* @param params* @return*/public static String createLinkString(Map<String, Object> map) {System.out.println("带签名排序params:::::" + map.toString())List<String> keys = new ArrayList<String>(map.keySet());Collections.sort(keys);System.out.println("排序完Keys:::::" + keys.toString())def sb = new StringBuffer()for (int i = 0; i < keys.size(); i++) {String key = keys.get(i);Object value = map.get(key)System.out.println("key::" + key + "::value::" + value)if ("sign".equals(key) || "sign_type".equals(key) || value == null || value == "") {continue;}if (i == keys.size() - 1) {sb.append(key).append("=").append(value)} else {sb.append(key).append("=").append(value).append("&")}}def sbStr = sb.toString()System.out.println("===========签名串:" + sbStr)return sbStr;}
package eims;import org.apache.commons.codec.digest.DigestUtils;import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
//MD5工具类
public class MD5Tool {public static String md5(byte[] data) {MessageDigest md5 = null;try {md5 = MessageDigest.getInstance("MD5");} catch (NoSuchAlgorithmException e) {e.printStackTrace();throw new IllegalStateException("System doesn't support MD5 algorithm.");}md5.update(data);byte[] encoded = md5.digest();StringBuffer buf = new StringBuffer();for (int i = 0; i < encoded.length; i++) {if ((encoded[i] & 0xff) < 0x10) {buf.append("0");}buf.append(Long.toString(encoded[i] & 0xff, 16));}return buf.toString();}/*** 签名字符** @param text*            要签名的字符* @param key*            密钥*            编码格式* @return 签名结果*/public static String sign(String text, String key, String charset) throws Exception {text = text + key;return DigestUtils.md5Hex(getContentBytes(text, charset));}/*** @param content* @param charset* @return* @throws java.io.UnsupportedEncodingException*/private static byte[] getContentBytes(String content, String charset) {if (charset == null || "".equals(charset)) {return content.getBytes();}try {return content.getBytes(charset);} catch (UnsupportedEncodingException e) {throw new RuntimeException("签名过程中出现错,指定的编码集不对,您目前指定的编码集是:" + charset);}}public static String md5(String str, String charset) {if (str == null) return null;try {return md5( str.getBytes(charset) );} catch (UnsupportedEncodingException e) {e.printStackTrace();throw new IllegalStateException("System doesn't support Charset '" + charset + "'");}}
}

到这里所有的参数都拼装好了,微信要的是XML 格式的所以我们需要进行转换

def requestXML = mapGetXML(reqMap);
System.out.println("req xml :\n" + requestXML);
String response = post(wxUrl, requestXML);
/*** map 转 XML* @param map* @return*/public static String mapGetXML(Map<String, String> map) {String xmlStr = null;StringBuffer sbf = new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");sbf.append("<xml>");for (Map.Entry<String, String> s : map.entrySet()) {sbf.append("<").append(s.getKey()).append(">").append(s.getValue()).append("</").append(s.getKey()).append(">");}sbf.append("</xml>");xmlStr = sbf.toString();return xmlStr;}

XML也转换好了,我们现在可以直接发送请求了 格式xml

请求地址  :https://api.mch.weixin.qq.com/pay/unifiedorder

 String response = post(wxUrl, requestXML);

post方法

/*** 发送xml数据请求到server端** @param url*            xml请求数据地址* @param xmlString*            发送的xml数据流* @return null发送失败,否则返回响应内容*/public static String post(String url, String xmlFileName) {// 关闭System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog");System.setProperty("org.apache.commons.logging.simplelog.showdatetime", "true");System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.commons.httpclient", "stdout");// 创建httpclient工具对象HttpClient client = new HttpClient();// 创建post请求方法PostMethod myPost = new PostMethod(url);// 设置请求超时时间client.setConnectionTimeout(300 * 1000);String responseString = null;try {// 设置请求头部类型myPost.setRequestHeader("Content-Type", "text/xml");myPost.setRequestHeader("charset", "utf-8");// 设置请求体,即xml文本内容,注:这里写了两种方式,一种是直接获取xml内容字符串,一种是读取xml文件以流的形式// myPost.setRequestBody(xmlString);// InputStream// body=this.getClass().getResourceAsStream("/"+xmlFileName);// myPost.setRequestBody(body);myPost.setRequestEntity(new StringRequestEntity(xmlFileName, "text/xml", "utf-8"));int statusCode = client.executeMethod(myPost);if (statusCode == HttpStatus.SC_OK) {BufferedInputStream bis = new BufferedInputStream(myPost.getResponseBodyAsStream());byte[] bytes = new byte[1024];ByteArrayOutputStream bos = new ByteArrayOutputStream();int count = 0;while ((count = bis.read(bytes)) != -1) {bos.write(bytes, 0, count);}byte[] strByte = bos.toByteArray();responseString = new String(strByte, 0, strByte.length, "utf-8");bos.close();bis.close();}} catch (Exception e) {e.printStackTrace();}myPost.releaseConnection();return responseString;}

搞了这么多终于看到点结果了

请求过后 微信端返回的也是XML 不利于我们处理,所以继续转map
  Map<String, Object> respMap = xmlString2Map(response);

xml 转map

/*** Xml string转换成Map* @param xmlStr* @return*/public static Map<String, Object> xmlString2Map(String xmlStr) {Map<String, Object> map = new HashMap<String, Object>();Document doc;try {doc = DocumentHelper.parseText(xmlStr);Element el = doc.getRootElement();map = recGetXmlElementValue(el, map);} catch (DocumentException e) {e.printStackTrace();}return map;}

转好map 后我们就开始取 prepay_id

搞了这么久就是为了丫的, 取出来后我们还需要把参数拼装一遍,扔到页面,掉起 JS 插件进行支付

参数:

appid 也有

timeStamp 时间戳 你们new Date();即可,因为我语言是 Groovy 所以需要getTime 才是秒数

package   prepay_id   已有了

nonceStr    随机数  getuuid方法就可以了

signType     固定值  MD5

sign           上面5个参数的签名结果

这里值得注意的是package 参数, 这个参数可不是简单的吧prepay_id 放进去

,要把 “prepay_id=”这个拼接上里面不能有多余的"或者'符号

之前没有拼接 ,微信支付的时候返回 缺少total_fee参数, 可是上一步给微信传的时候并没有少,微信返回的都成功了
所以还是抛页面的时候出现的错误,害我整了好久。

 String retCode = respMap.get("result_code");if ("SUCCESS".equals(retCode)) {String prepay_id = respMap.get("prepay_id");Map map = new HashMap();map.put("appId", wxappid);map.put("timeStamp", new Date().getTime());map.put("package", "prepay_id=" + prepay_id);map.put("nonceStr", getUUid());map.put("signType", "MD5");def newmd5str = createLinkString(map);System.out.println("ShoppingCartService---> md5str:" + newmd5str + "&key=" + wxapiKey);def newSign = MD5Tool.md5(newmd5str + "&key=" + wxapiKey, charset).toUpperCase();map.put("paySign", newSign);}

参数装完后直接传到页面,看自己框架了,我就不贴了

下面直接把页面贴出了 值得注意的是

package 这个参数 ,在页面是一个域 ,所以在后台传的时候重新改个名 我改的是paypackage

<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head><title>微信支付</title><meta http-equiv="content-type" content="text/html;charset=utf-8"/>
</head>
<script>function onBridgeReady() {<%System.out.println("incoming...");System.out.println(request.getParameter("appId"));System.out.println(request.getParameter("timeStamp"));System.out.println(request.getParameter("nonceStr"));System.out.println(request.getParameter("paypackage"));System.out.println(request.getParameter("signType"));System.out.println(request.getParameter("paySign"));System.out.println('deviceType:::'+request.getParameter("deviceType"));System.out.println("incoming...");%>WeixinJSBridge.invoke('getBrandWCPayRequest', {"appId": "<%= request.getParameter("appId") %>",     //公众号名称,由商户传入"timeStamp": "<%= request.getParameter("timeStamp") %>",         //时间戳,自1970年以来的秒数"nonceStr": "<%= request.getParameter("nonceStr") %>", //随机串"package": "<%= request.getParameter("paypackage") %>","signType": "<%= request.getParameter("signType") %>",         //微信签名方式:"paySign": "<%= request.getParameter("paySign") %>" //微信签名,paySign 采用统一的微信支付 Sign 签名生成方法,注意这里 appId 也要参与签名,appId 与 config 中传入的 appId 一致,即最后参与签名的参数有appId, timeStamp, nonceStr, package, signType。},function (res) {if (res.err_msg == "get_brand_wcpay_request:ok") {     // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。alert('支付成功!');} else if (res.err_msg == "get_brand_wcpay_request:cancel") {alert('已取消微信支付!');} else {alert('支付失败!' + res.err_msg)}/*WeixinJSBridge.call('closeWindow');*/  //关闭微信端窗口});}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();}
</script>
</html>

到这里就完成了,微信支付只能去生产上面测试,这是比较操蛋的
如果按照以上的步骤一步步走的话不会出现问题的,如有问题请留言,看到会及时答复(有人看吗,哈哈哈哈哈够呛!!)

微信公众号支付的那些坑相关推荐

  1. 使用beecloud和easychat 做微信H5 微信公众号支付遇到的坑

    首先我先来说明微信h5和微信公众号支付的区别 微信H5指的是微信网页端支付.微信公众号支付指的是微信浏览器内发起微信支付,这种支付都是属于微信公众号支付.俩者本质的区别就是H5不需要微信网页授权,但是 ...

  2. 对接银联商务微信公众号支付遇到的坑

    微信公众号支付和微信扫码支付在 支付结果通知上有很大的区别,公众号支付主要是以异步通知去确定支付结果,也可以通过查询接口,而扫码支付通常调用查询接口去查询支付状态.因而,公众号支付的异步通知是一个需要 ...

  3. php微信公众号支付vue,vue项目使用微信公众号支付总结及遇到的坑

    微信公众号支付 1. 使用jssdk调用微信支付,具体查看开发文档: 使用的vuex,在mutations中 wechatPay (state, data) { state.payObject = d ...

  4. php公众号支付后的微信通知,关于微信公众号支付细数我踩过的坑

    微信公众号支付流程 项目背景: 1.前后端分离,前端是jquery 获取后端接口 (php) 开发流程: 1.配置 进入微信公众平台,申请开通微信支付,不开通就别进行下一步了.开通后,进入左侧的微信支 ...

  5. 微信公众号支付的坑,我已经帮你踩过了(JAVA)

    简单介绍微信公众号支付的申请.接入.使用.支付等相关流程,具体会调到微信的授权登录.支付以及关单等api,里面会着重提到需要注意的坑. 1. 准备阶段: 申请 申请步骤请直接查看微信公众平台官方文档 ...

  6. prepay id为空php,微信公众号支付踩坑笔记

    微信公众号支付,简单说主要分为如下几个步骤. 1.openId的获取 openId是微信用户与特定公众号对应关系的记录. 1.1设置回调域名 官方解释:用户在网页授权页同意授权给公众号后,微信会将授权 ...

  7. vue 微信公众号支付接口_基于vue的h5项目之支付宝支付与微信支付

    本文仅记录基于vue开发h5项目过程中使用支付宝和微信支付过程中的重点与槽点,仅为前端部分,如有疏漏不正之处,请于文末评论探讨.注意:标红部分灰常重要,仔细阅读官方文档非常重要,耐心非常重要,细心非常 ...

  8. 微信支付整理 (微信公众号支付)

    由于本人愚钝,在最初接触微信支付时遇到很多坑.在第一次将微信支付调试成功之后在网上炫耀般的留了一个qq(24xxxx)号,以便帮助到更多的人.由此便接触到了大概100多号人的qq询问,期间也拒绝了很多 ...

  9. 微信公众号支付开发手记(node)

    微信支付 前言 总结一下最近业务开发中对微信公众号支付的开发过程,微信支付的开发前提是已经具备可上线微信公众号开发的基础上进行的,如果你的开发阶段目前停留在起步,建议参考这篇文章开始. 好了,来聊一聊 ...

最新文章

  1. python array函数_Python 中的range()函数与array()函数
  2. Apache服务器下载、安装、启动、关闭及卸载(win版)
  3. scandall pro找不到扫描仪_想要高清无色差的石材图片就得用扫描仪
  4. 在全员编程时代下,软件测试员又该何去何从?
  5. [LeetCode] Intersection of Two Arrays 两个数组相交
  6. Cesium专栏-裁剪效果(基于3dtiles模型,附源码下载)
  7. 2012年8月 发散的安全焦点
  8. React使用iconfont阿里巴巴矢量图库
  9. ssh服务端配置了authorized_keys,免密登录失败
  10. 自然语言处理(1)——绪论与概述
  11. php企业公司员工考勤加班系统
  12. 推荐系统中特征交叉模型之——DeepWide/DeepFM/NFM
  13. Conflict with dependency
  14. 三层架构与Mvc设计模式
  15. USB 调试开关设置项
  16. 新浪微博客户端的开发
  17. 采样电阻转化电流为电压 高低端采样的问题
  18. 根据navigator.userAgent用户代理信息IE浏览器版本以及移动端设备
  19. linux 板子修改ip地址
  20. python输出学生信息_python3.6实现学生信息管理系统

热门文章

  1. Abaqus子程序HETVAL模拟混凝土水化热温度场
  2. GNU Radio及软件无线电USRP N210安装说明
  3. 移动硬盘提示需要格式化
  4. 打开网页发生了什么?
  5. 开源文字游戏代码_文字游戏和开源的简要历史
  6. Mac识别time machine出错或者打开备份盘里空白
  7. CMU-15112课程笔记
  8. lanm中cdn之varnish服务的搭建
  9. 全景视频拼接(二):双摄像头获取视频
  10. NOr flash onenand