Skip to content
This repository has been archived by the owner on Feb 16, 2022. It is now read-only.

Commit

Permalink
Merge pull request #51 from heidelpay/develop
Browse files Browse the repository at this point in the history
Release 1.2.2
  • Loading branch information
Jan-Erik Spreng authored Apr 8, 2021
2 parents 86b7f4b + 3299a27 commit d131d80
Show file tree
Hide file tree
Showing 26 changed files with 393 additions and 147 deletions.
27 changes: 27 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
stages:
- setup
- test
- deploy
- package

cache:
Expand Down Expand Up @@ -64,6 +65,7 @@ sw-phpstan:
- cd ${CI_PROJECT_DIR}/sw_dir/custom/plugins/${CI_PROJECT_NAME} && vendor/bin/phpstan analyse -c phpstan_sw.neon -a vendor/autoload.php -a ../../../vendor/autoload.php .

shopware-validate:
allow_failure: true
image:
name: friendsofshopware/plugin-uploader:0.3.5
entrypoint: [ "/bin/sh", "-c" ]
Expand All @@ -81,9 +83,34 @@ shopware-validate:
- php /app/bin/pluginupload ext:zip . $CI_COMMIT_SHA
- php /app/bin/pluginupload ext:validate *$CI_COMMIT_SHA.zip

staging:
image: "edbizarro/gitlab-ci-pipeline-php:7.2"
stage: deploy
environment:
name: staging
url: https://unzer-sw5.kellerkinder.io
only:
- master
before_script:
# Run ssh-agent (inside the build environment)
- eval $(ssh-agent -s)

# Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store
- ssh-add <(echo "$SSH_PRIVATE_KEY")

# For Docker builds disable host key checking. Be aware that by adding that
# you are suspectible to man-in-the-middle attacks.
# WARNING: Use this only with the Docker executor, if you use it with shell
# you will overwrite your user's SSH config.
- mkdir -p ~/.ssh
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
script:
- ssh -A [email protected] 'cd /var/www/share/unzer-sw5.kellerkinder.io/htdocs/custom/plugins/HeidelPayment && git pull && /var/www/share/unzer-sw5.kellerkinder.io/htdocs/bin/console sw:cache:clear && /var/www/share/unzer-sw5.kellerkinder.io/htdocs/bin/console sw:plugin:refresh && /var/www/share/unzer-sw5.kellerkinder.io/htdocs/bin/console sw:plugin:uninstall HeidelPayment && /var/www/share/unzer-sw5.kellerkinder.io/htdocs/bin/console sw:plugin:install --activate HeidelPayment && /var/www/share/unzer-sw5.kellerkinder.io/htdocs/bin/console sw:theme:cache:generate'

package:
image: kellerkinder/shopware-package-plugin:latest
stage: package
needs: []
only:
- tags
- master
Expand Down
24 changes: 24 additions & 0 deletions Components/HeidelpayDebugHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace HeidelPayment\Components;

use heidelpayPHP\Interfaces\DebugHandlerInterface;
use Psr\Log\LoggerInterface;

class HeidelpayDebugHandler implements DebugHandlerInterface
{
/** @var LoggerInterface */
protected $logger;

public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}

public function log(string $message): void
{
$this->logger->info($message);
}
}
120 changes: 65 additions & 55 deletions Components/Hydrator/ResourceHydrator/BasketHydrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

