From 5fa8d3881e5fc59a1477b37672a73a9dd167d4ed Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 16 Apr 2019 14:00:16 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=B2=99=E7=AE=B1=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E8=8E=B7=E5=8F=96=E5=AF=86=E9=92=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/wx/api/WxPayService.java | 210 +++++++++++------- .../egzosn/pay/wx/bean/WxTransactionType.java | 11 +- 2 files changed, 128 insertions(+), 93 deletions(-) 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 eb55f27..9163ad5 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 @@ -17,6 +17,7 @@ import com.egzosn.pay.wx.bean.WxPayError; import com.egzosn.pay.wx.bean.WxTransactionType; import com.egzosn.pay.common.util.XML; import com.egzosn.pay.wx.bean.WxTransferType; + import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; @@ -31,9 +32,9 @@ import static com.egzosn.pay.wx.bean.WxTransferType.*; * * @author egan *
- *         email egzosn@gmail.com
- *         date 2016-5-18 14:09:01
- *         
+ * email egzosn@gmail.com + * date 2016-5-18 14:09:01 + * */ public class WxPayService extends BasePayService { @@ -57,44 +58,48 @@ public class WxPayService extends BasePayService { private static final String HMACSHA256 = "HMACSHA256"; private static final String RETURN_MSG_CODE = "return_msg"; private static final String RESULT_CODE = "result_code"; - - - + private static final String MCH_ID = "mch_id"; + private static final String NONCE_STR = "nonce_str"; /** * 创建支付服务 + * * @param payConfigStorage 微信对应的支付配置 */ public WxPayService(WxPayConfigStorage payConfigStorage) { super(payConfigStorage); } + /** * 创建支付服务 + * * @param payConfigStorage 微信对应的支付配置 - * @param configStorage 微信对应的网络配置,包含代理配置、ssl证书配置 + * @param configStorage 微信对应的网络配置,包含代理配置、ssl证书配置 */ public WxPayService(WxPayConfigStorage payConfigStorage, HttpConfigStorage configStorage) { super(payConfigStorage, configStorage); } + /** * 设置支付配置 + * * @param payConfigStorage 支付配置 */ @Override public BasePayService setPayConfigStorage(WxPayConfigStorage payConfigStorage) { String signType = payConfigStorage.getSignType(); - if (HMAC_SHA256.equals(signType)){ + if (HMAC_SHA256.equals(signType)) { payConfigStorage.setSignType(HMACSHA256); } this.payConfigStorage = payConfigStorage; return this; } + /** * 根据交易类型获取url * * @param transactionType 交易类型 - * * @return 请求url */ private String getUrl(TransactionType transactionType) { @@ -111,12 +116,12 @@ public class WxPayService extends BasePayService { @Override public boolean verify(Map params) { - if (!(SUCCESS.equals(params.get(RETURN_CODE)) && SUCCESS.equals(params.get(RESULT_CODE)))){ + if (!(SUCCESS.equals(params.get(RETURN_CODE)) && SUCCESS.equals(params.get(RESULT_CODE)))) { LOG.debug(String.format("微信支付异常:return_code=%s,参数集=%s", params.get(RETURN_CODE), params)); return false; } - if(null == params.get(SIGN)) { + if (null == params.get(SIGN)) { LOG.debug("微信支付异常:签名为空!out_trade_no=" + params.get("out_trade_no")); return false; } @@ -165,13 +170,13 @@ public class WxPayService extends BasePayService { Map parameters = new TreeMap(); parameters.put(APPID, payConfigStorage.getAppid()); - parameters.put("mch_id", payConfigStorage.getMchId()); + parameters.put(MCH_ID, payConfigStorage.getMchId()); //判断如果是服务商模式信息则加入 - if (!StringUtils.isEmpty(payConfigStorage.getSubAppid()) && !StringUtils.isEmpty(payConfigStorage.getSubMchId())){ + if (!StringUtils.isEmpty(payConfigStorage.getSubAppid()) && !StringUtils.isEmpty(payConfigStorage.getSubMchId())) { parameters.put("sub_appid", payConfigStorage.getSubAppid()); parameters.put("sub_mch_id", payConfigStorage.getSubMchId()); } - parameters.put("nonce_str", SignUtils.randomStr()); + parameters.put(NONCE_STR, SignUtils.randomStr()); return parameters; @@ -194,24 +199,24 @@ public class WxPayService extends BasePayService { // parameters.put("detail", order.getBody()); // 订单号 parameters.put("out_trade_no", order.getOutTradeNo()); - parameters.put("spbill_create_ip", StringUtils.isEmpty(order.getSpbillCreateIp()) ? "192.168.1.150" : order.getSpbillCreateIp() ); + parameters.put("spbill_create_ip", StringUtils.isEmpty(order.getSpbillCreateIp()) ? "192.168.1.150" : order.getSpbillCreateIp()); // 总金额单位为分 - parameters.put("total_fee", Util.conversionCentAmount( order.getPrice())); - if (StringUtils.isNotEmpty(order.getAddition())){ + parameters.put("total_fee", Util.conversionCentAmount(order.getPrice())); + if (StringUtils.isNotEmpty(order.getAddition())) { parameters.put("attach", order.getAddition()); } parameters.put("notify_url", payConfigStorage.getNotifyUrl()); parameters.put("trade_type", order.getTransactionType().getType()); - if (null != order.getExpirationTime()){ + if (null != order.getExpirationTime()) { parameters.put("time_start", DateUtils.formatDate(new Date(), DateUtils.YYYYMMDDHHMMSS)); - parameters.put("time_expire", DateUtils.formatDate(order.getExpirationTime(), DateUtils.YYYYMMDDHHMMSS)); + parameters.put("time_expire", DateUtils.formatDate(order.getExpirationTime(), DateUtils.YYYYMMDDHHMMSS)); } ((WxTransactionType) order.getTransactionType()).setAttribute(parameters, order); - setSign(parameters); + setSign(parameters); String requestXML = XML.getMap2Xml(parameters); - if (LOG.isDebugEnabled()){ + if (LOG.isDebugEnabled()) { LOG.debug("requestXML:" + requestXML); } //调起支付的参数列表 @@ -240,7 +245,7 @@ public class WxPayService extends BasePayService { // 对微信返回的数据进行校验 if (verify(result)) { //如果是扫码支付或者刷卡付无需处理,直接返回 - if (((WxTransactionType)order.getTransactionType()).isReturn()) { + if (((WxTransactionType) order.getTransactionType()).isReturn()) { return result; } @@ -250,14 +255,14 @@ public class WxPayService extends BasePayService { params.put("signType", payConfigStorage.getSignType()); params.put("appId", payConfigStorage.getAppid()); params.put("timeStamp", System.currentTimeMillis() / 1000); - params.put("nonceStr", result.get("nonce_str")); + params.put("nonceStr", result.get(NONCE_STR)); params.put("package", "prepay_id=" + result.get("prepay_id")); } else if (WxTransactionType.APP == order.getTransactionType()) { params.put("partnerid", payConfigStorage.getPid()); params.put(APPID, payConfigStorage.getAppid()); params.put("prepayid", result.get("prepay_id")); params.put("timestamp", System.currentTimeMillis() / 1000); - params.put("noncestr", result.get("nonce_str")); + params.put("noncestr", result.get(NONCE_STR)); params.put("package", "Sign=WXPay"); } String paySign = createSign(SignUtils.parameterText(params), payConfigStorage.getInputCharset()); @@ -276,7 +281,7 @@ public class WxPayService extends BasePayService { */ private Map setSign(Map parameters) { String signType = payConfigStorage.getSignType(); - if (HMACSHA256.equals(signType)){ + if (HMACSHA256.equals(signType)) { signType = HMAC_SHA256; } parameters.put("sign_type", signType); @@ -285,6 +290,31 @@ public class WxPayService extends BasePayService { return parameters; } + + /** + * 获取验签秘钥 + * + * @return 验签秘钥 + */ + private String getKeyPrivate() { + if (!payConfigStorage.isTest()) { + return payConfigStorage.getKeyPrivate(); + } + SortedMap parameters = new TreeMap(); + parameters.put(MCH_ID, payConfigStorage.getMchId()); + parameters.put(NONCE_STR, SignUtils.randomStr()); + + String sign = createSign(SignUtils.parameterText(parameters, "&", SIGN, "appId"), payConfigStorage.getInputCharset(), false); + parameters.put(SIGN, sign); + + JSONObject result = requestTemplate.postForObject(getUrl(WxTransactionType.GETSIGNKEY), XML.getMap2Xml(parameters), JSONObject.class); + if (SUCCESS.equals(result.get(RETURN_CODE))) { + return result.getString("sandbox_signkey"); + } + LOG.error("获取sandbox_signkey失败", new PayErrorException(new PayException(result.getString(RETURN_CODE), result.getString(RETURN_MSG_CODE), result.toJSONString()))); + return null; + } + /** * 签名 * @@ -294,8 +324,24 @@ public class WxPayService extends BasePayService { */ @Override public String createSign(String content, String characterEncoding) { + + return createSign(content, characterEncoding, payConfigStorage.isTest()); + } + /** + * 签名 + * + * @param content 需要签名的内容 不包含key + * @param characterEncoding 字符编码 + * @param test 是否为沙箱环境 + * @return 签名结果 + */ + public String createSign(String content, String characterEncoding, boolean test) { SignUtils signUtils = SignUtils.valueOf(payConfigStorage.getSignType().toUpperCase()); - return signUtils.createSign(content + "&key=" + (signUtils == SignUtils.MD5 ? "" : payConfigStorage.getKeyPrivate()) , payConfigStorage.getKeyPrivate(), characterEncoding).toUpperCase(); + String keyPrivate = payConfigStorage.getKeyPrivate(); + if (test){ + keyPrivate = getKeyPrivate(); + } + return signUtils.createSign(content + "&key=" + (signUtils == SignUtils.MD5 ? "" : keyPrivate), keyPrivate, characterEncoding).toUpperCase(); } /** @@ -356,7 +402,7 @@ public class WxPayService extends BasePayService { throw new PayErrorException(new WxPayError((String) orderInfo.get(RETURN_CODE), (String) orderInfo.get(RETURN_MSG_CODE))); } if (WxTransactionType.MWEB.name().equals(orderInfo.get("trade_type"))) { - return String.format("",orderInfo.get("mweb_url"), StringUtils.isEmpty(payConfigStorage.getReturnUrl()) ? "" : "&redirect_url=" + URLEncoder.encode(payConfigStorage.getReturnUrl())); + return String.format("", orderInfo.get("mweb_url"), StringUtils.isEmpty(payConfigStorage.getReturnUrl()) ? "" : "&redirect_url=" + URLEncoder.encode(payConfigStorage.getReturnUrl())); } throw new UnsupportedOperationException(); @@ -406,7 +452,6 @@ public class WxPayService extends BasePayService { } - /** * 交易关闭接口 * @@ -423,8 +468,8 @@ public class WxPayService extends BasePayService { /** * 交易交易撤销 * - * @param transactionId 支付平台订单号 - * @param outTradeNo 商户单号 + * @param transactionId 支付平台订单号 + * @param outTradeNo 商户单号 * @return 返回支付方交易撤销后的结果 */ @Override @@ -450,8 +495,8 @@ public class WxPayService extends BasePayService { } - private Map setParameters(Map parameters, String key, String value){ - if (!StringUtils.isEmpty(value)){ + private Map setParameters(Map parameters, String key, String value) { + if (!StringUtils.isEmpty(value)) { parameters.put(key, value); } return parameters; @@ -460,7 +505,7 @@ public class WxPayService extends BasePayService { /** * 申请退款接口 * - * @param refundOrder 退款订单信息 + * @param refundOrder 退款订单信息 * @return 返回支付方申请退款后的结果 */ @Override @@ -481,9 +526,6 @@ public class WxPayService extends BasePayService { } - - - /** * 查询退款 * @@ -512,7 +554,7 @@ public class WxPayService extends BasePayService { setParameters(parameters, "out_refund_no", refundOrder.getRefundNo()); //设置签名 setSign(parameters); - return requestTemplate.postForObject(getUrl( WxTransactionType.REFUNDQUERY), XML.getMap2Xml(parameters) , JSONObject.class); + return requestTemplate.postForObject(getUrl(WxTransactionType.REFUNDQUERY), XML.getMap2Xml(parameters), JSONObject.class); } @@ -538,10 +580,10 @@ public class WxPayService extends BasePayService { setSign(parameters); String respStr = requestTemplate.postForObject(getUrl(WxTransactionType.DOWNLOADBILL), XML.getMap2Xml(parameters), String.class); if (respStr.indexOf("<") == 0) { - return XML.toJSONObject(respStr); + return XML.toJSONObject(respStr); } - Map ret = new HashMap(); + Map ret = new HashMap(3); ret.put(RETURN_CODE, SUCCESS); ret.put(RETURN_MSG_CODE, "ok"); ret.put("data", respStr); @@ -549,7 +591,6 @@ public class WxPayService extends BasePayService { } - /** * @param transactionIdOrBillDate 支付平台订单号或者账单类型, 具体请 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} * @param outTradeNoBillType 商户单号或者 账单类型 @@ -557,49 +598,48 @@ public class WxPayService extends BasePayService { * @return 返回支付方对应接口的结果 */ @Override - public Map secondaryInterface(Object transactionIdOrBillDate, String outTradeNoBillType, TransactionType transactionType) { + public Map secondaryInterface(Object transactionIdOrBillDate, String outTradeNoBillType, TransactionType transactionType) { if (transactionType == WxTransactionType.REFUND) { throw new PayErrorException(new PayException(FAILURE, "通用接口不支持:" + transactionType)); } - if (transactionType == WxTransactionType.DOWNLOADBILL){ - if (transactionIdOrBillDate instanceof Date){ + if (transactionType == WxTransactionType.DOWNLOADBILL) { + if (transactionIdOrBillDate instanceof Date) { return downloadbill((Date) transactionIdOrBillDate, outTradeNoBillType); } throw new PayErrorException(new PayException(FAILURE, "非法类型异常:" + transactionIdOrBillDate.getClass())); } - if (!(null == transactionIdOrBillDate || transactionIdOrBillDate instanceof String)){ + if (!(null == transactionIdOrBillDate || transactionIdOrBillDate instanceof String)) { throw new PayErrorException(new PayException(FAILURE, "非法类型异常:" + transactionIdOrBillDate.getClass())); } //获取公共参数 Map parameters = getPublicParameters(); - if (StringUtils.isEmpty((String)transactionIdOrBillDate)){ + if (StringUtils.isEmpty((String) transactionIdOrBillDate)) { parameters.put("out_trade_no", outTradeNoBillType); - }else { + } else { parameters.put("transaction_id", transactionIdOrBillDate); } //设置签名 setSign(parameters); - return requestTemplate.postForObject(getUrl(transactionType), XML.getMap2Xml(parameters) , JSONObject.class); + return requestTemplate.postForObject(getUrl(transactionType), XML.getMap2Xml(parameters), JSONObject.class); } /** * 转账 * * @param order 转账订单 - *
+     *              
      *
-     * 注意事项:
-     * ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。
-     * ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。
-     * ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。
-     * ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。
-     *
-     *
+ * 注意事项: + * ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。 + * ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。 + * ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。 + * ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。 * + *
* @return 对应的转账结果 */ @Override @@ -609,34 +649,35 @@ public class WxPayService extends BasePayService { parameters.put("partner_trade_no", order.getOutNo()); parameters.put("amount", Util.conversionCentAmount(order.getAmount())); - if (!StringUtils.isEmpty(order.getRemark())){ + if (!StringUtils.isEmpty(order.getRemark())) { parameters.put("desc", order.getRemark()); } - parameters.put("nonce_str", SignUtils.randomStr()); - if (null != order.getTransferType() && TRANSFERS == order.getTransferType()){ + parameters.put(NONCE_STR, SignUtils.randomStr()); + if (null != order.getTransferType() && TRANSFERS == order.getTransferType()) { transfers(parameters, order); parameters.put("mchid", payConfigStorage.getPid()); - }else { - parameters.put("mch_id", payConfigStorage.getPid()); + } else { + parameters.put(MCH_ID, payConfigStorage.getPid()); order.setTransferType(WxTransferType.PAY_BANK); payBank(parameters, order); } parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); - return getHttpRequestTemplate().postForObject(getUrl(order.getTransferType()), XML.getMap2Xml(parameters), JSONObject.class); + return getHttpRequestTemplate().postForObject(getUrl(order.getTransferType()), XML.getMap2Xml(parameters), JSONObject.class); } /** * 转账到余额所需要参数 + * * @param parameters 参数信息 - * @param order 转账订单 + * @param order 转账订单 * @return 包装后参数信息 *

- * 企业付款到零钱 - * 商户企业付款到银行卡 + * 企业付款到零钱 + * 商户企业付款到银行卡 *

*/ - public Map transfers(Map parameters, TransferOrder order){ + public Map transfers(Map parameters, TransferOrder order) { //转账到余额, 申请商户号的appid或商户号绑定的appid parameters.put("mch_appid", payConfigStorage.getAppid()); parameters.put("openid", order.getPayeeAccount()); @@ -644,7 +685,7 @@ public class WxPayService extends BasePayService { //默认不校验真实姓名 parameters.put("check_name", "NO_CHECK"); //当存在时候 校验收款用户真实姓名 - if (!StringUtils.isEmpty(order.getPayeeName())){ + if (!StringUtils.isEmpty(order.getPayeeName())) { parameters.put("check_name", "FORCE_CHECK"); parameters.put("re_user_name", order.getPayeeName()); } @@ -653,11 +694,12 @@ public class WxPayService extends BasePayService { /** * 转账到银行卡所需要参数 + * * @param parameters 参数信息 - * @param order 转账订单 + * @param order 转账订单 * @return 包装后参数信息 */ - public Map payBank(Map parameters, TransferOrder order){ + public Map payBank(Map parameters, TransferOrder order) { parameters.put("enc_bank_no", keyPublic(order.getPayeeAccount())); parameters.put("enc_true_name", keyPublic(order.getPayeeName())); @@ -669,43 +711,41 @@ public class WxPayService extends BasePayService { /** * 转账查询 * - * @param outNo 商户转账订单号 + * @param outNo 商户转账订单号 * @param wxTransferType 微信转账类型,.....这里没办法了只能这样写(┬_┬),请见谅 {@link com.egzosn.pay.wx.bean.WxTransferType} - * - *

- * 企业付款到零钱 - * 商户企业付款到银行卡 - *

+ *

+ *

+ * 企业付款到零钱 + * 商户企业付款到银行卡 + *

* @return 对应的转账订单 */ @Override public Map transferQuery(String outNo, String wxTransferType) { Map parameters = new TreeMap(); - parameters.put("mch_id", payConfigStorage.getPid()); + parameters.put(MCH_ID, payConfigStorage.getPid()); parameters.put("partner_trade_no", outNo); - parameters.put("nonce_str", SignUtils.randomStr()); + parameters.put(NONCE_STR, SignUtils.randomStr()); parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); - if (StringUtils.isEmpty(wxTransferType)){ + if (StringUtils.isEmpty(wxTransferType)) { throw new PayErrorException(new WxPayError(FAILURE, "微信转账类型 #transferQuery(String outNo, String wxTransferType) 必填,详情com.egzosn.pay.wx.bean.WxTransferType")); } //如果类型为余额方式 - if (TRANSFERS.getType().equals(wxTransferType) || GETTRANSFERINFO.getType().equals(wxTransferType)){ - return getHttpRequestTemplate().postForObject(getUrl(GETTRANSFERINFO), XML.getMap2Xml(parameters), JSONObject.class); + if (TRANSFERS.getType().equals(wxTransferType) || GETTRANSFERINFO.getType().equals(wxTransferType)) { + return getHttpRequestTemplate().postForObject(getUrl(GETTRANSFERINFO), XML.getMap2Xml(parameters), JSONObject.class); } //默认查询银行卡的记录 - return getHttpRequestTemplate().postForObject(getUrl(QUERY_BANK), XML.getMap2Xml(parameters), JSONObject.class); + return getHttpRequestTemplate().postForObject(getUrl(QUERY_BANK), XML.getMap2Xml(parameters), JSONObject.class); } - - public String keyPublic(String content){ + public String keyPublic(String content) { try { - return RSA2.encrypt(content, payConfigStorage.getKeyPublic(), CIPHER_ALGORITHM, payConfigStorage.getInputCharset()); + return RSA2.encrypt(content, payConfigStorage.getKeyPublic(), CIPHER_ALGORITHM, payConfigStorage.getInputCharset()); } catch (Exception e) { - throw new PayErrorException(new WxPayError(FAILURE, e.getLocalizedMessage())); + throw new PayErrorException(new WxPayError(FAILURE, e.getLocalizedMessage())); } } - } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxTransactionType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxTransactionType.java index 80787a9..990d16c 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxTransactionType.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxTransactionType.java @@ -131,15 +131,10 @@ public enum WxTransactionType implements TransactionType { */ DOWNLOADBILL("pay/downloadbill"), /** - * 银行卡转账 + * 获取验签秘钥,沙箱使用 */ - @Deprecated - BANK("mmpaysptrans/pay_bank"), - /** - * 转账查询 - */ - @Deprecated - QUERY_BANK("mmpaysptrans/query_bank") + GETSIGNKEY("pay/getsignkey"), + ; WxTransactionType(String method) {