微信公众号开发笔记(三):微信JSAPI支付功能开发

很久之前做了微信支付,其中也趟过很多坑,现在有时间就做个自我梳理吧算是。

公众号开发的基本配置(不明白的可以参考https://blog.csdn.net/TOP__ONE/article/details/78183209),这里不再继续阐述。

如果以下代码涉及到微信工具类方法,而我没有提到的,请到链接下载:https://download.csdn.net/download/top__one/10875681

实现微信页面的分享自定义接口功能,需要先配置js-sdk以下数据项,所以需要先获取这些数据项。详细配置可以参考上一篇文章https://blog.csdn.net/TOP__ONE/article/details/85247401中的第一步后台参数准备配置,这里就不重复写了。

在页面配置好以下参数,同样是引用的js-1.2.0版本

<script type="text/javascript">

$(document).ready(function(){
	var appId = $("#appId").val();
	var timestamp = $("#timestamp").val();
	var nonceStr = $("#nonceStr").val();
	var signature = $("#signature").val();
	//var jsonObj = eval('('+t+')');
	wx.config({
      debug: false,
      appId: appId,
      timestamp: timestamp,
      nonceStr: nonceStr,
      signature: signature,
      jsApiList: [
        'chooseWXPay',
        'checkJsApi',
        'closeWindow'
      ]
  	});
  
  wx.error(function(res){
  	//alert(JSON.stringify(res));
    // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
});
  	wx.checkJsApi({
	    jsApiList: ['checkJsApi'], // 需要检测的JS接口列表,所有JS接口列表见附录2,
	    success: function(res) {
	    	//alert(res);
	        // 以键值对的形式返回,可用的api值true,不可用为false
	        // 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}
	    }
	});
  
 }); 

准备好页面参数配置好以后就可以进行下面程序了,进行 统一下单 微信统一下单操作:

$(function(){	 
      //此方法就是为了获取统一订单id之类的信息,与后台配套
   		 $("#paybt").click(function(){
   		 	$.ajax({
				   type: "POST",
				   url: "*/payGetPreId.action",
				   data:,
				   dataType : "html",
				   success : function(msg) {
				      msg=eval('(' + msg + ')'); 
				   if("fail"==msg.info){
				   		alert(mgs.content);
				   		return false;
				   }
				 
					wx.chooseWXPay({
						timestamp : msg.timeStamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
						nonceStr : msg.nonceStr, // 支付签名随机串,不长于 32 位
						package : msg.prep, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***)
						signType : 'MD5', // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
						paySign : msg.paySign, // 支付签名
						success : function(res) {
							 $.ajax({
							   type: "POST",
							   async: false,
							   url: pay/success/",
							   data:,
							   dataType : "html",
							   success : function(msg) {
									//alert(msg);
								}
							}); 
							a();
						},
						cancel: function(res) {
								//支付取消
								alert('支付取消');
							}
					});
				}
			});
		});
	});

后台对应的payGetPreId方法

