PayOne - Integration into the Legacy Demoshop Project

Edit on GitHub
You are browsing a previous version of the document. The latest version is 202212.0.

There is currently an issue when using gifcards with PayOne. Our team is developing a fix for it.

Objectives:

  • Place order with PayPal express checkout.
  • Be redirected to summary page of standard checkout.
  • Have shipping a method selector on summary page.

First of all we need to provide a URL to Payone module, which will be used to redirect user when the quote is filled with data obtained from PayPal. To achieve this, make the following steps:

  1. Add custom controller action to src/Pyz/Yves/Checkout/Controller/CheckoutController.php:
/**
* @return \Symfony\Component\HttpFoundation\RedirectResponse
*/
public function paypalExpressCheckoutEntryPointAction()
{
$this->getFactory()
->createExpressCheckoutHandler()
->fulfillPostConditionsUntilSummaryStep();
return $this->redirectResponseInternal(CheckoutControllerProvider::CHECKOUT_SUMMARY);
}
  1. Register a new controller action in controller provider:
...
const CHECKOUT_PAYPAL_EXPRESS_CHECKOUT_ENTRY_POINT = 'checkout-paypal-express-checkout-entry-point';
...
protected function defineControllers(Application $app)
{
...
$this->createController('/{checkout}/paypal-express-checkout-entry-point', self::CHECKOUT_PAYPAL_EXPRESS_CHECKOUT_ENTRY_POINT, 'Checkout', 'Checkout', 'paypalExpressCheckoutEntryPoint')
->assert('checkout', $allowedLocalesPattern . 'checkout|checkout')
->value('checkout', 'checkout')
->method('GET');
...
}
  1. Create ExpressCheckoutHandler class src/Pyz/Yves/Checkout/Handler/ExpressCheckoutHandler.php with corresponding interface:
namespace Pyz\Yves\Checkout\Handler;
use Generated\Shared\Transfer\ExpenseTransfer;
use Spryker\Client\Cart\CartClientInterface;
use Spryker\Shared\Shipment\ShipmentConstants;
class ExpressCheckoutHandler implements ExpressCheckoutHandlerInterface
{
/**
* @var \Spryker\Client\Cart\CartClientInterface
*/
protected $cartClient;
/**
* @param \Spryker\Client\Cart\CartClientInterface $cartClient
*/
public function __construct(CartClientInterface $cartClient)
{
$this->cartClient = $cartClient;
}
/**
* @return void
*/
public function fulfillPostConditionsUntilSummaryStep()
{
$quoteTransfer = $this->cartClient->getQuote();
$quoteTransfer->addExpense(
(new ExpenseTransfer())->setType(ShipmentConstants::SHIPMENT_EXPENSE_TYPE)
);
}
}
  1. Add ExpressCheckoutHandler related method in src/Pyz/Yves/Checkout/CheckoutFactory.php:
/**
* @return \Pyz\Yves\Checkout\Handler\ExpressCheckoutHandler
*/
public function createExpressCheckoutHandler()
{
return new ExpressCheckoutHandler(
$this->getCartClient()
);
}
  1. Extend ShipmentForm class to override a property_path option (create src/Pyz/Yves/Shipment/Form/ShipmentSubForm.php):
namespace Pyz\Yves\Shipment\Form;
class ShipmentSubForm extends ShipmentForm
{
/**
* @const string
*/
const SHIPMENT_SELECTION_PROPERTY_PATH = self::SHIPMENT_SELECTION;
}
  1. Adjust summary form by adding a shipment subform in src/Pyz/Yves/Checkout/Form/Steps/SummaryForm.php:
class SummaryForm extends AbstractType
{
/**
* Builds the form.
*
* This method is called for each type in the hierarchy starting from the
* top most type. Type extensions can further modify the form.
*
* @see FormTypeExtensionInterface::buildForm()
*
* @param \Symfony\Component\Form\FormBuilderInterface $builder The form builder
* @param array $options The options
*
* @return void
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(
'shipmentForm',
ShipmentSubForm::class,
array_merge(
$options,
[
'data_class' => ShipmentTransfer::class,
'property_path' => 'shipment',
]
)
);
}
/**
* @param \Symfony\Component\OptionsResolver\OptionsResolver $resolver
*
* @return void
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setRequired('shipmentMethods');
$resolver->setDefaults([
'data_class' => QuoteTransfer::class,
]);
}
/**
* Returns the name of this type.
*
* @return string The name of this type
*/
public function getName()
{
return 'summaryForm';
}
}
  1. Create shipment subform template for summary page in src/Pyz/Yves/Checkout/Theme/default/checkout/partials/shipment.twig:

<div class="row columns">

 {% set shipmentForm = summaryForm.shipmentForm %}

 <div class="callout">
  <ul class="no-bullet">

   {% for name, choices in shipmentForm.idShipmentMethod.vars.choices %}

   <h4>{{ name }}</h4>

   {% for key, choice in choices %}
   <li>
    <label>
     {{ form_widget(shipmentForm.idShipmentMethod[key], {'attr': {'class': '__toggler'}}) }}
     {{ shipmentForm.idShipmentMethod[key].vars.label | raw }}
    </label>
   </li>
   {% endfor %}
   {% endfor %}
  </ul>
 </div>
</div>
  1. Update summary twig template in src/Pyz/Yves/Checkout/Theme/default/checkout/summary.twig. Move form_start expression to the top of a content section and form_end to the end:
