Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ private function getStatusMappings(): array
$statusKeys = [
Config::MOLLIE_STATUS_AWAITING => $this->module->l('Awaiting', self::FILE_NAME),
Config::MOLLIE_STATUS_OPEN => $this->module->l('Open', self::FILE_NAME),
Config::MOLLIE_AUTHORIZABLE_PAYMENT_STATUS_AUTHORIZED => $this->module->l('Authorized', self::FILE_NAME),
Config::MOLLIE_STATUS_PAID => $this->module->l('Paid', self::FILE_NAME),
Config::MOLLIE_STATUS_COMPLETED => $this->module->l('Completed', self::FILE_NAME),
Config::MOLLIE_STATUS_CANCELED => $this->module->l('Canceled', self::FILE_NAME),
Expand Down
2 changes: 1 addition & 1 deletion controllers/admin/AdminMollieAjaxController.php
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ private function processCapture(): void
$captureService = $this->module->getService(CaptureService::class);

$amount = $captureAmount ? (float) $captureAmount : $order->total_paid;
$status = $captureService->handleCapture($transactionId, $amount);
$status = $captureService->handleCapture($transactionId, $amount, $orderId);

$this->ajaxRender(json_encode($status));
} catch (\Throwable $e) {
Expand Down
33 changes: 33 additions & 0 deletions controllers/admin/AdminMolliePaymentMethodsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,16 @@ public function init(): void
'apiNotConfiguredMessage' => $this->module->l('Please configure your Mollie API keys in the API Configuration tab before managing payment methods.', self::FILE_NAME),
'infoBannerText' => $this->module->l('Here you can see all of the %s payment options. To include new payment methods go to', self::FILE_NAME),
'mollieDashboard' => $this->module->l('Mollie dashboard', self::FILE_NAME),

'captureMode' => $this->module->l('Capture mode', self::FILE_NAME),
'automatic' => $this->module->l('Automatic', self::FILE_NAME),
'manual' => $this->module->l('Manual', self::FILE_NAME),
'captureModeAutomatic' => $this->module->l('Payment is captured immediately at checkout.', self::FILE_NAME),
'captureModeManual' => $this->module->l('Payment stays authorized until you capture from the order page or via auto-capture below.', self::FILE_NAME),
'autoCaptureOnStatus' => $this->module->l('Auto-capture on status change', self::FILE_NAME),
'autoCaptureStatuses' => $this->module->l('Trigger on statuses', self::FILE_NAME),
'autoCaptureInfo' => $this->module->l('When the order reaches one of these statuses, the authorized payment will be captured automatically. You can always capture manually from the order page.', self::FILE_NAME),
'selectStatuses' => $this->module->l('Select statuses', self::FILE_NAME),
],
]);

Expand All @@ -262,6 +272,8 @@ public function init(): void
'customerGroups' => $this->getCustomerGroups(),
'onlyOrderMethods' => Config::ORDER_API_ONLY_METHODS,
'onlyPaymentsMethods' => Config::PAYMENT_API_ONLY_METHODS,
'orderStatuses' => $this->getOrderStatuses(),
'manualCaptureEligibleMethods' => Config::MOLLIE_MANUAL_CAPTURE_ELIGIBLE_METHODS,
],
]);

Expand Down Expand Up @@ -423,6 +435,12 @@ private function ajaxGetPaymentMethods(): void
'directCart' => (bool) ($this->configuration->get(Config::MOLLIE_APPLE_PAY_DIRECT_CART) ?: 0),
'buttonStyle' => (int) ($this->configuration->get(Config::MOLLIE_APPLE_PAY_DIRECT_STYLE) ?: 0),
] : null,
'captureMode' => !empty($methodObj->is_manual_capture) ? 'manual' : 'automatic',
'isManualCaptureEligible' => in_array($methodId, Config::MOLLIE_MANUAL_CAPTURE_ELIGIBLE_METHODS),
'autoCapture' => in_array($methodId, Config::MOLLIE_MANUAL_CAPTURE_ELIGIBLE_METHODS) ? [
'enabled' => (bool) $this->configuration->get(Config::MOLLIE_METHOD_AUTO_CAPTURE_ENABLED . $methodId),
'statuses' => json_decode($this->configuration->get(Config::MOLLIE_METHOD_AUTO_CAPTURE_STATUSES . $methodId) ?: '[]', true),
] : null,
],
];
} catch (Exception $e) {
Expand Down Expand Up @@ -759,6 +777,21 @@ private function getTaxRulesGroups(): array
return $taxRulesGroups;
}

