Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
L
loveisland
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
刘小敏
loveisland
Commits
bdd6d174
Commit
bdd6d174
authored
Jul 16, 2026
by
刘小敏
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix(xhs): 改用静态凭证回传转化事件
parent
dd5ce668
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
95 additions
and
81 deletions
+95
-81
ConversionService.php
addons/shopro/service/xhs/ConversionService.php
+89
-80
config.php
application/config.php
+6
-1
No files found.
addons/shopro/service/xhs/ConversionService.php
View file @
bdd6d174
...
...
@@ -5,13 +5,18 @@ namespace addons\shopro\service\xhs;
use
app\admin\model\shopro\xhs\ConversionLog
as
ConversionLogModel
;
/**
* 小红书聚光
Marketing API
转化回传服务
* 小红书聚光
「种草直达」
转化回传服务
*
* 负责调用小红书开放平台接口回传转化事件
* 包含 Token 管理、API 调用、重试逻辑
* 负责调用小红书转化回传 API 上报转化事件
*
* ⚠️ 使用前需在聚光开放平台 (ad-market.xiaohongshu.com) 申请应用,
* 并在环境变量中配置 PHP_XHS_APP_KEY / PHP_XHS_APP_SECRET
* ⚠️ 凭证说明:
* app_id + access_token 由小红书运营/销售侧提供(静态凭证),非 OAuth 动态获取。
* 配置方式:
* - 环境变量: PHP_XHS_APP_ID / PHP_XHS_ACCESS_TOKEN
* - 或 shopro 配置表 code=xhs_ad 组中配置 APP_ID / ACCESS_TOKEN
*
* 注意:OAuth 接口(app_key + app_secret → /oauth/token)用于管理类 API,
* 转化回传 API 不使用 OAuth token,此处保留 getAccessToken() 仅供其他场景参考。
*/
class
ConversionService
{
...
...
@@ -38,23 +43,23 @@ class ConversionService
* @return string|null
* @throws \Exception
*/
public
function
getAccessToken
()
{
// 检查缓存
$cached
=
redis_cache
(
'?'
.
self
::
TOKEN_CACHE_KEY
);
if
(
$cached
)
{
$tokenData
=
json_decode
(
$cached
,
true
);
if
(
$tokenData
&&
isset
(
$tokenData
[
'token'
]))
{
$now
=
time
();
if
(
$tokenData
[
'expires_at'
]
>
(
$now
+
self
::
TOKEN_REFRESH_AHEAD
))
{
return
$tokenData
[
'token'
];
}
}
}
// 获取新 Token
return
$this
->
refreshAccessToken
();
}
//
public function getAccessToken()
//
{
//
// 检查缓存
//
$cached = redis_cache('?' . self::TOKEN_CACHE_KEY);
//
if ($cached) {
//
$tokenData = json_decode($cached, true);
//
if ($tokenData && isset($tokenData['token'])) {
//
$now = time();
//
if ($tokenData['expires_at'] > ($now + self::TOKEN_REFRESH_AHEAD)) {
//
return $tokenData['token'];
//
}
//
}
//
}
//
// 获取新 Token
//
return $this->refreshAccessToken();
//
}
/**
* 强制刷新 Access Token
...
...
@@ -62,40 +67,40 @@ class ConversionService
* @return string|null
* @throws \Exception
*/
private
function
refreshAccessToken
()
{
$appKey
=
$this
->
getConfig
(
'APP_KEY'
,
''
);
$appSecret
=
$this
->
getConfig
(
'APP_SECRET'
,
''
);
//
private function refreshAccessToken()
//
{
//
$appKey = $this->getConfig('APP_KEY', '');
//
$appSecret = $this->getConfig('APP_SECRET', '');
if
(
empty
(
$appKey
)
||
empty
(
$appSecret
))
{
throw
new
\Exception
(
'小红书聚光 API 凭证未配置(PHP_XHS_APP_KEY / PHP_XHS_APP_SECRET)'
);
}
//
if (empty($appKey) || empty($appSecret)) {
//
throw new \Exception('小红书聚光 API 凭证未配置(PHP_XHS_APP_KEY / PHP_XHS_APP_SECRET)');
//
}
$url
=
self
::
API_BASE
.
self
::
TOKEN_URL
;
$params
=
[
'app_key'
=>
$appKey
,
'app_secret'
=>
$appSecret
,
'grant_type'
=>
'client_credentials'
,
];
//
$url = self::API_BASE . self::TOKEN_URL;
//
$params = [
//
'app_key' => $appKey,
//
'app_secret' => $appSecret,
//
'grant_type' => 'client_credentials',
//
];
$response
=
$this
->
httpPost
(
$url
,
$params
);
//
$response = $this->httpPost($url, $params);
if
(
empty
(
$response
[
'access_token'
]))
{
throw
new
\Exception
(
'获取 access_token 失败: '
.
json_encode
(
$response
));
}
//
if (empty($response['access_token'])) {
//
throw new \Exception('获取 access_token 失败: ' . json_encode($response));
//
}
$token
=
$response
[
'access_token'
];
$expiresIn
=
intval
(
$response
[
'expires_in'
]
??
7200
);
//
$token = $response['access_token'];
//
$expiresIn = intval($response['expires_in'] ?? 7200);
// 缓存 Token
$tokenData
=
[
'token'
=>
$token
,
'expires_at'
=>
time
()
+
$expiresIn
,
];
redis_cache
(
self
::
TOKEN_CACHE_KEY
,
json_encode
(
$tokenData
),
$expiresIn
);
//
// 缓存 Token
//
$tokenData = [
//
'token' => $token,
//
'expires_at' => time() + $expiresIn,
//
];
//
redis_cache(self::TOKEN_CACHE_KEY, json_encode($tokenData), $expiresIn);
return
$token
;
}
//
return $token;
//
}
/**
* 回传转化事件到小红书(对齐官方「种草直达」API 文档)
...
...
@@ -120,15 +125,19 @@ class ConversionService
}
// 获取 app_id 和 access_token
$appId
=
$this
->
getConfig
(
'APP_ID'
,
''
);
try
{
$accessToken
=
$this
->
getAccessToken
(
);
}
catch
(
\Exception
$e
)
{
return
[
'success'
=>
false
,
'response'
=>
'获取 Token 失败: '
.
$e
->
getMessage
()];
}
// 注意:转化回传 API 的 access_token 不同于管理 API 的 OAuth token
// 官方文档说明:"app_id和access_token需要联系您的产品获取",即由小红书运营/销售侧提供的静态凭证
$appId
=
config
(
'shopro.xhs_app_id'
);
$accessToken
=
config
(
'shopro.xhs_access_token'
);
custom_log
(
'[xhs.context] 配置调试:app_id:'
.
$appId
.
'accessToken:'
.
$accessToken
,
0
,
'xhs_ad'
,
'info'
);
if
(
empty
(
$appId
))
{
return
[
'success'
=>
false
,
'response'
=>
'app_id 未配置'
];
return
[
'success'
=>
false
,
'response'
=>
'app_id 未配置(需联系小红书运营获取)'
];
}
if
(
empty
(
$accessToken
))
{
return
[
'success'
=>
false
,
'response'
=>
'access_token 未配置(需联系小红书运营获取)'
];
}
// 转换事件类型为小红书平台编码
...
...
@@ -365,30 +374,30 @@ class ConversionService
return
$map
[
$type
]
??
0
;
}
/**
* 获取配置(优先环境变量,其次 shopro 配置组)
*
* @param string $key
* @param mixed $default
* @return mixed
*/
private
function
getConfig
(
$key
,
$default
=
null
)
{
// 1. 优先环境变量(如 PHP_XHS_APP_
KEY / PHP_XHS_APP_SECRET
)
$envKey
=
'PHP_XHS_'
.
strtoupper
(
$key
);
$envValue
=
getenv
(
$envKey
);
if
(
$envValue
!==
false
&&
$envValue
!==
''
)
{
return
$envValue
;
}
// 2. 从 shopro 配置组中获取(需先在 fa_shopro_config 表添加 code=xhs_ad 的配置组)
$config
=
sheep_config
(
'xhs_ad'
);
if
(
$config
&&
isset
(
$config
[
$key
]))
{
return
$config
[
$key
];
}
return
$default
;
}
/
/ /
**
//
* 获取配置(优先环境变量,其次 shopro 配置组)
//
*
//
* @param string $key
//
* @param mixed $default
//
* @return mixed
//
*/
//
private function getConfig($key, $default = null)
//
{
// 1. 优先环境变量(如 PHP_XHS_APP_
ID / PHP_XHS_ACCESS_TOKEN
)
//
$envKey = 'PHP_XHS_' . strtoupper($key);
//
$envValue = getenv($envKey);
//
if ($envValue !== false && $envValue !== '') {
//
return $envValue;
//
}
//
//
2. 从 shopro 配置组中获取(需先在 fa_shopro_config 表添加 code=xhs_ad 的配置组)
//
$config = sheep_config('xhs_ad');
//
if ($config && isset($config[$key])) {
//
return $config[$key];
//
}
//
return $default;
//
}
/**
* HTTP POST (form-urlencoded)
...
...
application/config.php
View file @
bdd6d174
...
...
@@ -335,7 +335,12 @@ return [
'obs_access_key'
=>
'HPUAWU3GPS8HEWX9JFLG'
,
'obs_secret_key'
=>
'Jj7Vt1WnU69pWG6QplBmHBkaw6WM4oLU1AZa9Z9n'
,
'obs_endpoint'
=>
'obs.cn-east-3.myhuaweicloud.com'
,
// 根据区域修改
'obs_bucket'
=>
'beevera'
'obs_bucket'
=>
'beevera'
,
// 小红书配置
'xhs_app_id'
=>
Env
::
get
(
'xhs.app_id'
,
''
),
'xhs_access_token'
=>
Env
::
get
(
'xhs.access_token'
,
''
),
],
];
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment