| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273 |
- package com.ydtech.modules.admin.components;
- import com.alibaba.fastjson.JSONObject;
- import com.ydtech.config.properties.UnionPayApiConfigurationProperties;
- import com.ydtech.exception.SystemException;
- import com.ydtech.modules.admin.components.request.UnionPayRequest;
- import com.ydtech.modules.admin.components.response.UnionPayResponse;
- import com.ydtech.modules.admin.utils.UnionPayUtils;
- import com.ydtech.utils.SM2Utils;
- import com.ydtech.utils.StringUtils;
- import lombok.RequiredArgsConstructor;
- import org.redisson.api.RLock;
- import org.redisson.api.RedissonClient;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.data.redis.core.StringRedisTemplate;
- import org.springframework.http.HttpEntity;
- import org.springframework.http.HttpHeaders;
- import org.springframework.http.ResponseEntity;
- import org.springframework.stereotype.Component;
- import org.springframework.util.ObjectUtils;
- import org.springframework.web.client.RestTemplate;
- import java.net.InetAddress;
- import java.net.UnknownHostException;
- import java.security.InvalidParameterException;
- import java.text.SimpleDateFormat;
- import java.util.Base64;
- import java.util.Date;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.concurrent.TimeUnit;
- /**
- * @description: 银联Api
- **/
- @Component
- @RequiredArgsConstructor
- public class UnionPayRequestApiComponent {
- private final static Logger LOGGER = LoggerFactory.getLogger(UnionPayRequestApiComponent.class);
- private final UnionPayApiConfigurationProperties unionPayApiConfigurationProperties;
- private final RedissonClient redissonClient;
- private final StringRedisTemplate redisTemplate;
- private final RestTemplate restTemplate;
- private final static String UNION_PAY_MAP = "unionPay";
- private final static String UNION_PAY_ACCESS_TOKEN_KEY = "unionPayAccessToken";
- private final static String UNION_PAY_ACCESS_TOKEN_LOCK = "unionPayAccessTokenLock";
- private final static int UNION_PAY_LOCK_TIME = 10000;
- private final static int UNION_PAY_SLEEP_TIME = 5000;
- private final static int RANDOM_MAX = 999999999;
- private final static int RANDOM_MIN = 100000000;
- /**
- * 运营商二要素验证接口
- *
- * @param request
- */
- public UnionPayResponse twoFactorVerifyRequest(UnionPayRequest request) {
- String url = unionPayApiConfigurationProperties.getFactorVerifyUrl();
- JSONObject params = new JSONObject();
- if (StringUtils.isBlank(request.getPhoneNo())) {
- throw new InvalidParameterException("手机号不允许为空!");
- }
- params.put("phoneNo", request.getPhoneNo());
- //01:验证姓名+手机号,此时用户姓名必输 02:身份证+手机号,此时证件类型、证件
- if (StringUtils.isNotBlank(request.getCertNo())) {
- params.put("verifyType", "02");
- params.put("certType", "01");//证件类型 01:身份证
- params.put("certNo", request.getCertNo());
- } else if (StringUtils.isNotBlank(request.getName())) {
- params.put("verifyType", "01");
- params.put("name", request.getName());
- } else {
- throw new InvalidParameterException("身份证、用户姓名至少传值一个!");
- }
- return commonRequest(url, params);
- }
- /**
- * 银行卡验证接口
- *
- * @param request
- */
- public UnionPayResponse bankCardVerifyRequest(UnionPayRequest request) {
- String url = unionPayApiConfigurationProperties.getFactorVerifyUrl();
- JSONObject params = new JSONObject();
- if (StringUtils.isBlank(request.getCardNo())) {
- throw new InvalidParameterException("卡号不允许为空!");
- }
- if (StringUtils.isBlank(request.getCertNo()) && StringUtils.isBlank(request.getName()) &&
- StringUtils.isBlank(request.getPhoneNo())) {
- throw new InvalidParameterException("证件号、用户姓名、手机号至少传值一个!");
- }
- params.put("cardNo", request.getCardNo());
- params.put("personalMandate", "1");//是否取得个人授权,字段取值0或1。1:是 0:否
- params.put("sceneId", "13"); //TODO 业务场景 13:车险
- if (StringUtils.isNotBlank(request.getCertNo())) {
- params.put("certType", "01");//证件类型 01:身份证
- params.put("certNo", request.getCertNo());
- }
- if (StringUtils.isNotBlank(request.getName())) {
- params.put("name", request.getName());
- }
- if (StringUtils.isNotBlank(request.getPhoneNo())) {
- params.put("phoneNo", request.getPhoneNo());
- }
- params.put("appName", "01晋掌柜");//TODO 交易发起应用名称
- params.put("ipType", "04");//IP版本号 04:IPV4 06:IPV6
- try {
- params.put("sourceIp", InetAddress.getLocalHost().getHostAddress()); // ip
- } catch (UnknownHostException e) {
- LOGGER.error("获取ipv4地址异常", e);
- throw new RuntimeException("获取ipv4地址异常:", e);
- }
- params.put("protocolVersion", ""); // 用户授权协议版本号 //TODO
- params.put("protocolNo", ""); // 用户授权协议流水号
- return commonRequest(url, params);
- }
- /**
- * 调用银联接口
- *
- * @param url 地址
- * @param params 入参
- * @return 返参
- */
- private UnionPayResponse commonRequest(String url, JSONObject params) {
- try {
- String publicKey = unionPayApiConfigurationProperties.getPublicKey();
- JSONObject data = new JSONObject();
- String dataValue = Base64.getEncoder().encodeToString(SM2Utils.encrypt(params.toString(),
- publicKey).getBytes());
- data.put("data", dataValue);
- HttpHeaders httpHeaders = new HttpHeaders();
- httpHeaders.set("Content-Type", "application/json;charset=UTF-8");
- httpHeaders.set("Accept", "application/json");
- httpHeaders.set("Authorization", getAccessToken());
- // 请求
- HttpEntity<String> httpEntity = new HttpEntity<>(params.toString(), httpHeaders);
- ResponseEntity<String> response = restTemplate.postForEntity(url, httpEntity, String.class);
- JSONObject resp = JSONObject.parseObject(response.getBody());
- if ("20000000".equals(resp.getString("errCode"))) {
- String privateKey = unionPayApiConfigurationProperties.getPrivateKey();
- String respData = SM2Utils.decrypt(new String(Base64.getDecoder().decode(resp.getString("data"))),
- privateKey);
- return JSONObject.parseObject(respData, UnionPayResponse.class);
- } else {
- throw new SystemException("银联信息验证异常[" + resp.getString("errCode") + ":" +
- resp.getString("errInfo") + "]");
- }
- } catch (Exception e) {
- LOGGER.error("银联接口调用异常", e);
- throw new RuntimeException("银联接口调用异常:", e);
- }
- }
- /**
- * 获取银联token
- *
- * @return
- */
- private String getAccessToken() {
- Object token = redisTemplate.opsForHash().get(UNION_PAY_MAP, UNION_PAY_ACCESS_TOKEN_KEY);
- // 缓存中不存在就去请求token
- if (!ObjectUtils.isEmpty(token)) {
- return (String) token;
- }
- // 使用分布式锁 只有一个人可以访问登录
- RLock lock = redissonClient.getLock(UNION_PAY_ACCESS_TOKEN_LOCK);
- try {
- boolean isLock = lock.tryLock(1, UNION_PAY_LOCK_TIME, TimeUnit.MILLISECONDS);
- if (isLock) {
- try {
- return this.invokeToken();
- } finally {
- lock.unlock();
- }
- }
- } catch (InterruptedException e) {
- LOGGER.error("getAccessToken InterruptedException", e);
- lock.unlock();
- Thread.currentThread().interrupt();
- throw new RuntimeException("获取银联Token异常,请重试!");
- }
- // 睡眠一段时间 自调用 重新获取缓存中的 header
- try {
- Thread.sleep(UNION_PAY_SLEEP_TIME);
- } catch (InterruptedException e) {
- LOGGER.error("getAccessToken InterruptedException", e);
- Thread.currentThread().interrupt();
- throw new RuntimeException("获取银联Token异常,请重试!");
- }
- return getAccessToken();
- }
- /**
- * 调用银联接口获取token
- *
- * @return token
- */
- public String invokeToken() {
- String appId = unionPayApiConfigurationProperties.getAppId();
- String appKey = unionPayApiConfigurationProperties.getAppKey();
- String accessTokenUrl = unionPayApiConfigurationProperties.getAccessTokenUrl();
- SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
- String timestamp = sdf.format(new Date());
- String randNumber = UnionPayUtils.createData(3);
- String signature = UnionPayUtils.SHA256(appId + timestamp + randNumber + appKey);
- JSONObject params = new JSONObject();
- params.put("appId", appId);
- params.put("timestamp", timestamp);
- params.put("nonce", randNumber);
- params.put("signMethod", "SHA256");
- params.put("signature", signature);
- try {
- HttpHeaders httpHeaders = new HttpHeaders();
- httpHeaders.set("Content-Type", "application/json;charset=UTF-8");
- httpHeaders.set("Accept", "application/json");
- // 请求
- HttpEntity<JSONObject> httpEntity = new HttpEntity<>(params, httpHeaders);
- ResponseEntity<String> response = restTemplate.postForEntity(accessTokenUrl, httpEntity, String.class);
- JSONObject resp = JSONObject.parseObject(response.getBody());
- if ("0000".equals(resp.getString("errCode"))) {
- String token = resp.getString("accessToken");
- int expiresIn = resp.getIntValue("expiresIn"); //失效时间
- Map<String, String> map = new HashMap<>();
- map.put(UNION_PAY_MAP, "OPEN-ACCESS-TOKEN AccessToken=\"" + token + "\", appId=\"" + appId + "\"");
- redisTemplate.opsForHash().putAll(UNION_PAY_ACCESS_TOKEN_KEY, map);
- redisTemplate.expire(UNION_PAY_ACCESS_TOKEN_KEY, expiresIn - 30, TimeUnit.SECONDS);
- return token;
- } else {
- throw new SystemException("获取银联Token异常[" + resp.getString("errCode") + ":" +
- resp.getString("errInfo") + "]");
- }
- } catch (Exception e) {
- LOGGER.error("获取银联Token异常", e);
- throw new SystemException("获取银联Token异常,请重试!");
- }
- }
- }
|