Skip to content

Commit bb9669a

Browse files
committed
Added support for banks + Setting for banks/cards/both + Fixed order state stuck on "new" + Started using webhook
1 parent 54fc387 commit bb9669a

File tree

10 files changed

+186
-72
lines changed

10 files changed

+186
-72
lines changed

Controller/Redirect/ReturnAfterPayment.php

+2-5
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,10 @@ class ReturnAfterPayment implements \Magento\Framework\App\ActionInterface
2424
private RequestInterface $request;
2525
private Data $nofrixionHelper;
2626
private LoggerInterface $logger;
27-
private StoreManagerInterface $storeManager;
2827
private RedirectFactory $resultRedirectFactory;
2928

30-
public function __construct(StoreManagerInterface $storeManager, RequestInterface $request, LoggerInterface $logger, UrlInterface $url, RedirectFactory $resultRedirectFactory, PageFactory $resultPageFactory, Data $nofrixionHelper, OrderFactory $orderFactory, Session $checkoutSession)
29+
public function __construct(RequestInterface $request, LoggerInterface $logger, UrlInterface $url, RedirectFactory $resultRedirectFactory, PageFactory $resultPageFactory, Data $nofrixionHelper, OrderFactory $orderFactory, Session $checkoutSession)
3130
{
32-
$this->storeManager = $storeManager;
3331
$this->request = $request;
3432
$this->resultRedirectFactory = $resultRedirectFactory;
3533
$this->url = $url;
@@ -48,11 +46,10 @@ public function execute()
4846
{
4947
$nofrixionOrderId = $this->request->getParam('nofrixion_order_id');
5048
$resultRedirect = $this->resultRedirectFactory->create();
51-
$store = $this->storeManager->getStore();
5249

5350
try {
5451
$paymentRequest = $this->nofrixionHelper->getPaymentRequestByOrderId($nofrixionOrderId);
55-
$order = $this->nofrixionHelper->processPayment($paymentRequest['id'], (int)$store->getId());
52+
$order = $this->nofrixionHelper->processPayment($paymentRequest);
5653

5754
$this->checkoutSession->setLastQuoteId($order->getQuoteId());
5855
$this->checkoutSession->setLastSuccessQuoteId($order->getQuoteId());

Controller/Webhook/In.php

+19-3
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,41 @@
55
namespace Nofrixion\Payments\Controller\Webhook;
66

77
use Magento\Framework\App\ActionInterface;
8+
use Magento\Framework\App\RequestInterface;
9+
use Magento\Framework\Controller\Result\JsonFactory;
10+
use Magento\Store\Model\StoreManagerInterface;
811
use Nofrixion\Payments\Helper\Data as NoFrixionHelper;
912

1013
class In implements ActionInterface
1114
{
1215

1316
private NoFrixionHelper $nofrixionHelper;
17+
private RequestInterface $request;
18+
private StoreManagerInterface $storeManager;
19+
private JsonFactory $resultJsonFactory;
1420

15-
public function __construct(NoFrixionHelper $helper)
21+
public function __construct(RequestInterface $request, NoFrixionHelper $helper, StoreManagerInterface $storeManager, JsonFactory $resultJsonFactory)
1622
{
23+
$this->request = $request;
1724
$this->nofrixionHelper = $helper;
25+
$this->storeManager = $storeManager;
26+
$this->resultJsonFactory = $resultJsonFactory;
1827
}
1928

2029
public function execute()
2130
{
31+
$storeId = (int)$this->storeManager->getStore()->getId();
2232
$paymentRequestId = $this->request->getParam('id');
33+
$resultJson = $this->resultJsonFactory->create();
34+
2335
if ($paymentRequestId) {
24-
$this->nofrixionHelper->processPayment($paymentRequestId);
36+
$paymentRequest = $this->nofrixionHelper->getPaymentRequest($paymentRequestId, $storeId);
37+
$order = $this->nofrixionHelper->processPayment($paymentRequest);
38+
39+
return $resultJson->setData(['order_id' => (int)$order->getId(), 'order_increment_id' => $order->getIncrementId(), 'order_state' => $order->getState(), 'order_status' => $order->getStatus()]);
2540
} else {
26-
throw new \RuntimeException('Missing parameter "id"');
41+
$resultJson->setStatusHeader(400);
42+
return $resultJson->setData(['error_msg' => 'Missing querystring parameter "id"']);
2743
}
2844
}
2945
}

Helper/Data.php

+98-32
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@
66

77
use Magento\Framework\App\Config\ScopeConfigInterface;
88
use Magento\Framework\DB\TransactionFactory;
9+
use Magento\Framework\Exception\AlreadyExistsException;
910
use Magento\Framework\UrlInterface;
1011
use Magento\Sales\Api\Data\OrderInterface;
1112
use Magento\Sales\Model\Order;
1213
use Magento\Sales\Model\Order\Invoice;
1314
use Magento\Sales\Model\Order\Payment;
15+
use Magento\Sales\Model\Order\StatusFactory;
1416
use Magento\Sales\Model\OrderRepository;
17+
use Magento\Sales\Model\ResourceModel\Order\Status as StatusResource;
18+
use Magento\Sales\Model\ResourceModel\Order\StatusFactory as StatusResourceFactory;
1519
use Magento\Store\Model\ScopeInterface;
1620
use NoFrixion\Client\PaymentRequest;
1721
use Nofrixion\Payments\Model\OrderStatuses;
@@ -25,22 +29,30 @@ class Data
2529
private LoggerInterface $logger;
2630
private OrderRepository $orderRepository;
2731
private TransactionFactory $transactionFactory;
32+
private StatusFactory $statusFactory;
33+
private StatusResourceFactory $statusResourceFactory;
2834

2935
public const ORDER_ID_SEPARATOR = '-';
30-
36+
private StatusResource\CollectionFactory $statusCollectionFactory;
3137

3238
public function __construct(
3339
ScopeConfigInterface $scopeConfig,
3440
UrlInterface $url,
3541
OrderRepository $orderRepository,
3642
TransactionFactory $transactionFactory,
3743
LoggerInterface $logger,
44+
StatusFactory $statusFactory,
45+
StatusResourceFactory $statusResourceFactory,
46+
\Magento\Sales\Model\ResourceModel\Order\Status\CollectionFactory $statusCollectionFactory
3847
) {
3948
$this->scopeConfig = $scopeConfig;
4049
$this->url = $url;
4150
$this->orderRepository = $orderRepository;
4251
$this->transactionFactory = $transactionFactory;
4352
$this->logger = $logger;
53+
$this->statusFactory = $statusFactory;
54+
$this->statusResourceFactory = $statusResourceFactory;
55+
$this->statusCollectionFactory = $statusCollectionFactory;
4456
}
4557

4658
public function getPaymentRequestClient(?int $storeId = null): PaymentRequest
@@ -76,10 +88,11 @@ public function createPaymentRequest(Order $order): array
7688
$amount = $order->getTotalDue();
7789
$customerEmail = $order->getCustomerEmail();
7890
$currency = $order->getOrderCurrencyCode();
79-
$paymentMethodTypes = array('card', 'pisp');
91+
$paymentMethodTypes = $this->scopeConfig->getValue('payment/nofrixion/payment_options', 'stores', $storeId);
92+
$paymentMethodTypes = explode(',', $paymentMethodTypes);
8093
$originUrl = $this->url->getBaseUrl(['_store' => $storeId]);
8194
$nofrixionOrderId = $this->encodeOrderId($order);
82-
$callbackUrl = $this->url->getUrl('nofrixion/redirect/returnAfterPayment', ['_secure' => true, 'nofrixion_order_id' => $nofrixionOrderId]);
95+
$callbackUrl = $this->url->getUrl('nofrixion/redirect/returnAfterPayment', ['_store' => $storeId, '_secure' => true, 'nofrixion_order_id' => $nofrixionOrderId]);
8396
$amount = PreciseNumber::parseString((string)$amount);
8497
if ($order->getCustomerId()) {
8598
$customerId = (string)$order->getCustomerId();
@@ -90,7 +103,10 @@ public function createPaymentRequest(Order $order): array
90103
$createCardToken = true;
91104
$client = $this->getPaymentRequestClient($storeId);
92105
$originUrl = str_replace('http://', 'https://', $originUrl);
93-
$paymentRequest = $client->createPaymentRequest($originUrl, $callbackUrl, $amount, $customerEmail, $currency, $paymentMethodTypes, $nofrixionOrderId, $createCardToken, $customerId);
106+
107+
$webhookUrl = $this->url->getUrl('nofrixion/webhook/in', ['_secure' => true]);
108+
109+
$paymentRequest = $client->createPaymentRequest($originUrl, $callbackUrl, $amount, $customerEmail, $currency, $paymentMethodTypes, $nofrixionOrderId, $createCardToken, $customerId, false, false, $webhookUrl);
94110

95111
return $paymentRequest;
96112
}
@@ -101,57 +117,77 @@ public function getPaymentRequest(string $id, int $storeId): array
101117
return $client->getPaymentRequest($id);
102118
}
103119

104-
public function processPayment(string $paymentRequestId, int $storeId): OrderInterface
120+
public function processPayment(array $paymentRequest): OrderInterface
105121
{
106-
$paymentRequest = $this->getPaymentRequest($paymentRequestId, $storeId);
107122
$nofrixionOrderId = $paymentRequest['orderID'];
108123
$magentoOrderId = $this->decodeOrderId($nofrixionOrderId);
109124

110125
$order = $this->orderRepository->get($magentoOrderId);
111126

112127
$status = $paymentRequest['status'] ?? null;
113128

129+
$createInvoice = true;
130+
$isPaid = false;
114131
$newStatus = null;
115132

116133
switch ($status) {
134+
case 'Authorized':
135+
$newStatus = OrderStatuses::STATUS_CODE_AUTHORIZED_PAYMENT;
136+
$createInvoice = false;
137+
$isPaid = true;
138+
break;
117139
case 'OverPaid':
118140
$newStatus = OrderStatuses::STATUS_CODE_OVERPAID;
119-
141+
$isPaid = true;
142+
break;
120143
case 'FullyPaid':
121-
if ($order->getTotalDue() > 0) {
122-
if ($newStatus === null) {
123-
$newStatus = OrderStatuses::STATUS_CODE_PAID_CORRECTLY;
124-
}
125-
126-
$msg = 'Customer paid ' . $paymentRequest['amount'] . ' ' . $paymentRequest['currency'] . ' using the NoFrixion payment request with ID ' . $paymentRequest['id'];
127-
$order->addCommentToStatusHistory($msg, $newStatus, true);
128-
129-
$invoice = $order->prepareInvoice();
130-
$invoice->setRequestedCaptureCase(Invoice::CAPTURE_ONLINE);
131-
132-
// We need to set this so the "Refund" button appears when creating a credit memo for an invoice.
133-
// Refunds only work when creating a credit memo for an invoice. Creating a credit memo for an order (and not a specific invoice) will not show the "Refund" button. Only the "Refund Offline" button will show.
134-
$invoice->setTransactionId($paymentRequest['id']);
135-
136-
$invoice->register();
137-
138-
$saveTransaction = $this->transactionFactory->create();
139-
$saveTransaction->addObject($invoice);
140-
$saveTransaction->addObject($order);
141-
$saveTransaction->save();
142-
}
144+
$isPaid = true;
145+
$newStatus = OrderStatuses::STATUS_CODE_PAID_CORRECTLY;
143146

144147
break;
145148
case 'PartiallyPaid':
146149
// TODO This is tricky because we don't know which items were paid and which items not. So how can we know what to invoice?
150+
break;
147151
case 'Voided':
148152
// TODO What should we do? Cancel the order?
149-
case 'Authorized':
150-
// TODO Should we do something or just wait for another status?
153+
break;
151154
default:
152-
$this->logger->log('Unsupported status "' . $status . '" for payment request ID ' . $paymentRequestId);
155+
$this->logger->log('Unsupported status "' . $status . '" for payment request ID ' . $paymentRequest['id']);
156+
}
157+
158+
if ($isPaid && $newStatus && $order->getTotalDue() > 0) {
159+
160+
if($newStatus !== $order->getStatus()) {
161+
if ($createInvoice) {
162+
$msg = 'Customer paid ' . $paymentRequest['amount'] . ' ' . $paymentRequest['currency'] . ' using the NoFrixion payment request with ID ' . $paymentRequest['id'];
163+
} else {
164+
$msg = 'Customer authorized ' . $paymentRequest['amount'] . ' ' . $paymentRequest['currency'] . ' using the NoFrixion payment request with ID ' . $paymentRequest['id'];
165+
}
166+
$order->addCommentToStatusHistory($msg, $newStatus, true);
167+
168+
// Magento bugfix: Set the matching "state" on the order. Apparently Magento does not automatically set the "state" when the "status" changes.
169+
$this->fixOrderState($order);
170+
}
171+
172+
$saveTransaction = $this->transactionFactory->create();
173+
174+
if ($createInvoice) {
175+
$invoice = $order->prepareInvoice();
176+
$invoice->setRequestedCaptureCase(Invoice::CAPTURE_ONLINE);
177+
178+
// We need to set this so the "Refund" button appears when creating a credit memo for an invoice.
179+
// Refunds only work when creating a credit memo for an invoice. Creating a credit memo for an order (and not a specific invoice) will not show the "Refund" button. Only the "Refund Offline" button will show.
180+
$invoice->setTransactionId($paymentRequest['id']);
181+
182+
$invoice->register();
183+
$saveTransaction->addObject($invoice);
184+
}
185+
186+
$saveTransaction->addObject($order);
187+
$saveTransaction->save();
153188
}
154189

190+
155191
return $order;
156192
}
157193

@@ -195,4 +231,34 @@ public function refund(\Magento\Payment\Model\InfoInterface $payment, $amount)
195231
}
196232

197233

234+
public function addNewStatusToState(string $state, array $statusData): void
235+
{
236+
/* @var StatusResource $statusResource */
237+
$statusResource = $this->statusResourceFactory->create();
238+
$status = $this->statusFactory->create();
239+
$status->setData($statusData);
240+
try {
241+
$statusResource->save($status);
242+
} catch (AlreadyExistsException $exception) {
243+
return;
244+
}
245+
$status->assignState($state, false, true);
246+
}
247+
248+
private function fixOrderState(OrderInterface $order): void
249+
{
250+
$status = $order->getStatus();
251+
252+
/* @var $statusCollection \Magento\Sales\Model\ResourceModel\Order\Status\Collection\Interceptor */
253+
$statusCollection = $this->statusCollectionFactory->create();
254+
$statusCollection->joinStates();
255+
$statusCollection->addFieldToFilter('main_table.status', $status);
256+
257+
if ($statusCollection->count() === 1) {
258+
$newState = $statusCollection->getFirstItem()->getState();
259+
$order->setState($newState);
260+
}
261+
}
262+
263+
198264
}
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Nofrixion\Payments\Model\Config\Source;
6+
7+
class PaymentOptions implements \Magento\Framework\Data\OptionSourceInterface
8+
{
9+
10+
public function toOptionArray()
11+
{
12+
return [
13+
['value' => 'pisp', 'label' => __('Banks')],
14+
['value' => 'card', 'label' => __('Cards')],
15+
['value' => 'card,pisp', 'label' => __('Cards & Banks')],
16+
];
17+
}
18+
19+
}

