微信公众号支付 JAVA

前言:接到产品的一个需求,使用场景具体为生成的二维码进行宣传,用户扫码关注公众号之后,公众号会推送一条图文消息,用户点击则会跳转到事先准备好的支付选择页面,选择支付金额拉起微信支付控件, 输入密码进行支付。

仔细看微信公众号支付文档!仔细看微信公众号支付文档!仔细看微信公众号支付文档!重要的事情说三遍。因为本人没仔细看官方文档,所以走了不少弯路。 以下是公众号支付官方地址: 公众号支付

  1. 公众号开发前配置 支付目录

本人测试微信公众号支付目录只需要项目url的第一层路径即可,如上图。另外说一点给小白,关注公众号后,系统发送图文消息是可以拿到用户对应的当前公众号的openid,所以这里不需要再使用用户授权等这样的操作了。

2.微信统一下单 微信统一下单接口说明 这里主要说明几点,在调起微信统一下单之前,我们自己的系统数据库是需要有一个预支付的订单号的。另外需要关注几个比较重要的字段,公众账号ID AppID 查看地址(开发-基本配置)。商户ID mch_id查看地址(微信支付-商户信息-基本信息)。trade_type(交易类型,公众号必须选择填写"JSAPI")。另附上代码一段供大家参考。特别注意:文章中两处签名的方法内wxpayApiKey变量的值也就是官方签名中注释(key为商户平台设置的密钥key)这个密钥key是需要你登陆微信支付商户平台中获取的.如图 输入图片说明

这个是微信支付pojo类

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchemaType;
@XmlAccessorType(XmlAccessType.PROPERTY)
@XmlRootElement(name = "xml")
public class WxOrderVO {

    protected String appid;//公众账号ID
    protected String mch_id;//商户号
    protected String attach;//附加信息
    protected String device_info;//设备号
    protected String nonce_str;//随机字符串
    protected String sign;//签名
    protected String body;//商品描述
    protected String detail;//商品详情
    protected String out_trade_no;//商户订单号
    protected String fee_type;//货币类型
    protected int total_fee;//总金额
    protected String spbill_create_ip;//终端IP
    protected String time_start;//交易起始时间
    protected String time_expire;//交易结束时间
    protected String goods_tag;//商品标记
    protected String notify_url;//通知地址
    protected String trade_type;//交易类型
    protected String product_id;//商品ID
    protected String limit_pay;//指定支付方式
    protected String openid;//用户标识
    protected String result_code;
    protected String err_code;
    protected String err_code_des;
    protected String is_subscribe;
    protected String bank_type;
    protected int cash_fee;
    protected String cash_fee_type;
    protected Integer coupon_fee;
    protected String coupon_id_0;
    protected String coupon_fee_0;
    protected int coupon_count;
    protected String transaction_id;
    protected String time_end;
    protected String trade_state;
    protected String return_code;
    protected String return_msg;
    protected String prepay_id;//预付订单ID
    
	public String getAppid() {
		return appid;
	}
	public void setAppid(String appid) {
		this.appid = appid;
	}
	public String getMch_id() {
		return mch_id;
	}
	public void setMch_id(String mch_id) {
		this.mch_id = mch_id;
	}
	public String getAttach() {
		return attach;
	}
	public void setAttach(String attach) {
		this.attach = attach;
	}
	public String getDevice_info() {
		return device_info;
	}
	public void setDevice_info(String device_info) {
		this.device_info = device_info;
	}
	public String getNonce_str() {
		return nonce_str;
	}
	public void setNonce_str(String nonce_str) {
		this.nonce_str = nonce_str;
	}
	public String getSign() {
		return sign;
	}
	public void setSign(String sign) {
		this.sign = sign;
	}
	public String getBody() {
		return body;
	}
	public void setBody(String body) {
		this.body = body;
	}
	public String getDetail() {
		return detail;
	}
	public void setDetail(String detail) {
		this.detail = detail;
	}
	public String getOut_trade_no() {
		return out_trade_no;
	}
	public void setOut_trade_no(String out_trade_no) {
		this.out_trade_no = out_trade_no;
	}
	
