一。下载sdk 。其实这个sdk就已经把微信支付的功能封装在里面了,已经算是一个成形的代码级应用了.sdk已经把要做的工作都做了,剩下的就是你去调用了,就这么简单。
先看一下这几个接口和类大概是干嘛的(不了解也丝毫不影响开发):
可以对照自己的SDK来看。
1.IWXPayDomain.java
实现域名管理的,不需要我们做工作。
这个抽象接口,测试包中已经帮助我们实现了,我们可以拿过来直接用。复制粘贴改个名字。
2.WXPayConstants
微信支付常量类,这其中的常量很多,但是也都很好理解,内部类枚举,限定了签名方式只能是MD5或者HMACSHA256。这里要注意的是,签名也是有SDK内部实现的,只有在使用到沙箱环境时,才用MD5签名。最后两
部分主要是请求后缀,只不过前者是生产环境,后边的带有的SANbox的是沙箱测试环境。为了直观,我把他们都标注出来了。主要分六部分:
你可以把你们公司的名字MD5加密一下做加密或者直接加密的方式,MD5加密。这么做的好处是,加密源好记,可以防止丢失。
3.WXPayConfig.java
这是一个抽象类,里边是一些微信支付的基本配置。是需要你自己继承并完善的。但是这个实现在SDK自带的测试包中已经实现了,直接把他复制过来。把自己的配置搞进去。
这一步主要的就是下载证书,在商户平台下载证书后,生产环境或者测试的电脑主机才可以调用微信支付下载证书后,放到指定位置,在配置一下路径,很简单,例如我的:
这样配置的实现类就完成了。
4.WXPay.java
看名字你就应该知道,这是最重要的类。就是这个类中已经封装好了所有方法,我们只需要在创建一个类,来调用其中的方法就可以了。
5.WXPayUtil.java
工具类,里边包含了要用到的方法,很全面。
微信支付接口传输数据是通过XML字符串来传输的,然后再两端再分别解析成映射结合。这是封装在内部的我们了解一下就可以了。还包括符号的生成,你看,签名都给你写好了。
当然,你也可以根据自己的需求在放一些其他的工具方法。
6.request和report我没怎么看。看到这里就足够了。
二。创建工具类WXPayTool.java
这里我就直接复制了。排版有点乱,但方便大家。这是测试用,大家可以在改一下。在这里我还新创建了一个类,也就是OrderData类。在传参的时候还要多写一些代码,为什么我要创建这个类呢?我看了微信签名的生成规则,如果字符串为空或者空字符串。那么签名时会自动过滤掉,不参与签名。这么做的好处就是,易于扩展,如果以后公司在需要什么其他的支付方法,也比较方便,在调用一个wxpay中的方法,并传进对应的参数就可以了。我把WXPayTool和OrderData.java的实体类粘贴出来。
公共类WXPayTool {
私人WXPay wxpay;
私人WXPayConfigImpl配置;
公共WXPayTool()抛出异常{
config = WXPayConfigImpl.getInstance();
wxpay =新的WXPay(config,false,false);
}
/ **
*公众号支付
*发起支付后--- >>通信代码,必然返回。请求是否成功代码。如果成功则不返回或者。
*得到预付单编号
* /
public Map <String,String> doUnifiedOrder(OrderData orderData){
Map <String,String> data = new HashMap <String,String>();
data.put(“body”,orderData.getBody());
data.put(“out_trade_no”,orderData.getout_trade_no());
data.put(“total_fee”,orderData.getTotal_fee());
data.put(“spbill_create_ip”,orderData.getSpbill_create_ip());
data.put(“time_start”,orderData.getTime_start());
data.put(“time_expire”,orderData.getTime_expire());
data.put(“notify_url”,orderData.getNotify_url());
data.put(“trade_type”,orderData.getTrade_type());
data.put(“product_id”,orderData.getProduct_id());
data.put(“openid”,orderData.getOpenid());
尝试{
Map <String,String> r = wxpay.unifiedOrder(data,1000,1000);
的System.out.println(R);
返回r;
catch(Exception e){
e.printStackTrace();
返回null;
}
}
/ **
*关闭订单
* @param out_trade_no预付单
* @返回
* /
public Map <String,String> doOrderClose(String out_trade_no){
的System.out.println( “关闭订单”);
HashMap <String,String> data = new HashMap <String,String>();
data.put(“out_trade_no”,out_trade_no);
尝试{
Map <String,String> r = wxpay.closeOrder(data);
的System.out.println(R);
返回r;
catch(Exception e){
e.printStackTrace();
返回null;
}
}
public Map <String,String> doOrderQuery(String out_trade_no){
的System.out.println( “查询订单”);
HashMap <String,String> data = new HashMap <String,String>();
data.put(“out_trade_no”,out_trade_no);
// data.put(“transaction_id”,“4008852001201608221962061594”);
尝试{
Map <String,String> r = wxpay.orderQuery(data,1000,1000);
的System.out.println(R);
返回r;
catch(Exception e){
e.printStackTrace();
返回null;
}
}
/ **
*退款
*已测试
* @返回
* /
public Map <String,String> doRefund(String out_trade_no,String total_fee){
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”,total_fee);
data.put(“refund_fee”,total_fee);
data.put(“refund_fee_type”,“CNY”);
//data.put(“op_user_id”,config.getMchID());
尝试{
Map <String,String> r = wxpay.refund(data);
的System.out.println(R);
返回r;
catch(Exception e){
e.printStackTrace();
返回null;
}
}
/ **
*查询退款
*已经测试
* @返回
* /
public Map <String,String> doRefundQuery(String out_trade_no){
HashMap <String,String> data = new HashMap <String,String>();
data.put(“out_refund_no”,out_trade_no);
尝试{
Map <String,String> r = wxpay.refundQuery(data);
的System.out.println(R);
返回r;
catch(Exception e){
e.printStackTrace();
返回null;
}
}
/ **
*对账单下载
*已测试
* @返回
* /
public Map <String,String> doDownloadBill(long time){
HashMap <String,String> data = new HashMap <String,String>();
data.put(“bill_date”,WXPayUtil.generateBillDateStrByLong(time));
data.put(“bill_type”,“ALL”);
尝试{
Map <String,String> r = wxpay.downloadBill(data);
的System.out.println(R);
返回r;
catch(Exception e){
e.printStackTrace();
返回null;
}
}
}
/ **
*微信支付统一下单,所用到的参数,全部为String
* /
公共类OrderData扩展BaseEntity实现Serializable {
/ *参数说明---->
* 1。这里包含的参数,是出去的配置中的其他参数,配置中包含了
* APPID,mchid,标志,signtype,nonce_str
* 2。余下的是下面这些,***标记的为必填项
* 3。参数的初始值都是“”空字符串
* 因为在微信支付官方的SDK中,实现了参数为空不参与签名
* 4。把所有参数都实现的目的是,易于扩展
* 5。由于我们的使用场景,是JSAPI微信公众号支付,我们把要选择参数标记一下用###来标识
* /
private String device_info =“”; //设备号
private String body =“”; //商品简单描述维康动力 - 医疗###
private String detail =“”; //商品详情
私人字符串附加=“”; //附加数据,在查询时,原样返回待定
private String out_trade_no =“”; //订单号***
private String fee_type =“”; //类型,不传,默认为CNY
private String total_fee =“”; //总计多少钱***
private String spbill_create_ip =“”; //用户端ip,必传***
private String time_start =“”; // ###订单生成时间--->这里指的是预付单生成时间###
private String time_expire =“”; // ###订单失效时间--->预付单失效后,如果用户还要提交支付,需要发起重新请求订单接口,获得新的预付单ID ###
private String goods_tag =“”; //订单优惠说明待定
私人字符串notify_url =“”; //回调地址***
private String trade_type =“”; // JSAPI ***
私人字符串product_id =“”; //商品ID,###
private String limit_pay =“”; //非信用卡支付
private String openid =“”; //用户的OpenID ***
private String scene_info =“”; //场景信息
三。具体流程
上一张图,这是我测试用的前端页面,这样看起来也比较清楚。
第一个接口,也就是统一下单接口。这一步相当于用户选中了一个商品,并生成了订单,而这个订单就是预付单。这里有几个需要注意的地方
1)spbill_create_ip的填写文档中指的是客户端的IP,使用了SDK提供的方法WXPayUtil.getCustomerIp(请求)。但是貌似没有什么效果,直接用的127.0.0.1也没问题。微信支付系统虽然要求传这个参数,但貌似对这个参数没有多大的处理。
下面的开始时间和结束时间,微信支付是有要求的,格式为YYYYMMDDHHMMSS,下面的两个方法是我封装的方法。
2)在统一下单提交的数据中要添加notify_url。这是在用户支付成功之后,微信支付系统返回给系统的数据。以XML字符串数据流在HTTP实体中传过来。这是很重要的,在这个过程中,我们需要把返回的数据与平台内数据做对比,查看信息是否准确。并且校验之后要把信息返回给微信支付系统的。如果配置了过滤器过滤器,别忘了放了这个方法。
这个接口主要获得的就是prepay_id预付单ID,有了预付单号之后就可以进行下一步,在JS中调起微信支付控件了。
大概是这样的:
3)在调用返回的数据是Map集合,这样传到前端解析时有问题的,所以使用JSON.fromObject(map)解析成net.json,再传到前端就不会有问题了,后续的接口这些也是需要注意的。
第二步,已经有了订单,用户决定要不要支付。在JS中调起控件,发起支付。点击发起支付,输入密码付款成功。这里需要注意的是
1)package参数,packge参数的内部是prepay_id ='prepay_id'。在这一步测试时,最好用iphone测试,我在使用安卓时,出错了但是没有任何提示。在使用苹果测试时,弹出了错误信息,total_fee参数错误,但是在我们传的签名参数中根本就没有total_fee这个参数。这就比较奇怪了,再次查看文档,发现还是自己看的不仔细。
主要检查了下prepay_id,我传参数的时候格式不对,pacakge是js中的关键字,我把prepay_id传到了后台,拼接出键值对:package:'prepay_id = xxxxxx',在前端取值时就出了问题。所以我直接把prepay_id设置为js的全局变量。在js传参时直接把这个参数传进去。
2)在这里我引入了一个获取JS调用的参数的方法,这个方法也很简单,直接把prepay_id传入后端,用SDK生成签名。这里要注意的是signType不是MD5.MD5是沙箱测试才会使用签名方式。
第三步查询订单。,查询订单是可以根据微信生成的订单号或者平台自己的订单号来查询当前订单的状态的这里要注意的是:
1)要先分清几个单号的概念。预付单ID,商户订单ID,微信系统订单ID。商户订单,这里商户订单是平台自己的自己生成的,在平台中标识,并且也传到微信系统中。预付单,在把商品信息提交后产生的预付款的单号,只是在微信系统中生成了订单,但是还没有支付。而微信系统订单则是已经支付完成之后的单号。
开始的时候,我是用预付单查询订单,但是报错为参数长度有误。开发文档中写的是微信系统订单和商户订单二选一,并且长度都是32位的。后来我做了一个骚操作,数了一下预付单ID的长度,的确的确,它是31位的!后来尝试用支付成功后返回的微信系统订单来查询订单,发现还是不行的.