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();
+ }
+}