当前位置: 首页 > news >正文

微信公众号支付接口网页开发示例

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

微信公众号支付是指在微信内嵌浏览器进入到公众号之后,点击电商页面的支付接口,他使用了微信内嵌浏览器的支持,开发步骤如下:

(1)首先开通企业公众号账户并且开通支付接口,详细步骤可以参考相关文档,这里不再细数,这一步问题不大,主要是企业账号等信息要设置准确,除了公众号还有企业商户号要设置。

(2)将用户openid传递到支付链接的参数中,例如:

<a href="/product/buy?id=productid&openid=user_openid">支付     </a>

(3)先写好支付成功后的微信回调函数处理程序,成功的话微信会访问配置好的通知url(notify_url),会尝试8次直到我们写回去success或者fail,示例代码如下:

@RequestMapping(value = "/wxpaycallback", method = { RequestMethod.POST,
            RequestMethod.GET }, produces = { "application/json;charset=utf-8" })
    public String weixinNotify(HttpServletRequest request,
            HttpServletResponse response) {
        try {
            response.setCharacterEncoding("UTF-8");
            InputStream inStream = request.getInputStream();
            ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = inStream.read(buffer)) != -1) {
                outSteam.write(buffer, 0, len);
            }
            outSteam.close();
            inStream.close();
            String resultStr = new String(outSteam.toByteArray(), "utf-8");
             Map<String, String> resultMap =MessageUtil.parseXml(resultStr);
             String out_trade_no = resultMap.get("out_trade_no");//订单号
             String return_code = resultMap.get("return_code");
             String mch_id=resultMap.get("mch_id");        
            if (return_code.contains("SUCCESS")) {
                // 此处就是你的逻辑代码
                if (mch_id.equals(WeixinUtil.MCH_ID)){
                    OrderLogic.finishOrder(out_trade_no);//处理该订单的状态
                }
            }
            //返回消息给微信服务器
            String retXml = "<xml>  <return_code><![CDATA[SUCCESS]]></return_code>  <return_msg><![CDATA[OK]]></return_msg></xml>";
            PrintWriter out = response.getWriter();
            out.print(retXml);
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "success";//这句话可以不用吧?
    }

 

(4)Js端代码:

商品页面增加以下代码就会调用支付流程:

    function onBridgeReady() {        
        var appId = $('#appId').val();// getUrlParam('appId');        
        var timeStamp = $('#timeStamp').val();//  getUrlParam('timeStamp');
        var nonceStr = $('#nonceStr').val();//  getUrlParam('nonceStr');
        var Package = $('#packageid').val();//  getUrlParam('package');
        var signType =  $('#signType').val();// getUrlParam('signType');
        var paySign =  $('#paySign').val();// getUrlParam('paySign');
        WeixinJSBridge.invoke('getBrandWCPayRequest', {
            "appId" : appId, //公众号名称,由商户传入
            "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("success");
            }
            if (res.err_msg == "get_brand_wcpay_request:cancel") {
                alert("cancel");
            }
            if (res.err_msg == "get_brand_wcpay_request:fail") {
                alert("fail");
            }
        });
    }

    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();
        }
    }

 

(5)还需要预先调用预下单接口才行,这部分代码暂时找不到了,后面补充。

写的不是很流畅,第一次写,感觉微信支付接口挺绕的,不如支付宝来的简洁。

附件:

微信用户在不同的公众号里时候openid值是不一样的,因此在支付的时候要先将用户的openid获取到传递给支付页面。获取openid一个方式是通过Oauth链接,就是说先进入Oauth地址,将H5页面url作为参数传递给Oauth链接,微信会回调这个url并将随机的code传给这个H5,通过code来查询微信api获取openid值。

public class WeixinLogic {
    private static final Logger logger = LoggerFactory
            .getLogger(WeixinLogic.class);
    private static Gson gson = MyGlobal.gson();

    /*
     * 生成并记录短信验证码到数据库
     */
    private static String genBindSmsCode(String mobile) {
        String code = "";
        DB db = MyMongo.getDB();
        if (db != null) {
            code = UserLogic.createRandom(true, 4);
            DBCollection collection = db.getCollection("wxsmscode");
            BasicDBObject cond = new BasicDBObject("mobile", mobile);
            DBObject obj = collection.findOne(cond);
            if (obj != null) {
                DBObject doc = new BasicDBObject("$set", new BasicDBObject(
                        "code", code));
                collection.update(cond, doc);
            } else {
                DBObject doc = new BasicDBObject().append("mobile", mobile)
                        .append("code", code);
                collection.insert(doc);
            }
        }
        return code;
    }

