```
	public function saveOrderItems(array $cartItems): bool
	{
		$this->logDebug("Save Order Items, cartItems", ['cart items' => $cartItems]);
		
		$validItems = $this->getValidOrderItems($cartItems);
		
		$this->logDebug("Save Order Items, validItems", ['valid items' => $validItems]);
		
		// No valid items, nothing to save
		if (empty($validItems))
		{
			return false;
		}
		
		var_dump($validItems);//die;// TODO
		
		try
		{
			foreach ($validItems as $itemData)
			{
				$product = $itemData['product'];
				$cart = $itemData['cart'];
				$quantity = $itemData['quantity'];
				
				$orderItem = new OrderItem();
				$orderItem->order_id = $this->id;
				$orderItem->product_id = $product['id'];
				$orderItem->product_title = $product['title'] ?? \Yii::t('app', 'Unknown product');
				$orderItem->quantity = $quantity;
				$orderItem->unit_price = $product['price'];
				$orderItem->product_image_one = $product['product_image_one'] ?? null;
				$orderItem->meta_description = $product['meta_description'] ?? '';
				
				$this->logDebug("Save Order Items, attributes before save", ['attributes' => $orderItem->attributes]);
				
				if (!$orderItem->save())
				{
					$this->logDebug("Save Order Items, failed to save order items", ['errors' => $orderItem->getErrors()]);
				
					$error = implode(', ', $orderItem->getFirstErrors());
					throw new YiiDbException("Failed to save order item: " . $error);
				}
				
				$this->logDebug("Save Order Items, saved with IDs", ['id' => $orderItem->id]);
			}
			
			// Lazy-load and cache
			$this->_cachedOrderItems = $this->orderItems;
			
			var_dump($this->_cachedOrderItems);die;
			
			return true;
		}
		catch (Throwable $e)
		{
			// Use the ExceptionLogger to handle and log the exception.
			ExceptionLogger::log($e, 'order_model_error_');
			
			// Rethrow so outer transaction rolls back
			throw $e;
		}
		
		return false;
	}
	
	/**
	* Fetch valid order items by validating the cartItems against DB products
	* @param array $cartItems
	* @return array ['product' => [...], 'cart' => [...], 'quantity' => int]
	*/
	protected function getValidOrderItems(array $cartItems): array
	{
		$productIds = array_column($cartItems, 'id');
		
		$this->logDebug("Get Valid Order Item, product IDs extracted", ['product IDs' => $productIds]);
		
		$products = Content::find()
			->id($productIds)
			->ofType('product')
			->indexBy('id')
			->asArray()
			->all();
		
		$this->logDebug("Get Valid Order Item, products fetched from DB", ['products' => $products]);
		
		$validItems = [];
		
		foreach ($cartItems as $item)
		{
			$productId = $item['id'] ?? null;
			
			if (!$productId || !isset($products[$productId]))
			{
				$this->logDebug(
					"Get Valid Order Item, skipping invalid or missing product ID", 
					['product IDs' => $productId]
				);
				
				continue;
			}
			
			$product = $products[$productId];
			
			/**
			* TODO
			* - last time to check inventory available_quantity vs product['quantity']
			*/
			
			$quantity = max(1, (int) ($item['quantity'] ?? 1));
			
			if ($quantity === 0)
			{
				$this->logDebug(
					"Get Valid Order Item, skipping zero quantity for product ID", 
					['product IDs' => $productId]
				);
				
				continue;
			}
			
			$validItems[] = [
				'product' => $product,
				'cart' => $item,
				'quantity' => $quantity,
			];
		}
		
		$this->logDebug("Get Valid Order Item, valided items", ['items' => $validItems]);
		
		return $validItems;
	}
	
	/**
	* Build Stripe line items from saved order items
	* @return array
	* @url https://stripe.com/docs/api/checkout/sessions/object#checkout_session_object-line_items
	*
	* Data example:
	*
	*	["id"]=> string(1) "1" 
	*	["title"]=> string(8) "Monsters" 
	*	["product_image_one"]=> string(52) "http://localhost:8080/uploads/image.jpg" 
	*	["price"]=> string(4) "1.00" 
	*	["total_price"]=> float(1) 
	*	["slug"]=> string(8) "monsters" 
	*	["meta_description"]=> "painting of..."
	*	["quantity"]=> int(1) 
	*	["product_quantity"]=> string(1) "7"
	*/
	public function getStripeLineItems(): array
	{
		if ($this->_cachedOrderItems === null)
		{
			// Lazy-load and cache
			$this->_cachedOrderItems = $this->orderItems;
			
			$this->logDebug(
				"Get Stripe Line Items, loaded orderItems",
				['orderItems' => $this->_cachedOrderItems]
			);
		}
		
		$lineItems = [];
		
		foreach ($this->_cachedOrderItems as $orderItem)
		{
			$orderId = $orderItem->order_id;
			$quantity = $orderItem->quantity;
			$unitPrice = $orderItem->unit_price;
			
			$this->logDebug("Get Stripe Line Items, processing orderItem ID", [
				'order ID' => $orderId,
				'order quantity' => $quantity,
				'order unit price' => $unitPrice
			]);
			
			if ($orderItem->quantity <= 0)
			{
				$this->logDebug("Get Stripe Line Items, skipped quantity <= 0");
				
				continue;
			}
			
			// Calculate total price = quantity × unit price (products of your stored values)
			$totalPrice = $unitPrice * $quantity;
			
			$this->logDebug("Get Stripe Line Items, skipped total price", ['total price' => $totalPrice]);
			
			if ($totalPrice <= 0)
			{
				$this->logDebug("Get Stripe Line Items, skip, total price <= 0");
				
				continue;
			}
			
			// For Stripe, amount is in cents (integer)
			$unitAmount = (int) round($totalPrice / $quantity);
			
			$this->logDebug(
				"Get Stripe Line Items, unit amount(rounded)",
				['unit amount(rounded)' => $unitAmount]
			);
			
			if ($unitAmount <= 0)
			{
				$this->logDebug("Get Stripe Line Items, skipped unit amount <= 0");
				
				continue;
			}
			
			// For currencies without cents
			// Some currencies do not have minor units (e.g. Japanese Yen JPY).
			// In that case, the amount is just the integer without multiplying by 100.
			$formattedPrice = MoneyHelper::getIntl($totalPrice);
			
			$this->logDebug("Get Stripe Line Items, formatted Price", ['price' => $formattedPrice]);
			
			$lineItems[] = [
				'price_data' => [
					'currency' => $this->_formatter->currencyCode(),
					'unit_amount' => $unitAmount,
					'product_data' => [
						'name' => $orderItem->product_title,
						'description' => $orderItem->meta_description,
						'images' => [$orderItem->product_image_one],
					],
				],
				'quantity' => $quantity,
			];
		}
		
		$this->logDebug("Get Stripe Line Items, stripe line items", ['line items' => $lineItems]);
		
		return $lineItems;
	}
```

