13.12.2017

Разработка PHP SDK для транспортной компании DPD

Задача

Для упрощения внедрения клиентов транспортной компании DPD к их API, нужно реализовать набор инструментов разработки PHP.

Мы разработали библиотеку, в которой представлены шаблоны кодов для выполнения следующих задач:

  • Расчет стоимости доставки
  • Отправка заявки на доставку
  • Работа с местоположениями и терминалами
  • Отслеживание статусов заявок на доставку

Требования

  • PHP 5.4.* или выше
  • SOAP
  • PDO
  • PDO-SQLite

Установка

Для установки скачайте tar.gz или zip, распакуйте его и подключите файл autoload.php в Вашем проекте.

require_once 'path/to/dpd.sdk/src/autoload.php';

После этого необходимо произвести импорт начальных данных. Примеры загрузчиков находятся в папке examples/ внутри архива.

Настройки

Следующие параметры используются в модуле

  • UPLOAD_DIR - задается путь к директории для хранения запрошенных файлов
  • DB - параметры подключения к БД. Модуль использует БД для хранения списка городов обслуживания и списка ПВЗ. По умолчанию модуль использует локальную SQLite БД.
  • DB.DSN - dsn строка подключения к БД
  • DB.USERNAME - имя пользователя для подключения к БД
  • DB.PASSWORD - пароль пользователя для подключения к БД
  • DB.DRIVER - используемый драйвер при подключении к БД. По умолчанию будет вычеслен автоматически из строки DSN
  • DB.PDO - вместо всех параметров, можно сразу передать готово подключение в виде объекта \PDO
  • KLIENT_NUMBER - клиентский номер в системе DPD
  • KLIENT_KEY - секретный ключ для авторизации в системе DPD
  • KLIENT_CURRENCY - валюта аккаунта
  • KLIENT_NUMBER_KZ,KLIENT_KEY_KZ,KLIENT_CURRENCY_KZ - параметры для подключения казахского аккаунта
  • KLIENT_NUMBER_BY,KLIENT_KEY_BY,KLIENT_CURRENCY_BY - параметры для подключения белорусского аккаунта
  • API_DEF_COUNTRY - возможные значения RU,KZ,BY. Код аккаунта по умолчанию
  • IS_TEST - вкл./выкл. тестовый режим
  • WEIGHT - вес товара по умолчанию
  • LENGTH - длина товара по умолчанию
  • WIDTH - ширина товара по умолчанию
  • HEIGHT - высота товара по умолчанию
  • TARIFF_OFF - список тарифов которые НЕ будут использованы в расчетах. возможные значения PCL,CSM,ECN,ECU
  • DEFAULT_TARIFF_CODE - тариф по умолчанию, данный тариф будет выбран, если стоимость доставки меньше чем указано в DEFAULT_TARIFF_THRESHOLD.
  • DEFAULT_TARIFF_THRESHOLD - макс сумма доставки при которой будет использован тариф по умолчанию
  • DECLARED_VALUE - Включать страховку в стоимость доставки
  • COMMISSION_NPP_CHECK - Включать комиссию за инкассацию наложенным платежом в стоимость доставки, массив вида [PERSONE_TYPE_ID => bool, ...]
  • COMMISSION_NPP_PERCENT - Комиссия от стоимости товаров в заказе (в процентах), %. массив вида [PERSONE_TYPE_ID => double, ...]
  • COMMISSION_NPP_MINSUM - Минимальная сумма комиссии, руб. массив вида [PERSONE_TYPE_ID => double, ...]
  • COMMISSION_NPP_PAYMENT - ID платежных системы, которые означают что оплата будет происходить наложенным платежом . массив вида [PERSONE_TYPE_ID => [PAYMENT_SYSTEM_ID, ...], ...]
  • COMMISSION_NPP_DEFAULT - Если платежную систему определить не удалось, считать ли что оплата будет происходить наложенным платежом по умолчанию?. массив вида [PERSONE_TYPE_ID => bool, ...]