    /*
     * 发送短信验证码
     */
    public static void SendSmsCode(String mobile) {
        String code = genBindSmsCode(mobile);
        UserLogic.sendSmsCode(mobile, code);
    }

    /*
     * 绑定公众号和手机号 成功返回true
     */
    public static boolean bindwx(String openid, String mobile, String code) {
        boolean ret = false;
        if (!isMobileExist(mobile)) {
            return false;
        }
        if (!checkSmsCode(mobile, code)) {
            return false;
        }
        String old = getbindmobile(openid);
        DB db = MyMongo.getDB();
        if (db != null) {
            DBCollection collection = db.getCollection("bindweixin");
            if (!old.equals("")) {
                collection.remove(new BasicDBObject("openid", openid));
            }
            try {
                BindWeixin info = new BindWeixin();
                DBObject doc = (DBObject) JSON.parse(gson.toJson(info));
                collection.insert(doc);
                return true;
            } catch (Exception e) {
                logger.error("bindwx" + e.getMessage());
                return false;
            }
        }
        return ret;
    }

    private static boolean isMobileExist(String mobile) {
        boolean ret = true;

        return ret;
    }

    /*
     * 短信码是否正确
     */
    private static boolean checkSmsCode(String mobile, String code) {
        boolean ret = false;
        DB db = MyMongo.getDB();
        if (db != null) {
            DBCollection collection = db.getCollection("wxsmscode");
            BasicDBObject cond = new BasicDBObject("mobile", mobile).append(
                    "code", code);
            DBObject obj = collection.findOne(cond);
            if (obj != null) {
                ret = true;
            }
        }
        return ret;
    }

    /*
     * 返回微信用户绑定的手机号
     */
    public static String getbindmobile(String openid) {
        String mobile = "";
        DB db = MyMongo.getDB();
        if (db != null) {
            DBCollection collection = db.getCollection("bindweixin");
            try {
                DBObject obj = collection.findOne(new BasicDBObject("openid",
                        openid));
                if (obj != null) {
                    BindWeixin info = gson.fromJson(obj.toString(),
                            BindWeixin.class);
                    mobile = info.mobile;
                }
            } catch (Exception e) {
                logger.error("getbindmobile:" + e.getMessage());
            }
        }
        return mobile;
    }

    
    public static void sendWXMonny(String re_openId, int money) {
        String URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
        String mch_id =WeixinUtil.MCH_ID;
        SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
        parameters.put("wxappid",WeixinUtil.APPID);
        parameters.put("mch_id", mch_id);
        String nonscr = UUID.randomUUID().toString().toUpperCase()
                .replace("-", "");
        parameters.put("nonce_str", nonscr);// 随机字符串
        parameters.put("send_name", "中医管家");
        String billno = createBillNo(mch_id, "01");
        parameters.put("mch_billno",billno);        
        parameters.put("re_openid", re_openId);//我的微信openid: "oW9-5s8Aecs_teXlIuwTwNbLQyHk"        
        parameters.put("total_amount",money+"");
        parameters.put("total_num", "1");
        parameters.put("wishing", "中医管家");
        parameters.put("client_ip", "127.0.0.1");
        parameters.put("act_name", "测试红包用例");
        parameters.put("remark", "中医管家");
    
        // String sign = createSign("UTF-8", parameters);
        // parameters.put("sign", sign);
        sign(parameters);
        String requestXML = getRequestXml(parameters);
        String result = httpsRequest(URL, "POST", requestXML);
        System.out.println("result:" + result);
    }
    
    
    public static String unifiedOrder(String openid,String trade_no,int fee,String doctorName){
        //统一下单支付
        String unified_url="https://api.mch.weixin.qq.com/pay/unifiedorder";
        String notify_url="http://123.56.140.11/health/wxpaycallback";
//        trade_no=UUID.randomUUID().toString().replace("-","");    
        try {
          //生成sign签名
          SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();
          parameters.put("appid",WeixinUtil.APPID); 
          parameters.put("attach", "中医管家");
          parameters.put("body",doctorName);
          parameters.put("mch_id",WeixinUtil.MCH_ID);
          String nonscr =trade_no;// UUID.randomUUID().toString().toUpperCase().replace("-", "");
          parameters.put("nonce_str", nonscr);
          parameters.put("notify_url",notify_url);
          parameters.put("out_trade_no", trade_no);
          parameters.put("total_fee",fee+"");
          parameters.put("trade_type", "JSAPI");
          parameters.put("spbill_create_ip","127.0.0.1");
          parameters.put("openid", openid);
          parameters.put("device_info", "WEB");
          sign(parameters);
            String requestXML = getRequestXml(parameters);
            String result =WeixinUtil. httpsRequest(unified_url, "POST", requestXML);
            System.out.println("result:" + result);
            return result;
        } catch (Exception e) {
          e.printStackTrace();
        } 
        return "";
      }
    
