package com.github.wxpay.sdk;import java.util.HashMap;import java.util.Map;import com.github.wxpay.sdk.WXPayConstants.SignType;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); } private void checkWXPayConfig() throws Exception { if (this.config == null) { throw new Exception("config is null"); } if (this.config.getAppID() == null || this.config.getAppID().trim().length() == 0) { throw new Exception("appid in config is empty"); } if (this.config.getMchID() == null || this.config.getMchID().trim().length() == 0) { throw new Exception("appid in config is empty"); } if (this.config.getCertStream() == null) { throw new Exception("cert stream in config is empty"); }// if (this.config.getPrimaryDomain() == null || this.config.getPrimaryDomain().trim().length() == 0) {// throw new Exception("primary domain in config is empty");// }//// // todo 海外就填两个相同的? 下面的逻辑待考虑// if (this.config.getAlternateDomain() == null || this.config.getAlternateDomain().trim().length() == 0) {// throw new Exception("alternate domain in config is empty");// } if (this.config.getWXPayDomain() == null){ throw new Exception("config.getWXPayDomain() is null"); } if (this.config.getHttpConnectTimeoutMs() < 10) { throw new Exception("http connect timeout is too small"); } if (this.config.getHttpReadTimeoutMs() < 10) { throw new Exception("http read timeout is too small"); } } /** * 向 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", config.getAppID()); reqData.put("mch_id", config.getMchID()); reqData.put("nonce_str", WXPayUtil.generateUUID()); if (SignType.MD5.equals(this.signType)) { reqData.put("sign_type", WXPayConstants.MD5); } else if (SignType.HMACSHA256.equals(this.signType)) { reqData.put("sign_type", WXPayConstants.HMACSHA256); } reqData.put("sign", WXPayUtil.generateSignature(reqData, config.getKey(), this.signType)); return reqData; } /** * 判断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); } /** * 判断支付结果通知中的sign是否有效 * * @param reqData 向wxpay post的请求数据 * @return 签名是否有效 * @throws Exception */ public boolean isPayResultNotifySignatureValid(Map<String, String> reqData) throws Exception { String signTypeInData = reqData.get(WXPayConstants.FIELD_SIGN_TYPE); SignType signType; if (signTypeInData == null) { signType = SignType.MD5; } else { signTypeInData = signTypeInData.trim(); if (signTypeInData.length() == 0) { signType = SignType.MD5; } else if (WXPayConstants.MD5.equals(signTypeInData)) { signType = SignType.MD5; } else if (WXPayConstants.HMACSHA256.equals(signTypeInData)) { signType = SignType.HMACSHA256; } else { throw new Exception(String.format("Unsupported sign_type: %s", signTypeInData)); } } return WXPayUtil.isSignatureValid(reqData, this.config.getKey(), signType); } /** * 不需要证书的请求 * @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 = this.wxPayRequest.requestWithoutCert(urlSuffix, msgUUID, reqBody, connectTimeoutMs, readTimeoutMs, autoReport); return resp; } /** * 需要证书的请求 * @param urlSuffix String * @param reqData 向wxpay post的请求数据 Map * @param connectTimeoutMs 超时时间,单位是毫秒 * @param readTimeoutMs 超时时间,单位是毫秒 * @return API返回数据 * @throws Exception */ public String requestWithCert(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 = this.wxPayRequest.requestWithCert(urlSuffix, msgUUID, reqBody, connectTimeoutMs, readTimeoutMs, this.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); 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)); } } /** * 作用:提交刷卡支付<br> * 场景:刷卡支付 * @param reqData 向wxpay post的请求数据 * @return API返回数据 * @throws Exception */ public Map<String, String> microPay(Map<String, String> reqData) throws Exception { return this.microPay(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs()); } /** * 作用:提交刷卡支付<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; } else { url = WXPayConstants.MICROPAY_URL_SUFFIX; } String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs); return this.processResponseXml(respXml); } /** * 提交刷卡支付,针对软POS,尽可能做成功 * 内置重试机制,最多60s * @param reqData * @return * @throws Exception */ public Map<String, String> microPayWithPos(Map<String, String> reqData) throws Exception { return this.microPayWithPos(reqData, this.config.getHttpConnectTimeoutMs()); } /** * 提交刷卡支付,针对软POS,尽可能做成功 * 内置重试机制,最多60s * @param reqData * @param connectTimeoutMs * @return * @throws Exception */ public Map<String, String> microPayWithPos(Map<String, String> reqData, int connectTimeoutMs) throws Exception { int remainingTimeMs = 60*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"); if (returnCode.equals("SUCCESS")) { 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; } } /** * 作用:统一下单<br> * 场景:公共号支付、扫码支付、APP支付 * @param reqData 向wxpay post的请求数据 * @return API返回数据 * @throws Exception */ public Map<String, String> unifiedOrder(Map<String, String> reqData) throws Exception { return this.unifiedOrder(reqData, config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs()); } /** * 作用:统一下单<br> * 场景:公共号支付、扫码支付、APP支付 * @param reqData 向wxpay post的请求数据 * @param connectTimeoutMs 连接超时时间,单位是毫秒 * @param readTimeoutMs 读超时时间,单位是毫秒 * @return API返回数据 * @throws Exception */ public Map<String, String> unifiedOrder(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception { String url; if (this.useSandbox) { url = WXPayConstants.SANDBOX_UNIFIEDORDER_URL_SUFFIX; } else { url = WXPayConstants.UNIFIEDORDER_URL_SUFFIX; } if(this.notifyUrl != null) { reqData.put("notify_url", this.notifyUrl); } String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs); return this.processResponseXml(respXml); } /** * 作用:查询订单<br> * 场景:刷卡支付、公共号支付、扫码支付、APP支付 * @param reqData 向wxpay post的请求数据 * @return API返回数据 * @throws Exception */ public Map<String, String> orderQuery(Map<String, String> reqData) throws Exception { return this.orderQuery(reqData, config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs()); } /** * 作用:查询订单<br> * 场景:刷卡支付、公共号支付、扫码支付、APP支付 * @param reqData 向wxpay post的请求数据 int * @param connectTimeoutMs 连接超时时间,单位是毫秒 * @param readTimeoutMs 读超时时间,单位是毫秒 * @return API返回数据 * @throws Exception */ public Map<String, String> orderQuery(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception { String url; if (this.useSandbox) { url = WXPayConstants.SANDBOX_ORDERQUERY_URL_SUFFIX; } else { url = WXPayConstants.ORDERQUERY_URL_SUFFIX; } String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs); return this.processResponseXml(respXml); } /** * 作用:撤销订单<br> * 场景:刷卡支付 * @param reqData 向wxpay post的请求数据 * @return API返回数据 * @throws Exception */ public Map<String, String> reverse(Map<String, String> reqData) throws Exception { return this.reverse(reqData, config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs()); } /** * 作用:撤销订单<br> * 场景:刷卡支付<br> * 其他:需要证书 * @param reqData 向wxpay post的请求数据 * @param connectTimeoutMs 连接超时时间,单位是毫秒 * @param readTimeoutMs 读超时时间,单位是毫秒 * @return API返回数据 * @throws Exception */ public Map<String, String> reverse(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception { String url; if (this.useSandbox) { url = WXPayConstants.SANDBOX_REVERSE_URL_SUFFIX; } else { url = WXPayConstants.REVERSE_URL_SUFFIX; } String respXml = this.requestWithCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs); return this.processResponseXml(respXml); } /** * 作用:关闭订单<br> * 场景:公共号支付、扫码支付、APP支付 * @param reqData 向wxpay post的请求数据 * @return API返回数据 * @throws Exception */ public Map<String, String> closeOrder(Map<String, String> reqData) throws Exception { return this.closeOrder(reqData, config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs()); } /** * 作用:关闭订单<br> * 场景:公共号支付、扫码支付、APP支付 * @param reqData 向wxpay post的请求数据 * @param connectTimeoutMs 连接超时时间,单位是毫秒 * @param readTimeoutMs 读超时时间,单位是毫秒 * @return API返回数据 * @throws Exception */ public Map<String, String> closeOrder(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception { String url; if (this.useSandbox) { url = WXPayConstants.SANDBOX_CLOSEORDER_URL_SUFFIX; } else { url = WXPayConstants.CLOSEORDER_URL_SUFFIX; } String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs); return this.processResponseXml(respXml); } /** * 作用:申请退款<br> * 场景:刷卡支付、公共号支付、扫码支付、APP支付 * @param reqData 向wxpay post的请求数据 * @return API返回数据 * @throws Exception */ public Map<String, String> refund(Map<String, String> reqData) throws Exception { return this.refund(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs()); } /** * 作用:申请退款<br> * 场景:刷卡支付、公共号支付、扫码支付、APP支付<br> * 其他:需要证书 * @param reqData 向wxpay post的请求数据 * @param connectTimeoutMs 连接超时时间,单位是毫秒 * @param readTimeoutMs 读超时时间,单位是毫秒 * @return API返回数据 * @throws Exception */ public Map<String, String> refund(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception { String url; if (this.useSandbox) { url = WXPayConstants.SANDBOX_REFUND_URL_SUFFIX; } else { url = WXPayConstants.REFUND_URL_SUFFIX; } String respXml = this.requestWithCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs); return this.processResponseXml(respXml); } /** * 作用:退款查询<br> * 场景:刷卡支付、公共号支付、扫码支付、APP支付 * @param reqData 向wxpay post的请求数据 * @return API返回数据 * @throws Exception */ public Map<String, String> refundQuery(Map<String, String> reqData) throws Exception { return this.refundQuery(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs()); } /** * 作用:退款查询<br> * 场景:刷卡支付、公共号支付、扫码支付、APP支付 * @param reqData 向wxpay post的请求数据 * @param connectTimeoutMs 连接超时时间,单位是毫秒 * @param readTimeoutMs 读超时时间,单位是毫秒 * @return API返回数据 * @throws Exception */ public Map<String, String> refundQuery(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception { String url; if (this.useSandbox) { url = WXPayConstants.SANDBOX_REFUNDQUERY_URL_SUFFIX; } else { url = WXPayConstants.REFUNDQUERY_URL_SUFFIX; } String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs); return this.processResponseXml(respXml); } /** * 作用:对账单下载(成功时返回对账单数据,失败时返回XML格式数据)<br> * 场景:刷卡支付、公共号支付、扫码支付、APP支付 * @param reqData 向wxpay post的请求数据 * @return API返回数据 * @throws Exception */ public Map<String, String> downloadBill(Map<String, String> reqData) throws Exception { return this.downloadBill(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs()); } /** * 作用:对账单下载<br> * 场景:刷卡支付、公共号支付、扫码支付、APP支付<br> * 其他:无论是否成功都返回Map。若成功,返回的Map中含有return_code、return_msg、data, * 其中return_code为`SUCCESS`,data为对账单数据。 * @param reqData 向wxpay post的请求数据 * @param connectTimeoutMs 连接超时时间,单位是毫秒 * @param readTimeoutMs 读超时时间,单位是毫秒 * @return 经过封装的API返回数据 * @throws Exception */ public Map<String, String> downloadBill(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception { String url; if (this.useSandbox) { url = WXPayConstants.SANDBOX_DOWNLOADBILL_URL_SUFFIX; } else { url = WXPayConstants.DOWNLOADBILL_URL_SUFFIX; } String respStr = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs).trim(); Map<String, String> ret; // 出现错误,返回XML数据 if (respStr.indexOf("<") == 0) { ret = WXPayUtil.xmlToMap(respStr); } else { // 正常返回csv数据 ret = new HashMap<String, String>(); ret.put("return_code", WXPayConstants.SUCCESS); ret.put("return_msg", "ok"); ret.put("data", respStr); } return ret; } /** * 作用:交易保障<br> * 场景:刷卡支付、公共号支付、扫码支付、APP支付 * @param reqData 向wxpay post的请求数据 * @return API返回数据 * @throws Exception */ public Map<String, String> report(Map<String, String> reqData) throws Exception { return this.report(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs()); } /** * 作用:交易保障<br> * 场景:刷卡支付、公共号支付、扫码支付、APP支付 * @param reqData 向wxpay post的请求数据 * @param connectTimeoutMs 连接超时时间,单位是毫秒 * @param readTimeoutMs 读超时时间,单位是毫秒 * @return API返回数据 * @throws Exception */ public Map<String, String> report(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception { String url; if (this.useSandbox) { url = WXPayConstants.SANDBOX_REPORT_URL_SUFFIX; } else { url = WXPayConstants.REPORT_URL_SUFFIX; } String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs); return WXPayUtil.xmlToMap(respXml); } /** * 作用:转换短链接<br> * 场景:刷卡支付、扫码支付 * @param reqData 向wxpay post的请求数据 * @return API返回数据 * @throws Exception */ public Map<String, String> shortUrl(Map<String, String> reqData) throws Exception { return this.shortUrl(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs()); } /** * 作用:转换短链接<br> * 场景:刷卡支付、扫码支付 * @param reqData 向wxpay post的请求数据 * @return API返回数据 * @throws Exception */ public Map<String, String> shortUrl(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception { String url; if (this.useSandbox) { url = WXPayConstants.SANDBOX_SHORTURL_URL_SUFFIX; } else { url = WXPayConstants.SHORTURL_URL_SUFFIX; } String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs); return this.processResponseXml(respXml); } /** * 作用:授权码查询OPENID接口<br> * 场景:刷卡支付 * @param reqData 向wxpay post的请求数据 * @return API返回数据 * @throws Exception */ public Map<String, String> authCodeToOpenid(Map<String, String> reqData) throws Exception { return this.authCodeToOpenid(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs()); } /** * 作用:授权码查询OPENID接口<br> * 场景:刷卡支付 * @param reqData 向wxpay post的请求数据 * @param connectTimeoutMs 连接超时时间,单位是毫秒 * @param readTimeoutMs 读超时时间,单位是毫秒 * @return API返回数据 * @throws Exception */ public Map<String, String> authCodeToOpenid(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception { String url; if (this.useSandbox) { url = WXPayConstants.SANDBOX_AUTHCODETOOPENID_URL_SUFFIX; } else { url = WXPayConstants.AUTHCODETOOPENID_URL_SUFFIX; } String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs); return this.processResponseXml(respXml); }} // end class
下载java微信支付源码(WxPayAPI_JAVA_v3)用户还喜欢
- 18480 文章数
- 500万+ 热度
作者专栏
编辑推荐
- 淡抹u2引擎,修复内容较多,物有所值
- 界域传说·经典巨作=传世单机(一键安装)
- 丸子版本(175个传世版本大集合)
- GS版本:神话公益服务端+客户端
- 图片放大工具(放大图片不模糊)
- 剪映无限制VIP版
- 传奇世界客户端下载器,史上最全传世客户端
- 传世GS20220920商业引擎注册+登录配置器 解压密码是1
- U2官方排行榜游戏网关 支持元神,支持传家宝
- GS开战传世客户端+服务端
- (淡漠夕阳)u2引擎合区工具
- 传世GS引擎消除“你的游戏客户端版本号过旧,请及时更新”提示
- 传世一机多区双线路配置器--免密码版本
- 传世凤凰登陆器劫持修复软件
- SQLite3 for Navicat
- 传奇世界npc对话框编辑工具
- 传世GS落霞铭文服务器端
- gs_20210409引擎包+注册机(无限制)
- 传奇世界NPC对话封包查看器[支持时长版和极速版]
- 彩虹引擎传世脚本编辑工具1.7版来了,支持函数脚本翻译
评论