Model/OrderStatuses.php

+3
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,8 @@ class OrderStatuses extends \Magento\Framework\Model\AbstractModel
1919
const STATUS_CODE_PENDING_PAYMENT = 'nofrixion_pending_payment';
2020
const STATUS_LABEL_PENDING_PAYMENT = 'Pending Nofrixion payment';
2121

22+
const STATUS_CODE_AUTHORIZED_PAYMENT = 'nofrixion_authorized_payment';
23+
const STATUS_LABEL_AUTHORIZED_PAYMENT = 'Authorized Nofrixion payment';
24+
2225

2326
}

Setup/InstallSchema.php

+8-31
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,20 @@
44

55
namespace Nofrixion\Payments\Setup;
66

7-
use Magento\Framework\App\Config\ConfigResource\ConfigInterface;
8-
use Magento\Framework\Exception\AlreadyExistsException;
97
use Magento\Framework\Setup\InstallSchemaInterface;
108
use Magento\Framework\Setup\ModuleContextInterface;
119
use Magento\Framework\Setup\SchemaSetupInterface;
1210
use Magento\Sales\Model\Order;
13-
use Magento\Sales\Model\Order\StatusFactory;
14-
use Magento\Sales\Model\ResourceModel\Order\Status as StatusResource;
15-
use Magento\Sales\Model\ResourceModel\Order\StatusFactory as StatusResourceFactory;
11+
use Nofrixion\Payments\Helper\Data as NofrixionHelper;
1612
use Nofrixion\Payments\Model\OrderStatuses;
1713

