| @@ -1,73 +1,79 @@ | |||
| <?xml version="1.0"?> | |||
| <project | |||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" | |||
| xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <parent> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>weixin-java-parent</artifactId> | |||
| <version>2.4.6.BETA</version> | |||
| </parent> | |||
| <artifactId>weixin-java-mp</artifactId> | |||
| <name>WeiXin Java Tools - MP</name> | |||
| <description>微信公众号Java SDK</description> | |||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <parent> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>weixin-java-parent</artifactId> | |||
| <version>2.4.6.BETA</version> | |||
| </parent> | |||
| <artifactId>weixin-java-mp</artifactId> | |||
| <name>WeiXin Java Tools - MP</name> | |||
| <description>微信公众号Java SDK</description> | |||
| <dependencies> | |||
| <dependency> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>weixin-java-common</artifactId> | |||
| <version>${project.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>junit</groupId> | |||
| <artifactId>junit</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.testng</groupId> | |||
| <artifactId>testng</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.mockito</groupId> | |||
| <artifactId>mockito-all</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.google.inject</groupId> | |||
| <artifactId>guice</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.eclipse.jetty</groupId> | |||
| <artifactId>jetty-server</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.eclipse.jetty</groupId> | |||
| <artifactId>jetty-servlet</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>joda-time</groupId> | |||
| <artifactId>joda-time</artifactId> | |||
| <version>2.9.4</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| </dependencies> | |||
| <dependencies> | |||
| <dependency> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>weixin-java-common</artifactId> | |||
| <version>${project.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>junit</groupId> | |||
| <artifactId>junit</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.testng</groupId> | |||
| <artifactId>testng</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.mockito</groupId> | |||
| <artifactId>mockito-all</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.google.inject</groupId> | |||
| <artifactId>guice</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.eclipse.jetty</groupId> | |||
| <artifactId>jetty-server</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.eclipse.jetty</groupId> | |||
| <artifactId>jetty-servlet</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>joda-time</groupId> | |||
| <artifactId>joda-time</artifactId> | |||
| <version>2.9.4</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>io.rest-assured</groupId> | |||
| <artifactId>xml-path</artifactId> | |||
| <version>3.0.1</version> | |||
| </dependency> | |||
| <build> | |||
| <plugins> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-surefire-plugin</artifactId> | |||
| <configuration> | |||
| <suiteXmlFiles> | |||
| <suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile> | |||
| </suiteXmlFiles> | |||
| </configuration> | |||
| </plugin> | |||
| </plugins> | |||
| </build> | |||
| </dependencies> | |||
| <build> | |||
| <plugins> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-surefire-plugin</artifactId> | |||
| <configuration> | |||
| <suiteXmlFiles> | |||
| <suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile> | |||
| </suiteXmlFiles> | |||
| </configuration> | |||
| </plugin> | |||
| </plugins> | |||
| </build> | |||
| </project> | |||
| @@ -310,20 +310,25 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||
| } | |||
| @Override | |||
| public void setSslContextFilePath(String filePath) throws Exception { | |||
| public void setSslContextFilePath(String filePath) { | |||
| if (null == partnerId) { | |||
| throw new Exception("请先将partnerId进行赋值"); | |||
| throw new IllegalArgumentException("请设置partnerId的值"); | |||
| } | |||
| File file = new File(filePath); | |||
| if (!file.exists()) { | |||
| throw new RuntimeException(file.getPath() + ":文件不存在!在设置SSLContext的时候"); | |||
| throw new RuntimeException("证书文件:【" + file.getPath() + "】不存在!"); | |||
| } | |||
| try { | |||
| FileInputStream inputStream = new FileInputStream(file); | |||
| KeyStore keystore = KeyStore.getInstance("PKCS12"); | |||
| char[] partnerId2charArray = partnerId.toCharArray(); | |||
| keystore.load(inputStream, partnerId2charArray); | |||
| this.sslContext = SSLContexts.custom().loadKeyMaterial(keystore, partnerId2charArray).build(); | |||
| } catch (Exception e) { | |||
| throw new RuntimeException("证书文件有问题,请核实!", e); | |||
| } | |||
| FileInputStream inputStream = new FileInputStream(file); | |||
| KeyStore keystore = KeyStore.getInstance("PKCS12"); | |||
| char[] partnerId2charArray = partnerId.toCharArray(); | |||
| keystore.load(inputStream, partnerId2charArray); | |||
| this.sslContext = SSLContexts.custom().loadKeyMaterial(keystore, partnerId2charArray).build(); | |||
| } | |||
| @Override | |||
| @@ -1,14 +1,11 @@ | |||
| package me.chanjar.weixin.mp.api.impl; | |||
| import com.thoughtworks.xstream.XStream; | |||
| import me.chanjar.weixin.common.bean.result.WxError; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.common.util.BeanUtils; | |||
| import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||
| import me.chanjar.weixin.mp.api.WxMpConfigStorage; | |||
| import me.chanjar.weixin.mp.api.WxMpPayService; | |||
| import me.chanjar.weixin.mp.api.WxMpService; | |||
| import me.chanjar.weixin.mp.bean.pay.WxPayOrderNotifyResultConverter; | |||
| import me.chanjar.weixin.mp.bean.pay.request.*; | |||
| import me.chanjar.weixin.mp.bean.pay.result.*; | |||
| import org.apache.commons.codec.digest.DigestUtils; | |||
| @@ -54,20 +51,16 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| @Override | |||
| public WxPayRefundResult refund(WxPayRefundRequest request) throws WxErrorException { | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.processAnnotations(WxPayRefundRequest.class); | |||
| xstream.processAnnotations(WxPayRefundResult.class); | |||
| initRequest(request); | |||
| this.initRequest(request); | |||
| if (StringUtils.isBlank(request.getOpUserId())) { | |||
| request.setOpUserId(getConfig().getPartnerId()); | |||
| request.setOpUserId(this.getConfig().getPartnerId()); | |||
| } | |||
| checkParameters(request); | |||
| this.checkParameters(request); | |||
| request.setSign(this.createSign(request)); | |||
| String url = PAY_BASE_URL + "/secapi/pay/refund"; | |||
| String responseContent = this.executeRequestWithKeyFile(url, xstream.toXML(request)); | |||
| WxPayRefundResult result = (WxPayRefundResult) xstream.fromXML(responseContent); | |||
| String responseContent = this.executeWithKey(url, request.toXML()); | |||
| WxPayRefundResult result = WxPayRefundResult.fromXML(responseContent, WxPayRefundResult.class); | |||
| this.checkResult(result); | |||
| return result; | |||
| } | |||
| @@ -79,12 +72,8 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| throw new IllegalArgumentException("transaction_id , out_trade_no,out_refund_no, refund_id 必须四选一"); | |||
| } | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.processAnnotations(WxPayRefundQueryRequest.class); | |||
| xstream.processAnnotations(WxPayRefundQueryResult.class); | |||
| WxPayRefundQueryRequest request = new WxPayRefundQueryRequest(); | |||
| initRequest(request); | |||
| this.initRequest(request); | |||
| request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); | |||
| request.setTransactionId(StringUtils.trimToNull(transactionId)); | |||
| request.setOutRefundNo(StringUtils.trimToNull(outRefundNo)); | |||
| @@ -92,8 +81,8 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| request.setSign(this.createSign(request)); | |||
| String url = PAY_BASE_URL + "/pay/refundquery"; | |||
| String responseContent = this.executeRequest(url, xstream.toXML(request)); | |||
| WxPayRefundQueryResult result = (WxPayRefundQueryResult) xstream.fromXML(responseContent); | |||
| String responseContent = this.executeRequest(url, request.toXML()); | |||
| WxPayRefundQueryResult result = WxPayRefundQueryResult.fromXML(responseContent, WxPayRefundQueryResult.class); | |||
| result.composeRefundRecords(responseContent); | |||
| this.checkResult(result); | |||
| return result; | |||
| @@ -102,8 +91,9 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| private void checkResult(WxPayBaseResult result) throws WxErrorException { | |||
| //校验返回结果签名 | |||
| if (!checkSign(result.toMap())) { | |||
| log.debug("校验结果签名失败,参数:{}", result.toMap()); | |||
| Map<String, String> map = result.toMap(); | |||
| if (!this.checkSign(map)) { | |||
| log.debug("校验结果签名失败,参数:{}", map); | |||
| throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg("参数格式校验错误!").build()); | |||
| } | |||
| @@ -115,7 +105,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| + result.getReturnMsg() + ", 结果代码: " + result.getResultCode() + ", 错误代码: " | |||
| + result.getErrCode() + ", 错误详情: " + result.getErrCodeDes()) | |||
| .build(); | |||
| log.error("结果业务代码异常,参数:{},详细:{}", result.toMap(), error); | |||
| log.error("结果业务代码异常,参数:{},详细:{}", map, error); | |||
| throw new WxErrorException(error); | |||
| } | |||
| } | |||
| @@ -138,10 +128,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| public WxPayOrderNotifyResult getOrderNotifyResult(String xmlData) throws WxErrorException { | |||
| try { | |||
| log.trace("微信支付回调参数详细:{}", xmlData); | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.processAnnotations(WxPayOrderNotifyResult.class); | |||
| xstream.registerConverter(new WxPayOrderNotifyResultConverter(xstream.getMapper(), xstream.getReflectionProvider())); | |||
| WxPayOrderNotifyResult result = (WxPayOrderNotifyResult) xstream.fromXML(xmlData); | |||
| WxPayOrderNotifyResult result = WxPayOrderNotifyResult.fromXML(xmlData); | |||
| log.debug("微信支付回调结果对象:{}", result); | |||
| this.checkResult(result); | |||
| return result; | |||
| @@ -157,11 +144,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| @Override | |||
| public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request) | |||
| throws WxErrorException { | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.processAnnotations(WxPaySendRedpackRequest.class); | |||
| xstream.processAnnotations(WxPaySendRedpackResult.class); | |||
| initRequest(request); | |||
| this.initRequest(request); | |||
| request.setSign(this.createSign(request)); | |||
| String url = PAY_BASE_URL + "/mmpaymkttransfers/sendredpack"; | |||
| @@ -169,18 +152,14 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| //裂变红包 | |||
| url = PAY_BASE_URL + "/mmpaymkttransfers/sendgroupredpack"; | |||
| } | |||
| String responseContent = this.executeRequestWithKeyFile(url, xstream.toXML(request)); | |||
| WxPaySendRedpackResult result = (WxPaySendRedpackResult) xstream.fromXML(responseContent); | |||
| String responseContent = this.executeWithKey(url, request.toXML()); | |||
| WxPaySendRedpackResult result = WxPaySendRedpackResult.fromXML(responseContent, WxPaySendRedpackResult.class); | |||
| this.checkResult(result); | |||
| return result; | |||
| } | |||
| @Override | |||
| public WxPayRedpackQueryResult queryRedpack(String mchBillNo) throws WxErrorException { | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.processAnnotations(WxPayRedpackQueryRequest.class); | |||
| xstream.processAnnotations(WxPayRedpackQueryResult.class); | |||
| WxPayRedpackQueryRequest request = new WxPayRedpackQueryRequest(); | |||
| request.setMchBillNo(mchBillNo); | |||
| request.setBillType("MCHT"); | |||
| @@ -188,8 +167,8 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| request.setSign(this.createSign(request)); | |||
| String url = PAY_BASE_URL + "/mmpaymkttransfers/gethbinfo"; | |||
| String responseContent = this.executeRequestWithKeyFile(url, xstream.toXML(request)); | |||
| WxPayRedpackQueryResult result = (WxPayRedpackQueryResult) xstream.fromXML(responseContent); | |||
| String responseContent = this.executeWithKey(url, request.toXML()); | |||
| WxPayRedpackQueryResult result = WxPayRedpackQueryResult.fromXML(responseContent, WxPayRedpackQueryResult.class); | |||
| this.checkResult(result); | |||
| return result; | |||
| } | |||
| @@ -201,10 +180,6 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| throw new IllegalArgumentException("transaction_id 和 out_trade_no 不能同时存在或同时为空,必须二选一"); | |||
| } | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.processAnnotations(WxPayOrderQueryRequest.class); | |||
| xstream.processAnnotations(WxPayOrderQueryResult.class); | |||
| WxPayOrderQueryRequest request = new WxPayOrderQueryRequest(); | |||
| request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); | |||
| request.setTransactionId(StringUtils.trimToNull(transactionId)); | |||
| @@ -212,8 +187,8 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| request.setSign(this.createSign(request)); | |||
| String url = PAY_BASE_URL + "/pay/orderquery"; | |||
| String responseContent = this.executeRequest(url, xstream.toXML(request)); | |||
| WxPayOrderQueryResult result = (WxPayOrderQueryResult) xstream.fromXML(responseContent); | |||
| String responseContent = this.executeRequest(url, request.toXML()); | |||
| WxPayOrderQueryResult result = WxPayOrderQueryResult.fromXML(responseContent, WxPayOrderQueryResult.class); | |||
| result.composeCoupons(responseContent); | |||
| this.checkResult(result); | |||
| return result; | |||
| @@ -225,18 +200,14 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| throw new IllegalArgumentException("out_trade_no 不能为空"); | |||
| } | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.processAnnotations(WxPayOrderCloseRequest.class); | |||
| xstream.processAnnotations(WxPayOrderCloseResult.class); | |||
| WxPayOrderCloseRequest request = new WxPayOrderCloseRequest(); | |||
| request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); | |||
| initRequest(request); | |||
| request.setSign(this.createSign(request)); | |||
| String url = PAY_BASE_URL + "/pay/closeorder"; | |||
| String responseContent = this.executeRequest(url, xstream.toXML(request)); | |||
| WxPayOrderCloseResult result = (WxPayOrderCloseResult) xstream.fromXML(responseContent); | |||
| String responseContent = this.executeRequest(url, request.toXML()); | |||
| WxPayOrderCloseResult result = WxPayOrderCloseResult.fromXML(responseContent, WxPayOrderCloseResult.class); | |||
| this.checkResult(result); | |||
| return result; | |||
| @@ -246,11 +217,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| public WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) | |||
| throws WxErrorException { | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.processAnnotations(WxPayUnifiedOrderRequest.class); | |||
| xstream.processAnnotations(WxPayUnifiedOrderResult.class); | |||
| initRequest(request); | |||
| this.initRequest(request); | |||
| if (StringUtils.isBlank(request.getNotifyURL())) { | |||
| request.setNotifyURL(getConfig().getNotifyURL()); | |||
| } | |||
| @@ -261,12 +228,12 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| request.setSign(this.createSign(request)); | |||
| String url = PAY_BASE_URL + "/pay/unifiedorder"; | |||
| String xmlParam = xstream.toXML(request); | |||
| String xmlParam = request.toXML(); | |||
| log.debug("微信统一下单接口,URL:{},参数:{}", url, xmlParam); | |||
| String responseContent = this.executeRequest(url, xmlParam); | |||
| log.debug("微信统一下单接口,URL:{},结果:{}", url, responseContent); | |||
| WxPayUnifiedOrderResult result = (WxPayUnifiedOrderResult) xstream.fromXML(responseContent); | |||
| WxPayUnifiedOrderResult result = WxPayUnifiedOrderResult.fromXML(responseContent, WxPayUnifiedOrderResult.class); | |||
| this.checkResult(result); | |||
| return result; | |||
| } | |||
| @@ -324,35 +291,27 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| @Override | |||
| public WxEntPayResult entPay(WxEntPayRequest request) throws WxErrorException { | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.processAnnotations(WxEntPayRequest.class); | |||
| xstream.processAnnotations(WxEntPayResult.class); | |||
| initRequest(request); | |||
| this.initRequest(request); | |||
| BeanUtils.checkRequiredFields(request); | |||
| request.setSign(this.createSign(request)); | |||
| String url = PAY_BASE_URL + "/mmpaymkttransfers/promotion/transfers"; | |||
| String responseContent = this.executeRequestWithKeyFile(url, xstream.toXML(request)); | |||
| WxEntPayResult result = (WxEntPayResult) xstream.fromXML(responseContent); | |||
| String responseContent = this.executeWithKey(url, request.toXML()); | |||
| WxEntPayResult result = WxEntPayResult.fromXML(responseContent, WxEntPayResult.class); | |||
| this.checkResult(result); | |||
| return result; | |||
| } | |||
| @Override | |||
| public WxEntPayQueryResult queryEntPay(String partnerTradeNo) throws WxErrorException { | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.processAnnotations(WxEntPayQueryRequest.class); | |||
| xstream.processAnnotations(WxEntPayQueryResult.class); | |||
| WxEntPayQueryRequest request = new WxEntPayQueryRequest(); | |||
| initRequest(request); | |||
| this.initRequest(request); | |||
| request.setSign(this.createSign(request)); | |||
| String url = PAY_BASE_URL + "/mmpaymkttransfers/gettransferinfo"; | |||
| String responseContent = this.executeRequestWithKeyFile(url, xstream.toXML(request)); | |||
| WxEntPayQueryResult result = (WxEntPayQueryResult) xstream.fromXML(responseContent); | |||
| String responseContent = this.executeWithKey(url, request.toXML()); | |||
| WxEntPayQueryResult result = WxEntPayQueryResult.fromXML(responseContent, WxEntPayQueryResult.class); | |||
| this.checkResult(result); | |||
| return result; | |||
| } | |||
| @@ -379,11 +338,11 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| } | |||
| } | |||
| private String executeRequestWithKeyFile(String url, String requestStr) throws WxErrorException { | |||
| private String executeWithKey(String url, String requestStr) throws WxErrorException { | |||
| try { | |||
| SSLContext sslContext = getConfig().getSslContext(); | |||
| if (null == sslContext) { | |||
| throw new Exception("请将配置类(即WxMpConfigStorage的实现类)中的SSLContext初始化"); | |||
| throw new IllegalArgumentException("请先初始化配置类(即WxMpConfigStorage的实现类)中的SSLContext!"); | |||
| } | |||
| SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new String[]{"TLSv1"}, null, | |||
| @@ -412,17 +371,17 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| @Override | |||
| public String createSign(Object xmlBean) { | |||
| return createSign(BeanUtils.xmlBean2Map(xmlBean), getConfig().getPartnerKey()); | |||
| return this.createSign(BeanUtils.xmlBean2Map(xmlBean), getConfig().getPartnerKey()); | |||
| } | |||
| @Override | |||
| public String createSign(Object xmlBean, String signKey) { | |||
| return createSign(BeanUtils.xmlBean2Map(xmlBean), signKey); | |||
| return this.createSign(BeanUtils.xmlBean2Map(xmlBean), signKey); | |||
| } | |||
| @Override | |||
| public String createSign(Map<String, String> params) { | |||
| return createSign(params, getConfig().getPartnerKey()); | |||
| return this.createSign(params, this.getConfig().getPartnerKey()); | |||
| } | |||
| @Override | |||
| @@ -443,17 +402,17 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| @Override | |||
| public boolean checkSign(Object xmlBean) { | |||
| return checkSign(BeanUtils.xmlBean2Map(xmlBean), getConfig().getPartnerKey()); | |||
| return this.checkSign(BeanUtils.xmlBean2Map(xmlBean), getConfig().getPartnerKey()); | |||
| } | |||
| @Override | |||
| public boolean checkSign(Object xmlBean, String signKey) { | |||
| return checkSign(BeanUtils.xmlBean2Map(xmlBean), signKey); | |||
| return this.checkSign(BeanUtils.xmlBean2Map(xmlBean), signKey); | |||
| } | |||
| @Override | |||
| public boolean checkSign(Map<String, String> params) { | |||
| return checkSign(params, getConfig().getPartnerKey()); | |||
| return this.checkSign(params, getConfig().getPartnerKey()); | |||
| } | |||
| @Override | |||
| @@ -1,7 +1,9 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.request; | |||
| import com.thoughtworks.xstream.XStream; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import me.chanjar.weixin.common.util.ToStringUtils; | |||
| import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||
| import java.math.BigDecimal; | |||
| @@ -17,6 +19,7 @@ import java.math.BigDecimal; | |||
| * <li>示例值 | |||
| * <li>描述 | |||
| * </pre> | |||
| * | |||
| * @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||
| */ | |||
| public abstract class WxPayBaseRequest { | |||
| @@ -69,12 +72,22 @@ public abstract class WxPayBaseRequest { | |||
| @XStreamAlias("sign") | |||
| protected String sign; | |||
| /** | |||
| * 将单位为元转换为单位为分 | |||
| * | |||
| * @param yuan 将要转换的元的数值字符串 | |||
| */ | |||
| public static Integer yuanToFee(String yuan) { | |||
| return new BigDecimal(yuan).setScale(2, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100)).intValue(); | |||
| } | |||
| public String getAppid() { | |||
| return this.appid; | |||
| } | |||
| /** | |||
| * 如果配置中已经设置,可以不设置值 | |||
| * | |||
| * @param appid 微信公众号appid | |||
| */ | |||
| public void setAppid(String appid) { | |||
| @@ -87,6 +100,7 @@ public abstract class WxPayBaseRequest { | |||
| /** | |||
| * 如果配置中已经设置,可以不设置值 | |||
| * | |||
| * @param mchId 微信商户号 | |||
| */ | |||
| public void setMchId(String mchId) { | |||
| @@ -99,6 +113,7 @@ public abstract class WxPayBaseRequest { | |||
| /** | |||
| * 默认采用时间戳为随机字符串,可以不设置 | |||
| * | |||
| * @param nonceStr 随机字符串 | |||
| */ | |||
| public void setNonceStr(String nonceStr) { | |||
| @@ -113,17 +128,14 @@ public abstract class WxPayBaseRequest { | |||
| this.sign = sign; | |||
| } | |||
| /** | |||
| * 将单位为元转换为单位为分 | |||
| * | |||
| * @param yuan 将要转换的元的数值字符串 | |||
| */ | |||
| public static Integer yuanToFee(String yuan) { | |||
| return new BigDecimal(yuan).setScale(2, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100)).intValue(); | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return ToStringUtils.toSimpleString(this); | |||
| } | |||
| public String toXML() { | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.processAnnotations(this.getClass()); | |||
| return xstream.toXML(this); | |||
| } | |||
| } | |||
| @@ -1,7 +1,6 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.request; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import me.chanjar.weixin.common.annotation.Required; | |||
| /** | |||
| @@ -144,8 +143,6 @@ public class WxPayRefundRequest extends WxPayBaseRequest { | |||
| @XStreamAlias("refund_account") | |||
| private String refundAccount; | |||
| public String getDeviceInfo() { | |||
| return this.deviceInfo; | |||
| } | |||
| @@ -217,4 +214,5 @@ public class WxPayRefundRequest extends WxPayBaseRequest { | |||
| public void setRefundAccount(String refundAccount) { | |||
| this.refundAccount = refundAccount; | |||
| } | |||
| } | |||
| @@ -1,10 +1,19 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.result; | |||
| import com.google.common.collect.Maps; | |||
| import com.thoughtworks.xstream.XStream; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import me.chanjar.weixin.common.util.BeanUtils; | |||
| import io.restassured.internal.path.xml.NodeChildrenImpl; | |||
| import io.restassured.path.xml.XmlPath; | |||
| import io.restassured.path.xml.element.Node; | |||
| import io.restassured.path.xml.element.NodeChildren; | |||
| import me.chanjar.weixin.common.util.ToStringUtils; | |||
| import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import java.math.BigDecimal; | |||
| import java.util.Iterator; | |||
| import java.util.Map; | |||
| /** | |||
| @@ -15,65 +24,90 @@ import java.util.Map; | |||
| * </pre> | |||
| */ | |||
| public abstract class WxPayBaseResult { | |||
| @Override | |||
| public String toString() { | |||
| return ToStringUtils.toSimpleString(this); | |||
| } | |||
| /** | |||
| * 返回状态码 | |||
| */ | |||
| @XStreamAlias("return_code") | |||
| protected String returnCode; | |||
| /** | |||
| * 返回信息 | |||
| */ | |||
| @XStreamAlias("return_msg") | |||
| protected String returnMsg; | |||
| private String xmlString; | |||
| /** | |||
| * 业务结果 | |||
| */ | |||
| @XStreamAlias("result_code") | |||
| private String resultCode; | |||
| /** | |||
| * 错误代码 | |||
| */ | |||
| @XStreamAlias("err_code") | |||
| private String errCode; | |||
| /** | |||
| * 错误代码描述 | |||
| */ | |||
| @XStreamAlias("err_code_des") | |||
| private String errCodeDes; | |||
| /** | |||
| * 公众账号ID | |||
| */ | |||
| @XStreamAlias("appid") | |||
| private String appid; | |||
| /** | |||
| * 商户号 | |||
| */ | |||
| @XStreamAlias("mch_id") | |||
| private String mchId; | |||
| /** | |||
| * 随机字符串 | |||
| */ | |||
| @XStreamAlias("nonce_str") | |||
| private String nonceStr; | |||
| /** | |||
| * 签名 | |||
| */ | |||
| @XStreamAlias("sign") | |||
| private String sign; | |||
| /** | |||
| * 将单位分转换成单位圆 | |||
| * | |||
| * @param fee 将要被转换为元的分的数值 | |||
| */ | |||
| public static String feeToYuan(Integer fee) { | |||
| return new BigDecimal(Double.valueOf(fee) / 100).setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString(); | |||
| } | |||
| /** | |||
| * 从xml字符串创建bean对象 | |||
| */ | |||
| public static <T extends WxPayBaseResult> T fromXML(String xmlString, Class<T> clz) { | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.processAnnotations(clz); | |||
| T result = (T) xstream.fromXML(xmlString); | |||
| result.setXmlString(xmlString); | |||
| return result; | |||
| } | |||
| public String getXmlString() { | |||
| return this.xmlString; | |||
| } | |||
| public void setXmlString(String xmlString) { | |||
| this.xmlString = xmlString; | |||
| } | |||
| protected Logger getLogger() { | |||
| return LoggerFactory.getLogger(this.getClass()); | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return ToStringUtils.toSimpleString(this); | |||
| } | |||
| public String getReturnCode() { | |||
| return this.returnCode; | |||
| } | |||
| @@ -147,14 +181,43 @@ public abstract class WxPayBaseResult { | |||
| } | |||
| /** | |||
| * 将单位分转换成单位圆 | |||
| * @param fee 将要被转换为元的分的数值 | |||
| * 将bean通过保存的xml字符串转换成map | |||
| */ | |||
| public static String feeToYuan(Integer fee) { | |||
| return new BigDecimal(Double.valueOf(fee) / 100).setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString(); | |||
| } | |||
| public Map<String,String> toMap(){ | |||
| return BeanUtils.xmlBean2Map(this); | |||
| public Map<String, String> toMap() { | |||
| Map<String, String> result = Maps.newHashMap(); | |||
| XmlPath xmlPath = new XmlPath(this.xmlString); | |||
| NodeChildren nodeChildren = xmlPath.getNodeChildren("xml"); | |||
| Iterator<Node> iterator = nodeChildren.nodeIterator(); | |||
| while (iterator.hasNext()) { | |||
| Node node = iterator.next(); | |||
| result.put(node.name(), node.value()); | |||
| } | |||
| return result; | |||
| } | |||
| private String getXmlValueIfExists(XmlPath xmlPath, String path) { | |||
| if (xmlPath.get(path) instanceof NodeChildrenImpl) { | |||
| if (((NodeChildrenImpl) xmlPath.get(path)).size() == 0) { | |||
| return null; | |||
| } | |||
| } | |||
| return xmlPath.getString(path); | |||
| } | |||
| protected <T> T getXmlValueIfExists(XmlPath xmlPath, String path, Class<T> clz) { | |||
| String value = this.getXmlValueIfExists(xmlPath, path); | |||
| if (value == null) { | |||
| return null; | |||
| } | |||
| switch (clz.getSimpleName()) { | |||
| case "String": | |||
| return (T) value; | |||
| case "Integer": | |||
| return (T) Integer.valueOf(value); | |||
| } | |||
| throw new UnsupportedOperationException("暂时不支持此种类型的数据"); | |||
| } | |||
| } | |||
| @@ -1,10 +1,12 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.result; | |||
| import com.thoughtworks.xstream.XStream; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import me.chanjar.weixin.common.util.BeanUtils; | |||
| import me.chanjar.weixin.common.util.ToStringUtils; | |||
| import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||
| import me.chanjar.weixin.mp.bean.pay.WxPayOrderNotifyCoupon; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringStyle; | |||
| import me.chanjar.weixin.mp.bean.pay.WxPayOrderNotifyResultConverter; | |||
| import java.io.Serializable; | |||
| import java.util.List; | |||
| @@ -376,6 +378,15 @@ public class WxPayOrderNotifyResult extends WxPayBaseResult implements Serializ | |||
| @Override | |||
| public String toString() { | |||
| return ToStringBuilder.reflectionToString(this,ToStringStyle.MULTI_LINE_STYLE); | |||
| return ToStringUtils.toSimpleString(this); | |||
| } | |||
| public static WxPayOrderNotifyResult fromXML(String xmlString) { | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.processAnnotations(WxPayOrderNotifyResult.class); | |||
| xstream.registerConverter(new WxPayOrderNotifyResultConverter(xstream.getMapper(), xstream.getReflectionProvider())); | |||
| WxPayOrderNotifyResult result = (WxPayOrderNotifyResult) xstream.fromXML(xmlString); | |||
| result.setXmlString(xmlString); | |||
| return result; | |||
| } | |||
| } | |||
| @@ -2,6 +2,7 @@ package me.chanjar.weixin.mp.bean.pay.result; | |||
| import com.google.common.collect.Lists; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import io.restassured.path.xml.XmlPath; | |||
| import java.util.List; | |||
| @@ -186,7 +187,38 @@ public class WxPayRefundQueryResult extends WxPayBaseResult { | |||
| public void composeRefundRecords(String xmlString) { | |||
| if (this.refundCount != null && this.refundCount > 0) { | |||
| this.refundRecords = Lists.newArrayList(); | |||
| //TODO 暂时待实现 | |||
| XmlPath xmlPath = new XmlPath(xmlString); | |||
| for (int i = 0; i < this.refundCount; i++) { | |||
| RefundRecord refundRecord = new RefundRecord(); | |||
| this.refundRecords.add(refundRecord); | |||
| refundRecord.setOutRefundNo(this.getXmlValueIfExists(xmlPath, "xml.out_refund_no_" + i, String.class)); | |||
| refundRecord.setRefundId(this.getXmlValueIfExists(xmlPath, "xml.refund_id_" + i, String.class)); | |||
| refundRecord.setRefundChannel(this.getXmlValueIfExists(xmlPath, "xml.refund_channel_" + i, String.class)); | |||
| refundRecord.setRefundFee(this.getXmlValueIfExists(xmlPath, "xml.refund_fee_" + i, Integer.class)); | |||
| refundRecord.setSettlementRefundFee(this.getXmlValueIfExists(xmlPath, "xml.settlement_refund_fee_" + i, Integer.class)); | |||
| refundRecord.setCouponType(this.getXmlValueIfExists(xmlPath, "xml.coupon_type_" + i, String.class)); | |||
| refundRecord.setCouponRefundFee(this.getXmlValueIfExists(xmlPath, "xml.coupon_refund_fee_" + i, Integer.class)); | |||
| refundRecord.setCouponRefundCount(this.getXmlValueIfExists(xmlPath, "xml.coupon_refund_count_" + i, Integer.class)); | |||
| refundRecord.setRefundStatus(this.getXmlValueIfExists(xmlPath, "xml.refund_status_" + i, String.class)); | |||
| refundRecord.setRefundRecvAccout(this.getXmlValueIfExists(xmlPath, "xml.refund_recv_accout_" + i, String.class)); | |||
| if (refundRecord.getCouponRefundCount() == null || refundRecord.getCouponRefundCount() == 0) { | |||
| continue; | |||
| } | |||
| List<RefundRecord.RefundCoupon> coupons = Lists.newArrayList(); | |||
| for (int j = 0; j < refundRecord.getCouponRefundCount(); j++) { | |||
| coupons.add( | |||
| new RefundRecord.RefundCoupon( | |||
| this.getXmlValueIfExists(xmlPath, "xml.coupon_refund_id_" + i + "_" + j, String.class), | |||
| this.getXmlValueIfExists(xmlPath, "xml.coupon_refund_fee_" + i + "_" + j, Integer.class) | |||
| ) | |||
| ); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -241,7 +273,7 @@ public class WxPayRefundQueryResult extends WxPayBaseResult { | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("refund_fee") | |||
| private String refundFee; | |||
| private Integer refundFee; | |||
| /** | |||
| * <pre> | |||
| @@ -254,7 +286,7 @@ public class WxPayRefundQueryResult extends WxPayBaseResult { | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("settlement_refund_fee") | |||
| private String settlementRefundFee; | |||
| private Integer settlementRefundFee; | |||
| /** | |||
| * <pre> | |||
| @@ -293,7 +325,7 @@ public class WxPayRefundQueryResult extends WxPayBaseResult { | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("coupon_refund_fee") | |||
| private String couponRefundFee; | |||
| private Integer couponRefundFee; | |||
| /** | |||
| * <pre> | |||
| @@ -306,7 +338,7 @@ public class WxPayRefundQueryResult extends WxPayBaseResult { | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("coupon_refund_count") | |||
| private String couponRefundCount; | |||
| private Integer couponRefundCount; | |||
| private List<RefundCoupon> refundCoupons; | |||
| @@ -364,19 +396,19 @@ public class WxPayRefundQueryResult extends WxPayBaseResult { | |||
| this.refundChannel = refundChannel; | |||
| } | |||
| public String getRefundFee() { | |||
| public Integer getRefundFee() { | |||
| return refundFee; | |||
| } | |||
| public void setRefundFee(String refundFee) { | |||
| public void setRefundFee(Integer refundFee) { | |||
| this.refundFee = refundFee; | |||
| } | |||
| public String getSettlementRefundFee() { | |||
| public Integer getSettlementRefundFee() { | |||
| return settlementRefundFee; | |||
| } | |||
| public void setSettlementRefundFee(String settlementRefundFee) { | |||
| public void setSettlementRefundFee(Integer settlementRefundFee) { | |||
| this.settlementRefundFee = settlementRefundFee; | |||
| } | |||
| @@ -396,19 +428,19 @@ public class WxPayRefundQueryResult extends WxPayBaseResult { | |||
| this.couponType = couponType; | |||
| } | |||
| public String getCouponRefundFee() { | |||
| public Integer getCouponRefundFee() { | |||
| return couponRefundFee; | |||
| } | |||
| public void setCouponRefundFee(String couponRefundFee) { | |||
| public void setCouponRefundFee(Integer couponRefundFee) { | |||
| this.couponRefundFee = couponRefundFee; | |||
| } | |||
| public String getCouponRefundCount() { | |||
| public Integer getCouponRefundCount() { | |||
| return couponRefundCount; | |||
| } | |||
| public void setCouponRefundCount(String couponRefundCount) { | |||
| public void setCouponRefundCount(Integer couponRefundCount) { | |||
| this.couponRefundCount = couponRefundCount; | |||
| } | |||
| @@ -446,6 +478,8 @@ public class WxPayRefundQueryResult extends WxPayBaseResult { | |||
| * 100 | |||
| * 退款代金券批次ID ,$n为下标,$m为下标,从0开始编号 | |||
| * </pre> | |||
| * | |||
| * @deprecated 貌似是被去掉了,但不知是何时! | |||
| */ | |||
| @XStreamAlias("coupon_refund_batch_id") | |||
| private String couponRefundBatchId; | |||
| @@ -474,9 +508,15 @@ public class WxPayRefundQueryResult extends WxPayBaseResult { | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("coupon_refund_fee") | |||
| private String couponRefundFee; | |||
| private Integer couponRefundFee; | |||
| public RefundCoupon(String couponRefundBatchId, String couponRefundId, String couponRefundFee) { | |||
| public RefundCoupon(String couponRefundId, Integer couponRefundFee) { | |||
| this.couponRefundId = couponRefundId; | |||
| this.couponRefundFee = couponRefundFee; | |||
| } | |||
| @Deprecated | |||
| public RefundCoupon(String couponRefundBatchId, String couponRefundId, Integer couponRefundFee) { | |||
| this.couponRefundBatchId = couponRefundBatchId; | |||
| this.couponRefundId = couponRefundId; | |||
| this.couponRefundFee = couponRefundFee; | |||
| @@ -484,5 +524,6 @@ public class WxPayRefundQueryResult extends WxPayBaseResult { | |||
| } | |||
| } | |||
| } | |||
| @@ -13,8 +13,6 @@ import me.chanjar.weixin.mp.bean.pay.result.*; | |||
| import org.testng.annotations.Guice; | |||
| import org.testng.annotations.Test; | |||
| import java.io.File; | |||
| /** | |||
| * 测试支付相关接口 | |||
| * Created by Binary Wang on 2016/7/28. | |||
| @@ -90,7 +88,6 @@ public class WxMpPayServiceImplTest { | |||
| */ | |||
| @Test | |||
| public void testQueryRedpack() throws Exception { | |||
| File keyFile = new File("E:\\dlt.p12"); | |||
| WxPayRedpackQueryResult redpackResult = this.wxService.getPayService().queryRedpack("aaaa"); | |||
| System.err.println(redpackResult); | |||
| } | |||
| @@ -0,0 +1,49 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.result; | |||
| import org.testng.Assert; | |||
| import org.testng.annotations.Test; | |||
| /** | |||
| * <pre> | |||
| * Created by Binary Wang on 2016-12-29. | |||
| * @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||
| * </pre> | |||
| */ | |||
| public class WxPayRefundQueryResultTest { | |||
| @Test | |||
| public void composeRefundRecords() throws Exception { | |||
| /* | |||
| 该xml字符串来自于官方文档示例 | |||
| */ | |||
| String xmlString = "<xml>\n" + | |||
| " <appid><![CDATA[wx2421b1c4370ec43b]]></appid>\n" + | |||
| " <mch_id><![CDATA[10000100]]></mch_id>\n" + | |||
| " <nonce_str><![CDATA[TeqClE3i0mvn3DrK]]></nonce_str>\n" + | |||
| " <out_refund_no_0><![CDATA[1415701182]]></out_refund_no_0>\n" + | |||
| " <out_trade_no><![CDATA[1415757673]]></out_trade_no>\n" + | |||
| " <refund_count>1</refund_count>\n" + | |||
| " <refund_fee_0>1</refund_fee_0>\n" + | |||
| " <refund_id_0><![CDATA[2008450740201411110000174436]]></refund_id_0>\n" + | |||
| " <refund_status_0><![CDATA[PROCESSING]]></refund_status_0>\n" + | |||
| " <result_code><![CDATA[SUCCESS]]></result_code>\n" + | |||
| " <return_code><![CDATA[SUCCESS]]></return_code>\n" + | |||
| " <return_msg><![CDATA[OK]]></return_msg>\n" + | |||
| " <sign><![CDATA[1F2841558E233C33ABA71A961D27561C]]></sign>\n" + | |||
| " <transaction_id><![CDATA[1008450740201411110005820873]]></transaction_id>\n" + | |||
| "</xml>"; | |||
| WxPayRefundQueryResult result = new WxPayRefundQueryResult(); | |||
| result.setRefundCount(1); | |||
| result.composeRefundRecords(xmlString); | |||
| Assert.assertNotNull(result.getRefundRecords()); | |||
| Assert.assertEquals(result.getRefundRecords().size(), 1); | |||
| Assert.assertEquals(result.getRefundRecords().get(0).getRefundId(), "2008450740201411110000174436"); | |||
| Assert.assertEquals(result.getRefundRecords().get(0).getRefundFee().intValue(), 1); | |||
| Assert.assertEquals(result.getRefundRecords().get(0).getOutRefundNo(), "1415701182"); | |||
| Assert.assertEquals(result.getRefundRecords().get(0).getRefundStatus(), "PROCESSING"); | |||
| } | |||
| } | |||