first commit
This commit is contained in:
399
class/payments/class.Adyen.php
Normal file
399
class/payments/class.Adyen.php
Normal 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);
|
||||
// }
|
||||
}
|
||||
Reference in New Issue
Block a user