Symfony2: интеграция с платежными системами с помощью JMSPaymentCoreBundle
10.03.2013Вступление
JMSPaymentCoreBundle предоставляет базу для различных бэкендов платежных систем. Пакет представляет собой набор абстракций, унифицированный API для финансовых транзакций.
Краткий список возможностей:
- Простой, унифицированный API
- Возможность сохранения сущностей финансовых операций
- Управление транзакциями
- Шифрование данных
Лицензия
Документация - Attribution-NonCommercial-NoDerivs 3.0 Unported
license.
Установка
Для установки JMSPaymentCoreBundle можно воспользоваться Composer. Добавьте следующую строку в файл composer.json:
// composer.json { // ... require: { // ... "jms/payment-core-bundle": "master-dev" } }
$ php composer.phar update
Этой командой Composer загрузит и установит все необходимые файлы. Далее необходимо обновить
AppKernel.php добавив следующие строки:
<?php // in AppKernel::registerBundles() $bundles = array( // ... new JMSPaymentCoreBundleJMSPaymentCoreBundle(), // ... );
Первоначальная настройка
Настройка пакета JMSPaymentCoreBundle заключается в выборе "секретной" строки с помобщью которой будут шифроваться финансовый данные.
Следующие строки нужно добавить в configuration.yml проекта:
jms_payment_core: secret: someS3cretP4ssw0rd
Настройка бэкендов
Некоторые бэкенды платежных систем предоставляют свои пакеты, которые также требуют некоторые настройки.
Модель
Перед тем как принимать/обрабатывать платежи, давайте кратко ознакомимся с моделью платежей.
PaymentInstruction
PaymentInstruction это первый объект который вам нужно создать. Он содержит такую иниформацию как общее количество, способ оплат, валюта, и другие данные, которые необходимы для определенного типа платежа, например информацию кредитной карты.
Платеж (Patment)
Каждый объект PaymentInstruction может быть разделен на несколько платежей.
Payment всегда содержит общее количество и текущее состояние объекта, такие как initiated, approved, deposited, итд.
Это позволяет, например, запросить часть от общей суммы перед предоставлением "товара", и остальное после этого.
Финансовая операция (FinancialTransaction)
Каждый платеж (Payment) может иметь несколько транзакций. Каждая транзакция (FinancialTransaction)представляет специфическое взаимодействие с бэкендом конкретной платежной системы. Например, в случае оплаты кредитной картой, это может быть авторизация.
Ниже приведены состояния объекта FinancialTransaction:
Использование
<?php use DoctrineORMMapping as ORM; use JMSPaymentCoreBundleEntityPaymentInstruction; class Order { /** @ORMOneToOne(targetEntity="JMSPaymentCore:PaymentInstruction") */ private $paymentInstruction; /** @ORMColumn(type="string", unique = true) */ private $orderNumber; /** @ORMColumn(type="decimal", precision = 2) */ private $amount; // ... public function __construct($amount, $orderNumber) { $this->amount = $amount; $this->orderNumber = $orderNumber; } public function getOrderNumber() { return $this->orderNumber; } public function getAmount() { return $this->amount; } public function getPaymentInstruction() { return $this->paymentInstruction; } public function setPaymentInstruction(PaymentInstruction $instruction) { $this->paymentInstruction = $instruction; } // ... }
Выбор способа оплаты
Обычно, вы желаете предоставить потенциальному покупателю выбор способа оплаты. Для этих целей JMSPaymentCoreBundle поставляеться с специальной формой jms_choose_payment_method.
Следующий пример использует JMSDiExtraBundle, и SensioFrameworkExtraBundle но они не обязательны.
Заметка: в примере не учтены вопросы безопасности приложения. Для полноценной реализации следует учитывать права доступа на действия (detailsAction и другие).
<?php use JMSDiExtraBundleAnnotation as DI; use JMSPaymentCoreBundleEntityPayment; use JMSPaymentCoreBundlePluginControllerResult; use JMSPaymentCoreBundlePluginExceptionActionRequiredException; use JMSPaymentCoreBundlePluginExceptionActionVisitUrl; use SensioBundleFrameworkExtraBundleConfigurationRoute; use SensioBundleFrameworkExtraBundleConfigurationTemplate; use SymfonyComponentHttpFoundationRedirectResponse; /** * @Route("/payments") */ class PaymentController { /** @DIInject */ private $request; /** @DIInject */ private $router; /** @DIInject("doctrine.orm.entity_manager") */ private $em; /** @DIInject("payment.plugin_controller") */ private $ppc; /** * @Route("/{orderNumber}/details", name = "payment_details") * @Template */ public function detailsAction(Order $order) { $form = $this->getFormFactory()->create('jms_choose_payment_method', null, array( 'amount' => $order->getAmount(), 'currency' => 'EUR', 'default_method' => 'payment_paypal', // Optional 'predefined_data' => array( 'paypal_express_checkout' => array( 'return_url' => $this->router->generate('payment_complete', array( 'orderNumber' => $order->getOrderNumber(), ), true), 'cancel_url' => $this->router->generate('payment_cancel', array( 'orderNumber' => $order->getOrderNumber(), ), true) ), ), )); if ('POST' === $this->request->getMethod()) { $form->bindRequest($this->request); if ($form->isValid()) { $this->ppc->createPaymentInstruction($instruction = $form->getData()); $order->setPaymentInstruction($instruction); $this->em->persist($order); $this->em->flush($order); return new RedirectResponse($this->router->generate('payment_complete', array( 'orderNumber' => $order->getOrderNumber(), ))); } } return array( 'form' => $form->createView() ); } // ... /** @DILookupMethod("form.factory") */ protected function getFormFactory() { } }
Тип форм jms_choose_payment_method
автоматически отбражает форму с доступными способами оплаты. После связывания, форма проверит данные для выбраной платежной системы, и в случае успеха возвратит объект PaymentInstruction.
Перевод денег
В предыдущей главе мы получили объект PaymentInstruction. Давайте посмотрим как мы можем вносить деньги на счет. Если посмотреть на detailsAction, указанный в предыдущем примере, мы перенаправляем пользователя на маршрут payment_complete для которого мы сейчас создадим соответствующее действие в контроллере:
<?php use JMSDiExtraBundleAnnotation as DI; use JMSPaymentCoreBundleEntityPayment; use JMSPaymentCoreBundlePluginControllerResult; use JMSPaymentCoreBundlePluginExceptionActionRequiredException; use JMSPaymentCoreBundlePluginExceptionActionVisitUrl; use SensioBundleFrameworkExtraBundleConfigurationRoute; use SensioBundleFrameworkExtraBundleConfigurationTemplate; use SymfonyComponentHttpFoundationRedirectResponse; /** * @Route("/payments") */ class PaymentController { /** @DIInject */ private $request; /** @DIInject */ private $router; /** @DIInject("doctrine.orm.entity_manager") */ private $em; /** @DIInject("payment.plugin_controller") */ private $ppc; // ... see previous section /** * @Route("/{orderNumber}/complete", name = "payment_complete") */ public function completeAction(Order $order) { $instruction = $order->getPaymentInstruction(); if (null === $pendingTransaction = $instruction->getPendingTransaction()) { $payment = $this->ppc->createPayment($instruction->getId(), $instruction->getAmount() - $instruction->getDepositedAmount()); } else { $payment = $pendingTransaction->getPayment(); } $result = $this->ppc->approveAndDeposit($payment->getId(), $payment->getTargetAmount()); if (Result::STATUS_PENDING === $result->getStatus()) { $ex = $result->getPluginException(); if ($ex instanceof ActionRequiredException) { $action = $ex->getAction(); if ($action instanceof VisitUrl) { return new RedirectResponse($action->getUrl()); } throw $ex; } } else if (Result::STATUS_SUCCESS !== $result->getStatus()) { throw new RuntimeException('Transaction was not successful: '.$result->getReasonCode()); } // payment was successful, do something interesting with the order } }
События
Заметка: Список возможных событий можно найти в классе JMSPaymentCoreBundlePluginControllerEventEvents.
События при изменении статуса платежа
Имя: payment.state_change
Класс события: JMSPaymentCoreBundlePluginControllerEventPaymentStateChangeEvent
Это событие возникает сразу после изменения статуса платежа. Все связанные сущности будут обновлены. Вы можете получить доступ к объектам Payment, PaymentInstruction, получить старое и новое состояние платежа.
Источник:
Свободный перевод документации: http://jmsyst.com/bundles/JMSPaymentCoreBundle
Комментарии