| @@ -1,10 +1,7 @@ | |||||
| package me.chanjar.weixin.mp.api; | package me.chanjar.weixin.mp.api; | ||||
| import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
| import me.chanjar.weixin.mp.bean.pay.request.WxEntPayRequest; | |||||
| import me.chanjar.weixin.mp.bean.pay.request.WxPayRefundRequest; | |||||
| import me.chanjar.weixin.mp.bean.pay.request.WxPaySendRedpackRequest; | |||||
| import me.chanjar.weixin.mp.bean.pay.request.WxPayUnifiedOrderRequest; | |||||
| import me.chanjar.weixin.mp.bean.pay.request.*; | |||||
| import me.chanjar.weixin.mp.bean.pay.result.*; | import me.chanjar.weixin.mp.bean.pay.result.*; | ||||
| import java.io.File; | import java.io.File; | ||||
| @@ -182,7 +179,6 @@ public interface WxMpPayService { | |||||
| */ | */ | ||||
| boolean checkSign(Map<String, String> params, String signKey); | boolean checkSign(Map<String, String> params, String signKey); | ||||
| /** | /** | ||||
| * 发送微信红包给个人用户 | * 发送微信红包给个人用户 | ||||
| * <pre> | * <pre> | ||||
| @@ -264,4 +260,17 @@ public interface WxMpPayService { | |||||
| * @return 生成的二维码的字节数组 | * @return 生成的二维码的字节数组 | ||||
| */ | */ | ||||
| byte[] createScanPayQrcodeMode2(String codeUrl, File logoFile, Integer sideLength); | byte[] createScanPayQrcodeMode2(String codeUrl, File logoFile, Integer sideLength); | ||||
| /** | |||||
| * <pre> | |||||
| * 交易保障 | |||||
| * 应用场景: | |||||
| * 商户在调用微信支付提供的相关接口时,会得到微信支付返回的相关信息以及获得整个接口的响应时间。 | |||||
| * 为提高整体的服务水平,协助商户一起提高服务质量,微信支付提供了相关接口调用耗时和返回信息的主动上报接口, | |||||
| * 微信支付可以根据商户侧上报的数据进一步优化网络部署,完善服务监控,和商户更好的协作为用户提供更好的业务体验。 | |||||
| * 接口地址: https://api.mch.weixin.qq.com/payitil/report | |||||
| * 是否需要证书:不需要 | |||||
| * </pre> | |||||
| */ | |||||
| void report(WxPayReportRequest request) throws WxErrorException; | |||||
| } | } | ||||
| @@ -95,7 +95,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||||
| private void checkResult(WxPayBaseResult result) throws WxErrorException { | private void checkResult(WxPayBaseResult result) throws WxErrorException { | ||||
| //校验返回结果签名 | //校验返回结果签名 | ||||
| Map<String, String> map = result.toMap(); | Map<String, String> map = result.toMap(); | ||||
| if (!this.checkSign(map)) { | |||||
| if (result.getSign() != null &&!this.checkSign(map)) { | |||||
| log.debug("校验结果签名失败,参数:{}", map); | log.debug("校验结果签名失败,参数:{}", map); | ||||
| throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg("参数格式校验错误!").build()); | throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg("参数格式校验错误!").build()); | ||||
| } | } | ||||
| @@ -354,6 +354,17 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||||
| return QrcodeUtils.createQrcode(codeUrl, sideLength, logoFile); | return QrcodeUtils.createQrcode(codeUrl, sideLength, logoFile); | ||||
| } | } | ||||
| public void report(WxPayReportRequest request) throws WxErrorException { | |||||
| BeanUtils.checkRequiredFields(request); | |||||
| this.initRequest(request); | |||||
| request.setSign(this.createSign(request)); | |||||
| String url = PAY_BASE_URL + "/payitil/report"; | |||||
| String responseContent = this.wxMpService.post(url, request.toXML()); | |||||
| WxPayBaseResult result = WxPayBaseResult.fromXML(responseContent, WxPayBaseResult.class); | |||||
| this.checkResult(result); | |||||
| } | |||||
| private String executeRequest(String url, String requestStr) throws WxErrorException { | private String executeRequest(String url, String requestStr) throws WxErrorException { | ||||
| HttpPost httpPost = new HttpPost(url); | HttpPost httpPost = new HttpPost(url); | ||||
| if (this.wxMpService.getHttpProxy() != null) { | if (this.wxMpService.getHttpProxy() != null) { | ||||
| @@ -0,0 +1,273 @@ | |||||
| package me.chanjar.weixin.mp.bean.pay.request; | |||||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
| import me.chanjar.weixin.common.annotation.Required; | |||||
| /** | |||||
| * <pre> | |||||
| * 微信支付-交易保障请求参数 | |||||
| * 注释中各行每个字段描述对应如下: | |||||
| * <li>字段名 | |||||
| * <li>变量名 | |||||
| * <li>是否必填 | |||||
| * <li>类型 | |||||
| * <li>示例值 | |||||
| * <li>描述 | |||||
| * </pre> | |||||
| * | |||||
| * @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||||
| */ | |||||
| @XStreamAlias("xml") | |||||
| public class WxPayReportRequest extends WxPayBaseRequest { | |||||
| /** | |||||
| * <pre> | |||||
| * 设备号 | |||||
| * device_info | |||||
| * 否 | |||||
| * String(32) | |||||
| * 013467007045764 | |||||
| * 商户自定义的终端设备号,如门店编号、设备的ID等 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("device_info") | |||||
| private String deviceInfo; | |||||
| /** | |||||
| * <pre> | |||||
| * 签名类型 | |||||
| * sign_type | |||||
| * 否 | |||||
| * String(32) | |||||
| * HMAC-SHA256 | |||||
| * 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("sign_type") | |||||
| private String signType; | |||||
| /** | |||||
| * <pre> | |||||
| * 接口URL | |||||
| * interface_url | |||||
| * 是 | |||||
| * String(127) | |||||
| * https://api.mch.weixin.qq.com/pay/unifiedorder | |||||
| * 报对应的接口的完整URL,类似:https://api.mch.weixin.qq.com/pay/unifiedorder, | |||||
| * 对于刷卡支付,为更好的和商户共同分析一次业务行为的整体耗时情况, | |||||
| * 对于两种接入模式,请都在门店侧对一次刷卡支付进行一次单独的整体上报, | |||||
| * 上报URL指定为:https://api.mch.weixin.qq.com/pay/micropay/total,关于两种接入模式具体可参考本文档章节: | |||||
| * 刷卡支付商户接入模式,其它接口调用仍然按照调用一次,上报一次来进行。 | |||||
| * </pre> | |||||
| */ | |||||
| @Required | |||||
| @XStreamAlias("interface_url") | |||||
| private String interfaceUrl; | |||||
| /** | |||||
| * <pre> | |||||
| * 接口耗时 | |||||
| * execute_time | |||||
| * 是 | |||||
| * Int | |||||
| * 1000 | |||||
| * 接口耗时情况,单位为毫秒 | |||||
| * </pre> | |||||
| */ | |||||
| @Required | |||||
| @XStreamAlias("execute_time_")//估计是官方接口搞错了 | |||||
| private Integer executeTime; | |||||
| /** | |||||
| * <pre> | |||||
| * 返回状态码 | |||||
| * return_code | |||||
| * 是 | |||||
| * String(16) | |||||
| * SUCCESS | |||||
| * SUCCESS/FAIL,此字段是通信标识,非交易标识,交易是否成功需要查看trade_state来判断 | |||||
| * </pre> | |||||
| */ | |||||
| @Required | |||||
| @XStreamAlias("return_code") | |||||
| private String returnCode; | |||||
| /** | |||||
| * <pre> | |||||
| * 返回信息 | |||||
| * return_msg | |||||
| * 否 | |||||
| * String(128) | |||||
| * 签名失败 | |||||
| * 返回信息,如非空,为错误原因,签名失败,参数格式校验错误 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("return_msg") | |||||
| private String returnMsg; | |||||
| /** | |||||
| * <pre> | |||||
| * 业务结果 | |||||
| * result_code | |||||
| * 是 | |||||
| * String(16) | |||||
| * SUCCESS | |||||
| * SUCCESS/FAIL | |||||
| * </pre> | |||||
| */ | |||||
| @Required | |||||
| @XStreamAlias("result_code") | |||||
| private String resultCode; | |||||
| /** | |||||
| * <pre> | |||||
| * 错误代码 | |||||
| * err_code | |||||
| * 否 | |||||
| * String(32) | |||||
| * SYSTEMERROR | |||||
| * ORDERNOTEXIST—订单不存在,SYSTEMERROR—系统错误 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("err_code") | |||||
| private String errCode; | |||||
| /** | |||||
| * <pre> | |||||
| * 错误代码描述 | |||||
| * err_code_des | |||||
| * 否 | |||||
| * String(128) | |||||
| * 系统错误 | |||||
| * 结果信息描述 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("err_code_des") | |||||
| private String errCodeDes; | |||||
| /** | |||||
| * <pre> | |||||
| * 商户订单号 | |||||
| * out_trade_no | |||||
| * 否 | |||||
| * String(32) | |||||
| * 1217752501201407033233368018 | |||||
| * 商户系统内部的订单号,商户可以在上报时提供相关商户订单号方便微信支付更好的提高服务质量。 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("out_trade_no") | |||||
| private String outTradeNo; | |||||
| /** | |||||
| * <pre> | |||||
| * 访问接口IP | |||||
| * user_ip | |||||
| * 是 | |||||
| * String(16) | |||||
| * 8.8.8.8 | |||||
| * 发起接口调用时的机器IP | |||||
| * </pre> | |||||
| */ | |||||
| @Required | |||||
| @XStreamAlias("user_ip") | |||||
| private String userIp; | |||||
| /** | |||||
| * <pre> | |||||
| * 商户上报时间 | |||||
| * time | |||||
| * 否 | |||||
| * String(14) | |||||
| * 20091227091010 | |||||
| * 系统时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。其他详见时间规则 | |||||
| * </pre> | |||||
| */ | |||||
| @XStreamAlias("time") | |||||
| private String time; | |||||
| public String getDeviceInfo() { | |||||
| return deviceInfo; | |||||
| } | |||||
| public void setDeviceInfo(String deviceInfo) { | |||||
| this.deviceInfo = deviceInfo; | |||||
| } | |||||
| public String getSignType() { | |||||
| return signType; | |||||
| } | |||||
| public void setSignType(String signType) { | |||||
| this.signType = signType; | |||||
| } | |||||
| public String getInterfaceUrl() { | |||||
| return interfaceUrl; | |||||
| } | |||||
| public void setInterfaceUrl(String interfaceUrl) { | |||||
| this.interfaceUrl = interfaceUrl; | |||||
| } | |||||
| public Integer getExecuteTime() { | |||||
| return executeTime; | |||||
| } | |||||
| public void setExecuteTime(Integer executeTime) { | |||||
| this.executeTime = executeTime; | |||||
| } | |||||
| public String getReturnCode() { | |||||
| return returnCode; | |||||
| } | |||||
| public void setReturnCode(String returnCode) { | |||||
| this.returnCode = returnCode; | |||||
| } | |||||
| public String getReturnMsg() { | |||||
| return returnMsg; | |||||
| } | |||||
| public void setReturnMsg(String returnMsg) { | |||||
| this.returnMsg = returnMsg; | |||||
| } | |||||
| public String getResultCode() { | |||||
| return resultCode; | |||||
| } | |||||
| public void setResultCode(String resultCode) { | |||||
| this.resultCode = resultCode; | |||||
| } | |||||
| public String getErrCode() { | |||||
| return errCode; | |||||
| } | |||||
| public void setErrCode(String errCode) { | |||||
| this.errCode = errCode; | |||||
| } | |||||
| public String getErrCodeDes() { | |||||
| return errCodeDes; | |||||
| } | |||||
| public void setErrCodeDes(String errCodeDes) { | |||||
| this.errCodeDes = errCodeDes; | |||||
| } | |||||
| public String getOutTradeNo() { | |||||
| return outTradeNo; | |||||
| } | |||||
| public void setOutTradeNo(String outTradeNo) { | |||||
| this.outTradeNo = outTradeNo; | |||||
| } | |||||
| public String getUserIp() { | |||||
| return userIp; | |||||
| } | |||||
| public void setUserIp(String userIp) { | |||||
| this.userIp = userIp; | |||||
| } | |||||
| public String getTime() { | |||||
| return time; | |||||
| } | |||||
| public void setTime(String time) { | |||||
| this.time = time; | |||||
| } | |||||
| } | |||||
| @@ -24,7 +24,8 @@ import java.util.Map; | |||||
| * @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | * @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | ||||
| * </pre> | * </pre> | ||||
| */ | */ | ||||
| public abstract class WxPayBaseResult { | |||||
| @XStreamAlias("xml") | |||||
| public class WxPayBaseResult { | |||||
| /** | /** | ||||
| * 返回状态码 | * 返回状态码 | ||||
| */ | */ | ||||
| @@ -6,10 +6,7 @@ import me.chanjar.weixin.common.exception.WxErrorException; | |||||
| import me.chanjar.weixin.mp.api.ApiTestModule; | import me.chanjar.weixin.mp.api.ApiTestModule; | ||||
| import me.chanjar.weixin.mp.api.WxMpService; | import me.chanjar.weixin.mp.api.WxMpService; | ||||
| import me.chanjar.weixin.mp.api.WxXmlMpInMemoryConfigStorage; | import me.chanjar.weixin.mp.api.WxXmlMpInMemoryConfigStorage; | ||||
| import me.chanjar.weixin.mp.bean.pay.request.WxEntPayRequest; | |||||
| import me.chanjar.weixin.mp.bean.pay.request.WxPayRefundRequest; | |||||
| import me.chanjar.weixin.mp.bean.pay.request.WxPaySendRedpackRequest; | |||||
| import me.chanjar.weixin.mp.bean.pay.request.WxPayUnifiedOrderRequest; | |||||
| import me.chanjar.weixin.mp.bean.pay.request.*; | |||||
| import me.chanjar.weixin.mp.bean.pay.result.*; | import me.chanjar.weixin.mp.bean.pay.result.*; | ||||
| import org.testng.Assert; | import org.testng.Assert; | ||||
| import org.testng.annotations.Guice; | import org.testng.annotations.Guice; | ||||
| @@ -35,6 +32,18 @@ public class WxMpPayServiceImplTest { | |||||
| } | } | ||||
| @Test | |||||
| public void testReport() throws Exception { | |||||
| WxPayReportRequest request = new WxPayReportRequest(); | |||||
| request.setInterfaceUrl("hahahah"); | |||||
| request.setSignType("HMAC-SHA256");//貌似接口未校验此字段 | |||||
| request.setExecuteTime(1000); | |||||
| request.setReturnCode("aaa"); | |||||
| request.setResultCode("aaa"); | |||||
| request.setUserIp("8.8.8"); | |||||
| this.wxService.getPayService().report(request); | |||||
| } | |||||
| /** | /** | ||||
| * 需要证书的接口需要先执行该方法 | * 需要证书的接口需要先执行该方法 | ||||
| */ | */ | ||||