269 lines
10 KiB
PHP
269 lines
10 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace KupShop\BankAutoPaymentBundle\PaymentSources;
|
|
|
|
use KupShop\AdminBundle\Util\ActivityLog;
|
|
use KupShop\BankAutoPaymentBundle\Entity\BankTransaction;
|
|
use KupShop\I18nBundle\Util\PriceConverter;
|
|
use KupShop\KupShopBundle\Context\CurrencyContext;
|
|
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
|
|
use KupShop\KupShopBundle\Util\Contexts;
|
|
use KupShop\KupShopBundle\Util\Database\QueryHint;
|
|
use KupShop\KupShopBundle\Util\Logging\SentryLogger;
|
|
use KupShop\KupShopBundle\Util\StringUtil;
|
|
use KupShop\KupShopBundle\Util\System\CurlUtil;
|
|
use Query\Operator;
|
|
use Symfony\Contracts\Service\Attribute\Required;
|
|
|
|
abstract class AbstractPaymentSource
|
|
{
|
|
protected static string $name;
|
|
protected static string $logTag;
|
|
|
|
protected CurlUtil $curlUtil;
|
|
protected SentryLogger $sentryLogger;
|
|
protected array $config = [];
|
|
protected array $allowedPaymentTypes = [];
|
|
|
|
abstract public function checkPayments(?\DateTime $forceDate = null): bool;
|
|
|
|
abstract protected function loadConfig();
|
|
|
|
public static function getName(): string
|
|
{
|
|
return static::$name;
|
|
}
|
|
|
|
public static function getLogTag(): string
|
|
{
|
|
return static::$logTag;
|
|
}
|
|
|
|
protected function checkPayment(BankTransaction $transaction): void
|
|
{
|
|
$type = $transaction->getType() ?? false;
|
|
$price = $transaction->getPrice() ?? false;
|
|
$transactionCurrency = $transaction->getCurrency() ?? false;
|
|
$transactionId = $transaction->getId() ?? false;
|
|
$transactionComment = $transaction->getComment() ?? false;
|
|
$vs = $transaction->getVariableSymbol() ?? false;
|
|
|
|
$transactionAccount = $transaction->getAccount();
|
|
|
|
$orderIdentifiers = $transaction->getOrderIdentifier();
|
|
|
|
$transactionData = [
|
|
'type' => $type,
|
|
'orderIdentifiers' => $orderIdentifiers,
|
|
'price' => $price,
|
|
'transactionCurrency' => $transactionCurrency,
|
|
'transactionAccount' => $transactionAccount,
|
|
];
|
|
|
|
// TMP: Log all payments - #15184
|
|
ServiceContainer::getService('logger')->notice('checkPayment', [...$transactionData, 'transactionId' => $transactionId]);
|
|
|
|
if (empty($orderIdentifiers) || !$type || !$transactionCurrency || $price === false
|
|
|| (!empty($this->allowedPaymentTypes) && !in_array($type, $this->allowedPaymentTypes))) {
|
|
if ($price && ($price > 0)) {
|
|
addActivityLog(ActivityLog::SEVERITY_WARNING,
|
|
ActivityLog::TYPE_SYNC,
|
|
sprintf($this::getName().': '.translate('orderNotFound', 'autoPayment', false, true), $transactionId, $vs),
|
|
$transactionData,
|
|
[$this::getLogTag()]);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (!$orderID = $this->findPaymentOrder($transactionId, $orderIdentifiers, $transactionData)) {
|
|
return;
|
|
}
|
|
|
|
$order = new \Order();
|
|
$order->createFromDB($orderID);
|
|
|
|
if ($order->getCurrency() != $transactionCurrency) {
|
|
$currencyContext = Contexts::get(CurrencyContext::class);
|
|
if (array_key_exists($transactionCurrency, $currencyContext->getSupported())) {
|
|
$priceConverter = ServiceContainer::getService(PriceConverter::class);
|
|
$price = $priceConverter->convert($transactionCurrency, $order->getCurrency(), $price)->asFloat();
|
|
} else {
|
|
$data = ['transactionCurrency' => $transactionCurrency];
|
|
addActivityLog(ActivityLog::SEVERITY_ERROR,
|
|
ActivityLog::TYPE_SYNC,
|
|
sprintf($this::getName().': '.translate('paymentInvalidCurrency', 'autoPayment', false, true), $order->order_no, $transactionCurrency),
|
|
$data + ActivityLog::addObjectData([$order->id => $order->order_no], 'orders'),
|
|
[$this::getLogTag()]);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// "číslo protiúčtu"
|
|
$accountNo = $transaction->getAccountNumber() ?? '';
|
|
|
|
// "kód banky"
|
|
$bankCode = $transaction->getBankCode();
|
|
$accountNo .= !empty($bankCode) ? "/{$bankCode}" : '';
|
|
|
|
$paymentId = $this->createPayment($orderID, $price, $transactionId, $accountNo);
|
|
|
|
if ($paymentId) {
|
|
// v updatePayments() se nastavuje date_handle a následně se neodeslal email sendPaymentReceipt
|
|
$dateHandleBeforeUpdate = $order->date_handle;
|
|
$order->updatePayments();
|
|
|
|
$transactionComment = StringUtil::unicode_trim($transactionComment);
|
|
$logComment = 'Modul '.$this::getName().' - '.sprintf(translate('orderPaidComment', 'autoPayment', false, true), $transactionId)
|
|
.((empty($transactionComment)) ? '' : ' '.sprintf(translate('paymentComment', 'autoPayment', false, true),
|
|
$transactionComment));
|
|
|
|
if ($order->status_payed == 1 && is_numeric($this->config['newState']) && empty($dateHandleBeforeUpdate)) {
|
|
$order->changeStatus($this->config['newState'], $logComment, false);
|
|
} else {
|
|
$order->logHistory($logComment);
|
|
}
|
|
|
|
$activityMessage = 'orderPaidActivityFail';
|
|
|
|
if ($order->status_payed == 1) {
|
|
$doNotSendEmail = $this->config['doNotSendEmail'] ?? false;
|
|
if (empty($dateHandleBeforeUpdate) && empty($doNotSendEmail)) {
|
|
$order->sendPaymentReceipt($paymentId);
|
|
}
|
|
$activityMessage = 'orderPaidActivitySuccess';
|
|
}
|
|
|
|
$data = ['paymentId' => $paymentId];
|
|
addActivityLog(($activityMessage == 'orderPaidActivityFail') ? ActivityLog::SEVERITY_ERROR : ActivityLog::SEVERITY_SUCCESS,
|
|
ActivityLog::TYPE_SYNC,
|
|
$this::getName().': '.sprintf(translate($activityMessage, 'autoPayment', false, true), $order->order_no),
|
|
$data + ActivityLog::addObjectData([$order->id => $order->order_no], 'orders'),
|
|
[$this::getLogTag()]);
|
|
}
|
|
}
|
|
|
|
public function findPaymentOrder($transactionId, $orderIdentifiers, array $transactionData = [])
|
|
{
|
|
$qb = $this->getOrderQuery();
|
|
$qb->andWhere(Operator::inStringArray($orderIdentifiers, 'order_no'));
|
|
$orderID = $qb->execute()->fetchColumn();
|
|
|
|
if (!$orderID) {
|
|
$qb = $this->getOrderQuery();
|
|
$qb->andWhere(Operator::inStringArray($orderIdentifiers, "TRIM(LEADING '0' FROM order_no)"));
|
|
$orderID = $qb->execute()->fetchColumn();
|
|
}
|
|
|
|
if (!$orderID) {
|
|
$allInOne = join('', $orderIdentifiers);
|
|
preg_match_all('/(\d{5,})/', $allInOne, $agreements);
|
|
$qb = $this->getOrderQuery();
|
|
$qb->andWhere(Operator::inStringArray($agreements[0], 'order_no'));
|
|
$orderID = $qb->execute()->fetchColumn();
|
|
}
|
|
|
|
if (!$orderID) {
|
|
$data = $transactionData ?: ['orderIdentifiers' => $orderIdentifiers];
|
|
addActivityLog(ActivityLog::SEVERITY_ERROR,
|
|
ActivityLog::TYPE_SYNC,
|
|
sprintf($this::getName().': '.translate('orderNotFound', 'autoPayment', false, true), $transactionId, json_encode($orderIdentifiers)),
|
|
$data,
|
|
[$this::getLogTag()]);
|
|
|
|
return null;
|
|
}
|
|
|
|
return $orderID;
|
|
}
|
|
|
|
public function getOrderQuery()
|
|
{
|
|
return sqlQueryBuilder()->select('id')
|
|
->from('orders')
|
|
->andWhere(
|
|
Operator::orX(
|
|
Operator::inIntArray(getStatuses('nothandled'), 'status'),
|
|
'TIMESTAMPDIFF(MONTH, date_handle, NOW()) <= 6'
|
|
)
|
|
)
|
|
->orderBy('id', 'DESC')
|
|
->sendToMaster();
|
|
}
|
|
|
|
protected function createPayment($orderId, $price, $transactionId, string $accountNo = ''): false|int|string
|
|
{
|
|
$fields = [
|
|
'id_order' => $orderId,
|
|
'price' => $price,
|
|
'note' => "Platba č. {$transactionId} modulu ".$this::getName().' '.(!empty($accountNo) ? " č.ú.: {$accountNo}" : ''),
|
|
'date' => (new \DateTime())->format('Y-m-d H:i:s'),
|
|
'payment_data' => json_encode(['transactionId' => $transactionId]),
|
|
'method' => \Payment::METHOD_TRANSFER,
|
|
];
|
|
|
|
$paymentExists = sqlQueryBuilder()->select('id')->from('order_payments')
|
|
->where(Operator::equals(['id_order' => $fields['id_order']]))
|
|
->andWhere(
|
|
Operator::orX(
|
|
Operator::like(['payment_data' => "%\"transactionId\":{$transactionId}%"]),
|
|
Operator::like(['payment_data' => "%\"transactionId\":\"{$transactionId}\"%"])
|
|
))
|
|
->sendToMaster()
|
|
->execute()->fetch();
|
|
|
|
if (!$paymentExists) {
|
|
return QueryHint::withRouteToMaster(function () use ($fields) {
|
|
sqlQueryBuilder()->insert('order_payments')->directValues($fields)->execute();
|
|
|
|
return sqlInsertId();
|
|
});
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function runCheckPaymentsCron(): bool
|
|
{
|
|
if (!$this->isEnabled() || !($this->config['enableCron'] ?? false)) {
|
|
return true;
|
|
}
|
|
|
|
return $this->checkPayments();
|
|
}
|
|
|
|
public function runCheckPayments(?\DateTime $date = null)
|
|
{
|
|
if (!$this->isEnabled()) {
|
|
return true;
|
|
}
|
|
|
|
addActivityLog(ActivityLog::SEVERITY_NOTICE,
|
|
ActivityLog::TYPE_SYNC,
|
|
'Kontrola plateb '.$this->getName().' API (manuálně)',
|
|
tags: [$this::getLogTag()]);
|
|
|
|
return $this->checkPayments(forceDate: $date);
|
|
}
|
|
|
|
protected function isEnabled(): bool
|
|
{
|
|
return $this->loadConfig() && !($this->config['test'] ?? false);
|
|
}
|
|
|
|
#[Required]
|
|
public function setCurlUtil(CurlUtil $curlUtil): void
|
|
{
|
|
$this->curlUtil = $curlUtil;
|
|
}
|
|
|
|
#[Required]
|
|
public function setSentryLogger(SentryLogger $sentryLogger): void
|
|
{
|
|
$this->sentryLogger = $sentryLogger;
|
|
}
|
|
}
|