From e0d87ebe49e34a36e0d95a804452d4a223586cd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E6=B5=A9?= Date: Wed, 26 Aug 2020 12:17:30 +0800 Subject: [PATCH] =?UTF-8?q?new:=E5=90=88=E5=8D=95=E6=94=AF=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CombineTransactionsJsResult.java | 30 ---- ...t.java => CombineTransactionsRequest.java} | 146 ++++++++++++------ .../ecommerce/CombineTransactionsResult.java | 119 ++++++++++++++ .../bean/ecommerce/enums/TradeTypeEnum.java | 27 ++++ .../wxpay/service/EcommerceService.java | 4 +- .../service/impl/EcommerceServiceImpl.java | 10 +- .../binarywang/wxpay/v3/util/SignUtils.java | 47 ++++++ 7 files changed, 300 insertions(+), 83 deletions(-) delete mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsResult.java rename weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/{CombineTransactionsJsRequest.java => CombineTransactionsRequest.java} (81%) create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsResult.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/enums/TradeTypeEnum.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/SignUtils.java diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsResult.java deleted file mode 100644 index 7a33ec840..000000000 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsResult.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.github.binarywang.wxpay.bean.ecommerce; - -import com.google.gson.annotations.SerializedName; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -/** - * 合单支付 JSAPI支付结果响应 - */ -@Data -@NoArgsConstructor -public class CombineTransactionsJsResult implements Serializable { - - /** - *
-   * 字段名:预支付交易会话标识
-   * 变量名:prepay_id
-   * 是否必填:是
-   * 类型:string(64)
-   * 描述:
-   *  数字和字母。微信生成的预支付会话标识,用于后续接口调用使用。
-   *  示例值:wx201410272009395522657a690389285100
-   * 
- */ - @SerializedName("prepay_id") - private String prepayId; - -} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsRequest.java similarity index 81% rename from weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsRequest.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsRequest.java index fa39f8297..3b138b488 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsRequest.java @@ -8,11 +8,11 @@ import java.io.Serializable; import java.util.List; /** - * 合单支付 JSAPI支付 + * 合单支付 对象 */ @Data @NoArgsConstructor -public class CombineTransactionsJsRequest implements Serializable { +public class CombineTransactionsRequest implements Serializable { /** *
    * 字段名:合单商户appid
@@ -85,7 +85,7 @@ public class CombineTransactionsJsRequest implements Serializable {
    * 
    * 字段名:+支付者
    * 变量名:combine_payer_info
-   * 是否必填:是
+   * 是否必填:否(JSAPI必填)
    * 类型:object
    * 描述:支付者信息
    * 
@@ -136,25 +136,10 @@ public class CombineTransactionsJsRequest implements Serializable { @SerializedName(value = "notify_url") private String notifyUrl; - /** - *
-   * 字段名:指定支付方式
-   * 变量名:limit_pay
-   * 是否必填:否
-   * 类型:array
-   * 描述:
-   *  指定支付方式
-   *  no_credit:指定不能使用信用卡支付
-   *  特殊规则:长度最大限制32个字节
-   *  示例值:no_credit
-   * 
- */ - @SerializedName(value = "limit_pay") - private List limitPay; @Data @NoArgsConstructor - public static class SceneInfo implements Serializable{ + public static class SceneInfo implements Serializable { /** *
      * 字段名:商户端设备号
@@ -185,11 +170,23 @@ public class CombineTransactionsJsRequest implements Serializable {
     @SerializedName(value = "payer_client_ip")
     private String payerClientIp;
 
+    /**
+     * 
+     * 字段名:H5场景信息
+     * 变量名:h5_info
+     * 是否必填:否(H5支付必填)
+     * 类型:object
+     * 描述:
+     *  H5场景信息
+     * 
+ */ + @SerializedName(value = "h5_info") + private H5Info h5Info; } @Data @NoArgsConstructor - public static class SubOrders implements Serializable{ + public static class SubOrders implements Serializable { /** *
      * 字段名:子单商户号
@@ -261,34 +258,6 @@ public class CombineTransactionsJsRequest implements Serializable {
     @SerializedName(value = "sub_mchid")
     private String subMchid;
 
-    /**
-     * 
-     * 字段名:商品详情
-     * 变量名:detail
-     * 是否必填:否
-     * 类型:string(6000)
-     * 描述:商品详细描述(商品列表)
-     * 
- */ - @SerializedName(value = "detail") - private String detail; - - /** - *
-     * 字段名:是否指定分账
-     * 变量名:profit_sharing
-     * 是否必填:是
-     * 类型:bool
-     * 描述:
-     *  是否指定分账
-     *  true:是
-     *  false:否
-     *  示例值:true
-     * 
- */ - @SerializedName(value = "profit_sharing") - private Boolean profitSharing; - /** *
      * 字段名:商品描述
@@ -319,7 +288,7 @@ public class CombineTransactionsJsRequest implements Serializable {
 
   @Data
   @NoArgsConstructor
-  public static class CombinePayerInfo implements Serializable{
+  public static class CombinePayerInfo implements Serializable {
     /**
      * 
      * 字段名:用户标识
@@ -404,4 +373,83 @@ public class CombineTransactionsJsRequest implements Serializable {
 
   }
 
+  @Data
+  @NoArgsConstructor
+  public static class H5Info implements Serializable {
+
+    /**
+     * 
+     * 字段名:场景类型
+     * 变量名:type
+     * 是否必填:是
+     * 类型:string(32)
+     * 描述:
+     *  场景类型,枚举值:
+     *  iOS:IOS移动应用;
+     *  Android:安卓移动应用;
+     *  Wap:WAP网站应用;
+     *  示例值:iOS
+     * 
+ */ + @SerializedName(value = "type") + private String type; + + /** + *
+     * 字段名:应用名称
+     * 变量名:app_name
+     * 是否必填:否
+     * 类型:string(64)
+     * 描述:
+     *  应用名称
+     *  示例值:王者荣耀
+     * 
+ */ + @SerializedName(value = "app_name") + private String appName; + + /** + *
+     * 字段名:网站URL
+     * 变量名:app_url
+     * 是否必填:否
+     * 类型:string(128)
+     * 描述:
+     *  网站URL
+     *  示例值:https://pay.qq.com
+     * 
+ */ + @SerializedName(value = "app_url") + private String appUrl; + + /** + *
+     * 字段名:iOS平台BundleID
+     * 变量名:bundle_id
+     * 是否必填:否
+     * 类型:string(128)
+     * 描述:
+     *  iOS平台BundleID
+     *  示例值:com.tencent.wzryiOS
+     * 
+ */ + @SerializedName(value = "bundle_id") + private String bundleId; + + /** + *
+     * 字段名:Android平台PackageName
+     * 变量名:package_name
+     * 是否必填:否
+     * 类型:string(128)
+     * 描述:
+     *  Android平台PackageName
+     *  示例值:com.tencent.tmgp.sgame
+     * 
+ */ + @SerializedName(value = "package_name") + private String packageName; + + } + } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsResult.java new file mode 100644 index 000000000..cd4edc9d5 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsResult.java @@ -0,0 +1,119 @@ +package com.github.binarywang.wxpay.bean.ecommerce; + +import com.github.binarywang.wxpay.bean.ecommerce.enums.TradeTypeEnum; +import com.github.binarywang.wxpay.v3.util.AesUtils; +import com.github.binarywang.wxpay.v3.util.SignUtils; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.security.PrivateKey; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.UUID; + +/** + * 合单支付 JSAPI支付结果响应 + */ +@Data +@NoArgsConstructor +public class CombineTransactionsResult implements Serializable { + + /** + *
+   * 字段名:预支付交易会话标识 (APP支付、JSAPI支付 会返回)
+   * 变量名:prepay_id
+   * 是否必填:是
+   * 类型:string(64)
+   * 描述:
+   *  数字和字母。微信生成的预支付会话标识,用于后续接口调用使用。
+   *  示例值:wx201410272009395522657a690389285100
+   * 
+ */ + @SerializedName("prepay_id") + private String prepayId; + + /** + *
+   * 字段名:支付跳转链接   (H5支付 会返回)
+   * 变量名:h5_url
+   * 是否必填:是
+   * 类型:string(512)
+   * 描述:
+   *  支付跳转链接
+   *  示例值:https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx2016121516420242444321ca0631331346&package=1405458241
+   * 
+ */ + @SerializedName("h5_url") + private String h5Url; + + /** + *
+   * 字段名:二维码链接  (NATIVE支付 会返回)
+   * 变量名:h5_url
+   * 是否必填:是
+   * 类型:string(512)
+   * 描述:
+   *  二维码链接
+   * 示例值:weixin://pay.weixin.qq.com/bizpayurl/up?pr=NwY5Mz9&groupid=00
+   * 
+ */ + @SerializedName("code_url") + private String codeUrl; + + @Data + @Accessors(chain = true) + public static class JsapiResult implements Serializable { + private String appId; + private String timeStamp; + private String nonceStr; + private String packageValue; + private String signType; + private String paySign; + + private String getSignStr(){ + return String.format("%s\n%s\n%s\n%s\n", appId, timeStamp, nonceStr, packageValue); + } + } + + @Data + @Accessors(chain = true) + public static class AppResult implements Serializable { + private String appid; + private String partnerid; + private String prepayid; + private String packageValue; + private String noncestr; + private String timestamp; + + } + + public T getPayInfo(TradeTypeEnum 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"); + return (T) appResult; + case NATIVE: + return (T) this.codeUrl; + } + return null; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/enums/TradeTypeEnum.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/enums/TradeTypeEnum.java new file mode 100644 index 000000000..c65600ecf --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/enums/TradeTypeEnum.java @@ -0,0 +1,27 @@ +package com.github.binarywang.wxpay.bean.ecommerce.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 支付方式 + */ +@Getter +@AllArgsConstructor +public enum TradeTypeEnum { + + APP("/v3/combine-transactions/app","/v3/pay/partner/transactions/app"), + JSAPI("/v3/combine-transactions/jsapi","/v3/pay/partner/transactions/jsapi"), + NATIVE("/v3/combine-transactions/native","/v3/pay/partner/transactions/native"), + H5("/v3/combine-transactions/h5","/v3/pay/partner/transactions/h5") + ; + + /** + * 合单url + */ + private String combineUrl; + /** + * 单独下单url + */ + private String partnerUrl; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java index 066fe372c..c9faaeddf 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java @@ -1,6 +1,7 @@ package com.github.binarywang.wxpay.service; import com.github.binarywang.wxpay.bean.ecommerce.*; +import com.github.binarywang.wxpay.bean.ecommerce.enums.TradeTypeEnum; import com.github.binarywang.wxpay.exception.WxPayException; /** @@ -63,5 +64,6 @@ public interface EcommerceService { * @param request 请求对象 * @return 预支付交易会话标识, 数字和字母。微信生成的预支付会话标识,用于后续接口调用使用。 */ - CombineTransactionsJsResult combineTransactions(CombineTransactionsJsRequest request) throws WxPayException; + T combineTransactions(TradeTypeEnum tradeType, CombineTransactionsRequest request) throws WxPayException; + } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java index fe916819e..18f51a45c 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java @@ -1,6 +1,7 @@ package com.github.binarywang.wxpay.service.impl; import com.github.binarywang.wxpay.bean.ecommerce.*; +import com.github.binarywang.wxpay.bean.ecommerce.enums.TradeTypeEnum; import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.service.EcommerceService; import com.github.binarywang.wxpay.service.WxPayService; @@ -8,6 +9,7 @@ import com.github.binarywang.wxpay.v3.util.RsaCryptoUtil; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; import java.net.URI; @@ -40,10 +42,12 @@ public class EcommerceServiceImpl implements EcommerceService { } @Override - public CombineTransactionsJsResult combineTransactions(CombineTransactionsJsRequest request) throws WxPayException { - String url = String.format("%s/v3/combine-transactions/jsapi", this.payService.getPayBaseUrl()); + public T combineTransactions(TradeTypeEnum tradeType, CombineTransactionsRequest request) throws WxPayException { + String url = this.payService.getPayBaseUrl() + tradeType.getCombineUrl(); String response = this.payService.postV3(url, GSON.toJson(request)); - return GSON.fromJson(response, CombineTransactionsJsResult.class); + CombineTransactionsResult result = GSON.fromJson(response, CombineTransactionsResult.class); + return result.getPayInfo(tradeType, request.getCombineAppid(), + request.getCombineMchid(), payService.getConfig().getPrivateKey()); } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/SignUtils.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/SignUtils.java new file mode 100644 index 000000000..275a8d51b --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/SignUtils.java @@ -0,0 +1,47 @@ +package com.github.binarywang.wxpay.v3.util; + +import java.security.*; +import java.util.Base64; +import java.util.Random; + +public class SignUtils { + + public static String sign(String string, PrivateKey privateKey){ + try { + Signature sign = Signature.getInstance("SHA256withRSA"); + sign.initSign(privateKey); + sign.update(string.getBytes()); + + return Base64.getEncoder().encodeToString(sign.sign()); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("当前Java环境不支持SHA256withRSA", e); + } catch (SignatureException e) { + throw new RuntimeException("签名计算失败", e); + } catch (InvalidKeyException e) { + throw new RuntimeException("无效的私钥", e); + } + } + + /** + * 随机生成32位字符串. + */ + public static String genRandomStr(){ + return genRandomStr(32); + } + + /** + * 生成随机字符串 + * @param length 字符串长度 + * @return + */ + public static String genRandomStr(int length) { + String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + Random random = new Random(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < length; i++) { + int number = random.nextInt(base.length()); + sb.append(base.charAt(number)); + } + return sb.toString(); + } +}