微信刷卡支付API详解

最近因项目需要微信支付,通过扫码抢扫描微信付款码,调用微信刷卡支付API完成扣费,过程中遇到了遇到了一些问题,填了很多坑,所以把自己的经验分享给大家,本篇文章介绍如何使用刷卡支付API。

场景:

收银员使用扫码设备读取微信用户刷卡授权码以后,二维码或条码信息传送至商户收银台,由商户收银台或者商户后台调用该接口发起支付。首先我们看一下微信官方提供的支付demo,链接:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=5_1#

*对于一些必选参数,即使没什么用,也一定要传。

map.put("appid","你的公众号appid");
map.put("mch_id", "微信支付商户号");
map.put("sub_mch_id", "微信支付分配的子商户号,服务商必填");
map.put("device_info", "终端设备号"));
map.put("body", "商品名称");
map.put("detail", "单品优惠功能字段");
map.put("attach","附加数据");
map.put("out_trade_no", "订单号,要求32个字符内");
map.put("total_fee","支付金额"));
map.put("fee_type","默认人民币:CNY");
map.put("spbill_create_ip",InetAddress.getLocalHost().getHostAddress().toString());//获取调用支付接口的机器终端ip
map.put("goods_tag",  "");
map.put("limit_pay",  "");
map.put("time_start", "");
map.put("auth_code","付款码" );
map.put("scene_info","");
map.put("nonce_str", "");
sign = APIUtil.createSign(map, apikey, "utf-8");
logger.info("sign:"+sign);
map.put("sign",sign);
xml = APIUtil.buildXML(map);
try {
    if(visitUrl!=""||visitUrl!=null) {
        reStr= APIUtil.post(visitUrl, xml);
        System.out.println("reStr:"+reStr);
        if (reStr != null ) {
            xmlMap = APIUtil.reqMsg2Map(reStr);
            String reSign = APIUtil.createSign(xmlMap, apikey, "utf-8");
            logger.info(xmlMap.toString());
            if (reSign.toLowerCase().equals(xmlMap.get("sign").toString().toLowerCase())) {
                logger.info("reSign", true);
            }
        }
    }
    logger.info("接口返回数据=" + reStr);
} catch (Exception e) {
    // TODO Auto-generated catch block
    logger.error(e.getMessage());
}
return xmlMap;

1.首先要明确微信支付是使用MD5进行签名的,这个是进行两次验签的,一次支付前进行验签,一次接收微信返回数据后进行验签,目的是确保这是一次交易。验签我们必须明确:

当然按ASCII排序直接一个TreeMap搞定(不熟悉TreeMap的孩纸们自行查阅资料)。

2. 我们要知道你发过去的数据是个XML,所以必须用把你的参数构造成XML形式。

3. 返回给你的数据也是XML,所以你得转两次。

4. 发送数据了,你发现返回给你签名错误,这时候从哪儿找问题呢?

(1)你的那些参数中有没有中文,有的话进行编码设置,可能你的数据已经乱码

(2)你的商户号和子商户号有没有写对(首先要明确一点,你是服务商还是特约客户,特约客户就是子商户,如果你是服务商,这两个参数你都应该有,如果没有请登陆商户平台进行查询)

(3) 你的密钥是否正确,加密的时候是拼接key的,所以确保你的key正确无误,如果不确定key,可以登陆商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置

