payola-payments 1.2.0 → 1.2.1

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/payola/checkout_button.js +9 -9
  3. data/app/assets/javascripts/payola/form.js +6 -6
  4. data/app/assets/javascripts/payola/subscription_form.js +6 -6
  5. data/app/controllers/concerns/payola/affiliate_behavior.rb +17 -0
  6. data/app/controllers/concerns/payola/async_behavior.rb +36 -0
  7. data/app/controllers/payola/subscriptions_controller.rb +31 -46
  8. data/app/controllers/payola/transactions_controller.rb +16 -32
  9. data/app/mailers/payola/admin_mailer.rb +7 -15
  10. data/app/mailers/payola/receipt_mailer.rb +2 -0
  11. data/app/models/concerns/payola/guid_behavior.rb +20 -0
  12. data/app/models/concerns/payola/plan.rb +0 -1
  13. data/app/models/payola/sale.rb +6 -12
  14. data/app/models/payola/subscription.rb +9 -12
  15. data/app/services/payola/charge_card.rb +42 -33
  16. data/app/services/payola/create_plan.rb +2 -2
  17. data/app/services/payola/invoice_behavior.rb +55 -0
  18. data/app/services/payola/invoice_failed.rb +4 -29
  19. data/app/services/payola/invoice_paid.rb +4 -31
  20. data/lib/payola.rb +13 -11
  21. data/lib/payola/version.rb +1 -1
  22. data/spec/concerns/plan_spec.rb +0 -5
  23. data/spec/controllers/payola/subscriptions_controller_spec.rb +10 -1
  24. data/spec/controllers/payola/transactions_controller_spec.rb +10 -1
  25. data/spec/dummy/app/models/subscription_plan_without_interval_count.rb +3 -0
  26. data/spec/dummy/config/environments/test.rb +2 -0
  27. data/spec/dummy/db/development.sqlite3 +0 -0
  28. data/spec/dummy/db/migrate/20141120170744_create_subscription_plan_without_interval_counts.rb +12 -0
  29. data/spec/dummy/db/schema.rb +10 -1
  30. data/spec/dummy/db/test.sqlite3 +0 -0
  31. data/spec/dummy/log/development.log +222 -0
  32. data/spec/dummy/log/test.log +15830 -141203
  33. data/spec/factories/subscription_plan.rb +7 -0
  34. data/spec/mailers/payola/admin_mailer_spec.rb +35 -0
  35. data/spec/mailers/payola/receipt_mailer_spec.rb +52 -0
  36. data/spec/models/payola/subscription_spec.rb +21 -0
  37. data/spec/payola_spec.rb +6 -2
  38. data/spec/services/payola/create_plan_spec.rb +9 -0
  39. data/spec/services/payola/process_sale_spec.rb +15 -0
  40. data/spec/services/payola/process_subscription_spec.rb +15 -0
  41. data/spec/services/payola/send_mail_spec.rb +25 -0
  42. data/spec/spec_helper.rb +13 -0
  43. data/spec/worker_spec.rb +55 -0
  44. metadata +40 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d521cb632f52fe32dbb63dd06926fd551fb0f44b
4
- data.tar.gz: 6c9f77ab172e04d2b89cb4fe8c257ec2ae4efcb7
3
+ metadata.gz: 4c4d4f9f5bd8ecf269a31f484690a7fdd910e39d
4
+ data.tar.gz: 4caf45459de1c9d0913cd6c025d86cf96ea805aa
5
5
  SHA512:
6
- metadata.gz: a27328550b968b7824f42f91b8fafe9562cf9fef30bc5b482e752aa83f2822f396664579ed3983ead027b577863a81cc4c464bcc22a6c7e1bb3a51720824c34b
7
- data.tar.gz: 1fe5bcdc56753e6887a1bd70cc8465bd4c1fb807be1c6802b7e6258ee76c4f076f2dab9cb9a53a437459204ffaf45fed790c459bd03f12665779280de08a4340
6
+ metadata.gz: 14920d40f2f7d4ebaeadfc6e34c75dea86a277eecf3d2f0e44f6e85e2106a0ef16e63f90b32fb2adec4099137572950f2a64e2882259e01d3ac0898b49e61118
7
+ data.tar.gz: 598088dfc2c1a12c955e801522ff2ec8c21c04846971df7f399635b5eee9f1e14ac9b32f5d5d570993975269fe8accfe57977555763ea39608aa517d780e6a23
@@ -7,13 +7,13 @@ var PayolaCheckout = {
7
7
  },
