pay 2.7.2 → 3.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 +34 -731
- data/app/controllers/pay/webhooks/braintree_controller.rb +10 -3
- data/app/controllers/pay/webhooks/paddle_controller.rb +7 -8
- data/app/controllers/pay/webhooks/stripe_controller.rb +6 -3
- data/app/jobs/pay/{email_sync_job.rb → customer_sync_job.rb} +3 -4
- data/app/models/pay/application_record.rb +0 -5
- data/app/models/pay/charge.rb +31 -18
- data/app/models/pay/customer.rb +87 -0
- data/app/models/pay/merchant.rb +19 -0
- data/app/models/pay/payment_method.rb +41 -0
- data/app/models/pay/subscription.rb +32 -30
- data/app/models/pay/webhook.rb +36 -0
- data/app/views/layouts/pay/application.html.erb +2 -3
- data/app/views/pay/payments/show.html.erb +109 -81
- data/app/views/pay/user_mailer/receipt.html.erb +2 -2
- data/app/views/pay/user_mailer/refund.html.erb +2 -2
- data/config/locales/en.yml +1 -1
- data/db/migrate/1_create_pay_tables.rb +72 -0
- data/lib/pay/attributes.rb +74 -0
- data/lib/pay/billable/sync_customer.rb +30 -0
- data/lib/pay/braintree/billable.rb +126 -108
- data/lib/pay/braintree/payment_method.rb +33 -0
- data/lib/pay/braintree/subscription.rb +7 -12
- data/lib/pay/braintree/webhooks/subscription_canceled.rb +1 -1
- data/lib/pay/braintree/webhooks/subscription_charged_successfully.rb +4 -4
- data/lib/pay/braintree/webhooks/subscription_charged_unsuccessfully.rb +1 -1
- data/lib/pay/braintree/webhooks/subscription_expired.rb +1 -1
- data/lib/pay/braintree/webhooks/subscription_trial_ended.rb +2 -2
- data/lib/pay/braintree/webhooks/subscription_went_active.rb +1 -1
- data/lib/pay/braintree/webhooks/subscription_went_past_due.rb +1 -1
- data/lib/pay/braintree.rb +3 -2
- data/lib/pay/engine.rb +6 -1
- data/lib/pay/fake_processor/billable.rb +45 -21
- data/lib/pay/fake_processor/payment_method.rb +21 -0
- data/lib/pay/fake_processor/subscription.rb +11 -10
- data/lib/pay/fake_processor.rb +2 -1
- data/lib/pay/nano_id.rb +13 -0
- data/lib/pay/paddle/billable.rb +18 -48
- data/lib/pay/paddle/charge.rb +5 -5
- data/lib/pay/paddle/payment_method.rb +58 -0
- data/lib/pay/paddle/response.rb +0 -0
- data/lib/pay/paddle/subscription.rb +47 -8
- data/lib/pay/paddle/webhooks/subscription_cancelled.rb +6 -3
- data/lib/pay/paddle/webhooks/subscription_created.rb +1 -40
- data/lib/pay/paddle/webhooks/subscription_payment_refunded.rb +3 -3
- data/lib/pay/paddle/webhooks/subscription_payment_succeeded.rb +26 -28
- data/lib/pay/paddle/webhooks/subscription_updated.rb +2 -2
- data/lib/pay/paddle.rb +7 -3
- data/lib/pay/payment.rb +1 -1
- data/lib/pay/receipts.rb +35 -7
- data/lib/pay/stripe/billable.rb +50 -64
- data/lib/pay/stripe/charge.rb +18 -15
- data/lib/pay/stripe/merchant.rb +10 -10
- data/lib/pay/stripe/payment_method.rb +61 -0
- data/lib/pay/stripe/subscription.rb +22 -17
- data/lib/pay/stripe/webhooks/account_updated.rb +2 -3
- data/lib/pay/stripe/webhooks/charge_refunded.rb +1 -1
- data/lib/pay/stripe/webhooks/charge_succeeded.rb +2 -2
- data/lib/pay/stripe/webhooks/checkout_session_async_payment_succeeded.rb +3 -1
- data/lib/pay/stripe/webhooks/checkout_session_completed.rb +3 -1
- data/lib/pay/stripe/webhooks/customer_deleted.rb +7 -15
- data/lib/pay/stripe/webhooks/customer_updated.rb +10 -3
- data/lib/pay/stripe/webhooks/payment_action_required.rb +2 -2
- data/lib/pay/stripe/webhooks/payment_intent_succeeded.rb +6 -8
- data/lib/pay/stripe/webhooks/payment_method_attached.rb +2 -4
- data/lib/pay/stripe/webhooks/payment_method_detached.rb +1 -6
- data/lib/pay/stripe/webhooks/payment_method_updated.rb +10 -4
- data/lib/pay/stripe/webhooks/subscription_created.rb +1 -1
- data/lib/pay/stripe/webhooks/subscription_deleted.rb +2 -1
- data/lib/pay/stripe/webhooks/subscription_renewing.rb +12 -2
- data/lib/pay/stripe.rb +6 -3
- data/lib/pay/version.rb +1 -1
- data/lib/pay/webhooks/delegator.rb +4 -0
- data/lib/pay/webhooks/process_job.rb +9 -0
- data/lib/pay/webhooks.rb +1 -0
- data/lib/pay.rb +7 -78
- metadata +20 -37
- data/db/migrate/20170205020145_create_pay_subscriptions.rb +0 -17
- data/db/migrate/20170727235816_create_pay_charges.rb +0 -18
- data/db/migrate/20190816015720_add_status_to_pay_subscriptions.rb +0 -14
- data/db/migrate/20200603134434_add_data_to_pay_models.rb +0 -6
- data/db/migrate/20210309004259_add_data_to_pay_billable.rb +0 -10
- data/db/migrate/20210406215234_add_currency_to_pay_charges.rb +0 -5
- data/db/migrate/20210406215506_add_application_fee_to_pay_models.rb +0 -7
- data/db/migrate/20210714175351_add_uniqueness_to_pay_models.rb +0 -6
- data/lib/pay/billable/sync_email.rb +0 -40
- data/lib/pay/billable.rb +0 -172
- data/lib/pay/stripe/webhooks/payment_method_automatically_updated.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6a85b63ecf485a80f4825041bdfc4d66be639b65060c1836135422f367e0b878
|
4
|
+
data.tar.gz: d43a4bf47062f27d6fcdc4771517a2d2911bb3c38298d0c66b883446a42ac8ed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 88701e517c153b29434b56132cc567145f5c6bce65d0e6592c3118cd700147c45616840bb246df12c6d9999940c4e44147d332af7d4857b0ea02dcd2699d0d34
|
7
|
+
data.tar.gz: d57a35f69149eb0a49a9417fef01f9e3b70703bc077a3fcc7a2b7c6acb1f5f228570f695942ab75a67fd6f664baee7e6aa58ef7b16e1744b7633a4d7caec31e1
|
data/README.md
CHANGED
@@ -1,755 +1,57 @@
|
|
1
1
|
<p align="center"><img src="docs/images/logo.svg" height="50px"></p>
|
2
2
|
|
3
|
-
|
3
|
+
# 💳 Pay - Payments engine for Ruby on Rails
|
4
4
|
|
5
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
|
|
9
|
-
Pay is a payments engine for Ruby on Rails
|
9
|
+
Pay is a payments engine for Ruby on Rails 6.0 and higher.
|
10
10
|
|
11
|
-
**
|
11
|
+
**Upgrading?** Check the [UPGRADE](UPGRADE.md) guide for required changes and/or migration when upgrading from a previous version of Pay.
|
12
12
|
|
13
|
-
|
14
|
-
- Paddle (SCA Compatible & supports PayPal)
|
15
|
-
- Braintree (supports PayPal)
|
16
|
-
- [Fake Processor](docs/fake_processor.md)
|
17
|
-
|
18
|
-
Want to add a new payment provider? Contributions are welcome and the instructions [are here](https://github.com/jasoncharnes/pay/wiki/New-Payment-Provider).
|
19
|
-
|
20
|
-
**Check the CHANGELOG for any required migrations or changes needed if you're upgrading from a previous version of Pay.**
|
21
|
-
|
22
|
-
## Tutorial
|
13
|
+
## 🧑💻 Tutorial
|
23
14
|
|
24
15
|
Want to see how Pay works? Check out our video getting started guide.
|
25
16
|
|
26
17
|
<a href="https://www.youtube.com/watch?v=hYlOmqyJIgc" target="_blank"><img width="50%" src="http://i3.ytimg.com/vi/hYlOmqyJIgc/maxresdefault.jpg"></a>
|
27
18
|
|
28
|
-
##
|
29
|
-
|
30
|
-
Add these lines to your application's Gemfile:
|
31
|
-
|
32
|
-
```ruby
|
33
|
-
gem 'pay', '~> 2.0'
|
34
|
-
|
35
|
-
# To use Stripe, also include:
|
36
|
-
gem 'stripe', '< 6.0', '>= 2.8'
|
37
|
-
|
38
|
-
# To use Braintree + PayPal, also include:
|
39
|
-
gem 'braintree', '< 3.0', '>= 2.92.0'
|
40
|
-
|
41
|
-
# To use Paddle, also include:
|
42
|
-
gem 'paddle_pay', '~> 0.1'
|
43
|
-
|
44
|
-
# To use Receipts
|
45
|
-
gem 'receipts', '~> 1.0.0'
|
46
|
-
```
|
47
|
-
|
48
|
-
And then execute:
|
49
|
-
|
50
|
-
```bash
|
51
|
-
bundle
|
52
|
-
```
|
53
|
-
|
54
|
-
Next, we need to add migrations to your application, run the following migration:
|
55
|
-
|
56
|
-
````bash
|
57
|
-
bin/rails pay:install:migrations
|
58
|
-
````
|
59
|
-
|
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.
|
61
|
-
|
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.
|
63
|
-
|
64
|
-
```bash
|
65
|
-
bin/rails g pay:billable User
|
66
|
-
```
|
67
|
-
|
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.
|
69
|
-
|
70
|
-
**Note:** An `email` attribute or method on your `Billable` model is required.
|
71
|
-
|
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.
|
73
|
-
|
74
|
-
Finally, run the migrations
|
75
|
-
|
76
|
-
```bash
|
77
|
-
bin/rails db:migrate
|
78
|
-
```
|
79
|
-
|
80
|
-
> If you run into `NoMethodError (undefined method 'stripe_customer' for #<User:0x00007fbc34b9bf20>)`, fully restart your Rails application `bin/spring stop && rails s`
|
81
|
-
|
82
|
-
Lastly, make sure you've configured your ActionMailer default_url_options so Pay can generate links to for features like Stripe Checkout.
|
83
|
-
|
84
|
-
```ruby
|
85
|
-
# config/application.rb
|
86
|
-
config.action_mailer.default_url_options = { host: "example.com" }
|
87
|
-
```
|
88
|
-
|
89
|
-
## Configuration
|
90
|
-
|
91
|
-
Need to make some changes to how Pay is used? You can create an initializer `config/initializers/pay.rb`
|
92
|
-
|
93
|
-
```ruby
|
94
|
-
Pay.setup do |config|
|
95
|
-
# config.chargeable_class = 'Pay::Charge'
|
96
|
-
# config.chargeable_table = 'pay_charges'
|
97
|
-
|
98
|
-
# For use in the receipt/refund/renewal mailers
|
99
|
-
config.business_name = "Business Name"
|
100
|
-
config.business_address = "1600 Pennsylvania Avenue NW"
|
101
|
-
config.application_name = "My App"
|
102
|
-
config.support_email = "helpme@example.com"
|
103
|
-
|
104
|
-
config.send_emails = true
|
105
|
-
|
106
|
-
config.default_product_name = "default"
|
107
|
-
config.default_plan_name = "default"
|
108
|
-
|
109
|
-
config.automount_routes = true
|
110
|
-
config.routes_path = "/pay" # Only when automount_routes is true
|
111
|
-
end
|
112
|
-
```
|
113
|
-
|
114
|
-
This allows you to create your own Charge class for instance, which could add receipt functionality:
|
115
|
-
|
116
|
-
```ruby
|
117
|
-
class Charge < Pay::Charge
|
118
|
-
def receipts
|
119
|
-
# do some receipts stuff using the https://github.com/excid3/receipts gem
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
Pay.setup do |config|
|
124
|
-
config.chargeable_class = 'Charge'
|
125
|
-
end
|
126
|
-
```
|
127
|
-
|
128
|
-
### Credentials
|
129
|
-
|
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
|
-
bin/rails credentials:edit --environment=development
|
138
|
-
```
|
139
|
-
|
140
|
-
They should be formatted like the following:
|
141
|
-
|
142
|
-
```yaml
|
143
|
-
stripe:
|
144
|
-
private_key: sk_test_xxxx
|
145
|
-
public_key: pk_test_yyyy
|
146
|
-
signing_secret: whsec_zzzz
|
147
|
-
|
148
|
-
braintree:
|
149
|
-
private_key: xxxx
|
150
|
-
public_key: yyyy
|
151
|
-
merchant_id: aaaa
|
152
|
-
environment: sandbox
|
153
|
-
|
154
|
-
paddle:
|
155
|
-
vendor_id: xxxx
|
156
|
-
vendor_auth_code: yyyy
|
157
|
-
public_key_base64: MII...==
|
158
|
-
environment: sandbox
|
159
|
-
```
|
160
|
-
|
161
|
-
You can also nest these credentials under the Rails environment if using a shared credentials file or secrets.
|
162
|
-
|
163
|
-
```yaml
|
164
|
-
development:
|
165
|
-
stripe:
|
166
|
-
private_key: sk_test_xxxx
|
167
|
-
public_key: pk_test_yyyy
|
168
|
-
signing_secret: whsec_zzzz
|
169
|
-
# ...
|
170
|
-
```
|
171
|
-
|
172
|
-
##### Environment Variables
|
173
|
-
|
174
|
-
Pay will also check environment variables for API keys:
|
175
|
-
|
176
|
-
* `STRIPE_PUBLIC_KEY`
|
177
|
-
* `STRIPE_PRIVATE_KEY`
|
178
|
-
* `STRIPE_SIGNING_SECRET`
|
179
|
-
* `BRAINTREE_MERCHANT_ID`
|
180
|
-
* `BRAINTREE_PUBLIC_KEY`
|
181
|
-
* `BRAINTREE_PRIVATE_KEY`
|
182
|
-
* `BRAINTREE_ENVIRONMENT`
|
183
|
-
* `PADDLE_VENDOR_ID`
|
184
|
-
* `PADDLE_VENDOR_AUTH_CODE`
|
185
|
-
* `PADDLE_PUBLIC_KEY_BASE64`
|
186
|
-
* `PADDLE_ENVIRONMENT`
|
187
|
-
|
188
|
-
### Generators
|
189
|
-
|
190
|
-
If you want to modify the Stripe SCA template or any other views, you can copy over the view files using:
|
191
|
-
|
192
|
-
```bash
|
193
|
-
bin/rails generate pay:views
|
194
|
-
```
|
195
|
-
|
196
|
-
If you want to modify the email templates, you can copy over the view files using:
|
197
|
-
|
198
|
-
```bash
|
199
|
-
bin/rails generate pay:email_views
|
200
|
-
```
|
201
|
-
|
202
|
-
### Emails
|
203
|
-
|
204
|
-
Emails can be enabled/disabled using the `send_emails` configuration option (enabled by default).
|
205
|
-
|
206
|
-
When enabled, the following emails will be sent when:
|
207
|
-
|
208
|
-
- A charge succeeded
|
209
|
-
- A charge was refunded
|
210
|
-
- A subscription is about to renew
|
211
|
-
|
212
|
-
|
213
|
-
## Billable API
|
214
|
-
|
215
|
-
#### Trials
|
216
|
-
|
217
|
-
You can check if the user is on a trial by simply asking:
|
218
|
-
|
219
|
-
```ruby
|
220
|
-
user = User.find_by(email: 'michael@bluthcompany.co')
|
221
|
-
|
222
|
-
user.on_trial? #=> true or false
|
223
|
-
```
|
224
|
-
|
225
|
-
The `on_trial?` method has two optional arguments with default values.
|
19
|
+
## 🏦 Payment Processors
|
226
20
|
|
227
|
-
|
228
|
-
user = User.find_by(email: 'michael@bluthcompany.co')
|
229
|
-
|
230
|
-
user.on_trial?(name: 'default', plan: 'plan') #=> true or false
|
231
|
-
```
|
232
|
-
|
233
|
-
#### Generic Trials
|
234
|
-
|
235
|
-
For trials that don't require cards upfront:
|
236
|
-
|
237
|
-
```ruby
|
238
|
-
user = User.create(
|
239
|
-
email: 'michael@bluthcompany.co',
|
240
|
-
trial_ends_at: 30.days.from_now
|
241
|
-
)
|
242
|
-
|
243
|
-
user.on_generic_trial? #=> true
|
244
|
-
```
|
245
|
-
|
246
|
-
#### Creating a Charge
|
247
|
-
|
248
|
-
##### Stripe and Braintree
|
249
|
-
|
250
|
-
```ruby
|
251
|
-
user = User.find_by(email: 'michael@bluthcompany.co')
|
252
|
-
|
253
|
-
user.processor = 'stripe'
|
254
|
-
user.card_token = 'payment_method_id'
|
255
|
-
user.charge(1500) # $15.00 USD
|
256
|
-
|
257
|
-
user = User.find_by(email: 'michael@bluthcompany.co')
|
258
|
-
|
259
|
-
user.processor = 'braintree'
|
260
|
-
user.card_token = 'nonce'
|
261
|
-
user.charge(1500) # $15.00 USD
|
262
|
-
```
|
263
|
-
|
264
|
-
The `charge` method takes the amount in cents as the primary argument.
|
265
|
-
|
266
|
-
You may pass optional arguments that will be directly passed on to
|
267
|
-
either Stripe or Braintree. You can use these options to charge
|
268
|
-
different currencies, etc.
|
269
|
-
|
270
|
-
On failure, a `Pay::Error` will be raised with details about the payment
|
271
|
-
failure.
|
272
|
-
|
273
|
-
##### Paddle
|
274
|
-
It is only possible to create immediate one-time charges on top of an existing subscription.
|
275
|
-
|
276
|
-
```ruby
|
277
|
-
user = User.find_by(email: 'michael@bluthcompany.co')
|
278
|
-
|
279
|
-
user.processor = 'paddle'
|
280
|
-
user.charge(1500, {charge_name: "Test"}) # $15.00 USD
|
281
|
-
|
282
|
-
```
|
283
|
-
|
284
|
-
An existing subscription and a charge name are required.
|
285
|
-
|
286
|
-
#### Creating a Subscription
|
287
|
-
|
288
|
-
##### Stripe and Braintree
|
289
|
-
|
290
|
-
```ruby
|
291
|
-
user = User.find_by(email: 'michael@bluthcompany.co')
|
292
|
-
|
293
|
-
user.processor = 'stripe'
|
294
|
-
user.card_token = 'payment_method_id'
|
295
|
-
user.subscribe
|
296
|
-
```
|
297
|
-
|
298
|
-
A `card_token` must be provided as an attribute.
|
299
|
-
|
300
|
-
The subscribe method has three optional arguments with default values.
|
301
|
-
|
302
|
-
```ruby
|
303
|
-
def subscribe(name: Pay.default_product_name, plan: Pay.default_plan_name, **options)
|
304
|
-
...
|
305
|
-
end
|
306
|
-
```
|
307
|
-
|
308
|
-
For example, you can pass the `quantity` option to subscribe to a plan with for per-seat pricing.
|
309
|
-
|
310
|
-
```ruby
|
311
|
-
|
312
|
-
user.subscribe(name: Pay.default_product_name, plan: Pay.default_plan_name, quantity: 3)
|
313
|
-
```
|
314
|
-
|
315
|
-
###### Name
|
316
|
-
|
317
|
-
Name is an internally used name for the subscription.
|
318
|
-
|
319
|
-
###### Plan
|
320
|
-
|
321
|
-
Plan is the plan ID or price ID from the payment processor. For example: `plan_xxxxx` or `price_xxxxx`
|
322
|
-
|
323
|
-
###### Options
|
324
|
-
|
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.
|
328
|
-
|
329
|
-
##### Paddle
|
330
|
-
It is currently not possible to create a subscription through the API. Instead the subscription in Pay is created by the Paddle Subscription Webhook. In order to be able to assign the subcription to the correct owner, the Paddle [passthrough parameter](https://developer.paddle.com/guides/how-tos/checkout/pass-parameters) has to be used for checkout.
|
331
|
-
|
332
|
-
To ensure that the owner cannot be tampered with, Pay uses a Signed Global ID with a purpose. The purpose string consists of "paddle_" and the subscription plan id (or product id respectively).
|
333
|
-
|
334
|
-
Javascript Checkout:
|
335
|
-
```javascript
|
336
|
-
Paddle.Checkout.open({
|
337
|
-
product: 12345,
|
338
|
-
passthrough: "<%= Pay::Paddle.passthrough(owner: current_user) %>"
|
339
|
-
});
|
340
|
-
```
|
341
|
-
|
342
|
-
Paddle Button Checkout:
|
343
|
-
```html
|
344
|
-
<a href="#!" class="paddle_button" data-product="12345" data-email="<%= current_user.email %>" data-passthrough="<%= Pay::Paddle.passthrough(owner: current_user) %>"
|
345
|
-
```
|
346
|
-
|
347
|
-
###### Passthrough
|
348
|
-
|
349
|
-
Pay providers a helper method for generating the passthrough JSON object to associate the purchase with the correct Rails model.
|
350
|
-
|
351
|
-
```ruby
|
352
|
-
Pay::Paddle.passthrough(owner: current_user, foo: :bar)
|
353
|
-
#=> { owner_sgid: "xxxxxxxx", foo: "bar" }
|
354
|
-
|
355
|
-
# To generate manually without the helper
|
356
|
-
#=> { owner_sgid: current_user.to_sgid.to_s, foo: "bar" }.to_json
|
357
|
-
```
|
358
|
-
|
359
|
-
Pay parses the passthrough JSON string and verifies the `owner_sgid` hash to match the webhook with the correct billable record.
|
360
|
-
The passthrough parameter `owner_sgid` is only required for creating a subscription.
|
361
|
-
|
362
|
-
#### Retrieving a Subscription from the Database
|
363
|
-
|
364
|
-
```ruby
|
365
|
-
user = User.find_by(email: 'gob@bluthcompany.co')
|
366
|
-
|
367
|
-
user.subscription
|
368
|
-
```
|
369
|
-
|
370
|
-
A subscription can be retrieved by name, too.
|
371
|
-
|
372
|
-
```ruby
|
373
|
-
user = User.find_by(email: 'gob@bluthcompany.co')
|
374
|
-
|
375
|
-
user.subscription(name: 'bananastand+')
|
376
|
-
```
|
377
|
-
|
378
|
-
#### Checking a User's Trial/Subscription Status
|
379
|
-
|
380
|
-
```ruby
|
381
|
-
user = User.find_by(email: 'george.senior@bluthcompany.co')
|
382
|
-
user.on_trial_or_subscribed?
|
383
|
-
```
|
384
|
-
|
385
|
-
The `on_trial_or_subscribed?` method has two optional arguments with default values.
|
386
|
-
|
387
|
-
```ruby
|
388
|
-
def on_trial_or_subscribed?(name: 'default', plan: nil)
|
389
|
-
...
|
390
|
-
end
|
391
|
-
```
|
392
|
-
|
393
|
-
#### Checking a User's Subscription Status
|
394
|
-
|
395
|
-
```ruby
|
396
|
-
user = User.find_by(email: 'george.senior@bluthcompany.co')
|
397
|
-
user.subscribed?
|
398
|
-
```
|
21
|
+
Our supported payment processors are:
|
399
22
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
...
|
405
|
-
end
|
406
|
-
```
|
407
|
-
|
408
|
-
##### Name
|
409
|
-
|
410
|
-
Name is an internally used name for the subscription.
|
411
|
-
|
412
|
-
##### Plan
|
413
|
-
|
414
|
-
Plan is the plan ID from the payment processor.
|
415
|
-
|
416
|
-
#### Retrieving a Payment Processor Account
|
417
|
-
|
418
|
-
##### Stripe and Braintree
|
419
|
-
|
420
|
-
```ruby
|
421
|
-
user = User.find_by(email: 'george.michael@bluthcompany.co')
|
422
|
-
|
423
|
-
user.customer #> Stripe or Braintree customer account
|
424
|
-
```
|
425
|
-
|
426
|
-
##### Paddle
|
427
|
-
|
428
|
-
It is currently not possible to retrieve a payment processor account through the API.
|
429
|
-
|
430
|
-
#### Updating a Customer's Credit Card
|
431
|
-
|
432
|
-
##### Stripe and Braintree
|
433
|
-
|
434
|
-
```ruby
|
435
|
-
user = User.find_by(email: 'tobias@bluthcompany.co')
|
436
|
-
|
437
|
-
user.update_card('payment_method_id')
|
438
|
-
```
|
439
|
-
|
440
|
-
##### Paddle
|
441
|
-
|
442
|
-
Paddle provides a unique [Update URL](https://developer.paddle.com/guides/how-tos/subscriptions/update-payment-details) for each user, which allows them to update the payment method.
|
443
|
-
```ruby
|
444
|
-
user = User.find_by(email: 'tobias@bluthcompany.co')
|
445
|
-
|
446
|
-
user.subscription.paddle_update_url
|
447
|
-
```
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
#### Retrieving a Customer's Subscription from the Processor
|
452
|
-
|
453
|
-
```ruby
|
454
|
-
user = User.find_by(email: 'lucille@bluthcompany.co')
|
455
|
-
|
456
|
-
user.processor_subscription(subscription_id) #=> Stripe, Braintree or Paddle Subscription
|
457
|
-
```
|
458
|
-
|
459
|
-
## Subscription API
|
460
|
-
|
461
|
-
#### Checking a Subscription's Trial Status
|
462
|
-
|
463
|
-
```ruby
|
464
|
-
user = User.find_by(email: 'lindsay@bluthcompany.co')
|
465
|
-
|
466
|
-
user.subscription.on_trial? #=> true or false
|
467
|
-
```
|
468
|
-
|
469
|
-
#### Checking a Subscription's Cancellation Status
|
470
|
-
|
471
|
-
```ruby
|
472
|
-
user = User.find_by(email: 'buster@bluthcompany.co')
|
473
|
-
|
474
|
-
user.subscription.cancelled? #=> true or false
|
475
|
-
```
|
476
|
-
|
477
|
-
#### Checking a Subscription's Grace Period Status
|
478
|
-
|
479
|
-
```ruby
|
480
|
-
user = User.find_by(email: 'her?@bluthcompany.co')
|
481
|
-
|
482
|
-
user.subscription.on_grace_period? #=> true or false
|
483
|
-
```
|
484
|
-
|
485
|
-
#### Checking to See If a Subscription Is Active
|
486
|
-
|
487
|
-
```ruby
|
488
|
-
user = User.find_by(email: 'carl.weathers@bluthcompany.co')
|
489
|
-
|
490
|
-
user.subscription.active? #=> true or false
|
491
|
-
```
|
492
|
-
|
493
|
-
#### Checking to See If a Subscription Is Paused
|
494
|
-
|
495
|
-
```ruby
|
496
|
-
user = User.find_by(email: 'carl.weathers@bluthcompany.co')
|
497
|
-
|
498
|
-
user.subscription.paused? #=> true or false
|
499
|
-
```
|
500
|
-
|
501
|
-
#### Cancel a Subscription (At End of Billing Cycle)
|
502
|
-
|
503
|
-
##### Stripe, Braintree and Paddle
|
504
|
-
|
505
|
-
```ruby
|
506
|
-
user = User.find_by(email: 'oscar@bluthcompany.co')
|
507
|
-
|
508
|
-
user.subscription.cancel
|
509
|
-
```
|
510
|
-
|
511
|
-
##### Paddle
|
512
|
-
In addition to the API, Paddle provides a subscription [Cancel URL](https://developer.paddle.com/guides/how-tos/subscriptions/cancel-and-pause) that you can redirect customers to cancel their subscription.
|
513
|
-
|
514
|
-
```ruby
|
515
|
-
user.subscription.paddle_cancel_url
|
516
|
-
```
|
517
|
-
|
518
|
-
#### Cancel a Subscription Immediately
|
519
|
-
|
520
|
-
```ruby
|
521
|
-
user = User.find_by(email: 'annyong@bluthcompany.co')
|
522
|
-
|
523
|
-
user.subscription.cancel_now!
|
524
|
-
```
|
525
|
-
|
526
|
-
#### Pause a Subscription
|
527
|
-
|
528
|
-
##### Paddle
|
529
|
-
|
530
|
-
```ruby
|
531
|
-
user = User.find_by(email: 'oscar@bluthcompany.co')
|
532
|
-
|
533
|
-
user.subscription.pause
|
534
|
-
```
|
535
|
-
|
536
|
-
#### Swap a Subscription to another Plan
|
537
|
-
|
538
|
-
```ruby
|
539
|
-
user = User.find_by(email: 'steve.holt@bluthcompany.co')
|
540
|
-
|
541
|
-
user.subscription.swap("yearly")
|
542
|
-
```
|
543
|
-
|
544
|
-
#### Resume a Subscription
|
545
|
-
|
546
|
-
##### Stripe or Braintree Subscription (on Grace Period)
|
547
|
-
|
548
|
-
```ruby
|
549
|
-
user = User.find_by(email: 'steve.holt@bluthcompany.co')
|
550
|
-
|
551
|
-
user.subscription.resume
|
552
|
-
```
|
553
|
-
|
554
|
-
##### Paddle (Paused)
|
555
|
-
|
556
|
-
```ruby
|
557
|
-
user = User.find_by(email: 'steve.holt@bluthcompany.co')
|
558
|
-
|
559
|
-
user.subscription.resume
|
560
|
-
```
|
561
|
-
|
562
|
-
#### Retrieving the Subscription from the Processor
|
563
|
-
|
564
|
-
```ruby
|
565
|
-
user = User.find_by(email: 'lucille2@bluthcompany.co')
|
566
|
-
|
567
|
-
user.subscription.processor_subscription
|
568
|
-
```
|
569
|
-
|
570
|
-
### Customizing Pay Models
|
571
|
-
|
572
|
-
Want to add methods to `Pay::Subscription` or `Pay::Charge`? You can
|
573
|
-
define a concern and simply include it in the model when Rails loads the
|
574
|
-
code.
|
575
|
-
|
576
|
-
Pay uses the `to_prepare` method to allow concerns to be
|
577
|
-
included every time Rails reloads the models in development as well.
|
578
|
-
|
579
|
-
```ruby
|
580
|
-
# app/models/concerns/subscription_extensions.rb
|
581
|
-
module SubscriptionExtensions
|
582
|
-
extend ActiveSupport::Concern
|
583
|
-
|
584
|
-
included do
|
585
|
-
# associations and other class level things go here
|
586
|
-
end
|
587
|
-
|
588
|
-
# instance methods and code go here
|
589
|
-
end
|
590
|
-
```
|
591
|
-
|
592
|
-
```ruby
|
593
|
-
# config/initializers/subscription_extensions.rb
|
594
|
-
|
595
|
-
# Re-include the SubscriptionExtensions every time Rails reloads
|
596
|
-
Rails.application.config.to_prepare do
|
597
|
-
Pay.subscription_model.include SubscriptionExtensions
|
598
|
-
end
|
599
|
-
```
|
600
|
-
|
601
|
-
## Routes & Webhooks
|
602
|
-
|
603
|
-
Routes are automatically mounted to `/pay` by default.
|
604
|
-
|
605
|
-
We provide a route for confirming SCA payments at `/pay/payments/:payment_intent_id`
|
606
|
-
|
607
|
-
Webhooks are automatically mounted at `/pay/webhooks/{provider}`
|
608
|
-
|
609
|
-
#### Customizing webhook mount path
|
610
|
-
|
611
|
-
If you have a catch all route (for 404s etc) and need to control where/when the webhook endpoints mount, you will need to disable automatic mounting and mount the engine above your catch all route.
|
612
|
-
|
613
|
-
```ruby
|
614
|
-
# config/initializers/pay.rb
|
615
|
-
Pay.setup do |config|
|
616
|
-
# ...
|
617
|
-
|
618
|
-
config.automount_routes = false
|
619
|
-
end
|
620
|
-
|
621
|
-
# config/routes.rb
|
622
|
-
Rails.application.routes.draw do
|
623
|
-
mount Pay::Engine, at: '/pay' # You can change the `at` path to feed your needs.
|
624
|
-
|
625
|
-
# Other routes here
|
626
|
-
end
|
627
|
-
```
|
628
|
-
|
629
|
-
If you just want to modify where the engine mounts it's routes then you can change the path.
|
630
|
-
|
631
|
-
```ruby
|
632
|
-
# config/initializers/pay.rb
|
633
|
-
|
634
|
-
Pay.setup do |config|
|
635
|
-
# ...
|
636
|
-
|
637
|
-
config.routes_path = '/pay'
|
638
|
-
end
|
639
|
-
```
|
640
|
-
|
641
|
-
## Payment Providers
|
642
|
-
|
643
|
-
We support Stripe, Braintree and Paddle and make our best attempt to
|
644
|
-
standardize the three. They function differently so keep that in mind if
|
645
|
-
you plan on doing more complex payments. It would be best to stick with
|
646
|
-
a single payment provider in that case so you don't run into
|
647
|
-
discrepancies.
|
648
|
-
|
649
|
-
#### Braintree
|
650
|
-
|
651
|
-
```yaml
|
652
|
-
development:
|
653
|
-
braintree:
|
654
|
-
private_key: xxxx
|
655
|
-
public_key: yyyy
|
656
|
-
merchant_id: zzzz
|
657
|
-
environment: sandbox
|
658
|
-
```
|
659
|
-
#### Paddle
|
660
|
-
|
661
|
-
```yaml
|
662
|
-
paddle:
|
663
|
-
vendor_id: xxxx
|
664
|
-
vendor_auth_code: yyyy
|
665
|
-
public_key_base64: MII...==
|
666
|
-
environment: sandbox
|
667
|
-
```
|
668
|
-
|
669
|
-
Paddle receipts can be retrieved by a charge receipt URL.
|
670
|
-
```ruby
|
671
|
-
user = User.find_by(email: 'annyong@bluthcompany.co')
|
672
|
-
|
673
|
-
charge = user.charges.first
|
674
|
-
charge.paddle_receipt_url
|
675
|
-
```
|
676
|
-
#### Stripe
|
677
|
-
|
678
|
-
You'll need to add your private Stripe API key to your Rails secrets `config/secrets.yml`, credentials `rails credentials:edit`
|
679
|
-
|
680
|
-
```yaml
|
681
|
-
development:
|
682
|
-
stripe:
|
683
|
-
private_key: xxxx
|
684
|
-
public_key: yyyy
|
685
|
-
signing_secret: zzzz
|
686
|
-
```
|
687
|
-
|
688
|
-
You can also use the `STRIPE_PRIVATE_KEY` and `STRIPE_SIGNING_SECRET` environment variables.
|
689
|
-
|
690
|
-
**To see how to use Stripe Elements JS & Devise, [click here](https://github.com/jasoncharnes/pay/wiki/Using-Stripe-Elements-and-Devise).**
|
691
|
-
|
692
|
-
You need the following event types to trigger the webhook:
|
693
|
-
|
694
|
-
```
|
695
|
-
customer.subscription.updated
|
696
|
-
customer.subscription.deleted
|
697
|
-
customer.subscription.created
|
698
|
-
payment_method.updated
|
699
|
-
invoice.payment_action_required
|
700
|
-
customer.updated
|
701
|
-
customer.deleted
|
702
|
-
charge.succeeded
|
703
|
-
charge.refunded
|
704
|
-
```
|
705
|
-
|
706
|
-
##### Strong Customer Authentication (SCA)
|
707
|
-
|
708
|
-
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.
|
709
|
-
|
710
|
-
Subscriptions that require SCA are marked as `incomplete` by default.
|
711
|
-
Once payment is authenticated, Stripe will send a webhook updating the
|
712
|
-
status of the subscription. You'll need to use the [Stripe CLI](https://github.com/stripe/stripe-cli) to forward
|
713
|
-
webhooks to your application to make sure your subscriptions work
|
714
|
-
correctly for SCA payments.
|
715
|
-
|
716
|
-
```bash
|
717
|
-
stripe listen --forward-to localhost:3000/pay/webhooks/stripe
|
718
|
-
```
|
719
|
-
|
720
|
-
You should use `stripe.confirmCardSetup` 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.confirmCardPayment` if you'd like to charge the customer immediately (think checking out of a shopping cart).
|
721
|
-
|
722
|
-
The Javascript also needs to have a PaymentIntent or SetupIntent created server-side and the ID passed into the Javascript to do this. That way it knows how to safely handle the card tokenization if it meets the SCA requirements.
|
723
|
-
|
724
|
-
**Payment Confirmations**
|
725
|
-
|
726
|
-
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.
|
727
|
-
|
728
|
-
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.
|
729
|
-
|
730
|
-
[<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)
|
731
|
-
|
732
|
-
If you use the default views for payment confirmations, and also have a Content Security Policy in place for your application, make sure to add the following domains to their respective configurations in your `content_security_policy.rb` (otherwise these views won't load properly):
|
733
|
-
|
734
|
-
* `style_src`: `https://unpkg.com`
|
735
|
-
* `script_src`: `https://cdn.jsdelivr.net` and `https://js.stripe.com`
|
736
|
-
* `frame_src`: `https://js.stripe.com`
|
737
|
-
|
738
|
-
#### Background jobs
|
739
|
-
|
740
|
-
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.
|
23
|
+
- Stripe ([SCA Compatible](https://stripe.com/docs/strong-customer-authentication) using API version `2020-08-27`)
|
24
|
+
- Paddle (SCA Compatible & supports PayPal)
|
25
|
+
- Braintree (supports PayPal)
|
26
|
+
- [Fake Processor](docs/fake_processor.md) (used for generic trials without cards, free subscriptions, testing, etc)
|
741
27
|
|
742
|
-
|
28
|
+
Want to add a new payment provider? Contributions are welcome.
|
743
29
|
|
30
|
+
> We make our best attempt to standardize the different payment providers. They function differently so keep that in mind if you plan on doing more complex payments. It would be best to stick witha single payment provider in that case so you don't run into discrepancies.
|
744
31
|
|
745
|
-
##
|
32
|
+
## 📚 Docs
|
746
33
|
|
747
|
-
|
748
|
-
|
34
|
+
* [Installation](docs/1_installation.md)
|
35
|
+
* [Configuration](docs/2_configuration.md)
|
36
|
+
* **Usage**
|
37
|
+
* [Customers](docs/3_customers.md)
|
38
|
+
* [Payment Methods](docs/4_payment_methods.md)
|
39
|
+
* [Charges](docs/5_charges.md)
|
40
|
+
* [Subscriptions](docs/6_subscriptions.md)
|
41
|
+
* [Routes & Webhooks](docs/7_webhooks.md)
|
42
|
+
* [Customizing Pay Models](docs/8_customizing_models.md)
|
749
43
|
|
750
|
-
|
44
|
+
* **Payment Processors**
|
45
|
+
* [Stripe](docs/stripe/1_overview.md)
|
46
|
+
* [Braintree](docs/braintree/1_overview.md)
|
47
|
+
* [Paddle](docs/paddle/1_overview.md)
|
48
|
+
* [Fake Processor](docs/fake_processor/1_overview.md)
|
49
|
+
* **Marketplaces**
|
50
|
+
* [Stripe Connect](docs/marketplaces/stripe_connect.md)
|
51
|
+
* **Contributing**
|
52
|
+
* [Adding A Payment Processor](docs/contributing/adding_a_payment_processor.md)
|
751
53
|
|
752
|
-
|
54
|
+
## 🙏 Contributing
|
753
55
|
|
754
56
|
If you have an issue you'd like to submit, please do so using the issue tracker in GitHub. In order for us to help you in the best way possible, please be as detailed as you can.
|
755
57
|
|
@@ -758,8 +60,9 @@ If you'd like to open a PR please make sure the following things pass:
|
|
758
60
|
```ruby
|
759
61
|
bin/rails db:test:prepare
|
760
62
|
bin/rails test
|
63
|
+
bundle exec standardrb
|
761
64
|
```
|
762
65
|
|
763
|
-
## License
|
66
|
+
## 📝 License
|
764
67
|
|
765
68
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|