mirror of
https://gitee.com/binary/weixin-java-tools.git
synced 2025-05-05 05:07:46 +08:00
🎨 #1733 微信支付服务商配置优化,增加服务商合单支付接口
* art:微信服务商配置优化 * new:jsapi合单支付 * new:合单支付 Co-authored-by: 曾浩 <epdcgsi@dingtalk.com>
This commit is contained in:
parent
425b08245a
commit
6c490e3295
@ -7,6 +7,10 @@ import lombok.NoArgsConstructor;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 二级商户进件 查询申请状态结果响应
|
||||
*
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class ApplymentsStatusResult implements Serializable {
|
||||
|
@ -0,0 +1,455 @@
|
||||
package com.github.binarywang.wxpay.bean.ecommerce;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 合单支付 对象
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class CombineTransactionsRequest implements Serializable {
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:合单商户appid
|
||||
* 变量名:combine_appid
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 合单发起方的appid。
|
||||
* 示例值:wxd678efh567hg6787
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "combine_appid")
|
||||
private String combineAppid;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:合单商户号
|
||||
* 变量名:combine_mchid
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 合单发起方商户号。
|
||||
* 示例值:1900000109
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "combine_mchid")
|
||||
private String combineMchid;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:合单商户订单号
|
||||
* 变量名:combine_out_trade_no
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 合单支付总订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。
|
||||
* 示例值:P20150806125346
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "combine_out_trade_no")
|
||||
private String combineOutTradeNo;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:+场景信息
|
||||
* 变量名:scene_info
|
||||
* 是否必填:否
|
||||
* 类型:object
|
||||
* 描述:支付场景信息描述
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "scene_info")
|
||||
private SceneInfo sceneInfo;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:+子单信息
|
||||
* 变量名:sub_orders
|
||||
* 是否必填:是
|
||||
* 类型:array
|
||||
* 描述:
|
||||
* 最多支持子单条数:50
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "sub_orders")
|
||||
private List<SubOrders> subOrders;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:+支付者
|
||||
* 变量名:combine_payer_info
|
||||
* 是否必填:否(JSAPI必填)
|
||||
* 类型:object
|
||||
* 描述:支付者信息
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "combine_payer_info")
|
||||
private CombinePayerInfo combinePayerInfo;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:交易起始时间
|
||||
* 变量名:time_start
|
||||
* 是否必填:否
|
||||
* 类型:string(14)
|
||||
* 描述:
|
||||
* 订单生成时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日 13点29分35秒。
|
||||
* 示例值:2019-12-31T15:59:60+08:00
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "time_start")
|
||||
private String timeStart;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:交易结束时间
|
||||
* 变量名:time_expire
|
||||
* 是否必填:否
|
||||
* 类型:string(14)
|
||||
* 描述:
|
||||
* 订单失效时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日 13点29分35秒。
|
||||
* 示例值:2019-12-31T15:59:60+08:00
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "time_expire")
|
||||
private String timeExpire;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:通知地址
|
||||
* 变量名:notify_url
|
||||
* 是否必填:是
|
||||
* 类型:string(256)
|
||||
* 描述:
|
||||
* 接收微信支付异步通知回调地址,通知url必须为直接可访问的URL,不能携带参数。
|
||||
* 格式: URL
|
||||
* 示例值:https://yourapp.com/notify
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "notify_url")
|
||||
private String notifyUrl;
|
||||
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class SceneInfo implements Serializable {
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:商户端设备号
|
||||
* 变量名:device_id
|
||||
* 是否必填:否
|
||||
* 类型:string(16)
|
||||
* 描述:
|
||||
* 终端设备号(门店号或收银设备ID)。
|
||||
* 特殊规则:长度最小7个字节
|
||||
* 示例值:POS1:1
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "device_id")
|
||||
private String deviceId;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:用户终端IP
|
||||
* 变量名:payer_client_ip
|
||||
* 是否必填:是
|
||||
* 类型:string(45)
|
||||
* 描述:
|
||||
* 用户端实际ip
|
||||
* 格式: ip(ipv4+ipv6)
|
||||
* 示例值:14.17.22.32
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "payer_client_ip")
|
||||
private String payerClientIp;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:H5场景信息
|
||||
* 变量名:h5_info
|
||||
* 是否必填:否(H5支付必填)
|
||||
* 类型:object
|
||||
* 描述:
|
||||
* H5场景信息
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "h5_info")
|
||||
private H5Info h5Info;
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class SubOrders implements Serializable {
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:子单商户号
|
||||
* 变量名:mchid
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 子单发起方商户号,必须与发起方appid有绑定关系。
|
||||
* 示例值:1900000109
|
||||
* 此处一般填写服务商商户号
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "mchid")
|
||||
private String mchid;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:附加信息
|
||||
* 变量名:attach
|
||||
* 是否必填:是
|
||||
* 类型:string(128)
|
||||
* 描述:
|
||||
* 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。
|
||||
* 示例值:深圳分店
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "attach")
|
||||
private String attach;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:+订单金额
|
||||
* 变量名:amount
|
||||
* 是否必填:是
|
||||
* 类型:object
|
||||
* 描述:
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "amount")
|
||||
private Amount amount;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:子单商户订单号
|
||||
* 变量名:out_trade_no
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。
|
||||
* 特殊规则:最小字符长度为6
|
||||
* 示例值:20150806125346
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "out_trade_no")
|
||||
private String outTradeNo;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:二级商户号
|
||||
* 变量名:sub_mchid
|
||||
* 是否必填:是
|
||||
* 类型:string(32)
|
||||
* 描述:
|
||||
* 二级商户商户号,由微信支付生成并下发。
|
||||
* 注意:仅适用于电商平台 服务商
|
||||
* 示例值:1900000109
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "sub_mchid")
|
||||
private String subMchid;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:商品描述
|
||||
* 变量名:description
|
||||
* 是否必填:是
|
||||
* 类型:string(128)
|
||||
* 描述:
|
||||
* 商品简单描述。需传入应用市场上的APP名字-实际商品名称,例如:天天爱消除-游戏充值。
|
||||
* 示例值:腾讯充值中心-QQ会员充值
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "description")
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:+结算信息
|
||||
* 变量名:settle_info
|
||||
* 是否必填:否
|
||||
* 类型:Object
|
||||
* 描述:结算信息
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "settle_info")
|
||||
private SettleInfo settleInfo;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class CombinePayerInfo implements Serializable {
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:用户标识
|
||||
* 变量名:openid
|
||||
* 是否必填:是
|
||||
* 类型:string(128)
|
||||
* 描述:
|
||||
* 使用合单appid获取的对应用户openid。是用户在商户appid下的唯一标识。
|
||||
* 示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "openid")
|
||||
private String openid;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class Amount implements Serializable {
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:标价金额
|
||||
* 变量名:total_amount
|
||||
* 是否必填:是
|
||||
* 类型:int64
|
||||
* 描述:
|
||||
* 子单金额,单位为分。
|
||||
* 示例值:100
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "total_amount")
|
||||
private Integer totalAmount;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:标价币种
|
||||
* 变量名:currency
|
||||
* 是否必填:是
|
||||
* 类型:string(8)
|
||||
* 描述:
|
||||
* 符合ISO 4217标准的三位字母代码,人民币:CNY。
|
||||
* 示例值:CNY
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "currency")
|
||||
private String currency;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class SettleInfo implements Serializable{
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:是否指定分账
|
||||
* 变量名:profit_sharing
|
||||
* 是否必填:否
|
||||
* 类型:bool
|
||||
* 描述:
|
||||
* 是否分账,与外层profit_sharing同时存在时,以本字段为准。
|
||||
* true:是
|
||||
* false:否
|
||||
* 示例值:true
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "profit_sharing")
|
||||
private Boolean profitSharing;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:补差金额
|
||||
* 变量名:subsidy_amount
|
||||
* 是否必填:否
|
||||
* 类型:int64
|
||||
* 描述:
|
||||
* SettleInfo.profit_sharing为true时,该金额才生效。
|
||||
* 示例值:10
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "subsidy_amount")
|
||||
private Integer subsidyAmount;
|
||||
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
@ -19,6 +19,7 @@ import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyStore;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
@ -229,7 +230,7 @@ public class WxPayConfig {
|
||||
public CloseableHttpClient initApiV3HttpClient() throws WxPayException {
|
||||
String privateKeyPath = this.getPrivateKeyPath();
|
||||
String privateCertPath = this.getPrivateCertPath();
|
||||
String certSerialNo = this.getCertSerialNo();
|
||||
String serialNo = this.getCertSerialNo();
|
||||
String apiV3Key = this.getApiV3Key();
|
||||
if (StringUtils.isBlank(privateKeyPath)) {
|
||||
throw new WxPayException("请确保privateKeyPath已设置");
|
||||
@ -237,9 +238,9 @@ public class WxPayConfig {
|
||||
if (StringUtils.isBlank(privateCertPath)) {
|
||||
throw new WxPayException("请确保privateCertPath已设置");
|
||||
}
|
||||
if (StringUtils.isBlank(certSerialNo)) {
|
||||
throw new WxPayException("请确保certSerialNo证书序列号已设置");
|
||||
}
|
||||
// if (StringUtils.isBlank(certSerialNo)) {
|
||||
// throw new WxPayException("请确保certSerialNo证书序列号已设置");
|
||||
// }
|
||||
if (StringUtils.isBlank(apiV3Key)) {
|
||||
throw new WxPayException("请确保apiV3Key值已设置");
|
||||
}
|
||||
@ -248,6 +249,10 @@ public class WxPayConfig {
|
||||
InputStream certInputStream = this.loadConfigInputStream(privateCertPath);
|
||||
try {
|
||||
PrivateKey merchantPrivateKey = PemUtils.loadPrivateKey(keyInputStream);
|
||||
X509Certificate certificate = PemUtils.loadCertificate(certInputStream);
|
||||
if(StringUtils.isBlank(serialNo)){
|
||||
this.certSerialNo = certificate.getSerialNumber().toString(16).toUpperCase();
|
||||
}
|
||||
|
||||
AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
|
||||
new WxPayCredentials(mchId, new PrivateKeySigner(certSerialNo, merchantPrivateKey)),
|
||||
@ -255,7 +260,7 @@ public class WxPayConfig {
|
||||
|
||||
CloseableHttpClient httpClient = WxPayV3HttpClientBuilder.create()
|
||||
.withMerchant(mchId, certSerialNo, merchantPrivateKey)
|
||||
.withWechatpay(Collections.singletonList(PemUtils.loadCertificate(certInputStream)))
|
||||
.withWechatpay(Collections.singletonList(certificate))
|
||||
.withValidator(new WxPayValidator(verifier))
|
||||
.build();
|
||||
this.apiV3HttpClient = httpClient;
|
||||
|
@ -1,8 +1,7 @@
|
||||
package com.github.binarywang.wxpay.service;
|
||||
|
||||
import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsRequest;
|
||||
import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsResult;
|
||||
import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsStatusResult;
|
||||
import com.github.binarywang.wxpay.bean.ecommerce.*;
|
||||
import com.github.binarywang.wxpay.bean.ecommerce.enums.TradeTypeEnum;
|
||||
import com.github.binarywang.wxpay.exception.WxPayException;
|
||||
|
||||
/**
|
||||
@ -55,4 +54,16 @@ public interface EcommerceService {
|
||||
*/
|
||||
ApplymentsStatusResult queryApplyStatusByOutRequestNo(String outRequestNo) throws WxPayException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 合单下单-JS支付API.
|
||||
* 请求URL:https://api.mch.weixin.qq.com/v3/combine-transactions/jsapi
|
||||
* 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/combine/chapter3_2.shtml
|
||||
* </pre>
|
||||
*
|
||||
* @param request 请求对象
|
||||
* @return 预支付交易会话标识, 数字和字母。微信生成的预支付会话标识,用于后续接口调用使用。
|
||||
*/
|
||||
<T> T combineTransactions(TradeTypeEnum tradeType, CombineTransactionsRequest request) throws WxPayException;
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
package com.github.binarywang.wxpay.service.impl;
|
||||
|
||||
import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsRequest;
|
||||
import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsResult;
|
||||
import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsStatusResult;
|
||||
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;
|
||||
@ -10,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;
|
||||
|
||||
@ -41,5 +41,14 @@ public class EcommerceServiceImpl implements EcommerceService {
|
||||
return GSON.fromJson(result, ApplymentsStatusResult.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
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));
|
||||
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();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user