微信支付接口的调用(转)

微信支付接口的调用

2018年04月13日 10:53:16 Forward_duyu 阅读数:20092

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zoroduyu/article/details/79911278

在上周的博客中我讲了调用支付宝的接口实现支付,这周我们继续来讲一讲如何调用微信的支付接口。 
在讲之前依然先给出微信的官方接口说明。官方的场景介绍图如下:

其实pc端的支付场景都差不多,用户点击按钮,生成一个二维码,微信扫码之后支付成功。要调用微信的接口,首先你需要引入微信支付的jar包,如下:

        <dependency>
            <groupId>com.github.wxpay</groupId>
            <artifactId>wxpay-sdk</artifactId>
            <version>0.0.3</version>
        </dependency>

我把微信官方的调用示例拿来改了一下,成为了下面这个工具类:

package com.example.ffmpeg;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.wxpay.sdk.WXPay;

public class WXService {

    private static Logger logger = LoggerFactory.getLogger(WXService.class);
    private WXPay wxpay;
    private WXPayConfigImpl config;
    private static WXService INSTANCE;  

    private WXService() throws Exception {
        config = WXPayConfigImpl.getInstance();
        wxpay = new WXPay(config);
    }

    public static WXService getInstance() throws Exception {
        if (INSTANCE == null) {
            synchronized (WXPayConfigImpl.class) {
                if (INSTANCE == null) {
                    INSTANCE = new WXService();
                }
            }
        }
        return INSTANCE;
    }

    /**
     * 微信下單接口
     *
     * @param out_trade_no
     * @param body
     * @param money
     * @param applyNo
     * @return
     */
    public String doUnifiedOrder(String out_trade_no, String body, Double money, String applyNo) {
        String amt = String.valueOf(money * 100);
        HashMap<String, String> data = new HashMap<String, String>();
        data.put("body", body);
        data.put("out_trade_no", out_trade_no);
        data.put("device_info", "web");
        data.put("fee_type", "CNY");
        data.put("total_fee", amt.substring(0, amt.lastIndexOf(".")));
        data.put("spbill_create_ip", config.getSpbillCreateIp());
        data.put("notify_url", config.getNotifUrl());
        data.put("trade_type", config.getTradeType());
        data.put("product_id", applyNo);
        System.out.println(String.valueOf(money * 100));
        // data.put("time_expire", "20170112104120");

        try {
            Map<String, String> r = wxpay.unifiedOrder(data);
            logger.info("返回的参数是" + r);
            return r.get("code_url");
        } catch (Exception e) {
            e.printStackTrace();
            logger.info(e.getMessage());
            return null;
        }
    }

