微信支付异步回调,带你解决微信支付的深坑

1.首先我们先下载微信支付的服务器端demo





2.个文件作用介绍

index.jsp  下单  payRequest.jsp  获取微信支付prepay_id等。

重点我说说这个payNotifyUrl.jsp

<%@ page language="java" contentType="text/html; charset=GBK" pageEncoding="GBK"%>
<%@ page import="com.tenpay.RequestHandler" %>
<%@ page import="com.tenpay.ResponseHandler" %>   
<%@ page import="com.tenpay.client.ClientResponseHandler" %>    
<%@ page import="com.tenpay.client.TenpayHttpClient" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%
//---------------------------------------------------------
//财付通支付通知(后台通知)示例,商户按照此文档进行开发即可
//---------------------------------------------------------
//商户号  
String partner = "1900000109";

//密钥
String key = "8934e7d15453e97507ef794cf7b0519d";

//创建支付应答对象
ResponseHandler resHandler = new ResponseHandler(request, response);
resHandler.setKey(key);

//判断签名
if(resHandler.isTenpaySign()) {
	
	//通知id
	String notify_id = resHandler.getParameter("notify_id");
	
	//创建请求对象
	RequestHandler queryReq = new RequestHandler(null, null);
	//通信对象
	TenpayHttpClient httpClient = new TenpayHttpClient();
	//应答对象
	ClientResponseHandler queryRes = new ClientResponseHandler();
	
	//通过通知ID查询,确保通知来至财付通
	queryReq.init();
	queryReq.setKey(key);
	queryReq.setGateUrl("https://gw.tenpay.com/gateway/verifynotifyid.xml");
	queryReq.setParameter("partner", partner);
	queryReq.setParameter("notify_id", notify_id);
	
	//通信对象
	httpClient.setTimeOut(5);
	//设置请求内容
	httpClient.setReqContent(queryReq.getRequestURL());
	System.out.println("queryReq:" + queryReq.getRequestURL());
	//后台调用
	if(httpClient.call()) {
		//设置结果参数
		queryRes.setContent(httpClient.getResContent());
		System.out.println("queryRes:" + httpClient.getResContent());
		queryRes.setKey(key);
			
			
		//获取返回参数
		String retcode = queryRes.getParameter("retcode");
		String trade_state = queryRes.getParameter("trade_state");
	
		String trade_mode = queryRes.getParameter("trade_mode");
			
		//判断签名及结果
		if(queryRes.isTenpaySign()&& "0".equals(retcode) && "0".equals(trade_state) && "1".equals(trade_mode)) {
			System.out.println("订单查询成功");
			//取结果参数做业务处理				
			System.out.println("out_trade_no:" + queryRes.getParameter("out_trade_no")+
					" transaction_id:" + queryRes.getParameter("transaction_id"));
			System.out.println("trade_state:" + queryRes.getParameter("trade_state")+
					" total_fee:" + queryRes.getParameter("total_fee"));
		        //如果有使用折扣券,discount有值,total_fee+discount=原请求的total_fee
			System.out.println("discount:" + queryRes.getParameter("discount")+
					" time_end:" + queryRes.getParameter("time_end"));
			//------------------------------
			//处理业务开始
			//------------------------------
			
			//处理数据库逻辑
			//注意交易单不要重复处理
			//注意判断返回金额
			
			//------------------------------
			//处理业务完毕
			//------------------------------
			resHandler.sendToCFT("Success");
		}
		else{
				//错误时,返回结果未签名,记录retcode、retmsg看失败详情。
				System.out.println("查询验证签名失败或业务错误");
				System.out.println("retcode:" + queryRes.getParameter("retcode")+
						" retmsg:" + queryRes.getParameter("retmsg"));
		}
	
	} else {

		System.out.println("后台调用通信失败");
			
		System.out.println(httpClient.getResponseCode());
		System.out.println(httpClient.getErrInfo());
		//有可能因为网络原因,请求已经处理,但未收到应答。
	}
}
else{
	System.out.println("通知签名验证失败");
}

%>

就是上面的这代码。完全没有用。查看sdk源码才知道 这个异步回调是接收微信发送的所有参数,然后排序  加密 验签。  最坑的是  微信 的参数根本不是通过的参数返回的。而是通过的流。所以这个太坑了。下面我把我修改过的源码发出来 帮助大家解决回调问题。

