pay 6.4.0 → 6.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b4633aee05e88559e1433e3cb2078d693db0acd19bf605e9de91c6c4fff5f182
4
- data.tar.gz: b60f17927b33068b19b4890b2b484d120e096ac8cff44de6e32974617ce92183
3
+ metadata.gz: 48b9300fb2d1eaaff0b5f8f52a7e16483e1b528e31eaa142f6ce6715cb49b29e
4
+ data.tar.gz: 4c67d04f2e0e04e723f7af6314ced1e3edbd57dd6b41d4eceb579db5f5ec3d8c
5
5
  SHA512:
6
- metadata.gz: 340aad778f4df4390481a3e1afdd38427410d31343e739a86ed1f14491f4d4ada04c1027ce1f82621a408b277f47d58b3366c59ed4266caed046f5e43ad0714e
7
- data.tar.gz: 467739d756394085a5f0113ee2be78ab477f61f25982eb77342e549e525ab8907abf43860674d0ecc3299c46dad859d8b889fbcfa95c33093dc769aaf3c253c9
6
+ metadata.gz: 2859a3b93d305d940d21518a7684db3f80dce911a787728fbfbd6dfb99703647be2097b66a02662c230889c44087131fe0576680fd39f44ada37bda33fbc44da
7
+ data.tar.gz: ab54ba319c7143fafe7d2b2bad7d6c84431ec1c07fa630350e5fde3967fb75422677a8ca96b2838dec4e444fc5e8b0aada38be104c50e49a5eadffc10eb3c3d6
@@ -104,23 +104,6 @@ module Pay
104
104
  ends_at? && ends_at <= Time.current
105
105
  end
106
106
 
107
- def on_grace_period?
108
- (ends_at? && ends_at > Time.current) ||
109
- ((status == "paused" || pause_behavior == "void") && will_pause?)
110
- end
111
-
112
- def will_pause?
113
- pause_starts_at? && Time.current < pause_starts_at
114
- end
115
-
116
- def pause_active?
117
- if status == "paused" # Paddle
118
- true
119
- elsif pause_behavior == "void"
120
- pause_starts_at.nil? || Time.current.after?(pause_starts_at)
121
- end
122
- end
123
-
124
107
  # If you cancel during a trial, you should still retain access until the end of the trial
125
108
  # Otherwise a subscription is active unless it has ended or is currently paused
126
109
  # Check the subscription status so we don't accidentally consider "incomplete", "past_due", or other statuses as active
@@ -18,6 +18,8 @@ module Pay
18
18
  has_one :payment_processor, -> { where(default: true, deleted_at: nil) }, class_name: "Pay::Customer", as: :owner, inverse_of: :owner
19
19
 
20
20
  after_commit :cancel_active_pay_subscriptions!, on: [:destroy]
21
+
22
+ Pay::Stripe.model_names << name if Pay::Stripe.enabled?
21
23
  end
22
24
 
23
25
  # Changes a user's payment processor
@@ -5,7 +5,7 @@ module Pay
5
5
 
6
6
  delegate :active?,
7
7
  :canceled?,
8
- :on_grace_period?,
8
+ :ends_at?,
9
9
  :customer,
10
10
  :ends_at,
11
11
  :name,
@@ -100,6 +100,10 @@ module Pay
100
100
  raise NotImplementedError, "Braintree does not support setting quantity on subscriptions"
101
101
  end
102
102
 
103
+ def on_grace_period?
104
+ ends_at? && ends_at > Time.current
105
+ end
106
+
103
107
  def paused?
104
108
  false
105
109
  end
@@ -8,6 +8,7 @@ module Pay
8
8
  :on_grace_period?,
9
9
  :on_trial?,
10
10
  :ends_at,
11
+ :ends_at?,
11
12
  :owner,
12
13
  :processor_subscription,
13
14
  :processor_id,
@@ -49,7 +50,7 @@ module Pay
49
50
  end
50
51
 
51
52
  def on_grace_period?
52
- canceled? && Time.current < ends_at
53
+ ends_at? && ends_at > Time.current
53
54
  end
54
55
 
55
56
  def paused?
