Skip to main content
Version: 2024.4

Recurring Payment

Pimcore currently supports recurring payment for the payment provider Datatrans (Alias). It is performed via backend operations (server-to-server) which are used to create a new order and a new payment attempt by reusing the payment information available in a previous order, the so-called source order.

Recurring payment can for example be used to implement sequential payments like hiring or leasing agreements, or also to perform a one time payment for an authorized user which already committed at least one previous order successfully.

Additional Information

Best Practice

Bad Way

Recurring Payment BadRecurring Payment BadRecurring Payment Bad

Good Way

Recurring Payment GoodRecurring Payment GoodRecurring Payment Good

Implementation

The following code will briefly show how to perform a one time payment inside the checkout process for Wirecard Checkout Page (former known as Qpay).

CheckoutController.php

public function paymentAction(Request $request): Response
{
$factory = \Pimcore\Bundle\EcommerceFrameworkBundle\Factory::getInstance();

$checkoutManager = $factory->getCheckoutManager($this->cart);
$user = $this->getUser();

// open payment or submit recurring payment
if ($request->getMethod() == 'POST') {
if ($sourceOrderId = $request->get("recurring-payment")) {
/* Recurring Payment */
if ($user && $sourceOrderId) {
$sourceOrder = \Pimcore\Model\DataObject\OnlineShopOrder::getById($sourceOrderId);

try {
$targetOrder = $checkoutManager->startAndCommitRecurringOrderPayment($sourceOrder, $user->getId());
return $this->redirect($this->generateUrl('checkout', ['action' => 'completed']));
} catch (\Exception $exception) {
// show warning
}
}
}
}

$templateParams = [];
$paymentMethods = ["SEPA-DD", "CCARD"]; // supported payment methods

if ($user) {
$sourceOrders = [];

foreach ($paymentMethods as $paymentMethod) {
$orderManager = $factory->getOrderManager();
$sourceOrder = $orderManager->getRecurringPaymentSourceOrder($user->getId(), $checkoutManager->getPayment(), $paymentMethod);

$sourceOrders[$paymentMethod] = $sourceOrder;
}

$templateParams['sourceOrders'] = $sourceOrders;
}

$templateParams['paymentMethods'] = $paymentMethods;

return $this->render('checkout/payment.html.twig', $templateParams);
}

payment.html.php

?>
<form method="post" action="{{ pimcore_url({action: 'confirm'}, 'checkout', true) }}">

{% if sourceOrders is not empty %}

<h4>{{ 'checkout.use-recurring-payment'|trans }}</h4>

{% for paymentMethod in paymentMethods %}
{% if sourceOrders[paymentMethod] is defined %}
{% set sourceOrder = sourceOrders[paymentMethod] %}
{% set paymentProvider = sourceOrder.paymentProvider.paymentProviderQpay %}
{% if paymentProvider %}
{% currentPaymentMethod = paymentProvider.Auth_paymentType %}
<p>
<input name="recurring-payment" value="{{ sourceOrder.id }}" type="radio">
<strong>{{ currentPaymentMethod }}</strong>

{% if currentPaymentMethod is same as('SEPA-DD') %}
{{ paymentProvider.Auth_bankAccountOwner }} {{ paymentProvider.Auth_bankAccountIBAN }}
{% elseif currentPaymentMethod is same as('CCARD') %}
{{ paymentProvider.Auth_maskedPan }} {{ paymentProvider.Auth_expiry }}
{% endif %}
</p>
{% endif %}
{% endif %}
{% endfor %}

<hr>
{% endif %}

</form>

ecommerce.yaml

payment_manager:
providers:
qpay:
profiles:
sandbox:
secret: 'CHCSH7UGHVVX2P7EHDHSY4T2S4CGYK4QBE4M5YUUG2ND5BEZWNRZW5EJYVJQ'
customer: 'D200411'
toolkit_password: 'XXXXXXX' # necessary for recurPayment operation
optional_payment_properties:
- paymentType
- transactionIdentifier # necessary for recurPayment based on SEPA DIRECT DEBIT
recurring_payment_enabled: true # enable recurring payment