From aa4ee211b0703453c7523badee34092dacc6f547 Mon Sep 17 00:00:00 2001 From: mayankamencherla Date: Wed, 2 Aug 2017 22:10:40 +0530 Subject: [PATCH 01/17] [webhooks] Style guide fixes for php / js --- Controller/BaseController.php | 12 ++++++++---- Model/Config.php | 14 ++++++++------ Model/ConfigProvider.php | 6 ++++-- Model/PaymentMethod.php | 16 +++++++++++----- .../payment/method-renderer/razorpay-method.js | 6 ++---- 5 files changed, 33 insertions(+), 21 deletions(-) diff --git a/Controller/BaseController.php b/Controller/BaseController.php index 9560f867..f7305ef8 100644 --- a/Controller/BaseController.php +++ b/Controller/BaseController.php @@ -47,7 +47,8 @@ public function __construct( \Magento\Customer\Model\Session $customerSession, \Magento\Checkout\Model\Session $checkoutSession, \Razorpay\Magento\Model\Config $config - ) { + ) + { parent::__construct($context); $this->customerSession = $customerSession; $this->checkoutSession = $checkoutSession; @@ -68,7 +69,8 @@ public function __construct( protected function initCheckout() { $quote = $this->getQuote(); - if (!$quote->hasItems() || $quote->getHasError()) { + if (!$quote->hasItems() || $quote->getHasError()) + { $this->getResponse()->setStatusHeader(403, '1.1', 'Forbidden'); throw new \Magento\Framework\Exception\LocalizedException(__('We can\'t initialize checkout.')); } @@ -81,7 +83,8 @@ protected function initCheckout() */ protected function getQuote() { - if (!$this->quote) { + if (!$this->quote) + { $this->quote = $this->checkoutSession->getQuote(); } return $this->quote; @@ -92,7 +95,8 @@ protected function getQuote() */ protected function getCheckout() { - if (!$this->checkout) { + if (!$this->checkout) + { $this->checkout = $this->checkoutFactory->create( [ 'params' => [ diff --git a/Model/Config.php b/Model/Config.php index 0a392255..a7a9cb50 100644 --- a/Model/Config.php +++ b/Model/Config.php @@ -31,9 +31,8 @@ class Config /** * @param ScopeConfigInterface $scopeConfig */ - public function __construct( - ScopeConfigInterface $scopeConfig - ) { + public function __construct(ScopeConfigInterface $scopeConfig) + { $this->scopeConfig = $scopeConfig; } @@ -70,7 +69,8 @@ public function setStoreId($storeId) */ public function getConfigData($field, $storeId = null) { - if ($storeId == null) { + if ($storeId == null) + { $storeId = $this->storeId; } @@ -99,9 +99,11 @@ public function canUseForCountry($country) /* for specific country, the flag will set up as 1 */ - if ($this->getConfigData(self::KEY_ALLOW_SPECIFIC) == 1) { + if ($this->getConfigData(self::KEY_ALLOW_SPECIFIC) == 1) + { $availableCountries = explode(',', $this->getConfigData(self::KEY_SPECIFIC_COUNTRY)); - if (!in_array($country, $availableCountries)) { + if (!in_array($country, $availableCountries)) + { return false; } } diff --git a/Model/ConfigProvider.php b/Model/ConfigProvider.php index dc53c9d0..3831bec3 100644 --- a/Model/ConfigProvider.php +++ b/Model/ConfigProvider.php @@ -49,7 +49,8 @@ public function __construct( \Razorpay\Magento\Model\Config $config, \Magento\Checkout\Model\Session $checkoutSession, \Magento\Customer\Model\Session $customerSession - ) { + ) + { $this->assetRepo = $assetRepo; $this->request = $request; $this->urlBuilder = $urlBuilder; @@ -66,7 +67,8 @@ public function __construct( */ public function getConfig() { - if (!$this->config->isActive()) { + if (!$this->config->isActive()) + { return []; } diff --git a/Model/PaymentMethod.php b/Model/PaymentMethod.php index d38a1320..0a2473a3 100644 --- a/Model/PaymentMethod.php +++ b/Model/PaymentMethod.php @@ -132,7 +132,8 @@ public function __construct( \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [] - ) { + ) + { parent::__construct( $context, $registry, @@ -171,13 +172,17 @@ public function __construct( public function validate() { $info = $this->getInfoInstance(); - if ($info instanceof \Magento\Sales\Model\Order\Payment) { + if ($info instanceof \Magento\Sales\Model\Order\Payment) + { $billingCountry = $info->getOrder()->getBillingAddress()->getCountryId(); - } else { + } + else + { $billingCountry = $info->getQuote()->getBillingAddress()->getCountryId(); } - if (!$this->config->canUseForCountry($billingCountry)) { + if (!$this->config->canUseForCountry($billingCountry)) + { throw new LocalizedException(__('Selected payment type is not allowed for billing country.')); } @@ -275,7 +280,8 @@ protected function getChannel() */ public function getConfigData($field, $storeId = null) { - if ('order_place_redirect_url' === $field) { + if ('order_place_redirect_url' === $field) + { return $this->getOrderPlaceRedirectUrl(); } return $this->config->getConfigData($field, $storeId); diff --git a/view/frontend/web/js/view/payment/method-renderer/razorpay-method.js b/view/frontend/web/js/view/payment/method-renderer/razorpay-method.js index 9201075c..aa16b4bc 100644 --- a/view/frontend/web/js/view/payment/method-renderer/razorpay-method.js +++ b/view/frontend/web/js/view/payment/method-renderer/razorpay-method.js @@ -94,8 +94,7 @@ define( if (!customer.isLoggedIn()) { this.user.email = quote.guestEmail; } - else - { + else { this.user.email = customer.customerData.email; } @@ -176,8 +175,7 @@ define( } }; - if (data.quote_currency !== 'INR') - { + if (data.quote_currency !== 'INR') { options.display_currency = data.quote_currency; options.display_amount = data.quote_amount; } From d614949851dd687652746f943c9a6c2424d9b1c6 Mon Sep 17 00:00:00 2001 From: mayankamencherla Date: Thu, 3 Aug 2017 19:30:54 +0530 Subject: [PATCH 02/17] [webhooks] Added a controller class called Webhook.php to receive webhook post data --- Controller/Payment/Webhook.php | 43 ++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 Controller/Payment/Webhook.php diff --git a/Controller/Payment/Webhook.php b/Controller/Payment/Webhook.php new file mode 100644 index 00000000..673e9f42 --- /dev/null +++ b/Controller/Payment/Webhook.php @@ -0,0 +1,43 @@ +checkoutFactory = $checkoutFactory; + $this->catalogSession = $catalogSession; + } + + /** + * Webhook posts data to this url + */ + public function execute() + { + + } +} \ No newline at end of file From b3fc2c7f52df007085858520c0190776c3265d3f Mon Sep 17 00:00:00 2001 From: mayankamencherla Date: Fri, 4 Aug 2017 13:35:25 +0530 Subject: [PATCH 03/17] [webhooks] Added paymentAuthorized webhook functionality --- Controller/Payment/Order.php | 3 +- Controller/Payment/Webhook.php | 91 +++++++++++++++++++++++++++++++--- 2 files changed, 87 insertions(+), 7 deletions(-) diff --git a/Controller/Payment/Order.php b/Controller/Payment/Order.php index e9f690d8..93f86def 100644 --- a/Controller/Payment/Order.php +++ b/Controller/Payment/Order.php @@ -26,7 +26,8 @@ public function __construct( \Razorpay\Magento\Model\CheckoutFactory $checkoutFactory, \Razorpay\Magento\Model\Config $config, \Magento\Catalog\Model\Session $catalogSession - ) { + ) + { parent::__construct( $context, $customerSession, diff --git a/Controller/Payment/Webhook.php b/Controller/Payment/Webhook.php index 673e9f42..ba1fca6c 100644 --- a/Controller/Payment/Webhook.php +++ b/Controller/Payment/Webhook.php @@ -2,16 +2,34 @@ namespace Razorpay\Magento\Controller\Payment; -class Webhook extends Razorpay\Magento\Controller\BaseController +class Webhook extends \Razorpay\Magento\Controller\BaseController { + /** + * @var \Magento\Checkout\Model\Session + */ protected $checkoutSession; + /** + * @var \Magento\Quote\Model\QuoteRepository + */ + protected $quoteRepository; + + /** + * @var \Magento\Sales\Api\Data\OrderInterface + */ + protected $order; + + const STATUS_APPROVED = 'APPROVED'; + /** * @param \Magento\Framework\App\Action\Context $context * @param \Magento\Customer\Model\Session $customerSession * @param \Magento\Checkout\Model\Session $checkoutSession * @param \Magento\Razorpay\Model\CheckoutFactory $checkoutFactory - * @param \Magento\Razorpay\Model\Config\Payment $razorpayConfig + * @param \Razorpay\Magento\Model\Config $config + * @param \Magento\Catalog\Model\Session $catalogSession + * @param \Magento\Quote\Model\QuoteRepository $quoteRepository, + * @param \Magento\Sales\Api\Data\OrderInterface $order */ public function __construct( \Magento\Framework\App\Action\Context $context, @@ -19,8 +37,10 @@ public function __construct( \Magento\Checkout\Model\Session $checkoutSession, \Razorpay\Magento\Model\CheckoutFactory $checkoutFactory, \Razorpay\Magento\Model\Config $config, - \Magento\Catalog\Model\Session $catalogSession - ) + \Magento\Catalog\Model\Session $catalogSession, + \Magento\Quote\Model\QuoteRepository $quoteRepository, + \Magento\Sales\Api\Data\OrderInterface $order + ) { parent::__construct( $context, @@ -29,15 +49,74 @@ public function __construct( $config ); + $this->order = $order; $this->checkoutFactory = $checkoutFactory; - $this->catalogSession = $catalogSession; + $this->catalogSession = $catalogSession; + $this->quoteRepository = $quoteRepository; } /** - * Webhook posts data to this url + * Processes the incoming webhook */ public function execute() { + $post = $this->getPostData(); + + if (json_last_error() !== 0) + { + return; + } + + // + // Need to add signature verification + // + switch ($post['event']) + { + case 'payment.authorized': + return $this->paymentAuthorized($post); + + default: + return; + } + } + + /** + * Payment Authorized webhook + * + * @param array $post + */ + protected function paymentAuthorized(array $post) + { + $quoteId = $post['payload']['payment']['entity']['notes']['merchant_order_id']; + $amount = $post['payload']['payment']['entity']['amount']; + $paymentId = $post['payload']['payment']['entity']['id']; + + $quote = $this->quoteRepository->get($quoteId); + + $orderId = $quote->getReservedOrderId(); + + $order = $this->order->loadByIncrementId($orderId); + + $payment = $order->getPayment(); + + $payment->setStatus(self::STATUS_APPROVED) + ->setAmountPaid($amount) + ->setLastTransId($paymentId) + ->setTransactionId($paymentId) + ->setIsTransactionClosed(true) + ->setShouldCloseParentTransaction(true); + + $order->setStatus('processing'); + $order->save(); + } + + /** + * @return Webhook post data as an array + */ + protected function getPostData() : array + { + $request = file_get_contents('php://input'); + return json_decode($request, true); } } \ No newline at end of file From 50449bcae53bb6d6439e0ff6702d7e2eaf67a215 Mon Sep 17 00:00:00 2001 From: mayankamencherla Date: Sat, 5 Aug 2017 18:19:57 +0530 Subject: [PATCH 04/17] [webhooks] Code quality fixes --- Controller/Payment/Order.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Controller/Payment/Order.php b/Controller/Payment/Order.php index 93f86def..a443c6b1 100644 --- a/Controller/Payment/Order.php +++ b/Controller/Payment/Order.php @@ -43,21 +43,21 @@ public function execute() { $amount = (int) (round($this->getQuote()->getBaseGrandTotal(), 2) * 100); - $receipt_id = $this->getQuote()->getId(); + $receiptId = $this->getQuote()->getId(); $code = 400; try { $order = $this->rzp->order->create([ - 'amount' => $amount, - 'receipt' => $receipt_id, - 'currency' => $this->_currency, - 'payment_capture' => 1 // auto-capture + 'amount' => $amount, + 'receipt' => $receiptId, + 'currency' => $this->_currency, + 'payment_capture' => 1 ]); $responseContent = [ - 'message' => 'Unable to create your order. Please contact support.', + 'message' => 'Unable to create your order. Please contact support.', 'parameters' => [] ]; @@ -66,7 +66,7 @@ public function execute() $responseContent = [ 'success' => true, 'rzp_order' => $order->id, - 'order_id' => $receipt_id, + 'order_id' => $receiptId, 'amount' => $order->amount, 'quote_currency' => $this->getQuote()->getQuoteCurrencyCode(), 'quote_amount' => round($this->getQuote()->getGrandTotal(), 2) @@ -80,14 +80,14 @@ public function execute() catch(\Razorpay\Api\Errors\Error $e) { $responseContent = [ - 'message' => $e->getMessage(), + 'message' => $e->getMessage(), 'parameters' => [] ]; } catch(\Exception $e) { $responseContent = [ - 'message' => $e->getMessage(), + 'message' => $e->getMessage(), 'parameters' => [] ]; } From a0e6f39279bdb4634f7e515f1666e9f9a3fc5a1e Mon Sep 17 00:00:00 2001 From: mayankamencherla Date: Sat, 5 Aug 2017 18:55:36 +0530 Subject: [PATCH 05/17] [webhooks] Added webhook config to plugin config --- etc/adminhtml/system.xml | 5 +++++ etc/config.xml | 1 + 2 files changed, 6 insertions(+) diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index a349ea24..ae76af32 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -15,6 +15,11 @@ Razorpay\Magento\Model\PaymentAction payment/razorpay/payment_action + + + ** {baseUrl}/razorpay/payment/webhook ** + Magento\Config\Model\Config\Source\Yesno + payment/razorpay/enable_webhook diff --git a/etc/config.xml b/etc/config.xml index 15c9894c..1580a2c0 100644 --- a/etc/config.xml +++ b/etc/config.xml @@ -7,6 +7,7 @@ Razorpay Magento authorize + 0 0 processing Key ID From 506bf07a17618dbd4403800127eccbb517cdd7e6 Mon Sep 17 00:00:00 2001 From: mayankamencherla Date: Mon, 7 Aug 2017 14:46:45 +0530 Subject: [PATCH 06/17] [webhooks] If order is already processing, don't process webhook --- Controller/Payment/Webhook.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Controller/Payment/Webhook.php b/Controller/Payment/Webhook.php index ba1fca6c..3d387d25 100644 --- a/Controller/Payment/Webhook.php +++ b/Controller/Payment/Webhook.php @@ -21,6 +21,8 @@ class Webhook extends \Razorpay\Magento\Controller\BaseController const STATUS_APPROVED = 'APPROVED'; + const ORDER_PROCESSING = 'processing'; + /** * @param \Magento\Framework\App\Action\Context $context * @param \Magento\Customer\Model\Session $customerSession @@ -93,10 +95,20 @@ protected function paymentAuthorized(array $post) $quote = $this->quoteRepository->get($quoteId); + // + // We reserve a new order id if one is not reserved already + // + $quote->reserveOrderId(); + $orderId = $quote->getReservedOrderId(); $order = $this->order->loadByIncrementId($orderId); + if ($order->getStatus() === self::ORDER_PROCESSING) + { + return; + } + $payment = $order->getPayment(); $payment->setStatus(self::STATUS_APPROVED) @@ -106,7 +118,7 @@ protected function paymentAuthorized(array $post) ->setIsTransactionClosed(true) ->setShouldCloseParentTransaction(true); - $order->setStatus('processing'); + $order->setStatus(self::ORDER_PROCESSING); $order->save(); } From 886c8e0a244b134b7a1eaa807b0268b62e56f594 Mon Sep 17 00:00:00 2001 From: mayankamencherla Date: Mon, 7 Aug 2017 14:48:47 +0530 Subject: [PATCH 07/17] [webhooks] If webhook processes order first, don't process regular payment --- Model/PaymentMethod.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Model/PaymentMethod.php b/Model/PaymentMethod.php index 0a2473a3..189ca930 100644 --- a/Model/PaymentMethod.php +++ b/Model/PaymentMethod.php @@ -24,6 +24,7 @@ class PaymentMethod extends \Magento\Payment\Model\Method\AbstractMethod const METHOD_CODE = 'razorpay'; const CONFIG_MASKED_FIELDS = 'masked_fields'; const CURRENCY = 'INR'; + const ORDER_PROCESSING = 'processing'; /** * @var string @@ -203,6 +204,12 @@ public function authorize(InfoInterface $payment, $amount) { /** @var \Magento\Sales\Model\Order\Payment $payment */ $order = $payment->getOrder(); + + if ($order->getStatus() === self::ORDER_PROCESSING) + { + return; + } + $orderId = $order->getIncrementId(); $request = $this->getPostData(); @@ -217,6 +224,9 @@ public function authorize(InfoInterface $payment, $amount) ->setTransactionId($payment_id) ->setIsTransactionClosed(true) ->setShouldCloseParentTransaction(true); + + $order->setStatus(self::ORDER_PROCESSING); + $order->save(); } catch (\Exception $e) { From d93d70799f828456550f5d0d4747ada09c7d41f4 Mon Sep 17 00:00:00 2001 From: mayankamencherla Date: Mon, 7 Aug 2017 14:49:59 +0530 Subject: [PATCH 08/17] [webhooks] Using one line of code to reservOrderId and get the orderid --- Controller/Payment/Webhook.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Controller/Payment/Webhook.php b/Controller/Payment/Webhook.php index 3d387d25..41455b12 100644 --- a/Controller/Payment/Webhook.php +++ b/Controller/Payment/Webhook.php @@ -98,9 +98,7 @@ protected function paymentAuthorized(array $post) // // We reserve a new order id if one is not reserved already // - $quote->reserveOrderId(); - - $orderId = $quote->getReservedOrderId(); + $orderId = $quote->reserveOrderId()->getReservedOrderId(); $order = $this->order->loadByIncrementId($orderId); From 49ae63349ad869fee6dd4e8859d6253d96fe7570 Mon Sep 17 00:00:00 2001 From: mayankamencherla Date: Mon, 7 Aug 2017 15:15:35 +0530 Subject: [PATCH 09/17] [webhooks] Verifying webhook signature before processing webhook payload --- Controller/Payment/Webhook.php | 56 ++++++++++++++++++++++++++++------ Model/Config.php | 22 ++++++++++--- etc/adminhtml/system.xml | 5 +++ etc/config.xml | 1 + 4 files changed, 70 insertions(+), 14 deletions(-) diff --git a/Controller/Payment/Webhook.php b/Controller/Payment/Webhook.php index 41455b12..56a4e765 100644 --- a/Controller/Payment/Webhook.php +++ b/Controller/Payment/Webhook.php @@ -2,6 +2,10 @@ namespace Razorpay\Magento\Controller\Payment; +use Razorpay\Api\Api; +use Razorpay\Api\Errors; +use Razorpay\Magento\Model\Config; + class Webhook extends \Razorpay\Magento\Controller\BaseController { /** @@ -19,6 +23,8 @@ class Webhook extends \Razorpay\Magento\Controller\BaseController */ protected $order; + protected $api; + const STATUS_APPROVED = 'APPROVED'; const ORDER_PROCESSING = 'processing'; @@ -51,6 +57,10 @@ public function __construct( $config ); + $keyId = $this->config->getConfigData(Config::KEY_PUBLIC_KEY); + $keySecret = $this->config->getConfigData(Config::KEY_PRIVATE_KEY); + $this->api = new Api($keyId, $keySecret); + $this->order = $order; $this->checkoutFactory = $checkoutFactory; $this->catalogSession = $catalogSession; @@ -69,16 +79,44 @@ public function execute() return; } - // - // Need to add signature verification - // - switch ($post['event']) + if ($this->config->isWebhookEnabled() && empty($post['event']) === false) { - case 'payment.authorized': - return $this->paymentAuthorized($post); - - default: - return; + if (isset($_SERVER['HTTP_X_RAZORPAY_SIGNATURE']) === true) + { + $webhookSecret = $this->config->getWebhookSecret(); + + // + // To accept webhooks, the merchant must configure + // it on the magento backend by setting the secret + // + if (empty($webhookSecret) === true) + { + return; + } + + try + { + $this->api->utility->verifyWebhookSignature($post, + $_SERVER['HTTP_X_RAZORPAY_SIGNATURE'], + $webhookSecret); + } + catch (Errors\SignatureVerificationError $e) + { + // + // Need to log the error + // + return; + } + + switch ($post['event']) + { + case 'payment.authorized': + return $this->paymentAuthorized($post); + + default: + return; + } + } } } diff --git a/Model/Config.php b/Model/Config.php index a7a9cb50..4929f185 100644 --- a/Model/Config.php +++ b/Model/Config.php @@ -6,12 +6,14 @@ class Config { - const KEY_ALLOW_SPECIFIC = 'allowspecific'; - const KEY_SPECIFIC_COUNTRY = 'specificcountry'; - const KEY_ACTIVE = 'active'; - const KEY_PUBLIC_KEY = 'key_id'; - const KEY_PRIVATE_KEY = 'key_secret'; + const KEY_ALLOW_SPECIFIC = 'allowspecific'; + const KEY_SPECIFIC_COUNTRY = 'specificcountry'; + const KEY_ACTIVE = 'active'; + const KEY_PUBLIC_KEY = 'key_id'; + const KEY_PRIVATE_KEY = 'key_secret'; const KEY_MERCHANT_NAME_OVERRIDE = 'merchant_name_override'; + const ENABLE_WEBHOOK = 'enable_webhook'; + const WEBHOOK_SECRET = 'webhook_secret'; /** * @var string @@ -49,6 +51,16 @@ public function getKeyId() return $this->getConfigData(self::KEY_PUBLIC_KEY); } + public function isWebhookEnabled() + { + return ($this->getConfigData(self::ENABLE_WEBHOOK) === 1); + } + + public function getWebhookSecret() + { + return $this->getConfigData(self::WEBHOOK_SECRET); + } + /** * @param int $storeId * @return $this diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index ae76af32..945413f4 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -21,6 +21,11 @@ Magento\Config\Model\Config\Source\Yesno payment/razorpay/enable_webhook + + + ** If Enable Webhook is set to true, this field needs to match the field set here. ** + payment/razorpay/webhook_secret + payment/razorpay/title diff --git a/etc/config.xml b/etc/config.xml index 1580a2c0..5d42f070 100644 --- a/etc/config.xml +++ b/etc/config.xml @@ -8,6 +8,7 @@ Magento authorize 0 + 0 processing Key ID From 48bb23670200d6e3bc43c392c7a9e3e615cf6c7b Mon Sep 17 00:00:00 2001 From: mayankamencherla Date: Mon, 7 Aug 2017 15:42:33 +0530 Subject: [PATCH 10/17] [webhooks] Webhook secret config description changed --- etc/adminhtml/system.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 945413f4..963b9225 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -23,7 +23,7 @@ - ** If Enable Webhook is set to true, this field needs to match the field set here. ** + This field has to match the one set in dashboard.razorpay.com/webhooks payment/razorpay/webhook_secret From 5c7c98633bf2e03287d0b62b65c7d09639f13ff0 Mon Sep 17 00:00:00 2001 From: mayankamencherla Date: Mon, 7 Aug 2017 16:01:59 +0530 Subject: [PATCH 11/17] [webhooks] verifying webhook signature using a json encoded string of the post data --- Controller/Payment/Webhook.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Controller/Payment/Webhook.php b/Controller/Payment/Webhook.php index 56a4e765..d99a8d3e 100644 --- a/Controller/Payment/Webhook.php +++ b/Controller/Payment/Webhook.php @@ -79,7 +79,8 @@ public function execute() return; } - if ($this->config->isWebhookEnabled() && empty($post['event']) === false) + if (($this->config->isWebhookEnabled() === true) && + (empty($post['event']) === false)) { if (isset($_SERVER['HTTP_X_RAZORPAY_SIGNATURE']) === true) { @@ -96,7 +97,7 @@ public function execute() try { - $this->api->utility->verifyWebhookSignature($post, + $this->api->utility->verifyWebhookSignature(json_encode($post), $_SERVER['HTTP_X_RAZORPAY_SIGNATURE'], $webhookSecret); } From ca7addcf8e124ed630001c21d21edaa6bb4bcdb6 Mon Sep 17 00:00:00 2001 From: mayankamencherla Date: Mon, 7 Aug 2017 16:22:26 +0530 Subject: [PATCH 12/17] [webhooks] Logging warning when webhook signature verification failes --- Controller/Payment/Webhook.php | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Controller/Payment/Webhook.php b/Controller/Payment/Webhook.php index d99a8d3e..724910ad 100644 --- a/Controller/Payment/Webhook.php +++ b/Controller/Payment/Webhook.php @@ -25,6 +25,8 @@ class Webhook extends \Razorpay\Magento\Controller\BaseController protected $api; + protected $logger; + const STATUS_APPROVED = 'APPROVED'; const ORDER_PROCESSING = 'processing'; @@ -47,7 +49,8 @@ public function __construct( \Razorpay\Magento\Model\Config $config, \Magento\Catalog\Model\Session $catalogSession, \Magento\Quote\Model\QuoteRepository $quoteRepository, - \Magento\Sales\Api\Data\OrderInterface $order + \Magento\Sales\Api\Data\OrderInterface $order, + \Psr\Log\LoggerInterface $logger ) { parent::__construct( @@ -59,9 +62,11 @@ public function __construct( $keyId = $this->config->getConfigData(Config::KEY_PUBLIC_KEY); $keySecret = $this->config->getConfigData(Config::KEY_PRIVATE_KEY); - $this->api = new Api($keyId, $keySecret); + $this->api = new Api($keyId, $keySecret); $this->order = $order; + $this->logger = $logger; + $this->checkoutFactory = $checkoutFactory; $this->catalogSession = $catalogSession; $this->quoteRepository = $quoteRepository; @@ -103,9 +108,13 @@ public function execute() } catch (Errors\SignatureVerificationError $e) { - // - // Need to log the error - // + $this->logger->warning( + $e->getMessage(), + [ + 'data' => $post, + 'event' => 'razorpay.wc.signature.verify_failed' + ]); + return; } From e43b3d543d4b77d6572da108963dca213a78679e Mon Sep 17 00:00:00 2001 From: mayankamencherla Date: Mon, 7 Aug 2017 22:51:38 +0530 Subject: [PATCH 13/17] [webhooks] Using quoteManagement to create new order for webhook authorize flow --- Controller/Payment/Webhook.php | 87 ++++++++++++++++++++++++++-------- Model/PaymentMethod.php | 18 +++---- 2 files changed, 78 insertions(+), 27 deletions(-) diff --git a/Controller/Payment/Webhook.php b/Controller/Payment/Webhook.php index 724910ad..af3427f2 100644 --- a/Controller/Payment/Webhook.php +++ b/Controller/Payment/Webhook.php @@ -5,6 +5,7 @@ use Razorpay\Api\Api; use Razorpay\Api\Errors; use Razorpay\Magento\Model\Config; +use Razorpay\Magento\Model\PaymentMethod; class Webhook extends \Razorpay\Magento\Controller\BaseController { @@ -27,9 +28,15 @@ class Webhook extends \Razorpay\Magento\Controller\BaseController protected $logger; - const STATUS_APPROVED = 'APPROVED'; + protected $quoteManagement; + + protected $objectManagement; + + protected $storeManager; + + protected $customerRepository; - const ORDER_PROCESSING = 'processing'; + const STATUS_APPROVED = 'APPROVED'; /** * @param \Magento\Framework\App\Action\Context $context @@ -50,6 +57,9 @@ public function __construct( \Magento\Catalog\Model\Session $catalogSession, \Magento\Quote\Model\QuoteRepository $quoteRepository, \Magento\Sales\Api\Data\OrderInterface $order, + \Magento\Quote\Model\QuoteManagement $quoteManagement, + \Magento\Store\Model\StoreManagerInterface $storeManagement, + \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository, \Psr\Log\LoggerInterface $logger ) { @@ -67,9 +77,13 @@ public function __construct( $this->order = $order; $this->logger = $logger; - $this->checkoutFactory = $checkoutFactory; - $this->catalogSession = $catalogSession; - $this->quoteRepository = $quoteRepository; + $this->objectManagement = \Magento\Framework\App\ObjectManager::getInstance(); + $this->quoteManagement = $quoteManagement; + $this->checkoutFactory = $checkoutFactory; + $this->catalogSession = $catalogSession; + $this->quoteRepository = $quoteRepository; + $this->storeManagement = $storeManagement; + $this->customerRepository = $customerRepository; } /** @@ -138,34 +152,69 @@ public function execute() protected function paymentAuthorized(array $post) { $quoteId = $post['payload']['payment']['entity']['notes']['merchant_order_id']; - $amount = $post['payload']['payment']['entity']['amount']; + $amount = $post['payload']['payment']['entity']['amount'] / 100; $paymentId = $post['payload']['payment']['entity']['id']; - $quote = $this->quoteRepository->get($quoteId); + $quote = $this->getQuoteObject($post, $quoteId); - // - // We reserve a new order id if one is not reserved already - // - $orderId = $quote->reserveOrderId()->getReservedOrderId(); + $order = $this->quoteManagement->submit($quote); - $order = $this->order->loadByIncrementId($orderId); + $payment = $order->getPayment(); - if ($order->getStatus() === self::ORDER_PROCESSING) + if (empty($payment->getLastTransId()) === false) { return; } - $payment = $order->getPayment(); - - $payment->setStatus(self::STATUS_APPROVED) - ->setAmountPaid($amount) + $payment->setAmountPaid($amount) ->setLastTransId($paymentId) ->setTransactionId($paymentId) ->setIsTransactionClosed(true) ->setShouldCloseParentTransaction(true); - $order->setStatus(self::ORDER_PROCESSING); - $order->save(); + $payment->save(); + } + + protected function getQuoteObject($post, $quoteId) + { + $email = $post['payload']['payment']['entity']['email']; + + $quote = $this->quoteRepository->get($quoteId); + + $firstName = $quote->getBillingAddress()['customer_firstname'] ?? 'null'; + $lastName = $quote->getBillingAddress()['customer_lastname'] ?? 'null'; + + $quote->getPayment()->setMethod(PaymentMethod::METHOD_CODE); + + $store = $this->storeManagement->getStore(); + + $websiteId = $store->getWebsiteId(); + + $customer = $this->objectManagement->create('Magento\Customer\Model\Customer'); + $customer->setWebsiteId($websiteId); + + $customer = $customer->loadByEmail($email); + + if (empty($customer->getEntityId()) === true) + { + $customer->setWebsiteId($websiteId) + ->setStore($store) + ->setFirstname($firstName) + ->setLastname($lastName) + ->setEmail($email); + + $customer->save(); + } + + $customer = $this->customerRepository->getById($customer->getEntityId()); + + $quote->assignCustomer($customer); + + $quote->setStore($store); + + $quote->save(); + + return $quote; } /** diff --git a/Model/PaymentMethod.php b/Model/PaymentMethod.php index 189ca930..8203c260 100644 --- a/Model/PaymentMethod.php +++ b/Model/PaymentMethod.php @@ -24,7 +24,6 @@ class PaymentMethod extends \Magento\Payment\Model\Method\AbstractMethod const METHOD_CODE = 'razorpay'; const CONFIG_MASKED_FIELDS = 'masked_fields'; const CURRENCY = 'INR'; - const ORDER_PROCESSING = 'processing'; /** * @var string @@ -205,7 +204,7 @@ public function authorize(InfoInterface $payment, $amount) /** @var \Magento\Sales\Model\Order\Payment $payment */ $order = $payment->getOrder(); - if ($order->getStatus() === self::ORDER_PROCESSING) + if (empty($payment->getLastTransId()) === false) { return; } @@ -214,19 +213,22 @@ public function authorize(InfoInterface $payment, $amount) $request = $this->getPostData(); + if (empty($request['paymentMethod']) === true) + { + return; + } + $payment_id = $request['paymentMethod']['additional_data']['rzp_payment_id']; $this->validateSignature($request); - $payment->setStatus(self::STATUS_APPROVED) - ->setAmountPaid($amount) - ->setLastTransId($payment_id) - ->setTransactionId($payment_id) + $payment->setAmountPaid($amount) + ->setLastTransId($paymentId) + ->setTransactionId($paymentId) ->setIsTransactionClosed(true) ->setShouldCloseParentTransaction(true); - $order->setStatus(self::ORDER_PROCESSING); - $order->save(); + $payment->save(); } catch (\Exception $e) { From decfe487a50f77fc7c37b0b1228c84675c35c73f Mon Sep 17 00:00:00 2001 From: mayankamencherla Date: Thu, 10 Aug 2017 15:57:08 +0530 Subject: [PATCH 14/17] [webhooks] bug fix: Using paymentId instead of payment_id --- Model/PaymentMethod.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Model/PaymentMethod.php b/Model/PaymentMethod.php index 8203c260..e79bc21f 100644 --- a/Model/PaymentMethod.php +++ b/Model/PaymentMethod.php @@ -218,7 +218,7 @@ public function authorize(InfoInterface $payment, $amount) return; } - $payment_id = $request['paymentMethod']['additional_data']['rzp_payment_id']; + $paymentId = $request['paymentMethod']['additional_data']['rzp_payment_id']; $this->validateSignature($request); From a0a6eba5efe4c75f174eb15514df18c57bdcf373 Mon Sep 17 00:00:00 2001 From: mayankamencherla Date: Thu, 10 Aug 2017 16:12:34 +0530 Subject: [PATCH 15/17] [webhooks] Removed ->save() --- Model/PaymentMethod.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/Model/PaymentMethod.php b/Model/PaymentMethod.php index e79bc21f..3eada809 100644 --- a/Model/PaymentMethod.php +++ b/Model/PaymentMethod.php @@ -227,8 +227,6 @@ public function authorize(InfoInterface $payment, $amount) ->setTransactionId($paymentId) ->setIsTransactionClosed(true) ->setShouldCloseParentTransaction(true); - - $payment->save(); } catch (\Exception $e) { From 3eff818512f001d51e965435db57b768e447cae9 Mon Sep 17 00:00:00 2001 From: mayankamencherla Date: Wed, 23 Aug 2017 22:47:45 +0530 Subject: [PATCH 16/17] [webhooks] Bug fix: closed field tag for payment action --- etc/adminhtml/system.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 963b9225..35f4d6e2 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -15,6 +15,7 @@ Razorpay\Magento\Model\PaymentAction payment/razorpay/payment_action + ** {baseUrl}/razorpay/payment/webhook ** From 0ea40771011e3ad0b4dc3b580ea532c485a0c7e8 Mon Sep 17 00:00:00 2001 From: mayankamencherla Date: Wed, 11 Oct 2017 19:28:34 +0530 Subject: [PATCH 17/17] [webhooks] Added debug log --- Controller/Payment/Webhook.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Controller/Payment/Webhook.php b/Controller/Payment/Webhook.php index af3427f2..c296f257 100644 --- a/Controller/Payment/Webhook.php +++ b/Controller/Payment/Webhook.php @@ -161,11 +161,15 @@ protected function paymentAuthorized(array $post) $payment = $order->getPayment(); + $this->logger->warning('Debug Log --------------------- 1'); + if (empty($payment->getLastTransId()) === false) { return; } + $this->logger->warning('Debug Log --------------------- 2'); + $payment->setAmountPaid($amount) ->setLastTransId($paymentId) ->setTransactionId($paymentId) @@ -173,6 +177,8 @@ protected function paymentAuthorized(array $post) ->setShouldCloseParentTransaction(true); $payment->save(); + + $this->logger->warning('Debug Log --------------------- 3'); } protected function getQuoteObject($post, $quoteId)