@fleetbase/storefront-engine 0.3.26 → 0.3.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/composer.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fleetbase/storefront-api",
3
- "version": "0.3.26",
3
+ "version": "0.3.27",
4
4
  "description": "Headless Commerce & Marketplace Extension for Fleetbase",
5
5
  "keywords": [
6
6
  "fleetbase-extension",
package/extension.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "Storefront",
3
- "version": "0.3.26",
3
+ "version": "0.3.27",
4
4
  "description": "Headless Commerce & Marketplace Extension for Fleetbase",
5
5
  "repository": "https://github.com/fleetbase/storefront",
6
6
  "license": "AGPL-3.0-or-later",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fleetbase/storefront-engine",
3
- "version": "0.3.26",
3
+ "version": "0.3.27",
4
4
  "description": "Headless Commerce & Marketplace Extension for Fleetbase",
5
5
  "fleetbase": {
6
6
  "route": "storefront",
@@ -19,6 +19,7 @@ use Fleetbase\Storefront\Http\Requests\InitializeCheckoutRequest;
19
19
  use Fleetbase\Storefront\Models\Cart;
20
20
  use Fleetbase\Storefront\Models\Checkout;
21
21
  use Fleetbase\Storefront\Models\Customer;
22
+ use Fleetbase\Storefront\Models\FoodTruck;
22
23
  use Fleetbase\Storefront\Models\Gateway;
23
24
  use Fleetbase\Storefront\Models\Product;
24
25
  use Fleetbase\Storefront\Models\Store;
@@ -475,8 +476,8 @@ class CheckoutController extends Controller
475
476
  $invoiceAmount = $amount;
476
477
  $invoiceCode = $gateway->sandbox ? 'TEST_INVOICE' : $gateway->config->invoice_id;
477
478
  $invoiceDescription = $about->name . ' cart checkout';
478
- $invoiceReceiverCode = $gateway->public_id;
479
- $senderInvoiceNo = $cart->id;
479
+ $invoiceReceiverCode = $checkout->public_id;
480
+ $senderInvoiceNo = $checkout->public_id;
480
481
 
481
482
  // Create qpay invoice
482
483
  $invoice = $qpay->createSimpleInvoice($invoiceAmount, $invoiceCode, $invoiceDescription, $invoiceReceiverCode, $senderInvoiceNo, $callbackUrl);
@@ -491,69 +492,137 @@ class CheckoutController extends Controller
491
492
  ]);
492
493
  }
493
494
 
495
+ /**
496
+ * Capture and process QPay callback.
497
+ *
498
+ * This controller method handles QPay callback requests by verifying and processing
499
+ * payment information for a specified checkout. It performs the following steps:
500
+ *
501
+ * - Retrieves the checkout identifier from the request.
502
+ * - Looks up the associated Checkout and Gateway records.
503
+ * - If in sandbox mode and a test scenario is provided, it simulates a test payment
504
+ * response for either success or error scenarios.
505
+ * - Initializes a QPay instance with the gateway configuration and sets the authentication token.
506
+ * - Retrieves the invoice ID from the checkout options and performs a payment check using QPay's API.
507
+ * - Publishes the payment data or error response to the SocketCluster channel.
508
+ *
509
+ * Depending on the 'respond' flag from the request, the method returns a JSON response
510
+ * or completes the processing without returning data.
511
+ *
512
+ * @param Request $request The HTTP request containing:
513
+ * - `checkout` (string): The public checkout identifier.
514
+ * - `respond` (boolean): Whether to return a JSON response.
515
+ * - `test` (string|null): A test scenario indicator ('success' or 'error') for sandbox mode.
516
+ *
517
+ * @return \Illuminate\Http\JsonResponse a JSON response with payment data or error details
518
+ *
519
+ * @throws \Exception if an error occurs during payment processing, an API error is returned
520
+ */
494
521
  public function captureQPayCallback(Request $request)
