对接微信支付,先聊一聊开发步骤:你需要提交必要参数请求微信下单,微信会给你返回一个微信下单号,表示下单成功,然后拿上下单号,和一些必须的参数再次请求微信调起微信支付,付款成功后,微信会访问你的回调地址(必须外网能访问的地址),给你返回信息,然后你需要在回调接口中进行业务处理,并给微信正确返回回调. 其中下单和付款都需要验证签名(就是判断他传给你的参数在传输中有没有被篡改,具体验证方式见微信说明,工具类微信开发文档上也有)
微信开发地址: https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
涉及工具类微信开发文档基本有,
链接:https://pan.baidu.com/s/1Ec89zdogUluKoyE1zBeIVA
提取码:7z0y
1.微信下单接口:
@ApiOperation(value = "微信下单,返回微信预付款订单号", notes = "传入付款金额,订单号 ,商品信息")
@PostMapping("/create/wxorder")
public Object createWXOrder(@RequestBody HWeiXinPayParam payParam) throws Exception {
SortedMap<String, String> sortedMap = new TreeMap<String, String>();
sortedMap.put("appid", wxPayConfigEntity.getApppid()); //应用ID
sortedMap.put("mch_id", wxPayConfigEntity.getMchid()); //商户号
String nonceStr = WXPayUtil.generateNonceStr(); //生成随机数
sortedMap.put("nonce_str", nonceStr); //随机字符串
sortedMap.put("body", "玖阅-" + payParam.getBody()); //商品描述
String orderSn = payParam.getOrderSn();//商户订单号
sortedMap.put("out_trade_no", orderSn); //商户订单号
Integer totalFee = payParam.getTotalFee() * 100;//总金额 需要乘以100 ,微信支付默认单位分
sortedMap.put("total_fee", totalFee + ""); //总金额
sortedMap.put("spbill_create_ip", wxPayConfigEntity.getSpbillcreateip()); //终端IP
sortedMap.put("notify_url", wxPayConfigEntity.getNotifyurl()); //通知地址
sortedMap.put("trade_type", wxPayConfigEntity.getTradetype()); //交易类型
//生成签名
String sign = WXPayUtil.generateSignature(sortedMap, wxPayConfigEntity.getKey(), WXPayConstants.SignType.MD5);
sortedMap.put("sign", sign);
//将Map转换为XML格式的字符串 发送给微信xml
String xml = WXPayUtil.generateSignedXml(sortedMap, wxPayConfigEntity.getKey(), WXPayConstants.SignType.MD5);
HttpClient httpClient = HttpClientBuilder.create().build();
HttpPost httpPost = new HttpPost(wxPayConfigEntity.getPayurl());
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(30000).setConnectTimeout(30000).build();
httpPost.setConfig(requestConfig);
StringEntity postEntity = new StringEntity(xml, "UTF-8");
httpPost.addHeader("Content-Type", "text/xml");
httpPost.setEntity(postEntity);
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
String string = EntityUtils.toString(httpEntity, "UTF-8"); //微信返回的xml信息
boolean bool = WXPayUtil.isSignatureValid(string, wxPayConfigEntity.getKey());//验证签名是否正确
Map map = new HashMap();
if (bool) {
Map<String, String> resultMap = WXPayUtil.xmlToMap(string);
String prepayId = resultMap.get("prepay_id");//微信下单号
String timeStamp = (new Date().getTime() / 1000) + ""; //时间戳
SortedMap<String, String> twoSortedMap = new TreeMap<String, String>();
twoSortedMap.put("appid", wxPayConfigEntity.getApppid()); //应用ID
twoSortedMap.put("partnerid", wxPayConfigEntity.getMchid()); //商户号
twoSortedMap.put("package", wxPayConfigEntity.getPackages()); //商户号
twoSortedMap.put("noncestr", nonceStr);//随机数
twoSortedMap.put("prepayid", prepayId); //微信下单号
twoSortedMap.put("timestamp", timeStamp); //时间戳
//二次签名 将请求参数返回前端,由前端app调起支付接口支付
String twoSign = WXPayUtil.generateSignature(twoSortedMap, wxPayConfigEntity.getKey(), WXPayConstants.SignType.MD5);
map.put("return_code", "操作成功");
map.put("prepayId", prepayId); //微信下单号
map.put("nonceStr", nonceStr); //随机数
map.put("sign", twoSign); //二次签名
map.put("timestamp", timeStamp);//时间戳
cartItemService.updateTotleFree(totalFee, prepayId, orderSn); //数据库保存信息 根据订单编号修改订单表保存微信需要付款总金额,支付微信下单号
return map;
}
map.put("return_code", "操作失败");
return map;
}
2.微信回调接口:
@ApiOperation(value = "微信支付回调地址,", notes = "")
@PostMapping("/callback/wxorder")
public Object callBack(HttpServletRequest request) throws Exception {
//读取微信回调的内容
InputStream inStream = request.getInputStream();
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
String resultxml = new String(outSteam.toByteArray(), "utf-8");
outSteam.close();
inStream.close();
//xml转成map
Map<String, String> params = WXPayUtil.xmlToMap(resultxml);
Map<String, String> return_data = new HashMap<String, String>(); //返回给微信信息
if ("SUCCESS".equals(params.get("return_code"))) { //返回成功 付款成功
boolean bool = WXPayUtil.isSignatureValid(resultxml, wxPayConfigEntity.getKey());//验证签名是否正确
if (bool) { //签名验证成功
String outTradeNo = params.get("out_trade_no"); //返回的商户订单号
Integer totalFee = Integer.parseInt(params.get("total_fee")); //总金额
Integer totalFeeTable = cartItemService.selectTotleFre(outTradeNo);//根据订单编号查询订单表保存微信需要付款总金额
if (null != totalFeeTable && totalFee.equals(totalFeeTable)) { //验证金额是否相同
int wechatStatus = cartItemService.selectWechatStatus(outTradeNo);//查询订单处理状态
if (wechatStatus == 1) { //订单业务已处理,直接返回成功
return_data.put("return_code", "SUCCESS");
return_data.put("return_msg", "OK");
return WXPayUtil.mapToXml(return_data); //转成xml返回
}
String paymentTime = params.get("time_end"); //支付完成时间
//发票表添加支付时间
cartItemService.updatePaymentTime(paymentTime, outTradeNo);
String transactionId = params.get("transaction_id"); //微信支付订单号
Integer status = 1; //待发货
wechatStatus = 1; //业务已处理
String modifyTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());//修改时间
//支付成功后修改订单信息 paymentTime transactionId微信支付订单号 status modifyTime outTradeNo
cartItemService.updateOrderMessage(paymentTime, transactionId, status, modifyTime, outTradeNo, wechatStatus); //修改数据库字段信息,业务处理
return_data.put("return_code", "SUCCESS");
return_data.put("return_msg", "OK"); //正确返回给微信
return WXPayUtil.mapToXml(return_data);
}
}
}
// 支付失败
return_data.put("return_code", "FAIL");
return_data.put("return_msg", "return_code不正确");
return WXPayUtil.mapToXml(return_data);
}
3.申请退款,(申请退款需要证书):
@ApiOperation(value = "微信申请退款", notes = "")
@PostMapping("/refund/wxorder")
public Object refundOrder(@RequestBody
@ApiParam(name = "参数说明", value = "totalFee:订单金额 ,refundFee:退款金额 ,orderSn:订单号")
HWeiXinRefundParam param) throws Exception {
SortedMap<String, String> sortedMap = new TreeMap<String, String>();
Integer totalFee = param.getTotalFee() * 100; //订单金额 金额单位为分 *100
Integer refundFee = param.getRefundFee() * 100; //退款金额 金额单位为分 *100
String refundId = cartItemService.selectRefundId(param.getOrderSn()); //根据订单编号查询退款单号
if (refundId != null) {
sortedMap.put("out_refund_no", refundId); //商户退款单号
}
sortedMap.put("appid", wxPayConfigEntity.getApppid()); //应用ID
sortedMap.put("mch_id", wxPayConfigEntity.getMchid()); //商户号
String nonceStr = WXPayUtil.generateNonceStr(); //生成随机数
sortedMap.put("nonce_str", nonceStr); //随机字符串
sortedMap.put("out_trade_no", param.getOrderSn()); //商户订单号
sortedMap.put("total_fee", totalFee + "");
sortedMap.put("refund_fee", refundFee + "");
//生成签名
String sign = WXPayUtil.generateSignature(sortedMap, wxPayConfigEntity.getKey(), WXPayConstants.SignType.MD5);
sortedMap.put("sign", sign);
//将Map转换为XML格式的字符串 发送给微信xml
String xml = WXPayUtil.generateSignedXml(sortedMap, wxPayConfigEntity.getKey(), WXPayConstants.SignType.MD5);
String string = clientCustomSSL.doRefund(wxPayConfigEntity.getRefund(), xml); //发起申请退款请求
System.out.println("微信返回的xml===>>>" + string);
boolean bool = WXPayUtil.isSignatureValid(string, wxPayConfigEntity.getKey());//验证签名是否正确
Map<String, String> map = new HashMap<String, String>();
if (bool) {
Map<String, String> resultMap = WXPayUtil.xmlToMap(string);
if ("SUCCESS".equals(resultMap.get("return_code"))) { //返回成功 退款成功
map.put("refund_fee", resultMap.get("refund_fee"));//退款金额
map.put("out_refund_no", resultMap.get("out_refund_no")); //退款单号
map.put("message", "SUCCESS");//退款成功
return map;
}
}
map.put("message", "faild"); //退款失败
return map;
}
退款还涉及到一个工具类粘一下:退款需要证书,证书是在申请微信商户号,发送到你的邮箱,然后下载下来.这里需要读取证书
@Component
public class ClientCustomSSL {
@Autowired
private WXPayConfigEntity wxPayConfigEntity;
@SuppressWarnings("deprecation")
public String doRefund(String url,String data) throws Exception {
/**
* 注意PKCS12证书 是从微信商户平台-》账户设置-》 API安全 中下载的
*/
KeyStore keyStore = KeyStore.getInstance("PKCS12");
/**
*此处要改
*wxconfig.SSLCERT_PATH : 指向你的证书的绝对路径,带着证书去访问
*/
//你下载的证书,解压后的apiclient_cert.p12这个文件全路径加全名
FileInputStream instream = new FileInputStream(new File(wxPayConfigEntity.getCertificateurl()));//P12文件目录
try {
/**
* 此处要改
*
* 下载证书时的密码、默认密码是你的MCHID mch_id
* */
keyStore.load(instream, wxPayConfigEntity.getMchid().toCharArray());//这里写密码.默认商户id
} finally {
instream.close();
}
// Trust own CA and all self-signed certs
/**
* 此处要改
* 下载证书时的密码、默认密码是你的MCHID mch_id
* */
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, wxPayConfigEntity.getMchid().toCharArray())//这里也是写密码的,默认商户id
.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();
}
}
}
5,统一管理发起请求微信需要的参数,这里涉及隐私,这些有的参数需要申请微信支付商户才会有.
@Component
@Setter
@Getter
public class WXPayConfigEntity {
@Value(value = "${wx.appid}")
private String apppid; //应用ID
@Value(value = "${wx.mchid}")
private String mchid ; //商户ID
@Value(value = "${wx.key}")
private String key ; //密钥key
@Value(value = "${wx.spbillcreate.ip}")
private String spbillcreateip; //终端IP
@Value(value = "${wx.notify.url}")
private String notifyurl; //通知回调地址
@Value(value = "${wx.tradetype}")
private String tradetype; //交易类型
@Value(value = "${wx.pay.url}")
private String payurl; //下单地址
@Value(value = "${wx.package}")
private String packages; //扩展字段
@Value(value = "${wx.refund}")
private String refund; //微信退款地址
@Value(value = "${wx.certificate.url}")
private String certificateurl;//证书地址
}
6.大概就这么多