pay 6.2.3 → 6.3.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.
- checksums.yaml +4 -4
- data/app/mailers/pay/user_mailer.rb +4 -0
- data/app/models/pay/charge.rb +1 -1
- data/app/models/pay/customer.rb +5 -4
- data/app/models/pay/subscription.rb +5 -0
- data/app/views/pay/user_mailer/payment_action_required.html.erb +2 -3
- data/app/views/pay/user_mailer/payment_failed.html.erb +5 -0
- data/app/views/pay/user_mailer/subscription_renewing.html.erb +1 -3
- data/app/views/pay/user_mailer/subscription_trial_ended.html.erb +0 -2
- data/app/views/pay/user_mailer/subscription_trial_will_end.html.erb +0 -2
- data/config/locales/en.yml +2 -0
- data/lib/pay/braintree/billable.rb +7 -2
- data/lib/pay/braintree/subscription.rb +3 -0
- data/lib/pay/stripe/subscription.rb +15 -2
- data/lib/pay/stripe/webhooks/payment_failed.rb +24 -0
- data/lib/pay/stripe.rb +4 -0
- data/lib/pay/version.rb +1 -1
- data/lib/pay.rb +1 -0
- metadata +7 -59
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aeee41b07bfa798c33c4d05b73c7035905f562d45cec48564d543e371ff53a7e
|
4
|
+
data.tar.gz: 7b7b1695db44d1f3e00637431f91be10e12c54541921f4b555d83253b981dc04
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ee0f52b211a5894d00d7226d486ccdbce5ca8f18a66a1fa1fcd7c37344bd842f0672cf4c4070d7ee12684b8c64801fcd8c8674fa22d17f7204e95609f3b5cfa0
|
7
|
+
data.tar.gz: 4dcf8f4142c67ae179749e955baff64b454c2fc2eba3664727fd41572d22b0e6c1913cac9d5bc2c063e4c484ade0663b401b6d44d283a203c65145f678d28e77
|
data/app/models/pay/charge.rb
CHANGED
@@ -50,7 +50,7 @@ module Pay
|
|
50
50
|
customer.processor == processor_name
|
51
51
|
end
|
52
52
|
|
53
|
-
scope processor_name, -> { where(processor: processor_name) }
|
53
|
+
scope processor_name, -> { joins(:customer).where(pay_customers: {processor: processor_name}) }
|
54
54
|
end
|
55
55
|
|
56
56
|
delegate :capture, :credit_note!, :credit_notes, to: :payment_processor
|
data/app/models/pay/customer.rb
CHANGED
@@ -46,7 +46,7 @@ module Pay
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def subscription(name: Pay.default_product_name)
|
49
|
-
subscriptions.order(
|
49
|
+
subscriptions.order(created_at: :desc).for_name(name).first
|
50
50
|
end
|
51
51
|
|
52
52
|
def subscribed?(name: Pay.default_product_name, processor_plan: nil)
|
@@ -93,9 +93,10 @@ module Pay
|
|
93
93
|
subscription.trial_ends_at == subscription.ends_at
|
94
94
|
end
|
95
95
|
|
96
|
-
# Attempts to pay all past_due
|
97
|
-
|
98
|
-
|
96
|
+
# Attempts to pay all past_due subscription invoices to bring them back to active state
|
97
|
+
# Pass in `statuses: []` if you would like to only include specific subscription statuses
|
98
|
+
def retry_past_due_subscriptions!(status: [:past_due])
|
99
|
+
subscriptions.where(status: Array.wrap(status)).each(&:retry_failed_payments)
|
99
100
|
end
|
100
101
|
end
|
101
102
|
end
|
@@ -17,6 +17,7 @@ module Pay
|
|
17
17
|
scope :active_or_paused, -> { active.or(paused) }
|
18
18
|
scope :incomplete, -> { where(status: :incomplete) }
|
19
19
|
scope :past_due, -> { where(status: :past_due) }
|
20
|
+
scope :unpaid, -> { where(status: :unpaid) }
|
20
21
|
scope :metered, -> { where(metered: true) }
|
21
22
|
scope :with_active_customer, -> { joins(:customer).merge(Customer.active) }
|
22
23
|
scope :with_deleted_customer, -> { joins(:customer).merge(Customer.deleted) }
|
@@ -131,6 +132,10 @@ module Pay
|
|
131
132
|
status == "past_due"
|
132
133
|
end
|
133
134
|
|
135
|
+
def unpaid?
|
136
|
+
status == "unpaid"
|
137
|
+
end
|
138
|
+
|
134
139
|
def incomplete?
|
135
140
|
status == "incomplete"
|
136
141
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
<h3>Extra confirmation is needed to process your payment</h3>
|
2
2
|
<p>Your <%= Pay.application_name %> subscription requires confirmation to process your payment to continue access.</p>
|
3
|
-
|
4
|
-
<p
|
5
|
-
|
3
|
+
<p><%= link_to "Confirm your payment", pay.payment_url(params[:payment_intent_id]) %></p>
|
4
|
+
<p>If you have any questions, please hit reply and let us know.</p>
|
6
5
|
<p>— The <%= Pay.application_name %> Team</p>
|
@@ -0,0 +1,5 @@
|
|
1
|
+
<h3>Your payment was declined</h3>
|
2
|
+
<p>We were unable to charge your payment method for your <%= Pay.application_name %> subscription. Please update your billing information.</p>
|
3
|
+
<p><%= link_to "Update billing information", root_url %></p>
|
4
|
+
<p>Let us know if you have any questions.</p>
|
5
|
+
<p>— The <%= Pay.application_name %> Team</p>
|
@@ -1,6 +1,4 @@
|
|
1
1
|
<h3>Your upcoming <%= Pay.application_name %> subscription renewal</h3>
|
2
|
-
<p>This is
|
3
|
-
|
2
|
+
<p>This is a friendly reminder that your <%= link_to Pay.application_name, root_url %> subscription will renew automatically on <%= l params[:date].to_date, format: :long %>.</p>
|
4
3
|
<p>You may <%= link_to "manage your subscription", root_url %> via your account. If you have any questions, please hit reply and let us know.</p>
|
5
|
-
|
6
4
|
<p>— The <%= Pay.application_name %> Team</p>
|
@@ -1,6 +1,4 @@
|
|
1
1
|
<h3>Your <%= Pay.application_name %> trial has ended</h3>
|
2
2
|
<p>This is just a friendly reminder that your <%= link_to Pay.application_name, root_url %> trial has ended.</p>
|
3
|
-
|
4
3
|
<p>You may <%= link_to "manage your subscription", root_url %> via your account. If you have any questions, please hit reply and let us know.</p>
|
5
|
-
|
6
4
|
<p>— The <%= Pay.application_name %> Team</p>
|
@@ -1,6 +1,4 @@
|
|
1
1
|
<h3>Your <%= Pay.application_name %> trial is ending soon</h3>
|
2
2
|
<p>This is just a friendly reminder that your <%= link_to Pay.application_name, root_url %> trial will be ending soon.</p>
|
3
|
-
|
4
3
|
<p>You may <%= link_to "manage your subscription", root_url %> via your account. If you have any questions, please hit reply and let us know.</p>
|
5
|
-
|
6
4
|
<p>— The <%= Pay.application_name %> Team</p>
|
data/config/locales/en.yml
CHANGED
@@ -56,6 +56,8 @@ en:
|
|
56
56
|
subject: "[%{application}] Your upcoming subscription renewal"
|
57
57
|
payment_action_required:
|
58
58
|
subject: "[%{application}] Confirm your payment"
|
59
|
+
payment_failed:
|
60
|
+
subject: "[%{application}] Action Required – Your payment failed"
|
59
61
|
subscription_trial_will_end:
|
60
62
|
subject: "[%{application}] Your trial is ending soon"
|
61
63
|
subscription_trial_ended:
|
@@ -174,12 +174,17 @@ module Pay
|
|
174
174
|
attrs[:metadata] = transaction.custom_fields
|
175
175
|
attrs[:currency] = transaction.currency_iso_code
|
176
176
|
attrs[:application_fee_amount] = transaction.service_fee_amount
|
177
|
+
attrs[:created_at] = transaction.created_at
|
177
178
|
|
178
179
|
# Associate charge with subscription if we can
|
179
180
|
if transaction.subscription_id
|
180
181
|
pay_subscription = pay_customer.subscriptions.find_by(processor_id: transaction.subscription_id)
|
181
|
-
|
182
|
-
|
182
|
+
pay_subscription ||= Pay::Braintree::Subscription.sync(transaction.subscription_id)
|
183
|
+
|
184
|
+
if pay_subscription
|
185
|
+
attrs[:subscription] = pay_subscription
|
186
|
+
attrs[:metadata] = pay_subscription.metadata
|
187
|
+
end
|
183
188
|
end
|
184
189
|
|
185
190
|
charge = pay_customer.charges.find_or_initialize_by(processor_id: transaction.id)
|
@@ -28,6 +28,9 @@ module Pay
|
|
28
28
|
pay_customer = Pay::Customer.find_by(processor: :braintree, processor_id: payment_method.customer_id)
|
29
29
|
return unless pay_customer
|
30
30
|
|
31
|
+
# Sync the PaymentMethod since we've got it
|
32
|
+
pay_customer.save_payment_method(payment_method, default: payment_method.default?)
|
33
|
+
|
31
34
|
attributes = {
|
32
35
|
created_at: object.created_at,
|
33
36
|
current_period_end: object.billing_period_end_date,
|
@@ -301,13 +301,26 @@ module Pay
|
|
301
301
|
end
|
302
302
|
|
303
303
|
# Retries the latest invoice for a Past Due subscription and attempts to pay it
|
304
|
-
def retry_failed_payment
|
305
|
-
|
304
|
+
def retry_failed_payment(payment_intent_id: nil)
|
305
|
+
payment_intent_id ||= subscription.latest_invoice.payment_intent.id
|
306
|
+
payment_intent = ::Stripe::PaymentIntent.retrieve({id: payment_intent_id}, stripe_options)
|
307
|
+
|
308
|
+
payment_intent = if payment_intent.status == "requires_payment_method"
|
309
|
+
::Stripe::PaymentIntent.confirm(payment_intent_id, {payment_method: pay_subscription.customer.default_payment_method.processor_id}, stripe_options)
|
310
|
+
else
|
311
|
+
::Stripe::PaymentIntent.confirm(payment_intent_id, stripe_options)
|
312
|
+
end
|
306
313
|
Pay::Payment.new(payment_intent).validate
|
307
314
|
rescue ::Stripe::StripeError => e
|
308
315
|
raise Pay::Stripe::Error, e
|
309
316
|
end
|
310
317
|
|
318
|
+
def pay_open_invoices
|
319
|
+
::Stripe::Invoice.list({subscription: processor_id, status: :open}, stripe_options).auto_paging_each do |invoice|
|
320
|
+
retry_failed_payment(payment_intent_id: invoice.payment_intent)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
311
324
|
private
|
312
325
|
|
313
326
|
# Options for Stripe requests
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Pay
|
2
|
+
module Stripe
|
3
|
+
module Webhooks
|
4
|
+
class PaymentFailed
|
5
|
+
def call(event)
|
6
|
+
# Event is of type "invoice" see:
|
7
|
+
# https://stripe.com/docs/api/invoices/object
|
8
|
+
|
9
|
+
object = event.data.object
|
10
|
+
|
11
|
+
pay_subscription = Pay::Subscription.find_by_processor_and_id(:stripe, object.subscription)
|
12
|
+
return if pay_subscription.nil?
|
13
|
+
|
14
|
+
if Pay.send_email?(:payment_failed, pay_subscription)
|
15
|
+
Pay.mailer.with(
|
16
|
+
pay_customer: pay_subscription.customer,
|
17
|
+
stripe_invoice: object
|
18
|
+
).payment_failed.deliver_now
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/pay/stripe.rb
CHANGED
@@ -16,6 +16,7 @@ module Pay
|
|
16
16
|
autoload :CustomerDeleted, "pay/stripe/webhooks/customer_deleted"
|
17
17
|
autoload :CustomerUpdated, "pay/stripe/webhooks/customer_updated"
|
18
18
|
autoload :PaymentActionRequired, "pay/stripe/webhooks/payment_action_required"
|
19
|
+
autoload :PaymentFailed, "pay/stripe/webhooks/payment_failed"
|
19
20
|
autoload :PaymentIntentSucceeded, "pay/stripe/webhooks/payment_intent_succeeded"
|
20
21
|
autoload :PaymentMethodAttached, "pay/stripe/webhooks/payment_method_attached"
|
21
22
|
autoload :PaymentMethodDetached, "pay/stripe/webhooks/payment_method_detached"
|
@@ -78,6 +79,9 @@ module Pay
|
|
78
79
|
# Payment action is required to process an invoice
|
79
80
|
events.subscribe "stripe.invoice.payment_action_required", Pay::Stripe::Webhooks::PaymentActionRequired.new
|
80
81
|
|
82
|
+
# If an invoice payment fails, we want to notify the user via email to update their payment details
|
83
|
+
events.subscribe "stripe.invoice.payment_failed", Pay::Stripe::Webhooks::PaymentFailed.new
|
84
|
+
|
81
85
|
# If a subscription is manually created on Stripe, we want to sync
|
82
86
|
events.subscribe "stripe.customer.subscription.created", Pay::Stripe::Webhooks::SubscriptionCreated.new
|
83
87
|
|
data/lib/pay/version.rb
CHANGED
data/lib/pay.rb
CHANGED
@@ -59,6 +59,7 @@ module Pay
|
|
59
59
|
mattr_accessor :emails
|
60
60
|
@@emails = ActiveSupport::OrderedOptions.new
|
61
61
|
@@emails.payment_action_required = true
|
62
|
+
@@emails.payment_failed = true
|
62
63
|
@@emails.receipt = true
|
63
64
|
@@emails.refund = true
|
64
65
|
# This only applies to Stripe, therefor we supply the second argument of price
|
metadata
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pay
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 6.
|
4
|
+
version: 6.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Charnes
|
8
8
|
- Chris Oliver
|
9
|
+
- Collin Jilbert
|
9
10
|
autorequire:
|
10
11
|
bindir: bin
|
11
12
|
cert_chain: []
|
12
|
-
date: 2023-01-
|
13
|
+
date: 2023-01-13 00:00:00.000000000 Z
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
16
|
name: rails
|
@@ -25,66 +26,11 @@ dependencies:
|
|
25
26
|
- - ">="
|
26
27
|
- !ruby/object:Gem::Version
|
27
28
|
version: 6.0.0
|
28
|
-
- !ruby/object:Gem::Dependency
|
29
|
-
name: mocha
|
30
|
-
requirement: !ruby/object:Gem::Requirement
|
31
|
-
requirements:
|
32
|
-
- - ">="
|
33
|
-
- !ruby/object:Gem::Version
|
34
|
-
version: '0'
|
35
|
-
type: :development
|
36
|
-
prerelease: false
|
37
|
-
version_requirements: !ruby/object:Gem::Requirement
|
38
|
-
requirements:
|
39
|
-
- - ">="
|
40
|
-
- !ruby/object:Gem::Version
|
41
|
-
version: '0'
|
42
|
-
- !ruby/object:Gem::Dependency
|
43
|
-
name: standardrb
|
44
|
-
requirement: !ruby/object:Gem::Requirement
|
45
|
-
requirements:
|
46
|
-
- - ">="
|
47
|
-
- !ruby/object:Gem::Version
|
48
|
-
version: '0'
|
49
|
-
type: :development
|
50
|
-
prerelease: false
|
51
|
-
version_requirements: !ruby/object:Gem::Requirement
|
52
|
-
requirements:
|
53
|
-
- - ">="
|
54
|
-
- !ruby/object:Gem::Version
|
55
|
-
version: '0'
|
56
|
-
- !ruby/object:Gem::Dependency
|
57
|
-
name: vcr
|
58
|
-
requirement: !ruby/object:Gem::Requirement
|
59
|
-
requirements:
|
60
|
-
- - ">="
|
61
|
-
- !ruby/object:Gem::Version
|
62
|
-
version: '0'
|
63
|
-
type: :development
|
64
|
-
prerelease: false
|
65
|
-
version_requirements: !ruby/object:Gem::Requirement
|
66
|
-
requirements:
|
67
|
-
- - ">="
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
-
version: '0'
|
70
|
-
- !ruby/object:Gem::Dependency
|
71
|
-
name: webmock
|
72
|
-
requirement: !ruby/object:Gem::Requirement
|
73
|
-
requirements:
|
74
|
-
- - ">="
|
75
|
-
- !ruby/object:Gem::Version
|
76
|
-
version: '0'
|
77
|
-
type: :development
|
78
|
-
prerelease: false
|
79
|
-
version_requirements: !ruby/object:Gem::Requirement
|
80
|
-
requirements:
|
81
|
-
- - ">="
|
82
|
-
- !ruby/object:Gem::Version
|
83
|
-
version: '0'
|
84
29
|
description: Stripe, Paddle, and Braintree payments for Ruby on Rails apps
|
85
30
|
email:
|
86
31
|
- jason@thecharnes.com
|
87
32
|
- excid3@gmail.com
|
33
|
+
- cjilbert504@gmail.com
|
88
34
|
executables: []
|
89
35
|
extensions: []
|
90
36
|
extra_rdoc_files: []
|
@@ -116,6 +62,7 @@ files:
|
|
116
62
|
- app/views/pay/payments/show.html.erb
|
117
63
|
- app/views/pay/stripe/_checkout_button.html.erb
|
118
64
|
- app/views/pay/user_mailer/payment_action_required.html.erb
|
65
|
+
- app/views/pay/user_mailer/payment_failed.html.erb
|
119
66
|
- app/views/pay/user_mailer/receipt.html.erb
|
120
67
|
- app/views/pay/user_mailer/refund.html.erb
|
121
68
|
- app/views/pay/user_mailer/subscription_renewing.html.erb
|
@@ -187,6 +134,7 @@ files:
|
|
187
134
|
- lib/pay/stripe/webhooks/customer_deleted.rb
|
188
135
|
- lib/pay/stripe/webhooks/customer_updated.rb
|
189
136
|
- lib/pay/stripe/webhooks/payment_action_required.rb
|
137
|
+
- lib/pay/stripe/webhooks/payment_failed.rb
|
190
138
|
- lib/pay/stripe/webhooks/payment_intent_succeeded.rb
|
191
139
|
- lib/pay/stripe/webhooks/payment_method_attached.rb
|
192
140
|
- lib/pay/stripe/webhooks/payment_method_detached.rb
|
@@ -220,7 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
220
168
|
- !ruby/object:Gem::Version
|
221
169
|
version: '0'
|
222
170
|
requirements: []
|
223
|
-
rubygems_version: 3.
|
171
|
+
rubygems_version: 3.4.1
|
224
172
|
signing_key:
|
225
173
|
specification_version: 4
|
226
174
|
summary: Payments engine for Ruby on Rails
|