payola-payments 1.1.4 → 1.2.0

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.
Files changed (87) hide show
  1. checksums.yaml +5 -13
  2. data/app/assets/javascripts/payola/form.js +1 -0
  3. data/app/assets/javascripts/payola/subscription_form.js +74 -0
  4. data/app/controllers/payola/application_controller.rb +1 -2
  5. data/app/controllers/payola/subscriptions_controller.rb +94 -0
  6. data/app/models/concerns/payola/plan.rb +50 -0
  7. data/app/models/payola/subscription.rb +161 -0
  8. data/app/services/payola/cancel_subscription.rb +12 -0
  9. data/app/services/payola/change_subscription_plan.rb +29 -0
  10. data/app/services/payola/create_plan.rb +17 -0
  11. data/app/services/payola/create_subscription.rb +20 -0
  12. data/app/services/payola/invoice_failed.rb +42 -0
  13. data/app/services/payola/invoice_paid.rb +43 -0
  14. data/app/services/payola/process_subscription.rb +7 -0
  15. data/app/services/payola/start_subscription.rb +41 -0
  16. data/app/services/payola/sync_subscription.rb +11 -0
  17. data/app/services/payola/update_card.rb +27 -0
  18. data/app/services/payola/update_subscription.rb +11 -0
  19. data/app/views/payola/subscriptions/_cancel.html.erb +10 -0
  20. data/app/views/payola/subscriptions/_change_plan.html.erb +10 -0
  21. data/config/routes.rb +7 -0
  22. data/db/migrate/20141105043439_create_payola_subscriptions.rb +31 -0
  23. data/db/migrate/20141106034610_add_currency_to_payola_subscriptions.rb +6 -0
  24. data/db/migrate/20141107025420_add_guid_to_payola_subscriptions.rb +6 -0
  25. data/db/migrate/20141109203101_add_stripe_status_to_payola_subscription.rb +5 -0
  26. data/db/migrate/20141112024805_add_affiliate_id_to_payola_subscriptions.rb +5 -0
  27. data/db/migrate/20141114032013_add_coupon_code_to_payola_subscriptions.rb +5 -0
  28. data/db/migrate/20141114154223_add_signed_custom_fields_to_payola_subscription.rb +5 -0
  29. data/db/migrate/20141114163841_add_addresses_to_payola_subscription.rb +6 -0
  30. data/lib/payola.rb +11 -0
  31. data/lib/payola/engine.rb +10 -2
  32. data/lib/payola/version.rb +1 -1
  33. data/spec/concerns/plan_spec.rb +43 -0
  34. data/spec/controllers/payola/subscriptions_controller_spec.rb +148 -0
  35. data/spec/dummy/app/controllers/application_controller.rb +4 -0
  36. data/spec/dummy/app/controllers/home_controller.rb +6 -0
  37. data/spec/dummy/app/controllers/subscribe_controller.rb +11 -0
  38. data/spec/dummy/app/models/subscription_plan.rb +3 -0
  39. data/spec/dummy/app/views/home/index.html.erb +7 -0
  40. data/spec/dummy/app/views/subscribe/index.html.erb +21 -0
  41. data/spec/dummy/app/views/subscribe/show.html.erb +4 -0
  42. data/spec/dummy/config/initializers/payola.rb +4 -0
  43. data/spec/dummy/config/routes.rb +4 -0
  44. data/spec/dummy/db/development.sqlite3 +0 -0
  45. data/spec/dummy/db/migrate/20141105010234_create_subscription_plans.rb +14 -0
  46. data/spec/dummy/db/schema.rb +51 -1
  47. data/spec/dummy/db/test.sqlite3 +0 -0
  48. data/spec/dummy/log/development.log +275 -0
  49. data/spec/dummy/log/test.log +109420 -0
  50. data/spec/dummy/tmp/cache/assets/development/sprockets/0e585aeb88c1555ae8f5292f7c7a9068 +0 -0
  51. data/spec/dummy/tmp/cache/assets/development/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
  52. data/spec/dummy/tmp/cache/assets/development/sprockets/1b54952339cbc96811097cee5f8923b4 +0 -0
  53. data/spec/dummy/tmp/cache/assets/development/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
  54. data/spec/dummy/tmp/cache/assets/development/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
  55. data/spec/dummy/tmp/cache/assets/development/sprockets/5afa4bc970e0bc781b3500c696ba25ff +0 -0
  56. data/spec/dummy/tmp/cache/assets/development/sprockets/664efeb51d1b486dcfc30d142db56f3c +0 -0
  57. data/spec/dummy/tmp/cache/assets/development/sprockets/68a3bbdc64a0729b5896fa1e33fce4b1 +0 -0
  58. data/spec/dummy/tmp/cache/assets/development/sprockets/96b1ccdc193a02b2bbea195310262fa5 +0 -0
  59. data/spec/dummy/tmp/cache/assets/development/sprockets/aea159adbc0944b120d91d5b578612ba +0 -0
  60. data/spec/dummy/tmp/cache/assets/development/sprockets/b20dab973e971c61bc7e334a38453ae0 +0 -0
  61. data/spec/dummy/tmp/cache/assets/development/sprockets/b83da90cb58011313e13a97a4a41cb6e +0 -0
  62. data/spec/dummy/tmp/cache/assets/development/sprockets/c0b954b40f2881a6d286061bc069643e +0 -0
  63. data/spec/dummy/tmp/cache/assets/development/sprockets/c5b3877d49b39932a7557611cefca9a6 +0 -0
  64. data/spec/dummy/tmp/cache/assets/development/sprockets/ca491e325ff6948be9292f701aa4ce64 +0 -0
  65. data/spec/dummy/tmp/cache/assets/development/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
  66. data/spec/dummy/tmp/cache/assets/development/sprockets/d3f4b67cc032016dfeebc71f2148b9dc +0 -0
  67. data/spec/dummy/tmp/cache/assets/development/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
  68. data/spec/dummy/tmp/cache/assets/development/sprockets/d9856638a3c7b9cfd9d1b884e3b33982 +0 -0
  69. data/spec/dummy/tmp/cache/assets/development/sprockets/ddba6e841f28d3005e828fbfffd18b74 +0 -0
  70. data/spec/dummy/tmp/cache/assets/development/sprockets/df935e399019055729aa968bd7062096 +0 -0
  71. data/spec/dummy/tmp/cache/assets/development/sprockets/ef82c31948061751bb026663b54484e1 +0 -0
  72. data/spec/dummy/tmp/cache/assets/development/sprockets/f0b5d4bea0c6cdd1904e1e9a0a45532f +0 -0
  73. data/spec/dummy/tmp/cache/assets/development/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
  74. data/spec/factories/payola_subscriptions.rb +25 -0
  75. data/spec/factories/subscription_plan.rb +9 -0
  76. data/spec/models/payola/subscription_spec.rb +50 -0
  77. data/spec/services/payola/cancel_subscription_spec.rb +29 -0
  78. data/spec/services/payola/change_subscription_plan_spec.rb +31 -0
  79. data/spec/services/payola/charge_card_spec.rb +9 -5
  80. data/spec/services/payola/create_plan_spec.rb +24 -0
  81. data/spec/services/payola/create_subscription_spec.rb +65 -0
  82. data/spec/services/payola/invoice_failed_spec.rb +29 -0
  83. data/spec/services/payola/invoice_paid_spec.rb +49 -0
  84. data/spec/services/payola/start_subscription_spec.rb +36 -0
  85. data/spec/services/payola/sync_subscription_spec.rb +14 -0
  86. data/spec/services/payola/update_card_spec.rb +26 -0
  87. metadata +95 -30
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- OGQyNWRjNTdhYmQyNGU4MjFkNzM4MzY0MzZjNWM4YjU2NzRhMWU2MA==
5
- data.tar.gz: !binary |-
6
- N2Q0MmIyZmJiOGJhYThjOWUyYTlhM2Q4NTc4Mjg3MWQxMGFiMDQ2Mw==
2
+ SHA1:
3
+ metadata.gz: d521cb632f52fe32dbb63dd06926fd551fb0f44b
4
+ data.tar.gz: 6c9f77ab172e04d2b89cb4fe8c257ec2ae4efcb7
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- NDE0NjlkYmJkMzkyYjI5MGQ2ZGI5ODg0ODU4ZWUxM2IwNzhkODQ4MjVlYTQy
10
- MTU1NmM2NzcyMTAzZDllOThhNzE2MTIwZGJhYzkzY2UyZjNkMmFkZGIzMzNi
11
- ZTU1MDIzOWFkNmE3M2Y0ODJhNDU0Y2E4YmJiMDQ4M2IwNzVhYWU=
12
- data.tar.gz: !binary |-
13
- ZDc2YzBjYmExZTYzM2Y2MGQyNDJlYzAwZWZmZTBlYmQ2Nzg2MmFkMWZmODFm
14
- MzY4ZTdlNjU2ZGJkYmJkMDQ2OTcwNTk0YTBlMjYzNjE0ZDI2YWMzYTJlZWE3
15
- MTM3M2ZhMDExY2UyMDNmMTk0YjRmZjhkMWRiMWI2MjMxOTMxZDA=
6
+ metadata.gz: a27328550b968b7824f42f91b8fafe9562cf9fef30bc5b482e752aa83f2822f396664579ed3983ead027b577863a81cc4c464bcc22a6c7e1bb3a51720824c34b
7
+ data.tar.gz: 1fe5bcdc56753e6887a1bd70cc8465bd4c1fb807be1c6802b7e6258ee76c4f076f2dab9cb9a53a437459204ffaf45fed790c459bd03f12665779280de08a4340
@@ -47,6 +47,7 @@ var PayolaPaymentForm = {
47
47
  $.get(base_path + '/status/' + guid, function(data) {
48
48
  if (data.status === "finished") {
49
49
  form.append($('<input type="hidden" name="payola_sale_guid"></input>').val(guid));
50
+ form.append($('<input type="hidden" name="authenticity_token"></input>').val($('meta[name="csrf-token"]').attr("content")));
50
51
  form.get(0).submit();
51
52
  } else if (data.status === "errored") {
52
53
  PayolaPaymentForm.showError(form, data.error);
@@ -0,0 +1,74 @@
1
+ var PayolaSubscriptionForm = {
2
+ initialize: function() {
3
+ $(document).on('submit', '.payola-subscription-form', function() {
4
+ return PayolaSubscriptionForm.handleSubmit($(this));
5
+ });
6
+ },
7
+
8
+ handleSubmit: function(form) {
9
+ $(':submit').prop('disabled', true);
10
+ $('.payola-spinner').show();
11
+ Stripe.card.createToken(form, function(status, response) {
12
+ PayolaSubscriptionForm.stripeResponseHandler(form, status, response);
13
+ });
14
+ return false;
15
+ },
16
+
17
+ stripeResponseHandler: function(form, status, response) {
18
+ if (response.error) {
19
+ PayolaSubscriptionForm.showError(form, response.error.message);
20
+ $(':submit').prop('disabled', false);
21
+ } else {
22
+ var email = form.find("[data-payola='email']").val();
23
+ var coupon = form.find("[data-payola='coupon']").val();
24
+
25
+ var base_path = form.data('payola-base-path');
26
+ var plan_type = form.data('payola-plan-type');
27
+ var plan_id = form.data('payola-plan-id');
28
+
29
+ var data_form = $('<form></form>');
30
+ data_form.append($('<input type="hidden" name="stripeToken">').val(response.id));
31
+ data_form.append($('<input type="hidden" name="stripeEmail">').val(email));
32
+ data_form.append($('<input type="hidden" name="coupon">').val(coupon));
33
+ data_form.append(form.find('input[name="authenticity_token"]'));
34
+ $.ajax({
35
+ type: "POST",
36
+ url: base_path + "/subscribe/" + plan_type + "/" + plan_id,
37
+ data: data_form.serialize(),
38
+ success: function(data) { PayolaSubscriptionForm.poll(form, 60, data.guid, base_path) },
39
+ error: function(data) { PayolaSubscriptionForm.showError(form, data.responseJSON.error) }
40
+ });
41
+ }
42
+ },
43
+
44
+ poll: function(form, num_retries_left, guid, base_path) {
45
+ if (num_retries_left == 0) {
46
+ PayolaSubscriptionForm.showError(form, "This seems to be taking too long. Please contact support and give them transaction ID: " + guid)
47
+ }
48
+ $.get(base_path + '/subscription_status/' + guid, function(data) {
49
+ if (data.status === "active") {
50
+ form.append($('<input type="hidden" name="payola_subscription_guid"></input>').val(guid));
51
+ form.append($('<input type="hidden" name="authenticity_token"></input>').val($('meta[name="csrf-token"]').attr("content")));
52
+ form.get(0).submit();
53
+ } else if (data.status === "errored") {
54
+ PayolaSubscriptionForm.showError(form, data.error);
55
+ } else {
56
+ setTimeout(function() { PayolaSubscriptionForm.poll(form, num_retries_left - 1, guid, base_path) }, 500);
57
+ }
58
+ });
59
+ },
60
+
61
+ showError: function(form, message) {
62
+ $('.payola-spinner').hide();
63
+ var error_selector = form.data('payola-error-selector');
64
+ if (error_selector) {
65
+ $(error_selector).text(message);
66
+ $(error_selector).show();
67
+ } else {
68
+ form.find('.payola-payment-error').text(message);
69
+ form.find('.payola-payment-error').show();
70
+ }
71
+ }
72
+ };
73
+
74
+ $(function() { PayolaSubscriptionForm.initialize() } );
@@ -1,6 +1,5 @@
1
1
  module Payola
2
- class ApplicationController < ActionController::Base
3
- helper ApplicationHelper
2
+ class ApplicationController < ::ApplicationController
4
3
  helper PriceHelper
5
4
  end
6
5
  end
@@ -0,0 +1,94 @@
1
+ module Payola
2
+ class SubscriptionsController < ApplicationController
3
+ before_filter :find_plan_and_coupon_and_affiliate, only: [:create, :change_plan]
4
+ before_filter :check_modify_permissions, only: [:destroy, :change_plan, :update_card]
5
+
6
+ def show
7
+ subscription = Subscription.find_by!(guid: params[:guid])
8
+ plan = subscription.plan
9
+
10
+ new_path = plan.respond_to?(:redirect_path) ? plan.redirect_path(subscription) : '/'
11
+ redirect_to new_path
12
+ end
13
+
14
+ def status
15
+ @subscription = Subscription.where(guid: params[:guid]).first
16
+ render nothing: true, status: 404 and return unless @subscription
17
+ render json: {guid: @subscription.guid, status: @subscription.state, error: @subscription.error}
18
+ end
19
+
20
+ def create
21
+ create_params = params.permit!.merge(
22
+ plan: @plan,
23
+ coupon: @coupon,
24
+ affiliate: @affiliate
25
+ )
26
+
27
+ @subscription = CreateSubscription.call(create_params)
28
+
29
+ if @subscription.save
30
+ Payola.queue!(Payola::ProcessSubscription, @subscription.guid)
31
+ render json: { guid: @subscription.guid }
32
+ else
33
+ render json: { error: @subscription.errors.full_messages.join(". ") }, status: 400
34
+ end
35
+ end
36
+
37
+ def destroy
38
+ subscription = Subscription.find_by!(guid: params[:guid])
39
+ Payola::CancelSubscription.call(subscription)
40
+ redirect_to confirm_subscription_path(subscription)
41
+ end
42
+
43
+ def change_plan
44
+ subscription = Subscription.find_by!(guid: params[:guid])
45
+ Payola::ChangeSubscriptionPlan.call(subscription, @plan)
46
+
47
+ if subscription.valid?
48
+ redirect_to confirm_subscription_path(subscription), notice: "Subscription plan updated"
49
+ else
50
+ redirect_to confirm_subscription_path(subscription), alert: subscription.errors.full_messages.to_sentence
51
+ end
52
+ end
53
+
54
+ def update_card
55
+ subscription = Subscription.find_by!(guid: params[:guid])
56
+ Payola::UpdateCard.call(subscription, params[:stripeToken])
57
+
58
+ if subscription.valid?
59
+ redirect_to confirm_subscription_path(subscription), notice: "Card updated"
60
+ else
61
+ redirect_to confirm_subscription_path(subscription), alert: subscription.errors.full_messages.to_sentence
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def find_plan_and_coupon_and_affiliate
68
+ @plan_class = Payola.subscribables[params[:plan_class]]
69
+
70
+ raise ActionController::RoutingError.new('Not Found') unless @plan_class && @plan_class.subscribable?
71
+
72
+ @plan = @plan_class.find_by!(id: params[:plan_id])
73
+
74
+ @coupon = cookies[:cc] || params[:cc] || params[:coupon_code] || params[:coupon]
75
+
76
+ affiliate_code = cookies[:aff] || params[:aff]
77
+ @affiliate = Affiliate.where('lower(code) = lower(?)', affiliate_code).first
78
+ if @affiliate
79
+ cookies[:aff] = affiliate_code
80
+ end
81
+
82
+ end
83
+
84
+ def check_modify_permissions
85
+ subscription = Subscription.find_by!(guid: params[:guid])
86
+ if self.respond_to?(:payola_can_modify_subscription?)
87
+ redirect_to(
88
+ confirm_subscription_path(subscription),
89
+ alert: "You cannot modify this subscription."
90
+ ) and return unless self.payola_can_modify_subscription?(subscription)
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,50 @@
1
+ require 'active_support/concern'
2
+
3
+ module Payola
4
+ module Plan
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ validates_presence_of :amount
9
+ validates_presence_of :interval
10
+ validates_presence_of :interval_count
11
+ validates_presence_of :stripe_id
12
+ validates_presence_of :name
13
+
14
+ validates_uniqueness_of :stripe_id
15
+
16
+ before_save :create_stripe_plan, on: :create
17
+
18
+ has_many :subscriptions, :class_name => "Payola::Subscription"
19
+
20
+ Payola.register_subscribable(self)
21
+ end
22
+
23
+ def create_stripe_plan
24
+ Payola::CreatePlan.call(self)
25
+ end
26
+
27
+ def plan_class
28
+ self.class.plan_class
29
+ end
30
+
31
+ def product_class
32
+ plan_class
33
+ end
34
+
35
+ def price
36
+ amount
37
+ end
38
+
39
+ module ClassMethods
40
+ def subscribable?
41
+ true
42
+ end
43
+
44
+ def plan_class
45
+ self.to_s.underscore
46
+ end
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,161 @@
1
+ require 'aasm'
2
+
3
+ module Payola
4
+ class Subscription < ActiveRecord::Base
5
+
6
+ validates_presence_of :email
7
+ validates_presence_of :plan_id
8
+ validates_presence_of :plan_type
9
+ validates_presence_of :stripe_token
10
+ validates_presence_of :currency
11
+
12
+ validates_uniqueness_of :guid
13
+
14
+ before_save :populate_guid
15
+
16
+ belongs_to :plan, polymorphic: true
17
+ belongs_to :owner, polymorphic: true
18
+ belongs_to :affiliate
19
+
20
+ has_many :sales, class_name: 'Payola::Sale'
21
+
22
+ include AASM
23
+
24
+ attr_accessor :old_plan
25
+
26
+ aasm column: 'state', skip_validation_on_save: true do
27
+ state :pending, initial: true
28
+ state :processing
29
+ state :active
30
+ state :canceled
31
+ state :errored
32
+
33
+ event :process, after: :start_subscription do
34
+ transitions from: :pending, to: :processing
35
+ end
36
+
37
+ event :activate, after: :instrument_activate do
38
+ transitions from: :processing, to: :active
39
+ end
40
+
41
+ event :cancel, after: :instrument_canceled do
42
+ transitions from: :active, to: :canceled
43
+ end
44
+
45
+ event :fail, after: :instrument_fail do
46
+ transitions from: [:pending, :processing], to: :errored
47
+ end
48
+
49
+ event :refund, after: :instrument_refund do
50
+ transitions from: :finished, to: :refunded
51
+ end
52
+ end
53
+
54
+ def name
55
+ self.plan.name
56
+ end
57
+
58
+ def price
59
+ self.plan.amount
60
+ end
61
+
62
+ def redirect_path(sale)
63
+ self.plan.redirect_path(self)
64
+ end
65
+
66
+ def verifier
67
+ @verifier ||= ActiveSupport::MessageVerifier.new(Payola.secret_key_for_sale(self), digest: 'SHA256')
68
+ end
69
+
70
+ def verify_charge
71
+ begin
72
+ self.verify_charge!
73
+ rescue RuntimeError => e
74
+ self.error = e.message
75
+ self.fail!
76
+ end
77
+ end
78
+
79
+ def verify_charge!
80
+ if Payola.charge_verifier.arity > 1
81
+ Payola.charge_verifier.call(self, custom_fields)
82
+ else
83
+ Payola.charge_verifier.call(self)
84
+ end
85
+ end
86
+
87
+ def custom_fields
88
+ if self.signed_custom_fields.present?
89
+ verifier.verify(self.signed_custom_fields)
90
+ else
91
+ nil
92
+ end
93
+ end
94
+
95
+ def sync_with!(stripe_sub)
96
+ self.current_period_start = Time.at(stripe_sub.current_period_start)
97
+ self.current_period_end = Time.at(stripe_sub.current_period_end)
98
+ self.ended_at = Time.at(stripe_sub.ended_at) if stripe_sub.ended_at
99
+ self.trial_start = Time.at(stripe_sub.trial_start) if stripe_sub.trial_start
100
+ self.trial_end = Time.at(stripe_sub.trial_end) if stripe_sub.trial_end
101
+ self.quantity = stripe_sub.quantity
102
+ self.stripe_status = stripe_sub.status
103
+
104
+ self.save!
105
+ self
106
+ end
107
+
108
+ def to_param
109
+ guid
110
+ end
111
+
112
+ def instrument_plan_changed(old_plan)
113
+ self.old_plan = old_plan
114
+ Payola.instrument(instrument_key('plan_changed'), self)
115
+ Payola.instrument(instrument_key('plan_changed', false), self)
116
+ end
117
+
118
+ private
119
+
120
+ def start_subscription
121
+ Payola::StartSubscription.call(self)
122
+ end
123
+
124
+ def instrument_activate
125
+ Payola.instrument(instrument_key('active'), self)
126
+ Payola.instrument(instrument_key('active', false), self)
127
+ end
128
+
129
+ def instrument_canceled
130
+ Payola.instrument(instrument_key('canceled'), self)
131
+ Payola.instrument(instrument_key('canceled', false), self)
132
+ end
133
+
134
+ def instrument_fail
135
+ Payola.instrument(instrument_key('failed'), self)
136
+ Payola.instrument(instrument_key('failed', false), self)
137
+ end
138
+
139
+ def instrument_refund
140
+ Payola.instrument(instrument_key('refunded'), self)
141
+ Payola.instrument(instrument_key('refunded', false), self)
142
+ end
143
+
144
+ def instrument_key(instrument_type, include_class=true)
145
+ if include_class
146
+ "payola.#{plan_type}.subscription.#{instrument_type}"
147
+ else
148
+ "payola.subscription.#{instrument_type}"
149
+ end
150
+ end
151
+
152
+ def populate_guid
153
+ if new_record?
154
+ while !valid? || self.guid.nil?
155
+ self.guid = SecureRandom.random_number(1_000_000_000).to_s(32)
156
+ end
157
+ end
158
+ end
159
+
160
+ end
161
+ end
@@ -0,0 +1,12 @@
1
+ module Payola
2
+ class CancelSubscription
3
+ def self.call(subscription)
4
+ secret_key = Payola.secret_key_for_sale(subscription)
5
+ customer = Stripe::Customer.retrieve(subscription.stripe_customer_id, secret_key)
6
+ customer.subscriptions.retrieve(subscription.stripe_id,secret_key).delete
7
+ subscription.cancel!
8
+ end
9
+
10
+ end
11
+ end
12
+
@@ -0,0 +1,29 @@
1
+ module Payola
2
+ class ChangeSubscriptionPlan
3
+ def self.call(subscription, plan)
4
+ secret_key = Payola.secret_key_for_sale(subscription)
5
+ old_plan = subscription.plan
6
+
7
+ begin
8
+ customer = Stripe::Customer.retrieve(subscription.stripe_customer_id, secret_key)
9
+ sub = customer.subscriptions.retrieve(subscription.stripe_id)
10
+
11
+ prorate = plan.respond_to?(:should_prorate?) ? plan.should_prorate?(subscription) : true
12
+
13
+ sub.plan = plan.stripe_id
14
+ sub.prorate = prorate
15
+ sub.save
16
+
17
+ subscription.plan = plan
18
+ subscription.save!
19
+
20
+ subscription.instrument_plan_changed(old_plan)
21
+
22
+ rescue RuntimeError, Stripe::StripeError => e
23
+ subscription.errors[:base] << e.message
24
+ end
25
+
26
+ subscription
27
+ end
28
+ end
29
+ end