8
8
 
9
9
  handleCheckoutButtonClick: function(button) {
10
- form = button.parent('form');
11
- options = form.data();
10
+ var form = button.parent('form');
11
+ var options = form.data();
12
12
 
13
13
  var handler = StripeCheckout.configure({
14
14
  key: options.publishable_key,
15
15
  image: options.product_image_path,
16
- token: function(token) { PayolaCheckout.tokenHandler(token, options) },
16
+ token: function(token) { PayolaCheckout.tokenHandler(token, options); },
17
17
  name: options.name,
18
18
  description: options.description,
19
19
  amount: options.price,
@@ -45,8 +45,8 @@ var PayolaCheckout = {
45
45
  type: "POST",
46
46
  url: options.base_path + "/buy/" + options.product_class + "/" + options.product_permalink,
47
47
  data: form.serialize(),
48
- success: function(data) { PayolaCheckout.poll(data.guid, 60, options) },
49
- error: function(data) { PayolaCheckout.showError(data.responseJSON.error, options) }
48
+ success: function(data) { PayolaCheckout.poll(data.guid, 60, options); },
49
+ error: function(data) { PayolaCheckout.showError(data.responseJSON.error, options); }
50
50
  });
51
51
  },
52
52
 
@@ -60,7 +60,7 @@ var PayolaCheckout = {
60
60
  },
61
61
 
62
62
  poll: function(guid, num_retries_left, options) {
63
- if (num_retries_left == 0) {
63
+ if (num_retries_left === 0) {
64
64
  PayolaCheckout.showError("This seems to be taking too long. Please contact support and give them transaction ID: " + guid, options);
65
65
  return;
66
66
  }
@@ -71,9 +71,9 @@ var PayolaCheckout = {
71
71
  } else if (data.status === "errored") {
72
72
  PayolaCheckout.showError(data.error, options);
73
73
  } else {
74
- setTimeout(function() { PayolaCheckout.poll(guid, num_retries_left - 1, options) }, 500);
74
+ setTimeout(function() { PayolaCheckout.poll(guid, num_retries_left - 1, options); }, 500);
75
75
  }
76
76
  });
77
77
  }
78
- }
79
- PayolaCheckout.initialize();
78
+ };
79
+ $(function() { PayolaCheckout.initialize(); });
@@ -34,15 +34,15 @@ var PayolaPaymentForm = {
34
34
  type: "POST",
35
35
  url: base_path + "/buy/" + product + "/" + permalink,
36
36
  data: data_form.serialize(),
37
- success: function(data) { PayolaPaymentForm.poll(form, 60, data.guid, base_path) },
38
- error: function(data) { PayolaPaymentForm.showError(form, data.responseJSON.error) }
37
+ success: function(data) { PayolaPaymentForm.poll(form, 60, data.guid, base_path); },
38
+ error: function(data) { PayolaPaymentForm.showError(form, data.responseJSON.error); }
39
39
  });
40
40
  }
41
41
  },
42
42
 