private function getOrderStatuses(): array
{
$orderStatuses = OrderState::getOrderStates($this->context->language->id);
$result = [];

foreach ($orderStatuses as $status) {
$result[] = [
'id' => (string) $status['id_order_state'],
'name' => $status['name'],
];
}

return $result;
}

private function getCustomerGroups(): array
{
$customerGroups = [];
Expand Down
58 changes: 57 additions & 1 deletion mollie.php
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,8 @@ public function hookDisplayAdminOrder($params)

$order = new Order($params['id_order']);

if (!$order->hasBeenPaid()) {
$isAuthorized = isset($transaction['bank_status']) && $transaction['bank_status'] === 'authorized';
if (!$order->hasBeenPaid() && !$isAuthorized) {
return false;
}

Expand All @@ -628,6 +629,48 @@ public function hookDisplayAdminOrder($params)
$isShipped = $shipService->isShipped($mollieTransactionId);
$isCanceled = $cancelService->isCanceled($mollieTransactionId);

if ($mollieApiType === 'payments' && $products) {
$hasDiscount = false;
foreach ($products as $line) {
if ((isset($line->description) && $line->description === 'Discount')
|| (float) $line->totalAmount->value < 0
) {
$hasDiscount = true;
break;
}
}

$captureAmounts = $captureService->getCapturedAmounts($mollieTransactionId);
$refundAmounts = $refundService->getRefundedAmounts($mollieTransactionId);
foreach ($products as $line) {
$line->mollieLineCaptured = false;
$line->mollieLineRefunded = false;
$lineTotal = (float) $line->totalAmount->value;
foreach ($captureAmounts as $capIdx => $capAmount) {
if (abs($capAmount - $lineTotal) < 0.005) {
$line->mollieLineCaptured = true;
unset($captureAmounts[$capIdx]);
break;
}
}
foreach ($refundAmounts as $refIdx => $refAmount) {
if (abs($refAmount - $lineTotal) < 0.005) {
$line->mollieLineRefunded = true;
unset($refundAmounts[$refIdx]);
break;
}
}
$line->mollieCanCapture = !$isCaptured
&& !$hasDiscount
&& !$line->mollieLineCaptured
&& $lineTotal <= ((float) $capturableAmount + 0.005);
$line->mollieCanRefund = !$hasDiscount
&& $line->mollieLineCaptured
&& !$line->mollieLineRefunded
&& $lineTotal <= ((float) $refundableAmount + 0.005);
}
}

$lineActions = [];
if ($mollieApiType === 'orders' && $products) {
foreach ($products as $line) {
Expand Down Expand Up @@ -875,6 +918,19 @@ public function hookActionOrderStatusUpdate(array $params): void

return;
}

try {
/** @var \Mollie\Service\AutoCaptureService $autoCaptureService */
$autoCaptureService = $this->getService(\Mollie\Service\AutoCaptureService::class);
$autoCaptureService->handleAutoCaptureOnStatusChange(
(int) $order->id,
(int) $orderStatus->id
);
} catch (\Throwable $exception) {
$logger->error(sprintf('%s - Auto-capture failed', self::FILE_NAME), [
'exceptions' => ExceptionUtility::getExceptions($exception),
]);
}
}

/**
Expand Down
8 changes: 8 additions & 0 deletions src/Config/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,14 @@ class Config

const PAYMENT_API_ONLY_METHODS = [];

const MOLLIE_MANUAL_CAPTURE_ELIGIBLE_METHODS = [
'creditcard',
'klarna',
];

const MOLLIE_METHOD_AUTO_CAPTURE_ENABLED = 'MOLLIE_METHOD_AUTO_CAPTURE_ENABLED_';
const MOLLIE_METHOD_AUTO_CAPTURE_STATUSES = 'MOLLIE_METHOD_AUTO_CAPTURE_STATUSES_';

const ROUTE_RESEND_SECOND_CHANCE_PAYMENT_MESSAGE = 'mollie_module_admin_resend_payment_message';

const PAYMENT_FEE_SKU = 'payment-fee-sku';
Expand Down
7 changes: 7 additions & 0 deletions src/Entity/MolPaymentMethod.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ class MolPaymentMethod extends ObjectModel
* @var int
*/
public $id_shop;

/**
* @var bool
*/
public $is_manual_capture;

/**
* @var array
*/
Expand Down Expand Up @@ -140,6 +146,7 @@ class MolPaymentMethod extends ObjectModel
'live_environment' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
'position' => ['type' => self::TYPE_INT, 'validate' => 'isInt'],
'id_shop' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'],
'is_manual_capture' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
],
];

