first commit

This commit is contained in:
2025-08-02 16:30:27 +02:00
commit 23646bfcee
14851 changed files with 1750626 additions and 0 deletions

View File

@@ -0,0 +1,449 @@
<?php
use KupShop\AdminBundle\Util\ActivityLog;
use KupShop\KupShopBundle\Config;
use KupShop\KupShopBundle\Context\CurrencyContext;
use KupShop\KupShopBundle\Context\LanguageContext;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\KupShopBundle\Util\Contexts;
class ThePay extends Payment
{
public static $name = 'ThePay platební brána';
public $template = 'payment.ThePay.tpl';
protected $templateCart = 'payment.ThePay.cart.tpl';
public $class = 'ThePay';
public $tp_id_payment;
/**
* @var TpDataApiPayment
*/
public $tp_payment;
public $method;
protected $pay_method = Payment::METHOD_ONLINE;
/** Get payment icon url and name.
* @return array|null
*/
public function getIcon()
{
$method = getVal($this->method, $this->getAvailableMethods());
if ($method) {
return [
'url' => "https://www.thepay.cz/gate/images/logos/public/209x127/{$method['id']}.png",
'name' => $method['name'],
];
}
return null;
}
public function storePaymentInfo()
{
$data = parent::storePaymentInfo();
$data['method'] = explode('-', getVal('payment_id'))[1];
return $data;
}
/* Payment creation */
public function getPayment()
{
$payment = new TpPayment($this->getMerchantConfig());
$payment->setValue(roundPrice($this->order->getRemainingPayment())->asFloat());
if (findModule(\Modules::CURRENCIES)) {
$payment->setCurrency($this->order->currency ?: 'CZK');
}
$payment->setCustomerEmail($this->order->invoice_email);
$payment->setDescription('Platba objednávky '.$this->order->order_no);
$payment->setReturnUrl(createScriptURL([
's' => 'payment',
'IDo' => $this->order->id,
'cf' => $this->order->getSecurityCode(),
'step' => 5,
'class' => $this->class,
'absolute' => true,
]));
$payment->setMerchantData($this->order->order_no);
if (empty($this->method)) {
$paymentData = $this->order->getData('payment_data');
if (!$paymentData) {
$paymentData = ['methodSelectionAllowed' => 1];
}
$this->loadPaymentInfo($paymentData);
}
$payment->setMethodId($this->method);
return $payment;
}
public function getPaymentUrl()
{
try {
return $this->getMerchantConfig()->gateUrl.'?'.$this->buildQuery();
} catch (Exception $e) {
return '/';
}
}
/* Payment status change */
public function getPaymentStatus()
{
$payment = TpDataApiHelper::getPayment($this->getMerchantConfig(), $this->tp_id_payment)->getPayment();
if ($this->orderId != $payment->getMerchantData() && $this->order->order_no != $payment->getMerchantData()) {
$this->error('Neplatné číslo objednávky: '.$this->orderId.' != '.$payment->getMerchantData());
}
$payment->setId($this->tp_id_payment);
$payment->setMerchantData($this->orderId);
$this->tp_payment = $payment;
return true;
}
public function checkReceivedSignature()
{
try {
$payment = new TpReturnedPayment($this->getMerchantConfig());
$payment->verifySignature();
} catch (TpMissingParameterException $e) {
return $this->error('Chybějící položky v odpověďi platební brány');
} catch (TpInvalidSignatureException $e) {
logError(__FILE__, __LINE__, 'ThePay: Neplatná odpověď platební brány! '.print_r([$payment, $this->getMerchantConfig()], true));
return $this->error('Neplatná odpověď platební brány');
}
$this->tp_id_payment = $payment->getPaymentId();
return $payment;
}
public function updatePaymentStatus()
{
// Ensure payment exists
if (!$this->getStatus($this->tp_id_payment)) {
$this->createPayment($this->tp_id_payment, $this->tp_payment->getValue(), []);
}
switch ($this->tp_payment->getState()) {
case TpReturnedPayment::STATUS_OK: // platba je zaplacena, můžeme si ji uložit jako zaplacenou a dále zpracovat (vyřídit objednávku atp.).
if (!$this->setStatus(Payment::STATUS_FINISHED, $this->tp_id_payment)) {
logError(__FILE__, __LINE__, 'ThePay::updatePaymentStatus: setStatus failed!');
}
return true;
case TpReturnedPayment::STATUS_CANCELED: // zákazník platbu stornoval
case TpReturnedPayment::STATUS_ERROR: // při zpracování platby došlo k chybě
if (!$this->setStatus(Payment::STATUS_STORNO, $this->tp_id_payment)) {
logError(__FILE__, __LINE__, 'ThePay::updatePaymentStatus: setStatus failed!');
}
break;
case TpReturnedPayment::STATUS_UNDERPAID: // zákazník zaplatil pouze část platby
// Modify paid value
$this->updateSQL('order_payments', ['price' => $this->tp_payment->getReceivedValue()], ['id' => $this->paymentId]);
if (!$this->setStatus(Payment::STATUS_FINISHED, $this->tp_id_payment)) {
logError(__FILE__, __LINE__, 'ThePay::updatePaymentStatus: setStatus failed!');
}
return true;
case TpReturnedPayment::STATUS_CARD_DEPOSIT: // částka byla zablokována na účtu zákazníka - pouze pro platbu kartou
case TpReturnedPayment::STATUS_WAITING: // platba proběhla úspěšně, ale čeká na potvrzení od poskytovatele platební metody. S vyřízením objednávky je nutno počkat na potvrzovací request se statusem TpReturnedPayment:: STATUS_OK, pokud nepřijde, platba nebyla dokončena.
if (!$this->setStatus(Payment::STATUS_PENDING, $this->tp_id_payment)) {
logError(__FILE__, __LINE__, 'ThePay::updatePaymentStatus: setStatus failed!');
}
break;
}
return false;
}
/* Payment steps */
public function processStep_1()
{
redirection($this->getPaymentUrl());
}
public function processStep_5()
{
$this->checkReceivedSignature();
try {
$this->getPaymentStatus();
$this->updatePaymentStatus();
} catch (\KupShop\KupShopBundle\Exception\RedirectException $e) {
throw $e;
} catch (Exception $e) {
return $this->error($e->getMessage());
}
switch ($this->status) {
case Payment::STATUS_FINISHED:
$this->success(translate('paymentSuccess', 'payment'));
break;
case Payment::STATUS_STORNO:
$this->step(-3, 'storno');
break;
default:
$this->success('Platba byla zaznamenána a čeká se na její potvrzení. O přijetí platby Vám zašleme email.');
}
}
/* Payment description */
public function getName()
{
if (is_null($this->method)) {
return parent::getName();
}
$methods = $this->getAvailableMethods();
return parent::getName().' - '.getVal($this->method, $methods, ['name' => 'Neznámý typ ThePay platby'])['name'];
}
public function getAvailableMethods()
{
$domainContextID = ServiceContainer::getService(\KupShop\KupShopBundle\Context\DomainContext::class)->getActiveId();
if (!($methods = getCache('thepay-methods-'.$domainContextID))) {
$methods = $this->fetchAvailableMethods();
setCache('thepay-methods-'.$domainContextID, $methods);
}
$currencyContext = ServiceContainer::getService(CurrencyContext::class);
if ($currencyContext->getActiveId() == 'EUR') {
if (isset($methods[21])) {
$method = $methods[21];
$methods = [];
$methods[21] = $method;
} elseif (isset($methods[31])) {
$method = $methods[31];
$methods = [];
$methods[31] = $method;
} else {
$methods = [];
}
}
return $methods;
}
/* Payment methods */
public function getMerchantConfig()
{
static $config = null;
if (!$config) {
$config = new TpMerchantConfig();
if (getVal('test', $this->config)) {
$defaults = [
'gateUrl' => 'https://www.thepay.cz/demo-gate/',
'webServicesWsdl' => 'https://www.thepay.cz/demo-gate/api/api-demo.wsdl',
'dataWebServicesWsdl' => 'https://www.thepay.cz/demo-gate/api/data-demo.wsdl',
];
unset($this->config['merchantId']);
unset($this->config['accountId']);
unset($this->config['password']);
unset($this->config['dataApiPassword']);
} else {
$defaults = [
'gateUrl' => 'https://www.thepay.cz/gate/',
'webServicesWsdl' => 'https://www.thepay.cz/gate/api/gate-api.wsdl',
'dataWebServicesWsdl' => 'https://www.thepay.cz/gate/api/data.wsdl',
];
}
$this->config = array_merge($defaults, $this->config);
foreach ($this->config as $key => $value) {
$config->$key = $value;
}
}
return $config;
}
public function fetchAvailableMethods()
{
try {
/** @var TpDataApiGetPaymentMethodsResponse $response */
$response = TpDataApiHelper::getPaymentMethods($this->getMerchantConfig());
} catch (TpSoapException $e) {
return [];
}
$overwriteMethodNames = $this->config['overwriteMethodNames'] ?? [
21 => translate('creditCardMethodTitle', 'payment'),
31 => translate('creditCardMethodTitle', 'payment'),
];
$methods = [];
foreach ($response->getMethods() as $method) {
$methods[$method->getId()] = [
'name' => $overwriteMethodNames[$method->getId()] ?? $method->getName(),
'id' => $method->getId(),
];
}
return $methods;
}
/* Utils */
public function buildQuery($args = [])
{
$payment = $this->getPayment();
if (isset($this->methodSelectionAllowed)) {
$args['methodSelectionAllowed'] = $this->methodSelectionAllowed;
}
$out = array_merge(
$payment->getArgs(), // Arguments of the payment
$args, // Optional helper arguments
['signature' => $payment->getSignature()] // Signature
);
$str = [];
foreach ($out as $key => $val) {
$str[] = rawurlencode($key).'='.rawurlencode($val);
}
return implode('&', $str);
}
public function requiresEET()
{
return true;
}
public function hasOnlinePayment()
{
return true;
}
public static function isEnabled($className)
{
$cfg = Config::get();
if (empty($cfg['Modules']['payments'][$className])) {
return false;
}
return true;
}
/**
* Check paid orders (and update orders.status_payed).
*/
public function checkPaidOrders()
{
if (getVal('test', $this->config)) {
return false;
}
$searchParams = new TpDataApiGetPaymentsSearchParams();
// select only paid payments
// 1 waiting for payment, payment wasnt completed
// 2 payment was successfully completed
// 3 payment wasnt completed, payment was canceled by customer
// 4 some error occurred
// 6 payment is partly paid
// 7 payment is waiting for confirmation from payment system
// 8 payment was cancelled and the money has been returned to customer
// 9 amount is blocked on customers account (in case of payments by card)
$searchParams->setState([2]);
// filter payments by accountId
$searchParams->setAccountId($this->getAccountIds());
// finishedOnFrom datetime - date when payment was paid
$dateTimeFrom = new DateTime();
$dateTimeFrom->modify('-1 day');
$searchParams->setFinishedOnFrom($dateTimeFrom);
// set ordering (default order is DESC, use setOrderHow('ASC') to ascending)
$ordering = new TpDataApiOrdering();
$ordering->setOrderBy('finishedOn');
$ordering->setOrderHow('ASC');
// set pagination
$pagination = new TpDataApiPaginationRequest();
// default 50
$pagination->setItemsOnPage(200);
$pagination->setPage(0);
$totalPages = 1;
do {
$response = TpDataApiHelper::getPayments($this->getMerchantConfig(), $searchParams, $pagination, $ordering);
foreach ($response->getPayments() as $payment) {
$order = returnSQLResult('SELECT id FROM orders WHERE order_no=:order_no', [
'order_no' => $payment->getMerchantData(),
]);
if ($order) {
$this->setOrder($order);
$this->tp_id_payment = $payment->getId();
$this->tp_payment = $payment;
try {
$this->updatePaymentStatus();
} catch (\KupShop\KupShopBundle\Exception\RedirectException $e) {
throw $e;
} catch (Exception $e) {
addActivityLog(ActivityLog::SEVERITY_ERROR, ActivityLog::TYPE_COMMUNICATION, 'ThePay error: '.$e->getMessage());
}
}
}
// use pagesCount received on first request only, otherwise it could (theoretically) loop forever
if ($response->getPagination()->getPage() <= 0) {
$totalPages = $response->getPagination()->getTotalPages();
}
// default = 0
$pagination->setPage($response->getPagination()->getPage() + 1);
} while ($response->getPagination()->getPage() + 1 < $totalPages);
return 0;
}
private function getAccountIds(): array
{
$ids = [$this->config['accountId']];
// load all account ids from local config
foreach ($this->config['domain_config'] ?? [] as $domain => $config) {
$ids[] = $config['accountId'];
}
// load all account ids from db config
$languageContext = Contexts::get(LanguageContext::class);
foreach ($languageContext->getSupported() as $language) {
$settings = Settings::getFromCache($language->getId());
$dbConfig = array_filter($settings->payments[$this->class] ?? []);
if ($dbConfig['accountId'] ?? false) {
$ids[] = $dbConfig['accountId'];
}
}
return array_filter(array_unique($ids));
}
}