Integrating the PayNow payment method for Computop
Edit on GitHubExample State Machine
Front-end Integration
To adjust the frontend appearance, provide the following templates in your theme directory: src/<project_name>/Yves/Computop/Theme/<custom_theme_name>/paynow.twig
State Machine Integration
The Computop provides a demo state machine for the PayNow payment method which implements Authorization/Capture
flow.
To enable the demo state machine, extend the configuration with the following values:
<?php
$config[SalesConstants::PAYMENT_METHOD_STATEMACHINE_MAPPING] = [
...
ComputopConfig::PAYMENT_METHOD_PAY_NOW => 'ComputopPayNow01',
];
$config[OmsConstants::ACTIVE_PROCESSES] = [
...
'ComputopPayNow01',
];
Checkout Integration
Add the following lines to Yves\Checkout\CheckoutDependencyProvider.php
:
$container[static::PAYMENT_METHOD_HANDLER] = function () {
$paymentMethodHandler = new StepHandlerPluginCollection();
.....
$paymentMethodHandler->add(new ComputopPaymentHandlerPlugin(), PaymentTransfer::COMPUTOP_PAY_NOW);
return $paymentMethodHandler;
};
$container[static::PAYMENT_SUB_FORMS] = function () {
$paymentSubFormPlugin = new SubFormPluginCollection();
.....
$paymentSubFormPlugin->add(new PayNowSubFormPlugin());
return $paymentSubFormPlugin;
};
protected function provideClients(Container $container)
{
$container = parent::provideClients($container);
.....
$container[static::CLIENT_COMPUTOP] = function (Container $container) {
return $container->getLocator()->computop()->client();
};
return $container;
}
Computop PayNow payment method also provides a new Checkout Step for filling the Credit Card data and sending it to the Computop paygate. You have to create Yves/Checkout/Process/Steps/PayNowStep.php
class with the following content:
Click here to expand the code sample
<?php
/**
* This file is part of the Spryker Demoshop.
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Pyz\Yves\Checkout\Process\Steps;
use Spryker\Shared\Kernel\Transfer\AbstractTransfer;
use SprykerEco\Shared\Computop\ComputopConfig;
class PayNowStep extends AbstractBaseStep
{
/**
* @param string $stepRoute
* @param string $escapeRoute
*/
public function __construct(
$stepRoute,
$escapeRoute
) {
parent::__construct($stepRoute, $escapeRoute);
}
/**
* @param \Spryker\Shared\Kernel\Transfer\AbstractTransfer|\Generated\Shared\Transfer\QuoteTransfer $quoteTransfer
*
* @return bool
*/
public function requireInput(AbstractTransfer $quoteTransfer)
{
if ($this->isMethodPayNow($quoteTransfer)) {
return true;
}
return false;
}
/**
* @param \Spryker\Shared\Kernel\Transfer\AbstractTransfer|\Generated\Shared\Transfer\QuoteTransfer $quoteTransfer
*
* @return bool
*/
public function postCondition(AbstractTransfer $quoteTransfer)
{
return true;
}
/**
* @param \Spryker\Shared\Kernel\Transfer\AbstractTransfer|\Generated\Shared\Transfer\QuoteTransfer $quoteTransfer
*
* @return array
*/
public function getTemplateVariables(AbstractTransfer $quoteTransfer)
{
return [
'data' => $quoteTransfer->getPayment()->getComputopPayNow()->getData(),
'len' => $quoteTransfer->getPayment()->getComputopPayNow()->getLen(),
'merchant' => $quoteTransfer->getPayment()->getComputopPayNow()->getMerchantId(),
'action' => $quoteTransfer->getPayment()->getComputopPayNow()->getUrl(),
'brandOptions' => $this->getBrandOptions(),
];
}
/**
* @param \Spryker\Shared\Kernel\Transfer\AbstractTransfer|\Generated\Shared\Transfer\QuoteTransfer $quoteTransfer
*
* @return bool
*/
protected function isMethodPayNow(AbstractTransfer $quoteTransfer)
{
return $quoteTransfer->getPayment()->getPaymentSelection() === ComputopConfig::PAYMENT_METHOD_PAY_NOW;
}
/**
* @return array
*/
protected function getBrandOptions()
{
return [
'VISA' => 'Visa',
'MasterCard' => 'Master Card',
'AMEX' => 'American Express',
'DINERS' => 'Diners Club',
'JCB' => 'JCB',
'CBN' => 'CBN',
'SWITCH' => 'Switch',
'SOLO' => 'Solo',
];
}
}
Then you need to add it to Yves/Checkout/Process/StepFactory.php
right after the PlaceOrder
step and before the Success
step.
protected function createPayNowStep()
{
return new PayNowStep(
CheckoutControllerProvider::CHECKOUT_COMPUTOP_PAYNOW,
ApplicationControllerProvider::ROUTE_HOME
);
}
public function createStepCollection()
{
$stepCollection = new StepCollection(
$this->getUrlGenerator(),
CheckoutControllerProvider::CHECKOUT_ERROR
);
$stepCollection
....
->addStep($this->createPlaceOrderStep())
->addStep($this->createPayNowStep())
->addStep($this->createSuccessStep());
return $stepCollection;
}
Also you need to add action to Yves/Checkout/Controller/CheckoutController.php
public function paynowAction(Request $request)
{
return $this->createStepProcess()->process($request);
}
And define this action in Yves/Checkout/Plugin/Provider/CheckoutControllerProvider.php
protected function defineControllers(Application $app)
{
$allowedLocalesPattern = $this->getAllowedLocalesPattern();
.....
$this->createController('/{checkout}/base-shop/third-party-integrations/computop/paynow', self::CHECKOUT_COMPUTOP_PAYNOW, 'Checkout', 'Checkout', 'paynow')
->assert('checkout', $allowedLocalesPattern . 'checkout|checkout')
->value('checkout', 'checkout')
->method('GET|POST');
}
The final step is to create a template for rendering PayNow
step in Yves/Checkout/Theme/default/checkout/paynow.twig
Click here to expand the code sample
{% extends "@checkout/layout.twig" %}
{% block breadcrumb %}{% endblock %}
{% block content %}
<div class="row columns">
<form name="paynowStepForm" method="post" action="{{ action }}">
<div id="paynowStepForm" class="callout">
<div class="small-12 large-6 columns">
<div>
<label for="paynowStepForm_CCBrand" class="required">Credit Card Brand</label>
<select id="paynowStepForm_CCBrand" name="CCBrand">
{% for key, value in brandOptions %}
<option value="{{ key }}">{{ value }}</option>
{% endfor %}
</select>
</div>
<div>
<label for="paynowStepForm_CCNr" class="required">Credit Card Number</label>
<input type="text" id="paynowStepForm_CCNr" name="CCNr" required="required">
</div>
<div>
<label for="paynowStepForm_CCExpiry" class="required">Credit card expiry date in the format YYYYMM, e.g. 201807</label>
<input type="text" id="paynowStepForm_CCExpiry" name="CCExpiry" required="required">
</div>
<div>
<label for="paynowStepForm_CCCVC" class="required">CVV</label>
<input type="text" id="paynowStepForm_CCCVC" name="CCCVC" required="required">
</div>
<input type="hidden" id="paynowStepForm_MerchantID" name=""MerchantID" value="{{ merchant }}">
<input type="hidden" id="paynowStepForm_Data" name="Data" value="{{ data }}">
<input type="hidden" id="paynowStepForm_Len" name="Len" value="{{ len }}">
<input type="hidden" id="paynowStepForm__token" name="paynowStepForm[_token]" value="QU3KalLrQ3pyUPPNc13NzPQ5U4O_vdjK5gkPKR5pkEo">
<div class="row align-right">
<div class="small-12 medium-6 large-4 xlarge-6 columns">
<button type="submit" class="button success expanded __no-margin-bottom">Pay</button>
</div>
</div>
</div>
</div>
</form>
</div>
{% endblock %}
To make this specific step work only with PayNow payment method we have to update Yves/Checkout/Process/Steps/PlaceOrderStep.php
public function execute(Request $request, AbstractTransfer $quoteTransfer)
{
if ($this->isPaymentPayNow($quoteTransfer) && $this->isComputopPaymentExist($quoteTransfer)) {
return $quoteTransfer;
}
$quoteTransfer = parent::execute($request, $quoteTransfer);
if ($this->isPaymentPayNow($quoteTransfer)) {
$this->setComputopInitData($quoteTransfer);
}
return $quoteTransfer;
}
protected function isPaymentPayNow(QuoteTransfer $quoteTransfer)
{
return $quoteTransfer->getPayment()->getPaymentSelection() === ComputopConfig::PAYMENT_METHOD_PAY_NOW;
}
protected function isComputopPaymentExist(QuoteTransfer $quoteTransfer)
{
$quoteTransfer = $this->computopClient->isComputopPaymentExist($quoteTransfer);
return (bool)$quoteTransfer->getPayment()->getIsComputopPaymentExist();
}
Also, you will need to add ComputopClient
to PlaceOrderSpet
dependecy Yves/Checkout/Process/StepFactory.php
public function getComputopClient()
{
return $this->getProvidedDependency(CheckoutDependencyProvider::CLIENT_COMPUTOP);
}
protected function createPlaceOrderStep()
{
return new PlaceOrderStep(
$this->getCheckoutClient(),
$this->getFlashMessenger(),
$this->getComputopClient(),
CheckoutControllerProvider::CHECKOUT_PLACE_ORDER,
ApplicationControllerProvider::ROUTE_HOME,
[
'payment failed' => CheckoutControllerProvider::CHECKOUT_PAYMENT,
ShipmentCheckoutConnectorConfig::ERROR_CODE_SHIPMENT_FAILED => CheckoutControllerProvider::CHECKOUT_SHIPMENT,
]
);
}
PayNow Payment Flow
- There is a radio button on Payment step. After submitting the order, the customer is redirected to the to PayNow checkout step. The step contains Credit Card form with the following fields:
- Credit Card brand choice
- Credit Card number
- Credit Card expires date (in the format
YYYYMM
, e.g. 201807) - Credit Card security code (CVV)
- Data (hidden field, encrypted parameters, e.g. currency, amount, description)
- Length (hidden field, length of
data
parameter) - Merchant id (hidden field, assigned by Computop)
Form posts directly to Computop paygate. After the process is requested, Computop redirects the customer to success or failure URL.
- By default, on success the customer will be redirected to “Success” step. The response contains
payId
. On error, the customer will be redirected to “Payment” step with the error message. Response data is stored in the DB. - Authorization is added by default right after the success init action. Capture/Refund and Cancel actions are implemented in the Administration Interface (on manage order). On requests, Spryker will use
payId
parameter stored in the DB to identify a payment.
Thank you!
For submitting the form