package com.ruoyi.database.service; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeReference; import com.alibaba.fastjson.parser.Feature; import com.alipay.api.AlipayApiException; import com.alipay.api.internal.util.AlipayEncrypt; import com.alipay.api.internal.util.AlipaySignature; import com.fasterxml.jackson.databind.ObjectMapper; import com.ruoyi.database.exception.BusinessException; import lombok.extern.slf4j.Slf4j; import org.checkerframework.checker.units.qual.A; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.*; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.Signature; import java.security.spec.PKCS8EncodedKeySpec; import java.util.*; import java.util.stream.Collectors; @Slf4j @Service public class AlipayPhoneService { @Autowired private RestTemplate restTemplate; @Autowired private ObjectMapper objectMapper; @Value("${alipay.appId}") private String appId; @Value("${alipay.privateKey}") private String privateKey; @Value("${alipay.gateway}") private String gateway; private final String AES_STR = "LoKBapEj35+bRnC/Rrhe8g=="; public String getPhoneNumber(String authCode) { try { // 1. 使用auth_code获取access_token // String accessToken = getAccessToken(authCode); System.out.println("authCode:" + authCode); String charset = "UTF-8"; String encryptType = "AES"; //判断是否为加密内容 boolean isDataEncrypted = !authCode.startsWith("{"); //3. 解密 String plainData = null; if (isDataEncrypted) { try { plainData = AlipayEncrypt.decryptContent(authCode, encryptType, AES_STR, charset); System.out.println("解密:" + plainData); } catch (AlipayApiException e) { //解密异常, 记录日志 throw new Exception("解密异常"); } } else { plainData = authCode; } // 2. 使用access_token获取用户信息(包含手机号) Map responseMap = objectMapper.readValue(plainData, Map.class); return responseMap.get("mobile").toString(); } catch (Exception e) { log.error("获取支付宝手机号异常", e); throw new BusinessException("ALIPAY_PHONE_ERROR", "获取支付宝手机号异常: " + e.getMessage()); } } /** * 获取用户id */ private String getOpenId(String authCode) { try { Map params = buildBaseParams(); params.put("method", "alipay.system.oauth.token"); params.put("grant_type", "authorization_code"); params.put("code", authCode); log.info("获取access_token请求参数: {}", params); String sign = generateSign(params); params.put("sign", sign); String responseBody = callAlipayApiWithCharset(params); log.info("获取access_token响应: {}", responseBody); Map responseMap = objectMapper.readValue(responseBody, Map.class); Map tokenResponse = (Map) responseMap.get("alipay_system_oauth_token_response"); if (tokenResponse == null) { Map errorResponse = (Map) responseMap.get("error_response"); if (errorResponse != null) { String subCode = (String) errorResponse.get("sub_code"); String subMsg = (String) errorResponse.get("sub_msg"); throw new BusinessException("ALIPAY_ACCESS_TOKEN_ERROR", String.format("获取access_token失败[%s]: %s", subCode, subMsg)); } throw new BusinessException("ALIPAY_ACCESS_TOKEN_ERROR", "获取access_token失败: 响应格式错误"); } String accessToken = (String) tokenResponse.get("open_id"); if (accessToken == null || accessToken.trim().isEmpty()) { throw new BusinessException("ALIPAY_ACCESS_TOKEN_ERROR", "获取access_token失败: access_token为空"); } log.info("成功获取access_token: {}", accessToken); return accessToken; } catch (Exception e) { log.error("获取access_token异常", e); throw new BusinessException("ALIPAY_ACCESS_TOKEN_ERROR", "获取access_token异常: " + e.getMessage()); } } /** * 修复后的API调用方法 - 正确处理charset参数 */ private String callAlipayApiWithCharset(Map params) { try { // 1. 将charset参数提取到URL查询字符串中 String charset = params.get("charset"); params.remove("charset"); // 从业务参数中移除 // 2. 构建表单数据 String formData = params.entrySet().stream() .map(entry -> { try { // 使用指定的charset进行编码 String encodedValue = URLEncoder.encode(entry.getValue(), charset); return entry.getKey() + "=" + encodedValue; } catch (Exception e) { log.error("参数编码失败: key={}, value={}", entry.getKey(), entry.getValue(), e); return entry.getKey() + "=" + entry.getValue(); } }) .collect(Collectors.joining("&")); // 3. 构建完整的URL,包含charset参数 String url = gateway + "?charset=" + charset; log.debug("支付宝API请求URL: {}", url); log.debug("支付宝API请求表单数据: {}", formData); // 4. 设置请求头 HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); headers.add("User-Agent", "Alipay Client"); HttpEntity request = new HttpEntity<>(formData, headers); // 5. 调用接口 ResponseEntity response = restTemplate.postForEntity(url, request, String.class); return response.getBody(); } catch (Exception e) { log.error("调用支付宝API异常", e); throw new BusinessException("ALIPAY_API_ERROR", "调用支付宝API异常: " + e.getMessage()); } } /** * 修复签名生成方法 */ private String generateSign(Map params) { try { // 1. 参数排序 List keys = new ArrayList<>(params.keySet()); Collections.sort(keys); // 2. 拼接待签名字符串(排除sign参数) String signContent = keys.stream() .filter(key -> !"sign".equals(key) && params.get(key) != null && !params.get(key).isEmpty()) .map(key -> { try { // 使用UTF-8编码进行签名(即使charset是其他值) String value = params.get(key); return key + "=" + value; } catch (Exception e) { return key + "=" + params.get(key); } }) .collect(Collectors.joining("&")); log.debug("待签名字符串: {}", signContent); // 3. 使用RSA2签名 Signature signature = Signature.getInstance("SHA256WithRSA"); signature.initSign(getPrivateKey()); signature.update(signContent.getBytes(StandardCharsets.UTF_8)); // 使用UTF-8编码签名 byte[] signed = signature.sign(); // 4. Base64编码 String sign = Base64.getEncoder().encodeToString(signed); log.debug("生成签名: {}", sign); return sign; } catch (Exception e) { log.error("生成支付宝签名异常", e); throw new BusinessException("ALIPAY_SIGN_ERROR", "生成签名失败: " + e.getMessage()); } } /** * 构建基础参数 - 确保charset正确 */ private Map buildBaseParams() { Map params = new HashMap<>(); params.put("app_id", appId); params.put("charset", "UTF-8"); // 明确指定UTF-8 params.put("sign_type", "RSA2"); params.put("timestamp", getCurrentTimestamp()); params.put("version", "1.0"); params.put("format", "json"); return params; } /** * 获取当前时间戳 */ private String getCurrentTimestamp() { return java.time.LocalDateTime.now().format( java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") ); } /** * 获取私钥 */ private PrivateKey getPrivateKey() { try { String privateKeyContent = privateKey .replace("-----BEGIN PRIVATE KEY-----", "") .replace("-----END PRIVATE KEY-----", "") .replaceAll("\\s", ""); byte[] keyBytes = Base64.getDecoder().decode(privateKeyContent); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePrivate(keySpec); } catch (Exception e) { throw new BusinessException("ALIPAY_KEY_ERROR", "加载私钥失败: " + e.getMessage()); } } /** * 调用支付宝API */ private String callAlipayApi(Map params) { try { // 构建表单数据 String formData = params.entrySet().stream() .map(entry -> { try { return entry.getKey() + "=" + URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8.name()); } catch (Exception e) { return entry.getKey() + "=" + entry.getValue(); } }) .collect(Collectors.joining("&")); log.debug("支付宝API请求表单数据: {}", formData); // 设置请求头 HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); headers.add("User-Agent", "Alipay Client"); HttpEntity request = new HttpEntity<>(formData, headers); // 调用接口 ResponseEntity response = restTemplate.postForEntity(gateway, request, String.class); return response.getBody(); } catch (Exception e) { log.error("调用支付宝API异常", e); throw new BusinessException("ALIPAY_API_ERROR", "调用支付宝API异常: " + e.getMessage()); } } /** * 验证支付宝响应签名(简化版,实际需要完整实现) */ private boolean verifySign(Map responseMap) { try { // 这里需要实现完整的支付宝响应签名验证 // 包括提取sign、排序参数、使用支付宝公钥验证等 // 生产环境必须实现此方法 log.warn("支付宝响应签名验证需要完整实现"); return true; // 测试环境暂时返回true } catch (Exception e) { log.error("验证支付宝签名异常", e); return false; } } /** * 验证手机号格式 */ public boolean validatePhoneNumber(String phoneNumber) { if (phoneNumber == null || phoneNumber.trim().isEmpty()) { return false; } // 简单的手机号格式验证 return phoneNumber.matches("^1[3-9]\\d{9}$"); } /** * 获取支付宝用户ID (如果需要) */ public String getAlipayUserId(String authCode) { try { String openId = getOpenId(authCode); // 在实际业务中,你可能需要根据手机号关联用户ID // 这里返回手机号作为示例,实际应该返回支付宝用户ID return openId; } catch (Exception e) { log.error("获取支付宝用户ID异常", e); throw new BusinessException("ALIPAY_USER_ID_ERROR", "获取支付宝用户ID异常: " + e.getMessage()); } } }