pay 2.0.3 → 2.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 300877c7b5a4a24f3796a7fe32c6deff0c2851a9a43d2c30ea86b69892384865
4
- data.tar.gz: 6c6a7b986a3fe4fd2731dcf21ee9d3adb1a4fb05f6cc07060b51a0aa50566459
3
+ metadata.gz: c17e00d646e15b10c34d222479c5dc636c36b7eb5caa52ef8783613234c71408
4
+ data.tar.gz: 0f9eed7cf14a994b3aabbb45dbf635e297212a59d1e6ec6f30246f21de458b47
5
5
  SHA512:
6
- metadata.gz: 2b0b709fb6e3a3f51b3f74774908775af8a9ef3ab71ca8b2981c9fc010ede85cf0b71d779659e0e3dfb185ba20dcc16d90b71140992bc5e7241711be9334add2
7
- data.tar.gz: e22547e8e914cb70cc75d95bfd8414d1c5ca203e2cc7a8717416296c538b2123762499841b34aff655d74d7c7548a71d3f5de9f467af91acf6ee36d223f3b7d1
6
+ metadata.gz: 362c0ba3348eb01c07e7965262bdcbf71c7c19a0e5287899d5821223bc20658e5cb088e480064f0efa3d44d5a16eb0f4eca74513ba89fb1f91f2c790f4068252
7
+ data.tar.gz: e49bd76481889087ae37095717311a77662aa6c011e6bcd5d1b3c79917b6207f62074a6de07c19f579561d6d17fa86c0247d895b61f68bea71d18804a68edbe9
data/README.md CHANGED
@@ -1,10 +1,8 @@
1
- <p align="center"><img src="logo.png"></p>
1
+ <p align="center"><img src="docs/logo.svg" height="50px"></p>
2
2
 
3
- ## Pay
3
+ ## Pay - Payments engine for Ruby on Rails
4
4
 
