diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayPartnerOrderCloseV3Request.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayPartnerOrderCloseV3Request.java new file mode 100644 index 0000000000..7a62629fb4 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayPartnerOrderCloseV3Request.java @@ -0,0 +1,61 @@ +package com.github.binarywang.wxpay.bean.request; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * 服务商关闭订单请求对象类 + * 文档见:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_1_3.shtml + * + * @author Guo Shuai + * @version 1.0 + * @date 2023/3/2 + */ +@Data +@NoArgsConstructor +@Accessors(chain = true) +public class WxPayPartnerOrderCloseV3Request implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+ * 字段名:服务商商户号 + * 变量名:sp_mchid + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 服务商商户号,由微信支付生成并下发。 + * 示例值:1230000109 + *+ */ + @SerializedName(value = "sp_mchid") + private String spMchId; + /** + *
+ * 字段名:特约商户商户号 + * 变量名:sp_mchid + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 特约商户商户号,由微信支付生成并下发。 + * 示例值:1230000109 + *+ */ + @SerializedName(value = "sub_mchid") + private String subMchId; + /** + *
+ * 字段名:商户订单号 + * 变量名:out_trade_no + * 是否必填:是 + * 类型:string[6,32] + * 描述: + * 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一 + * 示例值:1217752501201407033233368018 + *+ */ + private transient String outTradeNo; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayPartnerRefundV3Request.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayPartnerRefundV3Request.java index 89d0e11f58..299cf2a94b 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayPartnerRefundV3Request.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayPartnerRefundV3Request.java @@ -21,19 +21,6 @@ @Accessors(chain = true) public class WxPayPartnerRefundV3Request extends WxPayRefundV3Request implements Serializable { private static final long serialVersionUID = -1L; - /** - *
- * 字段名:子商户的商户号 - * 变量名:sub_mchid - * 是否必填:是 - * 类型:string[1, 32] - * 描述: - * 子商户商户号,由微信支付生成并下发。 - * 示例值:1230000109 - *- */ - @SerializedName(value = "sub_mchid") - private String subMchId; /** *
* 字段名:退款资金来源 diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundV3Request.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundV3Request.java index 31a41d9222..e9f1f3b140 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundV3Request.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundV3Request.java @@ -238,6 +238,17 @@ public static class GoodsDetail implements Serializable { private Integer refundQuantity; } + /** + *+ * 字段名:子商户的商户号 + * 变量名:sub_mchid + * 是否必填:是 + * 类型:string[1, 32] + * 描述: + * 子商户商户号,由微信支付生成并下发。 + * 示例值:1230000109 + *+ */ @SerializedName(value = "sub_mchid") private String subMchid; } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderV3Result.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderV3Result.java index 309fb8e752..f37d268739 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderV3Result.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderV3Result.java @@ -1,5 +1,6 @@ package com.github.binarywang.wxpay.bean.result; +import com.github.binarywang.wxpay.bean.result.enums.PartnerTradeTypeEnum; import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum; import com.github.binarywang.wxpay.v3.util.SignUtils; import com.google.gson.annotations.SerializedName; @@ -132,4 +133,32 @@ publicT getPayInfo(TradeTypeEnum tradeType, String appId, String mchId, Pri throw new WxRuntimeException("不支持的支付类型"); } } + + public T getPartnerPayInfo(PartnerTradeTypeEnum tradeType, String appId, String mchId, PrivateKey privateKey) { + String timestamp = String.valueOf(System.currentTimeMillis() / 1000); + String nonceStr = SignUtils.genRandomStr(); + switch (tradeType) { + case JSAPI: + JsapiResult jsapiResult = new JsapiResult(); + jsapiResult.setAppId(appId).setTimeStamp(timestamp) + .setPackageValue("prepay_id=" + this.prepayId).setNonceStr(nonceStr) + //签名类型,默认为RSA,仅支持RSA。 + .setSignType("RSA").setPaySign(SignUtils.sign(jsapiResult.getSignStr(), privateKey)); + return (T) jsapiResult; + case H5: + return (T) this.h5Url; + case APP: + AppResult appResult = new AppResult(); + appResult.setAppid(appId).setPrepayId(this.prepayId).setPartnerId(mchId) + .setNoncestr(nonceStr).setTimestamp(timestamp) + //暂填写固定值Sign=WXPay + .setPackageValue("Sign=WXPay") + .setSign(SignUtils.sign(appResult.getSignStr(), privateKey)); + return (T) appResult; + case NATIVE: + return (T) this.codeUrl; + default: + throw new WxRuntimeException("不支持的支付类型"); + } + } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/enums/PartnerTradeTypeEnum.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/enums/PartnerTradeTypeEnum.java new file mode 100644 index 0000000000..7704bf7d25 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/enums/PartnerTradeTypeEnum.java @@ -0,0 +1,40 @@ +package com.github.binarywang.wxpay.bean.result.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 支付方式 + * + * @author thinsstar + */ +@Getter +@AllArgsConstructor +public enum PartnerTradeTypeEnum { + /** + * APP + */ + APP("/v3/pay/partner/transactions/app", "/v3/combine-transactions/app"), + /** + * JSAPI 或 小程序 + */ + JSAPI("/v3/pay/partner/transactions/jsapi", "/v3/combine-transactions/jsapi"), + /** + * NATIVE + */ + NATIVE("/v3/pay/partner/transactions/native", "/v3/combine-transactions/native"), + /** + * H5 + */ + H5("/v3/pay/partner/transactions/h5", "/v3/combine-transactions/h5"); + + /** + * 单独下单url + */ + private final String partnerUrl; + + /** + * 合并下单url + */ + private final String combineUrl; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java index fd889cf601..0ac0c43cc9 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java @@ -5,6 +5,7 @@ import com.github.binarywang.wxpay.bean.notify.*; import com.github.binarywang.wxpay.bean.request.*; import com.github.binarywang.wxpay.bean.result.*; +import com.github.binarywang.wxpay.bean.result.enums.PartnerTradeTypeEnum; import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum; import com.github.binarywang.wxpay.config.WxPayConfig; import com.github.binarywang.wxpay.constant.WxPayConstants; @@ -477,6 +478,23 @@ public interface WxPayService { */ void closeOrderV3(String outTradeNo) throws WxPayException; + /** + * + * 服务商关闭订单 + * 应用场景 + * 以下情况需要调用关单接口: + * 1、商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付; + * 2、系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口。 + * 注意:关单没有时间限制,建议在订单生成后间隔几分钟(最短5分钟)再调用关单接口,避免出现订单状态同步不及时导致关单失败。 + * 接口地址:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_3.shtml + *+ * + * @param outTradeNo 商户系统内部的订单号 + * @return the wx pay order close result + * @throws WxPayException the wx pay exception + */ + void closePartnerOrderV3(String outTradeNo) throws WxPayException; + /** ** 关闭订单 @@ -494,6 +512,23 @@ public interface WxPayService { */ void closeOrderV3(WxPayOrderCloseV3Request request) throws WxPayException; + /** + *+ * 服务商关闭订单 + * 应用场景 + * 以下情况需要调用关单接口: + * 1、商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付; + * 2、系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口。 + * 注意:关单没有时间限制,建议在订单生成后间隔几分钟(最短5分钟)再调用关单接口,避免出现订单状态同步不及时导致关单失败。 + * 接口地址:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_3.shtml + *+ * + * @param request 关闭订单请求对象 + * @return the wx pay order close result + * @throws WxPayException the wx pay exception + */ + void closePartnerOrderV3(WxPayPartnerOrderCloseV3Request request) throws WxPayException; + /** ** 合单关闭订单API @@ -559,7 +594,7 @@ public interface WxPayService { * @return 返回 {@link com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result}里的内部类或字段 * @throws WxPayException the wx pay exception */ -T createPartnerOrderV3(TradeTypeEnum tradeType, WxPayPartnerUnifiedOrderV3Request request) throws WxPayException; + T createPartnerOrderV3(PartnerTradeTypeEnum tradeType, WxPayPartnerUnifiedOrderV3Request request) throws WxPayException; /** * 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识" @@ -569,7 +604,7 @@ public interface WxPayService { * @return the wx pay unified order result * @throws WxPayException the wx pay exception */ - WxPayUnifiedOrderV3Result unifiedPartnerOrderV3(TradeTypeEnum tradeType, WxPayPartnerUnifiedOrderV3Request request) throws WxPayException; + WxPayUnifiedOrderV3Result unifiedPartnerOrderV3(PartnerTradeTypeEnum tradeType, WxPayPartnerUnifiedOrderV3Request request) throws WxPayException; /** * 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识" diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java index a749f51fd9..4cc2fc3c1c 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java @@ -10,6 +10,7 @@ import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult; import com.github.binarywang.wxpay.bean.request.*; import com.github.binarywang.wxpay.bean.result.*; +import com.github.binarywang.wxpay.bean.result.enums.PartnerTradeTypeEnum; import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum; import com.github.binarywang.wxpay.config.WxPayConfig; import com.github.binarywang.wxpay.config.WxPayConfigHolder; @@ -538,6 +539,16 @@ public void closeOrderV3(String outTradeNo) throws WxPayException { this.closeOrderV3(request); } + @Override + public void closePartnerOrderV3(String outTradeNo) throws WxPayException { + if (StringUtils.isBlank(outTradeNo)) { + throw new WxPayException("out_trade_no不能为空"); + } + WxPayPartnerOrderCloseV3Request request = new WxPayPartnerOrderCloseV3Request(); + request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); + this.closePartnerOrderV3(request); + } + @Override public void closeOrderV3(WxPayOrderCloseV3Request request) throws WxPayException { if (StringUtils.isBlank(request.getMchid())) { @@ -547,6 +558,18 @@ public void closeOrderV3(WxPayOrderCloseV3Request request) throws WxPayException this.postV3(url, GSON.toJson(request)); } + @Override + public void closePartnerOrderV3(WxPayPartnerOrderCloseV3Request request) throws WxPayException { + if (StringUtils.isBlank(request.getSpMchId())) { + request.setSpMchId(this.getConfig().getMchId()); + } + if (StringUtils.isBlank(request.getSubMchId())) { + request.setSubMchId(this.getConfig().getSubMchId()); + } + String url = String.format("%s/v3/pay/partner/transactions/out-trade-no/%s/close", this.getPayBaseUrl(), request.getOutTradeNo()); + this.postV3(url, GSON.toJson(request)); + } + @Override public void closeCombine(CombineCloseRequest request) throws WxPayException { String url = String.format("%s/v3/combine-transactions/out-trade-no/%s/close", this.getPayBaseUrl(), request.getCombineOutTradeNo()); @@ -664,34 +687,19 @@ public T createOrderV3(TradeTypeEnum tradeType, WxPayUnifiedOrderV3Request r } @Override - public T createPartnerOrderV3(TradeTypeEnum tradeType, WxPayPartnerUnifiedOrderV3Request request) throws WxPayException { + public T createPartnerOrderV3(PartnerTradeTypeEnum tradeType, WxPayPartnerUnifiedOrderV3Request request) throws WxPayException { WxPayUnifiedOrderV3Result result = this.unifiedPartnerOrderV3(tradeType, request); //获取应用ID String appId = StringUtils.isBlank(request.getSubAppid()) ? request.getSpAppid() : request.getSubAppid(); - return result.getPayInfo(tradeType, appId, request.getSubMchId(), this.getConfig().getPrivateKey()); + return result.getPartnerPayInfo(tradeType, appId, request.getSubMchId(), this.getConfig().getPrivateKey()); } @Override - public WxPayUnifiedOrderV3Result unifiedPartnerOrderV3(TradeTypeEnum tradeType, WxPayPartnerUnifiedOrderV3Request request) throws WxPayException { - if (StringUtils.isBlank(request.getSpAppid())) { - request.setSpAppid(this.getConfig().getAppId()); - } - if (StringUtils.isBlank(request.getSpMchId())) { - request.setSpMchId(this.getConfig().getMchId()); - } - if (StringUtils.isBlank(request.getNotifyUrl())) { - request.setNotifyUrl(this.getConfig().getNotifyUrl()); - } - if (StringUtils.isBlank(request.getSubAppid())) { - request.setSubAppid(this.getConfig().getSubAppId()); - } - if (StringUtils.isBlank(request.getSubMchId())) { - request.setSubMchId(this.getConfig().getSubMchId()); - } - - String url = this.getPayBaseUrl() + tradeType.getPartnerUrl(); - String response = this.postV3(url, GSON.toJson(request)); - return GSON.fromJson(response, WxPayUnifiedOrderV3Result.class); + public WxPayUnifiedOrderV3Result unifiedPartnerOrderV3(PartnerTradeTypeEnum tradeType, WxPayPartnerUnifiedOrderV3Request request) throws WxPayException { + WxPayUnifiedOrderV3Result result = this.unifiedPartnerOrderV3(tradeType, request); + //获取应用ID + String appId = StringUtils.isBlank(request.getSubAppid()) ? request.getSpAppid() : request.getSubAppid(); + return result.getPartnerPayInfo(tradeType, appId, request.getSubMchId(), this.getConfig().getPrivateKey()); } @Override