Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions Block/Adminhtml/System/Config/Field/RefusalReasonMapping.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php
/**
* Adyen Payment Module
*
* Copyright (c) 2023 Adyen N.V.
* This file is open source and available under the MIT license.
* See the LICENSE file for more info.
*
* Author: Adyen <[email protected]>
*/

namespace Adyen\Payment\Block\Adminhtml\System\Config\Field;

use Magento\Config\Block\System\Config\Form\Field\FieldArray\AbstractFieldArray;
use Magento\Framework\DataObject;

/**
* Class RefusalReasonMapping
*
* @package Adyen\Payment\Block\Adminhtml\System\Config\Field
*/
class RefusalReasonMapping extends AbstractFieldArray
{
protected ?RefusalReasonSelect $refusalReasonSelect = null;

/**
* @inheritDoc
* @throws \Exception
*/
protected function _prepareToRender(): void
{
$this->addColumn(
'refusal_reason',
['label' => __('Refusal Reason'), 'renderer' => $this->getRefusalReasonSelect()]
);

$this->addColumn(
'value',
['label' => __('Value')]
);

$this->_addAfter = false;
$this->_addButtonLabel = __('Add Refusal Reason');
parent::_prepareToRender();
}

/**
* @throws \Exception
* @return RefusalReasonSelect
*/
private function getRefusalReasonSelect(): RefusalReasonSelect
{
if (!$this->refusalReasonSelect instanceof RefusalReasonSelect) {
/** @var RefusalReasonSelect $countriesRenderer */
$select = $this->getLayout()->createBlock(
RefusalReasonSelect::class,
'',
['data' => ['is_render_to_js_template' => true]]
);

$this->refusalReasonSelect = $select;
}

return $this->refusalReasonSelect;
}

/**
* @inheritDoc
* @throws \Exception
*/
protected function _prepareArrayRow(DataObject $row): void
{
$options = [];
$refusalReason = $row->getData('refusal_reason');
if (is_string($refusalReason) && $refusalReason !== '') {
$options['option_' . $this->getRefusalReasonSelect()->calcOptionHash($refusalReason)]
= 'selected="selected"';
}

$row->setData('option_extra_attrs', $options);
}
}
58 changes: 58 additions & 0 deletions Block/Adminhtml/System/Config/Field/RefusalReasonSelect.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php
/**
* Adyen Payment Module
*
* Copyright (c) 2023 Adyen N.V.
* This file is open source and available under the MIT license.
* See the LICENSE file for more info.
*
* Author: Adyen <[email protected]>
*/

namespace Adyen\Payment\Block\Adminhtml\System\Config\Field;

use Magento\Framework\View\Element\Html\Select;
use Adyen\Payment\Enum\AdyenRefusalReason;

/**
* Class RefusalReasonSelect
*
* @package Adyen\Payment\Block\Adminhtml\System\Config\Field
*/
class RefusalReasonSelect extends Select
{
/**
* @param string $value
* @return self
*/
public function setInputName(string $value): self
{
return $this->setData('name', $value);
}

/**
* @param $value
* @return self
*/
public function setInputId($value): self
{
return $this->setId($value);
}

/**
* @inheritDoc
*/
public function _toHtml(): string
{
if (!$this->getOptions()) {
$options = array_map(fn (AdyenRefusalReason $reason) => [
'label' => $reason->getLabel(),
'value' => $reason->value,
], AdyenRefusalReason::cases());

$this->setOptions($options);
}

return parent::_toHtml();
}
}
121 changes: 121 additions & 0 deletions Enum/AdyenRefusalReason.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<?php
/**
* Adyen Payment Module
*
* Copyright (c) 2023 Adyen N.V.
* This file is open source and available under the MIT license.
* See the LICENSE file for more info.
*
* Author: Adyen <[email protected]>
*/

namespace Adyen\Payment\Enum;

