|
@@ -0,0 +1,268 @@
|
|
|
|
+package com.zanxiang.sdk.service.Impl.pay;
|
|
|
|
+
|
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
|
+import com.zanxiang.common.domain.MiPayConfig;
|
|
|
|
+import com.zanxiang.common.domain.ResultMap;
|
|
|
|
+import com.zanxiang.common.enums.HttpStatusEnum;
|
|
|
|
+import com.zanxiang.common.enums.StatusEnum;
|
|
|
|
+import com.zanxiang.common.exception.BaseException;
|
|
|
|
+import com.zanxiang.common.exception.CustomException;
|
|
|
|
+import com.zanxiang.common.utils.JsonUtil;
|
|
|
|
+import com.zanxiang.common.utils.StringUtils;
|
|
|
|
+import com.zanxiang.common.utils.URIUtil;
|
|
|
|
+import com.zanxiang.mybatis.entity.GamePayWay;
|
|
|
|
+import com.zanxiang.sdk.common.constant.MiPayConstants;
|
|
|
|
+import com.zanxiang.sdk.common.util.MiPayUtil;
|
|
|
|
+import com.zanxiang.sdk.common.util.RedisUtil;
|
|
|
|
+import com.zanxiang.sdk.domain.bo.ProductPayParamBO;
|
|
|
|
+import com.zanxiang.sdk.domain.dto.PlatformOrderDTO;
|
|
|
|
+import com.zanxiang.sdk.service.GamePayWayService;
|
|
|
|
+import com.zanxiang.sdk.service.OrderPayService;
|
|
|
|
+import com.zanxiang.sdk.service.PlatformOrderService;
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
+import org.apache.logging.log4j.util.Strings;
|
|
|
|
+import org.jdom.JDOMException;
|
|
|
|
+import org.slf4j.Logger;
|
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
|
+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 javax.servlet.http.HttpServletRequest;
|
|
|
|
+import javax.servlet.http.HttpServletResponse;
|
|
|
|
+import java.io.IOException;
|
|
|
|
+import java.util.HashMap;
|
|
|
|
+import java.util.Map;
|
|
|
|
+import java.util.Objects;
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * 米大师服务实现
|
|
|
|
+ *
|
|
|
|
+ * @author xufeng
|
|
|
|
+ * @date 2022/7/14 16:33
|
|
|
|
+ */
|
|
|
|
+@Component
|
|
|
|
+@Slf4j
|
|
|
|
+@Service("MiPayService")
|
|
|
|
+public class MiPayServiceImpl extends PayService implements OrderPayService {
|
|
|
|
+ protected final Logger logger = LoggerFactory.getLogger(MiPayServiceImpl.class);
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private RedisUtil<String> redisUtil;
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private PlatformOrderService platformOrderService;
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private GamePayWayService gamePayWayService;
|
|
|
|
+
|
|
|
|
+ @Value("${spring.profiles.active}")
|
|
|
|
+ private String springActive;
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private RestTemplate restTemplate;
|
|
|
|
+
|
|
|
|
+ private String code;
|
|
|
|
+
|
|
|
|
+ private int isSand = 1;
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public ResultMap create(ProductPayParamBO product) {
|
|
|
|
+ this.code = product.getCode();
|
|
|
|
+ //查询订单
|
|
|
|
+ PlatformOrderDTO orderInfo = platformOrderService.info(product.getOutTradeNo());
|
|
|
|
+ if (Objects.isNull(orderInfo)) {
|
|
|
|
+ throw new CustomException(HttpStatusEnum.ORDER_NO_FIND);
|
|
|
|
+ }
|
|
|
|
+ if (orderInfo.getGameId() == null || orderInfo.getGameId() == 0) {
|
|
|
|
+ throw new CustomException(HttpStatusEnum.ORDER_GAME_ID_IS_NULL);
|
|
|
|
+ }
|
|
|
|
+ this.isSand = Objects.equals(this.springActive, "dev") ? 1 : 2;
|
|
|
|
+ //查询游戏配置
|
|
|
|
+ GamePayWay one = gamePayWayService.getOne(new LambdaQueryWrapper<GamePayWay>()
|
|
|
|
+ .eq(GamePayWay::getGameId, orderInfo.getGameId())
|
|
|
|
+ .eq(GamePayWay::getStatus, StatusEnum.YES.getCode())
|
|
|
|
+ .gt(GamePayWay::getPayBoxId, 0)
|
|
|
|
+ .last("limit 1")
|
|
|
|
+ );
|
|
|
|
+ if (Objects.isNull(one) || StringUtils.isEmpty(one.getPayConfig())) {
|
|
|
|
+ throw new CustomException(HttpStatusEnum.GET_CONFIG_ERROR);
|
|
|
|
+ }
|
|
|
|
+ //米大师支付配置
|
|
|
|
+ MiPayConfig miPayConfig = JSONObject.parseObject(one.getPayConfig(), MiPayConfig.class);
|
|
|
|
+ String openId = this.getOpenId(code, miPayConfig.getWxAppId());
|
|
|
|
+ //获取米大师钱包余额
|
|
|
|
+ Long balance = this.midasGetBalance(openId, miPayConfig);
|
|
|
|
+ //返回参数
|
|
|
|
+ Map<String, String> result = new HashMap<>();
|
|
|
|
+ //余额充足, 直接扣除
|
|
|
|
+ if (balance >= orderInfo.getAmount().longValue()) {
|
|
|
|
+ String billNo = this.midasPay(openId, miPayConfig, orderInfo);
|
|
|
|
+ result.put("status", "0");
|
|
|
|
+ result.put("billNo", billNo);
|
|
|
|
+ }
|
|
|
|
+ //余额不足, 返回订单id, 前端调充值接口, 充值完后再继续支付
|
|
|
|
+ if (balance < orderInfo.getAmount().longValue()) {
|
|
|
|
+ result.put("status", "1");
|
|
|
|
+ result.put("need", orderInfo.getAmount().toString());
|
|
|
|
+ result.put("balance", String.valueOf(balance));
|
|
|
|
+ }
|
|
|
|
+ return ResultMap.ok(result);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 查询余额
|
|
|
|
+ */
|
|
|
|
+ private Long midasGetBalance(String openId, MiPayConfig miPayConfig) {
|
|
|
|
+ Map<String, String> paramMap = new HashMap<>();
|
|
|
|
+ paramMap.put("openid", openId);
|
|
|
|
+ paramMap.put("appid", miPayConfig.getWxAppId());
|
|
|
|
+ paramMap.put("offer_id", miPayConfig.getAppId());
|
|
|
|
+ paramMap.put("ts", String.valueOf(System.currentTimeMillis() / 1000));
|
|
|
|
+ paramMap.put("zone_id", "1");
|
|
|
|
+ paramMap.put("pf", "android");
|
|
|
|
+ String urlPath = this.isSand == 1 ? "/cgi-bin/midas/sandbox/getbalance" : "/cgi-bin/midas/getbalance";
|
|
|
|
+ String sig = MiPayUtil.miPaySin("POST", urlPath, paramMap, miPayConfig.getAppKey());
|
|
|
|
+ paramMap.put("sig", sig);
|
|
|
|
+ Map<String, String> result = this.miPayApi(MiPayConstants.BALANCE_URL, miPayConfig, paramMap);
|
|
|
|
+ return Long.valueOf(result.get("balance"));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 虚拟币扣除
|
|
|
|
+ *
|
|
|
|
+ * @return
|
|
|
|
+ */
|
|
|
|
+ private String midasPay(String openId, MiPayConfig miPayConfig, PlatformOrderDTO orderInfo) {
|
|
|
|
+ Map<String, String> paramMap = new HashMap<>();
|
|
|
|
+ paramMap.put("appid", miPayConfig.getWxAppId());
|
|
|
|
+ paramMap.put("openid", openId);
|
|
|
|
+ paramMap.put("offer_id", miPayConfig.getAppId());
|
|
|
|
+ paramMap.put("ts", String.valueOf(System.currentTimeMillis() / 1000));
|
|
|
|
+ paramMap.put("pf", "android");
|
|
|
|
+ paramMap.put("zone_id", "1");
|
|
|
|
+ paramMap.put("amt", orderInfo.getAmount().toString());
|
|
|
|
+ paramMap.put("bill_no", orderInfo.getId());
|
|
|
|
+ String urlPath = this.isSand == 1 ? "/cgi-bin/midas/sandbox/getbalance" : "/cgi-bin/midas/getbalance";
|
|
|
|
+ String sig = MiPayUtil.miPaySin("POST", urlPath, paramMap, miPayConfig.getAppKey());
|
|
|
|
+ paramMap.put("sig", sig);
|
|
|
|
+ Map<String, String> result = this.miPayApi(MiPayConstants.PAY_URL, miPayConfig, paramMap);
|
|
|
|
+ return result.get("bill_no");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取AccessToken
|
|
|
|
+ *
|
|
|
|
+ * @param appId
|
|
|
|
+ * @param appSecret
|
|
|
|
+ * @return AccessToken
|
|
|
|
+ */
|
|
|
|
+ private String getAccessToken(String appId, String appSecret) {
|
|
|
|
+// String redisKey = RedisKeyConstant.WEIXIN_ACCESS_TOKEN + appId;
|
|
|
|
+// String cache = redisUtil.getCache(redisKey);
|
|
|
|
+// if (StringUtils.isNotEmpty(cache)) {
|
|
|
|
+// return cache;
|
|
|
|
+// }
|
|
|
|
+ HashMap<String, String> param = new HashMap<>();
|
|
|
|
+ param.put("grant_type", "client_credential");
|
|
|
|
+ param.put("appid", "wxa8ede4e6c65fad30");
|
|
|
|
+ param.put("secret", "a8df1c1146f89f06d07d1e494f845ede");
|
|
|
|
+
|
|
|
|
+ String url = URIUtil.fillUrlParams(MiPayConstants.TOKEN_URL, param, Boolean.FALSE);
|
|
|
|
+
|
|
|
|
+ String result = restTemplate.getForObject(url, String.class);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ if (StringUtils.isEmpty(result)) {
|
|
|
|
+ return "";
|
|
|
|
+ }
|
|
|
|
+ JSONObject data = JSONObject.parseObject(result);
|
|
|
|
+ if (Objects.isNull(data)) {
|
|
|
|
+ return "";
|
|
|
|
+ }
|
|
|
|
+ Object accessToken = data.get("access_token");
|
|
|
|
+ if (Objects.isNull(accessToken)) {
|
|
|
|
+ return "";
|
|
|
|
+ }
|
|
|
|
+ String token = accessToken.toString();
|
|
|
|
+// redisUtil.setCache(redisKey, token, Long.valueOf(data.get("expires_in").toString()));
|
|
|
|
+ return token;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 接口请求
|
|
|
|
+ *
|
|
|
|
+ * @param url 请求路径
|
|
|
|
+ * @return
|
|
|
|
+ */
|
|
|
|
+ private Map<String, String> miPayApi(String url, MiPayConfig miPayConfig, Map<String, String> paramMap) {
|
|
|
|
+ try {
|
|
|
|
+ //使用沙箱时,沙箱地址替换
|
|
|
|
+ String appKey = miPayConfig.getAppKey();
|
|
|
|
+ if (this.isSand == 1) {
|
|
|
|
+ url = url.replace("/cgi-bin/midas/", "/cgi-bin/midas/sandbox/");
|
|
|
|
+ appKey = miPayConfig.getAppKeyDev();
|
|
|
|
+ }
|
|
|
|
+ String accessToken = getAccessToken(miPayConfig.getAppId(), appKey);
|
|
|
|
+ if (StringUtils.isEmpty(accessToken)) {
|
|
|
|
+ throw new CustomException(HttpStatusEnum.ACCESS_TOKEN_CREATE_ERROR);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ url += "?access_token=" + accessToken;
|
|
|
|
+ String result = restTemplate.postForObject(url, paramMap, String.class);
|
|
|
|
+ if (StringUtils.isEmpty(result)) {
|
|
|
|
+ throw new CustomException(HttpStatusEnum.MDS_CONNECT_ERROR);
|
|
|
|
+ }
|
|
|
|
+ Map<String, String> data = JsonUtil.toMap(result, Map.class, String.class, String.class);
|
|
|
|
+ if (Objects.isNull(data)) {
|
|
|
|
+ throw new CustomException(HttpStatusEnum.MDS_CONNECT_ERROR);
|
|
|
|
+ }
|
|
|
|
+ if (!data.get("errcode").equals("0")) {
|
|
|
|
+ String errmsg = data.get("errmsg");
|
|
|
|
+ Integer errcode = Integer.valueOf(data.get("errcode"));
|
|
|
|
+ throw new CustomException(errmsg, errcode);
|
|
|
|
+ }
|
|
|
|
+ return data;
|
|
|
|
+ } catch (RuntimeException e) {
|
|
|
|
+ throw new RuntimeException("米大师接口调用失败");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private String getOpenId(String code, String appId) {
|
|
|
|
+ // 请求微信服务器,使用code获取openid
|
|
|
|
+ Map<String, String> paramMap = new HashMap<>(4);
|
|
|
|
+ paramMap.put("appid", appId);
|
|
|
|
+ paramMap.put("secret", "a8df1c1146f89f06d07d1e494f845ede");
|
|
|
|
+ paramMap.put("js_code", code);
|
|
|
|
+ paramMap.put("grant_type", "authorization_code");
|
|
|
|
+ // 发送请求
|
|
|
|
+ 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);
|
|
|
|
+ if (userMap == null || Strings.isBlank(userMap.get("openid"))) {
|
|
|
|
+ throw new BaseException("小程序支付获取用户openId为空");
|
|
|
|
+ }
|
|
|
|
+ return userMap.get("openid");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public String notify(HttpServletRequest request, HttpServletResponse response) throws IOException, JDOMException {
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public ResultMap synNotify(HttpServletRequest request) {
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void configInit(String obj) {
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+}
|