• 微信支付(APP支付)-服务端开发(一)


    微信支付,首先需要注册一个商户平台公众账号,(网址:https://pay.weixin.qq.com/index.php/home/d_login)

    目前微信支付的接入方式有四种方式:公众号支付,APP支付,扫描支付,刷卡支付。本文中我将详细讲解一下APP支付。

    微信支付→APP支付官方文档:https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=8_1

    主要流程如下:(https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=8_3)

    商户系统和微信支付系统主要交互说明:

    步骤1:用户在商户APP中选择商品,提交订单,选择微信支付。

    步骤2:商户后台收到用户支付单,调用微信支付统一下单接口。参见【统一下单API】。

    步骤3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式为Sign=WXPay

    步骤4:商户APP调起微信支付。api参见本章节【app端开发步骤说明

    步骤5:商户后台接收支付通知。api参见【支付结果通知API

    步骤6:商户后台查询支付结果。,api参见【查询订单API

    其中与后台相关的主要为步骤2和步骤6。虽然都有官方文档的说明,但是开发过程中,难免还会遇到不少的坑,比如签名问题,大小写问题等。

    我首先讲解一下步骤2(统一下单API:https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_1):

    统一下单接口地址:URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder,请求参数,返回参数等文档中都有详细描述,就不再一一说明,直接上代码:

    @RequestMapping(value = "/createOrder", method = {RequestMethod.GET, RequestMethod.POST})
    public String createOrder(Map<String, String> model) throws Exception{
    log.debug("WeChatPayController.createOrder,parameter[{trade_no,subject,total_fee},{"
    + request.getParameter("trade_no")
    + ","
    + request.getParameter("subject")
    + ","
    + request.getParameter("total_fee") + "}]");

    WeChatRsp response = new WeChatRsp();
    WeChat weChat = new WeChat();

    String orderNo = request.getParameter("trade_no"); //订单号
    String money = request.getParameter("total_fee"); //订单金额
    String body = request.getParameter("subject"); //商品描述根据情况修改

    //金额转化为分为单位
    float sessionmoney = Float.parseFloat(money);
    String finalmoney = String.format("%.2f", sessionmoney);
    finalmoney = finalmoney.replace(".", "");

    //商户相关资料
    String appid = CommonUtils.getPropertiesValue("config", "appid");
    String appsecret = CommonUtils.getPropertiesValue("config", "appsecret");
    String partner = CommonUtils.getPropertiesValue("config", "partnerId");
    String partnerkey = CommonUtils.getPropertiesValue("config", "partnerkey");

    //商户号
    String mch_id = partner;
    //随机数
    Random random = new Random();
    String nonce_str = cn.emagsoftware.utils.MD5Util.getMD5String(String.valueOf(random.nextInt(10000)));

    //商户订单号
    String out_trade_no = orderNo;
    int intMoney = Integer.parseInt(finalmoney);

    //总金额以分为单位,不带小数点
    int total_fee = intMoney;
    //订单生成的机器 IP
    String spbill_create_ip = request.getRemoteAddr();
    System.out.println("订单生成的机器IP:"+spbill_create_ip);

    //这里notify_url是 支付完成后微信发给该链接信息,可以判断会员是否支付成功,改变订单状态等。
    String notify_url = CommonUtils.getPropertiesValue("config", "weChat_notify_url");
    //交易类型
    String trade_type = CommonUtils.getPropertiesValue("config", "trade_type");

    SortedMap<String, String> packageParams = new TreeMap<String, String>();
    packageParams.put("appid", appid);
    packageParams.put("mch_id", mch_id);
    packageParams.put("nonce_str", nonce_str);
    packageParams.put("body", body);
    packageParams.put("out_trade_no", out_trade_no);

    packageParams.put("total_fee", total_fee+"");
    packageParams.put("spbill_create_ip", spbill_create_ip);
    packageParams.put("notify_url", notify_url);

    packageParams.put("trade_type", trade_type);

    HttpServletResponse httpServletResponse = null;
    RequestHandler reqHandler = new RequestHandler(request, httpServletResponse);
    reqHandler.init(appid, appsecret, partnerkey);

    String sign = reqHandler.createSign(packageParams);
    String xml="<xml>"+
    "<appid>"+appid+"</appid>"+
    "<body><![CDATA["+body+"]]></body>"+
    "<mch_id>"+mch_id+"</mch_id>"+
    "<nonce_str>"+nonce_str+"</nonce_str>"+
    "<notify_url>"+notify_url+"</notify_url>"+
    "<out_trade_no>"+out_trade_no+"</out_trade_no>"+
    "<sign>"+sign+"</sign>"+
    "<spbill_create_ip>"+spbill_create_ip+"</spbill_create_ip>"+
    "<total_fee>"+total_fee+"</total_fee>"+
    "<trade_type>"+trade_type+"</trade_type>"+
    "</xml>";
    log.debug("xml = "+xml);
    String createOrderURL = WECHAT_CREATE_ORDER_URL;
    String prepay_id="";
    //获取预支付交易号
    try {
    prepay_id = new GetWxOrderno().getPayNo(createOrderURL, xml, "prepay_id");
    } catch (Exception e1) {
    e1.printStackTrace();
    response.setResultCode(Constant.ERROR_CODE_9998);
    response.setResultMessage(Constant.ERROR_MESSAGE.get(Constant.ERROR_CODE_9998));
    }

    if (prepay_id!=null& !prepay_id.equals("")) {
    response.setResultCode(Constant.SUCCESS_CODE);
    response.setResultMessage(Constant.ERROR_MESSAGE.get(Constant.SUCCESS_CODE));
    weChat.setAppid(appid);
    weChat.setPrepayid(prepay_id);
    String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
    String nonce_string = cn.emagsoftware.utils.MD5Util.getMD5String(timestamp);
    weChat.setTimestamp(timestamp);
    weChat.setNoncestr(nonce_string);
    weChat.setPackages("Sign=WXPay");
    weChat.setPartnerid(partner);

    //二次签名
    SortedMap<String, String> finalpackage = new TreeMap<String, String>();
    finalpackage.put("appid", appid);
    finalpackage.put("timestamp", timestamp);
    finalpackage.put("noncestr", nonce_string);
    finalpackage.put("package", "Sign=WXPay");
    finalpackage.put("partnerid", mch_id);
    finalpackage.put("prepayid", prepay_id);
    String finalsign = reqHandler.createSign(finalpackage);
    weChat.setSign(finalsign);
    response.setData(weChat);
    }else {
    log.debug("预支付交易号生成失败。。。");
    response.setResultCode(Constant.ERROR_CODE_9998);
    response.setResultMessage(Constant.ERROR_MESSAGE.get(Constant.ERROR_CODE_9998));
    }

    try{
    model.put(Constant.RETURN_MESSAGE, JsonUtils.getJSONString(response));
    log.debug("WeChatPayController.createOrder.response=="+JsonUtils.getJSONString(response));
    } catch (Exception ex) {
    log.error("VersionController.getVersion", ex);
    }
    return RET_JSP;
    }

    APP端发起支付请求之后,会发送订单号给服务端程序,服务端拿到订单号之后,根据统一下单地址,发送xml文件给微信,微信接受处理后,如果返回成功,则同时会返回一个预支付交易会话标识(prepay_id),拿到这个标识之后,服务端进行二次签名,生成sign,签名参数如下:

    //二次签名
    SortedMap<String, String> finalpackage = new TreeMap<String, String>();
    finalpackage.put("appid", appid);   //appid
    finalpackage.put("timestamp", timestamp);  //时间戳  十位  
    finalpackage.put("noncestr", nonce_string); //随机字符串
    finalpackage.put("package", "Sign=WXPay");   //固定值
    finalpackage.put("partnerid", mch_id);  //商户id(微信商户平台获取)
    finalpackage.put("prepayid", prepay_id);  //第一次请求微信,成功后,返回的参数
    String finalsign = reqHandler.createSign(finalpackage);  //生成签名
    weChat.setSign(finalsign);  //生成签名后,放入对象中

    response.setData(weChat);  //参数返回给前端

    第一次发送统一下单的时候,官方文档中都有说明,哪些参数是必须的,哪些参数不是必须的,以及参数类型,此处不再一一解释,不理解的可以参考官方文档。重点说一下二次签名:

    二次签名的时候涉及到的参数有appid,timestamp,noncestr,partnerid,prepayid,package,这6个参数全部是小写(大小写不同,MD5加密结果不一致,二次签名官方没有文档,比较坑)

    APP端获取到服务端传递的参数后,调起支付接口(https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_12&index=2),这一点是APP端的操作,不在详解。

    APP端支付成功后,会再次发送请求到服务器端,确定订单是否付款成功,服务端需要再次向微信发起请求,查询订单,具体操作查看下一章节。

    注释:开发中遇到任务问题(服务端),欢迎咨询,我也是初次开发微信支付,希望可以帮到你。

  • 相关阅读:
    PGsql(PostgreSQL)的本地连接和远程连接的问题
    CreateFolder文件夹操作【Create】
    给Windows Phone Application 换开发环境!
    C#函数,PadLeft(),填充指定数量的空格。
    sql中格式化字符串或时间,遇到多少,写多少,持续记录。
    C# 将字符串转换成GB2312很蛋疼的一个Class
    asp.net获取客户端IP Class
    MessageBox HelperClass
    Cookie帮助类
    今何していますか
  • 原文地址:https://www.cnblogs.com/dalianmao890710/p/4976575.html
Copyright © 2020-2023  润新知