    /**
     * 退款 已测试
     */
    public void doRefund(String out_trade_no, String total_fee) {
        logger.info("退款时的订单号为:" + out_trade_no + "退款时的金额为:" + total_fee);
        String amt = String.valueOf(Double.parseDouble(total_fee) * 100);
        logger.info("修正后的金额为:" + amt);
        logger.info("最终的金额为:" + amt.substring(0, amt.lastIndexOf(".")));
        HashMap<String, String> data = new HashMap<String, String>();
        data.put("out_trade_no", out_trade_no);
        data.put("out_refund_no", out_trade_no);
        data.put("total_fee", amt.substring(0, amt.lastIndexOf(".")));
        data.put("refund_fee", amt.substring(0, amt.lastIndexOf(".")));
        data.put("refund_fee_type", "CNY");
        data.put("op_user_id", config.getMchID());

        try {
            Map<String, String> r = wxpay.refund(data);
            logger.info("退款操作返回的参数为" + r);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 微信验签接口
     *
     * @param out_trade_no
     * @param body
     * @param money
     * @param applyNo
     * @return
     * @throws DocumentException
     */
    public boolean checkSign(String  strXML) throws DocumentException {
         SortedMap<String, String> smap = new TreeMap<String, String>();
         Document doc = DocumentHelper.parseText(strXML);
         Element root = doc.getRootElement();
         for (Iterator iterator = root.elementIterator(); iterator.hasNext();) {
             Element e = (Element) iterator.next();
             smap.put(e.getName(), e.getText());
         }
         return isWechatSign(smap,config.getKey());
    }

    private boolean isWechatSign(SortedMap<String, String> smap,String apiKey) {
         StringBuffer sb = new StringBuffer();
         Set<Entry<String, String>> es = smap.entrySet();
         Iterator<Entry<String, String>> it = es.iterator();
         while (it.hasNext()) {
             Entry<String, String> entry =  it.next();
             String k = (String) entry.getKey();
             String v = (String) entry.getValue();
             if (!"sign".equals(k) && null != v && !"".equals(v) && !"key".equals(k)) {
                 sb.append(k + "=" + v + "&");
             }
         }
         sb.append("key=" + apiKey);
         /** 验证的签名 */
         String sign = MD5Util.MD5Encode(sb.toString(), "utf-8").toUpperCase();
         /** 微信端返回的合法签名 */
         String validSign = ((String) smap.get("sign")).toUpperCase();
         return validSign.equals(sign);
     }
}

我把微信的下单,退款,验签操作封装到了WXService 这个工具类里面。这个类需要两个成员变量wxpay和config,分别是WXPay和WXPayConfigImpl的实例化对象。WXPay是引自微信的工具包。WXPayConfigImpl则是自己写的一个类,代码如下:

package com.example.ffmpeg;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

import com.github.wxpay.sdk.WXPayConfig;

public class WXPayConfigImpl implements WXPayConfig{

     private byte[] certData;
        private static WXPayConfigImpl INSTANCE;

        private WXPayConfigImpl() throws Exception{
            String certPath = "F:\\weixin\\apiclient_cert.p12";
            File file = new File(certPath);
            InputStream certStream = new FileInputStream(file);
            this.certData = new byte[(int) file.length()];
            certStream.read(this.certData);
            certStream.close();
        }

        public static WXPayConfigImpl getInstance() throws Exception{
            if (INSTANCE == null) {
                synchronized (WXPayConfigImpl.class) {
                    if (INSTANCE == null) {
                        INSTANCE = new WXPayConfigImpl();
                    }
                }
            }
            return INSTANCE;
        }

        public String getAppID() {
            return "你的appid";
        }

        public String getMchID() {
            return "你的商户id";
        }

        public String getKey() {
            return "你设置的key值";
        }

        public String getNotifUrl() {
            return "微信通知回调的url接口";
        }

        public String getTradeType() {
            return "NATIVE";
        }

        public InputStream getCertStream() {
            ByteArrayInputStream certBis;
            certBis = new ByteArrayInputStream(this.certData);
            return certBis;
        }

        public int getHttpConnectTimeoutMs() {
            return 2000;
        }

        public int getHttpReadTimeoutMs() {
            return 10000;
        }

//      IWXPayDomain getWXPayDomain() {
//          return WXPayDomainSimpleImpl.instance();
//      }

        public String getPrimaryDomain() {
            return "api.mch.weixin.qq.com";
        }

        public String getAlternateDomain() {
            return "api2.mch.weixin.qq.com";
        }

        public int getReportWorkerNum() {
            return 1;
        }

        public int getReportBatchSize() {
            return 2;
        }

        public String getSpbillCreateIp() {
            // TODO Auto-generated method stub
            return "192.168.1.1";
        }
}

可以看到,这个类实现了微信提供的WXPayConfig这个接口,里面封装了一些方法,主要是返回微信接口所需要的一些参数。值得注意的是,这里需要去读取一个文件名叫apiclient_cert.p12的证书文件。这个证书文件你可以登录微信的商户平台。在这里去下载你所需要的证书。WXPayConfigImpl 在构造方法里面去读取这个文件,所以构造方法抛了异常。因为构造器抛出异常,所以这里没有采用静态内部类而是采用双检锁的方式去实现单例。

回到WXService这个类中,代码往下走,在WXService的构造器中对config和wxpay进行了实例化。接下来同样是用双检锁的方式实现的单例。往下走,微信的下单接口,分别传入out_trade_no(外部订单号),body(商品描述), money(付款金额), applyNo(对应微信的product_id:商品id,由商户自定义)四个参数。进入方法后第一句话String amt = String.valueOf(money * 100);是把传入的钱数乘以100,并转换成字符串。这里之所以乘以100是因为微信那边会把我们传过去的钱数除以100得到应付金额,且不能传小数,所以下面的那一句amt.substring(0, amt.lastIndexOf(“.”))就是为了把金额中的小数点去掉。往下走,new出了一个hashmap,将参数传入hashmap中,然后调用wxpay.unifiedOrder(data);下单接口下单。得到返回的map集合,从map中获得的code_url这个参数就是微信返回给我们生成二维码的字符串。这样,下单的整个流程就跑通了,现在写个测试类来测试一下。

package com.example.ffmpeg;

public class Test {

    public static void main(String[] args) throws Exception {
        WXService wx = WXService.getInstance();
        String QRcode = wx.doUnifiedOrder("test001", "测试下单接口", 0.01, "a123456");
        System.out.println("得到的二维码是:"+QRcode);
    }
}

运行结果如下图:

如何检验该二维码是否是正确的喃?很简单,打开百度,搜索二维码生成器,如下图所示:

点击进入第二个百度应用里面的进入应用,出现如下图所示:

选择通用文本,在中间的文本框中粘贴刚才拿到的二维码字符串,点击生成按钮,右边就会生成一个二维码了。如下:

当然,这只是我们后台人员测试时使用的方法,实际生产环境中前端可以用一些javascript的插件去生成二维码。

下单接口完了之后,紧接着就是退款的方法,该方法比较简单且和下单方法大同小异,同学们自己看看注释应该可以理解了。再往后走是微信验签的方法。在讲这个方法之前,先来看看微信的回调方法:

/**
     * 微信回调的接口
     *
     * @param uuid
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/wxReturnPay")
    public void wxReturnPay(HttpServletResponse response, HttpServletRequest request)
            throws Exception {

        logger.info("****************************************wxReturnPay微信的回调函数被调用******************************");
        String inputLine;
        String notityXml = "";
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        response.setHeader("Access-Control-Allow-Origin", "*");
        // 微信给返回的东西
        try {
            while ((inputLine = request.getReader().readLine()) != null) {
                notityXml += inputLine;
            }
            request.getReader().close();
        } catch (Exception e) {
            e.printStackTrace();
            logger.info("xml获取失败");
            response.getWriter().write(setXml("fail", "xml获取失败"));
            return;
        }
        if (StringUtils.isEmpty(notityXml)) {
            logger.info("xml为空");
            response.getWriter().write(setXml("fail", "xml为空"));
            return;
        }

        WXService wxService = WXService.getInstance();
        if(!wxService.checkSign(notityXml)) {
            response.getWriter().write(setXml("fail", "验签失败"));
        }
        logger.info("xml的值为:" + notityXml);
        XMLSerializer xmlSerializer = new XMLSerializer();
        JSON json = xmlSerializer.read(notityXml);
        logger.info(json.toString());
         JSONObject jsonObject=JSONObject.fromObject(json.toString());
         UnifiedOrderRespose returnPay = (UnifiedOrderRespose) JSONObject.toBean(jsonObject, UnifiedOrderRespose.class);
         logger.info(("转换后的实体bean为:"+returnPay.toString()));
         logger.info(("订单号:"+returnPay.getOut_trade_no()+"价格:"+returnPay.getTotal_fee()));
        if (returnPay.getReturn_code().equals("SUCCESS") && returnPay.getOut_trade_no() != null
                && !returnPay.getOut_trade_no().isEmpty()) {
            double fee = Double.parseDouble(returnPay.getTotal_fee());
            returnPay.setTotal_fee(String.valueOf(fee/100));
            logger.info("微信的支付状态为SUCCESS");
            tbPaymentRecordsService.wxPaySuccess(returnPay);
        }
    }

在支付成功后,微信会回调该方法(回调的url是我们在调用下单接口时传过去的)。进入方法,首先会获得HttpServletRequest 实例对象的流,将他读取出来,这里面notityXml 是微信读取出来的结果,是一串xml格式的字符串,里面有各种回调的参数信息,示例返回结果如下:

<xml>
  <appid><![CDATA[wx2421b1c4370ec43b]]></appid>
  <attach><![CDATA[支付测试]]></attach>
  <bank_type><![CDATA[CFT]]></bank_type>
  <fee_type><![CDATA[CNY]]></fee_type>
  <is_subscribe><![CDATA[Y]]></is_subscribe>
  <mch_id><![CDATA[10000100]]></mch_id>
  <nonce_str><![CDATA[5d2b6c2a8db53831f7eda20af46e531c]]></nonce_str>
  <openid><![CDATA[oUpF8uMEb4qRXf22hE3X68TekukE]]></openid>
  <out_trade_no><![CDATA[1409811653]]></out_trade_no>
  <result_code><![CDATA[SUCCESS]]></result_code>
  <return_code><![CDATA[SUCCESS]]></return_code>
  <sign><![CDATA[B552ED6B279343CB493C5DD0D78AB241]]></sign>
  <sub_mch_id><![CDATA[10000100]]></sub_mch_id>
  <time_end><![CDATA[20140903131540]]></time_end>
  <total_fee>1</total_fee>
<coupon_fee><![CDATA[10]]></coupon_fee>
<coupon_count><![CDATA[1]]></coupon_count>
<coupon_type><![CDATA[CASH]]></coupon_type>
<coupon_id><![CDATA[10000]]></coupon_id>
<coupon_fee><![CDATA[100]]></coupon_fee>
  <trade_type><![CDATA[JSAPI]]></trade_type>
  <transaction_id><![CDATA[1004400740201409030005092168]]></transaction_id>
</xml> 

返回参数是这个样子的那后面自然涉及到对xml参数的解析。进入验签的方法:

/**
     * 微信验签接口
     *
     * @param out_trade_no
     * @param body
     * @param money
     * @param applyNo
     * @return
     * @throws DocumentException
     */
    public boolean checkSign(String  strXML) throws DocumentException {
         SortedMap<String, String> smap = new TreeMap<String, String>();
         Document doc = DocumentHelper.parseText(strXML);
         Element root = doc.getRootElement();
         for (Iterator iterator = root.elementIterator(); iterator.hasNext();) {
             Element e = (Element) iterator.next();
             smap.put(e.getName(), e.getText());
         }
         return isWechatSign(smap,config.getKey());
    }

    private boolean isWechatSign(SortedMap<String, String> smap,String apiKey) {
         StringBuffer sb = new StringBuffer();
         Set<Entry<String, String>> es = smap.entrySet();
         Iterator<Entry<String, String>> it = es.iterator();
         while (it.hasNext()) {
             Entry<String, String> entry =  it.next();
             String k = (String) entry.getKey();
             String v = (String) entry.getValue();
             if (!"sign".equals(k) && null != v && !"".equals(v) && !"key".equals(k)) {
                 sb.append(k + "=" + v + "&");
             }
         }
         sb.append("key=" + apiKey);
         /** 验证的签名 */
         String sign = MD5Util.MD5Encode(sb.toString(), "utf-8").toUpperCase();
         /** 微信端返回的合法签名 */
         String validSign = ((String) smap.get("sign")).toUpperCase();
         return validSign.equals(sign);
     }

首先用dom4j的DocumentHelper解析字符串得到Document 对象,然后得到跟元素对象,遍历,将key和value值存入SortedMap中,然后将SortedMap与微信的key一同传入isWechatSign方法,在方法中讲该SortedMap用迭代器遍历,把key和value拼接成key1=value1&key2=value2这样的形式,拼接时且注意以下几点:

◆ 参数名ASCII码从小到大排序(字典序); 
◆ 如果参数的值为空不参与签名; 
◆ 参数名区分大小写; 
◆ 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。 
◆ 微信接口可能增加字段,验证签名时必须支持增加的扩展字段

字符串拼接完成后,使用MD5加密,然后把得到的字符串全部转换成大写。将转换后的字符串与微信传给我们的sign参数作对比,若相同,则验签成功,若不相同,则验签失败。

验证签名之后的方法就属于业务逻辑范畴了,每个人业务逻辑都不相同,我这里也就不再赘述了。那么到此,调用微信的最基本操作我就已经讲完了,想看如何调用支付宝的同学可以去看我的支付宝支付接口的调用这篇文章。那么这次博客就到这里,拜拜。

原文地址:https://www.cnblogs.com/LiZhongZhongY/p/10992552.html

时间: 2024-10-07 19:10:40

微信支付接口的调用(转)的相关文章

调用微信支付接口总结

毕业设计项目里面用到了微信支付的功能,在视频教程里面学习了一下,这里做个总结来记录.教材的模板里面用到的是anglusJS来做前端开发,我这边简化了,用的ajax进行异步的调用,然后业务也直接写在了控制层(不过不应该这么写的..),应该是个简洁明了的模板,主要是让自己理解整个流程. 1 关于在调用微信支付接口之前的一些准备工作,申请公众号,认证,拿到密钥等等操作就不概述了, 微信的在线支付文档中都有描述,这里贴出地址 :https://pay.weixin.qq.com/wiki/doc/api

微信小程序&amp;PHP 微信支付接口调用

小程序端 /** * 微信支付接口 */ wxPaymoney:function (out_trade_no, true_money){ //out_trade_no 后台统一下单接口需要用 var that = this wx.hideToast() //隐藏toast wx.request({ method: 'POST', data: { openid: '************',    //调用人的openid out_trade_no: out_trade_no, body: '答

php微信支付接口开发程序(流程已通)

php微信支付接口开发程序(流程已通) 来源:未知    时间:2014-12-11 17:11   阅读数:11843   作者:xxadmin [导读] 微信支付接口现在也慢慢的像支付宝一个可以利用api接口来实现第三方网站或应用进行支付了,下文整理了一个php微信支付接口开发程序并且己测试,有兴趣的朋友可进入参考. 必要条... 微信支付接口现在也慢慢的像支付宝一个可以利用api接口来实现第三方网站或应用进行支付了,下文整理了一个php微信支付接口开发程序并且己测试,有兴趣的朋友可进入参考

php微信支付接口开发程序

php微信支付接口开发程序讲解 微信支付接口现在也慢慢的像支付宝一个可以利用api接口来实现第三方网站或应用进行支付了, 下文整理了一个php微信支付接口开发程序并且己测试,有兴趣的朋友可进入参考. 必要条件: appid //公众号后台开发者中心获得(和邮件内的一样)   mchid//邮件内获得  key//商户后台自己设置  appsecret //公众号开发者中心获得 两个证书文件,邮件内获得 apiclient_cert.pem   apiclient_key.pem 注意事项: 公众

关于在微信支付接口和支付宝接口中使用到的辅助函数

刚才在shell中敲代码的时候,习惯性地用tab键去补全,然后就一直报错,所以以后记得不要在vim编写代码的时候去用tab补全. 然后现在是总结一下在生成两个接口数据时使用到的一些辅助函数,因为我自己是先开发微信支付接口使用的,所以先总结微信支付的函数,首先是生成随机字符串,我自己是先生成一个26个小写字母和0到9的数字的字符串,然后从中随机挑选32个字符加入一个list,然后用join联合函数直接组成随机字符串,代码如下: def nonce_str(self): # 生成随机字符 chars

php微信支付接口开发的实现程序

微信支付接口现在也慢慢的像支付宝一个可以利用api接口来实现第三方网站或应用进行支付了,下文是我公司的技术人员整理了一个php微信支付接口开发程序并且己测试,有兴趣的朋友可进入参考. 必要条件: appid //公众号后台开发者中心获得(和邮件内的一样) mchid//邮件内获得 key//商户后台自己设置 appsecret //公众号开发者中心获得 两个证书文件,邮件内获得 apiclient_cert.pem apiclient_key.pem 注意事项: 公众号后台微信支付->开发配置-

支付宝支付接口的调用(转)

支付宝支付接口的调用 2018年04月07日 17:54:51 Forward_duyu 阅读数:15167 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/zoroduyu/article/details/79825880 应公司业务要求,需要调用支付宝的支付接口进行支付的操作,于是将整个调用过程用博客形式记录下来,以供以后使用. 本次调用支付宝采用的是电脑支付,官方文档页面如下: 电脑端调用支付宝,流程很简单,在页面有一个立即支付的按钮,点击

个人商家怎么申请微信支付接口

微信支付接口:点击进去 一直让大家翘首以盼的微信最核心的杀手锏——“微信支付”终于开放申请了!今天微信团队在官网发布消息,微信公众平台支付功能已正式开放申请,已开通公众号的开发者可以登录公众平台申请App支付功能.未开通公众号的APP开发者,我们将在近期开放,届时可在微信开放平台申请. 微信官方正式宣布,此前尚处于内测阶段的微信支付接口,即日起对通过微信认证的服务号全面开放.需要申请微信支付接口的商家,首先需要申请成为服务号,并在申请微信认证,签订合同并缴纳保证金后,申请全网发布,即可开通微信支

怎么申请微信支付接口

3月6日,微信官方正式宣布,此前尚处于内测阶段的微信支付接口,对通过微信认证的服务号全面开放.需要申请微信支付接口的商家,首先需要申请成为服务号,并在申请微信认证,签订合同并缴纳保证金后,申请全网发布,即可开通微信支付能力. 工具/原料 微信认证公众服务号,财务通企业账户 商户营业执照扫描件,组织机构代码扫描件 1.申请微信公众服务号 1 登录微信公众平台,点击右上角注册. 2 设置登录帐号基本信息. 3 査收自己的注册邮箱,点击链接,激活帐号. 4 选择自己对应的选项,进行商户信息登记. 5