pay 2.6.11 → 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 +35 -693
- 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 +1 -5
- data/app/models/pay/charge.rb +32 -17
- 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 +34 -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/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 +17 -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/adapter.rb +22 -0
- data/lib/pay/attributes.rb +74 -0
- data/lib/pay/billable/sync_customer.rb +30 -0
- data/lib/pay/braintree/billable.rb +130 -105
- data/lib/pay/braintree/payment_method.rb +33 -0
- data/lib/pay/braintree/subscription.rb +9 -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/env.rb +8 -0
- 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 -8
- data/lib/pay/fake_processor.rb +2 -1
- data/lib/pay/merchant.rb +37 -0
- 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 +49 -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 +23 -23
- 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 +82 -93
- data/lib/pay/stripe/charge.rb +65 -4
- data/lib/pay/stripe/merchant.rb +66 -0
- data/lib/pay/stripe/payment_method.rb +61 -0
- data/lib/pay/stripe/subscription.rb +91 -24
- data/lib/pay/stripe/webhooks/account_updated.rb +16 -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 +15 -0
- data/lib/pay/stripe/webhooks/checkout_session_completed.rb +15 -0
- 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 -14
- data/lib/pay/stripe/webhooks/payment_method_attached.rb +15 -0
- data/lib/pay/stripe/webhooks/payment_method_detached.rb +12 -0
- data/lib/pay/stripe/webhooks/payment_method_updated.rb +10 -4
- data/lib/pay/stripe/webhooks/subscription_created.rb +1 -35
- data/lib/pay/stripe/webhooks/subscription_deleted.rb +2 -9
- data/lib/pay/stripe/webhooks/subscription_renewing.rb +14 -6
- data/lib/pay/stripe/webhooks/subscription_updated.rb +1 -28
- data/lib/pay/stripe.rb +17 -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 +8 -57
- metadata +34 -36
- 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 -22
- data/db/migrate/20210423235138_add_currency_to_pay_charges.rb +0 -5
- data/lib/generators/active_record/pay_generator.rb +0 -58
- data/lib/generators/active_record/templates/migration.rb +0 -9
- data/lib/pay/billable/sync_email.rb +0 -40
- data/lib/pay/billable.rb +0 -168
@@ -0,0 +1,17 @@
|
|
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.public_send(Pay::Adapter.json_column_type, :pay_data)
|
9
|
+
t.datetime :trial_ends_at
|
10
|
+
t.string :card_type
|
11
|
+
t.string :card_last4
|
12
|
+
t.string :card_exp_month
|
13
|
+
t.string :card_exp_year
|
14
|
+
t.text :extra_billing_info
|
15
|
+
end
|
16
|
+
end
|
17
|
+
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
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators/named_base"
|
4
|
+
|
5
|
+
module Pay
|
6
|
+
module Generators
|
7
|
+
class MerchantGenerator < Rails::Generators::NamedBase
|
8
|
+
include Rails::Generators::ResourceHelpers
|
9
|
+
|
10
|
+
source_root File.expand_path("../templates", __FILE__)
|
11
|
+
|
12
|
+
desc "Generates a migration to add Pay::Merchant fields to a model."
|
13
|
+
|
14
|
+
hook_for :orm
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -3,12 +3,6 @@
|
|
3
3
|
module Pay
|
4
4
|
module Generators
|
5
5
|
module OrmHelpers
|
6
|
-
def model_contents
|
7
|
-
<<-CONTENT
|
8
|
-
include Pay::Billable
|
9
|
-
CONTENT
|
10
|
-
end
|
11
|
-
|
12
6
|
private
|
13
7
|
|
14
8
|
def model_exists?
|
@@ -30,6 +24,16 @@ module Pay
|
|
30
24
|
def model_path
|
31
25
|
@model_path ||= File.join("app", "models", "#{file_path}.rb")
|
32
26
|
end
|
27
|
+
|
28
|
+
def migration_version
|
29
|
+
if rails5_and_up?
|
30
|
+
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def rails5_and_up?
|
35
|
+
Rails::VERSION::MAJOR >= 5
|
36
|
+
end
|
33
37
|
end
|
34
38
|
end
|
35
39
|
end
|
data/lib/pay/adapter.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module Pay
|
2
|
+
module Adapter
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def self.current_adapter
|
6
|
+
if ActiveRecord::Base.respond_to?(:connection_db_config)
|
7
|
+
ActiveRecord::Base.connection_db_config.adapter
|
8
|
+
else
|
9
|
+
ActiveRecord::Base.connection_config[:adapter]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.json_column_type
|
14
|
+
case current_adapter
|
15
|
+
when "postgresql"
|
16
|
+
:jsonb
|
17
|
+
else
|
18
|
+
:json
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Pay
|
2
|
+
# Adds Pay methods to ActiveRecord models
|
3
|
+
|
4
|
+
module Attributes
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module CustomerExtension
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
has_many :pay_customers, class_name: "Pay::Customer", as: :owner, inverse_of: :owner
|
12
|
+
has_many :charges, through: :pay_customers, class_name: "Pay::Charge"
|
13
|
+
has_many :subscriptions, through: :pay_customers, class_name: "Pay::Subscription"
|
14
|
+
has_one :payment_processor, -> { where(default: true, deleted_at: nil) }, class_name: "Pay::Customer", as: :owner, inverse_of: :owner
|
15
|
+
|
16
|
+
after_commit :cancel_active_pay_subscriptions!, on: [:destroy]
|
17
|
+
end
|
18
|
+
|
19
|
+
# Changes a user's payment processor
|
20
|
+
#
|
21
|
+
# This has several effects:
|
22
|
+
# - Finds or creates a Pay::Customer for the process and marks it as default
|
23
|
+
# - Removes the default flag from all other Pay::Customers
|
24
|
+
# - Removes the default flag from all Pay::PaymentMethods
|
25
|
+
def set_payment_processor(processor_name, allow_fake: false, **attributes)
|
26
|
+
raise Pay::Error, "Processor `#{processor_name}` is not allowed" if processor_name.to_s == "fake_processor" && !allow_fake
|
27
|
+
|
28
|
+
ActiveRecord::Base.transaction do
|
29
|
+
pay_customers.update_all(default: false)
|
30
|
+
pay_customer = pay_customers.active.where(processor: processor_name).first_or_initialize
|
31
|
+
pay_customer.update!(attributes.merge(default: true))
|
32
|
+
end
|
33
|
+
|
34
|
+
# Return new payment processor
|
35
|
+
reload_payment_processor
|
36
|
+
end
|
37
|
+
|
38
|
+
def cancel_active_pay_subscriptions!
|
39
|
+
subscriptions.active.each(&:cancel_now!)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module MerchantExtension
|
44
|
+
extend ActiveSupport::Concern
|
45
|
+
|
46
|
+
included do
|
47
|
+
has_many :pay_merchants, class_name: "Pay::Merchant", as: :owner, inverse_of: :owner
|
48
|
+
has_one :merchant_processor, -> { where(default: true) }, class_name: "Pay::Merchant", as: :owner, inverse_of: :owner
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_merchant_processor(processor_name, **attributes)
|
52
|
+
ActiveRecord::Base.transaction do
|
53
|
+
pay_merchants.update_all(default: false)
|
54
|
+
pay_merchant = pay_merchants.where(processor: processor_name).first_or_initialize
|
55
|
+
pay_merchant.update!(attributes.merge(default: true))
|
56
|
+
end
|
57
|
+
|
58
|
+
# Return new payment processor
|
59
|
+
reload_merchant_processor
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class_methods do
|
64
|
+
def pay_customer
|
65
|
+
include Billable::SyncCustomer
|
66
|
+
include CustomerExtension
|
67
|
+
end
|
68
|
+
|
69
|
+
def pay_merchant
|
70
|
+
include MerchantExtension
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Pay
|
2
|
+
module Billable
|
3
|
+
module SyncCustomer
|
4
|
+
# Syncs customer details to the payment processor.
|
5
|
+
# This way they're kept in sync and email notifications are
|
6
|
+
# always sent to the correct email address after an update.
|
7
|
+
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
after_update :enqeue_sync_email_job, if: :pay_should_sync_customer?
|
12
|
+
end
|
13
|
+
|
14
|
+
def pay_should_sync_customer?
|
15
|
+
try(:saved_change_to_email?)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def enqeue_sync_email_job
|
21
|
+
if saved_change_to_email?
|
22
|
+
# Queue job to update each payment processor for this customer
|
23
|
+
pay_customers.pluck(:id).each do |pay_customer_id|
|
24
|
+
CustomerSyncJob.perform_later(id)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -1,40 +1,42 @@
|
|
1
1
|
module Pay
|
2
2
|
module Braintree
|
3
3
|
class Billable
|
4
|
-
attr_reader :
|
4
|
+
attr_reader :pay_customer
|
5
5
|
|
6
6
|
delegate :processor_id,
|
7
7
|
:processor_id?,
|
8
8
|
:email,
|
9
9
|
:customer_name,
|
10
|
-
:
|
11
|
-
|
10
|
+
:payment_method_token,
|
11
|
+
:payment_method_token?,
|
12
|
+
to: :pay_customer
|
12
13
|
|
13
|
-
def initialize(
|
14
|
-
@
|
14
|
+
def initialize(pay_customer)
|
15
|
+
@pay_customer = pay_customer
|
15
16
|
end
|
16
17
|
|
17
|
-
#
|
18
|
+
# Retrieve the Braintree::Customer object
|
18
19
|
#
|
19
|
-
#
|
20
|
+
# - If no processor_id is present, creates a Customer.
|
21
|
+
# - When 'payment_method_token' is present, it will also set the default payment method
|
20
22
|
def customer
|
21
23
|
if processor_id?
|
22
24
|
customer = gateway.customer.find(processor_id)
|
23
|
-
|
25
|
+
|
26
|
+
if payment_method_token?
|
27
|
+
add_payment_method(payment_method_token, default: true)
|
28
|
+
pay_customer.payment_method_token = nil
|
29
|
+
end
|
30
|
+
|
24
31
|
customer
|
25
32
|
else
|
26
|
-
result = gateway.customer.create(
|
27
|
-
email: email,
|
28
|
-
first_name: try(:first_name),
|
29
|
-
last_name: try(:last_name),
|
30
|
-
payment_method_nonce: card_token
|
31
|
-
)
|
33
|
+
result = gateway.customer.create(email: email, first_name: try(:first_name), last_name: try(:last_name), payment_method_nonce: payment_method_token)
|
32
34
|
raise Pay::Braintree::Error, result unless result.success?
|
35
|
+
pay_customer.update!(processor_id: result.customer.id)
|
33
36
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
update_card_on_file result.customer.payment_methods.last
|
37
|
+
if payment_method_token?
|
38
|
+
save_payment_method(result.customer.payment_methods.last, default: true)
|
39
|
+
pay_customer.payment_method_token = nil
|
38
40
|
end
|
39
41
|
|
40
42
|
result.customer
|
@@ -45,14 +47,12 @@ module Pay
|
|
45
47
|
raise Pay::Braintree::Error, e
|
46
48
|
end
|
47
49
|
|
48
|
-
# Handles Billable#charge
|
49
|
-
#
|
50
|
-
# Returns a Pay::Charge
|
51
50
|
def charge(amount, options = {})
|
52
51
|
args = {
|
53
52
|
amount: amount.to_i / 100.0,
|
54
53
|
customer_id: customer.id,
|
55
|
-
options: {submit_for_settlement: true}
|
54
|
+
options: {submit_for_settlement: true},
|
55
|
+
custom_fields: options.delete(:metadata)
|
56
56
|
}.merge(options)
|
57
57
|
|
58
58
|
result = gateway.transaction.sale(args)
|
@@ -65,9 +65,6 @@ module Pay
|
|
65
65
|
raise Pay::Braintree::Error, e
|
66
66
|
end
|
67
67
|
|
68
|
-
# Handles Billable#subscribe
|
69
|
-
#
|
70
|
-
# Returns Pay::Subscription
|
71
68
|
def subscribe(name: Pay.default_product_name, plan: Pay.default_plan_name, **options)
|
72
69
|
token = customer.payment_methods.find(&:default?).try(:token)
|
73
70
|
raise Pay::Error, "Customer has no default payment method" if token.nil?
|
@@ -77,6 +74,7 @@ module Pay
|
|
77
74
|
options.merge!(trial_period: true, trial_duration: trial_period_days, trial_duration_unit: :day)
|
78
75
|
end
|
79
76
|
|
77
|
+
metadata = options.delete(:metadata)
|
80
78
|
subscription_options = options.merge(
|
81
79
|
payment_method_token: token,
|
82
80
|
plan_id: plan
|
@@ -85,32 +83,44 @@ module Pay
|
|
85
83
|
result = gateway.subscription.create(subscription_options)
|
86
84
|
raise Pay::Braintree::Error, result unless result.success?
|
87
85
|
|
88
|
-
|
86
|
+
pay_customer.subscriptions.create!(
|
87
|
+
name: name,
|
88
|
+
processor_id: result.subscription.id,
|
89
|
+
processor_plan: plan,
|
90
|
+
status: :active,
|
91
|
+
trial_ends_at: trial_end_date(result.subscription),
|
92
|
+
ends_at: nil,
|
93
|
+
metadata: metadata
|
94
|
+
)
|
89
95
|
rescue ::Braintree::AuthorizationError => e
|
90
96
|
raise Pay::Braintree::AuthorizationError, e
|
91
97
|
rescue ::Braintree::BraintreeError => e
|
92
98
|
raise Pay::Braintree::Error, e
|
93
99
|
end
|
94
100
|
|
95
|
-
|
96
|
-
#
|
97
|
-
# Returns true if successful
|
98
|
-
def update_card(token)
|
101
|
+
def add_payment_method(token, default: false)
|
99
102
|
customer unless processor_id?
|
100
103
|
|
101
104
|
result = gateway.payment_method.create(
|
102
105
|
customer_id: processor_id,
|
103
106
|
payment_method_nonce: token,
|
104
107
|
options: {
|
105
|
-
make_default:
|
108
|
+
make_default: default,
|
106
109
|
verify_card: true
|
107
110
|
}
|
108
111
|
)
|
109
112
|
raise Pay::Braintree::Error, result unless result.success?
|
110
113
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
+
pay_payment_method = save_payment_method(result.payment_method, default: default)
|
115
|
+
|
116
|
+
# Update existing subscriptions to the new payment method
|
117
|
+
pay_customer.subscriptions.each do |subscription|
|
118
|
+
if subscription.active?
|
119
|
+
gateway.subscription.update(subscription.processor_id, {payment_method_token: token})
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
pay_payment_method
|
114
124
|
rescue ::Braintree::AuthorizationError => e
|
115
125
|
raise Pay::Braintree::AuthorizationError, e
|
116
126
|
rescue ::Braintree::BraintreeError => e
|
@@ -127,35 +137,26 @@ module Pay
|
|
127
137
|
subscription.first_billing_date.end_of_day
|
128
138
|
end
|
129
139
|
|
130
|
-
def update_subscriptions_to_payment_method(token)
|
131
|
-
billable.subscriptions.braintree.each do |subscription|
|
132
|
-
if subscription.active?
|
133
|
-
gateway.subscription.update(subscription.processor_id, {payment_method_token: token})
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
140
|
def processor_subscription(subscription_id, options = {})
|
139
141
|
gateway.subscription.find(subscription_id)
|
140
142
|
end
|
141
143
|
|
142
|
-
def braintree_invoice!(options = {})
|
143
|
-
# pass
|
144
|
-
end
|
145
|
-
|
146
|
-
def braintree_upcoming_invoice
|
147
|
-
# pass
|
148
|
-
end
|
149
|
-
|
150
144
|
def save_transaction(transaction)
|
151
145
|
attrs = card_details_for_braintree_transaction(transaction)
|
152
146
|
attrs[:amount] = transaction.amount.to_f * 100
|
147
|
+
attrs[:metadata] = transaction.custom_fields
|
148
|
+
attrs[:currency] = transaction.currency_iso_code
|
149
|
+
attrs[:application_fee_amount] = transaction.service_fee_amount
|
150
|
+
|
151
|
+
# Associate charge with subscription if we can
|
152
|
+
if transaction.subscription_id
|
153
|
+
pay_subscription = pay_customer.subscriptions.find_by(processor_id: transaction.subscription_id)
|
154
|
+
attrs[:subscription] = pay_subscription
|
155
|
+
attrs[:metadata] = pay_subscription.metadata
|
156
|
+
end
|
153
157
|
|
154
|
-
charge =
|
155
|
-
|
156
|
-
processor_id: transaction.id
|
157
|
-
)
|
158
|
-
charge.update(attrs)
|
158
|
+
charge = pay_customer.charges.find_or_initialize_by(processor_id: transaction.id)
|
159
|
+
charge.update!(attrs)
|
159
160
|
charge
|
160
161
|
end
|
161
162
|
|
@@ -165,74 +166,98 @@ module Pay
|
|
165
166
|
Pay.braintree_gateway
|
166
167
|
end
|
167
168
|
|
168
|
-
def
|
169
|
-
case payment_method
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
169
|
+
def save_payment_method(payment_method, default:)
|
170
|
+
attributes = case payment_method
|
171
|
+
when ::Braintree::CreditCard, ::Braintree::ApplePayCard, ::Braintree::GooglePayCard, ::Braintree::SamsungPayCard, ::Braintree::VisaCheckoutCard
|
172
|
+
{
|
173
|
+
payment_method_type: :card,
|
174
|
+
brand: payment_method.card_type,
|
175
|
+
last4: payment_method.last_4,
|
176
|
+
exp_month: payment_method.expiration_month,
|
177
|
+
exp_year: payment_method.expiration_year
|
178
|
+
}
|
179
|
+
|
180
|
+
when ::Braintree::PayPalAccount
|
181
|
+
{
|
182
|
+
payment_method_type: :paypal,
|
183
|
+
brand: "PayPal",
|
184
|
+
email: payment_method.email
|
185
|
+
}
|
186
|
+
when ::Braintree::VenmoAccount
|
187
|
+
{
|
188
|
+
payment_method_type: :venmo,
|
189
|
+
brand: "Venmo",
|
190
|
+
username: payment_method.username
|
191
|
+
}
|
192
|
+
when ::Braintree::UsBankAccount
|
193
|
+
{
|
194
|
+
payment_method_type: "us_bank_account",
|
195
|
+
bank: payment_method.bank_name,
|
196
|
+
last4: payment_method.last_4
|
197
|
+
}
|
198
|
+
else
|
199
|
+
{
|
200
|
+
payment_method_type: payment_method.class.name.demodulize.underscore
|
201
|
+
}
|
183
202
|
end
|
184
203
|
|
185
|
-
|
186
|
-
|
204
|
+
pay_payment_method = pay_customer.payment_methods.where(processor_id: payment_method.token).first_or_initialize
|
205
|
+
|
206
|
+
pay_customer.payment_methods.update_all(default: false) if default
|
207
|
+
pay_payment_method.update!(attributes.merge(default: default))
|
208
|
+
|
209
|
+
# Reload the Rails association
|
210
|
+
pay_customer.reload_default_payment_method if default
|
211
|
+
|
212
|
+
pay_payment_method
|
187
213
|
end
|
188
214
|
|
189
215
|
def card_details_for_braintree_transaction(transaction)
|
190
216
|
case transaction.payment_instrument_type
|
191
|
-
when "credit_card", "
|
192
|
-
|
193
|
-
|
194
|
-
card_type: payment_method.card_type,
|
195
|
-
card_last4: payment_method.last_4,
|
196
|
-
card_exp_month: payment_method.expiration_month,
|
197
|
-
card_exp_year: payment_method.expiration_year
|
198
|
-
}
|
217
|
+
when "android_pay_card", "apple_pay_card", "credit_card", "google_pay_card", "samsung_pay_card", "visa_checkout_card"
|
218
|
+
# Lookup the attribute with the payment method details by name
|
219
|
+
attribute_name = transaction.payment_instrument_type
|
199
220
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
221
|
+
# The attribute name for Apple and Google Pay don't include _card for some reason
|
222
|
+
if ["apple_pay_card", "google_pay_card"].include?(transaction.payment_instrument_type)
|
223
|
+
attribute_name = attribute_name.split("_card").first
|
224
|
+
|
225
|
+
# Android Pay was renamed to Google Pay, but test nonces still use android_pay_card
|
226
|
+
elsif attribute_name == "android_pay_card"
|
227
|
+
attribute_name = "google_pay"
|
228
|
+
end
|
229
|
+
|
230
|
+
# Retrieve payment method details from transaction
|
231
|
+
payment_method = transaction.send("#{attribute_name}_details")
|
207
232
|
|
208
|
-
when "android_pay_card"
|
209
|
-
payment_method = transaction.android_pay_details
|
210
233
|
{
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
234
|
+
payment_method_type: :card,
|
235
|
+
brand: payment_method.card_type,
|
236
|
+
last4: payment_method.last_4,
|
237
|
+
exp_month: payment_method.expiration_month,
|
238
|
+
exp_year: payment_method.expiration_year
|
215
239
|
}
|
216
240
|
|
217
|
-
when "
|
241
|
+
when "paypal_account"
|
218
242
|
{
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
243
|
+
payment_method_type: :paypal,
|
244
|
+
brand: "PayPal",
|
245
|
+
last4: transaction.paypal_details.payer_email,
|
246
|
+
exp_month: nil,
|
247
|
+
exp_year: nil
|
223
248
|
}
|
224
249
|
|
225
|
-
when "
|
226
|
-
payment_method = transaction.apple_pay_details
|
250
|
+
when "venmo_account"
|
227
251
|
{
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
252
|
+
payment_method_type: :venmo,
|
253
|
+
brand: "Venmo",
|
254
|
+
last4: transaction.venmo_account_details.username,
|
255
|
+
exp_month: nil,
|
256
|
+
exp_year: nil
|
232
257
|
}
|
233
258
|
|
234
259
|
else
|
235
|
-
{}
|
260
|
+
{payment_method_type: "unknown"}
|
236
261
|
end
|
237
262
|
end
|
238
263
|
end
|