diff --git a/Block/Adminhtml/Config/NotificationEndpoint.php b/Block/Adminhtml/Config/NotificationEndpoint.php index 9e84795..56c8e30 100644 --- a/Block/Adminhtml/Config/NotificationEndpoint.php +++ b/Block/Adminhtml/Config/NotificationEndpoint.php @@ -5,22 +5,25 @@ use Magento\Backend\Block\Template\Context; use Magento\Config\Block\System\Config\Form\Field; use Magento\Framework\Data\Form\Element\AbstractElement; -use Midtrans\Snap\Helper\Data; +use Midtrans\Snap\Helper\MidtransDataConfiguration; class NotificationEndpoint extends Field { - protected Data $_midtransHelper; + /** + * @var MidtransDataConfiguration + */ + protected $midtransDataConfiguration; /** * constructor. * @param Context $context * @param array $data - * @param Data $midtransHelper + * @param MidtransDataConfiguration $midtransDataConfiguration */ - public function __construct(Data $midtransHelper, Context $context, array $data = []) + public function __construct(MidtransDataConfiguration $midtransDataConfiguration, Context $context, array $data = []) { parent::__construct($context, $data); - $this->_midtransHelper = $midtransHelper; + $this->midtransDataConfiguration = $midtransDataConfiguration; } /** @@ -31,6 +34,6 @@ public function __construct(Data $midtransHelper, Context $context, array $data */ protected function _getElementHtml(AbstractElement $element) { - return $this->_midtransHelper->getNotificationEndpoint(); + return $this->midtransDataConfiguration->getNotificationEndpoint(); } } diff --git a/Block/Adminhtml/Config/Version.php b/Block/Adminhtml/Config/Version.php index 119e4ab..999dc23 100644 --- a/Block/Adminhtml/Config/Version.php +++ b/Block/Adminhtml/Config/Version.php @@ -5,22 +5,25 @@ use Magento\Backend\Block\Template\Context; use Magento\Config\Block\System\Config\Form\Field; use Magento\Framework\Data\Form\Element\AbstractElement; -use Midtrans\Snap\Helper\Data; +use Midtrans\Snap\Helper\MidtransDataConfiguration; class Version extends Field { - protected $_midtransHelper; + /** + * @var MidtransDataConfiguration + */ + protected $midtransDataConfiguration; /** * Version constructor. * @param Context $context * @param array $data - * @param Data $midtransHelper + * @param MidtransDataConfiguration $midtransDataConfiguration */ - public function __construct(Data $midtransHelper, Context $context, array $data = []) + public function __construct(MidtransDataConfiguration $midtransDataConfiguration, Context $context, array $data = []) { parent::__construct($context, $data); - $this->_midtransHelper = $midtransHelper; + $this->midtransDataConfiguration = $midtransDataConfiguration; } /** @@ -32,7 +35,7 @@ public function __construct(Data $midtransHelper, Context $context, array $data */ protected function _getElementHtml(AbstractElement $element) { - $moduleVersion = $this->_midtransHelper->getModuleVersion(); + $moduleVersion = $this->midtransDataConfiguration->getModuleVersion(); $response = $this->getModuleLatestVersion(); $latestModuleVersion = isset($response['tag_name']) ? $response['tag_name'] : 'Not Found!'; diff --git a/Block/Close.php b/Block/Close.php index f475fb7..46f0cdd 100755 --- a/Block/Close.php +++ b/Block/Close.php @@ -8,6 +8,9 @@ class Close extends Template { + /** + * @var Registry + */ protected $registry; /** diff --git a/Block/Finish.php b/Block/Finish.php index ff88c93..7336fd7 100755 --- a/Block/Finish.php +++ b/Block/Finish.php @@ -8,7 +8,17 @@ class Finish extends Template { + /** + * @var Registry + */ protected $registry; + + /** + * Finish constructor. + * + * @param Context $context + * @param Registry $registry + */ public function __construct( Context $context, Registry $registry diff --git a/Block/Pending.php b/Block/Pending.php index 3d87876..e16c686 100755 --- a/Block/Pending.php +++ b/Block/Pending.php @@ -4,7 +4,13 @@ use Magento\Framework\View\Element\Template; use Magento\Framework\View\Element\Template\Context; - +/** + * Class Pending block is to handle information when the payment order is still pending + * @deprecated since version 2.5.5 Pending class no longer used, the pending page merged on finish page. Will be deleted + * on the next major release + * @see \Midtrans\Snap\Block\Finish + * + */ class Pending extends Template { public function __construct( diff --git a/Controller/Index/Close.php b/Controller/Index/Close.php index bbe3ee1..5d8e827 100755 --- a/Controller/Index/Close.php +++ b/Controller/Index/Close.php @@ -3,9 +3,9 @@ namespace Midtrans\Snap\Controller\Index; use Magento\Sales\Model\Order; -use Midtrans\Snap\Controller\Payment\AbstractAction; +use Midtrans\Snap\Controller\Payment\Action; -class Close extends AbstractAction +class Close extends Action { public function execute() { @@ -15,10 +15,10 @@ public function execute() if ($param !== null) { if (strpos($param, 'multishipping-') !== false) { $quoteId = str_replace('multishipping-', '', $param); - $incrementIds = $this->getIncrementIdsByQuoteId($quoteId); + $incrementIds = $this->paymentOrderRepository->getIncrementIdsByQuoteId($quoteId); foreach ($incrementIds as $key => $orderId) { - $order = $this->getOrderByIncrementId($orderId); + $order = $this->paymentOrderRepository->getOrderByIncrementId($orderId); $this->closedOrder($order); $ordersCanceled[$orderId] = $orderId; } @@ -46,7 +46,7 @@ private function closedOrder(Order $order) if ($order->getState() == Order::STATE_NEW && !$order->hasInvoices()) { $order_note = "Midtrans | Payment Page close - by User"; try { - $this->cancelOrder($order, Order::STATE_CANCELED, $order_note); + $this->paymentOrderRepository->cancelOrder($order, Order::STATE_CANCELED, $order_note); } catch (\Exception $e) { $this->_midtransLogger->midtransError('PaymentClose: ' . $e); } diff --git a/Controller/Index/Finish.php b/Controller/Index/Finish.php index bee2995..8906fda 100755 --- a/Controller/Index/Finish.php +++ b/Controller/Index/Finish.php @@ -3,12 +3,11 @@ namespace Midtrans\Snap\Controller\Index; use Exception; -use Magento\Framework\View\Result\Page; -use Midtrans\Snap\Controller\Payment\AbstractAction; +use Midtrans\Snap\Controller\Payment\Action; use Midtrans\Snap\Gateway\Config\Config; use Midtrans\Snap\Gateway\Transaction; -class Finish extends AbstractAction +class Finish extends Action { const PAYMENT_CODE = 'snap'; @@ -16,31 +15,31 @@ public function execute() { $orderIdRequest = $this->getRequest()->getParam('order_id'); $midtransResult = null; + try { /* Handle for BCA Klikpay */ $transactionId = $this->getRequest()->getParam('id'); if ($transactionId != null) { - Config::$isProduction = $this->data->isProduction(); - Config::$serverKey = $this->data->getServerKey(self::PAYMENT_CODE); + Config::$isProduction = $this->midtransDataConfiguration->isProduction(); + Config::$serverKey = $this->midtransDataConfiguration->getServerKey(self::PAYMENT_CODE); $transaction = new Transaction(); $midtransResult = $transaction::status($transactionId); } /* Handle for direct debit, cardless credit, gopay, cc */ - else { - if ($orderIdRequest == null) { - $postValue = $this->getRequest()->getPostValue(); - $response = $postValue['response']; - $decoded_response = $this->data->json->unserialize($response); - $orderIdRequest = $decoded_response['order_id']; - } + + elseif ($this->getRequest()->getPostValue() != null) { + $postValue = $this->getRequest()->getPostValue(); + $response = $postValue['response']; + $decoded_response = $this->midtransDataConfiguration->json->unserialize($response); + $orderIdRequest = $decoded_response['order_id']; if (strpos($orderIdRequest, 'multishipping-') !== false) { // 2. Finish for multishipping $quoteId = str_replace('multishipping-', '', $orderIdRequest); - $incrementIds = $this->getIncrementIdsByQuoteId($quoteId); + $incrementIds = $this->paymentOrderRepository->getIncrementIdsByQuoteId($quoteId); foreach ($incrementIds as $key => $id) { - $order = $this->getOrderByIncrementId($id); + $order = $this->paymentOrderRepository->getOrderByIncrementId($id); $paymentCode = $order->getPayment()->getMethod(); $midtransResult = $this->midtransGetStatus($orderIdRequest, $paymentCode); } @@ -49,7 +48,30 @@ public function execute() $order = $this->_order->loadByIncrementId($orderIdRequest); $midtransResult = $this->midtransGetStatus($order); } + } else { + $checkoutSession = $this->_checkoutSession->getData(); + $param = $this->_checkoutSession->getLastRealOrder()->getIncrementId(); + + if (isset($checkoutSession['checkout_state']) && $checkoutSession['checkout_state'] === 'multishipping_success') { + $quoteId = $checkoutSession['last_quote_id']; + $incrementIds = $this->paymentOrderRepository->getIncrementIdsByQuoteId($quoteId); + + $paymentCode = null; + foreach ($incrementIds as $key => $orderId) { + $order = $this->paymentOrderRepository->getOrderByIncrementId($orderId); + $paymentCode = $order->getPayment()->getMethod(); + } + $param = 'multishipping-' . $quoteId; + $midtransResult = $this->midtransGetStatus($param, $paymentCode); + } elseif ($param !== null) { + $order = $this->paymentOrderRepository->getOrderByIncrementId($param); + $midtransResult = $this->midtransGetStatus($order); + } else { + return $this->resultRedirectFactory->create()->setPath('checkout/cart'); + } + $this->unSetValue(); } + $orderId = $midtransResult->order_id; $amount = $midtransResult->gross_amount; $transaction = $midtransResult->transaction_status; @@ -62,9 +84,9 @@ public function execute() } catch (Exception $e) { error_log($e->getMessage()); $this->_midtransLogger->midtransError('FinishController-' . $e->getMessage()); + $this->unSetValue(); return $this->resultRedirectFactory->create()->setPath('checkout/cart'); } - /** @var Page $resultPage */ $resultPage = $this->_pageFactory->create(); return $resultPage; } diff --git a/Controller/Index/Pending.php b/Controller/Index/Pending.php index bb6ff94..2e2bac9 100755 --- a/Controller/Index/Pending.php +++ b/Controller/Index/Pending.php @@ -3,27 +3,24 @@ namespace Midtrans\Snap\Controller\Index; use Magento\Checkout\Model\Session\SuccessValidator; -use Magento\Framework\App\Action\Action; -use Magento\Framework\App\Action\Context; -use Magento\Framework\View\Result\PageFactory; +use Midtrans\Snap\Controller\Payment\Action; +/** + * Class Pending controller is to handle information when the payment order is still pending + * @deprecated since version 2.5.5 Pending class no longer used, the pending page merged on finish page. Will be deleted + * on the next major release + * @see \Midtrans\Snap\Controller\Index\Finish + * + */ class Pending extends Action { - protected $request; - protected $resultPageFactory; - - public function __construct(Context $context, PageFactory $pageFactory) - { - $this->resultPageFactory = $pageFactory; - - parent::__construct($context); - } - public function execute() { - if (!$this->_objectManager->get(SuccessValidator::class)->isValid()) { + $param = $this->getValue(); + + if (!$this->objectManager->get(SuccessValidator::class)->isValid()) { return $this->resultRedirectFactory->create()->setPath('checkout/cart'); } - return $this->resultPageFactory->create(); + return $this->_pageFactory->create(); } } diff --git a/Controller/Payment/AbstractAction.php b/Controller/Payment/AbstractAction.php index 8c5efdd..6276486 100644 --- a/Controller/Payment/AbstractAction.php +++ b/Controller/Payment/AbstractAction.php @@ -19,11 +19,14 @@ use Magento\Sales\Model\Service\InvoiceService; use Midtrans\Snap\Gateway\Config\Config; use Midtrans\Snap\Gateway\Transaction as MidtransTransaction; -use Midtrans\Snap\Helper\Data; +use Midtrans\Snap\Helper\MidtransDataConfiguration; use Midtrans\Snap\Logger\MidtransLogger; /** * Class AbstractAction to handle basic action order + * @deprecated since version 2.5.5 AbstractAction classes no longer used, will be deleted on the next major release + * @see \Midtrans\Snap\Controller\Payment\Action + * */ abstract class AbstractAction extends Action { @@ -32,7 +35,7 @@ abstract class AbstractAction extends Action */ protected $_coreSession; /** - * @var Data + * @var MidtransDataConfiguration */ protected $data; /** @@ -101,7 +104,7 @@ abstract class AbstractAction extends Action * @param Session $checkoutSession * @param Order $order * @param Session\SuccessValidator $successValidator - * @param Data $data + * @param MidtransDataConfiguration $data * @param InvoiceService $invoiceService * @param Transaction $transaction * @param ResourceModel $resourceModel @@ -122,7 +125,7 @@ public function __construct( Session $checkoutSession, Order $order, Session\SuccessValidator $successValidator, - Data $data, + MidtransDataConfiguration $data, InvoiceService $invoiceService, Transaction $transaction, ResourceModel $resourceModel, @@ -193,7 +196,7 @@ public function getOrderByIncrementId($realOrderId) /** * Get Midtrans data config * - * @return Data + * @return MidtransDataConfiguration */ public function getMidtransDataConfig() { diff --git a/Controller/Payment/Action.php b/Controller/Payment/Action.php new file mode 100644 index 0000000..0ec12ed --- /dev/null +++ b/Controller/Payment/Action.php @@ -0,0 +1,356 @@ +_coreSession = $coreSession; + $this->_checkoutSession = $checkoutSession; + $this->_order = $order; + $this->midtransDataConfiguration = $midtransDataConfiguration; + $this->objectManager = $objectManager; + $this->_pageFactory = $pageFactory; + $this->_invoiceService = $invoiceService; + $this->_transaction = $transaction; + $this->_resourceModel = $resourceModel; + $this->_orderRepository = $orderRepository; + $this->_invoice = $invoice; + $this->_creditmemoFactory = $creditmemoFactory; + $this->_creditmemoService = $creditmemoService; + $this->_creditmemoRepository = $creditmemoRepository; + $this->_midtransLogger = $midtransLogger; + $this->registry = $registry; + $this->_customerSession = $customerSession; + $this->_contextHttp = $contextHttp; + $this->context = $context; + $this->resultFactory = $context->getResultFactory(); + $this->_redirect = $context->getRedirect(); + $this->resultRedirectFactory = $context->getResultRedirectFactory(); + $this->_request = $context->getRequest(); + $this->_response = $context->getResponse(); + $this->paymentOrderRepository = $paymentOrderRepository; + $this->paymentRequestRepository = $paymentRequestRepository; + } + + /** + * Retrieve request object + * + * @return RequestInterface + */ + public function getRequest() + { + return $this->_request; + } + + /** + * Retrieve response object + * + * @return ResponseInterface + */ + public function getResponse() + { + return $this->_response; + } + + /** + * Set redirect into response + * + * @param string $path + * @param array $arguments + * @return ResponseInterface + */ + protected function _redirect($path, $arguments = []) + { + $this->_redirect->redirect($this->getResponse(), $path, $arguments); + return $this->getResponse(); + } + + /** + * Get increment id from last order checkout session + * + * @return string + */ + public function getOrderIdSession() + { + return $this->_checkoutSession->getLastRealOrder()->getIncrementId(); + } + + /** + * Get order by incrementId from session checkout + * + * @return Order + */ + public function getOrderFromSession() + { + return $this->_order->loadByIncrementId($this->getOrderIdSession()); + } + + /** + * Get Midtrans data config + * + * @return MidtransDataConfiguration + */ + public function getMidtransDataConfig() + { + return $this->midtransDataConfiguration; + } + + /** + * Set value to core session + * + * @param $order_id + */ + public function setValue($order_id) + { + $this->_coreSession->start(); + $this->_coreSession->setMessage($order_id); + } + + /** + * get value from core session + * + * @return mixed + */ + public function getValue() + { + $this->_coreSession->start(); + return $this->_coreSession->getMessage(); + } + + /** + * unset value from core sessions + * + * @return mixed + */ + public function unSetValue() + { + $this->_coreSession->start(); + return $this->_coreSession->unsMessage(); + } + + /** + * Get Midtrans status via API + * + * @param mixed $param it can be Midtrans order-id/transaction-id or Magento Order object + * @param null $paymentCode Magento payment method code + * @return mixed[] Midtrans API response + * @throws \Exception + */ + public function midtransGetStatus($param, $paymentCode = null) + { + $orderId = null; + if ($param instanceof Order) { + $orderId = $param->getIncrementId(); + $paymentCode = $param->getPayment()->getMethod(); + } else { + $orderId = $param; + } + Config::$serverKey = $this->getMidtransDataConfig()->getServerKey($paymentCode); + Config::$isProduction = $this->getMidtransDataConfig()->isProduction(); + return MidtransTransaction::status($orderId); + } +} diff --git a/Controller/Payment/Multishipping.php b/Controller/Payment/Multishipping.php index e258e02..5b80972 100644 --- a/Controller/Payment/Multishipping.php +++ b/Controller/Payment/Multishipping.php @@ -7,7 +7,7 @@ use Midtrans\Snap\Gateway\Config\Config; use Midtrans\Snap\Gateway\SnapApi; -class Multishipping extends AbstractAction +class Multishipping extends Action { public function execute() { @@ -21,7 +21,7 @@ public function execute() // 2. Get quote-id from checkout session $quoteId = $checkoutSession['last_quote_id']; - $paymentCode = $this->getPaymentCodeByQuoteId($quoteId); + $paymentCode = $this->paymentOrderRepository->getPaymentCodeByQuoteId($quoteId); $requestConfig = $this->getMidtransDataConfig()->getRequestConfig($paymentCode); $isRedirect = $this->getMidtransDataConfig()->isRedirect(); @@ -35,7 +35,7 @@ public function execute() Config::$isSanitized = true; Config::$is3ds = $is3ds; - $payloads = $this->getPayload($requestConfig, $paymentCode, $multishipping); + $payloads = $this->paymentRequestRepository->getPayload($requestConfig, $paymentCode, null, $multishipping); /*Override notification, if override notification from admin setting is active (default is active) */ if ($this->getMidtransDataConfig()->isOverrideNotification() && $this->getMidtransDataConfig()->getNotificationEndpoint() != null) { @@ -45,6 +45,9 @@ public function execute() $_info = 'Info - Payloads: ' . print_r($payloads, true); $this->_midtransLogger->midtransRequest($_info); + $paymentOrderId = $payloads['transaction_details']['order_id']; + $this->setValue($paymentOrderId); + $snapApi = new SnapApi(); $data = null; diff --git a/Controller/Payment/Notification.php b/Controller/Payment/Notification.php index 5a34c3e..a0ffcb4 100755 --- a/Controller/Payment/Notification.php +++ b/Controller/Payment/Notification.php @@ -8,7 +8,7 @@ * Class Notification * Handle notifications from midtrans http notifications */ -class Notification extends AbstractAction +class Notification extends Action { /** * Main function @@ -20,7 +20,7 @@ public function execute() { // 1. Get body from request $input_source = $this->getRequest()->getContent(); - $rawBody = $this->data->json->unserialize($input_source); + $rawBody = $this->midtransDataConfiguration->json->unserialize($input_source); $orderIdRequest = $rawBody['order_id']; $order = null; @@ -28,14 +28,14 @@ public function execute() if (strpos($orderIdRequest, 'multishipping-') !== false) { // 2. Process notification multishipping $quoteId = str_replace('multishipping-', '', $orderIdRequest); - $incrementIds = $this->getIncrementIdsByQuoteId($quoteId); + $incrementIds = $this->paymentOrderRepository->getIncrementIdsByQuoteId($quoteId); $this->getResponse()->setBody('ok'); foreach ($incrementIds as $key => $id) { - $order = $this->getOrderByIncrementId($id); + $order = $this->paymentOrderRepository->getOrderByIncrementId($id); $paymentCode = $order->getPayment()->getMethod(); - if ($this->canProcess($order)) { + if ($this->paymentOrderRepository->canProcess($order)) { $midtransStatusResult = $this->midtransGetStatus($orderIdRequest, $paymentCode); $this->processOrder($order, $midtransStatusResult, $rawBody); } else { @@ -47,7 +47,7 @@ public function execute() // 3. Process notification regular order $this->getResponse()->setBody('OK'); $order = $this->_order->loadByIncrementId($orderIdRequest); - if ($this->canProcess($order)) { + if ($this->paymentOrderRepository->canProcess($order)) { $midtransStatusResult = $this->midtransGetStatus($order); $this->processOrder($order, $midtransStatusResult, $rawBody); } else { @@ -81,42 +81,42 @@ public function processOrder(Order $order, $midtransStatusResult, $rawBody) $note_prefix = "MIDTRANS NOTIFICATION | "; if ($transaction == 'capture') { - $this->setPaymentInformation($order, $trxId, $payment_type); + $this->paymentOrderRepository->setPaymentInformation($order, $trxId, $payment_type); if ($fraud == 'challenge') { $order_note = $note_prefix . 'Payment status challenged. Please take action on your Merchant Administration Portal - ' . $payment_type; - $this->setOrderStateAndStatus($order, Order::STATE_PAYMENT_REVIEW, $order_note); + $this->paymentOrderRepository->setOrderStateAndStatus($order, Order::STATE_PAYMENT_REVIEW, $order_note); } elseif ($fraud == 'accept') { $order_note = $note_prefix . 'Payment Completed - ' . $payment_type; if ($order->canInvoice()) { - $this->generateInvoice($order); + $this->paymentOrderRepository->generateInvoice($order); } - $this->setOrderStateAndStatus($order, Order::STATE_PROCESSING, $order_note); + $this->paymentOrderRepository->setOrderStateAndStatus($order, Order::STATE_PROCESSING, $order_note); } } elseif ($transaction == 'settlement') { - $this->setPaymentInformation($order, $trxId, $payment_type); + $this->paymentOrderRepository->setPaymentInformation($order, $trxId, $payment_type); if ($payment_type != 'credit_card') { $order_note = $note_prefix . 'Payment Completed - ' . $payment_type; if ($order->canInvoice()) { - $this->generateInvoice($order); + $this->paymentOrderRepository->generateInvoice($order); } - $this->setOrderStateAndStatus($order, Order::STATE_PROCESSING, $order_note); + $this->paymentOrderRepository->setOrderStateAndStatus($order, Order::STATE_PROCESSING, $order_note); } } elseif ($transaction == 'pending') { - $this->setPaymentInformation($order, $trxId, $payment_type); + $this->paymentOrderRepository->setPaymentInformation($order, $trxId, $payment_type); $order_note = $note_prefix . 'Awaiting Payment - ' . $payment_type; - $this->setOrderStateAndStatus($order, Order::STATE_PENDING_PAYMENT, $order_note); + $this->paymentOrderRepository->setOrderStateAndStatus($order, Order::STATE_PENDING_PAYMENT, $order_note); } elseif ($transaction == 'cancel') { $order_note = $note_prefix . 'Canceled Payment - ' . $payment_type; - $this->cancelOrder($order, Order::STATE_CANCELED, $order_note); + $this->paymentOrderRepository->cancelOrder($order, Order::STATE_CANCELED, $order_note); } elseif ($transaction == 'expire') { if ($order->canCancel()) { $order_note = $note_prefix . 'Expired Payment - ' . $payment_type; - $this->cancelOrder($order, Order::STATE_CANCELED, $order_note); + $this->paymentOrderRepository->cancelOrder($order, Order::STATE_CANCELED, $order_note); } } elseif ($transaction == 'deny') { - $this->setPaymentInformation($order, $trxId, $payment_type); + $this->paymentOrderRepository->setPaymentInformation($order, $trxId, $payment_type); $order_note = $note_prefix . 'Payment Deny - ' . $payment_type; - $this->setOrderStateAndStatus($order, Order::STATE_PAYMENT_REVIEW, $order_note); + $this->paymentOrderRepository->setOrderStateAndStatus($order, Order::STATE_PAYMENT_REVIEW, $order_note); } elseif ($transaction == 'refund' || $transaction == 'partial_refund') { /** @@ -138,7 +138,7 @@ public function processOrder(Order $order, $midtransStatusResult, $rawBody) */ $midtransOrderId = $this->getOrderIdFromReason($refund_reason); if ($midtransOrderId !== null) { - $orderRefund = $this->getOrderByIncrementId($midtransOrderId); + $orderRefund = $this->paymentOrderRepository->getOrderByIncrementId($midtransOrderId); $this->processRefund($orderRefund, $refunds, true, $grossAmount); } else { /** @@ -148,14 +148,14 @@ public function processOrder(Order $order, $midtransStatusResult, $rawBody) /** Check order-id is not contain multishipping */ if (strpos($midtransOrderId, 'multishipping-') !== true) { - $order = $this->getOrderByIncrementId($midtransOrderId); + $order = $this->paymentOrderRepository->getOrderByIncrementId($midtransOrderId); $this->processRefund($order, $refunds, false, $grossAmount); } } } } - $this->saveOrder($order); + $this->paymentOrderRepository->saveOrder($order); /** * If log request isEnabled, add request payload to var/log/midtrans/request.log @@ -184,7 +184,7 @@ private function processRefund(Order $orderRefund, $refunds, $isFromMagento, $gr /** Handling full refund */ if ($isFullRefund && $orderRefund->getStatus() != Order::STATE_CLOSED && $orderRefund->getState() != Order::STATE_CLOSED) { - $this->cancelOrder($orderRefund, Order::STATE_CLOSED, $refund_note); + $this->paymentOrderRepository->cancelOrder($orderRefund, Order::STATE_CLOSED, $refund_note); } /** Handling partial refund */ elseif ($orderRefund->getStatus() != Order::STATE_CLOSED && $orderRefund->getState() != Order::STATE_CLOSED) { @@ -192,10 +192,10 @@ private function processRefund(Order $orderRefund, $refunds, $isFromMagento, $gr if (!$this->isOrderCommentExist($orderRefund, $refund_note)) { if ($isFullRefund) { /** Close order if total amount refund array is equal with grand total order / gross amount */ - $this->cancelOrder($orderRefund, Order::STATE_CLOSED, $refund_note); + $this->paymentOrderRepository->cancelOrder($orderRefund, Order::STATE_CLOSED, $refund_note); } else { /** Put status history if total amount refund array is not equal with grand total order / gross amount */ - $this->setOrderStateAndStatus($orderRefund, Order::STATE_PROCESSING, $refund_note); + $this->paymentOrderRepository->setOrderStateAndStatus($orderRefund, Order::STATE_PROCESSING, $refund_note); } } } diff --git a/Controller/Payment/Redirect.php b/Controller/Payment/Redirect.php index 2d7bff5..ca93c88 100755 --- a/Controller/Payment/Redirect.php +++ b/Controller/Payment/Redirect.php @@ -7,18 +7,20 @@ use Midtrans\Snap\Gateway\Config\Config; use Midtrans\Snap\Gateway\SnapApi; -class Redirect extends AbstractAction +class Redirect extends Action { public function execute() { try { $incrementId = $this->_checkoutSession->getLastRealOrder()->getIncrementId(); - $paymentCode = $this->getPaymentCode($incrementId); + $paymentCode = $this->paymentOrderRepository->getPaymentCode($incrementId); $requestConfig = $this->getMidtransDataConfig()->getRequestConfig($paymentCode); $enableRedirect = $this->getMidtransDataConfig()->isRedirect(); - $payloads = $this->getPayload($requestConfig, $paymentCode); + $order = $this->getOrderFromSession(); + + $payloads = $this->paymentRequestRepository->getPayload($requestConfig, $paymentCode, $order); $is3ds = $requestConfig['is3ds']; $isProduction = $this->getMidtransDataConfig()->isProduction(); @@ -48,6 +50,8 @@ public function execute() $_info = 'Info - Redirect URL :' . print_r($data, true); $this->_midtransLogger->midtransRequest($_info); } + $paymentOrderId = $payloads['transaction_details']['order_id']; + $this->setValue($paymentOrderId); $result = $this->resultFactory->create(ResultFactory::TYPE_JSON); $result->setData($data); return $result; diff --git a/Helper/Data.php b/Helper/MidtransDataConfiguration.php similarity index 95% rename from Helper/Data.php rename to Helper/MidtransDataConfiguration.php index e425cf8..bf2c5ba 100644 --- a/Helper/Data.php +++ b/Helper/MidtransDataConfiguration.php @@ -7,21 +7,28 @@ use Magento\Framework\Serialize\Serializer\Json; use Midtrans\Snap\Model\Config\Source\Payment\Settings; -class Data +class MidtransDataConfiguration { - private Settings $settings; - private EncryptorInterface $_encryptor; + /** + * @var Settings + */ + private $settings; + + /** + * @var EncryptorInterface + */ + private $_encryptor; /** * Serialize data to JSON, unserialize JSON encoded data * @var Json */ - public Json $json; + public $json; /** * @var ComponentRegistrarInterface */ - private ComponentRegistrarInterface $componentRegistrar; + private $componentRegistrar; /** * Data constructor. diff --git a/Helper/Utils.php b/Helper/Utils.php new file mode 100644 index 0000000..50da5fc --- /dev/null +++ b/Helper/Utils.php @@ -0,0 +1,289 @@ + $max_length) { + $offset = ($max_length - 3) - strlen($cleanItemName); + $cleanItemName = substr($cleanItemName, 0, strrpos($cleanItemName, ' ', $offset)); + } + return $cleanItemName; + } + + /** + * function convert country code + * + * @param $country_code + * @return mixed + */ + public function convert_country_code($country_code) + { + // 3 digits country codes + $cc_three = [ + 'AF' => 'AFG', + 'AX' => 'ALA', + 'AL' => 'ALB', + 'DZ' => 'DZA', + 'AD' => 'AND', + 'AO' => 'AGO', + 'AI' => 'AIA', + 'AQ' => 'ATA', + 'AG' => 'ATG', + 'AR' => 'ARG', + 'AM' => 'ARM', + 'AW' => 'ABW', + 'AU' => 'AUS', + 'AT' => 'AUT', + 'AZ' => 'AZE', + 'BS' => 'BHS', + 'BH' => 'BHR', + 'BD' => 'BGD', + 'BB' => 'BRB', + 'BY' => 'BLR', + 'BE' => 'BEL', + 'PW' => 'PLW', + 'BZ' => 'BLZ', + 'BJ' => 'BEN', + 'BM' => 'BMU', + 'BT' => 'BTN', + 'BO' => 'BOL', + 'BQ' => 'BES', + 'BA' => 'BIH', + 'BW' => 'BWA', + 'BV' => 'BVT', + 'BR' => 'BRA', + 'IO' => 'IOT', + 'VG' => 'VGB', + 'BN' => 'BRN', + 'BG' => 'BGR', + 'BF' => 'BFA', + 'BI' => 'BDI', + 'KH' => 'KHM', + 'CM' => 'CMR', + 'CA' => 'CAN', + 'CV' => 'CPV', + 'KY' => 'CYM', + 'CF' => 'CAF', + 'TD' => 'TCD', + 'CL' => 'CHL', + 'CN' => 'CHN', + 'CX' => 'CXR', + 'CC' => 'CCK', + 'CO' => 'COL', + 'KM' => 'COM', + 'CG' => 'COG', + 'CD' => 'COD', + 'CK' => 'COK', + 'CR' => 'CRI', + 'HR' => 'HRV', + 'CU' => 'CUB', + 'CW' => 'CUW', + 'CY' => 'CYP', + 'CZ' => 'CZE', + 'DK' => 'DNK', + 'DJ' => 'DJI', + 'DM' => 'DMA', + 'DO' => 'DOM', + 'EC' => 'ECU', + 'EG' => 'EGY', + 'SV' => 'SLV', + 'GQ' => 'GNQ', + 'ER' => 'ERI', + 'EE' => 'EST', + 'ET' => 'ETH', + 'FK' => 'FLK', + 'FO' => 'FRO', + 'FJ' => 'FJI', + 'FI' => 'FIN', + 'FR' => 'FRA', + 'GF' => 'GUF', + 'PF' => 'PYF', + 'TF' => 'ATF', + 'GA' => 'GAB', + 'GM' => 'GMB', + 'GE' => 'GEO', + 'DE' => 'DEU', + 'GH' => 'GHA', + 'GI' => 'GIB', + 'GR' => 'GRC', + 'GL' => 'GRL', + 'GD' => 'GRD', + 'GP' => 'GLP', + 'GT' => 'GTM', + 'GG' => 'GGY', + 'GN' => 'GIN', + 'GW' => 'GNB', + 'GY' => 'GUY', + 'HT' => 'HTI', + 'HM' => 'HMD', + 'HN' => 'HND', + 'HK' => 'HKG', + 'HU' => 'HUN', + 'IS' => 'ISL', + 'IN' => 'IND', + 'ID' => 'IDN', + 'IR' => 'RIN', + 'IQ' => 'IRQ', + 'IE' => 'IRL', + 'IM' => 'IMN', + 'IL' => 'ISR', + 'IT' => 'ITA', + 'CI' => 'CIV', + 'JM' => 'JAM', + 'JP' => 'JPN', + 'JE' => 'JEY', + 'JO' => 'JOR', + 'KZ' => 'KAZ', + 'KE' => 'KEN', + 'KI' => 'KIR', + 'KW' => 'KWT', + 'KG' => 'KGZ', + 'LA' => 'LAO', + 'LV' => 'LVA', + 'LB' => 'LBN', + 'LS' => 'LSO', + 'LR' => 'LBR', + 'LY' => 'LBY', + 'LI' => 'LIE', + 'LT' => 'LTU', + 'LU' => 'LUX', + 'MO' => 'MAC', + 'MK' => 'MKD', + 'MG' => 'MDG', + 'MW' => 'MWI', + 'MY' => 'MYS', + 'MV' => 'MDV', + 'ML' => 'MLI', + 'MT' => 'MLT', + 'MH' => 'MHL', + 'MQ' => 'MTQ', + 'MR' => 'MRT', + 'MU' => 'MUS', + 'YT' => 'MYT', + 'MX' => 'MEX', + 'FM' => 'FSM', + 'MD' => 'MDA', + 'MC' => 'MCO', + 'MN' => 'MNG', + 'ME' => 'MNE', + 'MS' => 'MSR', + 'MA' => 'MAR', + 'MZ' => 'MOZ', + 'MM' => 'MMR', + 'NA' => 'NAM', + 'NR' => 'NRU', + 'NP' => 'NPL', + 'NL' => 'NLD', + 'AN' => 'ANT', + 'NC' => 'NCL', + 'NZ' => 'NZL', + 'NI' => 'NIC', + 'NE' => 'NER', + 'NG' => 'NGA', + 'NU' => 'NIU', + 'NF' => 'NFK', + 'KP' => 'MNP', + 'NO' => 'NOR', + 'OM' => 'OMN', + 'PK' => 'PAK', + 'PS' => 'PSE', + 'PA' => 'PAN', + 'PG' => 'PNG', + 'PY' => 'PRY', + 'PE' => 'PER', + 'PH' => 'PHL', + 'PN' => 'PCN', + 'PL' => 'POL', + 'PT' => 'PRT', + 'QA' => 'QAT', + 'RE' => 'REU', + 'RO' => 'SHN', + 'RU' => 'RUS', + 'RW' => 'EWA', + 'BL' => 'BLM', + 'SH' => 'SHN', + 'KN' => 'KNA', + 'LC' => 'LCA', + 'MF' => 'MAF', + 'SX' => 'SXM', + 'PM' => 'SPM', + 'VC' => 'VCT', + 'SM' => 'SMR', + 'ST' => 'STP', + 'SA' => 'SAU', + 'SN' => 'SEN', + 'RS' => 'SRB', + 'SC' => 'SYC', + 'SL' => 'SLE', + 'SG' => 'SGP', + 'SK' => 'SVK', + 'SI' => 'SVN', + 'SB' => 'SLB', + 'SO' => 'SOM', + 'ZA' => 'ZAF', + 'GS' => 'SGS', + 'KR' => 'KOR', + 'SS' => 'SSD', + 'ES' => 'ESP', + 'LK' => 'LKA', + 'SD' => 'SDN', + 'SR' => 'SUR', + 'SJ' => 'SJM', + 'SZ' => 'SWZ', + 'SE' => 'SWE', + 'CH' => 'CHE', + 'SY' => 'SYR', + 'TW' => 'TWN', + 'TJ' => 'TJK', + 'TZ' => 'TZA', + 'TH' => 'THA', + 'TL' => 'TLS', + 'TG' => 'TGO', + 'TK' => 'TKL', + 'TO' => 'TON', + 'TT' => 'TTO', + 'TN' => 'TUN', + 'TR' => 'TUR', + 'TM' => 'TKM', + 'TC' => 'TCA', + 'TV' => 'TUV', + 'UG' => 'UGA', + 'UA' => 'UKR', + 'AE' => 'ARE', + 'GB' => 'GBR', + 'US' => 'USA', + 'UY' => 'URY', + 'UZ' => 'UZB', + 'VU' => 'VUT', + 'VA' => 'VAT', + 'VE' => 'VEN', + 'VN' => 'VNM', + 'WF' => 'WLF', + 'EH' => 'ESH', + 'WS' => 'WSM', + 'YE' => 'YEM', + 'ZM' => 'ZMB', + 'ZW' => 'ZWE' + ]; + // Check if country code exists + if (isset($cc_three[$country_code]) && $cc_three[$country_code] != '') { + $country_code = $cc_three[$country_code]; + } + return $country_code; + } + +} diff --git a/Logger/MidtransLogger.php b/Logger/MidtransLogger.php index dd4b9e3..eafc0bb 100644 --- a/Logger/MidtransLogger.php +++ b/Logger/MidtransLogger.php @@ -16,7 +16,11 @@ class MidtransLogger extends Logger const REQUEST = 100; const ERROR = 400; const NOTIFICATION = 200; - protected Settings $settings; + + /** + * @var Settings + */ + protected $settings; /** * MidtransLogger constructor. diff --git a/Model/Config/Source/Payment/AbstractPayment.php b/Model/Config/Source/Payment/AbstractPayment.php index 720e28e..0edfffe 100644 --- a/Model/Config/Source/Payment/AbstractPayment.php +++ b/Model/Config/Source/Payment/AbstractPayment.php @@ -17,7 +17,7 @@ use Magento\Store\Model\StoreManagerInterface; use Midtrans\Snap\Gateway\Config\Config; use Midtrans\Snap\Gateway\Transaction; -use Midtrans\Snap\Helper\Data; +use Midtrans\Snap\Helper\MidtransDataConfiguration; use Midtrans\Snap\Logger\MidtransLogger; /** @@ -58,7 +58,7 @@ class AbstractPayment extends Adapter public $infoBlockType; /** - * @var Data + * @var MidtransDataConfiguration */ protected $dataConfig; @@ -82,7 +82,7 @@ class AbstractPayment extends Adapter * @param ManagerInterface $eventManager * @param ValueHandlerPoolInterface $valueHandlerPool * @param PaymentDataObjectFactory $paymentDataObjectFactory - * @param Data $dataConfig + * @param MidtransDataConfiguration $dataConfig * @param UrlInterface $urlInterface * @param StoreManagerInterface $storeManager * @param MidtransLogger $midtransLogger @@ -97,7 +97,7 @@ public function __construct( ManagerInterface $eventManager, ValueHandlerPoolInterface $valueHandlerPool, PaymentDataObjectFactory $paymentDataObjectFactory, - Data $dataConfig, + MidtransDataConfiguration $dataConfig, UrlInterface $urlInterface, StoreManagerInterface $storeManager, MidtransLogger $midtransLogger, @@ -192,4 +192,10 @@ public function refund(InfoInterface $payment, $amount) throw new LocalizedException(__("Oops, Refund request failed. Due to no response received. Please try again later.")); } } + + public function getCode() + { + return parent::getCode(); + } + } diff --git a/Model/Order/OrderRepository.php b/Model/Order/OrderRepository.php new file mode 100644 index 0000000..2088710 --- /dev/null +++ b/Model/Order/OrderRepository.php @@ -0,0 +1,380 @@ +order = $order; + $this->objectManager = $objectManager; + $this->magentoOrderRepository = $magentoOrderRepository; + $this->midtransLogger = $midtransLogger; + $this->invoice = $invoice; + $this->invoiceService = $invoiceService; + $this->creditmemoFactory = $creditmemoFactory; + $this->creditmemoService = $creditmemoService; + $this->creditmemoRepository = $creditmemoRepository; + $this->transaction = $transaction; + $this->messageManager = $messageManager; + } + + /** + * Get order by incrementId + * + * @param $realOrderId + * @return Order + */ + public function getOrderByIncrementId($realOrderId) + { + return $this->order->loadByIncrementId($realOrderId); + } + + /** + * Get payment code from Order by incrementId + * + * @param $incrementId + * @return string + */ + public function getPaymentCode($incrementId) + { + return $this->getOrderByIncrementId($incrementId)->getPayment()->getMethod(); + } + + /** + * Find orders from database by field key + * + * @param $fieldKey + * @param $value + * @return OrderInterface[] + */ + public function getOrderCollection($fieldKey, $value) + { + $searchCriteriaBuilder = $this->objectManager->create('Magento\Framework\Api\SearchCriteriaBuilder'); + $searchCriteria = $searchCriteriaBuilder->addFilter($fieldKey, $value, 'eq')->create(); + return $this->magentoOrderRepository->getList($searchCriteria)->getItems(); + } + + /** + * Find increment ids by quote id + * + * @param $quoteId + * @return array + */ + public function getIncrementIdsByQuoteId($quoteId) + { + $orderIds = []; + $orders = $this->getOrderCollection('quote_id', $quoteId); + foreach ($orders as $order) { + $realOrderId = $order->getRealOrderId(); + $orderIds[] = $realOrderId; + } + return $orderIds; + } + + /** + * Save order with order repository + * + * @param OrderInterface $order + * @throws Exception + */ + public function saveOrder(OrderInterface $order) + { + try { + $this->magentoOrderRepository->save($order); + } catch (Exception $e) { + $error_exception = "AbstractAction.class SaveOrder : " . $e; + $this->midtransLogger->midtransError($error_exception); + } + } + + /** + * Set order status, state and comment status history + * + * @param Order $order + * @param $status + * @param $order_note + * @return void + * @throws Exception + */ + public function setOrderStateAndStatus(Order $order, $status, $order_note) + { + $order->setState($status); + $order->setStatus($status); + $order->addStatusToHistory($status, $order_note, false); + $this->saveOrder($order); + } + + /** + * Check order is available + * + * @param Order $order + * @return bool + */ + public function canProcess(Order $order) + { + /** + * Do not process if order not found, + * if Log enable, add record to /var/log/midtrans/notification.log + */ + if ($order->isEmpty() || $order === null) { + $_info = "NOTIFICATION: 404 NOT FOUND - Order not found on Magento 2"; + $this->midtransLogger->midtransNotification($_info); + return false; + } else { + return true; + } + } + + /** + * Set payment gateway information to Order + * + * @param Order $order + * @param $trxId + * @param $paymentType + * @throws Exception + */ + public function setPaymentInformation(Order $order, $trxId, $paymentType) + { + $order->getPayment()->setAdditionalInformation('payment_method', strtoupper($paymentType)); + $order->getPayment()->setAdditionalInformation('midtrans_trx_id', $trxId); + $this->saveOrder($order); + } + + /** + * Get payment code by quote id + * + * @param $quoteId + * @return string|null + */ + public function getPaymentCodeByQuoteId($quoteId) + { + $paymentCode = null; + $orders = $this->getOrderCollection('quote_id', $quoteId); + foreach ($orders as $order) { + $paymentCode = $order->getPayment()->getMethod(); + } + return $paymentCode; + } + + /** + * Do cancel order, and set status, state, also comment status history + * + * @param Order $order + * @param $status + * @param $order_note + * @return Order + * @throws Exception + */ + public function cancelOrder(Order $order, $status, $order_note) + { + $order->setState($status); + $order->setStatus($status); + $order->addStatusToHistory($status, $order_note, false); + $order->cancel(); + $this->saveOrder($order); + + return $order; + } + + /** + * Check is order contain virtual product + * + * @param $incrementId + * @return bool + */ + public function isContainVirtualProduct($incrementId) + { + $items = $this->getOrderByIncrementId($incrementId)->getAllItems(); + foreach ($items as $item) { + //look for virtual products + if ($item->getProduct()->getIsVirtual()) { + return true; + } else { + return false; + } + } + } + + /** + * Do generate Invoice + * + * @param Order $order + * @return InvoiceInterface|Order\Invoice + */ + public function generateInvoice(Order $order) + { + try { + if ($order->isEmpty()) { + throw new \Magento\Framework\Exception\LocalizedException(__('MIDTRANS-INFO: The order no longer exists.')); + } + if (!$order->canInvoice()) { + throw new \Magento\Framework\Exception\LocalizedException( + __('MIDTRANS-INFO: The order does not allow an invoice to be created.') + ); + } + + $invoice = $this->invoiceService->prepareInvoice($order); + if (!$invoice) { + throw new \Magento\Framework\Exception\LocalizedException(__('MIDTRANS-INFO: We can\'t save the invoice right now.')); + } + if (!$invoice->getTotalQty()) { + throw new \Magento\Framework\Exception\LocalizedException(__('MIDTRANS-INFO: You can\'t create an invoice without products.')); + } + + $invoice->setTransactionId($order->getIncrementId()); + $invoice->setRequestedCaptureCase(\Magento\Sales\Model\Order\Invoice::CAPTURE_OFFLINE); + // $invoice->pay(); + + $invoice->register(); + $invoice->getOrder()->setCustomerNoteNotify(false); + + //Save Invoice + $transactionSave = $this->transaction + ->addObject($invoice) + ->addObject($invoice->getOrder()); + $transactionSave->save(); + } catch (\Exception $e) { + $this->messageManager->addErrorMessage($e->getMessage()); + } + return $invoice; + } + + /** + * Create credit memo for refund from midtrans dashboard, + * if refund from midtrans dashboard is fullRefund. create credit memo and cancel order. + * + * @param Order $order + * @param $isFullRefund + * @param $refund_note + * @throws \Exception + */ + public function createCreditMemo(Order $order, $isFullRefund, $refund_note) + { + $invoices = $order->getInvoiceCollection(); + $orderId = $order->getIncrementId(); + foreach ($invoices as $invoice) { + $invoiceIncrementId = $invoice->getIncrementId(); + } + if (isset($invoiceIncrementId)) { + $orderInvoice = $this->invoice->loadByIncrementId($invoiceIncrementId); + } else { + $this->midtransLogger->midtransError("AbstractAction.class CreateCreditMemo:: Failed create Credit Memo with order-id " . $orderId . " Invoice not found"); + } + if ($isFullRefund) { + $creditMemo = $this->creditmemoFactory->createByOrder($order); + if (isset($orderInvoice)) { + $creditMemo->setInvoice($orderInvoice); + } else { + $this->midtransLogger->midtransError("AbstractAction.class CreateCreditMemo:: Failed create Credit Memo with order-id " . $orderId . " Invoice not found"); + } + $creditMemo->setState(Order\Creditmemo::STATE_REFUNDED); + } + try { + if (isset($creditMemo)) { + $this->creditmemoRepository->save($creditMemo); + } else { + $this->midtransLogger->midtransError("AbstractAction.class CreateCreditMemo:: Failed create Credit Memo with order-id " . $orderId . " Invoice not found"); + } + } catch (CouldNotSaveException $e) { + $error_exception = "AbstractAction.class saveCreditMemo :" . $e; + $this->midtransLogger->midtransError($error_exception); + } + } +} diff --git a/Model/PaymentRequestRepository.php b/Model/PaymentRequestRepository.php new file mode 100644 index 0000000..9349cea --- /dev/null +++ b/Model/PaymentRequestRepository.php @@ -0,0 +1,487 @@ +paymentOrderRepository = $paymentOrderRepository; + $this->midtransDataConfig = $midtransDataConfig; + $this->utils = $utils; + } + + /** + * do create billing address for payload request to Midtrans + * + * @param $order_billing_address + * @return array + */ + public function payloadBillingAddress($order_billing_address) + { + $payload_billing_address = []; + $payload_billing_address['first_name'] = $order_billing_address->getFirstname(); + $payload_billing_address['last_name'] = $order_billing_address->getLastname(); + $payload_billing_address['address'] = $order_billing_address->getStreet()[0]; + $payload_billing_address['city'] = $order_billing_address->getCity(); + $payload_billing_address['postal_code'] = $order_billing_address->getPostcode(); + $payload_billing_address['country_code'] = $this->utils->convert_country_code($order_billing_address->getCountryId()); + $payload_billing_address['phone'] = $order_billing_address->getTelephone(); + return $payload_billing_address; + } + + /** + * do create shipping address for payload request to Midtrans + * + * @param $order_shipping_address + * @return array + */ + public function payloadShippingAddress($order_shipping_address) + { + $shipping_address = []; + $shipping_address['first_name'] = $order_shipping_address->getFirstname(); + $shipping_address['last_name'] = $order_shipping_address->getLastname(); + $shipping_address['address'] = $order_shipping_address->getStreet()[0]; + $shipping_address['city'] = $order_shipping_address->getCity(); + $shipping_address['postal_code'] = $order_shipping_address->getPostcode(); + $shipping_address['phone'] = $order_shipping_address->getTelephone(); + $shipping_address['country_code'] = $this->utils->convert_country_code($order_shipping_address->getCountryId()); + return $shipping_address; + } + + /** + * do create item details for payload request to Midtrans + * + * @param Order $order + * @param $isMultishipping + * @return array + */ + public function payloadItemDetail(Order $order, $isMultishipping) + { + $item_details = []; + $items = $order->getAllItems(); + + $itemPrefix = ''; + if ($isMultishipping) { + $itemPrefix = ' For Order :' . $order->getIncrementId(); + } + foreach ($items as $newItem) { + $newItem = [ + 'id' => 'ITEM-ID ' . $newItem->getProductId() . $itemPrefix, + 'price' => (string)round($newItem->getPrice()), + 'quantity' => (string)round($newItem->getQtyOrdered()), + 'name' => $this->utils->sanitizeItemName($newItem->getName()) + ]; + $item_details[] = $newItem; + } + + if ($order->getDiscountAmount() != 0) { + $couponItem = [ + 'id' => 'DISCOUNT' . $itemPrefix, + 'price' => round($order->getDiscountAmount()), + 'quantity' => 1, + 'name' => 'DISCOUNT' + ]; + $item_details[] = $couponItem; + } + + if ($order->getShippingAmount() > 0) { + $shipping_item = [ + 'id' => 'SHIPPING' . $itemPrefix, + 'price' => round($order->getShippingAmount()), + 'quantity' => 1, + 'name' => 'Shipping Cost' + ]; + $item_details[] = $shipping_item; + } + + if ($order->getShippingTaxAmount() > 0) { + $shipping_tax_item = [ + 'id' => 'SHIPPING_TAX' . $itemPrefix, + 'price' => round($order->getShippingTaxAmount()), + 'quantity' => 1, + 'name' => 'Shipping Tax' + ]; + $item_details[] = $shipping_tax_item; + } + + if ($order->getTaxAmount() > 0) { + $tax_item = [ + 'id' => 'TAX' . $itemPrefix, + 'price' => round($order->getTaxAmount()), + 'quantity' => 1, + 'name' => 'Tax' + ]; + $item_details[] = $tax_item; + } + + if ($order->getBaseGiftCardsAmount() != 0) { + $giftcardAmount = [ + 'id' => 'GIFTCARD' . $itemPrefix, + 'price' => round($order->getBaseGiftCardsAmount() * -1), + 'quantity' => 1, + 'name' => 'GIFTCARD' + ]; + $item_details[] = $giftcardAmount; + } + + if ($order->getBaseCustomerBalanceAmount() != 0) { + $balancAmount = [ + 'id' => 'STORE CREDIT' . $itemPrefix, + 'price' => round($order->getBaseCustomerBalanceAmount() * -1), + 'quantity' => 1, + 'name' => 'STORE CREDIT' + ]; + $item_details[] = $balancAmount; + } + + if ($order->getPaymentFee() != 0) { + $paymentFee = [ + 'id' => 'PAYMENT FEE' . $itemPrefix, + 'price' => round($order->getPaymentFee()), + 'quantity' => 1, + 'name' => 'PAYMENT FEE' + ]; + $item_details[] = $paymentFee; + } + return $item_details; + } + + /** + * do create payload body request for get Midtrans Snap token + * + * @param $config + * @param $paymentCode + * @param null $order + * @param null $multishipping + * @return array payload + * @throws Exception + */ + public function getPayload( + $config, + $paymentCode, + $order = null, + $multishipping = null + ) { + $payloads = []; + $customer_details = []; + $totalPrice = 0; + + $merchantId = $this->midtransDataConfig->getMerchantId($paymentCode); + /** + * Check if request for multishipping + */ + if (isset($multishipping)) { + /** + * 1. find incrementIds by quote id from request + */ + $quoteId = $multishipping['quote_id']; + $incrementIds = $this->paymentOrderRepository->getIncrementIdsByQuoteId($quoteId); + $midtransOrderId = "multishipping-" . $quoteId; + + $item_details = []; + foreach ($incrementIds as $key => $orderId) { + $order = $this->paymentOrderRepository->getOrderByIncrementId($orderId); + $payment = $order->getPayment(); + $payment->setAdditionalInformation('payment_gateway', 'Midtrans'); + $payment->setAdditionalInformation('merchant_id', $merchantId); + $payment->setAdditionalInformation('midtrans_order_id', $midtransOrderId); + //$this->saveOrder($order); + $this->paymentOrderRepository->saveOrder($order); + foreach ($this->payloadItemDetail($order, true) as $item) { + $item_details[] = $item; + } + } + + /** + * Set items cart to item details object for payload + */ + foreach ($item_details as $item) { + $totalPrice += $item['price'] * $item['quantity']; + } + + /** + * Set billing address order to billing address object for payload + */ + $order_billing_address = $order->getBillingAddress(); + $billing_address = $this->payloadBillingAddress($order_billing_address); + + $customer_details['first_name'] = $order_billing_address->getFirstname(); + $customer_details['last_name'] = $order_billing_address->getLastname(); + $customer_details['email'] = $order_billing_address->getEmail(); + $customer_details['phone'] = $order_billing_address->getTelephone(); + $customer_details['billing_address'] = $billing_address; + + /** + * Initial Payload for request to Midtrans Payment + */ + $transaction_details['order_id'] = $midtransOrderId; + $transaction_details['gross_amount'] = $totalPrice; + + $payloads['transaction_details'] = $transaction_details; + $payloads['item_details'] = $item_details; + } else { + /** + * For regular order + */ + $incrementId = $order->getIncrementId(); + + // Get Billing address from order + $order_billing_address = $order->getBillingAddress(); + + // Set additional payment info + $payment = $order->getPayment(); + $payment->setAdditionalInformation('payment_gateway', 'Midtrans'); + $payment->setAdditionalInformation('merchant_id', $merchantId); + $payment->setAdditionalInformation('midtrans_order_id', $incrementId); + $this->paymentOrderRepository->saveOrder($order); + + /** + * Set billing address order to billing address object for payload + */ + $billing_address = $this->payloadBillingAddress($order_billing_address); + + if (!$this->paymentOrderRepository->isContainVirtualProduct($incrementId)) { + //Get Shipping Address from order + $order_shipping_address = $order->getShippingAddress(); + + /** + * Set shipping address order to shipping address object for payload + */ + $shipping_address = $this->payloadShippingAddress($order_shipping_address); + } + + /** + * Set items cart to item details object for payload + */ + $item_details = $this->payloadItemDetail($order, false); + + /** + * Set Customer details object for payload + */ + + $customer_details['billing_address'] = $billing_address; + if (isset($shipping_address)) { + $customer_details['shipping_address'] = $shipping_address; + } + $customer_details['first_name'] = $order_billing_address->getFirstname(); + $customer_details['last_name'] = $order_billing_address->getLastname(); + $customer_details['email'] = $order_billing_address->getEmail(); + $customer_details['phone'] = $order_billing_address->getTelephone(); + $customer_details['billing_address'] = $billing_address; + + foreach ($item_details as $item) { + $totalPrice += $item['price'] * $item['quantity']; + } + + /** + * Initial Payload for request to Midtrans Payment + */ + $transaction_details = []; + $transaction_details['order_id'] = $order->getIncrementId(); + $transaction_details['gross_amount'] = $totalPrice; + + $payloads['transaction_details'] = $transaction_details; + $payloads['item_details'] = $item_details; + } + + $payloads['customer_details'] = $customer_details; + + if ($paymentCode == 'snap') { + $credit_card = $this->getSnapCardConfig($config); + if ($credit_card != null) { + $payloads['credit_card'] = $credit_card; + } + } elseif ($paymentCode == 'specific') { + $credit_card = $this->getSpecificCardConfig($config); + $payloads['credit_card'] = $credit_card; + $enablePayment = $config['enabled_payments']; + if (!empty($enablePayment)) { + $enabled_payments = explode(',', $enablePayment); + $payloads['enabled_payments'] = $enabled_payments; + } + } elseif ($paymentCode == 'installment') { + $minimalAmount = $config['minimal_amount']; + $credit_card = $this->getInstallmentCardConfig($config, $minimalAmount, $totalPrice); + $payloads['enabled_payments'] = ['credit_card']; + $payloads['credit_card'] = $credit_card; + } elseif ($paymentCode == 'offline') { + $minimalAmount = $config['minimal_amount']; + $credit_card = $this->getOfflineCardConfig($config, $minimalAmount, $totalPrice); + $payloads['enabled_payments'] = ['credit_card']; + $payloads['credit_card'] = $credit_card; + } + + if ($config['one_click']) { + $payloads['user_id'] = crypt($order_billing_address->getEmail(), $this->midtransDataConfig->getServerKey($paymentCode)); + } + if (isset($config['custom_expiry'])) { + $customExpiry = explode(" ", $config['custom_expiry']); + $expiry_unit = $customExpiry[1]; + $expiry_duration = (int)$customExpiry[0]; + + $payloads['expiry'] = [ + 'unit' => $expiry_unit, + 'duration' => (int)$expiry_duration + ]; + } + return $payloads; + } + + /** + * Get credit card config for payment code snap + * + * @param $config + * @return array config for snap credit card + */ + protected function getSnapCardConfig($config) + { + $snap_credit_card = []; + $bank = $config['bank']; + if (!empty($bank)) { + $snap_credit_card['bank'] = $bank; + } + $binNumber = $config['bin']; + if (!empty($binNumber)) { + $bin = explode(',', $binNumber); + $snap_credit_card['whitelist_bins'] = $bin; + } + $oneClick = $config['one_click']; + if ($oneClick) { + $snap_credit_card['save_card'] = true; + } + return $snap_credit_card; + } + + /** + * Get credit card config for payment code specific + * + * @param $config + * @return array + */ + protected function getSpecificCardConfig($config) + { + $specific_credit_card = []; + $bank = $config['bank']; + if (!empty($bank)) { + $specific_credit_card['bank'] = $bank; + } + $binNumber = $config['bin']; + if (!empty($binNumber)) { + $bin = explode(',', $binNumber); + $specific_credit_card['whitelist_bins'] = $bin; + } + $oneClick = $config['one_click']; + if ($oneClick) { + $specific_credit_card['save_card'] = true; + } + return $specific_credit_card; + } + + /** + * Get credit card config for payment code installment + * + * @param $config + * @param $minAmount + * @param $totalPrice + * @return array + */ + protected function getInstallmentCardConfig($config, $minAmount, $totalPrice) + { + $installment_credit_card = []; + $oneClick = $config['one_click']; + if ($oneClick) { + $installment_credit_card['save_card'] = true; + } + if ($totalPrice >= $minAmount) { + $terms = [3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36]; + $installment = []; + $installment['required'] = true; + $installment['terms'] = [ + 'bca' => $terms, + 'bri' => $terms, + 'maybank' => $terms, + 'mega' => $terms, + 'bni' => $terms, + 'mandiri' => $terms, + 'cimb' => $terms + ]; + $installment_credit_card['installment'] = $installment; + } + return $installment_credit_card; + } + + /** + * Get credit card config for payment code offline + * + * @param $config + * @param $minAmount + * @param $totalPrice + * @return array + */ + protected function getOfflineCardConfig($config, $minAmount, $totalPrice) + { + $offline_credit_card = []; + $bank = $config['bank']; + if (!empty($bank)) { + $offline_credit_card['bank'] = $bank; + } + + $oneClick = $config['one_click']; + if ($oneClick) { + $offline_credit_card['save_card'] = true; + } + + if ($totalPrice >= $minAmount) { + $installTerms = $config['terms']; + $termsStr = explode(',', $installTerms); + $terms = []; + foreach ($termsStr as $termStr) { + $terms[] = (int)$termStr; + } + + $installment = []; + $installment['required'] = true; + $installment['terms'] = [ + 'offline' => $terms + ]; + + $offline_credit_card['installment'] = $installment; + + //add bin filter + $binFilter = $config['bin']; + if (!empty($binFilter)) { + $whitelist_bin = explode(',', $binFilter); + $offline_credit_card['whitelist_bins'] = $whitelist_bin; + } + } + return $offline_credit_card; + } +} diff --git a/Model/Ui/ConfigProvider.php b/Model/Ui/ConfigProvider.php index dd834d5..db107a3 100755 --- a/Model/Ui/ConfigProvider.php +++ b/Model/Ui/ConfigProvider.php @@ -4,29 +4,29 @@ use Magento\Checkout\Model\ConfigProviderInterface; use Magento\Framework\App\ObjectManager; -use Midtrans\Snap\Helper\Data; +use Midtrans\Snap\Helper\MidtransDataConfiguration; class ConfigProvider implements ConfigProviderInterface { const CODE = 'snap'; - protected $data; + protected $midtransDataConfiguration; - public function __construct(Data $data) + public function __construct(MidtransDataConfiguration $midtransDataConfiguration) { - $this->data = $data; + $this->midtransDataConfiguration = $midtransDataConfiguration; } public function getConfig() { - $production = $this->data->isProduction(); - $clientkey = $this->data->getClientKey(self::CODE); - $merchantid = $this->data->getMerchantId(self::CODE); - $enableredirect = $this->data->isRedirect(); - $mixpanelkey = $this->data->getMixPanelKey(); + $production = $this->midtransDataConfiguration->isProduction(); + $clientkey = $this->midtransDataConfiguration->getClientKey(self::CODE); + $merchantid = $this->midtransDataConfiguration->getMerchantId(self::CODE); + $enableredirect = $this->midtransDataConfiguration->isRedirect(); + $mixpanelkey = $this->midtransDataConfiguration->getMixPanelKey(); $magentoversion = ObjectManager::getInstance()->get('Magento\Framework\App\ProductMetadataInterface')->getVersion(); - $pluginversion = $this->data->getModuleVersion(); + $pluginversion = $this->midtransDataConfiguration->getModuleVersion(); return [ 'payment' => [ diff --git a/Model/Ui/InstallmentConfigProvider.php b/Model/Ui/InstallmentConfigProvider.php index bcc232e..b0298f4 100644 --- a/Model/Ui/InstallmentConfigProvider.php +++ b/Model/Ui/InstallmentConfigProvider.php @@ -4,20 +4,20 @@ use Magento\Checkout\Model\ConfigProviderInterface; use Magento\Framework\App\ObjectManager; -use Midtrans\Snap\Helper\Data; +use Midtrans\Snap\Helper\MidtransDataConfiguration; class InstallmentConfigProvider implements ConfigProviderInterface { const CODE = 'installment'; - protected $data; + protected $midtransDataConfiguration; /** * SpecificConfigProvider constructor. - * @param $data + * @param $midtransDataConfiguration */ - public function __construct(Data $data) + public function __construct(MidtransDataConfiguration $midtransDataConfiguration) { - $this->data = $data; + $this->midtransDataConfiguration = $midtransDataConfiguration; } /** @@ -26,15 +26,15 @@ public function __construct(Data $data) */ public function getConfig() { - $production = $this->data->isProduction(); - $clientkey = $this->data->getClientKey(self::CODE); - $merchantid = $this->data->getMerchantId(self::CODE); - $enableredirect = $this->data->isRedirect(); - $mixpanelkey = $this->data->getMixPanelKey(); + $production = $this->midtransDataConfiguration->isProduction(); + $clientkey = $this->midtransDataConfiguration->getClientKey(self::CODE); + $merchantid = $this->midtransDataConfiguration->getMerchantId(self::CODE); + $enableredirect = $this->midtransDataConfiguration->isRedirect(); + $mixpanelkey = $this->midtransDataConfiguration->getMixPanelKey(); $magentoversion = ObjectManager::getInstance()->get('Magento\Framework\App\ProductMetadataInterface')->getVersion(); - $pluginversion = $this->data->getModuleVersion(); + $pluginversion = $this->midtransDataConfiguration->getModuleVersion(); return [ 'payment' => [ diff --git a/Model/Ui/OfflineConfigProvider.php b/Model/Ui/OfflineConfigProvider.php index 8a8a60b..af8afca 100644 --- a/Model/Ui/OfflineConfigProvider.php +++ b/Model/Ui/OfflineConfigProvider.php @@ -4,20 +4,20 @@ use Magento\Checkout\Model\ConfigProviderInterface; use Magento\Framework\App\ObjectManager; -use Midtrans\Snap\Helper\Data; +use Midtrans\Snap\Helper\MidtransDataConfiguration; class OfflineConfigProvider implements ConfigProviderInterface { const CODE = 'offline'; - protected $data; + protected $midtransDataConfiguration; /** * SpecificConfigProvider constructor. - * @param $data + * @param $midtransDataConfiguration */ - public function __construct(Data $data) + public function __construct(MidtransDataConfiguration $midtransDataConfiguration) { - $this->data = $data; + $this->midtransDataConfiguration = $midtransDataConfiguration; } /** @@ -26,15 +26,15 @@ public function __construct(Data $data) */ public function getConfig() { - $production = $this->data->isProduction(); - $clientkey = $this->data->getClientKey(self::CODE); - $merchantid = $this->data->getMerchantId(self::CODE); - $enableredirect = $this->data->isRedirect(); - $mixpanelkey = $this->data->getMixPanelKey(); + $production = $this->midtransDataConfiguration->isProduction(); + $clientkey = $this->midtransDataConfiguration->getClientKey(self::CODE); + $merchantid = $this->midtransDataConfiguration->getMerchantId(self::CODE); + $enableredirect = $this->midtransDataConfiguration->isRedirect(); + $mixpanelkey = $this->midtransDataConfiguration->getMixPanelKey(); $magentoversion = ObjectManager::getInstance()->get('Magento\Framework\App\ProductMetadataInterface')->getVersion(); - $pluginversion = $this->data->getModuleVersion(); + $pluginversion = $this->midtransDataConfiguration->getModuleVersion(); return [ 'payment' => [ diff --git a/Model/Ui/SpecificConfigProvider.php b/Model/Ui/SpecificConfigProvider.php index e11c76b..46f1219 100644 --- a/Model/Ui/SpecificConfigProvider.php +++ b/Model/Ui/SpecificConfigProvider.php @@ -4,20 +4,20 @@ use Magento\Checkout\Model\ConfigProviderInterface; use Magento\Framework\App\ObjectManager; -use Midtrans\Snap\Helper\Data; +use Midtrans\Snap\Helper\MidtransDataConfiguration; class SpecificConfigProvider implements ConfigProviderInterface { const CODE = 'specific'; - protected $data; + protected $midtransDataConfiguration; /** * SpecificConfigProvider constructor. - * @param $data + * @param $midtransDataConfiguration */ - public function __construct(Data $data) + public function __construct(MidtransDataConfiguration $midtransDataConfiguration) { - $this->data = $data; + $this->midtransDataConfiguration = $midtransDataConfiguration; } /** @@ -26,15 +26,15 @@ public function __construct(Data $data) */ public function getConfig() { - $production = $this->data->isProduction(); - $clientkey = $this->data->getClientKey(self::CODE); - $merchantid = $this->data->getMerchantId(self::CODE); - $enableredirect = $this->data->isRedirect(); - $mixpanelkey = $this->data->getMixPanelKey(); + $production = $this->midtransDataConfiguration->isProduction(); + $clientkey = $this->midtransDataConfiguration->getClientKey(self::CODE); + $merchantid = $this->midtransDataConfiguration->getMerchantId(self::CODE); + $enableredirect = $this->midtransDataConfiguration->isRedirect(); + $mixpanelkey = $this->midtransDataConfiguration->getMixPanelKey(); $magentoversion = ObjectManager::getInstance()->get('Magento\Framework\App\ProductMetadataInterface')->getVersion(); - $pluginversion = $this->data->getModuleVersion(); + $pluginversion = $this->midtransDataConfiguration->getModuleVersion(); return [ 'payment' => [ diff --git a/README.md b/README.md index 51183be..5a6ea9a 100644 --- a/README.md +++ b/README.md @@ -167,7 +167,31 @@ If you make refund from the Midtrans Dashboard, Refund notification is sent to M The status change may not mean that the refund has carried out successfully on Midtrans side. When the transaction status in Midtrans dashboard changes to REFUND, then the refund went through successfully + +### Configuring Custom Payment Fee +In case you need it, payment fee can optionally be added by using additional (3rd party) extension: [Mageprince Magento2 PaymentFee](https://github.com/mageprince/magento2-paymentfee/) extension. It allows adding extra charges for specific payment methods and displays them on the cart page, checkout page, invoice, and credit memo. + +All the fee calculation will be handled by that extension. Midtrans extension will then take the produced PaymentFee value (from Magento order object), and parse it into additional item object for Midtrans API param. + +#### Example on how to use the PaymentFee Extension +
Click to expand info +
+You can try the demo extension [here](https://github.com/mageprince/magento2-paymentfee/#demo) + +#### Installation & Configuration Instructions +You can install from [Magento Marketplace](https://marketplace.magento.com/prince-magento2-paymentfee.html) or follow [the manual installation step](https://github.com/mageprince/magento2-paymentfee/#installation-instruction). + +#### How to configure the extension +1. Go to menu `Stores -> Configuration -> MagePrince -> Payment Fee` +2. Set Enable field to `Yes` +3. Go to `Payment Fee Settings` section, choose `PriceType`, Fill in `Minimum` and `Maximum order amount`. +4. Click `Add Fee` button, then choose Midtrans Payment method on `Payment Method Fee` and Fill in `Fee Amount` +5. Click `Save Config` + +This is just an example configuration that works at the time of this writing, the extension may change in the future. For further & most up to date details of Mageprince PaymentFee extension configuration, you can check [the official extension documentation](https://github.com/mageprince/magento2-paymentfee/#configuration). +
+ #### Get help * [General Documentation Midtrans](http://docs.midtrans.com) diff --git a/composer.json b/composer.json index 07ac377..82f72e9 100755 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "midtrans/snap", "description": "Midtrans Accept Online Payment Magento 2 module", "type": "magento2-module", - "version": "2.5.4", + "version": "2.6.4", "authors" : [ { "name": "Zaki Ibrahim", diff --git a/etc/di.xml b/etc/di.xml index 3ab0c4f..4f6363f 100755 --- a/etc/di.xml +++ b/etc/di.xml @@ -270,7 +270,7 @@ Magento\Framework\App\Config\ScopeConfigInterface - Midtrans\Snap\Helper\Data + Midtrans\Snap\Helper\MidtransDataConfiguration diff --git a/view/frontend/layout/snap_index_finish.xml b/view/frontend/layout/snap_index_finish.xml index 6ffbc67..350a117 100755 --- a/view/frontend/layout/snap_index_finish.xml +++ b/view/frontend/layout/snap_index_finish.xml @@ -15,7 +15,7 @@ - + - \ No newline at end of file + diff --git a/view/frontend/templates/finish.phtml b/view/frontend/templates/finish.phtml index 9a91c73..5de69d3 100755 --- a/view/frontend/templates/finish.phtml +++ b/view/frontend/templates/finish.phtml @@ -3,12 +3,13 @@ * @var \Midtrans\Snap\Block\Finish $block */ ?> + getDataTransaction(); ?>

Payment Success!

-

We have received your payment, your order is being processed. Thank you!

+

We have received your payment, your payment number # is being processed. Thank you!

@@ -17,14 +18,14 @@ // BCA Klikpay specific, all non-settlement are considered failure ?>
-

Payment Failed

+

Payment Failed for payment number #

Sorry, we are unable to receive your payment.


-

Your Order # is Awaiting Your Payment

+

Your payment number # is Awaiting Your Payment

Please complete the payment as instructed earlier. Thank you!

@@ -33,7 +34,8 @@ ?>

Payment Is Not Received

-

Your payment is not yet completed. Please complete your payment or do another checkout. Thank you!

+

Your payment for payment number # is not yet completed. Please complete your payment or do another checkout. Thank you!

+