...
{% block content %}
{{ form_start(summaryForm, {'attr': {'class': 'row'}}) }}
...
...
...
{{ form_end(summaryForm) }}
{% endblock %}
  1. Remove {% include '@checkout/checkout/partials/voucher-form.twig' %} include from summary template. Include checkout/partials/shipment.twig in your summary template:
      ...
{{ 'checkout.step.summary.shipping' | trans }}
      {% include '@checkout/checkout/partials/shipment.twig' %}
      ...
  1. Add shipment form data provider and remove voucher form from summary form collection (it is just an example, if you need voucher form, you need to adjust summary page on your own). In src/Pyz/Yves/Checkout/Form/FormFactory.php:
/**
* @param \Generated\Shared\Transfer\QuoteTransfer
*
* @return \Spryker\Yves\StepEngine\Form\FormCollectionHandlerInterface
*/
public function createSummaryFormCollection()
{
return $this->createFormCollection($this->createSummaryFormTypes(), $this->getShipmentFormDataProviderPlugin());
}

....


/**
* @return \Symfony\Component\Form\FormTypeInterface[]
*/
protected function createSummaryFormTypes()
{
return [
$this->createSummaryForm(),
//$this->createVoucherFormType(),
];
}
  1. Handle shipment form data, when summary form is submitted. Inject two dependencies into src/Pyz/Yves/Checkout/Process/Steps/SummaryStep.php:
/**
* @var \Spryker\Client\Calculation\CalculationClientInterface
*/
protected $calculationClient;
/**
* @var \Spryker\Yves\StepEngine\Dependency\Plugin\Handler\StepHandlerPluginCollection
*/
protected $shipmentPlugins;
/**
* @param \Spryker\Yves\ProductBundle\Grouper\ProductBundleGrouperInterface $productBundleGrouper
* @param \Spryker\Client\Cart\CartClientInterface $cartClient
* @param \Spryker\Client\Calculation\CalculationClientInterface $calculationClient
* @param \Spryker\Client\Calculation\CalculationClientInterface $shipmentPlugins
* @param string $stepRoute
* @param string $escapeRoute
*/
public function __construct(
ProductBundleGrouperInterface $productBundleGrouper,
CartClientInterface $cartClient,
CalculationClientInterface $calculationClient,
StepHandlerPluginCollection $shipmentPlugins,
$stepRoute,
$escapeRoute
) {
parent::__construct($stepRoute, $escapeRoute);
$this->productBundleGrouper = $productBundleGrouper;
$this->cartClient = $cartClient;
$this->calculationClient = $calculationClient;
$this->shipmentPlugins = $shipmentPlugins;
}
  1. Extend execute method:
/**
* @param \Symfony\Component\HttpFoundation\Request $request
* @param \Spryker\Shared\Kernel\Transfer\AbstractTransfer|\Generated\Shared\Transfer\QuoteTransfer $quoteTransfer
*
* @return \Generated\Shared\Transfer\QuoteTransfer
*/
public function execute(Request $request, AbstractTransfer $quoteTransfer)
{
$shipmentHandler = $this->shipmentPlugins->get(CheckoutDependencyProvider::PLUGIN_SHIPMENT_STEP_HANDLER);
$shipmentHandler->addToDataClass($request, $quoteTransfer);
$this->calculationClient->recalculate($quoteTransfer);
$this->markCheckoutConfirmed($request, $quoteTransfer);
return $quoteTransfer;
}
  1. Add Payone checkout plugins in Pyz\Zed\Checkout\CheckoutDependencyProvider:
<?php

/**
 * This file is part of the Spryker Suite.
 * For full license information, please view the LICENSE file that was distributed with this source code.
 */

namespace Pyz\Zed\Checkout;

//...
use SprykerEco\Zed\Payone\Communication\Plugin\Checkout\PayoneCheckoutDoSaveOrderPlugin;
use SprykerEco\Zed\Payone\Communication\Plugin\Checkout\PayoneCheckoutPostSavePlugin;
use SprykerEco\Zed\Payone\Communication\Plugin\Checkout\PayoneCheckoutPreConditionPlugin;

class CheckoutDependencyProvider extends SprykerCheckoutDependencyProvider
{
    /**
     * @param \Spryker\Zed\Kernel\Container $container
     *
     * @return \Spryker\Zed\CheckoutExtension\Dependency\Plugin\CheckoutPreConditionPluginInterface[]
     */
    protected function getCheckoutPreConditions(Container $container)
    {
        return [
            //...
            new PayoneCheckoutPreConditionPlugin(),
            //...
        ];
    }

    /**
     * @param \Spryker\Zed\Kernel\Container $container
     *
     * @return \Spryker\Zed\Checkout\Dependency\Plugin\CheckoutSaveOrderInterface[]|\Spryker\Zed\CheckoutExtension\Dependency\Plugin\CheckoutDoSaveOrderInterface[]
     */
    protected function getCheckoutOrderSavers(Container $container)
    {
        return [
            //...
            new PayoneCheckoutDoSaveOrderPlugin(),
            //...
        ];
    }

    /**
     * @param \Spryker\Zed\Kernel\Container $container
     *
     * @return \Spryker\Zed\CheckoutExtension\Dependency\Plugin\CheckoutPostSaveInterface[]
     */
    protected function getCheckoutPostHooks(Container $container)
    {
        return [
            //...
            new PayoneCheckoutPostSavePlugin(),
            //...
        ];
    }
    //...
}

Now, go to checkout and try placing an order with Paypal express checkout.