client = new Client(); $this->apiKey = config('shopify.api_key'); $this->apiPassword = config('shopify.password'); $this->shopDomain = config('shopify.shop_domain'); $this->apiVersion = config('shopify.api_version'); $this->client = new Client([ 'base_uri' => "https://{$this->shopDomain}.myshopify.com/admin/api/{$this->apiVersion}/", 'auth' => [$this->apiKey, $this->apiPassword], 'headers' => [ 'Content-Type' => 'application/json', 'Accept' => 'application/json', ], ]); } public function get($endpoint, $params = []) { $response = $this->client->get("https://{$this->shopDomain}.myshopify.com/admin/api/2023-04/{$endpoint}", [ 'auth' => [$this->apiKey, $this->apiPassword], 'query' => $params, ]); return json_decode($response->getBody()->getContents(), true); } public function post($endpoint, $data = []) { $response = $this->client->post("https://{$this->shopDomain}.myshopify.com/admin/api/2023-04/{$endpoint}", [ 'auth' => [$this->apiKey, $this->apiPassword], 'json' => $data, ]); return json_decode($response->getBody()->getContents(), true); } public function getProduct($id) { try { $response = $this->client->get("products/{$id}.json"); $product = json_decode($response->getBody()->getContents(), true)['product']; dd($product); return $product; } catch (\Exception $e) { throw new \Exception('Failed to fetch product: ' . $e->getMessage()); } } public function getProductsByCollection($collectionId) { $response = $this->client->get("collections/{$collectionId}/products.json"); return json_decode($response->getBody()->getContents(), true); } public function syncProducts() { $response = $this->client->get('products.json'); $products = json_decode($response->getBody()->getContents(), true)['products']; foreach ($products as $product) { $productModel = Product::updateOrCreate( ['shopify_id' => $product['id']], [ 'title' => $product['title'], 'body_html' => $product['body_html'], 'vendor' => $product['vendor'], 'product_type' => $product['product_type'], 'handle' => $product['handle'], 'created_at_shopify' => $product['created_at'], 'updated_at_shopify' => $product['updated_at'], 'published_at' => $product['published_at'], 'template_suffix' => $product['template_suffix'], 'published_scope' => $product['published_scope'], 'tags' => $product['tags'], 'status' => $product['status'], 'admin_graphql_api_id' => $product['admin_graphql_api_id'], ] ); // Sync product collections $this->syncProductCollections($productModel, $product['id']); // Sync product variants $this->syncProductVariants($productModel, $product['variants']); // Sync product options $this->syncProductOptions($productModel, $product['options']); // Sync product images and link primary image $this->syncProductImages($productModel, $product['images']); if (isset($product['image'])) { $primaryImage = ShopifyImage::where('shopify_id', $product['image']['id'])->first(); if ($primaryImage) { $productModel->primary_image_id = $primaryImage->id; $productModel->save(); } } } } protected function syncProductImages(Product $product, $images) { foreach ($images as $image) { ShopifyImage::updateOrCreate( ['shopify_id' => $image['id']], [ 'product_id' => $product->id, 'src' => $image['src'], 'width' => $image['width'], 'height' => $image['height'], 'alt' => $image['alt'], 'position' => $image['position'], 'admin_graphql_api_id' => $image['admin_graphql_api_id'], ] ); } } public function syncCollections() { $response = $this->client->get('custom_collections.json'); $collections = json_decode($response->getBody()->getContents(), true)['custom_collections']; foreach ($collections as $collection) { // echo($collection['title']. '
'); Collection::updateOrCreate( ['shopify_id' => $collection['id']], [ 'title' => $collection['title'], 'body_html' => $collection['body_html'], 'handle' => $collection['handle'], ] ); } } protected function syncProductCollections(Product $product, $shopifyProductId) { // Get collection associations for this product using the collects endpoint $response = $this->client->get("collects.json?product_id={$shopifyProductId}"); $collects = json_decode($response->getBody()->getContents(), true)['collects']; $collectionIds = []; foreach ($collects as $collect) { $collectionModel = Collection::where('shopify_id', $collect['collection_id'])->first(); if ($collectionModel) { $collectionIds[] = $collectionModel->id; } } // Sync the product-collection relationships $product->collections()->sync($collectionIds); } protected function syncProductVariants(Product $product, $variants) { foreach ($variants as $variant) { ShopifyVariant::updateOrCreate( ['shopify_id' => $variant['id']], [ 'product_id' => $product->id, 'title' => $variant['title'], 'sku' => $variant['sku'], 'price' => $variant['price'], 'position' => $variant['position'], 'inventory_policy' => $variant['inventory_policy'], 'compare_at_price' => $variant['compare_at_price'], 'fulfillment_service' => $variant['fulfillment_service'], 'inventory_management' => $variant['inventory_management'], 'option1' => $variant['option1'], 'option2' => $variant['option2'], 'option3' => $variant['option3'], 'taxable' => $variant['taxable'], 'barcode' => $variant['barcode'], 'grams' => $variant['grams'], 'weight' => $variant['weight'], 'weight_unit' => $variant['weight_unit'], 'inventory_item_id' => $variant['inventory_item_id'], 'inventory_quantity' => $variant['inventory_quantity'], 'old_inventory_quantity' => $variant['old_inventory_quantity'], 'requires_shipping' => $variant['requires_shipping'], 'admin_graphql_api_id' => $variant['admin_graphql_api_id'], 'image_id' => $variant['image_id'] ] ); } } protected function syncProductOptions(Product $product, $options) { foreach ($options as $option) { ShopifyOption::updateOrCreate( ['shopify_id' => $option['id']], [ 'product_id' => $product->id, 'name' => $option['name'], 'position' => $option['position'] ] ); } } public function getCustomers() { $response = $this->client->get('customers.json'); if ($response->getStatusCode() === 200) { return json_decode($response->getBody()->getContents(), true)['customers']; } return []; } public function syncCustomers() { $customers = $this->getCustomers(); foreach ($customers as $customer) { if ($this->isSegmentCustomer($customer)) { User::updateOrCreate( ['shopify_id' => $customer['id']], [ 'name' => $customer['first_name'] . ' ' . $customer['last_name'], 'email' => $customer['email'], 'password' => bcrypt('defaultpassword'), // Set a default password or handle password management 'first_name' => $customer['first_name'], 'last_name' => $customer['last_name'], 'phone' => $customer['phone'], 'tags' => $customer['tags'], 'is_admin' => 0, 'business_id' => $customer['business_id'] ?? null, ] ); } } } private function isSegmentCustomer($customer) { // Define the criteria for segment customers return in_array('B2B', explode(',', $customer['tags'])); } public function getDiscounts() { $response = $this->client->get('price_rules.json'); $priceRules = json_decode($response->getBody()->getContents(), true)['price_rules']; $discounts = []; foreach ($priceRules as $rule) { $discounts[] = [ 'id' => $rule['id'], 'title' => $rule['title'], 'value' => $rule['value'], 'value_type' => $rule['value_type'], 'starts_at' => $rule['starts_at'], 'ends_at' => $rule['ends_at'], 'usage_limit' => $rule['usage_limit'], 'created_at' => $rule['created_at'], 'updated_at' => $rule['updated_at'] ]; } return $discounts; } public function getOrders() { $response = $this->client->get('orders.json'); return json_decode($response->getBody()->getContents(), true)['orders']; } public function getDraftOrders() { $response = $this->client->get('draft_orders.json'); return json_decode($response->getBody()->getContents(), true)['draft_orders']; } public function syncDiscounts() { try { $response = $this->client->get('price_rules.json'); $priceRules = json_decode($response->getBody()->getContents(), true)['price_rules']; foreach ($priceRules as $rule) { Discount::updateOrCreate( ['shopify_id' => $rule['id']], [ 'title' => $rule['title'], 'value_type' => $rule['value_type'], 'value' => $rule['value'], 'customer_selection' => $rule['customer_selection'], 'target_type' => $rule['target_type'], 'target_selection' => $rule['target_selection'], 'allocation_method' => $rule['allocation_method'], 'allocation_limit' => $rule['allocation_limit'], 'once_per_customer' => $rule['once_per_customer'], 'usage_limit' => $rule['usage_limit'], 'starts_at' => $rule['starts_at'], 'ends_at' => $rule['ends_at'], 'created_at' => $rule['created_at'], 'updated_at' => $rule['updated_at'], 'entitled_product_ids' => json_encode($rule['entitled_product_ids']), 'entitled_variant_ids' => json_encode($rule['entitled_variant_ids']), 'entitled_collection_ids' => json_encode($rule['entitled_collection_ids']), 'entitled_country_ids' => json_encode($rule['entitled_country_ids']), 'prerequisite_product_ids' => json_encode($rule['prerequisite_product_ids']), 'prerequisite_variant_ids' => json_encode($rule['prerequisite_variant_ids']), 'prerequisite_collection_ids' => json_encode($rule['prerequisite_collection_ids']), 'customer_segment_prerequisite_ids' => json_encode($rule['customer_segment_prerequisite_ids']), 'prerequisite_customer_ids' => json_encode($rule['prerequisite_customer_ids']), 'prerequisite_subtotal_range' => json_encode($rule['prerequisite_subtotal_range']), 'prerequisite_quantity_range' => json_encode($rule['prerequisite_quantity_range']), 'prerequisite_shipping_price_range' => json_encode($rule['prerequisite_shipping_price_range']), 'prerequisite_to_entitlement_quantity_ratio' => json_encode($rule['prerequisite_to_entitlement_quantity_ratio']), 'prerequisite_to_entitlement_purchase' => json_encode($rule['prerequisite_to_entitlement_purchase']), 'admin_graphql_api_id' => $rule['admin_graphql_api_id'] ] ); } } catch (\Exception $e) { throw new \Exception('Failed to sync discounts: ' . $e->getMessage()); } } public function getDiscount($id) { try { $response = $this->client->get("price_rules/{$id}.json"); $discount = json_decode($response->getBody()->getContents(), true)['price_rule']; return $discount; } catch (\Exception $e) { throw new \Exception('Failed to fetch discount: ' . $e->getMessage()); } } public function getOrderList() { try { $response = $this->client->get('orders.json'); $orders = json_decode($response->getBody()->getContents(), true)['orders']; return array_map(function ($order) { return [ 'id' => $order['id'], 'created_at' => $order['created_at'], 'shopify_id' => $order['id'] ]; }, $orders); } catch (\Exception $e) { throw new \Exception('Failed to fetch orders: ' . $e->getMessage()); } } public function getOrderDetails($id) { try { $response = $this->client->get("orders/{$id}.json"); $order = json_decode($response->getBody()->getContents(), true)['order']; return $order; } catch (\Exception $e) { throw new \Exception('Failed to fetch order details: ' . $e->getMessage()); } } public function getDraftOrder($id) { try { $response = $this->client->get("draft_orders/{$id}.json"); $draftOrder = json_decode($response->getBody()->getContents(), true)['draft_order']; return $draftOrder; } catch (\Exception $e) { throw new \Exception('Failed to fetch draft order: ' . $e->getMessage()); } } public function syncDraftOrders() { $orders = $this->getDraftOrders(); foreach ($orders as $orderData) { // Sync customer if (isset($orderData['customer'])) { $customerData = $orderData['customer']; $user = User::updateOrCreate( ['shopify_id' => $customerData['id']], [ 'email' => $customerData['email'] ?? '', 'name' => ($customerData['first_name'] ?? '') . ' ' . ($customerData['last_name'] ?? ''), 'first_name' => $customerData['first_name'] ?? '', 'last_name' => $customerData['last_name'] ?? '', 'orders_count' => $customerData['orders_count'] ?? 0, 'phone' => $customerData['phone'] ?? '', 'password' => bcrypt('defaultpassword'), // Set a default password 'total_spent' => $customerData['total_spent'] ?? 0, 'tags' => json_encode($customerData['tags'] ?? []), ] ); $user = User::where('shopify_id', $customerData['id'])->first(); } else { continue; // Skip order if customer data is not available } $order = Order::updateOrCreate( ['shopify_id' => $orderData['id']], [ 'note' => $orderData['note'] ?? '', 'email' => $orderData['email'] ?? '', 'taxes_included' => $orderData['taxes_included'] ?? false, 'currency' => $orderData['currency'] ?? '', 'invoice_sent_at' => $orderData['invoice_sent_at'] ?? null, 'created_at_shopify' => $orderData['created_at'] ?? null, 'updated_at_shopify' => $orderData['updated_at'] ?? null, 'tax_exempt' => $orderData['tax_exempt'] ?? false, 'completed_at' => $orderData['completed_at'] ?? null, 'name' => $orderData['name'] ?? '', 'status' => $orderData['status'] ?? '', 'subtotal_price' => $orderData['subtotal_price'] ?? 0, 'total_price' => $orderData['total_price'] ?? 0, 'total_tax' => $orderData['total_tax'] ?? 0, 'invoice_url' => $orderData['invoice_url'] ?? '', 'order_id' => $orderData['order_id'] ?? null, 'shipping_title' => $orderData['shipping_line']['title'] ?? '', 'shipping_price' => $orderData['shipping_line']['price'] ?? 0, 'user_id' => $user->id, ] ); // Sync line items if (isset($orderData['line_items'])) { foreach ($orderData['line_items'] as $lineItemData) { LineItem::updateOrCreate( ['shopify_id' => $lineItemData['id']], [ 'order_id' => $order->id, 'variant_id' => $lineItemData['variant_id'] ?? null, 'product_id' => $lineItemData['product_id'] ?? null, 'title' => $lineItemData['title'] ?? '', 'variant_title' => $lineItemData['variant_title'] ?? '', 'sku' => $lineItemData['sku'] ?? '', 'vendor' => $lineItemData['vendor'] ?? '', 'quantity' => $lineItemData['quantity'] ?? 0, 'requires_shipping' => $lineItemData['requires_shipping'] ?? false, 'taxable' => $lineItemData['taxable'] ?? false, 'gift_card' => $lineItemData['gift_card'] ?? false, 'fulfillment_service' => $lineItemData['fulfillment_service'] ?? '', 'grams' => $lineItemData['grams'] ?? 0, 'price' => $lineItemData['price'] ?? 0, 'tax_lines' => json_encode($lineItemData['tax_lines'] ?? []), 'properties' => json_encode($lineItemData['properties'] ?? []), ] ); } } // Sync shipping address if (isset($orderData['shipping_address'])) { Address::updateOrCreate( ['order_id' => $order->id, 'type' => 'shipping'], [ 'first_name' => $orderData['shipping_address']['first_name'] ?? '', 'last_name' => $orderData['shipping_address']['last_name'] ?? '', 'company' => $orderData['shipping_address']['company'] ?? '', 'address1' => $orderData['shipping_address']['address1'] ?? '', 'address2' => $orderData['shipping_address']['address2'] ?? '', 'city' => $orderData['shipping_address']['city'] ?? '', 'province' => $orderData['shipping_address']['province'] ?? '', 'country' => $orderData['shipping_address']['country'] ?? '', 'zip' => $orderData['shipping_address']['zip'] ?? '', 'phone' => $orderData['shipping_address']['phone'] ?? '', 'name' => $orderData['shipping_address']['name'] ?? '', 'country_code' => $orderData['shipping_address']['country_code'] ?? '', 'province_code' => $orderData['shipping_address']['province_code'] ?? '', ] ); } // Sync billing address if (isset($orderData['billing_address'])) { Address::updateOrCreate( ['order_id' => $order->id, 'type' => 'billing'], [ 'first_name' => $orderData['billing_address']['first_name'] ?? '', 'last_name' => $orderData['billing_address']['last_name'] ?? '', 'company' => $orderData['billing_address']['company'] ?? '', 'address1' => $orderData['billing_address']['address1'] ?? '', 'address2' => $orderData['billing_address']['address2'] ?? '', 'city' => $orderData['billing_address']['city'] ?? '', 'province' => $orderData['billing_address']['province'] ?? '', 'country' => $orderData['billing_address']['country'] ?? '', 'zip' => $orderData['billing_address']['zip'] ?? '', 'phone' => $orderData['billing_address']['phone'] ?? '', 'name' => $orderData['billing_address']['name'] ?? '', 'country_code' => $orderData['billing_address']['country_code'] ?? '', 'province_code' => $orderData['billing_address']['province_code'] ?? '', ] ); } } } public function saveCart($cartCollection) { // Assuming we have a logged-in user $user = auth()->user(); // Format the data to match Shopify's Draft Order requirements $lineItems = []; foreach ($cartCollection as $item) { // Retrieve the correct Shopify variant ID $shopifyVariant = ShopifyVariant::where('id', $item->id)->first(); if (!$shopifyVariant) { \Log::error('Invalid variant ID:', [$item->id]); continue; // Skip this item if the variant ID is not valid } // Log the Shopify variant ID for debugging purposes \Log::info('Shopify Variant ID:', [$shopifyVariant->shopify_id]); // Get price with conditions applied $lineItemPrice = $item->getPriceWithConditions(); // Create discounts for item-specific conditions $appliedDiscount = null; foreach ($item->conditions as $condition) { if ($condition->getType() === 'discount') { $discount = $this->createShopifyDiscount($condition, $shopifyVariant->shopify_id, $user); if ($discount) { $appliedDiscount = $discount; } } } // Assuming attributes are stored in an array format $properties = []; foreach ($item->attributes as $key => $value) { if (is_array($value)) { // Convert array to a JSON string $properties[] = [ 'name' => $key, 'value' => json_encode($value), ]; } else { $properties[] = [ 'name' => $key, 'value' => (string)$value, // Convert value to string ]; } } $lineItems[] = [ 'variant_id' => $shopifyVariant->shopify_id, 'quantity' => $item->quantity, 'price' => (string)$lineItemPrice, // Adjusted price with conditions 'title' => $item->name, 'properties' => $properties, 'applied_discount' => $appliedDiscount, ]; } // Calculate conditions for the cart total $cartTotal = \Cart::getTotal(); $cartConditions = \Cart::getConditions(); foreach ($cartConditions as $condition) { // Assuming conditions are either discounts (negative) or additional charges (positive) $conditionValue = $condition->getValue(); if (strpos($conditionValue, '%') !== false) { // If the condition is a percentage, apply it to the total $percentage = str_replace('%', '', $conditionValue) / 100; $cartTotal += $cartTotal * $percentage; } else { // If the condition is a fixed amount, add/subtract it directly $cartTotal += $conditionValue; } } // Include customer information in the draft order $customerData = [ 'email' => $user->email, 'first_name' => $user->first_name, 'last_name' => $user->last_name, 'phone' => $user->phone, ]; $draftOrderData = [ 'draft_order' => [ 'line_items' => $lineItems, 'note' => 'Draft order created from Laravel cart', 'customer' => $customerData, // Add customer data here 'email' => $user->email, // Add customer email here 'total_price' => (string)$cartTotal, // Adjusted cart total with conditions ] ]; // Send request to Shopify Draft Order API $response = $this->post('draft_orders.json', $draftOrderData); if (isset($response['draft_order'])) { return redirect()->back()->with('success', 'Cart saved as draft order successfully.'); } else { \Log::error('Failed to save draft order:', $response); return redirect()->back()->with('error', 'Failed to save cart as draft order.'); } } public function createShopifyDiscount($condition, $variantId, $user) { \Log::info('Creating Shopify discount for user:', [$user->shopify_id]); // Format the value $valueType = (strpos($condition->value, '%') !== false) ? 'percentage' : 'fixed_amount'; $value = str_replace('%', '', $condition->value); $discountData = [ 'price_rule' => [ 'title' => $condition->name, 'target_type' => 'line_item', 'target_selection' => 'entitled', // Specific to the variant 'allocation_method' => 'across', 'value_type' => $valueType, 'value' => $value, 'customer_selection' => 'prerequisite', 'prerequisite_customer_ids' => [$user->shopify_id], 'entitled_variant_ids' => [ $variantId ], 'starts_at' => now()->toISOString(), ] ]; \Log::info('Discount data being sent to Shopify:', $discountData); try { $response = $this->post('price_rules.json', $discountData); \Log::info('Shopify discount creation response:', $response); if (isset($response['price_rule'])) { return [ 'price_rule_id' => $response['price_rule']['id'], 'value' => $response['price_rule']['value'], 'value_type' => $response['price_rule']['value_type'], 'allocation_method' => $response['price_rule']['allocation_method'], ]; } else { \Log::error('Failed to create discount:', $response); return null; } } catch (\Exception $e) { \Log::error('Exception while creating discount:', ['message' => $e->getMessage(), 'data' => $discountData]); return null; } } protected function postGraphQL($query, $variables = []) { $response = $this->client->post("https://{$this->shopDomain}.myshopify.com/admin/api/graphql.json", [ 'auth' => [$this->apiKey, $this->apiPassword], 'json' => ['query' => $query, 'variables' => $variables], ]); return json_decode($response->getBody()->getContents(), true); } public function createDiscountOnShopify(Condition $condition) { // Determine the value type based on the condition value $valueType = 'percentage'; $value = str_replace('%', '', $condition->value); if (strpos($condition->value, '%') === false) { $valueType = 'fixed_amount'; } // Prepare the discount data for the REST API $discountData = [ 'price_rule' => [ 'title' => $condition->name, 'target_type' => 'line_item', 'target_selection' => 'entitled', 'allocation_method' => 'across', 'value_type' => $valueType, 'value' => -1 * floatval($value), 'customer_selection' => 'prerequisite', 'prerequisite_customer_ids' => [$condition->customer_id], 'entitled_variant_ids' => [$condition->variant_id], 'starts_at' => now()->toIso8601String(), ], ]; // Send request to Shopify Price Rules API $response = $this->post('price_rules.json', $discountData); if (isset($response['price_rule'])) { $priceRuleId = $response['price_rule']['id']; $discountCodeData = [ 'discount_code' => [ 'code' => $condition->name, ], ]; $discountCodeResponse = $this->post("price_rules/{$priceRuleId}/discount_codes.json", $discountCodeData); if (isset($discountCodeResponse['discount_code'])) { return $discountCodeResponse['discount_code']; } else { \Log::error('Failed to create discount code on Shopify:', $discountCodeResponse); throw new \Exception('Failed to create discount code on Shopify.'); } } else { \Log::error('Failed to create price rule on Shopify:', $response); throw new \Exception('Failed to create price rule on Shopify.'); } } // public function createShopifyDiscount($condition, $variantId, $user) // { // \Log::info('Creating Shopify discount for user:', [$user->shopify_id]); // $discountData = [ // 'price_rule' => [ // 'title' => $condition->getName(), // 'target_type' => 'line_item', // 'target_selection' => 'entitled', // Specific to the variant // 'allocation_method' => 'across', // 'value_type' => (strpos($condition->getValue(), '%') !== false) ? 'percentage' : 'fixed_amount', // 'value' => str_replace('%', '', $condition->getValue()), // 'customer_selection' => 'prerequisite', // 'prerequisite_customer_ids' => [$user->shopify_id], // 'entitled_variant_ids' => [ // $variantId // ], // 'starts_at' => now()->toISOString(), // ] // ]; // \Log::info('Discount data being sent to Shopify:', $discountData); // try { // $response = $this->post('price_rules.json', $discountData); // \Log::info('Shopify discount creation response:', $response); // if (isset($response['price_rule'])) { // return [ // 'price_rule_id' => $response['price_rule']['id'], // 'value' => $response['price_rule']['value'], // 'value_type' => $response['price_rule']['value_type'], // 'allocation_method' => $response['price_rule']['allocation_method'], // ]; // } else { // \Log::error('Failed to create discount:', $response); // return null; // } // } catch (\Exception $e) { // \Log::error('Exception while creating discount:', ['message' => $e->getMessage(), 'data' => $discountData]); // return null; // } // } } Server Error
500
Server Error