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
@@ -7,7 +7,6 @@ module Payola
7
7
  included do
8
8
  validates_presence_of :amount
9
9
  validates_presence_of :interval
10
- validates_presence_of :interval_count
11
10
  validates_presence_of :stripe_id
12
11
  validates_presence_of :name
13
12
 
@@ -2,6 +2,8 @@ require 'aasm'
2
2
 
3
3
  module Payola
4
4
  class Sale < ActiveRecord::Base
5
+ include Payola::GuidBehavior
6
+
5
7
  has_paper_trail if respond_to? :has_paper_trail
6
8
 
7
9
  validates_presence_of :email
@@ -10,10 +12,6 @@ module Payola
10
12
  validates_presence_of :stripe_token
11
13
  validates_presence_of :currency
12
14
 
13
- validates_uniqueness_of :guid
14
-
15
- before_save :populate_guid
16
-
17
15
  belongs_to :product, polymorphic: true
18
16
  belongs_to :owner, polymorphic: true
19
17
  belongs_to :coupon
@@ -74,6 +72,10 @@ module Payola
74
72
  end
75
73
  end
76
74
 
75
+ def redirector
76
+ product
77
+ end
78
+
77
79
  private
78
80
 
79
81
  def charge_card
@@ -107,13 +109,5 @@ module Payola
107
109
  end
108
110
  end
109
111
 
110
- def populate_guid
111
- if new_record?
112
- while !valid? || self.guid.nil?
113
- self.guid = SecureRandom.random_number(1_000_000_000).to_s(32)
114
- end
115
- end
116
- end
117
-
118
112
  end
119
113
  end
@@ -2,6 +2,9 @@ require 'aasm'
2
2
 
3
3
  module Payola
4
4
  class Subscription < ActiveRecord::Base
5
+ include Payola::GuidBehavior
6
+
7
+ has_paper_trail if respond_to? :has_paper_trail
5
8
 
6
9
  validates_presence_of :email
7
10
  validates_presence_of :plan_id
@@ -9,10 +12,6 @@ module Payola
9
12
  validates_presence_of :stripe_token
10
13
  validates_presence_of :currency
11
14
 
12
- validates_uniqueness_of :guid
13
-
14
- before_save :populate_guid
15
-
16
15
  belongs_to :plan, polymorphic: true
17
16
  belongs_to :owner, polymorphic: true
18
17
  belongs_to :affiliate
@@ -98,8 +97,10 @@ module Payola
98
97
  self.ended_at = Time.at(stripe_sub.ended_at) if stripe_sub.ended_at
99
98
  self.trial_start = Time.at(stripe_sub.trial_start) if stripe_sub.trial_start
100
99
  self.trial_end = Time.at(stripe_sub.trial_end) if stripe_sub.trial_end
100
+ self.canceled_at = Time.at(stripe_sub.canceled_at) if stripe_sub.canceled_at
101
101
  self.quantity = stripe_sub.quantity
102
102
  self.stripe_status = stripe_sub.status
103
+ self.cancel_at_period_end = stripe_sub.cancel_at_period_end
103
104
 
104
105
  self.save!
105
106
  self
@@ -115,6 +116,10 @@ module Payola
115
116
  Payola.instrument(instrument_key('plan_changed', false), self)
116
117
  end
117
118
 
119
+ def redirector
120
+ plan
121
+ end
122
+
118
123
  private
119
124
 
120
125
  def start_subscription
@@ -149,13 +154,5 @@ module Payola
149
154
  end
150
155
  end
151
156
 
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
157
  end
161
158
  end
@@ -7,40 +7,13 @@ module Payola
7
7
  begin
8
8
  sale.verify_charge!
9
9
 
10
- customer = Stripe::Customer.create({
11
- card: sale.stripe_token,
12
- email: sale.email
13
- }, secret_key)
14
-
15
- charge_attributes = {
16
- amount: sale.amount,
17
- currency: sale.currency,
18
- customer: customer.id,
19
- description: sale.guid,
20
- }.merge(Payola.additional_charge_attributes.call(sale, customer))
21
-
22
- charge = Stripe::Charge.create(charge_attributes, secret_key)
23
-
24
- if charge.respond_to?(:fee)
25
- fee = charge.fee
26
- else
27
- balance = Stripe::BalanceTransaction.retrieve(charge.balance_transaction, secret_key)
28
- fee = balance.fee
29
- end
30
-
31
- sale.update_attributes(
32
- stripe_id: charge.id,
33
- stripe_customer_id: customer.id,
34
- card_last4: charge.card.last4,
35
- card_expiration: Date.new(charge.card.exp_year, charge.card.exp_month, 1),
36
- card_type: charge.card.respond_to?(:brand) ? charge.card.brand : charge.card.type,
37
- fee_amount: fee
38
- )
10
+ customer = create_customer(sale, secret_key)
11
+ charge = create_charge(sale, customer, secret_key)
12
+
13
+ update_sale(sale, customer, charge, secret_key)
14
+
39
15
  sale.finish!
