pay 1.0.2 → 2.0.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/README.md +99 -31
- data/Rakefile +19 -19
- data/app/controllers/pay/payments_controller.rb +7 -0
- data/app/controllers/pay/webhooks/braintree_controller.rb +19 -6
- data/app/mailers/pay/application_mailer.rb +2 -2
- data/app/mailers/pay/user_mailer.rb +12 -0
- data/app/models/pay/subscription.rb +41 -10
- data/app/views/layouts/pay/application.html.erb +11 -5
- data/app/views/pay/payments/show.html.erb +134 -0
- data/app/views/pay/user_mailer/payment_action_required.html.erb +6 -0
- data/config/locales/en.yml +17 -0
- data/config/routes.rb +3 -2
- data/db/migrate/20190816015720_add_status_to_subscriptions.rb +14 -0
- data/lib/generators/pay/email_views_generator.rb +3 -3
- data/lib/generators/pay/views_generator.rb +13 -0
- data/lib/pay/billable/sync_email.rb +3 -4
- data/lib/pay/billable.rb +25 -18
- data/lib/pay/braintree/billable.rb +88 -82
- data/lib/pay/braintree/charge.rb +0 -2
- data/lib/pay/braintree/subscription.rb +74 -72
- data/lib/pay/braintree.rb +6 -6
- data/lib/pay/engine.rb +10 -10
- data/lib/pay/env.rb +0 -1
- data/lib/pay/payment.rb +52 -0
- data/lib/pay/receipts.rb +7 -7
- data/lib/pay/stripe/billable.rb +68 -37
- data/lib/pay/stripe/charge.rb +0 -2
- data/lib/pay/stripe/subscription.rb +7 -6
- data/lib/pay/stripe/webhooks/charge_refunded.rb +0 -2
- data/lib/pay/stripe/webhooks/charge_succeeded.rb +9 -10
- data/lib/pay/stripe/webhooks/customer_deleted.rb +5 -7
- data/lib/pay/stripe/webhooks/customer_updated.rb +0 -2
- data/lib/pay/stripe/webhooks/payment_action_required.rb +27 -0
- data/lib/pay/stripe/webhooks/{source_deleted.rb → payment_method_updated.rb} +1 -3
- data/lib/pay/stripe/webhooks/subscription_created.rb +6 -11
- data/lib/pay/stripe/webhooks/subscription_deleted.rb +1 -3
- data/lib/pay/stripe/webhooks/subscription_renewing.rb +0 -2
- data/lib/pay/stripe/webhooks/subscription_updated.rb +13 -10
- data/lib/pay/stripe/webhooks.rb +17 -11
- data/lib/pay/stripe.rb +5 -5
- data/lib/pay/version.rb +1 -1
- data/lib/pay.rb +42 -15
- metadata +27 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9665c51290c7d5fddc48d2c7a719f936907eb3a4c818c1bd886ddd0a01b3ac8f
|
4
|
+
data.tar.gz: 845efceadcd8e44a93228d30b82ec438e9d58842f683c16b97ebd44aef7b53bf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ff36eeb6ea85bb3d7d057b75534d3ce02fd0b193e3c6356d52d03c17f8108add95b961299849511a1b744df86d8c38f16fb47650a769721531f722b01682b823
|
7
|
+
data.tar.gz: 1cab7944c0c018079b5acb4f08e13c5ddaee3881a977a618f1e84730fc6bb24040b8f60ff2da924fd8957dea81ebc5208b8b0ef3cda7c9aa84c4a2819804b5be
|
data/README.md
CHANGED
@@ -4,27 +4,27 @@
|
|
4
4
|
|
5
5
|
### Payments engine for Ruby on Rails
|
6
6
|
|
7
|
-
[![Build Status](https://
|
7
|
+
[![Build Status](https://github.com/excid3/pay/workflows/Tests/badge.svg)](https://github.com/excid3/pay/actions)
|
8
8
|
|
9
9
|
Pay is a payments engine for Ruby on Rails 4.2 and higher.
|
10
10
|
|
11
11
|
**Current Payment Providers**
|
12
12
|
|
13
|
-
- Stripe (API version [
|
13
|
+
- Stripe ([supports SCA](https://stripe.com/docs/strong-customer-authentication), API version [2019-03-14](https://stripe.com/docs/upgrades#2019-03-14) or higher required)
|
14
14
|
- Braintree
|
15
15
|
|
16
16
|
Want to add a new payment provider? Contributions are welcome and the instructions [are here](https://github.com/jasoncharnes/pay/wiki/New-Payment-Provider).
|
17
17
|
|
18
18
|
## Installation
|
19
19
|
|
20
|
-
Add
|
20
|
+
Add these lines to your application's Gemfile:
|
21
21
|
|
22
22
|
```ruby
|
23
23
|
gem 'pay'
|
24
24
|
|
25
25
|
# To use Stripe, also include:
|
26
|
-
gem 'stripe', '<
|
27
|
-
gem 'stripe_event', '~> 2.
|
26
|
+
gem 'stripe', '< 6.0', '>= 2.8'
|
27
|
+
gem 'stripe_event', '~> 2.3'
|
28
28
|
|
29
29
|
# To use Braintree + PayPal, also include:
|
30
30
|
gem 'braintree', '< 3.0', '>= 2.92.0'
|
@@ -36,18 +36,18 @@ gem 'receipts', '~> 0.2.2'
|
|
36
36
|
And then execute:
|
37
37
|
|
38
38
|
```bash
|
39
|
-
|
39
|
+
bundle
|
40
40
|
```
|
41
41
|
|
42
42
|
Or install it yourself as:
|
43
43
|
|
44
44
|
```bash
|
45
|
-
|
45
|
+
gem install pay
|
46
46
|
```
|
47
47
|
|
48
48
|
## Setup
|
49
49
|
|
50
|
-
|
50
|
+
### Migrations
|
51
51
|
|
52
52
|
This engine will create a subscription model and the neccessary migrations for the model you want to make "billable." The most common use case for the billable model is a User.
|
53
53
|
|
@@ -55,32 +55,51 @@ To add the migrations to your application, run the following migration:
|
|
55
55
|
|
56
56
|
`$ bin/rails pay:install:migrations`
|
57
57
|
|
58
|
-
This will install
|
58
|
+
This will install four migrations:
|
59
59
|
|
60
60
|
- db/migrate/create_subscriptions.pay.rb
|
61
61
|
- db/migrate/add_fields_to_users.pay.rb
|
62
62
|
- db/migrate/create_charges.pay.rb
|
63
|
+
- db/migrate/add_status_to_subscriptions.pay.rb
|
63
64
|
|
64
|
-
|
65
|
+
### The Billable Module
|
65
66
|
|
66
|
-
|
67
|
+
To enable payments for a model, you simply include the `Pay::Billable`
|
68
|
+
module in it. By default, we assume this is `User`.
|
67
69
|
|
68
|
-
If you
|
70
|
+
If you'd like to use a different model, you can configure it in an
|
71
|
+
initializer:
|
69
72
|
|
70
|
-
|
73
|
+
```ruby
|
74
|
+
Pay.setup do |config|
|
75
|
+
# Make the billable class the same name as your ActiveRecord model
|
76
|
+
config.billable_class = "Team"
|
71
77
|
|
72
|
-
|
78
|
+
# Make the billable table the same name as your ActiveRecord table name for the model
|
79
|
+
# This is optional.
|
80
|
+
# Once you update the billable class, the table name will use the ActiveRecord inflected table name
|
81
|
+
config.billable_table = "teams"
|
82
|
+
end
|
83
|
+
```
|
73
84
|
|
74
85
|
#### Run the Migrations
|
75
86
|
|
76
87
|
Finally, run the migrations with `$ rake db:migrate`
|
77
88
|
|
78
|
-
####
|
89
|
+
#### Getting NoMethodError?
|
79
90
|
|
80
91
|
`NoMethodError (undefined method 'stripe_customer' for #<User:0x00007fbc34b9bf20>)`
|
81
92
|
|
82
93
|
Fully restart your Rails application `bin/spring stop && rails s`
|
83
94
|
|
95
|
+
## Payment Providers
|
96
|
+
|
97
|
+
We support both Stripe and Braintree and make our best attempt to
|
98
|
+
standardize the two. They function differently so keep that in mind if
|
99
|
+
you plan on doing more complex payments. It would be best to stick with
|
100
|
+
a single payment provider in that case so you don't run into
|
101
|
+
discrepancies.
|
102
|
+
|
84
103
|
#### Stripe
|
85
104
|
|
86
105
|
You'll need to add your private Stripe API key to your Rails secrets `config/secrets.yml`, credentials `rails credentials:edit`
|
@@ -97,6 +116,30 @@ You can also use the `STRIPE_PRIVATE_KEY` and `STRIPE_SIGNING_SECRET` environmen
|
|
97
116
|
|
98
117
|
**To see how to use Stripe Elements JS & Devise, [click here](https://github.com/jasoncharnes/pay/wiki/Using-Stripe-Elements-and-Devise).**
|
99
118
|
|
119
|
+
##### Strong Customer Authentication (SCA)
|
120
|
+
|
121
|
+
Our Stripe integration **requires** the use of Payment Method objects to correctly support Strong Customer Authentication with Stripe. If you've previously been using card tokens, you'll need to upgrade your Javascript integration.
|
122
|
+
|
123
|
+
Subscriptions that require SCA are marked as `incomplete` by default.
|
124
|
+
Once payment is authenticated, Stripe will send a webhook updating the
|
125
|
+
status of the subscription. You'll need to use the [Stripe CLI](https://github.com/stripe/stripe-cli) to forward
|
126
|
+
webhooks to your application to make sure your subscriptions work
|
127
|
+
correctly for SCA payments.
|
128
|
+
|
129
|
+
```bash
|
130
|
+
stripe listen --forward-to localhost:3000/pay/webhooks/stripe
|
131
|
+
```
|
132
|
+
|
133
|
+
You should use `stripe.handleCardSetup` on the client to collect card information anytime you want to save the card and charge them later (adding a card, then charging them on the next page for example). Use `stripe.handleCardPayment` if you'd like to charge the customer immediately (think checking out of a shopping cart).
|
134
|
+
|
135
|
+
**Payment Confirmations**
|
136
|
+
|
137
|
+
Sometimes you'll have a payment that requires extra authentication. In this case, Pay provides a webhook and action for handling these payments. It will automatically email the customer and provide a link with the PaymentIntent ID in the url where the customer will be asked to fill out their name and card number to confirm the payment. Once done, they'll be redirected back to your application.
|
138
|
+
|
139
|
+
If you'd like to change the views of the payment confirmation page, you can install the views using the generator and modify the template.
|
140
|
+
|
141
|
+
[<img src="https://d1jfzjx68gj8xs.cloudfront.net/items/2s3Z0J3Z3b1J1v2K2O1a/Screen%20Shot%202019-10-10%20at%2012.56.32%20PM.png?X-CloudApp-Visitor-Id=51470" alt="Stripe SCA Payment Confirmation" style="zoom: 25%;" />](https://d1jfzjx68gj8xs.cloudfront.net/items/2s3Z0J3Z3b1J1v2K2O1a/Screen%20Shot%202019-10-10%20at%2012.56.32%20PM.png)
|
142
|
+
|
100
143
|
#### Background jobs
|
101
144
|
|
102
145
|
If a user's email is updated and they have a `processor_id` set, Pay will enqueue a background job (EmailSyncJob) to sync the email with the payment processor.
|
@@ -137,7 +180,7 @@ Pay.setup do |config|
|
|
137
180
|
config.send_emails = true
|
138
181
|
|
139
182
|
config.automount_webhook_routes = true
|
140
|
-
config.
|
183
|
+
config.routes_path = "/pay" # Only when automount_webhook_routes is true
|
141
184
|
end
|
142
185
|
```
|
143
186
|
|
@@ -155,19 +198,38 @@ Pay.setup do |config|
|
|
155
198
|
end
|
156
199
|
```
|
157
200
|
|
201
|
+
### Credentials
|
202
|
+
|
203
|
+
You'll need to add your private Stripe API key to your Rails secrets `config/secrets.yml`, credentials `rails credentials:edit`
|
204
|
+
|
205
|
+
```yaml
|
206
|
+
development:
|
207
|
+
stripe:
|
208
|
+
private_key: xxxx
|
209
|
+
public_key: yyyy
|
210
|
+
signing_secret: zzzz
|
211
|
+
braintree:
|
212
|
+
private_key: xxxx
|
213
|
+
public_key: yyyy
|
214
|
+
```
|
215
|
+
|
216
|
+
You can also use the `STRIPE_PUBLIC_KEY`, `STRIPE_PRIVATE_KEY` and `STRIPE_SIGNING_SECRET` environment variables.
|
217
|
+
|
158
218
|
### Generators
|
159
219
|
|
160
|
-
|
220
|
+
If you want to modify the Stripe SCA template or any other views, you can copy over the view files using:
|
221
|
+
|
222
|
+
```bash
|
223
|
+
bin/rails generate pay:views
|
224
|
+
```
|
161
225
|
|
162
226
|
If you want to modify the email templates, you can copy over the view files using:
|
163
227
|
|
228
|
+
```bash
|
229
|
+
bin/rails generate pay:email_views
|
164
230
|
```
|
165
|
-
$ bin/rails generate pay:email_views
|
166
|
-
```
|
167
|
-
|
168
|
-
## Emails
|
169
231
|
|
170
|
-
###
|
232
|
+
### Emails
|
171
233
|
|
172
234
|
Emails can be enabled/disabled using the `send_emails` configuration option (enabled per default). When enabled, the following emails will be sent:
|
173
235
|
|
@@ -175,7 +237,7 @@ Emails can be enabled/disabled using the `send_emails` configuration option (ena
|
|
175
237
|
- When a charge was refunded
|
176
238
|
- When a subscription is about to renew
|
177
239
|
|
178
|
-
##
|
240
|
+
## Billable API
|
179
241
|
|
180
242
|
#### Trials
|
181
243
|
|
@@ -214,7 +276,7 @@ user.on_generic_trial? #=> true
|
|
214
276
|
user = User.find_by(email: 'michael@bluthcompany.co')
|
215
277
|
|
216
278
|
user.processor = 'stripe'
|
217
|
-
user.card_token = '
|
279
|
+
user.card_token = 'payment_method_id'
|
218
280
|
user.charge(1500) # $15.00 USD
|
219
281
|
|
220
282
|
user = User.find_by(email: 'michael@bluthcompany.co')
|
@@ -236,7 +298,7 @@ different currencies, etc.
|
|
236
298
|
user = User.find_by(email: 'michael@bluthcompany.co')
|
237
299
|
|
238
300
|
user.processor = 'stripe'
|
239
|
-
user.card_token = '
|
301
|
+
user.card_token = 'payment_method_id'
|
240
302
|
user.subscribe
|
241
303
|
```
|
242
304
|
|
@@ -260,7 +322,9 @@ Plan is the plan ID from the payment processor.
|
|
260
322
|
|
261
323
|
##### Options
|
262
324
|
|
263
|
-
|
325
|
+
By default, the trial specified on the subscription will be used.
|
326
|
+
|
327
|
+
`trial_period_days: 30` can be set to override and a trial to the subscription. This works the same for Braintree and Stripe.
|
264
328
|
|
265
329
|
#### Retrieving a Subscription from the Database
|
266
330
|
|
@@ -329,7 +393,7 @@ user.customer #> Stripe or Braintree customer account
|
|
329
393
|
```ruby
|
330
394
|
user = User.find_by(email: 'tobias@bluthcompany.co')
|
331
395
|
|
332
|
-
user.update_card('
|
396
|
+
user.update_card('payment_method_id')
|
333
397
|
```
|
334
398
|
|
335
399
|
#### Retrieving a Customer's Subscription from the Processor
|
@@ -445,9 +509,13 @@ Rails.application.config.to_prepare do
|
|
445
509
|
end
|
446
510
|
```
|
447
511
|
|
448
|
-
## Webhooks
|
512
|
+
## Routes & Webhooks
|
513
|
+
|
514
|
+
Routes are automatically mounted to `/pay` by default.
|
515
|
+
|
516
|
+
We provide a route for confirming SCA payments at `/pay/payments/:payment_intent_id`
|
449
517
|
|
450
|
-
Webhooks are automatically mounted
|
518
|
+
Webhooks are automatically mounted at `/pay/webhooks/{provider}`
|
451
519
|
|
452
520
|
#### Customizing webhook mount path
|
453
521
|
|
@@ -455,7 +523,7 @@ If you have a catch all route (for 404s etc) and need to control where/when the
|
|
455
523
|
|
456
524
|
```ruby
|
457
525
|
# config/initializers/pay.rb
|
458
|
-
config.
|
526
|
+
config.automount_routes = false
|
459
527
|
|
460
528
|
# config/routes.rb
|
461
529
|
mount Pay::Engine, at: '/secret-webhook-path'
|
@@ -466,7 +534,7 @@ If you just want to modify where the engine mounts it's routes then you can chan
|
|
466
534
|
```ruby
|
467
535
|
# config/initializers/pay.rb
|
468
536
|
|
469
|
-
config.
|
537
|
+
config.routes_path = '/secret-webhook-path'
|
470
538
|
```
|
471
539
|
|
472
540
|
## Contributors
|
data/Rakefile
CHANGED
@@ -1,40 +1,40 @@
|
|
1
1
|
begin
|
2
|
-
require
|
2
|
+
require "bundler/setup"
|
3
3
|
rescue LoadError
|
4
|
-
puts
|
4
|
+
puts "You must `gem install bundler` and `bundle install` to run rake tasks"
|
5
5
|
end
|
6
6
|
|
7
|
-
require
|
7
|
+
require "rdoc/task"
|
8
8
|
|
9
9
|
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
-
rdoc.rdoc_dir =
|
11
|
-
rdoc.title
|
12
|
-
rdoc.options <<
|
13
|
-
rdoc.rdoc_files.include(
|
14
|
-
rdoc.rdoc_files.include(
|
10
|
+
rdoc.rdoc_dir = "rdoc"
|
11
|
+
rdoc.title = "Pay"
|
12
|
+
rdoc.options << "--line-numbers"
|
13
|
+
rdoc.rdoc_files.include("README.md")
|
14
|
+
rdoc.rdoc_files.include("lib/**/*.rb")
|
15
15
|
end
|
16
16
|
|
17
|
-
APP_RAKEFILE = File.expand_path(
|
17
|
+
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
18
18
|
|
19
|
-
load
|
20
|
-
load
|
19
|
+
load "rails/tasks/engine.rake"
|
20
|
+
load "rails/tasks/statistics.rake"
|
21
21
|
|
22
|
-
require
|
23
|
-
require
|
22
|
+
require "bundler/gem_tasks"
|
23
|
+
require "rake/testtask"
|
24
24
|
|
25
25
|
Rake::TestTask.new(:test) do |t|
|
26
|
-
t.libs <<
|
27
|
-
t.libs <<
|
28
|
-
t.pattern =
|
26
|
+
t.libs << "lib"
|
27
|
+
t.libs << "test"
|
28
|
+
t.pattern = "test/**/*_test.rb"
|
29
29
|
t.verbose = false
|
30
30
|
end
|
31
31
|
|
32
32
|
task default: :test
|
33
33
|
|
34
34
|
task :console do
|
35
|
-
require
|
36
|
-
require
|
37
|
-
require
|
35
|
+
require "irb"
|
36
|
+
require "irb/completion"
|
37
|
+
require "pay"
|
38
38
|
ARGV.clear
|
39
39
|
IRB.start
|
40
40
|
end
|
@@ -7,13 +7,15 @@ module Pay
|
|
7
7
|
|
8
8
|
def create
|
9
9
|
case webhook_notification.kind
|
10
|
-
when
|
10
|
+
when "subscription_charged_successfully"
|
11
11
|
subscription_charged_successfully(webhook_notification)
|
12
|
-
when
|
12
|
+
when "subscription_canceled"
|
13
13
|
subscription_canceled(webhook_notification)
|
14
|
+
when "subscription_trial_ended"
|
15
|
+
subscription_trial_ended(webhook_notification)
|
14
16
|
end
|
15
17
|
|
16
|
-
render json: {
|
18
|
+
render json: {success: true}, status: :ok
|
17
19
|
rescue ::Braintree::InvalidSignature => e
|
18
20
|
head :ok
|
19
21
|
end
|
@@ -24,9 +26,10 @@ module Pay
|
|
24
26
|
subscription = event.subscription
|
25
27
|
return if subscription.nil?
|
26
28
|
|
27
|
-
|
28
|
-
return unless
|
29
|
+
pay_subscription = Pay.subscription_model.find_by(processor: :braintree, processor_id: subscription.id)
|
30
|
+
return unless pay_subscription.present?
|
29
31
|
|
32
|
+
user = pay_subscription.owner
|
30
33
|
charge = user.save_braintree_transaction(subscription.transactions.first)
|
31
34
|
|
32
35
|
if Pay.send_emails
|
@@ -45,8 +48,18 @@ module Pay
|
|
45
48
|
user.update(braintree_subscription_id: nil)
|
46
49
|
end
|
47
50
|
|
51
|
+
def subscription_trial_ended(event)
|
52
|
+
subscription = event.subscription
|
53
|
+
return if subscription.nil?
|
54
|
+
|
55
|
+
pay_subscription = Pay.subscription_model.find_by(processor: :braintree, processor_id: subscription.id)
|
56
|
+
return unless pay_subscription.present?
|
57
|
+
|
58
|
+
pay_subscription.update(trial_ends_at: Time.zone.now)
|
59
|
+
end
|
60
|
+
|
48
61
|
def webhook_notification
|
49
|
-
@webhook_notification ||=
|
62
|
+
@webhook_notification ||= Pay.braintree_gateway.webhook_notification.parse(
|
50
63
|
params[:bt_signature],
|
51
64
|
params[:bt_payload]
|
52
65
|
)
|
@@ -23,12 +23,24 @@ module Pay
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def subscription_renewing(user, subscription)
|
26
|
+
@user, @subscription = user, subscription
|
27
|
+
|
26
28
|
mail(
|
27
29
|
to: to(user),
|
28
30
|
subject: Pay.email_renewing_subject,
|
29
31
|
)
|
30
32
|
end
|
31
33
|
|
34
|
+
def payment_action_required(user, payment_intent_id, subscription)
|
35
|
+
payment = Payment.from_id(payment_intent_id)
|
36
|
+
@user, @payment, @subscription = user, payment, subscription
|
37
|
+
|
38
|
+
mail(
|
39
|
+
to: to(user),
|
40
|
+
subject: Pay.payment_action_required_subject
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
32
44
|
private
|
33
45
|
|
34
46
|
def to(user)
|
@@ -2,6 +2,8 @@ module Pay
|
|
2
2
|
class Subscription < ApplicationRecord
|
3
3
|
self.table_name = Pay.subscription_table
|
4
4
|
|
5
|
+
STATUSES = %w[incomplete incomplete_expired trialing active past_due canceled unpaid]
|
6
|
+
|
5
7
|
# Associations
|
6
8
|
belongs_to :owner, class_name: Pay.billable_class, foreign_key: :owner_id
|
7
9
|
|
@@ -11,13 +13,16 @@ module Pay
|
|
11
13
|
validates :processor_id, presence: true
|
12
14
|
validates :processor_plan, presence: true
|
13
15
|
validates :quantity, presence: true
|
16
|
+
validates :status, presence: true
|
14
17
|
|
15
18
|
# Scopes
|
16
19
|
scope :for_name, ->(name) { where(name: name) }
|
17
|
-
scope :on_trial, ->{ where.not(trial_ends_at: nil).where("
|
18
|
-
scope :cancelled, ->{ where.not(ends_at: nil) }
|
19
|
-
scope :on_grace_period, ->{ cancelled.where("
|
20
|
-
scope :active, ->{ where(ends_at: nil).or(on_grace_period).or(on_trial) }
|
20
|
+
scope :on_trial, -> { where.not(trial_ends_at: nil).where("trial_ends_at > ?", Time.zone.now) }
|
21
|
+
scope :cancelled, -> { where.not(ends_at: nil) }
|
22
|
+
scope :on_grace_period, -> { cancelled.where("ends_at > ?", Time.zone.now) }
|
23
|
+
scope :active, -> { where(ends_at: nil).or(on_grace_period).or(on_trial) }
|
24
|
+
scope :incomplete, -> { where(status: :incomplete) }
|
25
|
+
scope :past_due, -> { where(status: :past_due) }
|
21
26
|
|
22
27
|
attribute :prorate, :boolean, default: true
|
23
28
|
|
@@ -33,16 +38,32 @@ module Pay
|
|
33
38
|
trial_ends_at? && Time.zone.now < trial_ends_at
|
34
39
|
end
|
35
40
|
|
36
|
-
def
|
41
|
+
def canceled?
|
37
42
|
ends_at?
|
38
43
|
end
|
39
44
|
|
45
|
+
def cancelled?
|
46
|
+
canceled?
|
47
|
+
end
|
48
|
+
|
40
49
|
def on_grace_period?
|
41
|
-
|
50
|
+
canceled? && Time.zone.now < ends_at
|
42
51
|
end
|
43
52
|
|
44
53
|
def active?
|
45
|
-
ends_at.nil? || on_grace_period? || on_trial?
|
54
|
+
["trialing", "active"].include?(status) && (ends_at.nil? || on_grace_period? || on_trial?)
|
55
|
+
end
|
56
|
+
|
57
|
+
def past_due?
|
58
|
+
status == "past_due"
|
59
|
+
end
|
60
|
+
|
61
|
+
def incomplete?
|
62
|
+
status == "incomplete"
|
63
|
+
end
|
64
|
+
|
65
|
+
def has_incomplete_payment?
|
66
|
+
past_due? || incomplete?
|
46
67
|
end
|
47
68
|
|
48
69
|
def cancel
|
@@ -56,7 +77,7 @@ module Pay
|
|
56
77
|
def resume
|
57
78
|
unless on_grace_period?
|
58
79
|
raise StandardError,
|
59
|
-
|
80
|
+
"You can only resume subscriptions within their grace period."
|
60
81
|
end
|
61
82
|
|
62
83
|
send("#{processor}_resume")
|
@@ -70,8 +91,18 @@ module Pay
|
|
70
91
|
update(processor_plan: plan, ends_at: nil)
|
71
92
|
end
|
72
93
|
|
73
|
-
def
|
74
|
-
|
94
|
+
def swap_and_invoice(plan)
|
95
|
+
swap(plan)
|
96
|
+
owner.invoice!(subscription_id: processor_id)
|
97
|
+
end
|
98
|
+
|
99
|
+
def processor_subscription(options = {})
|
100
|
+
owner.processor_subscription(processor_id, options)
|
101
|
+
end
|
102
|
+
|
103
|
+
def latest_payment
|
104
|
+
return unless stripe?
|
105
|
+
processor_subscription(expand: ["latest_invoice.payment_intent"]).latest_invoice.payment_intent
|
75
106
|
end
|
76
107
|
end
|
77
108
|
end
|
@@ -1,12 +1,18 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html>
|
3
|
-
<head>
|
4
|
-
<
|
5
|
-
|
6
|
-
<%=
|
3
|
+
<head class="h-full">
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
6
|
+
<title>Payment Confirmation - <%= Pay.business_name %></title>
|
7
|
+
<%#= stylesheet_link_tag "pay/application", media: "all" %>
|
8
|
+
<%#= javascript_include_tag "pay/application" %>
|
7
9
|
<%= csrf_meta_tags %>
|
10
|
+
|
11
|
+
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
|
12
|
+
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.min.js"></script>
|
13
|
+
<script src="https://js.stripe.com/v3"></script>
|
8
14
|
</head>
|
9
|
-
<body>
|
15
|
+
<body class="font-sans text-gray-600 bg-gray-200 leading-normal p-4 h-full">
|
10
16
|
|
11
17
|
<%= yield %>
|
12
18
|
|