|
@@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSONObject;
|
|
|
import com.zanxiang.common.constant.Constants;
|
|
|
import com.zanxiang.common.domain.ResultMap;
|
|
|
import com.zanxiang.common.enums.ResEnum;
|
|
|
+import com.zanxiang.common.exception.BaseException;
|
|
|
import com.zanxiang.common.utils.JsonUtil;
|
|
|
import com.zanxiang.common.utils.RandomStringUtil;
|
|
|
import com.zanxiang.common.utils.URIUtil;
|
|
@@ -13,13 +14,13 @@ import com.zanxiang.sdk.domain.bo.ProductPayParamBO;
|
|
|
import com.zanxiang.sdk.domain.bo.WxPayConfigBO;
|
|
|
import com.zanxiang.sdk.service.OrderPayService;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.logging.log4j.util.Strings;
|
|
|
import org.jdom.JDOMException;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
import org.springframework.stereotype.Component;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.web.client.RestTemplate;
|
|
|
-import weixin.popular.api.SnsAPI;
|
|
|
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
|
import javax.servlet.http.HttpServletResponse;
|
|
@@ -40,7 +41,7 @@ import java.util.TreeMap;
|
|
|
public class WxPayServiceImpl extends PayService implements OrderPayService {
|
|
|
|
|
|
/**
|
|
|
- * 二维码支付
|
|
|
+ * pc二维码支付
|
|
|
*/
|
|
|
private static final String WX_PAY_NATIVE = "NATIVE";
|
|
|
|
|
@@ -50,7 +51,7 @@ public class WxPayServiceImpl extends PayService implements OrderPayService {
|
|
|
private static final String WX_PAY_MWEB = "MWEB";
|
|
|
|
|
|
/**
|
|
|
- * JSAPI支付
|
|
|
+ * 小程序支付
|
|
|
*/
|
|
|
private static final String WX_PAY_JSAPI = "JSAPI";
|
|
|
|
|
@@ -100,26 +101,19 @@ public class WxPayServiceImpl extends PayService implements OrderPayService {
|
|
|
switch (product.getPayDevice()) {
|
|
|
case 1:
|
|
|
//PC端二维码
|
|
|
- return this.mobile(product, WX_PAY_NATIVE);
|
|
|
+ return this.pcPay(product);
|
|
|
case 2:
|
|
|
//手机端H5
|
|
|
- return this.mobile(product, WX_PAY_MWEB);
|
|
|
+ return this.h5Pay(product);
|
|
|
// case 3:
|
|
|
case 4:
|
|
|
- //微信商城小程序支付
|
|
|
+ //小程序支付
|
|
|
return this.miniAppPay(product);
|
|
|
default:
|
|
|
throw new RuntimeException("未知支付方式");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 异步回调
|
|
|
- *
|
|
|
- * @param request
|
|
|
- * @param response
|
|
|
- * @return
|
|
|
- */
|
|
|
@Override
|
|
|
public String notify(HttpServletRequest request, HttpServletResponse response) throws IOException, JDOMException {
|
|
|
// 读取参数
|
|
@@ -179,94 +173,87 @@ public class WxPayServiceImpl extends PayService implements OrderPayService {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 同步回调
|
|
|
- *
|
|
|
- * @param request
|
|
|
- * @return
|
|
|
- */
|
|
|
-
|
|
|
@Override
|
|
|
public ResultMap synNotify(HttpServletRequest request) {
|
|
|
- log.info("微信支付无同步回调 request:{}", request);
|
|
|
+ log.info("微信支付没有同步回调 request:{}", request);
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
+ private ResultMap pcPay(ProductPayParamBO product) {
|
|
|
+ //下单
|
|
|
+ Map<String, String> successMap = this.unifiedOrder(product, WX_PAY_NATIVE, null);
|
|
|
+ //获取二维码链接
|
|
|
+ String urlCode = successMap.get("code_url");
|
|
|
+ //判断是否获取到支付链接
|
|
|
+ if (Strings.isBlank(urlCode)) {
|
|
|
+ log.error("微信PC支付链接urlCode为空, product:{}", JsonUtil.toString(product));
|
|
|
+ throw new BaseException("微信PC支付链接urlCode为空");
|
|
|
+ }
|
|
|
+ //返回结果
|
|
|
+ return ResultMap.ok(product.getPayDevice(), urlCode);
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
- * 小程序(需要HTTPS)
|
|
|
- *
|
|
|
- * @param product
|
|
|
- * @return
|
|
|
- */
|
|
|
- private ResultMap mp(ProductPayParamBO product) {
|
|
|
- //redirect_uri 需要在微信支付端添加认证网址
|
|
|
- totalFee = WxPayUtil.subZeroAndDot(this.totalFee);
|
|
|
- String redirectUri = serverUrl + "/api/sdk/weixinMobile/dopay?outTradeNo=" + product.getOutTradeNo() + "&totalFee=" + totalFee;
|
|
|
- //也可以通过state传递参数 redirect_uri 后面加参数未经过验证
|
|
|
- String result = SnsAPI.connectOauth2Authorize(config.getAppId(), redirectUri, true, null);
|
|
|
- return ResultMap.ok(result);
|
|
|
+ private ResultMap h5Pay(ProductPayParamBO product) {
|
|
|
+ //下单
|
|
|
+ Map<String, String> successMap = this.unifiedOrder(product, WX_PAY_MWEB, null);
|
|
|
+ //获取h5支付链接
|
|
|
+ String urlCode = successMap.get("mweb_url");
|
|
|
+ if (Strings.isBlank(urlCode)) {
|
|
|
+ log.error("微信h5支付链接urlCode为空, product:{}", JsonUtil.toString(product));
|
|
|
+ throw new BaseException("微信h5支付链接urlCode为空");
|
|
|
+ }
|
|
|
+ //转换deepLink
|
|
|
+ String deepLink;
|
|
|
+ try {
|
|
|
+ deepLink = wxPayUtil.readUrl(urlCode, serverUrl);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("微信h5支付urlCode转deepLink异常, urlCode:{}", urlCode);
|
|
|
+ throw new BaseException("微信h5支付urlCode转deepLink异常");
|
|
|
+ }
|
|
|
+ //返回结果
|
|
|
+ return ResultMap.ok(product.getPayDevice(), deepLink);
|
|
|
}
|
|
|
|
|
|
private ResultMap miniAppPay(ProductPayParamBO product) {
|
|
|
try {
|
|
|
Map<Object, Object> miniMap = new HashMap<>(5);
|
|
|
- // 请求微信服务器,使用code获取openid和session_key
|
|
|
+ // 请求微信服务器,使用code获取openid
|
|
|
Map<String, String> paramMap = new HashMap<>(4);
|
|
|
paramMap.put("appid", config.getAppId());
|
|
|
paramMap.put("secret", config.getAppSecret());
|
|
|
paramMap.put("js_code", product.getCode());
|
|
|
paramMap.put("grant_type", "authorization_code");
|
|
|
// 发送请求
|
|
|
- String url = URIUtil.fillUrlParams("https://api.weixin.qq.com/sns/jscode2session", paramMap, false);
|
|
|
+ String url = URIUtil.fillUrlParams("https://api.weixin.qq.com/sns/jscode2session", paramMap, Boolean.FALSE);
|
|
|
String sr = restTemplate.getForObject(url, String.class);
|
|
|
// 解析相应内容(转换成json对象)
|
|
|
Map<String, String> userMap = JsonUtil.toMap(sr, Map.class, String.class);
|
|
|
- // 小程序调取微信支付需要的随机字符串
|
|
|
- String nonceStr = WxPayUtil.generateNonceStr();
|
|
|
- //参数
|
|
|
- Map<String, String> paramData = new HashMap<>(13);
|
|
|
- paramData.put("appid", config.getAppId());
|
|
|
- paramData.put("mch_id", config.getMchId());
|
|
|
- paramData.put("nonce_str", WxPayUtil.generateNonceStr());
|
|
|
- paramData.put("openid", userMap == null ? null : userMap.get("openid"));
|
|
|
- paramData.put("body", body);
|
|
|
- paramData.put("out_trade_no", outTradeNo);
|
|
|
- paramData.put("fee_type", "CNY");
|
|
|
- paramData.put("total_fee", totalFee);
|
|
|
- paramData.put("spbill_create_ip", product.getSpbillCreateIp());
|
|
|
- paramData.put("notify_url", notifyUrl);
|
|
|
- paramData.put("trade_type", WX_PAY_JSAPI);
|
|
|
- paramData.put("sign_type", config.getSignType());
|
|
|
- paramData.put("sign", WxPayUtil.generateSignature(paramData, config.getApiKey()));
|
|
|
- // 下单, 获取结果
|
|
|
- String result = restTemplate.postForObject(WxPayUrl.UNIFIED_ORDER_URL, XMLUtil.mapToXml(paramData), String.class);
|
|
|
- // 时间戳
|
|
|
- long timeStamp = System.currentTimeMillis() / 1000;
|
|
|
- // 这边要将返回的时间戳转化成字符串,不然小程序端调用wx.requestPayment方法会报签名错误
|
|
|
- String createTime = String.valueOf(timeStamp);
|
|
|
- Map<String, String> successMap = XMLUtil.xmlToMap(result);
|
|
|
- // 返回状态码
|
|
|
- String returnCode = successMap.get("return_code");
|
|
|
- // 结果状态码
|
|
|
- String resultCode = successMap.get("result_code");
|
|
|
- String prepayId = successMap.get("prepay_id");
|
|
|
- // 获取统一下单回调结果;判断并二次签名
|
|
|
- if (Constants.SUCCESS.equalsIgnoreCase(returnCode) && returnCode.equals(resultCode)) {
|
|
|
- HashMap<String, String> map = new HashMap<>();
|
|
|
- map.put("appId", config.getAppId());
|
|
|
- map.put("timeStamp", createTime);
|
|
|
- map.put("nonceStr", nonceStr);
|
|
|
- map.put("package", "prepay_id=" + prepayId);
|
|
|
- map.put("signType", config.getSignType());
|
|
|
- // 再次签名sign,这个签名用于小程序端调用支付方法
|
|
|
- String sign = WxPayUtil.generateSignature(map, config.getApiKey());
|
|
|
- miniMap.put("paySign", sign);
|
|
|
+ if (userMap == null || Strings.isBlank(userMap.get("openid"))) {
|
|
|
+ throw new BaseException("小程序支付获取用户openId为空");
|
|
|
}
|
|
|
+ //下单
|
|
|
+ Map<String, String> successMap = this.unifiedOrder(product, WX_PAY_JSAPI, userMap.get("openid"));
|
|
|
+ // 支付参数
|
|
|
+ String prepayId = successMap.get("prepay_id");
|
|
|
+ // 随机字符串
|
|
|
+ String nonceStr = WxPayUtil.generateNonceStr();
|
|
|
+ // 时间戳转化成字符串,否则前端wx.requestPayment方法会报签名错误
|
|
|
+ String createTime = String.valueOf(System.currentTimeMillis() / 1000);
|
|
|
+ // 获取统一下单回调结果, 二次签名
|
|
|
+ HashMap<String, String> map = new HashMap<>(5);
|
|
|
+ map.put("appId", config.getAppId());
|
|
|
+ map.put("timeStamp", createTime);
|
|
|
+ map.put("nonceStr", nonceStr);
|
|
|
+ map.put("package", "prepay_id=" + prepayId);
|
|
|
+ map.put("signType", config.getSignType());
|
|
|
+ // 再次签名sign,这个签名用于小程序端调用支付方法
|
|
|
+ String sign = WxPayUtil.generateSignature(map, config.getApiKey());
|
|
|
+ //组装返回结果
|
|
|
+ miniMap.put("paySign", sign);
|
|
|
miniMap.put("signType", config.getSignType());
|
|
|
miniMap.put("timeStamp", createTime);
|
|
|
miniMap.put("nonceStr", nonceStr);
|
|
|
- miniMap.put("package", XMLUtil.xmlToMap(result));
|
|
|
+ miniMap.put("package", successMap);
|
|
|
return ResultMap.ok(miniMap);
|
|
|
} catch (Exception e) {
|
|
|
log.error("微信小程序支付通信异常, 订单号:{}, e : {}", product.getOutTradeNo(), e.getMessage());
|
|
@@ -340,6 +327,48 @@ public class WxPayServiceImpl extends PayService implements OrderPayService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private Map<String, String> unifiedOrder(ProductPayParamBO product, String tradeType, String openId) {
|
|
|
+ try {
|
|
|
+ Map<String, String> paramData = new HashMap<>(13);
|
|
|
+ paramData.put("appid", config.getAppId());
|
|
|
+ paramData.put("mch_id", config.getMchId());
|
|
|
+ paramData.put("nonce_str", WxPayUtil.generateNonceStr());
|
|
|
+ paramData.put("body", body);
|
|
|
+ paramData.put("out_trade_no", outTradeNo);
|
|
|
+ paramData.put("fee_type", "CNY");
|
|
|
+ paramData.put("total_fee", WxPayUtil.subZeroAndDot(this.totalFee));
|
|
|
+ paramData.put("spbill_create_ip", product.getSpbillCreateIp());
|
|
|
+ paramData.put("notify_url", notifyUrl);
|
|
|
+ paramData.put("trade_type", tradeType);
|
|
|
+ paramData.put("attach", JsonUtil.toString(this.attach));
|
|
|
+ paramData.put("sign_type", config.getSignType());
|
|
|
+ //小程序商城下单需要携带openId
|
|
|
+ if (Strings.isNotBlank(openId)) {
|
|
|
+ paramData.put("openid", openId);
|
|
|
+ }
|
|
|
+ //接口签名
|
|
|
+ String sign = WxPayUtil.generateSignature(paramData, config.getApiKey());
|
|
|
+ paramData.put("sign", sign);
|
|
|
+ // 下单, 获取结果
|
|
|
+ String result = restTemplate.postForObject(WxPayUrl.UNIFIED_ORDER_URL, XMLUtil.mapToXml(paramData), String.class);
|
|
|
+ Map<String, String> successMap = XMLUtil.xmlToMap(result);
|
|
|
+ // 返回状态码
|
|
|
+ String returnCode = successMap.get("return_code");
|
|
|
+ // 结果状态码
|
|
|
+ String resultCode = successMap.get("result_code");
|
|
|
+ //成功, 返回结果
|
|
|
+ if (Constants.SUCCESS.equalsIgnoreCase(returnCode) && returnCode.equals(resultCode)) {
|
|
|
+ return successMap;
|
|
|
+ }
|
|
|
+ //下单失败
|
|
|
+ log.error("微信小程序支付通信异常, paramData:{}", JsonUtil.toString(paramData));
|
|
|
+ throw new BaseException("微信小程序支付下单失败");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("微信小程序支付通信异常, 订单号:{}, e : {}", product.getOutTradeNo(), e.getMessage());
|
|
|
+ throw new BaseException("微信小程序支付下单异常");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 配置初始化
|
|
|
*/
|