class BasketHydrator implements ResourceHydratorInterface
{
public const UNZER_DEFAULT_PRECISION = 4;

private const SW_VOUCHER_MODE = '2';
private const SW_DISCOUNT = '3';
private const SW_ABO_DISCOUNT_MODE = '10';
Expand All @@ -31,91 +33,99 @@ public function hydrateOrFetch(
return $heidelpayObj->fetchBasket($resourceId);
}

$isAmountInNet = isset($data['sAmountWithTax']);
$isTaxFree = $data['taxFree'];
$amountTotalGrossTransaction = $isAmountInNet && !$isTaxFree ? $data['sAmountWithTax'] : $data['sAmount'];
$isAmountInNet = isset($data['sAmountWithTax']);
$isTaxFree = $data['taxFree'];
$amountTotalGross = $isAmountInNet && !$isTaxFree ? $data['sAmountWithTax'] : $data['sAmount'];

$basketAmountTotalGross = 0;
$basketAmountTotalVat = 0;
$basketAmountTotalDiscount = 0;
$basket = new Basket(
$this->generateOrderId(),
round($amountTotalGross, self::UNZER_DEFAULT_PRECISION),
$data['sCurrencyName']
);

$result = new Basket();
$result->setCurrencyCode($data['sCurrencyName']);
$result->setOrderId($this->generateOrderId());
$basket->setAmountTotalVat(round($data['sAmountTax'], self::UNZER_DEFAULT_PRECISION));

//Actual line items
foreach ($data['content'] as $lineItem) {
$amountNet = abs($lineItem['amountnetNumeric']);
$amountGross = $isAmountInNet ? abs($lineItem['amountWithTax']) : abs($lineItem['amountNumeric']);
$amountPerUnit = $isAmountInNet
? $amountGross / $lineItem['quantity']
: abs($lineItem['additional_details']['price_numeric']);
$this->hydrateBasketItems($basket, $data['content'], $isAmountInNet);
$this->hydrateDispatch($basket, $data);
$this->hydrateDiscount($basket);

if (!$amountPerUnit) {
$amountPerUnit = abs($lineItem['priceNumeric']);
}
return $basket;
}

protected function hydrateBasketItems(Basket $basket, array $lineItems, bool $isAmountInNet): void
{
foreach ($lineItems as $lineItem) {
$basketItem = new BasketItem();
$basketItem->setType($this->getBasketItemType($lineItem));
$basketItem->setTitle($lineItem['articlename']);
$basketItem->setQuantity((int) $lineItem['quantity']);

if ($this->isBasketItemVoucher($lineItem)) {
$basketItem->setType($this->getBasketItemType($lineItem));
$basketItem->setTitle($lineItem['articlename']);
$basketItem->setAmountDiscount(round($amountGross, 4));
$basketItem->setQuantity((int) $lineItem['quantity']);
$amountGross = round(abs(
$isAmountInNet ? $lineItem['amountWithTax'] : $lineItem['amountNumeric']
), self::UNZER_DEFAULT_PRECISION);

$basketAmountTotalDiscount += $basketItem->getAmountDiscount();
if ($this->isBasketItemVoucher($lineItem)) {
$basketItem->setAmountDiscount($amountGross);
} else {
$basketItem->setType($this->getBasketItemType($lineItem));
$basketItem->setTitle($lineItem['articlename']);
$basketItem->setAmountPerUnit(round($amountPerUnit, 4));
$basketItem->setAmountGross(round($amountGross, 4));
$basketItem->setAmountNet(round($amountNet, 4));
$basketItem->setAmountVat(round(abs(str_replace(',', '.', $lineItem['tax'])), 4));
$basketItem->setQuantity((int) $lineItem['quantity']);
$amountPerUnit = $isAmountInNet
? $amountGross / $lineItem['quantity']
: abs($lineItem['additional_details']['price_numeric']);

if (!$amountPerUnit) {
$amountPerUnit = abs($lineItem['priceNumeric']);
}

$basketItem->setAmountPerUnit(round($amountPerUnit, self::UNZER_DEFAULT_PRECISION));
$basketItem->setAmountGross($amountGross);
$basketItem->setAmountNet(round(abs($lineItem['amountnetNumeric']), self::UNZER_DEFAULT_PRECISION));
$basketItem->setAmountVat(round(abs(str_replace(',', '.', $lineItem['tax'])), self::UNZER_DEFAULT_PRECISION));
$basketItem->setVat((float) $lineItem['tax_rate']);

$basketAmountTotalGross += $basketItem->getAmountGross();
$basketAmountTotalVat += $basketItem->getAmountVat();
}

if ($lineItem['abo_attributes']['isAboArticle']) {
$result->setSpecialParams(array_merge($result->getSpecialParams(), ['isAbo' => true]));
if (array_key_exists('abo_attributes', $lineItem) && !empty($lineItem['abo_attributes'])
&& array_key_exists('isAboArticle', $lineItem['abo_attributes']) && !empty($lineItem['abo_attributes']['isAboArticle'])) {
$basket->setSpecialParams(array_merge($basket->getSpecialParams(), ['isAbo' => true]));
$basketItem->setSpecialParams([
'aboCommerce' => $lineItem['aboCommerce'],
]);
}

$result->addBasketItem($basketItem);
$basket->addBasketItem($basketItem);
}
}

//No dispatch selected!
if (empty($data['sDispatch'])) {
return $result;
protected function hydrateDispatch(Basket $basket, array $data): void
{
if (!array_key_exists('sDispatch', $data) || empty($data['sDispatch'])) {
return;
}

//Shipping cost line item
$dispatchBasketItem = new BasketItem();
$dispatchBasketItem->setType(BasketItemTypes::SHIPMENT);
$dispatchBasketItem->setTitle($data['sDispatch']['name']);
$dispatchBasketItem->setAmountGross(round($data['sShippingcostsWithTax'], 4));
$dispatchBasketItem->setAmountPerUnit(round($data['sShippingcostsWithTax'], 4));
$dispatchBasketItem->setAmountNet(round($data['sShippingcostsNet'], 4));
$dispatchBasketItem->setAmountVat(round($data['sShippingcostsWithTax'] - $data['sShippingcostsNet'], 4));
$dispatchBasketItem->setQuantity(1);
$dispatchBasketItem->setAmountGross(round($data['sShippingcostsWithTax'], self::UNZER_DEFAULT_PRECISION));
$dispatchBasketItem->setAmountPerUnit(round($data['sShippingcostsWithTax'], self::UNZER_DEFAULT_PRECISION));
$dispatchBasketItem->setAmountNet(round($data['sShippingcostsNet'], self::UNZER_DEFAULT_PRECISION));
$dispatchBasketItem->setAmountVat(round($data['sShippingcostsWithTax'] - $data['sShippingcostsNet'], self::UNZER_DEFAULT_PRECISION));
$dispatchBasketItem->setVat((float) $data['sShippingcostsTax']);
$dispatchBasketItem->setQuantity(1);

$result->addBasketItem($dispatchBasketItem);
$basket->addBasketItem($dispatchBasketItem);
}

$basketAmountTotalGross += $dispatchBasketItem->getAmountGross();
$basketAmountTotalVat += $dispatchBasketItem->getAmountVat();
$basketAmountTotalDiscount += $dispatchBasketItem->getAmountDiscount();
protected function hydrateDiscount(Basket $basket): void
{
$calculatedDiscount = 0;

// setting of all totalAmounts
$result->setAmountTotalGross(round((float) $basketAmountTotalGross, 4));
$result->setAmountTotalVat(round((float) $basketAmountTotalVat, 4));
$result->setAmountTotalDiscount(round((float) $basketAmountTotalDiscount, 4));
/** @var BasketItem $basketItem */
foreach ($basket->getBasketItems() as $basketItem) {
if ((int) round($basketItem->getAmountDiscount(), self::UNZER_DEFAULT_PRECISION) !== 0) {
$calculatedDiscount += round($basketItem->getAmountDiscount(), self::UNZER_DEFAULT_PRECISION);
}
}

return $result;
$basket->setAmountTotalDiscount($calculatedDiscount);
}

private function generateOrderId(): string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@

abstract class AbstractCustomerHydrator
{
/*
* This regex is used to remove any non whitespace, plus or number parts,
* while also removing occurrences of more than one whitespace at a time.
*/
public const PHONE_NUMBER_REGEX = '/([^0-9 +]|\s{2,})/';

/** @var Connection */
protected $connection;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public function hydrateOrFetch(
$shippingAddress = $data['shippingaddress'];
$billingAddress = $data['billingaddress'];

$phoneNumber = \preg_replace(self::PHONE_NUMBER_REGEX, '', $billingAddress['phone']);

try {
if ($heidelpayObj) {
$result = $heidelpayObj->fetchCustomerByExtCustomerId($resourceId);
Expand All @@ -43,7 +45,7 @@ public function hydrateOrFetch(
$result->setEmail($user['email']);
$result->setSalutation($this->getSalutation($billingAddress['salutation']));
$result->setCustomerId($user['customernumber']);
$result->setPhone($billingAddress['phone']);
$result->setPhone($phoneNumber);

$result->setBillingAddress($this->getHeidelpayAddress($billingAddress));
$result->setShippingAddress($this->getHeidelpayAddress($shippingAddress));
Expand Down
2 changes: 1 addition & 1 deletion Controllers/AbstractHeidelpayPaymentController.php
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ public function recurring(): void
}

$heidelBasket = $this->getRecurringBasket($recurringData['order']);
$this->paymentDataStruct = new PaymentDataStruct($heidelBasket->getAmountTotalGross(), $recurringData['order']['currency'], $this->getChargeRecurringUrl());
$this->paymentDataStruct = new PaymentDataStruct($this->getAmount(), $recurringData['order']['currency'], $this->getChargeRecurringUrl());

$this->paymentDataStruct->fromArray([
'basket' => $heidelBasket,
Expand Down
58 changes: 54 additions & 4 deletions Controllers/Widgets/HeidelpayCreditCard.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public function createPaymentAction(): void
{
parent::pay();

if ($this->paymentDataStruct->isRecurring()) {
if (!$this->paymentType && $this->paymentDataStruct->isRecurring()) {
$activateRecurring = false;

try {
Expand All @@ -41,9 +41,19 @@ public function createPaymentAction(): void

return;
}

if ($this->recurring->getRedirectUrl() !== null) {
$this->view->assign('redirectUrl', $this->recurring->getRedirectUrl());

return;
}
}

$this->handleNormalPayment();
if ($this->paymentType && $this->paymentType->isRecurring()) {
$this->recurringFinishedAction();
} else {
$this->handleNormalPayment();
}
}

public function chargeRecurringPaymentAction(): void
Expand All @@ -57,9 +67,8 @@ public function chargeRecurringPaymentAction(): void
return;
}

$this->handleNormalPayment();

try {
$this->charge($this->paymentDataStruct->getReturnUrl());
$orderNumber = $this->createRecurringOrder();
} catch (HeidelpayApiException $ex) {
$this->getApiLogger()->logException($ex->getMessage(), $ex);
Expand All @@ -73,6 +82,47 @@ public function chargeRecurringPaymentAction(): void
}
}

protected function recurringFinishedAction(): void
{
try {
parent::pay();

if (!$this->paymentType) {
$session = $this->container->get('session');
$paymentTypeId = $session->offsetGet('PaymentTypeId');
$this->paymentType = $this->heidelpayClient->fetchPaymentType($paymentTypeId);
}

if (!$this->paymentType->isRecurring()) {
$this->getApiLogger()->getPluginLogger()->warning('Recurring could not be activated for basket', [$this->paymentDataStruct->getBasket()->jsonSerialize()]);
$redirectUrl = $this->getHeidelpayErrorUrlFromSnippet('recurringError');
}

$bookingMode = $this->container->get('heidel_payment.services.config_reader')->get('credit_card_bookingmode');

if (in_array($bookingMode, [BookingMode::CHARGE, BookingMode::CHARGE_REGISTER])) {
$redirectUrl = $this->charge($this->paymentDataStruct->getReturnUrl());
} elseif (in_array($bookingMode, [BookingMode::AUTHORIZE, BookingMode::AUTHORIZE_REGISTER])) {
$redirectUrl = $this->authorize($this->paymentDataStruct->getReturnUrl());
}

$this->saveToDeviceVault($bookingMode);
} catch (HeidelpayApiException $ex) {
$this->getApiLogger()->logException('Error while creating CreditCard recurring payment', $ex);
$redirectUrl = $this->getHeidelpayErrorUrl($ex->getClientMessage());
} catch (RuntimeException $ex) {
$redirectUrl = $this->getHeidelpayErrorUrlFromSnippet('communicationError');
} finally {
if (!$redirectUrl) {
$this->getApiLogger()->getPluginLogger()->warning('CreditCard is not chargeable for basket', [$this->paymentDataStruct->getBasket()->jsonSerialize()]);

$redirectUrl = $this->getHeidelpayErrorUrlFromSnippet('communicationError');
}

$this->view->assign('redirectUrl', $redirectUrl);
}
}

private function handleNormalPayment(): void
{
$bookingMode = $this->container->get('heidel_payment.services.config_reader')->get('credit_card_bookingmode');
Expand Down
Loading

0 comments on commit d131d80

Please sign in to comment.