Razorpay for WooCommerce - Version 1.4.3

Version Description

  • Added razorpay webhooks
Download this release

Release Info

Developer mayankamencherla
Plugin Icon 128x128 Razorpay for WooCommerce
Version 1.4.3
Comparing to
See all releases

Code changes from version 1.4.2 to 1.4.3

.editorconfig DELETED
@@ -1,13 +0,0 @@
1
- ; This file is for unifying the coding style for different editors and IDEs.
2
- ; More information at http://EditorConfig.org
3
-
4
- root = true
5
- ; Use 2 spaces for indentation in all files
6
-
7
- [*.php]
8
- end_of_line = lf
9
- charset = utf-8
10
- trim_trailing_whitespace = true
11
- indent_style = space
12
- indent_size = 4
13
- insert_final_newline = true
 
 
 
 
 
 
 
 
 
 
 
 
 
images/logo.jpg DELETED
Binary file
includes/razorpay-webhook.php ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once __DIR__.'/../razorpay-payments.php';
4
+ require_once __DIR__.'/../razorpay-sdk/Razorpay.php';
5
+
6
+ use Razorpay\Api\Api;
7
+ use Razorpay\Api\Errors;
8
+
9
+ class RZP_Webhook
10
+ {
11
+ protected $razorpay;
12
+ protected $api;
13
+
14
+ function __construct()
15
+ {
16
+ $this->razorpay = new WC_Razorpay();
17
+
18
+ $this->api = $this->razorpay->getRazorpayApiInstance();
19
+ }
20
+
21
+ public function process()
22
+ {
23
+ $post = file_get_contents('php://input');
24
+
25
+ $data = json_decode($post, true);
26
+
27
+ if (json_last_error() !== 0)
28
+ {
29
+ return;
30
+ }
31
+
32
+ if ($this->razorpay->enable_webhook === 'yes' && empty($data['event']) === false)
33
+ {
34
+ if ((isset($_SERVER['HTTP_X_RAZORPAY_SIGNATURE']) === true))
35
+ {
36
+ $razorpayWebhookSecret = $this->razorpay->webhook_secret;
37
+
38
+ //
39
+ // If the webhook secret isn't set on wordpress, return
40
+ //
41
+ if (empty($razorpayWebhookSecret) === true)
42
+ {
43
+ return;
44
+ }
45
+
46
+ try
47
+ {
48
+ $this->api->utility->verifyWebhookSignature($post,
49
+ $_SERVER['HTTP_X_RAZORPAY_SIGNATURE'],
50
+ $razorpayWebhookSecret);
51
+ }
52
+ catch (Errors\SignatureVerificationError $e)
53
+ {
54
+ write_log(['message' => $e->getMessage()]);
55
+ return;
56
+ }
57
+ }
58
+
59
+ switch ($data['event'])
60
+ {
61
+ case 'payment.authorized':
62
+ return $this->paymentAuthorized($data);
63
+
64
+ default:
65
+ return;
66
+ }
67
+ }
68
+ }
69
+
70
+ protected function paymentAuthorized($data)
71
+ {
72
+ global $woocommerce;
73
+
74
+ //
75
+ // Order entity should be sent as part of the webhook payload
76
+ //
77
+ $orderId = $data['payload']['payment']['entity']['notes']['woocommerce_order_id'];
78
+
79
+ $order = new WC_Order($orderId);
80
+
81
+ if ($order->needs_payment() === false)
82
+ {
83
+ return;
84
+ }
85
+
86
+ $razorpayPaymentId = $data['payload']['payment']['entity']['id'];
87
+
88
+ $payment = $this->api->payment->fetch($razorpayPaymentId);
89
+
90
+ $amount = $this->razorpay->getOrderAmountAsInteger($order);
91
+
92
+ $success = false;
93
+ $errorMessage = 'The payment has failed.';
94
+
95
+ if ($payment['status'] === 'captured')
96
+ {
97
+ $success = true;
98
+ }
99
+ else if (($payment['status'] === 'authorized') and
100
+ ($this->razorpay->payment_action === 'capture'))
101
+ {
102
+ //
103
+ // If the payment is only authorized, we capture it
104
+ // If the merchant has enabled auto capture
105
+ //
106
+ $payment->capture(array('amount' => $amount));
107
+ }
108
+
109
+ $this->razorpay->updateOrder($order, $success, $errorMessage, $razorpayPaymentId);
110
+
111
+ exit;
112
+ }
113
+ }
razorpay-payments.php CHANGED
@@ -8,21 +8,27 @@ Author: Razorpay
8
  Author URI: https://razorpay.com
