Skip to content
Open
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
27 changes: 10 additions & 17 deletions core/components/minishop3/elements/snippets/ms3_get_order.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use MiniShop3\MiniShop3;
use MiniShop3\Model\msOrder;
use MiniShop3\Services\Payment\PaymentLinkResolver;
use MiniShop3\Model\msOrderProduct;
use MiniShop3\Model\msProduct;
use MiniShop3\Model\msProductData;
Expand Down Expand Up @@ -282,25 +283,17 @@
return $modx->lexicon('ms3_err_order_load');
}

if ($payment && $class = $payment->get('class')) {
$payStatuses = $modx->getOption('payStatus', $scriptProperties, '1');
$payStatuses = array_map('trim', explode(',', $payStatuses));
if ($payment) {
$payStatusCsv = (string) $modx->getOption('payStatus', $scriptProperties, '1');
$eligibleStatusIds = PaymentLinkResolver::parseEligibleStatusIds($payStatusCsv);

if (in_array($msOrder->get('status_id'), $payStatuses)) {
try {
if (class_exists($class)) {
/** @var \MiniShop3\Controllers\Payment\Payment $paymentHandler */
$paymentHandler = new $class($ms3, []);
/** @var PaymentLinkResolver $paymentLinkResolver */
$paymentLinkResolver = $modx->services->get('ms3_payment_link_resolver');
$orderStatus = $msOrder->getOne('Status');
$link = $paymentLinkResolver->resolveForOrder($msOrder, $orderStatus, $eligibleStatusIds);

$link = $paymentHandler->getPaymentLink($msOrder);

if ($link) {
$pls['payment_link'] = $link;
}
}
} catch (\Exception $e) {
$modx->log(modX::LOG_LEVEL_WARN, '[msGetOrder] Error getting payment link: ' . $e->getMessage());
}
if ($link) {
$pls['payment_link'] = $link;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use MiniShop3\Model\msOrderStatus;
use MiniShop3\Model\msNotificationConfig;
use MiniShop3\Services\Notification\NotificationConfigService;
use MiniShop3\Services\Payment\PaymentLinkResolver;
use MiniShop3\Notifications\Notification;
use MiniShop3\Notifications\Messages\EmailMessage;
use MiniShop3\Notifications\Messages\TelegramMessage;
Expand All @@ -24,6 +25,8 @@ class StatusChangedNotification extends Notification
protected ?msOrderStatus $oldStatus;
protected msOrderStatus $newStatus;
protected ?NotificationConfigService $configService = null;
private ?string $paymentLink = null;
private bool $paymentLinkResolved = false;

public function __construct(
modX $modx,
Expand Down Expand Up @@ -267,13 +270,30 @@ public function getPlaceholders(): array
$pls['old_status_name'] = $this->modx->lexicon($oldStatusKey) ?: $oldStatusKey;
}

$paymentLink = $this->resolvePaymentLink();
if ($paymentLink !== null) {
$pls['payment_link'] = $paymentLink;
}

// Site info
$pls['site_name'] = $this->modx->getOption('site_name');
$pls['site_url'] = $this->modx->getOption('site_url');

return $pls;
}

private function resolvePaymentLink(): ?string
{
if (!$this->paymentLinkResolved) {
/** @var PaymentLinkResolver $resolver */
$resolver = $this->modx->services->get('ms3_payment_link_resolver');
$this->paymentLink = $resolver->resolveForOrder($this->order, $this->newStatus);
$this->paymentLinkResolved = true;
}

return $this->paymentLink;
}

/**
* Process subject line with placeholders
*
Expand Down
4 changes: 4 additions & 0 deletions core/components/minishop3/src/ServiceRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ class ServiceRegistry
'class' => \MiniShop3\Services\Payment\PaymentService::class,
'interface' => null,
],
'ms3_payment_link_resolver' => [
'class' => \MiniShop3\Services\Payment\PaymentLinkResolver::class,
'interface' => null,
],
'ms3_order_service' => [
'class' => \MiniShop3\Services\Order\OrderService::class,
'interface' => null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

use MiniShop3\MiniShop3;
use MiniShop3\Model\msOrder;
use MiniShop3\Model\msPayment;
use MiniShop3\Services\Payment\PaymentService;
use MiniShop3\Model\msOrderAddress;
use MiniShop3\Model\msOrderStatus as msOrderStatusModel;
use MiniShop3\Model\msCustomer;
Expand Down Expand Up @@ -397,28 +395,4 @@ protected function getLang(msOrder $msOrder): string

return $lang;
}

/**
* Get payment link for order
*/
protected function getPaymentLink(mixed $msPayment, msOrder $msOrder): string
{
if (!$msPayment instanceof msPayment) {
return '';
}

$class = (string) $msPayment->get('class');
if ($class === '') {
return '';
}

/** @var PaymentService $paymentService */
$paymentService = $this->modx->services->get('ms3_payment_service');
$controller = $paymentService->loadPaymentHandler($msPayment);
if ($controller === null) {
return '';
}

return $controller->getPaymentLink($msOrder) ?? '';
}
}
147 changes: 147 additions & 0 deletions core/components/minishop3/src/Services/Payment/PaymentLinkResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
<?php

namespace MiniShop3\Services\Payment;

use MiniShop3\Model\msOrder;
use MiniShop3\Model\msOrderStatus;
use MiniShop3\Model\msPayment;

/**
* Resolves online payment URLs for order emails and storefront views.
*
* Eligible status IDs:
* - notifications: system setting {@see ms3_payment_link_statuses} (CSV), fallback {@see ms3_status_new}
* - ms3_get_order snippet: property `payStatus` (CSV), same parsing via {@see parseEligibleStatusIds()}
*/
final class PaymentLinkResolver
{
/**
* @param modX|object $modx MODX instance (plain object in smoke tests)
*/
public function __construct(
private object $modx,
private ?object $paymentServiceOverride = null,
) {
}

/**
* @return int[]
*/
public static function parseEligibleStatusIds(string $csv): array
{
if ($csv === '') {
return [];
}

$ids = [];
foreach (explode(',', $csv) as $part) {
$part = trim($part);
if ($part === '' || !ctype_digit($part)) {
continue;
}
$ids[] = (int) $part;
}

return array_values(array_unique($ids));
}

public static function isStatusEligibleForPaymentLink(
int $statusId,
bool $isFinal,
int $paidStatusId,
array $eligibleStatusIds
): bool {
if ($isFinal || $statusId === $paidStatusId) {
return false;
}

return in_array($statusId, $eligibleStatusIds, true);
}

public static function normalizePaymentLink(?string $link): ?string
{
return $link !== null && $link !== '' ? (string) $link : null;
}

/**
* @return int[]
*/
public function getEligibleStatusIds(): array
{
$csv = (string) $this->modx->getOption('ms3_payment_link_statuses', null, '');
$ids = self::parseEligibleStatusIds($csv);
if ($ids !== []) {
return $ids;
}

return [(int) $this->modx->getOption('ms3_status_new', null, 2)];
}

public function shouldResolveForStatus(?msOrderStatus $status): bool
{
if ($status === null) {
return false;
}

return self::isStatusEligibleForPaymentLink(
(int) $status->get('id'),
(bool) $status->get('final'),
(int) $this->modx->getOption('ms3_status_paid', null, 3),
$this->getEligibleStatusIds()
);
}

/**
* @param int[]|null $eligibleStatusIds Override eligible statuses (e.g. snippet payStatus property)
*/
public function resolveForOrder(
msOrder $order,
?msOrderStatus $status = null,
?array $eligibleStatusIds = null
): ?string {
$eligibleStatusIds ??= $this->getEligibleStatusIds();
$paidStatusId = (int) $this->modx->getOption('ms3_status_paid', null, 3);

if ($status !== null) {
if (!self::isStatusEligibleForPaymentLink(
(int) $status->get('id'),
(bool) $status->get('final'),
$paidStatusId,
$eligibleStatusIds
)) {
return null;
}
} elseif (!in_array((int) $order->get('status_id'), $eligibleStatusIds, true)) {
return null;
}

$payment = $order->getOne('Payment');
if (!$payment) {
return null;
}

$controller = $this->loadPaymentHandler($payment);
if ($controller === null || !method_exists($controller, 'getPaymentLink')) {
return null;
}

return self::normalizePaymentLink($controller->getPaymentLink($order));
}

private function loadPaymentHandler(object $payment): ?object
{
if ($this->paymentServiceOverride !== null) {
return $this->paymentServiceOverride->loadPaymentHandler($payment);
}

return $this->paymentService()->loadPaymentHandler($payment);
}

private function paymentService(): PaymentService
{
/** @var PaymentService $paymentService */
$paymentService = $this->modx->services->get('ms3_payment_service');

return $paymentService;
}
}
Loading