|
|
@@ -1,510 +0,0 @@
|
|
|
-# 货拉拉 OAuth2.0 授权实现指南
|
|
|
-
|
|
|
-## 概述
|
|
|
-
|
|
|
-货拉拉开放平台采用标准的 OAuth 2.0 `Authorization Code` 模式进行商户授权。
|
|
|
-此文档详细说明了授权流程、API接口、参数说明以及在项目中的集成方法。
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-## 目录
|
|
|
-
|
|
|
-1. [授权流程](#授权流程)
|
|
|
-2. [API 接口](#api-接口)
|
|
|
-3. [环境配置](#环境配置)
|
|
|
-4. [使用示例](#使用示例)
|
|
|
-5. [常见问题](#常见问题)
|
|
|
-6. [参考资源](#参考资源)
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-## 授权流程
|
|
|
-
|
|
|
-### Authorization Code 流程
|
|
|
-
|
|
|
-货拉拉的 OAuth 2.0 授权采用三步流程:
|
|
|
-
|
|
|
-```
|
|
|
-┌─────────────────┐ ┌──────────────┐ ┌────────────────┐
|
|
|
-│ 第三方平台 │ │ 货拉拉授权页 │ │ 货拉拉授权服务器 │
|
|
|
-│ (ISV) │ │ │ │ │
|
|
|
-└────────┬────────┘ └──────────────┘ └────────────────┘
|
|
|
- │ │ │
|
|
|
- │ 1. 跳转到授权页面 │ │
|
|
|
- │ (generateAuthUrl) │ │
|
|
|
- │─────────────────────────>│ │
|
|
|
- │ │ │
|
|
|
- │ 用户授权 │
|
|
|
- │ │ │
|
|
|
- │ │ 2. 返回授权码(code) │
|
|
|
- │<──────────────────────────┤<─────────────────────────┤
|
|
|
- │ redirect_uri?code=xxx │ │
|
|
|
- │ │ │
|
|
|
- │ 3. 使用code换取token │ │
|
|
|
- │ (getAccessToken) │ │
|
|
|
- │────────────────────────────────────────────────────>│
|
|
|
- │ │ │
|
|
|
- │ │ 返回 access_token │
|
|
|
- │<────────────────────────────────────────────────────│
|
|
|
- │ │ │
|
|
|
-```
|
|
|
-
|
|
|
-### 流程步骤详解
|
|
|
-
|
|
|
-#### 步骤1:生成授权URL
|
|
|
-
|
|
|
-商户系统中生成授权链接,引导用户跳转到货拉拉授权页面:
|
|
|
-
|
|
|
-```php
|
|
|
-$auth = new \common\components\delivery\platform\huolala\Auth();
|
|
|
-$redirectUrl = 'https://your-domain.com/delivery/huolala-callback';
|
|
|
-$authUrl = $auth->generateAuthUrl($redirectUrl);
|
|
|
-
|
|
|
-// 在页面中跳转
|
|
|
-Yii::$app->response->redirect($authUrl);
|
|
|
-```
|
|
|
-
|
|
|
-**生成的URL格式:**
|
|
|
-```
|
|
|
-https://open.huolala.cn/#/oauth/authorize?response_type=code&client_id=xxx&redirect_uri=xxx&isSandbox=true
|
|
|
-```
|
|
|
-
|
|
|
-#### 步骤2:用户授权
|
|
|
-
|
|
|
-用户在货拉拉授权页面填写账号信息并同意授权。货拉拉会重定向回 `redirect_uri` 并携带授权码 `code` 参数。
|
|
|
-
|
|
|
-#### 步骤3:交换Access Token
|
|
|
-
|
|
|
-在回调页面获取 `code`,调用接口交换 Access Token:
|
|
|
-
|
|
|
-```php
|
|
|
-$code = Yii::$app->request->get('code');
|
|
|
-$auth = new \common\components\delivery\platform\huolala\Auth();
|
|
|
-$result = $auth->getAccessToken($code);
|
|
|
-
|
|
|
-if ($result['success']) {
|
|
|
- // 保存到数据库
|
|
|
- $accessToken = $result['access_token'];
|
|
|
- $refreshToken = $result['refresh_token'];
|
|
|
- $expiresIn = $result['expires_in']; // 3个月
|
|
|
-} else {
|
|
|
- // 处理错误
|
|
|
- $error = $result['error'];
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-## API 接口
|
|
|
-
|
|
|
-### 1. 生成授权URL
|
|
|
-
|
|
|
-**方法:** `generateAuthUrl($redirectUri)`
|
|
|
-
|
|
|
-**说明:** 生成用户授权的URL,用户通过访问此URL进行授权。
|
|
|
-
|
|
|
-**参数:**
|
|
|
-
|
|
|
-| 参数 | 类型 | 必须 | 说明 |
|
|
|
-|------|------|------|------|
|
|
|
-| redirectUri | string | 是 | 授权回调地址,用户授权后会跳转到此地址 |
|
|
|
-
|
|
|
-**返回值:** 授权URL字符串
|
|
|
-
|
|
|
-**示例:**
|
|
|
-
|
|
|
-```php
|
|
|
-$auth = new Auth();
|
|
|
-$authUrl = $auth->generateAuthUrl('http://your-domain.com/callback');
|
|
|
-// https://open.huolala.cn/#/oauth/authorize?response_type=code&client_id=xxx&redirect_uri=xxx&isSandbox=true
|
|
|
-```
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-### 2. 获取Access Token
|
|
|
-
|
|
|
-**方法:** `getAccessToken($code)`
|
|
|
-
|
|
|
-**说明:** 使用授权码获取访问令牌和刷新令牌。
|
|
|
-
|
|
|
-**接口:**
|
|
|
-- 路径:`/oauth/token`
|
|
|
-- 请求方式:`GET`
|
|
|
-- 环境:根据 `YII_ENV` 自动判断沙箱或生产环境
|
|
|
-
|
|
|
-**请求参数:**
|
|
|
-
|
|
|
-| 参数 | 类型 | 必须 | 说明 |
|
|
|
-|------|------|------|------|
|
|
|
-| grant_type | string | 是 | 固定值:`authorization_code` |
|
|
|
-| client_id | string | 是 | 应用 App Key |
|
|
|
-| code | string | 是 | 授权码(来自授权回调) |
|
|
|
-| isSandbox | boolean | 是 | 沙箱环境标志(自动处理) |
|
|
|
-
|
|
|
-**返回值:**
|
|
|
-
|
|
|
-```php
|
|
|
-[
|
|
|
- 'success' => true, // 请求是否成功
|
|
|
- 'access_token' => 'xxx', // 访问令牌
|
|
|
- 'refresh_token' => 'yyy', // 刷新令牌
|
|
|
- 'expires_in' => 7776000, // 过期时间(秒),3个月
|
|
|
-]
|
|
|
-```
|
|
|
-
|
|
|
-**示例:**
|
|
|
-
|
|
|
-```php
|
|
|
-$auth = new Auth();
|
|
|
-$code = Yii::$app->request->get('code');
|
|
|
-$result = $auth->getAccessToken($code);
|
|
|
-
|
|
|
-if ($result['success']) {
|
|
|
- echo "Token: " . $result['access_token'];
|
|
|
-} else {
|
|
|
- echo "Error: " . $result['error'];
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-### 3. 刷新Access Token
|
|
|
-
|
|
|
-**方法:** `refreshAccessToken($refreshToken)`
|
|
|
-
|
|
|
-**说明:** 使用刷新令牌获取新的访问令牌,无需用户重新授权。
|
|
|
-
|
|
|
-**接口:**
|
|
|
-- 路径:`/oauth/token`
|
|
|
-- 请求方式:`GET`
|
|
|
-- 环境:根据 `YII_ENV` 自动判断沙箱或生产环境
|
|
|
-
|
|
|
-**请求参数:**
|
|
|
-
|
|
|
-| 参数 | 类型 | 必须 | 说明 |
|
|
|
-|------|------|------|------|
|
|
|
-| grant_type | string | 是 | 固定值:`refresh_token` |
|
|
|
-| client_id | string | 是 | 应用 App Key |
|
|
|
-| refresh_token | string | 是 | 刷新令牌 |
|
|
|
-| isSandbox | boolean | 是 | 沙箱环境标志(自动处理) |
|
|
|
-
|
|
|
-**返回值:**
|
|
|
-
|
|
|
-```php
|
|
|
-[
|
|
|
- 'success' => true, // 请求是否成功
|
|
|
- 'access_token' => 'xxx', // 新的访问令牌
|
|
|
- 'refresh_token' => 'yyy', // 新的刷新令牌
|
|
|
- 'expires_in' => 7776000, // 新的过期时间(秒)
|
|
|
-]
|
|
|
-```
|
|
|
-
|
|
|
-**示例:**
|
|
|
-
|
|
|
-```php
|
|
|
-$auth = new Auth();
|
|
|
-$result = $auth->refreshAccessToken($oldRefreshToken);
|
|
|
-
|
|
|
-if ($result['success']) {
|
|
|
- // 使用新的 token
|
|
|
- $newAccessToken = $result['access_token'];
|
|
|
- $newRefreshToken = $result['refresh_token'];
|
|
|
-} else {
|
|
|
- echo "Error: " . $result['error'];
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-## 环境配置
|
|
|
-
|
|
|
-### 环境变量
|
|
|
-
|
|
|
-**YII_ENV 变量的作用:**
|
|
|
-
|
|
|
-| 值 | 说明 | isSandbox | 基础URL |
|
|
|
-|-----|------|----------|--------|
|
|
|
-| production | 正式环境 | false | https://open.huolala.cn |
|
|
|
-| 其他 | 沙箱/测试环境 | true | https://open.huolala.cn(同一个域名,通过参数区分) |
|
|
|
-
|
|
|
-### 沙箱环境测试
|
|
|
-
|
|
|
-**沙箱测试账户:**
|
|
|
-- 验证码固定为:`6666`
|
|
|
-- 环境:`isSandbox=true`
|
|
|
-
|
|
|
-**切换到沙箱环境:**
|
|
|
-
|
|
|
-```bash
|
|
|
-# 设置环境变量
|
|
|
-export YII_ENV=dev
|
|
|
-
|
|
|
-# 或在 env.php 中配置
|
|
|
-define('YII_ENV', 'dev');
|
|
|
-```
|
|
|
-
|
|
|
-### 配置应用凭证
|
|
|
-
|
|
|
-在 `Auth.php` 构造函数中修改:
|
|
|
-
|
|
|
-```php
|
|
|
-// 生产环境配置
|
|
|
-$this->appKey = '正式环境的app_key';
|
|
|
-$this->appSecret = '正式环境的app_secret';
|
|
|
-
|
|
|
-// 沙箱环境配置
|
|
|
-$this->appKey = '沙箱环境的app_key';
|
|
|
-$this->appSecret = '沙箱环境的app_secret';
|
|
|
-```
|
|
|
-
|
|
|
-或通过环境变量注入(推荐):
|
|
|
-
|
|
|
-```php
|
|
|
-public function __construct()
|
|
|
-{
|
|
|
- $isProduction = getenv('YII_ENV') == 'production';
|
|
|
-
|
|
|
- if ($isProduction) {
|
|
|
- $this->appKey = getenv('HUOLALA_PROD_APP_KEY');
|
|
|
- $this->appSecret = getenv('HUOLALA_PROD_APP_SECRET');
|
|
|
- } else {
|
|
|
- $this->appKey = getenv('HUOLALA_SANDBOX_APP_KEY');
|
|
|
- $this->appSecret = getenv('HUOLALA_SANDBOX_APP_SECRET');
|
|
|
- }
|
|
|
-
|
|
|
- $this->isSandbox = !$isProduction;
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-## 使用示例
|
|
|
-
|
|
|
-### 完整的授权流程
|
|
|
-
|
|
|
-```php
|
|
|
-<?php
|
|
|
-namespace app\controllers;
|
|
|
-
|
|
|
-use common\components\delivery\platform\huolala\Auth;
|
|
|
-use Yii;
|
|
|
-
|
|
|
-class DeliveryController extends \yii\web\Controller
|
|
|
-{
|
|
|
- /**
|
|
|
- * 步骤1:跳转到授权页面
|
|
|
- */
|
|
|
- public function actionAuthorizeHuolala()
|
|
|
- {
|
|
|
- $auth = new Auth();
|
|
|
- $redirectUrl = Yii::$app->urlManager->createAbsoluteUrl(['delivery/huolala-callback']);
|
|
|
- $authUrl = $auth->generateAuthUrl($redirectUrl);
|
|
|
-
|
|
|
- return Yii::$app->response->redirect($authUrl);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 步骤2:处理授权回调
|
|
|
- */
|
|
|
- public function actionHuoLalaCallback()
|
|
|
- {
|
|
|
- $code = Yii::$app->request->get('code');
|
|
|
-
|
|
|
- if (!$code) {
|
|
|
- return $this->redirect(['index', 'error' => '授权失败']);
|
|
|
- }
|
|
|
-
|
|
|
- $auth = new Auth();
|
|
|
- $result = $auth->getAccessToken($code);
|
|
|
-
|
|
|
- if (!$result['success']) {
|
|
|
- return $this->redirect(['index', 'error' => $result['error']]);
|
|
|
- }
|
|
|
-
|
|
|
- // 保存 token 到数据库
|
|
|
- $this->saveHuoLalaToken([
|
|
|
- 'access_token' => $result['access_token'],
|
|
|
- 'refresh_token' => $result['refresh_token'],
|
|
|
- 'expires_at' => time() + $result['expires_in'],
|
|
|
- ]);
|
|
|
-
|
|
|
- return $this->redirect(['index', 'success' => '授权成功']);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 刷新过期的 Token
|
|
|
- */
|
|
|
- public function actionRefreshHuoLalaToken()
|
|
|
- {
|
|
|
- // 从数据库获取存储的 refresh_token
|
|
|
- $storedToken = $this->getHuoLalaToken();
|
|
|
-
|
|
|
- if (!$storedToken || !$storedToken['refresh_token']) {
|
|
|
- throw new \Exception('未找到有效的刷新令牌');
|
|
|
- }
|
|
|
-
|
|
|
- $auth = new Auth();
|
|
|
- $result = $auth->refreshAccessToken($storedToken['refresh_token']);
|
|
|
-
|
|
|
- if (!$result['success']) {
|
|
|
- throw new \Exception('Token 刷新失败:' . $result['error']);
|
|
|
- }
|
|
|
-
|
|
|
- // 更新 token
|
|
|
- $this->saveHuoLalaToken([
|
|
|
- 'access_token' => $result['access_token'],
|
|
|
- 'refresh_token' => $result['refresh_token'],
|
|
|
- 'expires_at' => time() + $result['expires_in'],
|
|
|
- ]);
|
|
|
-
|
|
|
- return $result;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 保存 Token
|
|
|
- */
|
|
|
- private function saveHuoLalaToken($tokenData)
|
|
|
- {
|
|
|
- // 实现数据库保存逻辑
|
|
|
- // ...
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取 Token
|
|
|
- */
|
|
|
- private function getHuoLalaToken()
|
|
|
- {
|
|
|
- // 实现数据库获取逻辑
|
|
|
- // ...
|
|
|
- }
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
-### 处理Token过期
|
|
|
-
|
|
|
-```php
|
|
|
-<?php
|
|
|
-/**
|
|
|
- * 检查 Token 是否即将过期,并自动刷新
|
|
|
- */
|
|
|
-class HuoLalaTokenManager
|
|
|
-{
|
|
|
- const REFRESH_THRESHOLD = 7 * 24 * 3600; // 提前7天刷新
|
|
|
-
|
|
|
- public static function ensureTokenValid()
|
|
|
- {
|
|
|
- $token = self::getToken();
|
|
|
-
|
|
|
- // 检查是否即将过期
|
|
|
- if ($token['expires_at'] - time() < self::REFRESH_THRESHOLD) {
|
|
|
- $auth = new Auth();
|
|
|
- $result = $auth->refreshAccessToken($token['refresh_token']);
|
|
|
-
|
|
|
- if ($result['success']) {
|
|
|
- self::saveToken([
|
|
|
- 'access_token' => $result['access_token'],
|
|
|
- 'refresh_token' => $result['refresh_token'],
|
|
|
- 'expires_at' => time() + $result['expires_in'],
|
|
|
- ]);
|
|
|
-
|
|
|
- return $result['access_token'];
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return $token['access_token'];
|
|
|
- }
|
|
|
-
|
|
|
- private static function getToken()
|
|
|
- {
|
|
|
- // 从数据库获取
|
|
|
- }
|
|
|
-
|
|
|
- private static function saveToken($data)
|
|
|
- {
|
|
|
- // 保存到数据库
|
|
|
- }
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-## 常见问题
|
|
|
-
|
|
|
-### Q1: 授权码(code)的有效期是多久?
|
|
|
-
|
|
|
-**A:** 授权码有效期为 1 分钟,且只能使用一次。超期或多次使用都会失效。
|
|
|
-
|
|
|
-### Q2: Access Token 有效期是多久?
|
|
|
-
|
|
|
-**A:** Access Token 的有效期为 3 个月(7776000 秒)。当即将过期时,使用 `Refresh Token` 进行更新。
|
|
|
-
|
|
|
-### Q3: 如何切换生产环境和沙箱环境?
|
|
|
-
|
|
|
-**A:** 通过 `YII_ENV` 环境变量控制:
|
|
|
-- `YII_ENV=production` 为生产环境(`isSandbox=false`)
|
|
|
-- 其他值为沙箱环境(`isSandbox=true`)
|
|
|
-
|
|
|
-### Q4: 刷新 Token 时会改变 Refresh Token 吗?
|
|
|
-
|
|
|
-**A:** 是的,刷新 Token 后会同时返回新的 `access_token` 和 `refresh_token`。建议每次更新都保存新的 Refresh Token。
|
|
|
-
|
|
|
-### Q5: 授权时如何传递自定义参数?
|
|
|
-
|
|
|
-**A:** 可以在 `redirect_uri` 中添加查询参数,例如:
|
|
|
-```php
|
|
|
-$redirectUrl = 'https://your-domain.com/callback?shop_id=123&user_id=456';
|
|
|
-$authUrl = $auth->generateAuthUrl($redirectUrl);
|
|
|
-```
|
|
|
-
|
|
|
-回调时可以直接从 URL 中获取这些参数。
|
|
|
-
|
|
|
-### Q6: 如何处理 HTTP 请求失败?
|
|
|
-
|
|
|
-**A:** Auth 类会自动捕获异常并返回标准格式的错误响应:
|
|
|
-```php
|
|
|
-$result = $auth->getAccessToken($code);
|
|
|
-if (!$result['success']) {
|
|
|
- $error = $result['error'];
|
|
|
- $statusCode = $result['status_code'] ?? null;
|
|
|
- // 处理错误
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-## 参考资源
|
|
|
-
|
|
|
-### 相关接口
|
|
|
-
|
|
|
-- **生成授权URL:** `generateAuthUrl($redirectUri)`
|
|
|
-- **获取Access Token:** `getAccessToken($code)`
|
|
|
-- **刷新Token:** `refreshAccessToken($refreshToken)`
|
|
|
-
|
|
|
-### 类方法
|
|
|
-
|
|
|
-- `getAppKey()` - 获取应用 App Key
|
|
|
-- `getAppSecret()` - 获取应用 App Secret
|
|
|
-- `isSandbox()` - 获取是否为沙箱环境
|
|
|
-
|
|
|
-### 官方文档
|
|
|
-
|
|
|
-- [货拉拉开放平台](https://open.huolala.cn)
|
|
|
-- OAuth 2.0 规范:https://tools.ietf.org/html/rfc6749
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-## 文件位置
|
|
|
-
|
|
|
-```
|
|
|
-common/components/delivery/platform/huolala/Auth.php
|
|
|
-```
|
|
|
-
|
|
|
-## 版本历史
|
|
|
-
|
|
|
-| 版本 | 日期 | 描述 |
|
|
|
-|------|------|------|
|
|
|
-| 1.0 | 2024-10-24 | 初始实现,支持 Authorization Code 流程 |
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-最后更新:2024-10-24
|