Skip to content

Commit

Permalink
Merge pull request #65 from PAYONE-GmbH/feature/PAYONE-80
Browse files Browse the repository at this point in the history
[PAYONE-80] Add secure invoice payment
  • Loading branch information
hreinberger authored Oct 12, 2020
2 parents 65b309f + d9ed89c commit d4d5105
Show file tree
Hide file tree
Showing 48 changed files with 1,425 additions and 559 deletions.
141 changes: 141 additions & 0 deletions src/Components/Hydrator/LineItemHydrator/LineItemHydrator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<?php

declare(strict_types=1);

namespace PayonePayment\Components\Hydrator\LineItemHydrator;

use Exception;
use Shopware\Core\Checkout\Cart\LineItem\LineItem;
use Shopware\Core\Checkout\Cart\Tax\Struct\CalculatedTaxCollection;
use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemCollection;
use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity;
use Shopware\Core\Checkout\Promotion\Cart\PromotionProcessor;
use Shopware\Core\System\Currency\CurrencyEntity;

class LineItemHydrator implements LineItemHydratorInterface
{
public const TYPE_GOODS = 'goods';
public const TYPE_VOUCHER = 'voucher';

public function mapPayoneOrderLinesByRequest(
CurrencyEntity $currency,
OrderLineItemCollection $orderLineItems,
array $requestLines
): array {
$requestLineItems = [];
$counter = 0;

foreach ($requestLines as $orderLine) {
if (!array_key_exists('id', $orderLine)) {
continue;
}

$lineItem = $orderLineItems->get($orderLine['id']);

if ($lineItem === null) {
continue;
}

if ($this->isCustomizedProduct($lineItem)) {
continue;
}

$taxes = $lineItem->getPrice() ? $lineItem->getPrice()->getCalculatedTaxes() : null;

if (null === $taxes || null === $taxes->first()) {
continue;
}

$requestLineItems = array_merge(
$requestLineItems,
$this->getLineItemRequest(
++$counter,
$lineItem,
$currency,
$taxes,
$orderLine['quantity']
)
);
}

return $requestLineItems;
}

public function mapOrderLines(CurrencyEntity $currency, OrderLineItemCollection $lineItemCollection): array
{
$requestLineItems = [];
$counter = 0;

/** @var OrderLineItemEntity $lineItem */
foreach ($lineItemCollection as $lineItem) {
if ($this->isCustomizedProduct($lineItem)) {
continue;
}

$taxes = $lineItem->getPrice() ? $lineItem->getPrice()->getCalculatedTaxes() : null;

if (null === $taxes || null === $taxes->first()) {
continue;
}

$requestLineItems = array_merge(
$requestLineItems,
$this->getLineItemRequest(
++$counter,
$lineItem,
$currency,
$taxes,
$lineItem->getQuantity()
)
);
}

return $requestLineItems;
}

protected function mapItemType(?string $itemType): string
{
if ($itemType === LineItem::CREDIT_LINE_ITEM_TYPE) {
return self::TYPE_VOUCHER;
}

if ($itemType === PromotionProcessor::LINE_ITEM_TYPE) {
return self::TYPE_VOUCHER;
}

return self::TYPE_GOODS;
}

private function isCustomizedProduct(OrderLineItemEntity $lineItemEntity): bool
{
try {
/** @phpstan-ignore-next-line */
if (class_exists('Swag\CustomizedProducts\Core\Checkout\CustomizedProductsCartDataCollector') &&
CustomizedProductsCartDataCollector::CUSTOMIZED_PRODUCTS_TEMPLATE_LINE_ITEM_TYPE === $lineItemEntity->getType(
) &&
null === $lineItemEntity->getParentId()) {
return true;
}
} catch (Exception $exception) {
// Catch class not found if SwagCustomizedProducts plugin is not installed
}

return false;
}

private function getLineItemRequest(int $index, OrderLineItemEntity $lineItemEntity, CurrencyEntity $currencyEntity, CalculatedTaxCollection $taxCollection, int $quantity): array
{
$productNumber = is_array($lineItemEntity->getPayload()) && array_key_exists('productNumber', $lineItemEntity->getPayload())
? $lineItemEntity->getPayload()['productNumber']
: $lineItemEntity->getIdentifier();

return [
'it[' . $index . ']' => $this->mapItemType($lineItemEntity->getType()),
'id[' . $index . ']' => $productNumber,
'pr[' . $index . ']' => (int) round($lineItemEntity->getUnitPrice() * (10 ** $currencyEntity->getDecimalPrecision())),
'no[' . $index . ']' => $quantity,
'de[' . $index . ']' => $lineItemEntity->getLabel(),
'va[' . $index . ']' => (int) round($taxCollection->first()->getTaxRate() * (10 ** $currencyEntity->getDecimalPrecision())),
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace PayonePayment\Components\Hydrator\LineItemHydrator;

use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemCollection;
use Shopware\Core\System\Currency\CurrencyEntity;

interface LineItemHydratorInterface
{
public function mapPayoneOrderLinesByRequest(
CurrencyEntity $currency,
OrderLineItemCollection $orderLineItems,
array $requestLines
): array;

public function mapOrderLines(CurrencyEntity $currency, OrderLineItemCollection $lineItemCollection): array;
}
72 changes: 11 additions & 61 deletions src/Components/RequestBuilder/AbstractRequestBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,76 +4,26 @@

namespace PayonePayment\Components\RequestBuilder;

use Exception;
use PayonePayment\Components\Hydrator\LineItemHydrator\LineItemHydratorInterface;
use PayonePayment\Struct\PaymentTransaction;
use Shopware\Core\Checkout\Cart\LineItem\LineItem;
use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemCollection;
use Shopware\Core\Checkout\Promotion\Cart\PromotionProcessor;
use Shopware\Core\Framework\Context;
use Shopware\Core\System\Currency\CurrencyEntity;
use Swag\CustomizedProducts\Core\Checkout\CustomizedProductsCartDataCollector;
use Symfony\Component\HttpFoundation\ParameterBag;

abstract class AbstractRequestBuilder
{
public const TYPE_GOODS = 'goods';
public const TYPE_VOUCHER = 'voucher';
/** @var LineItemHydratorInterface */
protected $lineItemHydrator;

abstract public function supports(string $paymentMethodId): bool;

abstract public function getAdditionalRequestParameters(PaymentTransaction $transaction, Context $context, ParameterBag $parameterBag): array;

protected function mapPayoneOrderLines(CurrencyEntity $currency, OrderLineItemCollection $orderLineItems, array $requestLines): array
public function __construct(LineItemHydratorInterface $lineItemHydrator)
{
$requestLineItems = [];
$counter = 1;

foreach ($requestLines as $orderLine) {
foreach ($orderLineItems as $lineItem) {
try {
/** @phpstan-ignore-next-line */
if (class_exists('Swag\CustomizedProducts\Core\Checkout\CustomizedProductsCartDataCollector') &&
CustomizedProductsCartDataCollector::CUSTOMIZED_PRODUCTS_TEMPLATE_LINE_ITEM_TYPE === $lineItem->getType() &&
null === $lineItem->getParentId()) {
continue;
}
} catch (Exception $exception) {
// Catch class not found if SwagCustomizedProducts plugin is not installed
}

$taxes = $lineItem->getPrice() ? $lineItem->getPrice()->getCalculatedTaxes() : null;

if (null === $taxes || null === $taxes->first()) {
continue;
}

if ($lineItem->getId() !== $orderLine['id']) {
continue;
}

$requestLineItems['it[' . $counter . ']'] = $this->mapItemType($lineItem->getType());
$requestLineItems['id[' . $counter . ']'] = $lineItem->getIdentifier();
$requestLineItems['pr[' . $counter . ']'] = (int) round(($lineItem->getUnitPrice() * (10 ** $currency->getDecimalPrecision())));
$requestLineItems['no[' . $counter . ']'] = $orderLine['quantity'];
$requestLineItems['de[' . $counter . ']'] = $lineItem->getLabel();
$requestLineItems['va[' . $counter . ']'] = (int) round(($taxes->first()->getTaxRate() * (10 ** $currency->getDecimalPrecision())));
++$counter;
}
}

return $requestLineItems;
$this->lineItemHydrator = $lineItemHydrator;
}

protected function mapItemType(?string $itemType): string
{
if ($itemType === LineItem::CREDIT_LINE_ITEM_TYPE) {
return self::TYPE_VOUCHER;
}

if ($itemType === PromotionProcessor::LINE_ITEM_TYPE) {
return self::TYPE_VOUCHER;
}
abstract public function supports(string $paymentMethodId): bool;

return self::TYPE_GOODS;
}
abstract public function getAdditionalRequestParameters(
PaymentTransaction $transaction,
Context $context,
ParameterBag $parameterBag
): array;
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ public function getAdditionalRequestParameters(PaymentTransaction $transaction,
return [];
}

return $this->mapPayoneOrderLines($currency, $transaction->getOrder()->getLineItems(), $orderLines);
return $this->lineItemHydrator->mapPayoneOrderLinesByRequest($currency, $transaction->getOrder()->getLineItems(), $orderLines);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ public function getAdditionalRequestParameters(PaymentTransaction $transaction,
return [];
}

return $this->mapPayoneOrderLines($currency, $transaction->getOrder()->getLineItems(), $orderLines);
return $this->lineItemHydrator->mapPayoneOrderLinesByRequest($currency, $transaction->getOrder()->getLineItems(), $orderLines);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ public function getAdditionalRequestParameters(PaymentTransaction $transaction,
return [];
}

return $this->mapPayoneOrderLines($currency, $transaction->getOrder()->getLineItems(), $orderLines);
return $this->lineItemHydrator->mapPayoneOrderLinesByRequest($currency, $transaction->getOrder()->getLineItems(), $orderLines);
}
}
30 changes: 30 additions & 0 deletions src/Components/RequestBuilder/SecureInvoiceRequestBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace PayonePayment\Components\RequestBuilder;

use PayonePayment\PaymentMethod\PayoneSecureInvoice;
use PayonePayment\Struct\PaymentTransaction;
use Shopware\Core\Framework\Context;
use Symfony\Component\HttpFoundation\ParameterBag;

class SecureInvoiceRequestBuilder extends AbstractRequestBuilder
{
public function supports(string $paymentMethodId): bool
{
return $paymentMethodId === PayoneSecureInvoice::UUID;
}

public function getAdditionalRequestParameters(PaymentTransaction $transaction, Context $context, ParameterBag $parameterBag): array
{
$currency = $transaction->getOrder()->getCurrency();
$orderLines = $parameterBag->get('orderLines', []);

if ($currency === null || empty($orderLines) || empty($transaction->getOrder()->getLineItems())) {
return [];
}

return $this->lineItemHydrator->mapPayoneOrderLinesByRequest($currency, $transaction->getOrder()->getLineItems(), $orderLines);
}
}
4 changes: 4 additions & 0 deletions src/Components/Validator/BirthdayValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ class BirthdayValidator extends AbstractComparisonValidator
*/
protected function compareValues($value1, $value2)
{
if (empty($value1)) {
return false;
}

$birthday = DateTime::createFromFormat('Y-m-d', $value1);

return $birthday < $value2;
Expand Down
2 changes: 2 additions & 0 deletions src/Configuration/ConfigurationPrefixes.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ interface ConfigurationPrefixes
public const CONFIGURATION_PREFIX_IDEAL = 'iDeal';
public const CONFIGURATION_PREFIX_PAYDIREKT = 'paydirekt';
public const CONFIGURATION_PREFIX_PREPAYMENT = 'prepayment';
public const CONFIGURATION_PREFIX_SECURE_INVOICE = 'secureInvoice';

public const CONFIGURATION_PREFIXES = [
Handler\PayoneCreditCardPaymentHandler::class => self::CONFIGURATION_PREFIX_CREDITCARD,
Expand All @@ -34,5 +35,6 @@ interface ConfigurationPrefixes
Handler\PayoneIDealPaymentHandler::class => self::CONFIGURATION_PREFIX_IDEAL,
Handler\PayonePaydirektPaymentHandler::class => self::CONFIGURATION_PREFIX_PAYDIREKT,
Handler\PayonePrepaymentPaymentHandler::class => self::CONFIGURATION_PREFIX_PREPAYMENT,
Handler\PayoneSecureInvoicePaymentHandler::class => self::CONFIGURATION_PREFIX_SECURE_INVOICE,
];
}
23 changes: 23 additions & 0 deletions src/Controller/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use PayonePayment\PaymentHandler\PayonePaypalExpressPaymentHandler;
use PayonePayment\PaymentHandler\PayonePaypalPaymentHandler;
use PayonePayment\PaymentHandler\PayonePrepaymentPaymentHandler;
use PayonePayment\PaymentHandler\PayoneSecureInvoicePaymentHandler;
use PayonePayment\PaymentHandler\PayoneSofortBankingPaymentHandler;
use PayonePayment\Payone\Client\Exception\PayoneRequestException;
use PayonePayment\Payone\Client\PayoneClientInterface;
Expand Down Expand Up @@ -327,6 +328,28 @@ private function getPaymentParameters(string $paymentClass): array
];

break;
case PayoneSecureInvoicePaymentHandler::class:
return [
'request' => 'preauthorization',
'clearingtype' => 'rec',
'financingtype' => 'POV',
'amount' => 10000,
'currency' => 'EUR',
'reference' => sprintf('%s%d', self::REFERENCE_PREFIX_TEST, random_int(1000000000000, 9999999999999)),
'birthday' => '19900505',
'firstname' => 'Test',
'lastname' => 'Test',
'country' => 'DE',
'email' => '[email protected]',
'street' => 'teststreet 2',
'zip' => '12345',
'city' => 'Test',
'ip' => '127.0.0.1',
'businessrelation' => 'b2c',
];

break;

default:
$this->logger->error(sprintf('There is no test data defined for payment class %s', $paymentClass));
throw new RuntimeException(sprintf('There is no test data defined for payment class %s', $paymentClass));
Expand Down
14 changes: 14 additions & 0 deletions src/DependencyInjection/handler/payment_handler.xml
Original file line number Diff line number Diff line change
Expand Up @@ -167,5 +167,19 @@

<tag name="shopware.payment.method.async" />
</service>

<service id="PayonePayment\PaymentHandler\PayoneSecureInvoicePaymentHandler">
<argument type="service" id="PayonePayment\Components\ConfigReader\ConfigReader" />
<argument type="service" id="PayonePayment\Payone\Request\SecureInvoice\SecureInvoicePreAuthorizeRequestFactory" />
<argument type="service" id="PayonePayment\Payone\Request\SecureInvoice\SecureInvoiceAuthorizeRequestFactory" />
<argument type="service" id="PayonePayment\Payone\Client\PayoneClient" />
<argument type="service" id="translator" />
<argument type="service" id="PayonePayment\Components\DataHandler\Transaction\TransactionDataHandler" />
<argument type="service" id="order_line_item.repository" />
<argument type="service" id="request_stack" />

<tag name="shopware.payment.method.sync" />
</service>

</services>
</container>
Loading

0 comments on commit d4d5105

Please sign in to comment.