/**
* Class AdyenRefusalReason
*
* @package Adyen\Payment\Enum
* @see https://docs.adyen.com/development-resources/refusal-reasons/
*/
enum AdyenRefusalReason: int
{
case None = 0;
case Refused = 2;
case Referral = 3;
case AcquirerError = 4;
case BlockedCard = 5;
case ExpiredCard = 6;
case InvalidAmount = 7;
case InvalidCardNumber = 8;
case IssuerUnavailable = 9;
case NotSupported = 10;
case NotAuthenticated3D = 11;
case NotEnoughBalance = 12;
case AcquirerFraud = 14;
case Cancelled = 15;
case ShopperCancelled = 16;
case InvalidPin = 17;
case PinTriesExceeded = 18;
case PinValidationNotPossible = 19;
case Fraud = 20;
case NotSubmitted = 21;
case FraudCancelled = 22;
case TransactionNotPermitted = 23;
case CVCDeclined = 24;
case RestrictedCard = 25;
case RevocationOfAuth = 26;
case DeclinedNonGeneric = 27;
case WithdrawalAmountExceeded = 28;
case WithdrawalCountExceeded = 29;
case IssuerSuspectedFraud = 31;
case AVSDeclined = 32;
case CardRequiresOnlinePin = 33;
case NoCheckingAccountAvailableOnCard = 34;
case NoSavingsAccountAvailableOnCard = 35;
case MobilePINRequired = 36;
case ContactlessFallback = 37;
case AuthenticationRequired = 38;
case RReqNotReceivedFromDS = 39;
case CurrentAIDIsInPenaltyBox = 40;
case CVMRequiredRestartPayment = 41;
case AuthenticationError3DS = 42;
case OnlinePINRequired = 43;
case TryAnotherInterface = 44;
case ChipDowngradeMode = 45;
case TransactionBlockedByAdyen = 46;

/**
* @return string
*/
public function getLabel(): string
{
$label = match ($this) {
self::None => __('Unknown'),
self::Refused => __('Refused'),
self::Referral => __('Referral'),
self::AcquirerError => __('Acquirer error'),
self::BlockedCard => __('Blocked card'),
self::ExpiredCard => __('Expired card'),
self::InvalidAmount => __('Invalid amount'),
self::InvalidCardNumber => __('Invalid card number'),
self::IssuerUnavailable => __('Issuer unavailable'),
self::NotSupported => __('Not supported'),
self::NotAuthenticated3D => __('3D Not Authenticated'),
self::NotEnoughBalance => __('Not enough balance'),
self::AcquirerFraud => __('Acquirer Fraud'),
self::Cancelled => __('Cancelled'),
self::ShopperCancelled => __('Shopper cancelled'),
self::InvalidPin => __('Invalid PIN'),
self::PinTriesExceeded => __('PIN tries exceeded'),
self::PinValidationNotPossible => __('PIN validation not possible'),
self::Fraud => __('Fraud'),
self::NotSubmitted => __('Not submitted'),
self::FraudCancelled => __('FRAUD-CANCELLED'),
self::TransactionNotPermitted => __('Transaction not permitted'),
self::CVCDeclined => __('CVC Declined'),
self::RestrictedCard => __('Restricted card'),
self::RevocationOfAuth => __('Revocation of auth'),
self::DeclinedNonGeneric => __('Declined non-generic'),
self::WithdrawalAmountExceeded => __('Withdrawal amount exceeded'),
self::WithdrawalCountExceeded => __('Withdrawal count exceeded'),
self::IssuerSuspectedFraud => __('Issuer Suspected Fraud'),
self::AVSDeclined => __('AVS Declined'),
self::CardRequiresOnlinePin => __('Card requires online PIN'),
self::NoCheckingAccountAvailableOnCard => __('No checking account available on card'),
self::NoSavingsAccountAvailableOnCard => __('No savings account available on card'),
self::MobilePINRequired => __('Mobile PIN required'),
self::ContactlessFallback => __('Contactless fallback'),
self::AuthenticationRequired => __('Authentication required'),
self::RReqNotReceivedFromDS => __('RReq not received from DS'),
self::CurrentAIDIsInPenaltyBox => __('Current AID is in penalty box'),
self::CVMRequiredRestartPayment => __('CVM required restart payment'),
self::AuthenticationError3DS => __('3DS Authentication Error'),
self::OnlinePINRequired => __('Online PIN required'),
self::TryAnotherInterface => __('Try another interface'),
self::ChipDowngradeMode => __('Chip downgrade mode'),
self::TransactionBlockedByAdyen => __('Transaction blocked by Adyen to prevent excessive retry fees'),
};

return sprintf('%s - %s', $this->value, $label);
}
}
50 changes: 50 additions & 0 deletions Enum/CallbackOrderProperty.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php
/**
* Adyen Payment Module
*
* Copyright (c) 2023 Adyen N.V.
* This file is open source and available under the MIT license.
* See the LICENSE file for more info.
*
* Author: Adyen <[email protected]>
*/

