| @@ -1,5 +1,6 @@ | |||||
| package com.github.binarywang.wxpay.bean.request; | package com.github.binarywang.wxpay.bean.request; | ||||
| import com.github.binarywang.wxpay.constant.WxPayConstants.BillType; | |||||
| import com.github.binarywang.wxpay.exception.WxPayException; | import com.github.binarywang.wxpay.exception.WxPayException; | ||||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
| import me.chanjar.weixin.common.annotation.Required; | import me.chanjar.weixin.common.annotation.Required; | ||||
| @@ -17,7 +18,8 @@ import java.util.Arrays; | |||||
| */ | */ | ||||
| @XStreamAlias("xml") | @XStreamAlias("xml") | ||||
| public class WxPayDownloadBillRequest extends WxPayBaseRequest { | public class WxPayDownloadBillRequest extends WxPayBaseRequest { | ||||
| private static final String[] BILL_TYPE = new String[]{"ALL", "REFUND", "SUCCESS"}; | |||||
| private static final String[] BILL_TYPES = new String[]{BillType.ALL, BillType.SUCCESS, BillType.REFUND, BillType.RECHARGE_REFUND}; | |||||
| private static final String TAR_TYPE_GZIP = "GZIP"; | |||||
| /** | /** | ||||
| * <pre> | * <pre> | ||||
| @@ -130,13 +132,13 @@ public class WxPayDownloadBillRequest extends WxPayBaseRequest { | |||||
| @Override | @Override | ||||
| protected void checkConstraints() throws WxPayException { | protected void checkConstraints() throws WxPayException { | ||||
| if (StringUtils.isNotBlank(this.getTarType()) && !"GZIP".equals(this.getTarType())) { | |||||
| if (StringUtils.isNotBlank(this.getTarType()) && !TAR_TYPE_GZIP.equals(this.getTarType())) { | |||||
| throw new WxPayException("tar_type值如果存在,只能为GZIP"); | throw new WxPayException("tar_type值如果存在,只能为GZIP"); | ||||
| } | } | ||||
| if (!ArrayUtils.contains(BILL_TYPE, this.getBillType())) { | |||||
| throw new WxPayException(String.format("bill_tpye目前必须为%s其中之一,实际值:%s", | |||||
| Arrays.toString(BILL_TYPE), this.getBillType())); | |||||
| if (!ArrayUtils.contains(BILL_TYPES, this.getBillType())) { | |||||
| throw new WxPayException(String.format("bill_type目前必须为%s其中之一,实际值:%s", | |||||
| Arrays.toString(BILL_TYPES), this.getBillType())); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,87 @@ | |||||
| package com.github.binarywang.wxpay.constant; | |||||
| /** | |||||
| * <pre> | |||||
| * 微信支付常量类 | |||||
| * Created by Binary Wang on 2017-8-24. | |||||
| * </pre> | |||||
| * | |||||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
| */ | |||||
| public class WxPayConstants { | |||||
| /** | |||||
| * 校验用户姓名选项,企业付款时使用 | |||||
| */ | |||||
| public static class CheckNameOption { | |||||
| /** | |||||
| * 不校验真实姓名 | |||||
| */ | |||||
| public static final String NO_CHECK = "NO_CHECK"; | |||||
| /** | |||||
| * 强校验真实姓名 | |||||
| */ | |||||
| public static final String FORCE_CHECK = "FORCE_CHECK"; | |||||
| } | |||||
| /** | |||||
| * 订单类型 | |||||
| */ | |||||
| public static class BillType { | |||||
| /** | |||||
| * 查询红包时使用:通过商户订单号获取红包信息 | |||||
| */ | |||||
| public static final String MCHT = "MCHT"; | |||||
| //以下为下载对账单时的账单类型 | |||||
| /** | |||||
| * 返回当日所有订单信息,默认值 | |||||
| */ | |||||
| public static final String ALL = "ALL"; | |||||
| /** | |||||
| * 返回当日成功支付的订单 | |||||
| */ | |||||
| public static final String SUCCESS = "SUCCESS"; | |||||
| /** | |||||
| * 返回当日退款订单 | |||||
| */ | |||||
| public static final String REFUND = "REFUND"; | |||||
| /** | |||||
| * 返回当日充值退款订单(相比其他对账单多一栏“返还手续费”) | |||||
| */ | |||||
| public static final String RECHARGE_REFUND = "RECHARGE_REFUND"; | |||||
| } | |||||
| /** | |||||
| * 交易类型 | |||||
| */ | |||||
| public static class TradeType { | |||||
| /** | |||||
| * 原生扫码支付 | |||||
| */ | |||||
| public static final String NATIVE = "NATIVE"; | |||||
| /** | |||||
| * App支付 | |||||
| */ | |||||
| public static final String APP = "APP"; | |||||
| /** | |||||
| * 公众号支付 | |||||
| */ | |||||
| public static final String JSAPI = "JSAPI"; | |||||
| /** | |||||
| * 刷卡支付,刷卡支付有单独的支付接口,不调用统一下单接口 | |||||
| */ | |||||
| public static final String MICROPAY = "MICROPAY"; | |||||
| } | |||||
| /** | |||||
| * 签名类型 | |||||
| */ | |||||
| public static class SignType { | |||||
| public static final String HMAC_SHA256 = "HMAC-SHA256"; | |||||
| public static final String MD5 = "MD5"; | |||||
| } | |||||
| } | |||||
| @@ -5,6 +5,9 @@ import com.github.binarywang.wxpay.bean.coupon.*; | |||||
| import com.github.binarywang.wxpay.bean.request.*; | import com.github.binarywang.wxpay.bean.request.*; | ||||
| import com.github.binarywang.wxpay.bean.result.*; | import com.github.binarywang.wxpay.bean.result.*; | ||||
| import com.github.binarywang.wxpay.config.WxPayConfig; | import com.github.binarywang.wxpay.config.WxPayConfig; | ||||
| import com.github.binarywang.wxpay.constant.WxPayConstants.BillType; | |||||
| import com.github.binarywang.wxpay.constant.WxPayConstants.SignType; | |||||
| import com.github.binarywang.wxpay.constant.WxPayConstants.TradeType; | |||||
| import com.github.binarywang.wxpay.exception.WxPayException; | import com.github.binarywang.wxpay.exception.WxPayException; | ||||
| import com.github.binarywang.wxpay.service.WxPayService; | import com.github.binarywang.wxpay.service.WxPayService; | ||||
| import com.github.binarywang.wxpay.util.SignUtils; | import com.github.binarywang.wxpay.util.SignUtils; | ||||
| @@ -129,7 +132,7 @@ public abstract class WxPayServiceAbstractImpl implements WxPayService { | |||||
| public WxPayRedpackQueryResult queryRedpack(String mchBillNo) throws WxPayException { | public WxPayRedpackQueryResult queryRedpack(String mchBillNo) throws WxPayException { | ||||
| WxPayRedpackQueryRequest request = new WxPayRedpackQueryRequest(); | WxPayRedpackQueryRequest request = new WxPayRedpackQueryRequest(); | ||||
| request.setMchBillNo(mchBillNo); | request.setMchBillNo(mchBillNo); | ||||
| request.setBillType("MCHT"); | |||||
| request.setBillType(BillType.MCHT); | |||||
| request.checkAndSign(this.getConfig()); | request.checkAndSign(this.getConfig()); | ||||
| String url = this.getPayBaseUrl() + "/mmpaymkttransfers/gethbinfo"; | String url = this.getPayBaseUrl() + "/mmpaymkttransfers/gethbinfo"; | ||||
| @@ -199,35 +202,36 @@ public abstract class WxPayServiceAbstractImpl implements WxPayService { | |||||
| Map<String, String> payInfo = new HashMap<>(); | Map<String, String> payInfo = new HashMap<>(); | ||||
| String timestamp = String.valueOf(System.currentTimeMillis() / 1000); | String timestamp = String.valueOf(System.currentTimeMillis() / 1000); | ||||
| String nonceStr = String.valueOf(System.currentTimeMillis()); | String nonceStr = String.valueOf(System.currentTimeMillis()); | ||||
| if ("NATIVE".equals(request.getTradeType())) { | |||||
| if (TradeType.NATIVE.equals(request.getTradeType())) { | |||||
| payInfo.put("codeUrl", unifiedOrderResult.getCodeURL()); | payInfo.put("codeUrl", unifiedOrderResult.getCodeURL()); | ||||
| } else if ("APP".equals(request.getTradeType())) { | |||||
| } else if (TradeType.APP.equals(request.getTradeType())) { | |||||
| // APP支付绑定的是微信开放平台上的账号,APPID为开放平台上绑定APP后发放的参数 | // APP支付绑定的是微信开放平台上的账号,APPID为开放平台上绑定APP后发放的参数 | ||||
| String appId = getConfig().getAppId(); | String appId = getConfig().getAppId(); | ||||
| Map<String, String> configMap = new HashMap<>(); | Map<String, String> configMap = new HashMap<>(); | ||||
| // 此map用于参与调起sdk支付的二次签名,格式全小写,timestamp只能是10位,格式固定,切勿修改 | // 此map用于参与调起sdk支付的二次签名,格式全小写,timestamp只能是10位,格式固定,切勿修改 | ||||
| String partnerid = getConfig().getMchId(); | |||||
| String partnerId = getConfig().getMchId(); | |||||
| configMap.put("prepayid", prepayId); | configMap.put("prepayid", prepayId); | ||||
| configMap.put("partnerid", partnerid); | |||||
| configMap.put("package", "Sign=WXPay"); | |||||
| configMap.put("partnerid", partnerId); | |||||
| String packageValue = "Sign=WXPay"; | |||||
| configMap.put("package", packageValue); | |||||
| configMap.put("timestamp", timestamp); | configMap.put("timestamp", timestamp); | ||||
| configMap.put("noncestr", nonceStr); | configMap.put("noncestr", nonceStr); | ||||
| configMap.put("appid", appId); | configMap.put("appid", appId); | ||||
| // 此map用于客户端与微信服务器交互 | // 此map用于客户端与微信服务器交互 | ||||
| payInfo.put("sign", SignUtils.createSign(configMap, this.getConfig().getMchKey())); | payInfo.put("sign", SignUtils.createSign(configMap, this.getConfig().getMchKey())); | ||||
| payInfo.put("prepayId", prepayId); | payInfo.put("prepayId", prepayId); | ||||
| payInfo.put("partnerId", partnerid); | |||||
| payInfo.put("partnerId", partnerId); | |||||
| payInfo.put("appId", appId); | payInfo.put("appId", appId); | ||||
| payInfo.put("packageValue", "Sign=WXPay"); | |||||
| payInfo.put("packageValue", packageValue); | |||||
| payInfo.put("timeStamp", timestamp); | payInfo.put("timeStamp", timestamp); | ||||
| payInfo.put("nonceStr", nonceStr); | payInfo.put("nonceStr", nonceStr); | ||||
| } else if ("JSAPI".equals(request.getTradeType())) { | |||||
| } else if (TradeType.JSAPI.equals(request.getTradeType())) { | |||||
| payInfo.put("appId", unifiedOrderResult.getAppid()); | payInfo.put("appId", unifiedOrderResult.getAppid()); | ||||
| // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符 | // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符 | ||||
| payInfo.put("timeStamp", timestamp); | payInfo.put("timeStamp", timestamp); | ||||
| payInfo.put("nonceStr", nonceStr); | payInfo.put("nonceStr", nonceStr); | ||||
| payInfo.put("package", "prepay_id=" + prepayId); | payInfo.put("package", "prepay_id=" + prepayId); | ||||
| payInfo.put("signType", "MD5"); | |||||
| payInfo.put("signType", SignType.MD5); | |||||
| payInfo.put("paySign", SignUtils.createSign(payInfo, this.getConfig().getMchKey())); | payInfo.put("paySign", SignUtils.createSign(payInfo, this.getConfig().getMchKey())); | ||||
| } | } | ||||
| @@ -278,7 +282,6 @@ public abstract class WxPayServiceAbstractImpl implements WxPayService { | |||||
| String sign = SignUtils.createSign(params, this.getConfig().getMchKey()); | String sign = SignUtils.createSign(params, this.getConfig().getMchKey()); | ||||
| params.put("sign", sign); | params.put("sign", sign); | ||||
| for (String key : params.keySet()) { | for (String key : params.keySet()) { | ||||
| codeUrl.append(key + "=" + params.get(key) + "&"); | codeUrl.append(key + "=" + params.get(key) + "&"); | ||||
| } | } | ||||
| @@ -327,8 +330,7 @@ public abstract class WxPayServiceAbstractImpl implements WxPayService { | |||||
| result.checkResult(this); | result.checkResult(this); | ||||
| return null; | return null; | ||||
| } else { | } else { | ||||
| WxPayBillResult wxPayBillResult = billInformationDeal(responseContent); | |||||
| return wxPayBillResult; | |||||
| return billInformationDeal(responseContent); | |||||
| } | } | ||||
| } | } | ||||
| @@ -346,10 +348,8 @@ public abstract class WxPayServiceAbstractImpl implements WxPayService { | |||||
| * 交易时间:2017-04-06 01:00:02 公众账号ID: 商户号: 子商户号:0 设备号:WEB 微信订单号: 商户订单号:2017040519091071873216 用户标识: 交易类型:NATIVE | * 交易时间:2017-04-06 01:00:02 公众账号ID: 商户号: 子商户号:0 设备号:WEB 微信订单号: 商户订单号:2017040519091071873216 用户标识: 交易类型:NATIVE | ||||
| * 交易状态:REFUND 付款银行:CFT 货币种类:CNY 总金额:0.00 企业红包金额:0.00 微信退款单号: 商户退款单号:20170406010000933 退款金额:0.01 企业红包退款金额:0.00 | * 交易状态:REFUND 付款银行:CFT 货币种类:CNY 总金额:0.00 企业红包金额:0.00 微信退款单号: 商户退款单号:20170406010000933 退款金额:0.01 企业红包退款金额:0.00 | ||||
| * 退款类型:ORIGINAL 退款状态:SUCCESS 商品名称: 商户数据包: 手续费:0.00000 费率 :0.60% | * 退款类型:ORIGINAL 退款状态:SUCCESS 商品名称: 商户数据包: 手续费:0.00000 费率 :0.60% | ||||
| * 参考以上格式进行取值 | |||||
| */ | */ | ||||
| // 参考以上格式进行取值 | |||||
| List<WxPayBillBaseResult> wxPayBillBaseResultLst = new LinkedList<>(); | List<WxPayBillBaseResult> wxPayBillBaseResultLst = new LinkedList<>(); | ||||
| String newStr = listStr.replaceAll(",", " "); // 去空格 | String newStr = listStr.replaceAll(",", " "); // 去空格 | ||||
| String[] tempStr = newStr.split("`"); // 数据分组 | String[] tempStr = newStr.split("`"); // 数据分组 | ||||
| @@ -387,12 +387,11 @@ public abstract class WxPayServiceAbstractImpl implements WxPayService { | |||||
| k += t.length; | k += t.length; | ||||
| } | } | ||||
| wxPayBillResult.setWxPayBillBaseResultLst(wxPayBillBaseResultLst); | wxPayBillResult.setWxPayBillBaseResultLst(wxPayBillBaseResultLst); | ||||
| /* | /* | ||||
| * 总交易单数,总交易额,总退款金额,总代金券或立减优惠退款金额,手续费总金额 `2,`0.02,`0.0,`0.0,`0 | * 总交易单数,总交易额,总退款金额,总代金券或立减优惠退款金额,手续费总金额 `2,`0.02,`0.0,`0.0,`0 | ||||
| * 参考以上格式进行取值 | |||||
| */ | */ | ||||
| // 参考以上格式进行取值 | |||||
| String totalStr = objStr.replaceAll(",", " "); | String totalStr = objStr.replaceAll(",", " "); | ||||
| String[] totalTempStr = totalStr.split("`"); | String[] totalTempStr = totalStr.split("`"); | ||||
| wxPayBillResult.setTotalRecord(totalTempStr[1]); | wxPayBillResult.setTotalRecord(totalTempStr[1]); | ||||
| @@ -4,6 +4,10 @@ import com.github.binarywang.utils.qrcode.QrcodeUtils; | |||||
| import com.github.binarywang.wxpay.bean.coupon.*; | import com.github.binarywang.wxpay.bean.coupon.*; | ||||
| import com.github.binarywang.wxpay.bean.request.*; | import com.github.binarywang.wxpay.bean.request.*; | ||||
| import com.github.binarywang.wxpay.bean.result.*; | import com.github.binarywang.wxpay.bean.result.*; | ||||
| import com.github.binarywang.wxpay.constant.WxPayConstants; | |||||
| import com.github.binarywang.wxpay.constant.WxPayConstants.BillType; | |||||
| import com.github.binarywang.wxpay.constant.WxPayConstants.SignType; | |||||
| import com.github.binarywang.wxpay.constant.WxPayConstants.TradeType; | |||||
| import com.github.binarywang.wxpay.exception.WxPayException; | import com.github.binarywang.wxpay.exception.WxPayException; | ||||
| import com.github.binarywang.wxpay.service.WxPayService; | import com.github.binarywang.wxpay.service.WxPayService; | ||||
| import com.github.binarywang.wxpay.testbase.ApiTestModule; | import com.github.binarywang.wxpay.testbase.ApiTestModule; | ||||
| @@ -33,23 +37,58 @@ public class WxPayServiceAbstractImplTest { | |||||
| @Inject | @Inject | ||||
| private WxPayService payService; | private WxPayService payService; | ||||
| /** | |||||
| * Test method for {@link WxPayService#unifiedOrder(WxPayUnifiedOrderRequest)}. | |||||
| */ | |||||
| @Test | |||||
| public void testUnifiedOrder() throws WxPayException { | |||||
| WxPayUnifiedOrderResult result = this.payService | |||||
| .unifiedOrder(WxPayUnifiedOrderRequest.newBuilder() | |||||
| .body("我去") | |||||
| .totalFee(1) | |||||
| .spbillCreateIp("11.1.11.1") | |||||
| .notifyURL("111111") | |||||
| .tradeType(TradeType.JSAPI) | |||||
| .openid(((XmlWxPayConfig) this.payService.getConfig()).getOpenid()) | |||||
| .outTradeNo("1111112") | |||||
| .build()); | |||||
| this.logger.info(result.toString()); | |||||
| } | |||||
| @Test | @Test | ||||
| public void testGetPayInfo() throws Exception { | public void testGetPayInfo() throws Exception { | ||||
| Map<String, String> payInfo = this.payService.getPayInfo(WxPayUnifiedOrderRequest.newBuilder() | Map<String, String> payInfo = this.payService.getPayInfo(WxPayUnifiedOrderRequest.newBuilder() | ||||
| .body("我去") | .body("我去") | ||||
| .totalFee(1) | .totalFee(1) | ||||
| .spbillCreateIp("111111") | |||||
| .spbillCreateIp("1.11.1.11") | |||||
| .notifyURL("111111") | .notifyURL("111111") | ||||
| .tradeType("JSAPI") | |||||
| .outTradeNo("111111") | |||||
| .tradeType(TradeType.JSAPI) | |||||
| .outTradeNo("1111113") | |||||
| .openid(((XmlWxPayConfig) this.payService.getConfig()).getOpenid()) | .openid(((XmlWxPayConfig) this.payService.getConfig()).getOpenid()) | ||||
| .build()); | .build()); | ||||
| this.logger.info(payInfo.toString()); | this.logger.info(payInfo.toString()); | ||||
| } | } | ||||
| /** | |||||
| * Test method for {@link WxPayService#queryOrder(String, String)} . | |||||
| */ | |||||
| @Test | |||||
| public void testQueryOrder() throws WxPayException { | |||||
| this.logger.info(this.payService.queryOrder("11212121", null).toString()); | |||||
| this.logger.info(this.payService.queryOrder(null, "11111").toString()); | |||||
| } | |||||
| /** | |||||
| * Test method for {@link WxPayService#closeOrder(String)} . | |||||
| */ | |||||
| @Test | |||||
| public void testCloseOrder() throws WxPayException { | |||||
| this.logger.info(this.payService.closeOrder("11212121").toString()); | |||||
| } | |||||
| @Test | @Test | ||||
| public void testDownloadBill() throws Exception { | public void testDownloadBill() throws Exception { | ||||
| WxPayBillResult wxPayBillResult = this.payService.downloadBill("20170101", "ALL", "GZIP", "1111111"); | |||||
| WxPayBillResult wxPayBillResult = this.payService.downloadBill("20170101", BillType.ALL, "GZIP", "1111111"); | |||||
| //前一天没有账单记录返回null | //前一天没有账单记录返回null | ||||
| assertNotNull(wxPayBillResult); | assertNotNull(wxPayBillResult); | ||||
| //必填字段为空时,抛出异常 | //必填字段为空时,抛出异常 | ||||
| @@ -60,7 +99,7 @@ public class WxPayServiceAbstractImplTest { | |||||
| public void testReport() throws Exception { | public void testReport() throws Exception { | ||||
| WxPayReportRequest request = new WxPayReportRequest(); | WxPayReportRequest request = new WxPayReportRequest(); | ||||
| request.setInterfaceUrl("hahahah"); | request.setInterfaceUrl("hahahah"); | ||||
| request.setSignType("HMAC-SHA256");//貌似接口未校验此字段 | |||||
| request.setSignType(SignType.HMAC_SHA256);//貌似接口未校验此字段 | |||||
| request.setExecuteTime(1000); | request.setExecuteTime(1000); | ||||
| request.setReturnCode("aaa"); | request.setReturnCode("aaa"); | ||||
| request.setResultCode("aaa"); | request.setResultCode("aaa"); | ||||
| @@ -130,41 +169,6 @@ public class WxPayServiceAbstractImplTest { | |||||
| this.logger.info(redpackResult.toString()); | this.logger.info(redpackResult.toString()); | ||||
| } | } | ||||
| /** | |||||
| * Test method for {@link WxPayService#unifiedOrder(WxPayUnifiedOrderRequest)}. | |||||
| */ | |||||
| @Test | |||||
| public void testUnifiedOrder() throws WxPayException { | |||||
| WxPayUnifiedOrderResult result = this.payService | |||||
| .unifiedOrder(WxPayUnifiedOrderRequest.newBuilder() | |||||
| .body("我去") | |||||
| .totalFee(1) | |||||
| .spbillCreateIp("11.1.11.1") | |||||
| .notifyURL("111111") | |||||
| .tradeType("JSAPI") | |||||
| .openid(((XmlWxPayConfig) this.payService.getConfig()).getOpenid()) | |||||
| .outTradeNo("111111") | |||||
| .build()); | |||||
| this.logger.info(result.toString()); | |||||
| } | |||||
| /** | |||||
| * Test method for {@link WxPayService#queryOrder(String, String)} . | |||||
| */ | |||||
| @Test | |||||
| public void testQueryOrder() throws WxPayException { | |||||
| this.logger.info(this.payService.queryOrder("11212121", null).toString()); | |||||
| this.logger.info(this.payService.queryOrder(null, "11111").toString()); | |||||
| } | |||||
| /** | |||||
| * Test method for {@link WxPayService#closeOrder(String)} . | |||||
| */ | |||||
| @Test | |||||
| public void testCloseOrder() throws WxPayException { | |||||
| this.logger.info(this.payService.closeOrder("11212121").toString()); | |||||
| } | |||||
| /** | /** | ||||
| * Test method for {@link WxPayService#entPay(WxEntPayRequest)}. | * Test method for {@link WxPayService#entPay(WxEntPayRequest)}. | ||||
| */ | */ | ||||
| @@ -175,7 +179,7 @@ public class WxPayServiceAbstractImplTest { | |||||
| .openid("ojOQA0y9o-Eb6Aep7uVTdbkJqrP4") | .openid("ojOQA0y9o-Eb6Aep7uVTdbkJqrP4") | ||||
| .amount(1) | .amount(1) | ||||
| .spbillCreateIp("10.10.10.10") | .spbillCreateIp("10.10.10.10") | ||||
| .checkName("NO_CHECK") | |||||
| .checkName(WxPayConstants.CheckNameOption.NO_CHECK) | |||||
| .description("描述信息") | .description("描述信息") | ||||
| .build(); | .build(); | ||||