payola-payments 1.2.2 → 1.2.3
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/app/assets/javascripts/payola/checkout_button.js +8 -1
- data/app/assets/javascripts/payola/subscription_form_onestep.js +13 -2
- data/app/assets/javascripts/payola/subscription_form_twostep.js +10 -1
- data/app/controllers/concerns/payola/async_behavior.rb +14 -5
- data/app/controllers/payola/subscriptions_controller.rb +16 -3
- data/app/controllers/payola/transactions_controller.rb +1 -1
- data/app/models/concerns/payola/plan.rb +1 -1
- data/app/models/payola/subscription.rb +7 -1
- data/app/services/payola/change_subscription_quantity.rb +25 -0
- data/app/services/payola/charge_card.rb +8 -4
- data/app/services/payola/create_subscription.rb +1 -0
- data/app/services/payola/start_subscription.rb +48 -12
- data/app/views/payola/subscriptions/_change_plan.html.erb +1 -1
- data/config/database.yml.ci +6 -0
- data/config/routes.rb +1 -0
- data/db/migrate/20141213205847_add_active_to_payola_coupon.rb +5 -0
- data/lib/payola.rb +4 -4
- data/lib/payola/engine.rb +1 -1
- data/lib/payola/version.rb +1 -1
- data/lib/payola/worker.rb +1 -1
- data/spec/concerns/plan_spec.rb +9 -0
- data/spec/concerns/sellable_spec.rb +1 -1
- data/spec/controllers/payola/subscriptions_controller_spec.rb +41 -13
- data/spec/controllers/payola/transactions_controller_spec.rb +7 -5
- data/spec/dummy/app/models/user.rb +2 -0
- data/spec/dummy/app/views/subscribe/index.html.erb +2 -0
- data/spec/dummy/config/environments/test.rb +5 -1
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20141204170622_create_users.rb +8 -0
- data/spec/dummy/db/schema.rb +16 -10
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +131 -0
- data/spec/dummy/log/test.log +75119 -0
- data/spec/factories/subscription_plan.rb +2 -2
- data/spec/mailers/payola/receipt_mailer_spec.rb +6 -1
- data/spec/models/payola/coupon_spec.rb +27 -0
- data/spec/payola_spec.rb +2 -2
- data/spec/services/payola/change_subscription_plan_spec.rb +0 -1
- data/spec/services/payola/change_subscription_quantity_spec.rb +29 -0
- data/spec/services/payola/charge_card_spec.rb +9 -0
- data/spec/services/payola/start_subscription_spec.rb +37 -0
- data/spec/worker_spec.rb +1 -1
- metadata +15 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 247fe7a59796463c2fe0ed750a5ca659eb76423b
|
4
|
+
data.tar.gz: 5c42d5a2a48234a91d33e0f6bf9c250fc1cc412b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 68fbf08cf3771d397a60ce0918cd7ab0b2db98ac981b32fa3d80140651ec558c65eb40f42cff32a37ff88f444d10acad8b207c1179e06624cb06a9bcae40d5d6
|
7
|
+
data.tar.gz: 5343de6dd80fb7357678a70fcac9f30bc0806903e1a83f08a6ec7dc66aa48463356f87c964caa19b11e0da1c1f9e5ec14cbd482fc764ae8ba79d5b76efd41b20
|
@@ -65,7 +65,7 @@ var PayolaCheckout = {
|
|
65
65
|
return;
|
66
66
|
}
|
67
67
|
|
68
|
-
|
68
|
+
var handler = function(data) {
|
69
69
|
if (data.status === "finished") {
|
70
70
|
window.location = options.base_path + "/confirm/" + guid;
|
71
71
|
} else if (data.status === "errored") {
|
@@ -73,6 +73,13 @@ var PayolaCheckout = {
|
|
73
73
|
} else {
|
74
74
|
setTimeout(function() { PayolaCheckout.poll(guid, num_retries_left - 1, options); }, 500);
|
75
75
|
}
|
76
|
+
};
|
77
|
+
|
78
|
+
$.ajax({
|
79
|
+
type: "GET",
|
80
|
+
url: options.base_path + "/status/" + guid,
|
81
|
+
success: handler,
|
82
|
+
error: handler
|
76
83
|
});
|
77
84
|
}
|
78
85
|
};
|
@@ -20,20 +20,24 @@ var PayolaOnestepSubscriptionForm = {
|
|
20
20
|
} else {
|
21
21
|
var email = form.find("[data-payola='email']").val();
|
22
22
|
var coupon = form.find("[data-payola='coupon']").val();
|
23
|
+
var quantity = form.find("[data-payola='quantity']").val();
|
23
24
|
|
24
25
|
var base_path = form.data('payola-base-path');
|
25
26
|
var plan_type = form.data('payola-plan-type');
|
26
27
|
var plan_id = form.data('payola-plan-id');
|
27
28
|
|
29
|
+
var action = $(form).attr('action');
|
30
|
+
|
28
31
|
form.append($('<input type="hidden" name="plan_type">').val(plan_type));
|
29
32
|
form.append($('<input type="hidden" name="plan_id">').val(plan_id));
|
30
33
|
form.append($('<input type="hidden" name="stripeToken">').val(response.id));
|
31
34
|
form.append($('<input type="hidden" name="stripeEmail">').val(email));
|
32
35
|
form.append($('<input type="hidden" name="coupon">').val(coupon));
|
36
|
+
form.append($('<input type="hidden" name="quantity">').val(quantity));
|
33
37
|
form.append(PayolaOnestepSubscriptionForm.authenticityTokenInput());
|
34
38
|
$.ajax({
|
35
39
|
type: "POST",
|
36
|
-
url:
|
40
|
+
url: action,
|
37
41
|
data: form.serialize(),
|
38
42
|
success: function(data) { PayolaOnestepSubscriptionForm.poll(form, 60, data.guid, base_path); },
|
39
43
|
error: function(data) { PayolaOnestepSubscriptionForm.showError(form, data.responseJSON.error); }
|
@@ -45,7 +49,7 @@ var PayolaOnestepSubscriptionForm = {
|
|
45
49
|
if (num_retries_left === 0) {
|
46
50
|
PayolaOnestepSubscriptionForm.showError(form, "This seems to be taking too long. Please contact support and give them transaction ID: " + guid);
|
47
51
|
}
|
48
|
-
|
52
|
+
var handler = function(data) {
|
49
53
|
if (data.status === "active") {
|
50
54
|
window.location = base_path + '/confirm_subscription/' + guid;
|
51
55
|
} else if (data.status === "errored") {
|
@@ -53,6 +57,13 @@ var PayolaOnestepSubscriptionForm = {
|
|
53
57
|
} else {
|
54
58
|
setTimeout(function() { PayolaOnestepSubscriptionForm.poll(form, num_retries_left - 1, guid, base_path); }, 500);
|
55
59
|
}
|
60
|
+
};
|
61
|
+
|
62
|
+
$.ajax({
|
63
|
+
type: 'GET',
|
64
|
+
url: base_path + '/subscription_status/' + guid,
|
65
|
+
success: handler,
|
66
|
+
error: handler
|
56
67
|
});
|
57
68
|
},
|
58
69
|
|
@@ -20,6 +20,7 @@ var PayolaSubscriptionForm = {
|
|
20
20
|
} else {
|
21
21
|
var email = form.find("[data-payola='email']").val();
|
22
22
|
var coupon = form.find("[data-payola='coupon']").val();
|
23
|
+
var quantity = form.find("[data-payola='quantity']").val();
|
23
24
|
|
24
25
|
var base_path = form.data('payola-base-path');
|
25
26
|
var plan_type = form.data('payola-plan-type');
|
@@ -29,6 +30,7 @@ var PayolaSubscriptionForm = {
|
|
29
30
|
data_form.append($('<input type="hidden" name="stripeToken">').val(response.id));
|
30
31
|
data_form.append($('<input type="hidden" name="stripeEmail">').val(email));
|
31
32
|
data_form.append($('<input type="hidden" name="coupon">').val(coupon));
|
33
|
+
data_form.append($('<input type="hidden" name="quantity">').val(quantity));
|
32
34
|
data_form.append(PayolaSubscriptionForm.authenticityTokenInput());
|
33
35
|
$.ajax({
|
34
36
|
type: "POST",
|
@@ -44,7 +46,7 @@ var PayolaSubscriptionForm = {
|
|
44
46
|
if (num_retries_left === 0) {
|
45
47
|
PayolaSubscriptionForm.showError(form, "This seems to be taking too long. Please contact support and give them transaction ID: " + guid);
|
46
48
|
}
|
47
|
-
|
49
|
+
var handler = function(data) {
|
48
50
|
if (data.status === "active") {
|
49
51
|
form.append($('<input type="hidden" name="payola_subscription_guid"></input>').val(guid));
|
50
52
|
form.append(PayolaSubscriptionForm.authenticityTokenInput());
|
@@ -54,6 +56,13 @@ var PayolaSubscriptionForm = {
|
|
54
56
|
} else {
|
55
57
|
setTimeout(function() { PayolaSubscriptionForm.poll(form, num_retries_left - 1, guid, base_path); }, 500);
|
56
58
|
}
|
59
|
+
};
|
60
|
+
|
61
|
+
$.ajax({
|
62
|
+
type: 'GET',
|
63
|
+
url: base_path + '/subscription_status/' + guid,
|
64
|
+
success: handler,
|
65
|
+
error: handler
|
57
66
|
});
|
58
67
|
},
|
59
68
|
|
@@ -16,11 +16,20 @@ module Payola
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def create_object(object_class, object_creator_class, object_processor_class, product_key, product)
|
19
|
-
create_params =
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
create_params = if object_class == Subscription
|
20
|
+
params.permit!.merge(
|
21
|
+
product_key => product,
|
22
|
+
coupon: @coupon,
|
23
|
+
quantity: @quantity,
|
24
|
+
affiliate: @affiliate
|
25
|
+
)
|
26
|
+
else
|
27
|
+
params.permit!.merge(
|
28
|
+
product_key => product,
|
29
|
+
coupon: @coupon,
|
30
|
+
affiliate: @affiliate
|
31
|
+
)
|
32
|
+
end
|
24
33
|
|
25
34
|
object = object_creator_class.call(create_params)
|
26
35
|
|
@@ -4,8 +4,8 @@ module Payola
|
|
4
4
|
include Payola::StatusBehavior
|
5
5
|
include Payola::AsyncBehavior
|
6
6
|
|
7
|
-
before_filter :
|
8
|
-
before_filter :check_modify_permissions, only: [:destroy, :change_plan, :update_card]
|
7
|
+
before_filter :find_plan_coupon_and_quantity, only: [:create, :change_plan]
|
8
|
+
before_filter :check_modify_permissions, only: [:destroy, :change_plan, :change_quantity, :update_card]
|
9
9
|
|
10
10
|
def show
|
11
11
|
show_object(Subscription)
|
@@ -32,6 +32,14 @@ module Payola
|
|
32
32
|
confirm_with_message("Subscription plan updated")
|
33
33
|
end
|
34
34
|
|
35
|
+
def change_quantity
|
36
|
+
find_quantity
|
37
|
+
@subscription = Subscription.find_by!(guid: params[:guid])
|
38
|
+
Payola::ChangeSubscriptionQuantity.call(@subscription, @quantity)
|
39
|
+
|
40
|
+
confirm_with_message("Subscription quantity updated")
|
41
|
+
end
|
42
|
+
|
35
43
|
def update_card
|
36
44
|
@subscription = Subscription.find_by!(guid: params[:guid])
|
37
45
|
Payola::UpdateCard.call(@subscription, params[:stripeToken])
|
@@ -41,9 +49,10 @@ module Payola
|
|
41
49
|
|
42
50
|
private
|
43
51
|
|
44
|
-
def
|
52
|
+
def find_plan_coupon_and_quantity
|
45
53
|
find_plan
|
46
54
|
find_coupon
|
55
|
+
find_quantity
|
47
56
|
end
|
48
57
|
|
49
58
|
def find_plan
|
@@ -58,6 +67,10 @@ module Payola
|
|
58
67
|
@coupon = cookies[:cc] || params[:cc] || params[:coupon_code] || params[:coupon]
|
59
68
|
end
|
60
69
|
|
70
|
+
def find_quantity
|
71
|
+
@quantity = params[:quantity].blank? ? 1 : params[:quantity].to_i
|
72
|
+
end
|
73
|
+
|
61
74
|
def check_modify_permissions
|
62
75
|
subscription = Subscription.find_by!(guid: params[:guid])
|
63
76
|
if self.respond_to?(:payola_can_modify_subscription?)
|
@@ -35,7 +35,7 @@ module Payola
|
|
35
35
|
def find_coupon
|
36
36
|
coupon_code = cookies[:cc] || params[:cc] || params[:coupon_code]
|
37
37
|
@coupon = Coupon.where('lower(code) = lower(?)', coupon_code).first
|
38
|
-
if @coupon
|
38
|
+
if @coupon && @coupon.active?
|
39
39
|
cookies[:cc] = coupon_code
|
40
40
|
@price = @product.price * (1 - @coupon.percent_off / 100.0)
|
41
41
|
else
|
@@ -20,7 +20,7 @@ module Payola
|
|
20
20
|
|
21
21
|
include AASM
|
22
22
|
|
23
|
-
attr_accessor :old_plan
|
23
|
+
attr_accessor :old_plan, :old_quantity
|
24
24
|
|
25
25
|
aasm column: 'state', skip_validation_on_save: true do
|
26
26
|
state :pending, initial: true
|
@@ -116,6 +116,12 @@ module Payola
|
|
116
116
|
Payola.instrument(instrument_key('plan_changed', false), self)
|
117
117
|
end
|
118
118
|
|
119
|
+
def instrument_quantity_changed(old_quantity)
|
120
|
+
self.old_quantity = old_quantity
|
121
|
+
Payola.instrument(instrument_key('quantity_changed'), self)
|
122
|
+
Payola.instrument(instrument_key('quantity_changed', false), self)
|
123
|
+
end
|
124
|
+
|
119
125
|
def redirector
|
120
126
|
plan
|
121
127
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Payola
|
2
|
+
class ChangeSubscriptionQuantity
|
3
|
+
def self.call(subscription, quantity)
|
4
|
+
secret_key = Payola.secret_key_for_sale(subscription)
|
5
|
+
old_quantity = subscription.quantity
|
6
|
+
|
7
|
+
begin
|
8
|
+
customer = Stripe::Customer.retrieve(subscription.stripe_customer_id, secret_key)
|
9
|
+
sub = customer.subscriptions.retrieve(subscription.stripe_id)
|
10
|
+
sub.quantity = quantity
|
11
|
+
sub.save
|
12
|
+
|
13
|
+
subscription.quantity = quantity
|
14
|
+
subscription.save!
|
15
|
+
|
16
|
+
subscription.instrument_plan_changed(old_quantity)
|
17
|
+
|
18
|
+
rescue RuntimeError, Stripe::StripeError => e
|
19
|
+
subscription.errors[:base] << e.message
|
20
|
+
end
|
21
|
+
|
22
|
+
subscription
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -22,10 +22,14 @@ module Payola
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def self.create_customer(sale, secret_key)
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
if sale.stripe_customer_id.present?
|
26
|
+
Stripe::Customer.retrieve(sale.stripe_customer_id, secret_key)
|
27
|
+
else
|
28
|
+
Stripe::Customer.create({
|
29
|
+
card: sale.stripe_token,
|
30
|
+
email: sale.email
|
31
|
+
}, secret_key)
|
32
|
+
end
|
29
33
|
end
|
30
34
|
|
31
35
|
def self.create_charge(sale, customer, secret_key)
|
@@ -1,35 +1,42 @@
|
|
1
1
|
module Payola
|
2
2
|
class StartSubscription
|
3
|
+
attr_reader :subscription, :secret_key
|
4
|
+
|
3
5
|
def self.call(subscription)
|
4
6
|
subscription.save!
|
5
7
|
secret_key = Payola.secret_key_for_sale(subscription)
|
6
8
|
|
9
|
+
new(subscription, secret_key).run
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(subscription, secret_key)
|
13
|
+
@subscription = subscription
|
14
|
+
@secret_key = secret_key
|
15
|
+
end
|
16
|
+
|
17
|
+
def run
|
7
18
|
begin
|
8
19
|
subscription.verify_charge!
|
9
20
|
|
21
|
+
customer = find_or_create_customer
|
22
|
+
|
10
23
|
create_params = {
|
11
|
-
|
12
|
-
|
13
|
-
plan: subscription.plan.stripe_id,
|
24
|
+
plan: subscription.plan.stripe_id,
|
25
|
+
quantity: subscription.quantity
|
14
26
|
}
|
15
27
|
create_params[:coupon] = subscription.coupon if subscription.coupon.present?
|
16
|
-
|
17
|
-
|
18
|
-
customer = Stripe::Customer.create(create_params, secret_key)
|
28
|
+
stripe_sub = customer.subscriptions.create(create_params)
|
19
29
|
|
20
30
|
card = customer.cards.data.first
|
21
31
|
subscription.update_attributes(
|
22
|
-
stripe_id:
|
32
|
+
stripe_id: stripe_sub.id,
|
23
33
|
stripe_customer_id: customer.id,
|
24
34
|
card_last4: card.last4,
|
25
35
|
card_expiration: Date.new(card.exp_year, card.exp_month, 1),
|
26
36
|
card_type: card.respond_to?(:brand) ? card.brand : card.type
|
27
37
|
)
|
28
38
|
subscription.activate!
|
29
|
-
rescue Stripe::StripeError => e
|
30
|
-
subscription.update_attributes(error: e.message)
|
31
|
-
subscription.fail!
|
32
|
-
rescue RuntimeError => e
|
39
|
+
rescue Stripe::StripeError, RuntimeError => e
|
33
40
|
subscription.update_attributes(error: e.message)
|
34
41
|
subscription.fail!
|
35
42
|
end
|
@@ -37,5 +44,34 @@ module Payola
|
|
37
44
|
subscription
|
38
45
|
end
|
39
46
|
|
47
|
+
def find_or_create_customer
|
48
|
+
subs = Subscription.where(owner: subscription.owner) if subscription.owner
|
49
|
+
if subs && subs.length > 1
|
50
|
+
first_sub = subs.first
|
51
|
+
customer_id = first_sub.stripe_customer_id
|
52
|
+
return Stripe::Customer.retrieve(customer_id, secret_key)
|
53
|
+
else
|
54
|
+
customer_create_params = {
|
55
|
+
card: subscription.stripe_token,
|
56
|
+
email: subscription.email
|
57
|
+
}
|
58
|
+
|
59
|
+
customer = Stripe::Customer.create(customer_create_params, secret_key)
|
60
|
+
end
|
61
|
+
|
62
|
+
if subscription.setup_fee.present?
|
63
|
+
plan = subscription.plan
|
64
|
+
description = plan.try(:setup_fee_description, subscription) || 'Setup Fee'
|
65
|
+
Stripe::InvoiceItem.create(
|
66
|
+
customer: customer.id,
|
67
|
+
amount: subscription.setup_fee,
|
68
|
+
currency: subscription.currency,
|
69
|
+
description: description
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
customer
|
74
|
+
end
|
40
75
|
end
|
41
|
-
|
76
|
+
|
77
|
+
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
button_text = local_assigns.fetch :button_text, "Update Plan"
|
4
4
|
%>
|
5
5
|
|
6
|
-
<%= form_for subscription, method: :post, url:
|
6
|
+
<%= form_for subscription, method: :post, url: payola.change_subscription_plan_path(subscription) do |f| %>
|
7
7
|
<%= hidden_field_tag 'plan_class', new_plan.plan_class %>
|
8
8
|
<%= hidden_field_tag 'plan_id', new_plan.id %>
|
9
9
|
<%= button_tag button_text, class: button_class %>
|
data/config/routes.rb
CHANGED
@@ -8,6 +8,7 @@ Payola::Engine.routes.draw do
|
|
8
8
|
match '/subscription_status/:guid' => 'subscriptions#status', via: :get, as: :subscription_status
|
9
9
|
match '/cancel_subscription/:guid' => 'subscriptions#destroy', via: :delete, as: :cancel_subscription
|
10
10
|
match '/change_plan/:guid' => 'subscriptions#change_plan', via: :post, as: :change_subscription_plan
|
11
|
+
match '/change_quantity/:guid' => 'subscriptions#change_quantity', via: :post, as: :change_subscription_quantity
|
11
12
|
match '/update_card/:guid' => 'subscriptions#update_card', via: :post, as: :update_card
|
12
13
|
|
13
14
|
mount StripeEvent::Engine => '/events'
|
data/lib/payola.rb
CHANGED
@@ -75,10 +75,10 @@ module Payola
|
|
75
75
|
self.background_worker = nil
|
76
76
|
self.event_filter = lambda { |event| event }
|
77
77
|
self.charge_verifier = lambda { |event| true }
|
78
|
-
self.publishable_key = ENV['STRIPE_PUBLISHABLE_KEY']
|
79
|
-
self.secret_key = ENV['STRIPE_SECRET_KEY']
|
80
|
-
self.secret_key_retriever = lambda { |sale| Payola.secret_key }
|
81
|
-
self.publishable_key_retriever = lambda { |sale| Payola.publishable_key }
|
78
|
+
self.publishable_key = lambda { ENV['STRIPE_PUBLISHABLE_KEY'] }
|
79
|
+
self.secret_key = lambda { ENV['STRIPE_SECRET_KEY'] }
|
80
|
+
self.secret_key_retriever = lambda { |sale| Payola.secret_key.respond_to?(:call) ? Payola.secret_key.call() : Payola.secret_key }
|
81
|
+
self.publishable_key_retriever = lambda { |sale| Payola.publishable_key.respond_to?(:call) ? Payola.publishable_key.call() : Payola.publishable_key }
|
82
82
|
self.support_email = 'sales@example.com'
|
83
83
|
self.default_currency = 'usd'
|
84
84
|
self.sellables = {}
|
data/lib/payola/engine.rb
CHANGED
@@ -20,7 +20,7 @@ module Payola
|
|
20
20
|
|
21
21
|
initializer :inject_helpers do |app|
|
22
22
|
ActiveSupport.on_load :action_controller do
|
23
|
-
::
|
23
|
+
::ActionController::Base.send(:helper, Payola::PriceHelper)
|
24
24
|
end
|
25
25
|
|
26
26
|
ActiveSupport.on_load :action_mailer do
|