495
522
  {
496
- $checkoutId = $request->input('checkout');
497
- $respond = $request->boolean('respond');
498
- $test = $request->input('test'); // success, error
499
-
500
- if ($checkoutId) {
501
- // Get the checkout instance
502
- $checkout = Checkout::where('public_id', $checkoutId)->first();
503
- if ($checkout) {
504
- // Restore the payment gateway instance
505
- $gateway = Gateway::where('uuid', $checkout->gateway_uuid)->first();
506
- if ($gateway) {
507
- try {
508
- // If testing send back test data
509
- // Handle test below
510
- if ($gateway->sandbox && is_string($test) && in_array($test, ['success', 'error'])) {
511
- $data = ['error' => null, 'checkout' => $checkout->public_id, 'payment' => null];
512
- if ($test === 'success') {
513
- $data['payment'] = QPay::createTestPaymentDataFromCheckout($checkout);
514
- } else {
515
- $data['error'] = ['error' => 'PAYMENT_NOT_PAID', 'message' => 'Payment has not paid!'];
516
- }
517
-
518
- SocketClusterService::publish('checkout.' . $checkout->public_id, $data);
519
- if ($respond) {
520
- return response()->json($data);
521
- }
522
-
523
- return;
524
- }
525
-
526
- // Create qpay instance
527
- $qpay = QPay::instance($gateway->config->username, $gateway->config->password, $gateway->callback_url);
528
- if ($gateway->sandbox) {
529
- $qpay = $qpay->useSandbox();
530
- }
531
-
532
- // Set auth token
533
- $qpay = $qpay->setAuthToken();
534
-
535
- // Check payment status from qpay
536
- $payment = $qpay->paymentGet($checkoutId);
537
- if ($payment) {
538
- $data = [];
539
- if ($payment->error) {
540
- $data = ['error' => (array) $payment, 'checkout' => $checkout->public_id, 'payment' => null];
541
- } else {
542
- $data = ['payment' =>(array) $payment, 'checkout' => $checkout->public_id, 'error' => null];
543
- }
544
-
545
- SocketClusterService::publish('checkout.' . $checkout->public_id, $data);
546
- if ($respond) {
547
- return response()->json($data);
548
- }
549
- }
550
- } catch (\Exception $e) {
551
- Log::error('[QPAY CHECKOUT ERROR: ' . $e->getMessage() . ']', $checkout->toArray());
552
- if ($respond) {
553
- return response()->apiError($e->getMessage());
554
- }
555
- }
523
+ $checkoutId = $request->input('checkout');
524
+ $shouldRespond = $request->boolean('respond');
525
+ $testScenario = $request->input('test'); // Expected: 'success' or 'error'
526
+
527
+ if (!$checkoutId) {
528
+ return response()->json([
529
+ 'error' => 'CHECKOUT_ID_MISSING',
530
+ 'checkout' => null,
531
+ 'payment' => null,
532
+ ]);
533
+ }
534
+
535
+ $checkout = Checkout::where('public_id', $checkoutId)->first();
536
+ if (!$checkout) {
537
+ return response()->json([
538
+ 'error' => 'CHECKOUT_SESSION_NOT_FOUND',
539
+ 'checkout' => null,
540
+ 'payment' => null,
541
+ ]);
542
+ }
543
+
544
+ $gateway = Gateway::where('uuid', $checkout->gateway_uuid)->first();
545
+ if (!$gateway) {
546
+ return response()->json([
547
+ 'error' => 'GATEWAY_NOT_CONFIGURED',
548
+ 'checkout' => $checkout->public_id,
549
+ 'payment' => null,
550
+ ]);
551
+ }
552
+
553
+ try {
554
+ // Handle test scenarios if in sandbox mode.
555
+ if ($gateway->sandbox && in_array($testScenario, ['success', 'error'], true)) {
556
+ $data = [
557
+ 'checkout' => $checkout->public_id,
558
+ 'payment' => null,
559
+ 'error' => null,
560
+ ];
561
+
562
+ if ($testScenario === 'success') {
563
+ $data['payment'] = QPay::createTestPaymentDataFromCheckout($checkout);
564
+ } else {
565
+ $data['error'] = [
566
+ 'error' => 'PAYMENT_NOT_PAID',
567
+ 'message' => 'Payment has not been paid!',
568
+ ];
556
569
  }
570
+
571
+ SocketClusterService::publish('checkout.' . $checkout->public_id, $data);
572
+
573
+ return $shouldRespond ? response()->json($data) : response()->json();
574
+ }
575
+
576
+ // Create the QPay instance.
577
+ $qpay = QPay::instance(
578
+ $gateway->config->username,
579
+ $gateway->config->password,
580
+ $gateway->callback_url
581
+ );
582
+
583
+ if ($gateway->sandbox) {
584
+ $qpay->useSandbox();
585
+ }
586
+
587
+ $qpay->setAuthToken();
588
+
589
+ $invoiceId = $checkout->getOption('qpay_invoice_id');
590
+ if (!$invoiceId) {
591
+ Log::error("Missing QPay invoice ID for checkout: {$checkout->public_id}");
592
+
593
+ return response()->json([
594
+ 'error' => 'MISSING_INVOICE_ID',
595
+ 'checkout' => $checkout->public_id,
596
+ 'payment' => null,
597
+ ]);
598
+ }
599
+
600
+ $paymentCheck = $qpay->paymentCheck($invoiceId);
601
+
602
+ if (!$paymentCheck || empty($paymentCheck->count) || $paymentCheck->count < 1) {
603
+ return response()->json([
604
+ 'error' => 'PAYMENT_NOTFOUND',
605
+ 'checkout' => $checkout->public_id,
606
+ 'payment' => null,
607
+ ]);
608
+ }
609
+
610
+ $payment = data_get($paymentCheck, 'rows.0');
611
+ if ($payment) {
612
+ $data = [
613
+ 'checkout' => $checkout->public_id,
614
+ 'payment' => (array) $payment,
615
+ 'error' => null,
616
+ ];
617
+
618
+ SocketClusterService::publish('checkout.' . $checkout->public_id, $data);
619
+
620
+ return $shouldRespond ? response()->json($data) : response()->json();
621
+ }
622
+ } catch (\Exception $e) {
623
+ Log::error('[QPAY CHECKOUT ERROR]: ' . $e->getMessage(), ['checkout' => $checkout->toArray()]);
624
+ if ($shouldRespond) {
625
+ return response()->apiError($e->getMessage());
557
626
  }
558
627
  }
559
628
 
@@ -738,6 +807,18 @@ class CheckoutController extends Controller
738
807
  $origin = Arr::first($origin);
739
808
  }
740
809
 
810
+ // Check if the order origin is from a food truck via cart property
811
+ $foodTruck = collect($cart->items)
812
+ ->map(function ($cartItem) {
813
+ return data_get($cartItem, 'food_truck_id');
814
+ })
815
+ ->unique()
816
+ ->filter()
817
+ ->map(function ($foodTruckId) {
818
+ return FoodTruck::where('public_id', $foodTruckId)->first();
819
+ })
820
+ ->first();
821
+
741
822
  // if there is no origin attempt to get from cart
742
823
  if (!$origin) {
743
824
  $storeLocation = collect($cart->items)->map(function ($cartItem) {
@@ -818,6 +899,11 @@ class CheckoutController extends Controller
818
899
  ...$transactionDetails,
819
900
  ]);
820
901
 
902
+ // if there is a food truck include it in the order meta
903
+ if ($foodTruck) {
904
+ $orderMeta['food_truck_id'] = $foodTruck->public_id;
905
+ }
906
+
821
907
  // initialize order creation input
822
908
  $orderInput = [
823
909
  'company_uuid' => $store->company_uuid ?? session('company'),
@@ -5,6 +5,7 @@ namespace Fleetbase\Storefront\Http\Controllers\v1;
5
5
  use Fleetbase\Http\Controllers\Controller;
6
6
  use Fleetbase\Storefront\Http\Resources\FoodTruck as FoodTruckResource;
7
7
  use Fleetbase\Storefront\Models\FoodTruck;
8
+ use Illuminate\Database\Eloquent\ModelNotFoundException;
8
9
  use Illuminate\Http\Request;
9
10
 
10
11
  class FoodTruckController extends Controller
@@ -36,4 +37,22 @@ class FoodTruckController extends Controller
36
37
 
37
38
  return FoodTruckResource::collection($results);
38
39
  }
40
+
41
+ /**
42
+ * Finds a single Storefront FoodTruck resources.
43
+ *
44
+ * @return \Fleetbase\Http\Resources\EntityCollection
45
+ */
46
+ public function find($id)
47
+ {
48
+ // find for the food truck
49
+ try {
50
+ $foodTruck = FoodTruck::findRecordOrFail($id);
51
+ } catch (ModelNotFoundException $exception) {
52
+ return response()->error('Food Truck resource not found.');
53
+ }
54
+
55
+ // response the product resource
56
+ return new FoodTruckResource($foodTruck);
57
+ }
39
58
  }
@@ -20,23 +20,24 @@ class FoodTruck extends FleetbaseResource
20
20
  public function toArray($request)
21
21
  {
22
22
  return [
23
- 'id' => $this->when(Http::isInternalRequest(), $this->id, $this->public_id),
24
- 'uuid' => $this->when(Http::isInternalRequest(), $this->uuid),
25
- 'public_id' => $this->when(Http::isInternalRequest(), $this->public_id),
26
- 'company_uuid' => $this->when(Http::isInternalRequest(), $this->company_uuid),
27
- 'created_by_uuid' => $this->when(Http::isInternalRequest(), $this->created_by_uuid),
28
- 'store_uuid' => $this->when(Http::isInternalRequest(), $this->store_uuid),
29
- 'service_area_uuid' => $this->when(Http::isInternalRequest(), $this->service_area_uuid),
30
- 'zone_uuid' => $this->when(Http::isInternalRequest(), $this->zone_uuid),
31
- 'vehicle_uuid' => $this->when(Http::isInternalRequest(), $this->vehicle_uuid),
32
- 'vehicle' => $this->vehicle ? new VehicleResource($this->vehicle) : null,
33
- 'service_area' => $this->serviceArea ? new ServiceAreaResource($this->serviceArea) : null,
34
- 'zone' => $this->zone ? new ZoneResource($this->zone) : null,
35
- 'catalogs' => Catalog::collection($this->catalogs ?? []),
36
- 'online' => $this->vehicle ? $this->vehicle->online : false,
37
- 'status' => $this->status,
38
- 'created_at' => $this->created_at,
39
- 'updated_at' => $this->updated_at,
23
+ 'id' => $this->when(Http::isInternalRequest(), $this->id, $this->public_id),
24
+ 'uuid' => $this->when(Http::isInternalRequest(), $this->uuid),
25
+ 'public_id' => $this->when(Http::isInternalRequest(), $this->public_id),
26
+ 'company_uuid' => $this->when(Http::isInternalRequest(), $this->company_uuid),
27
+ 'created_by_uuid' => $this->when(Http::isInternalRequest(), $this->created_by_uuid),
28
+ 'store_uuid' => $this->when(Http::isInternalRequest(), $this->store_uuid),
29
+ 'service_area_uuid' => $this->when(Http::isInternalRequest(), $this->service_area_uuid),
30
+ 'zone_uuid' => $this->when(Http::isInternalRequest(), $this->zone_uuid),
31
+ 'vehicle_uuid' => $this->when(Http::isInternalRequest(), $this->vehicle_uuid),
32
+ 'vehicle' => $this->vehicle ? new VehicleResource($this->vehicle) : null,
33
+ 'service_area' => $this->serviceArea ? new ServiceAreaResource($this->serviceArea) : null,
34
+ 'zone' => $this->zone ? new ZoneResource($this->zone) : null,
35
+ 'catalogs' => Catalog::collection($this->catalogs ?? []),
36
+ 'location' => $this->vehicle ? $this->vehicle->location : null,
37
+ 'online' => $this->vehicle ? $this->vehicle->online : false,
38
+ 'status' => $this->status,
39
+ 'created_at' => $this->created_at,
40
+ 'updated_at' => $this->updated_at,
40
41
  ];
41
42
  }
42
43
  }