@@ -49,8 +49,10 @@ module Pay
49
49
  # pass
50
50
  end
51
51
 
52
- def add_payment_method(token, default: true)
53
- Pay::Paddle::PaymentMethod.sync(self)
52
+ # Paddle does not use payment method tokens. The method signature has it here
53
+ # to have a uniform API with the other payment processors.
54
+ def add_payment_method(token = nil, default: true)
55
+ Pay::Paddle::PaymentMethod.sync(pay_customer: pay_customer)
54
56
  end
55
57
 
56
58
  def trial_end_date(subscription)
@@ -11,6 +11,7 @@ module Pay
11
11
  :name,
12
12
  :owner,
13
13
  :pause_starts_at,
14
+ :pause_starts_at?,
14
15
  :processor_id,
15
16
  :processor_plan,
16
17
  :processor_subscription,
@@ -107,8 +108,10 @@ module Pay
107
108
  raise NotImplementedError, "Paddle does not support setting quantity on subscriptions"
108
109
  end
109
110
 
111
+ # A subscription could be set to cancel or pause in the future
112
+ # It is considered on grace period until the cancel or pause time begins
110
113
  def on_grace_period?
111
- canceled? && Time.current < ends_at || paused? && Time.current < paddle_paused_from
114
+ (canceled? && Time.current < ends_at) || (paused? && pause_starts_at? && Time.current < pause_starts_at)
112
115
  end
113
116
 
114
117
  def paused?
@@ -6,10 +6,12 @@ module Pay
6
6
 
7
7
  delegate :active?,
8
8
  :canceled?,
9
- :on_grace_period?,
9
+ :ends_at?,
10
10
  :ends_at,
11
11
  :name,
12
12
  :on_trial?,
13
+ :pause_starts_at,
14
+ :pause_starts_at?,
13
15
  :processor_id,
14
16
  :processor_plan,
15
17
  :processor_subscription,
@@ -184,6 +186,18 @@ module Pay
184
186
  raise Pay::Stripe::Error, e
185
187
  end
186
188
 
189
+ def on_grace_period?
190
+ (ends_at? && ends_at > Time.current) || (paused? && will_pause?)
191
+ end
192
+
193
+ def pause_active?
194
+ paused? && (pause_starts_at.nil? || Time.current.after?(pause_starts_at))
195
+ end
196
+
197
+ def will_pause?
198
+ pause_starts_at? && Time.current < pause_starts_at
199
+ end
200
+
187
201
  def paused?
188
202
  pause_behavior == "void"
189
203
  end
@@ -5,6 +5,9 @@ module Pay
5
5
  def call(event)
6
6
  locate_owner(event.data.object)
7
7
 
8
+ # By the time CheckoutSessionCompleted is fired, we probably missed the original events
9
+ # Instead, we can sync the payment intent or subscription during this event to ensure they're in the database
10
+
8
11
  if (payment_intent_id = event.data.object.payment_intent)
9
12
  payment_intent = ::Stripe::PaymentIntent.retrieve({id: payment_intent_id}, {stripe_account: event.try(:account)}.compact)
10
13
  Pay::Stripe::Charge.sync(payment_intent.latest_charge, stripe_account: event.try(:account))
@@ -18,11 +21,8 @@ module Pay
18
21
  def locate_owner(object)
19
22
  return if object.client_reference_id.nil?
20
23
 
21
- # If there is a client reference ID, make sure we have a Pay::Customer record
22
- owner = GlobalID::Locator.locate_signed(object.client_reference_id)
24
+ owner = Pay::Stripe.find_by_client_reference_id(object.client_reference_id)
23
25
  owner&.add_payment_processor(:stripe, processor_id: object.customer)
24
- rescue
25
- Rails.logger.debug "[Pay] Unable to locate record with SGID: #{object.client_reference_id}"
26
26
  end
27
27
  end
28
28
  end
@@ -6,6 +6,9 @@ module Pay
6
6
  object = event.data.object
7
7
  pay_customer = Pay::Customer.find_by(processor: :stripe, processor_id: object.id)
8
8
 
9
+ # Skip processing if this customer is not in the database
10
+ return unless pay_customer
11
+
9
12
  # Mark all subscriptions as canceled
