diff --git a/scripts/playx-verify-token-callback-test.php b/scripts/playx-verify-token-callback-test.php new file mode 100644 index 0000000..db0b64c --- /dev/null +++ b/scripts/playx-verify-token-callback-test.php @@ -0,0 +1,131 @@ +" + * + * 可选环境变量(未设置则从项目根 .env 读取): + * PLAYX_TOKEN_VERIFY_URL 默认 https://callback-mallsys.superior3.net/callback/api/mallsys/plx/auth/verify-token + * PLAYX_ANGPOW_IMPORT_AUTH_KEY 与 angpow-imports 相同的 HMAC 密钥 + */ + +$root = dirname(__DIR__); + +function loadDotEnv(string $path): void +{ + if (!is_file($path)) { + return; + } + foreach (file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [] as $line) { + $line = trim($line); + if ($line === '' || str_starts_with($line, '#')) { + continue; + } + if (!str_contains($line, '=')) { + continue; + } + $pos = strpos($line, '='); + $name = trim(substr($line, 0, $pos)); + $value = trim(substr($line, $pos + 1)); + if ($name !== '') { + $_ENV[$name] = $value; + putenv($name . '=' . $value); + } + } +} + +function resolveKeyBytes(string $authKey): string +{ + $maybeBase64 = base64_decode($authKey, true); + if (is_string($maybeBase64) && $maybeBase64 !== '') { + return $maybeBase64; + } + $isHex = ctype_xdigit($authKey) && (strlen($authKey) % 2 === 0); + if ($isHex) { + $hex = hex2bin($authKey); + if (is_string($hex) && $hex !== '') { + return $hex; + } + } + + return $authKey; +} + +function buildSignature(string $input, string $authKey): string +{ + $raw = hash_hmac('sha1', $input, resolveKeyBytes($authKey), true); + + return base64_encode($raw); +} + +loadDotEnv($root . DIRECTORY_SEPARATOR . '.env'); + +$token = $argv[1] ?? ''; +if ($token === '') { + fwrite(STDERR, "用法: php scripts/playx-verify-token-callback-test.php \"\"\n"); + exit(1); +} + +$url = strval($_ENV['PLAYX_TOKEN_VERIFY_URL'] ?? getenv('PLAYX_TOKEN_VERIFY_URL') ?: ''); +if ($url === '') { + $url = 'https://callback-mallsys.superior3.net/callback/api/mallsys/plx/auth/verify-token'; +} + +$authKey = strval($_ENV['PLAYX_ANGPOW_IMPORT_AUTH_KEY'] ?? getenv('PLAYX_ANGPOW_IMPORT_AUTH_KEY') ?: ''); +if ($authKey === '') { + fwrite(STDERR, "缺少 PLAYX_ANGPOW_IMPORT_AUTH_KEY(请在 .env 配置或导出环境变量)\n"); + exit(1); +} + +$requestId = 'mall_cli_' . uniqid(); +$canonical = 'request_id=' . $requestId . '&token=' . $token; +$signature = buildSignature($canonical, $authKey); + +$payload = json_encode([ + 'request_id' => $requestId, + 'token' => $token, +], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); +if ($payload === false) { + fwrite(STDERR, "JSON 编码失败\n"); + exit(1); +} + +$escapedPayload = str_replace("'", "'\\''", $payload); +$escapedSig = str_replace("'", "'\\''", $signature); + +echo "--- 等价 curl(Linux / macOS / Git Bash)---\n"; +echo "curl -sS -X POST '" . $url . "' \\\n"; +echo " -H 'Content-Type: application/json' \\\n"; +echo " -H 'X-Request-Signature: " . $signature . "' \\\n"; +echo " -d '" . $payload . "'\n\n"; + +echo "--- 本机直接请求(PHP stream)---\n"; +echo "request_id={$requestId}\n"; +echo "canonical={$canonical}\n\n"; + +$body = $payload; +$ctx = stream_context_create([ + 'http' => [ + 'method' => 'POST', + 'header' => "Content-Type: application/json\r\nX-Request-Signature: {$signature}\r\n", + 'content' => $body, + 'timeout' => 15, + ], + 'ssl' => [ + 'verify_peer' => true, + 'verify_peer_name' => true, + ], +]); + +$result = @file_get_contents($url, false, $ctx); +if ($result === false) { + $err = error_get_last(); + fwrite(STDERR, "请求失败: " . ($err['message'] ?? 'unknown') . "\n"); + exit(1); +} + +echo $result . "\n";