43
43
  poll: function(form, num_retries_left, guid, base_path) {
44
- if (num_retries_left == 0) {
45
- PayolaPaymentForm.showError(form, "This seems to be taking too long. Please contact support and give them transaction ID: " + guid)
44
+ if (num_retries_left === 0) {
45
+ PayolaPaymentForm.showError(form, "This seems to be taking too long. Please contact support and give them transaction ID: " + guid);
46
46
  }
47
47
  $.get(base_path + '/status/' + guid, function(data) {
48
48
  if (data.status === "finished") {
@@ -52,7 +52,7 @@ var PayolaPaymentForm = {
52
52
  } else if (data.status === "errored") {
53
53
  PayolaPaymentForm.showError(form, data.error);
54
54
  } else {
55
- setTimeout(function() { PayolaPaymentForm.poll(form, num_retries_left - 1, guid, base_path) }, 500);
55
+ setTimeout(function() { PayolaPaymentForm.poll(form, num_retries_left - 1, guid, base_path); }, 500);
56
56
  }
57
57
  });
58
58
  },
@@ -68,4 +68,4 @@ var PayolaPaymentForm = {
68
68
  }
69
69
  };
70
70
 
71
- $(function() { PayolaPaymentForm.initialize() } );
71
+ $(function() { PayolaPaymentForm.initialize(); } );
@@ -35,15 +35,15 @@ var PayolaSubscriptionForm = {
35
35
  type: "POST",
36
36
  url: base_path + "/subscribe/" + plan_type + "/" + plan_id,
37
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) }
38
+ success: function(data) { PayolaSubscriptionForm.poll(form, 60, data.guid, base_path); },
39
+ error: function(data) { PayolaSubscriptionForm.showError(form, data.responseJSON.error); }
40
40
  });
41
41
  }
42
42
  },
43
43
 
44
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)
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
47
  }
48
48
  $.get(base_path + '/subscription_status/' + guid, function(data) {
49
49
  if (data.status === "active") {
@@ -53,7 +53,7 @@ var PayolaSubscriptionForm = {
53
53
  } else if (data.status === "errored") {
54
54
  PayolaSubscriptionForm.showError(form, data.error);
55
55
  } else {
56
- setTimeout(function() { PayolaSubscriptionForm.poll(form, num_retries_left - 1, guid, base_path) }, 500);
56
+ setTimeout(function() { PayolaSubscriptionForm.poll(form, num_retries_left - 1, guid, base_path); }, 500);
57
57
  }
58
58
  });
59
59
  },
@@ -71,4 +71,4 @@ var PayolaSubscriptionForm = {
71
71
  }
72
72
  };
73
73
 
74
- $(function() { PayolaSubscriptionForm.initialize() } );
74
+ $(function() { PayolaSubscriptionForm.initialize(); } );
@@ -0,0 +1,17 @@
1
+ module Payola
2
+ module AffiliateBehavior
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ before_filter :find_affiliate
7
+ end
8
+
9
+ def find_affiliate
10
+ affiliate_code = cookies[:aff] || params[:aff]
11
+ @affiliate = Affiliate.where('lower(code) = lower(?)', affiliate_code).first
12
+ if @affiliate
13
+ cookies[:aff] = affiliate_code
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,36 @@
1
+ module Payola
2
+ module AsyncBehavior
3
+ extend ActiveSupport::Concern
4
+
5
+ def show_object(object_class)
6
+ object = object_class.find_by!(guid: params[:guid])
7
+ redirector = object.redirector
8
+
9
+ new_path = redirector.respond_to?(:redirect_path) ? redirector.redirect_path(object) : '/'
10
+ redirect_to new_path
11
+ end
12
+
13
+ def object_status(object_class)
14
+ object = object_class.find_by(guid: params[:guid])
15
+ render nothing: true, status: 404 and return unless object
16
+ render json: {guid: object.guid, status: object.state, error: object.error}
17
+ end
18
+
19
+ def create_object(object_class, object_creator_class, object_processor_class, product_key, product)
20
+ create_params = params.permit!.merge(
21
+ product_key => product,
22
+ coupon: @coupon,
23
+ affiliate: @affiliate
24
+ )
25
+
26
+ object = object_creator_class.call(create_params)
27
+
28
+ if object.save
29
+ Payola.queue!(object_processor_class, object.guid)
30
+ render json: { guid: object.guid }
31
+ else
32
+ render json: { error: object.errors.full_messages.join(". ") }, status: 400
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,37 +1,21 @@
1
1
  module Payola
2
2
  class SubscriptionsController < ApplicationController