	public int getTotal_fee() {
		return total_fee;
	}
	public void setTotal_fee(int total_fee) {
		this.total_fee = total_fee;
	}
	public String getSpbill_create_ip() {
		return spbill_create_ip;
	}
	public void setSpbill_create_ip(String spbill_create_ip) {
		this.spbill_create_ip = spbill_create_ip;
	}
	public String getTime_start() {
		return time_start;
	}
	public void setTime_start(String time_start) {
		this.time_start = time_start;
	}
	public String getTime_expire() {
		return time_expire;
	}
	public void setTime_expire(String time_expire) {
		this.time_expire = time_expire;
	}
	public String getGoods_tag() {
		return goods_tag;
	}
	public void setGoods_tag(String goods_tag) {
		this.goods_tag = goods_tag;
	}
	public String getNotify_url() {
		return notify_url;
	}
	public void setNotify_url(String notify_url) {
		this.notify_url = notify_url;
	}
	public String getTrade_type() {
		return trade_type;
	}
	public void setTrade_type(String trade_type) {
		this.trade_type = trade_type;
	}
	public String getProduct_id() {
		return product_id;
	}
	public void setProduct_id(String product_id) {
		this.product_id = product_id;
	}
	public String getLimit_pay() {
		return limit_pay;
	}
	public void setLimit_pay(String limit_pay) {
		this.limit_pay = limit_pay;
	}
	public String getOpenid() {
		return openid;
	}
	public void setOpenid(String openid) {
		this.openid = openid;
	}
	public String getFee_type() {
		return fee_type;
	}
	public void setFee_type(String fee_type) {
		this.fee_type = fee_type;
	}
	public String getResult_code() {
		return result_code;
	}
	public void setResult_code(String result_code) {
		this.result_code = result_code;
	}
	public String getErr_code() {
		return err_code;
	}
	public void setErr_code(String err_code) {
		this.err_code = err_code;
	}
	public String getErr_code_des() {
		return err_code_des;
	}
	public void setErr_code_des(String err_code_des) {
		this.err_code_des = err_code_des;
	}
	public String getIs_subscribe() {
		return is_subscribe;
	}
	public void setIs_subscribe(String is_subscribe) {
		this.is_subscribe = is_subscribe;
	}
	public String getBank_type() {
		return bank_type;
	}
	public void setBank_type(String bank_type) {
		this.bank_type = bank_type;
	}
	public int getCash_fee() {
		return cash_fee;
	}
	public void setCash_fee(int cash_fee) {
		this.cash_fee = cash_fee;
	}
	public String getCash_fee_type() {
		return cash_fee_type;
	}
	public void setCash_fee_type(String cash_fee_type) {
		this.cash_fee_type = cash_fee_type;
	}
	public Integer getCoupon_fee() {
		return coupon_fee;
	}
	public void setCoupon_fee(Integer coupon_fee) {
		this.coupon_fee = coupon_fee;
	}
	public String getCoupon_id_0() {
		return coupon_id_0;
	}
	public void setCoupon_id_0(String coupon_id_0) {
		this.coupon_id_0 = coupon_id_0;
	}
	public String getCoupon_fee_0() {
		return coupon_fee_0;
	}
	public void setCoupon_fee_0(String coupon_fee_0) {
		this.coupon_fee_0 = coupon_fee_0;
	}
	public String getTransaction_id() {
		return transaction_id;
	}
	public void setTransaction_id(String transaction_id) {
		this.transaction_id = transaction_id;
	}
	public String getTime_end() {
		return time_end;
	}
	public void setTime_end(String time_end) {
		this.time_end = time_end;
	}
	public String getTrade_state() {
		return trade_state;
	}
	public void setTrade_state(String trade_state) {
		this.trade_state = trade_state;
	}
	public String getReturn_code() {
		return return_code;
	}
	public void setReturn_code(String return_code) {
		this.return_code = return_code;
	}
	public String getReturn_msg() {
		return return_msg;
	}
	public void setReturn_msg(String return_msg) {
		this.return_msg = return_msg;
	}
	public int getCoupon_count() {
		return coupon_count;
	}
	public void setCoupon_count(int coupon_count) {
		this.coupon_count = coupon_count;
	}
    public String getPrepay_id() {
        return prepay_id;
    }
    public void setPrepay_id(String prepay_id) {
        this.prepay_id = prepay_id;
    }
    
