pay 2.6.11 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of pay might be problematic. Click here for more details.

Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +35 -693
  3. data/app/controllers/pay/webhooks/braintree_controller.rb +10 -3
  4. data/app/controllers/pay/webhooks/paddle_controller.rb +7 -8
  5. data/app/controllers/pay/webhooks/stripe_controller.rb +6 -3
  6. data/app/jobs/pay/{email_sync_job.rb → customer_sync_job.rb} +3 -4
  7. data/app/models/pay/application_record.rb +1 -5
  8. data/app/models/pay/charge.rb +32 -17
  9. data/app/models/pay/customer.rb +87 -0
  10. data/app/models/pay/merchant.rb +19 -0
  11. data/app/models/pay/payment_method.rb +41 -0
  12. data/app/models/pay/subscription.rb +34 -30
  13. data/app/models/pay/webhook.rb +36 -0
  14. data/app/views/layouts/pay/application.html.erb +2 -3
  15. data/app/views/pay/payments/show.html.erb +109 -81
  16. data/app/views/pay/user_mailer/receipt.html.erb +2 -2
  17. data/app/views/pay/user_mailer/refund.html.erb +2 -2
  18. data/config/locales/en.yml +1 -1
  19. data/db/migrate/1_create_pay_tables.rb +72 -0
  20. data/lib/generators/active_record/billable_generator.rb +44 -0
  21. data/lib/generators/active_record/merchant_generator.rb +44 -0
  22. data/lib/generators/active_record/templates/billable_migration.rb +17 -0
  23. data/lib/generators/active_record/templates/merchant_migration.rb +12 -0
  24. data/lib/generators/pay/{pay_generator.rb → billable_generator.rb} +2 -3
  25. data/lib/generators/pay/merchant_generator.rb +17 -0
  26. data/lib/generators/pay/orm_helpers.rb +10 -6
  27. data/lib/pay/adapter.rb +22 -0
  28. data/lib/pay/attributes.rb +74 -0
  29. data/lib/pay/billable/sync_customer.rb +30 -0
  30. data/lib/pay/braintree/billable.rb +130 -105
  31. data/lib/pay/braintree/payment_method.rb +33 -0
  32. data/lib/pay/braintree/subscription.rb +9 -12
  33. data/lib/pay/braintree/webhooks/subscription_canceled.rb +1 -1
  34. data/lib/pay/braintree/webhooks/subscription_charged_successfully.rb +4 -4
  35. data/lib/pay/braintree/webhooks/subscription_charged_unsuccessfully.rb +1 -1
  36. data/lib/pay/braintree/webhooks/subscription_expired.rb +1 -1
  37. data/lib/pay/braintree/webhooks/subscription_trial_ended.rb +2 -2
  38. data/lib/pay/braintree/webhooks/subscription_went_active.rb +1 -1
  39. data/lib/pay/braintree/webhooks/subscription_went_past_due.rb +1 -1
  40. data/lib/pay/braintree.rb +3 -2
  41. data/lib/pay/engine.rb +6 -1
  42. data/lib/pay/env.rb +8 -0
  43. data/lib/pay/fake_processor/billable.rb +45 -21
  44. data/lib/pay/fake_processor/payment_method.rb +21 -0
  45. data/lib/pay/fake_processor/subscription.rb +11 -8
  46. data/lib/pay/fake_processor.rb +2 -1
  47. data/lib/pay/merchant.rb +37 -0
  48. data/lib/pay/nano_id.rb +13 -0
  49. data/lib/pay/paddle/billable.rb +18 -48
  50. data/lib/pay/paddle/charge.rb +5 -5
  51. data/lib/pay/paddle/payment_method.rb +58 -0
  52. data/lib/pay/paddle/response.rb +0 -0
  53. data/lib/pay/paddle/subscription.rb +49 -8
  54. data/lib/pay/paddle/webhooks/subscription_cancelled.rb +6 -3
  55. data/lib/pay/paddle/webhooks/subscription_created.rb +1 -40
  56. data/lib/pay/paddle/webhooks/subscription_payment_refunded.rb +3 -3
  57. data/lib/pay/paddle/webhooks/subscription_payment_succeeded.rb +23 -23
  58. data/lib/pay/paddle/webhooks/subscription_updated.rb +2 -2
  59. data/lib/pay/paddle.rb +7 -3
  60. data/lib/pay/payment.rb +1 -1
  61. data/lib/pay/receipts.rb +35 -7
  62. data/lib/pay/stripe/billable.rb +82 -93
  63. data/lib/pay/stripe/charge.rb +65 -4
  64. data/lib/pay/stripe/merchant.rb +66 -0
  65. data/lib/pay/stripe/payment_method.rb +61 -0
  66. data/lib/pay/stripe/subscription.rb +91 -24
  67. data/lib/pay/stripe/webhooks/account_updated.rb +16 -0
  68. data/lib/pay/stripe/webhooks/charge_refunded.rb +2 -7
  69. data/lib/pay/stripe/webhooks/charge_succeeded.rb +2 -8
  70. data/lib/pay/stripe/webhooks/checkout_session_async_payment_succeeded.rb +15 -0
  71. data/lib/pay/stripe/webhooks/checkout_session_completed.rb +15 -0
  72. data/lib/pay/stripe/webhooks/customer_deleted.rb +7 -15
  73. data/lib/pay/stripe/webhooks/customer_updated.rb +10 -3
  74. data/lib/pay/stripe/webhooks/payment_action_required.rb +2 -2
  75. data/lib/pay/stripe/webhooks/payment_intent_succeeded.rb +6 -14
  76. data/lib/pay/stripe/webhooks/payment_method_attached.rb +15 -0
  77. data/lib/pay/stripe/webhooks/payment_method_detached.rb +12 -0
  78. data/lib/pay/stripe/webhooks/payment_method_updated.rb +10 -4
  79. data/lib/pay/stripe/webhooks/subscription_created.rb +1 -35
  80. data/lib/pay/stripe/webhooks/subscription_deleted.rb +2 -9
  81. data/lib/pay/stripe/webhooks/subscription_renewing.rb +14 -6
  82. data/lib/pay/stripe/webhooks/subscription_updated.rb +1 -28
  83. data/lib/pay/stripe.rb +17 -3
  84. data/lib/pay/version.rb +1 -1
  85. data/lib/pay/webhooks/delegator.rb +4 -0
  86. data/lib/pay/webhooks/process_job.rb +9 -0
  87. data/lib/pay/webhooks.rb +1 -0
  88. data/lib/pay.rb +8 -57
  89. metadata +34 -36
  90. data/db/migrate/20170205020145_create_pay_subscriptions.rb +0 -17
  91. data/db/migrate/20170727235816_create_pay_charges.rb +0 -18
  92. data/db/migrate/20190816015720_add_status_to_pay_subscriptions.rb +0 -14
  93. data/db/migrate/20200603134434_add_data_to_pay_models.rb +0 -22
  94. data/db/migrate/20210423235138_add_currency_to_pay_charges.rb +0 -5
  95. data/lib/generators/active_record/pay_generator.rb +0 -58
  96. data/lib/generators/active_record/templates/migration.rb +0 -9
  97. data/lib/pay/billable/sync_email.rb +0 -40
  98. data/lib/pay/billable.rb +0 -168
