From 0a1cad73417f2359175e22787d7e4a51411d10fb Mon Sep 17 00:00:00 2001 From: jinyu Date: Sat, 19 Dec 2015 16:41:48 +0800 Subject: [PATCH] =?UTF-8?q?=20=E5=88=A0=E9=99=A4PayUtil=E7=B1=BB,=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E8=BD=AC=E7=A7=BB=E5=88=B0PayApi=E7=B1=BB=20#16?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGE.md | 5 + weixin4j-base/CHANGE.md | 6 +- .../com/foxinmy/weixin4j/api/CashApi.java | 10 +- .../com/foxinmy/weixin4j/api/CouponApi.java | 8 +- .../com/foxinmy/weixin4j/api/Pay3Api.java | 304 ++++++++++++- .../weixin4j/payment/PayURLConsts.java | 48 ++ .../com/foxinmy/weixin4j/payment/PayUtil.java | 409 ------------------ .../com/foxinmy/weixin4j/payment/README.md | 13 +- .../weixin4j/payment/WeixinPayProxy.java | 81 +++- .../weixin4j/payment/mch/MchPayRequest.java | 4 +- .../payment/mch/NativePayResponse.java | 60 ++- .../com/foxinmy/weixin4j/util/DigestUtil.java | 41 ++ .../com/foxinmy/weixin4j/mp/api/Pay2Api.java | 107 ++++- .../mp/payment/v2/NativePayResponseV2.java | 33 ++ .../weixin4j/mp/payment/v2/PayUtil2.java | 203 --------- .../com/foxinmy/weixin4j/mp/test/PayTest.java | 15 +- 16 files changed, 649 insertions(+), 698 deletions(-) delete mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/PayUtil.java delete mode 100644 weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/PayUtil2.java diff --git a/CHANGE.md b/CHANGE.md index e6522680..487cf2f9 100644 --- a/CHANGE.md +++ b/CHANGE.md @@ -553,3 +553,8 @@ + weixin4j-mp:新增个性化菜单接口 + weixin4j-mp:WeixinProxy.getCustomRecord 参数变更为 Date startTime, Date endTime, Pageable pageable + + +* 2015-12-19 + + + weixin4j-base:删除PayUtil类,接口转移到PayApi类 diff --git a/weixin4j-base/CHANGE.md b/weixin4j-base/CHANGE.md index a8da0023..0f2f8f8c 100644 --- a/weixin4j-base/CHANGE.md +++ b/weixin4j-base/CHANGE.md @@ -91,4 +91,8 @@ +【重要】修改PayUtil中的createPayJsRequest方法的返回值为MchPayRequest,便于二次发起支付。 - +【重要】添加MchPayRequest的构造函数,便于二次发起支付。 \ No newline at end of file + +【重要】添加MchPayRequest的构造函数,便于二次发起支付。 + +* 2015-12-19 + + + 删除PayUtil类,接口转移到PayApi类 \ No newline at end of file diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/CashApi.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/CashApi.java index f28e360c..675286b7 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/CashApi.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/CashApi.java @@ -14,13 +14,13 @@ import com.foxinmy.weixin4j.http.weixin.WeixinResponse; import com.foxinmy.weixin4j.http.weixin.WeixinSSLRequestExecutor; import com.foxinmy.weixin4j.model.WeixinPayAccount; import com.foxinmy.weixin4j.payment.PayURLConsts; -import com.foxinmy.weixin4j.payment.PayUtil; import com.foxinmy.weixin4j.payment.mch.MPPayment; import com.foxinmy.weixin4j.payment.mch.MPPaymentRecord; import com.foxinmy.weixin4j.payment.mch.MPPaymentResult; import com.foxinmy.weixin4j.payment.mch.Redpacket; import com.foxinmy.weixin4j.payment.mch.RedpacketRecord; import com.foxinmy.weixin4j.payment.mch.RedpacketSendResult; +import com.foxinmy.weixin4j.util.DigestUtil; import com.foxinmy.weixin4j.util.RandomUtil; import com.foxinmy.weixin4j.xml.XmlStream; @@ -65,7 +65,7 @@ public class CashApi { obj.put("mch_id", weixinAccount.getMchId()); obj.put("sub_mch_id", weixinAccount.getSubMchId()); obj.put("wxappid", weixinAccount.getId()); - String sign = PayUtil.paysignMd5(obj, weixinAccount.getPaySignKey()); + String sign = DigestUtil.paysignMd5(obj, weixinAccount.getPaySignKey()); obj.put("sign", sign); String param = XmlStream.map2xml(obj); WeixinResponse response = null; @@ -108,7 +108,7 @@ public class CashApi { para.put("bill_type", "MCHT"); para.put("appid", weixinAccount.getId()); para.put("mch_billno", outTradeNo); - String sign = PayUtil.paysignMd5(para, weixinAccount.getPaySignKey()); + String sign = DigestUtil.paysignMd5(para, weixinAccount.getPaySignKey()); para.put("sign", sign); String param = XmlStream.map2xml(para); WeixinResponse response = null; @@ -152,7 +152,7 @@ public class CashApi { obj.put("sub_mch_id", weixinAccount.getSubMchId()); obj.put("mch_appid", weixinAccount.getId()); obj.put("device_info", weixinAccount.getDeviceInfo()); - String sign = PayUtil.paysignMd5(obj, weixinAccount.getPaySignKey()); + String sign = DigestUtil.paysignMd5(obj, weixinAccount.getPaySignKey()); obj.put("sign", sign); String param = XmlStream.map2xml(obj); WeixinResponse response = null; @@ -198,7 +198,7 @@ public class CashApi { obj.put("mch_id", weixinAccount.getMchId()); obj.put("appid", weixinAccount.getId()); obj.put("partner_trade_no", outTradeNo); - String sign = PayUtil.paysignMd5(obj, weixinAccount.getPaySignKey()); + String sign = DigestUtil.paysignMd5(obj, weixinAccount.getPaySignKey()); obj.put("sign", sign); String param = XmlStream.map2xml(obj); WeixinResponse response = null; diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/CouponApi.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/CouponApi.java index 66c41815..cf052520 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/CouponApi.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/CouponApi.java @@ -12,10 +12,10 @@ import com.foxinmy.weixin4j.http.weixin.WeixinResponse; import com.foxinmy.weixin4j.http.weixin.WeixinSSLRequestExecutor; import com.foxinmy.weixin4j.model.WeixinPayAccount; import com.foxinmy.weixin4j.payment.PayURLConsts; -import com.foxinmy.weixin4j.payment.PayUtil; import com.foxinmy.weixin4j.payment.coupon.CouponDetail; import com.foxinmy.weixin4j.payment.coupon.CouponResult; import com.foxinmy.weixin4j.payment.coupon.CouponStock; +import com.foxinmy.weixin4j.util.DigestUtil; import com.foxinmy.weixin4j.util.RandomUtil; import com.foxinmy.weixin4j.util.StringUtil; import com.foxinmy.weixin4j.xml.XmlStream; @@ -75,7 +75,7 @@ public class CouponApi { map.put("op_user_id", opUserId); map.put("version", "1.0"); map.put("type", "XML"); - String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey()); + String sign = DigestUtil.paysignMd5(map, weixinAccount.getPaySignKey()); map.put("sign", sign); String param = XmlStream.map2xml(map); WeixinResponse response = null; @@ -112,7 +112,7 @@ public class CouponApi { throws WeixinException { Map map = baseMap(); map.put("coupon_stock_id", couponStockId); - String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey()); + String sign = DigestUtil.paysignMd5(map, weixinAccount.getPaySignKey()); map.put("sign", sign); String param = XmlStream.map2xml(map); WeixinResponse response = weixinExecutor.post( @@ -136,7 +136,7 @@ public class CouponApi { throws WeixinException { Map map = baseMap(); map.put("coupon_id", couponId); - String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey()); + String sign = DigestUtil.paysignMd5(map, weixinAccount.getPaySignKey()); map.put("sign", sign); String param = XmlStream.map2xml(map); WeixinResponse response = weixinExecutor.post( diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/Pay3Api.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/Pay3Api.java index 71a123ed..fa28457c 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/Pay3Api.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/Pay3Api.java @@ -18,24 +18,31 @@ import java.util.Map; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeReference; import com.foxinmy.weixin4j.exception.WeixinException; +import com.foxinmy.weixin4j.exception.WeixinPayException; import com.foxinmy.weixin4j.http.weixin.WeixinRequestExecutor; import com.foxinmy.weixin4j.http.weixin.WeixinResponse; import com.foxinmy.weixin4j.http.weixin.WeixinSSLRequestExecutor; import com.foxinmy.weixin4j.http.weixin.XmlResult; import com.foxinmy.weixin4j.model.Consts; import com.foxinmy.weixin4j.model.WeixinPayAccount; +import com.foxinmy.weixin4j.payment.MicroPayPackage; import com.foxinmy.weixin4j.payment.PayURLConsts; -import com.foxinmy.weixin4j.payment.PayUtil; import com.foxinmy.weixin4j.payment.mch.ApiResult; import com.foxinmy.weixin4j.payment.mch.AuthCodeOpenIdResult; +import com.foxinmy.weixin4j.payment.mch.MchPayPackage; +import com.foxinmy.weixin4j.payment.mch.MchPayRequest; import com.foxinmy.weixin4j.payment.mch.Order; +import com.foxinmy.weixin4j.payment.mch.PrePay; import com.foxinmy.weixin4j.payment.mch.RefundRecord; import com.foxinmy.weixin4j.payment.mch.RefundResult; import com.foxinmy.weixin4j.type.BillType; import com.foxinmy.weixin4j.type.CurrencyType; import com.foxinmy.weixin4j.type.IdQuery; import com.foxinmy.weixin4j.type.IdType; +import com.foxinmy.weixin4j.type.SignType; +import com.foxinmy.weixin4j.type.TradeType; import com.foxinmy.weixin4j.util.DateUtil; +import com.foxinmy.weixin4j.util.DigestUtil; import com.foxinmy.weixin4j.util.RandomUtil; import com.foxinmy.weixin4j.util.StringUtil; import com.foxinmy.weixin4j.util.Weixin4jConfigUtil; @@ -55,7 +62,6 @@ import com.foxinmy.weixin4j.xml.XmlStream; public class Pay3Api { private final WeixinRequestExecutor weixinExecutor; - private final WeixinPayAccount weixinAccount; public Pay3Api(WeixinPayAccount weixinAccount) { @@ -63,6 +69,276 @@ public class Pay3Api { this.weixinExecutor = new WeixinRequestExecutor(); } + /** + * 统一下单接口
+ * 除被扫支付场景以外,商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易回话标识后再按扫码、JSAPI + * 、APP等不同场景生成交易串调起支付。 + * + * @param payPackage + * 包含订单信息的对象 + * @see com.foxinmy.weixin4j.payment.mch.MchPayPackage + * @see com.foxinmy.weixin4j.payment.mch.PrePay + * @see 统一下单接口 + * @return 预支付对象 + */ + public PrePay createPrePay(MchPayPackage payPackage) + throws WeixinPayException { + if (StringUtil.isBlank(payPackage.getSign())) { + payPackage.setSign(DigestUtil.paysignMd5(payPackage, + weixinAccount.getPaySignKey())); + } + String payJsRequestXml = XmlStream.toXML(payPackage); + try { + WeixinResponse response = weixinExecutor.post( + PayURLConsts.MCH_UNIFIEDORDER_URL, payJsRequestXml); + PrePay prePay = response.getAsObject(new TypeReference() { + }); + if (!prePay.getReturnCode().equalsIgnoreCase(Consts.SUCCESS)) { + throw new WeixinPayException(prePay.getReturnMsg(), + prePay.getReturnCode()); + } + if (!prePay.getResultCode().equalsIgnoreCase(Consts.SUCCESS)) { + throw new WeixinPayException(prePay.getResultCode(), + prePay.getErrCodeDes()); + } + return prePay; + } catch (WeixinException e) { + throw new WeixinPayException(e.getErrorCode(), e.getErrorMsg()); + } + } + + /** + * 生成V3.x版本JSAPI支付字符串 + * + * @param openId + * 用户ID + * @param body + * 订单描述 + * @param outTradeNo + * 订单号 + * @param totalFee + * 订单总额 按实际金额传入即可(元) 构造函数会转换为分 + * @param notifyUrl + * 支付通知地址 + * @param createIp + * ip地址 + * @param weixinAccount + * 商户信息 + * @return 支付json串 + * @throws WeixinPayException + */ + public String createPayJsRequestJson(String openId, String body, + String outTradeNo, double totalFee, String notifyUrl, + String createIp, WeixinPayAccount weixinAccount) + throws WeixinPayException { + return JSON.toJSONString(createPayJsRequest(openId, body, outTradeNo, + totalFee, notifyUrl, createIp, null, null, null, null, null)); + } + + /** + * 生成V3.x版本JSAPI支付对象【完整参数】 + * + * @param openId + * 用户ID + * @param body + * 商品描述 + * @param outTradeNo + * 商户内部唯一订单号 + * @param totalFee + * 商品总额 单位元 + * @param notifyUrl + * 支付回调URL + * @param createIp + * 订单生成的机器 IP + * @param attach + * 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据 + * @param timeStart + * 订单生成时间,格式为yyyyMMddHHmmss + * @param timeExpire + * 订单失效时间,格式为yyyyMMddHHmmss;注意:最短失效时间间隔必须大于5分钟 + * @param goodsTag + * 商品标记,代金券或立减优惠功能的参数 + * @param limitPay + * 指定支付方式:no_credit--指定不能使用信用卡支付 + * @see com.foxinmy.weixin4j.payment.mch.MchPayRequest + * @return MchPayRequest对象;注意:如果要转换为JSON格式请使用fastjson中的JSON对象或者直接用MchPayRequest# + * asPayJsRequestJson方法 + * @throws WeixinPayException + */ + public MchPayRequest createPayJsRequest(String openId, String body, + String outTradeNo, double totalFee, String notifyUrl, + String createIp, String attach, Date timeStart, Date timeExpire, + String goodsTag, String limitPay) throws WeixinPayException { + MchPayPackage payPackage = new MchPayPackage(weixinAccount, openId, + body, outTradeNo, totalFee, notifyUrl, createIp, + TradeType.JSAPI); + payPackage.setAttach(attach); + payPackage.setTimeStart(timeStart); + payPackage.setTimeExpire(timeExpire); + payPackage.setGoodsTag(goodsTag); + payPackage.setLimitPay(limitPay); + payPackage.setSign(DigestUtil.paysignMd5(payPackage, + weixinAccount.getPaySignKey())); + PrePay prePay = createPrePay(payPackage); + MchPayRequest jsPayRequest = new MchPayRequest(prePay); + jsPayRequest.setSignType(SignType.MD5); + jsPayRequest.setPaySign(DigestUtil.paysignMd5(jsPayRequest, + weixinAccount.getPaySignKey())); + return jsPayRequest; + } + + /** + * 创建V3.x NativePay支付(扫码支付)链接【模式一】 + * + * @param productId + * 与订单ID等价 + * @return 支付链接 + * @see 扫码支付 + * @see 模式一 + */ + public String createNativePayRequestURL(String productId) { + Map map = new HashMap(); + String timestamp = DateUtil.timestamp2string(); + String noncestr = RandomUtil.generateString(16); + map.put("appid", weixinAccount.getId()); + map.put("mch_id", weixinAccount.getMchId()); + map.put("time_stamp", timestamp); + map.put("nonce_str", noncestr); + map.put("product_id", productId); + String sign = DigestUtil.paysignMd5(map, weixinAccount.getPaySignKey()); + return String.format(PayURLConsts.MCH_NATIVE_URL, sign, + weixinAccount.getId(), weixinAccount.getMchId(), productId, + timestamp, noncestr); + } + + /** + * 创建V3.x NativePay支付(扫码支付)链接【模式二】【必填参数】 + * + * @param productId + * 商品ID + * @param body + * 商品描述 + * @param outTradeNo + * 商户内部唯一订单号 + * @param totalFee + * 商品总额 单位元 + * @param notifyUrl + * 支付回调URL + * @param createIp + * 订单生成的机器 IP + * @return 支付链接 + * @see 扫码支付 + * @see 模式二 + * @throws WeixinPayException + */ + public String createNativePayRequestURL(String productId, String body, + String outTradeNo, double totalFee, String notifyUrl, + String createIp) throws WeixinPayException { + return createNativePayRequestURL(productId, body, outTradeNo, totalFee, + notifyUrl, createIp, null, null, null, null, null); + } + + /** + * 创建V3.x NativePay支付(扫码支付)链接【模式二】【完整参数】 + * + * @param productId + * 商品ID + * @param body + * 商品描述 + * @param outTradeNo + * 商户内部唯一订单号 + * @param totalFee + * 商品总额 单位元 + * @param notifyUrl + * 支付回调URL + * @param createIp + * 订单生成的机器 IP + * @param attach + * 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据 + * @param timeStart + * 订单生成时间,格式为yyyyMMddHHmmss + * @param timeExpire + * 订单失效时间,格式为yyyyMMddHHmmss;注意:最短失效时间间隔必须大于5分钟 + * @param goodsTag + * 商品标记,代金券或立减优惠功能的参数 + * @param limitPay + * 指定支付方式:no_credit--指定不能使用信用卡支付 + * @return 支付链接 + * @see 扫码支付 + * @see 模式二 + * @throws WeixinPayException + */ + public String createNativePayRequestURL(String productId, String body, + String outTradeNo, double totalFee, String notifyUrl, + String createIp, String attach, Date timeStart, Date timeExpire, + String goodsTag, String limitPay) throws WeixinPayException { + MchPayPackage payPackage = new MchPayPackage(weixinAccount, null, body, + outTradeNo, totalFee, notifyUrl, createIp, TradeType.NATIVE); + payPackage.setProductId(productId); + payPackage.setAttach(attach); + payPackage.setTimeStart(timeStart); + payPackage.setTimeExpire(timeExpire); + payPackage.setGoodsTag(goodsTag); + payPackage.setLimitPay(limitPay); + payPackage.setSign(DigestUtil.paysignMd5(payPackage, + weixinAccount.getPaySignKey())); + PrePay prePay = createPrePay(payPackage); + return prePay.getCodeUrl(); + } + + /** + * 提交被扫支付 + * + * @param authCode + * 扫码支付授权码 ,设备读取用户微信中的条码或者二维码信息 + * @param body + * 商品描述 + * @param orderNo + * 商户内部唯一订单号 + * @param orderFee + * 商品总额 单位元 + * @param createIp + * 订单生成的机器 IP + * @return 支付的订单信息 + * @see {@link #createMicroPay(MicroPayPackage)} + * @throws WeixinException + */ + public Order createMicroPay(String authCode, String body, String orderNo, + double orderFee, String createIp) throws WeixinException { + MicroPayPackage payPackage = new MicroPayPackage(weixinAccount, + authCode, body, orderNo, orderFee, createIp); + return createMicroPay(payPackage); + } + + /** + * 提交被扫支付:收银员使用扫码设备读取微信用户刷卡授权码以后,二维码或条码信息传送至商户收银台,由商户收银台或者商户后台调用该接口发起支付. + * + * @param payPackage + * 订单信息 + * @return 支付的订单信息 + * @throws WeixinException + * @see com.foxinmy.weixin4j.payment.mch.Order + * @see 提交被扫支付API + */ + public Order createMicroPay(MicroPayPackage payPackage) + throws WeixinException { + String sign = DigestUtil.paysignMd5(payPackage, + weixinAccount.getPaySignKey()); + payPackage.setSign(sign); + String para = XmlStream.toXML(payPackage); + WeixinResponse response = weixinExecutor.post( + PayURLConsts.MCH_MICROPAY_URL, para); + return response + .getAsObject(new TypeReference() { + }); + } + /** * 订单查询 *

@@ -82,7 +358,7 @@ public class Pay3Api { */ public Order orderQuery(IdQuery idQuery) throws WeixinException { Map map = baseMap(idQuery); - String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey()); + String sign = DigestUtil.paysignMd5(map, weixinAccount.getPaySignKey()); map.put("sign", sign); String param = XmlStream.map2xml(map); WeixinResponse response = weixinExecutor.post( @@ -142,8 +418,8 @@ public class Pay3Api { refundFeeType = CurrencyType.CNY; } map.put("refund_fee_type", refundFeeType.name()); - String sign = PayUtil - .paysignMd5(map, weixinAccount.getPaySignKey()); + String sign = DigestUtil.paysignMd5(map, + weixinAccount.getPaySignKey()); map.put("sign", sign); String param = XmlStream.map2xml(map); WeixinRequestExecutor weixinExecutor = new WeixinSSLRequestExecutor( @@ -175,7 +451,7 @@ public class Pay3Api { * 商户系统内部的退款单号,商 户系统内部唯一,同一退款单号多次请求只退一笔 * @param totalFee * 订单总金额,单位为元 - * @see {@link #refundApply(InputStream, IdQuery, String, double, double, String, CurrencyType)} + * @see {@link #refundApply(InputStream, IdQuery, String, double, double,CurrencyType, String)} */ public RefundResult refundApply(InputStream ca, IdQuery idQuery, String outRefundNo, double totalFee) throws WeixinException { @@ -204,8 +480,8 @@ public class Pay3Api { WeixinRequestExecutor weixinExecutor = new WeixinSSLRequestExecutor( weixinAccount.getMchId(), ca); Map map = baseMap(idQuery); - String sign = PayUtil - .paysignMd5(map, weixinAccount.getPaySignKey()); + String sign = DigestUtil.paysignMd5(map, + weixinAccount.getPaySignKey()); map.put("sign", sign); String param = XmlStream.map2xml(map); WeixinResponse response = weixinExecutor.post( @@ -241,7 +517,7 @@ public class Pay3Api { } catch (UnsupportedEncodingException ignore) { ; } - String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey()); + String sign = DigestUtil.paysignMd5(map, weixinAccount.getPaySignKey()); map.put("sign", sign); String param = XmlStream.map2xml(map); WeixinResponse response = weixinExecutor.post( @@ -268,7 +544,7 @@ public class Pay3Api { public ApiResult closeOrder(String outTradeNo) throws WeixinException { Map map = baseMap(new IdQuery(outTradeNo, IdType.TRADENO)); - String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey()); + String sign = DigestUtil.paysignMd5(map, weixinAccount.getPaySignKey()); map.put("sign", sign); String param = XmlStream.map2xml(map); WeixinResponse response = weixinExecutor.post( @@ -317,7 +593,7 @@ public class Pay3Api { Map map = baseMap(null); map.put("bill_date", formatBillDate); map.put("bill_type", billType.name()); - String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey()); + String sign = DigestUtil.paysignMd5(map, weixinAccount.getPaySignKey()); map.put("sign", sign); String param = XmlStream.map2xml(map); WeixinResponse response = weixinExecutor.post( @@ -373,7 +649,7 @@ public class Pay3Api { */ public RefundRecord refundQuery(IdQuery idQuery) throws WeixinException { Map map = baseMap(idQuery); - String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey()); + String sign = DigestUtil.paysignMd5(map, weixinAccount.getPaySignKey()); map.put("sign", sign); String param = XmlStream.map2xml(map); WeixinResponse response = weixinExecutor.post( @@ -414,7 +690,7 @@ public class Pay3Api { map.put("user_ip", ip); map.put("time", DateUtil.fortmat2yyyyMMddHHmmss(time)); map.putAll((Map) JSON.toJSON(returnXml)); - String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey()); + String sign = DigestUtil.paysignMd5(map, weixinAccount.getPaySignKey()); map.put("sign", sign); String param = XmlStream.map2xml(map); WeixinResponse response = weixinExecutor.post( @@ -437,7 +713,7 @@ public class Pay3Api { throws WeixinException { Map map = baseMap(null); map.put("auth_code", authCode); - String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey()); + String sign = DigestUtil.paysignMd5(map, weixinAccount.getPaySignKey()); map.put("sign", sign); String param = XmlStream.map2xml(map); WeixinResponse response = weixinExecutor.post( diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/PayURLConsts.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/PayURLConsts.java index d1cbb497..c7f2d723 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/PayURLConsts.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/PayURLConsts.java @@ -1,5 +1,15 @@ package com.foxinmy.weixin4j.payment; +import java.util.HashMap; +import java.util.Map; + +import com.alibaba.fastjson.JSON; +import com.foxinmy.weixin4j.type.SignType; +import com.foxinmy.weixin4j.util.DateUtil; +import com.foxinmy.weixin4j.util.DigestUtil; +import com.foxinmy.weixin4j.util.MapUtil; +import com.foxinmy.weixin4j.util.RandomUtil; + /** * 支付URL常量类 * @@ -108,4 +118,42 @@ public final class PayURLConsts { */ public static final String MCH_AUTHCODE_OPENID_URL = MCH_BASE_URL + "/tools/authcodetoopenid"; + + /** + *

+ * 生成编辑地址请求 + *

+ * + * err_msg edit_address:ok获取编辑收货地址成功
edit_address:fail获取编辑收货地址失败
+ * userName 收货人姓名
telNumber 收货人电话
addressPostalCode 邮编
+ * proviceFirstStageName 国标收货地址第一级地址
addressCitySecondStageName + * 国标收货地址第二级地址
addressCountiesThirdStageName 国标收货地址第三级地址
+ * addressDetailInfo 详细收货地址信息
nationalCode 收货地址国家码
+ * + * @param appId + * 公众号的ID + * @param url + * 当前访问页的URL + * @param accessToken + * snsapi_base授权时产生的token + * @return + */ + public static String createAddressRequestJson(String appId, String url, + String accessToken) { + Map map = new HashMap(); + map.put("appId", appId); + map.put("timeStamp", DateUtil.timestamp2string()); + map.put("nonceStr", RandomUtil.generateString(16)); + map.put("url", url); + map.put("accessToken", accessToken); + String sign = DigestUtil.SHA1(MapUtil.toJoinString(map, false, true, + null)); + map.remove("url"); + map.remove("accessToken"); + map.put("scope", "jsapi_address"); + map.put("signType", SignType.SHA1.name().toLowerCase()); + map.put("addrSign", sign); + + return JSON.toJSONString(map); + } } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/PayUtil.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/PayUtil.java deleted file mode 100644 index 1f4e8eb3..00000000 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/PayUtil.java +++ /dev/null @@ -1,409 +0,0 @@ -package com.foxinmy.weixin4j.payment; - -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.TypeReference; -import com.foxinmy.weixin4j.exception.WeixinException; -import com.foxinmy.weixin4j.exception.WeixinPayException; -import com.foxinmy.weixin4j.http.weixin.WeixinRequestExecutor; -import com.foxinmy.weixin4j.http.weixin.WeixinResponse; -import com.foxinmy.weixin4j.model.Consts; -import com.foxinmy.weixin4j.model.WeixinPayAccount; -import com.foxinmy.weixin4j.payment.mch.MchPayPackage; -import com.foxinmy.weixin4j.payment.mch.MchPayRequest; -import com.foxinmy.weixin4j.payment.mch.Order; -import com.foxinmy.weixin4j.payment.mch.PrePay; -import com.foxinmy.weixin4j.type.SignType; -import com.foxinmy.weixin4j.type.TradeType; -import com.foxinmy.weixin4j.util.DateUtil; -import com.foxinmy.weixin4j.util.DigestUtil; -import com.foxinmy.weixin4j.util.MapUtil; -import com.foxinmy.weixin4j.util.RandomUtil; -import com.foxinmy.weixin4j.util.StringUtil; -import com.foxinmy.weixin4j.util.Weixin4jConfigUtil; -import com.foxinmy.weixin4j.xml.XmlStream; - -/** - * 支付工具类(JSAPI,NATIVE,MicroPay) - * - * @className PayUtil - * @author jy - * @date 2014年10月28日 - * @since JDK 1.6 - * @see - */ -public class PayUtil { - - /** - * md5签名(一般用于V3.x支付接口) - * - * @param obj - * 签名对象 - * @param paySignKey - * 支付API的密钥 - * @return - */ - public static String paysignMd5(Object obj, String paySignKey) { - StringBuilder sb = new StringBuilder(); - // a--->string1 - sb.append(MapUtil.toJoinString(obj, false, false, null)); - // b---> - // 在 string1 最后拼接上 key=paternerKey 得到 stringSignTemp 字符串,并 对 - // stringSignTemp 进行 md5 运算 - // 再将得到的 字符串所有字符转换为大写 ,得到 sign 值 signValue。 - sb.append("&key=").append(paySignKey); - return DigestUtil.MD5(sb.toString()).toUpperCase(); - } - - /** - * 生成V3.x版本JSAPI支付字符串 - * - * @param openId - * 用户ID - * @param body - * 订单描述 - * @param outTradeNo - * 订单号 - * @param totalFee - * 订单总额 按实际金额传入即可(元) 构造函数会转换为分 - * @param notifyUrl - * 支付通知地址 - * @param createIp - * ip地址 - * @param weixinAccount - * 商户信息 - * @return 支付json串 - * @throws WeixinPayException - */ - public static String createPayJsRequestJson(String openId, String body, - String outTradeNo, double totalFee, String notifyUrl, - String createIp, WeixinPayAccount weixinAccount) - throws WeixinPayException { - return JSON.toJSONString(createPayJsRequest(weixinAccount, openId, - body, outTradeNo, totalFee, notifyUrl, createIp, null, null, - null, null, null)); - } - - /** - * 生成V3.x版本JSAPI支付对象【完整参数】 - * - * @param weixinAccount - * 支付配置信息 - * @param openId - * 用户ID - * @param body - * 商品描述 - * @param outTradeNo - * 商户内部唯一订单号 - * @param totalFee - * 商品总额 单位元 - * @param notifyUrl - * 支付回调URL - * @param createIp - * 订单生成的机器 IP - * @param attach - * 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据 - * @param timeStart - * 订单生成时间,格式为yyyyMMddHHmmss - * @param timeExpire - * 订单失效时间,格式为yyyyMMddHHmmss;注意:最短失效时间间隔必须大于5分钟 - * @param goodsTag - * 商品标记,代金券或立减优惠功能的参数 - * @param limitPay - * 指定支付方式:no_credit--指定不能使用信用卡支付 - * @see com.foxinmy.weixin4j.payment.mch.MchPayRequest - * @return MchPayRequest对象;注意:如果要转换为JSON格式请使用fastjson包或者直接用MchPayRequest# - * asPayJsRequestJson方法 - * @throws WeixinPayException - */ - public static MchPayRequest createPayJsRequest( - WeixinPayAccount weixinAccount, String openId, String body, - String outTradeNo, double totalFee, String notifyUrl, - String createIp, String attach, Date timeStart, Date timeExpire, - String goodsTag, String limitPay) throws WeixinPayException { - MchPayPackage payPackage = new MchPayPackage(weixinAccount, openId, - body, outTradeNo, totalFee, notifyUrl, createIp, - TradeType.JSAPI); - payPackage.setAttach(attach); - payPackage.setTimeStart(timeStart); - payPackage.setTimeExpire(timeExpire); - payPackage.setGoodsTag(goodsTag); - payPackage.setLimitPay(limitPay); - String paySignKey = weixinAccount.getPaySignKey(); - payPackage.setSign(paysignMd5(payPackage, paySignKey)); - PrePay prePay = createPrePay(payPackage, paySignKey); - MchPayRequest jsPayRequest = new MchPayRequest(prePay); - jsPayRequest.setSignType(SignType.MD5); - jsPayRequest.setPaySign(paysignMd5(jsPayRequest, paySignKey)); - return jsPayRequest; - } - - /** - * 统一下单接口
- * 除被扫支付场景以外,商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易回话标识后再按扫码、JSAPI - * 、APP等不同场景生成交易串调起支付。 - * - * @param payPackage - * 包含订单信息的对象 - * @param paySignKey - * 如果sign为空 则拿paysignkey进行签名 - * @see com.foxinmy.weixin4j.payment.mch.MchPayPackage - * @see com.foxinmy.weixin4j.payment.mch.PrePay - * @see 统一下单接口 - * @return 预支付对象 - */ - private final static WeixinRequestExecutor httpClient = new WeixinRequestExecutor(); - - public static PrePay createPrePay(MchPayPackage payPackage, - String paySignKey) throws WeixinPayException { - if (StringUtil.isBlank(payPackage.getSign())) { - payPackage.setSign(paysignMd5(payPackage, paySignKey)); - } - String payJsRequestXml = XmlStream.toXML(payPackage); - try { - WeixinResponse response = httpClient.post( - PayURLConsts.MCH_UNIFIEDORDER_URL, payJsRequestXml); - PrePay prePay = response.getAsObject(new TypeReference() { - }); - if (!prePay.getReturnCode().equalsIgnoreCase(Consts.SUCCESS)) { - throw new WeixinPayException(prePay.getReturnMsg(), - prePay.getReturnCode()); - } - if (!prePay.getResultCode().equalsIgnoreCase(Consts.SUCCESS)) { - throw new WeixinPayException(prePay.getResultCode(), - prePay.getErrCodeDes()); - } - return prePay; - } catch (WeixinException e) { - throw new WeixinPayException(e.getErrorCode(), e.getErrorMsg()); - } - } - - /** - *

- * 生成编辑地址请求 - *

- * - * err_msg edit_address:ok获取编辑收货地址成功
edit_address:fail获取编辑收货地址失败
- * userName 收货人姓名
telNumber 收货人电话
addressPostalCode 邮编
- * proviceFirstStageName 国标收货地址第一级地址
addressCitySecondStageName - * 国标收货地址第二级地址
addressCountiesThirdStageName 国标收货地址第三级地址
- * addressDetailInfo 详细收货地址信息
nationalCode 收货地址国家码
- * - * @param appId - * 公众号的ID - * @param url - * 当前访问页的URL - * @param accessToken - * snsapi_base授权时产生的token - * @return - */ - public static String createAddressRequestJson(String appId, String url, - String accessToken) { - Map map = new HashMap(); - map.put("appId", appId); - map.put("timeStamp", DateUtil.timestamp2string()); - map.put("nonceStr", RandomUtil.generateString(16)); - map.put("url", url); - map.put("accessToken", accessToken); - String sign = DigestUtil.SHA1(MapUtil.toJoinString(map, false, true, - null)); - map.remove("url"); - map.remove("accessToken"); - map.put("scope", "jsapi_address"); - map.put("signType", SignType.SHA1.name().toLowerCase()); - map.put("addrSign", sign); - - return JSON.toJSONString(map); - } - - /** - * 创建V3.x NativePay支付(扫码支付)链接【模式一】 - * - * @param weixinAccount - * 支付配置信息 - * @param productId - * 与订单ID等价 - * @return 支付链接 - * @see 扫码支付 - * @see 模式一 - */ - public static String createNativePayRequestURL( - WeixinPayAccount weixinAccount, String productId) { - Map map = new HashMap(); - String timestamp = DateUtil.timestamp2string(); - String noncestr = RandomUtil.generateString(16); - map.put("appid", weixinAccount.getId()); - map.put("mch_id", weixinAccount.getMchId()); - map.put("time_stamp", timestamp); - map.put("nonce_str", noncestr); - map.put("product_id", productId); - String sign = paysignMd5(map, weixinAccount.getPaySignKey()); - return String.format(PayURLConsts.MCH_NATIVE_URL, sign, - weixinAccount.getId(), weixinAccount.getMchId(), productId, - timestamp, noncestr); - } - - /** - * 创建V3.x NativePay支付(扫码支付)链接【模式二】【必填参数】 - * - * @param weixinAccount - * 支付配置信息 - * @param productId - * 商品ID - * @param body - * 商品描述 - * @param outTradeNo - * 商户内部唯一订单号 - * @param totalFee - * 商品总额 单位元 - * @param notifyUrl - * 支付回调URL - * @param createIp - * 订单生成的机器 IP - * @return 支付链接 - * @see 扫码支付 - * @see 模式二 - * @throws WeixinPayException - */ - public static String createNativePayRequestURL( - WeixinPayAccount weixinAccount, String productId, String body, - String outTradeNo, double totalFee, String notifyUrl, - String createIp) throws WeixinPayException { - return createNativePayRequestURL(weixinAccount, productId, body, - outTradeNo, totalFee, notifyUrl, createIp, null, null, null, - null, null); - } - - /** - * 创建V3.x NativePay支付(扫码支付)链接【模式二】【完整参数】 - * - * @param weixinAccount - * 支付配置信息 - * @param productId - * 商品ID - * @param body - * 商品描述 - * @param outTradeNo - * 商户内部唯一订单号 - * @param totalFee - * 商品总额 单位元 - * @param notifyUrl - * 支付回调URL - * @param createIp - * 订单生成的机器 IP - * @param attach - * 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据 - * @param timeStart - * 订单生成时间,格式为yyyyMMddHHmmss - * @param timeExpire - * 订单失效时间,格式为yyyyMMddHHmmss;注意:最短失效时间间隔必须大于5分钟 - * @param goodsTag - * 商品标记,代金券或立减优惠功能的参数 - * @param limitPay - * 指定支付方式:no_credit--指定不能使用信用卡支付 - * @return 支付链接 - * @see 扫码支付 - * @see 模式二 - * @throws WeixinPayException - */ - public static String createNativePayRequestURL( - WeixinPayAccount weixinAccount, String productId, String body, - String outTradeNo, double totalFee, String notifyUrl, - String createIp, String attach, Date timeStart, Date timeExpire, - String goodsTag, String limitPay) throws WeixinPayException { - MchPayPackage payPackage = new MchPayPackage(weixinAccount, null, body, - outTradeNo, totalFee, notifyUrl, createIp, TradeType.NATIVE); - payPackage.setProductId(productId); - payPackage.setAttach(attach); - payPackage.setTimeStart(timeStart); - payPackage.setTimeExpire(timeExpire); - payPackage.setGoodsTag(goodsTag); - payPackage.setLimitPay(limitPay); - String paySignKey = weixinAccount.getPaySignKey(); - payPackage.setSign(paysignMd5(payPackage, paySignKey)); - PrePay prePay = createPrePay(payPackage, paySignKey); - return prePay.getCodeUrl(); - } - - /** - * 提交被扫支付 - * - * @param authCode - * 扫码支付授权码 ,设备读取用户微信中的条码或者二维码信息 - * @param body - * 商品描述 - * @param orderNo - * 商户内部唯一订单号 - * @param orderFee - * 商品总额 单位元 - * @param createIp - * 订单生成的机器 IP - * @param weixinAccount - * 商户信息 - * @return 支付的订单信息 - * @see {@link #createMicroPay(MicroPayPackage, WeixinPayAccount)} - * @throws WeixinException - */ - public static Order createMicroPay(String authCode, String body, - String orderNo, double orderFee, String createIp, - WeixinPayAccount weixinAccount) throws WeixinException { - MicroPayPackage payPackage = new MicroPayPackage(weixinAccount, - authCode, body, orderNo, orderFee, createIp); - return createMicroPay(payPackage, weixinAccount); - } - - /** - * 提交被扫支付:收银员使用扫码设备读取微信用户刷卡授权码以后,二维码或条码信息传送至商户收银台,由商户收银台或者商户后台调用该接口发起支付. - * - * @param payPackage - * 订单信息 - * @param weixinAccount - * 商户信息 - * @return 支付的订单信息 - * @throws WeixinException - * @see com.foxinmy.weixin4j.payment.mch.Order - * @see 提交被扫支付API - */ - public static Order createMicroPay(MicroPayPackage payPackage, - WeixinPayAccount weixinAccount) throws WeixinException { - String sign = paysignMd5(payPackage, weixinAccount.getPaySignKey()); - payPackage.setSign(sign); - String para = XmlStream.toXML(payPackage); - WeixinResponse response = httpClient.post( - PayURLConsts.MCH_MICROPAY_URL, para); - return response - .getAsObject(new TypeReference() { - }); - } - - private static String JSAPI() throws WeixinPayException { - WeixinPayAccount weixinAccount = JSON.parseObject( - Weixin4jConfigUtil.getValue("account"), WeixinPayAccount.class); - return createPayJsRequestJson("oyFLst1bqtuTcxK-ojF8hOGtLQao", "支付测试", - "JSAPI01", 0.01d, "http://127.0.0.1/jsapi/notify", "127.0.0.0", - weixinAccount); - } - - private static String NATIVE() { - WeixinPayAccount weixinAccount = JSON.parseObject( - Weixin4jConfigUtil.getValue("account"), WeixinPayAccount.class); - return createNativePayRequestURL(weixinAccount, "P1"); - } - - public static void main(String[] args) throws WeixinPayException { - // V3版本下的JS支付 - System.out.println(JSAPI()); - // V3版本下的原生支付 - System.out.println(NATIVE()); - } -} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/README.md b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/README.md index cb3cb22b..bb8bcc2c 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/README.md +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/README.md @@ -6,7 +6,8 @@ **在`2014年10月9号`之前申请并审核通过的支付接口应该属于`V2版本`支付,而之后申请的接口则为`V3版本(商户平台)`支付** -[PayUtil](./PayUtil.java) + +[Pay3Api](../api/Pay3Api.java) ------------------------- * createPayJsRequestJson: 创建V3版本(商户平台)的JSAPI支付串 @@ -17,12 +18,6 @@ * createMicroPay: 创建刷卡支付(商户平台)请求 -* createAddressRequestJson: 生成编辑收货地址请求串 - - -[Pay3Api](../api/Pay3Api.java) -------------------------- - * orderQuery: 订单查询接口 * refundOrder: 退款申请接口 @@ -39,6 +34,10 @@ [Pay2Api](https://github.com/foxinmy/weixin4j/blob/master/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/Pay2Api.java) ------------------------- +* createPayJsRequestJson: 创建V2版本的JSAPI支付串 + +* createNativePayRequestURL: 创建V2版本的扫码支付链接 + * orderQuery: 订单查询接口 * refundOrder: 退款申请接口 diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/WeixinPayProxy.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/WeixinPayProxy.java index 6a357cbc..a504a8b9 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/WeixinPayProxy.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/WeixinPayProxy.java @@ -11,6 +11,7 @@ import com.foxinmy.weixin4j.api.CashApi; import com.foxinmy.weixin4j.api.CouponApi; import com.foxinmy.weixin4j.api.Pay3Api; import com.foxinmy.weixin4j.exception.WeixinException; +import com.foxinmy.weixin4j.exception.WeixinPayException; import com.foxinmy.weixin4j.http.weixin.XmlResult; import com.foxinmy.weixin4j.model.WeixinPayAccount; import com.foxinmy.weixin4j.payment.coupon.CouponDetail; @@ -21,7 +22,9 @@ import com.foxinmy.weixin4j.payment.mch.AuthCodeOpenIdResult; import com.foxinmy.weixin4j.payment.mch.MPPayment; import com.foxinmy.weixin4j.payment.mch.MPPaymentRecord; import com.foxinmy.weixin4j.payment.mch.MPPaymentResult; +import com.foxinmy.weixin4j.payment.mch.MchPayPackage; import com.foxinmy.weixin4j.payment.mch.Order; +import com.foxinmy.weixin4j.payment.mch.PrePay; import com.foxinmy.weixin4j.payment.mch.Redpacket; import com.foxinmy.weixin4j.payment.mch.RedpacketRecord; import com.foxinmy.weixin4j.payment.mch.RedpacketSendResult; @@ -48,8 +51,6 @@ public class WeixinPayProxy { private final CouponApi couponApi; private final CashApi cashApi; - private final String DEFAULT_CA_FILE; - /** * 使用weixin4j.properties配置的账号信息 */ @@ -68,8 +69,24 @@ public class WeixinPayProxy { this.pay3Api = new Pay3Api(weixinAccount); this.couponApi = new CouponApi(weixinAccount); this.cashApi = new CashApi(weixinAccount); - this.DEFAULT_CA_FILE = Weixin4jConfigUtil.getClassPathValue("certificate.file", - Weixin4jConst.DEFAULT_CAFILE_PATH); + } + + /** + * 统一下单接口
+ * 除被扫支付场景以外,商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易回话标识后再按扫码、JSAPI + * 、APP等不同场景生成交易串调起支付。 + * + * @param payPackage + * 包含订单信息的对象 + * @see com.foxinmy.weixin4j.payment.mch.MchPayPackage + * @see com.foxinmy.weixin4j.payment.mch.PrePay + * @see 统一下单接口 + * @return 预支付对象 + */ + public PrePay createPrePay(MchPayPackage payPackage) + throws WeixinPayException { + return pay3Api.createPrePay(payPackage); } /** @@ -150,8 +167,12 @@ public class WeixinPayProxy { public com.foxinmy.weixin4j.payment.mch.RefundResult refundApply( IdQuery idQuery, String outRefundNo, double totalFee) throws WeixinException, IOException { - return pay3Api.refundApply(new FileInputStream(DEFAULT_CA_FILE), - idQuery, outRefundNo, totalFee); + return pay3Api + .refundApply( + new FileInputStream(Weixin4jConfigUtil + .getClassPathValue("certificate.file", + Weixin4jConst.DEFAULT_CAFILE_PATH)), + idQuery, outRefundNo, totalFee); } /** @@ -236,8 +257,12 @@ public class WeixinPayProxy { */ public ApiResult reverseOrder(IdQuery idQuery) throws WeixinException, IOException { - return pay3Api.reverseOrder(new FileInputStream(DEFAULT_CA_FILE), - idQuery); + return pay3Api + .reverseOrder( + new FileInputStream(Weixin4jConfigUtil + .getClassPathValue("certificate.file", + Weixin4jConst.DEFAULT_CAFILE_PATH)), + idQuery); } /** @@ -344,8 +369,12 @@ public class WeixinPayProxy { */ public CouponResult sendCoupon(String couponStockId, String partnerTradeNo, String openId) throws WeixinException, IOException { - return couponApi.sendCoupon(new FileInputStream(DEFAULT_CA_FILE), - couponStockId, partnerTradeNo, openId, null); + return couponApi + .sendCoupon( + new FileInputStream(Weixin4jConfigUtil + .getClassPathValue("certificate.file", + Weixin4jConst.DEFAULT_CAFILE_PATH)), + couponStockId, partnerTradeNo, openId, null); } /** @@ -409,8 +438,12 @@ public class WeixinPayProxy { */ public RedpacketSendResult sendRedpack(Redpacket redpacket) throws WeixinException, IOException { - return cashApi.sendRedpack(new FileInputStream(DEFAULT_CA_FILE), - redpacket); + return cashApi + .sendRedpack( + new FileInputStream(Weixin4jConfigUtil + .getClassPathValue("certificate.file", + Weixin4jConst.DEFAULT_CAFILE_PATH)), + redpacket); } /** @@ -439,8 +472,12 @@ public class WeixinPayProxy { */ public RedpacketRecord queryRedpack(String outTradeNo) throws WeixinException, IOException { - return cashApi.queryRedpack(new FileInputStream(DEFAULT_CA_FILE), - outTradeNo); + return cashApi + .queryRedpack( + new FileInputStream(Weixin4jConfigUtil + .getClassPathValue("certificate.file", + Weixin4jConst.DEFAULT_CAFILE_PATH)), + outTradeNo); } /** @@ -470,8 +507,12 @@ public class WeixinPayProxy { */ public MPPaymentResult mpPayment(MPPayment mpPayment) throws WeixinException, IOException { - return cashApi.mchPayment(new FileInputStream(DEFAULT_CA_FILE), - mpPayment); + return cashApi + .mchPayment( + new FileInputStream(Weixin4jConfigUtil + .getClassPathValue("certificate.file", + Weixin4jConst.DEFAULT_CAFILE_PATH)), + mpPayment); } /** @@ -500,8 +541,12 @@ public class WeixinPayProxy { */ public MPPaymentRecord mpPaymentQuery(String outTradeNo) throws WeixinException, IOException { - return cashApi.mchPaymentQuery(new FileInputStream(DEFAULT_CA_FILE), - outTradeNo); + return cashApi + .mchPaymentQuery( + new FileInputStream(Weixin4jConfigUtil + .getClassPathValue("certificate.file", + Weixin4jConst.DEFAULT_CAFILE_PATH)), + outTradeNo); } /** diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/mch/MchPayRequest.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/mch/MchPayRequest.java index 1c64594a..02cb77aa 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/mch/MchPayRequest.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/mch/MchPayRequest.java @@ -8,8 +8,8 @@ import javax.xml.bind.annotation.XmlTransient; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.annotation.JSONField; import com.foxinmy.weixin4j.payment.PayRequest; -import com.foxinmy.weixin4j.payment.PayUtil; import com.foxinmy.weixin4j.type.SignType; +import com.foxinmy.weixin4j.util.DigestUtil; /** * JS支付:get_brand_wcpay_request
@@ -71,7 +71,7 @@ public class MchPayRequest extends PayRequest { @JSONField(serialize = false) public String asPayJsRequestJson(String paySignKey) { this.setSignType(SignType.MD5); - this.setPaySign(PayUtil.paysignMd5(this, paySignKey)); + this.setPaySign(DigestUtil.paysignMd5(this, paySignKey)); return asPayJsRequestJson(); } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/mch/NativePayResponse.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/mch/NativePayResponse.java index 806b7925..b25c115c 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/mch/NativePayResponse.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/mch/NativePayResponse.java @@ -2,19 +2,22 @@ package com.foxinmy.weixin4j.payment.mch; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; import com.alibaba.fastjson.annotation.JSONField; import com.foxinmy.weixin4j.exception.WeixinPayException; import com.foxinmy.weixin4j.model.Consts; -import com.foxinmy.weixin4j.payment.PayUtil; +import com.foxinmy.weixin4j.model.WeixinPayAccount; +import com.foxinmy.weixin4j.util.DigestUtil; import com.foxinmy.weixin4j.util.RandomUtil; +import com.foxinmy.weixin4j.xml.XmlStream; /** * Native支付时的回调响应 * - * @className NativePayResponseV3 + * @className NativePayResponse * @author jy * @date 2014年10月28日 * @since JDK 1.6 @@ -26,10 +29,7 @@ public class NativePayResponse extends ApiResult { private static final long serialVersionUID = 6119895998783333012L; - @XmlTransient - @JSONField(serialize = false) - private PrePay prePay; - + @XmlElement(name = "prepay_id") @JSONField(name = "prepay_id") private String prepayId; @@ -38,7 +38,7 @@ public class NativePayResponse extends ApiResult { } /** - * 一般作为校验失败时返回 + * 作为return_code 为 FAIL 的时候返回 * * @param returnMsg * 失败消息 @@ -56,33 +56,53 @@ public class NativePayResponse extends ApiResult { /** * 作为return_code 为 SUCCESS 的时候返回 * - * @param payPackage - * 订单信息 + * @param weixinAccount + * 商户信息 + * @param prepayId + * 调用统一下单接口生成的预支付ID * @throws WeixinPayException */ - public NativePayResponse(MchPayPackage payPackage, String paysignKey) - throws WeixinPayException { + public NativePayResponse(WeixinPayAccount weixinAccount, String prepayId) { super.setReturnCode(Consts.SUCCESS); this.setResultCode(Consts.SUCCESS); - this.setMchId(payPackage.getMchId()); - this.setAppId(payPackage.getAppId()); + this.setMchId(weixinAccount.getMchId()); + this.setAppId(weixinAccount.getId()); this.setNonceStr(RandomUtil.generateString(16)); - this.prePay = PayUtil.createPrePay(payPackage, paysignKey); - this.prepayId = prePay.getPrepayId(); + this.prepayId = prepayId; } public String getPrepayId() { return prepayId; } - public void setPrepayId(String prepayId) { - this.prepayId = prepayId; + /** + * 针对已签名的 NativePayResponse + * + * @return native回调字符串 + */ + @XmlTransient + @JSONField(serialize = false) + public String asRequestXml() { + return XmlStream.toXML(this); + } + + /** + * 针对未签名的 NativePayResponse + * + * @param paySignKey + * 支付签名密钥 + * @return native回调字符串 + */ + @XmlTransient + @JSONField(serialize = false) + public String asRequestXml(String paysignKey) { + this.setSign(DigestUtil.paysignMd5(this, paysignKey)); + return XmlStream.toXML(this); } @Override public String toString() { - return "NativePayResponseV3 [prePay=" + prePay + ", prepayId=" - + prepayId + ", " + super.toString() + "]"; + return "NativePayResponse [prepayId=" + prepayId + ", " + + super.toString() + "]"; } - } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/DigestUtil.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/DigestUtil.java index e2bb5a34..a55d7b72 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/DigestUtil.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/DigestUtil.java @@ -2,6 +2,8 @@ package com.foxinmy.weixin4j.util; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.Map; import com.foxinmy.weixin4j.model.Consts; @@ -59,4 +61,43 @@ public final class DigestUtil { byte[] data = StringUtil.getBytesUtf8(content); return HexUtil.encodeHexString(getDigest(Consts.MD5).digest(data)); } + + /** + * md5签名(一般用于V3.x支付接口) + * + * @param obj + * 签名对象 + * @param paySignKey + * 支付API的密钥 + * @return + */ + public static String paysignMd5(Object obj, String paySignKey) { + StringBuilder sb = new StringBuilder(); + // a--->string1 + sb.append(MapUtil.toJoinString(obj, false, false, null)); + // b---> + // 在 string1 最后拼接上 key=paternerKey 得到 stringSignTemp 字符串,并 对 + // stringSignTemp 进行 md5 运算 + // 再将得到的 字符串所有字符转换为大写 ,得到 sign 值 signValue。 + sb.append("&key=").append(paySignKey); + return MD5(sb.toString()).toUpperCase(); + } + + /** + * sha签名(一般用于V2.x支付接口) + * + * @param obj + * 签名对象 + * @param paySignKey + * 支付API的密钥请注意排序放进去的是put("appKey", + * paySignKey) + * @return + */ + public static String paysignSha(Object obj, String paySignKey) { + Map extra = new HashMap(); + if (StringUtil.isNotBlank(paySignKey)) { + extra.put("appKey", paySignKey); + } + return SHA1(MapUtil.toJoinString(obj, false, true, extra)); + } } diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/Pay2Api.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/Pay2Api.java index 3b9f0e76..e8120ab1 100644 --- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/Pay2Api.java +++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/Pay2Api.java @@ -34,12 +34,12 @@ import com.foxinmy.weixin4j.http.weixin.WeixinSSLRequestExecutor; import com.foxinmy.weixin4j.model.Consts; import com.foxinmy.weixin4j.model.Token; import com.foxinmy.weixin4j.model.WeixinPayAccount; +import com.foxinmy.weixin4j.mp.payment.v2.JsPayRequestV2; import com.foxinmy.weixin4j.mp.payment.v2.OrderV2; -import com.foxinmy.weixin4j.mp.payment.v2.PayUtil2; +import com.foxinmy.weixin4j.mp.payment.v2.PayPackageV2; import com.foxinmy.weixin4j.mp.payment.v2.RefundRecordV2; import com.foxinmy.weixin4j.mp.payment.v2.RefundResultV2; import com.foxinmy.weixin4j.mp.token.WeixinTokenCreator; -import com.foxinmy.weixin4j.payment.PayUtil; import com.foxinmy.weixin4j.token.TokenHolder; import com.foxinmy.weixin4j.token.TokenStorager; import com.foxinmy.weixin4j.type.BillType; @@ -49,6 +49,7 @@ import com.foxinmy.weixin4j.type.SignType; import com.foxinmy.weixin4j.util.DateUtil; import com.foxinmy.weixin4j.util.DigestUtil; import com.foxinmy.weixin4j.util.MapUtil; +import com.foxinmy.weixin4j.util.RandomUtil; import com.foxinmy.weixin4j.util.StringUtil; import com.foxinmy.weixin4j.util.Weixin4jConfigUtil; import com.foxinmy.weixin4j.util.Weixin4jConst; @@ -84,6 +85,98 @@ public class Pay2Api extends MpApi { tokenStorager); } + /** + * 生成V2.x版本JSAPI支付字符串 + * + * @param body + * 支付详情 + * @param outTradeNo + * 订单号 + * @param totalFee + * 订单总额 按实际金额传入即可(元) 构造函数会转换为分 + * @param notifyUrl + * 支付回调URL + * @param createIp + * 订单生成的机器 IP + * @return 支付json串 + */ + public String createPayJsRequestJson(String body, String outTradeNo, + double totalFee, String notifyUrl, String createIp) { + return createPayJsRequestJson(body, outTradeNo, totalFee, notifyUrl, + createIp, null, null, null, 0d, 0d, null); + } + + /** + * 生成V2.x版本JSAPI支付字符串 + * + * @param body + * 支付详情 + * @param outTradeNo + * 订单号 + * @param totalFee + * 订单总额 按实际金额传入即可(元) 构造函数会转换为分 + * @param notifyUrl + * 支付回调URL + * @param createIp + * 订单生成的机器 IP + * @param attach + * 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据 + * @param timeStart + * 订单生成时间,格式为yyyyMMddHHmmss + * @param timeExpire + * 订单失效时间,格式为yyyyMMddHHmmss;注意:最短失效时间间隔必须大于5分钟 + * @param transportFee + * 物流费用 如有值 必须保证 transportFee+productFee=totalFee + * @param transportFee + * 商品费用 如有值 必须保证 transportFee+productFee=totalFee + * @param goodsTag + * 商品标记,代金券或立减优惠功能的参数 + * @return 支付json串 + */ + public String createPayJsRequestJson(String body, String outTradeNo, + double totalFee, String notifyUrl, String createIp, String attach, + Date timeStart, Date timeExpire, double transportFee, + double productFee, String goodsTag) { + PayPackageV2 payPackage = new PayPackageV2( + weixinAccount.getPartnerId(), body, outTradeNo, totalFee, + notifyUrl, createIp); + payPackage.setAttach(attach); + payPackage.setTimeStart(timeStart); + payPackage.setTimeExpire(timeExpire); + payPackage.setTransportFee(transportFee); + payPackage.setProductFee(productFee); + payPackage.setGoodsTag(goodsTag); + JsPayRequestV2 jsPayRequest = new JsPayRequestV2(weixinAccount, + payPackage); + jsPayRequest.setPaySign(DigestUtil.paysignSha(jsPayRequest, + weixinAccount.getPaySignKey())); + jsPayRequest.setSignType(SignType.SHA1); + return JSON.toJSONString(jsPayRequest); + } + + /** + * 创建V2.x NativePay支付链接 + * + * @param productId + * 与订单ID等价 + * @return 支付链接 + */ + public String createNativePayRequestURL(String productId) { + Map map = new HashMap(); + String timestamp = DateUtil.timestamp2string(); + String noncestr = RandomUtil.generateString(16); + map.put("appid", weixinAccount.getId()); + map.put("timestamp", timestamp); + map.put("noncestr", noncestr); + map.put("productid", productId); + map.put("appkey", weixinAccount.getPaySignKey()); + String sign = DigestUtil.paysignSha(map, null); + return String + .format("􏳈􏳈􏳈􏳈􏱗􏱗􏱗􏱗􏱕􏱕􏱕􏱕􏳉􏳉􏳉􏳉􏱕􏱕􏱕􏱕􏱩􏱩􏱩􏱩􏰛􏰛􏰛􏰛􏳊􏳊􏳊􏳊􏳊􏳊􏳊􏳊􏳈􏳈􏳈􏳈􏳉􏳉􏳉􏳉􏱶􏱶􏱶􏱶􏱓􏱓􏱓􏱓􏱭􏱭􏱭􏱭􏳊􏳊􏳊􏳊􏳋􏳋􏳋􏳋􏱕􏱕􏱕􏱕􏳌􏳌􏳌􏳌􏱶􏱶􏱶􏱶􏱓􏱓􏱓􏱓􏱭􏱭􏱭􏱭􏱰􏱰􏱰􏱰􏱨􏱨􏱨􏱨􏳍􏳍􏳍􏳍􏳎􏳎􏳎􏳎􏱱􏱱􏱱􏱱􏱕􏱕􏱕􏱕􏱦􏱦􏱦􏱦􏱩􏱩􏱩􏱩􏳜􏳜􏳜􏳜􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳞􏳞􏳞􏳞􏱓􏱓􏱓􏱓􏱶􏱶􏱶􏱶􏱶􏱶􏱶􏱶􏱕􏱕􏱕􏱕􏱪􏱪􏱪􏱪􏳜􏳜􏳜􏳜􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳞􏳞􏳞􏳞􏱶􏱶􏱶􏱶􏱨􏱨􏱨􏱨􏳟􏳟􏳟􏳟􏱪􏱪􏱪􏱪􏱰􏱰􏱰􏱰􏱷􏱷􏱷􏱷􏱔􏱔􏱔􏱔􏱕􏱕􏱕􏱕􏱪􏱪􏱪􏱪􏳜􏳜􏳜􏳜􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳞􏳞􏳞􏳞􏱔􏱔􏱔􏱔􏱕􏱕􏱕􏱕􏳈􏳈􏳈􏳈􏱗􏱗􏱗􏱗􏱕􏱕􏱕􏱕􏳉􏳉􏳉􏳉􏱕􏱕􏱕􏱕􏱩􏱩􏱩􏱩􏰛􏰛􏰛􏰛􏳊􏳊􏳊􏳊􏳊􏳊􏳊􏳊􏳈􏳈􏳈􏳈􏳉􏳉􏳉􏳉􏱶􏱶􏱶􏱶􏱓􏱓􏱓􏱓􏱭􏱭􏱭􏱭􏳊􏳊􏳊􏳊􏳋􏳋􏳋􏳋􏱕􏱕􏱕􏱕􏳌􏳌􏳌􏳌􏱶􏱶􏱶􏱶􏱓􏱓􏱓􏱓􏱭􏱭􏱭􏱭􏱰􏱰􏱰􏱰􏱨􏱨􏱨􏱨􏳍􏳍􏳍􏳍􏳎􏳎􏳎􏳎􏱱􏱱􏱱􏱱􏱕􏱕􏱕􏱕􏱦􏱦􏱦􏱦􏱩􏱩􏱩􏱩􏳜􏳜􏳜􏳜􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳞􏳞􏳞􏳞􏱓􏱓􏱓􏱓􏱶􏱶􏱶􏱶􏱶􏱶􏱶􏱶􏱕􏱕􏱕􏱕􏱪􏱪􏱪􏱪􏳜􏳜􏳜􏳜􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳞􏳞􏳞􏳞􏱶􏱶􏱶􏱶􏱨􏱨􏱨􏱨􏳟􏳟􏳟􏳟􏱪􏱪􏱪􏱪􏱰􏱰􏱰􏱰􏱷􏱷􏱷􏱷􏱔􏱔􏱔􏱔􏱕􏱕􏱕􏱕􏱪􏱪􏱪􏱪􏳜􏳜􏳜􏳜􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳞􏳞􏳞􏳞􏱔􏱔􏱔􏱔􏱕􏱕􏱕􏱕􏳠􏳠􏳠􏳠􏱗􏱗􏱗􏱗􏱱􏱱􏱱􏱱􏱔􏱔􏱔􏱔􏱓􏱓􏱓􏱓􏳠􏳠􏳠􏳠􏱶􏱶􏱶􏱶􏳜􏳜􏳜􏳜􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳞􏳞􏳞􏳞􏱩􏱩􏱩􏱩􏳟􏳟􏳟􏳟􏱩􏱩􏱩􏱩􏱷􏱷􏱷􏱷􏱗􏱗􏱗􏱗􏱱􏱱􏱱􏱱􏱔􏱔􏱔􏱔􏱨􏱨􏱨􏱨􏳜􏳜􏳜􏳜􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝weixin://wxpay/bizpayurl?sign=%s&appid=%s&productid=%s×tamp=%s&nocestr=%s􏳠􏳠􏳠􏳠􏱗􏱗􏱗􏱗􏱱􏱱􏱱􏱱􏱔􏱔􏱔􏱔􏱓􏱓􏱓􏱓􏳠􏳠􏳠􏳠􏱶􏱶􏱶􏱶􏳜􏳜􏳜􏳜􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳞􏳞􏳞􏳞􏱩􏱩􏱩􏱩􏳟􏳟􏳟􏳟􏱩􏱩􏱩􏱩􏱷􏱷􏱷􏱷􏱗􏱗􏱗􏱗􏱱􏱱􏱱􏱱􏱔􏱔􏱔􏱔􏱨􏱨􏱨􏱨􏳜􏳜􏳜􏳜􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝", + sign, weixinAccount.getId(), productId, timestamp, + noncestr); + } + /** * 订单查询 * @@ -113,7 +206,7 @@ public class Pay2Api extends MpApi { obj.put("appkey", weixinAccount.getPaySignKey()); obj.put("package", sb.toString()); obj.put("timestamp", timestamp); - String signature = PayUtil2.paysignSha(obj); + String signature = DigestUtil.paysignSha(obj, null); obj.clear(); obj.put("appid", weixinAccount.getId()); @@ -190,8 +283,8 @@ public class Pay2Api extends MpApi { if (mopara != null && !mopara.isEmpty()) { map.putAll(mopara); } - String sign = PayUtil - .paysignMd5(map, weixinAccount.getPartnerKey()); + String sign = DigestUtil.paysignMd5(map, + weixinAccount.getPartnerKey()); map.put("sign", sign.toUpperCase()); SSLContext ctx = null; @@ -426,7 +519,7 @@ public class Pay2Api extends MpApi { map.put("input_charset", Consts.UTF_8.name()); map.put("partner", weixinAccount.getPartnerId()); map.put(idQuery.getType().getName(), idQuery.getId()); - String sign = PayUtil.paysignMd5(map, weixinAccount.getPartnerKey()); + String sign = DigestUtil.paysignMd5(map, weixinAccount.getPartnerKey()); map.put("sign", sign.toLowerCase()); WeixinResponse response = weixinExecutor.get(refundquery_uri, map); return ListsuffixResultDeserializer.deserialize(response.getAsString(), @@ -464,7 +557,7 @@ public class Pay2Api extends MpApi { map.put("deliver_timestamp", DateUtil.timestamp2string()); map.put("deliver_status", status ? "1" : "0"); map.put("deliver_msg", statusMsg); - map.put("app_signature", PayUtil2.paysignSha(map)); + map.put("app_signature", DigestUtil.paysignSha(map, null)); map.put("sign_method", SignType.SHA1.name().toLowerCase()); WeixinResponse response = weixinExecutor.post( diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/NativePayResponseV2.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/NativePayResponseV2.java index bdf8dda1..e943597b 100644 --- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/NativePayResponseV2.java +++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/NativePayResponseV2.java @@ -1,12 +1,20 @@ package com.foxinmy.weixin4j.mp.payment.v2; +import java.util.HashMap; +import java.util.Map; + import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; import com.alibaba.fastjson.annotation.JSONField; import com.foxinmy.weixin4j.model.WeixinPayAccount; +import com.foxinmy.weixin4j.util.DateUtil; +import com.foxinmy.weixin4j.util.DigestUtil; +import com.foxinmy.weixin4j.util.RandomUtil; +import com.foxinmy.weixin4j.xml.XmlStream; /** * V2 Native支付时的回调响应 @@ -35,6 +43,10 @@ public class NativePayResponseV2 extends JsPayRequestV2 { @XmlElement(name = "RetErrMsg") private String retMsg; + @XmlTransient + @JSONField(serialize = false) + private WeixinPayAccount weixinAccount; + protected NativePayResponseV2() { // jaxb required } @@ -44,6 +56,7 @@ public class NativePayResponseV2 extends JsPayRequestV2 { super(weixinAccount, payPackage); this.retCode = "0"; this.retMsg = "OK"; + this.weixinAccount = weixinAccount; } public String getRetCode() { @@ -62,6 +75,26 @@ public class NativePayResponseV2 extends JsPayRequestV2 { this.retMsg = retMsg; } + /** + * 生成 回调字符串 + * + * @return native回调字符串 + */ + @XmlTransient + @JSONField(serialize = false) + public String asReqeustXml() { + Map map = new HashMap(); + map.put("appid", weixinAccount.getId()); + map.put("appkey", weixinAccount.getPaySignKey()); + map.put("timestamp", DateUtil.timestamp2string()); + map.put("noncestr", RandomUtil.generateString(16)); + map.put("package", getPackageInfo()); + map.put("retcode", getRetCode()); + map.put("reterrmsg", getRetMsg()); + this.setPaySign(DigestUtil.paysignSha(map, null)); + return XmlStream.toXML(this); + } + @Override public String toString() { return "NativePayResponseV2 [retCode=" + retCode + ", retMsg=" + retMsg diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/PayUtil2.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/PayUtil2.java deleted file mode 100644 index 2af6ee1f..00000000 --- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/payment/v2/PayUtil2.java +++ /dev/null @@ -1,203 +0,0 @@ -package com.foxinmy.weixin4j.mp.payment.v2; - -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - -import com.alibaba.fastjson.JSON; -import com.foxinmy.weixin4j.exception.WeixinPayException; -import com.foxinmy.weixin4j.model.WeixinPayAccount; -import com.foxinmy.weixin4j.type.SignType; -import com.foxinmy.weixin4j.util.DateUtil; -import com.foxinmy.weixin4j.util.DigestUtil; -import com.foxinmy.weixin4j.util.MapUtil; -import com.foxinmy.weixin4j.util.RandomUtil; -import com.foxinmy.weixin4j.util.Weixin4jConfigUtil; -import com.foxinmy.weixin4j.xml.XmlStream; - -/** - * V2支付工具类(JSAPI,NATIVE) - * - * @className PayUtil2 - * @author jy - * @date 2014年10月28日 - * @since JDK 1.6 - * @see - */ -public class PayUtil2 { - - /** - * 生成V2.x版本JSAPI支付字符串 - * - * @param body - * 支付详情 - * @param outTradeNo - * 订单号 - * @param totalFee - * 订单总额 按实际金额传入即可(元) 构造函数会转换为分 - * @param notifyUrl - * 支付回调URL - * @param createIp - * 订单生成的机器 IP - * @param weixinAccount - * 商户信息 - * @return 支付json串 - */ - public static String createPayJsRequestJsonV2(String body, - String outTradeNo, double totalFee, String notifyUrl, - String createIp, WeixinPayAccount weixinAccount) { - return createPayJsRequestJsonV2(weixinAccount, body, outTradeNo, - totalFee, notifyUrl, createIp, null, null, null, 0d, 0d, null); - } - - /** - * 生成V2.x版本JSAPI支付字符串 - * - * @param weixinAccount - * 商户信息 - * @param body - * 支付详情 - * @param outTradeNo - * 订单号 - * @param totalFee - * 订单总额 按实际金额传入即可(元) 构造函数会转换为分 - * @param notifyUrl - * 支付回调URL - * @param createIp - * 订单生成的机器 IP - * @param attach - * 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据 - * @param timeStart - * 订单生成时间,格式为yyyyMMddHHmmss - * @param timeExpire - * 订单失效时间,格式为yyyyMMddHHmmss;注意:最短失效时间间隔必须大于5分钟 - * @param transportFee - * 物流费用 如有值 必须保证 transportFee+productFee=totalFee - * @param transportFee - * 商品费用 如有值 必须保证 transportFee+productFee=totalFee - * @param goodsTag - * 商品标记,代金券或立减优惠功能的参数 - * @return 支付json串 - */ - public static String createPayJsRequestJsonV2( - WeixinPayAccount weixinAccount, String body, String outTradeNo, - double totalFee, String notifyUrl, String createIp, String attach, - Date timeStart, Date timeExpire, double transportFee, - double productFee, String goodsTag) { - PayPackageV2 payPackage = new PayPackageV2( - weixinAccount.getPartnerId(), body, outTradeNo, totalFee, - notifyUrl, createIp); - payPackage.setAttach(attach); - payPackage.setTimeStart(timeStart); - payPackage.setTimeExpire(timeExpire); - payPackage.setTransportFee(transportFee); - payPackage.setProductFee(productFee); - payPackage.setGoodsTag(goodsTag); - JsPayRequestV2 jsPayRequest = new JsPayRequestV2(weixinAccount, - payPackage); - jsPayRequest.setPaySign(paysignSha(jsPayRequest, - weixinAccount.getPaySignKey())); - jsPayRequest.setSignType(SignType.SHA1); - return JSON.toJSONString(jsPayRequest); - } - - /** - * sha签名(一般用于V2.x支付接口) - * - * @param obj - * 签名对象 - * @return - */ - public static String paysignSha(Object obj) { - return DigestUtil.SHA1(MapUtil.toJoinString(obj, false, true, null)); - } - - /** - * sha签名(一般用于V2.x支付接口) - * - * @param obj - * 签名对象 - * @param paySignKey - * 支付API的密钥请注意排序放进去的是put("appKey", - * paySignKey) - * @return - */ - public static String paysignSha(Object obj, String paySignKey) { - Map extra = new HashMap(); - extra.put("appKey", paySignKey); - return DigestUtil.SHA1(MapUtil.toJoinString(obj, false, true, extra)); - } - - /** - * 创建V2.x NativePay支付链接 - * - * @param weixinAccount - * 商户信息 - * @param productId - * 与订单ID等价 - * @return 支付链接 - */ - public static String createNativePayRequestURLV2( - WeixinPayAccount weixinAccount, String productId) { - Map map = new HashMap(); - String timestamp = DateUtil.timestamp2string(); - String noncestr = RandomUtil.generateString(16); - map.put("appid", weixinAccount.getId()); - map.put("timestamp", timestamp); - map.put("noncestr", noncestr); - map.put("productid", productId); - map.put("appkey", weixinAccount.getPaySignKey()); - String sign = paysignSha(map); - return String - .format("􏳈􏳈􏳈􏳈􏱗􏱗􏱗􏱗􏱕􏱕􏱕􏱕􏳉􏳉􏳉􏳉􏱕􏱕􏱕􏱕􏱩􏱩􏱩􏱩􏰛􏰛􏰛􏰛􏳊􏳊􏳊􏳊􏳊􏳊􏳊􏳊􏳈􏳈􏳈􏳈􏳉􏳉􏳉􏳉􏱶􏱶􏱶􏱶􏱓􏱓􏱓􏱓􏱭􏱭􏱭􏱭􏳊􏳊􏳊􏳊􏳋􏳋􏳋􏳋􏱕􏱕􏱕􏱕􏳌􏳌􏳌􏳌􏱶􏱶􏱶􏱶􏱓􏱓􏱓􏱓􏱭􏱭􏱭􏱭􏱰􏱰􏱰􏱰􏱨􏱨􏱨􏱨􏳍􏳍􏳍􏳍􏳎􏳎􏳎􏳎􏱱􏱱􏱱􏱱􏱕􏱕􏱕􏱕􏱦􏱦􏱦􏱦􏱩􏱩􏱩􏱩􏳜􏳜􏳜􏳜􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳞􏳞􏳞􏳞􏱓􏱓􏱓􏱓􏱶􏱶􏱶􏱶􏱶􏱶􏱶􏱶􏱕􏱕􏱕􏱕􏱪􏱪􏱪􏱪􏳜􏳜􏳜􏳜􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳞􏳞􏳞􏳞􏱶􏱶􏱶􏱶􏱨􏱨􏱨􏱨􏳟􏳟􏳟􏳟􏱪􏱪􏱪􏱪􏱰􏱰􏱰􏱰􏱷􏱷􏱷􏱷􏱔􏱔􏱔􏱔􏱕􏱕􏱕􏱕􏱪􏱪􏱪􏱪􏳜􏳜􏳜􏳜􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳞􏳞􏳞􏳞􏱔􏱔􏱔􏱔􏱕􏱕􏱕􏱕􏳈􏳈􏳈􏳈􏱗􏱗􏱗􏱗􏱕􏱕􏱕􏱕􏳉􏳉􏳉􏳉􏱕􏱕􏱕􏱕􏱩􏱩􏱩􏱩􏰛􏰛􏰛􏰛􏳊􏳊􏳊􏳊􏳊􏳊􏳊􏳊􏳈􏳈􏳈􏳈􏳉􏳉􏳉􏳉􏱶􏱶􏱶􏱶􏱓􏱓􏱓􏱓􏱭􏱭􏱭􏱭􏳊􏳊􏳊􏳊􏳋􏳋􏳋􏳋􏱕􏱕􏱕􏱕􏳌􏳌􏳌􏳌􏱶􏱶􏱶􏱶􏱓􏱓􏱓􏱓􏱭􏱭􏱭􏱭􏱰􏱰􏱰􏱰􏱨􏱨􏱨􏱨􏳍􏳍􏳍􏳍􏳎􏳎􏳎􏳎􏱱􏱱􏱱􏱱􏱕􏱕􏱕􏱕􏱦􏱦􏱦􏱦􏱩􏱩􏱩􏱩􏳜􏳜􏳜􏳜􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳞􏳞􏳞􏳞􏱓􏱓􏱓􏱓􏱶􏱶􏱶􏱶􏱶􏱶􏱶􏱶􏱕􏱕􏱕􏱕􏱪􏱪􏱪􏱪􏳜􏳜􏳜􏳜􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳞􏳞􏳞􏳞􏱶􏱶􏱶􏱶􏱨􏱨􏱨􏱨􏳟􏳟􏳟􏳟􏱪􏱪􏱪􏱪􏱰􏱰􏱰􏱰􏱷􏱷􏱷􏱷􏱔􏱔􏱔􏱔􏱕􏱕􏱕􏱕􏱪􏱪􏱪􏱪􏳜􏳜􏳜􏳜􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳞􏳞􏳞􏳞􏱔􏱔􏱔􏱔􏱕􏱕􏱕􏱕􏳠􏳠􏳠􏳠􏱗􏱗􏱗􏱗􏱱􏱱􏱱􏱱􏱔􏱔􏱔􏱔􏱓􏱓􏱓􏱓􏳠􏳠􏳠􏳠􏱶􏱶􏱶􏱶􏳜􏳜􏳜􏳜􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳞􏳞􏳞􏳞􏱩􏱩􏱩􏱩􏳟􏳟􏳟􏳟􏱩􏱩􏱩􏱩􏱷􏱷􏱷􏱷􏱗􏱗􏱗􏱗􏱱􏱱􏱱􏱱􏱔􏱔􏱔􏱔􏱨􏱨􏱨􏱨􏳜􏳜􏳜􏳜􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝weixin://wxpay/bizpayurl?sign=%s&appid=%s&productid=%s×tamp=%s&nocestr=%s􏳠􏳠􏳠􏳠􏱗􏱗􏱗􏱗􏱱􏱱􏱱􏱱􏱔􏱔􏱔􏱔􏱓􏱓􏱓􏱓􏳠􏳠􏳠􏳠􏱶􏱶􏱶􏱶􏳜􏳜􏳜􏳜􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳞􏳞􏳞􏳞􏱩􏱩􏱩􏱩􏳟􏳟􏳟􏳟􏱩􏱩􏱩􏱩􏱷􏱷􏱷􏱷􏱗􏱗􏱗􏱗􏱱􏱱􏱱􏱱􏱔􏱔􏱔􏱔􏱨􏱨􏱨􏱨􏳜􏳜􏳜􏳜􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝􏳝", - sign, weixinAccount.getId(), productId, timestamp, - noncestr); - } - - /** - * 创建V2.x NATIVE回调时的响应字符串 - * - * @param weixinAccount - * 商户信息 - * @param payPackage - * 订单信息 - * @return - */ - public static String createNativePayResponseV2( - WeixinPayAccount weixinAccount, PayPackageV2 payPackage) { - NativePayResponseV2 payRequest = new NativePayResponseV2(weixinAccount, - payPackage); - Map map = new HashMap(); - String timestamp = DateUtil.timestamp2string(); - String noncestr = RandomUtil.generateString(16); - map.put("appid", weixinAccount.getId()); - map.put("appkey", weixinAccount.getPaySignKey()); - map.put("timestamp", timestamp); - map.put("noncestr", noncestr); - map.put("package", payRequest.getPackageInfo()); - map.put("retcode", payRequest.getRetCode()); - map.put("reterrmsg", payRequest.getRetMsg()); - payRequest.setPaySign(paysignSha(map)); - return XmlStream.toXML(payRequest); - } - - private static String JSAPIV2() { - WeixinPayAccount weixinAccount = JSON.parseObject( - Weixin4jConfigUtil.getValue("account"), WeixinPayAccount.class); - return createPayJsRequestJsonV2("支付测试", "JSAPI01", 0.01d, "127.0.0.0", - "http://127.0.0.1/jsapi/notify", weixinAccount); - } - - private static String NATIVEV2() { - WeixinPayAccount weixinAccount = JSON.parseObject( - Weixin4jConfigUtil.getValue("account"), WeixinPayAccount.class); - return createNativePayRequestURLV2(weixinAccount, "P1"); - } - - public static void main(String[] args) throws WeixinPayException { - // V2版本下的JS支付 - System.out.println(JSAPIV2()); - // V2版本下的原生支付 - System.out.println(NATIVEV2()); - } -} diff --git a/weixin4j-mp/src/test/java/com/foxinmy/weixin4j/mp/test/PayTest.java b/weixin4j-mp/src/test/java/com/foxinmy/weixin4j/mp/test/PayTest.java index 02242a9c..ac04bac9 100644 --- a/weixin4j-mp/src/test/java/com/foxinmy/weixin4j/mp/test/PayTest.java +++ b/weixin4j-mp/src/test/java/com/foxinmy/weixin4j/mp/test/PayTest.java @@ -9,12 +9,11 @@ import java.util.Date; import org.junit.Assert; import org.junit.Test; -import com.foxinmy.weixin4j.exception.WeixinPayException; import com.foxinmy.weixin4j.exception.WeixinException; +import com.foxinmy.weixin4j.exception.WeixinPayException; import com.foxinmy.weixin4j.http.weixin.XmlResult; import com.foxinmy.weixin4j.model.WeixinPayAccount; import com.foxinmy.weixin4j.mp.api.Pay2Api; -import com.foxinmy.weixin4j.payment.PayUtil; import com.foxinmy.weixin4j.payment.WeixinPayProxy; import com.foxinmy.weixin4j.payment.mch.ApiResult; import com.foxinmy.weixin4j.payment.mch.MchPayPackage; @@ -24,6 +23,7 @@ import com.foxinmy.weixin4j.token.FileTokenStorager; import com.foxinmy.weixin4j.type.IdQuery; import com.foxinmy.weixin4j.type.IdType; import com.foxinmy.weixin4j.type.TradeType; +import com.foxinmy.weixin4j.util.DigestUtil; import com.foxinmy.weixin4j.util.Weixin4jConfigUtil; public class PayTest { @@ -84,7 +84,7 @@ public class PayTest { System.err.println(order); String sign = order.getSign(); order.setSign(null); - String valiSign = PayUtil.paysignMd5(order, ACCOUNT3.getPaySignKey()); + String valiSign = DigestUtil.paysignMd5(order, ACCOUNT3.getPaySignKey()); System.err .println(String.format("sign=%s,valiSign=%s", sign, valiSign)); Assert.assertEquals(valiSign, sign); @@ -98,7 +98,7 @@ public class PayTest { // 这里的验证签名需要把details循环拼接 String sign = record.getSign(); record.setSign(null); - String valiSign = PayUtil.paysignMd5(record, ACCOUNT3.getPaySignKey()); + String valiSign = DigestUtil.paysignMd5(record, ACCOUNT3.getPaySignKey()); System.err .println(String.format("sign=%s,valiSign=%s", sign, valiSign)); Assert.assertEquals(valiSign, sign); @@ -127,7 +127,7 @@ public class PayTest { System.err.println(result); String sign = result.getSign(); result.setSign(null); - String valiSign = PayUtil.paysignMd5(result, ACCOUNT3.getPaySignKey()); + String valiSign = DigestUtil.paysignMd5(result, ACCOUNT3.getPaySignKey()); System.err .println(String.format("sign=%s,valiSign=%s", sign, valiSign)); Assert.assertEquals(valiSign, sign); @@ -141,8 +141,7 @@ public class PayTest { payPackageV3.setProductId("0001"); PrePay prePay = null; try { - prePay = PayUtil.createPrePay(payPackageV3, - ACCOUNT3.getPaySignKey()); + prePay = PAY3.createPrePay(payPackageV3); } catch (WeixinPayException e) { e.printStackTrace(); } @@ -155,7 +154,7 @@ public class PayTest { System.err.println(result); String sign = result.getSign(); result.setSign(null); - String valiSign = PayUtil.paysignMd5(result, ACCOUNT3.getPaySignKey()); + String valiSign = DigestUtil.paysignMd5(result, ACCOUNT3.getPaySignKey()); System.err .println(String.format("sign=%s,valiSign=%s", sign, valiSign)); Assert.assertEquals(valiSign, sign);