首先是控制器

/**
	 * 异步回调接口
	 * @param request
	 * @param response
	 * @throws Exception
	 */
	@RequestMapping(value="/weixin_parent_notify.do",produces="text/html;charset=utf-8")
	@ResponseBody
	public String WeixinParentNotifyPage(HttpServletRequest request,HttpServletResponse response) throws Exception{
		
		ServletInputStream instream = request.getInputStream();
		StringBuffer sb = new StringBuffer();
		int len = -1;
		byte[] buffer = new byte[1024];
		
		while((len = instream.read(buffer)) != -1){
			sb.append(new String(buffer,0,len));
		}
		instream.close();
		
		SortedMap<String,String> map = WXRequestUtil.doXMLParseWithSorted(sb.toString());//接受微信的通知参数
		Map<String,String> return_data = new HashMap<String,String>();
	
		//创建支付应答对象
		ResponseHandler resHandler = new ResponseHandler(request, response);
		
		resHandler.setAllparamenters(map);
		resHandler.setKey(ConstantUtil.PARTNER_KEY[0]);
		
		//判断签名
		if(resHandler.isTenpaySign()){
			if(!map.get("return_code").toString().equals("SUCCESS")){
				return_data.put("return_code", "FAIL");
				return_data.put("return_msg", "return_code不正确");
			}else{
				if(!map.get("result_code").toString().equals("SUCCESS")){
					return_data.put("return_code", "FAIL");
					return_data.put("return_msg", "result_code不正确");
				}
				
				
				
				String out_trade_no = map.get("out_trade_no").toString();
				String time_end = map.get("time_end").toString();
				BigDecimal total_fee = new BigDecimal(map.get("total_fee").toString());
				//付款完成后,支付宝系统发送该交易状态通知
				System.out.println("交易成功");
				Map order = orderdao.PaymentEndGetOrderInfo(out_trade_no);
				
				if(order == null){
					System.out.println("订单不存在");
					return_data.put("return_code", "FAIL");
					return_data.put("return_msg", "订单不存在");
					return WXRequestUtil.GetMapToXML(return_data);
				}
				
				int order_type = (int) order.get("order_type");
				boolean payment_status = (boolean) order.get("payment_status");
				int supplier_id = (int) order.get("supplier_id");
				
				BigDecimal p = new BigDecimal("100");  
				BigDecimal amount = (BigDecimal) order.get("amount");
				
				amount  = amount.multiply(p);
				
				//如果订单已经支付返回错误
				if(payment_status){
					System.out.println("订单已经支付");
					return_data.put("return_code", "SUCCESS");
					return_data.put("return_msg", "OK");
					return WXRequestUtil.GetMapToXML(return_data);
				}
				
				
				//如果支付金额不等于订单金额返回错误
				if(amount.compareTo(total_fee)!=0){
					System.out.println("资金异常");
					return_data.put("return_code", "FAIL");
					return_data.put("return_msg", "金额异常");
					return WXRequestUtil.GetMapToXML(return_data);
				}
				
				//更新订单信息
				if(orderdao.PaymentEndUpdateOrder(out_trade_no, time_end)){
					System.out.println("更新订单成功");
					//如果该订单是幼儿产品  并且 存在代理
					if(order_type == 2){
						if(supplier_id != 0){
							Map su = userdao.getSupplierInfo(supplier_id);
							String phone = (String) su.get("phone_number");
							String nickname = (String) su.get("nickname");
							String app_token = (String) su.get("app_token");
							
							String content = "【三盛科创】尊敬的"+ nickname +"您好。您在我们平台出售的商品有新用户下单。请您点击该链接查看发货信息。"+Config.WEB_SERVER+"/order/SupplierOrderInfo.do?order_number="+out_trade_no+"&sid="+app_token+".请您务必妥善包管。";
							MessageUtil.SendMessage(phone,content);
						}
					}else{
						orderdao.UpdateOrderStatus(out_trade_no, 3);//更新订单为已完成
					}
					
					return_data.put("return_code", "SUCCESS");
					return_data.put("return_msg", "OK");
					return WXRequestUtil.GetMapToXML(return_data);
				}else{
					return_data.put("return_code", "FAIL");
					return_data.put("return_msg", "更新订单失败");
					return WXRequestUtil.GetMapToXML(return_data);
				}
			}
		}else{
			return_data.put("return_code", "FAIL");
			return_data.put("return_msg", "签名错误");
		}
		
		String xml = WXRequestUtil.GetMapToXML(return_data);
		return xml;
	}