    private static String getRandomNum(int length) {
        String val = "";
        Random random = new Random();
        for (int i = 0; i < length; i++) {
            val += String.valueOf(random.nextInt(10));
        }
        return val;
    }

    public static String createBillNo(String mch_id, String userId) {
        // 组成: mch_id+yyyymmdd+10位一天内不能重复的数字
        // 10位一天内不能重复的数字实现方法如下:
        // 因为每个用户绑定了userId,他们的userId不同,加上随机生成的(10-length(userId))可保证这10位数字不一样
        Date dt = new Date();
        SimpleDateFormat df = new SimpleDateFormat("yyyymmdd");
        String nowTime = df.format(dt);
        int length = 10 - userId.length();
        return mch_id + nowTime + userId + getRandomNum(length);
    }

    
    public static void sign(SortedMap<Object, Object> params) {
         String KEY =WeixinUtil.APIKEY;
        Set<Entry<Object, Object>> entrys = params.entrySet();
        Iterator<Entry<Object, Object>> it = entrys.iterator();
        StringBuffer result = new StringBuffer();
        while (it.hasNext()) {
            Entry<Object, Object> entry = it.next();
            result.append(entry.getKey()).append("=").append(entry.getValue())
                    .append("&");
        }
        result.append("key=").append(KEY);
        params.put("sign", MD5Util.MD5Encode(result.toString(), "UTF-8")
                .toUpperCase());
    }
    /*
     * 发送红包接口示例
     */
    
    
    

    public static String getRequestXml(SortedMap<Object, Object> parameters) {
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        Set<Entry<Object, Object>> es = parameters.entrySet();
        Iterator<Entry<Object, Object>> it = es.iterator();
        while (it.hasNext()) {
            @SuppressWarnings("rawtypes")
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = String.valueOf(entry.getValue());
            if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k)
                    || "sign".equalsIgnoreCase(k)) {
                sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
            } else {
                sb.append("<" + k + ">" + v + "</" + k + ">");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }

    /**
     * 发送https请求
     * 
     * @param requestUrl
     *            请求地址
     * @param requestMethod
     *            请求方式(GET、POST)
     * @param outputStr
     *            提交的数据
     * @return 返回微信服务器响应的信息
     */
    public static String httpsRequest(String requestUrl, String requestMethod,
            String outputStr) {
        try {
            String result = "";
            // // 创建SSLContext对象,并使用我们指定的信任管理器初始化
            // TrustManager[] tm = { new MyX509TrustManager() };
            // SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
            // sslContext.init(null, tm, new java.security.SecureRandom());
            // // 从上述SSLContext对象中得到SSLSocketFactory对象
            // SSLSocketFactory ssf = sslContext.getSocketFactory();

            // 指定读取证书格式为PKCS12
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            // 读取本机存放的PKCS12证书文件
            FileInputStream instream = new FileInputStream(new File(
                    "/root/apiclient_cert.p12"));
            try {
                // 指定PKCS12的密码(商户ID)
                keyStore.load(instream,WeixinUtil.MCH_ID.toCharArray());
            } finally {
                instream.close();
            }
            SSLContext sslcontext = SSLContexts.custom()
                    .loadKeyMaterial(keyStore, WeixinUtil.MCH_ID.toCharArray())
                    .build();
            // 指定TLS版本
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                    sslcontext,
                    new String[] { "TLSv1" },
                    null,
                    SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);

            CloseableHttpClient httpclient = HttpClients.custom()
                    .setSSLSocketFactory(sslsf).build();

            //URL url = new URL(requestUrl);
            HttpPost httpPost = new HttpPost(requestUrl);
            StringEntity reqEntity = new StringEntity(outputStr, "utf-8"); // 如果此处编码不对,可能导致客户端签名跟微信的签名不一致
            reqEntity.setContentType("application/x-www-form-urlencoded");
            httpPost.setEntity(reqEntity);
            CloseableHttpResponse response = httpclient.execute(httpPost);
            try {
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    BufferedReader bufferedReader = new BufferedReader(
                            new InputStreamReader(entity.getContent(), "UTF-8"));
                    String text;
                    while ((text = bufferedReader.readLine()) != null) {
                        result += text;
                    }
                }
                EntityUtils.consume(entity);
            } finally {
                response.close();
            }
            return result;
            // HttpsURLConnection conn =
            // (HttpsURLConnection)httpclient.execute(httpPost);//
            // url.openConnection();
            // conn.setSSLSocketFactory(sslsf);
            // conn.setDoOutput(true);
            // conn.setDoInput(true);
            // conn.setUseCaches(false);
            // // 设置请求方式(GET/POST)
            // conn.setRequestMethod(requestMethod);
            // conn.setRequestProperty("content-type",
            // "application/x-www-form-urlencoded");
            // // 当outputStr不为null时向输出流写数据
            // if (null != outputStr) {
            // OutputStream outputStream = conn.getOutputStream();
            // // 注意编码格式
            // outputStream.write(outputStr.getBytes("UTF-8"));
            // outputStream.close();
            // }
            // // 从输入流读取返回内容
            // InputStream inputStream = conn.getInputStream();
            // InputStreamReader inputStreamReader = new InputStreamReader(
            // inputStream, "utf-8");
            // BufferedReader bufferedReader = new BufferedReader(
            // inputStreamReader);
            // String str = null;
            // StringBuffer buffer = new StringBuffer();
            // while ((str = bufferedReader.readLine()) != null) {
            // buffer.append(str);
            // }
            // // 释放资源
            // bufferedReader.close();
            // inputStreamReader.close();
            // inputStream.close();
            // inputStream = null;
            // conn.disconnect();
            // return buffer.toString();
        } catch (ConnectException ce) {
            logger.error("连接超时:{}", ce);
        } catch (Exception e) {
            logger.error("https请求异常:{}", e);
        }
        return null;
    }
}

