支付宝和微信登录与支付
This commit is contained in:
parent
644cbc1dcb
commit
a8f324ee5c
|
|
@ -1,13 +1,23 @@
|
||||||
package com.ruoyi.database.service;
|
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.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.ruoyi.database.exception.BusinessException;
|
import com.ruoyi.database.exception.BusinessException;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
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.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.http.*;
|
import org.springframework.http.*;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.KeyFactory;
|
import java.security.KeyFactory;
|
||||||
|
|
@ -36,53 +46,127 @@ public class AlipayPhoneService {
|
||||||
@Value("${alipay.gateway}")
|
@Value("${alipay.gateway}")
|
||||||
private String gateway;
|
private String gateway;
|
||||||
|
|
||||||
/**
|
|
||||||
* 通过授权码获取用户手机号
|
private final String AES_STR = "LoKBapEj35+bRnC/Rrhe8g==";
|
||||||
*/
|
|
||||||
public String getPhoneNumber(String authCode) {
|
public String getPhoneNumber(String authCode) {
|
||||||
try {
|
try {
|
||||||
// 1. 构建请求参数
|
// 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<String, Object> 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 第一步:使用授权码获取访问令牌
|
||||||
|
*/
|
||||||
|
private String getAccessToken(String authCode) {
|
||||||
|
try {
|
||||||
Map<String, String> params = buildBaseParams();
|
Map<String, String> params = buildBaseParams();
|
||||||
params.put("method", "alipay.system.oauth.token");
|
params.put("method", "alipay.system.oauth.token");
|
||||||
params.put("grant_type", "authorization_code");
|
params.put("grant_type", "authorization_code");
|
||||||
params.put("code", authCode);
|
params.put("code", authCode);
|
||||||
|
|
||||||
log.info("请求参数构建完成: {}", params);
|
log.info("获取access_token请求参数: {}", params);
|
||||||
|
|
||||||
// 2. 生成签名
|
|
||||||
String sign = generateSign(params);
|
String sign = generateSign(params);
|
||||||
params.put("sign", sign);
|
params.put("sign", sign);
|
||||||
|
|
||||||
log.info("签名生成完成");
|
|
||||||
|
|
||||||
// 3. 调用支付宝接口 - 使用修复后的方法
|
|
||||||
String responseBody = callAlipayApiWithCharset(params);
|
String responseBody = callAlipayApiWithCharset(params);
|
||||||
|
log.info("获取access_token响应: {}", responseBody);
|
||||||
|
|
||||||
log.info("支付宝手机号接口响应: {}", responseBody);
|
|
||||||
|
|
||||||
// 4. 解析响应
|
|
||||||
Map<String, Object> responseMap = objectMapper.readValue(responseBody, Map.class);
|
Map<String, Object> responseMap = objectMapper.readValue(responseBody, Map.class);
|
||||||
Map<String, Object> tokenResponse = (Map<String, Object>) responseMap.get("alipay_system_oauth_token_response");
|
Map<String, Object> tokenResponse = (Map<String, Object>) responseMap.get("alipay_system_oauth_token_response");
|
||||||
|
|
||||||
if (tokenResponse == null) {
|
if (tokenResponse == null) {
|
||||||
// 检查错误响应
|
|
||||||
Map<String, Object> errorResponse = (Map<String, Object>) responseMap.get("error_response");
|
Map<String, Object> errorResponse = (Map<String, Object>) responseMap.get("error_response");
|
||||||
if (errorResponse != null) {
|
if (errorResponse != null) {
|
||||||
String subCode = (String) errorResponse.get("sub_code");
|
String subCode = (String) errorResponse.get("sub_code");
|
||||||
String subMsg = (String) errorResponse.get("sub_msg");
|
String subMsg = (String) errorResponse.get("sub_msg");
|
||||||
throw new BusinessException("ALIPAY_PHONE_ERROR",
|
throw new BusinessException("ALIPAY_ACCESS_TOKEN_ERROR",
|
||||||
String.format("支付宝接口错误[%s]: %s", subCode, subMsg));
|
String.format("获取access_token失败[%s]: %s", subCode, subMsg));
|
||||||
}
|
}
|
||||||
throw new BusinessException("ALIPAY_PHONE_ERROR", "支付宝接口响应格式错误");
|
throw new BusinessException("ALIPAY_ACCESS_TOKEN_ERROR", "获取access_token失败: 响应格式错误");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 提取手机号
|
String accessToken = (String) tokenResponse.get("access_token");
|
||||||
String mobile = (String) tokenResponse.get("mobile");
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 第二步:使用access_token获取用户信息(包含手机号)
|
||||||
|
*/
|
||||||
|
private String getUserPhoneNumber(String accessToken) {
|
||||||
|
try {
|
||||||
|
Map<String, String> params = buildBaseParams();
|
||||||
|
params.put("method", "alipay.user.info.share");
|
||||||
|
params.put("auth_token", accessToken);
|
||||||
|
|
||||||
|
log.info("获取用户信息请求参数: {}", params);
|
||||||
|
|
||||||
|
String sign = generateSign(params);
|
||||||
|
params.put("sign", sign);
|
||||||
|
|
||||||
|
String responseBody = callAlipayApiWithCharset(params);
|
||||||
|
log.info("获取用户信息响应: {}", responseBody);
|
||||||
|
|
||||||
|
Map<String, Object> responseMap = objectMapper.readValue(responseBody, Map.class);
|
||||||
|
Map<String, Object> userResponse = (Map<String, Object>) responseMap.get("alipay_user_info_share_response");
|
||||||
|
|
||||||
|
if (userResponse == null) {
|
||||||
|
Map<String, Object> errorResponse = (Map<String, Object>) 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_USER_INFO_ERROR",
|
||||||
|
String.format("获取用户信息失败[%s]: %s", subCode, subMsg));
|
||||||
|
}
|
||||||
|
throw new BusinessException("ALIPAY_USER_INFO_ERROR", "获取用户信息失败: 响应格式错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取手机号
|
||||||
|
String mobile = (String) userResponse.get("mobile");
|
||||||
if (mobile == null || mobile.trim().isEmpty()) {
|
if (mobile == null || mobile.trim().isEmpty()) {
|
||||||
throw new BusinessException("ALIPAY_PHONE_ERROR", "获取手机号失败: 手机号为空");
|
// 有些用户可能没有绑定手机号
|
||||||
|
log.warn("用户未绑定手机号或手机号为空");
|
||||||
|
throw new BusinessException("ALIPAY_PHONE_EMPTY", "用户未绑定手机号");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. 验证手机号格式
|
// 验证手机号格式
|
||||||
if (!validatePhoneNumber(mobile)) {
|
if (!validatePhoneNumber(mobile)) {
|
||||||
log.warn("获取到的手机号格式可能不正确: {}", mobile);
|
log.warn("获取到的手机号格式可能不正确: {}", mobile);
|
||||||
}
|
}
|
||||||
|
|
@ -90,11 +174,9 @@ public class AlipayPhoneService {
|
||||||
log.info("成功获取用户手机号: {}", mobile);
|
log.info("成功获取用户手机号: {}", mobile);
|
||||||
return mobile;
|
return mobile;
|
||||||
|
|
||||||
} catch (BusinessException e) {
|
|
||||||
throw e;
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("获取支付宝手机号异常", e);
|
log.error("获取用户信息异常", e);
|
||||||
throw new BusinessException("ALIPAY_PHONE_ERROR", "获取支付宝手机号异常: " + e.getMessage());
|
throw new BusinessException("ALIPAY_USER_INFO_ERROR", "获取用户信息异常: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -202,7 +284,6 @@ public class AlipayPhoneService {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前时间戳
|
* 获取当前时间戳
|
||||||
*/
|
*/
|
||||||
|
|
@ -213,7 +294,6 @@ public class AlipayPhoneService {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取私钥
|
* 获取私钥
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,7 @@ wx:
|
||||||
|
|
||||||
alipay:
|
alipay:
|
||||||
appId: 2021006113615353
|
appId: 2021006113615353
|
||||||
privateKey: MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC7yP787s/Qr36GMnfAb/Blj9NR9voGYfGT/AsC0BGKE+NcymBhA55i27I+QfiXSsb/f/kz1ByMtZxoYHwz7Ha/la5FPps3SgwQNp+CnMIo0DpiP+3i5eVWb+Tffh8C0uL/LzfZKNxaGya68hkG9TitHf7XvBt+Z0Fdy2zKNZZn3xAhr0F6lEmZ645jp8cetDvqpemv95FCJ/2oTB5QDjy65+beOdeES1uvx0HopA+MJyMlJ08K/gopCfNYRqgOnmcDFb6kixUcajdAn8+pjaDpX0RIHnxmOmbcgDUOZS8pakfzbtc/NaSZa25FxXVer5LHR8dNIPKyZKslOHV+MvCtAgMBAAECggEAc/rfKh7hMXXCgmXXOn6ojkG3dc9BewGxMAIiVtlli89exKelwSV4BHzGaNdcDy07HQDQTGd8PpfIg1rcO0GX0hpDaTcSrKJKMxVZjVT+QjDhbl8nsNTmwW0YgcSmqJrRYmSqbLBk8C4GzL3MIVUBbUKybFIc9QQXBjQm+/j+7SV0Kr9hKTA/q4hk4gguem8Cg2+z752uM31eEExD71UlauTvAyj9C4kuhYOnSThpz1UeWZxhOl9n0ij/guEVXhMjlnYf9fvE/gV6yxuTxKWa4NWhTpqDHv6hwm0kjvL3spfTRorAfM7yeXmFYe1grVs5v4GVGPRQLP3/e71CMhGLTQKBgQDgcngshP07uht37yoXbX3bl4E15QU5FKQ7AhU+ZTCTJrmHIJFyBew7oEq34FnM4HPut8V/6BT3PfQJoAgLfNprieuXDiLa/ngdlmBmh5ywITnwC9aCLpwtJjN+bCU7mraQkd7X7ZTo0uiiHYGLTbBWcVAZ/0/zy32C0tsJVe5WWwKBgQDWLx0Z4FMAq4B4pdQfAD3DgqVaxBZAD6Pd4pFXCYBqFgw/RRYE44Gp1KqyfdP1f/JwoFdF/UtOWMe+RUdoU86Iqm2657/PWMSDjCPuYJ2KlIV5cqReXR/XycyIg64Gz+4wGjO788SzGG7oV9V++VZeZtVC2GKkxLlNIeha3SPTlwKBgQCQAAEoFv7dlg7PiOtJcp8gBvkIWfJa5+piTXjRulxK3LqFfpiyPiiNLM8jX9Zdgtf+nXsiEAV7hw0OK9VdgXIlMqyrEo+8Ty1DVlptDa6yte6VGxM6HKtosCyzfLD6p81fvabqhGxYYBCPCj014dcz7qlEtLBYKwNQrvWG7NHoJwKBgEcGxqq/FidhuC/KJibFqLW+DGGmJw7FYP0C1lm7n+cOOq98lIm4fFY6XQTl7zU5YcdxlJ90GNX5YPu686woJpRxmAPkOFplQH2zKnMxfOzoxb55gwJ79URU+kvHBx3hNEalWcSZWQBfOOA5yGXb/4U8qroJEeU8C4sFlI0VSn0vAoGBANOfx8geSo22AuqQKmX27cE4aw8YSPAbiGmjMb4K6wDVeiH2CjO1/boG0D+0F8CO9JDR4llIB8YixgkwFlhuVMk1jouTkOqH+TLE1gqZC5iE7lMCnL1K/OsOH+zGoQ4dP6MRBfcMEG8HyCZw5Uo180fHSwPYLSushxbOG3sHaffJ
|
privateKey: MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCiRzdzmuqvz4ohvOacFyKJIKyfyI22HkNgCJV8gWYNn5oR1wFjyt+3/mf1RAYV0yA4YVmnJkiGxtNdTRFQoOJU21NTDutN8MdjY3KgGFAmAffX1uZrLWn37A5SS/Lo6Ym7PSvqUI/dqMl2RY2Bs8BEMABmD4vwonEvBfPjiX3QhQG0NupfsAX6k5eVAsp4OwfWwgA1IY2dLihzLhWT12hElvRKlf5cdoTP8gDiAMLxLy4UbhLiUj4wimrTl5/UruTo+WBwY3E/abxLIx6UvoU6BwJKh+oF2Po5TMRFWq6KDv+nQskDeB296rNGSa45UXxKy4qnDy6Wwut81Ma0Ja1dAgMBAAECggEAVUpS9brNcHwHILZVCmMKbsIymIRjHv4G8VlxoA+uoKhq5Md5XobJUL4wy3LmM+BURe28niJ76gJkItyXpiX47xqbT8M+nydW1ID3RPeiYGeWaOX1Ew26bWivGkf6srnT/womo6+V/a1xvWzO9AWSnwQPoZS4O1BVZp6dUdD4xHfdZDaChnD0avXRMZrBe9HQjhcev6A+nKJyTGFDbooHSi4bzixcaaE595D/Fp9hxvW7V6rpY9pfgYFr/KgRwEgX+LooHCigYXVEi4ckf/JfH0HKYm0nWrcajal5nlUPq5PG6QMqNDl5m6R6llUl/LhM6JjEeMhmqPCHYGZWG/nIbQKBgQDPpn9o4KGdl3a/Jl5ACQVsk5S146sJ3hRkcmamQsXY/Dmxi+DMyBM5PRRRDiWpx9icZ1AdjPlU/aB81waJdExS/EfBACrf3I2KMA9lfYm5J9cMtsrAyFCLFOgcnGUb1apLe6PGafxYhLm4y3OhMhP71i02ofF1WqxpKewBo0bZ9wKBgQDIEDMrz8IgFoPrraU1gMqnvd57KgNhsX3xpSTfgZq+TeQOua6zeJ/EHfk7nxlatdx6pB1kn7zCUJkpMqZmVhXS6ZvJ1DUdinJCDJn/zokmwsDYE8xPNk41cr4ZPy4WR0svhe2xTc3tL2UmfKbLg9SZk7HUYj/KCUfKC2Y8QLY+SwKBgQDHGubgMUPGUA1Ui/2jeQLycTAOmBbQh1kWV3uFwFDlFjRbwbvzn4SPRbnNXrtOaImSrp1rOFl63RadnbBu7Eyi5bQHo5l4vYoaDqs0rYL5PvI9Bqiy4WAZfBp0FKH+Zom7hvoqrkWAuwM55hshXVs8BsmjsPRNinv2+nOJvn2ZUQKBgQCPR8reHbUR9g4UxBAF+W8qIzkrTDOPy+Y/Id7+k3uXv4ENar5LmqARfMX6hT9LT+PPkanbXut43vBSKQwzToPiwZvpOCmyNm0OEKhaJDjloaUrG0K/mEz6ymqK+kyvd+/I4UoSKX7J15/BqJRsPMYOF1DMonC86ViYwwE9NbtPcQKBgQCM7pHhGZJMUz0KKffzCWjqY9NHZjs7JMAexWjboGuaaS2JkGFV8d/4nWBPiFRqK3whhtZ9MeE3Af6SusQAGQ9Guu45PxiAWa8VVeWYEVm+I4MNGN2C/PC4SqdWqluWiUGhg0zFpwzf/eHvFxdxrutUMADF0X6vL2ZxZcHmZdXoXg==
|
||||||
apppublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAokc3c5rqr8+KIbzmnBciiSCsn8iNth5DYAiVfIFmDZ+aEdcBY8rft/5n9UQGFdMgOGFZpyZIhsbTXU0RUKDiVNtTUw7rTfDHY2NyoBhQJgH319bmay1p9+wOUkvy6OmJuz0r6lCP3ajJdkWNgbPARDAAZg+L8KJxLwXz44l90IUBtDbqX7AF+pOXlQLKeDsH1sIANSGNnS4ocy4Vk9doRJb0SpX+XHaEz/IA4gDC8S8uFG4S4lI+MIpq05ef1K7k6PlgcGNxP2m8SyMelL6FOgcCSofqBdj6OUzERVquig7/p0LJA3gdveqzRkmuOVF8SsuKpw8ulsLrfNTGtCWtXQIDAQAB
|
apppublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAokc3c5rqr8+KIbzmnBciiSCsn8iNth5DYAiVfIFmDZ+aEdcBY8rft/5n9UQGFdMgOGFZpyZIhsbTXU0RUKDiVNtTUw7rTfDHY2NyoBhQJgH319bmay1p9+wOUkvy6OmJuz0r6lCP3ajJdkWNgbPARDAAZg+L8KJxLwXz44l90IUBtDbqX7AF+pOXlQLKeDsH1sIANSGNnS4ocy4Vk9doRJb0SpX+XHaEz/IA4gDC8S8uFG4S4lI+MIpq05ef1K7k6PlgcGNxP2m8SyMelL6FOgcCSofqBdj6OUzERVquig7/p0LJA3gdveqzRkmuOVF8SsuKpw8ulsLrfNTGtCWtXQIDAQAB
|
||||||
publicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhHGI9i9ewOf1lKHkLy8rQS2eLgYwheyzhrfi0fX1BDB7CWKoKu4kqDT52CYP6438K7NvANzh+aSaXvi1vZEAf2ociyjozKypj2qKynL6nW/sLyXWDbCU2u41WJv9iDvm8l/AF6qcanoKSSzZPUFsXHD+ZDsGj/3EyLF+FUN7vQw1Bj9BmYnushjTL/0KQWQNbrZeSHtEPYkhiVGKRD63ZETQPqcTU4vOncmNSb879Z40dAHxob2qUzh7743hk8PnTMkDpacfolMTeeRdDrdsLgmaLypyWtKXe8DASKXE92YnW5Yq8Vkb3aZiS+u903WWNfatOoOGIn0XyuA6T5OCbwIDAQAB
|
publicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhHGI9i9ewOf1lKHkLy8rQS2eLgYwheyzhrfi0fX1BDB7CWKoKu4kqDT52CYP6438K7NvANzh+aSaXvi1vZEAf2ociyjozKypj2qKynL6nW/sLyXWDbCU2u41WJv9iDvm8l/AF6qcanoKSSzZPUFsXHD+ZDsGj/3EyLF+FUN7vQw1Bj9BmYnushjTL/0KQWQNbrZeSHtEPYkhiVGKRD63ZETQPqcTU4vOncmNSb879Z40dAHxob2qUzh7743hk8PnTMkDpacfolMTeeRdDrdsLgmaLypyWtKXe8DASKXE92YnW5Yq8Vkb3aZiS+u903WWNfatOoOGIn0XyuA6T5OCbwIDAQAB
|
||||||
notifyUrl: https://tingche.csckl.com/alipay/notify
|
notifyUrl: https://tingche.csckl.com/alipay/notify
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue