custom/plugins/MasterFFLCheckout/src/Subscriber/OrderPlacedSubscriber.php line 55

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace MasterFFL\Checkout\Subscriber;
  3. use MasterFFL\Checkout\Service\FFLConfigService;
  4. use Shopware\Core\Checkout\Cart\LineItem\LineItem;
  5. use Shopware\Core\Checkout\Order\Event\OrderStateMachineStateChangeEvent;
  6. use Shopware\Core\Checkout\Order\OrderEvents;
  7. use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenEvent// FIXED IMPORT
  8. use Shopware\Core\Checkout\Order\OrderEntity;
  9. use Shopware\Core\Content\Product\ProductEntity;
  10. use Shopware\Core\Framework\Context;
  11. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
  12. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  13. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
  14. use Shopware\Core\System\SystemConfig\SystemConfigService;
  15. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  16. use Symfony\Component\HttpFoundation\RequestStack;
  17. class OrderPlacedSubscriber implements EventSubscriberInterface
  18. {
  19.     private EntityRepository $orderRepository;
  20.     private EntityRepository $stateMachineStateRepository;
  21.     private EntityRepository $productRepository;
  22.     private SystemConfigService $systemConfigService;
  23.     private FFLConfigService $configService;
  24.     private RequestStack $requestStack;
  25.     public function __construct(
  26.         EntityRepository $orderRepository,
  27.         EntityRepository $productRepository,
  28.         SystemConfigService $systemConfigService,
  29.         FFLConfigService $configService,
  30.         EntityRepository $stateMachineStateRepository,
  31.         RequestStack $requestStack
  32.     ) {
  33.         $this->orderRepository $orderRepository;
  34.         $this->productRepository $productRepository;
  35.         $this->systemConfigService $systemConfigService;
  36.         $this->configService $configService;
  37.         $this->stateMachineStateRepository $stateMachineStateRepository;
  38.         $this->requestStack $requestStack;
  39.     }
  40.     public static function getSubscribedEvents(): array
  41.     {
  42.         return [
  43.             // Listen to multiple events to catch order placement
  44.             OrderEvents::ORDER_WRITTEN_EVENT => 'onOrderWritten',
  45.             OrderStateMachineStateChangeEvent::class => 'onOrderStateChanged',
  46.             'checkout.order.placed' => 'onOrderPlaced'
  47.         ];
  48.     }
  49.     public function onOrderWritten(EntityWrittenEvent $event): void // FIXED TYPE HINT
  50.     {
  51.         error_log("=== SHOPWARE ORDER WRITTEN EVENT TRIGGERED ===");
  52.         error_log("Event class: " get_class($event));
  53.         error_log("Entity name: " $event->getEntityName());
  54.         error_log("Write results count: " count($event->getWriteResults()));
  55.         
  56.         foreach ($event->getWriteResults() as $writeResult) {
  57.             $payload $writeResult->getPayload();
  58.             error_log("Order ID from write result: " . ($payload['id'] ?? 'N/A'));
  59.             
  60.             if (isset($payload['id'])) {
  61.                 $this->processOrder($payload['id'], $event->getContext());
  62.             }
  63.         }
  64.     }
  65.     public function onOrderPlaced($event): void
  66.     {
  67.         error_log("=== SHOPWARE ORDER PLACED EVENT TRIGGERED ===");
  68.         error_log("Event class: " get_class($event));
  69.         
  70.         if (method_exists($event'getOrder')) {
  71.             $order $event->getOrder();
  72.             error_log("Order ID from placed event: " $order->getId());
  73.             $this->processOrder($order->getId(), $event->getContext());
  74.         }
  75.     }
  76.     private function getOrderStatusTechnicalName(string $statusIdContext $context): string
  77.     {
  78.         if (empty($statusId)) {
  79.             return 'open'// default fallback
  80.         }
  81.         try {
  82.             $criteria = new Criteria([$statusId]);
  83.             $status $this->stateMachineStateRepository->search($criteria$context)->first();
  84.             
  85.             if ($status) {
  86.                 return $status->getTechnicalName();
  87.             }
  88.         } catch (\Exception $e) {
  89.             error_log('Error getting status technical name: ' $e->getMessage());
  90.         }
  91.         
  92.         return 'open'// fallback
  93.     }
  94.     public function onOrderStateChanged(OrderStateMachineStateChangeEvent $event): void
  95.     {
  96.         $order $event->getOrder();
  97.         $salesChannelId $order->getSalesChannelId();
  98.         
  99.         // Only process when order is placed/confirmed
  100.         if ($event->getToPlace()->getTechnicalName() !== 'open') {
  101.             return;
  102.         }
  103.         try {
  104.             // Get FFL configuration
  105.             $fflConfig $this->configService->getFFLConfig($salesChannelId);
  106.             $environment $fflConfig['environment'] ?? false;
  107.             
  108.             $webhookUrl $environment 
  109.                 ? ($fflConfig['webhookUrlTest'] ?? 'https://api-qa.masterffl.com/ffl/bigcommerce/app/api/order/shopware/submittransfer')
  110.                 : ($fflConfig['webhookUrl'] ?? 'https://ffl-api.masterffl.com/ffl/bigcommerce/app/api/order/shopware/submittransfer');
  111.             $orderStatusMapping $fflConfig['orderStatusMapping'] ?? 'open';
  112.             // Get full order with associations
  113.             $fullOrder $this->getFullOrder($order->getId(), $event->getContext());
  114.             if (!$fullOrder) {
  115.                 return;
  116.             }
  117.             // Check if order has FFL products and build item data
  118.             $itemData $this->buildFFLItemData($fullOrder$event->getContext(), $salesChannelId);
  119.             
  120.             if (empty($itemData)) {
  121.                 return; // No FFL products, skip webhook
  122.             }
  123.             // Build order data for webhook
  124.             $orderData $this->buildOrderData($fullOrder$itemData$orderStatusMapping);
  125.             // Send webhook using cURL (like Magento)
  126.             $this->sendWebhookWithCurl($webhookUrl$orderData);
  127.         } catch (\Exception $e) {
  128.             // Log error but don't throw to avoid breaking order process
  129.             error_log('FFL Webhook Error: ' $e->getMessage());
  130.         }
  131.     }
  132.     private function processOrder(string $orderIdContext $context): void
  133.     {
  134.         try {
  135.             error_log("=== PROCESSING ORDER: " $orderId " ===");
  136.             
  137.             // Get full order
  138.             $order $this->getFullOrder($orderId$context);
  139.             if (!$order) {
  140.                 error_log("ERROR: Could not retrieve order");
  141.                 return;
  142.             }
  143.             $salesChannelId $order->getSalesChannelId();
  144.             error_log("Sales Channel ID: " $salesChannelId);
  145.             error_log("Order Number: " $order->getOrderNumber());
  146.             error_log("Order State: " $order->getStateMachineState()->getTechnicalName());
  147.             error_log("Order Total: " $order->getAmountTotal());
  148.             error_log("Customer Email: " $order->getOrderCustomer()->getEmail());
  149.             // Get FFL configuration
  150.             $fflConfig $this->configService->getFFLConfig($salesChannelId);
  151.             $environment $fflConfig['environment'] ?? false;
  152.             
  153.             error_log("Environment: " . ($environment 'DEVELOPMENT/TEST' 'PRODUCTION'));
  154.             $webhookUrl $environment 
  155.             ? ($fflConfig['webhookUrlTest'] ?? 'https://api-qa.masterffl.com/ffl/bigcommerce/app/api/order/shopware/submittransfer')
  156.             : ($fflConfig['webhookUrl'] ?? 'https://ffl-api.masterffl.com/ffl/bigcommerce/app/api/order/shopware/submittransfer');
  157.             
  158.             
  159.             $orderStatusMapping $fflConfig['orderStatusMapping'] ?? 'open';
  160.             
  161.             error_log("Webhook URL: " $webhookUrl);
  162.             error_log("Order Status Mapping: " $orderStatusMapping);
  163.             
  164.             // Check if order has FFL products and build item data
  165.             // echo "<pre>"; print_r($order); die;
  166.             $itemData $this->buildFFLItemData($order$context$salesChannelId);
  167.             error_log("FFL Items found: " count($itemData));
  168.             
  169.             if (empty($itemData)) {
  170.                 error_log("No FFL products found in order - skipping webhook");
  171.                 return;
  172.             }
  173.             $dealerId $this->getDealerId($order);
  174.             $this->updateOrderCustomFields($orderId$dealerId$context);
  175.             // Log FFL items details
  176.             foreach ($itemData as $index => $item) {
  177.                 error_log("FFL Item " . ($index 1) . ":");
  178.                 error_log("  - ID: " $item['item_id']);
  179.                 error_log("  - Name: " $item['item_name']);
  180.                 error_log("  - SKU: " $item['item_sku']);
  181.                 error_log("  - Qty: " $item['item_qty']);
  182.                 error_log("  - Price: " $item['item_price']);
  183.                 error_log("  - Custom Fields: " json_encode($item['custom_fields']));
  184.             }
  185.             
  186.             // Build order data for webhook
  187.             $orderData $this->buildOrderData($order$itemData$orderStatusMapping);
  188.             
  189.             error_log("=== ORDER DATA BUILT FOR WEBHOOK ===");
  190.             error_log("Platform: " $orderData['platform']);
  191.             error_log("Callback URL: " $orderData['callback_url']);
  192.             error_log("Order Total: " $orderData['order_total']);
  193.             error_log("Customer Email: " $orderData['customer_email']);
  194.             error_log("License ID: " $orderData['license_id']);
  195.             error_log("Domain: " $orderData['domain']);
  196.             
  197.             // Send webhook using cURL (like Magento)
  198.             error_log("=== SENDING WEBHOOK ===");
  199.             
  200.             $this->sendWebhookWithCurl($webhookUrl$orderData);
  201.         } catch (\Exception $e) {
  202.             error_log('=== FFL WEBHOOK ERROR ===');
  203.             error_log('Error Message: ' $e->getMessage());
  204.             error_log('Error File: ' $e->getFile() . ':' $e->getLine());
  205.             error_log('Stack Trace: ' $e->getTraceAsString());
  206.         }
  207.     }
  208.     /**
  209.      * Send webhook using cURL (same as Magento implementation)
  210.      */
  211.     private function sendWebhookWithCurl(string $webhookUrl, array $orderData): void
  212.     {
  213.         try {
  214.             $json_shipping_add json_encode($orderData);
  215.             
  216.             // Initialize cURL
  217.             $ch curl_init();
  218.             
  219.             // Set cURL options (same as Magento's Curl client)
  220.             curl_setopt($chCURLOPT_URL$webhookUrl);
  221.             curl_setopt($chCURLOPT_POSTtrue);
  222.             curl_setopt($chCURLOPT_POSTFIELDS$json_shipping_add);
  223.             curl_setopt($chCURLOPT_RETURNTRANSFERtrue);
  224.             curl_setopt($chCURLOPT_HTTPHEADER, [
  225.                 'Content-Type: application/json'
  226.             ]);
  227.             curl_setopt($chCURLOPT_TIMEOUT30);
  228.             curl_setopt($chCURLOPT_SSL_VERIFYPEERfalse);
  229.             
  230.             // Execute the request
  231.             $response curl_exec($ch);
  232.             
  233.             // Get response info
  234.             $httpCode curl_getinfo($chCURLINFO_HTTP_CODE);
  235.             $error curl_error($ch);
  236.             
  237.             // Close cURL
  238.             curl_close($ch);
  239.             
  240.             // Handle errors
  241.             if ($error) {
  242.                 throw new \Exception('cURL Error: ' $error);
  243.             }
  244.             
  245.             // Log response (optional, like Magento)
  246.             error_log("FFL Webhook sent. Status: {$httpCode}, Response: {$response}");
  247.             
  248.         } catch (\Exception $e) {
  249.             error_log('=== WEBHOOK FAILURE ===');
  250.             error_log('FFL Webhook failed: ' $e->getMessage());
  251.             throw $e;
  252.         }
  253.     }
  254.     private function getFullOrder(string $orderIdContext $context): ?OrderEntity
  255.     {
  256.         $criteria = new Criteria([$orderId]);
  257.         $criteria->addAssociation('addresses');
  258.         $criteria->addAssociation('lineItems');
  259.         $criteria->addAssociation('deliveries.shippingOrderAddress');
  260.         $criteria->addAssociation('transactions.paymentMethod');
  261.         $criteria->addAssociation('salesChannel');
  262.         $criteria->addAssociation('salesChannel.domains'); // Add this line
  263.         $criteria->addAssociation('currency');
  264.         $criteria->addAssociation('orderCustomer.customer');
  265.         return $this->orderRepository->search($criteria$context)->first();
  266.     }
  267.     private function buildFFLItemData(OrderEntity $orderContext $contextstring $salesChannelId): array
  268.     {
  269.         $itemData = [];
  270.         $productIds = [];
  271.         // Collect product IDs
  272.         foreach ($order->getLineItems() as $lineItem) {
  273.             if ($lineItem->getType() === LineItem::PRODUCT_LINE_ITEM_TYPE) {
  274.                 $productIds[] = $lineItem->getReferencedId();
  275.             }
  276.         }
  277.         if (empty($productIds)) {
  278.             return [];
  279.         }
  280.         // Get products with categories
  281.         $criteria = new Criteria();
  282.         $criteria->addFilter(new EqualsAnyFilter('id'$productIds));
  283.         $criteria->addAssociation('categories');
  284.         $products $this->productRepository->search($criteria$context);
  285.         // Get FFL configuration
  286.         $fflAttributeName $this->systemConfigService->get('MasterFFLCheckout.config.fflAttributeName'$salesChannelId);
  287.         $fflAttributeValue $this->systemConfigService->get('MasterFFLCheckout.config.fflAttributeValue'$salesChannelId);
  288.         $firearmAttributeName $this->systemConfigService->get('MasterFFLCheckout.config.firearmAttributeName'$salesChannelId);
  289.         
  290.         // Get firearm type mappings
  291.         $firearmTypes = [
  292.             'hand_gun' => $this->systemConfigService->get('MasterFFLCheckout.config.handGunType'$salesChannelId),
  293.             'long_gun' => $this->systemConfigService->get('MasterFFLCheckout.config.longGunType'$salesChannelId),
  294.             'other_firearm' => $this->systemConfigService->get('MasterFFLCheckout.config.otherFirearmType'$salesChannelId),
  295.             'nfa' => $this->systemConfigService->get('MasterFFLCheckout.config.nfaType'$salesChannelId)
  296.         ];
  297.         // Get FFL category mappings
  298.         $fflCategoryMappings $this->getFFLCategoryMappings($salesChannelId);
  299.         
  300.         foreach ($order->getLineItems() as $lineItem) {
  301.             if ($lineItem->getType() !== LineItem::PRODUCT_LINE_ITEM_TYPE) {
  302.                 continue;
  303.             }
  304.             $product $products->get($lineItem->getReferencedId());
  305.             if (!$product) {
  306.                 continue;
  307.             }
  308.             $customAttributes = [];
  309.             $isFFLProduct false;
  310.             $firearmType '';
  311.             // Check custom fields first
  312.             $customFields $product->getCustomFields() ?? [];
  313.             
  314.             // Check if FFL field exists (try multiple variations)
  315.             $fflFieldValue null;
  316.             if (isset($customFields[$fflAttributeName])) {
  317.                 $fflFieldValue $customFields[$fflAttributeName];
  318.             } elseif (isset($customFields[strtolower($fflAttributeName)])) {
  319.                 $fflFieldValue $customFields[strtolower($fflAttributeName)];
  320.             } elseif (isset($customFields[strtoupper($fflAttributeName)])) {
  321.                 $fflFieldValue $customFields[strtoupper($fflAttributeName)];
  322.             }
  323.             if ($fflFieldValue !== null) {
  324.                 $fflFieldValueLower strtolower(trim((string)$fflFieldValue));
  325.                 $configValueLower strtolower(trim($fflAttributeValue));
  326.                 
  327.                 // Check for NO values first
  328.                 if ($fflFieldValueLower === 'no' || $fflFieldValueLower === 'false' || $fflFieldValueLower === '0') {
  329.                     continue; // Skip this product
  330.                 }
  331.                 
  332.                 // Check for YES values
  333.                 if ($fflFieldValue === $configValueLower || $fflFieldValueLower == $fflAttributeValue) {
  334.                     
  335.                     $isFFLProduct true;
  336.                     
  337.                     // Get firearm type - try multiple field name variations
  338.                     $firearmTypeValue null;
  339.                     if (isset($customFields[$firearmAttributeName])) {
  340.                         $firearmTypeValue $customFields[$firearmAttributeName];
  341.                     } 
  342.                     
  343.                     if ($firearmTypeValue) {
  344.                         $firearmTypeValueLower strtolower(trim((string)$firearmTypeValue));
  345.                         $firearmType $firearmTypes[$firearmTypeValueLower] ?? $firearmTypeValue;
  346.                     }
  347.                 }
  348.             }
  349.             // Check category mapping if not explicitly set
  350.             if (!$isFFLProduct) {
  351.                 if ($product->getCategories()) {
  352.                     foreach ($product->getCategories() as $category) {
  353.                         foreach ($fflCategoryMappings as $mapping) {
  354.                             if ($mapping['storeCategory'] === $category->getId()) {
  355.                                 $isFFLProduct true;
  356.                                 $fflMapping strtolower($mapping['fflMapping'] ?? '');
  357.                                 $firearmType $firearmTypes[$fflMapping] ?? $mapping['fflMapping'] ?? '';
  358.                                 break 2;
  359.                             }
  360.                         }
  361.                     }
  362.                 }
  363.             }
  364.             if ($isFFLProduct) {
  365.                 $customAttributes[] = [
  366.                     'name' => $fflAttributeName,
  367.                     'value' => $fflAttributeValue
  368.                 ];
  369.                 
  370.                 if ($firearmType) {
  371.                     if (strtolower($firearmType) === 'nfa' || strpos(strtolower($firearmType), 'suppressor') !== false) {
  372.                         $customAttributes[] = ['name' => $firearmAttributeName'value' => 'suppressor'];
  373.                     } else {
  374.                         $customAttributes[] = ['name' => $firearmAttributeName'value' => $firearmType];
  375.                     }
  376.                 }
  377.                 $itemData[] = [
  378.                     'item_id' => $lineItem->getReferencedId(),
  379.                     'item_name' => $lineItem->getLabel(),
  380.                     'item_sku' => $lineItem->getPayload()['productNumber'] ?? '',
  381.                     'price_ex_tax' => $lineItem->getUnitPrice(),
  382.                     'item_qty' => $lineItem->getQuantity(),
  383.                     'item_price' => $lineItem->getUnitPrice(),
  384.                     'custom_fields' => $customAttributes
  385.                 ];
  386.             }
  387.         }
  388.         
  389.         return $itemData;
  390.     }
  391.     private function checkProductCategories(ProductEntity $product, array $fflCategoryMappings, array $firearmTypes): array
  392.     {
  393.         $result = ['isFFLProduct' => false'firearmType' => ''];
  394.         if (!$product->getCategories()) {
  395.             error_log("Product has no categories");
  396.             return $result;
  397.         }
  398.         error_log("Product categories: " $product->getCategories()->count());
  399.         foreach ($product->getCategories() as $category) {
  400.             error_log("Checking category: " $category->getName() . " (ID: " $category->getId() . ")");
  401.             
  402.             foreach ($fflCategoryMappings as $mapping) {
  403.                 if ($mapping['storeCategory'] === $category->getId()) {
  404.                     error_log("Found FFL category mapping: " json_encode($mapping));
  405.                     $result['isFFLProduct'] = true;
  406.                     $fflMapping strtolower($mapping['fflMapping'] ?? '');
  407.                     $result['firearmType'] = $firearmTypes[$fflMapping] ?? $mapping['fflMapping'] ?? '';
  408.                     error_log("Category mapped to firearm type: " $result['firearmType']);
  409.                     return $result;
  410.                 }
  411.             }
  412.         }
  413.         error_log("No FFL category mapping found for product");
  414.         return $result;
  415.     }
  416.     private function getFFLCategoryMappings(string $salesChannelId): array
  417.     {
  418.         $configKey 'MasterFFLCheckout.config.categoryMappingTable';
  419.         $categoryMappings $this->systemConfigService->get($configKey$salesChannelId);
  420.         
  421.         error_log("Raw category mappings from config: " json_encode($categoryMappings));
  422.         
  423.         return is_array($categoryMappings) ? $categoryMappings : [];
  424.     }
  425.     private function buildOrderData(OrderEntity $order, array $itemDatastring $orderStatusMapping): array
  426.     {
  427.         $billingAddress null;
  428.         $shippingAddress null;
  429.         // Get addresses
  430.         foreach ($order->getAddresses() as $address) {
  431.             if ($address->getId() === $order->getBillingAddressId()) {
  432.                 $billingAddress $address;
  433.             }
  434.             if ($order->getDeliveries()->first() && 
  435.                 $address->getId() === $order->getDeliveries()->first()->getShippingOrderAddressId()) {
  436.                 $shippingAddress $address;
  437.             }
  438.         }
  439.         // Get payment method
  440.         $paymentMethod '';
  441.         if ($order->getTransactions()->first() && $order->getTransactions()->first()->getPaymentMethod()) {
  442.             $paymentMethod $order->getTransactions()->first()->getPaymentMethod()->getName();
  443.         }
  444.         // Get shipping method
  445.         $shippingMethod '';
  446.         if ($order->getDeliveries()->first() && $order->getDeliveries()->first()->getShippingMethod()) {
  447.             $shippingMethod $order->getDeliveries()->first()->getShippingMethod()->getName();
  448.         }
  449.         $baseUrl '';
  450.         if ($order->getSalesChannel() && 
  451.             $order->getSalesChannel()->getDomains() && 
  452.             $order->getSalesChannel()->getDomains()->first()) {
  453.             $baseUrl $order->getSalesChannel()->getDomains()->first()->getUrl();
  454.         } else {
  455.             if (isset($_SERVER['HTTP_HOST'])) {
  456.                 $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' 'https' 'http';
  457.                 $baseUrl $protocol '://' $_SERVER['HTTP_HOST'];
  458.             }
  459.         }
  460.         
  461.         $siteUrl parse_url($baseUrlPHP_URL_HOST) ?: 'localhost';
  462.         $billStreet $billingAddress $billingAddress->getStreet() : '';
  463.         $bill_street_one $billStreet;
  464.         $bill_street_two $billingAddress ? ($billingAddress->getAdditionalAddressLine1() ?? '') : '';
  465.         $shipStreet $shippingAddress $shippingAddress->getStreet() : '';
  466.         $ship_street_one $shipStreet;
  467.         $ship_street_two $shippingAddress ? ($shippingAddress->getAdditionalAddressLine1() ?? '') : '';
  468.         $callbackUrl rtrim($baseUrl'/') . '/api/_action/ffl/update-shipping-address/' $order->getOrderNumber();
  469.         $orderStatusTechnicalName $this->getOrderStatusTechnicalName($orderStatusMappingContext::createDefaultContext());
  470.         $dealerId $this->getDealerId($order);
  471.         $token $this->getToken($order);
  472.         $orderData = [
  473.             'platform' => 'shopware',
  474.             'callback_url' => $callbackUrl,
  475.             'order_id' => $order->getOrderNumber(),
  476.             'order_number' => $order->getId(),
  477.             'order_date' => $order->getOrderDateTime()->format('Y-m-d H:i:s'),
  478.             'date_modified' => $order->getUpdatedAt() ? $order->getUpdatedAt()->format('Y-m-d H:i:s') : $order->getOrderDateTime()->format('Y-m-d H:i:s'),
  479.             'status' => $order->getStateMachineState()->getTechnicalName(),
  480.             'shipping_total' => $order->getShippingTotal(),
  481.             'shipping_tax_total' => 0,
  482.             'fee_total' => '',
  483.             'fee_tax_total' => '',
  484.             'tax_total' => $order->getAmountTotal() - $order->getAmountNet(),
  485.             'order_discount' => 0,
  486.             'discount_total' => 0,
  487.             'order_total' => $order->getAmountTotal(),
  488.             'order_currency' => $order->getCurrency()->getIsoCode(),
  489.             'payment_method' => $paymentMethod,
  490.             'payment_amount' => $order->getAmountTotal(),
  491.             'shipping_method' => $shippingMethod,
  492.             'shipping_cost_ex_tax' => $order->getShippingTotal(),
  493.             'customer_id' => $order->getOrderCustomer()->getCustomerId() ?? '',
  494.             'customer_email' => $order->getOrderCustomer()->getEmail(),
  495.             'billing_first_name' => $billingAddress $billingAddress->getFirstName() : '',
  496.             'billing_last_name' => $billingAddress $billingAddress->getLastName() : '',
  497.             'billing_company' => $billingAddress $billingAddress->getCompany() : '',
  498.             'billing_email' => $order->getOrderCustomer()->getEmail(),
  499.             'billing_phone' => $billingAddress->getPhoneNumber() ?? '0000000000',
  500.             'billing_address_1' => $bill_street_one,
  501.             'billing_address_2' => $bill_street_two,
  502.             'billing_postcode' => $billingAddress $billingAddress->getZipcode() : '',
  503.             'billing_city' => $billingAddress $billingAddress->getCity() : '',
  504.             'billing_state' => $billingAddress && $billingAddress->getCountryState() ? $billingAddress->getCountryState()->getName() : '',
  505.             'billing_country' => $billingAddress && $billingAddress->getCountry() ? $billingAddress->getCountry()->getIso() : '',
  506.             'shipping_first_name' => $shippingAddress $shippingAddress->getFirstName() : ($billingAddress $billingAddress->getFirstName() : ''),
  507.             'shipping_last_name' => $shippingAddress $shippingAddress->getLastName() : ($billingAddress $billingAddress->getLastName() : ''),
  508.             'shipping_company' => $shippingAddress $shippingAddress->getCompany() : '',
  509.             'shipping_address_1' => $ship_street_one,
  510.             'shipping_address_2' => $ship_street_two,
  511.             'shipping_postcode' => $shippingAddress $shippingAddress->getZipcode() : '',
  512.             'shipping_city' => $shippingAddress $shippingAddress->getCity() : '',
  513.             'shipping_phone' => $shippingAddress $shippingAddress->getPhoneNumber() : '',
  514.             'shipping_state' => $shippingAddress && $shippingAddress->getCountryState() ? $shippingAddress->getCountryState()->getShortCode() : '',
  515.             'shipping_country' => $shippingAddress && $shippingAddress->getCountry() ? $shippingAddress->getCountry()->getIso() : '',
  516.             'customer_note' => $order->getCustomerComment() ?? '',
  517.             'domain' => $siteUrl,
  518.             'license_id' => $dealerId,
  519.             'token' => $token,
  520.             'order_status_mapping' => $orderStatusTechnicalName,
  521.             'item' => $itemData,
  522.         ];
  523.         return $orderData;
  524.     }
  525.     private function getDealerId(OrderEntity $order): string
  526.     {
  527.         $request $this->requestStack->getCurrentRequest();
  528.         if ($request && $request->hasSession()) {
  529.             $session $request->getSession();
  530.             $fflDealerData $session->get('ffl_dealer_data');
  531.             if ($fflDealerData && isset($fflDealerData['dealer_id'])) {
  532.                 return $fflDealerData['dealer_id'];
  533.             }
  534.         }
  535.         return '';
  536.     }
  537.     private function getToken(OrderEntity $order): string
  538.     {
  539.         $request $this->requestStack->getCurrentRequest();
  540.         if ($request && $request->hasSession()) {
  541.             $session $request->getSession();
  542.             $seassionData $session->get('ffl_dealer_data');
  543.             $token $seassionData['token'];
  544.             
  545.             if ($token) {
  546.                 return $token;
  547.             }
  548.         }
  549.         return '';
  550.     }
  551.     private function updateOrderCustomFields(string $orderIdstring $dealerIdContext $context): void
  552.     {
  553.         try {
  554.             if (empty($dealerId)) {
  555.                 error_log("Dealer ID is empty, skipping update");
  556.                 return;
  557.             }
  558.             error_log("Updating order {$orderId} with dealer ID: {$dealerId}");
  559.             
  560.             // Get existing custom fields to preserve other data
  561.             $order $this->getFullOrder($orderId$context);
  562.             if (!$order) {
  563.                 error_log("Order not found: {$orderId}");
  564.                 return;
  565.             }
  566.             $existingCustomFields $order->getCustomFields() ?? [];
  567.             
  568.             // Check if already set to avoid unnecessary updates
  569.             if (isset($existingCustomFields['masterffl_license_number']) && 
  570.                 $existingCustomFields['masterffl_license_number'] === $dealerId) {
  571.                 error_log("Dealer ID already set for order {$orderId}, skipping update");
  572.                 return;
  573.             }
  574.             
  575.             // Add/update the masterffl_license_number field
  576.             $existingCustomFields['masterffl_license_number'] = $dealerId;
  577.             // Update with complete custom fields array
  578.             $this->orderRepository->update([
  579.                 [
  580.                     'id' => $orderId,
  581.                     'customFields' => $existingCustomFields
  582.                 ]
  583.             ], $context);
  584.             
  585.             error_log("Successfully updated masterffl_license_number with: " $dealerId);
  586.             
  587.         } catch (\Exception $e) {
  588.             error_log('Error updating order custom fields: ' $e->getMessage());
  589.             error_log('Stack trace: ' $e->getTraceAsString());
  590.             // Don't throw exception to avoid breaking order process
  591.         }
  592.     }
  593. }