/**
	 * 获取统一下单的id
	 * 
	 * @author wang
	 * @dateTime 上午10:17:51
	 * @param RenewalController
	 *            .java
	 * @return
	 * @throws ServiceException
	 * 
	 */
	@ResponseBody
	@RequestMapping(value = "/payGetPreId", produces = { "application/json;charset=UTF-8" })
	public String payGetPreId(Model model, @RequestParam Map<String, String> map) {

		String buy_time = StringUtils.getNowTime();
		// 生成订单号
		String rStr = TokenGenerator.getPwd(6);
		String id = buy_time.replace("-", "").replace(" ", "").replace(":", "")
				+ rStr;

		WxPaySendData data = new WxPaySendData();
		String nonce_str = Sign.create_nonce_str();
		String timep = Sign.create_timestamp();
		String ip = GetIp.getLocalIp(this.getRequest());

		data.setAppid(PropertieSingle.getInstance().getProperty("APPID"));
		data.setMch_id(PropertieSingle.getInstance().getProperty("APP_MCH"));
		data.setBody("IncallOrderPay");
		data.setNonce_str(nonce_str);

		// 支付回调url需要重新设置
data.setNotify_url("http://*/payBack.action");
		logger.info("微信支付回调url地址======"+data.getNotify_url());
		data.setOut_trade_no(id);
		data.setTotal_fee((int) (Double.parseDouble(map.get("combo_price")) * 100));// 单位:分
		//data.setTotal_fee(1);// 单位:分
		data.setTrade_type("JSAPI");
		data.setSpbill_create_ip(ip);// 获取本地ip
		String openid = (String) this.getSession().getAttribute("openid");
		data.setOpenid(openid);
		logger.info("微信支付中的openid参数========"+data.getOpenid());
		
		String sign = UnifiedorderService.unifiedOrder(data, PropertieSingle
				.getInstance().getProperty("APP_SECRET"));

		Map<String, String> m = new DOMXML().parse(sign);
		String pre1 = m.get("prepay_id");
		String prep = "prepay_id=" + pre1;

		// 加 session
		SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
		parameters.put("appId",
				PropertieSingle.getInstance().getProperty("APPID"));
		parameters.put("timeStamp", timep);
		parameters.put("nonceStr", nonce_str);
		parameters.put("package", prep);
		parameters.put("signType", "MD5");
		this.getSession().setAttribute("p", parameters);

		// FIXME 再获取一次singture  查看下文档
		String signAgain = Sign.createSign(parameters, PropertieSingle
				.getInstance().getProperty("APP_SECRET"));
		String paySign = signAgain;
		String nonceStr = (String) parameters.get("nonceStr");
		String timeStamp = (String) parameters.get("timeStamp");

		JSONObject json = new JSONObject();
		json.put("info", "ok");
		json.put("timeStamp", timeStamp);
		json.put("nonceStr", nonceStr);
		json.put("prep", prep);
		json.put("paySign", paySign);
		
		map.put("wxhost", PropertieSingle.getInstance().getProperty("WXNOTIFYHOST"));
		
		json.put("map", map);
		
		return json.toString();

	}

	/**
	 * 微信支付回调方法,用于验证微信发来的请求
	 * 
	 * @param model
	 * @param map
	 */
	@ResponseBody
	@RequestMapping(value = "/payBack", produces = { "application/json;charset=UTF-8" })
	public void payBack(Model model, @RequestParam Map<String, String> map) {

		InputStream inStream = null;
		ByteArrayOutputStream outSteam = null;
		String result = "";
		try {
			inStream = this.getRequest().getInputStream();
			outSteam = new ByteArrayOutputStream();
			byte[] buffer = new byte[1024];
			int len = 0;
			while ((len = inStream.read(buffer)) != -1) {
				outSteam.write(buffer, 0, len);
			}

			result = new String(outSteam.toByteArray(), "utf-8");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (outSteam != null) {
				try {
					outSteam.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (inStream != null) {
				try {
					inStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

		// 解析微信发过来回调内容
		Map<String, String> m = XMLUtil.doXMLParse(result);
		if (m.get("result_code").equalsIgnoreCase("success")) {
			//成功回调,可以进行自己的业务操作,参数可以从m中取
			System.out.println(m.get("out_trade_no"));
			//m.get("out_trade_no").substring(0,m.get("out_trade_no").length()-1));
			try {
				this.getResponse().getWriter()
						.write(PayCommonUtil.setXML("SUCCESS", ""));
			} catch (IOException e) {
				e.printStackTrace();
			}
			// 告诉微信服务器,我收到信息了,不要在调用回调action了       
			System.out.println("-------------"
					+ PayCommonUtil.setXML("SUCCESS", ""));
		} else {
			System.out.println("11111111111111111");
		}
	}

 其中涉及到的工具类sign

package com.chinatsp.wechat.util;
 
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.UUID;
 
 
public class Sign {
    public static void main(String[] args) {
        String jsapi_ticket = "jsapi_ticket";
 
        // 注意 URL 一定要动态获取,不能 hardcode
        String url = "http://example.com";
        Map<String, String> ret = sign(jsapi_ticket, url);
        for (Map.Entry entry : ret.entrySet()) {
            System.out.println(entry.getKey() + ", " + entry.getValue());
        }
    };
 
    /**
     * 这是jssdk中需要的那个singture签名
     * @param jsapi_ticket
     * @param url
     * @return
     */
    public static Map<String, String> sign(String jsapi_ticket, String url) {
        Map<String, String> ret = new HashMap<String, String>();
        String nonce_str = create_nonce_str();//定义的字符串
        String timestamp = create_timestamp();//同上
        String string1;
        String signature = "";
        
        System.out.println(nonce_str);
        System.out.println(timestamp);
        System.out.println(jsapi_ticket);
        System.out.println(url);
        
        //注意这里参数名必须全部小写,且必须有序
        string1 = "jsapi_ticket=" + jsapi_ticket +
                  "&noncestr=" + nonce_str +
                  "&timestamp=" + timestamp +
                  "&url=" + url;
        System.out.println(string1);
 
        try
        {
            MessageDigest crypt = MessageDigest.getInstance("SHA-1");//加密方法
            crypt.reset();
            crypt.update(string1.getBytes("UTF-8"));
            signature = byteToHex(crypt.digest());
        }
        catch (NoSuchAlgorithmException e)
        {
            e.printStackTrace();
        }
        catch (UnsupportedEncodingException e)
        {
            e.printStackTrace();
        }
 
        ret.put("url", url);
        ret.put("jsapi_ticket", jsapi_ticket);
        ret.put("nonceStr", nonce_str);
        ret.put("timestamp", timestamp);
        ret.put("signature", signature);
 
        return ret;
    }
	

/**
     * 这是微信支付里面需要哪个签名
     * @param parameters
     * @param key
     * @return
     */
    private static String characterEncoding = "UTF-8";
    @SuppressWarnings("rawtypes")
	  public static String createSign(SortedMap<Object,Object> parameters,String key){ 
		  StringBuffer sb = new StringBuffer(); 
		    Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序(升序) 
		    Iterator it = es.iterator(); 
		    while(it.hasNext()) { 
		      Map.Entry entry = (Map.Entry)it.next(); 
		      String k = (String)entry.getKey(); 
		      Object v = entry.getValue(); 
	      if(null != v && !"".equals(v)  
	          && !"sign".equals(k) && !"key".equals(k)) { 
	        sb.append(k + "=" + v + "&"); 
	      } 
	    } 
	    sb.append("key=" + key);
	    String signature = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
	 
      return signature; 
	  }
       
	//加密方法
    private static String byteToHex(final byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash)
        {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }
 
    public static String create_nonce_str() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }
 
    public static String create_timestamp() {
        return Long.toString(System.currentTimeMillis() / 1000);
    }
}

工具类DOMXML

package com.chinatsp.wechat.util;
import java.io.StringReader;   
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;   
import javax.xml.parsers.DocumentBuilderFactory;   
import org.w3c.dom.Document;   
import org.w3c.dom.Element;   
import org.w3c.dom.Node;   
import org.w3c.dom.NodeList;   
import org.xml.sax.InputSource;   
  
 
public class DOMXML {
	public  Map<String,String> parse(String protocolXML) {   
        Map<String,String> m = new HashMap<String, String>();
        try {   
             DocumentBuilderFactory factory = DocumentBuilderFactory   
                     .newInstance();   
             DocumentBuilder builder = factory.newDocumentBuilder();   
             Document doc = builder   
                     .parse(new InputSource(new StringReader(protocolXML)));   
  
             Element root = doc.getDocumentElement();   
             NodeList books = root.getChildNodes();   
            if (books != null) {   
                for (int i = 0; i < books.getLength(); i++) {   
                     Node book = books.item(i);   
//                     System.out.println("节点=" + book.getNodeName() + "\ttext="  
//                             + book.getFirstChild().getNodeValue());   
                     m.put(book.getNodeName(), book.getFirstChild().getNodeValue());
                 }   
             }   
         } catch (Exception e) {   
             e.printStackTrace();   
         }
        return m;
     }   
	 
}

GetIp工具类

package com.chinatsp.wechat.util;

import javax.servlet.http.HttpServletRequest;


public class GetIp{
	
	 /**
     * 从Request对象中获得客户端IP,处理了HTTP代理服务器和Nginx的反向代理截取了ip
     * @param request
     * @return ip
     */
   public static String getLocalIp(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 =  forwarded.split(",")[0];
           }
       } else {
           if (realIp.equals(forwarded)) {
               ip = realIp;
           } else {
               if(forwarded != null){
                   forwarded = forwarded.split(",")[0];
               }
               ip =  forwarded;
           }
       }
       return ip;
   }
} 
	

UnifiedorderService.unifiedOrder工具类方法 重点

package com.chinatsp.wechat.util;


import java.io.IOException;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import weixin.util.HttpUtils;

import com.alibaba.fastjson.JSONObject;
import com.chinatsp.wechat.bean.WxPaySendData;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;


public class UnifiedorderService {
	private final static Logger logger = LoggerFactory.getLogger(UnifiedorderService.class);
    
	  public static String unifiedOrder(WxPaySendData data,String key){
	    //统一下单支付
	    String returnXml = null;
	    try {
	      //生成sign签名
	      SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();
	      parameters.put("appid", data.getAppid()); 
	      parameters.put("body", data.getBody());
	      parameters.put("mch_id", data.getMch_id());
	      parameters.put("nonce_str", data.getNonce_str());
	      parameters.put("notify_url", data.getNotify_url());
	      parameters.put("out_trade_no", data.getOut_trade_no());
	      parameters.put("total_fee", data.getTotal_fee()+"");
	      parameters.put("trade_type", data.getTrade_type());
	      parameters.put("spbill_create_ip", data.getSpbill_create_ip());
	      parameters.put("openid", data.getOpenid());
//	      parameters.put("time_start", data.getTime_start());
//	      parameters.put("time_expire", data.getTime_expire());
	      logger.info("SIGN:"+Sign.createSign(parameters,key));
	      data.setSign(Sign.createSign(parameters,key));
	      XStream xs = new XStream(new DomDriver("UTF-8",new XmlFriendlyNameCoder("-_", "_")));
	      xs.alias("xml", WxPaySendData.class);
	      String xml = xs.toXML(data);
	      logger.info("统一下单xml为:\n" + xml);
	    
	      returnXml = HttpUtils.doRequest("https://api.mch.weixin.qq.com/pay/unifiedorder","POST", xml);
	      logger.info("返回结果:" + returnXml);
	      System.out.println(returnXml); 
	    } catch (Exception e) {
	      e.printStackTrace();
	    } 
	    return returnXml;
	  }
	  
	  
	  
	  public static void main(String[] args) {
		  WxPaySendData data = new WxPaySendData();
		  data.setAppid("wx1b");
          data.setBody("wxgzzgzgufy");
          data.setMch_id("13901");
          data.setNonce_str("12345678");
          data.setNonce_str(Sign.create_nonce_str());
          data.setNotify_url("yy/testindex.action");
          data.setOut_trade_no("122125112");
          data.setTotal_fee(1);//单位:分
          data.setTrade_type("JSAPI");
          data.setSpbill_create_ip("192.16.");
         
          data.setOpenid("oogDE");
		String sign = UnifiedorderService.unifiedOrder(data, "pq19L**SwY5EB");

	
//		String s = "<xml><appid><![CDATA[wx2c43b]]></appid><attach><![CDATA[支付测试]]></attach><bank_type><![CDATA[CFT]]></bank_type>  <fee_type><![CDATA[CNY]]></fee_type>  <is_subscribe><![CDATA[Y]]></is_subscribe>  <mch_id><![CDATA[10000100]]></mch_id>  <nonce_str><![CDATA[5d2b6c46e531c]]></nonce_str>  <openid><![CDATA[oUpFkE]]></openid>  <out_trade_no><![CDATA[14653]]></out_trade_no>  <result_code><![CDATA[SUCCESS]]></result_code>  <return_code><![CDATA[SUCCESS]]></return_code>  <sign><![CDATA[B552ED6B278AB241]]></sign>  <sub_mch_id><![CDATA[10000]]></sub_mch_id>  <time_end><![CDATA[20140903131540]]></time_end>  <total_fee>1</total_fee>  <trade_type><![CDATA[JSAPI]]></trade_type>  <transaction_id><![CDATA[100440079030005092168]]></transaction_id>				</xml>";
//		String xmlToJSON = XmlUtils.xmlToJSON(s);
//		Map<String,String> map = (Map)JSONObject.toJSON(xmlToJSON);
//		System.out.println(map.get("prepay_id"));
		
		Map<String,String> m=XMLUtil.doXMLParse(sign);
		System.out.println(m.size());
		
	}
}

至此,微信公众号的支付功能就开发完毕了·、留此备忘一下~

具体的jsapi支付功能请参考详细文档https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值