其实关于微信支付,网上已经有很多的案例.今天主要是介绍下关于微信支付,退款,转账中遇到的那些容易而且极易被忽略的一些坑.至于key,mcid,oppid和证书下载网上已经很全面了 项目依赖
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
配置类:
public class WXConfigUtil implements WXPayConfig {
private final byte[] certData;
private String mchId;
private String key;
public WXConfigUtil(String certPath, String key, String mchId) throws Exception {
//从微信商户平台下载的安全证书存放的路径
File file = new File(certPath);
InputStream certStream = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int n;
while ((n = certStream.read(b)) != -1) {
bos.write(b, 0, n);
}
certStream.close();
bos.close();
//将读入的证书加载到cerData(这一步在网上的文章几乎都是没用)
this.certData = bos.toByteArray();
this.mchId = mchId;
this.key = key;
}
@Override
public String getAppID() {
return WechatConstant.APP_ID;
}
@Override
public String getMchID() {
return mchId;
}
@Override
public String getKey() {
return key;
}
@Override
public InputStream getCertStream() {
//请求时获取证书
return new ByteArrayInputStream(this.certData);
}
@Override
public int getHttpConnectTimeoutMs() {
return 8000;
}
@Override
public int getHttpReadTimeoutMs() {
return 10000;
}
}
上面的配置其实比较关键的就是证书读取的配置.废话也不多数,下面就是统一的下单接口的处理: 支付:(这里已app支付为例)
Map<String, String> response;
try {
WXConfigUtil wxConfigUtil = new WXConfigUtil(certPath, key, mchid);
WXPay wxPay = new WXPay(wxConfigUtil);
Map<String, String> data = new HashMap<>();
//随机生成商户订单号
System.out.println("商户订单号------------" + orderNo);
//商户号
data.put("mch_id", wxConfigUtil.getMchID());
//随机字符串
data.put("nonce_str", WXPayUtil.generateNonceStr());
//商品描述
data.put("body", "描述");
//商品订单号
data.put("out_trade_no", orderNo);
// 总金额(单位分)
data.put("total_fee",100);
//终端IP
data.put("spbill_create_ip", "124.xx.xx.xx");
//回调地址
data.put("notify_url", notifyUrl);
//appid
data.put("appid", wxConfigUtil.getAppID());
//交易类型
data.put("trade_type", "APP");
data.put("attach", wechatVo.getUserId().toString());
//生成签名
String sign = WXPayUtil.generateSignature(data, wxConfigUtil.getKey(), WXPayConstants.SignType.MD5);
data.put("sign", sign);
String str = WXPayUtil.mapToXml(data);
System.out.println("map转xml" + str);
System.out.println("我给的数据是" + data);
System.out.println("第一次签名------------------" + sign);
//使用官方API请求预付订单
response = wxPay.unifiedOrder(data);
} catch (Exception e) {
e.printStackTrace();
return null;
}
//返回结果,可以自己封装
return response;
企业转账到个人:
Map<String, String> restmap = null;
try {
WXConfigUtil wxConfigUtil = new WXConfigUtil(certPath, key, mchid);
WXPay wxPay = new WXPay(wxConfigUtil);
Map<String, String> param = new HashMap<String, String>();
//公众账号appid
param.put("mch_appid", wxConfigUtil.getAppID());
//商户号
param.put("mchid", wxConfigUtil.getMchID());
//随机字符串
param.put("nonce_str", WXPayUtil.generateNonceStr());
//商户订单号
param.put("partner_trade_no", "");
//用户openid
param.put("openid", wechatVo.getOpenId());
//校验用户姓名选项 OPTION_CHECK
param.put("check_name", "NO_CHECK");
param.put("amount","");
//企业付款描述信息
param.put("desc", "withdraw");
//服务器Ip地址
param.put("spbill_create_ip", "124.xx.xx.xx");
param.put("sign", WXPayUtil.generateSignature(param, wxConfigUtil.getKey()));
//携带证书请求
String restxml = wxPay.requestWithCert("https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers", param, 15000, 15000);
assert restxml != null;
restmap = WXPayUtil.xmlToMap(restxml);
} catch (Exception e) {
System.err.println(e.getMessage());
}
//返回请求结果
return restmap;
退款:
HashMap<String, String> data = new HashMap<String, String>();
String orderNo = "VxRefund_" + System.currentTimeMillis();
try {
WXConfigUtil wxConfigUtil = new WXConfigUtil(certPath, key, mchid);
WXPay wxPay = new WXPay(wxConfigUtil);
data.put("appid", wxConfigUtil.getAppID());
data.put("mch_id", wxConfigUtil.getMchID());
data.put("nonce_str", WXPayUtil.generateNonceStr());
//微信订单号 流水号____
data.put("transaction_id", "支付的流水号");
//商户退款单号 退款单号 ___
data.put("out_refund_no", orderNo);
//支付金额,微信支付提交的金额是不能带小数点的,且是以分为单位,这边需要转成字符串类型,否则后面的签名会失败
data.put("total_fee", "");
//退款总金额,订单总金额,单位为分,只能为整数
data.put("refund_fee", "");
data.put("op_user_id", wxConfigUtil.getMchID());
//MD5运算生成签名,这里是第一次签名,用于调用统一下单接口
String sign = WXPayUtil.generateSignature(data, wxConfigUtil.getKey());
//生成签名
data.put("sign", sign);
//使用携带证书请求
String s = wxPay.requestWithCert("https://api.mch.weixin.qq.com/secapi/pay/refund", data, 15000, 15000);
Map<String, String> response = WXPayUtil.xmlToMap(s);
if ("SUCCESS".equals(returnCode) && returnCode.equals(resultCode)) {
//todo 自己的业务逻辑
} else {
return ResultInfo.error("退款失败");
}
} catch (Exception e) {
e.printStackTrace();
}
return ResultInfo.successful("退款成功");
统一回调:
StringBuilder sb = new StringBuilder();
try {
//在获取wx回调数据
BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream()));
String line = null;
new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line);
}
br.close();
} catch (IOException e) {
e.printStackTrace();
}
//sb为微信返回的xml
String notityXml = sb.toString();
System.out.println("======================微信支付异步结果逻辑处理开始=================================");
WXConfigUtil config = null;
try {
config = new WXConfigUtil(certPath, key, mchid);
} catch (Exception e) {
e.printStackTrace();
}
WXPay wxpay = new WXPay(config);
String xmlBack = "";
Map<String, String> notifyMap = null;
try {
// 调用官方SDK转换成map类型数据
notifyMap = WXPayUtil.xmlToMap(notityXml);
System.out.println("返回的map----------------" + notifyMap);
//验证签名是否有效,有效则进一步处理
System.out.println("返回的错误代码--------" + notifyMap.get("err_code") + "返回的错误信息--------" + notifyMap.get("err_code_des"));
if (wxpay.isPayResultNotifySignatureValid(notifyMap)) {
//状态
String returnCode = notifyMap.get("return_code");
//商户订单号
String outTradeNo = notifyMap.get("out_trade_no");
String userId = notifyMap.get("attach");
if ("SUCCESS".equals(returnCode)) {
if (outTradeNo != null) {
//业务数据持久化
String transactionId = notifyMap.get("transaction_id");
//todo 业务逻辑
//..........
System.err.println("-------------------------------支付成功----------------------");
xmlBack = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
} else {
xmlBack = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
}
}
} else {
// 签名错误,如果数据里没有sign字段,也认为是签名错误
//失败的数据要不要存储?
xmlBack = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
}
} catch (Exception e) {
xmlBack = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
}
return ResultInfo.successful(xmlBack);
以上只是一些浅显的业务逻辑,希望对大家有所帮助