Внешние скидки в InSales

В SaaS платформе для разработки и эффективной эксплуатации Интернет-магазинов InSales сокрыто множество возможностей для успешных продаж в сети Интернет. Многие возможности требуют определенного исследования и разъяснения.

Нельзя не отметить, что встроенная система для скидок уже включена в большинство тарифных планов InSales и покрывает большую часть потребностей магазина. Кроме того, если встроенной системы скидок не хватает, для вас есть некоторое количество приложений в маркетплейсе интеграций, которые также позволяют делать скидочные программы в случаях, если встроенная система не до конца удовлетворяет вашим потребностям.

Однако, иногда может возникнуть необходимость в создании какой-либо нестандартной скидки именно для вашего магазина. В этой публикации мы расскажем, на конкретном примере, о создании внешних скидок для корзины, которые и призваны решать такие нестандартные задачи.

Что же такое внешняя ссылка? Это некий скрипт, написанный на любом языке программирования. Этот скрипт должен быть размещен на каком-либо сервере и доступен по URL для вашего магазина. Он будет получать от InSales данные корзины в формате JSON и должен будет возвращать ответ в том же формате.

Итак начнём. В административной панели вашего Интернет-магазина внешняя скидка добавляется в разделе Скидки. При добавлении в форму помещается ссылка на ваш скрипт и в общем-то все. Скидка начинает работать в корзине и форме оформления заказа (чекауте).

Рисунок 1: Добавление внешней скидки

Обо всём этом подробно рассказано в официальной документации на сайте InSales. А вот детали реализации возможно требуют некоторых пояснений и примеров.

Давайте создадим скрипт для скидки на сумму цены самого дешевого товара в корзине при 2 и более наименований товаров, добавленных покупателем в корзину.

Шаг первый

Посмотрим, что нам передает магазин и передает ли вообще. Для этого создадим простой скрипт, который будет сохранять полученный данные в файл.

Код скрипта:

<?php 
// Сохранить пришедшие данные в log. 
// По идее переменная $HTTP_RAW_POST_DATA устарела 
// и в новых версиях PHP нужно использовать конструкцию 
// $postdata = file_get_contents("php://input");
file_put_contents(
  'discount.log', 
  sprintf("%s\t%s" . PHP_EOL, date('c'), $HTTP_RAW_POST_DATA), 
  FILE_APPEND
);

// Мы должны вернуть магазину JSON объект с информацией о скидке.
// Пока вернем 0, потом это переделаем, пока это заглушка.
header('Content-Type: application/json', true, 200);
echo '{
 "discount": 0,
 "discount_type": "MONEY",
 "title": "Скидка на самый дешевый товар в корзине"
}';

Сохранив этот скрипт на сервере и подключив его с как внешнюю ссылку добавим несколько товаров в корзину и перейдем в нее. Мы должны увидеть в корзине, перед итогом по корзине строчку «Скидка на самый дешевый товар в корзине: 0». Если увидели значит все ок, если нет надо писать в техподдержку InSales. :)

Если же все работает, то стоит посмотреть на сервер, где рядом с нашим скриптом должен появится файл «discount.log». В нем сохранен объект полученный нами от магазина. Внутри этого файла появилась строка с JSON объектом, который нам передан от магазина. Там есть данные о клиенте, доставках и конечно же данные о товарах, которые добавлены в корзину. Вот именно эта часть объекта нам нужна для решения нашей задачи.

"order_lines": [{
  "id": null,
  "order_id": null,
  "sale_price": 37083,
  "full_sale_price": 37083,
  "total_price": 37083,
  "full_total_price": 37083,
  "discounts_amount": 0,
  "quantity": 1,
  "reserved_quantity": null,
  "weight": null,
  "dimensions": null,
  "variant_id": 133666502,
  "product_id": 78161350,
  "sku": null,
  "barcode": null,
  "title": "DELL INSPIRON 5720",
  "unit": "pce",
  "comment": "",
  "updated_at": null,
  "created_at": null,
  "bundle_id": null,
  "vat": -1
}, {
  "id": null,
  "order_id": null,
  "sale_price": 26670,
  "full_sale_price": 26670,
  "total_price": 26670,
  "full_total_price": 26670,
  "discounts_amount": 0,
  "quantity": 1,
  "reserved_quantity": null,
  "weight": null,
  "dimensions": null,
  "variant_id": 133666506,
  "product_id": 78161353,
  "sku": "17743",
  "barcode": null,
  "title": "LCD телевизор BBK LT4219HDU black",
  "unit": "pce",
  "comment": "",
  "updated_at": null,
  "created_at": null,
  "bundle_id": null,
  "vat": -1
}, {
  "id": null,
  "order_id": null,
  "sale_price": 491,
  "full_sale_price": 491,
  "total_price": 491,
  "full_total_price": 491,
  "discounts_amount": 0,
  "quantity": 1,
  "reserved_quantity": null,
  "weight": null,
  "dimensions": null,
  "variant_id": 142054998,
  "product_id": 83680232,
  "sku": "OZ490",
  "barcode": null,
  "title": "Обработка заказа",
  "unit": "pce",
  "comment": "Сервисный товар",
  "updated_at": null,
  "created_at": null,
  "bundle_id": null,
  "vat": -1
}]

