new:合单支付
This commit is contained in:
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:合单商户appid
|
||||
@@ -85,7 +85,7 @@ public class CombineTransactionsJsRequest implements Serializable {
|
||||
* <pre>
|
||||
* 字段名:+支付者
|
||||
* 变量名:combine_payer_info
|
||||
* 是否必填:是
|
||||
* 是否必填:否(JSAPI必填)
|
||||
* 类型:object
|
||||
* 描述:支付者信息
|
||||
* </pre>
|
||||
@@ -136,21 +136,6 @@ public class CombineTransactionsJsRequest implements Serializable {
|
||||
@SerializedName(value = "notify_url")
|
||||
private String notifyUrl;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:指定支付方式
|
||||
* 变量名:limit_pay
|
||||
* 是否必填:否
|
||||
* 类型:array
|
||||
* 描述:
|
||||
* 指定支付方式
|
||||
* no_credit:指定不能使用信用卡支付
|
||||
* 特殊规则:长度最大限制32个字节
|
||||
* 示例值:no_credit
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "limit_pay")
|
||||
private List<String> limitPay;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@@ -185,6 +170,18 @@ public class CombineTransactionsJsRequest implements Serializable {
|
||||
@SerializedName(value = "payer_client_ip")
|
||||
private String payerClientIp;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:H5场景信息
|
||||
* 变量名:h5_info
|
||||
* 是否必填:否(H5支付必填)
|
||||
* 类型:object
|
||||
* 描述:
|
||||
* H5场景信息
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "h5_info")
|
||||
private H5Info h5Info;
|
||||
}
|
||||
|
||||
@Data
|
||||
@@ -261,34 +258,6 @@ public class CombineTransactionsJsRequest implements Serializable {
|
||||
@SerializedName(value = "sub_mchid")
|
||||
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>
|
||||
* 字段名:商品描述
|
||||
@@ -404,4 +373,83 @@ public class CombineTransactionsJsRequest implements Serializable {
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class H5Info implements Serializable {
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:场景类型
|
||||
* 变量名:type
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 场景类型,枚举值:
|
||||
* iOS:IOS移动应用;
|
||||
* Android:安卓移动应用;
|
||||
* Wap:WAP网站应用;
|
||||
* 示例值: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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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> T combineTransactions(TradeTypeEnum tradeType, CombineTransactionsRequest request) throws WxPayException;
|
||||
|
||||
}
|
||||
|
||||
@@ -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> 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());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user