Ver Fonte

游客订单

chengwl há 1 dia atrás
pai
commit
c8e0b8ebe3

+ 84 - 12
packages/Webkul/BagistoApi/src/State/CustomerOrderProvider.php

@@ -9,8 +9,11 @@ use ApiPlatform\State\Pagination\Pagination;
 use ApiPlatform\State\ProviderInterface;
 use Illuminate\Pagination\LengthAwarePaginator;
 use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\Request;
 use Webkul\BagistoApi\Exception\AuthorizationException;
 use Webkul\BagistoApi\Exception\ResourceNotFoundException;
+use Webkul\BagistoApi\Facades\CartTokenFacade;
+use Webkul\BagistoApi\Facades\TokenHeaderFacade;
 use Webkul\BagistoApi\Models\CustomerOrder;
 use Webkul\Customer\Models\Customer;
 
@@ -31,15 +34,23 @@ class CustomerOrderProvider implements ProviderInterface
      */
     public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
     {
+        $isCollection = $operation instanceof GetCollection
+            || $operation instanceof \ApiPlatform\Metadata\GraphQl\QueryCollection;
+
         $customer = Auth::guard('sanctum')->user();
 
-        if (! $customer) {
+        // Collection API remains customer-only.
+        if ($isCollection && ! $customer) {
             throw new AuthorizationException(__('bagistoapi::app.graphql.logout.unauthenticated'));
         }
 
         /** Single item — GET /api/shop/customer-orders/{id} */
-        if (! $operation instanceof GetCollection && ! ($operation instanceof \ApiPlatform\Metadata\GraphQl\QueryCollection)) {
-            return $this->provideItem($customer, $uriVariables);
+        if (! $isCollection) {
+            if ($customer) {
+                return $this->provideCustomerItem($customer, $uriVariables);
+            }
+
+            return $this->provideGuestItem($uriVariables);
         }
 
         return $this->provideCollection($customer, $context);
@@ -48,21 +59,39 @@ class CustomerOrderProvider implements ProviderInterface
     /**
      * Return a single order owned by the customer
      */
-    private function provideItem(object $customer, array $uriVariables): CustomerOrder
+    private function provideCustomerItem(object $customer, array $uriVariables): CustomerOrder
     {
-        $id = $uriVariables['id'] ?? null;
+        $order = $this->baseOrderDetailsQuery()
+            ->where('customer_id', $customer->id)
+            ->where('customer_type', Customer::class)
+            ->find($this->resolveOrderId($uriVariables));
 
-        if (! $id) {
-            throw new ResourceNotFoundException(__('bagistoapi::app.graphql.customer-order.id-required'));
+        if (! $order) {
+            throw new ResourceNotFoundException(
+                __('bagistoapi::app.graphql.customer-order.not-found', ['id' => $uriVariables['id'] ?? null])
+            );
         }
 
-        $orderQuery = CustomerOrder::with(['items', 'addresses', 'payment', 'shipments.items', 'shipments.shippingAddress'])
-            ->where('customer_id', $customer->id)
-            ->where('customer_type', Customer::class);
+        return $order;
+    }
 
-        $order = $orderQuery->find($id);
+    /**
+     * Return a single order for a guest token owner.
+     */
+    private function provideGuestItem(array $uriVariables): CustomerOrder
+    {
+        $id = $this->resolveOrderId($uriVariables);
 
-        if (! $order) {
+        $request = Request::instance();
+        $token = $request ? TokenHeaderFacade::getAuthorizationBearerToken($request) : null;
+
+        if (! $token || CartTokenFacade::getTokenType($token) !== 'guest') {
+            throw new AuthorizationException(__('bagistoapi::app.graphql.logout.unauthenticated'));
+        }
+
+        $order = $this->baseOrderDetailsQuery()->find($id);
+
+        if (! $order || ! $this->guestTokenOwnsOrder($token, $order)) {
             throw new ResourceNotFoundException(
                 __('bagistoapi::app.graphql.customer-order.not-found', ['id' => $id])
             );
@@ -71,6 +100,49 @@ class CustomerOrderProvider implements ProviderInterface
         return $order;
     }
 
+    /**
+     * Shared order detail relations for single-order APIs.
+     */
+    private function baseOrderDetailsQuery()
+    {
+        return CustomerOrder::with(['items', 'addresses', 'payment', 'shipments.items', 'shipments.shippingAddress']);
+    }
+
+    private function resolveOrderId(array $uriVariables): int
+    {
+        $id = $uriVariables['id'] ?? null;
+
+        if (! $id || ! is_numeric($id)) {
+            throw new ResourceNotFoundException(__('bagistoapi::app.graphql.customer-order.id-required'));
+        }
+
+        return (int) $id;
+    }
+
+    /**
+     * Same ownership check used by CancelOrder: guest can access only orders
+     * paid with their own cart token.
+     */
+    private function guestTokenOwnsOrder(string $token, CustomerOrder $order): bool
+    {
+        $additional = $order->payment?->additional ?? [];
+        $expectedToken = $additional['cart_token'] ?? null;
+
+        if ($expectedToken && hash_equals((string) $expectedToken, $token)) {
+            return true;
+        }
+
+        $tokenRecord = CartTokenFacade::getGuestTokenRecord($token);
+
+        if (! $tokenRecord) {
+            return false;
+        }
+
+        $expectedCartId = $additional['cart_id'] ?? null;
+
+        return $expectedCartId && (int) $tokenRecord->cart_id === (int) $expectedCartId;
+    }
+
     /**
      * Enable debug dumps only when explicitly requested via header:
      * X-DEBUG-CUSTOMER-ORDER: 1