微信工具类WXRequestUtil.java

package com.tenpay.util;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.URL;
import java.security.KeyStore;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.net.ssl.SSLContext;

import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.glassfish.jersey.internal.util.Base64;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;

import com.zhiweism.util.MD5;
import com.zhiweism.util.Util;

/*
 * 用户发起统一下单请求
 * 作者:董志平
 * 用于发起微信扫码支付接口
 */
public class WXRequestUtil {
	private static String WXSign = null;
//	//测试
	public static void main(String[] args) {
		//Map<String,String> res = SendPayment("苹果","20170106113325",1,0);
	}
	
	/*
	 * 发起支付请求
	 * body	商品描述
	 * out_trade_no	订单号
	 * total_fee	订单金额		单位  元
	 * product_id	商品ID
	 */
	public static Map<String,String> SendPayment(String body,String out_trade_no,double total_fee,int app_type){
		String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
		String xml = WXParamGenerate(body,out_trade_no,total_fee,app_type);
		String res = httpsRequest(url,"POST",xml);
		Map<String,String> data = null;
		try {
			data = doXMLParse(res);
		} catch (Exception e) {
			data = null;
		}
		return data;
	}

	
	/**
	 * 获取签名
	 * @return
	 */
	public static String getWXSign() {
		return WXSign;
	}

	/**
	 * 	获得随机字符串
	 * 
	 */
	public static String NonceStr(){
		String res = Base64.encodeAsString(Math.random()+"::"+new Date().toString()).substring(0, 30);
		return res;
	}
	
	/**
	 * 获取时间戳
	 */
	public static String GetTimeStamp(){
		int t = (int)(System.currentTimeMillis()/1000);
		return t+"";
	}
	
	
	/**
	 * 	获取用户的ip
	 * 
	 */
	 public static String GetIp() {
        InetAddress ia=null;
        try {
            ia=InetAddress.getLocalHost();
            String localip=ia.getHostAddress();
            return localip;
        } catch (Exception e) {
            return null;
        }
    }
	 
	 /**
	  * 获取签名
	  * 
	  */
	 public static String GetSign(Map<String,String> param,int app_type){
		String StringA =  Util.formatUrlMap(param, false, false);
		String stringSignTemp = MD5.md5(StringA+"&key="+ConstantUtil.PARTNER_KEY[app_type]).toUpperCase();
		return stringSignTemp;
	 }
	 
	 /**
	  * 
	  * Map转xml数据
	  */
	 public static String GetMapToXML(Map<String,String> param){
		 StringBuffer sb = new StringBuffer();
		 sb.append("<xml>");
		 for (Map.Entry<String,String> entry : param.entrySet()) { 
			    sb.append("<"+ entry.getKey() +">");
			    sb.append(entry.getValue());
			    sb.append("</"+ entry.getKey() +">");
		}  
		 sb.append("</xml>");
		 return sb.toString();
	 }
	
	
	//微信统一下单参数设置
	public static String WXParamGenerate(String description,String out_trade_no,double total_fee,int app_type){
		int fee = (int)(total_fee * 100.00);
		Map<String,String> param = new HashMap<String,String>();
		param.put("appid", ConstantUtil.APP_ID[app_type]);
		param.put("mch_id", ConstantUtil.PARTNER[app_type]);
		param.put("nonce_str",NonceStr());
		param.put("body",description);
		param.put("out_trade_no",out_trade_no);
		param.put("total_fee", fee+"");
		param.put("spbill_create_ip", GetIp());
		param.put("notify_url", ConstantUtil.WEIXIN_NOTIFY[app_type]);
		param.put("trade_type", "APP");
		
		WXSign = GetSign(param,app_type);
		
		param.put("sign", WXSign);
		return GetMapToXML(param);
	}
	
