php - 使用 Sylius 0.16 和 Payum 实现异地支付网关时如何完成订单

标签 php payment-gateway sylius payum

我一直在努力思考如何使用 Sylius(sylius/sylius-standard,v0.16)实现经典的离线支付网关。

到目前为止,我在我的包的 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 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 以正确的订单 ID、价格等到达支付网关。这部分没问题。

付款后,返回成功 url 后,我再次进入 CaptureOffsiteAction::execute 并检查 pgw_transaction_id - 如果已设置,则意味着我已完成付款,我应该检测到订单已成功付款或已取消/支付失败。

问题是:

  • 如果付款成功 - 如何“关闭”订单(将购物车转换为订单、将付款标记为已捕获等?)

  • 我在其他支付网关中看到还有很多其他方法 - 假设我只对通过后重定向进行的异地支付感兴趣 - StatusAction 有什么作用?什么时候调用?

到目前为止,我发现 Payum(和 PayumBundle)非常令人困惑,我已经设法在不到一天的时间内使用 OmniPay(在其他与 Sylius 相关的项目上)实现了 2 个异地网关,但似乎无法解决这个 Payum 问题. :(

感谢任何帮助,提前致谢!

最佳答案

抱歉回复晚了 - 我设法弄清楚哪里出了问题。

整个想法是顺序调用 Action 。就我而言,这将是:

  • CaptureOffsiteAction
  • 状态 Action

我的第一个错误是认为 Payum 仅对特定工作流程使用一个操作。文档有点困惑(或者至少对我来说是这样),所以我没有得到 StatusAction 实际上负责处理从 PG 返回时处理订单状态的部分。

根据我收集到的信息:

  • 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::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
    );
}

希望这对你们中的一些人有所帮助!干杯。

关于php - 使用 Sylius 0.16 和 Payum 实现异地支付网关时如何完成订单,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35259458/

相关文章:

Sylius:创建产品组合、促销包

php - 尝试使用 PHP 正则表达式生成 url slugs,日文字符不通过

php - 使用数组 SQL 获取 ID

wordpress - 根据所选货币隐藏 Woocommerce 中的付款方式

wordpress - 如何直接从url调用wordpress函数?

payment-gateway - 禁用 Paypal Express Checkout 中的地址字段

php - 使用 PHP 和 SQL 隐藏不再使用的记录而不是将其永久删除

php - While 循环内是否在屏幕上打印了图像文件(对应于线程 ID)?

symfony - Sylius + Vagrant + Behat 不工作

php - 如何将 PayPal 与 Sylius 集成