pay 2.6.8 → 2.7.1
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 +73 -50
- data/app/models/pay.rb +5 -0
- data/app/models/pay/charge.rb +2 -0
- data/app/models/pay/subscription.rb +9 -2
- data/db/migrate/20200603134434_add_data_to_pay_models.rb +2 -18
- data/db/migrate/20210309004259_add_data_to_pay_billable.rb +10 -0
- data/db/migrate/20210406215234_add_currency_to_pay_charges.rb +5 -0
- data/db/migrate/20210406215506_add_application_fee_to_pay_models.rb +7 -0
- data/lib/generators/active_record/billable_generator.rb +44 -0
- data/lib/generators/active_record/merchant_generator.rb +44 -0
- data/lib/generators/active_record/templates/billable_migration.rb +16 -0
- data/lib/generators/active_record/templates/merchant_migration.rb +12 -0
- data/lib/generators/pay/{pay_generator.rb → billable_generator.rb} +2 -3
- data/lib/generators/pay/merchant_generator.rb +17 -0
- data/lib/generators/pay/orm_helpers.rb +10 -6
- data/lib/pay.rb +22 -0
- data/lib/pay/adapter.rb +22 -0
- data/lib/pay/billable.rb +4 -0
- data/lib/pay/braintree/billable.rb +11 -2
- data/lib/pay/braintree/subscription.rb +4 -0
- data/lib/pay/env.rb +8 -0
- data/lib/pay/fake_processor/subscription.rb +4 -0
- data/lib/pay/merchant.rb +37 -0
- data/lib/pay/paddle/subscription.rb +7 -0
- data/lib/pay/paddle/webhooks/subscription_payment_succeeded.rb +3 -1
- data/lib/pay/stripe.rb +11 -0
- data/lib/pay/stripe/billable.rb +38 -47
- data/lib/pay/stripe/charge.rb +38 -4
- data/lib/pay/stripe/merchant.rb +66 -0
- data/lib/pay/stripe/subscription.rb +64 -20
- data/lib/pay/stripe/webhooks/account_updated.rb +17 -0
- data/lib/pay/stripe/webhooks/charge_refunded.rb +2 -7
- data/lib/pay/stripe/webhooks/charge_succeeded.rb +2 -8
- data/lib/pay/stripe/webhooks/checkout_session_async_payment_succeeded.rb +13 -0
- data/lib/pay/stripe/webhooks/checkout_session_completed.rb +13 -0
- data/lib/pay/stripe/webhooks/payment_intent_succeeded.rb +2 -8
- data/lib/pay/stripe/webhooks/subscription_created.rb +1 -35
- data/lib/pay/stripe/webhooks/subscription_deleted.rb +1 -9
- data/lib/pay/stripe/webhooks/subscription_renewing.rb +4 -6
- data/lib/pay/stripe/webhooks/subscription_updated.rb +1 -28
- data/lib/pay/version.rb +1 -1
- metadata +19 -6
- data/lib/generators/active_record/pay_generator.rb +0 -58
- data/lib/generators/active_record/templates/migration.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a90781f9b476b2396d1ab4ea8d1a6317b46ac6289f5c4afef96dc991af035fd0
|
4
|
+
data.tar.gz: 8d20e7c6edcb13eb62d5ad398c4ab12a353e3be784c7651d795efd9feeb18402
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 11f281fe1df7c9eabfef2d4a0242257001115b3f7eaaa5571d73cbdacb1810891a0a57ab24a23caa01458de399093b3c82a3c251237e9d3d13f993e670f06b04
|
7
|
+
data.tar.gz: 11d760da77af12107686c4e91fcbec0f7bf4aa91e99d41281f1510bedabc00406146ade7656477ffa5ee4dda4e7b07af55a9dd0ae7675d1e780e8f4f121d6cc5
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
## Pay - Payments engine for Ruby on Rails
|
4
4
|
|
5
|
-
[![Build Status](https://github.com/pay-rails/pay/workflows/Tests/badge.svg)](https://github.com/pay-rails/pay/actions) [![Gem Version](https://badge.fury.io/rb/pay.svg)](https://badge.fury.io/rb/pay)
|
5
|
+
[![Build Status](https://github.com/pay-rails/pay/workflows/Tests/badge.svg)](https://github.com/pay-rails/pay/actions) [![Gem Version](https://badge.fury.io/rb/pay.svg)](https://badge.fury.io/rb/pay)
|
6
6
|
|
7
7
|
<img src="docs/images/stripe_partner_badge.svg" height="26px">
|
8
8
|
|
@@ -51,58 +51,49 @@ And then execute:
|
|
51
51
|
bundle
|
52
52
|
```
|
53
53
|
|
54
|
-
|
54
|
+
Next, we need to add migrations to your application, run the following migration:
|
55
55
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
```
|
60
|
-
|
61
|
-
#### Migrations
|
62
|
-
|
63
|
-
To add the migrations to your application, run the following migration:
|
56
|
+
````bash
|
57
|
+
bin/rails pay:install:migrations
|
58
|
+
````
|
64
59
|
|
65
|
-
`
|
60
|
+
>If your models rely on non integer ids (uuids for example) you will need to alter the `create_pay_subscriptions` and `create_pay_charges` migrations.
|
66
61
|
|
67
62
|
We also need to run migrations to add Pay to the User, Account, Team, etc models that we want to make payments in our app.
|
68
63
|
|
69
|
-
|
64
|
+
```bash
|
65
|
+
bin/rails g pay:billable User
|
66
|
+
```
|
70
67
|
|
71
68
|
This will generate a migration to add Pay fields to our User model and automatically includes the `Pay::Billable` module in our `User` model. Repeat this for all the models you want to make payments in your app.
|
72
69
|
|
73
|
-
|
70
|
+
**Note:** An `email` attribute or method on your `Billable` model is required.
|
74
71
|
|
75
|
-
`
|
72
|
+
To sync customer names, your `Billable` model should respond to the `first_name` and `last_name` methods. Pay will sync these over to your Customer objects in Stripe and Braintree.
|
76
73
|
|
77
|
-
|
78
|
-
|
79
|
-
`NoMethodError (undefined method 'stripe_customer' for #<User:0x00007fbc34b9bf20>)`
|
74
|
+
Finally, run the migrations
|
80
75
|
|
81
|
-
|
76
|
+
```bash
|
77
|
+
rake db:migrate
|
78
|
+
```
|
82
79
|
|
83
|
-
|
80
|
+
> If you run into `NoMethodError (undefined method 'stripe_customer' for #<User:0x00007fbc34b9bf20>)`, fully restart your Rails application `bin/spring stop && rails s`
|
84
81
|
|
85
|
-
|
82
|
+
Lastly, make sure you've configured your ActionMailer default_url_options so Pay can generate links to for features like Stripe Checkout.
|
86
83
|
|
87
84
|
```ruby
|
88
|
-
#
|
89
|
-
|
90
|
-
include Pay::Billable
|
91
|
-
end
|
85
|
+
# config/application.rb
|
86
|
+
config.action_mailer.default_url_options = { host: "example.com" }
|
92
87
|
```
|
93
88
|
|
94
|
-
An `email` attribute or method on your `Billable` model is required.
|
95
|
-
|
96
|
-
To sync over customer names, your `Billable` model should respond to the `first_name` and `last_name` methods. Pay will sync these over to your Customer objects in Stripe and Braintree.
|
97
|
-
|
98
89
|
## Configuration
|
99
90
|
|
100
91
|
Need to make some changes to how Pay is used? You can create an initializer `config/initializers/pay.rb`
|
101
92
|
|
102
93
|
```ruby
|
103
94
|
Pay.setup do |config|
|
104
|
-
config.chargeable_class = 'Pay::Charge'
|
105
|
-
config.chargeable_table = 'pay_charges'
|
95
|
+
# config.chargeable_class = 'Pay::Charge'
|
96
|
+
# config.chargeable_table = 'pay_charges'
|
106
97
|
|
107
98
|
# For use in the receipt/refund/renewal mailers
|
108
99
|
config.business_name = "Business Name"
|
@@ -136,29 +127,59 @@ end
|
|
136
127
|
|
137
128
|
### Credentials
|
138
129
|
|
139
|
-
|
130
|
+
Pay automatically looks up credentials for each payment provider. We recommend storing them in the Rails credentials.
|
131
|
+
|
132
|
+
##### Rails Credentials & Secrets
|
133
|
+
|
134
|
+
You'll need to add your API keys to your Rails credentials. You can do this by running:
|
135
|
+
|
136
|
+
```bash
|
137
|
+
rails credentials:edit --environment=development
|
138
|
+
```
|
139
|
+
|
140
|
+
They should be formatted like the following:
|
141
|
+
|
142
|
+
```yaml
|
143
|
+
stripe:
|
144
|
+
private_key: xxxx
|
145
|
+
public_key: yyyy
|
146
|
+
signing_secret: zzzz
|
147
|
+
braintree:
|
148
|
+
private_key: xxxx
|
149
|
+
public_key: yyyy
|
150
|
+
merchant_id: aaaa
|
151
|
+
environment: sandbox
|
152
|
+
paddle:
|
153
|
+
vendor_id: xxxx
|
154
|
+
vendor_auth_code: yyyy
|
155
|
+
public_key_base64: MII...==
|
156
|
+
environment: sandbox
|
157
|
+
```
|
158
|
+
|
159
|
+
You can also nest these credentials under the Rails environment if using a shared credentials file or secrets.
|
140
160
|
|
141
161
|
```yaml
|
142
162
|
development:
|
143
163
|
stripe:
|
144
164
|
private_key: xxxx
|
145
|
-
|
146
|
-
signing_secret: zzzz
|
147
|
-
braintree:
|
148
|
-
private_key: xxxx
|
149
|
-
public_key: yyyy
|
150
|
-
merchant_id: aaaa
|
151
|
-
environment: sandbox
|
152
|
-
paddle:
|
153
|
-
vendor_id: xxxx
|
154
|
-
vendor_auth_code: yyyy
|
155
|
-
public_key_base64: MII...==
|
156
|
-
environment: sandbox
|
165
|
+
# ...
|
157
166
|
```
|
158
167
|
|
159
|
-
|
160
|
-
|
161
|
-
|
168
|
+
##### Environment Variables
|
169
|
+
|
170
|
+
Pay will also check environment variables for API keys:
|
171
|
+
|
172
|
+
* `STRIPE_PUBLIC_KEY`
|
173
|
+
* `STRIPE_PRIVATE_KEY`
|
174
|
+
* `STRIPE_SIGNING_SECRET`
|
175
|
+
* `BRAINTREE_MERCHANT_ID`
|
176
|
+
* `BRAINTREE_PUBLIC_KEY`
|
177
|
+
* `BRAINTREE_PRIVATE_KEY`
|
178
|
+
* `BRAINTREE_ENVIRONMENT`
|
179
|
+
* `PADDLE_VENDOR_ID`
|
180
|
+
* `PADDLE_VENDOR_AUTH_CODE`
|
181
|
+
* `PADDLE_PUBLIC_KEY_BASE64`
|
182
|
+
* `PADDLE_ENVIRONMENT`
|
162
183
|
|
163
184
|
### Generators
|
164
185
|
|
@@ -176,11 +197,13 @@ bin/rails generate pay:email_views
|
|
176
197
|
|
177
198
|
### Emails
|
178
199
|
|
179
|
-
Emails can be enabled/disabled using the `send_emails` configuration option (enabled
|
200
|
+
Emails can be enabled/disabled using the `send_emails` configuration option (enabled by default).
|
201
|
+
|
202
|
+
When enabled, the following emails will be sent when:
|
180
203
|
|
181
|
-
-
|
182
|
-
-
|
183
|
-
-
|
204
|
+
- A charge succeeded
|
205
|
+
- A charge was refunded
|
206
|
+
- A subscription is about to renew
|
184
207
|
|
185
208
|
|
186
209
|
## Billable API
|
data/app/models/pay.rb
ADDED
data/app/models/pay/charge.rb
CHANGED
@@ -7,6 +7,7 @@ module Pay
|
|
7
7
|
|
8
8
|
# Associations
|
9
9
|
belongs_to :owner, polymorphic: true
|
10
|
+
belongs_to :subscription, optional: true, class_name: "Pay::Subscription", foreign_key: :pay_subscription_id
|
10
11
|
|
11
12
|
# Scopes
|
12
13
|
scope :sorted, -> { order(created_at: :desc) }
|
@@ -19,6 +20,7 @@ module Pay
|
|
19
20
|
validates :card_type, presence: true
|
20
21
|
|
21
22
|
store_accessor :data, :paddle_receipt_url
|
23
|
+
store_accessor :data, :stripe_account
|
22
24
|
|
23
25
|
# Helpers for payment processors
|
24
26
|
%w[braintree stripe paddle fake_processor].each do |processor_name|
|
@@ -9,6 +9,7 @@ module Pay
|
|
9
9
|
|
10
10
|
# Associations
|
11
11
|
belongs_to :owner, polymorphic: true
|
12
|
+
has_many :charges, class_name: "Pay::Charge", foreign_key: :pay_subscription_id
|
12
13
|
|
13
14
|
# Validations
|
14
15
|
validates :name, presence: true
|
@@ -31,6 +32,7 @@ module Pay
|
|
31
32
|
store_accessor :data, :paddle_update_url
|
32
33
|
store_accessor :data, :paddle_cancel_url
|
33
34
|
store_accessor :data, :paddle_paused_from
|
35
|
+
store_accessor :data, :stripe_account
|
34
36
|
|
35
37
|
attribute :prorate, :boolean, default: true
|
36
38
|
|
@@ -94,6 +96,11 @@ module Pay
|
|
94
96
|
past_due? || incomplete?
|
95
97
|
end
|
96
98
|
|
99
|
+
def change_quantity(quantity)
|
100
|
+
payment_processor.change_quantity(quantity)
|
101
|
+
update(quantity: quantity)
|
102
|
+
end
|
103
|
+
|
97
104
|
def resume
|
98
105
|
payment_processor.resume
|
99
106
|
update(ends_at: nil, status: "active")
|
@@ -110,8 +117,8 @@ module Pay
|
|
110
117
|
owner.invoice!(subscription_id: processor_id)
|
111
118
|
end
|
112
119
|
|
113
|
-
def processor_subscription(options
|
114
|
-
|
120
|
+
def processor_subscription(**options)
|
121
|
+
payment_processor.subscription(**options)
|
115
122
|
end
|
116
123
|
|
117
124
|
def latest_payment
|
@@ -1,22 +1,6 @@
|
|
1
1
|
class AddDataToPayModels < ActiveRecord::Migration[4.2]
|
2
2
|
def change
|
3
|
-
add_column :pay_subscriptions, :data,
|
4
|
-
add_column :pay_charges, :data,
|
5
|
-
end
|
6
|
-
|
7
|
-
def data_column_type
|
8
|
-
default_hash = ActiveRecord::Base.configurations.default_hash
|
9
|
-
|
10
|
-
# Rails 6.1 uses a symbol key instead of a string
|
11
|
-
adapter = default_hash.dig(:adapter) || default_hash.dig("adapter")
|
12
|
-
|
13
|
-
case adapter
|
14
|
-
when "mysql2"
|
15
|
-
:json
|
16
|
-
when "postgresql"
|
17
|
-
:jsonb
|
18
|
-
else
|
19
|
-
:text
|
20
|
-
end
|
3
|
+
add_column :pay_subscriptions, :data, Pay::Adapter.json_column_type
|
4
|
+
add_column :pay_charges, :data, Pay::Adapter.json_column_type
|
21
5
|
end
|
22
6
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class AddDataToPayBillable < ActiveRecord::Migration[4.2]
|
2
|
+
def change
|
3
|
+
# Load all the billable models
|
4
|
+
Rails.application.eager_load!
|
5
|
+
|
6
|
+
Pay.billable_models.each do |model|
|
7
|
+
add_column model.table_name, :pay_data, Pay::Adapter.json_column_type
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
class AddApplicationFeeToPayModels < ActiveRecord::Migration[4.2]
|
2
|
+
def change
|
3
|
+
add_column :pay_charges, :application_fee_amount, :integer
|
4
|
+
add_column :pay_subscriptions, :application_fee_percent, :decimal, precision: 8, scale: 2
|
5
|
+
add_column :pay_charges, :pay_subscription_id, :integer
|
6
|
+
end
|
7
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators/active_record"
|
4
|
+
require "generators/pay/orm_helpers"
|
5
|
+
|
6
|
+
module ActiveRecord
|
7
|
+
module Generators
|
8
|
+
class BillableGenerator < ActiveRecord::Generators::Base
|
9
|
+
include Pay::Generators::OrmHelpers
|
10
|
+
source_root File.expand_path("../templates", __FILE__)
|
11
|
+
|
12
|
+
def copy_pay_billable_migration
|
13
|
+
if (behavior == :invoke && model_exists?) || (behavior == :revoke && migration_exists?(table_name))
|
14
|
+
migration_template "billable_migration.rb", "#{migration_path}/add_pay_billable_to_#{table_name}.rb", migration_version: migration_version
|
15
|
+
else
|
16
|
+
say "#{model_path} does not exist.", :red
|
17
|
+
say "⚠️ Make sure the #{name} model exists before running this generator."
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# If the file already contains the contents, the user will receive this warning:
|
22
|
+
#
|
23
|
+
# File unchanged! The supplied flag value not found!
|
24
|
+
#
|
25
|
+
# This can be ignored as it just means the contents already exist and the file is unchanged.
|
26
|
+
# Thor will be updated to improve this message: https://github.com/rails/thor/issues/706
|
27
|
+
def inject_pay_billable_content
|
28
|
+
return unless model_exists?
|
29
|
+
|
30
|
+
content = model_contents
|
31
|
+
class_path = (namespaced? ? class_name.to_s.split("::") : [class_name])
|
32
|
+
indent_depth = class_path.size - 1
|
33
|
+
content = content.split("\n").map { |line| " " * indent_depth + line }.join("\n") << "\n"
|
34
|
+
inject_into_class(model_path, class_path.last, content)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def model_contents
|
40
|
+
" include Pay::Billable"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators/active_record"
|
4
|
+
require "generators/pay/orm_helpers"
|
5
|
+
|
6
|
+
module ActiveRecord
|
7
|
+
module Generators
|
8
|
+
class MerchantGenerator < ActiveRecord::Generators::Base
|
9
|
+
include Pay::Generators::OrmHelpers
|
10
|
+
source_root File.expand_path("../templates", __FILE__)
|
11
|
+
|
12
|
+
def copy_pay_merchant_migration
|
13
|
+
if (behavior == :invoke && model_exists?) || (behavior == :revoke && migration_exists?(table_name))
|
14
|
+
migration_template "merchant_migration.rb", "#{migration_path}/add_pay_merchant_to_#{table_name}.rb", migration_version: migration_version
|
15
|
+
else
|
16
|
+
say "#{model_path} does not exist.", :red
|
17
|
+
say "⚠️ Make sure the #{name} model exists before running this generator."
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# If the file already contains the contents, the user will receive this warning:
|
22
|
+
#
|
23
|
+
# File unchanged! The supplied flag value not found!
|
24
|
+
#
|
25
|
+
# This can be ignored as it just means the contents already exist and the file is unchanged.
|
26
|
+
# Thor will be updated to improve this message: https://github.com/rails/thor/issues/706
|
27
|
+
def inject_pay_merchant_content
|
28
|
+
return unless model_exists?
|
29
|
+
|
30
|
+
content = model_contents
|
31
|
+
class_path = (namespaced? ? class_name.to_s.split("::") : [class_name])
|
32
|
+
indent_depth = class_path.size - 1
|
33
|
+
content = content.split("\n").map { |line| " " * indent_depth + line }.join("\n") << "\n"
|
34
|
+
inject_into_class(model_path, class_path.last, content)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def model_contents
|
40
|
+
" include Pay::Merchant"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class AddPayBillableTo<%= table_name.camelize %> < ActiveRecord::Migration<%= migration_version %>
|
4
|
+
def change
|
5
|
+
change_table :<%= table_name %>, bulk: true do |t|
|
6
|
+
t.string :processor
|
7
|
+
t.string :processor_id
|
8
|
+
t.datetime :trial_ends_at
|
9
|
+
t.string :card_type
|
10
|
+
t.string :card_last4
|
11
|
+
t.string :card_exp_month
|
12
|
+
t.string :card_exp_year
|
13
|
+
t.text :extra_billing_info
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class AddPayMerchantTo<%= table_name.camelize %> < ActiveRecord::Migration<%= migration_version %>
|
4
|
+
def change
|
5
|
+
add_column table_name, :merchant_processor, :string
|
6
|
+
|
7
|
+
# We may already have the pay_data column if a Pay::Billable object is also a Pay::Merchant
|
8
|
+
unless ActiveRecord::Base.connection.column_exists?(table_name, :pay_data)
|
9
|
+
add_column :<%= table_name %>, :pay_data, Pay::Adapter.json_column_type
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -4,13 +4,12 @@ require "rails/generators/named_base"
|
|
4
4
|
|
5
5
|
module Pay
|
6
6
|
module Generators
|
7
|
-
class
|
7
|
+
class BillableGenerator < Rails::Generators::NamedBase
|
8
8
|
include Rails::Generators::ResourceHelpers
|
9
9
|
|
10
|
-
namespace "pay"
|
11
10
|
source_root File.expand_path("../templates", __FILE__)
|
12
11
|
|
13
|
-
desc "Generates a migration to add Billable fields to a model."
|
12
|
+
desc "Generates a migration to add Pay::Billable fields to a model."
|
14
13
|
|
15
14
|
hook_for :orm
|
16
15
|
end
|