新增物流中专
This commit is contained in:
@@ -56,23 +56,25 @@ class GancaoCallbackController extends BaseApiController
|
||||
*/
|
||||
public function orderStatus(): Response
|
||||
{
|
||||
$rawBody = (string) file_get_contents('php://input');
|
||||
$headers = $this->request->header();
|
||||
|
||||
$accessAppkey = (string) $this->pickHeader($headers, ['access-appkey', 'accessappkey', 'x-access-appkey']);
|
||||
$accessNonce = (string) $this->pickHeader($headers, ['access-nonce', 'accessnonce', 'x-access-nonce']);
|
||||
$accessTimestamp = (string) $this->pickHeader($headers, ['access-timestamp', 'accesstimestamp', 'x-access-timestamp']);
|
||||
$accessSign = (string) $this->pickHeader($headers, ['access-sign', 'accesssign', 'x-access-sign']);
|
||||
|
||||
Log::info(sprintf(
|
||||
'Gancao callback received | appkey=%s | nonce=%s | ts=%s | sign=%s | body=%s | headers=%s',
|
||||
$accessAppkey !== '' ? $accessAppkey : '(empty)',
|
||||
$accessNonce !== '' ? $accessNonce : '(empty)',
|
||||
$accessTimestamp !== '' ? $accessTimestamp : '(empty)',
|
||||
$accessSign !== '' ? $accessSign : '(empty)',
|
||||
$rawBody,
|
||||
json_encode($headers, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
|
||||
));
|
||||
|
||||
try {
|
||||
$rawBody = file_get_contents('php://input');
|
||||
|
||||
$headers = $this->request->header();
|
||||
$accessAppkey = (string) ($headers['access-appkey'] ?? '');
|
||||
$accessNonce = (string) ($headers['access-nonce'] ?? '');
|
||||
$accessTimestamp = (string) ($headers['access-timestamp'] ?? '');
|
||||
$accessSign = (string) ($headers['access-sign'] ?? '');
|
||||
|
||||
Log::info('Gancao callback received', [
|
||||
'appkey' => $accessAppkey,
|
||||
'nonce' => $accessNonce,
|
||||
'ts' => $accessTimestamp,
|
||||
'sign' => $accessSign,
|
||||
'body' => $rawBody,
|
||||
]);
|
||||
|
||||
if (!$this->verifySign($accessAppkey, $accessNonce, $accessTimestamp, $accessSign, $rawBody)) {
|
||||
Log::warning('Gancao callback sign verification failed');
|
||||
return $this->ok();
|
||||
@@ -100,27 +102,69 @@ class GancaoCallbackController extends BaseApiController
|
||||
/* 签名验证 */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* 兼容多种 header key 命名(ThinkPHP 默认都会统一成小写-连字符,但不同反向代理/php-fpm 下可能变体)
|
||||
*
|
||||
* @param array<string, string|array<int, string>> $headers
|
||||
* @param array<int, string> $candidates 按优先级排列的 header key
|
||||
*/
|
||||
private function pickHeader(array $headers, array $candidates): string
|
||||
{
|
||||
foreach ($candidates as $key) {
|
||||
if (!isset($headers[$key])) {
|
||||
continue;
|
||||
}
|
||||
$v = $headers[$key];
|
||||
if (is_array($v)) {
|
||||
$v = reset($v);
|
||||
}
|
||||
$v = trim((string) $v);
|
||||
if ($v !== '') {
|
||||
return $v;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* md5(access-appkey + secret-key + access-nonce + access-timestamp + $sBody)
|
||||
*
|
||||
* 注意:回调签名使用的是「回调通知账号」—— callback_appkey / callback_secret,
|
||||
* 与下单使用的 biz_ak / biz_sk 是不同的两套凭证。
|
||||
*/
|
||||
private function verifySign(string $appkey, string $nonce, string $timestamp, string $sign, string $body): bool
|
||||
{
|
||||
if ($sign === '' || $appkey === '') {
|
||||
$config = Config::get('gancao_scm', []);
|
||||
$cfgAppkey = (string) ($config['callback_appkey'] ?? '');
|
||||
$secretKey = (string) ($config['callback_secret'] ?? '');
|
||||
|
||||
if ($appkey === '' || $sign === '') {
|
||||
Log::warning(sprintf(
|
||||
'Gancao callback missing header | appkey=%s | sign=%s',
|
||||
$appkey !== '' ? $appkey : '(empty)',
|
||||
$sign !== '' ? $sign : '(empty)'
|
||||
));
|
||||
return false;
|
||||
}
|
||||
|
||||
$config = Config::get('gancao_scm', []);
|
||||
$cfgAppkey = (string) ($config['biz_ak'] ?? '');
|
||||
$secretKey = (string) ($config['biz_sk'] ?? '');
|
||||
|
||||
if ($appkey !== $cfgAppkey) {
|
||||
Log::warning('Gancao callback appkey mismatch', compact('appkey', 'cfgAppkey'));
|
||||
Log::warning(sprintf(
|
||||
'Gancao callback appkey mismatch | received=%s | expected(config.callback_appkey)=%s',
|
||||
$appkey,
|
||||
$cfgAppkey !== '' ? $cfgAppkey : '(empty, check GANCAO_SCM_CALLBACK_APPKEY in .env)'
|
||||
));
|
||||
return false;
|
||||
}
|
||||
|
||||
$expected = md5($appkey . $secretKey . $nonce . $timestamp . $body);
|
||||
if (!hash_equals($expected, $sign)) {
|
||||
Log::warning('Gancao callback sign mismatch', compact('sign', 'expected'));
|
||||
Log::warning(sprintf(
|
||||
'Gancao callback sign mismatch | received=%s | expected=%s | nonce=%s | ts=%s',
|
||||
$sign,
|
||||
$expected,
|
||||
$nonce,
|
||||
$timestamp
|
||||
));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,9 @@ return [
|
||||
'biz_ak' => $bizAk,
|
||||
'biz_sk' => $bizSk,
|
||||
'callback_url' => (string) zyt_gancao_scm_env('CALLBACK_URL', ''),
|
||||
// 回调签名凭证:与下单业务账号 biz_ak/biz_sk 不同,由甘草控制台「订单流转回调通知」配置
|
||||
'callback_appkey' => (string) zyt_gancao_scm_env('CALLBACK_APPKEY', ''),
|
||||
'callback_secret' => (string) zyt_gancao_scm_env('CALLBACK_SECRET', ''),
|
||||
'cradle_store' => (string) zyt_gancao_scm_env('CRADLE_STORE', '甄养堂互联网医院'),
|
||||
'df_id' => (int) zyt_gancao_scm_env('DF_ID', 102),
|
||||
'express_type' => (string) zyt_gancao_scm_env('EXPRESS_TYPE', 'general'),
|
||||
|
||||
Reference in New Issue
Block a user