first commit
This commit is contained in:
449
class/payments/class.ThePay.php
Normal file
449
class/payments/class.ThePay.php
Normal 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 wasn’t completed
|
||||
// 2 – payment was successfully completed
|
||||
// 3 – payment wasn’t 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 customer’s 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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user