java开发微信公众号支付

作者:hebedich 时间:2021-10-24 16:02:40 

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

    微信公众号支付的总体其实很简单,大致就分为三步。第一步需要获取用户授权;第二步调用统一下单接口获取预支付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,微信支付
0
投稿

猜你喜欢

  • Java手动实现Redis的LRU缓存机制

    2023-07-31 12:51:30
  • Java使用FileInputStream流读取文件示例详解

    2021-05-26 00:57:58
  • 详解Java高级特性之反射

    2021-06-09 14:01:06
  • 基于RxJava实现酷炫启动页

    2023-09-26 21:50:47
  • 浅谈Java内存区域划分和内存分配策略

    2023-08-11 18:52:49
  • 快速了解Maven

    2022-10-22 20:18:33
  • Android使用JobScheduler定期推送本地通知实例代码

    2023-07-26 22:43:28
  • springboot实现定时任务的四种方式小结

    2021-10-20 20:38:06
  • Java输入/输出流体系详解

    2023-03-01 06:37:00
  • Java实现拖拽列表项的排序功能

    2023-11-28 23:39:00
  • Java如何实现树的同构?

    2023-11-28 09:55:19
  • 基于Mybatis映射的一点心得(分享)

    2023-08-08 13:15:53
  • autoMapping和autoMappingBehavior的区别及说明

    2023-11-29 06:39:25
  • Java热门笔试试题整理

    2023-11-25 08:56:33
  • Java详细分析梳理垃圾回收机制

    2023-10-30 04:02:33
  • java 动态增加定时任务示例

    2023-07-29 06:56:00
  • java实现通过绑定邮箱找回密码功能

    2021-12-17 00:16:48
  • Java中防止数据重复提交超简单的6种方法

    2022-03-18 11:03:22
  • 解决@RequestBody部分属性丢失的问题

    2023-08-01 15:00:21
  • Java数据结构与算法之循环队列的实现

    2023-11-02 11:51:29
  • asp之家 软件编程 m.aspxhome.com