Ratenkauf by Easycredit - Installation and Configuration

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

Installation

To install Easycredit, run the following command in the console:

composer require spryker-eco/easycredit

After installation, you have to run propel:install command or you can check the following migration:

CREATE SEQUENCE "spy_payment_easycredit_api_log_pk_seq";
 
CREATE TABLE "spy_payment_easycredit_api_log"
(
    "id_payment_easycredit_api_log" INTEGER NOT NULL,
    "type" VARCHAR NOT NULL,
    "request" TEXT NOT NULL,
    "response" TEXT NOT NULL,
    "status_code" INT2,
    "error_code" VARCHAR,
    "error_message" VARCHAR,
    "error_type" VARCHAR,
    "created_at" TIMESTAMP,
    "updated_at" TIMESTAMP,
    PRIMARY KEY ("id_payment_easycredit_api_log")
);
 
CREATE SEQUENCE "spy_payment_easycredit_order_identifier_pk_seq";
 
CREATE TABLE "spy_payment_easycredit_order_identifier"
(
    "id_payment_easycredit_order_identifier" INTEGER NOT NULL,
    "fk_sales_order" INTEGER NOT NULL,
    "identifier" VARCHAR NOT NULL,
    "confirmed" BOOLEAN NOT NULL,
    PRIMARY KEY ("id_payment_easycredit_order_identifier")
);

To use FE functionality (js / css) with old demoshop, shop-ui-compatibility module is necessary required:

composer require spryker-eco/shop-ui-compatibility

After installing shop-ui-compatibility use procedures described in this migration guide - Setting up ShopUICompatibility Module in the Legacy Demoshop.

Configuration

Perform the initial configuration of Easycredit:

<?php
...
use SprykerEco\Shared\Easycredit\EasycreditConstants;
use Spryker\Shared\Oms\OmsConstants;
use Spryker\Shared\Sales\SalesConstants;
 
...
 
$config[OmsConstants::ACTIVE_PROCESSES] = [
    ...
    'Easycredit01',
];
 
$config[SalesConstants::PAYMENT_METHOD_STATEMACHINE_MAPPING] = [
    ...
    'easycredit' => 'Easycredit01',
];
 
...
$config[EasycreditConstants::SHOP_IDENTIFIER] = 'Your shop identifier';
$config[EasycreditConstants::SHOP_TOKEN] = 'Your shop token';
$config[EasycreditConstants::API_URL] = 'https://ratenkauf.easycredit.de/ratenkauf-ws/rest/v2';
$config[EasycreditConstants::SUCCESS_URL] = $config[ApplicationConstants::BASE_URL_YVES] . '/easycredit/payment/success';
$config[EasycreditConstants::CANCELLED_URL] = $config[ApplicationConstants::BASE_URL_YVES] . '/checkout/payment';
$config[EasycreditConstants::DENIED_URL] = $config[ApplicationConstants::BASE_URL_YVES] . '/checkout/payment';

Integration

  1. First of all, update CheckoutPageDependencyProvider by adding a new payment subform and a payment method handler. To show the Easycredit payment method on the payment step, you should define SubFormPlugin and StepHandlerPlugin.
CheckoutPageDependencyProvider.php
public const CLIENT_EASYCREDIT = 'CLIENT_EASYCREDIT';
 
