pay 2.7.0 → 3.0.1
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.
- checksums.yaml +4 -4
- data/README.md +35 -696
- data/app/controllers/pay/webhooks/braintree_controller.rb +10 -3
- data/app/controllers/pay/webhooks/paddle_controller.rb +7 -8
- data/app/controllers/pay/webhooks/stripe_controller.rb +6 -3
- data/app/jobs/pay/{email_sync_job.rb → customer_sync_job.rb} +3 -4
- data/app/models/pay/application_record.rb +1 -5
- data/app/models/pay/charge.rb +31 -18
- data/app/models/pay/customer.rb +87 -0
- data/app/models/pay/merchant.rb +19 -0
- data/app/models/pay/payment_method.rb +41 -0
- data/app/models/pay/subscription.rb +42 -30
- data/app/models/pay/webhook.rb +36 -0
- data/app/views/layouts/pay/application.html.erb +2 -3
- data/app/views/pay/payments/show.html.erb +109 -81
- data/app/views/pay/user_mailer/receipt.html.erb +2 -2
- data/app/views/pay/user_mailer/refund.html.erb +2 -2
- data/config/locales/en.yml +1 -1
- data/db/migrate/1_create_pay_tables.rb +72 -0
- data/lib/generators/active_record/billable_generator.rb +44 -0
- data/lib/generators/active_record/merchant_generator.rb +44 -0
- data/lib/generators/active_record/templates/billable_migration.rb +17 -0
- data/lib/generators/active_record/templates/merchant_migration.rb +12 -0
- data/lib/generators/pay/{pay_generator.rb → billable_generator.rb} +2 -3
- data/lib/generators/pay/merchant_generator.rb +17 -0
- data/lib/generators/pay/orm_helpers.rb +10 -6
- data/lib/pay/adapter.rb +9 -0
- data/lib/pay/attributes.rb +74 -0
- data/lib/pay/billable/sync_customer.rb +30 -0
- data/lib/pay/braintree/billable.rb +133 -110
- data/lib/pay/braintree/payment_method.rb +42 -0
- data/lib/pay/braintree/subscription.rb +9 -12
- data/lib/pay/braintree/webhooks/subscription_canceled.rb +1 -1
- data/lib/pay/braintree/webhooks/subscription_charged_successfully.rb +4 -4
- data/lib/pay/braintree/webhooks/subscription_charged_unsuccessfully.rb +1 -1
- data/lib/pay/braintree/webhooks/subscription_expired.rb +1 -1
- data/lib/pay/braintree/webhooks/subscription_trial_ended.rb +2 -2
- data/lib/pay/braintree/webhooks/subscription_went_active.rb +1 -1
- data/lib/pay/braintree/webhooks/subscription_went_past_due.rb +1 -1
- data/lib/pay/braintree.rb +3 -2
- data/lib/pay/engine.rb +6 -1
- data/lib/pay/env.rb +8 -0
- data/lib/pay/fake_processor/billable.rb +45 -21
- data/lib/pay/fake_processor/payment_method.rb +21 -0
- data/lib/pay/fake_processor/subscription.rb +11 -8
- data/lib/pay/fake_processor.rb +2 -1
- data/lib/pay/nano_id.rb +13 -0
- data/lib/pay/paddle/billable.rb +18 -48
- data/lib/pay/paddle/charge.rb +5 -5
- data/lib/pay/paddle/payment_method.rb +60 -0
- data/lib/pay/paddle/response.rb +0 -0
- data/lib/pay/paddle/subscription.rb +49 -8
- data/lib/pay/paddle/webhooks/subscription_cancelled.rb +6 -3
- data/lib/pay/paddle/webhooks/subscription_created.rb +1 -40
- data/lib/pay/paddle/webhooks/subscription_payment_refunded.rb +3 -3
- data/lib/pay/paddle/webhooks/subscription_payment_succeeded.rb +26 -28
- data/lib/pay/paddle/webhooks/subscription_updated.rb +2 -2
- data/lib/pay/paddle.rb +7 -3
- data/lib/pay/payment.rb +1 -1
- data/lib/pay/receipts.rb +35 -7
- data/lib/pay/stripe/billable.rb +80 -102
- data/lib/pay/stripe/charge.rb +59 -3
- data/lib/pay/stripe/merchant.rb +10 -10
- data/lib/pay/stripe/payment_method.rb +61 -0
- data/lib/pay/stripe/subscription.rb +70 -8
- data/lib/pay/stripe/webhooks/account_updated.rb +2 -3
- data/lib/pay/stripe/webhooks/charge_refunded.rb +2 -7
- data/lib/pay/stripe/webhooks/charge_succeeded.rb +2 -8
- data/lib/pay/stripe/webhooks/checkout_session_async_payment_succeeded.rb +15 -0
- data/lib/pay/stripe/webhooks/checkout_session_completed.rb +15 -0
- data/lib/pay/stripe/webhooks/customer_deleted.rb +7 -15
- data/lib/pay/stripe/webhooks/customer_updated.rb +10 -3
- data/lib/pay/stripe/webhooks/payment_action_required.rb +2 -2
- data/lib/pay/stripe/webhooks/payment_intent_succeeded.rb +6 -14
- data/lib/pay/stripe/webhooks/payment_method_attached.rb +15 -0
- data/lib/pay/stripe/webhooks/payment_method_detached.rb +12 -0
- data/lib/pay/stripe/webhooks/payment_method_updated.rb +10 -4
- data/lib/pay/stripe/webhooks/subscription_created.rb +1 -36
- data/lib/pay/stripe/webhooks/subscription_deleted.rb +2 -9
- data/lib/pay/stripe/webhooks/subscription_renewing.rb +14 -6
- data/lib/pay/stripe/webhooks/subscription_updated.rb +1 -29
- data/lib/pay/stripe.rb +12 -3
- data/lib/pay/version.rb +1 -1
- data/lib/pay/webhooks/delegator.rb +4 -0
- data/lib/pay/webhooks/process_job.rb +9 -0
- data/lib/pay/webhooks.rb +1 -0
- data/lib/pay.rb +7 -78
- data/lib/tasks/pay.rake +20 -0
- metadata +31 -39
- data/app/models/pay.rb +0 -5
- data/db/migrate/20170205020145_create_pay_subscriptions.rb +0 -17
- data/db/migrate/20170727235816_create_pay_charges.rb +0 -18
- data/db/migrate/20190816015720_add_status_to_pay_subscriptions.rb +0 -14
- data/db/migrate/20200603134434_add_data_to_pay_models.rb +0 -15
- data/db/migrate/20210309004259_add_data_to_pay_billable.rb +0 -19
- data/db/migrate/20210406215234_add_currency_to_pay_charges.rb +0 -5
- data/db/migrate/20210406215506_add_application_fee_to_pay_models.rb +0 -7
- data/lib/generators/active_record/pay_generator.rb +0 -58
- data/lib/generators/active_record/templates/migration.rb +0 -9
- data/lib/pay/billable/sync_email.rb +0 -40
- data/lib/pay/billable.rb +0 -172
data/app/models/pay.rb
DELETED
@@ -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,15 +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
|
-
case Pay::Adapter.current_adapter
|
9
|
-
when "postgresql"
|
10
|
-
:jsonb
|
11
|
-
else
|
12
|
-
:json
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
class AddDataToPayBillable < ActiveRecord::Migration[4.2]
|
2
|
-
def change
|
3
|
-
# Load all the billable models
|
4
|
-
Rails.application.eager_load!
|
5
|
-
|
6
|
-
Pay.billable_models.each do |model|
|
7
|
-
add_column model.table_name, :pay_data, data_column_type
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
def data_column_type
|
12
|
-
case Pay::Adapter.current_adapter
|
13
|
-
when "postgresql"
|
14
|
-
:jsonb
|
15
|
-
else
|
16
|
-
:json
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,7 +0,0 @@
|
|
1
|
-
class AddApplicationFeeToPayModels < ActiveRecord::Migration[4.2]
|
2
|
-
def change
|
3
|
-
add_column :pay_charges, :application_fee_amount, :integer
|
4
|
-
add_column :pay_subscriptions, :application_fee_percent, :decimal, precision: 8, scale: 2
|
5
|
-
add_column :pay_charges, :pay_subscription_id, :integer
|
6
|
-
end
|
7
|
-
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,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,172 +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
|
-
# Account(s) for marketplace payments
|
30
|
-
store_accessor :pay_data, :stripe_account
|
31
|
-
store_accessor :pay_data, :braintree_account
|
32
|
-
|
33
|
-
validate :pay_fake_processor_is_allowed, if: :processor_changed?
|
34
|
-
end
|
35
|
-
|
36
|
-
def payment_processor
|
37
|
-
@payment_processor ||= payment_processor_for(processor).new(self)
|
38
|
-
end
|
39
|
-
|
40
|
-
def payment_processor_for(name)
|
41
|
-
raise Error, "No payment processor set. Assign a payment processor with 'object.processor = :stripe' or any supported processor." if name.blank?
|
42
|
-
"Pay::#{name.to_s.classify}::Billable".constantize
|
43
|
-
end
|
44
|
-
|
45
|
-
# Reset the payment processor when it changes
|
46
|
-
def processor=(value)
|
47
|
-
super(value)
|
48
|
-
self.processor_id = nil if processor_changed?
|
49
|
-
@payment_processor = nil
|
50
|
-
end
|
51
|
-
|
52
|
-
def processor
|
53
|
-
super&.inquiry
|
54
|
-
end
|
55
|
-
|
56
|
-
delegate :charge, to: :payment_processor
|
57
|
-
delegate :subscribe, to: :payment_processor
|
58
|
-
delegate :update_card, to: :payment_processor
|
59
|
-
|
60
|
-
def customer
|
61
|
-
raise Pay::Error, I18n.t("errors.email_required") if email.nil?
|
62
|
-
|
63
|
-
customer = payment_processor.customer
|
64
|
-
payment_processor.update_card(card_token) if card_token.present?
|
65
|
-
customer
|
66
|
-
end
|
67
|
-
|
68
|
-
def customer_name
|
69
|
-
[try(:first_name), try(:last_name)].compact.join(" ")
|
70
|
-
end
|
71
|
-
|
72
|
-
def create_setup_intent
|
73
|
-
ActiveSupport::Deprecation.warn("This method will be removed in the next release. Use `@billable.payment_processor.create_setup_intent` instead.")
|
74
|
-
payment_processor.create_setup_intent
|
75
|
-
end
|
76
|
-
|
77
|
-
def on_trial?(name: Pay.default_product_name, plan: nil)
|
78
|
-
return true if default_generic_trial?(name, plan)
|
79
|
-
|
80
|
-
sub = subscription(name: name)
|
81
|
-
return sub&.on_trial? if plan.nil?
|
82
|
-
|
83
|
-
sub&.on_trial? && sub.processor_plan == plan
|
84
|
-
end
|
85
|
-
|
86
|
-
def on_generic_trial?
|
87
|
-
trial_ends_at? && trial_ends_at > Time.zone.now
|
88
|
-
end
|
89
|
-
|
90
|
-
def processor_subscription(subscription_id, options = {})
|
91
|
-
payment_processor.processor_subscription(subscription_id, options)
|
92
|
-
end
|
93
|
-
|
94
|
-
def subscribed?(name: Pay.default_product_name, processor_plan: nil)
|
95
|
-
subscription = subscription(name: name)
|
96
|
-
|
97
|
-
return false if subscription.nil?
|
98
|
-
return subscription.active? if processor_plan.nil?
|
99
|
-
|
100
|
-
subscription.active? && subscription.processor_plan == processor_plan
|
101
|
-
end
|
102
|
-
|
103
|
-
def on_trial_or_subscribed?(name: Pay.default_product_name, processor_plan: nil)
|
104
|
-
on_trial?(name: name, plan: processor_plan) ||
|
105
|
-
subscribed?(name: name, processor_plan: processor_plan)
|
106
|
-
end
|
107
|
-
|
108
|
-
def subscription(name: Pay.default_product_name)
|
109
|
-
subscriptions.loaded? ? subscriptions.reverse.detect { |s| s.name == name } : subscriptions.for_name(name).last
|
110
|
-
end
|
111
|
-
|
112
|
-
def invoice!(options = {})
|
113
|
-
ActiveSupport::Deprecation.warn("This will be removed in the next release. Use `@billable.payment_processor.invoice!` instead.")
|
114
|
-
payment_processor.invoice!(options)
|
115
|
-
end
|
116
|
-
|
117
|
-
def upcoming_invoice
|
118
|
-
ActiveSupport::Deprecation.warn("This will be removed in the next release. Use `@billable.payment_processor.upcoming_invoice` instead.")
|
119
|
-
payment_processor.upcoming_invoice
|
120
|
-
end
|
121
|
-
|
122
|
-
def stripe?
|
123
|
-
ActiveSupport::Deprecation.warn("This will be removed in the next release. Use `@billable.processor.stripe?` instead.")
|
124
|
-
processor == "stripe"
|
125
|
-
end
|
126
|
-
|
127
|
-
def braintree?
|
128
|
-
ActiveSupport::Deprecation.warn("This will be removed in the next release. Use `@billable.processor.braintree?` instead.")
|
129
|
-
processor == "braintree"
|
130
|
-
end
|
131
|
-
|
132
|
-
def paypal?
|
133
|
-
card_type == "PayPal"
|
134
|
-
end
|
135
|
-
|
136
|
-
def paddle?
|
137
|
-
ActiveSupport::Deprecation.warn("This will be removed in the next release. Use `@billable.processor.paddle?` instead.")
|
138
|
-
processor == "paddle"
|
139
|
-
end
|
140
|
-
|
141
|
-
def has_incomplete_payment?(name: Pay.default_product_name)
|
142
|
-
subscription(name: name)&.has_incomplete_payment?
|
143
|
-
end
|
144
|
-
|
145
|
-
# Used for creating a Pay::Subscription in the database
|
146
|
-
def create_pay_subscription(subscription, processor, name, plan, options = {})
|
147
|
-
options[:quantity] ||= 1
|
148
|
-
|
149
|
-
options.merge!(
|
150
|
-
name: name || "default",
|
151
|
-
processor: processor,
|
152
|
-
processor_id: subscription.id,
|
153
|
-
processor_plan: plan,
|
154
|
-
trial_ends_at: payment_processor.trial_end_date(subscription),
|
155
|
-
ends_at: nil
|
156
|
-
)
|
157
|
-
subscriptions.create!(options)
|
158
|
-
end
|
159
|
-
|
160
|
-
private
|
161
|
-
|
162
|
-
def default_generic_trial?(name, plan)
|
163
|
-
# Generic trials don't have plans or custom names
|
164
|
-
plan.nil? && name == "default" && on_generic_trial?
|
165
|
-
end
|
166
|
-
|
167
|
-
def pay_fake_processor_is_allowed
|
168
|
-
return unless processor == "fake_processor"
|
169
|
-
errors.add(:processor, "must be a valid payment processor") unless pay_fake_processor_allowed?
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|