diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxPayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxPayService.java index 2f2e7f3..f5eaf7b 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxPayService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxPayService.java @@ -12,6 +12,7 @@ import java.security.GeneralSecurityException; import java.util.Date; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.SortedMap; import java.util.TreeMap; import java.util.zip.GZIPInputStream; @@ -44,7 +45,6 @@ import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.NoticeRequest; -import com.egzosn.pay.common.bean.Order; import com.egzosn.pay.common.bean.OrderParaStructure; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; @@ -155,27 +155,23 @@ public class WxPayService extends BasePayService implements * @param noticeParams 回调回来的参数集 * @return 签名校验 true通过 */ + @Override public boolean verify(NoticeParams noticeParams) { final Map params = noticeParams.getBody(); //如果为退款不需要校验, 直接返回, - if (params.containsKey(REQ_INFO)){ + if (params.containsKey(REQ_INFO)) { return true; } - if (null == params.get(SIGN) || !(SUCCESS.equals(params.get(RETURN_CODE)) && SUCCESS.equals(params.get(RESULT_CODE)))) { + if (Objects.isNull(params.get(SIGN)) || !(SUCCESS.equals(params.get(RETURN_CODE)) && SUCCESS.equals(params.get(RESULT_CODE)))) { if (LOG.isErrorEnabled()) { LOG.error(String.format("微信支付异常:return_code=%s,参数集=%s", params.get(RETURN_CODE), params)); } return false; } - try { - return signVerify(params, (String) params.get(SIGN)); - } - catch (PayErrorException e) { - LOG.error("", e); - } - return false; + return signVerify(params, (String) params.get(SIGN)); + } @@ -186,7 +182,7 @@ public class WxPayService extends BasePayService implements * @param sign 比对的签名结果 * @return 生成的签名结果 */ - public boolean signVerify(Map params, String sign) { + private boolean signVerify(Map params, String sign) { return signVerify(params, sign, payConfigStorage.isTest()); } @@ -207,7 +203,7 @@ public class WxPayService extends BasePayService implements */ private Map getPublicParameters() { - Map parameters = new TreeMap(); + Map parameters = new TreeMap<>(); parameters.put(APPID, payConfigStorage.getAppId()); parameters.put(MCH_ID, payConfigStorage.getMchId()); //判断如果是服务商模式信息则加入 @@ -298,7 +294,7 @@ public class WxPayService extends BasePayService implements return result; } - Map params = new TreeMap(); + Map params = new TreeMap<>(); if (WxTransactionType.JSAPI == order.getTransactionType()) { params.put("signType", payConfigStorage.getSignType()); @@ -653,6 +649,7 @@ public class WxPayService extends BasePayService implements * RECHARGE_REFUND,返回当日充值退款订单 * @return 返回支付方下载对账单的结果 */ + @Override public Map downloadBill(Date billDate, String billType) { return downloadBill(billDate, WxPayBillType.valueOf(billType)); } @@ -668,6 +665,7 @@ public class WxPayService extends BasePayService implements * RECHARGE_REFUND,返回当日充值退款订单 * @return 返回支付方下载对账单的结果, 如果【账单类型】为gzip的话则返回值中key为data值为gzip的输入流 */ + @Override public Map downloadBill(Date billDate, BillType billType) { //获取公共参数 Map parameters = getPublicParameters(); @@ -710,24 +708,19 @@ public class WxPayService extends BasePayService implements //设置签名 setSign(parameters); InputStream inputStream = requestTemplate.postForObject(getReqUrl(WxTransactionType.DOWNLOADBILL), XML.getMap2Xml(parameters), InputStream.class); - try { - //解压流 - inputStream = uncompress(inputStream); - writeToLocal(path + DateUtils.formatDate(new Date(), DateUtils.YYYYMM) + "/" + DateUtils.formatDate(new Date(), DateUtils.YYYYMMDDHHMMSS) + ".txt", inputStream); - Map ret = new HashMap(3); + //解压流 + try (InputStream fileIs = uncompress(inputStream);) { + writeToLocal(path + DateUtils.formatDate(new Date(), DateUtils.YYYYMM) + "/" + DateUtils.formatDate(new Date(), DateUtils.YYYYMMDDHHMMSS) + ".txt", fileIs); + Map ret = new HashMap<>(3); ret.put(RETURN_CODE, SUCCESS); ret.put(RETURN_MSG_CODE, "ok"); ret.put("data", path); return ret; } - catch (Exception e) { - e.printStackTrace(); - Map ret = new HashMap(3); - ret.put(RETURN_CODE, FAIL); - ret.put(RETURN_MSG_CODE, "fail"); - ret.put("data", e.getMessage()); - return ret; + catch (IOException e) { + throw new PayErrorException(new WxPayError(FAIL, e.getMessage()), e); } + } /** @@ -739,15 +732,15 @@ public class WxPayService extends BasePayService implements */ @Deprecated public static InputStream uncompress(InputStream input) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - GZIPInputStream ungzip = new GZIPInputStream(input); - byte[] buffer = new byte[1024]; - int n; - while ((n = ungzip.read(buffer)) >= 0) { - out.write(buffer, 0, n); + try (GZIPInputStream ungZip = new GZIPInputStream(input); ByteArrayOutputStream out = new ByteArrayOutputStream()) { + byte[] buffer = new byte[1024]; + int n; + while ((n = ungZip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + return new ByteArrayInputStream(out.toByteArray()); } - InputStream is = new ByteArrayInputStream(out.toByteArray()); - return is; + } /** @@ -758,12 +751,11 @@ public class WxPayService extends BasePayService implements * @throws IOException IOException */ @Deprecated - private void writeToLocal(String destination, InputStream inputStream) - throws IOException { + private void writeToLocal(String destination, InputStream inputStream) throws IOException { // 判断字节大小 if (inputStream.available() != 0) { - System.out.println("结果大小:" + inputStream.available()); + LOG.debug("结果大小:{}", inputStream.available()); File file = new File(destination); if (!file.getParentFile().exists()) { boolean result = file.getParentFile().mkdirs(); @@ -771,17 +763,17 @@ public class WxPayService extends BasePayService implements LOG.warn("创建失败"); } } - OutputStream out = new FileOutputStream(file); - int size = 0; - int len = 0; - byte[] buf = new byte[1024]; - while ((size = inputStream.read(buf)) != -1) { - len += size; - out.write(buf, 0, size); + try (OutputStream out = new FileOutputStream(file)) { + int size = 0; + int len = 0; + byte[] buf = new byte[1024]; + while ((size = inputStream.read(buf)) != -1) { + len += size; + out.write(buf, 0, size); + } + LOG.debug("最终写入字节数大小:{}", len); } - LOG.info("最终写入字节数大小:" + len); - inputStream.close(); - out.close(); + } } @@ -813,7 +805,7 @@ public class WxPayService extends BasePayService implements * @param transactionType 交易类型 * @return 返回支付方对应接口的结果 */ - public Map secondaryInterface(Object transactionIdOrBillDate, String outTradeNoBillType, TransactionType transactionType) { + private Map secondaryInterface(Object transactionIdOrBillDate, String outTradeNoBillType, TransactionType transactionType) { if (transactionType == WxTransactionType.REFUND) { throw new PayErrorException(new PayException(FAILURE, "通用接口不支持:" + transactionType)); @@ -844,18 +836,11 @@ public class WxPayService extends BasePayService implements * * @param order 转账订单 *
-     *
-     * 注意事项:
-     * ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。
-     * ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。
-     * ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。
-     * ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。
-     * 
- * @return 对应的转账结果 + * @return 对应的转账结果 */ @Override public Map transfer(TransferOrder order) { - Map parameters = new TreeMap(); + Map parameters = new TreeMap<>(); parameters.put("partner_trade_no", order.getOutNo()); @@ -889,7 +874,7 @@ public class WxPayService extends BasePayService implements * 商户企业付款到银行卡 *

*/ - public Map transfers(Map parameters, TransferOrder order) { + private Map transfers(Map parameters, TransferOrder order) { //转账到余额, 申请商户号的appid或商户号绑定的appid parameters.put("mch_appid", payConfigStorage.getAppId()); parameters.put("openid", order.getPayeeAccount()); @@ -911,7 +896,7 @@ public class WxPayService extends BasePayService implements * @param order 转账订单 * @return 包装后参数信息 */ - public Map payBank(Map parameters, TransferOrder order) { + private Map payBank(Map parameters, TransferOrder order) { parameters.put("enc_bank_no", keyPublic(order.getPayeeAccount())); parameters.put("enc_true_name", keyPublic(order.getPayeeName())); @@ -952,7 +937,7 @@ public class WxPayService extends BasePayService implements } - public String keyPublic(String content) { + private String keyPublic(String content) { try { return RSA2.encrypt(content, payConfigStorage.getKeyPublic(), CIPHER_ALGORITHM, payConfigStorage.getInputCharset()); } @@ -981,7 +966,19 @@ public class WxPayService extends BasePayService implements */ @Override public Map sendredpack(RedpackOrder redpackOrder) { - Map parameters = new TreeMap(); + return sendRedPack(redpackOrder); + } + + /** + * 微信发红包 + * + * @param redpackOrder 红包实体 + * @return 返回发红包实体后的结果 + * @author faymanwang 1057438332@qq.com + */ + @Override + public Map sendRedPack(RedpackOrder redpackOrder) { + Map parameters = new TreeMap<>(); redPackParam(redpackOrder, parameters); final TransferType transferType = redpackOrder.getTransferType(); if (WxSendredpackType.SENDGROUPREDPACK == transferType) { @@ -998,7 +995,7 @@ public class WxPayService extends BasePayService implements if (WxSendredpackType.SENDMINIPROGRAMHB != transferType || FAIL.equals(resp.getString(RESULT_CODE))) { return resp; } - Map params = new TreeMap(); + Map params = new TreeMap<>(); params.put("appId", payConfigStorage.getAppId()); params.put("timeStamp", System.currentTimeMillis() / 1000 + ""); params.put("nonceStr", parameters.get(NONCE_STR)); @@ -1021,8 +1018,22 @@ public class WxPayService extends BasePayService implements */ @Override public Map gethbinfo(String mchBillno) { + return getHbInfo(mchBillno); + } + + /** + * 查询红包记录 + * 用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包 + * 查询红包记录API只支持查询30天内的红包订单,30天之前的红包订单请登录商户平台查询。 + * + * @param mchBillNo 商户发放红包的商户订单号 + * @return 返回查询结果 + * @author faymanwang 1057438332@qq.com + */ + @Override + public Map getHbInfo(String mchBillNo) { Map parameters = this.getPublicParameters(); - parameters.put("mch_billno", mchBillno); + parameters.put("mch_billno", mchBillNo); parameters.put("bill_type", "MCHT"); parameters.put(SIGN, createSign(SignTextUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); return requestTemplate.postForObject(getReqUrl(WxSendredpackType.GETHBINFO), XML.getMap2Xml(parameters), JSONObject.class); @@ -1037,10 +1048,13 @@ public class WxPayService extends BasePayService implements private void redPackParam(RedpackOrder redpackOrder, Map parameters) { parameters.put(NONCE_STR, SignTextUtils.randomStr()); parameters.put(MCH_ID, payConfigStorage.getPid()); - parameters.put("wxappid", payConfigStorage.getAppId()); + if (StringUtils.isEmpty(redpackOrder.getWxAppId())) { + throw new PayErrorException(new WxPayError(FAIL, "RedpackOrder#getWxAppId()公众账号appid 必填")); + } + parameters.put("wxappid", redpackOrder.getWxAppId()); parameters.put("send_name", redpackOrder.getSendName()); parameters.put("re_openid", redpackOrder.getReOpenid()); - parameters.put("mch_billno", redpackOrder.getMchBillno()); + parameters.put("mch_billno", redpackOrder.getMchBillNo()); parameters.put("total_amount", Util.conversionCentAmount(redpackOrder.getTotalAmount())); parameters.put("total_num", 1); parameters.put("wishing", redpackOrder.getWishing()); diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxRedPackService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxRedPackService.java index 341f3a8..a18cff5 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxRedPackService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxRedPackService.java @@ -18,8 +18,17 @@ public interface WxRedPackService { * * @param redpackOrder 红包实体 * @return 返回发红包实体后的结果 + * @see #sendRedPack(RedpackOrder) */ + @Deprecated Map sendredpack(RedpackOrder redpackOrder); + /** + * 微信发红包 + * + * @param redpackOrder 红包实体 + * @return 返回发红包实体后的结果 + */ + Map sendRedPack(RedpackOrder redpackOrder); /** * 查询红包记录 @@ -28,6 +37,17 @@ public interface WxRedPackService { * * @param mchBillno 商户发放红包的商户订单号 * @return 返回查询结果 + * @see #getHbInfo(String) */ + @Deprecated Map gethbinfo(String mchBillno); + /** + * 查询红包记录 + * 用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包 + * 查询红包记录API只支持查询30天内的红包订单,30天之前的红包订单请登录商户平台查询。 + * + * @param mchBillNo 商户发放红包的商户订单号 + * @return 返回查询结果 + */ + Map getHbInfo(String mchBillNo); } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/RedpackOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/RedpackOrder.java index 8354f3c..2587431 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/RedpackOrder.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/RedpackOrder.java @@ -1,32 +1,57 @@ package com.egzosn.pay.wx.bean; -import com.egzosn.pay.common.bean.TransferOrder; - import java.math.BigDecimal; +import com.egzosn.pay.common.bean.TransferOrder; + /** - * 发红包订单 + * 发红包订单 + * * @author 保网 faymanwang 1057438332@qq.com * 2020/5/15 12:40 */ public class RedpackOrder extends TransferOrder { - + /** + * 微信为发放红包商户分配的公众账号ID,接口传入的appid应该为公众号的appid或小程序的appid(在mp.weixin.qq.com申请的)或APP的appid(在open.weixin.qq.com申请的)。 + * 校验规则: + * 1、该appid需要与接口传入中的re_openid有对应关系; + * 2、该appid需要与发放红包商户号有绑定关系,若未绑定,可参考该指引完成绑定(商家商户号与AppID账号关联管理) + */ + private String wxAppId; + /** + * 商户订单号(每个订单号必须唯一。取值范围:0~9,a~z,A~Z) + * 接口根据商户订单号支持重入,如出现超时可再调用。 + */ + private String mchBillNo; /** * 商户订单号(每个订单号必须唯一。取值范围:0~9,a~z,A~Z) * 接口根据商户订单号支持重入,如出现超时可再调用 + * * @return 商户订单号 */ + @Deprecated public String getMchBillno() { - return getOutNo(); + return getMchBillNo(); } + @Deprecated public void setMchBillno(String mchBillno) { - setOutNo(mchBillno); + setMchBillNo(mchBillno); + } + + public String getMchBillNo() { + return mchBillNo; + } + + public void setMchBillNo(String mchBillNo) { + setOutNo(mchBillNo); + this.mchBillNo = mchBillNo; } /** * 商户名称:红包发送者名称 + * * @return 红包发送者名称 */ public String getSendName() { @@ -34,11 +59,12 @@ public class RedpackOrder extends TransferOrder { } public void setSendName(String sendName) { - super.setPayerName(sendName); + super.setPayerName(sendName); } /** * 用户openid + * * @return 用户openid */ public String getReOpenid() { @@ -46,11 +72,12 @@ public class RedpackOrder extends TransferOrder { } public void setReOpenid(String reOpenid) { - super.setPayeeAccount(reOpenid); + super.setPayeeAccount(reOpenid); } /** * 付款金额 每个红包金额必须在默认额度内(默认大于1元,小于200元,可在产品设置中自行申请调高额度) + * * @return 付款金额 */ public BigDecimal getTotalAmount() { @@ -60,22 +87,26 @@ public class RedpackOrder extends TransferOrder { public void setTotalAmount(BigDecimal totalAmount) { super.setAmount(totalAmount); } + /** * 红包发放总人数 * 普通红包:1 * 裂变:必须介于(包括)3到20之间 + * * @return 红包发放总人数 */ public int getTotalNum() { Object totalNum = getAttr("total_num"); - return null == totalNum ? 1 : (Integer)totalNum; + return null == totalNum ? 1 : (Integer) totalNum; } public void setTotalNum(int totalNum) { addAttr("total_num", totalNum); } + /** * 红包祝福语 + * * @return 红包祝福语 */ public String getWishing() { @@ -90,6 +121,7 @@ public class RedpackOrder extends TransferOrder { /** * 活动名称 + * * @return 活动名称 */ public String getActName() { @@ -103,6 +135,7 @@ public class RedpackOrder extends TransferOrder { public String getSceneId() { return (String) getAttr("scene_id"); } + /** * 发放红包使用场景,红包金额大于200或者小于1元时必传 * PRODUCT_1:商品促销 @@ -113,7 +146,8 @@ public class RedpackOrder extends TransferOrder { * PRODUCT_6:保险回馈 * PRODUCT_7:彩票派奖 * PRODUCT_8:税务刮奖 - * @param sceneId 红包使用场景 + * + * @param sceneId 红包使用场景 */ public void setSceneId(String sceneId) { addAttr("scene_id", sceneId); @@ -123,4 +157,13 @@ public class RedpackOrder extends TransferOrder { public void setTransferType(WxSendredpackType transferType) { super.setTransferType(transferType); } + + public String getWxAppId() { + return wxAppId; + } + + public void setWxAppId(String wxAppId) { + addAttr("wxappid", wxAppId); + this.wxAppId = wxAppId; + } }