	//发起微信支付请求
    public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {  
      try {  
          URL url = new URL(requestUrl);  
          HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
          
          conn.setDoOutput(true);  
          conn.setDoInput(true);  
          conn.setUseCaches(false);  
          // 设置请求方式(GET/POST)  
          conn.setRequestMethod(requestMethod);  
          conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");  
          // 当outputStr不为null时向输出流写数据  
          if (null != outputStr) {  
              OutputStream outputStream = conn.getOutputStream();  
              // 注意编码格式  
              outputStream.write(outputStr.getBytes("UTF-8"));  
              outputStream.close();  
          }  
          // 从输入流读取返回内容  
          InputStream inputStream = conn.getInputStream();  
          InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");  
          BufferedReader bufferedReader = new BufferedReader(inputStreamReader);  
          String str = null;
          StringBuffer buffer = new StringBuffer();  
          while ((str = bufferedReader.readLine()) != null) {  
              buffer.append(str);  
          }  
          // 释放资源  
          bufferedReader.close();  
          inputStreamReader.close();  
          inputStream.close();  
          inputStream = null;  
          conn.disconnect();  
          return buffer.toString();  
      } catch (ConnectException ce) {  
          System.out.println("连接超时:{}"+ ce);  
      } catch (Exception e) {  
          System.out.println("https请求异常:{}"+ e);  
      }  
      return null;  
    }  
      
    //退款的请求方法  
    public static String httpsRequest2(String requestMethod, String outputStr) throws Exception {  
          KeyStore keyStore  = KeyStore.getInstance("PKCS12");  
          StringBuilder res = new StringBuilder("");  
          FileInputStream instream = new FileInputStream(new File("/home/apiclient_cert.p12"));  
          try {  
              keyStore.load(instream, "".toCharArray());  
          } finally {  
              instream.close();  
          }  

          // Trust own CA and all self-signed certs  
          SSLContext sslcontext = SSLContexts.custom()  
                  .loadKeyMaterial(keyStore, "1313329201".toCharArray())  
                  .build();   
          SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(  
                  sslcontext,  
                  new String[] { "TLSv1" },  
                  null,  
                  SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);  
          CloseableHttpClient httpclient = HttpClients.custom()  
                  .setSSLSocketFactory(sslsf)  
                  .build();  
          try {  

              HttpPost httpost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");  
              httpost.addHeader("Connection", "keep-alive");  
              httpost.addHeader("Accept", "*/*");  
              httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");  
              httpost.addHeader("Host", "api.mch.weixin.qq.com");  
              httpost.addHeader("X-Requested-With", "XMLHttpRequest");  
              httpost.addHeader("Cache-Control", "max-age=0");  
              httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");  
               StringEntity entity2 = new StringEntity(outputStr ,Consts.UTF_8);  
               httpost.setEntity(entity2);  
              System.out.println("executing request" + httpost.getRequestLine());  

              CloseableHttpResponse response = httpclient.execute(httpost);  
               
              try {  
                  HttpEntity entity = response.getEntity();  
                    
                  System.out.println("----------------------------------------");  
                  System.out.println(response.getStatusLine());  
                  if (entity != null) {  
                      System.out.println("Response content length: " + entity.getContentLength());  
                      BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));  
                      String text = "";
                      res.append(text);  
                      while ((text = bufferedReader.readLine()) != null) {  
                          res.append(text);  
                          System.out.println(text);  
                      }  
                       
                  }  
                  EntityUtils.consume(entity);  
              } finally {  
                  response.close();  
              }  
          } finally {  
              httpclient.close();  
          }  
          return  res.toString();  
            
    }
      
    //xml解析  
    public static Map<String, String> doXMLParse(String strxml) throws Exception {  
          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 = getChildrenText(children);  
              }  
                
              m.put(k, v);  
          }  
            
          //关闭流  
          in.close();   
          return m;  
    }  
    
    //xml解析  
    public static SortedMap<String, String> doXMLParseWithSorted(String strxml) throws Exception {  
          strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");  
          if(null == strxml || "".equals(strxml)) {  
              return null;  
          }  
            
          SortedMap<String,String> m = new TreeMap<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 = getChildrenText(children);  
              }  
                
              m.put(k, v);  
          }  
            
          //关闭流  
          in.close();   
          return m;  
    }  
      
    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(getChildrenText(list));  
                  }  
                  sb.append(value);  
                  sb.append("</" + name + ">");  
              }  
          }   
          return sb.toString();  
    }
}
修改微信ResponseHandler.java

