欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

java开发微信公众号支付

程序员文章站 2024-03-05 12:28:54
最近做了微信公众号支付的开发,由于是第一次做也摸索了几天的时间,也只是达到了实现功能的水平,并没有太多考虑到性能问题,所以这篇文章比较适合初学者。   ...

最近做了微信公众号支付的开发,由于是第一次做也摸索了几天的时间,也只是达到了实现功能的水平,并没有太多考虑到性能问题,所以这篇文章比较适合初学者。

    微信公众号支付的总体其实很简单,大致就分为三步。第一步需要获取用户授权;第二步调用统一下单接口获取预支付id;第三步h5调起微信支付的内置的js。下面介绍具体每一步的开发流程。

一    首先要明确微信公众号支付属于网页版支付,所以相较于app的直接调取微信支付要多一步微信授权。也就是需要获取用户的openid。微信公众号使用的交易类型是jsapi,所以统一下单接口的文档明确的写到

java开发微信公众号支付

因此我们必须去获取openid,同时也可以处理一些我们需要的逻辑。获取用户授权有两种方式:1.scope=snsapi_base;2.scope=snsapi_userinfo.我使用的是snsapi_base

scope为snsapi_base

https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx520c15f417810387&redirect_uri=http%3a%2f%2fchong.qq.com%2fphp%2findex.php%3fd%3d%26c%3dwxadapter%26m%3dmobiledeal%26showwxpaytitle%3d1%26vb2ctag%3d4_2030_5_1194_60&response_type=code&scope=snsapi_base&state=123#wechat_redirect

scope为snsapi_userinfo

https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxf0e81c3bee622d60&redirect_uri=http%3a%2f%2fnba.bluewebgame.com%2foauth_response.php&response_type=code&scope=snsapi_userinfo&state=state#wechat_redirect

微信的官方文档也有对各个参数的详细说明,我就关键的参数仔细的说明一下。首先appid就不多说了就是你微信公众号的appid固定写死的,redirect_uri这个参数是最重要的,这个地址是访问你处理的接口地址。你可以在这个链接上拼接上你所需要的参数,一般你是要把订单的金额传到这个接口里的,访问这个链接的时候微信会给你code你需要用它去获取openid,记得要对其进行urlencode处理。state参数可以理解为扩展字段,其他的参数都是固定写法就不在多做介绍了。下面是获取openid的代码片段。

//获取openid
            httpclientutil util = httpclientutil.getinstance();
            map<string, string> map = new hashmap<string, string>();
            map.put("appid", wxpayconfig.appid);
            map.put("secret", wxpayconfig.appsecret);
            map.put("code", code);
            map.put("grant_type", wxpayconfig.grant_type);
            string returnstr = util.dopostretstring("https://api.weixin.qq.com/sns/oauth2/access_token", null,map);
            logger.info("returnstr:[" + returnstr + "]");
            accesstoken at = json.parseobject(returnstr, accesstoken.class);

accesstoken.java

public class accesstoken {
   
  private string access_token;
  private string expires_in;
  private string refresh_token;
  private string openid;
  private string scope;
  private string unionid;
   
  public string getaccess_token() {
    return access_token;
  }
  public void setaccess_token(string access_token) {
    this.access_token = access_token;
  }
  public string getexpires_in() {
    return expires_in;
  }
  public void setexpires_in(string expires_in) {
    this.expires_in = expires_in;
  }
  public string getrefresh_token() {
    return refresh_token;
  }
  public void setrefresh_token(string refresh_token) {
    this.refresh_token = refresh_token;
  }
  public string getopenid() {
    return openid;
  }
  public void setopenid(string openid) {
    this.openid = openid;
  }
  public string getscope() {
    return scope;
  }
  public void setscope(string scope) {
    this.scope = scope;
  }
  public string getunionid() {
    return unionid;
  }
  public void setunionid(string unionid) {
    this.unionid = unionid;
  }
  @override
  public string tostring() {
    return "accesstoken [access_token=" + access_token + ", expires_in="
        + expires_in + ", refresh_token=" + refresh_token + ", openid="
        + openid + ", scope=" + scope + ", unionid=" + unionid + "]";
  }
   
   
 
}

二    我们获取了openid后,就可以进行下一步的统一下单的开发了。微信上统一下单接口的文档写的比较详细了,具体的参数含义我就不多做介绍了。下面直接贴最直观的代码,特别提醒的是一定要注意签名的正确。签名所使用的key并不是appsecret而是你申请时自己定义的商户key。