...
    /**
     * @param \Spryker\Yves\Kernel\Container $container
     *
     * @return \Spryker\Yves\Kernel\Container
     */
    public function provideDependencies(Container $container): Container
    {
        $container = parent::provideDependencies($container);
        $container = $this->addEasycreditClient($container);
     
        return $container;
    }
 
    /**
     * @param \Spryker\Yves\Kernel\Container $container
     *
     * @return \Spryker\Yves\Kernel\Container
     */
    protected function addSubFormPluginCollection(Container $container): Container
    {
        $container[static::PAYMENT_SUB_FORMS] = function () {
            $subFormPluginCollection = new SubFormPluginCollection();
            $subFormPluginCollection->add(new EasycreditSubFormPlugin());
 
            return $subFormPluginCollection;
        };
         return $container;
    }
 
    /**
     * @param \Spryker\Yves\Kernel\Container $container
     *
     * @return \Spryker\Yves\Kernel\Container
     */
    protected function addPaymentMethodHandlerPluginCollection(Container $container): Container
    {
        $container[self::PAYMENT_METHOD_HANDLER] = function () {
            $stepHandlerPluginCollection = new StepHandlerPluginCollection();
            $stepHandlerPluginCollection->add(new EasycreditHandlerPlugin(), PaymentTransfer::EASYCREDIT);
             return $stepHandlerPluginCollection;
        };
 
        return $container;
    }
 
    /**
     * @param \Spryker\Yves\Kernel\Container $container
     *
     * @return \Spryker\Yves\Kernel\Container
     */
    protected function addEasycreditClient(Container $container): Container
    {
        $container[static::CLIENT_EASYCREDIT] = function (Container $container) {
            return $container->getLocator()->easycredit()->client();
        };
 
        return $container;
    }
...

  1. The next dependency provider you should update is OmsDependencyProvider. To use commands and conditions for events in OMS, define them.
OmsDependencyProvider
...
$container->extend(self::CONDITION_PLUGINS, function (ConditionCollectionInterface $conditionCollection) {
    $conditionCollection->add(new IsOrderConfirmedPlugin(), 'Easycredit/IsOrderConfirmed');
    return $conditionCollection;
});
...

  1. And one more dependency provider to be updated is CheckoutDependencyProvider. To send requests to Easycredit, you need the technical order identifier value. After adding this plugin, the order identifier will be saved to the database in table spy_payment_easycredit_order_identifier.
CheckoutDependencyProvider
...
protected function getCheckoutOrderSavers(Container $container)
{
    return [
        ...
        new EasycreditOrderIdentifierPlugin(),
    ];
}
 
...

To use Easycredit requests during the checkout process, you have to create your own checkout steps. To implement the checkout steps, follow the guidelines below:

  1. Extend StepFactory.
CheckoutPageFactory
<?php

namespace Pyz\Yves\CheckoutPage;

use Pyz\Yves\CheckoutPage\Process\StepFactory;
use SprykerShop\Yves\CheckoutPage\CheckoutPageFactory as SprykerCheckoutPageFactory;

class CheckoutPageFactory extends SprykerCheckoutPageFactory
{
	/**
	 * @return \SprykerShop\Yves\CheckoutPage\Process\StepFactory
	 */
	public function createStepFactory()
	{
		return new StepFactory();
	}
}

  1. Implement StepFactory as shown in this example:
StepFactory
<?php

namespace Pyz\Yves\CheckoutPage\Process;

use Pyz\Yves\CheckoutPage\CheckoutPageDependencyProvider;
use Pyz\Yves\CheckoutPage\Plugin\Provider\CheckoutPageControllerProvider;
use Pyz\Yves\CheckoutPage\Process\Steps\EasycreditStep;
use Pyz\Yves\CheckoutPage\Process\Steps\ShipmentStep;
use Pyz\Yves\CheckoutPage\Process\Steps\SummaryStep;
use Spryker\Client\Messenger\MessengerClientInterface;
use Spryker\Yves\StepEngine\Dependency\Step\StepInterface;
use Spryker\Yves\StepEngine\Process\StepCollection;
use Spryker\Zed\Messenger\Business\MessengerFacadeInterface;
use SprykerEco\Client\Easycredit\EasycreditClient;
use SprykerShop\Yves\CheckoutPage\Process\StepFactory as SprykerStepFactory;
use SprykerShop\Yves\HomePage\Plugin\Provider\HomePageControllerProvider;

class StepFactory extends SprykerStepFactory
{
    /**
     * @return StepInterface
     */
    public function createEasycreditStep(): StepInterface
    {
        return new EasycreditStep(
            CheckoutPageControllerProvider::CHECKOUT_EASY_CREDIT,
            HomePageControllerProvider::ROUTE_HOME,
            $this->getEasycreditClient()
        );
    }