40
- rescue Stripe::StripeError => e
41
- sale.update_attributes(error: e.message)
42
- sale.fail!
43
- rescue RuntimeError => e
16
+ rescue Stripe::StripeError, RuntimeError => e
44
17
  sale.update_attributes(error: e.message)
45
18
  sale.fail!
46
19
  end
@@ -48,5 +21,41 @@ module Payola
48
21
  sale
49
22
  end
50
23
 
24
+ def self.create_customer(sale, secret_key)
25
+ Stripe::Customer.create({
26
+ card: sale.stripe_token,
27
+ email: sale.email
28
+ }, secret_key)
29
+ end
30
+
31
+ def self.create_charge(sale, customer, secret_key)
32
+ charge_attributes = {
33
+ amount: sale.amount,
34
+ currency: sale.currency,
35
+ customer: customer.id,
36
+ description: sale.guid,
37
+ }.merge(Payola.additional_charge_attributes.call(sale, customer))
38
+
39
+ Stripe::Charge.create(charge_attributes, secret_key)
40
+ end
41
+
42
+ def self.update_sale(sale, customer, charge, secret_key)
43
+ if charge.respond_to?(:fee)
44
+ fee = charge.fee
45
+ else
46
+ balance = Stripe::BalanceTransaction.retrieve(charge.balance_transaction, secret_key)
47
+ fee = balance.fee
48
+ end
49
+
50
+ sale.update_attributes(
51
+ stripe_id: charge.id,
52
+ stripe_customer_id: customer.id,
53
+ card_last4: charge.card.last4,
54
+ card_expiration: Date.new(charge.card.exp_year, charge.card.exp_month, 1),
55
+ card_type: charge.card.respond_to?(:brand) ? charge.card.brand : charge.card.type,
56
+ fee_amount: fee
57
+ )
58
+ end
59
+
51
60
  end
52
61
  end
@@ -7,9 +7,9 @@ module Payola
7
7
  id: plan.stripe_id,
8
8
  amount: plan.amount,
9
9
  interval: plan.interval,
10
- interval_count: plan.interval_count,
11
- currency: plan.respond_to?(:currency) ? plan.currency : Payola.default_currency,
12
10
  name: plan.name,
11
+ interval_count: plan.respond_to?(:interval_count) ? plan.interval_count : nil,
12
+ currency: plan.respond_to?(:currency) ? plan.currency : Payola.default_currency,
13
13
  trial_period_days: plan.respond_to?(:trial_period_days) ? plan.trial_period_days : nil
14
14
  }, secret_key)
15
15
  end
@@ -0,0 +1,55 @@
1
+ require 'active_support/concern'
2
+
3
+ module Payola
4
+ module InvoiceBehavior
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ def create_sale_from_event(event)
9
+ invoice = event.data.object
10
+
11
+ return unless invoice.charge
12
+
13
+ subscription = Payola::Subscription.find_by!(stripe_id: invoice.subscription)
14
+ secret_key = Payola.secret_key_for_sale(subscription)
15
+
16
+ stripe_sub = Stripe::Customer.retrieve(subscription.stripe_customer_id, secret_key).subscriptions.retrieve(invoice.subscription, secret_key)
17
+ subscription.sync_with!(stripe_sub)
18
+
19
+ sale = create_sale(subscription, invoice)
20
+
21
+ charge = Stripe::Charge.retrieve(invoice.charge, secret_key)
22
+
23
+ update_sale_with_charge(sale, charge)
24
+
25
+ return sale, charge
26
+ end
27
+
28
+ def create_sale(subscription, invoice)
29
+ Payola::Sale.new do |s|
30
+ s.email = subscription.email
31
+ s.state = 'processing'
32
+ s.owner = subscription
33
+ s.product = subscription.plan
34
+ s.stripe_token = 'invoice'
35
+ s.amount = invoice.total
36
+ s.currency = invoice.currency
37
+ end
38
+ end
39
+
40
+ def update_sale_with_charge(sale, charge)
41
+ sale.stripe_id = charge.id
42
+ sale.card_type = charge.card.respond_to?(:brand) ? charge.card.brand : charge.card.type
43
+ sale.card_last4 = charge.card.last4
44
+
45
+ if charge.respond_to?(:fee)
46
+ sale.fee_amount = charge.fee
47
+ else
48
+ balance = Stripe::BalanceTransaction.retrieve(charge.balance_transaction, secret_key)
49
+ sale.fee_amount = balance.fee
50
+ end
51
+ end
52
+ end
53
+
54
+ end
55
+ end
@@ -1,36 +1,11 @@
1
1
  module Payola