Для хранения настроек в модуле реализован класс \Ipol\DPD\Config\Config, в конструктор класса можно передать массив опций, тем самым переопределив значения по умолчанию. Так же может быть реализован свой класс для хранения настроек, в этом случае он должен реализовывать интерфейс Ipol\DPD\Config\ConfigInterface.

Расчет стоимости доставки

Для расчета стоимости доставки вначале необходимо создать объект описывающий отправку. Для этого в модуле используется класс Ipol\DPD\Shipment

$config = new \Ipol\DPD\Config\Config([
    // ... параметры авторизации
]);

$shipment = new \Ipol\DPD\Shipment($config);

// Указываем города отправления и назначения
$shipment->setSender('Россия', 'Москва', 'г. Москва');
$shipment->setReceiver('Россия', 'Тульская область', 'г. Тула');

// указываем отправку терминал - дверь
$shipment->setSelfPickup(true);
$shipment->setSelfDelivery(false);

// список товаров входящих в отправку
$goods = [
    1 => [
        'NAME'     => 'Название товара',
        'QUANTITY' => 1, // кол-во
        'PRICE'    => 1000, // стоимость за еденицу
        'VAT_RATE' => 18, // ставка налога, процент или строка Без НДС
        'WEIGHT'   => 1000, // вес, граммы,
        'DIMENSIONS' => [
            'LENGTH' => 100, // длина, мм,
            'WIDTH'  => 200, // ширина, мм,
            'HEIGHT' => 200, // высота, мм,
        ]
    ],

    // ...
];

// объявленная ценность
$goodsPrice = 1000;

// устанавливаем товары входящие в отправку
$shipment->setItems($goods, $goodsPrice);

// так же можно указать тип покупателя и платежную систему
// в зависимости от указанных параметров можно реализовать наценку на доставку при использовании наложенного платежа
$personeTypeId = 1; // значения переменный должны быть использованы в настроках секция COMMISSION_NPP_*
$paySystemId   = 1;
$shipment->setPaymentMethod($personeTypeId, $paySystemId);

За сам расчет стоимости доставки в модуле отвечает класс \Ipol\DPD\Calculator на вход он получает отправку для которой необходимо произвести расчет стоимости.

// так можно получить калькулятор
$calc = $shipment->calculator();

// вернет информацию об актуальном тарифе для отправки
$tariff = $calc->calculate();

// а так можно расчитать стоимость доставки конкретного тарифа
$tariff = $calc->calculateWithTariff('PCL');

В методах calculate и calculateWithTariff калькулятора есть необязательный параметр $currency, в котором можно передать код валюты, в этом случае стоимость достаки будет сконвертирована в указанную валюту. Для поддержки конвертации Вам необходимо создать класс конвертер реализующий интерфейс \Ipol\DPD\Currency\ConverterInterface, а экземпляр этого класса необходимо передать калькулятору

class Converter implements \Ipol\DPD\Currency\ConverterInterface
{
    // реализация
}

$converter = new Converter();
$tariff = $calc
            ->setCurrencyConverter($converter)
            ->calculate('USD');

Отправка заказа

Для создания заказа в системе DPD сначала необходимо создать объект хранящий информацию об этом заказе. В модуле за это отвечает класс \Ipol\DPD\DB\Order\Model, данный заказ будет сохранен в БД.

$config = new \Ipol\DPD\Config\COnfig([
    // ...
]);

$shipment = new Shipment($config);
// указываем параметры отправления

$order = \Ipol\DPD\DB\Connection::getInstance($config)->getTable('order')->makeModel();
$order->orderId = 'Внешний код заказа';

// данная модель может заполнить часть параметров из переданной отправки
// в противном случае можно все эти параметры заполнить вручную указав значения соответствующих полей
$order->setShipment($order);

// указываем тариф отправки
$order->serviceCode = 'PCL';
// если не использовать объект отправки, так же необходимо указать вариант доставки
// в нашем случае это терминал - дверь
// $order->serviceVariant = [SELF_PICKUP => true, SELF_DELIVERY => false]

// Дата и время забора
$order->pickupDate = '2017-11-29';
$order->pickupTimePeriod = '9-18';