(4)还有一种,这个就是我说的坑(话说微信的接口文档真叫一个烂),如果你确定你的密钥、商户号、子商户号、appid这些都正确的话,还报签名错误,那么你可以去他们官方的签名校验工具(https://pay.weixin.qq.com/wiki/doc/api/micropay_sl.php?chapter=20_1)检验下你的签名算法是否正确,如果计算出来的签名和你的签名算法算出来一样,那么恭喜你,踩坑了,重置你的密钥再试试(有可能会重置好几次哦,做好准备)。

(5) 如果很幸运,你成功调用了接口,这时候你高兴的递交了代码,静等测试给你反馈了,你觉得没问题,很完美。好了,梦醒了,继续测吧,bug很多:

① 你发现钱扣了,但是返回失败,为什么呢?

明确一个很重要的点,return_code是通信表示,他返回成功知识说明你接口调通了,不能说明业务成功,业务成功看result_code。

② 你的用户有可能开通了免密支付,如果免密支付,那你这个代码很成功,no bug。如果你的用户设置了非免密支付,那么bug来了。这时候result_code也是FAIL,但是钱扣了,怎么办?如果你用debug调试,你会发现 err_code是USERPAYING,这就表明用户在输入密码,所以你得调查询订单接口,去查询订单,不要立即去查,每隔10秒。

如果这个时候result_code和rade_state都返回成功,这才表示用户支付成功了。

下来就是你根据自己的业务进行的一些操作,就不表述了,下来咋们看微信退款,支付是不需要证书的,但是退款需要双向证书。你先要想一想你把证书放哪儿,两个建议,第一种放数据库(以二进制的形式存在BLOB类型),第二种放在resource里面单独建一个文件夹。

我是以第二种形式存放的。

但是记得在pom.xml中配置。

微信退款和支付传参、加密等一摸一样,就不多说了,我只说重点,怎么加载呢?明确一下,咋们是不需要解析证书的!而且证书默认密码是你的商户号。

加载:

KeyStore keyStore  = KeyStore.getInstance("PKCS12");
FileInputStream instream = new FileInputStream(new File("文件目录"));//P12文件目录
try {
    keyStore.load(instream, "商户号".toCharArray());//这里写密码..默认是你的MCHID
} finally {
    instream.close();
}
SSLContext sslcontext = SSLContexts.custom()
        .loadKeyMaterial(keyStore, "商户号".toCharArray())//这里也是写密码的
        .build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
        sslcontext,
        new String[] { "TLSv1" },
        null,
        SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom()
        .setSSLSocketFactory(sslsf)
        .build();

这样你发现,加载成功了,但是你还是没有调用成功,因为你没设置响应头信息,传过去是乱码!!!!相信我,你需要它,在你加载证书的工具类里把这些东西加上就完美了:

try {
    HttpPost httpost = new HttpPost(url); // 设置响应头信息
    httpost.addHeader("Connection", "keep-alive");
    httpost.addHeader("Accept", "*/*");
    httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
    httpost.addHeader("Host", "api.mch.weixin.qq.com");
    httpost.addHeader("X-Requested-With", "XMLHttpRequest");
    httpost.addHeader("Cache-Control", "max-age=0");
    httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
    httpost.setEntity(new StringEntity(data, "UTF-8"));
    CloseableHttpResponse response = httpclient.execute(httpost);
    try {
        HttpEntity entity = response.getEntity();
        String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
        EntityUtils.consume(entity);
        return jsonStr;
    } finally {
        response.close();
    }
} finally {
    httpclient.close();
}

这样微信支付就完了,建议不要用撤销接口,没必要,以前一直给你们提供代码,现在发现思路更重要,所以就不提供源码了,有啥问题可以留言或者Email我,programmerSuperLuo@163.com.后面带你们踩踩支付宝的坑和银联的坑。

原文发布于微信公众号 - Java大联盟(javaunion)

原文发表时间:2018-06-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏c#开发者

C#开发终端式短信的原理和方法

简介   没发过短信的年轻人肯定是属于那种受保护的稀有动物,通讯发达的今天短信已经成为人们交流的重要手段,其中也蕴含着巨大的市场和经济利益,掌握短信技术的人才也...

47590
来自专栏函数式编程语言及工具

Akka(10): 分布式运算:集群-Cluster

   Akka-Cluster可以在一部物理机或一组网络连接的服务器上搭建部署。用Akka开发同一版本的分布式程序可以在任何硬件环境中运行,这样我们就可以确定以...

62190
来自专栏有刻

Java 小记 — RabbitMQ 的实践与思考

404100
来自专栏WindCoder

微信小程序踩坑记-Java基于SSM下的post请求

最近在持续踩微信小程序的坑,canvas和WebSocket的暂时还没找到相关的解决方案,暂时先将post请求无法获取data参数的坑填上。直接附上解决方案,已...

1.3K10
来自专栏恰童鞋骚年

操作系统核心原理-4.线程原理(下):死锁基础原理

我们都见过交通阻塞,一大堆汽车因为争夺行路权,互不相让而造成阻塞,又或者因为车辆发生故障抛锚或两辆车相撞而造成道路阻塞。在这种情况下,所有的车都停下来,谁也无...

14920
来自专栏逸鹏说道

我这么玩Web Api(一)

帮助页面或用户手册(Microsoft and Swashbuckle Help Page) 前言   你需要为客户编写Api调用手册?你需要测试你的Api接口...

32250
来自专栏草根专栏

使用xUnit为.net core程序进行单元测试(下2)

第1部分: https://cloud.tencent.com/developer/article/1019835

29770
来自专栏小樱的经验随笔

自己手动复现一个熊猫烧香病毒

最近逛了一下 bilibili ,偶然的一次机会,我在 bilibili 上看到了某个 up 主分享了一个他自己仿照熊猫病毒的原型制作的一个病毒的演示视频,虽然...

91220
来自专栏有趣的django

一个完整的Django入门指南(三)

第五部分  Introduction Welcome to the 5th part of the tutorial series! In this tutor...

51170
来自专栏开发与安全

linux网络编程之进程间通信基础(二):死锁、信号量与PV原语简介

一、死锁 (1) 死锁是指多个进程之间相互等待对方的资源,而在得到对方资源之前又不释放自己的资源,这样,造成循环等待的一种现象。如果所有进程都在等待一个不可能发...

27300

扫码关注云+社区

领取腾讯云代金券