payola-payments 1.1.4 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/app/assets/javascripts/payola/form.js +1 -0
- data/app/assets/javascripts/payola/subscription_form.js +74 -0
- data/app/controllers/payola/application_controller.rb +1 -2
- data/app/controllers/payola/subscriptions_controller.rb +94 -0
- data/app/models/concerns/payola/plan.rb +50 -0
- data/app/models/payola/subscription.rb +161 -0
- data/app/services/payola/cancel_subscription.rb +12 -0
- data/app/services/payola/change_subscription_plan.rb +29 -0
- data/app/services/payola/create_plan.rb +17 -0
- data/app/services/payola/create_subscription.rb +20 -0
- data/app/services/payola/invoice_failed.rb +42 -0
- data/app/services/payola/invoice_paid.rb +43 -0
- data/app/services/payola/process_subscription.rb +7 -0
- data/app/services/payola/start_subscription.rb +41 -0
- data/app/services/payola/sync_subscription.rb +11 -0
- data/app/services/payola/update_card.rb +27 -0
- data/app/services/payola/update_subscription.rb +11 -0
- data/app/views/payola/subscriptions/_cancel.html.erb +10 -0
- data/app/views/payola/subscriptions/_change_plan.html.erb +10 -0
- data/config/routes.rb +7 -0
- data/db/migrate/20141105043439_create_payola_subscriptions.rb +31 -0
- data/db/migrate/20141106034610_add_currency_to_payola_subscriptions.rb +6 -0
- data/db/migrate/20141107025420_add_guid_to_payola_subscriptions.rb +6 -0
- data/db/migrate/20141109203101_add_stripe_status_to_payola_subscription.rb +5 -0
- data/db/migrate/20141112024805_add_affiliate_id_to_payola_subscriptions.rb +5 -0
- data/db/migrate/20141114032013_add_coupon_code_to_payola_subscriptions.rb +5 -0
- data/db/migrate/20141114154223_add_signed_custom_fields_to_payola_subscription.rb +5 -0
- data/db/migrate/20141114163841_add_addresses_to_payola_subscription.rb +6 -0
- data/lib/payola.rb +11 -0
- data/lib/payola/engine.rb +10 -2
- data/lib/payola/version.rb +1 -1
- data/spec/concerns/plan_spec.rb +43 -0
- data/spec/controllers/payola/subscriptions_controller_spec.rb +148 -0
- data/spec/dummy/app/controllers/application_controller.rb +4 -0
- data/spec/dummy/app/controllers/home_controller.rb +6 -0
- data/spec/dummy/app/controllers/subscribe_controller.rb +11 -0
- data/spec/dummy/app/models/subscription_plan.rb +3 -0
- data/spec/dummy/app/views/home/index.html.erb +7 -0
- data/spec/dummy/app/views/subscribe/index.html.erb +21 -0
- data/spec/dummy/app/views/subscribe/show.html.erb +4 -0
- data/spec/dummy/config/initializers/payola.rb +4 -0
- data/spec/dummy/config/routes.rb +4 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20141105010234_create_subscription_plans.rb +14 -0
- data/spec/dummy/db/schema.rb +51 -1
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +275 -0
- data/spec/dummy/log/test.log +109420 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/0e585aeb88c1555ae8f5292f7c7a9068 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/1b54952339cbc96811097cee5f8923b4 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/5afa4bc970e0bc781b3500c696ba25ff +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/664efeb51d1b486dcfc30d142db56f3c +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/68a3bbdc64a0729b5896fa1e33fce4b1 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/96b1ccdc193a02b2bbea195310262fa5 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/aea159adbc0944b120d91d5b578612ba +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/b20dab973e971c61bc7e334a38453ae0 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/b83da90cb58011313e13a97a4a41cb6e +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/c0b954b40f2881a6d286061bc069643e +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/c5b3877d49b39932a7557611cefca9a6 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/ca491e325ff6948be9292f701aa4ce64 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d3f4b67cc032016dfeebc71f2148b9dc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d9856638a3c7b9cfd9d1b884e3b33982 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/ddba6e841f28d3005e828fbfffd18b74 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/df935e399019055729aa968bd7062096 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/ef82c31948061751bb026663b54484e1 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/f0b5d4bea0c6cdd1904e1e9a0a45532f +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/spec/factories/payola_subscriptions.rb +25 -0
- data/spec/factories/subscription_plan.rb +9 -0
- data/spec/models/payola/subscription_spec.rb +50 -0
- data/spec/services/payola/cancel_subscription_spec.rb +29 -0
- data/spec/services/payola/change_subscription_plan_spec.rb +31 -0
- data/spec/services/payola/charge_card_spec.rb +9 -5
- data/spec/services/payola/create_plan_spec.rb +24 -0
- data/spec/services/payola/create_subscription_spec.rb +65 -0
- data/spec/services/payola/invoice_failed_spec.rb +29 -0
- data/spec/services/payola/invoice_paid_spec.rb +49 -0
- data/spec/services/payola/start_subscription_spec.rb +36 -0
- data/spec/services/payola/sync_subscription_spec.rb +14 -0
- data/spec/services/payola/update_card_spec.rb +26 -0
- metadata +95 -30
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
N2Q0MmIyZmJiOGJhYThjOWUyYTlhM2Q4NTc4Mjg3MWQxMGFiMDQ2Mw==
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d521cb632f52fe32dbb63dd06926fd551fb0f44b
|
4
|
+
data.tar.gz: 6c9f77ab172e04d2b89cb4fe8c257ec2ae4efcb7
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
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() } );
|
@@ -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
|