123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- package com.webchat.pay.service;
- import com.webchat.common.bean.APIResponseBean;
- import com.webchat.common.enums.CommonStatusEnum;
- import com.webchat.common.enums.RedisKeyEnum;
- import com.webchat.common.service.RedisService;
- import com.webchat.common.service.SnowflakeIdGeneratorService;
- import com.webchat.common.util.AkSkGenerator;
- import com.webchat.common.util.IDGenerateUtil;
- import com.webchat.common.util.JsonUtil;
- import com.webchat.common.util.SignUtil;
- import com.webchat.domain.dto.payment.PaymentOrderCreateDTO;
- import com.webchat.domain.dto.payment.TransIdDTO;
- import com.webchat.domain.vo.dto.payment.AppAkSkDTO;
- import com.webchat.domain.vo.response.payment.AppBaseResponseVO;
- import com.webchat.pay.config.constants.PaymentConstant;
- import com.webchat.pay.config.exception.PaymentAccessAuthException;
- import com.webchat.pay.repository.entity.PaymentOrderEntity;
- import org.apache.commons.lang3.ObjectUtils;
- import org.apache.commons.lang3.StringUtils;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
- import org.springframework.util.Assert;
- @Service
- public class PaymentApiService {
- @Autowired
- private PaymentAppService appService;
- @Autowired
- private RedisService redisService;
- @Autowired
- private SnowflakeIdGeneratorService snowflakeIdGeneratorService;
- /**
- * 交易凭证获取签名的有效期:5分钟
- */
- private static final Long SIGNATURE_EXPIRE_TIME = 5 * 60 * 1000L;
- /**
- * 生成交易凭证
- *
- * @param appId
- * @param accessKey
- * @param timestamp
- * @param signature
- * @param logId
- * @return
- */
- public String token(Long appId,
- String accessKey,
- String secretKey,
- Long timestamp,
- String signature,
- String logId) {
- /**
- * 这里三重校验
- * ------------------------
- * 1. 校验访问凭证
- * 2. 校验校验有效
- * 3. 校验签名
- */
- // 1. 校验访问凭证
- AppBaseResponseVO app = appService.appInfo(appId);
- if (app == null || !CommonStatusEnum.PUBLISHED.getStatusCode().equals(app.getStatus())) {
- throw new PaymentAccessAuthException("应用不存在/未发布");
- }
- AppAkSkDTO akSkDTO = appService.getAppAkSk(appId);
- if (akSkDTO == null) {
- throw new PaymentAccessAuthException("应用不存在");
- }
- boolean checkAk = ObjectUtils.equals(akSkDTO.getAccessKey(), accessKey);
- boolean checkSk = AkSkGenerator.checkKey(secretKey, akSkDTO.getSecretHashKey());
- if (!(checkAk && checkSk)) {
- throw new PaymentAccessAuthException("应用访问凭证校验失败");
- }
- // 2. 校验校验有效
- Long diffTime = System.currentTimeMillis() - timestamp;
- if (diffTime > SIGNATURE_EXPIRE_TIME) {
- throw new PaymentAccessAuthException("签名已过期");
- }
- // 3. 校验签名
- String validSignature = SignUtil.generateSignature(secretKey,
- String.valueOf(appId),
- accessKey,
- String.valueOf(timestamp));
- if (!ObjectUtils.equals(signature, validSignature)) {
- throw new PaymentAccessAuthException("签名校验失败");
- }
- /**
- * 生成并返回接入方access-token(交易凭证,用于后续的交易校验)
- */
- return this.generateAccessToken(appId);
- }
- /**
- * 校验支付交易接口请求token的有效性
- *
- * @param accessToken
- * @return
- */
- public boolean validateAccessToken(String accessToken) {
- /**
- * token 由支付平台生成且未失效 ===> accToken是有效的
- */
- String cacheKey = this.accessTokenCacheKey(accessToken);
- return redisService.exists(cacheKey);
- }
- /**
- * 生成交易事务id
- *
- * @return
- */
- public String transId(String accessToken) {
- Long appId = this.getAppIdFromAccessToken(accessToken);
- Assert.notNull(appId, "交易凭证失效");
- AppBaseResponseVO appBaseResponse = appService.appInfo(appId);
- Assert.notNull(appBaseResponse, "应用不存在,联系客服");
- Assert.isTrue(CommonStatusEnum.PUBLISHED.getStatusCode().equals(appBaseResponse.getStatus()), "应用未发布");
- /**
- * 基于雪花算法生成分布式下唯一id,作为交易分布式事务id,用于后续异常交易快速回滚
- */
- String transId = snowflakeIdGeneratorService.generateId();
- /**
- * 初始化TransId缓存,用于后续事务类操作校验
- */
- this.setPaymentTransIdCache(transId, appId, PaymentConstant.TrasnsIdStatusEnum.INIT);
- return transId;
- }
- /**
- * 走缓存获取分布式事务id详情
- *
- * @param transId
- * @return
- */
- public TransIdDTO getTransIdDTO(String transId) {
- String cacheKey = this.transIdCacheKey(transId);
- String cache = redisService.get(cacheKey);
- if (StringUtils.isBlank(cache)) {
- return null;
- }
- return JsonUtil.fromJson(cache, TransIdDTO.class);
- }
- /**
- * 刷新事务id状态
- *
- * @param transId
- * @param status
- */
- public void setPaymentTransIdCache(String transId, PaymentConstant.TrasnsIdStatusEnum status) {
- TransIdDTO transIdDTO = this.getTransIdDTO(transId);
- Assert.notNull(transIdDTO, "transId is null!");
- this.setPaymentTransIdCache(transId, transIdDTO.getAppId(), status);
- }
- /**
- * 分布式交易事务id添加orderId信息
- *
- * @param transId
- * @param orderId
- */
- public void setPaymentTransOrderInfo(String transId, String orderId) {
- TransIdDTO transIdDTO = this.getTransIdDTO(transId);
- Assert.notNull(transIdDTO, "transId is null!");
- transIdDTO.setOrderId(orderId);
- String cacheKey = this.transIdCacheKey(transId);
- redisService.set(cacheKey, JsonUtil.toJsonString(transIdDTO), RedisKeyEnum.PAYMENT_TRANS_ID_CACHE.getExpireTime());
- }
- /**
- * 刷新分布式交易事务id缓存
- *
- * @param transId 事务id
- * @param appId 应用id
- * @param status 状态
- */
- private void setPaymentTransIdCache(String transId, Long appId, PaymentConstant.TrasnsIdStatusEnum status) {
- String cacheKey = this.transIdCacheKey(transId);
- TransIdDTO transIdDTO = TransIdDTO.builder()
- .transId(transId)
- .appId(appId)
- .status(status.getStatus())
- .build();
- redisService.set(cacheKey, JsonUtil.toJsonString(transIdDTO), RedisKeyEnum.PAYMENT_TRANS_ID_CACHE.getExpireTime());
- }
- private String transIdCacheKey(String transId) {
- return RedisKeyEnum.PAYMENT_TRANS_ID_CACHE.getKey(transId);
- }
- /**
- * 获取交易凭证背后的应用信息
- *
- * @param accessToken
- * @return
- */
- public Long getAppIdFromAccessToken(String accessToken) {
- String accessTokenCacheKey = this.accessTokenCacheKey(accessToken);
- String cache = redisService.get(accessTokenCacheKey);
- if (StringUtils.isNotBlank(cache)) {
- return Long.valueOf(cache);
- }
- return null;
- }
- private String generateAccessToken(Long appId) {
- /**
- * 生成交易凭证
- */
- String accessToken = IDGenerateUtil.uuid();
- String accessTokenCacheKey = this.accessTokenCacheKey(accessToken);
- redisService.set(accessTokenCacheKey, String.valueOf(appId),
- RedisKeyEnum.PAYMENT_APP_ACCESS_TOKEN_CACHE.getExpireTime());
- return accessToken;
- }
- private String accessTokenCacheKey(String accessToken) {
- return RedisKeyEnum.PAYMENT_APP_ACCESS_TOKEN_CACHE.getKey(accessToken);
- }
- }
|