spree_razorpay_checkout 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +68 -26
- data/app/assets/images/payment_icons/razorpaycheckout.svg +1 -0
- data/app/controllers/concerns/spree/razor_pay.rb +8 -3
- data/app/controllers/spree/razorpay_controller.rb +73 -58
- data/app/models/spree/gateway/razorpay_gateway.rb +35 -13
- data/app/models/spree/razorpay_checkout.rb +22 -1
- data/app/models/spree_razorpay_checkout/spree/order_decorator.rb +21 -16
- data/app/services/razorpay/rp_order/api.rb +5 -4
- data/app/views/spree/admin/payment_methods/descriptions/_razorpay.html.erb +2 -6
- data/app/views/spree/checkout/payment/_razorpay.html.erb +16 -5
- data/config/initializers/assets.rb +2 -1
- data/config/routes.rb +3 -2
- data/db/migrate/20220816080140_create_spree_razorpay_checkouts.rb +6 -3
- data/lib/spree_razorpay_checkout/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e72dc88db8b53ff22661766a04c41d6a4b481d6183638de96d767983682f4c74
|
|
4
|
+
data.tar.gz: 033dd1d5f35534ccdbecaaaa5655e21a7065563684592be0c19cd94625742f77
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5e69954e3d286c3e7e6cfcacfa3866c7aca7f61eaf5097861ac4fe3d437b1da154b28e1bf1122dab2914fec8a69344111c4abb3e970048de221e7a6f38d68ad9
|
|
7
|
+
data.tar.gz: 9206adcb915d630577fbd26b8d69a6a4465202d6dfc20c6e5e604dcd9218d200017ba087b8986179f3c87027c09665388504124babea8d6622a1a50a111a47ff
|
data/README.md
CHANGED
|
@@ -4,68 +4,56 @@
|
|
|
4
4
|
style="height: 100px; border-radius: 12em;"
|
|
5
5
|
/>
|
|
6
6
|
|
|
7
|
+
This Razorpay Checkout Reprository is mentioned in official [Spree Commerce Docs](https://spreecommerce.org/docs/integrations/payments/razorpay).
|
|
8
|
+
|
|
7
9
|
## Razorpay Extension for Spree Commerce v5
|
|
8
10
|
RazorPay is the only payments solution in India that allows businesses to accept, process and disburse payments with its product suite.
|
|
9
11
|
|
|
10
|
-
## Installation
|
|
12
|
+
## Installation
|
|
11
13
|
|
|
12
|
-
1. Add
|
|
14
|
+
1. Add Gem:
|
|
13
15
|
|
|
14
16
|
```ruby
|
|
15
|
-
|
|
17
|
+
bundle add spree_razorpay_checkout
|
|
16
18
|
```
|
|
17
19
|
|
|
18
|
-
2. Install the Gem
|
|
19
|
-
|
|
20
|
-
```ruby
|
|
21
|
-
bundle install
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
3. Copy & Run Migrations:
|
|
20
|
+
2. Install the Gem:
|
|
25
21
|
|
|
26
22
|
```ruby
|
|
27
23
|
bundle exec rails g spree_razorpay_checkout:install
|
|
28
24
|
```
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
3. Compile Assets (Optional):
|
|
32
27
|
```ruby
|
|
33
|
-
|
|
28
|
+
bin/rails assets:precompile
|
|
34
29
|
```
|
|
35
30
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
```ruby
|
|
31
|
+
4. Start Server:
|
|
32
|
+
```ruby
|
|
39
33
|
foreman start -f Procfile.dev
|
|
40
34
|
```
|
|
41
35
|
|
|
42
36
|
## Installation (For Docker)
|
|
43
37
|
|
|
44
|
-
1. Add
|
|
38
|
+
1. Add Gem using docker compose:
|
|
45
39
|
|
|
46
40
|
```ruby
|
|
47
|
-
|
|
41
|
+
docker compose run web bundle add spree_razorpay_checkout
|
|
48
42
|
```
|
|
49
43
|
|
|
50
44
|
2. Install the Gem using Docker's Bundle Install:
|
|
51
45
|
|
|
52
|
-
```ruby
|
|
53
|
-
docker compose run web bundle install
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
3. Run Install Generator to Copy Migrations in Docker way:
|
|
57
|
-
|
|
58
46
|
```ruby
|
|
59
47
|
docker compose run web bundle exec rails g spree_razorpay_checkout:install
|
|
60
48
|
```
|
|
61
49
|
|
|
62
|
-
|
|
50
|
+
3. Compile Assests for Razorpay logo & assets (Recommended):
|
|
63
51
|
|
|
64
52
|
```ruby
|
|
65
53
|
docker compose run web bundle exec rails assets:precompile
|
|
66
54
|
```
|
|
67
55
|
|
|
68
|
-
|
|
56
|
+
4. Re-Start Server (Recommended):
|
|
69
57
|
|
|
70
58
|
```ruby
|
|
71
59
|
docker compose down
|
|
@@ -114,6 +102,60 @@ Thankyou for supporting this plugin. if you find any issues related to plugin yo
|
|
|
114
102
|
|
|
115
103
|
---
|
|
116
104
|
|
|
105
|
+
## Uninstallation
|
|
106
|
+
|
|
107
|
+
1. Uninstall Gem:
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
gem uninstall spree_razorpay_checkout
|
|
111
|
+
gem uninstall razorpay
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
2. Update Gemfile:
|
|
115
|
+
|
|
116
|
+
```ruby
|
|
117
|
+
bundle install
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
3. Remove Migrations:
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
rm db/migrate/*_create_spree_razorpay_checkouts.spree_razorpay_checkout.rb
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
4. Open Rails Console:
|
|
127
|
+
|
|
128
|
+
```ruby
|
|
129
|
+
rails c
|
|
130
|
+
|
|
131
|
+
5. Drop Razorpay Database:
|
|
132
|
+
|
|
133
|
+
```ruby
|
|
134
|
+
ActiveRecord::Base.connection.drop_table(:spree_razorpay_checkouts)
|
|
135
|
+
``````
|
|
136
|
+
6. Check Razorpay (You should see "nill"):
|
|
137
|
+
|
|
138
|
+
```ruby
|
|
139
|
+
defined?(Razorpay) # => nil
|
|
140
|
+
```
|
|
141
|
+
Note: If you see "nill" then Razorpay is completely uninstalled from Spree commerce, either if you see "constant" try "gem uninstall razorpay" & "bundle update".
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
### Roadmap
|
|
145
|
+
|
|
146
|
+
| **Features** | **Status** |
|
|
147
|
+
|-----------------------------------------|------------|
|
|
148
|
+
| Auto-Capture Order in Razorpay | Working |
|
|
149
|
+
| Test Button for Testmode | Working |
|
|
150
|
+
| Razorpay order creation using [OrdersAPI](https://razorpay.com/docs/payments/orders/apis/) | Working |
|
|
151
|
+
| Fetching Total Amount in Modal | Working |
|
|
152
|
+
| Order Creation after Payment | Working |
|
|
153
|
+
| Razorpay Logo in Order's Page | Working |
|
|
154
|
+
| Admin "Capture" order button | Working |
|
|
155
|
+
| Admin side "Refund" order | Pending |
|
|
156
|
+
| E-Mail after successful order | Pending |
|
|
157
|
+
| Disable Pay Button for Accidental Order | Partial |
|
|
158
|
+
|
|
117
159
|
### Contributing
|
|
118
160
|
|
|
119
161
|
Contributions are welcome! Please open issues or submit pull requests to help improve this plugin for the Spree + Razorpay community in India.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 38 24" width="38" height="24" aria-labelledby="pi-payu"><path opacity=".07" d="M35 0H3C1.3 0 0 1.3 0 3v18c0 1.7 1.4 3 3 3h32c1.7 0 3-1.3 3-3V3c0-1.7-1.4-3-3-3"/><path fill="#fff" d="M35 1c1.1 0 2 .9 2 2v18c0 1.1-.9 2-2 2H3c-1.1 0-2-.9-2-2V3c0-1.1.9-2 2-2z"/><style>.st0{opacity:7.000000e-02;enable-background:new}.st1{fill:#fff}</style><path class="st0" d="M35 0H3C1.3 0 0 1.3 0 3v18c0 1.7 1.4 3 3 3h32c1.7 0 3-1.3 3-3V3c0-1.7-1.4-3-3-3"/><path class="st1" d="M35 1c1.1 0 2 .9 2 2v18c0 1.1-.9 2-2 2H3c-1.1 0-2-.9-2-2V3c0-1.1.9-2 2-2z"/><path class="st0" d="M35 0H3C1.3 0 0 1.3 0 3v18c0 1.7 1.4 3 3 3h32c1.7 0 3-1.3 3-3V3c0-1.7-1.4-3-3-3"/><path class="st1" d="M35 1c1.1 0 2 .9 2 2v18c0 1.1-.9 2-2 2H3c-1.1 0-2-.9-2-2V3c0-1.1.9-2 2-2z"/><g transform="matrix(.85 0 0 .85 4.5 1.8)"><path id="Fill-17" style="fill-rule:evenodd;clip-rule:evenodd;fill:#3395ff" d="m16.2 9-.9 3 4.8-3-3 11.6h3.1l4.6-17.2z"/><path id="Fill-19" d="m11.1 15.7-1.4 4.9h6.5l2.6-10z" style="fill-rule:evenodd;clip-rule:evenodd;fill:#072654"/></g></svg>
|
|
@@ -3,15 +3,20 @@ module Spree
|
|
|
3
3
|
extend ActiveSupport::Concern
|
|
4
4
|
|
|
5
5
|
def update_razorpay_checkout
|
|
6
|
-
|
|
7
|
-
razorpay_checkout.
|
|
6
|
+
return unless order
|
|
7
|
+
razorpay_checkout = Spree::RazorpayCheckout.find_or_initialize_by(order_id: order.id)
|
|
8
|
+
razorpay_checkout.assign_attributes(update_razorpay_response)
|
|
9
|
+
razorpay_checkout.save! if razorpay_checkout.changed?
|
|
10
|
+
rescue => e
|
|
11
|
+
Rails.logger.error("Failed to update RazorpayCheckout: #{e.message}")
|
|
8
12
|
end
|
|
9
13
|
|
|
10
14
|
def update_razorpay_response
|
|
11
15
|
{
|
|
12
16
|
razorpay_payment_id: params['razorpay_payment_id'],
|
|
13
17
|
razorpay_order_id: params['razorpay_order_id'],
|
|
14
|
-
razorpay_signature: params['razorpay_signature']
|
|
18
|
+
razorpay_signature: params['razorpay_signature'],
|
|
19
|
+
status: (defined?(razorpay_payment) && razorpay_payment.try(:status)) || params['status']
|
|
15
20
|
}
|
|
16
21
|
end
|
|
17
22
|
end
|
|
@@ -4,67 +4,72 @@ module Spree
|
|
|
4
4
|
|
|
5
5
|
include Spree::RazorPay
|
|
6
6
|
|
|
7
|
-
#Step 1: Create Razorpay Order
|
|
7
|
+
# Step 1: Create Razorpay Order (Kept from the "New" version for security)
|
|
8
8
|
def create_order
|
|
9
|
-
|
|
9
|
+
order = Spree::Order.find_by(id: params[:order_id])
|
|
10
|
+
return render json: { success: false, error: 'Order not found' }, status: :not_found unless order
|
|
11
|
+
|
|
12
|
+
razorpay_order_id, amount = ::Razorpay::RpOrder::Api.new.create(order.id)
|
|
10
13
|
|
|
11
14
|
if razorpay_order_id.present?
|
|
12
|
-
render json: { success: true, razorpay_order_id: razorpay_order_id }
|
|
15
|
+
render json: { success: true, razorpay_order_id: razorpay_order_id, amount: amount }
|
|
13
16
|
else
|
|
14
17
|
render json: { success: false, error: "Failed to create Razorpay order" }, status: :unprocessable_entity
|
|
15
18
|
end
|
|
16
19
|
end
|
|
17
20
|
|
|
18
|
-
#Step 2:
|
|
21
|
+
# Step 2: Handle Response
|
|
19
22
|
def razor_response
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
checkout_record = Spree::RazorpayCheckout.find_by(
|
|
26
|
-
order_id: order.id,
|
|
27
|
-
razorpay_order_id: params[:razorpay_order_id]
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
if checkout_record
|
|
31
|
-
checkout_record.update!(
|
|
32
|
-
razorpay_payment_id: razorpay_payment_id,
|
|
33
|
-
razorpay_signature: params[:razorpay_signature],
|
|
34
|
-
status: razorpay_payment.status,
|
|
35
|
-
payment_method: razorpay_payment.method,
|
|
36
|
-
card_id: razorpay_payment.card_id,
|
|
37
|
-
bank: razorpay_payment.bank,
|
|
38
|
-
wallet: razorpay_payment.wallet,
|
|
39
|
-
vpa: razorpay_payment.vpa,
|
|
40
|
-
email: razorpay_payment.email,
|
|
41
|
-
contact: razorpay_payment.contact
|
|
42
|
-
)
|
|
43
|
-
else
|
|
44
|
-
Rails.logger.warn("RazorpayCheckout record not found for order #{order.id}")
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
# Add payment to Spree order
|
|
48
|
-
order.razor_payment(razorpay_payment, payment_method, params[:razorpay_signature])
|
|
49
|
-
order.next
|
|
50
|
-
|
|
51
|
-
flash['order_completed'] = true if order.completed?
|
|
52
|
-
redirect_to checkout_state_path_or_completion and return
|
|
53
|
-
rescue StandardError => e
|
|
54
|
-
Rails.logger.error("Razorpay Error: #{e.message}")
|
|
55
|
-
flash[:error] = "Razorpay Error: #{e.message}"
|
|
56
|
-
end
|
|
57
|
-
else
|
|
58
|
-
flash[:error] = 'Razorpay payment verification failed'
|
|
23
|
+
# 1. Find the Order
|
|
24
|
+
order = Spree::Order.find_by(number: params[:order_id] || params[:order_number])
|
|
25
|
+
unless order
|
|
26
|
+
flash[:error] = "Order not found."
|
|
27
|
+
return redirect_to checkout_state_path(:payment)
|
|
59
28
|
end
|
|
60
29
|
|
|
61
|
-
|
|
30
|
+
# 2. Verify Signature
|
|
31
|
+
unless valid_signature?
|
|
32
|
+
flash[:error] = "Payment signature verification failed."
|
|
33
|
+
return redirect_to checkout_state_path(order.state)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
begin
|
|
37
|
+
# 3. Capture and Verify Payment on Razorpay
|
|
38
|
+
razorpay_payment = gateway.verify_and_capture_razorpay_payment(order, razorpay_payment_id)
|
|
39
|
+
|
|
40
|
+
# 4. Create Spree Payment Record
|
|
41
|
+
spree_payment = order.razor_payment(razorpay_payment, payment_method, params[:razorpay_signature])
|
|
42
|
+
|
|
43
|
+
# 5. FORCE COMPLETE THE PAYMENT
|
|
44
|
+
# This ensures the payment is marked 'completed' immediately
|
|
45
|
+
if spree_payment.respond_to?(:complete!)
|
|
46
|
+
spree_payment.complete!
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# 6. Advance Order State
|
|
50
|
+
# Keep calling next! until the order is fully finalized
|
|
51
|
+
while !order.completed?
|
|
52
|
+
order.next!
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# 7. Force update payment state to 'paid'
|
|
56
|
+
order.update(payment_state: 'paid') if order.respond_to?(:payment_state)
|
|
57
|
+
|
|
58
|
+
# 8. Redirect to the specific Token URL
|
|
59
|
+
#flash['order_completed'] = true
|
|
60
|
+
redirect_to completion_route
|
|
61
|
+
|
|
62
|
+
rescue StandardError => e
|
|
63
|
+
Rails.logger.error("Razorpay Error: #{e.message}\n#{e.backtrace.join("\n")}")
|
|
64
|
+
flash[:error] = "Payment Error: #{e.message}"
|
|
65
|
+
redirect_to checkout_state_path(order.state)
|
|
66
|
+
end
|
|
62
67
|
end
|
|
63
68
|
|
|
64
69
|
private
|
|
65
70
|
|
|
66
71
|
def razorpay_payment_id
|
|
67
|
-
params[:razorpay_payment_id]
|
|
72
|
+
params[:razorpay_payment_id] || params.dig(:payment_source, payment_method.id.to_s, :razorpay_payment_id)
|
|
68
73
|
end
|
|
69
74
|
|
|
70
75
|
def razorpay_payment
|
|
@@ -72,34 +77,44 @@ module Spree
|
|
|
72
77
|
end
|
|
73
78
|
|
|
74
79
|
def valid_signature?
|
|
80
|
+
p_id = payment_method.id.to_s
|
|
81
|
+
r_order_id = params[:razorpay_order_id] || params.dig(:payment_source, p_id, :razorpay_order_id)
|
|
82
|
+
r_pay_id = razorpay_payment_id
|
|
83
|
+
r_sig = params[:razorpay_signature] || params.dig(:payment_source, p_id, :razorpay_signature)
|
|
84
|
+
|
|
75
85
|
Razorpay::Utility.verify_payment_signature(
|
|
76
|
-
razorpay_order_id:
|
|
77
|
-
razorpay_payment_id:
|
|
78
|
-
razorpay_signature:
|
|
86
|
+
razorpay_order_id: r_order_id,
|
|
87
|
+
razorpay_payment_id: r_pay_id,
|
|
88
|
+
razorpay_signature: r_sig
|
|
79
89
|
)
|
|
80
90
|
rescue Razorpay::Error => e
|
|
81
91
|
Rails.logger.error("Razorpay signature verification failed: #{e.message}")
|
|
82
92
|
false
|
|
83
93
|
end
|
|
84
94
|
|
|
85
|
-
def order
|
|
86
|
-
@order ||= Spree::Order.find_by(number: params[:order_id])
|
|
87
|
-
end
|
|
88
|
-
|
|
89
95
|
def payment_method
|
|
90
|
-
@payment_method ||= Spree::PaymentMethod.
|
|
96
|
+
@payment_method ||= Spree::PaymentMethod.find_by(id: params[:payment_method_id]) || Spree::PaymentMethod.find_by(type: 'Spree::Gateway::RazorpayGateway')
|
|
91
97
|
end
|
|
92
98
|
|
|
93
99
|
def gateway
|
|
94
|
-
|
|
100
|
+
payment_method
|
|
95
101
|
end
|
|
96
102
|
|
|
97
|
-
def
|
|
98
|
-
order.
|
|
103
|
+
def order
|
|
104
|
+
@order ||= Spree::Order.find_by(number: params[:order_id] || params[:order_number])
|
|
99
105
|
end
|
|
100
|
-
|
|
106
|
+
|
|
101
107
|
def completion_route
|
|
102
|
-
|
|
108
|
+
# Retrieve the guest token (supports both old and new Spree versions)
|
|
109
|
+
token = order.respond_to?(:guest_token) ? order.guest_token : order.token
|
|
110
|
+
|
|
111
|
+
if token.present?
|
|
112
|
+
# Manually construct the URL: /checkout/TOKEN/complete
|
|
113
|
+
"/checkout/#{token}/complete"
|
|
114
|
+
else
|
|
115
|
+
# Fallback if no token exists (standard logged-in user path)
|
|
116
|
+
spree.order_path(order)
|
|
117
|
+
end
|
|
103
118
|
end
|
|
104
119
|
end
|
|
105
120
|
end
|
|
@@ -10,7 +10,7 @@ module Spree
|
|
|
10
10
|
preference :merchant_name, :string, default: 'Razorpay'
|
|
11
11
|
preference :merchant_description, :text, default: 'Razorpay Payment Gateway'
|
|
12
12
|
preference :merchant_address, :string, default: 'Razorpay, Bangalore, India'
|
|
13
|
-
preference :theme_color, :string, default: '#
|
|
13
|
+
preference :theme_color, :string, default: '#2e5bff'
|
|
14
14
|
|
|
15
15
|
def supports?(_source)
|
|
16
16
|
true
|
|
@@ -20,8 +20,16 @@ module Spree
|
|
|
20
20
|
false
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
def name
|
|
24
|
+
'Razorpay Secure (UPI, Wallets, Cards & Netbanking)'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def method_type
|
|
28
|
+
'razorpay'
|
|
29
|
+
end
|
|
30
|
+
|
|
23
31
|
def payment_source_class
|
|
24
|
-
|
|
32
|
+
'razorpay'
|
|
25
33
|
end
|
|
26
34
|
|
|
27
35
|
def payment_icon_name
|
|
@@ -56,10 +64,6 @@ module Spree
|
|
|
56
64
|
true
|
|
57
65
|
end
|
|
58
66
|
|
|
59
|
-
def method_type
|
|
60
|
-
'razorpay'
|
|
61
|
-
end
|
|
62
|
-
|
|
63
67
|
def request_type
|
|
64
68
|
'DEFAULT'
|
|
65
69
|
end
|
|
@@ -76,11 +80,12 @@ module Spree
|
|
|
76
80
|
payment.state != 'void'
|
|
77
81
|
end
|
|
78
82
|
|
|
83
|
+
# Not used directly (we use custom flow), but kept it for compatibility
|
|
79
84
|
def purchase(_amount, _transaction_details, _gateway_options = {})
|
|
80
85
|
ActiveMerchant::Billing::Response.new(true, 'Razorpay success')
|
|
81
86
|
end
|
|
82
87
|
|
|
83
|
-
def capture(*)
|
|
88
|
+
def capture(*args)
|
|
84
89
|
simulated_successful_billing_response
|
|
85
90
|
end
|
|
86
91
|
|
|
@@ -92,20 +97,37 @@ module Spree
|
|
|
92
97
|
ActiveMerchant::Billing::Response.new(true, 'Refund successful')
|
|
93
98
|
end
|
|
94
99
|
|
|
100
|
+
def cancel(payment, _options = {})
|
|
101
|
+
# If `payment` is a Spree::Payment, use its source
|
|
102
|
+
source = if payment.respond_to?(:source)
|
|
103
|
+
payment.source else payment end
|
|
104
|
+
payment.void! if payment.respond_to?(:void!)
|
|
105
|
+
if source.respond_to?(:razorpay_payment_id)
|
|
106
|
+
# Uncomment if you want to actually trigger refund
|
|
107
|
+
# Razorpay::Payment.fetch(source.razorpay_payment_id).refund
|
|
108
|
+
OpenStruct.new(success?: true, authorization: source.razorpay_payment_id)
|
|
109
|
+
else
|
|
110
|
+
# fallback for string/unknown source
|
|
111
|
+
OpenStruct.new(success?: true, authorization: nil)
|
|
112
|
+
end
|
|
113
|
+
rescue => e
|
|
114
|
+
Rails.logger.error("Razorpay cancel failed: #{e.message}")
|
|
115
|
+
OpenStruct.new(success?: false, message: e.message)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Verify signature, fetch payment and capture if required. Returns Razorpay::Payment object.
|
|
95
119
|
def verify_and_capture_razorpay_payment(order, razorpay_payment_id)
|
|
96
120
|
Razorpay.setup(current_key_id, current_key_secret)
|
|
97
121
|
|
|
98
122
|
begin
|
|
99
123
|
payment = Razorpay::Payment.fetch(razorpay_payment_id)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
124
|
+
# If payment is not captured and auto_capture set true, capture it
|
|
125
|
+
if payment.status == 'authorized'
|
|
126
|
+
amount = (order.total.to_f * 100).to_i
|
|
127
|
+
payment = payment.capture(amount: amount)
|
|
103
128
|
end
|
|
104
129
|
|
|
105
|
-
# Capture only if you're not using auto-capture (your setting says auto_capture = true)
|
|
106
|
-
payment.capture(amount: (order.total * 100).to_i)
|
|
107
130
|
payment
|
|
108
|
-
|
|
109
131
|
rescue Razorpay::Error => e
|
|
110
132
|
raise Spree::Core::GatewayError, "Razorpay error: #{e.message}"
|
|
111
133
|
end
|
|
@@ -1,4 +1,25 @@
|
|
|
1
1
|
module Spree
|
|
2
|
-
class RazorpayCheckout <
|
|
2
|
+
class RazorpayCheckout < Spree::Base
|
|
3
|
+
self.table_name = 'spree_razorpay_checkouts'
|
|
4
|
+
|
|
5
|
+
belongs_to :order, class_name: 'Spree::Order', optional: true
|
|
6
|
+
|
|
7
|
+
# Appears in Spree Admin > Payments
|
|
8
|
+
def name
|
|
9
|
+
"Razorpay"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def method_type
|
|
13
|
+
"razorpay"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Optional — to show the payment IDs in admin
|
|
17
|
+
def payment_id
|
|
18
|
+
self.razorpay_payment_id
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def order_id
|
|
22
|
+
self.razorpay_order_id
|
|
23
|
+
end
|
|
3
24
|
end
|
|
4
25
|
end
|
|
@@ -5,26 +5,31 @@ module SpreeRazorpayCheckout
|
|
|
5
5
|
(total.to_f * 100).to_i
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
+
# Create a Spree::Payment and return it so controller can complete it
|
|
8
9
|
def razor_payment(payment_object, payment_method, razorpay_signature)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
10
|
+
source = ::Spree::RazorpayCheckout.create!(
|
|
11
|
+
order_id: id,
|
|
12
|
+
razorpay_payment_id: payment_object.id,
|
|
13
|
+
razorpay_order_id: payment_object.order_id,
|
|
14
|
+
razorpay_signature: razorpay_signature,
|
|
15
|
+
status: payment_object.status,
|
|
16
|
+
payment_method: payment_object.method,
|
|
17
|
+
card_id: payment_object.card_id,
|
|
18
|
+
bank: payment_object.bank,
|
|
19
|
+
wallet: payment_object.wallet,
|
|
20
|
+
vpa: payment_object.vpa,
|
|
21
|
+
email: payment_object.email,
|
|
22
|
+
contact: payment_object.contact
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
payment = payments.create!(
|
|
26
|
+
source: source,
|
|
24
27
|
payment_method: payment_method,
|
|
25
28
|
amount: total,
|
|
26
|
-
response_code: payment_object.
|
|
29
|
+
response_code: payment_object.id
|
|
27
30
|
)
|
|
31
|
+
|
|
32
|
+
payment
|
|
28
33
|
end
|
|
29
34
|
|
|
30
35
|
::Spree::Order.prepend SpreeRazorpayCheckout::Spree::OrderDecorator
|
|
@@ -5,18 +5,19 @@ module Razorpay
|
|
|
5
5
|
|
|
6
6
|
def create(order_id)
|
|
7
7
|
@order = Spree::Order.find_by(id: order_id)
|
|
8
|
+
raise "Order not found" unless order
|
|
8
9
|
|
|
9
10
|
razorpay_order = Razorpay::Order.create(order_create_params)
|
|
10
11
|
|
|
11
12
|
if razorpay_order.try(:id).present?
|
|
12
13
|
log_order_in_db(razorpay_order.id)
|
|
13
|
-
return razorpay_order.id
|
|
14
|
+
return [razorpay_order.id, order.inr_amt_in_paise]
|
|
14
15
|
end
|
|
15
16
|
|
|
16
|
-
''
|
|
17
|
+
['', 0]
|
|
17
18
|
rescue StandardError => e
|
|
18
19
|
Rails.logger.error("Razorpay Order create failed: #{e.message}")
|
|
19
|
-
''
|
|
20
|
+
['', 0]
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
private
|
|
@@ -24,7 +25,7 @@ module Razorpay
|
|
|
24
25
|
def order_create_params
|
|
25
26
|
{
|
|
26
27
|
amount: order.inr_amt_in_paise,
|
|
27
|
-
currency: order.currency,
|
|
28
|
+
currency: order.currency || 'INR',
|
|
28
29
|
receipt: order.number
|
|
29
30
|
}
|
|
30
31
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p class="mb-1">
|
|
2
|
-
Razorpay
|
|
2
|
+
Razorpay India powers your store with simple, secure, and scalable payments across UPI, cards, wallets, and more.
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<div class="d-flex align-items-center">
|
|
@@ -7,9 +7,5 @@
|
|
|
7
7
|
<%= payment_method_icon_tag 'master', class: 'm-1' %>
|
|
8
8
|
<%= payment_method_icon_tag 'google_pay', class: 'm-1' %>
|
|
9
9
|
<%= payment_method_icon_tag 'amazon', class: 'm-1' %>
|
|
10
|
-
<%= payment_method_icon_tag 'mobikwik', class: 'm-1' %>
|
|
11
10
|
<%= payment_method_icon_tag 'upi', class: 'm-1' %>
|
|
12
|
-
|
|
13
|
-
<%= payment_method_icon_tag 'paytm', class: 'm-1' %>
|
|
14
|
-
<%= payment_method_icon_tag 'payzapp', class: 'm-1' %>
|
|
15
|
-
</div>
|
|
11
|
+
</div>
|
|
@@ -47,7 +47,9 @@ function setupRazorpay() {
|
|
|
47
47
|
}
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
-
razorpayBtn.addEventListener("click", function () {
|
|
50
|
+
razorpayBtn.addEventListener("click", function (e) {
|
|
51
|
+
e.preventDefault(); // Prevent any default button behavior
|
|
52
|
+
|
|
51
53
|
fetch("/razorpay/create_order", {
|
|
52
54
|
method: "POST",
|
|
53
55
|
headers: {
|
|
@@ -72,10 +74,19 @@ function setupRazorpay() {
|
|
|
72
74
|
description: "<%= payment_method.preferred_merchant_description.presence || 'Order Payment' %>",
|
|
73
75
|
handler: function (response) {
|
|
74
76
|
razorpayCompleted = true;
|
|
77
|
+
|
|
78
|
+
// 1. Populate Hidden Fields
|
|
75
79
|
document.getElementById("razorpay_payment_id").value = response.razorpay_payment_id;
|
|
76
80
|
document.getElementById("razorpay_order_id").value = response.razorpay_order_id;
|
|
77
81
|
document.getElementById("razorpay_signature").value = response.razorpay_signature;
|
|
78
|
-
|
|
82
|
+
|
|
83
|
+
// 2. Change Form Action to your custom controller
|
|
84
|
+
form.action = "/razorpay/response?order_id=<%= @order.number %>&payment_method_id=" + razorpayMethodId;
|
|
85
|
+
form.method = "POST";
|
|
86
|
+
|
|
87
|
+
// 3. Submit the Form (Standard Browser Submit)
|
|
88
|
+
// To ensures the redirect works correctly
|
|
89
|
+
form.submit();
|
|
79
90
|
},
|
|
80
91
|
modal: {
|
|
81
92
|
ondismiss: function () {
|
|
@@ -88,15 +99,15 @@ function setupRazorpay() {
|
|
|
88
99
|
contact: "<%= @order.bill_address&.phone %>"
|
|
89
100
|
},
|
|
90
101
|
theme: {
|
|
91
|
-
color: "<%= payment_method.preferred_theme_color.presence || '#
|
|
102
|
+
color: "<%= payment_method.preferred_theme_color.presence || '#2e5bff' %>"
|
|
92
103
|
}
|
|
93
104
|
};
|
|
94
|
-
delete options.session;
|
|
95
105
|
|
|
96
106
|
const rzp = new Razorpay(options);
|
|
97
107
|
rzp.open();
|
|
98
108
|
})
|
|
99
|
-
.catch(() => {
|
|
109
|
+
.catch((err) => {
|
|
110
|
+
console.error(err);
|
|
100
111
|
alert("Error creating Razorpay order. Please refresh and try again.");
|
|
101
112
|
});
|
|
102
113
|
});
|
data/config/routes.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
Spree::Core::Engine.add_routes do
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
post '/razorpay/create_order', to: 'razorpay#create_order'
|
|
4
|
-
|
|
4
|
+
#Accept both POST and PATCH
|
|
5
|
+
match '/razorpay/response', to: 'razorpay#razor_response', via: [:post, :patch], as: :razorpay_response
|
|
5
6
|
end
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
class CreateSpreeRazorpayCheckouts < ActiveRecord::Migration[6.1]
|
|
2
2
|
def change
|
|
3
3
|
create_table :spree_razorpay_checkouts do |t|
|
|
4
|
-
t.
|
|
5
|
-
t.string :razorpay_payment_id
|
|
4
|
+
t.references :order, null: true, foreign_key: { to_table: :spree_orders }
|
|
6
5
|
t.string :razorpay_order_id
|
|
6
|
+
t.string :razorpay_payment_id
|
|
7
7
|
t.string :razorpay_signature
|
|
8
8
|
t.string :status
|
|
9
9
|
t.string :payment_method
|
|
@@ -16,5 +16,8 @@ class CreateSpreeRazorpayCheckouts < ActiveRecord::Migration[6.1]
|
|
|
16
16
|
|
|
17
17
|
t.timestamps
|
|
18
18
|
end
|
|
19
|
+
|
|
20
|
+
add_index :spree_razorpay_checkouts, :razorpay_order_id
|
|
21
|
+
add_index :spree_razorpay_checkouts, :razorpay_payment_id
|
|
19
22
|
end
|
|
20
|
-
end
|
|
23
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spree_razorpay_checkout
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Umesh Ravani
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-
|
|
11
|
+
date: 2025-11-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: razorpay
|
|
@@ -28,14 +28,14 @@ dependencies:
|
|
|
28
28
|
name: spree
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- - "
|
|
31
|
+
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
33
|
version: '5.0'
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
|
-
- - "
|
|
38
|
+
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '5.0'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
@@ -103,6 +103,7 @@ extra_rdoc_files: []
|
|
|
103
103
|
files:
|
|
104
104
|
- README.md
|
|
105
105
|
- app/assets/images/payment_icons/razorpay.svg
|
|
106
|
+
- app/assets/images/payment_icons/razorpaycheckout.svg
|
|
106
107
|
- app/assets/javascripts/spree/frontend/process_razorpay.js
|
|
107
108
|
- app/assets/javascripts/spree/frontend/spree_razorpay.js
|
|
108
109
|
- app/controllers/concerns/spree/razor_pay.rb
|