1
0
mirror of synced 2025-12-19 23:08:13 +08:00

new:合单支付

This commit is contained in:
曾浩
2020-08-26 12:17:30 +08:00
parent 5db9706ba0
commit e0d87ebe49
7 changed files with 300 additions and 83 deletions

View File

@@ -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 {
/**
* <pre>
* 字段名:预支付交易会话标识
* 变量名prepay_id
* 是否必填:是
* 类型string(64)
* 描述:
* 数字和字母。微信生成的预支付会话标识,用于后续接口调用使用。
* 示例值wx201410272009395522657a690389285100
* </pre>
*/
@SerializedName("prepay_id")
private String prepayId;
}

View File

@@ -8,11 +8,11 @@ import java.io.Serializable;
import java.util.List; import java.util.List;
/** /**
* 合单支付 JSAPI支付 * 合单支付 对象
*/ */
@Data @Data
@NoArgsConstructor @NoArgsConstructor
public class CombineTransactionsJsRequest implements Serializable { public class CombineTransactionsRequest implements Serializable {
/** /**
* <pre> * <pre>
* 字段名合单商户appid * 字段名合单商户appid
@@ -85,7 +85,7 @@ public class CombineTransactionsJsRequest implements Serializable {
* <pre> * <pre>
* 字段名+支付者 * 字段名+支付者
* 变量名combine_payer_info * 变量名combine_payer_info
* 是否必填 * 是否必填(JSAPI必填)
* 类型object * 类型object
* 描述支付者信息 * 描述支付者信息
* </pre> * </pre>
@@ -136,25 +136,10 @@ public class CombineTransactionsJsRequest implements Serializable {
@SerializedName(value = "notify_url") @SerializedName(value = "notify_url")
private String notifyUrl; private String notifyUrl;
/**
* <pre>
* 字段名指定支付方式
* 变量名limit_pay
* 是否必填
* 类型array
* 描述
* 指定支付方式
* no_credit指定不能使用信用卡支付
* 特殊规则长度最大限制32个字节
* 示例值no_credit
* </pre>
*/
@SerializedName(value = "limit_pay")
private List<String> limitPay;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
public static class SceneInfo implements Serializable{ public static class SceneInfo implements Serializable {
/** /**
* <pre> * <pre>
* 字段名商户端设备号 * 字段名商户端设备号
@@ -185,11 +170,23 @@ public class CombineTransactionsJsRequest implements Serializable {
@SerializedName(value = "payer_client_ip") @SerializedName(value = "payer_client_ip")
private String payerClientIp; private String payerClientIp;
/**
* <pre>
* 字段名H5场景信息
* 变量名h5_info
* 是否必填(H5支付必填)
* 类型object
* 描述
* H5场景信息
* </pre>
*/
@SerializedName(value = "h5_info")
private H5Info h5Info;
} }
@Data @Data
@NoArgsConstructor @NoArgsConstructor
public static class SubOrders implements Serializable{ public static class SubOrders implements Serializable {
/** /**
* <pre> * <pre>
* 字段名子单商户号 * 字段名子单商户号
@@ -261,34 +258,6 @@ public class CombineTransactionsJsRequest implements Serializable {
@SerializedName(value = "sub_mchid") @SerializedName(value = "sub_mchid")
private String subMchid; private String subMchid;
/**
* <pre>
* 字段名商品详情
* 变量名detail
* 是否必填
* 类型string(6000)
* 描述商品详细描述商品列表)
* </pre>
*/
@SerializedName(value = "detail")
private String detail;
/**
* <pre>
* 字段名是否指定分账
* 变量名profit_sharing
* 是否必填
* 类型bool
* 描述
* 是否指定分账
* true
* false
* 示例值true
* </pre>
*/
@SerializedName(value = "profit_sharing")
private Boolean profitSharing;
/** /**
* <pre> * <pre>
* 字段名商品描述 * 字段名商品描述
@@ -319,7 +288,7 @@ public class CombineTransactionsJsRequest implements Serializable {
@Data @Data
@NoArgsConstructor @NoArgsConstructor
public static class CombinePayerInfo implements Serializable{ public static class CombinePayerInfo implements Serializable {
/** /**
* <pre> * <pre>
* 字段名用户标识 * 字段名用户标识
@@ -404,4 +373,83 @@ public class CombineTransactionsJsRequest implements Serializable {
} }
@Data
@NoArgsConstructor
public static class H5Info implements Serializable {
/**
* <pre>
* 字段名场景类型
* 变量名type
* 是否必填
* 类型string(32)
* 描述
* 场景类型枚举值
* iOSIOS移动应用
* Android安卓移动应用
* WapWAP网站应用
* 示例值iOS
* </pre>
*/
@SerializedName(value = "type")
private String type;
/**
* <pre>
* 字段名应用名称
* 变量名app_name
* 是否必填
* 类型string(64)
* 描述
* 应用名称
* 示例值王者荣耀
* </pre>
*/
@SerializedName(value = "app_name")
private String appName;
/**
* <pre>
* 字段名网站URL
* 变量名app_url
* 是否必填
* 类型string(128)
* 描述
* 网站URL
* 示例值https://pay.qq.com
* </pre>
*/
@SerializedName(value = "app_url")
private String appUrl;
/**
* <pre>
* 字段名iOS平台BundleID
* 变量名bundle_id
* 是否必填
* 类型string(128)
* 描述
* iOS平台BundleID
* 示例值com.tencent.wzryiOS
* </pre>
*/
@SerializedName(value = "bundle_id")
private String bundleId;
/**
* <pre>
* 字段名Android平台PackageName
* 变量名package_name
* 是否必填
* 类型string(128)
* 描述
* Android平台PackageName
* 示例值com.tencent.tmgp.sgame
* </pre>
*/
@SerializedName(value = "package_name")
private String packageName;
}
} }

View File

@@ -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 {
/**
* <pre>
* 字段名:预支付交易会话标识 APP支付、JSAPI支付 会返回)
* 变量名prepay_id
* 是否必填:是
* 类型string(64)
* 描述:
* 数字和字母。微信生成的预支付会话标识,用于后续接口调用使用。
* 示例值wx201410272009395522657a690389285100
* </pre>
*/
@SerializedName("prepay_id")
private String prepayId;
/**
* <pre>
* 字段名:支付跳转链接 H5支付 会返回)
* 变量名h5_url
* 是否必填:是
* 类型string(512)
* 描述:
* 支付跳转链接
* 示例值https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx2016121516420242444321ca0631331346&package=1405458241
* </pre>
*/
@SerializedName("h5_url")
private String h5Url;
/**
* <pre>
* 字段名:二维码链接 NATIVE支付 会返回)
* 变量名h5_url
* 是否必填:是
* 类型string(512)
* 描述:
* 二维码链接
* 示例值weixin://pay.weixin.qq.com/bizpayurl/up?pr=NwY5Mz9&groupid=00
* </pre>
*/
@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> 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;
}
}

