微信支付

主要分一下几个大步骤:

一。下载sdk。其实这个sdk就已经把微信支付的功能封装在里面了,已经算是一个成形的代码级应用了。sdk已经把要做的工作都做了,剩下的就是你去调用了,就这么简单。

先看一下这几个接口和类大概是干嘛的(不了解也丝毫不影响开发) :

可以对照自己的sdk来看。

1.IWXPayDomain.java

实现域名管理的,不需要我们做工作。

这个抽象接口,test包中已经帮助我们实现了,我们可以拿过来直接用。复制粘贴改个名字。

2.WXPayConstants

微信支付常量类,这其中的常量很多,但是也都很好理解,内部类枚举,限定了签名方式只能是MD5或者HMACSHA256。这里要注意的是,签名也是有sdk内部实现的,只有在使用到沙箱环境时,才用MD5签名。最后两部分主要是请求后缀,只不过前者是生产环境,后边的带有sanbox的是沙箱测试环境。为了直观,我把他们都标注出来了。主要分六部分:

APPID商户号自己取,KEY是自己在微信商户平台配置的32位置。你可以把你们公司的名字MD5加密一下做KEY,加密方式,你可以自己利用以写的程序加密或者直接百度搜索MD5加密。这么做的好处是,加密源好记,可以防止丢失。

3.WXPayConfig.java

这是一个抽象类,里边是一些微信支付的基本配置。是需要你自己继承并完善的。但是这个实现在sdk自带的test包中已经实现了,直接把他复制过来。把自己的配置搞进去。

这一步主要的就是下载证书,在商户平台下载证书后,生产环境或者测试的电脑主机才可以调用微信支付。下载证书后,放到指定位置,在配置一下路径,很简单,例如我的:

这样配置的实现类就完成了。

4.WXPay.java

看名字你就应该知道,这是最重要的类。就是这个类中已经封装好了所有方法,我们只需要在创建一个类,来调用其中的方法就可以了。

5.WXPayUtil.java

工具类。里边包含了要用到的方法,很全面。

微信支付接口传输数据是通过xml字符串来传输的,然后再两端再分别解析成map结合。这是封装在内部的我们了解一下就可以了。还包括sign的生成,你看,签名都给你写好了。

当然,你也可以根据自己的需求在放一些其他的工具方法。

6.request 和 report我没怎么看。看到这里就足够了。

二。创建工具类 WXPayTool.java

这里我就直接复制了。排版有点乱,但方便大家。这是测试用,大家可以在改一下。在这里我还新创建了一个类,也就是OrderData类。在传参的时候还要多写一些代码,为什么我要创建这个类呢?我看了微信签名的生成规则,如果字符串为空或者空字符串。那么签名时会自动过滤掉,不参与签名。这么做的好处就是,易于扩展,如果以后公司在需要什么其他的支付方法,也比较方便,在调用一个wxpay中的方法,并传进对应的参数就可以了。我把WXPayTool和OrderData.java的实体类粘贴出来。

import java.util.HashMap;

import java.util.Map;