2
2
  class InvoiceFailed
3
- def self.call(event)
4
- invoice = event.data.object
5
-
6
- subscription = Payola::Subscription.find_by!(stripe_id: invoice.subscription)
7
- secret_key = Payola.secret_key_for_sale(subscription)
8
-
9
- stripe_sub = Stripe::Customer.retrieve(subscription.stripe_customer_id, secret_key).subscriptions.retrieve(invoice.subscription, secret_key)
10
- subscription.sync_with!(stripe_sub)
3
+ include Payola::InvoiceBehavior
11
4
 
12
- sale = Payola::Sale.new do |s|
13
- s.email = subscription.email
14
- s.state = 'processing'
15
- s.owner = subscription
16
- s.product = subscription.plan
17
- s.stripe_token = 'invoice'
18
- s.amount = invoice.total
19
- s.currency = invoice.currency
20
- end
21
-
22
- charge = Stripe::Charge.retrieve(invoice.charge, secret_key)
23
-
24
- sale.stripe_id = charge.id
25
- sale.card_type = charge.card.respond_to?(:brand) ? charge.card.brand : charge.card.type
26
- sale.card_last4 = charge.card.last4
5
+ def self.call(event)
6
+ sale, charge = create_sale_from_event(event)
27
7
 
28
- if charge.respond_to?(:fee)
29
- sale.fee_amount = charge.fee
30
- else
31
- balance = Stripe::BalanceTransaction.retrieve(charge.balance_transaction, secret_key)
32
- sale.fee_amount = balance.fee
33
- end
8
+ return unless sale
34
9
 
35
10
  sale.error = charge.failure_message
36
11
  sale.save!
@@ -1,38 +1,11 @@
1
1
  module Payola
2
2
  class InvoicePaid
3
- def self.call(event)
4
- invoice = event.data.object
5
-
6
- return unless invoice.charge
7
-
8
- subscription = Payola::Subscription.find_by!(stripe_id: invoice.subscription)
9
-
10
- secret_key = Payola.secret_key_for_sale(subscription)
11
- stripe_sub = Stripe::Customer.retrieve(subscription.stripe_customer_id, secret_key).subscriptions.retrieve(invoice.subscription, secret_key)
12
- subscription.sync_with!(stripe_sub)
3
+ include Payola::InvoiceBehavior
13
4
 
14
- sale = Payola::Sale.new do |s|
15
- s.email = subscription.email
16
- s.state = 'processing'
17
- s.owner = subscription
18
- s.product = subscription.plan
19
- s.stripe_token = 'invoice'
20
- s.amount = invoice.total
21
- s.currency = invoice.currency
22
- end
23
-
24
- charge = Stripe::Charge.retrieve(invoice.charge, secret_key)
25
-
26
- sale.stripe_id = charge.id
27
- sale.card_type = charge.card.respond_to?(:brand) ? charge.card.brand : charge.card.type
28
- sale.card_last4 = charge.card.last4
5
+ def self.call(event)
6
+ sale, charge = create_sale_from_event(event)
29
7
 
30
- if charge.respond_to?(:fee)
31
- sale.fee_amount = charge.fee
32
- else
33
- balance = Stripe::BalanceTransaction.retrieve(charge.balance_transaction, secret_key)
34
- sale.fee_amount = balance.fee
35
- end
8
+ return unless sale
36
9
 
37
10
  sale.save!
38
11
  sale.finish!
data/lib/payola.rb CHANGED
@@ -4,6 +4,16 @@ require 'stripe_event'
4
4
  require 'jquery-rails'
5
5
 
6
6
  module Payola
7
+
8
+ DEFAULT_EMAILS = {
9
+ receipt: [ 'payola.sale.finished', 'Payola::ReceiptMailer', :receipt ],
10
+ refund: [ 'charge.refunded', 'Payola::ReceiptMailer', :refund ],
11
+ admin_receipt: [ 'payola.sale.finished', 'Payola::AdminMailer', :receipt ],
12
+ admin_dispute: [ 'dispute.created', 'Payola::AdminMailer', :dispute ],
13
+ admin_refund: [ 'payola.sale.refunded', 'Payola::AdminMailer', :refund ],
14
+ admin_failure: [ 'payola.sale.failed', 'Payola::AdminMailer', :failure ],
15
+ }
16
+
7
17
  class << self
