Как завершить оформление заказа при внедрении внешнего платежного шлюза с использованием Sylius 0.16 и Payum
Я пытался разобраться в том, как реализовать классический автономный платежный шлюз с использованием Sylius (sylius/sylius-standard, v0.16).
До сих пор я получал это в методе сборки() моего пакета:
public function build(ContainerBuilder $container)
{
/** @var PayumExtension $payum */
$payum = $container->getExtension('payum');
$payum->addGatewayFactory(new PayWayOffsiteGatewayFactory());
}
Вот мой файл конфигурации.yml:
sylius_payment:
gateways:
payway_offsite: PayWay
payum:
gateways:
payway_offsite:
payway_offsite:
secret_key: %tcompayway.secret_key%
shop_id: %tcompayway.shop_id%
username: %tcompayway.username%
password: %tcompayway.password%
sandbox: %tcompayway.sandbox%
Поля %compayway.*%, конечно, настроены нормально, пропуская эту часть. Затем я получил это в своем классе PayWayOffsiteGatewayFactory:
class PayWayOffsiteGatewayFactory extends AbstractGatewayFactory
{
/**
* {@inheritdoc}
*/
public function getName()
{
return 'payway_offsite';
}
/**
* {@inheritdoc}
*/
public function addConfiguration(ArrayNodeDefinition $builder)
{
parent::addConfiguration($builder);
$builder->children()
->scalarNode('secret_key')->isRequired()->cannotBeEmpty()->end()
->scalarNode('shop_id')->isRequired()->cannotBeEmpty()->end()
->scalarNode('username')->isRequired()->cannotBeEmpty()->end()
->scalarNode('password')->isRequired()->cannotBeEmpty()->end()
->booleanNode('sandbox')->defaultTrue()->end()
->end();
}
/**
* {@inheritDoc}
*/
protected function getPayumGatewayFactoryClass()
{
return 'WebBurza\PaymentBundle\Payum\PayWay\PayWayGatewayFactory';
}
/**
* {@inheritDoc}
*/
protected function getComposerPackage()
{
return 'webburza/tcompayway';
}
}
И класс PayWayGatewayFactory:
class PayWayGatewayFactory extends GatewayFactory
{
/**
* {@inheritDoc}
* @throws \Payum\Core\Exception\InvalidArgumentException
*/
protected function populateConfig(ArrayObject $config)
{
$config->defaults(array(
'payum.factory_name' => 'payway_offsite',
'payum.factory_title' => 'PayWay Offsite',
'payum.action.capture' => new CaptureOffsiteAction(),
'payum.action.capture_null' => new CaptureOffsiteNullAction(),
'payum.action.status' => new StatusAction(),
'payum.action.convert_payment' => new ConvertPaymentAction(),
));
if (false == $config['payum.api']) {
$config['payum.default_options'] = array(
'secret_key' => '',
'shop_id' => '',
'username' => '',
'password' => '',
'sandbox' => true
);
$config->defaults($config['payum.default_options']);
$config['payum.required_options'] = [
'secret_key',
'shop_id',
'username',
'password',
'sandbox'
];
$config['payum.api'] = function (ArrayObject $config) {
$config->validateNotEmpty($config['payum.required_options']);
$api = new Api([
'secret_key'=> $config['secret_key'],
'shop_id' => $config['shop_id'],
'username' => $config['username'],
'password' => $config['password'],
'sandbox' => $config['sandbox'],
], $config['payum.http_client']);
return $api;
};
}
}
}
До сих пор я нахожусь под впечатление, что все это более-менее нормально (поправьте меня, если я ошибаюсь). Мне удалось добраться до метода выполнения моего CaptureOffsiteAction:
public function execute($captureRequest)
{
RequestNotSupportedException::assertSupports($this, $captureRequest);
$model = ArrayObject::ensureArrayObject($captureRequest->getModel());
$httpRequest = new GetHttpRequest();
$this->gateway->execute($httpRequest);
if ($httpRequest->method === 'POST') {
$status = new GetHumanStatus($model);
if (isset($httpRequest->request['pgw_transaction_id']) && $httpRequest->request['pgw_transaction_id'] > 0) {
// mark this order id as paid, convert cart to order, etc.
} else {
// mark as failed/cancelled, redirect back to cart
}
return;
}
$model['successUrl'] = $captureRequest->getToken()->getTargetUrl();
$model['failureUrl'] = $captureRequest->getToken()->getTargetUrl();
$offsiteUrl = $this->api->getOffsiteUrl();
$data = $this->api->prepareOffsitePayment($model->toUnsafeArray());
$headers = ['application/x-www-form-urlencoded'];
throw new HttpPostRedirect(
$offsiteUrl,
$data,
200,
$headers
);
}
Во-первых, давайте посмотрим, правильно ли я понял - как только я начну процесс захвата (метод выполнения) - все работает так, как ожидалось, я заканчиваю в нижней части метода, создавая URL-адреса успеха и неудачи, подготавливая оплату за пределами сайта, хэш и все такое, и в конечном итоге попадаю на платежный шлюз через HttpPostRedirect с правильным идентификатором заказа, ценой и т. Д. Эта часть является Хорошо.
После оплаты, вернувшись на URL-адрес успешного выполнения, я снова попадаю в CaptureOffsiteAction::выполнить и проверить pgw_transaction_id - если этот параметр установлен, это означает, что я вернулся с платежа и должен обнаружить либо успешно оплаченный заказ, либо отмененный/неудачный платеж.
Вопросы таковы:
В случае успешной оплаты - как мне "закрыть" заказ (преобразовать корзину в заказ, пометить платеж как захваченный и т. Д.?)
Я вижу в другой оплате шлюзы, в которых есть много других способов - допустим, меня интересует только оплата за пределами сайта через пост-перенаправление - что делает StatusAction? И когда это называется?
До сих пор я находил Payum (и PayumBundle) очень запутанными, мне удалось реализовать 2 выездных шлюза менее чем за день с помощью Omnipay (в других проектах, не связанных с Sylius), но, похоже, не могу решить эту проблему с Payum. :(
Любая помощь приветствуется, заранее спасибо!
1 answers
Извините за поздний ответ - мне удалось выяснить, где я ошибся.
Вся идея заключается в последовательном вызове действий. В моем случае это было бы:
- Действие captureoffsiteaction
- Действие статуса
Моей первой ошибкой было думать, что Payum использует только 1 действие для определенного рабочего процесса. Документация была немного запутанной (или, по крайней мере, мне так показалось), поэтому я не понял ту часть, где StatusAction фактически заботится об обработке статуса заказа после возвращение с стр.
Итак, из того, что я собрал:
- captureoffsiteaction обрабатывает перенаправление на платежный шлюз и проверяет запрос обратной передачи при возврате
- Затем действие StatusAction вступает в силу и проверяет наличие надлежащих статусов, чтобы отметить заказ Завершенным, Новым или неудачным
Вот мой метод выполнения StatusAction, например:
public function execute($request)
{
RequestNotSupportedException::assertSupports($this, $request);
$model = new ArrayObject($request->getModel());
if (null === $model['EXECCODE']) {
$request->markNew();
return;
}
if (Api::STATUS_SUCCESS === $model['EXECCODE']) {
$request->markCaptured();
return;
}
$request->markFailed();
}
И вот окончательный метод CaptureOffsiteAction::выполнить:
public function execute($captureRequest)
{
RequestNotSupportedException::assertSupports($this, $captureRequest);
$model = ArrayObject::ensureArrayObject($captureRequest->getModel());
$httpRequest = new GetHttpRequest();
$this->gateway->execute($httpRequest);
if ($httpRequest->method === 'POST') {
// TODO: properly validate response signature here
if (isset($httpRequest->request['pgw_transaction_id'])) {
$model['EXECCODE'] = Api::STATUS_SUCCESS;
} else {
$model['EXECCODE'] = $httpRequest->request['pgw_result_code'];
}
return;
}
$model['successUrl'] = $captureRequest->getToken()->getTargetUrl();
$model['failureUrl'] = $captureRequest->getToken()->getTargetUrl();
$offsiteUrl = $this->api->getOffsiteUrl();
$data = $this->api->prepareOffsitePayment($model->toUnsafeArray(), $captureRequest);
$headers = ['application/x-www-form-urlencoded'];
throw new HttpPostRedirect(
$offsiteUrl,
$data,
200,
$headers
);
}
Надеюсь, это поможет некоторым из вас! Ваше здоровье.