pay 6.5.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: 8f418566efec491866a28f3bb47fde5658eb5350bfcbf249b50e388282ea6d31
4
- data.tar.gz: 3c6f258c34022ac7ee10b826b416dfd8b367de2aa84e5f826b2395ec4e43f0c6
3
+ metadata.gz: 48b9300fb2d1eaaff0b5f8f52a7e16483e1b528e31eaa142f6ce6715cb49b29e
4
+ data.tar.gz: 4c67d04f2e0e04e723f7af6314ced1e3edbd57dd6b41d4eceb579db5f5ec3d8c
5
5
  SHA512:
6
- metadata.gz: 6309fcbec95c89a45cf364037df4f6efb9596e7e13e0068afb9ef98f30773e8c0ce4429c427ab197005a17e1879e0a2a32e2ccf4bd2087d664073500bc8be8d4
7
- data.tar.gz: 983933113b7ee85e01e8d582d5bff735c0f27781e7005b65bc03537efd8d6e83f17f63401c36efd6c50af037ae5cab8fc0e90a4e95f31a75ad42efac753815d2
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.5.0"
2
+ VERSION = "6.6.0"
3
3
  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.5.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-17 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.10
171
+ rubygems_version: 3.4.13
172
172
  signing_key:
173
173
  specification_version: 4
174
174
  summary: Payments engine for Ruby on Rails