pay 6.4.0 → 6.6.0

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.
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