9
  */
10
 
11
- if ( ! defined( 'ABSPATH' ) ) {
 
12
  exit; // Exit if accessed directly
13
  }
14
 
 
 
15
  use Razorpay\Api\Api;
16
  use Razorpay\Api\Errors;
17
 
18
  require_once __DIR__.'/razorpay-sdk/Razorpay.php';
19
 
20
  add_action('plugins_loaded', 'woocommerce_razorpay_init', 0);
 
21
 
22
  function woocommerce_razorpay_init()
23
  {
24
  if (!class_exists('WC_Payment_Gateway'))
 
25
  return;
 
26
 
27
  class WC_Razorpay extends WC_Payment_Gateway
28
  {
@@ -44,6 +50,24 @@ function woocommerce_razorpay_init()
44
  $this->key_secret = $this->settings['key_secret'];
45
  $this->payment_action = $this->settings['payment_action'];
46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  $this->supports = array(
48
  'products',
49
  'refunds',
@@ -109,7 +133,20 @@ function woocommerce_razorpay_init()
109
  'authorize' => 'Authorize',
110
  'capture' => 'Authorize and Capture'
111
  )
112
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  );
114
  }
115
  public function admin_options()
@@ -274,7 +311,7 @@ function woocommerce_razorpay_init()
274
  /**
275
  * Returns the order amount, rounded as integer
276
  */
277
- protected function getOrderAmountAsInteger($order)
278
  {
279
  if (version_compare(WOOCOMMERCE_VERSION, '3.0.0', '>='))
280
  {
@@ -289,7 +326,7 @@ function woocommerce_razorpay_init()
289
  // Calls the helper function to create order data
290
  global $woocommerce;
291
 
292
- $api = new Api($this->key_id, $this->key_secret);
293
 
294
  $data = $this->getOrderCreationData($orderId);
295
  $razorpay_order = $api->order->create($data);
@@ -439,8 +476,8 @@ EOT;
439
  try
440
  {
441
  $refund = $client->payment
442
- ->fetch($paymentId)
443
- ->refund($data);
444
 
445
  $order->add_order_note(__( 'Refund Id: ' . $refund->id, 'woocommerce' ));
446
 
@@ -453,6 +490,7 @@ EOT;
453
 
454
  }
455
 
 
456
  /**
457
  * Process the payment and return the result
458
  **/
@@ -489,7 +527,7 @@ EOT;
489
  }
490
  }
491
 
492
- protected function getRazorpayApiInstance()
493
  {
494
  return new Api($this->key_id, $this->key_secret);
495
  }
@@ -502,9 +540,17 @@ EOT;
502
  global $woocommerce;
503
 
504
  $orderId = $woocommerce->session->get(self::SESSION_KEY);
505
-
506
  $order = new WC_Order($orderId);
507
 
 
 
 
 
 
 
 
 
 
508
  $razorpayPaymentId = null;
509
 
510
  if ($orderId and !empty($_POST[self::RAZORPAY_PAYMENT_ID]))
@@ -527,14 +573,18 @@ EOT;
527
  {
528
  $success = false;
529
  $error = 'Customer cancelled the payment';
530
-
531
  $this->handleErrorCase($order);
532
  }
533
 
534
  $this->updateOrder($order, $success, $error, $razorpayPaymentId);
535
 
536
- $this->add_notice($this->msg['message'], $this->msg['class']);
 
 
 
 
537
  $redirectUrl = $this->get_return_url($order);
 
538
  wp_redirect($redirectUrl);
539
  exit;
540
  }
@@ -590,7 +640,7 @@ EOT;
590
  *
591
  * @param $success, & $order
592
  */
593
- protected function updateOrder(& $order, $success, $errorMessage, $razorpayPaymentId)
594
  {
595
  global $woocommerce;
596
 
@@ -601,10 +651,14 @@ EOT;
601
  $this->msg['message'] = "Thank you for shopping with us. Your account has been charged and your transaction is successful. We will be processing your order soon. Order Id: $orderId";
602
  $this->msg['class'] = 'success';
603
 
604
- $order->payment_complete();
605
  $order->add_order_note("Razorpay payment successful <br/>Razorpay Id: $razorpayPaymentId");
606
  $order->add_order_note($this->msg['message']);
607
- $woocommerce->cart->empty_cart();
 
 
 
 
608
  }
609
  else
610
  {
@@ -619,6 +673,8 @@ EOT;
619
  $order->add_order_note("Transaction Failed: $errorMessage<br/>");
620
  $order->update_status('failed');
621
  }
 
 
622
  }
623
 
624
  protected function handleErrorCase(& $order)