转载于:https://my.oschina.net/u/778683/blog/829006

相关文章:

  • kisso
  • C#多线程学习一
  • 交换机-网络聚合存在的问题
  • python学习之MySQL数据库详解
  • win10的一些设置
  • 计算机操作系统
  • beego orm 模型定义
  • Linux下 ln 命令详解
  • 分布式系统中的定时任务全解(二)
  • 基于Vue2全家桶的移动端AppDEMO实现
  • 曾经的曾经的多么多么的爱一个人。。。
  • 手把手教你写Kconfig---基于tiny4412开发板
  • jquery点击回到页面顶部方法
  • Python 爬虫-下载图片
  • 中文转拼音without CJK
  • AWS实战 - 利用IAM对S3做访问控制
  • cookie和session
  • Intervention/image 图片处理扩展包的安装和使用
  • java8-模拟hadoop
  • Linux快速配置 VIM 实现语法高亮 补全 缩进等功能
  • node和express搭建代理服务器(源码)
  • node入门
  • php中curl和soap方式请求服务超时问题
  • SpiderData 2019年2月23日 DApp数据排行榜
  • storm drpc实例
  • vue 配置sass、scss全局变量
  • Xmanager 远程桌面 CentOS 7
  • 翻译 | 老司机带你秒懂内存管理 - 第一部(共三部)
  • 机器学习学习笔记一
  • 网络应用优化——时延与带宽
  • 我是如何设计 Upload 上传组件的
  • 想晋级高级工程师只知道表面是不够的!Git内部原理介绍
  • 协程
  • 云栖大讲堂Java基础入门(三)- 阿里巴巴Java开发手册介绍
  • 中文输入法与React文本输入框的问题与解决方案
  • ​软考-高级-信息系统项目管理师教程 第四版【第19章-配置与变更管理-思维导图】​
  • "无招胜有招"nbsp;史上最全的互…
  • #Ubuntu(修改root信息)
  • #我与Java虚拟机的故事#连载01:人在JVM,身不由己
  • %check_box% in rails :coditions={:has_many , :through}
  • (33)STM32——485实验笔记
  • (html5)在移动端input输入搜索项后 输入法下面为什么不想百度那样出现前往? 而我的出现的是换行...
  • (二)什么是Vite——Vite 和 Webpack 区别(冷启动)
  • (七)Java对象在Hibernate持久化层的状态
  • (全部习题答案)研究生英语读写教程基础级教师用书PDF|| 研究生英语读写教程提高级教师用书PDF
  • (四)鸿鹄云架构一服务注册中心
  • (一) storm的集群安装与配置
  • (转)ObjectiveC 深浅拷贝学习
  • .Net MVC4 上传大文件,并保存表单
  • .net6解除文件上传限制。Multipart body length limit 16384 exceeded
  • .NET8.0 AOT 经验分享 FreeSql/FreeRedis/FreeScheduler 均已通过测试
  • .Net各种迷惑命名解释
  • .NET与java的MVC模式(2):struts2核心工作流程与原理
  • .xml 下拉列表_RecyclerView嵌套recyclerview实现二级下拉列表,包含自定义IOS对话框...
  • ??javascript里的变量问题