    /**
    * @return StepInterface
    */
    public function createSummaryStep(): StepInterface
    {
        return new SummaryStep(
            $this->getProductBundleClient(),
            CheckoutPageControllerProvider::CHECKOUT_SUMMARY,
            HomePageControllerProvider::ROUTE_HOME
        );
    }

   /**
    * @return StepInterface
    */
    public function createShipmentStep(): StepInterface
    {
        return new ShipmentStep(
            $this->getCalculationClient(),
            $this->getShipmentPlugins(),
            CheckoutPageControllerProvider::CHECKOUT_SHIPMENT,
            HomePageControllerProvider::ROUTE_HOME,
            $this->getEasycreditClient()
        );
    }

    /**
     * @return \Spryker\Yves\StepEngine\Process\StepCollectionInterface
     */
    public function createStepCollection(): StepCollectionInterface
    {
        $stepCollection = new StepCollection(
            $this->getUrlGenerator(),            {% if data.easycredit %}
                {% include molecule('easycredit-summary', 'Easycredit') with {
                    data: {
                        interest: data.easycredit.interest,
                        url: data.easycredit.url,
                        text: data.easycredit.text
                    }
                } only %}
            {% endif %}

            CheckoutPageControllerProvider::CHECKOUT_ERROR
        );
         $stepCollection
            ->addStep($this->createEntryStep())
            ->addStep($this->createCustomerStep())
            ->addStep($this->createAddressStep())
            ->addStep($this->createShipmentStep())
            ->addStep($this->createPaymentStep())
            ->addStep($this->createEasycreditStep())
            ->addStep($this->createSummaryStep())
            ->addStep($this->createPlaceOrderStep())
            ->addStep($this->createSuccessStep());
         return $stepCollection;
    }

    /**
     * @return EasycreditClientInterface
     */
    protected function getEasycreditClient(): EasycreditClientInterface
    {
        return $this->getProvidedDependency(CheckoutPageDependencyProvider::CLIENT_EASYCREDIT);
    }
}

  1. Now you can extend the basic steps on the project level and can create your Easycredit step that will be called when a user takes Easycredit as PaymentMethod. Examples of steps implementations:
EasycreditStep.php
<?php

namespace Pyz\Yves\CheckoutPage\Process\Steps;

use Spryker\Shared\Kernel\Transfer\AbstractTransfer;
use Spryker\Yves\StepEngine\Dependency\Step\AbstractBaseStep;
use Spryker\Yves\StepEngine\Dependency\Step\StepWithExternalRedirectInterface;
use SprykerEco\Client\Easycredit\EasycreditClient;
use SprykerEco\Shared\Easycredit\EasycreditConfig;
use Symfony\Component\HttpFoundation\Request;

class EasycreditStep extends AbstractBaseStep implements StepWithExternalRedirectInterface
{
    protected const URL_EASYCREDIT_REDIRECT_URL = 'https://ratenkauf.easycredit.de/ratenkauf/content/intern/einstieg.jsf?vorgangskennung=';

    /**
     * @var string
     */
    protected $redirectUrl = '';

   /**
    * @var \SprykerEco\Client\Easycredit\EasycreditClientInterface
    */
    protected $easycreditClient;

     /**
     * @param string $stepRoute
     * @param string $escapeRoute
     * @param \SprykerEco\Client\Easycredit\EasycreditClientInterface $easycreditClient
     */
    public function __construct(
        $stepRoute,
        $escapeRoute,
        EasycreditClientInterface $easycreditClient
    ) {
        parent::__construct($stepRoute, $escapeRoute);
        $this->easycreditClient = $easycreditClient;
    }

    /**
     * @param \Spryker\Shared\Kernel\Transfer\AbstractTransfer|\Generated\Shared\Transfer\QuoteTransfer $quoteTransfer
     *
     * @return bool
     */
    public function requireInput(AbstractTransfer $quoteTransfer)
    {
        return false;
    }