3
- before_filter :find_plan_and_coupon_and_affiliate, only: [:create, :change_plan]
3
+ include Payola::AffiliateBehavior
4
+ include Payola::AsyncBehavior
5
+
6
+ before_filter :find_plan_and_coupon, only: [:create, :change_plan]
4
7
  before_filter :check_modify_permissions, only: [:destroy, :change_plan, :update_card]
5
8
 
6
9
  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
10
+ show_object(Subscription)
12
11
  end
13
12
 
14
13
  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}
14
+ object_status(Subscription)
18
15
  end
19
16
 
20
17
  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
18
+ create_object(Subscription, CreateSubscription, ProcessSubscription, :plan, @plan)
35
19
  end
36
20
 
37
21
  def destroy
@@ -41,44 +25,36 @@ module Payola
41
25
  end
42
26
 
43
27
  def change_plan
44
- subscription = Subscription.find_by!(guid: params[:guid])
45
- Payola::ChangeSubscriptionPlan.call(subscription, @plan)
28
+ @subscription = Subscription.find_by!(guid: params[:guid])
29
+ Payola::ChangeSubscriptionPlan.call(@subscription, @plan)
46
30
 
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
31
+ confirm_with_message("Subscription plan updated")
52
32
  end
53
33
 
54
34
  def update_card
55
- subscription = Subscription.find_by!(guid: params[:guid])
56
- Payola::UpdateCard.call(subscription, params[:stripeToken])
35
+ @subscription = Subscription.find_by!(guid: params[:guid])
36
+ Payola::UpdateCard.call(@subscription, params[:stripeToken])
57
37
 
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
38
+ confirm_with_message("Card updated")
63
39
  end
64
40
 
65
41
  private
66
42
 
67
- def find_plan_and_coupon_and_affiliate
43
+ def find_plan_and_coupon
44
+ find_plan
45
+ find_coupon
46
+ end
47
+
48
+ def find_plan
68
49
  @plan_class = Payola.subscribables[params[:plan_class]]
69
50
 
70
51
  raise ActionController::RoutingError.new('Not Found') unless @plan_class && @plan_class.subscribable?
71
52
 
72
53
  @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
54
+ end
81
55
 
56
+ def find_coupon
57
+ @coupon = cookies[:cc] || params[:cc] || params[:coupon_code] || params[:coupon]
82
58
  end
83
59
 
84
60
  def check_modify_permissions
@@ -90,5 +66,14 @@ module Payola
90
66
  ) and return unless self.payola_can_modify_subscription?(subscription)
91
67
  end
92
68
  end
69
+
70
+ def confirm_with_message(message)
71
+ if @subscription.valid?
72
+ redirect_to confirm_subscription_path(@subscription), notice: message
73
+ else
74
+ redirect_to confirm_subscription_path(@subscription), alert: @subscription.errors.full_messages.to_sentence
75
+ end
76
+ end
77
+
93
78
  end
94
79
  end
@@ -1,47 +1,38 @@
1
1
  module Payola
2
2
  class TransactionsController < ApplicationController
3
- before_filter :find_product_and_coupon_and_affiliate, only: [:create]
3
+ include Payola::AffiliateBehavior
4
+ include Payola::AsyncBehavior
4
5
 
5
- def show
6
- sale = Sale.find_by!(guid: params[:guid])
7
- product = sale.product
6
+ before_filter :find_product_and_coupon, only: [:create]
8
7
 
9
- new_path = product.respond_to?(:redirect_path) ? product.redirect_path(sale) : '/'
10
- redirect_to new_path
8
+ def show
9
+ show_object(Sale)
11
10
  end
12
11
 
13
12
  def status
14
- @sale = Sale.where(guid: params[:guid]).first
15
- render nothing: true, status: 404 and return unless @sale
16
- render json: {guid: @sale.guid, status: @sale.state, error: @sale.error}
13
+ object_status(Sale)
17
14
  end
18
15
 
19
16
  def create
