package com.tencent;
|
import java.util.Map;
|
|
|
import com.tencent.common.Configure;
|
import com.tencent.common.WXPayConfig;
|
import com.tencent.common.WXPayConstants;
|
import com.tencent.common.WXPayConstants.SignType;
|
import com.tencent.common.WXPayUtil;
|
import com.tencent.protocol.RefundReqData;
|
import com.tencent.protocol.UnifiedorderReqData;
|
import com.tencent.protocol.WXPayRequest;
|
import com.tencent.service.RefundService;
|
import com.tencent.service.UnifiedorderService;
|
/**
|
* SDK总入口
|
*/
|
/**
|
* SDK总入口
|
*/
|
public class WXPay {
|
|
private WXPayConfig config;
|
private SignType signType;
|
private boolean autoReport;
|
private boolean useSandbox;
|
private String notifyUrl;
|
private WXPayRequest wxPayRequest;
|
|
public WXPay(final WXPayConfig config) throws Exception {
|
this(config, null, true, false);
|
}
|
|
public WXPay(final WXPayConfig config, final boolean autoReport) throws Exception {
|
this(config, null, autoReport, false);
|
}
|
|
|
public WXPay(final WXPayConfig config, final boolean autoReport, final boolean useSandbox) throws Exception{
|
this(config, null, autoReport, useSandbox);
|
}
|
|
public WXPay(final WXPayConfig config, final String notifyUrl) throws Exception {
|
this(config, notifyUrl, true, false);
|
}
|
|
public WXPay(final WXPayConfig config, final String notifyUrl, final boolean autoReport) throws Exception {
|
this(config, notifyUrl, autoReport, false);
|
}
|
|
public WXPay(final WXPayConfig config, final String notifyUrl, final boolean autoReport, final boolean useSandbox) throws Exception {
|
this.config = config;
|
this.notifyUrl = notifyUrl;
|
this.autoReport = autoReport;
|
this.useSandbox = useSandbox;
|
if (useSandbox) {
|
this.signType = SignType.MD5; // 沙箱环境
|
}
|
else {
|
this.signType = SignType.HMACSHA256;
|
}
|
this.wxPayRequest = new WXPayRequest(config);
|
}
|
|
/**
|
* 初始化SDK依赖的几个关键配置
|
* @param key 签名算法需要用到的秘钥
|
* @param appID 公众账号ID
|
* @param mchID 商户ID
|
* @param sdbMchID 子商户ID,受理模式必填
|
* @param certLocalPath HTTP证书在服务器中的路径,用来加载证书用
|
* @param certPassword HTTP证书的密码,默认等于MCHID
|
*/
|
public static void initSDKConfiguration(String key,String appID,String mchID,String sdbMchID,String certLocalPath,String certPassword){
|
System.out.println("________@@@@@______initSDKConfiguration");
|
Configure.setKey(key);
|
Configure.setAppID(appID);
|
Configure.setMchID(mchID);
|
Configure.setSubMchID(sdbMchID);
|
Configure.setCertLocalPath(certLocalPath);
|
Configure.setCertPassword(certPassword);
|
}
|
|
/**
|
* 请求统一下单服务
|
*/
|
public static String requestUnifiedorderService(Integer apptype,UnifiedorderReqData unifiedorderReqData) throws Exception{
|
return new UnifiedorderService(apptype).request(unifiedorderReqData);
|
}
|
|
|
/**
|
* 商家向用户付款(提现)
|
*/
|
/* public static String requestPayToTheUserService(Integer apptype, PayToTheUserReqData payToTheUserReqData) throws Exception{
|
return new PayToTheUserService(apptype).request(payToTheUserReqData);
|
}*/
|
|
/**
|
* 请求退款服务
|
*/
|
public static String requestRefundService(Integer apptype, RefundReqData refundReqData) throws Exception{
|
return new RefundService(apptype).request(refundReqData);
|
}
|
|
/**
|
* 提交刷卡支付,针对软POS,尽可能做成功
|
* 内置重试机制,最多60s
|
* @param reqData
|
* @return
|
* @throws Exception
|
*/
|
public Map<String, String> microPayWithPos(Map<String, String> reqData) throws Exception {
|
return this.microPayWithPos(reqData, 6*1000);
|
}
|
|
/**
|
* 作用:提交刷卡支付<br>
|
* 场景:刷卡支付
|
* @param reqData 向wxpay post的请求数据
|
* @return API返回数据
|
* @throws Exception
|
*/
|
public Map<String, String> microPay(Map<String, String> reqData) throws Exception {
|
return this.microPay(reqData, 6*1000, 8*1000);
|
}
|
|
|
/**
|
* 作用:提交刷卡支付<br>
|
* 场景:刷卡支付
|
* @param reqData 向wxpay post的请求数据
|
* @param connectTimeoutMs 连接超时时间,单位是毫秒
|
* @param readTimeoutMs 读超时时间,单位是毫秒
|
* @return API返回数据
|
* @throws Exception
|
*/
|
public Map<String, String> microPay(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception {
|
String url;
|
/* if (this.useSandbox) {//沙箱环境
|
url = WXPayConstants.SANDBOX_MICROPAY_URL_SUFFIX;
|
}*/
|
url = WXPayConstants.MICROPAY_URL_SUFFIX;
|
String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
|
return this.processResponseXml(respXml);
|
}
|
|
/**
|
* 不需要证书的请求
|
* @param urlSuffix String
|
* @param reqData 向wxpay post的请求数据
|
* @param connectTimeoutMs 超时时间,单位是毫秒
|
* @param readTimeoutMs 超时时间,单位是毫秒
|
* @return API返回数据
|
* @throws Exception
|
*/
|
public String requestWithoutCert(String urlSuffix, Map<String, String> reqData,
|
int connectTimeoutMs, int readTimeoutMs) throws Exception {
|
String msgUUID = reqData.get("nonce_str");
|
String reqBody = WXPayUtil.mapToXml(reqData);
|
|
String resp = new WXPayRequest(config).requestWithoutCert(urlSuffix, msgUUID, reqBody, connectTimeoutMs, readTimeoutMs, autoReport);
|
return resp;
|
}
|
|
|
/**
|
* 处理 HTTPS API返回数据,转换成Map对象。return_code为SUCCESS时,验证签名。
|
* @param xmlStr API返回的XML格式数据
|
* @return Map类型数据
|
* @throws Exception
|
*/
|
public Map<String, String> processResponseXml(String xmlStr) throws Exception {
|
/*String RETURN_CODE = "return_code";
|
String return_code;*/
|
Map<String, String> respData = WXPayUtil.xmlToMap(xmlStr);
|
return respData;
|
/* if (respData.containsKey(RETURN_CODE)) {
|
return_code = respData.get(RETURN_CODE);
|
}
|
else {
|
throw new Exception(String.format("No `return_code` in XML: %s", xmlStr));
|
}
|
|
if (return_code.equals(WXPayConstants.FAIL)) {
|
return respData;
|
}
|
else if (return_code.equals(WXPayConstants.SUCCESS)) {
|
if (this.isResponseSignatureValid(respData)) {
|
return respData;
|
}
|
else {
|
throw new Exception(String.format("Invalid sign value in XML: %s", xmlStr));
|
}
|
}
|
else {
|
throw new Exception(String.format("return_code value %s is invalid in XML: %s", return_code, xmlStr));
|
}*/
|
}
|
|
/**
|
* 判断xml数据的sign是否有效,必须包含sign字段,否则返回false。
|
*
|
* @param reqData 向wxpay post的请求数据
|
* @return 签名是否有效
|
* @throws Exception
|
*/
|
public boolean isResponseSignatureValid(Map<String, String> reqData) throws Exception {
|
// 返回数据的签名方式和请求中给定的签名方式是一致的
|
return WXPayUtil.isSignatureValid(reqData, this.config.getKey(), this.signType);
|
}
|
|
/**
|
* 向 Map 中添加 appid、mch_id、nonce_str、sign_type、sign <br>
|
* 该函数适用于商户适用于统一下单等接口,不适用于红包、代金券接口
|
*
|
* @param reqData
|
* @return
|
* @throws Exception
|
*/
|
public Map<String, String> fillRequestData(Map<String, String> reqData) throws Exception {
|
reqData.put("appid", Configure.getAppid());
|
reqData.put("mch_id", Configure.getMchid());
|
reqData.put("nonce_str", WXPayUtil.generateUUID());
|
reqData.put("sign_type", WXPayConstants.MD5);
|
//reqData.put("sign_type", WXPayConstants.HMACSHA256);
|
reqData.put("sign", WXPayUtil.generateSignature(reqData, Configure.getKey(), SignType.MD5));
|
return reqData;
|
}
|
|
/**
|
* 提交刷卡支付,针对软POS,尽可能做成功
|
* 内置重试机制,最多60s
|
* @param reqData
|
* @param connectTimeoutMs
|
* @return
|
* @throws Exception
|
*/
|
public Map<String, String> microPayWithPos(Map<String, String> reqData, int connectTimeoutMs) throws Exception {
|
int remainingTimeMs = 10*1000;
|
long startTimestampMs = 0;
|
Map<String, String> lastResult = null;
|
Exception lastException = null;
|
|
|
while (true) {
|
startTimestampMs = WXPayUtil.getCurrentTimestampMs();
|
int readTimeoutMs = remainingTimeMs - connectTimeoutMs;
|
if (readTimeoutMs > 1000) {
|
try {
|
lastResult = this.microPay(reqData, connectTimeoutMs, readTimeoutMs);
|
String returnCode = lastResult.get("return_code");//return_code
|
if (returnCode.equals("SUCCESS")) {break;
|
/* String resultCode = lastResult.get("result_code");
|
String errCode = lastResult.get("err_code");
|
if (resultCode.equals("SUCCESS")) {
|
break;
|
}
|
else {
|
// 看错误码,若支付结果未知,则重试提交刷卡支付
|
if (errCode.equals("SYSTEMERROR") || errCode.equals("BANKERROR") || errCode.equals("USERPAYING")) {
|
remainingTimeMs = remainingTimeMs - (int)(WXPayUtil.getCurrentTimestampMs() - startTimestampMs);
|
if (remainingTimeMs <= 100) {
|
break;
|
}
|
else {
|
WXPayUtil.getLogger().info("microPayWithPos: try micropay again");
|
if (remainingTimeMs > 5*1000) {
|
Thread.sleep(5*1000);
|
}
|
else {
|
Thread.sleep(1*1000);
|
}
|
continue;
|
}
|
}
|
else {
|
break;
|
}
|
}*/
|
}
|
else {
|
break;
|
}
|
}
|
catch (Exception ex) {
|
lastResult = null;
|
lastException = ex;
|
}
|
}
|
else {
|
break;
|
}
|
}
|
|
if (lastResult == null) {
|
throw lastException;
|
}
|
else {
|
return lastResult;
|
}
|
}
|
|
}
|