package com.ruoyi.order.util.tencent; import java.util.Map; import com.ruoyi.order.util.tencent.common.Configure; import com.ruoyi.order.util.tencent.common.WXPayConfig; import com.ruoyi.order.util.tencent.common.WXPayConstants; import com.ruoyi.order.util.tencent.common.WXPayUtil; import com.ruoyi.order.util.tencent.protocol.UnifiedorderReqData; import com.ruoyi.order.util.tencent.protocol.WXPayRequest; import com.ruoyi.order.util.tencent.service.RefundService; import com.ruoyi.order.util.tencent.service.UnifiedorderService; import com.tencent.protocol.RefundReqData; /** * SDK总入口 */ /** * SDK总入口 */ public class WXPay { private WXPayConfig config; private WXPayConstants.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 = WXPayConstants.SignType.MD5; // 沙箱环境 } else { this.signType = WXPayConstants.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 microPayWithPos(Map reqData) throws Exception { return this.microPayWithPos(reqData, 6*1000); } /** * 作用:提交刷卡支付
* 场景:刷卡支付 * @param reqData 向wxpay post的请求数据 * @return API返回数据 * @throws Exception */ public Map microPay(Map reqData) throws Exception { return this.microPay(reqData, 6*1000, 8*1000); } /** * 作用:提交刷卡支付
* 场景:刷卡支付 * @param reqData 向wxpay post的请求数据 * @param connectTimeoutMs 连接超时时间,单位是毫秒 * @param readTimeoutMs 读超时时间,单位是毫秒 * @return API返回数据 * @throws Exception */ public Map microPay(Map 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 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 processResponseXml(String xmlStr) throws Exception { /*String RETURN_CODE = "return_code"; String return_code;*/ Map 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 reqData) throws Exception { // 返回数据的签名方式和请求中给定的签名方式是一致的 return WXPayUtil.isSignatureValid(reqData, this.config.getKey(), this.signType); } /** * 向 Map 中添加 appid、mch_id、nonce_str、sign_type、sign
* 该函数适用于商户适用于统一下单等接口,不适用于红包、代金券接口 * * @param reqData * @return * @throws Exception */ public Map fillRequestData(Map 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(), WXPayConstants.SignType.MD5)); return reqData; } /** * 提交刷卡支付,针对软POS,尽可能做成功 * 内置重试机制,最多60s * @param reqData * @param connectTimeoutMs * @return * @throws Exception */ public Map microPayWithPos(Map reqData, int connectTimeoutMs) throws Exception { int remainingTimeMs = 10*1000; long startTimestampMs = 0; Map 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; } } }