10
13
  pay_customer.subscriptions.active.update_all(ends_at: Time.current, status: "canceled")
11
14
 
@@ -13,7 +16,7 @@ module Pay
13
16
  pay_customer.payment_methods.destroy_all
14
17
 
15
18
  # Mark customer as deleted
16
- pay_customer&.update!(default: false, deleted_at: Time.current)
19
+ pay_customer.update!(default: false, deleted_at: Time.current)
17
20
  end
18
21
  end
19
22
  end
data/lib/pay/stripe.rb CHANGED
@@ -30,6 +30,10 @@ module Pay
30
30
 
31
31
  extend Env
32
32
 
33
+ # A list of database model names that include Pay
34
+ # Used for safely looking up models with client_reference_id
35
+ mattr_accessor :model_names, default: Set.new
36
+
33
37
  def self.enabled?
34
38
  return false unless Pay.enabled_processors.include?(:stripe) && defined?(::Stripe)
35
39
 
@@ -114,5 +118,24 @@ module Pay
114
118
  events.subscribe "stripe.checkout.session.async_payment_succeeded", Pay::Stripe::Webhooks::CheckoutSessionAsyncPaymentSucceeded.new
115
119
  end
116
120
  end
121
+
122
+ def self.to_client_reference_id(record)
123
+ raise ArgumentError, "#{record.class.name} does not include Pay. Allowed models: #{model_names.to_a.join(", ")}" unless model_names.include?(record.class.name)
124
+ [record.class.name, record.id].join("/")
125
+ end
126
+
127
+ def self.find_by_client_reference_id(client_reference_id)
128
+ # If there is a client reference ID, make sure we have a Pay::Customer record
129
+ # client_reference_id should be in the format of "User/1"
130
+ model_name, id = client_reference_id.split("/", 2)
131
+
132
+ # Only allow model names that use Pay
133
+ return unless model_names.include?(model_name)
134
+
135
+ model_name.constantize.find(id)
136
+ rescue ActiveRecord::RecordNotFound
137
+ Rails.logger.error "[Pay] Unable to locate record with: #{client_reference_id}"
138
+ nil
139
+ end
117
140
  end
118
141
  end
data/lib/pay/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Pay
2
- VERSION = "6.4.0"
2
+ VERSION = "6.6.0"
3
3
  end
data/lib/pay.rb CHANGED
@@ -57,6 +57,9 @@ module Pay
57
57
  mattr_accessor :enabled_processors
58
58
  @@enabled_processors = [:stripe, :braintree, :paddle]
59
59
 
60
+ mattr_accessor :send_emails
61
+ @@send_emails = true
62
+
60
63
  mattr_accessor :emails
61
64
  @@emails = ActiveSupport::OrderedOptions.new
62
65
  @@emails.payment_action_required = true
@@ -111,12 +114,19 @@ module Pay
111
114
  end
112
115
 
113
116
  def self.send_email?(email_option, *remaining_args)
114
- config_option = emails.send(email_option)
117
+ if resolve_option(send_emails, *remaining_args)
118
+ email_config_option_enabled = emails.send(email_option)
119
+ resolve_option(email_config_option_enabled, *remaining_args)
120
+ else
121
+ false
122
+ end
123
+ end
115
124
 
116
- if config_option.respond_to?(:call)
117
- config_option.call(*remaining_args)
125
+ def self.resolve_option(option, *remaining_args)
126
+ if option.respond_to?(:call)
127
+ option.call(*remaining_args)
118
128
  else
119
- config_option
129
+ option
120
130
  end
121
131
  end
122
132
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pay
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.4.0
4
+ version: 6.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Charnes
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2023-05-13 00:00:00.000000000 Z
13
+ date: 2023-06-02 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rails
@@ -168,7 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
168
168
  - !ruby/object:Gem::Version
169
169
  version: '0'
170
170
  requirements: []
171
- rubygems_version: 3.4.12
171
+ rubygems_version: 3.4.13
172
172
  signing_key:
173
173
  specification_version: 4
174
174
  summary: Payments engine for Ruby on Rails