Pull to refresh

Библиотека для работы с QIWI через SOAP

Reading time 4 min
Views 18K
Так уж получилось, что мы решили у себя подключить прием платежей через QIWI. Сказано — сделано! Вот только в процессе разработки пришлось столкнуться с убогостью примеров кода от разработчиков киви:
Код сервера, принимающего запрос от киви
<?php
/**
 * На этот скрипт приходят уведомления от QIWI Кошелька.
 * SoapServer парсит входящий SOAP-запрос, извлекает значения тегов login, password, txn, status,
 * помещает их в объект класса Param и вызывает функцию updateBill объекта класса TestServer.
 *
 * Логика обработки магазином уведомления должна быть в updateBill.
 */

 $s = new SoapServer('IShopClientWS.wsdl', array('classmap' => array('tns:updateBill' => 'Param', 'tns:updateBillResponse' => 'Response')));
// $s = new SoapServer('IShopClientWS.wsdl');
 $s->setClass('TestServer');
 $s->handle();

 class Response {
  public $updateBillResult;
 }

 class Param {
  public $login;
  public $password;
  public $txn;      
  public $status;
 }

 class TestServer {
  function updateBill($param) {
  
	// Выводим все принятые параметры в качестве примера и для отладки
    $f = fopen('c:\\phpdump.txt', 'w');
	fwrite($f, $param->login);
	fwrite($f, ', ');
	fwrite($f, $param->password);
	fwrite($f, ', ');
	fwrite($f, $param->txn);
	fwrite($f, ', ');
	fwrite($f, $param->status);
	fclose($f);
	
	// проверить password, login
	
	// В зависимости от статуса счета $param->status меняем статус заказа в магазине
	if ($param->status == 60) {
		// заказ оплачен
		// найти заказ по номеру счета ($param->txn), пометить как оплаченный
	} else if ($param->status > 100) {
		// заказ не оплачен (отменен пользователем, недостаточно средств на балансе и т.п.)
		// найти заказ по номеру счета ($param->txn), пометить как неоплаченный
	} else if ($param->status >= 50 && $param->status < 60) {
		// счет в процессе проведения
	} else {
		// неизвестный статус заказа
	}

	// формируем ответ на уведомление
	// если все операции по обновлению статуса заказа в магазине прошли успешно, отвечаем кодом 0
	// $temp->updateBillResult = 0
	// если произошли временные ошибки (например, недоступность БД), отвечаем ненулевым кодом
	// в этом случае QIWI Кошелёк будет периодически посылать повторные уведомления пока не получит код 0
	// или не пройдет 24 часа
	$temp = new Response();
	$temp->updateBillResult = 0;
	return $temp;
  }
 }
?>

Я конечно понимаю, пример исчерпывающий, но можно ведь было что-нибудь «поготовее» выложить? Поскольку система популярна, как и язык PHP — я решил сразу вынести библиотеку в публичный репозитарий, дабы упростить жизнь тем, кому только предстоит подключать эту систему. Так как в недавнем моем вопросе никто против поста не возражал — выкладываю ее тут.

В библиотеке есть как клиентская, так и серверная часть. Подключается элементарно — выгружаем через git или добавляем зависимость в composer (werkint/qiwi). Основная работа идет через экземпляр класса Qiwi\Client. Например, можно от него наследоваться, и в конструкторе указать имя/пароль магазина, а класс оформить как сервис Symfony2 (так у нас сделано):
Подключение либы
<?php
namespace MyOwnMegaPrefix\Qiwi;

use MyOwnMegaPrefix\Settings,
    Werkint\Qiwi;

class MyQiwi extends Qiwi\Client
{
    protected $settings;

    public function __construct(
        Settings $settings // Нечто, что выдает нам настройки
    ) {
        $this->settings = $settings;

        parent::__construct(
            $this->settings->get('qiwi.login'),
            $this->settings->get('qiwi.pass')
        );
    }
}

Работа клиентом (это тот, кто запросы на SOAP-сервер шлет) элементарна — берем экземпляр Qiwi\Client и вызываем соответствующие методы. Они — лишь простые обертки над классом для работы с php-soap, преобразовывают параметры, да код возврата меняют на более подробный статус. Поля даты — в \DateTime, все остальное — соответствует api qiwi (только логин/пароль дублировать не нужно).
Работа с сервером чуть сложнее. Поскольку методы сервера вызываются весьма хитро (SOAP же), было решено использовать замыкания для обработки запроса. Да, не забываем отключить сертификат x509 в настройках. Я так и не нашел способа прикрутить WSSE к php-soap (насколько я понял, никто не нашел). Обидно то, что в примере кода для Java проверка сертификата есть.
Код сервера, принимающего запрос от киви

use Werkint\Qiwi\ServerMethods\CheckBillResponse as QiwiBill;
$callback = function ($bill) use (&$myMegaService) {
    /** @var QiwiBill $bill */
    $row = $myMegaService->findByKey( // Ищем чек в нашей базе
        $bill->id
    );
    if (!$row) {
        throw new \Exception('Неправильный код чека');
    }
    $myMegaService->process($row); // Что-то делаем с этим
    return $myMegaService->status(); // Код возврата для сервера QIWI. 0 - все нормально
};
// Вызываем метод обработки запроса
$theQiwiObject->processRequest($callback);
// Если мы отдадим text/html, qiwi не пропустит платеж (да и вообще, надо протоколу следовать)
header('Content-Type: text/xml; charset=utf-8');

Удобно? Мне кажется — да. Вы можете просто сравнить с альтернативным путем. Итак, преимущества библиотеки:
  • Можно вообще не задумываться о SOAP, WSDL и т.д. Все просто работает.
  • Для сервера есть проверка подписи, после обновления статуса, чек повторно подгружается с сервера (как рекомендуют доки киви).
  • PHPDoc и все-все-все, так сложнее ошибиться. Кое-где есть дополнительные уточнения (где грабли лежат).
  • Статусы расшифровываются (код -> текст).
  • Есть в packagist, подключение займет 2 минуты.
  • Нормально работает с автолоадером.

Само-собой, есть минусы:
  • Какой-то стрёмный тип предлагает вам выписывать чеки через его библиотеку.
  • PHP 5.3. Замыкания + неймспейсы. Если вас это не устраивает, повырезать новое не составит труда.

Ссылки: описание протокола, пример кода, репозитарий.
Уже после того, как система была прикручена, я узнал о готовой библиотеке для работы с киви — от пользователя JekaRu. Значит, будет больший выбор. :)
Спасибо за внимание. Если будут замечания касательно чистоты кода — с радостью внемлю.

UPD: Добавил описание в Readme.md и перевел либу на PSR-2
UPD2: Перенес либу, nick4fake/ishop -> werkint/qiwi. Для совместимости оставил по старому адресу старый код.
Tags:
Hubs:
+14
Comments 16
Comments Comments 16

Articles