Expand Down
9 changes: 6 additions & 3 deletions src/Handler/Order/OrderCreationHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,12 @@ public function __construct(
*/
public function createOrder($apiPayment, int $cartId, bool $isAuthorizablePayment = false): int
{
$orderStatus = $isAuthorizablePayment ?
(int) Config::getStatuses()[PaymentStatus::STATUS_AUTHORIZED] :
(int) Config::getStatuses()[PaymentStatus::STATUS_PAID];
if ($isAuthorizablePayment) {
$orderStatus = (int) \Configuration::get(Config::MOLLIE_AUTHORIZABLE_PAYMENT_STATUS_AUTHORIZED)
?: (int) Config::getStatuses()[PaymentStatus::STATUS_AUTHORIZED];
} else {
$orderStatus = (int) Config::getStatuses()[PaymentStatus::STATUS_PAID];
}

$cart = new Cart($cartId);

Expand Down
29 changes: 29 additions & 0 deletions src/Handler/PaymentMethod/PaymentMethodSettingsHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ public function handlePaymentMethodSave(string $methodId, array $settings, int $
if ($methodId === Config::MOLLIE_VOUCHER_METHOD_ID && isset($settings['voucherCategory'])) {
$this->handleVoucherSettings($settings);
}

$this->handleCaptureSettings($methodId, $settings);
}

/**
Expand Down Expand Up @@ -178,6 +180,10 @@ private function handleBasicSettings(

$paymentMethod->live_environment = $environment ? true : false;
$paymentMethod->id_shop = $shopId;

if (in_array($methodId, Config::MOLLIE_MANUAL_CAPTURE_ELIGIBLE_METHODS)) {
$paymentMethod->is_manual_capture = ($settings['captureMode'] ?? 'automatic') === 'manual';
}
}

/**
Expand Down Expand Up @@ -499,4 +505,27 @@ private function fetchMethodFromApi(string $methodId): ?array
return null;
}
}

private function handleCaptureSettings(string $methodId, array $settings): void
{
if (!in_array($methodId, Config::MOLLIE_MANUAL_CAPTURE_ELIGIBLE_METHODS)) {
return;
}

if (!isset($settings['autoCapture'])) {
return;
}

$autoCapture = $settings['autoCapture'];

$this->configuration->updateValue(
Config::MOLLIE_METHOD_AUTO_CAPTURE_ENABLED . $methodId,
(int) ($autoCapture['enabled'] ?? false)
);

$this->configuration->updateValue(
Config::MOLLIE_METHOD_AUTO_CAPTURE_STATUSES . $methodId,
json_encode($autoCapture['statuses'] ?? [])
);
}
}
3 changes: 2 additions & 1 deletion src/Install/DatabaseTableInstaller.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ private function getCommands()
`max_amount` decimal(20,6),
`live_environment` TINYINT(1),
`position` INT(10),
`id_shop` INT(64) DEFAULT 1
`id_shop` INT(64) DEFAULT 1,
`is_manual_capture` TINYINT(1) DEFAULT 0
) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=utf8;';

$sql[] = 'CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'mol_order_payment_fee` (
Expand Down
13 changes: 13 additions & 0 deletions src/Repository/PaymentMethodRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,19 @@ public function getPaymentMethodIdByMethodId($paymentMethodId, $environment, $sh
return Db::getInstance()->getValue($sql);
}

public function isManualCapture(string $methodId, int $environment, ?int $shopId = null): bool
{
if (!$shopId) {
$shopId = Context::getContext()->shop->id;
}

$sql = 'SELECT is_manual_capture FROM `' . _DB_PREFIX_ . 'mol_payment_method`
WHERE id_method = "' . pSQL($methodId) . '" AND live_environment = "' . (int) $environment . '"
AND id_shop = ' . (int) $shopId;

return (bool) Db::getInstance()->getValue($sql);
}

/**
* @todo create const for table keys
*
Expand Down
2 changes: 2 additions & 0 deletions src/Repository/PaymentMethodRepositoryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,6 @@ public function updatePaymentReason($transactionId, $reason);
public function getCustomerGroupsForPaymentMethod(int $paymentMethodId): array;

public function getLatestPaymentByCustomerAndMethod($customerId, $method, array $statuses);

public function isManualCapture(string $methodId, int $environment, ?int $shopId = null): bool;
}
Loading
Loading