public class WXPayTool {

private WXPay wxpay;

private WXPayConfigImpl config;

public WXPayTool() throws Exception {

config = WXPayConfigImpl.getInstance();

wxpay = new WXPay(config,false,false);

}

/**

* 公众号支付

* 发起支付后--->>通信code,必然返回.请求是否成功code.如果成功则不返回 或者.

* 得到预付单id

*/

public Map doUnifiedOrder(OrderData orderData) {

Map data = new HashMap();

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

try {

Map r = wxpay.unifiedOrder(data,1000,1000);

return r;

} catch (Exception e) {

e.printStackTrace();

return null;

}

}

/**

* 关闭订单

* @param out_trade_no 预付单

* @return

*/

public Map doOrderClose(String out_trade_no) {

HashMap data = new HashMap();

data.put("out_trade_no", out_trade_no);

try {

Map r = wxpay.closeOrder(data);

return r;

} catch (Exception e) {

e.printStackTrace();

return null;

}

}

public Map doOrderQuery(String out_trade_no) {

HashMap data = new HashMap();

data.put("out_trade_no", out_trade_no);

//data.put("transaction_id", "4008852001201608221962061594");

try {

Map r = wxpay.orderQuery(data,1000,1000);

return r;

} catch (Exception e) {

e.printStackTrace();

return null;

}

}

/**

*

* @param out_trade_no

*/

public void doOrderReverse(String out_trade_no) {

HashMap data = new HashMap();

data.put("out_trade_no", out_trade_no);

//data.put("transaction_id", "4008852001201608221962061594");

try {

Map r = wxpay.reverse(data);

} catch (Exception e) {

e.printStackTrace();

}

}

/**

* 长链接转短链接

* 测试成功

* 此处是扫码支付,pos机支付用的

*/

public void doShortUrl() {

HashMap data = new HashMap();

data.put("long_url", long_url);

try {

Map r = wxpay.shortUrl(data);

} catch (Exception e) {

e.printStackTrace();

}

}

/**

* 退款

* 已测试

* @return

*/

public Map doRefund(String out_trade_no,String total_fee) {

HashMap data = new HashMap();

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

try {

Map r = wxpay.refund(data);

return r;

} catch (Exception e) {

e.printStackTrace();

return null;

}

}

/**

* 查询退款

* 已经测试

* @return

*/

public Map doRefundQuery(String out_trade_no) {

HashMap data = new HashMap();

data.put("out_refund_no", out_trade_no);

try {

Map r = wxpay.refundQuery(data);

return r;

} catch (Exception e) {

e.printStackTrace();

return null;

}

}

/**

* 对账单下载

* 已测试

* @return

*/

public Map doDownloadBill(long time) {

HashMap data = new HashMap();

data.put("bill_date", WXPayUtil.generateBillDateStrByLong(time));

data.put("bill_type", "ALL");

try {

Map r = wxpay.downloadBill(data);

return r;

} catch (Exception e) {

e.printStackTrace();

return null;

}

}

//获取沙箱key,不能用

public void doGetSandboxSignKey() throws Exception {

WXPayConfigImpl config = WXPayConfigImpl.getInstance();

HashMap data = new HashMap();

data.put("mch_id", config.getMchID());

data.put("nonce_str", WXPayUtil.generateNonceStr());

String sign = WXPayUtil.generateSignature(data, config.getKey());

data.put("sign", sign);

WXPay wxPay = new WXPay(config);

String result = wxPay.requestWithoutCert("https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey", data, 10000, 10000);

}

}

import java.io.Serializable;

/**

*/

public class OrderData extends BaseEntity implements Serializable{

/*参数说明 ---->

*1.这里包含的参数,是出去config中的其他参数,config中包含了

*appid,mchid,sign,signtype,nonce_str

*2.余下的是下面这些,***标记的为必填项

*3.参数的初始值都是 "" 空字符串

*因为在微信支付官方的sdk中,实现了参数为空不参与签名

*4.把所有参数都实现的目的是,易于扩展

*/

private String device_info = "";//设备号

private String body = ""; //商品简单描述 维康动力-医疗###

private String detail = ""; //商品详情

private String attach = ""; //附加数据,在查询时,原样返回 待定

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 = ""; //订单优惠说明 待定

private String notify_url = ""; //回调地址***

private String trade_type = ""; //JSAPI ***

private String product_id = ""; //商品id,###

private String limit_pay = ""; //非信用卡支付

private String openid = ""; //用户openid***

private String scene_info = ""; //场景信息

public OrderData() {

super();

// TODO Auto-generated constructor stub

}

public OrderData(String device_info, String body, String detail, String attach, String out_trade_no, String fee_type,

String total_fee, String spbill_create_ip, String time_start, String time_expire, String goods_tag,

String notify_url, String trade_type, String product_id, String limit_pay, String openid,

String scene_info) {

super();

this.device_info = device_info;

this.body = body;

this.detail = detail;

this.attach = attach;

this.out_trade_no = out_trade_no;

this.fee_type = fee_type;

this.total_fee = total_fee;

this.spbill_create_ip = spbill_create_ip;

this.time_start = time_start;

this.time_expire = time_expire;

this.goods_tag = goods_tag;

this.notify_url = notify_url;

this.trade_type = trade_type;

this.product_id = product_id;

this.limit_pay = limit_pay;

this.openid = openid;

this.scene_info = scene_info;

}

public String getDevice_info() {

return device_info;

}

public void setDevice_info(String device_info) {

this.device_info = device_info;

}

public String getBody() {

return body;

}

public void setBody(String body) {

this.body = body;

}

public String getDetail() {

return detail;

}

public void setDetail(String detail) {

this.detail = detail;

}

public String getAttach() {

return attach;

}

public void setAttach(String attach) {

this.attach = attach;

}

public String getout_trade_no() {

return out_trade_no;

}

public void setout_trade_no(String out_trade_no) {

this.out_trade_no = out_trade_no;

}

public String getFee_type() {

return fee_type;

}

public void setFee_type(String fee_type) {

this.fee_type = fee_type;

}

public String getTotal_fee() {

return total_fee;

}

public void setTotal_fee(String total_fee) {

this.total_fee = total_fee;

}

public String getSpbill_create_ip() {

return spbill_create_ip;

}

public void setSpbill_create_ip(String spbill_create_ip) {

this.spbill_create_ip = spbill_create_ip;

}

public String getTime_start() {

return time_start;

}

public void setTime_start(String time_start) {

this.time_start = time_start;

}

public String getTime_expire() {

return time_expire;

}

public void setTime_expire(String time_expire) {

this.time_expire = time_expire;

}

public String getGoods_tag() {

return goods_tag;

}

public void setGoods_tag(String goods_tag) {

this.goods_tag = goods_tag;

}

public String getNotify_url() {

return notify_url;

}

public void setNotify_url(String notify_url) {

this.notify_url = notify_url;

}

public String getTrade_type() {

return trade_type;

}

public void setTrade_type(String trade_type) {

this.trade_type = trade_type;

}

public String getProduct_id() {

return product_id;

}

public void setProduct_id(String product_id) {

this.product_id = product_id;

}

public String getLimit_pay() {

return limit_pay;

}

public void setLimit_pay(String limit_pay) {

this.limit_pay = limit_pay;

}

public String getOpenid() {

return openid;

}

public void setOpenid(String openid) {

this.openid = openid;

}

public String getScene_info() {

return scene_info;

}

public void setScene_info(String scene_info) {

this.scene_info = scene_info;

}

@Override

public String toString() {

return "OrderData [device_info=" + device_info + ", body=" + body + ", detail=" + detail + ", attach=" + attach

+ ", out_trade_no=" + out_trade_no + ", fee_type=" + fee_type + ", total_fee=" + total_fee

+ ", spbill_create_ip=" + spbill_create_ip + ", time_start=" + time_start + ", time_expire="

+ time_expire + ", goods_tag=" + goods_tag + ", notify_url=" + notify_url + ", trade_type=" + trade_type

+ ", product_id=" + product_id + ", limit_pay=" + limit_pay + ", openid=" + openid + ", scene_info="

+ scene_info + "]";

}

}