    /**
     * @param \Symfony\Component\HttpFoundation\Request $request
     * @param \Spryker\Shared\Kernel\Transfer\AbstractTransfer|\Generated\Shared\Transfer\QuoteTransfer $quoteTransfer
     *
     * @return \Spryker\Shared\Kernel\Transfer\AbstractTransfer
     */
    public function execute(Request $request, AbstractTransfer $quoteTransfer)
    {
        $payment = $quoteTransfer->getPayment();
         if ($payment->getPaymentSelection() === 'easycredit') {
            $responseTransfer = $this->easycreditClient->sendInitializePaymentRequest($quoteTransfer);
            $this->redirectUrl = static::URL_EASYCREDIT_REDIRECT_URL . $responseTransfer->getPaymentIdentifier();
            $quoteTransfer->getPayment()->getEasycredit()->setVorgangskennung($responseTransfer->getPaymentIdentifier());
        }
         return $quoteTransfer;
    }

    /**
     * @param \Spryker\Shared\Kernel\Transfer\AbstractTransfer|\Generated\Shared\Transfer\QuoteTransfer $quoteTransfer
     *
     * @return bool
     */
    public function postCondition(AbstractTransfer $quoteTransfer)
    {
        return true;
    }

    /**
     * Return external redirect url, when redirect occurs not within same application. Used after execute.
     *
     * @return string
     */
    public function getExternalRedirectUrl()
    {
        return $this->redirectUrl;
    }

    /**
     * Requirements for this step, return true when satisfied.
     *
     * @param \Generated\Shared\Transfer\QuoteTransfer $quoteTransfer
     *
     * @return bool
     */
    public function preCondition(AbstractTransfer $quoteTransfer)
    {
        return true;
    }
    ```
<br>
</details>

<details open>
<summary markdown='span'>ShipmentStep</summary> 
```php
<?php

namespace Pyz\Yves\CheckoutPage\Process\Steps;

use Generated\Shared\Transfer\EasycreditLegalTextTransfer;
use Generated\Shared\Transfer\QuoteTransfer;
use Spryker\Shared\Kernel\Transfer\AbstractTransfer;
use Spryker\Yves\StepEngine\Dependency\Plugin\Handler\StepHandlerPluginCollection;
use SprykerEco\Client\Easycredit\EasycreditClient;
use SprykerEco\Shared\Easycredit\EasycreditConfig;
use SprykerShop\Yves\CheckoutPage\Dependency\Client\CheckoutPageToCalculationClientInterface;
use SprykerShop\Yves\CheckoutPage\Process\Steps\ShipmentStep as SprykerShipmentStep;
use Symfony\Component\HttpFoundation\Request;

class ShipmentStep extends SprykerShipmentStep
{
    /**
     * @var EasycreditClientInterface
     */
    protected $easycreditClient;

    public function __construct(
        CheckoutPageToCalculationClientInterface $calculationClient,
        StepHandlerPluginCollection $shipmentPlugins,
        string $stepRoute,
        string $escapeRoute,
        EasycreditClientInterface $client
    ) {
        parent::__construct($calculationClient, $shipmentPlugins, $stepRoute, $escapeRoute);
        $this->easycreditClient = $client;
    }