package com.tenpay;


import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.tenpay.util.MD5Util;
import com.tenpay.util.TenpayUtil;

/**
 * 
 * @author miklchen
 *
 */
public class ResponseHandler { 

	private String key;
	

	private SortedMap parameters; 
	
	
	private String debugInfo;
	
	private HttpServletRequest request;
	
	private HttpServletResponse response;
	
	private String uriEncoding;
	
	/**
	 * 
	 * @param request
	 * @param response
	 */
	public ResponseHandler(HttpServletRequest request,
			HttpServletResponse response)  {
		this.request = request;
		this.response = response;

		this.key = "";
		this.parameters = new TreeMap();
		this.debugInfo = "";
		
		this.uriEncoding = "";
	}
	
	/**

	*/
	public String getKey() {
		return key;
	}

	/**
	*
	*/
	public void setKey(String key) {
		this.key = key;
	}

	/**
	 *ֵ
	 * @param parameter 
	 * @return String 
	 */
	public String getParameter(String parameter) {
		String s = (String)this.parameters.get(parameter); 
		return (null == s) ? "" : s;
	}
	
	/**
	 * @param parameter
	 * @param parameterValueֵ
	 */
	public void setParameter(String parameter, String parameterValue) {
		String v = "";
		if(null != parameterValue) {
			v = parameterValue.trim();
		}
		
		this.parameters.put(parameter, v);
	}
	
	/**
	 * 
	 * @return SortedMap
	 */
	public SortedMap getAllParameters() {
		return this.parameters;
	}
	
	public void setAllparamenters(SortedMap map){
		this.parameters = map;
	}
	
	/**
	 * 微信异步回调签名
	 * @return boolean
	 */
	public boolean isTenpaySign() {
		StringBuffer sb = new StringBuffer();
		Set es = this.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(!"sign".equals(k) && null != v && !"".equals(v)) {
				sb.append(k + "=" + v + "&");
			}
		}
		
		sb.append("key="+this.getKey());
		String enc = TenpayUtil.getCharacterEncoding(this.request, this.response);
		String sign = MD5Util.MD5Encode(sb.toString(), enc).toLowerCase();
		
		String tenpaySign = this.getParameter("sign").toLowerCase();
		
		System.out.println("sign:"+sign+"      tenpaysign:"+tenpaySign);
		
		return tenpaySign.equals(sign);
	}
	
	/**
	 * 
	 * @throws IOException 
	 */
	public void sendToCFT(String msg) throws IOException {
		String strHtml = msg;
		PrintWriter out = this.getHttpServletResponse().getWriter();
		out.println(strHtml);
		out.flush();
		out.close();

	}
	
	/**
	 *
	 * @return String
	 */
	public String getUriEncoding() {
		return uriEncoding;
	}

	/**
	 * @param uriEncoding
	 * @throws UnsupportedEncodingException
	 */
	public void setUriEncoding(String uriEncoding)
			throws UnsupportedEncodingException {
		if (!"".equals(uriEncoding.trim())) {
			this.uriEncoding = uriEncoding;

			
			String enc = TenpayUtil.getCharacterEncoding(request, response);
			Iterator it = this.parameters.keySet().iterator();
			while (it.hasNext()) {
				String k = (String) it.next();
				String v = this.getParameter(k);
				v = new String(v.getBytes(uriEncoding.trim()), enc);
				this.setParameter(k, v);
			}
		}
	}

	/**
	
	*/
	public String getDebugInfo() {
		return debugInfo;
	}
	
	/**
	*
	*/
	protected void setDebugInfo(String debugInfo) {
		this.debugInfo = debugInfo;
	}
	
	protected HttpServletRequest getHttpServletRequest() {
		return this.request;
	}
	
	protected HttpServletResponse getHttpServletResponse() {
		return this.response;
	}
	
}

试试是不是已经可以发起异步回调了。记得key  在  微信支付 -》API安全  下面设置。





你可能感兴趣的:(微信支付异步回调,带你解决微信支付的深坑)