@@ -1,17 +0,0 @@
1
- class CreatePaySubscriptions < ActiveRecord::Migration[4.2]
2
- def change
3
- create_table :pay_subscriptions do |t|
4
- # Some Billable objects use string as ID, add `type: :string` if needed
5
- t.references :owner, polymorphic: true
6
- t.string :name, null: false
7
- t.string :processor, null: false
8
- t.string :processor_id, null: false
9
- t.string :processor_plan, null: false
10
- t.integer :quantity, default: 1, null: false
11
- t.datetime :trial_ends_at
12
- t.datetime :ends_at
13
-
14
- t.timestamps
15
- end
16
- end
17
- end
@@ -1,18 +0,0 @@
1
- class CreatePayCharges < ActiveRecord::Migration[4.2]
2
- def change
3
- create_table :pay_charges do |t|
4
- # Some Billable objects use string as ID, add `type: :string` if needed
5
- t.references :owner, polymorphic: true
6
- t.string :processor, null: false
7
- t.string :processor_id, null: false
8
- t.integer :amount, null: false
9
- t.integer :amount_refunded
10
- t.string :card_type
11
- t.string :card_last4
12
- t.string :card_exp_month
13
- t.string :card_exp_year
14
-
15
- t.timestamps
16
- end
17
- end
18
- end
@@ -1,14 +0,0 @@
1
- class AddStatusToPaySubscriptions < ActiveRecord::Migration[4.2]
2
- def self.up
3
- add_column :pay_subscriptions, :status, :string
4
-
5
- # Any existing subscriptions should be marked as 'active'
6
- # This won't actually make them active if their ends_at column is expired
7
- Pay::Subscription.reset_column_information
8
- Pay::Subscription.update_all(status: :active)
9
- end
10
-
11
- def self.down
12
- remove_column :pay_subscriptions, :status
13
- end
14
- end
@@ -1,22 +0,0 @@
1
- class AddDataToPayModels < ActiveRecord::Migration[4.2]
2
- def change
3
- add_column :pay_subscriptions, :data, data_column_type
4
- add_column :pay_charges, :data, data_column_type
5
- end
6
-
7
- def data_column_type
8
- default_hash = ActiveRecord::Base.configurations.default_hash
9
-
10
- # Rails 6.1 uses a symbol key instead of a string
11
- adapter = default_hash.dig(:adapter) || default_hash.dig("adapter")
12
-
13
- case adapter
14
- when "mysql2"
15
- :json
16
- when "postgresql"
17
- :jsonb
18
- else
19
- :text
20
- end
21
- end
22
- end
@@ -1,5 +0,0 @@
1
- class AddCurrencyToPayCharges < ActiveRecord::Migration[6.1]
2
- def change
3
- add_column :pay_charges, :currency, :string
4
- end
5
- end
@@ -1,58 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "rails/generators/active_record"
4
- require "generators/pay/orm_helpers"
5
-
6
- module ActiveRecord
7
- module Generators
8
- class PayGenerator < ActiveRecord::Generators::Base
9
- include Pay::Generators::OrmHelpers
10
- source_root File.expand_path("../templates", __FILE__)
11
-
12
- def copy_pay_billable_migration
13
- if (behavior == :invoke && model_exists?) || (behavior == :revoke && migration_exists?(table_name))
14
- migration_template "migration.rb", "#{migration_path}/add_pay_billable_to_#{table_name}.rb", migration_version: migration_version
15
- end
16
- # TODO: Throw error here that model should already exist if it doesn't
17
- end
18
-
19
- def inject_pay_billable_content
20
- content = model_contents
21
-
22
- class_path = if namespaced?
23
- class_name.to_s.split("::")
24
- else
25
- [class_name]
26
- end
27
-
28
- indent_depth = class_path.size - 1
29
- content = content.split("\n").map { |line| " " * indent_depth + line }.join("\n") << "\n"
30
-
31
- inject_into_class(model_path, class_path.last, content) if model_exists?
32
- end
33
-
34
- def migration_data
35
- <<RUBY
36
- t.string :processor
37
- t.string :processor_id
38
- t.datetime :trial_ends_at
39
- t.string :card_type
40
- t.string :card_last4
41
- t.string :card_exp_month
42
- t.string :card_exp_year
43
- t.text :extra_billing_info
44
- RUBY
45
- end
46
-
47
- def rails5_and_up?
48
- Rails::VERSION::MAJOR >= 5
49
- end
50
-
51
- def migration_version
52
- if rails5_and_up?
53
- "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
54
- end
55
- end
56
- end
57
- end
58
- end
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class AddPayBillableTo<%= table_name.camelize %> < ActiveRecord::Migration<%= migration_version %>
4
- def change
5
- change_table :<%= table_name %>, bulk: true do |t|
6
- <%= migration_data -%>
7
- end
8
- end
9
- end
@@ -1,40 +0,0 @@
1
- module Pay
2
- module Billable
3
- module SyncEmail
4
- # Sync email address changes from the model to the processor.
5
- # This way they're kept in sync and email notifications are
6
- # always sent to the correct email address after an update.
7
- #
8
- # Processor classes simply need to implement a method named:
9
- #
10
- # update_PROCESSOR_email!
11
- #
12
- # This method should take the email address on the billable
13
- # object and update the associated API record.
14
-
15
- extend ActiveSupport::Concern
16
-
17
- included do
18
- after_update :enqeue_sync_email_job, if: :should_sync_email_with_processor?
19
- end
20
-
21
- def should_sync_email_with_processor?
22
- try(:saved_change_to_email?)
23
- end
24
-
25
- def sync_email_with_processor
26
- payment_processor.update_email!
27
- end
28
-
29
- private
30
-
31
- def enqeue_sync_email_job
32
- # Only update if the processor id is the same
33
- # This prevents duplicate API hits if this is their first time
34
- if processor_id? && !saved_change_to_processor_id? && saved_change_to_email?
35
- EmailSyncJob.perform_later(id, self.class.name)
36
- end
37
- end
38
- end
39
- end
40
- end
data/lib/pay/billable.rb DELETED
@@ -1,168 +0,0 @@
1
- require "pay/billable/sync_email"
2
-
3
- module Pay
4
- module Billable
5
- extend ActiveSupport::Concern
6
-
7
- # Keep track of which Billable models we have
8
- class << self
9
- attr_reader :includers
10
- end
11
-
12
- def self.included(base = nil, &block)
13
- @includers ||= []
14
- @includers << base if base
15
- super
16
- end
17
-
18
- included do |base|
19
- include Pay::Billable::SyncEmail
20
-
21
- has_many :charges, class_name: Pay.chargeable_class, foreign_key: :owner_id, inverse_of: :owner, as: :owner
22
- has_many :subscriptions, class_name: Pay.subscription_class, foreign_key: :owner_id, inverse_of: :owner, as: :owner
23
-
24
- attribute :plan, :string
25
- attribute :quantity, :integer
26
- attribute :card_token, :string
27
- attribute :pay_fake_processor_allowed, :boolean, default: false
28
-
29
- validate :pay_fake_processor_is_allowed, if: :processor_changed?
30
- end
31
-
32
- def payment_processor
33
- @payment_processor ||= payment_processor_for(processor).new(self)
34
- end
35
-
36
- def payment_processor_for(name)
37
- raise Error, "No payment processor set. Assign a payment processor with 'object.processor = :stripe' or any supported processor." if name.blank?
38
- "Pay::#{name.to_s.classify}::Billable".constantize
39
- end
40
-
41
- # Reset the payment processor when it changes
42
- def processor=(value)
43
- super(value)
44
- self.processor_id = nil if processor_changed?
45
- @payment_processor = nil
46
- end
47
-
48
- def processor
49
- super&.inquiry
50
- end
51
-
52
- delegate :charge, to: :payment_processor
53
- delegate :subscribe, to: :payment_processor
54
- delegate :update_card, to: :payment_processor
55
-
56
- def customer
57
- raise Pay::Error, I18n.t("errors.email_required") if email.nil?
58
-
59
- customer = payment_processor.customer
60
- payment_processor.update_card(card_token) if card_token.present?
61
- customer
62
- end
63
-
64
- def customer_name
65
- [try(:first_name), try(:last_name)].compact.join(" ")
66
- end
67
-
68
- def create_setup_intent
69
- ActiveSupport::Deprecation.warn("This method will be removed in the next release. Use `@billable.payment_processor.create_setup_intent` instead.")
70
- payment_processor.create_setup_intent
71
- end
72
-
73
- def on_trial?(name: Pay.default_product_name, plan: nil)
74
- return true if default_generic_trial?(name, plan)
75
-
76
- sub = subscription(name: name)
77
- return sub&.on_trial? if plan.nil?
78
-
79
- sub&.on_trial? && sub.processor_plan == plan
80
- end
81
-
82
- def on_generic_trial?
83
- trial_ends_at? && trial_ends_at > Time.zone.now
84
- end
85
-
86
- def processor_subscription(subscription_id, options = {})
87
- payment_processor.processor_subscription(subscription_id, options)
88
- end
89
-
90
- def subscribed?(name: Pay.default_product_name, processor_plan: nil)
91
- subscription = subscription(name: name)
92
-
93
- return false if subscription.nil?
94
- return subscription.active? if processor_plan.nil?
95
-
96
- subscription.active? && subscription.processor_plan == processor_plan
97
- end
98
-
99
- def on_trial_or_subscribed?(name: Pay.default_product_name, processor_plan: nil)
100
- on_trial?(name: name, plan: processor_plan) ||
101
- subscribed?(name: name, processor_plan: processor_plan)
102
- end
103
-
104
- def subscription(name: Pay.default_product_name)
105
- subscriptions.loaded? ? subscriptions.reverse.detect { |s| s.name == name } : subscriptions.for_name(name).last
106
- end
107
-
108
- def invoice!(options = {})
109
- ActiveSupport::Deprecation.warn("This will be removed in the next release. Use `@billable.payment_processor.invoice!` instead.")
110
- payment_processor.invoice!(options)
111
- end
112
-
113
- def upcoming_invoice
114
- ActiveSupport::Deprecation.warn("This will be removed in the next release. Use `@billable.payment_processor.upcoming_invoice` instead.")
115
- payment_processor.upcoming_invoice
116
- end
117
-
118
- def stripe?
119
- ActiveSupport::Deprecation.warn("This will be removed in the next release. Use `@billable.processor.stripe?` instead.")
120
- processor == "stripe"
121
- end
122
-
123
- def braintree?
124
- ActiveSupport::Deprecation.warn("This will be removed in the next release. Use `@billable.processor.braintree?` instead.")
125
- processor == "braintree"
126
- end
127
-
128
- def paypal?
129
- card_type == "PayPal"
130
- end
131
-
132
- def paddle?
133
- ActiveSupport::Deprecation.warn("This will be removed in the next release. Use `@billable.processor.paddle?` instead.")
134
- processor == "paddle"
135
- end
136
-
137
- def has_incomplete_payment?(name: Pay.default_product_name)
138
- subscription(name: name)&.has_incomplete_payment?
139
- end
140
-
141
- # Used for creating a Pay::Subscription in the database
142
- def create_pay_subscription(subscription, processor, name, plan, options = {})
143
- options[:quantity] ||= 1
144
-
145
- options.merge!(
146
- name: name || "default",
147
- processor: processor,
148
- processor_id: subscription.id,
149
- processor_plan: plan,
150
- trial_ends_at: payment_processor.trial_end_date(subscription),
151
- ends_at: nil
152
- )
153
- subscriptions.create!(options)
154
- end
155
-
156
- private
157
-
158
- def default_generic_trial?(name, plan)
159
- # Generic trials don't have plans or custom names
160
- plan.nil? && name == "default" && on_generic_trial?
161
- end
162
-
163
- def pay_fake_processor_is_allowed
164
- return unless processor == "fake_processor"
165
- errors.add(:processor, "must be a valid payment processor") unless pay_fake_processor_allowed?
166
- end
167
- end
168
- end