    /**
     * @param Request $request
     * @param QuoteTransfer $quoteTransfer
     * @return \Generated\Shared\Transfer\QuoteTransfer
     */
    public function execute(Request $request, AbstractTransfer $quoteTransfer)
    {
        $easycreditLegalTextTransfer = new EasycreditLegalTextTransfer();
        $easycreditLegalTextTransfer->setText($this->easycreditClient->sendApprovalTextRequest()->getText());
        $quoteTransfer->setEasycreditLegalText($easycreditLegalTextTransfer);
        return parent::execute($request, $quoteTransfer);
    }
}

SummaryStep ```php namespace Pyz\Yves\CheckoutPage\Process\Steps;

use Spryker\Shared\Kernel\Transfer\AbstractTransfer; use SprykerEco\Client\Easycredit\EasycreditClient; use SprykerShop\Yves\CheckoutPage\Dependency\Client\CheckoutPageToProductBundleClientInterface; use SprykerShop\Yves\CheckoutPage\Process\Steps\SummaryStep as SprykerSummaryStep; use Symfony\Component\HttpFoundation\Request;

class SummaryStep extends SprykerSummaryStep { /** * @param \Generated\Shared\Transfer\QuoteTransfer $quoteTransfer * * @return array */ public function getTemplateVariables(AbstractTransfer $quoteTransfer) { $easycreditData = []; if ($quoteTransfer->getPayment() && $quoteTransfer->getPayment()->getEasycredit()) { $easycreditData = [ ‘interest’ => $quoteTransfer->getPayment()->getEasycredit()->getAnfallendeZinsen(), ‘url’ => $quoteTransfer->getPayment()->getEasycredit()->getUrlVorvertraglicheInformationen(), ‘text’ => $quoteTransfer->getPayment()->getEasycredit()->getTilgungsplanText(), ]; }

    return [
        'quoteTransfer' => $quoteTransfer,
        'cartItems' => $this->productBundleClient->getGroupedBundleItems(
            $quoteTransfer->getItems(),
            $quoteTransfer->getBundleItems()
        ),
        'easycredit' => $easycreditData,
    ];
}

}

<br>
</details>

4. To run the step process for the new Easycredit step, you should extend the default `CheckoutController` with a new action for handling the Easycredit step.
<details open>
<summary markdown='span'>CheckoutController</summary>

```php
<?php

namespace Pyz\Yves\CheckoutPage\Controller;

use SprykerShop\Yves\CheckoutPage\Controller\CheckoutController as SprykerCheckoutController;
use Symfony\Component\HttpFoundation\Request;

class CheckoutController extends SprykerCheckoutController
{
	/**
	 * @param \Symfony\Component\HttpFoundation\Request $request
	 *
	 * @return array|\Symfony\Component\HttpFoundation\RedirectResponse
	 */
	public function easyCreditAction(Request $request)
	{
		return $this->createStepProcess()->process($request);
	}
}

  1. After creating a new action in the checkout controller, define a new route in CheckoutPageControllerProvider.
CheckoutPageControllerProvider.php
<?php

namespace Pyz\Yves\CheckoutPage\Plugin\Provider;

use Silex\Application;
use SprykerShop\Yves\CheckoutPage\Plugin\Provider\CheckoutPageControllerProvider as BaseCheckoutPageControllerProvider;

class CheckoutPageControllerProvider extends BaseCheckoutPageControllerProvider
{
	public const CHECKOUT_EASY_CREDIT = 'easy-credit';

	/**
	 * @param \Silex\Application $app
	 *
	 * @return void
	 */
	protected function defineControllers(Application $app)
	{
		parent::defineControllers($app);
		$this->addEasycreditStepRoute();
	}

	protected function addEasycreditStepRoute()
	{
		$this->createController('/{checkout}/easycredit', static::CHECKOUT_EASY_CREDIT, 'CheckoutPage', 'Checkout', 'easyCredit')
			->assert('checkout', $this->getAllowedLocalesPattern() . 'checkout|checkout')
			->value('checkout', 'checkout')
			->method('GET|POST');
	}
}