    @Override
    public String toString() {
        return "out_trade_no="+ out_trade_no + ",sign=" + sign;
    }

}

下面一段是统一支付订单基本参数组装,注意该对象中setNotify_url方法,这里需要你的项目中已经提供微信能够回调通知你服务器的url地址.

        WxOrderVO wxOrderVO = new WxOrderVO();
        wxOrderVO.setAppid(SystemDefine.getInstance().wxpayAppId);
        wxOrderVO.setMch_id(SystemDefine.getInstance().wxpayMchId);
        wxOrderVO.setAttach(product.getName());
        wxOrderVO.setBody("xxxxxx服务");
        String nonceStr = StringUtil.genRandomStr(30);
        wxOrderVO.setNonce_str(nonceStr);
        wxOrderVO.setNotify_url(SystemDefine.getInstance().wxpayNotifyUrl);
        wxOrderVO.setTotal_fee(userOrder.getPayPrice());
        wxOrderVO.setOut_trade_no(orderNo);
        wxOrderVO.setTrade_type("JSAPI");
        wxOrderVO.setOpenid(openId);
        String sign = WxPay.getSign(wxOrderVO);//签名处
        wxOrderVO.setSign(sign);

再附上WxPay.getSign签名方法具体代码

 public static String getSign(WxOrderVO wxOrder) {

        String jsonString = JSONObject.toJSONString(wxOrder);
        JSONObject jsonObject = JSONObject.parseObject(jsonString);
        
        TreeMap<String, Object> treeMap = new TreeMap<String, Object>();
        treeMap.putAll(jsonObject);
        
        StringBuffer stringA = new StringBuffer();
        for (Object key : treeMap.keySet()) {
            // sign 字段不参与验证
            if (!String.valueOf(key).equals("sign")) {
                String value = String.valueOf(jsonObject.get(key));
                if (StringUtils.isNotBlank(value) && !"null".equals(value) && !"0".equals(value)) {
                    stringA.append(key).append("=").append(value).append("&");
                }
            }
        }
        stringA.append("key=").append(SystemDefine.getInstance().wxpayApiKey);
        return MD5Class.MD5Encode(stringA.toString()).toUpperCase();
    }

下面向微信服务器下单

    /**
     * 向微信服务器下单
     * 
     * @param wxOrder
     * @return
     * @throws Exception 
     */
    public static String postUnifiedorder(WxOrderVO wxOrder) throws Exception {
        HttpClient client = new HttpClient();
        PostMethod method = new PostMethod(SystemDefine.getInstance().wxpayServerUrl);
        method.setRequestHeader("Content-type", "application/xml;charset=UTF-8");
        String xml = wxOrder2XML(wxOrder);
        String result = null;
        try {

            method.setRequestEntity(new StringRequestEntity(xml, "application/xml", "utf-8"));
            client.executeMethod(method);
            if (method.getStatusCode() == HttpStatus.SC_OK) {
                result =
                        new String(method.getResponseBodyAsString().getBytes("iso-8859-1"), "utf-8");
            }
        } catch (Exception e) {
            throw e;
        } finally {
            method.releaseConnection();
        }
        return result;
    }

    public static String wxOrder2XML(WxOrderVO wxOrder) {
        StringBuffer xml = new StringBuffer("<xml>");
        
        String jsonString = JSONObject.toJSONString(wxOrder);
        JSONObject jsonObject = JSONObject.parseObject(jsonString);
        
        for (Object key : jsonObject.keySet()) {
            String value = String.valueOf(jsonObject.get(key));
            if (StringUtils.isNotBlank(value) && !"null".equals(value) && !"0".equals(value)) {
                xml.append("<").append(key).append(">").append(value).append("</").append(key)
                        .append(">");
            }
        }
        return xml.append("</xml>").toString();
    }

到这里如果统一下单成功微信会返回给一段xml格式的字符串,里面有订单信息.拿到微信服务器的预付订单ID则可以继续前进。如果有错误请查看官方错误码 错误码查询

