| @@ -86,6 +86,7 @@ | |||
| <module>weixin-java-common</module> | |||
| <module>weixin-java-cp</module> | |||
| <module>weixin-java-mp</module> | |||
| <module>weixin-java-pay</module> | |||
| <module>weixin-java-osgi</module> | |||
| </modules> | |||
| @@ -18,21 +18,11 @@ | |||
| <artifactId>weixin-java-common</artifactId> | |||
| <version>${project.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>junit</groupId> | |||
| <artifactId>junit</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.testng</groupId> | |||
| <artifactId>testng</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.mockito</groupId> | |||
| <artifactId>mockito-all</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.google.inject</groupId> | |||
| <artifactId>guice</artifactId> | |||
| @@ -54,16 +44,6 @@ | |||
| <version>2.9.4</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>io.rest-assured</groupId> | |||
| <artifactId>xml-path</artifactId> | |||
| <version>3.0.1</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>qrcode-utils</artifactId> | |||
| <version>1.0</version> | |||
| </dependency> | |||
| </dependencies> | |||
| <build> | |||
| @@ -3,7 +3,6 @@ package me.chanjar.weixin.mp.api; | |||
| import me.chanjar.weixin.common.bean.WxAccessToken; | |||
| import me.chanjar.weixin.common.util.http.ApacheHttpClientBuilder; | |||
| import javax.net.ssl.SSLContext; | |||
| import java.io.File; | |||
| import java.util.concurrent.locks.Lock; | |||
| @@ -80,39 +79,8 @@ public interface WxMpConfigStorage { | |||
| String getAppId(); | |||
| /** | |||
| * 服务商模式下的子商户公众账号ID | |||
| */ | |||
| String getSubAppId(); | |||
| /** | |||
| * 服务商模式下的子商户号 | |||
| */ | |||
| String getSubMchId(); | |||
| String getSecret(); | |||
| String getPartnerId(); | |||
| String getPartnerKey(); | |||
| /** | |||
| * 微信支付异步回掉地址,通知url必须为直接可访问的url,不能携带参数。 | |||
| * | |||
| * @since 2.5.0 | |||
| */ | |||
| String getNotifyURL(); | |||
| /** | |||
| * 交易类型 | |||
| * <pre> | |||
| * JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付 | |||
| * </pre> | |||
| * | |||
| * @since 2.5.0 | |||
| */ | |||
| String getTradeType(); | |||
| String getToken(); | |||
| String getAesKey(); | |||
| @@ -131,17 +99,6 @@ public interface WxMpConfigStorage { | |||
| File getTmpDirFile(); | |||
| SSLContext getSslContext(); | |||
| void setSslContext(SSLContext sslContext); | |||
| /** | |||
| * 在此之前,必须将partnerId进行赋值 | |||
| * | |||
| * @param filePath apiclient_cert.p12的文件的绝对路径 | |||
| */ | |||
| void setSslContextFilePath(String filePath) throws Exception; | |||
| /** | |||
| * http client builder | |||
| * | |||
| @@ -154,8 +111,4 @@ public interface WxMpConfigStorage { | |||
| */ | |||
| boolean autoRefreshToken(); | |||
| /** | |||
| * 微信支付是否使用仿真测试环境 | |||
| */ | |||
| boolean useSandboxForWxPay(); | |||
| } | |||
| @@ -3,12 +3,8 @@ package me.chanjar.weixin.mp.api; | |||
| import me.chanjar.weixin.common.bean.WxAccessToken; | |||
| import me.chanjar.weixin.common.util.ToStringUtils; | |||
| import me.chanjar.weixin.common.util.http.ApacheHttpClientBuilder; | |||
| import org.apache.http.ssl.SSLContexts; | |||
| import javax.net.ssl.SSLContext; | |||
| import java.io.File; | |||
| import java.io.FileInputStream; | |||
| import java.security.KeyStore; | |||
| import java.util.concurrent.locks.Lock; | |||
| import java.util.concurrent.locks.ReentrantLock; | |||
| @@ -20,13 +16,7 @@ import java.util.concurrent.locks.ReentrantLock; | |||
| public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||
| protected volatile String appId; | |||
| protected volatile String subAppId; | |||
| protected volatile String subMchId; | |||
| protected volatile String secret; | |||
| protected volatile String partnerId; | |||
| protected volatile String partnerKey; | |||
| protected volatile String notifyURL; | |||
| protected volatile String tradeType; | |||
| protected volatile String token; | |||
| protected volatile String accessToken; | |||
| protected volatile String aesKey; | |||
| @@ -54,8 +44,6 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||
| */ | |||
| protected volatile File tmpDirFile; | |||
| protected volatile SSLContext sslContext; | |||
| protected volatile ApacheHttpClientBuilder apacheHttpClientBuilder; | |||
| @Override | |||
| @@ -257,41 +245,6 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||
| return ToStringUtils.toSimpleString(this); | |||
| } | |||
| @Override | |||
| public String getPartnerId() { | |||
| return this.partnerId; | |||
| } | |||
| public void setPartnerId(String partnerId) { | |||
| this.partnerId = partnerId; | |||
| } | |||
| @Override | |||
| public String getPartnerKey() { | |||
| return this.partnerKey; | |||
| } | |||
| public void setPartnerKey(String partnerKey) { | |||
| this.partnerKey = partnerKey; | |||
| } | |||
| public String getNotifyURL() { | |||
| return notifyURL; | |||
| } | |||
| public void setNotifyURL(String notifyURL) { | |||
| this.notifyURL = notifyURL; | |||
| } | |||
| public String getTradeType() { | |||
| return tradeType; | |||
| } | |||
| public void setTradeType(String tradeType) { | |||
| this.tradeType = tradeType; | |||
| } | |||
| @Override | |||
| public File getTmpDirFile() { | |||
| return this.tmpDirFile; | |||
| @@ -301,38 +254,6 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||
| this.tmpDirFile = tmpDirFile; | |||
| } | |||
| @Override | |||
| public SSLContext getSslContext() { | |||
| return this.sslContext; | |||
| } | |||
| @Override | |||
| public void setSslContext(SSLContext context) { | |||
| this.sslContext = context; | |||
| } | |||
| @Override | |||
| public void setSslContextFilePath(String filePath) { | |||
| if (null == partnerId) { | |||
| throw new IllegalArgumentException("请设置partnerId的值"); | |||
| } | |||
| File file = new File(filePath); | |||
| if (!file.exists()) { | |||
| throw new RuntimeException("证书文件:【" + file.getPath() + "】不存在!"); | |||
| } | |||
| try { | |||
| FileInputStream inputStream = new FileInputStream(file); | |||
| KeyStore keystore = KeyStore.getInstance("PKCS12"); | |||
| char[] partnerId2charArray = partnerId.toCharArray(); | |||
| keystore.load(inputStream, partnerId2charArray); | |||
| this.sslContext = SSLContexts.custom().loadKeyMaterial(keystore, partnerId2charArray).build(); | |||
| } catch (Exception e) { | |||
| throw new RuntimeException("证书文件有问题,请核实!", e); | |||
| } | |||
| } | |||
| @Override | |||
| public ApacheHttpClientBuilder getApacheHttpClientBuilder() { | |||
| return this.apacheHttpClientBuilder; | |||
| @@ -347,26 +268,4 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||
| return true; | |||
| } | |||
| @Override | |||
| public boolean useSandboxForWxPay() { | |||
| return false; | |||
| } | |||
| @Override | |||
| public String getSubAppId() { | |||
| return subAppId; | |||
| } | |||
| public void setSubAppId(String subAppId) { | |||
| this.subAppId = subAppId; | |||
| } | |||
| @Override | |||
| public String getSubMchId() { | |||
| return subMchId; | |||
| } | |||
| public void setSubMchId(String subMchId) { | |||
| this.subMchId = subMchId; | |||
| } | |||
| } | |||
| @@ -311,13 +311,6 @@ public interface WxMpService { | |||
| */ | |||
| WxMpCardService getCardService(); | |||
| /** | |||
| * 返回微信支付相关接口方法的实现类对象,以方便调用其各个接口 | |||
| * | |||
| * @return WxMpPayService | |||
| */ | |||
| WxMpPayService getPayService(); | |||
| /** | |||
| * 返回数据分析统计相关接口方法的实现类对象,以方便调用其各个接口 | |||
| * | |||
| @@ -45,7 +45,6 @@ public class WxMpServiceImpl implements WxMpService { | |||
| private WxMpUserTagService tagService = new WxMpUserTagServiceImpl(this); | |||
| private WxMpQrcodeService qrCodeService = new WxMpQrcodeServiceImpl(this); | |||
| private WxMpCardService cardService = new WxMpCardServiceImpl(this); | |||
| private WxMpPayService payService = new WxMpPayServiceImpl(this); | |||
| private WxMpStoreService storeService = new WxMpStoreServiceImpl(this); | |||
| private WxMpDataCubeService dataCubeService = new WxMpDataCubeServiceImpl(this); | |||
| private WxMpUserBlacklistService blackListService = new WxMpUserBlacklistServiceImpl(this); | |||
| @@ -505,11 +504,6 @@ public class WxMpServiceImpl implements WxMpService { | |||
| return this.cardService; | |||
| } | |||
| @Override | |||
| public WxMpPayService getPayService() { | |||
| return this.payService; | |||
| } | |||
| @Override | |||
| public WxMpDataCubeService getDataCubeService() { | |||
| return this.dataCubeService; | |||
| @@ -1,392 +0,0 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.result; | |||
| import com.thoughtworks.xstream.XStream; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import me.chanjar.weixin.common.util.BeanUtils; | |||
| import me.chanjar.weixin.common.util.ToStringUtils; | |||
| import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||
| import me.chanjar.weixin.mp.bean.pay.WxPayOrderNotifyCoupon; | |||
| import me.chanjar.weixin.mp.bean.pay.WxPayOrderNotifyResultConverter; | |||
| import java.io.Serializable; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| /** | |||
| * 支付结果通用通知 ,文档见:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7 | |||
| * @author aimilin6688 | |||
| * @since 2.5.0 | |||
| */ | |||
| @XStreamAlias("xml") | |||
| public class WxPayOrderNotifyResult extends WxPayBaseResult implements Serializable { | |||
| private static final long serialVersionUID = 5389718115223345496L; | |||
| /** | |||
| * <pre> | |||
| * 设备号 | |||
| * device_info | |||
| * 否 | |||
| * String(32) | |||
| * 013467007045764 | |||
| * 微信支付分配的终端设备号, | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("device_info") | |||
| private String deviceInfo; | |||
| /** | |||
| * <pre> | |||
| * 用户标识 | |||
| * openid | |||
| * 是 | |||
| * String(128) | |||
| * wxd930ea5d5a258f4f | |||
| * 用户在商户appid下的唯一标识 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("openid") | |||
| private String openid; | |||
| /** | |||
| * <pre> | |||
| * 是否关注公众账号 | |||
| * is_subscribe | |||
| * 否 | |||
| * String(1) | |||
| * Y | |||
| * 用户是否关注公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("is_subscribe") | |||
| private String isSubscribe; | |||
| /** | |||
| * <pre> | |||
| * 交易类型 | |||
| * trade_type | |||
| * 是 | |||
| * String(16) | |||
| * JSAPI JSAPI、NATIVE、APP | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("trade_type") | |||
| private String tradeType; | |||
| /** | |||
| * <pre> | |||
| * 付款银行 | |||
| * bank_type | |||
| * 是 | |||
| * String(16) | |||
| * CMC | |||
| * 银行类型,采用字符串类型的银行标识,银行类型见银行列表 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("bank_type") | |||
| private String bankType; | |||
| /** | |||
| * <pre> | |||
| * 订单金额 | |||
| * total_fee | |||
| * 是 | |||
| * Int | |||
| * 100 | |||
| * 订单总金额,单位为分 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("total_fee") | |||
| private Integer totalFee; | |||
| /** | |||
| * <pre> | |||
| * 应结订单金额 | |||
| * settlement_total_fee | |||
| * 否 | |||
| * Int | |||
| * 100 | |||
| * 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("settlement_total_fee") | |||
| private Integer settlementTotalFee; | |||
| /** | |||
| * <pre> | |||
| * 货币种类 | |||
| * fee_type | |||
| * 否 | |||
| * String(8) | |||
| * CNY | |||
| * 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("fee_type") | |||
| private String feeType; | |||
| /** | |||
| * <pre> | |||
| * 现金支付金额 | |||
| * cash_fee | |||
| * 是 | |||
| * Int | |||
| * 100 | |||
| * 现金支付金额订单现金支付金额,详见支付金额 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("cash_fee") | |||
| private Integer cashFee; | |||
| /** | |||
| * <pre> | |||
| * 现金支付货币类型 | |||
| * cash_fee_type | |||
| * 否 | |||
| * String(16) | |||
| * CNY | |||
| * 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("cash_fee_type") | |||
| private String cashFeeType; | |||
| /** | |||
| * <pre> | |||
| * 总代金券金额 | |||
| * coupon_fee | |||
| * 否 | |||
| * Int | |||
| * 10 | |||
| * 代金券金额<=订单金额,订单金额-代金券金额=现金支付金额,详见支付金额 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("coupon_fee") | |||
| private Integer couponFee; | |||
| /** | |||
| * <pre> | |||
| * 代金券使用数量 | |||
| * coupon_count | |||
| * 否 | |||
| * Int | |||
| * 1 | |||
| * 代金券使用数量 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("coupon_count") | |||
| private Integer couponCount; | |||
| private List<WxPayOrderNotifyCoupon> couponList; | |||
| /** | |||
| * <pre> | |||
| * 微信支付订单号 | |||
| * transaction_id | |||
| * 是 | |||
| * String(32) | |||
| * 1217752501201407033233368018 | |||
| * 微信支付订单号 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("transaction_id") | |||
| private String transactionId; | |||
| /** | |||
| * <pre> | |||
| * 商户订单号 | |||
| * out_trade_no | |||
| * 是 | |||
| * String(32) | |||
| * 1212321211201407033568112322 | |||
| * 商户系统的订单号,与请求一致。 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("out_trade_no") | |||
| private String outTradeNo; | |||
| /** | |||
| * <pre> | |||
| * 商家数据包 | |||
| * attach | |||
| * 否 | |||
| * String(128) | |||
| * 123456 | |||
| * 商家数据包,原样返回 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("attach") | |||
| private String attach; | |||
| /** | |||
| * <pre> | |||
| * 支付完成时间 | |||
| * time_end | |||
| * 是 | |||
| * String(14) | |||
| * 20141030133525 | |||
| * 支付完成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("time_end") | |||
| private String timeEnd; | |||
| public Integer getCouponCount() { | |||
| return couponCount; | |||
| } | |||
| public void setCouponCount(Integer couponCount) { | |||
| this.couponCount = couponCount; | |||
| } | |||
| public List<WxPayOrderNotifyCoupon> getCouponList() { | |||
| return couponList; | |||
| } | |||
| public void setCouponList(List<WxPayOrderNotifyCoupon> couponList) { | |||
| this.couponList = couponList; | |||
| } | |||
| public String getDeviceInfo() { | |||
| return deviceInfo; | |||
| } | |||
| public void setDeviceInfo(String deviceInfo) { | |||
| this.deviceInfo = deviceInfo; | |||
| } | |||
| public String getOpenid() { | |||
| return openid; | |||
| } | |||
| public void setOpenid(String openid) { | |||
| this.openid = openid; | |||
| } | |||
| public String getIsSubscribe() { | |||
| return isSubscribe; | |||
| } | |||
| public void setIsSubscribe(String isSubscribe) { | |||
| this.isSubscribe = isSubscribe; | |||
| } | |||
| public String getTradeType() { | |||
| return tradeType; | |||
| } | |||
| public void setTradeType(String tradeType) { | |||
| this.tradeType = tradeType; | |||
| } | |||
| public String getBankType() { | |||
| return bankType; | |||
| } | |||
| public void setBankType(String bankType) { | |||
| this.bankType = bankType; | |||
| } | |||
| public Integer getTotalFee() { | |||
| return totalFee; | |||
| } | |||
| public void setTotalFee(Integer totalFee) { | |||
| this.totalFee = totalFee; | |||
| } | |||
| public Integer getSettlementTotalFee() { | |||
| return settlementTotalFee; | |||
| } | |||
| public void setSettlementTotalFee(Integer settlementTotalFee) { | |||
| this.settlementTotalFee = settlementTotalFee; | |||
| } | |||
| public String getFeeType() { | |||
| return feeType; | |||
| } | |||
| public void setFeeType(String feeType) { | |||
| this.feeType = feeType; | |||
| } | |||
| public Integer getCashFee() { | |||
| return cashFee; | |||
| } | |||
| public void setCashFee(Integer cashFee) { | |||
| this.cashFee = cashFee; | |||
| } | |||
| public String getCashFeeType() { | |||
| return cashFeeType; | |||
| } | |||
| public void setCashFeeType(String cashFeeType) { | |||
| this.cashFeeType = cashFeeType; | |||
| } | |||
| public Integer getCouponFee() { | |||
| return couponFee; | |||
| } | |||
| public void setCouponFee(Integer couponFee) { | |||
| this.couponFee = couponFee; | |||
| } | |||
| public String getTransactionId() { | |||
| return transactionId; | |||
| } | |||
| public void setTransactionId(String transactionId) { | |||
| this.transactionId = transactionId; | |||
| } | |||
| public String getOutTradeNo() { | |||
| return outTradeNo; | |||
| } | |||
| public void setOutTradeNo(String outTradeNo) { | |||
| this.outTradeNo = outTradeNo; | |||
| } | |||
| public String getAttach() { | |||
| return attach; | |||
| } | |||
| public void setAttach(String attach) { | |||
| this.attach = attach; | |||
| } | |||
| public String getTimeEnd() { | |||
| return timeEnd; | |||
| } | |||
| public void setTimeEnd(String timeEnd) { | |||
| this.timeEnd = timeEnd; | |||
| } | |||
| @Override | |||
| public Map<String,String> toMap(){ | |||
| Map<String,String> resultMap = BeanUtils.xmlBean2Map(this); | |||
| if(this.getCouponCount() != null && this.getCouponCount() > 0){ | |||
| for (int i = 0; i < this.getCouponCount(); i++) { | |||
| WxPayOrderNotifyCoupon coupon = couponList.get(i); | |||
| resultMap.putAll(coupon.toMap(i)); | |||
| } | |||
| } | |||
| return resultMap; | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return ToStringUtils.toSimpleString(this); | |||
| } | |||
| public static WxPayOrderNotifyResult fromXML(String xmlString) { | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.processAnnotations(WxPayOrderNotifyResult.class); | |||
| xstream.registerConverter(new WxPayOrderNotifyResultConverter(xstream.getMapper(), xstream.getReflectionProvider())); | |||
| WxPayOrderNotifyResult result = (WxPayOrderNotifyResult) xstream.fromXML(xmlString); | |||
| result.setXmlString(xmlString); | |||
| return result; | |||
| } | |||
| } | |||
| @@ -1,195 +0,0 @@ | |||
| package me.chanjar.weixin.mp.api.impl; | |||
| import com.github.binarywang.utils.qrcode.QrcodeUtils; | |||
| import com.google.inject.Inject; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.mp.api.WxMpService; | |||
| import me.chanjar.weixin.mp.api.test.ApiTestModule; | |||
| import me.chanjar.weixin.mp.api.test.TestConfigStorage; | |||
| import me.chanjar.weixin.mp.bean.pay.request.*; | |||
| import me.chanjar.weixin.mp.bean.pay.result.*; | |||
| import org.testng.annotations.*; | |||
| import java.io.File; | |||
| import java.nio.file.Files; | |||
| import java.nio.file.Path; | |||
| import static org.testng.Assert.*; | |||
| /** | |||
| * 测试支付相关接口 | |||
| * Created by Binary Wang on 2016/7/28. | |||
| * | |||
| * @author binarywang (https://github.com/binarywang) | |||
| */ | |||
| @Test | |||
| @Guice(modules = ApiTestModule.class) | |||
| public class WxMpPayServiceImplTest { | |||
| @Inject | |||
| protected WxMpService wxService; | |||
| @Test | |||
| public void testGetPayInfo() throws Exception { | |||
| } | |||
| @Test | |||
| public void testDownloadBill() throws Exception { | |||
| File file = this.wxService.getPayService().downloadBill("20170101", "ALL", "GZIP", "1111111"); | |||
| assertNotNull(file); | |||
| //必填字段为空时,抛出异常 | |||
| this.wxService.getPayService().downloadBill("", "", "", null); | |||
| } | |||
| @Test | |||
| public void testReport() throws Exception { | |||
| WxPayReportRequest request = new WxPayReportRequest(); | |||
| request.setInterfaceUrl("hahahah"); | |||
| request.setSignType("HMAC-SHA256");//貌似接口未校验此字段 | |||
| request.setExecuteTime(1000); | |||
| request.setReturnCode("aaa"); | |||
| request.setResultCode("aaa"); | |||
| request.setUserIp("8.8.8"); | |||
| this.wxService.getPayService().report(request); | |||
| } | |||
| /** | |||
| * 需要证书的接口需要先执行该方法 | |||
| */ | |||
| @Test | |||
| public void setSSLKey() { | |||
| TestConfigStorage config = (TestConfigStorage) this.wxService.getWxMpConfigStorage(); | |||
| config.setSslContextFilePath(config.getKeyPath()); | |||
| } | |||
| /** | |||
| * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#refund(WxPayRefundRequest)} . | |||
| */ | |||
| @Test(dependsOnMethods = {"setSSLKey"}) | |||
| public void testRefund() throws Exception { | |||
| WxPayRefundRequest request = new WxPayRefundRequest(); | |||
| request.setOutRefundNo("aaa"); | |||
| request.setOutTradeNo("1111"); | |||
| request.setTotalFee(1222); | |||
| request.setRefundFee(111); | |||
| WxPayRefundResult result = this.wxService.getPayService().refund(request); | |||
| System.err.println(result); | |||
| } | |||
| /** | |||
| * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#refundQuery(String, String, String, String)} . | |||
| */ | |||
| @Test | |||
| public void testRefundQuery() throws Exception { | |||
| WxPayRefundQueryResult result; | |||
| result = this.wxService.getPayService().refundQuery("1", "", "", ""); | |||
| System.err.println(result); | |||
| result = this.wxService.getPayService().refundQuery("", "2", "", ""); | |||
| System.err.println(result); | |||
| result = this.wxService.getPayService().refundQuery("", "", "3", ""); | |||
| System.err.println(result); | |||
| result = this.wxService.getPayService().refundQuery("", "", "", "4"); | |||
| System.err.println(result); | |||
| //测试四个参数都填的情况,应该报异常的 | |||
| result = this.wxService.getPayService().refundQuery("1", "2", "3", "4"); | |||
| System.err.println(result); | |||
| } | |||
| /** | |||
| * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#sendRedpack(WxPaySendRedpackRequest)} . | |||
| */ | |||
| @Test(dependsOnMethods = {"setSSLKey"}) | |||
| public void testSendRedpack() throws Exception { | |||
| WxPaySendRedpackRequest request = new WxPaySendRedpackRequest(); | |||
| request.setActName("abc"); | |||
| request.setClientIp("aaa"); | |||
| request.setMchBillNo("aaaa"); | |||
| request | |||
| .setReOpenid(((TestConfigStorage) this.wxService.getWxMpConfigStorage()).getOpenid()); | |||
| WxPaySendRedpackResult redpackResult = this.wxService.getPayService().sendRedpack(request); | |||
| System.err.println(redpackResult); | |||
| } | |||
| /** | |||
| * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#queryRedpack(String)}. | |||
| */ | |||
| @Test(dependsOnMethods = {"setSSLKey"}) | |||
| public void testQueryRedpack() throws Exception { | |||
| WxPayRedpackQueryResult redpackResult = this.wxService.getPayService().queryRedpack("aaaa"); | |||
| System.err.println(redpackResult); | |||
| } | |||
| /** | |||
| * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#unifiedOrder(WxPayUnifiedOrderRequest)}. | |||
| */ | |||
| @Test | |||
| public void testUnifiedOrder() throws WxErrorException { | |||
| WxPayUnifiedOrderResult result = this.wxService.getPayService() | |||
| .unifiedOrder(WxPayUnifiedOrderRequest.builder().body("1111111") | |||
| .totalFee(1).spbillCreateIp("111111").notifyURL("111111") | |||
| .tradeType("JSAPI").openid("122").outTradeNo("111111").build()); | |||
| System.err.println(result); | |||
| } | |||
| /** | |||
| * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#queryOrder(String, String)} . | |||
| */ | |||
| @Test | |||
| public final void testQueryOrder() throws WxErrorException { | |||
| //System.err.println(this.wxService.getPayService().queryOrder(null, null)); | |||
| System.err.println(this.wxService.getPayService().queryOrder("11212121", null)); | |||
| System.err.println(this.wxService.getPayService().queryOrder(null, "11111")); | |||
| } | |||
| /** | |||
| * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#closeOrder(String)} . | |||
| */ | |||
| @Test | |||
| public final void testCloseOrder() throws WxErrorException { | |||
| System.err.println(this.wxService.getPayService().closeOrder("11212121")); | |||
| } | |||
| /** | |||
| * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#entPay(WxEntPayRequest)}. | |||
| */ | |||
| @Test(dependsOnMethods = {"setSSLKey"}) | |||
| public final void testEntPay() throws WxErrorException { | |||
| WxEntPayRequest request = new WxEntPayRequest(); | |||
| System.err.println(this.wxService.getPayService().entPay(request)); | |||
| } | |||
| /** | |||
| * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#queryEntPay(String)}. | |||
| */ | |||
| @Test(dependsOnMethods = {"setSSLKey"}) | |||
| public final void testQueryEntPay() throws WxErrorException { | |||
| System.err.println(this.wxService.getPayService().queryEntPay("11212121")); | |||
| } | |||
| @Test | |||
| public void testCreateScanPayQrcodeMode1() throws Exception { | |||
| String productId = "abc"; | |||
| byte[] bytes = this.wxService.getPayService().createScanPayQrcodeMode1(productId, null, null); | |||
| Path qrcodeFilePath = Files.createTempFile("qrcode_", ".jpg"); | |||
| Files.write(qrcodeFilePath, bytes); | |||
| String qrcodeContent = QrcodeUtils.decodeQrcode(qrcodeFilePath.toFile()); | |||
| System.out.println(qrcodeContent); | |||
| assertTrue(qrcodeContent.startsWith("weixin://wxpay/bizpayurl?")); | |||
| assertTrue(qrcodeContent.contains("product_id=" + productId)); | |||
| } | |||
| @Test | |||
| public void testCreateScanPayQrcodeMode2() throws Exception { | |||
| String qrcodeContent = "abc"; | |||
| byte[] bytes = this.wxService.getPayService().createScanPayQrcodeMode2(qrcodeContent, null, null); | |||
| Path qrcodeFilePath = Files.createTempFile("qrcode_", ".jpg"); | |||
| Files.write(qrcodeFilePath, bytes); | |||
| assertEquals(QrcodeUtils.decodeQrcode(qrcodeFilePath.toFile()), qrcodeContent); | |||
| } | |||
| } | |||
| @@ -64,10 +64,4 @@ public class TestConfigStorage extends WxMpInMemoryConfigStorage { | |||
| super.accessTokenLock = lock; | |||
| } | |||
| @Override | |||
| public boolean useSandboxForWxPay() { | |||
| return false; | |||
| //沙箱环境不成熟,有问题无法使用,暂时屏蔽掉 | |||
| // return true; | |||
| } | |||
| } | |||
| @@ -0,0 +1,50 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
| xmlns="http://maven.apache.org/POM/4.0.0" | |||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
| <parent> | |||
| <artifactId>weixin-java-parent</artifactId> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <version>2.5.1.BETA</version> | |||
| </parent> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <artifactId>weixin-java-pay</artifactId> | |||
| <dependencies> | |||
| <dependency> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>weixin-java-common</artifactId> | |||
| <version>${project.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.jodd</groupId> | |||
| <artifactId>jodd-http</artifactId> | |||
| <version>3.7</version> | |||
| <!-- 由于较新的3.8版本需要jdk8,故而此处采用较低版本 --> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>io.rest-assured</groupId> | |||
| <artifactId>xml-path</artifactId> | |||
| <version>3.0.1</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>qrcode-utils</artifactId> | |||
| <version>1.0</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.testng</groupId> | |||
| <artifactId>testng</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.google.inject</groupId> | |||
| <artifactId>guice</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| </dependencies> | |||
| </project> | |||
| @@ -1,64 +1,63 @@ | |||
| package me.chanjar.weixin.mp.bean.pay; | |||
| import java.io.Serializable; | |||
| import java.util.HashMap; | |||
| import java.util.Map; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringStyle; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| /** | |||
| * 支付异步通知代金券详细 | |||
| */ | |||
| public class WxPayOrderNotifyCoupon implements Serializable { | |||
| /** | |||
| * @fields serialVersionUID | |||
| */ | |||
| private static final long serialVersionUID = -4165343733538156097L; | |||
| @XStreamAlias("coupon_id") | |||
| private String couponId; | |||
| @XStreamAlias("coupon_type") | |||
| private String couponType; | |||
| @XStreamAlias("coupon_fee") | |||
| private Integer couponFee; | |||
| public String getCouponId() { | |||
| return couponId; | |||
| } | |||
| public void setCouponId(String couponId) { | |||
| this.couponId = couponId; | |||
| } | |||
| public String getCouponType() { | |||
| return couponType; | |||
| } | |||
| public void setCouponType(String couponType) { | |||
| this.couponType = couponType; | |||
| } | |||
| public Integer getCouponFee() { | |||
| return couponFee; | |||
| } | |||
| public void setCouponFee(Integer couponFee) { | |||
| this.couponFee = couponFee; | |||
| } | |||
| public Map<String, String> toMap(int index) { | |||
| Map<String, String> map = new HashMap<>(); | |||
| map.put("coupon_id_" + index, this.getCouponId()); | |||
| map.put("coupon_type_" + index, this.getCouponType()); | |||
| map.put("coupon_fee_" + index, this.getCouponFee() + ""); | |||
| return map; | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE); | |||
| } | |||
| } | |||
| package com.github.binarywang.wxpay.bean; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringStyle; | |||
| import java.io.Serializable; | |||
| import java.util.HashMap; | |||
| import java.util.Map; | |||
| /** | |||
| * 支付异步通知代金券详细 | |||
| */ | |||
| public class WxPayOrderNotifyCoupon implements Serializable { | |||
| /** | |||
| * @fields serialVersionUID | |||
| */ | |||
| private static final long serialVersionUID = -4165343733538156097L; | |||
| @XStreamAlias("coupon_id") | |||
| private String couponId; | |||
| @XStreamAlias("coupon_type") | |||
| private String couponType; | |||
| @XStreamAlias("coupon_fee") | |||
| private Integer couponFee; | |||
| public String getCouponId() { | |||
| return couponId; | |||
| } | |||
| public void setCouponId(String couponId) { | |||
| this.couponId = couponId; | |||
| } | |||
| public String getCouponType() { | |||
| return couponType; | |||
| } | |||
| public void setCouponType(String couponType) { | |||
| this.couponType = couponType; | |||
| } | |||
| public Integer getCouponFee() { | |||
| return couponFee; | |||
| } | |||
| public void setCouponFee(Integer couponFee) { | |||
| this.couponFee = couponFee; | |||
| } | |||
| public Map<String, String> toMap(int index) { | |||
| Map<String, String> map = new HashMap<>(); | |||
| map.put("coupon_id_" + index, this.getCouponId()); | |||
| map.put("coupon_type_" + index, this.getCouponType()); | |||
| map.put("coupon_fee_" + index, this.getCouponFee() + ""); | |||
| return map; | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE); | |||
| } | |||
| } | |||
| @@ -1,60 +1,59 @@ | |||
| package me.chanjar.weixin.mp.bean.pay; | |||
| import com.thoughtworks.xstream.XStream; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import com.thoughtworks.xstream.annotations.XStreamOmitField; | |||
| import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||
| @XStreamAlias("xml") | |||
| public class WxPayOrderNotifyResponse { | |||
| @XStreamOmitField | |||
| private transient static final String FAIL = "FAIL"; | |||
| @XStreamOmitField | |||
| private transient static final String SUCCESS = "SUCCESS"; | |||
| @XStreamAlias("return_code") | |||
| private String returnCode; | |||
| @XStreamAlias("return_msg") | |||
| private String returnMsg; | |||
| public String getReturnCode() { | |||
| return returnCode; | |||
| } | |||
| public void setReturnCode(String returnCode) { | |||
| this.returnCode = returnCode; | |||
| } | |||
| public String getReturnMsg() { | |||
| return returnMsg; | |||
| } | |||
| public void setReturnMsg(String returnMsg) { | |||
| this.returnMsg = returnMsg; | |||
| } | |||
| public WxPayOrderNotifyResponse() { | |||
| super(); | |||
| } | |||
| public WxPayOrderNotifyResponse(String returnCode, String returnMsg) { | |||
| super(); | |||
| this.returnCode = returnCode; | |||
| this.returnMsg = returnMsg; | |||
| } | |||
| public static String fail(String msg) { | |||
| WxPayOrderNotifyResponse response = new WxPayOrderNotifyResponse(FAIL, msg); | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.autodetectAnnotations(true); | |||
| return xstream.toXML(response); | |||
| } | |||
| public static String success(String msg) { | |||
| WxPayOrderNotifyResponse response = new WxPayOrderNotifyResponse(SUCCESS, msg); | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.autodetectAnnotations(true); | |||
| return xstream.toXML(response); | |||
| } | |||
| } | |||
| package com.github.binarywang.wxpay.bean; | |||
| import com.thoughtworks.xstream.XStream; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import com.thoughtworks.xstream.annotations.XStreamOmitField; | |||
| import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||
| @XStreamAlias("xml") | |||
| public class WxPayOrderNotifyResponse { | |||
| @XStreamOmitField | |||
| private transient static final String FAIL = "FAIL"; | |||
| @XStreamOmitField | |||
| private transient static final String SUCCESS = "SUCCESS"; | |||
| @XStreamAlias("return_code") | |||
| private String returnCode; | |||
| @XStreamAlias("return_msg") | |||
| private String returnMsg; | |||
| public WxPayOrderNotifyResponse() { | |||
| super(); | |||
| } | |||
| public WxPayOrderNotifyResponse(String returnCode, String returnMsg) { | |||
| super(); | |||
| this.returnCode = returnCode; | |||
| this.returnMsg = returnMsg; | |||
| } | |||
| public static String fail(String msg) { | |||
| WxPayOrderNotifyResponse response = new WxPayOrderNotifyResponse(FAIL, msg); | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.autodetectAnnotations(true); | |||
| return xstream.toXML(response); | |||
| } | |||
| public static String success(String msg) { | |||
| WxPayOrderNotifyResponse response = new WxPayOrderNotifyResponse(SUCCESS, msg); | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.autodetectAnnotations(true); | |||
| return xstream.toXML(response); | |||
| } | |||
| public String getReturnCode() { | |||
| return returnCode; | |||
| } | |||
| public void setReturnCode(String returnCode) { | |||
| this.returnCode = returnCode; | |||
| } | |||
| public String getReturnMsg() { | |||
| return returnMsg; | |||
| } | |||
| public void setReturnMsg(String returnMsg) { | |||
| this.returnMsg = returnMsg; | |||
| } | |||
| } | |||
| @@ -1,4 +1,4 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.request; | |||
| package com.github.binarywang.wxpay.bean.request; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import me.chanjar.weixin.common.annotation.Required; | |||
| @@ -16,6 +16,7 @@ import me.chanjar.weixin.common.util.ToStringUtils; | |||
| * <li>描述 | |||
| * </pre> | |||
| * Created by Binary Wang on 2016/10/19. | |||
| * | |||
| * @author binarywang (https://github.com/binarywang) | |||
| */ | |||
| @XStreamAlias("xml") | |||
| @@ -29,20 +30,20 @@ public class WxEntPayQueryRequest extends WxPayBaseRequest { | |||
| * String(32) | |||
| * 微信支付分配的商户号 | |||
| * </pre> | |||
| */ | |||
| */ | |||
| @XStreamAlias("mchid") | |||
| private String mchId; | |||
| /** | |||
| * <pre> | |||
| * 商户订单号 | |||
| * partner_trade_no | |||
| * 是 | |||
| * 10000098201411111234567890 | |||
| * String | |||
| * 商户订单号 | |||
| * 商户订单号 | |||
| * partner_trade_no | |||
| * 是 | |||
| * 10000098201411111234567890 | |||
| * String | |||
| * 商户订单号 | |||
| * </pre> | |||
| */ | |||
| */ | |||
| @Required | |||
| @XStreamAlias("partner_trade_no") | |||
| private String partnerTradeNo; | |||
| @@ -1,4 +1,4 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.request; | |||
| package com.github.binarywang.wxpay.bean.request; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import me.chanjar.weixin.common.util.ToStringUtils; | |||
| @@ -15,140 +15,141 @@ import me.chanjar.weixin.common.util.ToStringUtils; | |||
| * <li>描述 | |||
| * </pre> | |||
| * Created by Binary Wang on 2016/10/02. | |||
| * | |||
| * @author binarywang (https://github.com/binarywang) | |||
| */ | |||
| @XStreamAlias("xml") | |||
| public class WxEntPayRequest extends WxPayBaseRequest{ | |||
| public class WxEntPayRequest extends WxPayBaseRequest { | |||
| /** | |||
| * <pre> | |||
| * 公众账号appid | |||
| * mch_appid | |||
| * 是 | |||
| * wx8888888888888888 | |||
| * String | |||
| * 微信分配的公众账号ID(企业号corpid即为此appId) | |||
| * 公众账号appid | |||
| * mch_appid | |||
| * 是 | |||
| * wx8888888888888888 | |||
| * String | |||
| * 微信分配的公众账号ID(企业号corpid即为此appId) | |||
| * </pre> | |||
| */ | |||
| */ | |||
| @XStreamAlias("mch_appid") | |||
| private String mchAppid; | |||
| /** | |||
| * <pre> | |||
| * 商户号 | |||
| * mchid | |||
| * 是 | |||
| * 1900000109 | |||
| * String(32) | |||
| * 微信支付分配的商户号 | |||
| * 商户号 | |||
| * mchid | |||
| * 是 | |||
| * 1900000109 | |||
| * String(32) | |||
| * 微信支付分配的商户号 | |||
| * </pre> | |||
| */ | |||
| */ | |||
| @XStreamAlias("mchid") | |||
| private String mchId; | |||
| /** | |||
| * <pre> | |||
| * 设备号 | |||
| * device_info | |||
| * 否 | |||
| * 13467007045764 | |||
| * String(32) | |||
| *微信支付分配的终端设备号 | |||
| * 设备号 | |||
| * device_info | |||
| * 否 | |||
| * 13467007045764 | |||
| * String(32) | |||
| * 微信支付分配的终端设备号 | |||
| * </pre> | |||
| */ | |||
| */ | |||
| @XStreamAlias("device_info") | |||
| private String deviceInfo; | |||
| /** | |||
| * <pre> | |||
| * 商户订单号 | |||
| * partner_trade_no | |||
| * 是 | |||
| * 10000098201411111234567890 | |||
| * String | |||
| * 商户订单号 | |||
| * 商户订单号 | |||
| * partner_trade_no | |||
| * 是 | |||
| * 10000098201411111234567890 | |||
| * String | |||
| * 商户订单号 | |||
| * </pre> | |||
| */ | |||
| */ | |||
| @XStreamAlias("partner_trade_no") | |||
| private String partnerTradeNo; | |||
| /** | |||
| * <pre> | |||
| * 需保持唯一性 用户openid | |||
| * openid | |||
| * 是 | |||
| * oxTWIuGaIt6gTKsQRLau2M0yL16E | |||
| * String | |||
| * 商户appid下,某用户的openid | |||
| * 需保持唯一性 用户openid | |||
| * openid | |||
| * 是 | |||
| * oxTWIuGaIt6gTKsQRLau2M0yL16E | |||
| * String | |||
| * 商户appid下,某用户的openid | |||
| * </pre> | |||
| */ | |||
| */ | |||
| @XStreamAlias("openid") | |||
| private String openid; | |||
| /** | |||
| * <pre> | |||
| * 校验用户姓名选项 | |||
| * check_name | |||
| * 是 | |||
| * OPTION_CHECK | |||
| * String | |||
| * NO_CHECK:不校验真实姓名 | |||
| * FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账) | |||
| * OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功) | |||
| * 校验用户姓名选项 | |||
| * check_name | |||
| * 是 | |||
| * OPTION_CHECK | |||
| * String | |||
| * NO_CHECK:不校验真实姓名 | |||
| * FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账) | |||
| * OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功) | |||
| * </pre> | |||
| */ | |||
| */ | |||
| @XStreamAlias("check_name") | |||
| private String checkName; | |||
| /** | |||
| * <pre> | |||
| * 收款用户姓名 | |||
| * re_user_name | |||
| * 可选 | |||
| * 马花花 | |||
| * String | |||
| * 收款用户真实姓名。 | |||
| * 如果check_name设置为FORCE_CHECK或OPTION_CHECK, 则必填用户真实姓名 | |||
| * 收款用户姓名 | |||
| * re_user_name | |||
| * 可选 | |||
| * 马花花 | |||
| * String | |||
| * 收款用户真实姓名。 | |||
| * 如果check_name设置为FORCE_CHECK或OPTION_CHECK, 则必填用户真实姓名 | |||
| * </pre> | |||
| */ | |||
| */ | |||
| @XStreamAlias("re_user_name") | |||
| private String reUserName; | |||
| /** | |||
| * <pre> | |||
| * 金额 | |||
| * amount | |||
| * 是 | |||
| * 10099 | |||
| * int | |||
| * 企业付款金额, 单位为分 | |||
| * 金额 | |||
| * amount | |||
| * 是 | |||
| * 10099 | |||
| * int | |||
| * 企业付款金额, 单位为分 | |||
| * </pre> | |||
| */ | |||
| */ | |||
| @XStreamAlias("amount") | |||
| private Integer amount; | |||
| /** | |||
| * <pre> | |||
| * 企业付款描述信息 | |||
| * desc | |||
| * 是 | |||
| * 理赔 | |||
| * String | |||
| * 企业付款操作说明信息。必填。 | |||
| * 企业付款描述信息 | |||
| * desc | |||
| * 是 | |||
| * 理赔 | |||
| * String | |||
| * 企业付款操作说明信息。必填。 | |||
| * </pre> | |||
| */ | |||
| */ | |||
| @XStreamAlias("desc") | |||
| private String description; | |||
| /** | |||
| * <pre> | |||
| * Ip地址 | |||
| * spbill_create_ip | |||
| * 是 | |||
| * 192.168.0.1 | |||
| * String(32) | |||
| * 调用接口的机器Ip地址 | |||
| * Ip地址 | |||
| * spbill_create_ip | |||
| * 是 | |||
| * 192.168.0.1 | |||
| * String(32) | |||
| * 调用接口的机器Ip地址 | |||
| * </pre> | |||
| */ | |||
| */ | |||
| @XStreamAlias("spbill_create_ip") | |||
| private String spbillCreateIp; | |||
| @@ -1,4 +1,4 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.request; | |||
| package com.github.binarywang.wxpay.bean.request; | |||
| import com.thoughtworks.xstream.XStream; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| @@ -1,4 +1,4 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.request; | |||
| package com.github.binarywang.wxpay.bean.request; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import me.chanjar.weixin.common.annotation.Required; | |||
| @@ -1,4 +1,4 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.request; | |||
| package com.github.binarywang.wxpay.bean.request; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| @@ -10,7 +10,7 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("xml") | |||
| public class WxPayOrderCloseRequest extends WxPayBaseRequest{ | |||
| public class WxPayOrderCloseRequest extends WxPayBaseRequest { | |||
| /** | |||
| * <pre> | |||
| @@ -1,4 +1,4 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.request; | |||
| package com.github.binarywang.wxpay.bean.request; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| @@ -14,6 +14,7 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| * <li>示例值 | |||
| * <li>描述 | |||
| * </pre> | |||
| * | |||
| * @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||
| */ | |||
| @XStreamAlias("xml") | |||
| @@ -1,4 +1,4 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.request; | |||
| package com.github.binarywang.wxpay.bean.request; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| @@ -1,4 +1,4 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.request; | |||
| package com.github.binarywang.wxpay.bean.request; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| @@ -1,4 +1,4 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.request; | |||
| package com.github.binarywang.wxpay.bean.request; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import me.chanjar.weixin.common.annotation.Required; | |||
| @@ -143,6 +143,28 @@ public class WxPayRefundRequest extends WxPayBaseRequest { | |||
| @XStreamAlias("refund_account") | |||
| private String refundAccount; | |||
| private WxPayRefundRequest(Builder builder) { | |||
| setDeviceInfo(builder.deviceInfo); | |||
| setAppid(builder.appid); | |||
| setTransactionId(builder.transactionId); | |||
| setMchId(builder.mchId); | |||
| setOutTradeNo(builder.outTradeNo); | |||
| setSubAppId(builder.subAppId); | |||
| setSubMchId(builder.subMchId); | |||
| setOutRefundNo(builder.outRefundNo); | |||
| setNonceStr(builder.nonceStr); | |||
| setTotalFee(builder.totalFee); | |||
| setSign(builder.sign); | |||
| setRefundFee(builder.refundFee); | |||
| setRefundFeeType(builder.refundFeeType); | |||
| setOpUserId(builder.opUserId); | |||
| setRefundAccount(builder.refundAccount); | |||
| } | |||
| public static Builder newBuilder() { | |||
| return new Builder(); | |||
| } | |||
| public String getDeviceInfo() { | |||
| return this.deviceInfo; | |||
| } | |||
| @@ -215,4 +237,103 @@ public class WxPayRefundRequest extends WxPayBaseRequest { | |||
| this.refundAccount = refundAccount; | |||
| } | |||
| public static final class Builder { | |||
| private String deviceInfo; | |||
| private String appid; | |||
| private String transactionId; | |||
| private String mchId; | |||
| private String outTradeNo; | |||
| private String subAppId; | |||
| private String subMchId; | |||
| private String outRefundNo; | |||
| private String nonceStr; | |||
| private Integer totalFee; | |||
| private String sign; | |||
| private Integer refundFee; | |||
| private String refundFeeType; | |||
| private String opUserId; | |||
| private String refundAccount; | |||
| private Builder() { | |||
| } | |||
| public Builder deviceInfo(String deviceInfo) { | |||
| this.deviceInfo = deviceInfo; | |||
| return this; | |||
| } | |||
| public Builder appid(String appid) { | |||
| this.appid = appid; | |||
| return this; | |||
| } | |||
| public Builder transactionId(String transactionId) { | |||
| this.transactionId = transactionId; | |||
| return this; | |||
| } | |||
| public Builder mchId(String mchId) { | |||
| this.mchId = mchId; | |||
| return this; | |||
| } | |||
| public Builder outTradeNo(String outTradeNo) { | |||
| this.outTradeNo = outTradeNo; | |||
| return this; | |||
| } | |||
| public Builder subAppId(String subAppId) { | |||
| this.subAppId = subAppId; | |||
| return this; | |||
| } | |||
| public Builder subMchId(String subMchId) { | |||
| this.subMchId = subMchId; | |||
| return this; | |||
| } | |||
| public Builder outRefundNo(String outRefundNo) { | |||
| this.outRefundNo = outRefundNo; | |||
| return this; | |||
| } | |||
| public Builder nonceStr(String nonceStr) { | |||
| this.nonceStr = nonceStr; | |||
| return this; | |||
| } | |||
| public Builder totalFee(Integer totalFee) { | |||
| this.totalFee = totalFee; | |||
| return this; | |||
| } | |||
| public Builder sign(String sign) { | |||
| this.sign = sign; | |||
| return this; | |||
| } | |||
| public Builder refundFee(Integer refundFee) { | |||
| this.refundFee = refundFee; | |||
| return this; | |||
| } | |||
| public Builder refundFeeType(String refundFeeType) { | |||
| this.refundFeeType = refundFeeType; | |||
| return this; | |||
| } | |||
| public Builder opUserId(String opUserId) { | |||
| this.opUserId = opUserId; | |||
| return this; | |||
| } | |||
| public Builder refundAccount(String refundAccount) { | |||
| this.refundAccount = refundAccount; | |||
| return this; | |||
| } | |||
| public WxPayRefundRequest build() { | |||
| return new WxPayRefundRequest(this); | |||
| } | |||
| } | |||
| } | |||
| @@ -1,4 +1,4 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.request; | |||
| package com.github.binarywang.wxpay.bean.request; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import me.chanjar.weixin.common.annotation.Required; | |||
| @@ -1,18 +1,19 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.request; | |||
| package com.github.binarywang.wxpay.bean.request; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| /** | |||
| * 发送红包请求参数对象 | |||
| * Created by Binary Wang on 2016/9/24. | |||
| * 发送红包请求参数对象 | |||
| * Created by Binary Wang on 2016/9/24. | |||
| * | |||
| * @author binarywang (https://github.com/binarywang) | |||
| */ | |||
| @XStreamAlias("xml") | |||
| public class WxPaySendRedpackRequest extends WxPayBaseRequest{ | |||
| public class WxPaySendRedpackRequest extends WxPayBaseRequest { | |||
| /** | |||
| * mch_billno | |||
| * 商户订单号(每个订单号必须唯一) 组成:mch_id+yyyymmdd+10位一天内不能重复的数字。 接口根据商户订单号支持重入,如出现超时可再调用。 | |||
| */ | |||
| * mch_billno | |||
| * 商户订单号(每个订单号必须唯一) 组成:mch_id+yyyymmdd+10位一天内不能重复的数字。 接口根据商户订单号支持重入,如出现超时可再调用。 | |||
| */ | |||
| @XStreamAlias("mch_billno") | |||
| private String mchBillNo; | |||
| @@ -25,60 +26,60 @@ public class WxPaySendRedpackRequest extends WxPayBaseRequest{ | |||
| private String sendName; | |||
| /** | |||
| * re_openid | |||
| * 接受红包的用户 用户在wxappid下的openid | |||
| * re_openid | |||
| * 接受红包的用户 用户在wxappid下的openid | |||
| */ | |||
| @XStreamAlias("re_openid") | |||
| private String reOpenid; | |||
| /** | |||
| * total_amount | |||
| * 红包总额 | |||
| * total_amount | |||
| * 红包总额 | |||
| */ | |||
| @XStreamAlias("total_amount") | |||
| private Integer totalAmount; | |||
| /** | |||
| * total_num | |||
| * 红包发放总人数 | |||
| * total_num | |||
| * 红包发放总人数 | |||
| */ | |||
| @XStreamAlias("total_num") | |||
| private Integer totalNum; | |||
| /** | |||
| * amt_type | |||
| * 红包金额设置方式 | |||
| * ALL_RAND—全部随机,商户指定总金额和红包发放总人数,由微信支付随机计算出各红包金额 | |||
| * 裂变红包必填 | |||
| * amt_type | |||
| * 红包金额设置方式 | |||
| * ALL_RAND—全部随机,商户指定总金额和红包发放总人数,由微信支付随机计算出各红包金额 | |||
| * 裂变红包必填 | |||
| */ | |||
| @XStreamAlias("amt_type") | |||
| private String amtType; | |||
| /** | |||
| * wishing | |||
| * 红包祝福语 | |||
| * wishing | |||
| * 红包祝福语 | |||
| */ | |||
| @XStreamAlias("wishing") | |||
| private String wishing; | |||
| /** | |||
| * client_ip | |||
| * 服务器Ip地址 | |||
| * 调用接口的机器Ip地址 | |||
| * client_ip | |||
| * 服务器Ip地址 | |||
| * 调用接口的机器Ip地址 | |||
| */ | |||
| @XStreamAlias("client_ip") | |||
| private String clientIp; | |||
| /** | |||
| * act_name | |||
| * 活动名称 | |||
| * act_name | |||
| * 活动名称 | |||
| */ | |||
| @XStreamAlias("act_name") | |||
| private String actName; | |||
| /** | |||
| * remark | |||
| * 备注 | |||
| * remark | |||
| * 备注 | |||
| */ | |||
| @XStreamAlias("remark") | |||
| private String remark; | |||
| @@ -1,7 +1,6 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.request; | |||
| package com.github.binarywang.wxpay.bean.request; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import me.chanjar.weixin.common.annotation.Required; | |||
| /** | |||
| @@ -17,6 +16,7 @@ import me.chanjar.weixin.common.annotation.Required; | |||
| * <li>描述 | |||
| * </pre> | |||
| * Created by Binary Wang on 2016/9/25. | |||
| * | |||
| * @author binarywang (https://github.com/binarywang) | |||
| */ | |||
| @XStreamAlias("xml") | |||
| @@ -56,35 +56,35 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { | |||
| * 否 | |||
| * String(6000) | |||
| * { "goods_detail":[ | |||
| { | |||
| "goods_id":"iphone6s_16G", | |||
| "wxpay_goods_id":"1001", | |||
| "goods_name":"iPhone6s 16G", | |||
| "goods_num":1, | |||
| "price":528800, | |||
| "goods_category":"123456", | |||
| "body":"苹果手机" | |||
| }, | |||
| { | |||
| "goods_id":"iphone6s_32G", | |||
| "wxpay_goods_id":"1002", | |||
| "goods_name":"iPhone6s 32G", | |||
| "quantity":1, | |||
| "price":608800, | |||
| "goods_category":"123789", | |||
| "body":"苹果手机" | |||
| } | |||
| ] | |||
| } | |||
| 商品详细列表,使用Json格式,传输签名前请务必使用CDATA标签将JSON文本串保护起来。 | |||
| goods_detail []: | |||
| └ goods_id String 必填 32 商品的编号 | |||
| └ wxpay_goods_id String 可选 32 微信支付定义的统一商品编号 | |||
| └ goods_name String 必填 256 商品名称 | |||
| └ goods_num Int 必填 商品数量 | |||
| └ price Int 必填 商品单价,单位为分 | |||
| └ goods_category String 可选 32 商品类目Id | |||
| └ body String 可选 1000 商品描述信息 | |||
| * { | |||
| * "goods_id":"iphone6s_16G", | |||
| * "wxpay_goods_id":"1001", | |||
| * "goods_name":"iPhone6s 16G", | |||
| * "goods_num":1, | |||
| * "price":528800, | |||
| * "goods_category":"123456", | |||
| * "body":"苹果手机" | |||
| * }, | |||
| * { | |||
| * "goods_id":"iphone6s_32G", | |||
| * "wxpay_goods_id":"1002", | |||
| * "goods_name":"iPhone6s 32G", | |||
| * "quantity":1, | |||
| * "price":608800, | |||
| * "goods_category":"123789", | |||
| * "body":"苹果手机" | |||
| * } | |||
| * ] | |||
| * } | |||
| * 商品详细列表,使用Json格式,传输签名前请务必使用CDATA标签将JSON文本串保护起来。 | |||
| * goods_detail []: | |||
| * └ goods_id String 必填 32 商品的编号 | |||
| * └ wxpay_goods_id String 可选 32 微信支付定义的统一商品编号 | |||
| * └ goods_name String 必填 256 商品名称 | |||
| * └ goods_num Int 必填 商品数量 | |||
| * └ price Int 必填 商品单价,单位为分 | |||
| * └ goods_category String 可选 32 商品类目Id | |||
| * └ body String 可选 1000 商品描述信息 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("detail") | |||
| @@ -267,6 +267,9 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { | |||
| @XStreamAlias("openid") | |||
| private String openid; | |||
| public static WxUnifiedOrderRequestBuilder builder() { | |||
| return new WxUnifiedOrderRequestBuilder(); | |||
| } | |||
| public String getDeviceInfo() { | |||
| return this.deviceInfo; | |||
| @@ -362,6 +365,7 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { | |||
| /** | |||
| * 如果配置中已经设置,可以不设置值 | |||
| * | |||
| * @param notifyURL | |||
| */ | |||
| public void setNotifyURL(String notifyURL) { | |||
| @@ -374,6 +378,7 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { | |||
| /** | |||
| * 如果配置中已经设置,可以不设置值 | |||
| * | |||
| * @param tradeType 交易类型 | |||
| */ | |||
| public void setTradeType(String tradeType) { | |||
| @@ -404,10 +409,6 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { | |||
| this.openid = openid; | |||
| } | |||
| public static WxUnifiedOrderRequestBuilder builder() { | |||
| return new WxUnifiedOrderRequestBuilder(); | |||
| } | |||
| public static class WxUnifiedOrderRequestBuilder { | |||
| private String appid; | |||
| private String mchId; | |||
| @@ -1,10 +1,11 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.result; | |||
| package com.github.binarywang.wxpay.bean.result; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| /** | |||
| * 企业付款查询返回结果 | |||
| * Created by Binary Wang on 2016/10/19. | |||
| * | |||
| * @author binarywang (https://github.com/binarywang) | |||
| */ | |||
| @XStreamAlias("xml") | |||
| @@ -1,10 +1,11 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.result; | |||
| package com.github.binarywang.wxpay.bean.result; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| /** | |||
| * 企业付款返回结果 | |||
| * Created by Binary Wang on 2016/10/02. | |||
| * | |||
| * @author binarywang (https://github.com/binarywang) | |||
| */ | |||
| @XStreamAlias("xml") | |||
| @@ -1,4 +1,4 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.result; | |||
| package com.github.binarywang.wxpay.bean.result; | |||
| import com.google.common.collect.Maps; | |||
| import com.thoughtworks.xstream.XStream; | |||
| @@ -1,4 +1,4 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.result; | |||
| package com.github.binarywang.wxpay.bean.result; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| @@ -1,4 +1,4 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.result; | |||
| package com.github.binarywang.wxpay.bean.result; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| @@ -0,0 +1,392 @@ | |||
| package com.github.binarywang.wxpay.bean.result; | |||
| import com.github.binarywang.wxpay.bean.WxPayOrderNotifyCoupon; | |||
| import com.github.binarywang.wxpay.converter.WxPayOrderNotifyResultConverter; | |||
| import com.thoughtworks.xstream.XStream; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import me.chanjar.weixin.common.util.BeanUtils; | |||
| import me.chanjar.weixin.common.util.ToStringUtils; | |||
| import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||
| import java.io.Serializable; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| /** | |||
| * 支付结果通用通知 ,文档见:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7 | |||
| * | |||
| * @author aimilin6688 | |||
| * @since 2.5.0 | |||
| */ | |||
| @XStreamAlias("xml") | |||
| public class WxPayOrderNotifyResult extends WxPayBaseResult implements Serializable { | |||
| private static final long serialVersionUID = 5389718115223345496L; | |||
| /** | |||
| * <pre> | |||
| * 设备号 | |||
| * device_info | |||
| * 否 | |||
| * String(32) | |||
| * 013467007045764 | |||
| * 微信支付分配的终端设备号, | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("device_info") | |||
| private String deviceInfo; | |||
| /** | |||
| * <pre> | |||
| * 用户标识 | |||
| * openid | |||
| * 是 | |||
| * String(128) | |||
| * wxd930ea5d5a258f4f | |||
| * 用户在商户appid下的唯一标识 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("openid") | |||
| private String openid; | |||
| /** | |||
| * <pre> | |||
| * 是否关注公众账号 | |||
| * is_subscribe | |||
| * 否 | |||
| * String(1) | |||
| * Y | |||
| * 用户是否关注公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("is_subscribe") | |||
| private String isSubscribe; | |||
| /** | |||
| * <pre> | |||
| * 交易类型 | |||
| * trade_type | |||
| * 是 | |||
| * String(16) | |||
| * JSAPI JSAPI、NATIVE、APP | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("trade_type") | |||
| private String tradeType; | |||
| /** | |||
| * <pre> | |||
| * 付款银行 | |||
| * bank_type | |||
| * 是 | |||
| * String(16) | |||
| * CMC | |||
| * 银行类型,采用字符串类型的银行标识,银行类型见银行列表 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("bank_type") | |||
| private String bankType; | |||
| /** | |||
| * <pre> | |||
| * 订单金额 | |||
| * total_fee | |||
| * 是 | |||
| * Int | |||
| * 100 | |||
| * 订单总金额,单位为分 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("total_fee") | |||
| private Integer totalFee; | |||
| /** | |||
| * <pre> | |||
| * 应结订单金额 | |||
| * settlement_total_fee | |||
| * 否 | |||
| * Int | |||
| * 100 | |||
| * 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("settlement_total_fee") | |||
| private Integer settlementTotalFee; | |||
| /** | |||
| * <pre> | |||
| * 货币种类 | |||
| * fee_type | |||
| * 否 | |||
| * String(8) | |||
| * CNY | |||
| * 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("fee_type") | |||
| private String feeType; | |||
| /** | |||
| * <pre> | |||
| * 现金支付金额 | |||
| * cash_fee | |||
| * 是 | |||
| * Int | |||
| * 100 | |||
| * 现金支付金额订单现金支付金额,详见支付金额 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("cash_fee") | |||
| private Integer cashFee; | |||
| /** | |||
| * <pre> | |||
| * 现金支付货币类型 | |||
| * cash_fee_type | |||
| * 否 | |||
| * String(16) | |||
| * CNY | |||
| * 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("cash_fee_type") | |||
| private String cashFeeType; | |||
| /** | |||
| * <pre> | |||
| * 总代金券金额 | |||
| * coupon_fee | |||
| * 否 | |||
| * Int | |||
| * 10 | |||
| * 代金券金额<=订单金额,订单金额-代金券金额=现金支付金额,详见支付金额 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("coupon_fee") | |||
| private Integer couponFee; | |||
| /** | |||
| * <pre> | |||
| * 代金券使用数量 | |||
| * coupon_count | |||
| * 否 | |||
| * Int | |||
| * 1 | |||
| * 代金券使用数量 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("coupon_count") | |||
| private Integer couponCount; | |||
| private List<WxPayOrderNotifyCoupon> couponList; | |||
| /** | |||
| * <pre> | |||
| * 微信支付订单号 | |||
| * transaction_id | |||
| * 是 | |||
| * String(32) | |||
| * 1217752501201407033233368018 | |||
| * 微信支付订单号 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("transaction_id") | |||
| private String transactionId; | |||
| /** | |||
| * <pre> | |||
| * 商户订单号 | |||
| * out_trade_no | |||
| * 是 | |||
| * String(32) | |||
| * 1212321211201407033568112322 | |||
| * 商户系统的订单号,与请求一致。 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("out_trade_no") | |||
| private String outTradeNo; | |||
| /** | |||
| * <pre> | |||
| * 商家数据包 | |||
| * attach | |||
| * 否 | |||
| * String(128) | |||
| * 123456 | |||
| * 商家数据包,原样返回 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("attach") | |||
| private String attach; | |||
| /** | |||
| * <pre> | |||
| * 支付完成时间 | |||
| * time_end | |||
| * 是 | |||
| * String(14) | |||
| * 20141030133525 | |||
| * 支付完成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则 | |||
| * </pre> | |||
| */ | |||
| @XStreamAlias("time_end") | |||
| private String timeEnd; | |||
| public static WxPayOrderNotifyResult fromXML(String xmlString) { | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.processAnnotations(WxPayOrderNotifyResult.class); | |||
| xstream.registerConverter(new WxPayOrderNotifyResultConverter(xstream.getMapper(), xstream.getReflectionProvider())); | |||
| WxPayOrderNotifyResult result = (WxPayOrderNotifyResult) xstream.fromXML(xmlString); | |||
| result.setXmlString(xmlString); | |||
| return result; | |||
| } | |||
| public Integer getCouponCount() { | |||
| return couponCount; | |||
| } | |||
| public void setCouponCount(Integer couponCount) { | |||
| this.couponCount = couponCount; | |||
| } | |||
| public List<WxPayOrderNotifyCoupon> getCouponList() { | |||
| return couponList; | |||
| } | |||
| public void setCouponList(List<WxPayOrderNotifyCoupon> couponList) { | |||
| this.couponList = couponList; | |||
| } | |||
| public String getDeviceInfo() { | |||
| return deviceInfo; | |||
| } | |||
| public void setDeviceInfo(String deviceInfo) { | |||
| this.deviceInfo = deviceInfo; | |||
| } | |||
| public String getOpenid() { | |||
| return openid; | |||
| } | |||
| public void setOpenid(String openid) { | |||
| this.openid = openid; | |||
| } | |||
| public String getIsSubscribe() { | |||
| return isSubscribe; | |||
| } | |||
| public void setIsSubscribe(String isSubscribe) { | |||
| this.isSubscribe = isSubscribe; | |||
| } | |||
| public String getTradeType() { | |||
| return tradeType; | |||
| } | |||
| public void setTradeType(String tradeType) { | |||
| this.tradeType = tradeType; | |||
| } | |||
| public String getBankType() { | |||
| return bankType; | |||
| } | |||
| public void setBankType(String bankType) { | |||
| this.bankType = bankType; | |||
| } | |||
| public Integer getTotalFee() { | |||
| return totalFee; | |||
| } | |||
| public void setTotalFee(Integer totalFee) { | |||
| this.totalFee = totalFee; | |||
| } | |||
| public Integer getSettlementTotalFee() { | |||
| return settlementTotalFee; | |||
| } | |||
| public void setSettlementTotalFee(Integer settlementTotalFee) { | |||
| this.settlementTotalFee = settlementTotalFee; | |||
| } | |||
| public String getFeeType() { | |||
| return feeType; | |||
| } | |||
| public void setFeeType(String feeType) { | |||
| this.feeType = feeType; | |||
| } | |||
| public Integer getCashFee() { | |||
| return cashFee; | |||
| } | |||
| public void setCashFee(Integer cashFee) { | |||
| this.cashFee = cashFee; | |||
| } | |||
| public String getCashFeeType() { | |||
| return cashFeeType; | |||
| } | |||
| public void setCashFeeType(String cashFeeType) { | |||
| this.cashFeeType = cashFeeType; | |||
| } | |||
| public Integer getCouponFee() { | |||
| return couponFee; | |||
| } | |||
| public void setCouponFee(Integer couponFee) { | |||
| this.couponFee = couponFee; | |||
| } | |||
| public String getTransactionId() { | |||
| return transactionId; | |||
| } | |||
| public void setTransactionId(String transactionId) { | |||
| this.transactionId = transactionId; | |||
| } | |||
| public String getOutTradeNo() { | |||
| return outTradeNo; | |||
| } | |||
| public void setOutTradeNo(String outTradeNo) { | |||
| this.outTradeNo = outTradeNo; | |||
| } | |||
| public String getAttach() { | |||
| return attach; | |||
| } | |||
| public void setAttach(String attach) { | |||
| this.attach = attach; | |||
| } | |||
| public String getTimeEnd() { | |||
| return timeEnd; | |||
| } | |||
| public void setTimeEnd(String timeEnd) { | |||
| this.timeEnd = timeEnd; | |||
| } | |||
| @Override | |||
| public Map<String, String> toMap() { | |||
| Map<String, String> resultMap = BeanUtils.xmlBean2Map(this); | |||
| if (this.getCouponCount() != null && this.getCouponCount() > 0) { | |||
| for (int i = 0; i < this.getCouponCount(); i++) { | |||
| WxPayOrderNotifyCoupon coupon = couponList.get(i); | |||
| resultMap.putAll(coupon.toMap(i)); | |||
| } | |||
| } | |||
| return resultMap; | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return ToStringUtils.toSimpleString(this); | |||
| } | |||
| } | |||
| @@ -1,4 +1,4 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.result; | |||
| package com.github.binarywang.wxpay.bean.result; | |||
| import com.google.common.collect.Lists; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| @@ -396,7 +396,7 @@ public class WxPayOrderQueryResult extends WxPayBaseResult { | |||
| if (this.couponCount != null && this.couponCount > 0) { | |||
| this.coupons = Lists.newArrayList(); | |||
| XmlPath xmlPath = new XmlPath(this.getXmlString()); | |||
| for (int i = 0; i < this.couponCount; i++){ | |||
| for (int i = 0; i < this.couponCount; i++) { | |||
| this.coupons.add(new Coupon(this.getXmlValue(xmlPath, "xml.coupon_type_" + i, String.class), | |||
| this.getXmlValue(xmlPath, "xml.coupon_id_" + i, String.class), | |||
| this.getXmlValue(xmlPath, "xml.coupon_fee_" + i, Integer.class))); | |||
| @@ -1,4 +1,4 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.result; | |||
| package com.github.binarywang.wxpay.bean.result; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| @@ -1,4 +1,4 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.result; | |||
| package com.github.binarywang.wxpay.bean.result; | |||
| import com.google.common.collect.Lists; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| @@ -186,7 +186,7 @@ public class WxPayRefundQueryResult extends WxPayBaseResult { | |||
| /** | |||
| * 组装生成退款记录属性的内容 | |||
| */ | |||
| */ | |||
| public void composeRefundRecords() { | |||
| if (this.refundCount != null && this.refundCount > 0) { | |||
| this.refundRecords = Lists.newArrayList(); | |||
| @@ -1,19 +1,19 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.result; | |||
| import java.io.Serializable; | |||
| package com.github.binarywang.wxpay.bean.result; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import java.io.Serializable; | |||
| /** | |||
| * <pre> | |||
| * 微信支付-申请退款返回结果 | |||
| * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4 | |||
| * </pre> | |||
| * @author liukaitj | |||
| * | |||
| * @author liukaitj | |||
| */ | |||
| @XStreamAlias("xml") | |||
| public class WxPayRefundResult extends WxPayBaseResult implements Serializable{ | |||
| public class WxPayRefundResult extends WxPayBaseResult implements Serializable { | |||
| private static final long serialVersionUID = 1L; | |||
| @XStreamAlias("device_info") | |||
| @@ -1,14 +1,14 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.result; | |||
| import java.io.Serializable; | |||
| package com.github.binarywang.wxpay.bean.result; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import java.io.Serializable; | |||
| /** | |||
| * 向微信用户个人发现金红包返回结果 | |||
| * https://pay.weixin.qq.com/wiki/doc/api/cash_coupon.php?chapter=13_5 | |||
| * @author kane | |||
| * | |||
| * @author kane | |||
| */ | |||
| @XStreamAlias("xml") | |||
| public class WxPaySendRedpackResult extends WxPayBaseResult implements Serializable { | |||
| @@ -1,11 +1,11 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.result; | |||
| package com.github.binarywang.wxpay.bean.result; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| /** | |||
| * <pre> | |||
| * 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识"返回的结果 | |||
| * 统一下单(详见http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1) | |||
| * 统一下单(详见http://com.github.binarywang.wechat.pay.bean.pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1) | |||
| * </pre> | |||
| * | |||
| * @author chanjarster | |||
| @@ -0,0 +1,157 @@ | |||
| package com.github.binarywang.wxpay.config; | |||
| import org.apache.http.ssl.SSLContexts; | |||
| import javax.net.ssl.SSLContext; | |||
| import java.io.File; | |||
| import java.io.FileInputStream; | |||
| import java.security.KeyStore; | |||
| /** | |||
| * 微信支付配置 | |||
| * | |||
| * @author Binary Wang (https://github.com/binarywang) | |||
| */ | |||
| public class WxPayConfig { | |||
| private String appId; | |||
| private String subAppId; | |||
| private String mchId; | |||
| private String mchKey; | |||
| private String subMchId; | |||
| private String notifyUrl; | |||
| private String tradeType; | |||
| private SSLContext sslContext; | |||
| private String keyPath; | |||
| public void setNotifyUrl(String notifyUrl) { | |||
| this.notifyUrl = notifyUrl; | |||
| } | |||
| public void setTradeType(String tradeType) { | |||
| this.tradeType = tradeType; | |||
| } | |||
| /** | |||
| * 设置证书 | |||
| * @param keyPath apiclient_cert.p12的文件的绝对路径 | |||
| */ | |||
| public void setKeyPath(String keyPath) { | |||
| this.keyPath = keyPath; | |||
| } | |||
| public String getKeyPath() { | |||
| return keyPath; | |||
| } | |||
| public void setAppId(String appId) { | |||
| this.appId = appId; | |||
| } | |||
| public void setSubAppId(String subAppId) { | |||
| this.subAppId = subAppId; | |||
| } | |||
| public void setMchId(String mchId) { | |||
| this.mchId = mchId; | |||
| } | |||
| public void setMchKey(String mchKey) { | |||
| this.mchKey = mchKey; | |||
| } | |||
| public void setSubMchId(String subMchId) { | |||
| this.subMchId = subMchId; | |||
| } | |||
| public void setSslContext(SSLContext sslContext) { | |||
| this.sslContext = sslContext; | |||
| } | |||
| /** | |||
| * 商户号 | |||
| */ | |||
| public String getMchId() { | |||
| return this.mchId; | |||
| } | |||
| /** | |||
| * 商户密钥 | |||
| */ | |||
| public String getMchKey() { | |||
| return this.mchKey; | |||
| } | |||
| /** | |||
| * 公众号appid | |||
| */ | |||
| public String getAppId() { | |||
| return this.appId; | |||
| } | |||
| /** | |||
| * 服务商模式下的子商户公众账号ID | |||
| */ | |||
| public String getSubAppId() { | |||
| return this.subAppId; | |||
| } | |||
| /** | |||
| * 服务商模式下的子商户号 | |||
| */ | |||
| public String getSubMchId() { | |||
| return this.subMchId; | |||
| } | |||
| /** | |||
| * 微信支付异步回掉地址,通知url必须为直接可访问的url,不能携带参数。 | |||
| */ | |||
| public String getNotifyUrl() { | |||
| return this.notifyUrl; | |||
| } | |||
| /** | |||
| * 交易类型 | |||
| * <pre> | |||
| * JSAPI--公众号支付 | |||
| * NATIVE--原生扫码支付 | |||
| * APP--app支付 | |||
| * </pre> | |||
| */ | |||
| public String getTradeType() { | |||
| return this.tradeType; | |||
| } | |||
| public SSLContext getSslContext() { | |||
| return this.sslContext; | |||
| } | |||
| /** | |||
| * 微信支付是否使用仿真测试环境 | |||
| * 默认不使用 | |||
| */ | |||
| public boolean useSandboxForWxPay() { | |||
| return false; | |||
| } | |||
| public SSLContext initSSLContext() { | |||
| if (null == mchId) { | |||
| throw new IllegalArgumentException("请確保mchId已設置"); | |||
| } | |||
| File file = new File(this.keyPath); | |||
| if (!file.exists()) { | |||
| throw new RuntimeException("证书文件:【" + file.getPath() + "】不存在!"); | |||
| } | |||
| try { | |||
| FileInputStream inputStream = new FileInputStream(file); | |||
| KeyStore keystore = KeyStore.getInstance("PKCS12"); | |||
| char[] partnerId2charArray = mchId.toCharArray(); | |||
| keystore.load(inputStream, partnerId2charArray); | |||
| this.sslContext = SSLContexts.custom().loadKeyMaterial(keystore, partnerId2charArray).build(); | |||
| return this.sslContext; | |||
| } catch (Exception e) { | |||
| throw new RuntimeException("证书文件有问题,请核实!", e); | |||
| } | |||
| } | |||
| } | |||
| @@ -1,130 +1,129 @@ | |||
| package me.chanjar.weixin.mp.bean.pay; | |||
| import java.beans.PropertyDescriptor; | |||
| import java.lang.reflect.Field; | |||
| import java.util.ArrayList; | |||
| import java.util.Arrays; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| import org.apache.commons.lang3.StringUtils; | |||
| import com.google.common.base.Function; | |||
| import com.google.common.collect.Maps; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import com.thoughtworks.xstream.converters.MarshallingContext; | |||
| import com.thoughtworks.xstream.converters.UnmarshallingContext; | |||
| import com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter; | |||
| import com.thoughtworks.xstream.converters.reflection.ReflectionProvider; | |||
| import com.thoughtworks.xstream.io.HierarchicalStreamReader; | |||
| import com.thoughtworks.xstream.io.HierarchicalStreamWriter; | |||
| import com.thoughtworks.xstream.mapper.Mapper; | |||
| import me.chanjar.weixin.mp.bean.pay.result.WxPayOrderNotifyResult; | |||
| public class WxPayOrderNotifyResultConverter extends AbstractReflectionConverter { | |||
| public WxPayOrderNotifyResultConverter(Mapper mapper, ReflectionProvider reflectionProvider) { | |||
| super(mapper, reflectionProvider); | |||
| } | |||
| @Override | |||
| @SuppressWarnings("rawtypes") | |||
| public boolean canConvert(Class type) { | |||
| return type.equals(WxPayOrderNotifyResult.class); | |||
| } | |||
| @Override | |||
| public void marshal(Object original, HierarchicalStreamWriter writer, MarshallingContext context) { | |||
| super.marshal(original, writer, context); | |||
| WxPayOrderNotifyResult obj = (WxPayOrderNotifyResult) original; | |||
| List<WxPayOrderNotifyCoupon> list = obj.getCouponList(); | |||
| if (list == null || list.size() == 0) { | |||
| return; | |||
| } | |||
| for (int i = 0; i < list.size(); i++) { | |||
| WxPayOrderNotifyCoupon coupon = list.get(i); | |||
| writer.startNode("coupon_id_" + i); | |||
| writer.setValue(coupon.getCouponId()); | |||
| writer.endNode(); | |||
| writer.startNode("coupon_type_" + i); | |||
| writer.setValue(coupon.getCouponType()); | |||
| writer.endNode(); | |||
| writer.startNode("coupon_fee_" + i); | |||
| writer.setValue(coupon.getCouponFee() + ""); | |||
| writer.endNode(); | |||
| } | |||
| } | |||
| @Override | |||
| protected void marshallField(MarshallingContext context, Object newObj, Field field) { | |||
| if (field.getName().equals("couponList")) { | |||
| return; | |||
| } else { | |||
| super.marshallField(context, newObj, field); | |||
| } | |||
| } | |||
| @Override | |||
| public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { | |||
| WxPayOrderNotifyResult obj = new WxPayOrderNotifyResult(); | |||
| List<Field> fields = new ArrayList<>(Arrays.asList(obj.getClass().getDeclaredFields())); | |||
| fields.addAll(Arrays.asList(obj.getClass().getSuperclass().getDeclaredFields())); | |||
| Map<String, Field> fieldMap = getFieldMap(fields); | |||
| List<WxPayOrderNotifyCoupon> coupons = new ArrayList<>(10); | |||
| while (reader.hasMoreChildren()) { | |||
| reader.moveDown(); | |||
| if (fieldMap.containsKey(reader.getNodeName())) { | |||
| Field field = fieldMap.get(reader.getNodeName()); | |||
| setFieldValue(context, obj, field); | |||
| } else if (StringUtils.startsWith(reader.getNodeName(), "coupon_id_")) { | |||
| String id = (String) context.convertAnother(obj, String.class); | |||
| getIndex(coupons, reader.getNodeName()).setCouponId(id); | |||
| } else if (StringUtils.startsWith(reader.getNodeName(), "coupon_type_")) { | |||
| String type = (String) context.convertAnother(obj, String.class); | |||
| getIndex(coupons, reader.getNodeName()).setCouponType(type); | |||
| } else if (StringUtils.startsWith(reader.getNodeName(), "coupon_fee_")) { | |||
| Integer fee = (Integer) context.convertAnother(obj, Integer.class); | |||
| getIndex(coupons, reader.getNodeName()).setCouponFee(fee); | |||
| } | |||
| reader.moveUp(); | |||
| } | |||
| obj.setCouponList(coupons); | |||
| return obj; | |||
| } | |||
| private void setFieldValue(UnmarshallingContext context, WxPayOrderNotifyResult obj, Field field) { | |||
| Object val = context.convertAnother(obj, field.getType()); | |||
| try { | |||
| if (val != null) { | |||
| PropertyDescriptor pd = new PropertyDescriptor(field.getName(), obj.getClass()); | |||
| pd.getWriteMethod().invoke(obj, val); | |||
| } | |||
| } catch (Exception e) { | |||
| } | |||
| } | |||
| private Map<String, Field> getFieldMap(List<Field> fields) { | |||
| Map<String, Field> fieldMap = Maps.uniqueIndex(fields, new Function<Field, String>() { | |||
| @Override | |||
| public String apply(Field field) { | |||
| if (field.isAnnotationPresent(XStreamAlias.class)) { | |||
| return field.getAnnotation(XStreamAlias.class).value(); | |||
| } | |||
| return field.getName(); | |||
| } | |||
| }); | |||
| return fieldMap; | |||
| } | |||
| private WxPayOrderNotifyCoupon getIndex(List<WxPayOrderNotifyCoupon> coupons, String nodeName) { | |||
| Integer index = Integer.valueOf(StringUtils.substring(nodeName, nodeName.lastIndexOf("_") + 1)); | |||
| if (index >= coupons.size() || coupons.get(index) == null) { | |||
| coupons.add(index, new WxPayOrderNotifyCoupon()); | |||
| } | |||
| return coupons.get(index); | |||
| } | |||
| } | |||
| package com.github.binarywang.wxpay.converter; | |||
| import com.github.binarywang.wxpay.bean.WxPayOrderNotifyCoupon; | |||
| import com.github.binarywang.wxpay.bean.result.WxPayOrderNotifyResult; | |||
| import com.google.common.base.Function; | |||
| import com.google.common.collect.Maps; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import com.thoughtworks.xstream.converters.MarshallingContext; | |||
| import com.thoughtworks.xstream.converters.UnmarshallingContext; | |||
| import com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter; | |||
| import com.thoughtworks.xstream.converters.reflection.ReflectionProvider; | |||
| import com.thoughtworks.xstream.io.HierarchicalStreamReader; | |||
| import com.thoughtworks.xstream.io.HierarchicalStreamWriter; | |||
| import com.thoughtworks.xstream.mapper.Mapper; | |||
| import org.apache.commons.lang3.StringUtils; | |||
| import java.beans.PropertyDescriptor; | |||
| import java.lang.reflect.Field; | |||
| import java.util.ArrayList; | |||
| import java.util.Arrays; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| public class WxPayOrderNotifyResultConverter extends AbstractReflectionConverter { | |||
| public WxPayOrderNotifyResultConverter(Mapper mapper, ReflectionProvider reflectionProvider) { | |||
| super(mapper, reflectionProvider); | |||
| } | |||
| @Override | |||
| @SuppressWarnings("rawtypes") | |||
| public boolean canConvert(Class type) { | |||
| return type.equals(WxPayOrderNotifyResult.class); | |||
| } | |||
| @Override | |||
| public void marshal(Object original, HierarchicalStreamWriter writer, MarshallingContext context) { | |||
| super.marshal(original, writer, context); | |||
| WxPayOrderNotifyResult obj = (WxPayOrderNotifyResult) original; | |||
| List<WxPayOrderNotifyCoupon> list = obj.getCouponList(); | |||
| if (list == null || list.size() == 0) { | |||
| return; | |||
| } | |||
| for (int i = 0; i < list.size(); i++) { | |||
| WxPayOrderNotifyCoupon coupon = list.get(i); | |||
| writer.startNode("coupon_id_" + i); | |||
| writer.setValue(coupon.getCouponId()); | |||
| writer.endNode(); | |||
| writer.startNode("coupon_type_" + i); | |||
| writer.setValue(coupon.getCouponType()); | |||
| writer.endNode(); | |||
| writer.startNode("coupon_fee_" + i); | |||
| writer.setValue(coupon.getCouponFee() + ""); | |||
| writer.endNode(); | |||
| } | |||
| } | |||
| @Override | |||
| protected void marshallField(MarshallingContext context, Object newObj, Field field) { | |||
| if (field.getName().equals("couponList")) { | |||
| return; | |||
| } else { | |||
| super.marshallField(context, newObj, field); | |||
| } | |||
| } | |||
| @Override | |||
| public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { | |||
| WxPayOrderNotifyResult obj = new WxPayOrderNotifyResult(); | |||
| List<Field> fields = new ArrayList<>(Arrays.asList(obj.getClass().getDeclaredFields())); | |||
| fields.addAll(Arrays.asList(obj.getClass().getSuperclass().getDeclaredFields())); | |||
| Map<String, Field> fieldMap = getFieldMap(fields); | |||
| List<WxPayOrderNotifyCoupon> coupons = new ArrayList<>(10); | |||
| while (reader.hasMoreChildren()) { | |||
| reader.moveDown(); | |||
| if (fieldMap.containsKey(reader.getNodeName())) { | |||
| Field field = fieldMap.get(reader.getNodeName()); | |||
| setFieldValue(context, obj, field); | |||
| } else if (StringUtils.startsWith(reader.getNodeName(), "coupon_id_")) { | |||
| String id = (String) context.convertAnother(obj, String.class); | |||
| getIndex(coupons, reader.getNodeName()).setCouponId(id); | |||
| } else if (StringUtils.startsWith(reader.getNodeName(), "coupon_type_")) { | |||
| String type = (String) context.convertAnother(obj, String.class); | |||
| getIndex(coupons, reader.getNodeName()).setCouponType(type); | |||
| } else if (StringUtils.startsWith(reader.getNodeName(), "coupon_fee_")) { | |||
| Integer fee = (Integer) context.convertAnother(obj, Integer.class); | |||
| getIndex(coupons, reader.getNodeName()).setCouponFee(fee); | |||
| } | |||
| reader.moveUp(); | |||
| } | |||
| obj.setCouponList(coupons); | |||
| return obj; | |||
| } | |||
| private void setFieldValue(UnmarshallingContext context, WxPayOrderNotifyResult obj, Field field) { | |||
| Object val = context.convertAnother(obj, field.getType()); | |||
| try { | |||
| if (val != null) { | |||
| PropertyDescriptor pd = new PropertyDescriptor(field.getName(), obj.getClass()); | |||
| pd.getWriteMethod().invoke(obj, val); | |||
| } | |||
| } catch (Exception e) { | |||
| } | |||
| } | |||
| private Map<String, Field> getFieldMap(List<Field> fields) { | |||
| Map<String, Field> fieldMap = Maps.uniqueIndex(fields, new Function<Field, String>() { | |||
| @Override | |||
| public String apply(Field field) { | |||
| if (field.isAnnotationPresent(XStreamAlias.class)) { | |||
| return field.getAnnotation(XStreamAlias.class).value(); | |||
| } | |||
| return field.getName(); | |||
| } | |||
| }); | |||
| return fieldMap; | |||
| } | |||
| private WxPayOrderNotifyCoupon getIndex(List<WxPayOrderNotifyCoupon> coupons, String nodeName) { | |||
| Integer index = Integer.valueOf(StringUtils.substring(nodeName, nodeName.lastIndexOf("_") + 1)); | |||
| if (index >= coupons.size() || coupons.get(index) == null) { | |||
| coupons.add(index, new WxPayOrderNotifyCoupon()); | |||
| } | |||
| return coupons.get(index); | |||
| } | |||
| } | |||
| @@ -1,23 +1,26 @@ | |||
| package me.chanjar.weixin.mp.api; | |||
| package com.github.binarywang.wxpay.service; | |||
| import com.github.binarywang.wxpay.bean.request.*; | |||
| import com.github.binarywang.wxpay.bean.result.*; | |||
| import com.github.binarywang.wxpay.config.WxPayConfig; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.mp.bean.pay.request.*; | |||
| import me.chanjar.weixin.mp.bean.pay.result.*; | |||
| import java.io.File; | |||
| import java.util.Map; | |||
| /** | |||
| * <pre> | |||
| * 微信支付相关接口 | |||
| * Created by Binary Wang on 2016/7/28. | |||
| * </pre> | |||
| * | |||
| * @author binarywang (https://github.com/binarywang) | |||
| */ | |||
| public interface WxMpPayService { | |||
| public interface WxPayService { | |||
| /** | |||
| * <pre> | |||
| * 查询订单(详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2) | |||
| * 查询订单(详见https://com.github.binarywang.wechat.pay.bean.pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2) | |||
| * 该接口提供所有微信支付订单的查询,商户可以通过查询订单接口主动查询订单状态,完成下一步的业务逻辑。 | |||
| * 需要调用查询接口的情况: | |||
| * ◆ 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知; | |||
| @@ -49,7 +52,7 @@ public interface WxMpPayService { | |||
| WxPayOrderCloseResult closeOrder(String outTradeNo) throws WxErrorException; | |||
| /** | |||
| * 统一下单(详见http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1) | |||
| * 统一下单(详见http://com.github.binarywang.wechat.pay.bean.pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1) | |||
| * 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识" | |||
| * 接口地址:https://api.mch.weixin.qq.com/pay/unifiedorder | |||
| * | |||
| @@ -65,6 +68,16 @@ public interface WxMpPayService { | |||
| */ | |||
| Map<String, String> getPayInfo(WxPayUnifiedOrderRequest request) throws WxErrorException; | |||
| /** | |||
| * 获取配置 | |||
| */ | |||
| WxPayConfig getConfig(); | |||
| /** | |||
| * 设置配置对象 | |||
| */ | |||
| void setConfig(WxPayConfig config); | |||
| /** | |||
| * <pre> | |||
| * 微信支付-申请退款 | |||
| @@ -81,7 +94,8 @@ public interface WxMpPayService { | |||
| * <pre> | |||
| * 微信支付-查询退款 | |||
| * 应用场景: | |||
| * 提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。 | |||
| * 提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,用零钱支付的退款20分钟内到账, | |||
| * 银行卡支付的退款3个工作日后重新查询退款状态。 | |||
| * 详见 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_5 | |||
| * 接口链接:https://api.mch.weixin.qq.com/pay/refundquery | |||
| * </pre> | |||
| @@ -93,11 +107,12 @@ public interface WxMpPayService { | |||
| * @param refundId 微信退款单号 | |||
| * @return 退款信息 | |||
| */ | |||
| WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, String outRefundNo, String refundId) throws WxErrorException; | |||
| WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, String outRefundNo, String refundId) | |||
| throws WxErrorException; | |||
| /** | |||
| * 读取支付结果通知 | |||
| * 详见http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7 | |||
| * 详见http://com.github.binarywang.wechat.pay.bean.pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7 | |||
| */ | |||
| WxPayOrderNotifyResult getOrderNotifyResult(String xmlData) throws WxErrorException; | |||
| @@ -1,20 +1,21 @@ | |||
| package me.chanjar.weixin.mp.api.impl; | |||
| package com.github.binarywang.wxpay.service.impl; | |||
| import com.github.binarywang.utils.qrcode.QrcodeUtils; | |||
| import com.github.binarywang.wxpay.bean.request.*; | |||
| import com.github.binarywang.wxpay.bean.result.*; | |||
| import com.github.binarywang.wxpay.config.WxPayConfig; | |||
| import com.github.binarywang.wxpay.service.WxPayService; | |||
| import com.google.common.collect.Maps; | |||
| import jodd.http.HttpRequest; | |||
| import jodd.http.HttpResponse; | |||
| import me.chanjar.weixin.common.bean.result.WxError; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.common.util.BeanUtils; | |||
| import me.chanjar.weixin.mp.api.WxMpConfigStorage; | |||
| import me.chanjar.weixin.mp.api.WxMpPayService; | |||
| import me.chanjar.weixin.mp.api.WxMpService; | |||
| import me.chanjar.weixin.mp.bean.pay.request.*; | |||
| import me.chanjar.weixin.mp.bean.pay.result.*; | |||
| import org.apache.commons.codec.digest.DigestUtils; | |||
| import org.apache.commons.lang3.ArrayUtils; | |||
| import org.apache.commons.lang3.CharEncoding; | |||
| import org.apache.commons.lang3.StringUtils; | |||
| import org.apache.http.Consts; | |||
| import org.apache.http.client.config.RequestConfig; | |||
| import org.apache.http.client.methods.CloseableHttpResponse; | |||
| import org.apache.http.client.methods.HttpPost; | |||
| import org.apache.http.conn.ssl.DefaultHostnameVerifier; | |||
| @@ -28,7 +29,7 @@ import org.slf4j.LoggerFactory; | |||
| import javax.net.ssl.SSLContext; | |||
| import java.io.File; | |||
| import java.io.IOException; | |||
| import java.io.UnsupportedEncodingException; | |||
| import java.util.*; | |||
| /** | |||
| @@ -36,28 +37,31 @@ import java.util.*; | |||
| * | |||
| * @author binarywang (https://github.com/binarywang) | |||
| */ | |||
| public class WxMpPayServiceImpl implements WxMpPayService { | |||
| public class WxPayServiceImpl implements WxPayService { | |||
| private static final String PAY_BASE_URL = "https://api.mch.weixin.qq.com"; | |||
| private static final String[] TRADE_TYPES = new String[]{"JSAPI", "NATIVE", "APP"}; | |||
| private static final String[] REFUND_ACCOUNT = new String[]{"REFUND_SOURCE_RECHARGE_FUNDS", "REFUND_SOURCE_UNSETTLED_FUNDS"}; | |||
| private static final String[] BILL_TYPE = new String[]{"ALL","REFUND","SUCCESS"};; | |||
| private static final String[] BILL_TYPE = new String[]{"ALL", "REFUND", "SUCCESS"}; | |||
| private final Logger log = LoggerFactory.getLogger(this.getClass()); | |||
| private WxMpService wxMpService; | |||
| public WxMpPayServiceImpl(WxMpService wxMpService) { | |||
| this.wxMpService = wxMpService; | |||
| private WxPayConfig config; | |||
| @Override | |||
| public WxPayConfig getConfig() { | |||
| return this.config; | |||
| } | |||
| private WxMpConfigStorage getConfig() { | |||
| return wxMpService.getWxMpConfigStorage(); | |||
| @Override | |||
| public void setConfig(WxPayConfig config) { | |||
| this.config = config; | |||
| } | |||
| private String getPayBaseUrl(){ | |||
| if(this.getConfig().useSandboxForWxPay()){ | |||
| private String getPayBaseUrl() { | |||
| if (this.getConfig().useSandboxForWxPay()) { | |||
| return PAY_BASE_URL + "/sandboxnew"; | |||
| } | |||
| return PAY_BASE_URL; | |||
| } | |||
| @@ -65,22 +69,27 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| public WxPayRefundResult refund(WxPayRefundRequest request) throws WxErrorException { | |||
| this.initRequest(request); | |||
| if (StringUtils.isBlank(request.getOpUserId())) { | |||
| request.setOpUserId(this.getConfig().getPartnerId()); | |||
| request.setOpUserId(this.getConfig().getMchId()); | |||
| } | |||
| this.checkParameters(request); | |||
| request.setSign(this.createSign(request)); | |||
| String url = this.getPayBaseUrl() + "/secapi/pay/refund"; | |||
| String responseContent = this.executeWithKey(url, request.toXML()); | |||
| String responseContent = this.postWithKey(url, request.toXML()); | |||
| WxPayRefundResult result = WxPayRefundResult.fromXML(responseContent, WxPayRefundResult.class); | |||
| this.checkResult(result); | |||
| return result; | |||
| } | |||
| @Override | |||
| public WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, String outRefundNo, String refundId) throws WxErrorException { | |||
| if ((StringUtils.isBlank(transactionId) && StringUtils.isBlank(outTradeNo) && StringUtils.isBlank(outRefundNo) && StringUtils.isBlank(refundId)) || | |||
| (StringUtils.isNotBlank(transactionId) && StringUtils.isNotBlank(outTradeNo) && StringUtils.isNotBlank(outRefundNo) && StringUtils.isNotBlank(refundId))) { | |||
| public WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, | |||
| String outRefundNo, String refundId) | |||
| throws WxErrorException { | |||
| if ((StringUtils.isBlank(transactionId) && StringUtils.isBlank(outTradeNo) | |||
| && StringUtils.isBlank(outRefundNo) && StringUtils.isBlank(refundId)) || | |||
| (StringUtils.isNotBlank(transactionId) && StringUtils.isNotBlank(outTradeNo) | |||
| && StringUtils.isNotBlank(outRefundNo) && StringUtils.isNotBlank(refundId))) { | |||
| throw new IllegalArgumentException("transaction_id , out_trade_no,out_refund_no, refund_id 必须四选一"); | |||
| } | |||
| @@ -93,18 +102,17 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| request.setSign(this.createSign(request)); | |||
| String url = this.getPayBaseUrl() + "/pay/refundquery"; | |||
| String responseContent = this.executeRequest(url, request.toXML()); | |||
| String responseContent = this.post(url, request.toXML()); | |||
| WxPayRefundQueryResult result = WxPayRefundQueryResult.fromXML(responseContent, WxPayRefundQueryResult.class); | |||
| result.composeRefundRecords(); | |||
| this.checkResult(result); | |||
| return result; | |||
| } | |||
| private void checkResult(WxPayBaseResult result) throws WxErrorException { | |||
| //校验返回结果签名 | |||
| Map<String, String> map = result.toMap(); | |||
| if (result.getSign() != null &&!this.checkSign(map)) { | |||
| if (result.getSign() != null && !this.checkSign(map)) { | |||
| log.debug("校验结果签名失败,参数:{}", map); | |||
| throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg("参数格式校验错误!").build()); | |||
| } | |||
| @@ -112,12 +120,28 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| //校验结果是否成功 | |||
| if (!"SUCCESS".equalsIgnoreCase(result.getReturnCode()) | |||
| || !"SUCCESS".equalsIgnoreCase(result.getResultCode())) { | |||
| WxError error = WxError.newBuilder().setErrorCode(-1) | |||
| .setErrorMsg("返回代码: " + result.getReturnCode() + ", 返回信息: " | |||
| + result.getReturnMsg() + ", 结果代码: " + result.getResultCode() + ", 错误代码: " | |||
| + result.getErrCode() + ", 错误详情: " + result.getErrCodeDes()) | |||
| StringBuilder errorMsg = new StringBuilder(); | |||
| if (result.getReturnCode() != null) { | |||
| errorMsg.append("返回代码:" + result.getReturnCode()); | |||
| } | |||
| if (result.getReturnMsg() != null) { | |||
| errorMsg.append(",返回信息:" + result.getReturnMsg()); | |||
| } | |||
| if (result.getResultCode() != null) { | |||
| errorMsg.append(",结果代码:" + result.getResultCode()); | |||
| } | |||
| if (result.getErrCode() != null) { | |||
| errorMsg.append(",错误代码:" + result.getErrCode()); | |||
| } | |||
| if (result.getErrCodeDes() != null) { | |||
| errorMsg.append(",错误详情:" + result.getErrCodeDes()); | |||
| } | |||
| WxError error = WxError.newBuilder() | |||
| .setErrorCode(-1) | |||
| .setErrorMsg(errorMsg.toString()) | |||
| .build(); | |||
| log.error("结果业务代码异常,参数:{},详细:{}", map, error); | |||
| log.error("\n结果业务代码异常,返回結果:{},\n{}", map, error); | |||
| throw new WxErrorException(error); | |||
| } | |||
| } | |||
| @@ -129,8 +153,9 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| throw new IllegalArgumentException("tar_type值如果存在,只能为GZIP"); | |||
| } | |||
| if ( !ArrayUtils.contains(BILL_TYPE, request.getBillType())) { | |||
| throw new IllegalArgumentException("bill_tpye目前必须为" + Arrays.toString(BILL_TYPE) + "其中之一,实际值:" + request.getBillType()); | |||
| if (!ArrayUtils.contains(BILL_TYPE, request.getBillType())) { | |||
| throw new IllegalArgumentException("bill_tpye目前必须为" + Arrays.toString(BILL_TYPE) | |||
| + "其中之一,实际值:" + request.getBillType()); | |||
| } | |||
| } | |||
| @@ -140,7 +165,8 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| if (StringUtils.isNotBlank(request.getRefundAccount())) { | |||
| if (!ArrayUtils.contains(REFUND_ACCOUNT, request.getRefundAccount())) { | |||
| throw new IllegalArgumentException("refund_account目前必须为" + Arrays.toString(REFUND_ACCOUNT) + "其中之一,实际值:" + request.getRefundAccount()); | |||
| throw new IllegalArgumentException("refund_account目前必须为" + Arrays.toString(REFUND_ACCOUNT) | |||
| + "其中之一,实际值:" + request.getRefundAccount()); | |||
| } | |||
| } | |||
| @@ -152,7 +178,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| @Override | |||
| public WxPayOrderNotifyResult getOrderNotifyResult(String xmlData) throws WxErrorException { | |||
| try { | |||
| log.trace("微信支付回调参数详细:{}", xmlData); | |||
| log.debug("微信支付回调参数详细:{}", xmlData); | |||
| WxPayOrderNotifyResult result = WxPayOrderNotifyResult.fromXML(xmlData); | |||
| log.debug("微信支付回调结果对象:{}", result); | |||
| this.checkResult(result); | |||
| @@ -177,7 +203,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| //裂变红包 | |||
| url = this.getPayBaseUrl() + "/mmpaymkttransfers/sendgroupredpack"; | |||
| } | |||
| String responseContent = this.executeWithKey(url, request.toXML()); | |||
| String responseContent = this.postWithKey(url, request.toXML()); | |||
| WxPaySendRedpackResult result = WxPaySendRedpackResult.fromXML(responseContent, WxPaySendRedpackResult.class); | |||
| //毋须校验,因为没有返回签名信息 | |||
| // this.checkResult(result); | |||
| @@ -193,7 +219,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| request.setSign(this.createSign(request)); | |||
| String url = this.getPayBaseUrl() + "/mmpaymkttransfers/gethbinfo"; | |||
| String responseContent = this.executeWithKey(url, request.toXML()); | |||
| String responseContent = this.postWithKey(url, request.toXML()); | |||
| WxPayRedpackQueryResult result = WxPayRedpackQueryResult.fromXML(responseContent, WxPayRedpackQueryResult.class); | |||
| this.checkResult(result); | |||
| return result; | |||
| @@ -213,7 +239,11 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| request.setSign(this.createSign(request)); | |||
| String url = this.getPayBaseUrl() + "/pay/orderquery"; | |||
| String responseContent = this.executeRequest(url, request.toXML()); | |||
| String responseContent = this.post(url, request.toXML()); | |||
| if (StringUtils.isBlank(responseContent)) { | |||
| throw new WxErrorException(WxError.newBuilder().setErrorMsg("无响应结果").build()); | |||
| } | |||
| WxPayOrderQueryResult result = WxPayOrderQueryResult.fromXML(responseContent, WxPayOrderQueryResult.class); | |||
| result.composeCoupons(); | |||
| this.checkResult(result); | |||
| @@ -223,17 +253,17 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| @Override | |||
| public WxPayOrderCloseResult closeOrder(String outTradeNo) throws WxErrorException { | |||
| if (StringUtils.isBlank(outTradeNo)) { | |||
| throw new IllegalArgumentException("out_trade_no 不能为空"); | |||
| throw new IllegalArgumentException("out_trade_no不能为空"); | |||
| } | |||
| WxPayOrderCloseRequest request = new WxPayOrderCloseRequest(); | |||
| request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); | |||
| initRequest(request); | |||
| this.initRequest(request); | |||
| request.setSign(this.createSign(request)); | |||
| String url = this.getPayBaseUrl() + "/pay/closeorder"; | |||
| String responseContent = this.executeRequest(url, request.toXML()); | |||
| WxPayOrderCloseResult result = WxPayOrderCloseResult.fromXML(responseContent, WxPayOrderCloseResult.class); | |||
| String responseContent = this.post(url, request.toXML()); | |||
| WxPayOrderCloseResult result = WxPayBaseResult.fromXML(responseContent, WxPayOrderCloseResult.class); | |||
| this.checkResult(result); | |||
| return result; | |||
| @@ -242,23 +272,20 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| @Override | |||
| public WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) | |||
| throws WxErrorException { | |||
| this.initRequest(request); | |||
| if (StringUtils.isBlank(request.getNotifyURL())) { | |||
| request.setNotifyURL(getConfig().getNotifyURL()); | |||
| request.setNotifyURL(getConfig().getNotifyUrl()); | |||
| } | |||
| if (StringUtils.isBlank(request.getTradeType())) { | |||
| request.setTradeType(getConfig().getTradeType()); | |||
| } | |||
| checkParameters(request);//校验参数 | |||
| this.checkParameters(request);//校验参数 | |||
| request.setSign(this.createSign(request)); | |||
| String url = this.getPayBaseUrl() + "/pay/unifiedorder"; | |||
| String xmlParam = request.toXML(); | |||
| log.debug("微信统一下单接口,URL:{},参数:{}", url, xmlParam); | |||
| String responseContent = this.executeRequest(url, xmlParam); | |||
| log.debug("微信统一下单接口,URL:{},结果:{}", url, responseContent); | |||
| String responseContent = this.post(url, request.toXML()); | |||
| WxPayUnifiedOrderResult result = WxPayUnifiedOrderResult.fromXML(responseContent, WxPayUnifiedOrderResult.class); | |||
| this.checkResult(result); | |||
| return result; | |||
| @@ -266,19 +293,19 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| private void initRequest(WxPayBaseRequest request) { | |||
| if (StringUtils.isBlank(request.getAppid())) { | |||
| request.setAppid(getConfig().getAppId()); | |||
| request.setAppid(this.getConfig().getAppId()); | |||
| } | |||
| if (StringUtils.isBlank(request.getMchId())) { | |||
| request.setMchId(getConfig().getPartnerId()); | |||
| request.setMchId(this.getConfig().getMchId()); | |||
| } | |||
| if (StringUtils.isBlank(request.getSubAppId())) { | |||
| request.setAppid(getConfig().getSubAppId()); | |||
| request.setSubAppId(this.getConfig().getSubAppId()); | |||
| } | |||
| if (StringUtils.isBlank(request.getSubMchId())) { | |||
| request.setMchId(getConfig().getSubMchId()); | |||
| request.setSubMchId(this.getConfig().getSubMchId()); | |||
| } | |||
| if (StringUtils.isBlank(request.getNonceStr())) { | |||
| @@ -307,13 +334,14 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| WxPayUnifiedOrderResult unifiedOrderResult = this.unifiedOrder(request); | |||
| String prepayId = unifiedOrderResult.getPrepayId(); | |||
| if (StringUtils.isBlank(prepayId)) { | |||
| throw new RuntimeException(String.format("Failed to get prepay id due to error code '%s'(%s).", | |||
| throw new RuntimeException(String.format("无法获取prepay id,错误代码: '%s',信息:%s。", | |||
| unifiedOrderResult.getErrCode(), unifiedOrderResult.getErrCodeDes())); | |||
| } | |||
| Map<String, String> payInfo = new HashMap<>(); | |||
| payInfo.put("appId", getConfig().getAppId()); | |||
| // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符 | |||
| // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。 | |||
| // 但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符 | |||
| payInfo.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000)); | |||
| payInfo.put("nonceStr", String.valueOf(System.currentTimeMillis())); | |||
| payInfo.put("package", "prepay_id=" + prepayId); | |||
| @@ -333,7 +361,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| String url = this.getPayBaseUrl() + "/mmpaymkttransfers/promotion/transfers"; | |||
| String responseContent = this.executeWithKey(url, request.toXML()); | |||
| String responseContent = this.postWithKey(url, request.toXML()); | |||
| WxEntPayResult result = WxEntPayResult.fromXML(responseContent, WxEntPayResult.class); | |||
| this.checkResult(result); | |||
| return result; | |||
| @@ -346,7 +374,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| request.setSign(this.createSign(request)); | |||
| String url = this.getPayBaseUrl() + "/mmpaymkttransfers/gettransferinfo"; | |||
| String responseContent = this.executeWithKey(url, request.toXML()); | |||
| String responseContent = this.postWithKey(url, request.toXML()); | |||
| WxEntPayQueryResult result = WxEntPayQueryResult.fromXML(responseContent, WxEntPayQueryResult.class); | |||
| this.checkResult(result); | |||
| return result; | |||
| @@ -358,7 +386,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| StringBuilder codeUrl = new StringBuilder("weixin://wxpay/bizpayurl?"); | |||
| Map<String, String> params = Maps.newHashMap(); | |||
| params.put("appid", this.getConfig().getAppId()); | |||
| params.put("mch_id", this.getConfig().getPartnerId()); | |||
| params.put("mch_id", this.getConfig().getMchId()); | |||
| params.put("product_id", productId); | |||
| params.put("time_stamp", String.valueOf(System.currentTimeMillis())); | |||
| params.put("nonce_str", String.valueOf(System.currentTimeMillis())); | |||
| @@ -393,7 +421,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| request.setSign(this.createSign(request)); | |||
| String url = this.getPayBaseUrl() + "/payitil/report"; | |||
| String responseContent = this.wxMpService.post(url, request.toXML()); | |||
| String responseContent = this.post(url, request.toXML()); | |||
| WxPayCommonResult result = WxPayBaseResult.fromXML(responseContent, WxPayCommonResult.class); | |||
| this.checkResult(result); | |||
| } | |||
| @@ -411,7 +439,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| String url = this.getPayBaseUrl() + "/pay/downloadbill"; | |||
| //TODO 返回的内容可能是文件流,也有可能是xml,需要区分对待 | |||
| String responseContent = this.wxMpService.post(url, request.toXML()); | |||
| String responseContent = this.post(url, request.toXML()); | |||
| WxPayCommonResult result = WxPayBaseResult.fromXML(responseContent, WxPayCommonResult.class); | |||
| this.checkResult(result); | |||
| @@ -419,42 +447,41 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| return null; | |||
| } | |||
| private String executeRequest(String url, String requestStr) throws WxErrorException { | |||
| HttpPost httpPost = new HttpPost(url); | |||
| if (this.wxMpService.getHttpProxy() != null) { | |||
| httpPost.setConfig(RequestConfig.custom().setProxy(this.wxMpService.getHttpProxy()).build()); | |||
| private String post(String url, String xmlParam) { | |||
| String requestString = null; | |||
| try { | |||
| requestString = new String(xmlParam.getBytes(CharEncoding.UTF_8), CharEncoding.ISO_8859_1); | |||
| } catch (UnsupportedEncodingException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| try (CloseableHttpClient httpclient = HttpClients.custom().build()) { | |||
| httpPost.setEntity(new StringEntity(new String(requestStr.getBytes("UTF-8"), "ISO-8859-1"))); | |||
| try (CloseableHttpResponse response = httpclient.execute(httpPost)) { | |||
| String result = EntityUtils.toString(response.getEntity(), Consts.UTF_8); | |||
| this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", url, requestStr, result); | |||
| return result; | |||
| } | |||
| } catch (IOException e) { | |||
| this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[EXCEPTION]: {}", url, requestStr, e.getMessage()); | |||
| throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg(e.getMessage()).build(), e); | |||
| } finally { | |||
| httpPost.releaseConnection(); | |||
| HttpRequest request = HttpRequest.post(url).body(requestString); | |||
| HttpResponse response = request.send(); | |||
| String responseString = null; | |||
| try { | |||
| responseString = new String(response.bodyText().getBytes(CharEncoding.ISO_8859_1), CharEncoding.UTF_8); | |||
| } catch (UnsupportedEncodingException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", url, xmlParam, responseString); | |||
| return responseString; | |||
| } | |||
| private String executeWithKey(String url, String requestStr) throws WxErrorException { | |||
| /** | |||
| * 由于暂时未找到使用jodd-http实现证书配置的办法,故而暂时使用httpclient | |||
| */ | |||
| private String postWithKey(String url, String requestStr) throws WxErrorException { | |||
| try { | |||
| SSLContext sslContext = getConfig().getSslContext(); | |||
| SSLContext sslContext = this.getConfig().getSslContext(); | |||
| if (null == sslContext) { | |||
| throw new IllegalArgumentException("请先初始化配置类(即WxMpConfigStorage的实现类)中的SSLContext!"); | |||
| sslContext = this.getConfig().initSSLContext(); | |||
| } | |||
| SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new String[]{"TLSv1"}, null, | |||
| new DefaultHostnameVerifier()); | |||
| SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, | |||
| new String[]{"TLSv1"}, null, new DefaultHostnameVerifier()); | |||
| HttpPost httpPost = new HttpPost(url); | |||
| if (this.wxMpService.getHttpProxy() != null) { | |||
| httpPost.setConfig(RequestConfig.custom().setProxy(this.wxMpService.getHttpProxy()).build()); | |||
| } | |||
| try (CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build()) { | |||
| httpPost.setEntity(new StringEntity(new String(requestStr.getBytes("UTF-8"), "ISO-8859-1"))); | |||
| @@ -474,7 +501,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| @Override | |||
| public String createSign(Object xmlBean) { | |||
| return this.createSign(BeanUtils.xmlBean2Map(xmlBean), getConfig().getPartnerKey()); | |||
| return this.createSign(BeanUtils.xmlBean2Map(xmlBean), this.getConfig().getMchKey()); | |||
| } | |||
| @Override | |||
| @@ -484,14 +511,14 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| @Override | |||
| public String createSign(Map<String, String> params) { | |||
| return this.createSign(params, this.getConfig().getPartnerKey()); | |||
| return this.createSign(params, this.getConfig().getMchKey()); | |||
| } | |||
| @Override | |||
| public String createSign(Map<String, String> params, String signKey) { | |||
| if(this.getConfig().useSandboxForWxPay()){ | |||
| if (this.getConfig().useSandboxForWxPay()) { | |||
| //使用仿真测试环境 | |||
| //TODO 目前测试发现,以下两行代码都会出问题,所以暂不建议使用这个仿真测试环境 | |||
| //TODO 目前测试发现,以下两行代码都会出问题,所以暂不建议使用仿真测试环境 | |||
| signKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456"; | |||
| //return "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456"; | |||
| } | |||
| @@ -512,7 +539,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| @Override | |||
| public boolean checkSign(Object xmlBean) { | |||
| return this.checkSign(BeanUtils.xmlBean2Map(xmlBean), getConfig().getPartnerKey()); | |||
| return this.checkSign(BeanUtils.xmlBean2Map(xmlBean), getConfig().getMchKey()); | |||
| } | |||
| @Override | |||
| @@ -522,7 +549,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| @Override | |||
| public boolean checkSign(Map<String, String> params) { | |||
| return this.checkSign(params, getConfig().getPartnerKey()); | |||
| return this.checkSign(params, getConfig().getMchKey()); | |||
| } | |||
| @Override | |||
| @@ -1,5 +1,6 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.result; | |||
| package com.github.binarywang.wxpay.bean.result; | |||
| import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult; | |||
| import org.testng.*; | |||
| import org.testng.annotations.*; | |||
| @@ -1,4 +1,4 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.result; | |||
| package com.github.binarywang.wxpay.bean.result; | |||
| import org.testng.*; | |||
| import org.testng.annotations.*; | |||
| @@ -1,4 +1,4 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.result; | |||
| package com.github.binarywang.wxpay.bean.result; | |||
| import org.testng.*; | |||
| @@ -1,4 +1,4 @@ | |||
| package me.chanjar.weixin.mp.bean.pay.result; | |||
| package com.github.binarywang.wxpay.bean.result; | |||
| import com.thoughtworks.xstream.XStream; | |||
| import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||
| @@ -0,0 +1,196 @@ | |||
| package com.github.binarywang.wxpay.service.impl; | |||
| import com.github.binarywang.utils.qrcode.QrcodeUtils; | |||
| import com.github.binarywang.wxpay.bean.request.*; | |||
| import com.github.binarywang.wxpay.bean.result.*; | |||
| import com.github.binarywang.wxpay.service.WxPayService; | |||
| import com.github.binarywang.wxpay.testbase.ApiTestModule; | |||
| import com.github.binarywang.wxpay.testbase.TestPayConfig; | |||
| import com.google.inject.Inject; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import org.testng.annotations.*; | |||
| import java.io.File; | |||
| import java.nio.file.Files; | |||
| import java.nio.file.Path; | |||
| import static org.testng.Assert.*; | |||
| /** | |||
| * 测试支付相关接口 | |||
| * Created by Binary Wang on 2016/7/28. | |||
| * | |||
| * @author binarywang (https://github.com/binarywang) | |||
| */ | |||
| @Test | |||
| @Guice(modules = ApiTestModule.class) | |||
| public class WxMpPayServiceImplTest { | |||
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); | |||
| @Inject | |||
| protected WxPayService wxService; | |||
| @Test | |||
| public void testGetPayInfo() throws Exception { | |||
| } | |||
| @Test | |||
| public void testDownloadBill() throws Exception { | |||
| File file = this.wxService.downloadBill("20170101", "ALL", "GZIP", "1111111"); | |||
| assertNotNull(file); | |||
| //必填字段为空时,抛出异常 | |||
| this.wxService.downloadBill("", "", "", null); | |||
| } | |||
| @Test | |||
| public void testReport() throws Exception { | |||
| WxPayReportRequest request = new WxPayReportRequest(); | |||
| request.setInterfaceUrl("hahahah"); | |||
| request.setSignType("HMAC-SHA256");//貌似接口未校验此字段 | |||
| request.setExecuteTime(1000); | |||
| request.setReturnCode("aaa"); | |||
| request.setResultCode("aaa"); | |||
| request.setUserIp("8.8.8"); | |||
| this.wxService.report(request); | |||
| } | |||
| /** | |||
| * Test method for {@link WxPayService#refund(WxPayRefundRequest)} . | |||
| */ | |||
| @Test//(dependsOnMethods = {"setSSLKey"}) | |||
| public void testRefund() throws Exception { | |||
| WxPayRefundResult result = this.wxService.refund( | |||
| WxPayRefundRequest.newBuilder() | |||
| .outRefundNo("aaa") | |||
| .outTradeNo("1111") | |||
| .totalFee(1222) | |||
| .refundFee(111) | |||
| .build()); | |||
| this.logger.info(result.toString()); | |||
| } | |||
| /** | |||
| * Test method for {@link WxPayService#refundQuery(java.lang.String, java.lang.String, java.lang.String, java.lang.String)} . | |||
| */ | |||
| @Test | |||
| public void testRefundQuery() throws Exception { | |||
| WxPayRefundQueryResult result; | |||
| result = this.wxService.refundQuery("1", "", "", ""); | |||
| this.logger.info(result.toString()); | |||
| result = this.wxService.refundQuery("", "2", "", ""); | |||
| this.logger.info(result.toString()); | |||
| result = this.wxService.refundQuery("", "", "3", ""); | |||
| this.logger.info(result.toString()); | |||
| result = this.wxService.refundQuery("", "", "", "4"); | |||
| this.logger.info(result.toString()); | |||
| //测试四个参数都填的情况,应该报异常的 | |||
| result = this.wxService.refundQuery("1", "2", "3", "4"); | |||
| this.logger.info(result.toString()); | |||
| } | |||
| /** | |||
| * Test method for {@link WxPayService#sendRedpack(WxPaySendRedpackRequest)} . | |||
| */ | |||
| @Test//(dependsOnMethods = {"setSSLKey"}) | |||
| public void testSendRedpack() throws Exception { | |||
| WxPaySendRedpackRequest request = new WxPaySendRedpackRequest(); | |||
| request.setActName("abc"); | |||
| request.setClientIp("aaa"); | |||
| request.setMchBillNo("aaaa"); | |||
| request.setReOpenid(((TestPayConfig) this.wxService.getConfig()).getOpenid()); | |||
| WxPaySendRedpackResult redpackResult = this.wxService.sendRedpack(request); | |||
| this.logger.info(redpackResult.toString()); | |||
| } | |||
| /** | |||
| * Test method for {@link WxPayService#queryRedpack(java.lang.String)}. | |||
| */ | |||
| @Test//(dependsOnMethods = {"setSSLKey"}) | |||
| public void testQueryRedpack() throws Exception { | |||
| WxPayRedpackQueryResult redpackResult = this.wxService.queryRedpack("aaaa"); | |||
| this.logger.info(redpackResult.toString()); | |||
| } | |||
| /** | |||
| * Test method for {@link WxPayService#unifiedOrder(WxPayUnifiedOrderRequest)}. | |||
| */ | |||
| @Test | |||
| public void testUnifiedOrder() throws WxErrorException { | |||
| WxPayUnifiedOrderResult result = this.wxService | |||
| .unifiedOrder(WxPayUnifiedOrderRequest.builder() | |||
| .body("我去") | |||
| .totalFee(1) | |||
| .spbillCreateIp("111111") | |||
| .notifyURL("111111") | |||
| .tradeType("JSAPI") | |||
| .openid("122") | |||
| .outTradeNo("111111") | |||
| .build()); | |||
| this.logger.info(result.toString()); | |||
| } | |||
| /** | |||
| * Test method for {@link WxPayService#queryOrder(java.lang.String, java.lang.String)} . | |||
| */ | |||
| @Test | |||
| public void testQueryOrder() throws WxErrorException { | |||
| this.logger.info(this.wxService.queryOrder("11212121", null).toString()); | |||
| this.logger.info(this.wxService.queryOrder(null, "11111").toString()); | |||
| } | |||
| /** | |||
| * Test method for {@link WxPayService#closeOrder(java.lang.String)} . | |||
| */ | |||
| @Test | |||
| public void testCloseOrder() throws WxErrorException { | |||
| this.logger.info(this.wxService.closeOrder("11212121").toString()); | |||
| } | |||
| /** | |||
| * Test method for {@link WxPayService#entPay(WxEntPayRequest)}. | |||
| */ | |||
| @Test//(dependsOnMethods = {"setSSLKey"}) | |||
| public void testEntPay() throws WxErrorException { | |||
| WxEntPayRequest request = new WxEntPayRequest(); | |||
| this.logger.info(this.wxService.entPay(request).toString()); | |||
| } | |||
| /** | |||
| * Test method for {@link WxPayService#queryEntPay(java.lang.String)}. | |||
| */ | |||
| @Test//(dependsOnMethods = {"setSSLKey"}) | |||
| public void testQueryEntPay() throws WxErrorException { | |||
| this.logger.info(this.wxService.queryEntPay("11212121").toString()); | |||
| } | |||
| @Test | |||
| public void testCreateScanPayQrcodeMode1() throws Exception { | |||
| String productId = "abc"; | |||
| byte[] bytes = this.wxService.createScanPayQrcodeMode1(productId, null, null); | |||
| Path qrcodeFilePath = Files.createTempFile("qrcode_", ".jpg"); | |||
| Files.write(qrcodeFilePath, bytes); | |||
| String qrcodeContent = QrcodeUtils.decodeQrcode(qrcodeFilePath.toFile()); | |||
| this.logger.info(qrcodeContent); | |||
| assertTrue(qrcodeContent.startsWith("weixin://wxpay/bizpayurl?")); | |||
| assertTrue(qrcodeContent.contains("product_id=" + productId)); | |||
| } | |||
| @Test | |||
| public void testCreateScanPayQrcodeMode2() throws Exception { | |||
| String qrcodeContent = "abc"; | |||
| byte[] bytes = this.wxService.createScanPayQrcodeMode2(qrcodeContent, null, null); | |||
| Path qrcodeFilePath = Files.createTempFile("qrcode_", ".jpg"); | |||
| Files.write(qrcodeFilePath, bytes); | |||
| assertEquals(QrcodeUtils.decodeQrcode(qrcodeFilePath.toFile()), qrcodeContent); | |||
| } | |||
| } | |||
| @@ -0,0 +1,38 @@ | |||
| package com.github.binarywang.wxpay.testbase; | |||
| import com.github.binarywang.wxpay.config.WxPayConfig; | |||
| import com.github.binarywang.wxpay.service.WxPayService; | |||
| import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; | |||
| import com.google.inject.Binder; | |||
| import com.google.inject.Module; | |||
| import com.thoughtworks.xstream.XStream; | |||
| import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||
| import java.io.IOException; | |||
| import java.io.InputStream; | |||
| public class ApiTestModule implements Module { | |||
| @Override | |||
| public void configure(Binder binder) { | |||
| try (InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml")) { | |||
| TestPayConfig config = this.fromXml(TestPayConfig.class, is1); | |||
| WxPayService wxService = new WxPayServiceImpl(); | |||
| wxService.setConfig(config); | |||
| binder.bind(WxPayService.class).toInstance(wxService); | |||
| binder.bind(WxPayConfig.class).toInstance(config); | |||
| } catch (IOException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| @SuppressWarnings("unchecked") | |||
| private <T> T fromXml(Class<T> clazz, InputStream is) { | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.alias("xml", clazz); | |||
| xstream.processAnnotations(clazz); | |||
| return (T) xstream.fromXML(is); | |||
| } | |||
| } | |||
| @@ -0,0 +1,31 @@ | |||
| package com.github.binarywang.wxpay.testbase; | |||
| import com.github.binarywang.wxpay.config.WxPayConfig; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import org.apache.http.ssl.SSLContexts; | |||
| import javax.net.ssl.SSLContext; | |||
| import java.io.File; | |||
| import java.io.FileInputStream; | |||
| import java.security.KeyStore; | |||
| @XStreamAlias("xml") | |||
| public class TestPayConfig extends WxPayConfig { | |||
| private String openid; | |||
| public String getOpenid() { | |||
| return openid; | |||
| } | |||
| public void setOpenid(String openid) { | |||
| this.openid = openid; | |||
| } | |||
| @Override | |||
| public boolean useSandboxForWxPay() { | |||
| //沙箱环境不成熟,有问题无法使用,暂时屏蔽掉 | |||
| // return true; | |||
| return false; | |||
| } | |||
| } | |||
| @@ -0,0 +1,16 @@ | |||
| <configuration> | |||
| <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> | |||
| <!-- encoders are assigned the type | |||
| ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --> | |||
| <encoder> | |||
| <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> | |||
| </encoder> | |||
| </appender> | |||
| <root level="debug"> | |||
| <appender-ref ref="STDOUT"/> | |||
| </root> | |||
| <logger name="com.github.binarywang.wxpay" level="debug"/> | |||
| </configuration> | |||
| @@ -0,0 +1,13 @@ | |||
| <xml> | |||
| <appId>公众号appid</appId> | |||
| <mchId>微信商户平台ID</mchId> | |||
| <mchKey>商户平台设置的API密钥</mchKey> | |||
| <!--- | |||
| 以下为官网文档所提供样例参数,仅供部分接口测试使用 | |||
| <appId>wxd930ea5d5a258f4f</appId> | |||
| <mchId>10000100</mchId> | |||
| <mchKey>192006250b4c09247ec02edce69f6a2d</mchKey> | |||
| --> | |||
| <keyPath>商户平台的证书文件地址</keyPath> | |||
| <openid>某个openId</openid> | |||
| </xml> | |||