|
|
@@ -11,6 +11,8 @@
|
|
11
|
11
|
namespace app\common\repositories\user;
|
|
12
|
12
|
|
|
13
|
13
|
|
|
|
14
|
+use alipay\aop\AlipayConfig;
|
|
|
15
|
+use alipay\aop\AopCertClient;
|
|
14
|
16
|
use app\common\dao\BaseDao;
|
|
15
|
17
|
use app\common\dao\user\UserDao;
|
|
16
|
18
|
use app\common\model\merchant\order\OrderMerchant;
|
|
|
@@ -3321,6 +3323,170 @@ class UserRepository extends BaseRepository
|
|
3321
|
3323
|
return $w->getMessage();
|
|
3322
|
3324
|
}
|
|
3323
|
3325
|
}
|
|
|
3326
|
+
|
|
|
3327
|
+ /**
|
|
|
3328
|
+ * 使用证书方式,通过 authCode 换取支付宝 AccessToken 和用户信息
|
|
|
3329
|
+ *
|
|
|
3330
|
+ * @param string $authorization_code 从 APP 获取的授权码
|
|
|
3331
|
+ * @param bool $info 是否在获取 Token 后立即获取用户信息 (true: 获取, false: 只获取 Token)
|
|
|
3332
|
+ * @return array|string 成功时返回包含 token 或用户信息的数组,失败时返回错误信息数组或字符串
|
|
|
3333
|
+ * @throws \Exception
|
|
|
3334
|
+ */
|
|
|
3335
|
+ public function getTokenByCodeWithCert($authorization_code, $info = false)
|
|
|
3336
|
+ {
|
|
|
3337
|
+ try {
|
|
|
3338
|
+ // --- 1. 配置证书信息 ---
|
|
|
3339
|
+ // *** 请确保这里的 AppID 和 PrivateKey 是你进行【授权登录】的那个支付宝应用的 ***
|
|
|
3340
|
+ // *** 它可能与你进行【转账】的应用是同一个,也可能是不同的,请仔细核对 ***
|
|
|
3341
|
+ $app_id = "你的授权登录应用的AppID"; // 例如: 20210021XXXXXXXXXX
|
|
|
3342
|
+ $privateKey = "你的应用私钥(PKCS1格式内容或路径)"; // 从.pem文件读取 或 直接粘贴(不推荐)
|
|
|
3343
|
+
|
|
|
3344
|
+ // *** 证书文件路径 ***
|
|
|
3345
|
+ // 建议使用绝对路径或相对于项目根目录的路径,确保PHP有读取权限
|
|
|
3346
|
+ $app_cert_path = dirname(__FILE__) . '/../../../../extend/alipay/aop/cert/appCertPublicKey.crt'; // 应用公钥证书路径
|
|
|
3347
|
+ $alipay_cert_path = dirname(__FILE__) . '/../../../../extend/alipay/aop/cert/alipayCertPublicKey.crt'; // 支付宝公钥证书路径
|
|
|
3348
|
+ $alipay_root_cert_path = dirname(__FILE__) . '/../../../../extend/alipay/aop/cert/alipayRootCert.crt'; // 支付宝根证书路径
|
|
|
3349
|
+
|
|
|
3350
|
+ $url = "https://openapi.alipay.com/gateway.do"; // 支付宝网关
|
|
|
3351
|
+
|
|
|
3352
|
+ // --- 2. 初始化支付宝配置 (使用 AlipayConfig) ---
|
|
|
3353
|
+ $alipayConfig = new AlipayConfig();
|
|
|
3354
|
+ $alipayConfig->setAppId($app_id);
|
|
|
3355
|
+ $alipayConfig->setCharset("utf-8");
|
|
|
3356
|
+ $alipayConfig->setFormat("json");
|
|
|
3357
|
+ $alipayConfig->setSignType("RSA2");
|
|
|
3358
|
+ $alipayConfig->setServerUrl($url);
|
|
|
3359
|
+ $alipayConfig->setPrivateKey($privateKey); // 设置应用私钥
|
|
|
3360
|
+
|
|
|
3361
|
+ // *** 设置证书路径 ***
|
|
|
3362
|
+ $alipayConfig->setAppCertPath($app_cert_path);
|
|
|
3363
|
+ $alipayConfig->setAlipayPublicCertPath($alipay_cert_path);
|
|
|
3364
|
+ $alipayConfig->setRootCertPath($alipay_root_cert_path);
|
|
|
3365
|
+
|
|
|
3366
|
+ // --- 3. 实例化证书模式客户端 (使用 AopCertClient) ---
|
|
|
3367
|
+ $aop = new AopCertClient($alipayConfig);
|
|
|
3368
|
+ $aop->isCheckAlipayPublicCert = true; // 强烈建议启用支付宝公钥证书校验
|
|
|
3369
|
+
|
|
|
3370
|
+ // --- 4. 创建并执行获取 Token 的请求 ---
|
|
|
3371
|
+ $request = new AlipaySystemOauthTokenRequest();
|
|
|
3372
|
+ $request->setGrantType("authorization_code");
|
|
|
3373
|
+ $request->setCode($authorization_code); // 设置从 APP 获取的 authCode
|
|
|
3374
|
+
|
|
|
3375
|
+ // 执行请求
|
|
|
3376
|
+ $result = $aop->execute($request); // R 返回的是原始响应字符串,需要解码
|
|
|
3377
|
+ $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response"; // 结果节点的 key,例如 "alipay_system_oauth_token_response"
|
|
|
3378
|
+
|
|
|
3379
|
+ // 解析 JSON 响应
|
|
|
3380
|
+ $resultObj = json_decode($result);
|
|
|
3381
|
+
|
|
|
3382
|
+ // --- 5. 处理 Token 响应 ---
|
|
|
3383
|
+ if (isset($resultObj->$responseNode) && $resultObj->$responseNode->code == '10000') {
|
|
|
3384
|
+ // 成功获取 Token
|
|
|
3385
|
+ $response = $resultObj->$responseNode;
|
|
|
3386
|
+ $return_data = [
|
|
|
3387
|
+ 'code' => 200,
|
|
|
3388
|
+ 'msg' => 'Token fetched successfully',
|
|
|
3389
|
+ 'user_id' => $response->user_id,
|
|
|
3390
|
+ 'access_token' => $response->access_token,
|
|
|
3391
|
+ 'refresh_token' => $response->refresh_token, // 注意保存 refresh token 以便后续刷新
|
|
|
3392
|
+ 'expires_in' => $response->expires_in,
|
|
|
3393
|
+ 're_expires_in' => $response->re_expires_in,
|
|
|
3394
|
+ ];
|
|
|
3395
|
+ $access_token = $response->access_token; // 保存 access_token 供后续使用
|
|
|
3396
|
+
|
|
|
3397
|
+ // --- 6. (可选) 获取用户信息 ---
|
|
|
3398
|
+ if ($info) {
|
|
|
3399
|
+ $userInfoRequest = new AlipayUserInfoShareRequest; // 注意命名空间
|
|
|
3400
|
+ // 使用获取到的 access_token 执行请求
|
|
|
3401
|
+ // 注意:AopCertClient 的 execute 方法第二个参数是 accessToken
|
|
|
3402
|
+ $userInfoResult = $aop->execute($userInfoRequest, $access_token);
|
|
|
3403
|
+ $userInfoResponseNode = str_replace(".", "_", $userInfoRequest->getApiMethodName()) . "_response"; // 例如 "alipay_user_info_share_response"
|
|
|
3404
|
+ $userInfoResultObj = json_decode($userInfoResult);
|
|
|
3405
|
+
|
|
|
3406
|
+ if (isset($userInfoResultObj->$userInfoResponseNode) && $userInfoResultObj->$userInfoResponseNode->code == '10000') {
|
|
|
3407
|
+ // 成功获取用户信息
|
|
|
3408
|
+ $userInfoResponse = $userInfoResultObj->$userInfoResponseNode;
|
|
|
3409
|
+ // 将用户信息合并到返回结果中
|
|
|
3410
|
+ $return_data['user_info'] = [
|
|
|
3411
|
+ 'user_id' => $userInfoResponse->user_id, // 支付宝用户ID
|
|
|
3412
|
+ 'nick_name' => $userInfoResponse->nick_name ?? null, // 昵称
|
|
|
3413
|
+ 'avatar' => $userInfoResponse->avatar ?? null, // 头像
|
|
|
3414
|
+ // 根据你的授权范围,还可能包含性别(gender), 省份(province), 城市(city)等
|
|
|
3415
|
+ // 'gender' => $userInfoResponse->gender ?? null,
|
|
|
3416
|
+ // 'province' => $userInfoResponse->province ?? null,
|
|
|
3417
|
+ // 'city' => $userInfoResponse->city ?? null,
|
|
|
3418
|
+ ];
|
|
|
3419
|
+ Db::name('log')->insert(['data' => '支付宝用户信息获取成功: ' . json_encode($userInfoResponse, JSON_UNESCAPED_UNICODE)]);
|
|
|
3420
|
+ return $return_data; // 返回包含 Token 和用户信息的完整数据
|
|
|
3421
|
+ } else {
|
|
|
3422
|
+ // 获取用户信息失败
|
|
|
3423
|
+ $errorMsg = 'Failed to get user info';
|
|
|
3424
|
+ $subMsg = '';
|
|
|
3425
|
+ if (isset($userInfoResultObj->$userInfoResponseNode)) {
|
|
|
3426
|
+ $errorMsg .= ': ' . $userInfoResultObj->$userInfoResponseNode->sub_msg;
|
|
|
3427
|
+ $subMsg = $userInfoResultObj->$userInfoResponseNode->sub_msg;
|
|
|
3428
|
+ } elseif (isset($userInfoResultObj->error_response)) {
|
|
|
3429
|
+ $errorMsg .= ': ' . $userInfoResultObj->error_response->sub_msg;
|
|
|
3430
|
+ $subMsg = $userInfoResultObj->error_response->sub_msg;
|
|
|
3431
|
+ }
|
|
|
3432
|
+ Db::name('log')->insert(['data' => '支付宝用户信息获取失败: ' . $errorMsg . ' | Raw: ' . $userInfoResult]);
|
|
|
3433
|
+ // 即使获取用户信息失败,Token 仍然是有效的,可以选择只返回 Token 信息
|
|
|
3434
|
+ // 或者标记用户信息获取失败
|
|
|
3435
|
+ $return_data['user_info_error'] = $subMsg ?: $errorMsg;
|
|
|
3436
|
+ return $return_data; // 返回 Token 信息和用户信息获取失败标记
|
|
|
3437
|
+ // 或者直接返回错误
|
|
|
3438
|
+ // return ['code' => 500, 'msg' => $errorMsg];
|
|
|
3439
|
+ }
|
|
|
3440
|
+ } else {
|
|
|
3441
|
+ // 不需要获取用户信息,直接返回 Token 数据
|
|
|
3442
|
+ Db::name('log')->insert(['data' => '支付宝Token获取成功: ' . json_encode($response, JSON_UNESCAPED_UNICODE)]);
|
|
|
3443
|
+ return $return_data;
|
|
|
3444
|
+ }
|
|
|
3445
|
+
|
|
|
3446
|
+ } else {
|
|
|
3447
|
+ // 获取 Token 失败
|
|
|
3448
|
+ $errorMsg = 'Failed to exchange token';
|
|
|
3449
|
+ $subMsg = '';
|
|
|
3450
|
+ if (isset($resultObj->$responseNode)) {
|
|
|
3451
|
+ $errorMsg .= ': ' . ($resultObj->$responseNode->sub_msg ?? 'Unknown error');
|
|
|
3452
|
+ $subMsg = $resultObj->$responseNode->sub_msg ?? '';
|
|
|
3453
|
+ } elseif (isset($resultObj->error_response)) {
|
|
|
3454
|
+ $errorMsg .= ': ' . ($resultObj->error_response->sub_msg ?? 'Unknown error');
|
|
|
3455
|
+ $subMsg = $resultObj->error_response->sub_msg ?? '';
|
|
|
3456
|
+ }
|
|
|
3457
|
+ Db::name('log')->insert(['data' => '支付宝Token获取失败: ' . $errorMsg . ' | Raw: ' . $result]);
|
|
|
3458
|
+ return ['code' => 500, 'msg' => $subMsg ?: $errorMsg];
|
|
|
3459
|
+ }
|
|
|
3460
|
+
|
|
|
3461
|
+ } catch (\Exception $e) { // 捕获 SDK 或其他异常
|
|
|
3462
|
+ Db::name("log")->insert(['data' => 'Alipay Token/Info Error: '.$e->getFile() . ':' . $e->getLine() . '【' . $e->getMessage() . '】']); //日志记录
|
|
|
3463
|
+ return ['code' => 500, 'msg' => 'System Error: ' . $e->getMessage()];
|
|
|
3464
|
+ }
|
|
|
3465
|
+ }
|
|
|
3466
|
+
|
|
|
3467
|
+ // 你可以在 Controller 的其他方法中调用这个函数
|
|
|
3468
|
+ public function handleAlipayCallback($authCode)
|
|
|
3469
|
+ {
|
|
|
3470
|
+ if (!$authCode) {
|
|
|
3471
|
+ return ['status' => 'error', 'message' => 'Auth code is missing'];
|
|
|
3472
|
+ }
|
|
|
3473
|
+ // 调用上面封装的方法,并请求获取用户信息
|
|
|
3474
|
+ $result = $this->getTokenByCodeWithCert($authCode, true); // 第二个参数为 true
|
|
|
3475
|
+
|
|
|
3476
|
+ // 根据 $result 处理后续逻辑 (如用户登录注册,返回 token 给 APP 等)
|
|
|
3477
|
+ if ($result['code'] == 200) {
|
|
|
3478
|
+ // 成功处理
|
|
|
3479
|
+ // $alipay_user_id = $result['user_id'];
|
|
|
3480
|
+ // $nick_name = $result['user_info']['nick_name'] ?? 'Unknown';
|
|
|
3481
|
+ // $avatar = $result['user_info']['avatar'] ?? '';
|
|
|
3482
|
+
|
|
|
3483
|
+ return ['status' => 'success', 'data' => $result];
|
|
|
3484
|
+ } else {
|
|
|
3485
|
+ // 处理失败
|
|
|
3486
|
+ return ['status' => 'error', 'message' => $result['msg']];
|
|
|
3487
|
+ }
|
|
|
3488
|
+ }
|
|
|
3489
|
+
|
|
3324
|
3490
|
/**
|
|
3325
|
3491
|
* @param $id
|
|
3326
|
3492
|
* @return Form
|