payola-payments 1.2.2 → 1.2.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|