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,399 @@
<?php
use Adyen\Webhook\Exception\AuthenticationException;
use Adyen\Webhook\Receiver\HmacSignature;
use Adyen\Webhook\Receiver\NotificationReceiver;
use KupShop\AdminBundle\Util\ActivityLog;
use KupShop\BankAutoPaymentBundle\BankAutoPaymentBundle;
use KupShop\KupShopBundle\Util\Contexts;
use KupShop\OrderingBundle\Exception\PaymentException;
use KupShop\OrderingBundle\Util\Order\PaymentOrderSubMethodTrait;
/**
* "adyen/php-api-library": "^15.2.0",
* "adyen/php-webhook-module": "^0.8.0".
*/
class Adyen extends Payment
{
use PaymentOrderSubMethodTrait;
public static $name = 'Adyen platební brána';
public static bool $canAutoReturn = true;
protected $templateCart = 'payment.Adyen.cart.tpl';
protected $templateOrderView = 'payment.Adyen.orderView.tpl';
public $class = 'Adyen';
protected $pay_method = Payment::METHOD_ONLINE;
protected $method;
public function createAdyenSession()
{
$session = \KupShop\KupShopBundle\Util\Compat\ServiceContainer::getService('session');
$adyenSession = $session->get('adyen_'.$this->orderId);
// Aby když mě to přesměruje na detail a nestihl přijit webhook, naukázal se mi znova platební widget
if ($adyenSession['paymentResult'] == \Adyen\Model\Checkout\SessionResultResponse::STATUS_COMPLETED) {
return false;
}
// Když chci změnit metodu, tak tohle přeskočit
if (!getVal('rpm')) {
// Pokud mám sessionId a není starší pěti minut, použiji ho.
if ($adyenSession['sessionId'] && $adyenSession['expired'] > time() - (5 * 60)) {
return true;
}
}
$service = new \Adyen\Service\Checkout\PaymentsApi($this->getApiClient());
$createCheckoutSessionRequest = new \Adyen\Model\Checkout\CreateCheckoutSessionRequest();
$createCheckoutSessionRequest->setCountryCode(Contexts::get(\KupShop\KupShopBundle\Context\CountryContext::class)->getActiveId())
->setMerchantAccount($this->config['merchantAccount'])
->setReturnUrl($this->getGenericPaymentUrl(5))
->setReference($this->order->order_no.'_'.time())
->setShopperEmail($this->order->getUserEmail())
->setShopperLocale($this->getLocale());
$amount = new \Adyen\Model\Checkout\Amount();
$amount->setValue($this->order->getRemainingPayment(true)->mul(DecimalConstants::hundred())->asInteger())
->setCurrency($this->order->getCurrency());
$createCheckoutSessionRequest->setAmount($amount);
$this->kibanaLogger->notice('[Adyen] Create session', [
'order' => $this->order->order_no,
'amount' => $amount->getValue(),
]);
if ($this->method && !getVal('rpm')) {
$adyenMethods = [$this->method];
} else {
$methods = $this->getAvailableMethods($amount);
$adyenMethods = array_values(array_map(fn ($val) => $val['type'], $methods));
}
$createCheckoutSessionRequest->setAllowedPaymentMethods($adyenMethods);
$items = [];
foreach ($this->order->fetchItems() as $item) {
$items[] = (new \Adyen\Model\Checkout\LineItem())
->setQuantity($item['pieces'])
->setTaxPercentage($item['vat'])
->setDescription($item['descr'])
->setAmountIncludingTax($item['total_price']['value_with_vat']->div(toDecimal($item['pieces']))->mul(DecimalConstants::hundred())->asInteger());
}
$createCheckoutSessionRequest->setLineItems($items);
$adyenSessionRequestResult = $service->sessions($createCheckoutSessionRequest);
$session->set('adyen_'.$this->orderId, [
'sessionId' => $adyenSessionRequestResult->getId(),
'sessionData' => $adyenSessionRequestResult->getSessionData(),
'expired' => time(),
'orderId' => $this->orderId,
]
);
return true;
}
/** Return from gateway */
public function processStep_5()
{
$sessionId = $this->request->get('sessionId');
$sessionResult = $this->request->get('sessionResult');
if ($sessionResult) {
$result = $this->getSessionStatus($sessionId, $sessionResult);
}
// Z informace redirectResult vubec nic nedostanu. Takze proste zakaznikovi zakazu znova zaplatit a bude se cekat az vysledek dorazi pres webhook.
if (($result ?? false) == \Adyen\Model\Checkout\SessionResultResponse::STATUS_COMPLETED || $this->request->get('redirectResult')) {
$adyenSession = $this->request->getSession()->get('adyen_'.$this->orderId) ?: [];
$this->request->getSession()->set('adyen_'.$this->orderId, $adyenSession + ['paymentResult' => \Adyen\Model\Checkout\SessionResultResponse::STATUS_COMPLETED]);
}
$this->success(translate('paymentSuccess', 'payment'));
}
public function getSessionStatus($sessionId, $sessionResult)
{
$paymentsApi = new \Adyen\Service\Checkout\PaymentsApi($this->getApiClient());
$sessionResultResponse = $paymentsApi->getResultOfPaymentSession($sessionId, ['queryParams' => ['sessionResult' => $sessionResult]]);
return $sessionResultResponse->getStatus();
}
public function startPayment()
{
$pathData = ['id' => $this->order->id];
if (!\User::getCurrentUser()) {
$pathData['cf'] = $this->order->getSecurityCode();
}
redirection(path('payment-redirect', $pathData));
}
public function processStep_1()
{
redirection($this->order->getDetailUrl());
}
/** Return from gateway */
public function processStep_10()
{
$request = json_decode($this->request->getContent() ?? '', true);
if (empty($request['notificationItems'])) {
$this->error('Not found !!');
}
// Setup NotificationReceiver with dependency injection or create an instance as follows
$notificationReceiver = new NotificationReceiver(new HmacSignature());
$_SERVER['PHP_AUTH_USER'] = $this->request->headers->get('php-auth-user');
$_SERVER['PHP_AUTH_PW'] = $this->request->headers->get('php-auth-pw');
if (!$notificationReceiver->isAuthenticated(
$request['notificationItems'][0]['NotificationRequestItem'],
$this->config['merchantAccount'],
$this->config['basic_name'],
$this->config['basic_pass']
)) {
throw new AuthenticationException('Incoming webhook wasn\'t authenticated!');
}
foreach ($request['notificationItems'] as $notificationItem) {
$notifItem = $notificationItem['NotificationRequestItem'];
if ($notificationReceiver->validateHmac($notifItem, $this->config['hmac']) && in_array($notifItem['eventCode'], ['AUTHORISATION', 'REFUND'])) {
$isRefund = ($notifItem['eventCode'] == 'REFUND');
$message = ($isRefund ? '[Adyen] Refund payment' : '[Adyen] Incoming payment');
$this->kibanaLogger->notice($message, [
'notificationItem' => $notifItem,
]);
$merchantReference = $notifItem['merchantReference'];
$ref = explode('_', $merchantReference);
$id_order = ($ref[0] ?? false);
if ($notifItem['success'] == 'true') {
if ($id_order) {
try {
$order = Order::createFromDbOrderNo($id_order);
$payment_data = [
'paymentClass' => self::class,
'session' => $notifItem['additionalData']['checkoutSessionId'] ?? null,
'pspReference' => $notifItem['pspReference'] ?? null,
];
$price = $notifItem['amount']['value'] / 100;
$note = "Platba modulu {$this->class}";
if ($isRefund) {
$price *= -1;
$note = "Vrácení platby: objednávka {$order->order_no}, ID platby: {$notifItem['originalReference']}";
$payment_data['originalReference'] = $notifItem['originalReference'];
}
$order?->insertPayment($price, $note, method: self::METHOD_ONLINE, payment_data: json_encode($payment_data));
if ($order && !empty($notifItem['paymentMethod'])) {
$this->setPaymentSubMethod($notifItem['paymentMethod'], $order);
}
} catch (InvalidArgumentException $exception) {
getRaven()->captureException($exception);
}
}
} else {
addActivityLog(ActivityLog::SEVERITY_ERROR, ActivityLog::TYPE_COMMUNICATION,
"Objednávka {$id_order}: {$message} error: {$notifItem['reason']}", $notifItem, [BankAutoPaymentBundle::LOG_TAG_ADYEN]);
}
}
}
$this->sendNotificationResponse(200, '[accepted]');
}
public function doReturnPayment(array $payment, float $amount)
{
if ($paymentPspReference = $payment['payment_data']['pspReference'] ?? null) {
$class = new \Adyen\Service\Checkout\ModificationsApi($this->getApiClient());
$paymentRefundRequest = new \Adyen\Model\Checkout\PaymentRefundRequest();
$paymentRefundRequest->setMerchantAccount($this->config['merchantAccount']);
$paymentRefundRequest->setReference($this->order->order_no.'_'.time());
$amountCents = (int) floor($amount * -100); // musí být integer v centech
$amount = new \Adyen\Model\Checkout\Amount();
$amount->setValue($amountCents)->setCurrency($this->order->getCurrency());
$paymentRefundRequest->setAmount($amount);
$paymentRefundResponse = $class->refundCapturedPayment($paymentPspReference, $paymentRefundRequest);
if ($paymentRefundResponse->getStatus() == 'received') {
// the refund request was successfully received by Adyen - no result yet,
// because you receive the outcome of the refund request asynchronously, in a REFUND webhook.
// Vrácení platby bylo odesláno na platební bránu.
throw new PaymentException(translate('returnSucceed', 'orderPayment'), 'Adyen');
}
}
// Vrácení platby se nezdařilo, vyřešte, prosím, na platební bráně.);
throw new PaymentException(translate('returnFailed', 'orderPayment'));
}
public function getApiClient(): Adyen\Client
{
if (!isset($this->apiClient)) {
$config = new \Adyen\Config();
$testEnv = (isDevelopment() || ($this->config['test'] ?? '0') == '1');
if (!$testEnv) {
$config->set('prefix', $this->config['api_urls_prefix']);
}
$client = new \Adyen\Client($config);
$client->setXApiKey($this->config['apikey']);
$client->setEnvironment($testEnv ? \Adyen\Environment::TEST : \Adyen\Environment::LIVE);
$client->setTimeout(5);
$this->apiClient = $client;
}
return $this->apiClient;
}
/**
* @throws \Adyen\AdyenException
*/
public function getAvailableMethods(Adyen\Model\Checkout\Amount|Decimal|null $amount = null)
{
$countryContext = Contexts::get(\KupShop\KupShopBundle\Context\CountryContext::class);
$currencyContext = Contexts::get(\KupShop\KupShopBundle\Context\CurrencyContext::class);
$currency = $currencyContext->getActiveId();
$country = $countryContext->getActiveId();
$cacheKey = "adyen-methods-{$currency}-{$country}-{$this->getLocale()}";
if (!$amount) {
if ($paymentMethods = getCache($cacheKey)) {
return $paymentMethods;
}
}
try {
$class = new \Adyen\Service\Checkout\PaymentsApi($this->getApiClient());
$paymentMethodsRequest = new \Adyen\Model\Checkout\PaymentMethodsRequest();
$paymentMethodsRequest->setMerchantAccount($this->config['merchantAccount']);
$paymentMethodsRequest->setCountryCode($countryContext->getActiveId());
$paymentMethodsRequest->setShopperLocale($this->getLocale());
if ($amount instanceof Decimal) {
$amount = (new \Adyen\Model\Checkout\Amount())->setValue($amount->mul(DecimalConstants::hundred())->asInteger())->setCurrency($currencyContext->getActiveId());
}
if ($amount) {
$paymentMethodsRequest->setAmount($amount);
}
$paymentMethodsResponse = $class->paymentMethods($paymentMethodsRequest);
$paymentMethods = [];
foreach ($paymentMethodsResponse->getPaymentMethods() as $method) {
if ($method->getType() == 'scheme') {
$images = array_map(function ($brand) {
return "https://checkoutshopper-live.adyen.com/checkoutshopper/images/logos/{$brand}.svg";
}, $method->getBrands());
} else {
$images = ["https://checkoutshopper-live.adyen.com/checkoutshopper/images/logos/{$method->getType()}.svg"];
}
$paymentMethods[$method->getType()] = [
'name' => $method->getName(),
'type' => $method->getType(),
'images' => $images,
];
}
} catch (Exception $e) {
addActivityLog(ActivityLog::SEVERITY_ERROR, ActivityLog::TYPE_COMMUNICATION,
'Chyba Adyen při načítání platebních metod: '.$e->getMessage(),
['key' => $cacheKey, 'config' => $this->config], [BankAutoPaymentBundle::LOG_TAG_ADYEN]);
return [];
}
if (!$amount) {
setCache($cacheKey, $paymentMethods);
}
return $paymentMethods;
}
public function hasOnlinePayment()
{
return true;
}
public static function getSettingsConfiguration(): array
{
return ['fields' => [
'apikey' => [
'title' => 'API klíč',
'type' => 'text',
],
'merchantAccount' => [
'title' => 'Merchant Account',
'type' => 'text',
],
'clientKey' => [
'title' => 'Client Key',
'type' => 'text',
],
'hmac' => [
'title' => 'HMAC key',
'type' => 'text',
],
'api_urls_prefix' => [
'title' => 'API URLs prefix',
'type' => 'text',
],
'basic_name' => [
'title' => 'Basic auth name',
'type' => 'text',
],
'basic_pass' => [
'title' => 'Basic auth pass',
'type' => 'text',
],
'test' => [
'title' => 'Testovací režim',
'type' => 'toggle',
],
]];
}
public function getLocale()
{
$languageContext = Contexts::get(\KupShop\KupShopBundle\Context\LanguageContext::class);
return $languageContext->getActive()->getLocale();
}
// private function checkWebhooks()
// {
// Maybe later alligator
//
// $merchantAccount = $this->config['merchantAccount'];
// $webhooksApi = new \Adyen\Service\Management\WebhooksMerchantLevelApi($this->getApiClient());
// $allWebhooks = $webhooksApi->listAllWebhooks($merchantAccount);
//
// $webhooks = $allWebhooks->getData();
//
// if ($webhooks) {
// return true;
// }
//
// $createMerchantWebhookRequest = new \Adyen\Model\Management\CreateMerchantWebhookRequest();
// $createMerchantWebhookRequest->setUrl(path('kupshop_ordering_payment_legacypayment', ['class' => 'Adyen', 'step' => 10], \Symfony\Component\Routing\Router::ABSOLUTE_URL))
// ->setDescription('API - wpjshop')
// ->setType('standard')
// ->setCommunicationFormat('json');
//
// $webhooksApi->setUpWebhook($merchantAccount, $createMerchantWebhookRequest);
// }
}