背景:最近做项目,甲方提出一个需要要求在手机端直接微信注册成功后,直接登录并发起微信支付。再三思考后,才决定使用jsapi微信支付。
微信支付官方文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1
首先微信普通商户版有NATIVE支付、JSAPI支付、APP支付、H5支付、付款码支付、小程序支付;其中我认为作为web开发的最常用的是native、jsapi、h5支付了。但是jsapi支付在官方微信支付文档中,留下了很多坑,导致在使用过程中无比麻烦,下面具体来说说,希望能对各位看客有点帮助。
1.微信支付最麻烦的便是公众平台和商户平台的配置,将公众号APPID、APPSECRET,商户号appid、秘钥key配置好。
商户平台:(https://pay.weixin.qq.com/)
1)微信商户平台->产品中心->我的产品,查看你要的支付类型是否开通,未开通,则申请开通,按照开通流程走,主要是申请安装证书即可。

2)微信商户平台->产品中心->开发配置,查看支付配置是否已经配置成功。native支付则配置扫码支付回调地址,jsapi支付则配置公众号支付授权目录,该目录最多可添加5个,如发起支付页面为:http://baidu.com/index.html,则目录配置为:http://baidu.com/;如发起支付页面为:http://baidu.com/wxpay/index.html,则目录配置为:http://baidu.com/wxpay/;(这一步很关键,不然会在发起支付时提示注册url无效)
3)微信商户平台->账户中心->账户设置->API安全,查看是否已经设置了商户密钥key,此密钥在后面生成签名sign中特别重要,必须设置,设置成功后记住密钥;
以上是商户平台的配置;
微信公众平台:(https://mp.weixin.qq.com/)
1)微信公众平台->开发->基本配置,查看开发者密码AppSecret是否已经设置;
2)微信公众平台->设置->公众号设置,查看网页回调地址是否已经配置好,在这里我将所有的域名配置都配置好了。需要将MP_verify_MHYOHtHKmJzSkCj0.txt文件放置到项目的根目录下,只要访问域名后可以访问到就可以,如配置域名:baidu.com,则访问http://baidu.com/MP_verify_MHYOHtHKmJzSkCj0.txt时访问得到就表示配置成功。
以上是公众平台需要的配置。
2.配置完后,查看商户平台的jsapi微信支付开发文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1,在微信浏览器内发起支付之前,必须先获得预支付订单号prepay_id,也就是这个订单号,需要大费周折的去拼接各种数据;
1)由于获取prepay_id的必填参数中有openid,所以先获取openid,通过公众平台网页授权来获取。
微信公众平台->开发->接口权限->网页服务->网页授权(网页授权获取用户基本信息 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842)
第一步:用户同意授权,获取code:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
参数说明:APPID:微信公众平台AppId;
REDIRECT_URI:回调地址,在配置时需要配置域名,此处的回调地址域名必须与配置的一致否则无效。
SCOPE:默认拥有scope参数中的snsapi_base和snsapi_userinfo,此处是jsapi支付,必须使用snsapi_userinfo,否则会出现“此公众号并没有这些scope的权限,错误码:10005”的提示;
STATE:随机参数,可以用来区分或者携带其他参数到回调地址中;
返回值:code作为换取access_token的临时票据和state随机参数。
第二步:通过code换取网页授权access_token:
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
参数说明:APPID:微信公众平台AppId;
SECRET:微信公众号密钥,需要配置;
CODE:第一步获得的code参数;
返回值:access_token和openid,此处只写明这两个较为重要的参数。
到目前为止便得到了openid。可以进行统一下单了。
2).查看API列表->统一下单(调用该接口是为了在微信支付服务后台生成预支付交易单)
接口链接:https://api.mch.weixin.qq.com/pay/unifiedorder
请求参数:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1 查看官方文档,此处重点点名:openid,此次为JSAPI,所以必填;sign签名是整个请求参数中最难生成的。
当返回结果return_code 和result_code都为SUCCESS,可以获得prepay_id.
3)得到预订单号后,就可以在微信内置浏览器中发起支付。将统一下单接口返回的数据中拿到appid、nonce_str、prepay_id,并结合signType、timeStamp、key(商户号)通过MD5加密返回paySign支付签名,将这些数据一并返回前端页面调用微信内置对象WeixinJSBridge,发起支付。(微信内h5调起支付链接:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6 )
整个支付就到此结束了。
在这个过程中,我遇到的最惨痛的难点就是支付签名paySign的生成,因为在微信统一下单接口返回的数据中还包含了sign签名的返回,我一直以为可以使用这个作为支付签名。然而在网上找了很多大佬的总结后,都说统一下单接口返回的sign的签名加密方式不是MD5,然而支付签名必须是MD5加密而造成的。但是我觉得不是,因为我已经明确在下单接口中的参数signType写明就是MD5了,所以表示下单接口返回的sign并不能作为微信发起支付的签名paySign。必须自己重新根据返回的prepay_id生成新的支付签名。
代码的实现方式如下:
(后端为java,前端使用vue)
- controller层的代码实现:
/**
* 第三方发起微信授权登录请求,微信用户允许授权第三方应用后,
* 微信会拉起应用或重定向到第三方网站,并且带上授权临时票据code参数
* appid: 微信公众号唯一标识id
* redirect_uri: 回调地址,此处回调地址为/WXRegisterPay
* 该接口返回微信请求url
*
* @title 微信授权登录请求临时票据code
* @resp code_url第三方请求临时票据code的url|String|必填
* @return 第三方请求临时票据code的地址
* @Author chenxm
*/
@RequestMapping(value = "getWXCode",method = RequestMethod.GET)
@OpLog(value = "获取code",module = "微信支付")
@TokenIgnore
BaseResult getWXCode(){
Map<String,Object> map = new HashMap<String,Object>();
// 公众平台
String url="https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + PropertyUtil.WX_MP_APP_ID +
"&redirect_uri=" + URL.encode(PropertyUtil.WX_REGISTERPAY) +
"&response_type=code" +
"&scope=snsapi_userinfo" +
"&state=" + MREGISTER +
"#wechat_redirect";
map.put("code_url",url);
return BaseResult.succResult(map);
}
/**
* 获取code后,微信平台发起的回调接口
* @return
*/
@RequestMapping(value = "WXRegisterPay", method = RequestMethod.GET)
@OpLog(value = "手机微信注册支付", module = "Vip模块")
BaseResult WXRegisterPay(@LoginUser Member member, String code, String state, HttpServletRequest request){
// 微信支付参数
Map<String,Object> pay = new HashMap<>();
// 支付签名
StringBuilder paySign = new StringBuilder();
WebChatDto webChatAccess = weixinPayService.getAccessToken(code, 2);
//获取当前的ip地址
String ip = WxPayToolUtil.getRemoteHost(request);
try {
Map payMap = weixinPayService.weixinRegPay(member.getId(),ip,webChatAccess.getOpenId());
// 拼接微信支付参数
pay.put("appId",payMap.get("appid"));
pay.put("timeStamp",Long.toString(System.currentTimeMillis() / 1000));
pay.put("nonceStr",payMap.get("nonce_str"));
pay.put("signType",PropertyUtil.WX_SIGN_TYPE);
pay.put("package","prepay_id="+payMap.get("prepay_id"));
// 生成支付签名
paySign.append("appId=").append(pay.get("appId"));
paySign.append("&nonceStr=").append(pay.get("nonceStr"));
paySign.append("&package=").append(pay.get("package"));
paySign.append("&signType=").append(pay.get("signType"));
paySign.append("&timeStamp=").append(pay.get("timeStamp"));
paySign.append("&key=").append(PropertyUtil.WX_API_KEY);
pay.put("paySign", MD5Util.MD5Encode(paySign.toString(), "UTF-8").toUpperCase());
logger.info("URL,{}",payMap);
logger.info("paySign,{}",pay.get("paySign"));
} catch (Exception e) {
logger.info("URL,{}",e.getMessage());
}
return BaseResult.succResult(pay);
}
/**
* 发起支付后,微信平台发起的回调方法
*
* @title 微信回调方法
*/
@RequestMapping("weixinNotify")
@OpLog(value = "微信平台发起的回调方法", module = MODUEL)
@TokenIgnore
void weixinNotify(HttpServletRequest request, HttpServletResponse response) throws JDOMException, Exception {
//获取输入流
InputStream inputStream = request.getInputStream();
StringBuffer sb = new StringBuffer();
String s = null;
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
while ((s = in.readLine()) != null) {
sb.append(s);
}
in.close();
inputStream.close();
//解析xml成map
Map<String, String> m = new HashMap<>();
m = XMLUtil4jdom.doXMLParse(sb.toString());
//过滤空 设置 TreeMap
SortedMap<Object, Object> 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);
}
//判断签名是否正确
if (WxPayToolUtil.isTenpaySign("UTF-8", packageParams, PropertyUtil.WX_API_KEY)) {
String resXml = "";
if ("SUCCESS".equals((String) packageParams.get("result_code"))) {
//商户订单号
String out_trade_no = (String) packageParams.get("out_trade_no");
//微信订单号
String transaction_id = (String) packageParams.get("transaction_id");
//现金支付金额
float price = Float.valueOf((String) packageParams.get("cash_fee"));
//获取微信查询订单返回的数据
String str = weixinPayService.weixinOrderquery(transaction_id);
logger.info(str);
//通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
logger.info("支付成功!");
} else {
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
logger.info("支付失败!");
}
BufferedOutputStream out = null;
try {
out = new BufferedOutputStream(response.getOutputStream());
out.write(resXml.getBytes());
}catch (Exception e){
logger.info("微信平台发起的回调方法,{}", e.getMessage());
}finally {
if (null != out){
out.flush();
out.close();
}
}
} else {
logger.info("通知签名验证失败");
}
}
-
service层的代码实现:
(1)接口:
/**
* 请求获取access_token
* @param code
* @return
*/
WebChatDto getAccessToken( String code, Integer type);
/**
* 微信jsapi支付
* @return
* @throws Exception
*/
Map weixinRegPay(String userId,String ip, String openid) throws Exception;
/**
* 微信查询订单
* @param transaction_id 微信订单号
* @return
* @throws Exception
*/
String weixinOrderquery(String transaction_id) throws Exception;
(2)实现类:
/*获取access_token的url*/
public static final String AccessToken_url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code&type";
@Override
public WebChatDto getAccessToken(String code, Integer type) {
// 请求获取access_token
// 开放平台
String url = String.format(AccessToken_url,PropertyUtil.WX_OPEN_APPID,PropertyUtil.WX_OPEN_APPSECRET,code);
if(type == 2){
// 公众平台
url = String.format(AccessToken_url,PropertyUtil.WX_MP_APP_ID,PropertyUtil.WX_MP_APPSECRET,code);
}
// 获取请求接口返回的信息
JSONObject jsonObject = null;
try {
jsonObject = WeChatUtil.doGetJson(url);
} catch (IOException e) {
LOGGER.info("获取TOKEN,{}",e.getMessage());
}
// 微信用户唯一标识id
String openId = jsonObject.getString("openid");
// 许可token
String accessToken = jsonObject.getString("access_token");
WebChatDto webChat = new WebChatDto();
if(EmptyUtils.isNotEmpty(openId) && EmptyUtils.isNotEmpty(accessToken)){
webChat.setAccessToken(accessToken);
webChat.setOpenId(openId);
}else{
throw new ValidateException("微信授权登录失败");
}
return webChat;
}
@Override
public Map weixinRegPay(String userId,String spbill_create_ip, String openid) throws Exception {
//有序的Map
SortedMap<Object, Object> params = WxPayToolUtil.getPayParams(userId, openid, spbill_create_ip, PropertyUtil.WX_trade_type_JSAPI);
//格式化成XML形式,微信通信是借助XML
String requestXML = WxPayToolUtil.getRequestXml(params);
//得到微信返回的数据
String resXml = HttpUtil.postData(PropertyUtil.WX_UFDODER_URL, requestXML);
Map map = XMLUtil4jdom.doXMLParse(resXml);
return map;
}
@Override
public String weixinOrderquery(String transaction_id) throws Exception {
//有序的Map
SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
//公众账号ID
packageParams.put("appid", PropertyUtil.WX_MP_APP_ID);
//商户号
packageParams.put("mch_id", PropertyUtil.WX_MCH_ID);
//微信订单号
packageParams.put("transaction_id", transaction_id);
//获取当前日期和时间
String currTime = WxPayToolUtil.getCurrTime();
//获得当前时间
String strTime = currTime.substring(8, currTime.length());
//取出一个指定长度大小的随机正整数.
String strRandom = WxPayToolUtil.buildRandom(4) + "";
//随机字符串,长度要求在32位以内。
String nonce_str = strTime + strRandom;
//随机字符串
packageParams.put("nonce_str", nonce_str);
//签名类型
packageParams.put("sign_type", PropertyUtil.WX_SIGN_TYPE);
//生成签名
String sign = WxPayToolUtil.createSign("UTF-8", packageParams, PropertyUtil.WX_API_KEY);
packageParams.put("sign", sign);
//格式化成XML形式,微信通信是借助XML
String requestXML = WxPayToolUtil.getRequestXml(packageParams);
//得到微信返回的数据
String resXml = HttpUtil.postData(PropertyUtil.WX_ORDERQUERY_URL, requestXML);
return resXml;
}
- 辅助工具类utils的代码实现:
/**
* @desc 微信支付相关的工具类
**/
public class WxPayToolUtil implements Serializable {
private static final Logger logger = LoggerFactory.getLogger(WxPayToolUtil.class);
/**
* 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
* @return boolean
*/
public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
StringBuffer sb = new StringBuffer();
Set es = packageParams.entrySet();
Iterator it = es.iterator();
while(it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
String k = (String)entry.getKey();
String v = (String)entry.getValue();
if(!"sign".equals(k) && null != v && !"".equals(v)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + API_KEY);
//算出摘要
String mysign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase();
String tenpaySign = ((String)packageParams.get("sign")).toLowerCase();
return tenpaySign.equals(mysign);
}
/**
* @Description:sign签名
* @param characterEncoding 编码格式
* @return
*/
public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
StringBuffer sb = new StringBuffer();
Set es = packageParams.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
String v = (String) entry.getValue();
if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + API_KEY);
logger.info(sb.toString());
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
}
/**
* @Description:将请求参数转换为xml格式的string
* @param parameters 请求参数
* @return
*/
public static String getRequestXml(SortedMap<Object, Object> parameters) {
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set es = parameters.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
String v = (String) entry.getValue();
if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
} else {
sb.append("<" + k + ">" + v + "</" + k + ">");
}
}
sb.append("</xml>");
return sb.toString();
}
/**
* 取出一个指定长度大小的随机正整数.
*
* @param length int 设定所取出随机数的长度。length小于11
* @return int 返回生成的随机数。
*/
public static int buildRandom(int length) {
int num = 1;
double random = Math.random();
if (random < 0.1) {
random = random + 0.1;
}
for (int i = 0; i < length; i++) {
num = num * 10;
}
return (int) ((random * num));
}
/**
* 获取当前时间 yyyyMMddHHmmss
*
* @return String
*/
public static String getCurrTime() {
Date now = new Date();
SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
String s = outFormat.format(now);
return s;
}
/**
* 当前时间多加多少毫米 yyyyMMddHHmmss
*
* @return String
*/
public static String getAddCurrTime(Integer s) {
Date now = new Date(System.currentTimeMillis() + s);
SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
String str = outFormat.format(now);
return str;
}
/**
* 获取当前ip
* @param request
* @return
*/
public static String getRemoteHost(HttpServletRequest request){
String ip = request.getHeader("x-forwarded-for");
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
ip = request.getHeader("Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
ip = request.getHeader("WL-Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
ip = request.getRemoteAddr();
}
return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip;
}
/**
* 获取微信支付参数
*/
public static SortedMap<Object,Object> getPayParams(String userId,String openid, String spbill_create_ip,String trade_type){
//获取当前日期和时间
String currTime = getCurrTime();
//获得当前时间
String strTime = currTime.substring(8, currTime.length());
//取出一个指定长度大小的随机正整数.
String strRandom = buildRandom(4) + "";
//随机字符串,长度要求在32位以内。
String nonce_str = strTime + strRandom;
//有序的Map
SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
//公众账号ID
packageParams.put("appid", PropertyUtil.WX_MP_APP_ID);
//商户号
packageParams.put("mch_id", PropertyUtil.WX_MCH_ID);
//用户标识
packageParams.put("openid", openid);
//随机字符串
packageParams.put("nonce_str", nonce_str);
//商品描述
packageParams.put("body", "VIP会员" );
//价格 单位为分
packageParams.put("total_fee",“1”);
//终端IP
packageParams.put("spbill_create_ip", spbill_create_ip);
//标价币种 CNY:人民币
packageParams.put("fee_type", "CNY");
//通知地址(回调地址)此处为/weixinNotify
packageParams.put("notify_url", PropertyUtil.WX_NOTIFY_URL);
//交易类型 JSAPI支付、NATIVE支付、APP支付
packageParams.put("trade_type", trade_type);
//签名类型
packageParams.put("sign_type", PropertyUtil.WX_SIGN_TYPE);
//交易结束时间 time_expire (格式为yyyyMMddHHmmss,最短失效时间间隔必须大于5分钟)
packageParams.put("time_expire", getAddCurrTime(120000));
//附加信息
packageParams.put("attach", userId);
//生成签名
String sign = createSign("UTF-8", packageParams, PropertyUtil.WX_API_KEY);
packageParams.put("sign", sign);
return packageParams;
}
}
MD5加密,微信支付的加密方式还有HMAC-SHA256;
/**
* MD5加密工具类
**/
public class MD5Util implements Serializable {
private static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++) {
resultSb.append(byteToHexString(b[i]));
}
return resultSb.toString();
}
private static String byteToHexString(byte b) {
int n = b;
if (n < 0) {
n += 256;
}
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
public static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if (charsetname == null || "".equals(charsetname)) {
resultString = byteArrayToHexString(md.digest(resultString
.getBytes()));
}
else {
resultString = byteArrayToHexString(md.digest(resultString
.getBytes(charsetname)));
}
} catch (Exception exception) {
}
return resultString;
}
private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
}
http请求工具;
/**
* 微信http工具类
*/
public class WeChatUtil {
public static JSONObject doGetJson(String url) throws IOException {
JSONObject jsonObject = null;
DefaultHttpClient defaultHttpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
HttpResponse httpResponse = defaultHttpClient.execute(httpGet);
HttpEntity httpEntity = httpResponse.getEntity();
if(httpEntity != null){
String result = EntityUtils.toString(httpEntity,"UTF-8");
jsonObject = JSONObject.parseObject(result);
}
httpGet.releaseConnection();
return jsonObject;
}
}
/**
* http工具类,负责发起post请求并获取的返回
*/
public class HttpUtil implements Serializable{
//url连接时间
private final static int CONNECT_TIMEOUT = 5000;
//默认编码
private final static String DEFAULT_ENCODING = "UTF-8";
/**
* 发起post请求微信支付并获取的返回
*
* @param urlStr 微信统一下单地址
* @param data 微信支付所提供的信息
* @return 返回微信返回的数据
*/
public static String postData(String urlStr, String data){
BufferedReader reader = null;
try {
URL url = new URL(urlStr);
//打开连接
URLConnection conn = url.openConnection();
//URL 连接可用于输入和/或输出。如果打算使用 URL 连接进行输入,则将 DoInput 标志设置为 true;
// 如果不打算使用,则设置为 false。默认值为 true。
conn.setDoOutput(true);
//设置连接时间
conn.setConnectTimeout(CONNECT_TIMEOUT);
//设置读取时间
conn.setReadTimeout(CONNECT_TIMEOUT);
OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING);
if(data == null) {
data = "";
}
writer.write(data);
writer.flush();
writer.close();
reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line);
sb.append("\r\n");
}
return sb.toString();
} catch (IOException e) {
e.getMessage();
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
}
}
return null;
}
}
解析xml;
public class XMLUtil4jdom implements Serializable{
/**
* 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
* @param strxml
* @return
* @throws JDOMException
* @throws IOException
*/
public static Map doXMLParse(String strxml) throws JDOMException, IOException {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
if(null == strxml || "".equals(strxml)) {
return null;
}
Map<String, String> m = new HashMap<String, String>();
InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
Element root = doc.getRootElement();
List list = root.getChildren();
Iterator it = list.iterator();
while(it.hasNext()) {
Element e = (Element) it.next();
String k = e.getName();
String v = "";
List children = e.getChildren();
if(children.isEmpty()) {
v = e.getTextNormalize();
} else {
v = XMLUtil4jdom.getChildrenText(children);
}
m.put(k, v);
}
//关闭流
in.close();
return m;
}
/**
* 获取子结点的xml
* @param children
* @return String
*/
public static String getChildrenText(List children) {
StringBuffer sb = new StringBuffer();
if(!children.isEmpty()) {
Iterator it = children.iterator();
while(it.hasNext()) {
Element e = (Element) it.next();
String name = e.getName();
String value = e.getTextNormalize();
List list = e.getChildren();
sb.append("<" + name + ">");
if(!list.isEmpty()) {
sb.append(XMLUtil4jdom.getChildrenText(list));
}
sb.append(value);
sb.append("</" + name + ">");
}
}
return sb.toString();
}
}
-
前端vue代码实现:
mtranster.html:从注册页面注册登录成功后跳转过来,判断是否有code(因为是获取code的回调地址),没有便去请求,有则直接发起支付;
<!DOCTYPE html>
<html >
<head>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="//unpkg.com/[email protected]/lib/theme-chalk/index.css" target="_blank" rel="external nofollow" />
<link rel="icon" href="static/images/common/icon.gif" target="_blank" rel="external nofollow" type="image/x-icon">
<title>微信支付</title>
</head>
<body style="zoom: 200%;">
<div id="common">
<div v-if="paying">
<h1>正在进行微信支付...</h1>
<small>请不要关闭</small>
</div>
<div v-if="!paying">
<h1>{{payStr}}</h1>
</div>
</div>
<script type="text/javascript" src="static/js/polyfill.min.js" charset="utf-8"></script>
<script type="text/javascript" src="static/js/vue.js"></script>
<script type="text/javascript" src="static/js/element-ui.js"></script>
<script type="text/javascript" src="static/js/jquery-2.min.js" charset="utf-8"></script>
<script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js" charset="utf-8"></script>
<script type="text/javascript" src="static/js/time.js"></script>
<script type="text/javascript" src="static/js/cookie.js"></script>
<script type="text/javascript" src="static/js/Api.js"></script>
<script>
var vm = new Vue({
el : '#common',
data :function() {
return{
codeUrl: "",
webChat : {
code : "",
state : ""
},
payurl:"",
loading:"",
paying:true,// 支付中
payStr:"" // 支付结束
}
},
methods : {
WXRegisterPay : function(code, state) {
var self = this;
self.loading = this.$loading({
lock: true,
text: loadingText,
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
APIS.get(APIS.API_WXRegisterPay, {
"code" : code,
"state" : state
}).then(function(res) {
var pay = res.data.data;
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener){
document.addEventListener('WeixinJSBridgeReady', self.onBridgeReady(pay), false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', self.onBridgeReady(pay));
document.attachEvent('onWeixinJSBridgeReady', self.onBridgeReady(pay));
}
}else{
self.onBridgeReady(pay);
}
self.onBridgeReady(pay);
})
},
onBridgeReady:function (pay){
var self = this;
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":pay.appId, //公众号名称,由商户传入
"timeStamp":pay.timeStamp, //时间戳
"nonceStr":pay.nonceStr, //随机串
"package":pay.package,
"signType":pay.signType, //微信签名方式
"paySign":pay.paySign //微信签名
},function(res){
self.paying = false;
self.loading.close();
if(res.err_msg == "get_brand_wcpay_request:ok" ){
self.payStr = "支付成功!请到PC端查看详情。";
self.$confirm('支付成功!请到PC端查看详情。', '提示', {
confirmButtonText: '确定',
showClose:false,
showCancelButton:false,
closeOnClickModal:false,
type: 'success'
}).then(function(){
location.href = "payNotify.html?pay=ok";
})
}else{
if(res.err_msg == "get_brand_wcpay_request:fail"){
self.payStr = "支付失败!"
var url = "payNotify.html?pay=fail"; // 支付失败
}else{
self.payStr = "支付已取消!"
var url = "payNotify.html?pay=cancel"; // 取消支付
}
self.$confirm(JSON.stringify(res.err_desc), '提示', {
confirmButtonText: '确定',
showClose:false,
showCancelButton:false,
closeOnClickModal:false,
type: 'error'
}).then(function(){
location.href = url;
})
}
});
}
},
created : function() {},
mounted() {
var self = this;
var params = GetRequest();
if (null != params.code && undefined != params.code && '' != params.code) {
this.webChat.code = params.code;
this.webChat.state = params.state;
if (this.webChat.code !== "" && this.webChat.state !== "") {
// 获取openid,直接跳转支付
self.WXRegisterPay(this.webChat.code,this.webChat.state);
}
}else{
// 获取code
APIS.get(APIS.API_getWXCode).then(function (res) {
self.codeUrl = res.data.data.code_url;
location.href = self.codeUrl;
})
}
}
})
</script>
</body>
</html>
微信jsapi支付的流程大概就是这样,结合自己项目的业务逻辑实现。
以上便是我在做微信支付过程中的总结,若有描述不当的请评论提出哦!