使用 Sylius 0.16 和 Payum 实施异地支付网关时如何完成订单
How to finalize an order when implementing offsite payment gateway using Sylius 0.16 and Payum
我一直在思考如何使用 Sylius(sylius/sylius-standard、v0.16)实现 classic 离线支付网关。
到目前为止,我在我的包的 build() 方法中得到了这个:
public function build(ContainerBuilder $container)
{
/** @var PayumExtension $payum */
$payum = $container->getExtension('payum');
$payum->addGatewayFactory(new PayWayOffsiteGatewayFactory());
}
这是我的 config.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:
中得到了这个
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:
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 以正确的订单 ID、价格等到达支付网关。这部分没问题。
付款后,返回成功后 url,我再次进入 CaptureOffsiteAction::execute 并检查 pgw_transaction_id - 如果已设置,则表示我已从付款中取回并且我应该检测到成功支付的订单或 cancelled/failed 付款。
问题是:
如果付款成功 - 我如何'close'订单(将购物车转换为订单、将付款标记为已捕获等?)
我在其他支付网关中看到还有很多其他方法 - 可以说我只对通过 post-redirect 进行的异地支付感兴趣 - StatusAction 有什么作用?什么时候调用?
到目前为止,我发现 Payum(和 PayumBundle)非常令人困惑,我已经设法在不到一天的时间内使用 OmniPay(在其他与 Sylius 相关的项目上)实现了 2 个异地网关,但似乎无法解决这个问题付款的事情。 :(
感谢任何帮助,提前致谢!
抱歉回复晚了 - 我设法弄清楚哪里出了问题。
整个思路是按顺序调用 Actions。就我而言,那将是:
- CaptureOffsiteAction
- 状态动作
我的第一个错误是认为 Payum 仅对特定工作流程使用一个操作。文档有点混乱(或者至少对我来说是这样),所以我没有得到 StatusAction 实际上负责处理 return 来自 PG 的订单状态的部分。
根据我收集到的信息:
- CaptureOffsiteAction 处理重定向到支付网关,并检查 return
上的回发请求
- 然后 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::execute 方法:
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
);
}
希望这对你们中的一些人有所帮助!干杯。
我一直在思考如何使用 Sylius(sylius/sylius-standard、v0.16)实现 classic 离线支付网关。
到目前为止,我在我的包的 build() 方法中得到了这个:
public function build(ContainerBuilder $container)
{
/** @var PayumExtension $payum */
$payum = $container->getExtension('payum');
$payum->addGatewayFactory(new PayWayOffsiteGatewayFactory());
}
这是我的 config.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:
中得到了这个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:
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 以正确的订单 ID、价格等到达支付网关。这部分没问题。
付款后,返回成功后 url,我再次进入 CaptureOffsiteAction::execute 并检查 pgw_transaction_id - 如果已设置,则表示我已从付款中取回并且我应该检测到成功支付的订单或 cancelled/failed 付款。
问题是:
如果付款成功 - 我如何'close'订单(将购物车转换为订单、将付款标记为已捕获等?)
我在其他支付网关中看到还有很多其他方法 - 可以说我只对通过 post-redirect 进行的异地支付感兴趣 - StatusAction 有什么作用?什么时候调用?
到目前为止,我发现 Payum(和 PayumBundle)非常令人困惑,我已经设法在不到一天的时间内使用 OmniPay(在其他与 Sylius 相关的项目上)实现了 2 个异地网关,但似乎无法解决这个问题付款的事情。 :(
感谢任何帮助,提前致谢!
抱歉回复晚了 - 我设法弄清楚哪里出了问题。
整个思路是按顺序调用 Actions。就我而言,那将是:
- CaptureOffsiteAction
- 状态动作
我的第一个错误是认为 Payum 仅对特定工作流程使用一个操作。文档有点混乱(或者至少对我来说是这样),所以我没有得到 StatusAction 实际上负责处理 return 来自 PG 的订单状态的部分。
根据我收集到的信息:
- CaptureOffsiteAction 处理重定向到支付网关,并检查 return 上的回发请求
- 然后 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::execute 方法:
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
);
}
希望这对你们中的一些人有所帮助!干杯。