下面是将返回的数据进行类型转换,并且获取到预付订单ID。(这里需要结合上面代码放在一起)这里需要进行二次签名,微信需要确认是否正确,防止安全及数据问题。

        String result = WxPay.postUnifiedorder(wxOrderVO);
        JSONObject json = WxPay.xml2Json(result);
        String prepayId = json.getString("prepay_id");

           SortedMap<String, String> finalpackage = new TreeMap<String, String>();
          long timeStamp = System.currentTimeMillis()/1000;
          String prepayId = json.getString("prepay_id");
          // 加密
          String packages = "prepay_id="+prepayId;
          finalpackage.put("appId", SystemDefine.getInstance().wxpayAppId);
          finalpackage.put("timeStamp", String.valueOf(timeStamp));
          finalpackage.put("nonceStr", nonceStr);
          finalpackage.put("package", packages);
          finalpackage.put("signType", "MD5");
          String finalsign = WeiXinUtils.createSign(finalpackage);

下面代码对应 WeiXinUtils.createSign方法

  /**
   * 创建md5摘要,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
   */
  @SuppressWarnings("rawtypes")
  public static String createSign(SortedMap<String, String> packageParams) {
      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=").append(SystemDefine.getInstance().wxpayApiKey);
      String sign = MD5Class.MD5Encode(sb.toString()).toUpperCase();
      
      return sign;

  }

最终上面的finalsign会被用到前端html页面中也就是官方文档中H5调起微信的步骤 H5调起微信支付 附上楼主H5页面中JS代码:

	var appId;
	var timeStampStr;
	var nonceStr;
	var packageStr;
	var paySign;
	var orderNo;
	
	function toPay(){
		var userId = $("#userId").val();
		var productId = $("#productId").val();
		var openId = $("#openId").val();
		var wxUnionId = $("#wxUnionId").val();
		var serialNum = $("#serialNum").val();
		var params = {};  
	    params.userId=userId;
	    params.productId=productId;
	    params.openId=openId;
	    params.wxUnionId=wxUnionId;
	    params.serialNum=serialNum;
	    $.ajax({  
	        async:false,  
	        type: "POST",
	        url: "/scanScene/toPay",
	        data:params,  
	        dataType:"json",  
	        success:function(data){  
	            if(data.res_code == 0){
	            	appId = data.appId;
	            	timeStampStr = data.timeStamp;
	            	nonceStr = data.nonceStr;
	            	packageStr = data.packageStr;
	            	paySign = data.paySign;//此处的值对应文章代码中字符串类型finalsign变量
	            	orderNo = data.orderNo;
	            	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();
	        		}
	            }else{
	            	
	            }  
	        },  
	        error:function(data){  
	            alert(data.res_code);  
	        }  
	    });
	    
	}
	
	function onBridgeReady(){
		   WeixinJSBridge.invoke(
		       'getBrandWCPayRequest', {
		           "appId":appId,     //公众号名称,由商户传入     
		           "timeStamp":timeStampStr,         //时间戳,自1970年以来的秒数     
		           "nonceStr":nonceStr, //随机串     
		           "package":packageStr,     
		           "signType":"MD5",         //微信签名方式:     
		           "paySign":paySign //微信签名 
		       },
		       function(res){
				// 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。
		           if(res.err_msg == "get_brand_wcpay_request:ok" ) {
		        	   var userId = $("#userId").val();
		        	   var productId = $("#productId").val();
		        	   var serialNum = $("#serialNum").val();
		        	   var openId = $("#openId").val();
		        	   window.location.href = "/scanScene/success?orderNo=" + orderNo + "&userId="
		        			   + userId + "&productId=" + productId +"&serialNum=" +serialNum
		        			   + "&openId=" + openId;
		           }else{
		        	   	//alert(res.err_msg);
		           }      
		       }
		   ); 
	}
	
		

主要是通过这个H5页面和后台进行交互操作.自此微信公众号支付基本完结。有问题可以大家多多交流。

转载于:https://my.oschina.net/summerfornight/blog/1057877

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值