diff --git a/.env-example b/.env-example index c21ce8c..296d056 100644 --- a/.env-example +++ b/.env-example @@ -32,6 +32,12 @@ PLAYX_PARTNER_JWT_SECRET= AGENT_AUTH_JWT_SECRET= # token 会话缓存过期时间(秒) PLAYX_SESSION_EXPIRE_SECONDS=3600 +# verifyToken 是否仅本地联调(false=走对方远程校验) +PLAYX_VERIFY_TOKEN_LOCAL_ONLY=false +# verifyToken 仅本地联调时的默认用户ID +PLAYX_VERIFY_TOKEN_LOCAL_DEFAULT_USER_ID=testmyr +# verifyToken 仅本地联调时的默认用户名 +PLAYX_VERIFY_TOKEN_LOCAL_DEFAULT_USERNAME=yangyang123 # PlayX API(商城调用 PlayX 时使用) PLAYX_API_BASE_URL= PLAYX_API_SECRET_KEY= diff --git a/app/api/controller/v1/Playx.php b/app/api/controller/v1/Playx.php index 43a648d..700b042 100644 --- a/app/api/controller/v1/Playx.php +++ b/app/api/controller/v1/Playx.php @@ -411,21 +411,47 @@ class Playx extends Api return $this->error(__('Token expiration'), null, 0, ['statusCode' => 401]); } - $baseUrl = config('playx.api.base_url', ''); + $baseUrl = config('playx.angpow_import.base_url', ''); $verifyUrl = config('playx.api.token_verify_url', '/api/v1/auth/verify-token'); if ($baseUrl === '') { return $this->error(__('PlayX API not configured')); } try { + $merchantCode = strval(config('playx.angpow_import.merchant_code', '')); + $authKey = strval(config('playx.angpow_import.auth_key', '')); + if ($merchantCode === '' || $authKey === '') { + return $this->error(__('PlayX API not configured')); + } + + $requestId = 'mall_' . uniqid(); + $requestDate = gmdate('Y-m-d H:i:s'); + $signatureInput = 'merchant_code=' . $merchantCode + . '&request_date=' . $requestDate + . '&request_id=' . $requestId + . '&token=' . $token; + $signature = $this->buildPlayxTokenVerifySignature($signatureInput, $authKey); + if ($signature === null) { + return $this->error(__('Invalid signature'), null, 0, ['statusCode' => 500]); + } + $client = new \GuzzleHttp\Client([ 'base_uri' => rtrim($baseUrl, '/') . '/', 'timeout' => 10, ]); $res = $client->post($verifyUrl, [ + 'headers' => [ + 'Content-Type' => 'application/json', + 'X-Request-Signature' => $signature, + 'X-Signature' => $signature, + 'X-Request-Date' => $requestDate, + 'X-Request-ID' => $requestId, + ], 'json' => [ - 'request_id' => 'mall_' . uniqid(), - 'token' => $token, + 'merchant_code' => $merchantCode, + 'request_date' => $requestDate, + 'request_id' => $requestId, + 'token' => $token, ], ]); $code = $res->getStatusCode(); @@ -505,6 +531,40 @@ class Playx extends Api ]); } + /** + * 生成 token verify 所需签名:Base64(HMAC-SHA1(canonical, key)) + */ + private function buildPlayxTokenVerifySignature(string $input, string $authKey): ?string + { + $keyBytes = null; + + $maybeBase64 = base64_decode($authKey, true); + if (is_string($maybeBase64) && $maybeBase64 !== '') { + $keyBytes = $maybeBase64; + } + + if ($keyBytes === null) { + $isHex = ctype_xdigit($authKey) && (strlen($authKey) % 2 === 0); + if ($isHex) { + $hex = hex2bin($authKey); + if (is_string($hex) && $hex !== '') { + $keyBytes = $hex; + } + } + } + + if ($keyBytes === null) { + $keyBytes = $authKey; + } + + $raw = hash_hmac('sha1', $input, $keyBytes, true); + if (!is_string($raw) || $raw === '') { + return null; + } + + return base64_encode($raw); + } + /** * 用户资产 * GET /api/v1/playx/assets?user_id=xxx