├── .gitignore ├── .travis.yml ├── API.md ├── LICENSE.md ├── README.md ├── alipay.png ├── pom.xml ├── wechat.png ├── wepay-core ├── pom.xml └── src │ ├── main │ └── java │ │ └── me │ │ └── hao0 │ │ └── wepay │ │ ├── annotation │ │ └── Optional.java │ │ ├── core │ │ ├── Bills.java │ │ ├── Component.java │ │ ├── Notifies.java │ │ ├── Orders.java │ │ ├── Pays.java │ │ ├── Refunds.java │ │ ├── Wepay.java │ │ └── WepayBuilder.java │ │ ├── exception │ │ ├── SignException.java │ │ └── WepayException.java │ │ ├── model │ │ ├── bill │ │ │ ├── Bill.java │ │ │ ├── BillCount.java │ │ │ ├── BillDetail.java │ │ │ ├── BillFields.java │ │ │ ├── CommonBill.java │ │ │ └── RefundBill.java │ │ ├── common │ │ │ └── Coupon.java │ │ ├── enums │ │ │ ├── BillType.java │ │ │ ├── FeeType.java │ │ │ ├── RefundChannel.java │ │ │ ├── RefundStatus.java │ │ │ ├── TradeState.java │ │ │ ├── TradeType.java │ │ │ └── WepayField.java │ │ ├── order │ │ │ └── WePayOrder.java │ │ ├── pay │ │ │ ├── AppPayResponse.java │ │ │ ├── JsPayRequest.java │ │ │ ├── JsPayResponse.java │ │ │ ├── PayRequest.java │ │ │ ├── QrPayRequest.java │ │ │ └── QrPayResponse.java │ │ └── refund │ │ │ ├── RefundApplyRequest.java │ │ │ ├── RefundApplyResponse.java │ │ │ ├── RefundItem.java │ │ │ └── RefundQueryResponse.java │ │ ├── serializer │ │ ├── BooleanDeserializer.java │ │ ├── DateDeserializer.java │ │ ├── FeeTypeDeserializer.java │ │ ├── RefundChannelDeserializer.java │ │ ├── TradeStateDeserializer.java │ │ └── TradeTypeDeserializer.java │ │ └── util │ │ ├── Maps.java │ │ └── RandomStrs.java │ └── test │ └── java │ └── me │ └── hao0 │ └── wepay │ └── WepayTest.java └── wepay-demo ├── pom.xml └── src └── main ├── java └── me │ └── hao0 │ └── wepay │ └── demo │ ├── controller │ ├── Notifies.java │ ├── Pays.java │ └── Refunds.java │ └── support │ └── WepaySupport.java ├── resources ├── app-example.properties ├── logback.xml └── spring │ ├── root-context.xml │ └── web-context.xml └── webapp └── WEB-INF └── web.xml /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | server_log_home_IS_UNDEFINED 10 | 11 | # Packages # 12 | ############ 13 | # it's better to unpack these files and commit the raw source 14 | # git has its own built in compression methods 15 | *.7z 16 | *.dmg 17 | *.gz 18 | *.iso 19 | *.jar 20 | *.rar 21 | *.tar 22 | *.zip 23 | 24 | # Logs and databases # 25 | ###################### 26 | *.log 27 | 28 | # OS generated files # 29 | ###################### 30 | .DS_Store* 31 | ehthumbs.db 32 | #Icon? 33 | Thumbs.db 34 | 35 | # Editor Files # 36 | ################ 37 | *~ 38 | *.swp 39 | 40 | # Gradle Files # 41 | ################ 42 | .gradle 43 | 44 | # Build output directies 45 | /target 46 | */target 47 | /build 48 | */build 49 | /generated-sources 50 | */generated-sources 51 | *Thunderbolt/core/generated-sources/* 52 | var 53 | logs 54 | 55 | .idea 56 | # IntelliJ specific files/directories 57 | out 58 | .idea/* 59 | *.ipr 60 | *.iws 61 | *.iml 62 | atlassian-ide-plugin.xml 63 | 64 | # Eclipse specific files/directories 65 | .classpath 66 | .project 67 | .settings 68 | .metadata 69 | .recommenders 70 | 71 | # NetBeans specific files/directories 72 | .nbattrs 73 | *.orig 74 | service-location/service-location/* 75 | service-base/service-base/* 76 | service-coupon/service-coupon/* 77 | service-courier/service-courier/* 78 | service-demo/service-demo/* 79 | service-sms/service-sms/* 80 | service-user/service-user/* 81 | 82 | dev.properties 83 | 84 | Demo*.java 85 | 86 | *.p12 87 | 88 | app.properties 89 | 90 | pom.xml.versionsBackup 91 | 92 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | # - oraclejdk7 5 | - openjdk7 6 | 7 | script: mvn clean package -DskipTests -------------------------------------------------------------------------------- /API.md: -------------------------------------------------------------------------------- 1 | # Wepay API文档 2 | 3 | + 已实现组件: 4 | 5 | + 支付: pay() 6 | + 退款: refund() 7 | + 订单: order() 8 | + 通知: notify() 9 | + 账单: bill() 10 | 11 | + **支付pay()**: 12 | 13 | ```java 14 | /** 15 | * JS支付(公众号支付) 16 | * @param request 支付请求对象 17 | * @return JsPayResponse对象,或抛WepayException 18 | */ 19 | JsPayResponse jsPay(JsPayRequest request); 20 | 21 | /** 22 | * 动态二维码支付(NATIVE)[模式二] 23 | * @param request 支付请求对象 24 | * @param convert 是否转换为二维码图片链接(使用联图) 25 | * @return 可访问的二维码链接,或抛WepayException 26 | */ 27 | QrPayResponse qrPay(QrPayRequest request, Boolean convert); 28 | 29 | /** 30 | * app支付 31 | * @param request 支付请求对象 32 | * @return AppPayResponse对象,或抛WepayException 33 | */ 34 | AppPayResponse appPay(PayRequest request); 35 | ``` 36 | 37 | + **退款refund()**: 38 | 39 | ```java 40 | /** 41 | * 申请退款 42 | * @param request 退款请求对象 43 | * @return RefundResponse对象,或抛WepayException 44 | */ 45 | RefundApplyResponse apply(RefundApplyRequest request); 46 | 47 | /** 48 | * 通过商户订单号查询退款 49 | * @param outTradeNo 商户订单号 50 | * @return 退款查询对象,或抛WepayException 51 | */ 52 | RefundQueryResponse queryByOutTradeNo(String outTradeNo); 53 | 54 | /** 55 | * 通过商户退款单号查询退款 56 | * @param outRefundNo 商户退款单号 57 | * @return 退款查询对象,或抛WepayException 58 | */ 59 | RefundQueryResponse queryByOutRefundNo(String outRefundNo); 60 | 61 | /** 62 | * 通过微信订单号查询退款 63 | * @param transactionId 微信订单号 64 | * @return 退款查询对象,或抛WepayException 65 | */ 66 | RefundQueryResponse queryByTransactionId(String transactionId); 67 | 68 | /** 69 | * 通过微信退款单号查询退款 70 | * @param refundId 微信退款单号 71 | * @return 退款查询对象,或抛WepayException 72 | */ 73 | RefundQueryResponse queryByRefundId(String refundId); 74 | ``` 75 | 76 | + **订单order()**: 77 | 78 | ```java 79 | /** 80 | * 根据微信订单号查询订单 81 | * @param transactionId 微信订单号 82 | * @return PayOrder对象,或抛WepayException 83 | */ 84 | WePayOrder queryByTransactionId(String transactionId); 85 | 86 | /** 87 | * 根据商户订单号查询订单 88 | * @param outTradeNo 商户订单号 89 | * @return PayOrder对象,或抛WepayException 90 | */ 91 | WePayOrder queryByOutTradeNo(String outTradeNo); 92 | 93 | /** 94 | * 关闭订单 95 | * @param outTradeNo 商户订单号 96 | * @return 关闭成功返回true,或抛WepayException 97 | */ 98 | Boolean closeOrder(String outTradeNo); 99 | 100 | ``` 101 | 102 | + **通知notify()**: 103 | 104 | ```java 105 | /** 106 | * 签名校验 107 | * @param params 待验证参数(包含sign) 108 | * @return 验证通过返回true,反之false 109 | */ 110 | Boolean verifySign(Map params); 111 | 112 | /** 113 | * 通知成功 114 | * @return 通知成功的XML消息 115 | */ 116 | String ok(); 117 | 118 | /** 119 | * 通知不成功 120 | * @param errMsg 失败消息 121 | * @return 通知失败的XML消息 122 | */ 123 | String notOk(String errMsg); 124 | ``` 125 | 126 | + **通知bill()**: 127 | 128 | ```java 129 | /** 130 | * 查询所有账单 131 | * @param deviceInfo 微信支付分配的终端设备号,填写此字段,只下载该设备号的对账单 132 | * @param date 账单的日期 133 | * @return 账单明细 134 | */ 135 | BillDetail queryAll(String deviceInfo, String date); 136 | 137 | /** 138 | * 查询交易成功的账单 139 | * @param deviceInfo 微信支付分配的终端设备号,填写此字段,只下载该设备号的对账单 140 | * @param date 账单的日期 141 | * @return 账单明细 142 | */ 143 | BillDetail querySuccess(String deviceInfo, String date); 144 | 145 | /** 146 | * 查询退款账单 147 | * @param deviceInfo 微信支付分配的终端设备号,填写此字段,只下载该设备号的对账单 148 | * @param date 账单的日期 149 | * @return 账单明细 150 | */ 151 | BillDetail queryRefund(String deviceInfo, String date); 152 | ``` -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Being simple 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wepay[![Build Status](https://travis-ci.org/ihaolin/wepay.svg?branch=master)](https://travis-ci.org/ihaolin/wepay) 2 | 3 | 轻量的微信支付组件(A Lightweight Wechat Pay Component) 4 | --- 5 | 6 | + 包引入: 7 | 8 | ```xml 9 | 10 | me.hao0 11 | wepay-core 12 | 1.3.2 13 | 14 | ``` 15 | 16 | + 依赖包,注意引入项目时是否需要**exclude**: 17 | 18 | ```xml 19 | 20 | me.hao0 21 | common 22 | 1.1.2 23 | 24 | 25 | ``` 26 | 27 | + 基本用法: 28 | 29 | ```java 30 | Wepay wepay = WepayBuilder 31 | .newBuilder(appId, appKey, mchId) 32 | .config1(...) // 其他可选配置 33 | ... 34 | .build(); 35 | 36 | wepay.module().api(); 37 | ``` 38 | 39 | + 已实现的组件: 40 | 41 | + 支付``pay()``; 42 | + 退款``refund()``; 43 | + 订单``order()``; 44 | + 通知``notify()``; 45 | + 账单``bill()``。 46 | 47 | + API文档[这里](API.md)。 48 | 49 | + **关于测试**: 50 | 51 | + [测试用例中](wepay-core/src/test/java/me/hao0/wepay/WepayTest.java)是一些基本测试,需作一些配置: 52 | 53 | ```java 54 | // 在test/reources目录中配置dev.properties 55 | // 包括appId(APP ID), appKey(支付密钥), mchId(商户号) 56 | Properties props = new Properties(); 57 | InputStream in = Object.class.getResourceAsStream("/dev.properties"); 58 | props.load(in); 59 | in.close(); 60 | 61 | // 配置证书,退款需要证书,不配置可测试除退款的接口 62 | Path path = Paths.get("/path/to/your_cert.p12"); 63 | byte[] data = Files.readAllBytes(path); 64 | 65 | wepay = WepayBuilder.newBuilder( 66 | props.getProperty("appId"), 67 | props.getProperty("appKey"), 68 | props.getProperty("mchId")) 69 | .certPasswd(props.getProperty("mchId")) 70 | .certs(data) 71 | .build(); 72 | ``` 73 | 74 | + [wepay-demo](wepay-demo)项目是一个可运行web项目,方便测试,可按如下步骤进行测试,复制[wepay-demo](wepay-demo)中的``app-example.properties``为``app.properties``,并作相应配置: 75 | 76 | ```ruby 77 | # 微信app id 78 | appId= 79 | # 微信支付key 80 | appKey= 81 | # 商户号 82 | mchId= 83 | # 支付通知url 84 | payNotifyUrl=${your_domain}/notifies/paid 85 | ``` 86 | 87 | + **注意**:**``payNotifyUrl ``**应该配置为微信服务器可以外网调用的地址,本地测试建议使用[ngrok](https://ngrok.com/)工具来作本地外网映射。 88 | 89 | + 到[wepay-demo](wepay-demo)根目录运行以下命令即可: 90 | 91 | ```bash 92 | mvn clean jetty:run -Dmaven.test.skip -Djetty.port={自定义端口号} 93 | ``` 94 | 95 | + 动态二维码支付可访问(**请求正常后,会出现由联图生成的二维码图片,用微信扫描支付成功后,后台会得到对应通知``Notifies``**): 96 | 97 | ```bash 98 | http://localhost:{port}/pays/qrpay?orderNumber={自定义订单号} 99 | ``` 100 | 101 | + 退款可访问(**提交成功后,微信会有消息通知**): 102 | 103 | ```bash 104 | http://localhost:{port}/refunds/apply?orderNumber={商户订单号} ``` 105 | 106 | + 相关文档: 107 | 108 | + [微信支付文档](https://pay.weixin.qq.com/wiki/doc/api/index.html)。 109 | 110 | + 历史版本: 111 | 112 | + 1.0.0: 113 | 114 | + 基本功能实现。 115 | 116 | + 1.1.0: 117 | 118 | + 增加账单查询。 119 | 120 | + 1.1.1: 121 | 122 | + 修复prepayId。 123 | 124 | + 1.1.2: 125 | 126 | + 修复JS/APP支付签名问题。 127 | 128 | + 1.2.2: 129 | 130 | + 增加退款查询字段: 退款状态, 退款金额, 退款入账方。 131 | 132 | + 1.2.3: 133 | 134 | + fix isNullOrEmpty。 135 | 136 | + 1.2.4: 137 | 138 | + 升级common至1.1.2, 可配置解析微信XML的编码类型, 默认为UTF-8, 防止与本地默认编码不一致。 139 | 140 | + 1.2.5: 141 | 142 | + 二维码支付结果更新为[QrPayResponse](wepay-core/src/main/java/me/hao0/wepay/model/pay/QrPayResponse.java),并增加**prepay_id**字段。 143 | 144 | + 1.3.0: 145 | 146 | + 获取微信响应内容,签名校验失败时,抛出异常[SignException](wepay-core/src/main/java/me/hao0/wepay/exception/SignException.java)。 147 | 148 | + 1.3.1: 149 | 150 | + 修复`goods_tag`字段名。 151 | 152 | + 1.3.2: 153 | 154 | + 修复`clientIp`字段为`clientIp`。 155 | 156 | + 相关组件: 157 | 158 | + 支付宝支付组件; 159 | + 微信公众号组件。 160 | 161 | ## 有事请烧钱 162 | 163 | + 支付宝: 164 | 165 | 166 | 167 | + 微信: 168 | 169 | 170 | -------------------------------------------------------------------------------- /alipay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihaolin/wepay/3360b6a57493d879d5a19def833db36fb455f859/alipay.png -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.hao0 8 | wepay 9 | 1.3.2 10 | 11 | wepay-core 12 | wepay-demo 13 | 14 | pom 15 | 16 | wepay 17 | 轻量的微信支付组件(A Lightweight Wechat Pay Component) 18 | https://github.com/ihaolin/wepay 19 | 20 | 21 | 22 | The MIT License (MIT) 23 | https://opensource.org/licenses/MIT 24 | 25 | 26 | 27 | 28 | 29 | haolin 30 | haolin.h0@gmail.com 31 | 32 | 33 | 34 | 35 | scm:git:git@github.com:ihaolin/wepay.git 36 | scm:git:git@github.com:ihaolin/wepay.git 37 | git@github.com:ihaolin/wepay.git 38 | 39 | 40 | 41 | 42 | 43 | org.apache.maven.plugins 44 | maven-compiler-plugin 45 | 2.3.2 46 | 47 | 1.7 48 | 1.7 49 | 50 | 51 | 52 | org.codehaus.mojo 53 | versions-maven-plugin 54 | 2.2 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | org.codehaus.mojo 63 | findbugs-maven-plugin 64 | 3.0.3 65 | 66 | true 67 | target/site 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | release 76 | 77 | 78 | 79 | org.apache.maven.plugins 80 | maven-source-plugin 81 | 2.2.1 82 | 83 | 84 | package 85 | 86 | jar-no-fork 87 | 88 | 89 | 90 | 91 | 92 | 93 | org.apache.maven.plugins 94 | maven-javadoc-plugin 95 | 2.10.3 96 | 97 | 98 | package 99 | 100 | jar 101 | 102 | 103 | 104 | 105 | 106 | 107 | org.apache.maven.plugins 108 | maven-gpg-plugin 109 | 1.6 110 | 111 | 112 | verify 113 | 114 | sign 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | oss 124 | https://oss.sonatype.org/content/repositories/snapshots/ 125 | 126 | 127 | oss 128 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /wechat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihaolin/wepay/3360b6a57493d879d5a19def833db36fb455f859/wechat.png -------------------------------------------------------------------------------- /wepay-core/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | wepay 5 | me.hao0 6 | 1.3.2 7 | 8 | 4.0.0 9 | 10 | 1.3.2 11 | wepay-core 12 | jar 13 | 14 | wepay-core 15 | http://maven.apache.org 16 | 17 | 18 | UTF-8 19 | 20 | 21 | 22 | 23 | 24 | me.hao0 25 | common 26 | 1.1.2 27 | 28 | 29 | 30 | junit 31 | junit 32 | 4.11 33 | test 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/annotation/Optional.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.annotation; 2 | 3 | /** 4 | * 标记字段是可选的 5 | * Author: haolin 6 | * Email: haolin.h0@gmail.com 7 | * Date: 11/11/15 8 | * @since 1.0.0 9 | */ 10 | public @interface Optional { 11 | 12 | /** 13 | * 是否任何情况下都可选 14 | * @return optional or not 15 | */ 16 | boolean any() default true; 17 | } 18 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/core/Bills.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.core; 2 | 3 | import me.hao0.common.http.Http; 4 | import me.hao0.common.json.Jsons; 5 | import me.hao0.common.xml.XmlReaders; 6 | import me.hao0.wepay.exception.WepayException; 7 | import me.hao0.wepay.model.bill.Bill; 8 | import me.hao0.wepay.model.bill.BillCount; 9 | import me.hao0.wepay.model.bill.BillDetail; 10 | import me.hao0.wepay.model.bill.BillFields; 11 | import me.hao0.wepay.model.bill.CommonBill; 12 | import me.hao0.wepay.model.bill.RefundBill; 13 | import me.hao0.wepay.model.enums.BillType; 14 | import me.hao0.wepay.model.enums.WepayField; 15 | import me.hao0.wepay.util.Maps; 16 | import me.hao0.wepay.util.RandomStrs; 17 | import java.util.ArrayList; 18 | import java.util.HashMap; 19 | import java.util.List; 20 | import java.util.Map; 21 | import java.util.TreeMap; 22 | import static me.hao0.common.util.Preconditions.*; 23 | 24 | /** 25 | * 账单组件 26 | * Author: haolin 27 | * Email: haolin.h0@gmail.com 28 | * Date: 4/12/15 29 | * @since 1.1.0 30 | */ 31 | public class Bills extends Component { 32 | 33 | /** 34 | * 下载账单 35 | */ 36 | private static final String DOWNLOAD = "https://api.mch.weixin.qq.com/pay/downloadbill"; 37 | 38 | private static final String LINE_SEPARATOR = "\\n"; 39 | 40 | private static final String FIELD_SEPARATOR = ",`"; 41 | 42 | Bills(Wepay wepay) { 43 | super(wepay); 44 | } 45 | 46 | /** 47 | * 查询所有账单 48 | * @param deviceInfo 微信支付分配的终端设备号,填写此字段,只下载该设备号的对账单 49 | * @param date 账单的日期 50 | * @return 账单明细 51 | */ 52 | public BillDetail queryAll(String deviceInfo, String date){ 53 | String data = query(deviceInfo, date, BillType.ALL); 54 | return renderBillDetail(data, BillFields.ALL, CommonBill.class); 55 | } 56 | 57 | /** 58 | * 查询交易成功账单 59 | * @param deviceInfo 微信支付分配的终端设备号,填写此字段,只下载该设备号的对账单 60 | * @param date 账单的日期 61 | * @return 账单明细 62 | */ 63 | public BillDetail querySuccess(String deviceInfo, String date){ 64 | String data = query(deviceInfo, date, BillType.SUCCESS); 65 | return renderBillDetail(data, BillFields.SUCCESS, Bill.class); 66 | } 67 | 68 | /** 69 | * 查询退款账单 70 | * @param deviceInfo 微信支付分配的终端设备号,填写此字段,只下载该设备号的对账单 71 | * @param date 账单的日期 72 | * @return 账单明细 73 | */ 74 | public BillDetail queryRefund(String deviceInfo, String date){ 75 | String data = query(deviceInfo, date, BillType.REFUND); 76 | return renderBillDetail(data, BillFields.REFUND, RefundBill.class); 77 | } 78 | 79 | @SuppressWarnings("unchecked") 80 | private BillDetail renderBillDetail(String billData, List fields, Class billClazz) { 81 | String[] dataLines = billData.split(LINE_SEPARATOR); 82 | if (dataLines.length > 0){ 83 | List bills = new ArrayList<>(); 84 | T bill; 85 | for (int i = 1; i(bills, count); 92 | } 93 | return BillDetail.empty(); 94 | } 95 | 96 | private BillCount renderCount(String countData) { 97 | // remove first ` 98 | countData = countData.substring(1).replaceAll("\\r", ""); 99 | String[] fieldVals = countData.split(FIELD_SEPARATOR); 100 | Map billCount = new HashMap<>(); 101 | List fieldNames = BillFields.COUNT; 102 | for (int i = 0; i T renderBill(String dataLine, List fieldNames, Class billClazz) { 109 | // remove first ` 110 | dataLine = dataLine.substring(1).replaceAll("\\r", ""); 111 | String[] fieldVals = dataLine.split(FIELD_SEPARATOR); 112 | Map billData = new HashMap<>(); 113 | for (int i = 0; i downloadParams = buildDownloadParams(deviceInfo, date, type); 131 | String billData = Http.post(DOWNLOAD).body(Maps.toXml(downloadParams)).request(); 132 | if (billData.startsWith("")){ 133 | XmlReaders readers = XmlReaders.create(billData); 134 | throw new WepayException( 135 | readers.getNodeStr(WepayField.RETURN_CODE), 136 | readers.getNodeStr(WepayField.RETURN_MSG)); 137 | } 138 | return billData; 139 | } 140 | 141 | 142 | private Map buildDownloadParams(String deviceInfo, String date, BillType type) { 143 | Map downloadParams = new TreeMap<>(); 144 | 145 | buildConfigParams(downloadParams); 146 | 147 | put(downloadParams, WepayField.NONCE_STR, RandomStrs.generate(16)); 148 | put(downloadParams, WepayField.BILL_TYPE, type.type()); 149 | put(downloadParams, WepayField.BILL_DATE, date); 150 | 151 | putIfNotEmpty(downloadParams, WepayField.DEVICE_INFO, deviceInfo); 152 | 153 | buildSignParams(downloadParams); 154 | 155 | return downloadParams; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/core/Component.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.core; 2 | 3 | import me.hao0.common.http.Http; 4 | import me.hao0.common.http.Https; 5 | import me.hao0.common.json.Jsons; 6 | import me.hao0.common.security.MD5; 7 | import me.hao0.common.util.Strings; 8 | import me.hao0.common.xml.XmlReaders; 9 | import me.hao0.wepay.exception.SignException; 10 | import me.hao0.wepay.exception.WepayException; 11 | import me.hao0.wepay.model.enums.WepayField; 12 | import me.hao0.wepay.util.Maps; 13 | import java.util.Map; 14 | import java.util.TreeMap; 15 | 16 | /** 17 | * Author: haolin 18 | * Email: haolin.h0@gmail.com 19 | * Date: 26/11/15 20 | */ 21 | public abstract class Component { 22 | 23 | protected Wepay wepay; 24 | 25 | protected Component(Wepay wepay){ 26 | this.wepay = wepay; 27 | } 28 | 29 | protected Map doPost(final String url, final Map params){ 30 | String requestBody = Maps.toXml(params); 31 | String resp = Http.post(url).ssl().body(requestBody).request(); 32 | Map respMap = toMap(resp.replaceAll("(\\r|\\n)", "")); 33 | if (!doVerifySign(respMap)){ 34 | throw new SignException("微信响应内容签名非法: " + respMap); 35 | } 36 | return respMap; 37 | } 38 | 39 | protected T doHttpsPost(final String url, final Map params, Class respClazz){ 40 | String requestBody = Maps.toXml(params); 41 | String resp = Https.post(url).body(requestBody) 42 | .ssLSocketFactory(wepay.getSslSocketFactory()).request(); 43 | Map respMap = toMap(resp.replaceAll("(\\r|\\n)", "")); 44 | if (!doVerifySign(respMap)){ 45 | throw new SignException("微信响应内容签名非法: " + respMap); 46 | } 47 | return Jsons.DEFAULT.fromJson(Jsons.DEFAULT.toJson(respMap), respClazz); 48 | } 49 | 50 | 51 | /** 52 | * 将微信XML转换为Map 53 | * @param xml xml字符串 54 | * @return Map对象,或抛WechatException 55 | */ 56 | protected Map toMap(final String xml) { 57 | XmlReaders readers = readResp(xml); 58 | return Maps.toMap(readers); 59 | } 60 | 61 | /** 62 | * 读取微信xml响应 63 | * @param xml xml字符串 64 | * @return 若成功,返回对应Reader,反之抛WepayException 65 | */ 66 | private XmlReaders readResp(final String xml) { 67 | XmlReaders readers = XmlReaders.create(xml, wepay.respEncode); 68 | String returnCode = readers.getNodeStr(WepayField.RETURN_CODE); 69 | if (WepayField.SUCCESS.equals(returnCode)){ 70 | String resultCode = readers.getNodeStr(WepayField.RESULT_CODE); 71 | if (WepayField.SUCCESS.equals(resultCode)){ 72 | return readers; 73 | } 74 | throw new WepayException( 75 | readers.getNodeStr(WepayField.ERR_CODE), 76 | readers.getNodeStr(WepayField.ERR_CODE_DES)); 77 | } 78 | throw new WepayException( 79 | readers.getNodeStr(WepayField.RETURN_CODE), 80 | readers.getNodeStr(WepayField.RETURN_MSG)); 81 | } 82 | 83 | /** 84 | * 构建配置参数 85 | * @param params 参数 86 | */ 87 | protected void buildConfigParams(final Map params){ 88 | params.put(WepayField.APP_ID, wepay.getAppId()); 89 | params.put(WepayField.MCH_ID, wepay.getMchId()); 90 | } 91 | 92 | /** 93 | * 构建签名参数 94 | * @param params 支付参数 95 | */ 96 | protected void buildSignParams(final Map params) { 97 | String sign = doSign(params); 98 | put(params, WepayField.SIGN, sign); 99 | } 100 | 101 | /** 102 | * 支付请求前签名 103 | * @param params 参数(已经升序, 排出非空值和sign) 104 | * @return MD5的签名字符串(大写) 105 | */ 106 | protected String doSign(final Map params) { 107 | StringBuilder signing = new StringBuilder(); 108 | 109 | for (Map.Entry entry : params.entrySet()) { 110 | if (!Strings.isNullOrEmpty(entry.getValue())){ 111 | signing.append(entry.getKey()).append('=').append(entry.getValue()).append("&"); 112 | } 113 | } 114 | 115 | // append key 116 | signing.append("key=").append(wepay.getAppKey()); 117 | 118 | // md5 119 | return MD5.generate(signing.toString(), false).toUpperCase(); 120 | } 121 | 122 | /** 123 | * 校验参数 124 | * @param data 待校验参数 125 | * @return 校验成功返回true,反之false 126 | */ 127 | protected Boolean doVerifySign(final Map data) { 128 | String actualSign = String.valueOf(data.get(WepayField.SIGN)); 129 | Map signingMap = filterSignParams(data); 130 | String expectSign = doSign(signingMap); 131 | return expectSign.equals(actualSign); 132 | } 133 | 134 | /** 135 | * 过滤签名参数(升序,排出空值,sign) 136 | * @param params 待校验参数 137 | * @return 过滤后的参数 138 | */ 139 | protected Map filterSignParams(final Map params) { 140 | Map validParams = new TreeMap<>(); 141 | 142 | for (Map.Entry param : params.entrySet()){ 143 | if (WepayField.SIGN.equals(param.getKey()) 144 | || param.getValue() == null 145 | || "".equals(String.valueOf(param.getValue()))){ 146 | continue; 147 | } 148 | validParams.put(param.getKey(), String.valueOf(param.getValue())); 149 | } 150 | 151 | return validParams; 152 | } 153 | 154 | protected void putIfNotEmpty(final Map map, String field, String paramValue) { 155 | if (!Strings.isNullOrEmpty(paramValue)){ 156 | map.put(field, paramValue); 157 | } 158 | } 159 | 160 | protected void put(final Map map, String field, String paramValue){ 161 | map.put(field, paramValue); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/core/Notifies.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.core; 2 | 3 | import me.hao0.wepay.model.enums.WepayField; 4 | import me.hao0.wepay.util.Maps; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * 通知组件 11 | * Author: haolin 12 | * Email: haolin.h0@gmail.com 13 | * Date: 28/11/15 14 | * @since 1.0.0 15 | */ 16 | public class Notifies extends Component { 17 | 18 | Notifies(Wepay wepay) { 19 | super(wepay); 20 | } 21 | 22 | /** 23 | * 签名校验 24 | * @param params 待验证参数(包含sign) 25 | * @return 验证通过返回true,反之false 26 | */ 27 | public Boolean verifySign(Map params){ 28 | return doVerifySign(params); 29 | } 30 | 31 | /** 32 | * 通知成功 33 | * @return 通知成功的XML消息 34 | */ 35 | public String ok(){ 36 | Map notifyParams = new HashMap<>(); 37 | notifyParams.put(WepayField.RETURN_CODE, "SUCCESS"); 38 | notifyParams.put(WepayField.RETURN_MSG, "OK"); 39 | return Maps.toXml(notifyParams); 40 | } 41 | 42 | /** 43 | * 通知不成功 44 | * @param errMsg 失败消息 45 | * @return 通知失败的XML消息 46 | */ 47 | public String notOk(String errMsg){ 48 | Map notifyParams = new HashMap<>(); 49 | notifyParams.put(WepayField.RETURN_CODE, "FAIL"); 50 | notifyParams.put(WepayField.RETURN_MSG, errMsg); 51 | return Maps.toXml(notifyParams); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/core/Orders.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.core; 2 | 3 | import me.hao0.common.json.Jsons; 4 | import me.hao0.wepay.model.common.Coupon; 5 | import me.hao0.wepay.model.enums.WepayField; 6 | import me.hao0.wepay.model.order.WePayOrder; 7 | import me.hao0.wepay.util.RandomStrs; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.TreeMap; 12 | import static me.hao0.common.util.Preconditions.*; 13 | 14 | /** 15 | * 订单组件 16 | * Author: haolin 17 | * Email: haolin.h0@gmail.com 18 | * Date: 27/11/15 19 | * @since 1.0.0 20 | */ 21 | public final class Orders extends Component { 22 | 23 | /** 24 | * 查询订单 25 | */ 26 | private static final String ORDER_QUERY = "https://api.mch.weixin.qq.com/pay/orderquery"; 27 | 28 | /** 29 | * 关闭订单 30 | */ 31 | private static final String ORDER_CLOSE = "https://api.mch.weixin.qq.com/pay/closeorder"; 32 | 33 | Orders(Wepay wepay) { 34 | super(wepay); 35 | } 36 | 37 | /** 38 | * 根据微信订单号查询订单 39 | * @param transactionId 微信订单号 40 | * @return PayOrder对象,或抛WepayException 41 | */ 42 | public WePayOrder queryByTransactionId(String transactionId){ 43 | checkNotNullAndEmpty(transactionId, "transactionId"); 44 | Map queryParams = new TreeMap<>(); 45 | put(queryParams, WepayField.TRANSACTION_ID, transactionId); 46 | return doQueryOrder(queryParams); 47 | } 48 | 49 | /** 50 | * 根据商户订单号查询订单 51 | * @param outTradeNo 商户订单号 52 | * @return PayOrder对象,或抛WepayException 53 | */ 54 | public WePayOrder queryByOutTradeNo(String outTradeNo){ 55 | checkNotNullAndEmpty(outTradeNo, "outTradeNo"); 56 | Map queryParams = new TreeMap<>(); 57 | put(queryParams, WepayField.OUT_TRADE_NO, outTradeNo); 58 | return doQueryOrder(queryParams); 59 | } 60 | 61 | private WePayOrder doQueryOrder(Map queryParams) { 62 | buildQueryParams(queryParams); 63 | Map orderData = doPost(ORDER_QUERY, queryParams); 64 | WePayOrder order = Jsons.DEFAULT.fromJson(Jsons.DEFAULT.toJson(orderData), WePayOrder.class); 65 | setCoupons(order, orderData); 66 | return order; 67 | } 68 | 69 | private void setCoupons(WePayOrder order, Map orderData) { 70 | if (order != null 71 | && order.getCouponCount() != null 72 | && order.getCouponCount() > 0){ 73 | List coupons = new ArrayList<>(); 74 | Coupon coupon; 75 | for (int couponIndex = 0; couponIndex < order.getCouponCount(); couponIndex++){ 76 | coupon = Coupon.newCoupon( 77 | (String)orderData.get(WepayField.COUPON_BATCH_ID + "_" + couponIndex), 78 | (String) orderData.get(WepayField.COUPON_ID + "_" + couponIndex), 79 | Integer.parseInt((String) orderData.get(WepayField.COUPON_FEE + "_" + couponIndex)) 80 | ); 81 | coupons.add(coupon); 82 | } 83 | order.setCoupons(coupons); 84 | } 85 | } 86 | 87 | /** 88 | * 关闭订单 89 | * @param outTradeNo 商户订单号 90 | * @return 关闭成功返回true,或抛WepayException 91 | */ 92 | public Boolean closeOrder(String outTradeNo){ 93 | checkNotNullAndEmpty(outTradeNo, "outTradeNo"); 94 | Map closeParams = new TreeMap<>(); 95 | put(closeParams, WepayField.OUT_TRADE_NO, outTradeNo); 96 | buildCloseParams(closeParams); 97 | return doPost(ORDER_CLOSE, closeParams) != null; 98 | } 99 | 100 | /** 101 | * 构建关闭订单参数 102 | * @param closeParams 关闭参数 103 | */ 104 | private void buildCloseParams(Map closeParams) { 105 | buildConfigParams(closeParams); 106 | put(closeParams, WepayField.NONCE_STR, RandomStrs.generate(16)); 107 | buildSignParams(closeParams); 108 | } 109 | 110 | /** 111 | * 构建查询订单参数 112 | * @param queryParams 查询参数 113 | */ 114 | private void buildQueryParams(Map queryParams) { 115 | buildConfigParams(queryParams); 116 | put(queryParams, WepayField.NONCE_STR, RandomStrs.generate(16)); 117 | buildSignParams(queryParams); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/core/Pays.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.core; 2 | 3 | import me.hao0.common.date.Dates; 4 | import me.hao0.common.security.MD5; 5 | import me.hao0.wepay.exception.WepayException; 6 | import me.hao0.wepay.model.enums.TradeType; 7 | import me.hao0.wepay.model.enums.WepayField; 8 | import me.hao0.wepay.model.pay.AppPayResponse; 9 | import me.hao0.wepay.model.pay.JsPayRequest; 10 | import me.hao0.wepay.model.pay.JsPayResponse; 11 | import me.hao0.wepay.model.pay.PayRequest; 12 | import me.hao0.wepay.model.pay.QrPayRequest; 13 | import me.hao0.wepay.model.pay.QrPayResponse; 14 | import me.hao0.wepay.util.RandomStrs; 15 | import java.io.UnsupportedEncodingException; 16 | import java.net.URLEncoder; 17 | import java.util.Map; 18 | import java.util.TreeMap; 19 | import static me.hao0.common.util.Preconditions.*; 20 | 21 | /** 22 | * 支付组件 23 | * Author: haolin 24 | * Email: haolin.h0@gmail.com 25 | * Date: 26/11/15 26 | * @since 1.0.0 27 | */ 28 | public final class Pays extends Component { 29 | 30 | /** 31 | * 统一下单接口 32 | */ 33 | private static final String PAY_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; 34 | 35 | /** 36 | * 联图二维码转换 37 | */ 38 | private static final String LIANTU_URL = "http://qr.liantu.com/api.php?text="; 39 | 40 | protected Pays(Wepay wepay) { 41 | super(wepay); 42 | } 43 | 44 | /** 45 | * JS支付(公众号支付) 46 | * @param request 支付请求对象 47 | * @return JsPayResponse对象,或抛WepayException 48 | */ 49 | public JsPayResponse jsPay(JsPayRequest request){ 50 | checkJsPayParams(request); 51 | Map respData = doJsPay(request, TradeType.JSAPI); 52 | return buildJsPayResp(respData); 53 | } 54 | 55 | /** 56 | * 动态二维码支付(NATIVE) 57 | * @param request 支付请求对象 58 | * @return 使用联图生成的二维码链接,或抛WepayException 59 | */ 60 | public QrPayResponse qrPay(QrPayRequest request){ 61 | return qrPay(request, Boolean.TRUE); 62 | } 63 | 64 | /** 65 | * 动态二维码支付(NATIVE) 66 | * @param request 支付请求对象 67 | * @param convert 是否转换为二维码图片链接(使用联图) 68 | * @return 可访问的二维码链接,或抛WepayException 69 | */ 70 | public QrPayResponse qrPay(QrPayRequest request, Boolean convert){ 71 | 72 | checkPayParams(request); 73 | 74 | Map respData = doQrPay(request, TradeType.NATIVE); 75 | 76 | String codeUrl = String.valueOf(respData.get(WepayField.CODE_URL)); 77 | if (convert){ 78 | try { 79 | codeUrl = LIANTU_URL + URLEncoder.encode(codeUrl, "UTF-8"); 80 | } catch (UnsupportedEncodingException e) { 81 | throw new WepayException(e); 82 | } 83 | } 84 | 85 | String prepayId = String.valueOf(respData.get(WepayField.PREPAY_ID)); 86 | 87 | QrPayResponse resp = new QrPayResponse(); 88 | resp.setCodeUrl(codeUrl); 89 | resp.setPrepayId(prepayId); 90 | 91 | return resp; 92 | } 93 | 94 | /** 95 | * app支付 96 | * @param request 支付请求对象 97 | * @return AppPayResponse对象,或抛WepayException 98 | */ 99 | public AppPayResponse appPay(PayRequest request){ 100 | checkPayParams(request); 101 | Map respData = doAppPay(request, TradeType.APP); 102 | return buildAppPayResp(respData); 103 | } 104 | 105 | /** 106 | * JS支付 107 | * @param request 支付信息 108 | * @return 支付结果 109 | */ 110 | private Map doJsPay(JsPayRequest request, TradeType tradeType){ 111 | Map payParams = buildPayParams(request, tradeType); 112 | payParams.put(WepayField.OPEN_ID, request.getOpenId()); 113 | return doPay(payParams); 114 | } 115 | 116 | /** 117 | * APP支付 118 | * @param request 支付信 119 | * @return 支付结 120 | */ 121 | private Map doAppPay(PayRequest request, TradeType tradeType){ 122 | Map payParams = buildPayParams(request, tradeType); 123 | return doPay(payParams); 124 | } 125 | 126 | /** 127 | * 二维码支付 128 | * @param request 支付信息 129 | * @return 支付结果 130 | */ 131 | private Map doQrPay(QrPayRequest request, TradeType tradeType){ 132 | Map payParams = buildPayParams(request, tradeType); 133 | putIfNotEmpty(payParams, WepayField.PRODUCT_ID, request.getProductId()); 134 | return doPay(payParams); 135 | } 136 | 137 | private Map doPay(Map payParams) { 138 | buildSignParams(payParams); 139 | return doPost(PAY_URL, payParams); 140 | } 141 | 142 | private JsPayResponse buildJsPayResp(Map data) { 143 | 144 | String appId = wepay.getAppId(); 145 | String nonceStr = RandomStrs.generate(16); 146 | String timeStamp = String.valueOf(Dates.now().getTime() / 1000); 147 | String pkg = WepayField.PREPAY_ID + "=" + 148 | data.get(WepayField.PREPAY_ID); 149 | 150 | String signing = 151 | WepayField.APPID + "=" + appId + 152 | "&"+ WepayField.NONCESTR2 +"=" + nonceStr + 153 | "&" + WepayField.PKG + "=" + pkg + 154 | "&" + WepayField.SIGN_TYPE + "=MD5" + 155 | "&" + WepayField.TIME_STAMP + "=" + timeStamp + 156 | "&" + WepayField.KEY + "=" + wepay.getAppKey(); 157 | 158 | String signed = MD5.generate(signing, false).toUpperCase(); 159 | 160 | return new JsPayResponse(appId, timeStamp, nonceStr, pkg, "MD5", signed); 161 | } 162 | 163 | private AppPayResponse buildAppPayResp(Map data) { 164 | String appId = wepay.getAppId(); 165 | String partnerId= wepay.getMchId(); 166 | String nonceStr = RandomStrs.generate(16); 167 | String timeStamp = String.valueOf(Dates.now().getTime() / 1000); 168 | String prepayId = String.valueOf(data.get(WepayField.PREPAY_ID)); 169 | 170 | String signing = 171 | WepayField.APP_ID + "=" + appId + 172 | "&"+ WepayField.NONCESTR +"=" + nonceStr + 173 | "&" + WepayField.PKG + "=Sign=WXPay" + 174 | "&" + WepayField.PARTNERID + "=" + partnerId + 175 | "&" + WepayField.PREPAYID + "=" + prepayId + 176 | "&" + WepayField.TIMESTAMP + "=" + timeStamp + 177 | "&" + WepayField.KEY + "=" + wepay.getAppKey(); 178 | 179 | String signed = MD5.generate(signing, false).toUpperCase(); 180 | 181 | return new AppPayResponse(appId, partnerId, prepayId, timeStamp, nonceStr, signed); 182 | } 183 | 184 | /** 185 | * 检查支付参数合法性 186 | * @param request 支付请求对象 187 | */ 188 | private void checkJsPayParams(JsPayRequest request) { 189 | checkPayParams(request); 190 | checkNotNullAndEmpty(request.getOpenId(), "openId"); 191 | } 192 | 193 | private void checkPayParams(PayRequest request) { 194 | checkNotNull(request, "pay detail can't be null"); 195 | checkNotNullAndEmpty(request.getBody(), "body"); 196 | checkNotNullAndEmpty(request.getOutTradeNo(), "outTradeNo"); 197 | Integer totalFee = request.getTotalFee(); 198 | checkArgument(totalFee != null && totalFee > 0, "totalFee must > 0"); 199 | checkNotNullAndEmpty(request.getClientIp(), "clientId"); 200 | checkNotNullAndEmpty(request.getNotifyUrl(), "notifyUrl"); 201 | checkNotNull(request.getFeeType(), "feeType can't be null"); 202 | checkNotNullAndEmpty(request.getTimeStart(), "timeStart"); 203 | } 204 | 205 | /** 206 | * 构建公共支付参数 207 | * @param request 支付请求对象 208 | * @param tradeType 交易类型 209 | * @return 支付MAP参数 210 | */ 211 | private Map buildPayParams(PayRequest request, TradeType tradeType) { 212 | Map payParams = new TreeMap<>(); 213 | 214 | // 配置参数 215 | buildConfigParams(payParams); 216 | 217 | // 业务必需参数 218 | put(payParams, WepayField.BODY, request.getBody()); 219 | put(payParams, WepayField.OUT_TRADE_NO, request.getOutTradeNo()); 220 | put(payParams, WepayField.TOTAL_FEE, request.getTotalFee() + ""); 221 | put(payParams, WepayField.SPBILL_CREATE_IP, request.getClientIp()); 222 | put(payParams, WepayField.NOTIFY_URL, request.getNotifyUrl()); 223 | put(payParams, WepayField.FEE_TYPE, request.getFeeType().type()); 224 | put(payParams, WepayField.NONCE_STR, RandomStrs.generate(16)); 225 | put(payParams, WepayField.TIME_START, request.getTimeStart()); 226 | put(payParams, WepayField.TRADE_TYPE, tradeType.type()); 227 | 228 | // 业务可选参数 229 | putIfNotEmpty(payParams, WepayField.DEVICE_INFO, request.getDeviceInfo()); 230 | putIfNotEmpty(payParams, WepayField.ATTACH, request.getAttach()); 231 | putIfNotEmpty(payParams, WepayField.DETAIL, request.getDetail()); 232 | putIfNotEmpty(payParams, WepayField.GOODS_TAG, request.getGoodsTag()); 233 | putIfNotEmpty(payParams, WepayField.TIME_EXPIRE, request.getTimeExpire()); 234 | putIfNotEmpty(payParams, WepayField.LIMIT_PAY, request.getLimitPay()); 235 | 236 | return payParams; 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/core/Refunds.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.core; 2 | 3 | import me.hao0.common.util.Strings; 4 | import me.hao0.wepay.model.enums.FeeType; 5 | import me.hao0.wepay.model.enums.RefundChannel; 6 | import me.hao0.wepay.model.enums.RefundStatus; 7 | import me.hao0.wepay.model.enums.WepayField; 8 | import me.hao0.wepay.model.refund.RefundApplyRequest; 9 | import me.hao0.wepay.model.refund.RefundApplyResponse; 10 | import me.hao0.wepay.model.common.Coupon; 11 | import me.hao0.wepay.model.refund.RefundItem; 12 | import me.hao0.wepay.model.refund.RefundQueryResponse; 13 | import me.hao0.wepay.util.RandomStrs; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | import java.util.Map; 18 | import java.util.TreeMap; 19 | 20 | import static me.hao0.common.util.Preconditions.*; 21 | 22 | /** 23 | * 退款组件 24 | * Author: haolin 25 | * Email: haolin.h0@gmail.com 26 | * Date: 27/11/15 27 | * @since 1.0.0 28 | */ 29 | public final class Refunds extends Component { 30 | 31 | /** 32 | * 申请退款 33 | */ 34 | private static final String APPLY = "https://api.mch.weixin.qq.com/secapi/pay/refund"; 35 | 36 | /** 37 | * 查询退款 38 | */ 39 | private static final String QUERY = "https://api.mch.weixin.qq.com/pay/refundquery"; 40 | 41 | Refunds(Wepay wepay) { 42 | super(wepay); 43 | } 44 | 45 | /** 46 | * 申请退款 47 | * @param request 退款请求对象 48 | * @return RefundResponse对象,或抛WepayException 49 | */ 50 | public RefundApplyResponse apply(RefundApplyRequest request){ 51 | checkApplyParams(request); 52 | Map applyParams = buildApplyParams(request); 53 | return doHttpsPost(APPLY, applyParams, RefundApplyResponse.class); 54 | } 55 | 56 | /** 57 | * 通过商户订单号查询退款 58 | * @param outTradeNo 商户订单号 59 | * @return 退款查询对象,或抛WepayException 60 | */ 61 | public RefundQueryResponse queryByOutTradeNo(String outTradeNo){ 62 | Map queryParams = buildQueryParams(WepayField.OUT_TRADE_NO, outTradeNo); 63 | Map respData = doPost(QUERY, queryParams); 64 | return renderQueryResp(respData); 65 | } 66 | 67 | /** 68 | * 通过商户退款单号查询退款 69 | * @param outRefundNo 商户退款单号 70 | * @return 退款查询对象,或抛WepayException 71 | */ 72 | public RefundQueryResponse queryByOutRefundNo(String outRefundNo){ 73 | Map queryParams = buildQueryParams(WepayField.OUT_REFUND_NO, outRefundNo); 74 | Map respData = doPost(QUERY, queryParams); 75 | return renderQueryResp(respData); 76 | } 77 | 78 | /** 79 | * 通过微信订单号查询退款 80 | * @param transactionId 微信订单号 81 | * @return 退款查询对象,或抛WepayException 82 | */ 83 | public RefundQueryResponse queryByTransactionId(String transactionId){ 84 | Map queryParams = buildQueryParams(WepayField.TRANSACTION_ID, transactionId); 85 | Map respData = doPost(QUERY, queryParams); 86 | return renderQueryResp(respData); 87 | } 88 | 89 | /** 90 | * 通过微信退款单号查询退款 91 | * @param refundId 微信退款单号 92 | * @return 退款查询对象,或抛WepayException 93 | */ 94 | public RefundQueryResponse queryByRefundId(String refundId){ 95 | Map queryParams = buildQueryParams(WepayField.REFUND_ID, refundId); 96 | Map respData = doPost(QUERY, queryParams); 97 | return renderQueryResp(respData); 98 | } 99 | 100 | private RefundQueryResponse renderQueryResp(Map refundData) { 101 | RefundQueryResponse queryResp = new RefundQueryResponse(); 102 | 103 | queryResp.setOutTradeNo((String)refundData.get(WepayField.OUT_TRADE_NO)); 104 | queryResp.setTransactionId((String)refundData.get(WepayField.TRANSACTION_ID)); 105 | queryResp.setTotalFee(Integer.parseInt((String)refundData.get(WepayField.TOTAL_FEE))); 106 | queryResp.setCashFee(Integer.parseInt((String) refundData.get(WepayField.CASH_FEE))); 107 | String feeType = (String)refundData.get(WepayField.FEE_TYPE); 108 | if (!Strings.isNullOrEmpty(feeType)){ 109 | queryResp.setFeeType(FeeType.from(feeType)); 110 | } 111 | 112 | Integer refundCount = Integer.parseInt((String) refundData.get(WepayField.REFUND_COUNT)); 113 | 114 | List refundItems = new ArrayList<>(); 115 | RefundItem refundItem; 116 | for (int refundIndex = 0; refundIndex < refundCount; refundIndex++){ 117 | refundItem = renderRefundItem(refundData, refundIndex); 118 | refundItems.add(refundItem); 119 | } 120 | queryResp.setItems(refundItems); 121 | 122 | return queryResp; 123 | } 124 | 125 | private RefundItem renderRefundItem(Map refundData, int refundItemIndex) { 126 | RefundItem refundItem = new RefundItem(); 127 | refundItem.setOutRefundNo((String)refundData.get(WepayField.OUT_REFUND_NO + "_" + refundItemIndex)); 128 | refundItem.setRefundId((String)refundData.get(WepayField.REFUND_ID + "_" + refundItemIndex)); 129 | refundItem.setChannel(RefundChannel.from((String) refundData.get(WepayField.REFUND_CHANNEL + "_" + refundItemIndex))); 130 | refundItem.setRefundFee(Integer.parseInt((String)refundData.get(WepayField.REFUND_FEE + "_" + refundItemIndex))); 131 | refundItem.setRefundStatus(RefundStatus.from((String)refundData.get(WepayField.REFUND_STATUS + "_" + refundItemIndex))); 132 | 133 | String settlementRefundFee = (String)refundData.get(WepayField.SETTLEMENT_REFUND_FEE + "_" + refundItemIndex); 134 | if (!Strings.isNullOrEmpty(settlementRefundFee)){ 135 | refundItem.setSettlementRefundFee(Integer.parseInt(settlementRefundFee)); 136 | } 137 | 138 | refundItem.setRefundRecvAccout((String)refundData.get(WepayField.REFUND_RECV_ACCOUNT + "_" + refundItemIndex)); 139 | 140 | Object couponRefundFee = refundData.get(WepayField.COUPON_REFUND_FEE + "_" + refundItemIndex); 141 | if (couponRefundFee != null){ 142 | refundItem.setCouponRefundFee(Integer.parseInt((String)couponRefundFee)); 143 | } 144 | Object couponRefundCountObj = refundData.get(WepayField.COUPON_REFUND_COUNT + "_" + refundItemIndex); 145 | if (couponRefundCountObj != null){ 146 | Integer couponRefundCount = Integer.parseInt((String)couponRefundCountObj); 147 | if (couponRefundCount > 0){ 148 | List couponItems = new ArrayList<>(); 149 | Coupon couponItem; 150 | for (int couponItemIndex = 0; couponItemIndex < couponRefundCount; couponItemIndex++){ 151 | couponItem = Coupon.newCoupon( 152 | (String) refundData.get(WepayField.COUPON_REFUND_BATCH_ID + "_" + refundItemIndex + "_" + couponItemIndex), 153 | (String) refundData.get(WepayField.COUPON_REFUND_ID + "_" + refundItemIndex + "_" + couponItemIndex), 154 | Integer.parseInt((String) refundData.get(WepayField.COUPON_REFUND_FEE + "_" + refundItemIndex + "_" + couponItemIndex)) 155 | ); 156 | couponItems.add(couponItem); 157 | } 158 | refundItem.setCoupons(couponItems); 159 | } 160 | } 161 | 162 | return refundItem; 163 | } 164 | 165 | /** 166 | * 构建查询退款参数 167 | * @param queryFieldName 查询字段名 168 | * @param queryFieldValue 查询字段值 169 | * @return 查询参数 170 | */ 171 | private Map buildQueryParams(String queryFieldName, String queryFieldValue) { 172 | checkNotNullAndEmpty(queryFieldValue, queryFieldName); 173 | 174 | Map queryParams = new TreeMap<>(); 175 | buildConfigParams(queryParams); 176 | queryParams.put(WepayField.NONCE_STR, RandomStrs.generate(16)); 177 | queryParams.put(queryFieldName, queryFieldValue); 178 | buildSignParams(queryParams); 179 | 180 | return queryParams; 181 | } 182 | 183 | /** 184 | * 校验退款参数 185 | * @param request 退款请求对象 186 | */ 187 | private void checkApplyParams(RefundApplyRequest request) { 188 | checkNotNull(wepay.certs, "merchant certs can't be null before apply refund"); 189 | checkNotNullAndEmpty(wepay.certPasswd, "certPasswd"); 190 | checkNotNull(request, "apply request can't be null"); 191 | if (Strings.isNullOrEmpty(request.getTransactionId())){ 192 | checkNotNullAndEmpty(request.getOutTradeNo(), "transactionId && outTradeNo"); 193 | } 194 | checkNotNullAndEmpty(request.getOutRefundNo(), "outRefundNo"); 195 | checkNotNullAndEmpty(request.getOpUserId(), "opUserId"); 196 | Integer totalFee = request.getTotalFee(); 197 | Integer refundFee = request.getRefundFee(); 198 | checkPositive(totalFee, "totalFee"); 199 | checkPositive(refundFee, "refundFee"); 200 | checkPositive(totalFee - refundFee, "totalFee - refundFee"); 201 | } 202 | 203 | /** 204 | * 构建退款参数 205 | * @param request 退款请求 206 | * @return 退款参数 207 | */ 208 | private Map buildApplyParams(RefundApplyRequest request) { 209 | Map refundParams = new TreeMap<>(); 210 | 211 | // 配置参数 212 | buildConfigParams(refundParams); 213 | 214 | // 业务参数 215 | putIfNotEmpty(refundParams, WepayField.TRANSACTION_ID, request.getTransactionId()); 216 | putIfNotEmpty(refundParams, WepayField.OUT_TRADE_NO, request.getOutTradeNo()); 217 | put(refundParams, WepayField.OUT_REFUND_NO, request.getOutRefundNo()); 218 | put(refundParams, WepayField.TOTAL_FEE, request.getTotalFee() + ""); 219 | put(refundParams, WepayField.REFUND_FEE, request.getRefundFee() + ""); 220 | put(refundParams, WepayField.NONCE_STR, RandomStrs.generate(16)); 221 | put(refundParams, WepayField.OP_USER_ID, request.getOpUserId()); 222 | putIfNotEmpty(refundParams, WepayField.DEVICE_INFO, request.getDeviceInfo()); 223 | if (request.getRefundFeeType() != null){ 224 | put(refundParams, WepayField.REFUND_FEE_TYPE, request.getRefundFeeType().type()); 225 | } 226 | 227 | // 签名参数 228 | buildSignParams(refundParams); 229 | 230 | return refundParams; 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/core/Wepay.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.core; 2 | 3 | import me.hao0.common.util.Strings; 4 | import me.hao0.wepay.exception.WepayException; 5 | import javax.net.ssl.KeyManagerFactory; 6 | import javax.net.ssl.SSLContext; 7 | import javax.net.ssl.SSLSocketFactory; 8 | import java.io.ByteArrayInputStream; 9 | import java.io.InputStream; 10 | import java.security.KeyStore; 11 | import java.security.SecureRandom; 12 | 13 | /** 14 | * 微信支付核心类 15 | * Author: haolin 16 | * Email: haolin.h0@gmail.com 17 | * Date: 25/11/15 18 | * @since 1.0.0 19 | */ 20 | public final class Wepay { 21 | 22 | /** 23 | * 微信APP ID 24 | */ 25 | private final String appId; 26 | 27 | /** 28 | * 微信APP Key 29 | */ 30 | private final String appKey; 31 | 32 | /** 33 | * 商户ID 34 | */ 35 | private final String mchId; 36 | 37 | /** 38 | * 商户证书数据(p12) 39 | */ 40 | byte[] certs; 41 | 42 | /** 43 | * 商户证书密码,默认为商户号mchId 44 | */ 45 | String certPasswd; 46 | 47 | /** 48 | * 解析微信XML时, 使用的编码类型 49 | */ 50 | String respEncode = "UTF-8"; 51 | 52 | private SSLSocketFactory sslSocketFactory; 53 | 54 | /** 55 | * 支付组件 56 | */ 57 | private Pays pays; 58 | 59 | /** 60 | * 订单组件 61 | */ 62 | private Orders orders; 63 | 64 | /** 65 | * 退款组件 66 | */ 67 | private Refunds refunds; 68 | 69 | /** 70 | * 通知组件 71 | */ 72 | private Notifies notifies; 73 | 74 | /** 75 | * 账单组件 76 | */ 77 | private Bills bills; 78 | 79 | Wepay(String appId, String appKey, String mchId){ 80 | this.appId = appId; 81 | this.appKey = appKey; 82 | this.mchId = mchId; 83 | } 84 | 85 | public String getAppId() { 86 | return appId; 87 | } 88 | 89 | public String getAppKey() { 90 | return appKey; 91 | } 92 | 93 | public String getMchId() { 94 | return mchId; 95 | } 96 | 97 | public byte[] getCerts() { 98 | return certs; 99 | } 100 | 101 | public String getCertPasswd() { 102 | return certPasswd; 103 | } 104 | 105 | public String getRespEncode() { 106 | return respEncode; 107 | } 108 | 109 | public SSLSocketFactory getSslSocketFactory() { 110 | return sslSocketFactory; 111 | } 112 | 113 | /** 114 | * 初始化操作 115 | * @return this 116 | */ 117 | public Wepay init(){ 118 | pays = new Pays(this); 119 | orders = new Orders(this); 120 | refunds = new Refunds(this); 121 | notifies = new Notifies(this); 122 | bills = new Bills(this); 123 | if (certs != null && !Strings.isNullOrEmpty(certPasswd)){ 124 | sslSocketFactory = initSSLSocketFactory(); 125 | } 126 | return this; 127 | } 128 | 129 | private SSLSocketFactory initSSLSocketFactory() { 130 | try (InputStream certsInput = new ByteArrayInputStream(certs)){ 131 | 132 | KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); 133 | 134 | KeyStore keyStore = KeyStore.getInstance("PKCS12"); 135 | keyStore.load(certsInput, certPasswd.toCharArray()); 136 | certsInput.close(); 137 | 138 | keyManagerFactory.init(keyStore, certPasswd.toCharArray()); 139 | SSLContext context = SSLContext.getInstance("TLS"); 140 | context.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom()); 141 | 142 | return context.getSocketFactory(); 143 | 144 | } catch (Exception e) { 145 | throw new WepayException(e); 146 | } 147 | } 148 | 149 | /** 150 | * 调用支付组件 151 | * @return 支付组件 152 | */ 153 | public Pays pay(){ 154 | return pays; 155 | } 156 | 157 | /** 158 | * 调用订单组件 159 | * @return 订单组件 160 | */ 161 | public Orders order(){ 162 | return orders; 163 | } 164 | 165 | /** 166 | * 调用退款组件 167 | * @return 退款组件 168 | */ 169 | public Refunds refund(){ 170 | return refunds; 171 | } 172 | 173 | /** 174 | * 调用通知组件 175 | * @return 通知组件 176 | */ 177 | public Notifies notifies(){ 178 | return notifies; 179 | } 180 | 181 | /** 182 | * 调用账单组件 183 | * @return 账单组件 184 | */ 185 | public Bills bill(){ 186 | return bills; 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/core/WepayBuilder.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.core; 2 | 3 | /** 4 | * Wepay构建器 5 | * Author: haolin 6 | * Email: haolin.h0@gmail.com 7 | * Date: 25/11/15 8 | * @since 1.0.0 9 | */ 10 | public final class WepayBuilder { 11 | 12 | private Wepay wepay; 13 | 14 | private WepayBuilder(){} 15 | 16 | public static WepayBuilder newBuilder(String appId, String appSecret, String mchId){ 17 | WepayBuilder builder = new WepayBuilder(); 18 | builder.wepay = new Wepay(appId, appSecret, mchId); 19 | return builder; 20 | } 21 | 22 | /** 23 | * 设置证书密码 24 | * @param certPasswd 证书密码 25 | * @return this 26 | */ 27 | public WepayBuilder certPasswd(String certPasswd){ 28 | wepay.certPasswd = certPasswd; 29 | return this; 30 | } 31 | 32 | /** 33 | * 设置证书数据(p12文件) 34 | * @param certs 二进制数据 35 | * @return this 36 | */ 37 | public WepayBuilder certs(byte[] certs){ 38 | wepay.certs = certs; 39 | return this; 40 | } 41 | 42 | /** 43 | * 设置解析微信XML时, 使用的编码类型 44 | * @param encode 编码类型 45 | * @return this 46 | */ 47 | public WepayBuilder respEncode(String encode){ 48 | wepay.respEncode = encode; 49 | return this; 50 | } 51 | 52 | public Wepay build(){ 53 | return wepay.init(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/exception/SignException.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.exception; 2 | 3 | /** 4 | * 签名异常 5 | * Author: haolin 6 | * Email: haolin.h0@gmail.com 7 | */ 8 | public class SignException extends RuntimeException { 9 | 10 | public SignException() { 11 | super(); 12 | } 13 | 14 | public SignException(String message) { 15 | super(message); 16 | } 17 | 18 | public SignException(String message, Throwable cause) { 19 | super(message, cause); 20 | } 21 | 22 | public SignException(Throwable cause) { 23 | super(cause); 24 | } 25 | 26 | protected SignException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 27 | super(message, cause, enableSuppression, writableStackTrace); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/exception/WepayException.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.exception; 2 | 3 | /** 4 | * 微信支付异常 5 | * Author: haolin 6 | * Email: haolin.h0@gmail.com 7 | * Date: 26/11/15 8 | * @since 1.0.0 9 | */ 10 | public class WepayException extends RuntimeException { 11 | 12 | /** 13 | * 当微信发生错误时,对应的错误码 14 | */ 15 | private String errorCode; 16 | 17 | /** 18 | * 当微信发生错误时,对应的错误消息 19 | */ 20 | private String errorMsg; 21 | 22 | public WepayException(Throwable cause) { 23 | super(cause); 24 | } 25 | 26 | public WepayException(String errorCode, String errorMsg){ 27 | super("[" + errorCode + "]"+ errorMsg); 28 | this.errorCode = errorCode; 29 | this.errorMsg = errorMsg; 30 | } 31 | 32 | public String getErrorCode() { 33 | return errorCode; 34 | } 35 | 36 | public String getErrorMsg() { 37 | return errorMsg; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/bill/Bill.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.bill; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 5 | import me.hao0.wepay.model.enums.FeeType; 6 | import me.hao0.wepay.model.enums.TradeType; 7 | import me.hao0.wepay.model.enums.WepayField; 8 | import me.hao0.wepay.serializer.FeeTypeDeserializer; 9 | import me.hao0.wepay.serializer.TradeTypeDeserializer; 10 | import java.io.Serializable; 11 | 12 | /** 13 | * 交易成功账单 14 | * Author: haolin 15 | * Email: haolin.h0@gmail.com 16 | * Date: 4/12/15 17 | * @since 1.1.0 18 | */ 19 | public class Bill implements Serializable { 20 | 21 | private static final long serialVersionUID = 2385619717854141289L; 22 | 23 | /** 24 | * 交易时间 25 | */ 26 | @JsonProperty(WepayField.TRADE_TIME) 27 | private String tradeTime; 28 | 29 | /** 30 | * 公众帐号ID 31 | */ 32 | @JsonProperty(WepayField.APP_ID) 33 | private String appId; 34 | 35 | /** 36 | * 商户ID 37 | */ 38 | @JsonProperty(WepayField.MCH_ID) 39 | private String mchId; 40 | 41 | /** 42 | * 子商户ID 43 | */ 44 | @JsonProperty(WepayField.SUB_MCH_ID) 45 | private String subMchId; 46 | 47 | /** 48 | * 设备号 49 | */ 50 | @JsonProperty(WepayField.DEVICE_INFO) 51 | private String deviceInfo; 52 | 53 | /** 54 | * 微信订单号 55 | */ 56 | @JsonProperty(WepayField.TRANSACTION_ID) 57 | private String transactionId; 58 | 59 | /** 60 | * 商户订单号 61 | */ 62 | @JsonProperty(WepayField.OUT_TRADE_NO) 63 | private String outTradeNo; 64 | 65 | /** 66 | * 用户标识 67 | */ 68 | @JsonProperty(WepayField.OPEN_ID) 69 | private String openId; 70 | 71 | /** 72 | * 交易类型 73 | */ 74 | @JsonProperty(WepayField.TRADE_TYPE) 75 | @JsonDeserialize(using = TradeTypeDeserializer.class) 76 | private TradeType tradeType; 77 | 78 | /** 79 | * 交易状态 80 | */ 81 | @JsonProperty(WepayField.TRADE_STATE) 82 | private String tradeState; 83 | 84 | /** 85 | * 付款银行 86 | */ 87 | @JsonProperty(WepayField.BANK_TYPE) 88 | private String bankType; 89 | 90 | /** 91 | * 货币类型 92 | */ 93 | @JsonProperty(WepayField.FEE_TYPE) 94 | @JsonDeserialize(using = FeeTypeDeserializer.class) 95 | private FeeType feeType; 96 | 97 | /** 98 | * 总金额 99 | */ 100 | @JsonProperty(WepayField.TOTAL_FEE) 101 | private Float totalFee; 102 | 103 | /** 104 | * 企业红包金额 105 | */ 106 | @JsonProperty(WepayField.ENTER_RED_PKG_FEE) 107 | private Float enterRedPkgFee; 108 | 109 | /** 110 | * 商品名称 111 | */ 112 | private String body; 113 | 114 | /** 115 | * 商户数据包 116 | */ 117 | @JsonProperty(WepayField.DATA_PKG) 118 | private String dataPkg; 119 | 120 | /** 121 | * 手续费 122 | */ 123 | @JsonProperty(WepayField.COMMISSION_FEE) 124 | private String commissionFee; 125 | 126 | /** 127 | * 费率 128 | */ 129 | @JsonProperty(WepayField.FEE_RATE) 130 | private String feeRate; 131 | 132 | public String getTradeTime() { 133 | return tradeTime; 134 | } 135 | 136 | public void setTradeTime(String tradeTime) { 137 | this.tradeTime = tradeTime; 138 | } 139 | 140 | public String getAppId() { 141 | return appId; 142 | } 143 | 144 | public void setAppId(String appId) { 145 | this.appId = appId; 146 | } 147 | 148 | public String getMchId() { 149 | return mchId; 150 | } 151 | 152 | public void setMchId(String mchId) { 153 | this.mchId = mchId; 154 | } 155 | 156 | public String getSubMchId() { 157 | return subMchId; 158 | } 159 | 160 | public void setSubMchId(String subMchId) { 161 | this.subMchId = subMchId; 162 | } 163 | 164 | public String getDeviceInfo() { 165 | return deviceInfo; 166 | } 167 | 168 | public void setDeviceInfo(String deviceInfo) { 169 | this.deviceInfo = deviceInfo; 170 | } 171 | 172 | public String getTransactionId() { 173 | return transactionId; 174 | } 175 | 176 | public void setTransactionId(String transactionId) { 177 | this.transactionId = transactionId; 178 | } 179 | 180 | public String getOutTradeNo() { 181 | return outTradeNo; 182 | } 183 | 184 | public void setOutTradeNo(String outTradeNo) { 185 | this.outTradeNo = outTradeNo; 186 | } 187 | 188 | public String getOpenId() { 189 | return openId; 190 | } 191 | 192 | public void setOpenId(String openId) { 193 | this.openId = openId; 194 | } 195 | 196 | public TradeType getTradeType() { 197 | return tradeType; 198 | } 199 | 200 | public void setTradeType(TradeType tradeType) { 201 | this.tradeType = tradeType; 202 | } 203 | 204 | public String getTradeState() { 205 | return tradeState; 206 | } 207 | 208 | public void setTradeState(String tradeState) { 209 | this.tradeState = tradeState; 210 | } 211 | 212 | public String getBankType() { 213 | return bankType; 214 | } 215 | 216 | public void setBankType(String bankType) { 217 | this.bankType = bankType; 218 | } 219 | 220 | public FeeType getFeeType() { 221 | return feeType; 222 | } 223 | 224 | public void setFeeType(FeeType feeType) { 225 | this.feeType = feeType; 226 | } 227 | 228 | public String getBody() { 229 | return body; 230 | } 231 | 232 | public void setBody(String body) { 233 | this.body = body; 234 | } 235 | 236 | public String getDataPkg() { 237 | return dataPkg; 238 | } 239 | 240 | public void setDataPkg(String dataPkg) { 241 | this.dataPkg = dataPkg; 242 | } 243 | 244 | public String getFeeRate() { 245 | return feeRate; 246 | } 247 | 248 | public void setFeeRate(String feeRate) { 249 | this.feeRate = feeRate; 250 | } 251 | 252 | public Float getTotalFee() { 253 | return totalFee; 254 | } 255 | 256 | public void setTotalFee(Float totalFee) { 257 | this.totalFee = totalFee; 258 | } 259 | 260 | public Float getEnterRedPkgFee() { 261 | return enterRedPkgFee; 262 | } 263 | 264 | public void setEnterRedPkgFee(Float enterRedPkgFee) { 265 | this.enterRedPkgFee = enterRedPkgFee; 266 | } 267 | 268 | public String getCommissionFee() { 269 | return commissionFee; 270 | } 271 | 272 | public void setCommissionFee(String commissionFee) { 273 | this.commissionFee = commissionFee; 274 | } 275 | 276 | @Override 277 | public String toString() { 278 | return "Bill{" + 279 | "tradeTime='" + tradeTime + '\'' + 280 | ", appId='" + appId + '\'' + 281 | ", mchId='" + mchId + '\'' + 282 | ", subMchId='" + subMchId + '\'' + 283 | ", deviceInfo='" + deviceInfo + '\'' + 284 | ", transactionId='" + transactionId + '\'' + 285 | ", outTradeNo='" + outTradeNo + '\'' + 286 | ", openId='" + openId + '\'' + 287 | ", tradeType=" + tradeType + 288 | ", tradeState='" + tradeState + '\'' + 289 | ", bankType='" + bankType + '\'' + 290 | ", feeType=" + feeType + 291 | ", totalFee=" + totalFee + 292 | ", enterRedPkgFee=" + enterRedPkgFee + 293 | ", body='" + body + '\'' + 294 | ", dataPkg='" + dataPkg + '\'' + 295 | ", commissionFee=" + commissionFee + 296 | ", feeRate='" + feeRate + '\'' + 297 | '}'; 298 | } 299 | } 300 | 301 | 302 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/bill/BillCount.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.bill; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import me.hao0.wepay.model.enums.WepayField; 5 | import java.io.Serializable; 6 | 7 | /** 8 | * 账单统计 9 | * Author: haolin 10 | * Email: haolin.h0@gmail.com 11 | * Date: 4/12/15 12 | * @since 1.1.0 13 | */ 14 | public class BillCount implements Serializable { 15 | 16 | private static final long serialVersionUID = 2827339255910413151L; 17 | 18 | /** 19 | * 总交易笔数 20 | */ 21 | @JsonProperty(WepayField.TRADE_TOTAL_COUNT) 22 | private Integer tradeTotalCount; 23 | 24 | /** 25 | * 总交易额(元) 26 | */ 27 | @JsonProperty(WepayField.TRADE_TOTAL_FEE) 28 | private Float tradeTotalFee; 29 | 30 | /** 31 | * 总退款金额(元) 32 | */ 33 | @JsonProperty(WepayField.REFUND_TOTAL_FEE) 34 | private Float refundTotalFee; 35 | 36 | /** 37 | * 总代金券或立减优惠退款金额 38 | */ 39 | @JsonProperty(WepayField.COUPON_REFUND_TOTAL_FEE) 40 | private Float couponRefundTotalFee; 41 | 42 | /** 43 | * 手续费总金额 44 | */ 45 | @JsonProperty(WepayField.COMMISSION_TOTAL_FEE) 46 | private String commissionTotalFee; 47 | 48 | public Integer getTradeTotalCount() { 49 | return tradeTotalCount; 50 | } 51 | 52 | public void setTradeTotalCount(Integer tradeTotalCount) { 53 | this.tradeTotalCount = tradeTotalCount; 54 | } 55 | 56 | public Float getTradeTotalFee() { 57 | return tradeTotalFee; 58 | } 59 | 60 | public void setTradeTotalFee(Float tradeTotalFee) { 61 | this.tradeTotalFee = tradeTotalFee; 62 | } 63 | 64 | public Float getRefundTotalFee() { 65 | return refundTotalFee; 66 | } 67 | 68 | public void setRefundTotalFee(Float refundTotalFee) { 69 | this.refundTotalFee = refundTotalFee; 70 | } 71 | 72 | public Float getCouponRefundTotalFee() { 73 | return couponRefundTotalFee; 74 | } 75 | 76 | public void setCouponRefundTotalFee(Float couponRefundTotalFee) { 77 | this.couponRefundTotalFee = couponRefundTotalFee; 78 | } 79 | 80 | public String getCommissionTotalFee() { 81 | return commissionTotalFee; 82 | } 83 | 84 | public void setCommissionTotalFee(String commissionTotalFee) { 85 | this.commissionTotalFee = commissionTotalFee; 86 | } 87 | 88 | @Override 89 | public String toString() { 90 | return "BillCount{" + 91 | "tradeTotalCount=" + tradeTotalCount + 92 | ", tradeTotalFee=" + tradeTotalFee + 93 | ", refundTotalFee=" + refundTotalFee + 94 | ", couponRefundTotalFee=" + couponRefundTotalFee + 95 | ", commissionTotalFee=" + commissionTotalFee + 96 | '}'; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/bill/BillDetail.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.bill; 2 | 3 | import java.io.Serializable; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | /** 8 | * 账单信息 9 | * Author: haolin 10 | * Email: haolin.h0@gmail.com 11 | * Date: 4/12/15 12 | * @since 1.1.0 13 | */ 14 | public class BillDetail implements Serializable { 15 | 16 | private static final long serialVersionUID = 2763506940046963037L; 17 | 18 | /** 19 | * 账单记录 20 | */ 21 | private List bills; 22 | 23 | /** 24 | * 账单统计 25 | */ 26 | private BillCount count; 27 | 28 | @SuppressWarnings("unchecked") 29 | private static final BillDetail EMPTY = (BillDetail)new BillDetail(Collections.emptyList(), null); 30 | 31 | public BillDetail(List bills, BillCount count) { 32 | this.bills = bills; 33 | this.count = count; 34 | } 35 | 36 | public List getBills() { 37 | return bills; 38 | } 39 | 40 | public void setBills(List bills) { 41 | this.bills = bills; 42 | } 43 | 44 | public BillCount getCount() { 45 | return count; 46 | } 47 | 48 | public void setCount(BillCount count) { 49 | this.count = count; 50 | } 51 | 52 | @SuppressWarnings("unchecked") 53 | public static BillDetail empty(){ 54 | return EMPTY; 55 | } 56 | 57 | @Override 58 | public String toString() { 59 | return "BillDetail{" + 60 | "bills=" + bills + 61 | ", count=" + count + 62 | '}'; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/bill/BillFields.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.bill; 2 | 3 | import me.hao0.wepay.model.enums.WepayField; 4 | import java.util.ArrayList; 5 | import java.util.Arrays; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | /** 11 | * 账单字段 12 | * Author: haolin 13 | * Email: haolin.h0@gmail.com 14 | * Date: 4/12/15 15 | * @since 1.1.0 16 | */ 17 | public final class BillFields { 18 | 19 | /** 20 | * 账单公用起始字段 21 | */ 22 | private static final List START_FIELDS = Arrays.asList( 23 | WepayField.TRADE_TIME, WepayField.APP_ID, WepayField.MCH_ID, WepayField.SUB_MCH_ID, 24 | WepayField.DEVICE_INFO, WepayField.TRANSACTION_ID, WepayField.OUT_TRADE_NO, WepayField.OPEN_ID, 25 | WepayField.TRADE_TYPE, WepayField.TRADE_STATE, WepayField.BANK_TYPE, WepayField.FEE_TYPE, 26 | WepayField.TOTAL_FEE, WepayField.ENTER_RED_PKG_FEE 27 | ); 28 | 29 | private static final List END_FIELDS = Arrays.asList( 30 | WepayField.BODY, WepayField.DATA_PKG, WepayField.COMMISSION_FEE, WepayField.FEE_RATE 31 | ); 32 | 33 | /** 34 | * 所有订单账单的字段集,顺序与微信返回数据保持一致 35 | */ 36 | public static final List ALL = initAllFields(); 37 | 38 | /** 39 | * 退款账单的字段集合,顺序与微信返回数据保持一致 40 | */ 41 | public static final List REFUND = initRefundFields(); 42 | 43 | /** 44 | * 成功账单的字段集合,顺序与微信返回数据保持一致 45 | */ 46 | public static final List SUCCESS = initSuccessFields(); 47 | 48 | /** 49 | * 账单统计的字段集合,顺序与微信返回数据保持一致 50 | */ 51 | public static final List COUNT = Arrays.asList( 52 | WepayField.TRADE_TOTAL_COUNT, WepayField.TRADE_TOTAL_FEE, 53 | WepayField.REFUND_TOTAL_FEE, WepayField.COUPON_REFUND_TOTAL_FEE, 54 | WepayField.COMMISSION_TOTAL_FEE 55 | ); 56 | 57 | 58 | private BillFields(){} 59 | 60 | private static List initAllFields() { 61 | List all = new ArrayList<>(); 62 | startFields(all); 63 | initCommonRefundFields(all); 64 | endFields(all); 65 | return all; 66 | } 67 | 68 | private static List initRefundFields() { 69 | List refund = new ArrayList<>(); 70 | startFields(refund); 71 | refund.add(WepayField.REFUND_APPLY_TIME); 72 | refund.add(WepayField.REFUND_SUCCESS_TIME); 73 | initCommonRefundFields(refund); 74 | endFields(refund); 75 | return refund; 76 | } 77 | 78 | private static void initCommonRefundFields(List fields){ 79 | fields.add(WepayField.REFUND_ID); 80 | fields.add(WepayField.OUT_REFUND_NO); 81 | fields.add(WepayField.REFUND_FEE); 82 | fields.add(WepayField.ENTER_RED_PKG_REFUND_FEE); 83 | fields.add(WepayField.REFUND_CHANNEL); 84 | fields.add(WepayField.REFUND_STATUS); 85 | } 86 | 87 | private static List initSuccessFields() { 88 | List success = new ArrayList<>(); 89 | startFields(success); 90 | endFields(success); 91 | return success; 92 | } 93 | 94 | private static void startFields(List fields) { 95 | for (String f : START_FIELDS){ 96 | fields.add(f); 97 | } 98 | } 99 | 100 | private static void endFields(List fields) { 101 | for (String f : END_FIELDS){ 102 | fields.add(f); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/bill/CommonBill.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.bill; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 5 | import me.hao0.wepay.model.enums.RefundChannel; 6 | import me.hao0.wepay.model.enums.WepayField; 7 | import me.hao0.wepay.serializer.RefundChannelDeserializer; 8 | 9 | /** 10 | * 普通账单 11 | * Author: haolin 12 | * Email: haolin.h0@gmail.com 13 | * Date: 4/12/15 14 | * @since 1.1.0 15 | */ 16 | public class CommonBill extends Bill { 17 | 18 | private static final long serialVersionUID = -4518299029269484159L; 19 | 20 | /** 21 | * 微信退款单号 22 | */ 23 | @JsonProperty(WepayField.REFUND_ID) 24 | private String refundId; 25 | 26 | /** 27 | * 商户退款单号 28 | */ 29 | @JsonProperty(WepayField.OUT_REFUND_NO) 30 | private String outRefundNo; 31 | 32 | /** 33 | * 退款金额(元) 34 | */ 35 | @JsonProperty(WepayField.REFUND_FEE) 36 | private Float refundFee; 37 | 38 | /** 39 | * 企业红包退款金额(元) 40 | */ 41 | @JsonProperty(WepayField.ENTER_RED_PKG_REFUND_FEE) 42 | private Float enterRedPkgRefundFee; 43 | 44 | /** 45 | * 退款类型 46 | */ 47 | @JsonProperty(WepayField.REFUND_CHANNEL) 48 | @JsonDeserialize(using = RefundChannelDeserializer.class) 49 | private RefundChannel channel; 50 | 51 | /** 52 | * 退款状态 53 | */ 54 | @JsonProperty(WepayField.REFUND_STATUS) 55 | private String refundStatus; 56 | 57 | public String getRefundId() { 58 | return refundId; 59 | } 60 | 61 | public void setRefundId(String refundId) { 62 | this.refundId = refundId; 63 | } 64 | 65 | public String getOutRefundNo() { 66 | return outRefundNo; 67 | } 68 | 69 | public void setOutRefundNo(String outRefundNo) { 70 | this.outRefundNo = outRefundNo; 71 | } 72 | 73 | public Float getRefundFee() { 74 | return refundFee; 75 | } 76 | 77 | public void setRefundFee(Float refundFee) { 78 | this.refundFee = refundFee; 79 | } 80 | 81 | public Float getEnterRedPkgRefundFee() { 82 | return enterRedPkgRefundFee; 83 | } 84 | 85 | public void setEnterRedPkgRefundFee(Float enterRedPkgRefundFee) { 86 | this.enterRedPkgRefundFee = enterRedPkgRefundFee; 87 | } 88 | 89 | public RefundChannel getChannel() { 90 | return channel; 91 | } 92 | 93 | public void setChannel(RefundChannel channel) { 94 | this.channel = channel; 95 | } 96 | 97 | public String getRefundStatus() { 98 | return refundStatus; 99 | } 100 | 101 | public void setRefundStatus(String refundStatus) { 102 | this.refundStatus = refundStatus; 103 | } 104 | 105 | @Override 106 | public String toString() { 107 | return "CommonBill{" + 108 | "refundId='" + refundId + '\'' + 109 | ", outRefundNo='" + outRefundNo + '\'' + 110 | ", refundFee=" + refundFee + 111 | ", enterRedPkgRefundFee=" + enterRedPkgRefundFee + 112 | ", channel=" + channel + 113 | ", refundStatus='" + refundStatus + '\'' + 114 | "} " + super.toString(); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/bill/RefundBill.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.bill; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import me.hao0.wepay.model.enums.WepayField; 5 | 6 | /** 7 | * 退款账单 8 | * Author: haolin 9 | * Email: haolin.h0@gmail.com 10 | * Date: 4/12/15 11 | */ 12 | public class RefundBill extends CommonBill { 13 | 14 | private static final long serialVersionUID = -6051679079124227683L; 15 | 16 | /** 17 | * 退款申请时间 18 | */ 19 | @JsonProperty(WepayField.REFUND_APPLY_TIME) 20 | private String refundApplyTime; 21 | 22 | /** 23 | * 退款成功时间 24 | */ 25 | @JsonProperty(WepayField.REFUND_SUCCESS_TIME) 26 | private String refundSuccessTime; 27 | 28 | public String getRefundApplyTime() { 29 | return refundApplyTime; 30 | } 31 | 32 | public void setRefundApplyTime(String refundApplyTime) { 33 | this.refundApplyTime = refundApplyTime; 34 | } 35 | 36 | public String getRefundSuccessTime() { 37 | return refundSuccessTime; 38 | } 39 | 40 | public void setRefundSuccessTime(String refundSuccessTime) { 41 | this.refundSuccessTime = refundSuccessTime; 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "RefundBill{" + 47 | "refundApplyTime='" + refundApplyTime + '\'' + 48 | ", refundSuccessTime='" + refundSuccessTime + '\'' + 49 | "} " + super.toString(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/common/Coupon.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.common; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 代金券 7 | * Author: haolin 8 | * Email: haolin.h0@gmail.com 9 | * Date: 1/12/15 10 | * @since 1.0.0 11 | */ 12 | public class Coupon implements Serializable { 13 | 14 | private static final long serialVersionUID = -2006707624918389486L; 15 | 16 | /** 17 | * 代金券或立减优惠批次ID 18 | */ 19 | private String batchId; 20 | 21 | /** 22 | * 代金券或立减优惠ID 23 | */ 24 | private String id; 25 | 26 | /** 27 | * 单个代金券或立减优惠支付金额 28 | */ 29 | private Integer fee; 30 | 31 | private Coupon(){} 32 | 33 | public String getBatchId() { 34 | return batchId; 35 | } 36 | 37 | public void setBatchId(String batchId) { 38 | this.batchId = batchId; 39 | } 40 | 41 | public String getId() { 42 | return id; 43 | } 44 | 45 | public void setId(String id) { 46 | this.id = id; 47 | } 48 | 49 | public Integer getFee() { 50 | return fee; 51 | } 52 | 53 | public void setFee(Integer fee) { 54 | this.fee = fee; 55 | } 56 | 57 | public static Coupon newCoupon(String batchId, String id, Integer fee){ 58 | Coupon c = new Coupon(); 59 | c.batchId = batchId; 60 | c.id = id; 61 | c.fee = fee; 62 | return c; 63 | } 64 | 65 | @Override 66 | public String toString() { 67 | return "RefundCouponItem{" + 68 | "batchId='" + batchId + '\'' + 69 | ", id='" + id + '\'' + 70 | ", fee=" + fee + 71 | '}'; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/enums/BillType.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.enums; 2 | 3 | /** 4 | * 账单类型 5 | * Author: haolin 6 | * Email: haolin.h0@gmail.com 7 | * Date: 4/12/15 8 | * @since 1.1.0 9 | */ 10 | public enum BillType { 11 | 12 | /** 13 | * 所有订单信息 14 | */ 15 | ALL("ALL"), 16 | 17 | /** 18 | * 成功支付的订单 19 | */ 20 | SUCCESS("SUCCESS"), 21 | 22 | /** 23 | * 退款订单 24 | */ 25 | REFUND("REFUND"); 26 | 27 | private String type; 28 | 29 | private BillType(String type){ 30 | this.type = type; 31 | } 32 | 33 | public String type(){ 34 | return type; 35 | } 36 | 37 | public static BillType from(String t){ 38 | for (BillType bt : BillType.values()){ 39 | if (bt.type().equals(t)){ 40 | return bt; 41 | } 42 | } 43 | throw new IllegalArgumentException("unknown bill type: " + t); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/enums/FeeType.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.enums; 2 | 3 | /** 4 | * 货币类型 5 | * Author: haolin 6 | * Email: haolin.h0@gmail.com 7 | * Date: 26/11/15 8 | * @since 1.0.0 9 | */ 10 | public enum FeeType { 11 | 12 | /** 13 | * 人民币 14 | */ 15 | CNY("CNY"); 16 | 17 | private String type; 18 | 19 | private FeeType(String type){ 20 | this.type = type; 21 | } 22 | 23 | public String type(){ 24 | return type; 25 | } 26 | 27 | public static FeeType from(String t){ 28 | for (FeeType ft : FeeType.values()){ 29 | if (ft.type().equals(t)){ 30 | return ft; 31 | } 32 | } 33 | throw new IllegalArgumentException("unknown fee type: " + t); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/enums/RefundChannel.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.enums; 2 | 3 | /** 4 | * 退款渠道 5 | * Author: haolin 6 | * Email: haolin.h0@gmail.com 7 | * Date: 26/11/15 8 | * @since 1.0.0 9 | */ 10 | public enum RefundChannel { 11 | 12 | /** 13 | * 原路退款 14 | */ 15 | ORIGINAL("ORIGINAL"), 16 | 17 | /** 18 | * 退款到余额 19 | */ 20 | BALANCE("BALANCE"); 21 | 22 | private String type; 23 | 24 | private RefundChannel(String type){ 25 | this.type = type; 26 | } 27 | 28 | public String type(){ 29 | return type; 30 | } 31 | 32 | public static RefundChannel from(String t){ 33 | for (RefundChannel rc : RefundChannel.values()){ 34 | if (rc.type().equals(t)){ 35 | return rc; 36 | } 37 | } 38 | throw new IllegalArgumentException("unknown apply channel: " + t); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/enums/RefundStatus.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.enums; 2 | 3 | /** 4 | * @author haolin 5 | * @date 6/11/16 6 | * @mailto haolin.h0@gmail.com 7 | */ 8 | public enum RefundStatus { 9 | 10 | /** 11 | * 退款成功 12 | */ 13 | SUCCESS("SUCCESS"), 14 | 15 | /** 16 | * 退款失败 17 | */ 18 | FAIL("FAIL"), 19 | 20 | /** 21 | * 退款处理中 22 | */ 23 | PROCESSING("PROCESSING"), 24 | 25 | /** 26 | * 未确定,需要商户原退款单号重新发起 27 | */ 28 | NOTSURE("NOTSURE"), 29 | 30 | /** 31 | * 转入代发,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,资金回流到商户的现金帐号,需要商户人工干预,通过线下或者财付通转账的方式进行退款。 32 | */ 33 | CHANGE("CHANGE"); 34 | 35 | private String value; 36 | 37 | RefundStatus(String value){ 38 | this.value = value; 39 | } 40 | 41 | public String value(){ 42 | return value; 43 | } 44 | 45 | public static RefundStatus from(String s){ 46 | for (RefundStatus rs : RefundStatus.values()){ 47 | if (rs.value().equals(s)){ 48 | return rs; 49 | } 50 | } 51 | throw new IllegalArgumentException("unknown refund status: " + s); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/enums/TradeState.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.enums; 2 | 3 | /** 4 | * 交易状态 5 | * Author: haolin 6 | * Email: haolin.h0@gmail.com 7 | * Date: 26/11/15 8 | * @since 1.0.0 9 | */ 10 | public enum TradeState { 11 | 12 | /** 13 | * 支付成功 14 | */ 15 | SUCCESS("SUCCESS"), 16 | 17 | /** 18 | * 转入退款 19 | */ 20 | REFUND("REFUND"), 21 | 22 | /** 23 | * 未支付 24 | */ 25 | NOTPAY("NOTPAY"), 26 | 27 | /** 28 | * 已关闭 29 | */ 30 | CLOSED("CLOSED"), 31 | 32 | /** 33 | * 已撤销(刷卡支付) 34 | */ 35 | REVOKED("REVOKED"), 36 | 37 | /** 38 | * 已撤销(刷卡支付) 39 | */ 40 | USERPAYING("USERPAYING"), 41 | 42 | /** 43 | * 支付失败(其他原因,如银行返回失败) 44 | */ 45 | PAYERROR("PAYERROR"); 46 | 47 | private String type; 48 | 49 | private TradeState(String type){ 50 | this.type = type; 51 | } 52 | 53 | public String type(){ 54 | return type; 55 | } 56 | 57 | public static TradeState from(String s){ 58 | for (TradeState tt : TradeState.values()){ 59 | if (tt.type().equals(s)){ 60 | return tt; 61 | } 62 | } 63 | throw new IllegalArgumentException("unknown trade type: " + s); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/enums/TradeType.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.enums; 2 | 3 | /** 4 | * 交易类型 5 | * Author: haolin 6 | * Email: haolin.h0@gmail.com 7 | * Date: 26/11/15 8 | * @since 1.0.0 9 | */ 10 | public enum TradeType { 11 | 12 | /** 13 | * 公众号支付 14 | */ 15 | JSAPI("JSAPI"), 16 | 17 | /** 18 | * 原生扫码支付 19 | */ 20 | NATIVE("NATIVE"), 21 | 22 | /** 23 | * APP支付 24 | */ 25 | APP("APP"), 26 | 27 | /** 28 | * 刷卡支付 29 | */ 30 | MICROPAY("MICROPAY"); 31 | 32 | 33 | private String type; 34 | 35 | private TradeType(String type){ 36 | this.type = type; 37 | } 38 | 39 | public String type(){ 40 | return type; 41 | } 42 | 43 | public static TradeType from(String s){ 44 | for (TradeType tt : TradeType.values()){ 45 | if (tt.type().equals(s)){ 46 | return tt; 47 | } 48 | } 49 | throw new IllegalArgumentException("unknown trade type: " + s); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/enums/WepayField.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.enums; 2 | 3 | import me.hao0.wepay.annotation.Optional; 4 | 5 | /** 6 | * 微信支付字段 7 | * Author: haolin 8 | * Email: haolin.h0@gmail.com 9 | * Date: 26/11/15 10 | * @since 1.0.0 11 | */ 12 | public final class WepayField { 13 | 14 | /** 15 | * 成功标识 16 | */ 17 | public static final String SUCCESS = "SUCCESS"; 18 | 19 | /** 20 | * 响应结果 21 | */ 22 | public static final String RETURN_CODE = "return_code"; 23 | 24 | /** 25 | * 错误响应消息 26 | */ 27 | public static final String RETURN_MSG = "return_msg"; 28 | 29 | /** 30 | * 业务结果 31 | */ 32 | public static final String RESULT_CODE = "result_code"; 33 | 34 | /** 35 | * 业务错误码 36 | */ 37 | public static final String ERR_CODE = "err_code"; 38 | 39 | /** 40 | * 业务错误描述 41 | */ 42 | public static final String ERR_CODE_DES = "err_code_des"; 43 | 44 | /** 45 | * 公众帐号ID 46 | */ 47 | public static final String APP_ID = "appid"; 48 | 49 | /** 50 | * 公众帐号ID 51 | */ 52 | public static final String APPID = "appId"; 53 | 54 | /** 55 | * 微信支付分配的商户号 56 | */ 57 | public static final String MCH_ID = "mch_id"; 58 | 59 | /** 60 | * 终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB" 61 | */ 62 | @Optional 63 | public static final String DEVICE_INFO = "device_info"; 64 | 65 | /** 66 | * 随机字符串,不长于32位 67 | */ 68 | public static final String NONCE_STR = "nonce_str"; 69 | 70 | /** 71 | * 随机字符串,不长于32位 72 | */ 73 | public static final String NONCESTR = "noncestr"; 74 | 75 | /** 76 | * 随机字符串,不长于32位 77 | */ 78 | public static final String NONCESTR2 = "nonceStr"; 79 | 80 | /** 81 | * 签名 82 | */ 83 | public static final String SIGN = "sign"; 84 | 85 | /** 86 | * 签名类型 87 | */ 88 | public static final String SIGN_TYPE = "signType"; 89 | 90 | /** 91 | * 商品或支付单简要描述 92 | */ 93 | public static final String BODY = "body"; 94 | 95 | /** 96 | * 商品名称明细列表 97 | */ 98 | @Optional 99 | public static final String DETAIL = "detail"; 100 | 101 | /** 102 | * 附加数据 103 | * 在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据 104 | */ 105 | @Optional 106 | public static final String ATTACH = "attach"; 107 | 108 | /** 109 | * 商户订单号 110 | * 商户系统内部的订单号,32个字符内、可包含字母 111 | */ 112 | public static final String OUT_TRADE_NO = "out_trade_no"; 113 | 114 | /** 115 | * 货币类型 116 | * 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见 117 | */ 118 | @Optional 119 | public static final String FEE_TYPE = "fee_type"; 120 | 121 | /** 122 | * 订单总金额,单位为分 123 | */ 124 | public static final String TOTAL_FEE = "total_fee"; 125 | 126 | /** 127 | * 终端IP 128 | * APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。 129 | */ 130 | public static final String SPBILL_CREATE_IP = "spbill_create_ip"; 131 | 132 | /** 133 | * 交易起始时间 134 | * 订单生成时间,格式为yyyyMMddHHmmss 135 | */ 136 | public static final String TIME_START = "time_start"; 137 | 138 | /** 139 | * 交易结束时间 140 | * 订单失效时间,格式为yyyyMMddHHmmss。 141 | * 注意:最短失效时间间隔必须大于5分钟 142 | */ 143 | @Optional 144 | public static final String TIME_EXPIRE = "time_expire"; 145 | 146 | /** 147 | * 商品标记,代金券或立减优惠功能的参数 148 | */ 149 | @Optional 150 | public static final String GOODS_TAG = "goods_tag"; 151 | 152 | /** 153 | * 接收微信支付异步通知回调地址 154 | */ 155 | public static final String NOTIFY_URL = "notify_url"; 156 | 157 | /** 158 | * 交易类型 159 | * @see TradeType 160 | */ 161 | public static final String TRADE_TYPE = "trade_type"; 162 | 163 | /** 164 | * 商品ID 165 | * trade_type=NATIVE且为静态链接支付,此参数必传。此id为二维码中包含的商品ID,商户自行定义 166 | */ 167 | @Optional(any = false) 168 | public static final String PRODUCT_ID = "product_id"; 169 | 170 | /** 171 | * 指定支付方式 172 | * no_credit--指定不能使用信用卡支付 173 | */ 174 | @Optional 175 | public static final String LIMIT_PAY = "limit_pay"; 176 | 177 | /** 178 | * 用户标识 179 | * trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识 180 | */ 181 | @Optional(any = false) 182 | public static final String OPEN_ID = "openid"; 183 | 184 | /** 185 | * 预支付ID 186 | */ 187 | public static final String PREPAY_ID = "prepay_id"; 188 | 189 | /** 190 | * 预支付ID 191 | */ 192 | public static final String PREPAYID = "prepayid"; 193 | 194 | /** 195 | * 二维码链接 196 | */ 197 | public static final String CODE_URL = "code_url"; 198 | 199 | /** 200 | * 微信订单号 201 | */ 202 | public static final String TRANSACTION_ID = "transaction_id"; 203 | 204 | /** 205 | * 是否关注 206 | */ 207 | public static final String IS_SUBSCRIBE = "is_subscribe"; 208 | 209 | /** 210 | * 银行 211 | */ 212 | public static final String BANK_TYPE = "bank_type"; 213 | 214 | /** 215 | * 支付完成时间 216 | */ 217 | public static final String TIME_END = "time_end"; 218 | 219 | /** 220 | * 交易状态 221 | */ 222 | public static final String TRADE_STATE = "trade_state"; 223 | 224 | /** 225 | * 交易状态描述 226 | */ 227 | public static final String TRADE_STATE_DESC = "trade_state_desc"; 228 | 229 | /** 230 | * 现金支付金额 231 | */ 232 | public static final String CASH_FEE = "cash_fee"; 233 | 234 | /** 235 | * 现金支付货币类型 236 | */ 237 | public static final String CASH_FEE_TYPE = "cash_fee_type"; 238 | 239 | /** 240 | * 代金券或立减优惠金额 241 | */ 242 | public static final String COUPON_FEE = "coupon_fee"; 243 | 244 | /** 245 | * 代金券或立减优惠使用数量 246 | */ 247 | public static final String COUPON_COUNT = "coupon_count"; 248 | 249 | /** 250 | * prepay_id=xxx 251 | */ 252 | public static final String PKG = "package"; 253 | 254 | /** 255 | * 时间戳 256 | */ 257 | public static final String TIME_STAMP = "timeStamp"; 258 | 259 | /** 260 | * 时间戳 261 | */ 262 | public static final String TIMESTAMP = "timestamp"; 263 | 264 | /** 265 | * 签名KEY 266 | */ 267 | public static final String KEY = "key"; 268 | 269 | /** 270 | * 商户唯一退款订单号 271 | */ 272 | public static final String OUT_REFUND_NO = "out_refund_no"; 273 | 274 | /** 275 | * 退款金额 276 | */ 277 | public static final String REFUND_FEE = "refund_fee"; 278 | 279 | /** 280 | * 操作员ID 281 | */ 282 | public static final String OP_USER_ID = "op_user_id"; 283 | 284 | /** 285 | * 微信退款单号 286 | */ 287 | public static final String REFUND_ID = "refund_id"; 288 | 289 | /** 290 | * 退款渠道 291 | * @see me.hao0.wepay.model.enums.RefundChannel 292 | */ 293 | public static final String REFUND_CHANNEL = "refund_channel"; 294 | 295 | /** 296 | * 现金退款金额 297 | */ 298 | public static final String CASH_REFUND_FEE = "cash_refund_fee"; 299 | 300 | /** 301 | * 代金券或立减优惠退款金额 302 | */ 303 | public static final String COUPON_REFUND_FEE = "coupon_refund_fee"; 304 | 305 | /** 306 | * 代金券或立减优惠使用数量 307 | */ 308 | public static final String COUPON_REFUND_COUNT = "coupon_refund_count"; 309 | 310 | /** 311 | * 代金券或立减优惠ID 312 | */ 313 | public static final String COUPON_REFUND_ID = "coupon_refund_id"; 314 | 315 | /** 316 | * 退款货币类型 317 | */ 318 | public static final String REFUND_FEE_TYPE = "refund_fee_type"; 319 | 320 | /** 321 | * 退款笔数 322 | */ 323 | public static final String REFUND_COUNT = "refund_count"; 324 | 325 | /** 326 | * 代金券或立减优惠批次ID 327 | */ 328 | public static final String COUPON_REFUND_BATCH_ID = "coupon_refund_batch_id"; 329 | 330 | /** 331 | * 商户号 332 | */ 333 | public static final String PARTNER_ID = "partner_id"; 334 | 335 | /** 336 | * 商户号 337 | */ 338 | public static final String PARTNERID = "partnerid"; 339 | 340 | /** 341 | * 代金券或立减优惠批次ID 342 | */ 343 | public static final String COUPON_BATCH_ID = "coupon_batch_id"; 344 | 345 | /** 346 | * 代金券或立减优惠ID 347 | */ 348 | public static final String COUPON_ID = "coupon_id"; 349 | 350 | /** 351 | * 账单类型 352 | */ 353 | public static final String BILL_TYPE = "bill_type"; 354 | 355 | /** 356 | * 账单日期 357 | */ 358 | public static final String BILL_DATE = "bill_date"; 359 | 360 | /** 361 | * 子商户ID 362 | */ 363 | public static final String SUB_MCH_ID = "sub_mch_id"; 364 | 365 | /** 366 | * 企业红包金额 367 | */ 368 | public static final String ENTER_RED_PKG_FEE = "enter_ed_pkg_fee"; 369 | 370 | /** 371 | * 商户数据包 372 | */ 373 | public static final String DATA_PKG = "data_pkg"; 374 | 375 | /** 376 | * 商户手续费 377 | */ 378 | public static final String COMMISSION_FEE = "commission_fee"; 379 | 380 | /** 381 | * 费率 382 | */ 383 | public static final String FEE_RATE = "fee_rate"; 384 | 385 | /** 386 | * 企业红包退款金额 387 | */ 388 | public static final String ENTER_RED_PKG_REFUND_FEE = "enter_red_pkg_refund_fee"; 389 | 390 | /** 391 | * 退款申请时间 392 | */ 393 | public static final String REFUND_APPLY_TIME = "refund_apply_time"; 394 | 395 | /** 396 | * 退款成功时间 397 | */ 398 | public static final String REFUND_SUCCESS_TIME = "refund_success_time"; 399 | 400 | /** 401 | * 交易时间 402 | */ 403 | public static final String TRADE_TIME = "trade_time"; 404 | 405 | /** 406 | * 退款状态 407 | */ 408 | public static final String REFUND_STATUS = "refund_status"; 409 | 410 | /** 411 | * 总交易笔数 412 | */ 413 | public static final String TRADE_TOTAL_COUNT = "trade_total_count"; 414 | 415 | /** 416 | * 总交易额 417 | */ 418 | public static final String TRADE_TOTAL_FEE = "trade_total_fee"; 419 | 420 | /** 421 | * 总退款金额 422 | */ 423 | public static final String REFUND_TOTAL_FEE = "refund_total_fee"; 424 | 425 | /** 426 | * 总代金券或立减优惠退款金额 427 | */ 428 | public static final String COUPON_REFUND_TOTAL_FEE = "coupon_refund_total_fee"; 429 | 430 | /** 431 | * 手续费总金额 432 | */ 433 | public static final String COMMISSION_TOTAL_FEE = "commission_total_fee"; 434 | 435 | /** 436 | * 退款金额 437 | */ 438 | public static final String SETTLEMENT_REFUND_FEE = "settlement_refund_fee"; 439 | 440 | /** 441 | * 退款入账账户 442 | * 1)退回银行卡: 443 | * {银行名称}{卡类型}{卡尾号} 444 | * 2)退回支付用户零钱: 445 | * 支付用户零钱 446 | */ 447 | public static final String REFUND_RECV_ACCOUNT = "refund_recv_accout"; 448 | 449 | } 450 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/order/WePayOrder.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.order; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import me.hao0.wepay.model.common.Coupon; 7 | import me.hao0.wepay.model.enums.FeeType; 8 | import me.hao0.wepay.model.enums.TradeState; 9 | import me.hao0.wepay.model.enums.TradeType; 10 | import me.hao0.wepay.model.enums.WepayField; 11 | import me.hao0.wepay.serializer.BooleanDeserializer; 12 | import me.hao0.wepay.serializer.DateDeserializer; 13 | import me.hao0.wepay.serializer.FeeTypeDeserializer; 14 | import me.hao0.wepay.serializer.TradeStateDeserializer; 15 | import me.hao0.wepay.serializer.TradeTypeDeserializer; 16 | import java.io.Serializable; 17 | import java.util.Date; 18 | import java.util.List; 19 | 20 | /** 21 | * 微信查询订单对象 22 | * Author: haolin 23 | * Email: haolin.h0@gmail.com 24 | * Date: 27/11/15 25 | * @since 1.0.0 26 | */ 27 | public class WePayOrder implements Serializable { 28 | 29 | private static final long serialVersionUID = -3808893700552515824L; 30 | 31 | /** 32 | * 用户openId 33 | */ 34 | @JsonProperty(WepayField.OPEN_ID) 35 | private String openId; 36 | 37 | /** 38 | * 用户是否关注公众号 39 | */ 40 | @JsonProperty(WepayField.IS_SUBSCRIBE) 41 | @JsonDeserialize(using = BooleanDeserializer.class) 42 | private Boolean subscribe; 43 | 44 | /** 45 | * 交易类型 46 | */ 47 | @JsonProperty(WepayField.TRADE_TYPE) 48 | @JsonDeserialize(using = TradeTypeDeserializer.class) 49 | private TradeType tradeType; 50 | 51 | /** 52 | * 银行类型 53 | */ 54 | @JsonProperty(WepayField.BANK_TYPE) 55 | private String bankType; 56 | 57 | /** 58 | * 总金额 59 | */ 60 | @JsonProperty(WepayField.TOTAL_FEE) 61 | private Integer totalFee; 62 | 63 | @JsonProperty(WepayField.FEE_TYPE) 64 | @JsonDeserialize(using = FeeTypeDeserializer.class) 65 | private FeeType feeType; 66 | 67 | /** 68 | * 微信订单好 69 | */ 70 | @JsonProperty(WepayField.TRANSACTION_ID) 71 | private String transactionId; 72 | 73 | /** 74 | * 商户订单号 75 | */ 76 | @JsonProperty(WepayField.OUT_TRADE_NO) 77 | private String outTradeNo; 78 | 79 | /** 80 | * 附加数据 81 | */ 82 | private String attach; 83 | 84 | /** 85 | * 支付完成时间 86 | */ 87 | @JsonProperty(WepayField.TIME_END) 88 | @JsonDeserialize(using = DateDeserializer.class) 89 | private Date timeEnd; 90 | 91 | /** 92 | * 交易状态 93 | */ 94 | @JsonProperty(WepayField.TRADE_STATE) 95 | @JsonDeserialize(using = TradeStateDeserializer.class) 96 | private TradeState tradeState; 97 | 98 | /** 99 | * 交易状态描述 100 | */ 101 | @JsonProperty(WepayField.TRADE_STATE_DESC) 102 | private String tradeStateDesc; 103 | 104 | /** 105 | * 现金支付金额 106 | */ 107 | @JsonProperty(WepayField.CASH_FEE) 108 | private Integer cachFee; 109 | 110 | @JsonProperty(WepayField.CASH_FEE_TYPE) 111 | @JsonDeserialize(using = FeeTypeDeserializer.class) 112 | private FeeType cashFeeType; 113 | 114 | /** 115 | * 设备号 116 | */ 117 | @JsonProperty(WepayField.DEVICE_INFO) 118 | private String deviceInfo; 119 | 120 | /** 121 | * 代金券或立减优惠金额(分) 122 | */ 123 | @JsonProperty(WepayField.COUPON_FEE) 124 | private Integer couponFee; 125 | 126 | /** 127 | * 代金券或立减优惠使用数量 128 | */ 129 | @JsonProperty(WepayField.COUPON_COUNT) 130 | private Integer couponCount; 131 | 132 | /** 133 | * 代金券或立减优惠明细 134 | */ 135 | @JsonIgnore 136 | private List coupons; 137 | 138 | public String getOpenId() { 139 | return openId; 140 | } 141 | 142 | public void setOpenId(String openId) { 143 | this.openId = openId; 144 | } 145 | 146 | public Boolean getSubscribe() { 147 | return subscribe; 148 | } 149 | 150 | public void setSubscribe(Boolean subscribe) { 151 | this.subscribe = subscribe; 152 | } 153 | 154 | public TradeType getTradeType() { 155 | return tradeType; 156 | } 157 | 158 | public void setTradeType(TradeType tradeType) { 159 | this.tradeType = tradeType; 160 | } 161 | 162 | public String getBankType() { 163 | return bankType; 164 | } 165 | 166 | public void setBankType(String bankType) { 167 | this.bankType = bankType; 168 | } 169 | 170 | public Integer getTotalFee() { 171 | return totalFee; 172 | } 173 | 174 | public void setTotalFee(Integer totalFee) { 175 | this.totalFee = totalFee; 176 | } 177 | 178 | public FeeType getFeeType() { 179 | return feeType; 180 | } 181 | 182 | public void setFeeType(FeeType feeType) { 183 | this.feeType = feeType; 184 | } 185 | 186 | public String getTransactionId() { 187 | return transactionId; 188 | } 189 | 190 | public void setTransactionId(String transactionId) { 191 | this.transactionId = transactionId; 192 | } 193 | 194 | public String getOutTradeNo() { 195 | return outTradeNo; 196 | } 197 | 198 | public void setOutTradeNo(String outTradeNo) { 199 | this.outTradeNo = outTradeNo; 200 | } 201 | 202 | public String getAttach() { 203 | return attach; 204 | } 205 | 206 | public void setAttach(String attach) { 207 | this.attach = attach; 208 | } 209 | 210 | public Date getTimeEnd() { 211 | return timeEnd; 212 | } 213 | 214 | public void setTimeEnd(Date timeEnd) { 215 | this.timeEnd = timeEnd; 216 | } 217 | 218 | public TradeState getTradeState() { 219 | return tradeState; 220 | } 221 | 222 | public void setTradeState(TradeState tradeState) { 223 | this.tradeState = tradeState; 224 | } 225 | 226 | public String getTradeStateDesc() { 227 | return tradeStateDesc; 228 | } 229 | 230 | public void setTradeStateDesc(String tradeStateDesc) { 231 | this.tradeStateDesc = tradeStateDesc; 232 | } 233 | 234 | public Integer getCachFee() { 235 | return cachFee; 236 | } 237 | 238 | public void setCachFee(Integer cachFee) { 239 | this.cachFee = cachFee; 240 | } 241 | 242 | public FeeType getCashFeeType() { 243 | return cashFeeType; 244 | } 245 | 246 | public void setCashFeeType(FeeType cashFeeType) { 247 | this.cashFeeType = cashFeeType; 248 | } 249 | 250 | public String getDeviceInfo() { 251 | return deviceInfo; 252 | } 253 | 254 | public void setDeviceInfo(String deviceInfo) { 255 | this.deviceInfo = deviceInfo; 256 | } 257 | 258 | public Integer getCouponFee() { 259 | return couponFee; 260 | } 261 | 262 | public void setCouponFee(Integer couponFee) { 263 | this.couponFee = couponFee; 264 | } 265 | 266 | public Integer getCouponCount() { 267 | return couponCount; 268 | } 269 | 270 | public void setCouponCount(Integer couponCount) { 271 | this.couponCount = couponCount; 272 | } 273 | 274 | public List getCoupons() { 275 | return coupons; 276 | } 277 | 278 | public void setCoupons(List coupons) { 279 | this.coupons = coupons; 280 | } 281 | 282 | @Override 283 | public String toString() { 284 | return "WePayOrder{" + 285 | "openId='" + openId + '\'' + 286 | ", subscribe=" + subscribe + 287 | ", tradeType=" + tradeType + 288 | ", bankType='" + bankType + '\'' + 289 | ", totalFee=" + totalFee + 290 | ", feeType=" + feeType + 291 | ", transactionId='" + transactionId + '\'' + 292 | ", outTradeNo='" + outTradeNo + '\'' + 293 | ", attach='" + attach + '\'' + 294 | ", timeEnd=" + timeEnd + 295 | ", tradeState=" + tradeState + 296 | ", tradeStateDesc='" + tradeStateDesc + '\'' + 297 | ", cachFee=" + cachFee + 298 | ", cashFeeType=" + cashFeeType + 299 | ", deviceInfo='" + deviceInfo + '\'' + 300 | ", couponFee=" + couponFee + 301 | ", couponCount=" + couponCount + 302 | ", coupons=" + coupons + 303 | '}'; 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/pay/AppPayResponse.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.pay; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 公众号支付响应对象 7 | * Author: haolin 8 | * Email: haolin.h0@gmail.com 9 | * Date: 26/11/15 10 | */ 11 | public class AppPayResponse implements Serializable { 12 | 13 | private static final long serialVersionUID = 2540820967097836895L; 14 | 15 | /** 16 | * 微信APPID 17 | */ 18 | private String appId; 19 | 20 | /** 21 | * 商户Id 22 | */ 23 | private String partnerId; 24 | 25 | /** 26 | * 预支付ID 27 | */ 28 | private String prepayId; 29 | 30 | /** 31 | * 时间戳(s) 32 | */ 33 | private String timeStamp; 34 | 35 | /** 36 | * 随机字符串 37 | */ 38 | private String nonceStr; 39 | 40 | /** 41 | * package 42 | */ 43 | private String pkg = "Sign=WXPay"; 44 | 45 | /** 46 | * 签名 47 | */ 48 | private String paySign; 49 | 50 | public AppPayResponse(String appId, String partnerId, String prepayId, String timeStamp, String nonceStr, String paySign) { 51 | this.appId = appId; 52 | this.partnerId = partnerId; 53 | this.prepayId = prepayId; 54 | this.timeStamp = timeStamp; 55 | this.nonceStr = nonceStr; 56 | this.paySign = paySign; 57 | } 58 | 59 | public String getAppId() { 60 | return appId; 61 | } 62 | 63 | public String getTimeStamp() { 64 | return timeStamp; 65 | } 66 | 67 | public String getNonceStr() { 68 | return nonceStr; 69 | } 70 | 71 | public String getPkg() { 72 | return pkg; 73 | } 74 | 75 | public String getPaySign() { 76 | return paySign; 77 | } 78 | 79 | public String getPartnerId() { 80 | return partnerId; 81 | } 82 | 83 | public String getPrepayId() { 84 | return prepayId; 85 | } 86 | 87 | @Override 88 | public String toString() { 89 | return "AppPayResponse{" + 90 | "appId='" + appId + '\'' + 91 | ", partnerId='" + partnerId + '\'' + 92 | ", prepayId='" + prepayId + '\'' + 93 | ", timeStamp='" + timeStamp + '\'' + 94 | ", nonceStr='" + nonceStr + '\'' + 95 | ", pkg='" + pkg + '\'' + 96 | ", paySign='" + paySign + '\'' + 97 | '}'; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/pay/JsPayRequest.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.pay; 2 | 3 | 4 | /** 5 | * JS支付请求对象 6 | * Author: haolin 7 | * Email: haolin.h0@gmail.com 8 | * Date: 26/11/15 9 | * @since 1.0.0 10 | */ 11 | public class JsPayRequest extends PayRequest { 12 | 13 | /** 14 | * 用户标识 15 | * {@link me.hao0.wepay.model.enums.WepayField#OPEN_ID} 16 | */ 17 | private String openId; 18 | 19 | public String getOpenId() { 20 | return openId; 21 | } 22 | 23 | public void setOpenId(String openId) { 24 | this.openId = openId; 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return "JsPayRequest{" + 30 | "openId='" + openId + '\'' + 31 | "} " + super.toString(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/pay/JsPayResponse.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.pay; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 公众号支付响应对象 7 | * Author: haolin 8 | * Email: haolin.h0@gmail.com 9 | * Date: 26/11/15 10 | */ 11 | public class JsPayResponse implements Serializable { 12 | 13 | private static final long serialVersionUID = 2540820967097836895L; 14 | 15 | /** 16 | * 微信APPID 17 | */ 18 | private String appId; 19 | 20 | /** 21 | * 时间戳(s) 22 | */ 23 | private String timeStamp; 24 | 25 | /** 26 | * 随机字符串 27 | */ 28 | private String nonceStr; 29 | 30 | /** 31 | * 预支付ID串,如: prepare_id=xxx 32 | * 注意: JS前端调用时的字段名为package,与java关键字冲突 33 | */ 34 | private String pkg; 35 | 36 | /** 37 | * 签名类型MD5 38 | */ 39 | private String signType; 40 | 41 | /** 42 | * 签名 43 | */ 44 | private String paySign; 45 | 46 | public JsPayResponse(String appId, String timeStamp, String nonceStr, String pkg, String signType, String paySign) { 47 | this.appId = appId; 48 | this.timeStamp = timeStamp; 49 | this.nonceStr = nonceStr; 50 | this.pkg = pkg; 51 | this.signType = signType; 52 | this.paySign = paySign; 53 | } 54 | 55 | public String getAppId() { 56 | return appId; 57 | } 58 | 59 | public String getTimeStamp() { 60 | return timeStamp; 61 | } 62 | 63 | public String getNonceStr() { 64 | return nonceStr; 65 | } 66 | 67 | public String getPkg() { 68 | return pkg; 69 | } 70 | 71 | public String getSignType() { 72 | return signType; 73 | } 74 | 75 | public String getPaySign() { 76 | return paySign; 77 | } 78 | 79 | @Override 80 | public String toString() { 81 | return "JsPayResponse{" + 82 | "appId='" + appId + '\'' + 83 | ", timeStamp='" + timeStamp + '\'' + 84 | ", nonceStr='" + nonceStr + '\'' + 85 | ", pkg='" + pkg + '\'' + 86 | ", signType='" + signType + '\'' + 87 | ", paySign='" + paySign + '\'' + 88 | '}'; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/pay/PayRequest.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.pay; 2 | 3 | import me.hao0.wepay.annotation.Optional; 4 | import me.hao0.wepay.model.enums.FeeType; 5 | import java.io.Serializable; 6 | 7 | /** 8 | * 支付请求对象 9 | * Author: haolin 10 | * Email: haolin.h0@gmail.com 11 | * Date: 26/11/15 12 | * @since 1.0.0 13 | */ 14 | public class PayRequest implements Serializable { 15 | 16 | /** 17 | * 商品描述 18 | * {@link me.hao0.wepay.model.enums.WepayField#BODY} 19 | */ 20 | private String body; 21 | 22 | /** 23 | * 业务系统唯一订单号 24 | * {@link me.hao0.wepay.model.enums.WepayField#OUT_TRADE_NO} 25 | */ 26 | private String outTradeNo; 27 | 28 | /** 29 | * 总金额(分) 30 | * {@link me.hao0.wepay.model.enums.WepayField#TOTAL_FEE} 31 | */ 32 | private Integer totalFee; 33 | 34 | /** 35 | * 客户端IP 36 | * {@link me.hao0.wepay.model.enums.WepayField#SPBILL_CREATE_IP} 37 | */ 38 | private String clientIp; 39 | 40 | /** 41 | * 通知URL 42 | * {@link me.hao0.wepay.model.enums.WepayField#NOTIFY_URL} 43 | */ 44 | private String notifyUrl; 45 | 46 | /** 47 | * 设备号 48 | * {@link me.hao0.wepay.model.enums.WepayField#DEVICE_INFO} 49 | */ 50 | @Optional 51 | private String deviceInfo; 52 | 53 | /** 54 | * 附加信息 55 | * {@link me.hao0.wepay.model.enums.WepayField#ATTACH} 56 | */ 57 | @Optional 58 | private String attach; 59 | 60 | /** 61 | * 商品详情 62 | * {@link me.hao0.wepay.model.enums.WepayField#DETAIL} 63 | */ 64 | @Optional 65 | private String detail; 66 | 67 | /** 68 | * 货币类型 69 | * {@link me.hao0.wepay.model.enums.WepayField#FEE_TYPE} 70 | */ 71 | private FeeType feeType = FeeType.CNY; 72 | 73 | /** 74 | * 交易开始时间 75 | * {@link me.hao0.wepay.model.enums.WepayField#TIME_START} 76 | */ 77 | private String timeStart; 78 | 79 | /** 80 | * 交易结束时间 81 | * {@link me.hao0.wepay.model.enums.WepayField#TIME_EXPIRE} 82 | */ 83 | @Optional 84 | private String timeExpire; 85 | 86 | /** 87 | * 商品标记 88 | * {@link me.hao0.wepay.model.enums.WepayField#GOODS_TAG} 89 | */ 90 | @Optional 91 | private String goodsTag; 92 | 93 | /** 94 | * 指定支付方式 95 | * {@link me.hao0.wepay.model.enums.WepayField#LIMIT_PAY} 96 | */ 97 | @Optional 98 | private String limitPay; 99 | 100 | public String getBody() { 101 | return body; 102 | } 103 | 104 | public void setBody(String body) { 105 | this.body = body; 106 | } 107 | 108 | public String getOutTradeNo() { 109 | return outTradeNo; 110 | } 111 | 112 | public void setOutTradeNo(String outTradeNo) { 113 | this.outTradeNo = outTradeNo; 114 | } 115 | 116 | public Integer getTotalFee() { 117 | return totalFee; 118 | } 119 | 120 | public void setTotalFee(Integer totalFee) { 121 | this.totalFee = totalFee; 122 | } 123 | 124 | public String getClientIp() { 125 | return clientIp; 126 | } 127 | 128 | public void setClientIp(String clientIp) { 129 | this.clientIp = clientIp; 130 | } 131 | 132 | public String getNotifyUrl() { 133 | return notifyUrl; 134 | } 135 | 136 | public void setNotifyUrl(String notifyUrl) { 137 | this.notifyUrl = notifyUrl; 138 | } 139 | 140 | public String getDeviceInfo() { 141 | return deviceInfo; 142 | } 143 | 144 | public void setDeviceInfo(String deviceInfo) { 145 | this.deviceInfo = deviceInfo; 146 | } 147 | 148 | public String getAttach() { 149 | return attach; 150 | } 151 | 152 | public void setAttach(String attach) { 153 | this.attach = attach; 154 | } 155 | 156 | public String getDetail() { 157 | return detail; 158 | } 159 | 160 | public void setDetail(String detail) { 161 | this.detail = detail; 162 | } 163 | 164 | public FeeType getFeeType() { 165 | return feeType; 166 | } 167 | 168 | public void setFeeType(FeeType feeType) { 169 | this.feeType = feeType; 170 | } 171 | 172 | public String getTimeStart() { 173 | return timeStart; 174 | } 175 | 176 | public void setTimeStart(String timeStart) { 177 | this.timeStart = timeStart; 178 | } 179 | 180 | public String getTimeExpire() { 181 | return timeExpire; 182 | } 183 | 184 | public void setTimeExpire(String timeExpire) { 185 | this.timeExpire = timeExpire; 186 | } 187 | 188 | public String getGoodsTag() { 189 | return goodsTag; 190 | } 191 | 192 | public void setGoodsTag(String goodsTag) { 193 | this.goodsTag = goodsTag; 194 | } 195 | 196 | public String getLimitPay() { 197 | return limitPay; 198 | } 199 | 200 | public void setLimitPay(String limitPay) { 201 | this.limitPay = limitPay; 202 | } 203 | 204 | @Override 205 | public String toString() { 206 | return "PayDetail{" + 207 | "body='" + body + '\'' + 208 | ", outTradeNo='" + outTradeNo + '\'' + 209 | ", totalFee=" + totalFee + 210 | ", clientIp='" + clientIp + '\'' + 211 | ", notifyUrl='" + notifyUrl + '\'' + 212 | ", deviceInfo='" + deviceInfo + '\'' + 213 | ", attach='" + attach + '\'' + 214 | ", detail='" + detail + '\'' + 215 | ", feeType=" + feeType + 216 | ", timeStart='" + timeStart + '\'' + 217 | ", timeExpire='" + timeExpire + '\'' + 218 | ", goodsTag='" + goodsTag + '\'' + 219 | ", limitPay='" + limitPay + '\'' + 220 | '}'; 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/pay/QrPayRequest.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.pay; 2 | 3 | 4 | import me.hao0.wepay.annotation.Optional; 5 | 6 | /** 7 | * 二维码支付请求对象 8 | * Author: haolin 9 | * Email: haolin.h0@gmail.com 10 | * Date: 26/11/15 11 | * @since 1.0.0 12 | */ 13 | public class QrPayRequest extends PayRequest { 14 | 15 | /** 16 | * 商品ID 17 | * {@link me.hao0.wepay.model.enums.WepayField#PRODUCT_ID} 18 | */ 19 | @Optional(any = false) 20 | private String productId; 21 | 22 | public String getProductId() { 23 | return productId; 24 | } 25 | 26 | public void setProductId(String productId) { 27 | this.productId = productId; 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | return "QrPayRequest{" + 33 | "productId='" + productId + '\'' + 34 | "} " + super.toString(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/pay/QrPayResponse.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.pay; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 二维码支付(NATIVE)响应结果 7 | * Author: haolin 8 | * Email: haolin.h0@gmail.com 9 | */ 10 | public class QrPayResponse implements Serializable { 11 | 12 | private static final long serialVersionUID = 1383835653965951921L; 13 | 14 | /** 15 | * 预支付ID 16 | */ 17 | private String prepayId; 18 | 19 | /** 20 | * 支付二维码链接 21 | */ 22 | private String codeUrl; 23 | 24 | public String getPrepayId() { 25 | return prepayId; 26 | } 27 | 28 | public void setPrepayId(String prepayId) { 29 | this.prepayId = prepayId; 30 | } 31 | 32 | public String getCodeUrl() { 33 | return codeUrl; 34 | } 35 | 36 | public void setCodeUrl(String codeUrl) { 37 | this.codeUrl = codeUrl; 38 | } 39 | 40 | @Override 41 | public String toString() { 42 | return "QrPayResponse{" + 43 | "prepayId='" + prepayId + '\'' + 44 | ", codeUrl='" + codeUrl + '\'' + 45 | '}'; 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/refund/RefundApplyRequest.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.refund; 2 | 3 | import me.hao0.wepay.annotation.Optional; 4 | import me.hao0.wepay.model.enums.FeeType; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * 退款请求对象 10 | * Author: haolin 11 | * Email: haolin.h0@gmail.com 12 | * Date: 28/11/15 13 | * @since 1.0.0 14 | */ 15 | public class RefundApplyRequest implements Serializable { 16 | 17 | private static final long serialVersionUID = 5046932866574485686L; 18 | 19 | /** 20 | * 设备号 21 | */ 22 | @Optional 23 | private String deviceInfo; 24 | 25 | /** 26 | * 微信订单号,与outTradeNo二选一 27 | */ 28 | private String transactionId; 29 | 30 | /** 31 | * 商户订单号,与transactionId二选一 32 | */ 33 | private String outTradeNo; 34 | 35 | /** 36 | * 商户退款单号 37 | */ 38 | private String outRefundNo; 39 | 40 | /** 41 | * 订单总金额 42 | */ 43 | private Integer totalFee; 44 | 45 | /** 46 | * 退款金额 47 | */ 48 | private Integer refundFee; 49 | 50 | /** 51 | * 货币类型 52 | */ 53 | @Optional 54 | private FeeType refundFeeType = FeeType.CNY; 55 | 56 | /** 57 | * 操作员 58 | */ 59 | private String opUserId; 60 | 61 | public String getDeviceInfo() { 62 | return deviceInfo; 63 | } 64 | 65 | public void setDeviceInfo(String deviceInfo) { 66 | this.deviceInfo = deviceInfo; 67 | } 68 | 69 | public String getTransactionId() { 70 | return transactionId; 71 | } 72 | 73 | public void setTransactionId(String transactionId) { 74 | this.transactionId = transactionId; 75 | } 76 | 77 | public String getOutTradeNo() { 78 | return outTradeNo; 79 | } 80 | 81 | public void setOutTradeNo(String outTradeNo) { 82 | this.outTradeNo = outTradeNo; 83 | } 84 | 85 | public String getOutRefundNo() { 86 | return outRefundNo; 87 | } 88 | 89 | public void setOutRefundNo(String outRefundNo) { 90 | this.outRefundNo = outRefundNo; 91 | } 92 | 93 | public Integer getTotalFee() { 94 | return totalFee; 95 | } 96 | 97 | public void setTotalFee(Integer totalFee) { 98 | this.totalFee = totalFee; 99 | } 100 | 101 | public Integer getRefundFee() { 102 | return refundFee; 103 | } 104 | 105 | public void setRefundFee(Integer refundFee) { 106 | this.refundFee = refundFee; 107 | } 108 | 109 | public FeeType getRefundFeeType() { 110 | return refundFeeType; 111 | } 112 | 113 | public void setRefundFeeType(FeeType refundFeeType) { 114 | this.refundFeeType = refundFeeType; 115 | } 116 | 117 | public String getOpUserId() { 118 | return opUserId; 119 | } 120 | 121 | public void setOpUserId(String opUserId) { 122 | this.opUserId = opUserId; 123 | } 124 | 125 | @Override 126 | public String toString() { 127 | return "RefundRequest{" + 128 | "deviceInfo='" + deviceInfo + '\'' + 129 | ", transactionId='" + transactionId + '\'' + 130 | ", outTradeNo='" + outTradeNo + '\'' + 131 | ", outRefundNo='" + outRefundNo + '\'' + 132 | ", totalFee=" + totalFee + 133 | ", refundFee=" + refundFee + 134 | ", refundFeeType=" + refundFeeType + 135 | ", opUserId='" + opUserId + '\'' + 136 | '}'; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/refund/RefundApplyResponse.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.refund; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 5 | import me.hao0.wepay.annotation.Optional; 6 | import me.hao0.wepay.model.enums.FeeType; 7 | import me.hao0.wepay.model.enums.RefundChannel; 8 | import me.hao0.wepay.model.enums.WepayField; 9 | import me.hao0.wepay.serializer.FeeTypeDeserializer; 10 | import me.hao0.wepay.serializer.RefundChannelDeserializer; 11 | 12 | import java.io.Serializable; 13 | 14 | /** 15 | * 退款结果 16 | * Author: haolin 17 | * Email: haolin.h0@gmail.com 18 | * Date: 28/11/15 19 | * @since 1.0.0 20 | */ 21 | public class RefundApplyResponse implements Serializable { 22 | 23 | private static final long serialVersionUID = -8303581191923588820L; 24 | 25 | /** 26 | * 设备号 27 | */ 28 | @Optional 29 | @JsonProperty(WepayField.DEVICE_INFO) 30 | private String deviceInfo; 31 | 32 | /** 33 | * 随机字符串 34 | */ 35 | @JsonProperty(WepayField.NONCE_STR) 36 | private String nonceStr; 37 | 38 | /** 39 | * 微信订单号 40 | */ 41 | @JsonProperty(WepayField.TRANSACTION_ID) 42 | private String transactionId; 43 | 44 | /** 45 | * 商户订单号 46 | */ 47 | @JsonProperty(WepayField.OUT_TRADE_NO) 48 | private String outTradeNo; 49 | 50 | /** 51 | * 商户退款单号 52 | */ 53 | @JsonProperty(WepayField.OUT_REFUND_NO) 54 | private String outRefundNo; 55 | 56 | /** 57 | * 微信退款但号 58 | */ 59 | @JsonProperty(WepayField.REFUND_ID) 60 | private String refundId; 61 | 62 | /** 63 | * 退款渠道 64 | */ 65 | @JsonProperty(WepayField.REFUND_CHANNEL) 66 | @JsonDeserialize(using = RefundChannelDeserializer.class) 67 | private RefundChannel channel; 68 | 69 | /** 70 | * 退款金额 71 | */ 72 | @JsonProperty(WepayField.REFUND_FEE) 73 | private Integer refundFee; 74 | 75 | /** 76 | * 订单总金额 77 | */ 78 | @JsonProperty(WepayField.TOTAL_FEE) 79 | private Integer totalFee; 80 | 81 | /** 82 | * 货币类型 83 | */ 84 | @JsonProperty(WepayField.FEE_TYPE) 85 | @JsonDeserialize(using = FeeTypeDeserializer.class) 86 | private FeeType feeType; 87 | 88 | /** 89 | * 现金支付金额 90 | */ 91 | @JsonProperty(WepayField.CASH_FEE) 92 | private Integer cashFee; 93 | 94 | /** 95 | * 现金退款金额 96 | */ 97 | @JsonProperty(WepayField.CASH_REFUND_FEE) 98 | private Integer cashRefundFee; 99 | 100 | /** 101 | * 代金券或立减优惠退款金额 102 | */ 103 | @JsonProperty(WepayField.COUPON_REFUND_FEE) 104 | private Integer couponRefundFee; 105 | 106 | /** 107 | * 代金券或立减优惠使用数量 108 | */ 109 | @JsonProperty(WepayField.COUPON_REFUND_COUNT) 110 | private Integer couponRefundCount; 111 | 112 | /** 113 | * 代金券或立减优惠ID 114 | */ 115 | @JsonProperty(WepayField.COUPON_REFUND_ID) 116 | private Integer couponRefundId; 117 | 118 | public String getDeviceInfo() { 119 | return deviceInfo; 120 | } 121 | 122 | public void setDeviceInfo(String deviceInfo) { 123 | this.deviceInfo = deviceInfo; 124 | } 125 | 126 | public String getNonceStr() { 127 | return nonceStr; 128 | } 129 | 130 | public void setNonceStr(String nonceStr) { 131 | this.nonceStr = nonceStr; 132 | } 133 | 134 | public String getTransactionId() { 135 | return transactionId; 136 | } 137 | 138 | public void setTransactionId(String transactionId) { 139 | this.transactionId = transactionId; 140 | } 141 | 142 | public String getOutTradeNo() { 143 | return outTradeNo; 144 | } 145 | 146 | public void setOutTradeNo(String outTradeNo) { 147 | this.outTradeNo = outTradeNo; 148 | } 149 | 150 | public String getOutRefundNo() { 151 | return outRefundNo; 152 | } 153 | 154 | public void setOutRefundNo(String outRefundNo) { 155 | this.outRefundNo = outRefundNo; 156 | } 157 | 158 | public String getRefundId() { 159 | return refundId; 160 | } 161 | 162 | public void setRefundId(String refundId) { 163 | this.refundId = refundId; 164 | } 165 | 166 | public RefundChannel getChannel() { 167 | return channel; 168 | } 169 | 170 | public void setChannel(RefundChannel channel) { 171 | this.channel = channel; 172 | } 173 | 174 | public Integer getRefundFee() { 175 | return refundFee; 176 | } 177 | 178 | public void setRefundFee(Integer refundFee) { 179 | this.refundFee = refundFee; 180 | } 181 | 182 | public Integer getTotalFee() { 183 | return totalFee; 184 | } 185 | 186 | public void setTotalFee(Integer totalFee) { 187 | this.totalFee = totalFee; 188 | } 189 | 190 | public FeeType getFeeType() { 191 | return feeType; 192 | } 193 | 194 | public void setFeeType(FeeType feeType) { 195 | this.feeType = feeType; 196 | } 197 | 198 | public Integer getCashFee() { 199 | return cashFee; 200 | } 201 | 202 | public void setCashFee(Integer cashFee) { 203 | this.cashFee = cashFee; 204 | } 205 | 206 | public Integer getCashRefundFee() { 207 | return cashRefundFee; 208 | } 209 | 210 | public void setCashRefundFee(Integer cashRefundFee) { 211 | this.cashRefundFee = cashRefundFee; 212 | } 213 | 214 | public Integer getCouponRefundFee() { 215 | return couponRefundFee; 216 | } 217 | 218 | public void setCouponRefundFee(Integer couponRefundFee) { 219 | this.couponRefundFee = couponRefundFee; 220 | } 221 | 222 | public Integer getCouponRefundCount() { 223 | return couponRefundCount; 224 | } 225 | 226 | public void setCouponRefundCount(Integer couponRefundCount) { 227 | this.couponRefundCount = couponRefundCount; 228 | } 229 | 230 | public Integer getCouponRefundId() { 231 | return couponRefundId; 232 | } 233 | 234 | public void setCouponRefundId(Integer couponRefundId) { 235 | this.couponRefundId = couponRefundId; 236 | } 237 | 238 | @Override 239 | public String toString() { 240 | return "RefundResponse{" + 241 | "deviceInfo='" + deviceInfo + '\'' + 242 | ", nonceStr='" + nonceStr + '\'' + 243 | ", transactionId='" + transactionId + '\'' + 244 | ", outTradeNo='" + outTradeNo + '\'' + 245 | ", outRefundNo='" + outRefundNo + '\'' + 246 | ", refundId='" + refundId + '\'' + 247 | ", channel=" + channel + 248 | ", refundFee=" + refundFee + 249 | ", totalFee=" + totalFee + 250 | ", feeType=" + feeType + 251 | ", cashFee=" + cashFee + 252 | ", cashRefundFee=" + cashRefundFee + 253 | ", couponRefundFee=" + couponRefundFee + 254 | ", couponRefundCount=" + couponRefundCount + 255 | ", couponRefundId=" + couponRefundId + 256 | '}'; 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/refund/RefundItem.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.refund; 2 | 3 | import me.hao0.wepay.model.common.Coupon; 4 | import me.hao0.wepay.model.enums.RefundChannel; 5 | import me.hao0.wepay.model.enums.RefundStatus; 6 | import java.io.Serializable; 7 | import java.util.List; 8 | 9 | /** 10 | * 单笔退款 11 | * Author: haolin 12 | * Email: haolin.h0@gmail.com 13 | * Date: 1/12/15 14 | * @since 1.0.0 15 | */ 16 | public class RefundItem implements Serializable { 17 | 18 | private static final long serialVersionUID = -8803509387441693049L; 19 | 20 | /** 21 | * 商户退款单号 22 | */ 23 | private String outRefundNo; 24 | 25 | /** 26 | * 微信退款单号 27 | */ 28 | private String refundId; 29 | 30 | /** 31 | * 退款渠道 32 | */ 33 | private RefundChannel channel; 34 | 35 | /** 36 | * 申请退款金额, 可以做部分退款 37 | */ 38 | private Integer refundFee; 39 | 40 | /** 41 | * 退款金额, = 申请退款金额-非充值代金券退款金额,退款金额<=申请退款金额 42 | */ 43 | private Integer settlementRefundFee; 44 | 45 | /** 46 | * 退款状态 47 | * @see RefundStatus 48 | */ 49 | private RefundStatus refundStatus; 50 | 51 | /** 52 | * 取当前退款单的退款入账方 53 | * 1)退回银行卡: 54 | * {银行名称}{卡类型}{卡尾号} 55 | * 2)退回支付用户零钱: 56 | * 支付用户零钱 57 | */ 58 | private String refundRecvAccout; 59 | 60 | /** 61 | * 代金券或立减优惠退款金额 62 | */ 63 | private Integer couponRefundFee; 64 | 65 | /** 66 | * 代金券或立减优惠退款项 67 | */ 68 | private List coupons; 69 | 70 | public String getOutRefundNo() { 71 | return outRefundNo; 72 | } 73 | 74 | public void setOutRefundNo(String outRefundNo) { 75 | this.outRefundNo = outRefundNo; 76 | } 77 | 78 | public String getRefundId() { 79 | return refundId; 80 | } 81 | 82 | public void setRefundId(String refundId) { 83 | this.refundId = refundId; 84 | } 85 | 86 | public RefundChannel getChannel() { 87 | return channel; 88 | } 89 | 90 | public void setChannel(RefundChannel channel) { 91 | this.channel = channel; 92 | } 93 | 94 | public Integer getRefundFee() { 95 | return refundFee; 96 | } 97 | 98 | public void setRefundFee(Integer refundFee) { 99 | this.refundFee = refundFee; 100 | } 101 | 102 | public Integer getSettlementRefundFee() { 103 | return settlementRefundFee; 104 | } 105 | 106 | public void setSettlementRefundFee(Integer settlementRefundFee) { 107 | this.settlementRefundFee = settlementRefundFee; 108 | } 109 | 110 | public RefundStatus getRefundStatus() { 111 | return refundStatus; 112 | } 113 | 114 | public void setRefundStatus(RefundStatus refundStatus) { 115 | this.refundStatus = refundStatus; 116 | } 117 | 118 | public String getRefundRecvAccout() { 119 | return refundRecvAccout; 120 | } 121 | 122 | public void setRefundRecvAccout(String refundRecvAccout) { 123 | this.refundRecvAccout = refundRecvAccout; 124 | } 125 | 126 | public Integer getCouponRefundFee() { 127 | return couponRefundFee; 128 | } 129 | 130 | public void setCouponRefundFee(Integer couponRefundFee) { 131 | this.couponRefundFee = couponRefundFee; 132 | } 133 | 134 | public List getCoupons() { 135 | return coupons; 136 | } 137 | 138 | public void setCoupons(List coupons) { 139 | this.coupons = coupons; 140 | } 141 | 142 | @Override 143 | public String toString() { 144 | return "RefundItem{" + 145 | "outRefundNo='" + outRefundNo + '\'' + 146 | ", refundId='" + refundId + '\'' + 147 | ", channel=" + channel + 148 | ", refundFee=" + refundFee + 149 | ", settlementRefundFee=" + settlementRefundFee + 150 | ", refundStatus=" + refundStatus + 151 | ", refundRecvAccout='" + refundRecvAccout + '\'' + 152 | ", couponRefundFee=" + couponRefundFee + 153 | ", coupons=" + coupons + 154 | '}'; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/model/refund/RefundQueryResponse.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.model.refund; 2 | 3 | import me.hao0.wepay.model.enums.FeeType; 4 | 5 | import java.io.Serializable; 6 | import java.util.List; 7 | 8 | /** 9 | * 退款查询对象 10 | * Author: haolin 11 | * Email: haolin.h0@gmail.com 12 | * Date: 1/12/15 13 | * @since 1.0.0 14 | */ 15 | public class RefundQueryResponse implements Serializable { 16 | 17 | private static final long serialVersionUID = -3559898607397949643L; 18 | 19 | /** 20 | * 微信订单号 21 | */ 22 | private String transactionId; 23 | 24 | /** 25 | * 商户订单号 26 | */ 27 | private String outTradeNo; 28 | 29 | /** 30 | * 总金额 31 | */ 32 | private Integer totalFee; 33 | 34 | /** 35 | * 货币类型 36 | */ 37 | private FeeType feeType; 38 | 39 | /** 40 | * 现金支付金额 41 | */ 42 | private Integer cashFee; 43 | 44 | /** 45 | * 退款项 46 | */ 47 | private List items; 48 | 49 | public String getTransactionId() { 50 | return transactionId; 51 | } 52 | 53 | public void setTransactionId(String transactionId) { 54 | this.transactionId = transactionId; 55 | } 56 | 57 | public String getOutTradeNo() { 58 | return outTradeNo; 59 | } 60 | 61 | public void setOutTradeNo(String outTradeNo) { 62 | this.outTradeNo = outTradeNo; 63 | } 64 | 65 | public Integer getTotalFee() { 66 | return totalFee; 67 | } 68 | 69 | public void setTotalFee(Integer totalFee) { 70 | this.totalFee = totalFee; 71 | } 72 | 73 | public FeeType getFeeType() { 74 | return feeType; 75 | } 76 | 77 | public void setFeeType(FeeType feeType) { 78 | this.feeType = feeType; 79 | } 80 | 81 | public Integer getCashFee() { 82 | return cashFee; 83 | } 84 | 85 | public void setCashFee(Integer cashFee) { 86 | this.cashFee = cashFee; 87 | } 88 | 89 | public List getItems() { 90 | return items; 91 | } 92 | 93 | public void setItems(List items) { 94 | this.items = items; 95 | } 96 | 97 | @Override 98 | public String toString() { 99 | return "RefundQueryResponse{" + 100 | "transactionId='" + transactionId + '\'' + 101 | ", outTradeNo='" + outTradeNo + '\'' + 102 | ", totalFee=" + totalFee + 103 | ", feeType=" + feeType + 104 | ", cashFee=" + cashFee + 105 | ", items=" + items + 106 | '}'; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/serializer/BooleanDeserializer.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.serializer; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.databind.DeserializationContext; 5 | import com.fasterxml.jackson.databind.JsonDeserializer; 6 | 7 | import java.io.IOException; 8 | 9 | /** 10 | * 布尔反序列化器 11 | * Author: haolin 12 | * Email: haolin.h0@gmail.com 13 | * Date: 27/11/15 14 | * @since 1.0.0 15 | */ 16 | public class BooleanDeserializer extends JsonDeserializer { 17 | 18 | @Override 19 | public Boolean deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { 20 | String r = jp.getValueAsString(); 21 | return "Y".equals(r); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/serializer/DateDeserializer.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.serializer; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.databind.DeserializationContext; 5 | import com.fasterxml.jackson.databind.JsonDeserializer; 6 | import me.hao0.common.date.Dates; 7 | 8 | import java.io.IOException; 9 | import java.util.Date; 10 | 11 | /** 12 | * 日期反序列化器 13 | */ 14 | public class DateDeserializer extends JsonDeserializer { 15 | 16 | @Override 17 | public Date deserialize(JsonParser parser, DeserializationContext context) 18 | throws IOException { 19 | return Dates.toDate(parser.getValueAsString(), "yyyyMMddHHmmss"); 20 | } 21 | } -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/serializer/FeeTypeDeserializer.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.serializer; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.DeserializationContext; 6 | import com.fasterxml.jackson.databind.JsonDeserializer; 7 | import me.hao0.wepay.model.enums.FeeType; 8 | import java.io.IOException; 9 | 10 | /** 11 | * 交易类型反序列化器 12 | * Author: haolin 13 | * Email: haolin.h0@gmail.com 14 | * Date: 27/11/15 15 | * @since 1.0.0 16 | */ 17 | public class FeeTypeDeserializer extends JsonDeserializer { 18 | 19 | @Override 20 | public FeeType deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { 21 | return FeeType.from(jp.getValueAsString()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/serializer/RefundChannelDeserializer.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.serializer; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.databind.DeserializationContext; 5 | import com.fasterxml.jackson.databind.JsonDeserializer; 6 | import me.hao0.common.util.Strings; 7 | import me.hao0.wepay.model.enums.RefundChannel; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * 退款渠道反序列化器 13 | * Author: haolin 14 | * Email: haolin.h0@gmail.com 15 | * Date: 27/11/15 16 | * @since 1.0.0 17 | */ 18 | public class RefundChannelDeserializer extends JsonDeserializer { 19 | 20 | @Override 21 | public RefundChannel deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { 22 | String val = jp.getValueAsString(); 23 | if (Strings.isNullOrEmpty(val)){ 24 | return null; 25 | } 26 | return RefundChannel.from(val); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/serializer/TradeStateDeserializer.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.serializer; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.DeserializationContext; 6 | import com.fasterxml.jackson.databind.JsonDeserializer; 7 | import me.hao0.wepay.model.enums.TradeState; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * 交易状态反序列化器 13 | * Author: haolin 14 | * Email: haolin.h0@gmail.com 15 | * Date: 27/11/15 16 | * @since 1.0.0 17 | */ 18 | public class TradeStateDeserializer extends JsonDeserializer { 19 | 20 | @Override 21 | public TradeState deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { 22 | return TradeState.from(jp.getValueAsString()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/serializer/TradeTypeDeserializer.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.serializer; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.DeserializationContext; 6 | import com.fasterxml.jackson.databind.JsonDeserializer; 7 | import me.hao0.wepay.model.enums.TradeType; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * 交易类型反序列化器 13 | * Author: haolin 14 | * Email: haolin.h0@gmail.com 15 | * Date: 27/11/15 16 | * @since 1.0.0 17 | */ 18 | public class TradeTypeDeserializer extends JsonDeserializer { 19 | 20 | @Override 21 | public TradeType deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { 22 | return TradeType.from(jp.getValueAsString()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/util/Maps.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.util; 2 | 3 | import me.hao0.common.util.Strings; 4 | import me.hao0.common.xml.XmlReaders; 5 | import me.hao0.common.xml.XmlWriters; 6 | import org.w3c.dom.Node; 7 | import org.w3c.dom.NodeList; 8 | import java.util.Collections; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | /** 13 | * xml和map转换工具 14 | * Author: haolin 15 | * Email: haolin.h0@gmail.com 16 | * Date: 3/12/15 17 | * @since 1.0.0 18 | */ 19 | public final class Maps { 20 | 21 | /** 22 | * 转换微信XML为Map(仅支持2级) 23 | * @param xml xml内容 24 | * @return Map对象 25 | */ 26 | public static Map toMap(String xml){ 27 | xml = xml.replaceAll("(\\r|\\n)", ""); 28 | if (Strings.isNullOrEmpty(xml)){ 29 | return Collections.emptyMap(); 30 | } 31 | XmlReaders readers = XmlReaders.create(xml); 32 | return toMap(readers); 33 | } 34 | 35 | /** 36 | * 转换微信XML为Map(仅支持2级) 37 | * @param readers xmlReaders 38 | * @return Map对象 39 | */ 40 | public static Map toMap(XmlReaders readers){ 41 | Node root = readers.getNode("xml"); 42 | if (root == null){ 43 | return Collections.emptyMap(); 44 | } 45 | NodeList children = root.getChildNodes(); 46 | if (children.getLength() == 0){ 47 | return Collections.emptyMap(); 48 | } 49 | Map data = new HashMap<>(children.getLength()); 50 | Node n; 51 | for (int i = 0; i params) { 64 | XmlWriters writers = XmlWriters.create(); 65 | for (Map.Entry param : params.entrySet()){ 66 | if (!Strings.isNullOrEmpty(param.getValue())){ 67 | writers.element(param.getKey(), param.getValue()); 68 | } 69 | } 70 | return writers.build(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /wepay-core/src/main/java/me/hao0/wepay/util/RandomStrs.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.util; 2 | 3 | import java.util.Random; 4 | 5 | /** 6 | * 随机字符串工具 7 | * Author: haolin 8 | * Email: haolin.h0@gmail.com 9 | * Date: 26/11/15 10 | * @since 1.0.0 11 | */ 12 | public final class RandomStrs { 13 | 14 | private static final String seed = "abcdefghijklmnopqrstuvwxyz0123456789"; 15 | 16 | private static final Random random = new Random(); 17 | 18 | /** 19 | * 生成随机字符串 20 | * @param length 长度 21 | * @return 长度为length的字符串 22 | */ 23 | public static String generate(Integer length) { 24 | StringBuilder sb = new StringBuilder(); 25 | for (int i = 0; i < length; i++) { 26 | int number = random.nextInt(seed.length()); 27 | sb.append(seed.charAt(number)); 28 | } 29 | return sb.toString(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /wepay-core/src/test/java/me/hao0/wepay/WepayTest.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay; 2 | 3 | import me.hao0.common.date.Dates; 4 | import me.hao0.wepay.core.Wepay; 5 | import me.hao0.wepay.core.WepayBuilder; 6 | import me.hao0.wepay.model.bill.Bill; 7 | import me.hao0.wepay.model.bill.BillDetail; 8 | import me.hao0.wepay.model.bill.CommonBill; 9 | import me.hao0.wepay.model.bill.RefundBill; 10 | import me.hao0.wepay.model.order.WePayOrder; 11 | import me.hao0.wepay.model.pay.AppPayResponse; 12 | import me.hao0.wepay.model.pay.JsPayRequest; 13 | import me.hao0.wepay.model.pay.JsPayResponse; 14 | import me.hao0.wepay.model.pay.PayRequest; 15 | import me.hao0.wepay.model.pay.QrPayRequest; 16 | import me.hao0.wepay.model.pay.QrPayResponse; 17 | import me.hao0.wepay.model.refund.RefundApplyRequest; 18 | import me.hao0.wepay.model.refund.RefundApplyResponse; 19 | import me.hao0.wepay.model.refund.RefundQueryResponse; 20 | import org.junit.Before; 21 | import org.junit.Test; 22 | import java.io.IOException; 23 | import java.io.InputStream; 24 | import java.nio.file.Files; 25 | import java.nio.file.Path; 26 | import java.nio.file.Paths; 27 | import java.util.Properties; 28 | import static org.junit.Assert.*; 29 | 30 | /** 31 | * Author: haolin 32 | * Email: haolin.h0@gmail.com 33 | * Date: 25/11/15 34 | */ 35 | public class WepayTest { 36 | 37 | private Wepay wepay; 38 | 39 | private String openId = "onN_8trIW7PSoXLMzMSWySb5jfdY"; 40 | 41 | @Before 42 | public void init() throws IOException { 43 | Properties props = new Properties(); 44 | InputStream in = Object.class.getResourceAsStream("/dev.properties"); 45 | props.load(in); 46 | in.close(); 47 | 48 | Path path = Paths.get("/Users/haolin/GitHub/wepay/wepay-core/src/test/resources/cert.p12"); 49 | byte[] data = Files.readAllBytes(path); 50 | 51 | wepay = WepayBuilder.newBuilder( 52 | props.getProperty("appId"), 53 | props.getProperty("appKey"), 54 | props.getProperty("mchId")) 55 | //.certPasswd(props.getProperty("mchId")) 56 | //.certs(data) 57 | .build(); 58 | 59 | 60 | } 61 | 62 | @Test 63 | public void testJsPay(){ 64 | JsPayRequest request = new JsPayRequest(); 65 | request.setBody("测试订单"); 66 | request.setClientIp("127.0.0.1"); 67 | request.setTotalFee(1); 68 | request.setNotifyUrl("http://www.xxx.com/notify"); 69 | request.setOpenId(openId); 70 | request.setOutTradeNo("TEST12345678js"); 71 | request.setTimeStart(Dates.now("yyyyMMddHHmmss")); 72 | JsPayResponse resp = wepay.pay().jsPay(request); 73 | assertNotNull(resp); 74 | System.out.println(resp); 75 | } 76 | 77 | @Test 78 | public void testQrPay(){ 79 | QrPayRequest request = new QrPayRequest(); 80 | request.setBody("测试订单"); 81 | request.setClientIp("127.0.0.1"); 82 | request.setTotalFee(1); 83 | request.setNotifyUrl("http://www.xxx.com/notify"); 84 | request.setOutTradeNo("TEST1122334455"); 85 | request.setTimeStart(Dates.now("yyyyMMddHHmmss")); 86 | QrPayResponse resp = wepay.pay().qrPay(request); 87 | assertNotNull(resp); 88 | System.out.println(resp); 89 | } 90 | 91 | @Test 92 | public void testQrPayConvert(){ 93 | QrPayRequest request = new QrPayRequest(); 94 | request.setBody("测试订单"); 95 | request.setClientIp("127.0.0.1"); 96 | request.setTotalFee(1); 97 | request.setNotifyUrl("http://www.xxx.com/notify"); 98 | request.setOutTradeNo("TEST3344520"); 99 | request.setTimeStart(Dates.now("yyyyMMddHHmmss")); 100 | QrPayResponse resp = wepay.pay().qrPay(request, Boolean.TRUE); 101 | assertNotNull(resp); 102 | System.out.println(resp); 103 | } 104 | 105 | @Test 106 | public void testAppPay(){ 107 | PayRequest request = new PayRequest(); 108 | request.setBody("测试订单"); 109 | request.setClientIp("127.0.0.1"); 110 | request.setTotalFee(1); 111 | request.setNotifyUrl("http://www.xxx.com/notify"); 112 | request.setOutTradeNo("TEST12345678app"); 113 | request.setTimeStart(Dates.now("yyyyMMddHHmmss")); 114 | AppPayResponse resp = wepay.pay().appPay(request); 115 | assertNotNull(resp); 116 | System.out.println(resp); 117 | } 118 | 119 | @Test 120 | public void testQueryOrderByOutTradeNo(){ 121 | WePayOrder order = wepay.order().queryByOutTradeNo("TEST3344520"); 122 | assertNotNull(order); 123 | System.out.println(order); 124 | } 125 | 126 | @Test 127 | public void testQueryOrderByTransactionId(){ 128 | WePayOrder order = wepay.order().queryByTransactionId("1000530784201510111158030445"); 129 | assertNotNull(order); 130 | System.out.println(order); 131 | } 132 | 133 | @Test 134 | public void testCloseOrder(){ 135 | assertTrue(wepay.order().closeOrder("TEST12345678")); 136 | } 137 | 138 | @Test 139 | public void testRefundApply(){ 140 | RefundApplyRequest req = new RefundApplyRequest(); 141 | req.setTransactionId("1003110578201512021860142525"); 142 | req.setOutRefundNo("TEST3344520"); 143 | req.setTotalFee(1); 144 | req.setRefundFee(1); 145 | req.setOpUserId(wepay.getMchId()); 146 | 147 | RefundApplyResponse resp = wepay.refund().apply(req); 148 | assertNotNull(resp); 149 | System.out.println(resp); 150 | } 151 | 152 | @Test 153 | public void testRefundQuery(){ 154 | RefundQueryResponse resp = wepay.refund().queryByOutTradeNo("TEST3344556677"); 155 | assertNotNull(resp); 156 | System.out.println(resp); 157 | 158 | wepay.refund().queryByOutRefundNo("TEST3344556677"); 159 | assertNotNull(resp); 160 | System.out.println(resp); 161 | 162 | wepay.refund().queryByTransactionId("1003110578201511281803217943"); 163 | assertNotNull(resp); 164 | System.out.println(resp); 165 | 166 | wepay.refund().queryByRefundId("2003110578201512010090200506"); 167 | assertNotNull(resp); 168 | System.out.println(resp); 169 | 170 | } 171 | 172 | @Test 173 | public void testQueryBill(){ 174 | BillDetail allBill = wepay.bill().queryAll(null, "20151203"); 175 | assertNotNull(allBill); 176 | assertEquals(allBill.getBills().size(), allBill.getCount().getTradeTotalCount().intValue()); 177 | System.out.println(allBill.getBills().get(0)); 178 | System.out.println(allBill.getCount()); 179 | 180 | BillDetail successBill = wepay.bill().querySuccess(null, "20151203"); 181 | assertNotNull(successBill); 182 | assertEquals(successBill.getBills().size(), successBill.getCount().getTradeTotalCount().intValue()); 183 | System.out.println(successBill.getBills().get(0)); 184 | System.out.println(successBill.getCount()); 185 | 186 | BillDetail refundBill = wepay.bill().queryRefund(null, "20151203"); 187 | assertNotNull(refundBill); 188 | assertEquals(refundBill.getBills().size(), refundBill.getCount().getTradeTotalCount().intValue()); 189 | System.out.println(refundBill.getBills().get(0)); 190 | System.out.println(refundBill.getCount()); 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /wepay-demo/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | wepay 5 | me.hao0 6 | 1.3.2 7 | 8 | 4.0.0 9 | wepay-demo 10 | war 11 | wepay-demo Maven Webapp 12 | http://maven.apache.org 13 | 1.3.2 14 | 15 | 16 | 4.1.5.RELEASE 17 | 1.7 18 | 2.5 19 | 8.1.8.v20121106 20 | 21 | 22 | 23 | 24 | 25 | me.hao0 26 | wepay-core 27 | 1.3.2 28 | 29 | 30 | 31 | org.slf4j 32 | slf4j-api 33 | 1.7.2 34 | 35 | 36 | 37 | ch.qos.logback 38 | logback-core 39 | 1.1.2 40 | 41 | 42 | 43 | ch.qos.logback 44 | logback-classic 45 | 1.1.2 46 | 47 | 48 | 49 | com.google.guava 50 | guava 51 | 18.0 52 | 53 | 54 | 55 | javax.servlet 56 | servlet-api 57 | ${servlet.version} 58 | provided 59 | 60 | 61 | 62 | org.springframework 63 | spring-webmvc 64 | ${spring.version} 65 | 66 | 67 | 68 | 69 | wepay-demo 70 | 71 | 72 | 73 | src/main/resources 74 | true 75 | 76 | **/*.p12 77 | 78 | 79 | 80 | src/main/resources 81 | false 82 | 83 | **/*.p12 84 | 85 | 86 | 87 | 88 | 89 | org.apache.maven.plugins 90 | maven-compiler-plugin 91 | 92 | ${compile.version} 93 | ${compile.version} 94 | true 95 | 96 | 97 | 98 | org.mortbay.jetty 99 | jetty-maven-plugin 100 | ${jetty.version} 101 | 102 | 103 | / 104 | 105 | 106 | 107 | 108 | org.apache.maven.plugins 109 | maven-deploy-plugin 110 | 2.8.2 111 | 112 | true 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /wepay-demo/src/main/java/me/hao0/wepay/demo/controller/Notifies.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.demo.controller; 2 | 3 | import com.google.common.base.Throwables; 4 | import me.hao0.wepay.demo.support.WepaySupport; 5 | import me.hao0.wepay.util.Maps; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | import javax.servlet.http.HttpServletRequest; 12 | import java.io.BufferedReader; 13 | import java.io.IOException; 14 | import java.util.Map; 15 | 16 | /** 17 | * 通知 18 | * Author: haolin 19 | * Email: haolin.h0@gmail.com 20 | * Date: 2/12/15 21 | */ 22 | @RestController 23 | @RequestMapping("/notifies") 24 | public class Notifies { 25 | 26 | private static final Logger logger = LoggerFactory.getLogger(Notifies.class); 27 | 28 | @Autowired 29 | private WepaySupport wepaySupport; 30 | 31 | /** 32 | * 支付成功通知 33 | * @param request 请求对象 34 | * @return 处理结果 35 | */ 36 | @RequestMapping("/paid") 37 | public String paid(HttpServletRequest request){ 38 | 39 | String notifyXml = getPostRequestBody(request); 40 | if (notifyXml.isEmpty()){ 41 | return wepaySupport.notifyNotOk("body为空"); 42 | } 43 | 44 | Map notifyParams = Maps.toMap(notifyXml); 45 | 46 | if (wepaySupport.verifySign(notifyParams)){ 47 | 48 | // TODO business logic 49 | 50 | logger.info("verify sign success: {}", notifyParams); 51 | 52 | return wepaySupport.notifyOk(); 53 | } else { 54 | 55 | logger.error("verify sign failed: {}", notifyParams); 56 | return wepaySupport.notifyNotOk("签名失败"); 57 | } 58 | } 59 | 60 | public static String getPostRequestBody(HttpServletRequest req) { 61 | if (req.getMethod().equals("POST")) { 62 | StringBuilder sb = new StringBuilder(); 63 | try (BufferedReader br = req.getReader()) { 64 | char[] charBuffer = new char[128]; 65 | int bytesRead; 66 | while ((bytesRead = br.read(charBuffer)) != -1) { 67 | sb.append(charBuffer, 0, bytesRead); 68 | } 69 | } catch (IOException e) { 70 | logger.warn("failed to read request body, cause: {}", Throwables.getStackTraceAsString(e)); 71 | } 72 | return sb.toString(); 73 | } 74 | return ""; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /wepay-demo/src/main/java/me/hao0/wepay/demo/controller/Pays.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.demo.controller; 2 | 3 | import me.hao0.wepay.demo.support.WepaySupport; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Controller; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RequestParam; 10 | import javax.servlet.http.HttpServletResponse; 11 | import java.io.IOException; 12 | 13 | /** 14 | * 支付 15 | * Author: haolin 16 | * Email: haolin.h0@gmail.com 17 | * Date: 2/12/15 18 | */ 19 | @Controller 20 | @RequestMapping("/pays/") 21 | public class Pays { 22 | 23 | private static final Logger log = LoggerFactory.getLogger(Pays.class); 24 | 25 | @Autowired 26 | private WepaySupport wepaySupport; 27 | 28 | /** 29 | * 二维码支付 30 | * @param orderNumber 商户订单号 31 | */ 32 | @RequestMapping(value = "/qrpay") 33 | public void qrPay( 34 | @RequestParam("orderNumber") String orderNumber, 35 | HttpServletResponse response){ 36 | try { 37 | String qrUrl = wepaySupport.qrPay(orderNumber); 38 | response.sendRedirect(qrUrl); 39 | } catch (IOException e) { 40 | log.error("failed to qr pay(orderNumber={}), cause: {}", 41 | orderNumber, e.getMessage()); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /wepay-demo/src/main/java/me/hao0/wepay/demo/controller/Refunds.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.demo.controller; 2 | 3 | import me.hao0.wepay.demo.support.WepaySupport; 4 | import me.hao0.wepay.model.refund.RefundApplyResponse; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Controller; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RequestParam; 9 | import org.springframework.web.bind.annotation.ResponseBody; 10 | 11 | /** 12 | * Author: haolin 13 | * Email: haolin.h0@gmail.com 14 | * Date: 3/12/15 15 | */ 16 | @Controller 17 | @RequestMapping("/refunds") 18 | public class Refunds { 19 | 20 | @Autowired 21 | private WepaySupport wepaySupport; 22 | 23 | /** 24 | * 退款申请 25 | * @param orderNumber 商户订单号 26 | */ 27 | @RequestMapping("/apply") 28 | @ResponseBody 29 | public RefundApplyResponse apply(@RequestParam("orderNumber") String orderNumber){ 30 | return wepaySupport.refundApply(orderNumber); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /wepay-demo/src/main/java/me/hao0/wepay/demo/support/WepaySupport.java: -------------------------------------------------------------------------------- 1 | package me.hao0.wepay.demo.support; 2 | 3 | import com.google.common.io.ByteStreams; 4 | import me.hao0.common.date.Dates; 5 | import me.hao0.wepay.core.Wepay; 6 | import me.hao0.wepay.core.WepayBuilder; 7 | import me.hao0.wepay.model.pay.QrPayRequest; 8 | import me.hao0.wepay.model.refund.RefundApplyRequest; 9 | import me.hao0.wepay.model.refund.RefundApplyResponse; 10 | import org.springframework.beans.factory.annotation.Value; 11 | import org.springframework.stereotype.Component; 12 | import javax.annotation.PostConstruct; 13 | import java.io.InputStream; 14 | import java.util.Map; 15 | 16 | /** 17 | * Author: haolin 18 | * Email: haolin.h0@gmail.com 19 | * Date: 2/12/15 20 | */ 21 | @Component 22 | public class WepaySupport { 23 | 24 | @Value("${appId}") 25 | private String appId; 26 | 27 | @Value("${appKey}") 28 | private String appKey; 29 | 30 | @Value("${mchId}") 31 | private String mchId; 32 | 33 | @Value("${payNotifyUrl}") 34 | private String payNotifyUrl; 35 | 36 | private Wepay wepay; 37 | 38 | @PostConstruct 39 | public void initWepay() { 40 | try(InputStream in = this.getClass().getClassLoader().getResourceAsStream("cert.p12")) { 41 | // 加载证书文件 42 | byte[] certs = ByteStreams.toByteArray(in); 43 | wepay = WepayBuilder.newBuilder(appId, appKey, mchId) 44 | .certPasswd(mchId) 45 | .certs(certs) 46 | .build(); 47 | } catch (Exception e) { 48 | throw new RuntimeException(e); 49 | } 50 | } 51 | 52 | /** 53 | * 动态二维码支付 54 | * @param orderNumber 订单号 55 | * @return 二维码链接 56 | */ 57 | public String qrPay(String orderNumber){ 58 | QrPayRequest request = new QrPayRequest(); 59 | request.setBody("测试订单"); 60 | request.setClientIp("127.0.0.1"); 61 | request.setTotalFee(1); 62 | request.setNotifyUrl(payNotifyUrl); 63 | request.setOutTradeNo(orderNumber); 64 | request.setTimeStart(Dates.now("yyyyMMddHHmmss")); 65 | return wepay.pay().qrPay(request).getCodeUrl(); 66 | } 67 | 68 | /** 69 | * 校验签名 70 | * @param params 参数(包含sign) 71 | * @return 校验成功返回true,反之false 72 | */ 73 | public Boolean verifySign(Map params){ 74 | return wepay.notifies().verifySign(params); 75 | } 76 | 77 | /** 78 | * 通知成功 79 | */ 80 | public String notifyOk(){ 81 | return wepay.notifies().ok(); 82 | } 83 | 84 | /** 85 | * 通知不成功 86 | * @param errMsg 错误消息 87 | */ 88 | public String notifyNotOk(String errMsg){ 89 | return wepay.notifies().notOk(errMsg); 90 | } 91 | 92 | public RefundApplyResponse refundApply(String orderNumber){ 93 | RefundApplyRequest req = new RefundApplyRequest(); 94 | req.setOutTradeNo(orderNumber); 95 | req.setOutRefundNo(orderNumber); 96 | req.setTotalFee(1); 97 | req.setRefundFee(1); 98 | req.setOpUserId(wepay.getMchId()); 99 | return wepay.refund().apply(req); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /wepay-demo/src/main/resources/app-example.properties: -------------------------------------------------------------------------------- 1 | appId= 2 | appKey= 3 | mchId= 4 | payNotifyUrl=${your_domain}/notifies/paid -------------------------------------------------------------------------------- /wepay-demo/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %date{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /wepay-demo/src/main/resources/spring/root-context.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | classpath*:/app.properties 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /wepay-demo/src/main/resources/spring/web-context.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /wepay-demo/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | wepay-demo 7 | 8 | 9 | contextConfigLocation 10 | 11 | classpath*:/spring/root-context.xml 12 | 13 | 14 | 15 | 16 | defaultHtmlEscape 17 | true 18 | 19 | 20 | 21 | org.springframework.web.context.ContextLoaderListener 22 | 23 | 24 | 25 | DispatcherServlet 26 | org.springframework.web.servlet.DispatcherServlet 27 | 28 | contextConfigLocation 29 | classpath*:/spring/web-context.xml 30 | 31 | 1 32 | 33 | 34 | DispatcherServlet 35 | / 36 | 37 | 38 | 39 | encodingFilter 40 | org.springframework.web.filter.CharacterEncodingFilter 41 | 42 | encoding 43 | UTF-8 44 | 45 | 46 | forceEncoding 47 | true 48 | 49 | 50 | 51 | encodingFilter 52 | /* 53 | 54 | 55 | --------------------------------------------------------------------------------