1814
class InstallSchema implements InstallSchemaInterface
1915
{
16+
private NofrixionHelper $helper;
2017

21-
22-
protected StatusFactory $statusFactory;
23-
protected StatusResourceFactory $statusResourceFactory;
24-
private ConfigInterface $configResource;
25-
26-
public function __construct(ConfigInterface $configResource, StatusFactory $statusFactory, StatusResourceFactory $statusResourceFactory)
18+
public function __construct(NofrixionHelper $nofrixionHelper)
2719
{
28-
$this->configResource = $configResource;
29-
$this->statusFactory = $statusFactory;
30-
$this->statusResourceFactory = $statusResourceFactory;
20+
$this->helper = $nofrixionHelper;
3121
}
3222

3323
public function install(SchemaSetupInterface $setup, ModuleContextInterface $context)
@@ -36,26 +26,13 @@ public function install(SchemaSetupInterface $setup, ModuleContextInterface $con
3626
$installer->startSetup();
3727

3828
// Add new order statuses and assign them to states
39-
$this->addNewStatusToState(Order::STATE_PENDING_PAYMENT, ['status' => OrderStatuses::STATUS_CODE_PENDING_PAYMENT, 'label' => OrderStatuses::STATUS_LABEL_PENDING_PAYMENT]);
40-
$this->addNewStatusToState(Order::STATE_PAYMENT_REVIEW, ['status' => OrderStatuses::STATUS_CODE_UNDERPAID, 'label' => OrderStatuses::STATUS_LABEL_UNDERPAID]);
41-
$this->addNewStatusToState(Order::STATE_PROCESSING, ['status' => OrderStatuses::STATUS_CODE_PAID_CORRECTLY, 'label' => OrderStatuses::STATUS_LABEL_PAID_CORRECTLY]);
42-
$this->addNewStatusToState(Order::STATE_PAYMENT_REVIEW, ['status' => OrderStatuses::STATUS_CODE_OVERPAID, 'label' => OrderStatuses::STATUS_LABEL_OVERPAID]);
29+
$this->helper->addNewStatusToState(Order::STATE_PENDING_PAYMENT, ['status' => OrderStatuses::STATUS_CODE_PENDING_PAYMENT, 'label' => OrderStatuses::STATUS_LABEL_PENDING_PAYMENT]);
30+
$this->helper->addNewStatusToState(Order::STATE_PAYMENT_REVIEW, ['status' => OrderStatuses::STATUS_CODE_UNDERPAID, 'label' => OrderStatuses::STATUS_LABEL_UNDERPAID]);
31+
$this->helper->addNewStatusToState(Order::STATE_PROCESSING, ['status' => OrderStatuses::STATUS_CODE_PAID_CORRECTLY, 'label' => OrderStatuses::STATUS_LABEL_PAID_CORRECTLY]);
32+
$this->helper->addNewStatusToState(Order::STATE_PAYMENT_REVIEW, ['status' => OrderStatuses::STATUS_CODE_OVERPAID, 'label' => OrderStatuses::STATUS_LABEL_OVERPAID]);
4333

4434
$installer->endSetup();
4535
}
4636

47-
protected function addNewStatusToState($state, $statusData): void
48-
{
49-
/** @var StatusResource $statusResource */
50-
$statusResource = $this->statusResourceFactory->create();
51-
$status = $this->statusFactory->create();
52-
$status->setData($statusData);
53-
try {
54-
$statusResource->save($status);
55-
} catch (AlreadyExistsException $exception) {
56-
return;
57-
}
58-
$status->assignState($state, false, true);
59-
}
6037

6138
}

0 commit comments

Comments
 (0)