namespace Adyen\Payment\Enum;

use Magento\Framework\Phrase;

/**
* Class CallbackOrderProperty
*
* @package Adyen\Payment\Enum
*/
enum CallbackOrderProperty: string
{
case ShippingFirstName = 'shipping.firstname';
case ShippingLastName = 'shipping.lastname';
case ShippingPostCode = 'shipping.postcode';
case ShippingTelephone = 'shipping.telephone';
case BillingFirstName = 'billing.firstname';
case BillingLastName = 'billing.lastname';
case BillingPostCode = 'billing.postcode';
case BillingTelephone = 'billing.telephone';
case CustomerEmail = 'customer_email';

/**
* @return Phrase
*/
public function getLabel(): Phrase
{
return match ($this) {
self::ShippingFirstName => __('Shipping first name'),
self::ShippingLastName => __('Shipping last name'),
self::ShippingPostCode => __('Shipping postcode'),
self::ShippingTelephone => __('Shipping telephone'),
self::BillingFirstName => __('Billing first name'),
self::BillingLastName => __('Billing last name'),
self::BillingPostCode => __('Billing postcode'),
self::BillingTelephone => __('Billing telephone'),
self::CustomerEmail => __('Customer email'),
};
}
}
69 changes: 69 additions & 0 deletions Gateway/Request/TestingRefusalReasonBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php
/**
* Adyen Payment Module
*
* Copyright (c) 2023 Adyen N.V.
* This file is open source and available under the MIT license.
* See the LICENSE file for more info.
*
* Author: Adyen <[email protected]>
*/

namespace Adyen\Payment\Gateway\Request;

use Adyen\Payment\Helper\Config;
use Magento\Payment\Gateway\Helper\SubjectReader;
use Magento\Payment\Gateway\Request\BuilderInterface;
use Magento\Sales\Model\Order;
use Adyen\Payment\Enum\AdyenRefusalReason;
use Adyen\Payment\Model\TestingRefusalReason;

/**
* Class TestingRefusalReasonBuilder
*
* @package Adyen\Payment\Gateway\Request
* https://docs.adyen.com/development-resources/testing/result-codes/?tab=request_using_holder_name_0_1
*/
class TestingRefusalReasonBuilder implements BuilderInterface
{
/**
* TestingRefusalReasonBuilder Constructor
*
* @param Config $config
* @param TestingRefusalReason $testingRefusalReason
*/
public function __construct(
protected Config $config,
protected TestingRefusalReason $testingRefusalReason
) {
}

/**
* @inheritDoc
*/
public function build(array $buildSubject): array
{
$paymentDataObject = SubjectReader::readPayment($buildSubject);
$payment = $paymentDataObject->getPayment();
/** @var Order $order */
$order = $payment->getOrder();
$storeId = $order->getStoreId();

if (!$this->config->isDemoMode($storeId)) {
return [];
}

$reason = $this->testingRefusalReason->findRefusalReason($order);
if (!$reason instanceof AdyenRefusalReason) {
return [];
}

return [
'body' => [
'additionalData' => [
'RequestedTestAcquirerResponseCode' => $reason->value,
],
],
];
}
}
Loading