20
- create_params = params.permit!.merge(
21
- product: @product,
22
- coupon: @coupon,
23
- affiliate: @affiliate
24
- )
25
-
26
- @sale = CreateSale.call(create_params)
27
-
28
- if @sale.save
29
- Payola.queue!(Payola::ProcessSale, @sale.guid)
30
- render json: { guid: @sale.guid }
31
- else
32
- render json: { error: @sale.errors.full_messages.join(". ") }, status: 400
33
- end
17
+ create_object(Sale, CreateSale, ProcessSale, :product, @product)
34
18
  end
35
19
 
36
20
  private
37
- def find_product_and_coupon_and_affiliate
21
+ def find_product_and_coupon
22
+ find_product
23
+ find_coupon
24
+ end
25
+
26
+ def find_product
38
27
  @product_class = Payola.sellables[params[:product_class]]
39
28
 
40
29
  raise ActionController::RoutingError.new('Not Found') unless @product_class && @product_class.sellable?
41
30
 
42
31
  @product = @product_class.find_by!(permalink: params[:permalink])
43
- coupon_code = cookies[:cc] || params[:cc] || params[:coupon_code]
32
+ end
44
33
 
34
+ def find_coupon
35
+ coupon_code = cookies[:cc] || params[:cc] || params[:coupon_code]
45
36
  @coupon = Coupon.where('lower(code) = lower(?)', coupon_code).first
46
37
  if @coupon
47
38
  cookies[:cc] = coupon_code
@@ -49,13 +40,6 @@ module Payola
49
40
  else
50
41
  @price = @product.price
51
42
  end
52
-
53
- affiliate_code = cookies[:aff] || params[:aff]
54
- @affiliate = Affiliate.where('lower(code) = lower(?)', affiliate_code).first
55
- if @affiliate
56
- cookies[:aff] = affiliate_code
57
- end
58
-
59
43
  end
60
44
 
61
45
  end
@@ -3,30 +3,22 @@ module Payola
3
3
  helper Payola::PriceHelper
4
4
 
5
5
  def receipt(sale_guid)
6
- ActiveRecord::Base.connection_pool.with_connection do
7
- @sale = Payola::Sale.find_by(guid: sale_guid)
8
- @product = @sale.product
9
- mail(from: Payola.support_email, to: Payola.support_email)
10
- end
6
+ send_admin_mail(sale_guid)
11
7
  end
12
8
 
13
9
  def refund(sale_guid)
14
- ActiveRecord::Base.connection_pool.with_connection do
15
- @sale = Payola::Sale.find_by(guid: sale_guid)
16
- @product = @sale.product
17
- mail(from: Payola.support_email, to: Payola.support_email)
18
- end
10
+ send_admin_mail(sale_guid)
19
11
  end
20
12
 
21
13
  def dispute(sale_guid)
22
- ActiveRecord::Base.connection_pool.with_connection do
23
- @sale = Payola::Sale.find_by(guid: sale_guid)
24
- @product = @sale.product
25
- mail(from: Payola.support_email, to: Payola.support_email)
26
- end
14
+ send_admin_mail(sale_guid)
27
15
  end
28
16
 
29
17
  def failure(sale_guid)
18
+ send_admin_mail(sale_guid)
19
+ end
20
+
21
+ def send_admin_mail(sale_guid)
30
22
  ActiveRecord::Base.connection_pool.with_connection do
31
23
  @sale = Payola::Sale.find_by(guid: sale_guid)
32
24
  @product = @sale.product
@@ -9,6 +9,8 @@ module Payola
9
9
  @product = @sale.product
10
10
 
11
11
  if Payola.pdf_receipt
12
+ require 'docverter'
13
+
12
14
  pdf = Docverter::Conversion.run do |c|
13
15
  c.from = 'html'
14
16
  c.to = 'pdf'
@@ -0,0 +1,20 @@
1
+ require 'active_support/concern'
2
+
3
+ module Payola
4
+ module GuidBehavior
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ before_save :populate_guid
9
+ validates_uniqueness_of :guid
10
+ end
11
+
12
+ def populate_guid
13
+ if new_record?
14
+ while !valid? || self.guid.nil?
15
+ self.guid = Payola.guid_generator.call
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end