三。具体流程

上一张图,这是我测试用的前端页面,这样看起来也比较清楚。

第一个接口,也就是统一下单接口。这一步相当于用户选中了一个商品,并生成了订单,而这个订单就是预付单。这里有几个需要注意的地方

1)spbill_create_ip的填写文档中指的是客户端的ip,使用了sdk提供的方法WXPayUtil.getCustomerIp(request)。但是貌似没有什么效果,直接用的127.0.0.1也没问题。微信支付系统虽然要求传这个参数,但貌似对这个参数没有多大的处理。

这个接口主要获得的就是prepay_id预付单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位的!!!后来尝试用支付成功后返回的微信系统订单来查询订单,发现还是不行的。!还是长度错误,找了半天没找到原因。不管了用平台自己生成的,然后就能用了。如果你做到了这一步,那我就不用说了,wxpay中封装着订单查询方法呢,我就不做细描述了。

第四步,关闭订单。这里要注意的是,取消订单的时刻。

1)一个是用户自主发起的,下单了,但是我不想要了,可以关闭。还有就是订单超时,预付单id的有效期是两个小时。如果超时了,就不能在申请支付了,这个时候就会出现之前说过的total_fee异常。

2)如果用户还是要下单,我们就把这个 订单关闭,然后在利用用户之前生成的保存在数据库中信息再去申请一个预付单id。

第五步,申请退款。需要注意的是:

1)这个操作不是用户的操作,因为发起这个请求之后,微信支付系统就真的退款。所以用户的申请退款功能只是一个功能,这个功能请求到我们的后台,在根据情况,是否进行退款。当然,这个退款操作是可以在微信商户平台中完成的,但是这种不利于系统的操作。

第六步,查询退款。这个直接用系统单号来调用wxpay中封装的方法就可以了。

第七部,查询对账单。是以xml数据返回回来的。里边分通过\r\n换行来分割的。直接取出data,split("\r\n")就得到了一个数组strArr,数组有三个字符串,字符串是用逗号来分割的,但是这对于数据展示或者存入都是很不利的,我的做法是,新建一个数组obj[strArr.lengh],遍历strArr数组,split(",")每个字符串,得到的数组值在赋值给对应的obj[].刚开始尝试了二位数组,这也的确符合二位数据的数据结构,但是出了一点问题,最后以为数组就解决了。可以理解为在一个Object数组中存了诺干个对象,每个对象都是一个数组。之后的操作就方便了,无论你是展示在h5还是存入数据库,还是生成表格,都没问题了。

微信支付开发到此位置就已经讲的很清楚了,到这一步,流程已经走完了。我们就可以着手平台内部的开发了。

希望大家多多关注,不定期发送文章,什么不对的地方欢迎批评指正,希望大家能一起成长,成为牛逼的程序员!!!(ps.最好能摆脱单身,谁也别和我秀恩爱!)

  • 发表于:
  • 原文链接:https://kuaibao.qq.com/s/20180530G0MB3H00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券