spree_account_recurring 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +26 -0
- data/README.md +108 -0
- data/app/assets/javascripts/admin/spree_account_recurring.js +1 -0
- data/app/assets/javascripts/store/spree_account_recurring.js +1 -0
- data/app/assets/javascripts/store/stripe.js +53 -0
- data/app/assets/stylesheets/admin/spree_account_recurring.css +3 -0
- data/app/assets/stylesheets/store/spree_account_recurring.css +3 -0
- data/app/controllers/concerns/spree/admin/ransack_date_search.rb +47 -0
- data/app/controllers/spree/admin/plans_controller.rb +63 -0
- data/app/controllers/spree/admin/recurrings_controller.rb +66 -0
- data/app/controllers/spree/admin/subscription_events_controller.rb +14 -0
- data/app/controllers/spree/admin/subscriptions_controller.rb +14 -0
- data/app/controllers/spree/plans_controller.rb +7 -0
- data/app/controllers/spree/recurring_hooks_controller.rb +53 -0
- data/app/controllers/spree/subscriptions_controller.rb +69 -0
- data/app/models/concerns/before_each.rb +21 -0
- data/app/models/concerns/restrictive_destroyer.rb +44 -0
- data/app/models/concerns/spree/plan/api_handler.rb +46 -0
- data/app/models/concerns/spree/recurring/stripe_recurring/api_handler.rb +28 -0
- data/app/models/concerns/spree/recurring/stripe_recurring/api_handler/plan_api_handler.rb +49 -0
- data/app/models/concerns/spree/recurring/stripe_recurring/api_handler/subscription_api_handler.rb +20 -0
- data/app/models/concerns/spree/recurring/stripe_recurring/api_handler/subscription_event_api_handler.rb +19 -0
- data/app/models/concerns/spree/subscription/api_handler.rb +38 -0
- data/app/models/concerns/spree/subscription/role_subscriber.rb +36 -0
- data/app/models/spree/plan.rb +31 -0
- data/app/models/spree/recurring.rb +27 -0
- data/app/models/spree/recurring/stripe_recurring.rb +16 -0
- data/app/models/spree/subscriber_ability.rb +17 -0
- data/app/models/spree/subscription.rb +33 -0
- data/app/models/spree/subscription_event.rb +11 -0
- data/app/models/spree/user_decorator.rb +14 -0
- data/app/overrides/admin/view_decorator.rb +27 -0
- data/app/views/spree/admin/plans/_form.html.erb +47 -0
- data/app/views/spree/admin/plans/destroy.js.erb +0 -0
- data/app/views/spree/admin/plans/edit.html.erb +22 -0
- data/app/views/spree/admin/plans/index.html.erb +70 -0
- data/app/views/spree/admin/plans/new.html.erb +22 -0
- data/app/views/spree/admin/recurrings/_form.html.erb +42 -0
- data/app/views/spree/admin/recurrings/destroy.js.erb +0 -0
- data/app/views/spree/admin/recurrings/edit.html.erb +23 -0
- data/app/views/spree/admin/recurrings/index.html.erb +48 -0
- data/app/views/spree/admin/recurrings/new.html.erb +22 -0
- data/app/views/spree/admin/subscription_events/index.html.erb +72 -0
- data/app/views/spree/admin/subscriptions/index.html.erb +76 -0
- data/app/views/spree/plans/index.html.erb +14 -0
- data/app/views/spree/subscriptions/new.html.erb +36 -0
- data/app/views/spree/subscriptions/show.html.erb +4 -0
- data/config/initializers/stripe.rb +2 -0
- data/config/locales/en.yml +43 -0
- data/config/routes.rb +24 -0
- data/db/migrate/20131119054112_create_spree_recurring.rb +13 -0
- data/db/migrate/20131119054212_create_spree_plan.rb +21 -0
- data/db/migrate/20131119054322_create_spree_subscription.rb +16 -0
- data/db/migrate/20131125123820_create_spree_subscription_events.rb +14 -0
- data/db/migrate/20131202110012_add_subscriber_role_to_spree_roles.rb +15 -0
- data/db/migrate/20140301141327_add_stripe_customer_id_to_spree_users.rb +6 -0
- data/db/migrate/20140303072857_add_default_to_spree_plans.rb +6 -0
- data/db/migrate/20140319092254_add_response_to_spree_subscription_events.rb +5 -0
- data/lib/generators/spree_account_recurring/install/install_generator.rb +31 -0
- data/lib/spree_account_recurring.rb +2 -0
- data/lib/spree_account_recurring/engine.rb +22 -0
- data/lib/spree_account_recurring/factories.rb +6 -0
- data/lib/spree_account_recurring/testing_support/ransack_date_search_helper.rb +125 -0
- metadata +188 -0
data/LICENSE
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Copyright (c) 2013 [name of plugin creator]
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
* Redistributions of source code must retain the above copyright notice,
|
8
|
+
this list of conditions and the following disclaimer.
|
9
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
11
|
+
and/or other materials provided with the distribution.
|
12
|
+
* Neither the name Spree nor the names of its contributors may be used to
|
13
|
+
endorse or promote products derived from this software without specific
|
14
|
+
prior written permission.
|
15
|
+
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
17
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
18
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
19
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
20
|
+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
21
|
+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
22
|
+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
23
|
+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
24
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
25
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
26
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
Spree Account Recurring [![Code Climate](https://codeclimate.com/github/vinsol/spree-account-recurring.png)](https://codeclimate.com/github/vinsol/spree-account-recurring) [![Build Status](https://travis-ci.org/vinsol/spree-account-recurring.svg?branch=2-1-stable)](https://travis-ci.org/vinsol/spree-account-recurring)
|
2
|
+
=========================
|
3
|
+
|
4
|
+
Spree extension to manage recurring payments/subscriptions using [Stripe Payment Gateway](https://stripe.com/).
|
5
|
+
|
6
|
+
All plans and subscription scenarios are been managed as per [Stripe Docs](https://stripe.com/docs/api)
|
7
|
+
|
8
|
+
Installation
|
9
|
+
------------
|
10
|
+
|
11
|
+
Install `spree_account_recurring` by adding the following to your `Gemfile`:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'spree_account_recurring'
|
15
|
+
```
|
16
|
+
|
17
|
+
Bundle your dependencies and run the installation generator:
|
18
|
+
|
19
|
+
```shell
|
20
|
+
bundle
|
21
|
+
bundle exec rails g spree_account_recurring:install
|
22
|
+
```
|
23
|
+
|
24
|
+
Usage
|
25
|
+
-----
|
26
|
+
|
27
|
+
At Admin end this will add a configuration tab as "Recurring".
|
28
|
+
|
29
|
+
* Creating a Recurring Provider:
|
30
|
+
* Create a recurring using `Spree::Recurring::StripeRecurring` Provider and save
|
31
|
+
* Add secret key and public key provided by [stripe](https://stripe.com/) to this recurring.
|
32
|
+
|
33
|
+
* Creating Plans for Recurring Provider:
|
34
|
+
* Go to "Manage Plans" from Recurring edit page.
|
35
|
+
* Create a plan by specifying respective details. This will create the same plan on your stripe account.
|
36
|
+
* Only name can be updated for a plan.
|
37
|
+
|
38
|
+
One Recurring Provider can have multiple plans.
|
39
|
+
|
40
|
+
At Front end you can view all plans here: `http://your.domain.name/recurring/plans`
|
41
|
+
|
42
|
+
* Subscribe a plan:
|
43
|
+
* Click subscribe for any plan.
|
44
|
+
* Fill in credit card details and submit.
|
45
|
+
* This will create a customer in Stripe for user and subscribe that user respective to plan.
|
46
|
+
|
47
|
+
* Unsubscribe a plan:
|
48
|
+
* In plans page subscribed plan will be listed and from there user can unsubscribe from plan.
|
49
|
+
|
50
|
+
At Admin, all subscriptions can be seen under "Reports" -> "Subscriptions".
|
51
|
+
|
52
|
+
Stripe Webhook
|
53
|
+
--------------
|
54
|
+
|
55
|
+
Create a webhook at stripe with url `http://your.domain.name/recurring_hooks/handler` which will receive below mentioned stripe event hooks.
|
56
|
+
|
57
|
+
Events:
|
58
|
+
* `customer.subscription.deleted`
|
59
|
+
* `customer.subscription.created`
|
60
|
+
* `customer.subscription.updated`
|
61
|
+
* `invoice.payment_succeeded`
|
62
|
+
* `invoice.payment_failed`
|
63
|
+
* `charge.succeeded`
|
64
|
+
* `charge.failed`
|
65
|
+
* `charge.refunded`
|
66
|
+
* `charge.captured`
|
67
|
+
* `plan.created`
|
68
|
+
* `plan.updated`
|
69
|
+
* `plan.deleted`
|
70
|
+
|
71
|
+
These events can be viewed at admin in "Reports" -> "Subscription Events"
|
72
|
+
|
73
|
+
Testing
|
74
|
+
-------
|
75
|
+
|
76
|
+
Be sure to bundle your dependencies and then create a dummy test app for the specs to run against.
|
77
|
+
|
78
|
+
```shell
|
79
|
+
bundle
|
80
|
+
bundle exec rake test_app
|
81
|
+
bundle exec rspec spec
|
82
|
+
```
|
83
|
+
|
84
|
+
When testing your applications integration with this extension you may use it's factories.
|
85
|
+
Simply add this require statement to your spec_helper:
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
require 'spree_account_recurring/factories'
|
89
|
+
```
|
90
|
+
|
91
|
+
Contributing
|
92
|
+
------------
|
93
|
+
|
94
|
+
1. Fork the repo.
|
95
|
+
2. Clone your repo.
|
96
|
+
3. Run `bundle install`.
|
97
|
+
4. Run `bundle exec rake test_app` to create the test application in `spec/test_app`.
|
98
|
+
5. Make your changes.
|
99
|
+
6. Ensure specs pass by running `bundle exec rspec spec`.
|
100
|
+
7. Submit your pull request.
|
101
|
+
|
102
|
+
|
103
|
+
Credits
|
104
|
+
-------
|
105
|
+
|
106
|
+
[![vinsol.com: Ruby on Rails, iOS and Android developers](http://vinsol.com/vin_logo.png "Ruby on Rails, iOS and Android developers")](http://vinsol.com)
|
107
|
+
|
108
|
+
Copyright (c) 2014 [vinsol.com](http://vinsol.com "Ruby on Rails, iOS and Android developers"), released under the New MIT License
|
@@ -0,0 +1 @@
|
|
1
|
+
//= require admin/spree_backend
|
@@ -0,0 +1 @@
|
|
1
|
+
//= require store/spree_frontend
|
@@ -0,0 +1,53 @@
|
|
1
|
+
// Inspired by https://stripe.com/docs/stripe.js
|
2
|
+
|
3
|
+
mapCC = function(ccType){
|
4
|
+
if(ccType == 'MasterCard'){
|
5
|
+
return 'mastercard';
|
6
|
+
} else if(ccType == 'Visa'){
|
7
|
+
return 'visa';
|
8
|
+
} else if(ccType == 'American Express'){
|
9
|
+
return 'amex';
|
10
|
+
} else if(ccType == 'Discover'){
|
11
|
+
return 'discover';
|
12
|
+
} else if(ccType == 'Diners Club'){
|
13
|
+
return 'dinersclub';
|
14
|
+
} else if(ccType == 'JCB'){
|
15
|
+
return 'jcb';
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
$(document).ready(function(){
|
20
|
+
// For errors that happen later.
|
21
|
+
Spree.stripePaymentMethod.prepend("<div id='stripeError' class='errorExplanation' style='display:none'></div>")
|
22
|
+
|
23
|
+
$('.continue').on('click', function(){
|
24
|
+
$('#stripeError').hide();
|
25
|
+
if(Spree.stripePaymentMethod.is(':visible')){
|
26
|
+
expiration = $('.cardExpiry:visible').payment('cardExpiryVal')
|
27
|
+
params = {
|
28
|
+
number: $('.cardNumber:visible').val(),
|
29
|
+
cvc: $('.cardCode:visible').val(),
|
30
|
+
exp_month: expiration.month || 0,
|
31
|
+
exp_year: expiration.year || 0
|
32
|
+
};
|
33
|
+
|
34
|
+
Stripe.card.createToken(params, stripeResponseHandler);
|
35
|
+
return false;
|
36
|
+
}
|
37
|
+
});
|
38
|
+
});
|
39
|
+
|
40
|
+
stripeResponseHandler = function(status, response){
|
41
|
+
if(response.error){
|
42
|
+
$('#stripeError').html(response.error.message);
|
43
|
+
$('#stripeError').show();
|
44
|
+
} else {
|
45
|
+
Spree.stripePaymentMethod.find('#card_number, #card_expiry, #card_code').prop("disabled" , true);
|
46
|
+
Spree.stripePaymentMethod.find(".ccType").prop("disabled", false);
|
47
|
+
Spree.stripePaymentMethod.find(".ccType").val(mapCC(response.card.type))
|
48
|
+
token = response['id'];
|
49
|
+
// insert the token into the form so it gets submitted to the server
|
50
|
+
Spree.stripePaymentMethod.append("<input type='hidden' class='stripeToken' name='subscription[card_token]' value='" + token + "'/>");
|
51
|
+
Spree.stripePaymentMethod.parents("form").get(0).submit();
|
52
|
+
}
|
53
|
+
}
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Spree
|
2
|
+
module Admin
|
3
|
+
module RansackDateSearch
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def ransack_date_searchable(options={})
|
8
|
+
raise ArgumentError, "Hash expected, got #{options.class.name}" unless options.is_a?(Hash)
|
9
|
+
class_attribute :ransack_date_search_config, :ransack_date_search_col_ref, :ransack_date_search_param_gt, :ransack_date_search_param_lt
|
10
|
+
self.ransack_date_search_config = { date_col: "created_at" }.merge!(options)
|
11
|
+
# self.ransack_date_search_config[:before_action] = [ransack_date_search_config[:before_action]] unless ransack_date_search_config[:before_action].is_a?(Array)
|
12
|
+
self.ransack_date_search_col_ref = ransack_date_search_config[:date_col]
|
13
|
+
self.ransack_date_search_param_gt = "#{ransack_date_search_col_ref}_gt"
|
14
|
+
self.ransack_date_search_param_lt = "#{ransack_date_search_col_ref}_lt"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
included do
|
19
|
+
before_action :parse_ransack_date_search_param!, only: 'index'
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def parse_ransack_date_search_param!
|
25
|
+
params[:q] = {} unless params[:q]
|
26
|
+
parse_ransack_date_search_param_gt!
|
27
|
+
parse_ransack_date_search_param_lt!
|
28
|
+
params[:q][:s] ||= "#{ransack_date_search_col_ref} desc"
|
29
|
+
params[:q].delete_if{ |k, v| v.blank? }
|
30
|
+
end
|
31
|
+
|
32
|
+
def parse_ransack_date_search_param_gt!
|
33
|
+
if params[:q][ransack_date_search_param_gt].blank?
|
34
|
+
params[:q][ransack_date_search_param_gt] = Time.current.beginning_of_month
|
35
|
+
else
|
36
|
+
params[:q][ransack_date_search_param_gt] = Time.zone.parse(params[:q][ransack_date_search_param_gt]).beginning_of_day rescue Time.current.beginning_of_month
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def parse_ransack_date_search_param_lt!
|
41
|
+
if params[:q][ransack_date_search_param_lt].present?
|
42
|
+
params[:q][ransack_date_search_param_lt] = Time.zone.parse(params[:q][ransack_date_search_param_lt]).end_of_day rescue ""
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Spree
|
2
|
+
module Admin
|
3
|
+
class PlansController < Spree::Admin::BaseController
|
4
|
+
before_action :load_recurring
|
5
|
+
before_action :find_plan, :only => [:edit, :destroy, :update]
|
6
|
+
|
7
|
+
def index
|
8
|
+
@plans = Spree::Plan.undeleted.order('id desc')
|
9
|
+
end
|
10
|
+
|
11
|
+
def new
|
12
|
+
@plan = @recurring.plans.build
|
13
|
+
end
|
14
|
+
|
15
|
+
def create
|
16
|
+
@plan = @recurring.plans.build(plan_params)
|
17
|
+
if @plan.save_and_manage_api
|
18
|
+
flash[:notice] = 'Plan created successfully.'
|
19
|
+
redirect_to edit_admin_recurring_plan_path(@recurring, @plan)
|
20
|
+
else
|
21
|
+
render :new
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def update
|
26
|
+
if @plan.save_and_manage_api(plan_params(:update))
|
27
|
+
flash[:notice] = 'Plan updated successfully.'
|
28
|
+
redirect_to edit_admin_recurring_plan_path(@recurring, @plan)
|
29
|
+
else
|
30
|
+
render :edit
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def destroy
|
35
|
+
@plan.restrictive_destroy_with_api
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def load_recurring
|
41
|
+
unless @recurring = Spree::Recurring.undeleted.where(id: params[:recurring_id]).first
|
42
|
+
flash[:error] = "Recurring not found."
|
43
|
+
redirect_to admin_recurrings_path
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def plan_params(action=:create)
|
48
|
+
if action == :create
|
49
|
+
params.require(:plan).permit(:name, :trial_period_days, :interval, :currency, :amount, :active, :interval_count, :default)
|
50
|
+
else
|
51
|
+
params.require(:plan).permit(:name, :active, :default)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def find_plan
|
56
|
+
unless @plan = @recurring.plans.undeleted.where(id: params[:id]).first
|
57
|
+
flash[:error] = "Plan not found."
|
58
|
+
redirect_to admin_recurring_plans_path(@recurring)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Spree
|
2
|
+
module Admin
|
3
|
+
class RecurringsController < Spree::Admin::BaseController
|
4
|
+
before_action :find_recurring, :only => [:edit, :update, :destroy]
|
5
|
+
before_action :build_recurring, :only => :create
|
6
|
+
|
7
|
+
def index
|
8
|
+
@recurrings = Spree::Recurring.undeleted.order('id desc')
|
9
|
+
end
|
10
|
+
|
11
|
+
def new
|
12
|
+
@recurring = Spree::Recurring.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def create
|
16
|
+
if @recurring.save
|
17
|
+
flash[:notice] = "Recurring created succesfully."
|
18
|
+
redirect_to edit_admin_recurring_url(@recurring)
|
19
|
+
else
|
20
|
+
render :new
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def update
|
25
|
+
if @recurring.update_attributes(recurring_params(:update))
|
26
|
+
flash[:notice] = "Recurring updated succesfully."
|
27
|
+
redirect_to edit_admin_recurring_url(@recurring)
|
28
|
+
else
|
29
|
+
render :edit
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def destroy
|
34
|
+
@recurring.restrictive_destroy
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def find_recurring
|
40
|
+
unless @recurring = Spree::Recurring.undeleted.where(id: params[:id]).first
|
41
|
+
flash[:error] = "Recurring not found."
|
42
|
+
respond_to do |format|
|
43
|
+
format.html {redirect_to admin_recurrings_url}
|
44
|
+
format.js { }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def recurring_params(action=:create)
|
50
|
+
if action == :create
|
51
|
+
params.require(:recurring).permit(:name, :type, :description, :active)
|
52
|
+
else
|
53
|
+
params.require(:recurring).permit(:name, :description, :active).merge(preference_params)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def build_recurring
|
58
|
+
@recurring = recurring_params.delete(:type).constantize.new(recurring_params)
|
59
|
+
end
|
60
|
+
|
61
|
+
def preference_params
|
62
|
+
params[ActiveModel::Naming.param_key(@recurring)]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Spree
|
2
|
+
module Admin
|
3
|
+
class SubscriptionEventsController < Spree::Admin::BaseController
|
4
|
+
include RansackDateSearch
|
5
|
+
ransack_date_searchable date_col: 'created_at'
|
6
|
+
|
7
|
+
def index
|
8
|
+
@search = Spree::SubscriptionEvent.ransack(params[:q])
|
9
|
+
@subscription_events = @search.result.includes(subscription: { plan: :recurring }).references(subscription: { plan: :recurring }).page(params[:page]).per(15)
|
10
|
+
respond_with(@subscription_events)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Spree
|
2
|
+
module Admin
|
3
|
+
class SubscriptionsController < Spree::Admin::BaseController
|
4
|
+
include RansackDateSearch
|
5
|
+
ransack_date_searchable date_col: 'subscribed_at'
|
6
|
+
|
7
|
+
def index
|
8
|
+
@search = Spree::Subscription.ransack(params[:q])
|
9
|
+
@subscriptions = @search.result.includes(plan: :recurring).references(plan: :recurring).page(params[:page]).per(15)
|
10
|
+
respond_with(@subscriptions)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Spree
|
2
|
+
class RecurringHooksController < BaseController
|
3
|
+
skip_before_filter :verify_authenticity_token
|
4
|
+
|
5
|
+
before_action :authenticate_webhook
|
6
|
+
before_action :find_subscription
|
7
|
+
|
8
|
+
respond_to :json
|
9
|
+
|
10
|
+
def handler
|
11
|
+
@subscription_event = @subscription.events.build(subscription_event_params)
|
12
|
+
if @subscription_event.save
|
13
|
+
render_status_ok
|
14
|
+
else
|
15
|
+
render_status_failure
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def event
|
22
|
+
@event ||= (Rails.env.production? ? params.deep_dup : params.deep_dup[:recurring_hook])
|
23
|
+
end
|
24
|
+
|
25
|
+
def authenticate_webhook
|
26
|
+
render_status_ok if event.blank? || (event[:livemode] != Rails.env.production?) || (!Spree::Recurring::StripeRecurring::WEBHOOKS.include?(event[:type]))
|
27
|
+
end
|
28
|
+
|
29
|
+
def find_subscription
|
30
|
+
render_status_ok unless @subscription = Spree::User.find_by(stripe_customer_id: event[:data][:object][:customer]).subscription
|
31
|
+
end
|
32
|
+
|
33
|
+
def retrieve_api_event
|
34
|
+
@event = @subscription.provider.retrieve_event(event[:id])
|
35
|
+
end
|
36
|
+
|
37
|
+
def subscription_event_params
|
38
|
+
if retrieve_api_event && event.data.object.customer == @subscription.user.stripe_customer_id
|
39
|
+
{ event_id: event.id, request_type: event.type, response: event.to_json }
|
40
|
+
else
|
41
|
+
{}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def render_status_ok
|
46
|
+
render text: '', status: 200
|
47
|
+
end
|
48
|
+
|
49
|
+
def render_status_failure
|
50
|
+
render text: '', status: 403
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|