5
- ### Payments engine for Ruby on Rails
6
-
7
- [![Build Status](https://github.com/excid3/pay/workflows/Tests/badge.svg)](https://github.com/excid3/pay/actions)
5
+ [![Build Status](https://github.com/pay-rails/pay/workflows/Tests/badge.svg)](https://github.com/pay-rails/pay/actions)
8
6
 
9
7
  Pay is a payments engine for Ruby on Rails 4.2 and higher.
10
8
 
@@ -15,6 +13,12 @@ Pay is a payments engine for Ruby on Rails 4.2 and higher.
15
13
 
16
14
  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
15
 
16
+ ## Tutorial
17
+
18
+ Want to see how Pay works? Check out our video getting started guide.
19
+
20
+ <a href="https://www.youtube.com/watch?v=hYlOmqyJIgc" target="_blank"><img width="50%" src="http://i3.ytimg.com/vi/hYlOmqyJIgc/maxresdefault.jpg"></a>
21
+
18
22
  ## Installation
19
23
 
20
24
  Add these lines to your application's Gemfile:
@@ -55,32 +59,17 @@ To add the migrations to your application, run the following migration:
55
59
 
56
60
  `$ bin/rails pay:install:migrations`
57
61
 
58
- This will install four migrations:
62
+ This will install three migrations:
59
63
 
60
64
  - db/migrate/create_subscriptions.pay.rb
61
- - db/migrate/add_fields_to_users.pay.rb
62
65
  - db/migrate/create_charges.pay.rb
63
66
  - db/migrate/add_status_to_subscriptions.pay.rb
64
67
 
65
- ### The Billable Module
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`.
68
+ You'll also need a model that can make payments. This is called a
69
+ `Billable` model. The `pay` generator will add fields to the model and
70
+ add the `Pay::Billable` module to it.
69
71
 
70
- If you'd like to use a different model, you can configure it in an
71
- initializer:
72
-
73
- ```ruby
74
- Pay.setup do |config|
75
- # Make the billable class the same name as your ActiveRecord model
76
- config.billable_class = "Team"
77
-
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
- ```
72
+ `$ bin/rails g pay User`
84
73
 
85
74
  #### Run the Migrations
86
75
 
@@ -92,78 +81,9 @@ Finally, run the migrations with `$ rake db:migrate`
92
81
 
93
82
  Fully restart your Rails application `bin/spring stop && rails s`
94
83
 
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
-
103
- #### Braintree
104
-
105
- ```yaml
106
- development:
107
- braintree:
108
- private_key: xxxx
109
- public_key: yyyy
110
- merchant_id: zzzz
111
- environment: sandbox
112
- ```
113
-
114
- #### Stripe
115
-
116
- You'll need to add your private Stripe API key to your Rails secrets `config/secrets.yml`, credentials `rails credentials:edit`
117
-
118
- ```yaml
119
- development:
120
- stripe:
121
- private_key: xxxx
122
- public_key: yyyy
123
- signing_secret: zzzz
124
- ```
125
-
126
- You can also use the `STRIPE_PRIVATE_KEY` and `STRIPE_SIGNING_SECRET` environment variables.
127
-
128
- **To see how to use Stripe Elements JS & Devise, [click here](https://github.com/jasoncharnes/pay/wiki/Using-Stripe-Elements-and-Devise).**
129
-
130
- ##### Strong Customer Authentication (SCA)
131
-
132
- 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.
133
-
134
- Subscriptions that require SCA are marked as `incomplete` by default.
135
- Once payment is authenticated, Stripe will send a webhook updating the
136
- status of the subscription. You'll need to use the [Stripe CLI](https://github.com/stripe/stripe-cli) to forward
137
- webhooks to your application to make sure your subscriptions work
138
- correctly for SCA payments.
139
-
140
- ```bash
141
- stripe listen --forward-to localhost:3000/pay/webhooks/stripe
142
- ```
143
-
144
- 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).
145
-
146
- The Javascript will now need to use createPaymentMethod instead of createToken. https://stripe.com/docs/js/payment_intents/create_payment_method
147
-
148
- 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.
149
-
150
- **Payment Confirmations**
151
-
152
- 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.
153
-
154
- 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.
155
-
156
- [<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)
157
-
158
- #### Background jobs
159
-
160
- 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.
161
-
162
- It's important you set a queue_adapter for this to happen. If you don't, the code will be executed immediately upon user update. [More information here](https://guides.rubyonrails.org/v4.2/active_job_basics.html#backends)
163
-
164
84
  ## Usage
165
85
 
166
- Include the `Pay::Billable` module in the model you want to know about subscriptions.
86
+ The `Pay::Billable` module should be included in the models you want to make payments and subscriptions.
167
87
 
168
88
  ```ruby
169
89
  # app/models/user.rb
@@ -180,9 +100,6 @@ Need to make some changes to how Pay is used? You can create an initializer `con
180
100
 
181
101
  ```ruby
182
102
  Pay.setup do |config|
183
- config.billable_class = 'User'
184
- config.billable_table = 'users'
185
-
186
103
  config.chargeable_class = 'Pay::Charge'
187
104
  config.chargeable_table = 'pay_charges'
188
105
 
@@ -255,6 +172,7 @@ Emails can be enabled/disabled using the `send_emails` configuration option (ena
255
172
  - When a charge was refunded
256
173
  - When a subscription is about to renew
257
174
 
175
+
258
176
  ## Billable API
259
177
 
260
178
  #### Trials
@@ -555,6 +473,75 @@ If you just want to modify where the engine mounts it's routes then you can chan
555
473
  config.routes_path = '/secret-webhook-path'
556
474
  ```
557
475
 
476
+ ## Payment Providers
477
+
478
+ We support both Stripe and Braintree and make our best attempt to
479
+ standardize the two. They function differently so keep that in mind if
480
+ you plan on doing more complex payments. It would be best to stick with
481
+ a single payment provider in that case so you don't run into
482
+ discrepancies.
483
+
484
+ #### Braintree
485
+
486
+ ```yaml
487
+ development:
488
+ braintree:
489
+ private_key: xxxx
490
+ public_key: yyyy
491
+ merchant_id: zzzz
492
+ environment: sandbox
493
+ ```
494
+
495
+ #### Stripe
496
+
497
+ You'll need to add your private Stripe API key to your Rails secrets `config/secrets.yml`, credentials `rails credentials:edit`
498
+
499
+ ```yaml
500
+ development:
501
+ stripe:
502
+ private_key: xxxx
503
+ public_key: yyyy
504
+ signing_secret: zzzz
505
+ ```
506
+
507
+ You can also use the `STRIPE_PRIVATE_KEY` and `STRIPE_SIGNING_SECRET` environment variables.
508
+
509
+ **To see how to use Stripe Elements JS & Devise, [click here](https://github.com/jasoncharnes/pay/wiki/Using-Stripe-Elements-and-Devise).**
510
+
511
+ ##### Strong Customer Authentication (SCA)
512
+
513
+ 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.
514
+
515
+ Subscriptions that require SCA are marked as `incomplete` by default.
516
+ Once payment is authenticated, Stripe will send a webhook updating the
517
+ status of the subscription. You'll need to use the [Stripe CLI](https://github.com/stripe/stripe-cli) to forward
518
+ webhooks to your application to make sure your subscriptions work
519
+ correctly for SCA payments.
520
+
521
+ ```bash
522
+ stripe listen --forward-to localhost:3000/pay/webhooks/stripe
523
+ ```
524
+
525
+ 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).
526
+
527
+ The Javascript will now need to use createPaymentMethod instead of createToken. https://stripe.com/docs/js/payment_intents/create_payment_method
528
+
529
+ 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.
530
+
531
+ **Payment Confirmations**
532
+
533
+ 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.
534
+
535
+ 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.
536
+
537
+ [<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)
538
+
539
+ #### Background jobs
540
+
541
+ 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.
542
+
543
+ It's important you set a queue_adapter for this to happen. If you don't, the code will be executed immediately upon user update. [More information here](https://guides.rubyonrails.org/v4.2/active_job_basics.html#backends)
544
+
558
545
  ## Contributors
559
546
 
560
547
  - [Jason Charnes](https://twitter.com/jmcharnes)
@@ -16,7 +16,7 @@ module Pay
16
16
  end
17
17
 
18
18
  render json: {success: true}, status: :ok
19
- rescue ::Braintree::InvalidSignature => e
19
+ rescue ::Braintree::InvalidSignature
20
20
  head :ok
21
21
  end
22
22
 
@@ -29,11 +29,11 @@ module Pay
29
29
  pay_subscription = Pay.subscription_model.find_by(processor: :braintree, processor_id: subscription.id)
30
30
  return unless pay_subscription.present?
31
31
 
32
- user = pay_subscription.owner
33
- charge = user.save_braintree_transaction(subscription.transactions.first)
32
+ billable = pay_subscription.owner
33
+ charge = billable.save_braintree_transaction(subscription.transactions.first)
34
34
 
35
35
  if Pay.send_emails
36
- Pay::UserMailer.receipt(user, charge).deliver_later
36
+ Pay::UserMailer.receipt(billable, charge).deliver_later
37
37
  end
38
38
  end
39
39
 
@@ -41,11 +41,14 @@ module Pay
41
41
  subscription = event.subscription
42
42
  return if subscription.nil?
43
43
 
44
- user = Pay.user_model.find_by(processor: :braintree, processor_id: subscription.id)
45
- return unless user.present?
44
+ pay_subscription = Pay.subscription_model.find_by(processor: :braintree, processor_id: subscription.id)
45
+ return unless pay_subscription.present?
46
+
47
+ billable = pay_subscription.owner
48
+ return if billable.nil?
46
49
 
47
50
  # User canceled or failed to make payments
48
- user.update(braintree_subscription_id: nil)
51
+ billable.update(braintree_subscription_id: nil)
49
52
  end
50
53
 
51
54
  def subscription_trial_ended(event)
@@ -2,11 +2,11 @@ module Pay
2
2
  class EmailSyncJob < ApplicationJob
3
3
  queue_as :default
4
4
 
5
- def perform(id)
6
- billable = Pay.user_model.find(id)
5
+ def perform(id, class_name)
6
+ billable = class_name.constantize.find(id)
7
7
  billable.sync_email_with_processor
8
8
  rescue ActiveRecord::RecordNotFound
9
- Rails.logger.info "Couldn't find a #{Pay.billable_class} with ID = #{id}"
9
+ Rails.logger.info "Couldn't find a #{class_name} with ID = #{id}"
10
10
  end
11
11
  end
12
12
  end
@@ -9,7 +9,7 @@ module Pay
9
9
 
10
10
  mail(
11
11
  to: to(user),
12
- subject: Pay.email_receipt_subject,
12
+ subject: Pay.email_receipt_subject
13
13
  )
14
14
  end
15
15
 
@@ -18,7 +18,7 @@ module Pay
18
18
 
19
19
  mail(
20
20
  to: to(user),
21
- subject: Pay.email_refund_subject,
21
+ subject: Pay.email_refund_subject
22
22
  )
23
23
  end
24
24
 
@@ -27,7 +27,7 @@ module Pay
27
27
 
28
28
  mail(
29
29
  to: to(user),
30
- subject: Pay.email_renewing_subject,
30
+ subject: Pay.email_renewing_subject
31
31
  )
32
32
  end
33
33
 
@@ -3,7 +3,7 @@ module Pay
3
3
  self.table_name = Pay.chargeable_table
4
4
 
5
5
  # Associations
6
- belongs_to :owner, class_name: Pay.billable_class, foreign_key: :owner_id
6
+ belongs_to :owner, polymorphic: true
7
7
 
8
8
  # Scopes
9
9
  scope :sorted, -> { order(created_at: :desc) }
@@ -5,7 +5,7 @@ module Pay
5
5
  STATUSES = %w[incomplete incomplete_expired trialing active past_due canceled unpaid]
6
6
 
7
7
  # Associations
8
- belongs_to :owner, class_name: Pay.billable_class, foreign_key: :owner_id
8
+ belongs_to :owner, polymorphic: true
9
9
 
10
10
  # Validations
11
11
  validates :name, presence: true
@@ -1,7 +1,7 @@
1
1
  class CreatePayCharges < ActiveRecord::Migration[4.2]
2
2
  def change
3
3
  create_table :pay_charges do |t|
4
- t.references :owner
4
+ t.references :owner, polymorphic: true
5
5
  t.string :processor, null: false
6
6
  t.string :processor_id, null: false
7
7
  t.integer :amount, null: false
@@ -0,0 +1,58 @@
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 PayGenerator < 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 "migration.rb", "#{migration_path}/add_pay_billable_to_#{table_name}.rb", migration_version: migration_version
15
+ end
16
+ # TODO: Throw error here that model should already exist if it doesn't
17
+ end
18
+
19
+ def inject_pay_billable_content
20
+ content = model_contents
21
+
22
+ class_path = if namespaced?
23
+ class_name.to_s.split("::")
24
+ else
25
+ [class_name]
26
+ end
27
+
28
+ indent_depth = class_path.size - 1
29
+ content = content.split("\n").map { |line| " " * indent_depth + line } .join("\n") << "\n"
30
+
31
+ inject_into_class(model_path, class_path.last, content) if model_exists?
32
+ end
33
+
34
+ def migration_data
35
+ <<RUBY
36
+ t.string :processor
37
+ t.string :processor_id
38
+ t.datetime :trial_ends_at
39
+ t.string :card_type
40
+ t.string :card_last4
41
+ t.string :card_exp_month
42
+ t.string :card_exp_year
43
+ t.text :extra_billing_info
44
+ RUBY
45
+ end
46
+
47
+ def rails5_and_up?
48
+ Rails::VERSION::MAJOR >= 5
49
+ end
50
+
51
+ def migration_version
52
+ if rails5_and_up?
53
+ "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddPayTo<%= table_name.camelize %> < ActiveRecord::Migration<%= migration_version %>
4
+ def change
5
+ change_table :<%= table_name %> do |t|
6
+ <%= migration_data -%>
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pay
4
+ module Generators
5
+ module OrmHelpers
6
+ def model_contents
7
+ buffer = <<-CONTENT
8
+ include Pay::Billable
9
+ CONTENT
10
+ buffer
11
+ end
12
+
13
+ private
14
+
15
+ def model_exists?
16
+ File.exist?(File.join(destination_root, model_path))
17
+ end
18
+
19
+ def migration_exists?(table_name)
20
+ Dir.glob("#{File.join(destination_root, migration_path)}/[0-9]*_*.rb").grep(/\d+_add_devise_to_#{table_name}.rb$/).first
21
+ end
22
+
23
+ def migration_path
24
+ if Rails.version >= "5.0.3"
25
+ db_migrate_path
26
+ else
27
+ @migration_path ||= File.join("db", "migrate")
28
+ end
29
+ end
30
+
31
+ def model_path
32
+ @model_path ||= File.join("app", "models", "#{file_path}.rb")
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/named_base"
4
+
5
+ module Pay
6
+ module Generators
7
+ class PayGenerator < Rails::Generators::NamedBase
8
+ include Rails::Generators::ResourceHelpers
9
+
10
+ namespace "pay"
11
+ source_root File.expand_path("../templates", __FILE__)
12
+
13
+ desc "Generates a migration to add Billable fields to a model."
14
+
15
+ hook_for :orm
16
+ end
17
+ end
18
+ end
data/lib/pay.rb CHANGED
@@ -51,7 +51,23 @@ module Pay
51
51
  yield self
52
52
  end
53
53
 
54
+ def self.billable_models
55
+ Pay::Billable.includers
56
+ end
57
+
58
+ def self.find_billable(processor:, processor_id:)
59
+ billable_models.each do |model|
60
+ if (record = model.find_by(processor: processor, processor_id: processor_id))
61
+ return record
62
+ end
63
+ end
64
+
65
+ nil
66
+ end
67
+
54
68
  def self.user_model
69
+ ActiveSupport::Deprecation.warn("Pay.user_model is deprecated and will be removed in v3. Instead, use `Pay.billable_models` now to support more than one billable model.")
70
+
55
71
  if Rails.application.config.cache_classes
56
72
  @@user_model ||= billable_class.constantize
57
73
  else
@@ -4,8 +4,21 @@ module Pay
4
4
  module Billable
5
5
  extend ActiveSupport::Concern
6
6
 
7
- included do
7
+ # Keep track of which Billable models we have
8
+ class << self
9
+ attr_reader :includers
10
+ end
11
+
12
+ def self.included(base = nil, &block)
13
+ @includers ||= []
14
+ @includers << base if base
15
+ super
16
+ end
17
+
18
+ included do |base|
8
19
  include Pay::Billable::SyncEmail
20
+ include Pay::Stripe::Billable if defined? ::Stripe
21
+ include Pay::Braintree::Billable if defined? ::Braintree
9
22
 
10
23
  has_many :charges, class_name: Pay.chargeable_class, foreign_key: :owner_id, inverse_of: :owner
11
24
  has_many :subscriptions, class_name: Pay.subscription_class, foreign_key: :owner_id, inverse_of: :owner
@@ -112,7 +125,7 @@ module Pay
112
125
  private
113
126
 
114
127
  def check_for_processor
115
- raise StandardError, "No payment processor selected. Make sure to set the #{Pay.billable_class}'s `processor` attribute to either 'stripe' or 'braintree'." unless processor
128
+ raise StandardError, "No payment processor selected. Make sure to set the #{self.class.name}'s `processor` attribute to either 'stripe' or 'braintree'." unless processor
116
129
  end
117
130
 
118
131
  # Used for creating a Pay::Subscription in the database
@@ -125,7 +138,7 @@ module Pay
125
138
  processor_id: subscription.id,
126
139
  processor_plan: plan,
127
140
  trial_ends_at: send("#{processor}_trial_end_date", subscription),
128
- ends_at: nil,
141
+ ends_at: nil
129
142
  )
130
143
  subscriptions.create!(options)
131
144
  end
@@ -32,7 +32,7 @@ module Pay
32
32
  # Only update if the processor id is the same
33
33
  # This prevents duplicate API hits if this is their first time
34
34
  if processor_id? && !saved_change_to_processor_id? && saved_change_to_email?
35
- EmailSyncJob.perform_later(id)
35
+ EmailSyncJob.perform_later(id, self.class.name)
36
36
  end
37
37
  end
38
38
  end
@@ -19,7 +19,7 @@ module Pay
19
19
 
20
20
  Pay.charge_model.include Pay::Braintree::Charge
21
21
  Pay.subscription_model.include Pay::Braintree::Subscription
22
- Pay.user_model.include Pay::Braintree::Billable
22
+ Pay.billable_models.each { |model| model.include Pay::Stripe::Billable }
23
23
  end
24
24
 
25
25
  def public_key
@@ -12,7 +12,7 @@ module Pay
12
12
  email: email,
13
13
  first_name: try(:first_name),
14
14
  last_name: try(:last_name),
15
- payment_method_nonce: card_token,
15
+ payment_method_nonce: card_token
16
16
  )
17
17
  raise Pay::Error.new(result.message) unless result.success?
18
18
 
@@ -35,7 +35,7 @@ module Pay
35
35
  args = {
36
36
  amount: amount / 100.0,
37
37
  customer_id: customer.id,
38
- options: {submit_for_settlement: true},
38
+ options: {submit_for_settlement: true}
39
39
  }.merge(options)
40
40
 
41
41
  result = gateway.transaction.sale(args)
@@ -78,7 +78,7 @@ module Pay
78
78
  payment_method_nonce: token,
79
79
  options: {
80
80
  make_default: true,
81
- verify_card: true,
81
+ verify_card: true
82
82
  }
83
83
  )
84
84
  raise Pay::Error.new(result.message) unless result.success?
@@ -94,7 +94,7 @@ module Pay
94
94
  braintree_customer.update(
95
95
  email: email,
96
96
  first_name: try(:first_name),
97
- last_name: try(:last_name),
97
+ last_name: try(:last_name)
98
98
  )
99
99
  end
100
100
 
@@ -171,7 +171,7 @@ module Pay
171
171
  card_type: payment_method.card_type,
172
172
  card_last4: payment_method.last_4,
173
173
  card_exp_month: payment_method.expiration_month,
174
- card_exp_year: payment_method.expiration_year,
174
+ card_exp_year: payment_method.expiration_year
175
175
  }
176
176
 
177
177
  when "paypal_account"
@@ -179,7 +179,7 @@ module Pay
179
179
  card_type: "PayPal",
180
180
  card_last4: transaction.paypal_details.payer_email,
181
181
  card_exp_month: nil,
182
- card_exp_year: nil,
182
+ card_exp_year: nil
183
183
  }
184
184
 
185
185
  when "android_pay_card"
@@ -188,7 +188,7 @@ module Pay
188
188
  card_type: payment_method.source_card_type,
189
189
  card_last4: payment_method.source_card_last_4,
190
190
  card_exp_month: payment_method.expiration_month,
191
- card_exp_year: payment_method.expiration_year,
191
+ card_exp_year: payment_method.expiration_year
192
192
  }
193
193
 
194
194
  when "venmo_account"
@@ -196,7 +196,7 @@ module Pay
196
196
  card_type: "Venmo",
197
197
  card_last4: transaction.venmo_account_details.username,
198
198
  card_exp_month: nil,
199
- card_exp_year: nil,
199
+ card_exp_year: nil
200
200
  }
201
201
 
202
202
  when "apple_pay_card"
@@ -205,7 +205,7 @@ module Pay
205
205
  card_type: payment_method.card_type,
206
206
  card_last4: payment_method.last_4,
207
207
  card_exp_month: payment_method.expiration_month,
208
- card_exp_year: payment_method.expiration_year,
208
+ card_exp_year: payment_method.expiration_year
209
209
  }
210
210
 
211
211
  else
@@ -13,7 +13,7 @@ module Pay
13
13
  update(status: :canceled, ends_at: trial_ends_at)
14
14
  else
15
15
  gateway.subscription.update(subscription.id, {
16
- number_of_billing_cycles: subscription.current_billing_cycle,
16
+ number_of_billing_cycles: subscription.current_billing_cycle
17
17
  })
18
18
  update(status: :canceled, ends_at: subscription.billing_period_end_date.to_date)
19
19
  end
@@ -45,7 +45,7 @@ module Pay
45
45
 
46
46
  gateway.subscription.update(subscription.id, {
47
47
  never_expires: true,
48
- number_of_billing_cycles: nil,
48
+ number_of_billing_cycles: nil
49
49
  })
50
50
  end
51
51
 
@@ -80,8 +80,8 @@ module Pay
80
80
  never_expires: true,
81
81
  number_of_billing_cycles: nil,
82
82
  options: {
83
- prorate_charges: prorate?,
84
- },
83
+ prorate_charges: prorate?
84
+ }
85
85
  })
86
86
 
87
87
  if result.success?
@@ -159,10 +159,10 @@ module Pay
159
159
  {
160
160
  inherited_from_id: "plan-credit",
161
161
  amount: discount.amount,
162
- number_of_billing_cycles: discount.number_of_billing_cycles,
163
- },
164
- ],
165
- },
162
+ number_of_billing_cycles: discount.number_of_billing_cycles
163
+ }
164
+ ]
165
+ }
166
166
  }
167
167
  end
168
168
 
@@ -20,7 +20,7 @@ module Pay
20
20
  company: {
21
21
  name: Pay.business_name,
22
22
  address: Pay.business_address,
23
- email: Pay.support_email,
23
+ email: Pay.support_email
24
24
  },
25
25
  line_items: line_items
26
26
  )
@@ -32,7 +32,7 @@ module Pay
32
32
  ["Account Billed", "#{owner.name} (#{owner.email})"],
33
33
  ["Product", product],
34
34
  ["Amount", ActionController::Base.helpers.number_to_currency(amount / 100.0)],
35
- ["Charged to", charged_to],
35
+ ["Charged to", charged_to]
36
36
  ]
37
37
  line_items << ["Additional Info", owner.extra_billing_info] if owner.extra_billing_info?
38
38
  line_items
@@ -16,7 +16,6 @@ module Pay
16
16
 
17
17
  Pay.charge_model.include Pay::Stripe::Charge
18
18
  Pay.subscription_model.include Pay::Stripe::Subscription
19
- Pay.user_model.include Pay::Stripe::Billable
20
19
  end
21
20
 
22
21
  def public_key
@@ -17,7 +17,7 @@ module Pay
17
17
  def create_setup_intent
18
18
  ::Stripe::SetupIntent.create(
19
19
  customer: processor_id,
20
- usage: :off_session,
20
+ usage: :off_session
21
21
  )
22
22
  end
23
23
 
@@ -32,7 +32,7 @@ module Pay
32
32
  confirmation_method: :automatic,
33
33
  currency: "usd",
34
34
  customer: customer.id,
35
- payment_method: customer.invoice_settings.default_payment_method,
35
+ payment_method: customer.invoice_settings.default_payment_method
36
36
  }.merge(options)
37
37
 
38
38
  payment_intent = ::Stripe::PaymentIntent.create(args)
@@ -51,7 +51,7 @@ module Pay
51
51
  opts = {
52
52
  expand: ["pending_setup_intent", "latest_invoice.payment_intent"],
53
53
  items: [plan: plan],
54
- off_session: true,
54
+ off_session: true
55
55
  }.merge(options)
56
56
 
57
57
  # Inherit trial from plan unless trial override was specified
@@ -1,5 +1,5 @@
1
1
  require "stripe_event"
2
- Dir[File.join(__dir__, "webhooks", "**", "*.rb")].each { |file| require file }
2
+ Dir[File.join(__dir__, "webhooks", "**", "*.rb")].sort.each { |file| require file }
3
3
 
4
4
  StripeEvent.configure do |events|
5
5
  # Listen to the charge event to make sure we get non-subscription
@@ -4,23 +4,20 @@ module Pay
4
4
  class ChargeSucceeded
5
5
  def call(event)
6
6
  object = event.data.object
7
- user = Pay.user_model.find_by(
8
- processor: :stripe,
9
- processor_id: object.customer
10
- )
7
+ billable = Pay.find_billable(processor: :stripe, processor_id: object.customer)
11
8
 
12
- return unless user.present?
13
- return if user.charges.where(processor_id: object.id).any?
9
+ return unless billable.present?
10
+ return if billable.charges.where(processor_id: object.id).any?
14
11
 
15
- charge = create_charge(user, object)
16
- notify_user(user, charge)
12
+ charge = create_charge(billable, object)
13
+ notify_user(billable, charge)
17
14
  charge
18
15
  end
19
16
 
20
17
  def create_charge(user, object)
21
18
  charge = user.charges.find_or_initialize_by(
22
19
  processor: :stripe,
23
- processor_id: object.id,
20
+ processor_id: object.id
24
21
  )
25
22
 
26
23
  charge.update(
@@ -4,23 +4,23 @@ module Pay
4
4
  class CustomerDeleted
5
5
  def call(event)
6
6
  object = event.data.object
7
- user = Pay.user_model.find_by(processor: :stripe, processor_id: object.id)
7
+ billable = Pay.find_billable(processor: :stripe, processor_id: object.id)
8
8
 
9
9
  # Couldn't find user, we can skip
10
- return unless user.present?
10
+ return unless billable.present?
11
11
 
12
- user.update(
12
+ billable.update(
13
13
  processor_id: nil,
14
14
  trial_ends_at: nil,
15
15
  card_type: nil,
16
16
  card_last4: nil,
17
17
  card_exp_month: nil,
18
- card_exp_year: nil,
18
+ card_exp_year: nil
19
19
  )
20
20
 
21
- user.subscriptions.update_all(
21
+ billable.subscriptions.update_all(
22
22
  trial_ends_at: nil,
23
- ends_at: Time.zone.now,
23
+ ends_at: Time.zone.now
24
24
  )
25
25
  end
26
26
  end
@@ -4,12 +4,12 @@ module Pay
4
4
  class CustomerUpdated
5
5
  def call(event)
6
6
  object = event.data.object
7
- user = Pay.user_model.find_by(processor: :stripe, processor_id: object.id)
7
+ billable = Pay.find_billable(processor: :stripe, processor_id: object.id)
8
8
 
9
9
  # Couldn't find user, we can skip
10
- return unless user.present?
10
+ return unless billable.present?
11
11
 
12
- user.sync_card_from_stripe
12
+ billable.sync_card_from_stripe
13
13
  end
14
14
  end
15
15
  end
@@ -4,12 +4,12 @@ module Pay
4
4
  class PaymentMethodUpdated
5
5
  def call(event)
6
6
  object = event.data.object
7
- user = Pay.user_model.find_by(processor: :stripe, processor_id: object.customer)
7
+ billable = Pay.find_billable(processor: :stripe, processor_id: object.customer)
8
8
 
9
9
  # Couldn't find user, we can skip
10
- return unless user.present?
10
+ return unless billable.present?
11
11
 
12
- user.sync_card_from_stripe
12
+ billable.sync_card_from_stripe
13
13
  end
14
14
  end
15
15
  end
@@ -8,12 +8,15 @@ module Pay
8
8
  # We may already have the subscription in the database, so we can update that record
9
9
  subscription = Pay.subscription_model.find_by(processor: :stripe, processor_id: object.id)
10
10
 
11
+ # Create the subscription in the database if we don't have it already
11
12
  if subscription.nil?
12
13
  # The customer should already be in the database
13
- owner = Pay.user_model.find_by(processor: :stripe, processor_id: object.customer)
14
+ owner = Pay.find_billable(processor: :stripe, processor_id: object.customer)
14
15
 
15
- Rails.logger.error("[Pay] Unable to find #{Pay.user_model} with processor: :stripe and processor_id: '#{object.customer}'")
16
- return if owner.nil?
16
+ if owner.nil?
17
+ Rails.logger.error("[Pay] Unable to find Pay::Billable with processor: :stripe and processor_id: '#{object.customer}'. Searched these models: #{Pay.billable_models.join(", ")}")
18
+ return
19
+ end
17
20
 
18
21
  subscription = Pay.subscription_model.new(owner: owner)
19
22
  end
@@ -1,3 +1,3 @@
1
1
  module Pay
2
- VERSION = "2.0.3"
2
+ VERSION = "2.1.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: 2.0.3
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Charnes
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-03-10 00:00:00.000000000 Z
12
+ date: 2020-03-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -236,7 +236,11 @@ files:
236
236
  - db/migrate/20170503131610_add_fields_to_billable.rb
237
237
  - db/migrate/20170727235816_create_pay_charges.rb
238
238
  - db/migrate/20190816015720_add_status_to_pay_subscriptions.rb
239
+ - lib/generators/active_record/pay_generator.rb
240
+ - lib/generators/active_record/templates/migration.rb
239
241
  - lib/generators/pay/email_views_generator.rb
242
+ - lib/generators/pay/orm_helpers.rb
243
+ - lib/generators/pay/pay_generator.rb
240
244
  - lib/generators/pay/views_generator.rb
241
245
  - lib/pay.rb
242
246
  - lib/pay/billable.rb