diff --git a/.env-example b/.env-example index 009255a..65f8d05 100644 --- a/.env-example +++ b/.env-example @@ -33,7 +33,7 @@ PLAYX_PARTNER_JWT_SECRET= AGENT_AUTH_JWT_SECRET= # token 会话缓存过期时间(秒) PLAYX_SESSION_EXPIRE_SECONDS=3600 -# verifyToken 是否仅本地联调(false=走对方远程校验) +# verifyToken 是否仅本地联调(true=不调 PlayX,用下方默认用户签发 session;false=走 PLAYX_TOKEN_VERIFY_URL) PLAYX_VERIFY_TOKEN_LOCAL_ONLY=false # verifyToken:完整 https URL 时 Body merchant_code+request_date+request_id+token、签名明文同序;相对路径时拼基址+商户四字段 PLAYX_TOKEN_VERIFY_URL=https://callback-mallsys.superior3.net/callback/api/mallsys/plx/auth/verify-token diff --git a/app/api/controller/v1/Playx.php b/app/api/controller/v1/Playx.php index 4948a78..d99fa34 100644 --- a/app/api/controller/v1/Playx.php +++ b/app/api/controller/v1/Playx.php @@ -428,8 +428,8 @@ class Playx extends Api } /** - * Token 验证 - POST /api/v1/playx/verify-token - * 配置 playx.verify_token_local_only=true 时:不向 PlayX 请求,且不校验传入 token(联调占位)。 + * Token 验证 - POST /api/v1/mall/verifyToken + * 配置 playx.verify_token_local_only=true 时:不向 PlayX 请求,按 .env 默认用户签发 session(见 verify_token_local_default_*)。 */ public function verifyToken(Request $request): Response { @@ -439,7 +439,7 @@ class Playx extends Api } if (config('playx.verify_token_local_only', false)) { - return $this->verifyTokenLocalOpen(); + return $this->verifyTokenLocalOpen($request); } $token = strval($request->post('token', $request->post('session', $request->get('token', '')))); @@ -609,14 +609,17 @@ class Playx extends Api } /** - * 本地联调:不校验 token,按配置默认用户签发新 mall_session(待 PlayX 远程校验就绪后关闭 verify_token_local_only)。 + * 本地联调:不调用 PlayX verify-token;身份取自 PLAYX_VERIFY_TOKEN_LOCAL_DEFAULT_*,请求 token 仅作占位(有则通过、无则也通过)。 */ - private function verifyTokenLocalOpen(): Response + private function verifyTokenLocalOpen(Request $request): Response { - $playxUserId = strval(config('playx.verify_token_local_default_user_id', 'testmyr')); - $username = strval(config('playx.verify_token_local_default_username', 'yangyang123')); + $playxUserId = trim(strval(config('playx.verify_token_local_default_user_id', ''))); + $username = trim(strval(config('playx.verify_token_local_default_username', ''))); if ($playxUserId === '') { - return $this->error(__('PlayX API not configured')); + return $this->error(__('PlayX verify token local default user not configured')); + } + if ($username === '') { + $username = $playxUserId; } $asset = $this->ensureAssetForPlayx($playxUserId, $username); @@ -878,6 +881,57 @@ class Playx extends Api return $this->success('', ['list' => $arr]); } + /** + * 按订单 ID 查询订单(仅当前登录用户本人的订单) + * GET /api/v1/mall/order?order_id=xxx + * + * 鉴权:token / session_id / user_id(同 assets/orders) + */ + public function order(Request $request): Response + { + $response = $this->initializeApi($request); + if ($response !== null) { + return $response; + } + + $assetId = $this->resolvePlayxAssetIdFromRequest($request); + if ($assetId === null) { + return $this->error(__('Token expiration'), null, 0, ['statusCode' => 401]); + } + $asset = $this->getAssetById($assetId); + if (!$asset || strval($asset->playx_user_id ?? '') === '') { + return $this->error(__('Record not found')); + } + + $orderId = intval($request->get('order_id', $request->post('order_id', 0))); + if ($orderId <= 0) { + $orderId = intval($request->get('id', $request->post('id', 0))); + } + if ($orderId <= 0) { + return $this->error(__('Missing required fields')); + } + + $playxUserId = strval($asset->playx_user_id); + $order = MallOrder::where('id', $orderId) + ->where('user_id', $playxUserId) + ->with(['mallItem']) + ->find(); + if (!$order) { + return $this->error(__('Record not found')); + } + + $row = $order->toArray(); + if (isset($row['mallItem']) && is_array($row['mallItem'])) { + $row['mallItem'] = $this->applyMallItemLocaleForApi($row['mallItem']); + } + + return $this->success('', [ + 'order_id' => $order->id, + 'status' => $order->status, + 'order' => $row, + ]); + } + /** * 积分流水(领取/兑换/退回/管理员调整) * GET /api/v1/mall/pointsLogs diff --git a/app/api/lang/en.php b/app/api/lang/en.php index 10c99ba..e7d64ea 100644 --- a/app/api/lang/en.php +++ b/app/api/lang/en.php @@ -61,6 +61,7 @@ return [ 'nicknameChsDash' => 'Username may only contain letters, numbers, underscores and dashes', 'Invalid token' => 'Invalid or expired token', 'PlayX API not configured' => 'PlayX API is not configured', + 'PlayX verify token local default user not configured' => 'Local verify user is not configured; set PLAYX_VERIFY_TOKEN_LOCAL_DEFAULT_USER_ID', 'PlayX verify upstream failed' => 'Upstream token verification failed (HTTP %s): %s', 'Duplicate input' => 'Duplicate submission', 'Ok' => 'OK', diff --git a/app/api/lang/ms.php b/app/api/lang/ms.php index dd40eec..e843a06 100644 --- a/app/api/lang/ms.php +++ b/app/api/lang/ms.php @@ -62,6 +62,7 @@ return [ 'nicknameChsDash' => 'Nama pengguna hanya huruf, nombor, garis bawah dan sempang', 'Invalid token' => 'Token tidak sah atau tamat tempoh', 'PlayX API not configured' => 'API PlayX tidak dikonfigurasi', + 'PlayX verify token local default user not configured' => 'Pengguna verifikasi tempatan tidak dikonfigurasi; tetapkan PLAYX_VERIFY_TOKEN_LOCAL_DEFAULT_USER_ID', 'PlayX verify upstream failed' => 'Pengesahan token hulu gagal (HTTP %s): %s', 'Duplicate input' => 'Penghantaran pendua', 'Ok' => 'OK', diff --git a/app/api/lang/zh-cn.php b/app/api/lang/zh-cn.php index c54eec3..09faaac 100644 --- a/app/api/lang/zh-cn.php +++ b/app/api/lang/zh-cn.php @@ -63,6 +63,7 @@ return [ // PlayX API v1 /api/v1/* 'Invalid token' => '令牌无效或已过期', 'PlayX API not configured' => '未配置 PlayX 接口地址', + 'PlayX verify token local default user not configured' => '未配置本地联调用户,请设置 PLAYX_VERIFY_TOKEN_LOCAL_DEFAULT_USER_ID', 'PlayX verify upstream failed' => '上游 Token 校验失败(HTTP %s):%s', 'Duplicate input' => '重复提交', 'Ok' => '成功', diff --git a/config/playx.php b/config/playx.php index 5cbdf8b..9364f2a 100644 --- a/config/playx.php +++ b/config/playx.php @@ -16,10 +16,11 @@ return [ // token 会话缓存过期时间(秒) 'session_expire_seconds' => intval(env('PLAYX_SESSION_EXPIRE_SECONDS', '3600')), /** - * 为 true 时:verifyToken 不向 PlayX 请求;当前实现为联调占位——不校验请求中的 token, - * 每次签发新 session,用户标识见 verify_token_local_default_*(待对方提供校验接口后请设为 false 并走远程校验)。 + * 为 true 时:verifyToken 不向 PlayX 请求;不校验请求 token 真伪,按环境变量默认用户签发 mall_session。 + * 用户标识见 PLAYX_VERIFY_TOKEN_LOCAL_DEFAULT_USER_ID / PLAYX_VERIFY_TOKEN_LOCAL_DEFAULT_USERNAME。 + * 生产对接 playX 时请设为 false 并配置 PLAYX_TOKEN_VERIFY_URL。 */ - 'verify_token_local_only' => filter_var(env('PLAYX_VERIFY_TOKEN_LOCAL_ONLY', '1'), FILTER_VALIDATE_BOOLEAN), + 'verify_token_local_only' => filter_var(env('PLAYX_VERIFY_TOKEN_LOCAL_ONLY', '0'), FILTER_VALIDATE_BOOLEAN), /** verify_token_local_only 为 true 时写入 mall_session 的 playx 用户标识 */ 'verify_token_local_default_user_id' => strval(env('PLAYX_VERIFY_TOKEN_LOCAL_DEFAULT_USER_ID', 'testmyr')), /** verify_token_local_only 为 true 时写入 mall_session 的展示用户名 */ diff --git a/config/route.php b/config/route.php index 5c99e94..432c64f 100644 --- a/config/route.php +++ b/config/route.php @@ -65,6 +65,7 @@ Route::post('/api/v1/mall/bonusRedeem', [\app\api\controller\v1\Playx::class, 'b Route::post('/api/v1/mall/physicalRedeem', [\app\api\controller\v1\Playx::class, 'physicalRedeem']); Route::post('/api/v1/mall/withdrawApply', [\app\api\controller\v1\Playx::class, 'withdrawApply']); Route::get('/api/v1/mall/orders', [\app\api\controller\v1\Playx::class, 'orders']); +Route::get('/api/v1/mall/order', [\app\api\controller\v1\Playx::class, 'order']); Route::get('/api/v1/mall/pointsLogs', [\app\api\controller\v1\Playx::class, 'pointsLogs']); Route::get('/api/v1/mall/addressList', [\app\api\controller\v1\Playx::class, 'addressList']); Route::post('/api/v1/mall/addressAdd', [\app\api\controller\v1\Playx::class, 'addressAdd']); diff --git a/docs/H5-积分商城接口文档.md b/docs/H5-积分商城接口文档.md index 7d3eb45..6a37d9b 100644 --- a/docs/H5-积分商城接口文档.md +++ b/docs/H5-积分商城接口文档.md @@ -19,7 +19,8 @@ - `POST /api/v1/mall/claim` 领取积分(幂等) - `GET /api/v1/mall/items` 获取商品 - `POST /api/v1/mall/bonusRedeem` / `physicalRedeem` / `withdrawApply` 提交兑换/提现 - - `GET /api/v1/mall/orders` 查询订单 + - `GET /api/v1/mall/orders` 查询订单列表 + - `GET /api/v1/mall/order` 按订单 ID 查询单笔订单(含状态) - `GET/POST /api/v1/mall/address*` 管理地址(addressList/addressAdd/addressEdit/addressDelete) ### 1.2 流程 B:playX token 换取 session(兼容) @@ -159,6 +160,33 @@ curl "https://{域名}/api/v1/temLogin?username=test001" --- +### 3.8.1 单笔订单查询(按 ID) + +**GET** ` /api/v1/mall/order ` + +鉴权:`token` / `session_id` / `user_id`(同订单列表) + +参数(Query): +- `order_id`:必填,商城订单主键 `mall_order.id`(兼容参数名 `id`) + +说明: +- 仅能查询当前鉴权用户本人名下的订单;订单不存在或不属于当前用户时返回「记录不存在」,不泄露他人订单信息。 + +返回(成功 `data`): +- `order_id`:订单 ID +- `status`:订单状态(`PENDING` / `COMPLETED` / `SHIPPED` / `REJECTED`) +- `order`:订单详情(字段与订单列表单条一致,含 `reject_reason`、`grant_status`、`mallItem` 等) + +示例: + +```bash +curl -G "https://{域名}/api/v1/mall/order" \ + -H "token: " \ + --data-urlencode "order_id=123" +``` + +--- + ### 3.9 积分流水(领取/兑换/退回) **GET** ` /api/v1/mall/pointsLogs ` @@ -240,6 +268,13 @@ Body 含 `receiver_name`(收货人,建议填写;实物兑换下单快照 - `data.username` - `data.token_expire_at` +**本地联调(不请求 PlayX)**:在 `.env` 中设置 `PLAYX_VERIFY_TOKEN_LOCAL_ONLY=true`,并配置: + +- `PLAYX_VERIFY_TOKEN_LOCAL_DEFAULT_USER_ID`:写入 `mall_session.user_id` / 资产 `playx_user_id` +- `PLAYX_VERIFY_TOKEN_LOCAL_DEFAULT_USERNAME`:写入 `mall_session.username` 与资产展示名 + +此时任意(或空)`token` 均视为验证通过,返回的 `user_id` / `username` 以上述环境变量为准。生产环境请保持 `PLAYX_VERIFY_TOKEN_LOCAL_ONLY=false` 并配置 `PLAYX_TOKEN_VERIFY_URL`。 + --- ## 6. 常见错误与排查