last time to check inventory available_quantity vs product['quantity']


array (size=1)
  0 => 
    array (size=3)
      'product' => 
        array (size=32)
          'id' => int 1
          'type' => int 2
          'template' => string 'artwork' (length=7)
          'category_id' => int 4
          'category_title' => string 'Artwork' (length=7)
          'status' => int 1
          'title' => string 'hifi' (length=4)
          'slug' => string 'hifi' (length=4)
          'body' => string '<p>hi-fi is no low-fi.<br /></p>' (length=32)
          'image_header' => string '' (length=0)
          'meta_description' => string 'hi-fi is no low-fi' (length=18)
          'meta_keywords' => string 'hi-fi' (length=5)
          'start_date' => null
          'end_date' => null
          'artist' => string 'idm' (length=3)
          'dimensions' => string '' (length=0)
          'medium' => string '' (length=0)
          'year' => null
          'price' => int 400
          'hide_price' => int 0
          'purchase_options' => int 6
          'quantity' => int 4
          'product_image_one' => string 'http://localhost:8080/uploads/5e2c796a3c8f293ef-1753217922-687ffb820b789.jpg' (length=98)
          'product_image_two' => string '' (length=0)
          'product_image_three' => string '' (length=0)
          'created_by' => int 1
          'updated_by' => int 1
          'created_at' => int 1753218508
          'updated_at' => int 1753316365
          'hero_mobile' => string 'http://localhost:8080/uploads/5e2c796a3c8f293ef-1753217922-687ffb820b789.jpg' (length=98)
          'hero_tablet' => string 'http://localhost:8080/uploads/5e2c796a3c8f293ef-1753217922-687ffb820b789.jpg' (length=98)
          'hero_desktop' => string 'http://localhost:8080/uploads/5e2c796a3c8f293ef-1753217922-687ffb820b789.jpg' (length=98)
      'cart' => 
        array (size=4)
          'id' => int 1
          'quantity' => int 1
          'available_quantity' => int 4
          'total_price' => int 400
      'quantity' => int 1



array (size=1)
  0 => 
    object(app\models\OrderItem)[505]
      private '_events' (yii\base\Component) => 
        array (size=0)
          empty
      private '_eventWildcards' (yii\base\Component) => 
        array (size=0)
          empty
      private '_behaviors' (yii\base\Component) => 
        array (size=0)
          empty
      private '_errors' (yii\base\Model) => null
      private '_validators' (yii\base\Model) => null
      private '_scenario' (yii\base\Model) => string 'default' (length=7)
      private '_attributes' (yii\db\BaseActiveRecord) => 
        array (size=10)
          'id' => int 35
          'order_id' => int 49
          'product_id' => int 1
          'product_title' => string 'hifi' (length=4)
          'quantity' => int 1
          'unit_price' => int 400
          'product_image_one' => string 'http://localhost:8080/uploads/5e2c796a3c8f293ef-1753217922-687ffb820b789.jpg' (length=98)
          'product_image_two' => null
          'product_image_three' => null
          'meta_description' => string 'hi-fi is no low-fi' (length=18)
      private '_oldAttributes' (yii\db\BaseActiveRecord) => 
        array (size=10)
          'id' => int 35
          'order_id' => int 49
          'product_id' => int 1
          'product_title' => string 'hifi' (length=4)
          'quantity' => int 1
          'unit_price' => int 400
          'product_image_one' => string 'http://localhost:8080/uploads/5e2c796a3c8f293ef-1753217922-687ffb820b789.jpg' (length=98)
          'product_image_two' => null
          'product_image_three' => null
          'meta_description' => string 'hi-fi is no low-fi' (length=18)
      private '_related' (yii\db\BaseActiveRecord) => 
        array (size=0)
          empty
      private '_relationsDependencies' (yii\db\BaseActiveRecord) => 
        array (size=0)
          empty