View File

@@ -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;
}

View File

@@ -1,6 +1,7 @@
package com.github.binarywang.wxpay.service; package com.github.binarywang.wxpay.service;
import com.github.binarywang.wxpay.bean.ecommerce.*; 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.exception.WxPayException;
/** /**
@@ -63,5 +64,6 @@ public interface EcommerceService {
* @param request 请求对象 * @param request 请求对象
* @return 预支付交易会话标识, 数字和字母。微信生成的预支付会话标识,用于后续接口调用使用。 * @return 预支付交易会话标识, 数字和字母。微信生成的预支付会话标识,用于后续接口调用使用。
*/ */
CombineTransactionsJsResult combineTransactions(CombineTransactionsJsRequest request) throws WxPayException; <T> T combineTransactions(TradeTypeEnum tradeType, CombineTransactionsRequest request) throws WxPayException;
} }

View File

@@ -1,6 +1,7 @@
package com.github.binarywang.wxpay.service.impl; package com.github.binarywang.wxpay.service.impl;
import com.github.binarywang.wxpay.bean.ecommerce.*; 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.exception.WxPayException;
import com.github.binarywang.wxpay.service.EcommerceService; import com.github.binarywang.wxpay.service.EcommerceService;
import com.github.binarywang.wxpay.service.WxPayService; 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.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import java.net.URI; import java.net.URI;
@@ -40,10 +42,12 @@ public class EcommerceServiceImpl implements EcommerceService {
} }
@Override @Override
public CombineTransactionsJsResult combineTransactions(CombineTransactionsJsRequest request) throws WxPayException { public <T> T combineTransactions(TradeTypeEnum tradeType, CombineTransactionsRequest request) throws WxPayException {
String url = String.format("%s/v3/combine-transactions/jsapi", this.payService.getPayBaseUrl()); String url = this.payService.getPayBaseUrl() + tradeType.getCombineUrl();
String response = this.payService.postV3(url, GSON.toJson(request)); 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());
} }

View File

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