8
18
  attr_accessor :publishable_key,
9
19
  :publishable_key_retriever,
@@ -17,6 +27,7 @@ module Payola
17
27
  :charge_verifier,
18
28
  :default_currency,
19
29
  :additional_charge_attributes,
30
+ :guid_generator,
20
31
  :pdf_receipt
21
32
 
22
33
  def configure(&block)
@@ -74,6 +85,7 @@ module Payola
74
85
  self.subscribables = {}
75
86
  self.additional_charge_attributes = lambda { |sale, customer| { } }
76
87
  self.pdf_receipt = false
88
+ self.guid_generator = lambda { SecureRandom.random_number(1_000_000_000).to_s(32) }
77
89
  end
78
90
 
79
91
  def register_sellable(klass)
@@ -85,20 +97,10 @@ module Payola
85
97
  end
86
98
 
87
99
  def send_email_for(*emails)
88
- possible_emails = {
89
- receipt: [ 'payola.sale.finished', Payola::ReceiptMailer, :receipt ],
90
- refund: [ 'charge.refunded', Payola::ReceiptMailer, :refund ],
91
- admin_receipt: [ 'payola.sale.finished', Payola::AdminMailer, :receipt ],
92
- admin_dispute: [ 'dispute.created', Payola::AdminMailer, :dispute ],
93
- admin_refund: [ 'payola.sale.refunded', Payola::AdminMailer, :refund ],
94
- admin_failure: [ 'payola.sale.failed', Payola::AdminMailer, :failure ],
95
- }
96
-
97
100
  emails.each do |email|
98
- spec = possible_emails[email].dup
101
+ spec = DEFAULT_EMAILS[email].dup
99
102
  if spec
100
103
  Payola.subscribe(spec.shift) do |sale|
101
-
102
104
  if sale.is_a?(Stripe::Event)
103
105
  sale = Payola::Sale.find_by!(stripe_id: sale.data.object.id)
104
106
  end
@@ -1,3 +1,3 @@
1
1
  module Payola
2
- VERSION = "1.2.0"
2
+ VERSION = "1.2.1"
3
3
  end
@@ -18,11 +18,6 @@ module Payola
18
18
  expect(subscription_plan.valid?).to be false
19
19
  end
20
20
 
21
- it "should validate interval_count" do
22
- subscription_plan = build(:subscription_plan, interval_count: nil)
23
- expect(subscription_plan.valid?).to be false
24
- end
25
-
26
21
  it "should validate stripe_id" do
27
22
  subscription_plan = build(:subscription_plan, stripe_id: nil)
28
23
  expect(subscription_plan.valid?).to be false
@@ -13,7 +13,16 @@ module Payola
13
13
  subscription.should_receive(:save).and_return(true)
14
14
  subscription.should_receive(:guid).at_least(1).times.and_return(1)
15
15
 
16
- CreateSubscription.should_receive(:call).and_return(subscription)
16
+ CreateSubscription.should_receive(:call).with(
17
+ 'plan_class' => 'subscription_plan',
18
+ 'plan_id' => @plan.id.to_s,
19
+ 'controller' => 'payola/subscriptions',
20
+ 'action' => 'create',
21
+ 'plan' => @plan,
22
+ 'coupon' => nil,
23
+ 'affiliate' => nil
24
+ ).and_return(subscription)
25
+
17
26
  Payola.should_receive(:queue!)
18
27
  post :create, plan_class: @plan.plan_class, plan_id: @plan.id, use_route: :payola
19
28
 
@@ -13,7 +13,16 @@ module Payola
13
13
  sale.should_receive(:save).and_return(true)
14
14
  sale.should_receive(:guid).at_least(1).times.and_return('blah')
15
15
 
16
- CreateSale.should_receive(:call).and_return(sale)
16
+ CreateSale.should_receive(:call).with(
17
+ 'product_class' => 'product',
18
+ 'permalink' => @product.permalink,
19
+ 'controller' => 'payola/transactions',
20
+ 'action' => 'create',
21
+ 'product' => @product,
22
+ 'coupon' => nil,
23
+ 'affiliate' => nil
24
+ ).and_return(sale)
25
+
17
26
  Payola.should_receive(:queue!)
18
27
  post :create, product_class: @product.product_class, permalink: @product.permalink, use_route: :payola
19
28