В данном случае у нас в корзине три товара. Дальше дело техники, — пишем код который находит самый дешевый товар и делает на него скидку.

Шаг второй, заключительный

Код тестового скрипта внешней скидки:

<?php 
 
/**
 * Файл содержит демонстрационный пример скрипта внешней скидки InSales
 * @package    InSales-Api
 * @author     Константин Тарасов <info@insales-studio.ru>
 * @category   InSales
 * @copyright  Copyright (c) 2006-2018 InSales Studio. (https://insales-studio.ru)
 * @license    MIT License
 *
 */

/**
 * Сообщения об ошибках
 */
define('NO_ITEMS', 'Товары не обнаружены');
define('LESS_ITEMS', 'Скидка невозможна, поскольку товаров в корзине меньше 2');
define(
  'NIL_ITEMS', 
  'Скидка невозможна, поскольку товаров с ненулевой ценой в корзине меньше 2'
);

/**
 * Cервисный объект для построения отправляемого в магазин JSON со скидкой
 */
class ResponceDiscount {
  public $discount;
  public $discount_type;
  public $title;

  /**
   * Конcтруктор объект ответа со скидкой
   * @param $discount      - сумма/процент скидки
   * @param $discount_type - тип скидки MONEY/PERCENT
   * @param $discount      - название скидки для отображения в корзине/чекауте
   */
  public function __construct($discount = 0, $discount_type = 'MONEY', $title = 'Скидка на самый дешевый товар в корзине') {
    $this->title = $title;
    $this->discount = $discount;
    $this->discount_type = $discount_type;
  }
}

/**
 * Cервисный объект для построения отправляемого в магазин JSON с ошибкой
 */
class ResponceError {
  public $errors;

  /**
   * Конcтруктор объект ответа с ошибкой
   * @param $error_msg - сообщение об ошибке/массив строк с ошибками
   */
  public function __construct($error_msg = null) {
    $this->errors = array();
    if($error_msg) $this->addError($error_msg);
  }

  public function addError($error_msg) {
    if(is_array($error_msg)) {
      $this->errors = array_merge($this->errors, $error_msg);
    } elseif (is_string($error_msg)) {
      $this->errors[] = $error_msg;
    }
  }
}

function LogEntry($msg) {
  file_put_contents('count-discount.log', 
    sprintf("%s\t%s" . PHP_EOL, date('c'), 
      (is_scalar($msg) ? $msg : print_r($msg, true))
    ), 
    FILE_APPEND
  );
}

function process_items($items) {
  if(!is_array($items)) 
    return new ResponceError(NO_ITEMS);

  $items_count = count($items);
  if($items_count < 2)
    return new ResponceError(LESS_ITEMS);

  // создадим массив цен
  $prices = array();
  foreach ($items as $item) {
    if($item->sale_price == 0)
      continue;

    $prices[] = $item->sale_price;
  }

  if(count($prices) < 2)
    return new ResponceError(NIL_ITEMS);

  // сортируем массив цен товаров в корзине
  sort($prices, SORT_NUMERIC);

  // создадим объект ответа с минимальной ценой из массива
  return new ResponceDiscount($prices[0]);
}

// получаем тело запроса и его отправим сразу в лог
LogEntry($HTTP_RAW_POST_DATA);

// декодируем объект, полученный от InSales
$cart = json_decode($HTTP_RAW_POST_DATA);
// проверим наличие строк корзины в полученном объекте
$items = isset($cart->order_lines) ? $cart->order_lines : null;

$response = process_items($items);

unset($cart, $items);

header('Content-Type: application/json', true, 200);
echo json_encode($response);

Код достаточно подробно документирован, т.ч. нет особого смысла его как-то дополнительно обсуждать. Сразу скажу, что это демонстрационный код и в реальном проекте его можно и нужно совершенствовать. Другое дело, что его можно будет использовать, как основу для создания своего скрипта скидки.