//统一下单
 
            wxpaysenddata data = new wxpaysenddata();
            data.setappid(wxpayconfig.appid);
            data.setattach("微信支付");
            data.setbody("微信公众号支付");
            data.setmch_id(wxpayconfig.mchid);
            data.setnonce_str(noncestr);
            data.setnotify_url(wxpayconfig.notify_url);
            data.setout_trade_no(tradeno);
            data.settotal_fee((int)(fee*100));//单位:分
            data.settrade_type("jsapi");
            data.setspbill_create_ip(ip);
            data.setopenid(at.getopenid());
            string returnxml = unifiedorderservice.unifiedorder(data,wxpayconfig.key);
            wxpayreturndata redata = new wxpayreturndata();
            xstream xs1 = new xstream(new domdriver());
            xs1.alias("xml", wxpayreturndata.class);
            redata = (wxpayreturndata) xs1.fromxml(returnxml);

unifiedorderservice.java

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("attach", data.getattach());
      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("device_info", data.getdevice_info());
      logger.info("sign:"+wxsign.createsign(parameters,key));
      data.setsign(wxsign.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);
      httpclientutil util = httpclientutil.getinstance();
      returnxml = util.dopostforstring("https://api.mch.weixin.qq.com/pay/unifiedorder", null, xml);
      logger.info("返回结果:" + returnxml);
    } catch (exception e) {
      e.printstacktrace();
    } 
    return returnxml;
  }
   
}

wxsign

public class wxsign {
   
   
  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 sign = md5util.md5encode(sb.tostring(), characterencoding).touppercase(); 
    return sign; 
  }
   
  public static string getnoncestr() {
    random random = new random();
    return md5util.md5encode(string.valueof(random.nextint(10000)), "utf-8");
  }
 
  public static string gettimestamp() {
    return string.valueof(system.currenttimemillis() / 1000);
  }
 
}

最后要提一下的是notify_url回调地址,接收微信支付异步通知回调地址。

三    通过上面的操作我们获得了预支付交易会话标识prepay_id,这样我们就可以进行最后一步的操作了。使用h5调起支付api。

//h5调起支付
              attr.addattribute("appid", redata.getappid());
              attr.addattribute("timestamp", wxsign.gettimestamp());
              attr.addattribute("noncestr", redata.getnonce_str());
              attr.addattribute("package", "prepay_id="+redata.getprepay_id());
              attr.addattribute("signtype", "md5");
              sortedmap<object,object> signmap = new treemap<object,object>();
              signmap.put("appid", redata.getappid()); 
              signmap.put("timestamp", wxsign.gettimestamp());
              signmap.put("noncestr", redata.getnonce_str());
              signmap.put("package", "prepay_id="+redata.getprepay_id());
              signmap.put("signtype", "md5");
              logger.info("paysign:"+wxsign.createsign(signmap,wxpayconfig.key));
              attr.addattribute("paysign", wxsign.createsign(signmap,wxpayconfig.key));

将需要的参数传给页面后,使用微信提供方法调起支付。

<script>
  function geturlparam(name) {
    //构造一个含有目标参数的正则表达式对象 
    var reg = new regexp("(^|&)" + name + "=([^&]*)(&|$)");
    //匹配目标参数 
    var r = window.location.search.substr(1).match(reg);
    //返回参数值 
    if (r != null)
      return unescape(r[2]);
    return null;
  }
 
  function onbridgeready() {
    var appid = geturlparam('appid');
    var timestamp = geturlparam('timestamp');
    var noncestr = geturlparam('noncestr');
    var package = geturlparam('package');
    var signtype = geturlparam('signtype');
    var paysign = geturlparam('paysign');
    weixinjsbridge.invoke('getbrandwcpayrequest', {
      "appid" : appid,//"wx2421b1c4370ec43b", //公众号名称,由商户传入   
      "timestamp" : timestamp,//"1395712654", //时间戳,自1970年以来的秒数   
      "noncestr" : noncestr,//"e61463f8efa94090b1f366cccfbbb444", //随机串   
      "package" : package,//"prepay_id=u802345jgfjsdfgsdg888",
      "signtype" : signtype,//"md5", //微信签名方式:   
      "paysign" : paysign,//"70ea570631e4bb79628fbca90534c63ff7fadd89" //微信签名 
    }, function(res) { // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回  ok,但并不保证它绝对可靠。  
      //alert(res.err_msg);
      if (res.err_msg == "get_brand_wcpay_request:ok") {
        alert("支付成功");
      }
      if (res.err_msg == "get_brand_wcpay_request:cancel") {
        alert("交易取消");
      }
      if (res.err_msg == "get_brand_wcpay_request:fail") {
        alert("支付失败");
      }
    });
  }
 
  function callpay() {
    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>

在返回结果的地方可以自定义一些自己的返回页面。

    总结:由于我也是第一次做,写这篇文章是想记录一下自己的工作成果,和分享给一下也是新手的朋友们可以有一些帮助,最后希望有好的见解朋友可以留言讨论,大家一起学习进步。

以上就是关于java开发微信公众支付的所有内容了,希望大家能够喜欢。