@@ -671,3 +727,10 @@ EOT;
671
 
672
  add_filter('woocommerce_payment_gateways', 'woocommerce_add_razorpay_gateway' );
673
  }
 
 
 
 
 
 
 
8
  Author URI: https://razorpay.com
9
  */
10
 
11
+ if ( ! defined( 'ABSPATH' ) )
12
+ {
13
  exit; // Exit if accessed directly
14
  }
15
 
16
+ require_once __DIR__.'/includes/razorpay-webhook.php';
17
+
18
  use Razorpay\Api\Api;
19
  use Razorpay\Api\Errors;
20
 
21
  require_once __DIR__.'/razorpay-sdk/Razorpay.php';
22
 
23
  add_action('plugins_loaded', 'woocommerce_razorpay_init', 0);
24
+ add_action('admin_post_nopriv_rzp_wc_webhook', 'razorpay_webhook_init');
25
 
26
  function woocommerce_razorpay_init()
27
  {
28
  if (!class_exists('WC_Payment_Gateway'))
29
+ {
30
  return;
31
+ }
32
 
33
  class WC_Razorpay extends WC_Payment_Gateway
34
  {
50
  $this->key_secret = $this->settings['key_secret'];
51
  $this->payment_action = $this->settings['payment_action'];
52
 
53
+ if (isset($this->settings['enable_webhook']) === true)
54
+ {
55
+ $this->enable_webhook = $this->settings['enable_webhook'];
56
+ }
57
+ else
58
+ {
59
+ $this->enable_webhook = 'yes';
60
+ }
61
+
62
+ if (isset($this->settings['webhook_secret']) === true)
63
+ {
64
+ $this->webhook_secret = $this->settings['webhook_secret'];
65
+ }
66
+ else
67
+ {
68
+ $this->webhook_secret = '';
69
+ }
70
+
71
  $this->supports = array(
72
  'products',
73
  'refunds',
133
  'authorize' => 'Authorize',
134
  'capture' => 'Authorize and Capture'
135
  )
136
+ ),
137
+ 'enable_webhook' => array(
138
+ 'title' => __('Enable Webhook', 'razorpay'),
139
+ 'type' => 'checkbox',
140
+ 'description' => esc_url( admin_url('admin-post.php') ) . "?action=rzp_wc_webhook <br><br>Instructions and guide to <a href='https://github.com/razorpay/razorpay-woocommerce/wiki/Razorpay-Woocommerce-Webhooks'>Razorpay webhooks</a>",
141
+ 'label' => __('Enable Razorpay Webhook <a href="https://dashboard.razorpay.com/#/app/webhooks">here</a> with the URL listed below.', 'razorpay'),
142
+ 'default' => 'yes'
143
+ ),
144
+ 'webhook_secret' => array(
145
+ 'title' => __('Webhook Secret', 'razorpay'),
146
+ 'type' => 'text',
147
+ 'description' => __('Webhook secret is used for webhook signature verification. This has to match the one added <a href="https://dashboard.razorpay.com/#/app/webhooks">here</a>', 'razorpay'),
148
+ 'default' => ''
149
+ ),
150
  );
151
  }
152
  public function admin_options()
311
  /**
312
  * Returns the order amount, rounded as integer
313
  */
314
+ public function getOrderAmountAsInteger($order)
315
  {
316
  if (version_compare(WOOCOMMERCE_VERSION, '3.0.0', '>='))
317
  {
326
  // Calls the helper function to create order data
327
  global $woocommerce;
328
 
329
+ $api = $this->getRazorpayApiInstance();
330
 
331
  $data = $this->getOrderCreationData($orderId);
332
  $razorpay_order = $api->order->create($data);
476
  try
477
  {
478
  $refund = $client->payment
479
+ ->fetch($paymentId)
480
+ ->refund($data);
481
 
482
  $order->add_order_note(__( 'Refund Id: ' . $refund->id, 'woocommerce' ));
483
 
490
 
491
  }
492
 
493
+
494
  /**
495
  * Process the payment and return the result
496
  **/
527
  }
528
  }
529
 
530
+ public function getRazorpayApiInstance()
531
  {
532
  return new Api($this->key_id, $this->key_secret);
533
  }
540
  global $woocommerce;
541
 
542
  $orderId = $woocommerce->session->get(self::SESSION_KEY);
 
543
  $order = new WC_Order($orderId);
544
 
545
+ //
546
+ // If the order has already been paid for
547
+ // redirect user to success page
548
+ //
549
+ if ($order->needs_payment() === false)
550
+ {
551
+ $this->redirectUser($order);
552
+ }
553
+
554
  $razorpayPaymentId = null;