  1. Also, the Easycredit bundle has its own YvesController for handling a success response from Easycredit, so you have to define a controller in YvesBootstrap.
YvesBootstrap.php
<?php

namespace Pyz\Yves\ShopApplication;

...
use SprykerEco\Yves\Easycredit\Plugin\Provider\EasycreditControllerProvider;
...

class YvesBootstrap extends SprykerYvesBootstrap
{
	...
	protected function getControllerProviderStack($isSsl)
	{
		...
		new EasycreditControllerProvider($isSsl),
		...
	}
	...
}

Frontend part

Еo show the Easycredit info on the PDP page, and Summary and Payment steps, you have to extend some views on the project level .

You can find some examples below in [payment.twig](#payment-step), summary.twig and pdp.twig.

payment.twig

Payment step - src/Pyz/Yves/CheckoutPage/Theme/default/views/payment/payment.twig

{% extends template('page-layout-checkout', 'CheckoutPage') %}
{% define data = {
    backUrl: _view.previousStepUrl,
    forms: {
        payment: _view.paymentForm
    },
    title: 'checkout.step.payment.title' | trans,
    customForms: {
        'Easycredit/easycredit': 1
    }
} %}
{% block content %}
    {% embed molecule('form') with {
        class: 'box',
        data: {
            form: data.forms.payment,
            options: {
                attr: {
                    id: 'payment-form'
                }
            },
            submit: {
                enable: true,
                text: 'checkout.step.summary' | trans
            },
            cancel: {
                enable: true,
                url: data.backUrl,
                text: 'general.back.button' | trans
            },
            customForms: data.customForms
        }
    } only %}
        {% block fieldset %}
            {% for name, choices in data.form.paymentSelection.vars.choices %}
                {% set paymentProviderIndex = loop.index0 %}
                <h5>{{ ('checkout.payment.provider.' ~ name) | trans }}</h5>
                <ul>
                    {% for key, choice in choices %}
                        <li class="list__item spacing-y clear">
                            {% embed molecule('form') with {
                                data: {
                                    form: data.form[data.form.paymentSelection[key].vars.value],
                                    enableStart: false,
                                    enableEnd: false,
                                    customForms: data.customForms
                                },
                                embed: {
                                    index: loop.index ~ '-' ~ paymentProviderIndex,
                                    toggler: data.form.paymentSelection[key]
                                }
                            } only %}
                                {% block fieldset %}
                                    {{ form_row(embed.toggler, {
                                        required: false,
                                        component: molecule('toggler-radio'),
                                        attributes: {
                                            'target-selector': '.js-payment-method-' ~ embed.index,
                                            'class-to-toggle': 'is-hidden'
                                        }
                                    }) }}
                                    <div class="col col--sm-12 is-hidden js-payment-method-{{embed.index}}">
                                        <div class="col col--sm-12 col--md-6">
                                            {% if data.customForms[data.form.vars.template_path] is not defined %}
                                                {{ parent() }}
                                            {% else %}
                                                {% include view('easycredit', 'Easycredit') with {
                                                    data: {
                                                        form: data.form,
                                                        legalText: data.form.vars.legalText
                                                    }
                                                } only  %}
                                            {% endif %}
                                        </div>
                                    </div>
                                {% endblock %}
                            {% endembed %}
                        </li>
                    {% endfor %}
                </ul>
            {% endfor %}
        {% endblock %}
    {% endembed %}
{% endblock %}

summary.twig

Summary step - src/Pyz/Yves/CheckoutPage/Theme/default/views/summary/summary.twig

{% extends template('page-layout-checkout', 'CheckoutPage') %}

 {% define data = {
	backUrl: _view.previousStepUrl,
	transfer: _view.quoteTransfer,
	cartItems: _view.cartItems,
	easycredit: _view.easycredit,
	shippingAddress: _view.quoteTransfer.shippingAddress,
	billingAddress: _view.quoteTransfer.billingAddress,
	shipmentMethod: _view.quoteTransfer.shipment.method.name,
	paymentMethod: _view.quoteTransfer.payment.paymentMethod,

	 forms: {
		summary: _view.summaryForm
	},

	overview: {
		shipmentMethod: _view.quoteTransfer.shipment.method.name,
		expenses: _view.quoteTransfer.expenses,
		voucherDiscounts: _view.quoteTransfer.voucherDiscounts,
		cartRuleDiscounts: _view.quoteTransfer.cartRuleDiscounts,

		 prices: {
			subTotal: _view.quoteTransfer.totals.subtotal,
			storeCurrency: _view.quoteTransfer.shipment.method.storeCurrencyPrice,
			grandTotal: _view.quoteTransfer.totals.grandtotal,
			tax: _view.quoteTransfer.totals.taxtotal.amount,
			discountTotal: _view.quoteTransfer.totals.discounttotal | default
		}
	},

	title: 'checkout.step.summary.title' | trans
} %}

 {% block content %}
	<div class="grid">
		<div class="col col--sm-12 col--lg-4">
			<div class="box">
				<span class="float-right">{{ 'checkout.step.summary.with_method' | trans }} <strong>{{data.paymentMethod}}</strong></span>
				<h6>{{ 'checkout.step.summary.payment' | trans }}</h6>
				<hr/>

				{% include molecule('display-address') with {
					class: 'text-small',
					data: {
						address: data.billingAddress
					}
				 } only %}
			</div>

			<div class="box">
				<span class="float-right">{{ 'checkout.step.summary.with_method' | trans }} <strong>{{ data.shipmentMethod }}</strong></span>
				<h6>{{ 'checkout.step.summary.shipping' | trans }}</h6>
				<hr/>

				{% include molecule('display-address') with {
					class: 'text-small',
					data: {
						address: data.shippingAddress
					}
				} only %}
			</div>
		</div>

		<div class="col col--sm-12 col--lg-8">
			<div class="box">
				{% for item in data.cartItems %}
					{% set item = item.bundleProduct is defined ? item.bundleProduct : item %}
					{% embed molecule('summary-item', 'CheckoutPage') with {
						data: {
							name: item.name,
							quantity: item.quantity,
							price: item.sumPrice | money,
							options: item.productOptions | default({}),
							bundleItems: item.bundleItems | default([]),
							quantitySalesUnit: item.quantitySalesUnit,
							amountSalesUnit: item.amountSalesUnit,
							amount: item.amount
						},
						embed: {
							isLast: not loop.last,
							item: item
						}
					} only %}
						{% block body %}
							{{parent()}}
							{% if widgetExists('CartNoteQuoteItemNoteWidgetPlugin') %}
								{% if embed.item.cartNote is not empty %}
									{{ widget('CartNoteQuoteItemNoteWidgetPlugin', embed.item) }} {# @deprecated Use molecule('note-list', 'CartNoteWidget') instead. #}
								{% endif %}
							{% elseif embed.item.cartNote is not empty %}
								{% include molecule('note-list', 'CartNoteWidget') ignore missing with {
									data: {
										label: 'cart_note.checkout_page.item_note',
										note: embed.item.cartNote
									}
								} only %}
							{% endif %}
							{% if embed.isLast %}<hr/>{% endif %}
						{% endblock %}
					{% endembed %}
				{% endfor %}
			</div>

			 {% if data.transfer.cartNote is not empty %}
				{% if widgetExists('CartNoteQuoteNoteWidgetPlugin') %}
					<div class="box">
						{{ widget('CartNoteQuoteNoteWidgetPlugin', data.transfer) }}  {#@deprecated Use molecule('note-list', 'CartNoteWidget') instead.#}
					</div>
				{% else %}
					<div class="box">
						{% include molecule('note-list', 'CartNoteWidget') ignore missing with {
							data: {
								label: 'cart_note.checkout_page.quote_note',
								note: data.transfer.cartNote
							}
						} only %}
					</div>
				{% endif %}
			{% endif %}

			<div class="box">
				{% widget 'CheckoutVoucherFormWidget' args [data.transfer] only %}
				{% elsewidget 'CheckoutVoucherFormWidgetPlugin' args [data.transfer] only %} {# @deprecated Use CheckoutVoucherFormWidget instead. #}
				{% endwidget %}
			</div>
			% embed molecule('form') with {
				class: 'box',
				data: {
					form: data.forms.summary,
					submit: {
						enable: can('SeeOrderPlaceSubmitPermissionPlugin'),
						text: 'checkout.step.place.order' | trans
					},
					cancel: {
						enable: true,
						url: data.backUrl,
						text: 'general.back.button' | trans
					}
				},
				embed: {
					overview: data.overview
				}
			} only %}
				{% block body %}
					{% include molecule('summary-overview', 'CheckoutPage') with {
						data: embed.overview
					} only %}

					<hr />
					{{parent()}}
				{% endblock %}
			{% endembed %}
			{% if data.easycredit %}
				{% include molecule('easycredit-summary', 'Easycredit') with {
					data: {
						interest: data.easycredit.interest,
						url: data.easycredit.url,
						text: data.easycredit.text
					}
				} only %}
			{% endif %}
		</div>
	</div>
{% endblock %}

pdp.twig

PDP page - src/Pyz/Yves/ProductDetailPage/Theme/default/views/pdp/pdp.twig

{% extends template('page-layout-main') %}

{% define data = {
	product: _view.product,
	productUrl: _view.productUrl,

	title: product.metaTitle | default(_view.product.name),
	metaTitle: product.metaTitle | default(_view.product.name),
	metaDescription: _view.product.metaDescription | default,
	metaKeywords: _view.product.metaKeywords | default
} %}

{% block headScripts %}
	{{ parent() }}
	<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js" type="text/javascript"></script>
{% endblock %}

{% block breadcrumbs %}
	{% widget 'ProductBreadcrumbsWithCategoriesWidget' args [data.product] only %}
	{% elsewidget 'ProductCategoryWidgetPlugin' args [data.product] only %} {# @deprecated Use ProductBreadcrumbsWithCategoriesWidget instead. #}
	{% endwidget %}
{% endblock %}

{% block title %}
	<h3 itemprop="name">{{ data.product.name }}</h3>
	<link itemprop="url" href="{{ data.productUrl }}" />
{% endblock %}

{% block content %}
	<div class="grid">
		<div class="col col--sm-12 col--lg-7 col--xl-8">
			{% include molecule('product-carousel', 'ProductDetailPage') with {
				class: 'box',
				data: {
					product: data.product
				}
			} only %}
	</div>

	<div class="col col--sm-12 col--lg-5 col--xl-4">
		{% include molecule('product-configurator', 'ProductDetailPage') with {
			class: 'box',
			data: {
				product: data.product
			}
		} only %}
	</div>
</div>

{% widget 'ProductAlternativeListWidget' args [data.product] only %}
{% elsewidget 'ProductAlternativeWidgetPlugin' args [data.product] only %} {# @deprecated Use ProductAlternativeListWidget instead. #}
{% endwidget %}

{% include molecule('product-detail', 'ProductDetailPage') with {
	class: 'box',
	data: {
		description: data.product.description,
		attributes: data.product.attributes
	}
} only %}

{% widget 'ProductReplacementForListWidget' args [data.product.sku] only %}
{% elsewidget 'ProductReplacementForWidgetPlugin' args [data.product.sku] only %} {# @deprecated Use ProductReplacementForListWidget instead. #}
{% endwidget %}

{% widget 'ProductDetailPageReviewWidget' args [data.product.idProductAbstract] only %}
{% elsewidget 'ProductReviewWidgetPlugin' args [data.product.idProductAbstract] only %} {# @deprecated Use ProductDetailPageReviewWidget instead. #}
{% endwidget %}

{% widget 'SimilarProductsWidget' args [data.product] only %}
{% elsewidget 'SimilarProductsWidgetPlugin' args [data.product] only %} {# @deprecated Use SimilarProductsWidget instead. #}
{% endwidget %}

{% if widgetExists('ProductCmsBlockWidgetPlugin') %}
	{{ widget('ProductCmsBlockWidgetPlugin', data.product) }} {# @deprecated Use molecule('product-cms-block', 'CmsBlockWidget') instead. #}
{% else %}
	{% include molecule('product-cms-block', 'CmsBlockWidget') ignore missing with {
		class: 'box',
		data: {
			idProductAbstract: data.product.idProductAbstract
		}
	} only %}
{% endif %}

{% endblock %}

Note
You might want to configure the product detail page to add some validation and show the Easycredit badge in `src/Pyz/Yves/ProductDetailPage/Theme/default/components/molecules/product-configurator/product-configurator.twig`
product-configurator.twig
...
    {% set easyCreditMinTreshold = 20000 %}
    {% set easyCreditMaxTreshold = 500000 %}
    {% if data.product.price > easyCreditMinTreshold and data.product.price < easyCreditMaxTreshold %}
        {% include molecule('easycredit-badge', 'EasyCredit' ) with {
            data: {
                title: 'EasyCredit:',
                id: 'easy-credit-id'
            },
            attributes: {
                'easycredit-options': '{
                    "webshopId": "<Your shop identifier>",
                    "finanzierungsbetrag": "' ~ data.product.price / 100 ~ '" ,
                    "textVariante": "KURZ"
                }'
            }
        } only %}
    {% endif %}
...