// данные отправителя
$order->senderName = 'Наименование отправителя';
$order->senderFio = 'ФИО отправителя';
$order->senderPhone = 'Телефон отправителя';
$order->senderTerminalCode = 'Код терминала отправления';

// данные получателя
$order->receiverName = 'Наименование получателя';
$order->receiverFio = 'ФИО получателя';
$order->receiverPhone = 'Телефон получателя';
$order->receiverStreet = 'Улица';
$order->receiverStreetabbr = 'ул.';
$order->receiverHouse = 'дом';
$order->receiverComment = 'инструкция для курьера';

После того как все параметры заказа заполнены, нужно вызвать метод dpd. Данный метод возвращает объект класса Ipol\DPD\Order позволяющий производить операции с заказом на стороне dpd, в частности создание, отмену, запрос файла наклеек или накладной, проверка статуса и др.

// создаем заказ в системе dpd
$result = $order->dpd()->create();

// $order->dpd()->cancel(); // отменяем
// $order->dpd()->checkStatus(); // проверка статуса заказа
// $order->dpd()->getLabelFile(); // получить файл с наклейкой
// $order->dpd()->getInvoiceFile(); // получить файл с накладной

// получить созданный ранее заказ и отменить его
$orderId = 1; // внешний ID заказа
$order = \Ipol\DPD\DB\Connection::getInstance($config)->getTable('order')->getByOrderId($orderId);
$order->dpd()->cancel();

Работа с местоположениями и терминалами

При установке модуля и загрзуки первоначальных данных список городов и терминалов сохраняются во внутреннюю БД. В дальнейшем возможно использовать эти данные в своих целях. Для этого в модуле реализован простейший DataMapper к бд.

Получить доступ к таблице можно следующим образом

$orderTable    = \Ipol\DB\Connection::getInstance($config)->getTable('order');
$locationTable = \Ipol\DB\Connection::getInstance($config)->getTable('location');
$terminalTable = \Ipol\DB\Connection::getInstance($config)->getTable('terminal');

Во всех случаях будет возвращен объект класс которого реализует интерфейс \Ipol\DPD\DB\TableInterface. Помимо этого, в классе могут быть реализованы свои вспомогательные методы. Например у класса местоположений есть метод getByAddress($country, $region, $city, $select = '*') который ищет местоположение по текстовому представлению.

// получим терминалы принимающие наложенный платеж
$items = $terminalTable->find([
    'where' => 'NPP_AVAILABLE = "Y"',
])->fetchAll();

Cron

В модуле реализован класс \Ipol\DPD\Agents содержащий готовые методы для выполнения периодических заданий. В частности в данном классе есть метод обновляющий статусы заказов.

Для использования этих методов необходимо создать отдельный скрипт, в котором вызвать нужный метод. А выполнение данного скрипта поставить на cron.

Следующий пример показывает как создать скрипт обновления статусов заказов каждые 10 минут.

// dpd-check-status.php

$config = new \Ipol\DPD\Config\Config([
    // параметры
]);

\Ipol\DPD\Agents::checkOrderStatus($config);
# crontab
*/10 * * * * /path/to/php /path/to/dpd-check-status.php

Кодировка

Модуль работает с данными в кодировке UTF-8, если Ваш проект использует другую кодировку, Вам необходимо самостоятельно конвертировать передаваемые и получаемые данные.

Для облегчения конвертации в модуле есть вспомогательный метод позволяющий рекурсивно изменить кодировку переданного массива

$data = [
    'PARAM1' => 'Параметр 1'
    'PARAM2' => [
        'SUB1' => 'п 2.1',
        'SUB2' => 'п 2.2',
    ],
]

$convertData = \Ipol\DPD\Utils::convertEncoding($data, 'windows-1251', 'UTF-8');

Примеры

В каталоге examples/ приведены примеры использования модуля

Документация

Описание классов, методов и их параметров доступно здесь


Библиотеку DPD PHP SDK можно скачать здесь: https://bitbucket.org/DPDinRussia/dpd.sdk