555
 
556
  if ($orderId and !empty($_POST[self::RAZORPAY_PAYMENT_ID]))
573
  {
574
  $success = false;
575
  $error = 'Customer cancelled the payment';
 
576
  $this->handleErrorCase($order);
577
  }
578
 
579
  $this->updateOrder($order, $success, $error, $razorpayPaymentId);
580
 
581
+ $this->redirectUser($order);
582
+ }
583
+
584
+ protected function redirectUser($order)
585
+ {
586
  $redirectUrl = $this->get_return_url($order);
587
+
588
  wp_redirect($redirectUrl);
589
  exit;
590
  }
640
  *
641
  * @param $success, & $order
642
  */
643
+ public function updateOrder(& $order, $success, $errorMessage, $razorpayPaymentId)
644
  {
645
  global $woocommerce;
646
 
651
  $this->msg['message'] = "Thank you for shopping with us. Your account has been charged and your transaction is successful. We will be processing your order soon. Order Id: $orderId";
652
  $this->msg['class'] = 'success';
653
 
654
+ $order->payment_complete($razorpayPaymentId);
655
  $order->add_order_note("Razorpay payment successful <br/>Razorpay Id: $razorpayPaymentId");
656
  $order->add_order_note($this->msg['message']);
657
+
658
+ if (isset($woocommerce->cart) === true)
659
+ {
660
+ $woocommerce->cart->empty_cart();
661
+ }
662
  }
663
  else
664
  {
673
  $order->add_order_note("Transaction Failed: $errorMessage<br/>");
674
  $order->update_status('failed');
675
  }
676
+
677
+ $this->add_notice($this->msg['message'], $this->msg['class']);
678
  }
679
 
680
  protected function handleErrorCase(& $order)
727
 
728
  add_filter('woocommerce_payment_gateways', 'woocommerce_add_razorpay_gateway' );
729
  }
730
+
731
+ function razorpay_webhook_init()
732
+ {
733
+ $rzpWebhook = new RZP_Webhook();
734
+
735
+ $rzpWebhook->process();
736
+ }
razorpay-sdk/src/Utility.php CHANGED
@@ -17,14 +17,21 @@ class Utility
17
  return self::verifySignature($payload, $expectedSignature);
18
  }
19
 
20
- public function verifyWebhookSignature($payload, $expectedSignature)
21
  {
22
- return self::verifySignature($payload, $expectedSignature);
23
  }
24
 
25
- public function verifySignature($payload, $expectedSignature)
26
  {
27
- $actualSignature = hash_hmac(self::SHA256, $payload, Api::getSecret());
 
 
 
 
 
 
 
28
 
29
  // Use lang's built-in hash_equals if exists to mitigate timing attacks
30
  if (function_exists('hash_equals'))
17
  return self::verifySignature($payload, $expectedSignature);
18
  }
19
 
20
+ public function verifyWebhookSignature($payload, $expectedSignature, $webhookSecret)
21
  {
22
+ return self::verifySignature($payload, $expectedSignature, $webhookSecret);
23
  }
24
 
25
+ public function verifySignature($payload, $expectedSignature, $webhookSecret = '')
26
  {
27
+ if (isset($webhookSecret) === true)
28
+ {
29
+ $actualSignature = hash_hmac(self::SHA256, $payload, $webhookSecret);
30
+ }
31
+ else
32
+ {
33
+ $actualSignature = hash_hmac(self::SHA256, $payload, Api::getSecret());
34
+ }
35
 
36
  // Use lang's built-in hash_equals if exists to mitigate timing attacks
37
  if (function_exists('hash_equals'))
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: razorpay
3
  Tags: razorpay, payments, india, woocommerce, ecommerce
4
  Requires at least: 3.9.2
5
  Tested up to: 4.7
6
- Stable tag: 1.4.2
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
@@ -36,6 +36,9 @@ This is compatible with WooCommerce>=2.4, including the new 3.0 release.
36
 
37
  == Changelog ==
38
 
 
 
 
39
  = 1.4.2 =
40
  * Added missing classes in the WordPress release (Utility.php was missing)
41
 
3
  Tags: razorpay, payments, india, woocommerce, ecommerce
4
  Requires at least: 3.9.2
5
  Tested up to: 4.7
6
+ Stable tag: 1.4.3
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
36
 
37
  == Changelog ==
38
 
39
+ = 1.4.3 =
40
+ * Added razorpay webhooks
41
+
42
  = 1.4.2 =
43
  * Added missing classes in the WordPress release (Utility.php was missing)
44