function SMShopProcessNewOrder(SMKeyValueCollection $order) { // Variables $eDs = new SMDataSource("SMShopOrderEntries"); $pDs = new SMDataSource("SMShopProducts"); $products = null; $product = null; $discount = 0; $discountExpression = null; $expr = null; $price = 0; $priceTotal = 0; $vatTotal = 0; $currency = null; $weightTotal = 0; $weightUnit = null; $shippingExpense = 0; $shippingVat = 0; // Load order entries if ($eDs->GetDataSourceType() === SMDataSourceType::$Xml) { $eDs->Lock(); } $entries = $eDs->Select("*", "OrderId = '" . $eDs->Escape($order["Id"]) . "'"); // Ensure that order has order entries associated if (count($entries) === 0) { header("HTTP/1.1 500 Internal Server Error"); echo "Inconsistent order - no associated order entries found (must be created first)"; exit; } // Obtain new order ID (order was created with a temporary ID (GUID) generated client side) SMAttributes::Lock(); // Prevent two sessions from obtaining the same Order ID SMAttributes::Reload(false); // No data will be lost when reloading attributes from a callback since no extensions are being executed $orderIdStr = SMAttributes::GetAttribute("SMShopNextOrderId"); $orderId = $orderIdStr !== null ? (int) $orderIdStr : 1; SMAttributes::SetAttribute("SMShopNextOrderId", (string) ($orderId + 1)); SMAttributes::Commit(); // Also releases lock // Loop through order entries to extract currency, calculate // discounts/totals/VAT, and update entries with these information. foreach ($entries as $entry) { // Get product associated with entry $products = $pDs->Select("*", "Id = '" . $entry["ProductId"] . "'"); if (count($products) === 0) { header("HTTP/1.1 500 Internal Server Error"); echo "Product with ID '" . $entry["ProductId"] . "' has been removed"; exit; } $product = $products[0]; // Make sure all products are defined with the same currency and weight unit $currency = $currency !== null ? $currency : $product["Currency"]; if ($currency !== $product["Currency"]) { header("HTTP/1.1 500 Internal Server Error"); echo "Buying products with different currencies is not supported"; exit; } $weightUnit = $weightUnit !== null ? $weightUnit : $product["WeightUnit"]; if ($weightUnit !== $product["WeightUnit"]) { header("HTTP/1.1 500 Internal Server Error"); echo "Buying products with different weight units is not supported"; exit; } // Get discount expression $discount = 0; $discountExpression = $product["DiscountExpression"]; if ($discountExpression !== "") { // Security validation //$discountExpression = preg_replace("/Math\\.[a-z]+/i", "", $discountExpression); $discountExpression = preg_replace("/ |[0-9]|\\*|\\+|\\-|\\/|=|&|\\||!|\\.|:|\\(|\\)|>|<|\\?|true|false/", "", $discountExpression); $discountExpression = preg_replace("/units|price|vat|currency|weight|weightunit/", "", $discountExpression); if ($discountExpression !== "") { header("HTTP/1.1 500 Internal Server Error"); echo "Invalid and potentially insecure DiscountExpression detected"; exit; } // Make variables available to discount expression $expr = ""; $expr .= "\nunits = " . $entry["Units"] . ";"; $expr .= "\nprice = " . $product["Price"] . ";"; $expr .= "\nvat = " . $product["Vat"] . ";"; $expr .= "\ncurrency = \"" . $product["Currency"] . "\";"; $expr .= "\nweight = " . $product["Weight"] . ";"; $expr .= "\nweightunit = \"" . $product["WeightUnit"] . "\";"; $expr .= "\nreturn (" . $product["DiscountExpression"] . ");"; // Turn JS variables into PHP compliant variables $expr = str_replace("units", "\$units", $expr); $expr = str_replace("price", "\$price", $expr); $expr = str_replace("vat", "\$vat", $expr); $expr = str_replace("currency", "\$currency", $expr); $expr = str_replace("weight", "\$weight", $expr); // $weight AND $weightunit (both starts with "weight") // Evaluate discount expression, and calculate price and VAT $discount = eval($expr); if (is_numeric($discount) === false) { header("HTTP/1.1 500 Internal Server Error"); echo "DiscountExpression did not result in a valid numeric value"; exit; } } // Totals $price = (int) $entry["Units"] * (double) $product["Price"] - $discount; $priceTotal += $price; $vatTotal += $price * ((double) $product["Vat"] / 100); $weightTotal += (int) $entry["Units"] * (double) $product["Weight"]; // Update entry $entry["OrderId"] = (string) $orderId; $entry["UnitPrice"] = $product["Price"]; $entry["Vat"] = $product["Vat"]; $entry["Currency"] = $product["Currency"]; $entry["Discount"] = (string) $discount; $entry["DiscountMessage"] = $discount !== 0 ? $product["DiscountMessage"] : ""; $eDs->Update($entry, "Id = '" . $eDs->Escape($entry["Id"]) . "'"); } $eDs->Commit(); // Calculate shipping expense $shippingExpenseExpression = SMAttributes::GetAttribute("SMShopShippingExpenseExpression"); $shippingExpenseVatPercentage = SMAttributes::GetAttribute("SMShopShippingExpenseVat"); $shippingExpenseMessage = SMAttributes::GetAttribute("SMShopShippingExpenseMessage"); if ($shippingExpenseExpression !== null && $shippingExpenseExpression !== "") { // Security validation $shippingExpenseExpression = preg_replace("/ |[0-9]|\\*|\\+|\\-|\\/|=|&|\\||!|\\.|:|\\(|\\)|>|<|\\?|true|false/", "", $shippingExpenseExpression); $shippingExpenseExpression = preg_replace("/price|vat|currency|weight|weightunit/", "", $shippingExpenseExpression); if ($shippingExpenseExpression !== "") { header("HTTP/1.1 500 Internal Server Error"); echo "Invalid and potentially insecure ShippingExpenseExpression detected"; exit; } // Make variables available to discount expression $expr = ""; $expr .= "\nprice = " . $priceTotal . ";"; $expr .= "\nvat = " . $vatTotal . ";"; $expr .= "\ncurrency = \"" . $currency . "\";"; $expr .= "\nweight = " . $weightTotal . ";"; $expr .= "\nweightunit = \"" . $weightUnit . "\";"; $expr .= "\nreturn (" . SMAttributes::GetAttribute("SMShopShippingExpenseExpression") . ");"; // Turn JS variables into PHP compliant variables $expr = str_replace("price", "\$price", $expr); $expr = str_replace("vat", "\$vat", $expr); $expr = str_replace("currency", "\$currency", $expr); $expr = str_replace("weight", "\$weight", $expr); // $weight AND $weightunit (both starts with "weight") // Evaluate shipping expense expression $shippingExpense = eval($expr); if (is_numeric($shippingExpense) === false) { header("HTTP/1.1 500 Internal Server Error"); echo "ShippingExpenseExpression did not result in a valid numeric value"; exit; } $priceTotal += $shippingExpense; if ($shippingExpenseVatPercentage !== null && $shippingExpenseVatPercentage !== "") { $shippingVat = $shippingExpense * ((double) $shippingExpenseVatPercentage / 100); $vatTotal += $shippingVat; } } // Update order details $order["Id"] = (string) $orderId; $order["Price"] = (string) $priceTotal; $order["Vat"] = (string) $vatTotal; $order["Currency"] = $currency; $order["Weight"] = (string) $weightTotal; $order["WeightUnit"] = $weightUnit; $order["ShippingExpense"] = (string) $shippingExpense; $order["ShippingVat"] = (string) $shippingVat; $order["ShippingMessage"] = $shippingExpenseMessage !== null ? $shippingExpenseMessage : ""; $order["TransactionId"] = ""; $order["State"] = "Initial"; }