pay 1.0.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pay might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +99 -31
- data/Rakefile +19 -19
- data/app/controllers/pay/payments_controller.rb +7 -0
- data/app/controllers/pay/webhooks/braintree_controller.rb +19 -6
- data/app/mailers/pay/application_mailer.rb +2 -2
- data/app/mailers/pay/user_mailer.rb +12 -0
- data/app/models/pay/subscription.rb +41 -10
- data/app/views/layouts/pay/application.html.erb +11 -5
- data/app/views/pay/payments/show.html.erb +134 -0
- data/app/views/pay/user_mailer/payment_action_required.html.erb +6 -0
- data/config/locales/en.yml +17 -0
- data/config/routes.rb +3 -2
- data/db/migrate/20190816015720_add_status_to_subscriptions.rb +14 -0
- data/lib/generators/pay/email_views_generator.rb +3 -3
- data/lib/generators/pay/views_generator.rb +13 -0
- data/lib/pay/billable/sync_email.rb +3 -4
- data/lib/pay/billable.rb +25 -18
- data/lib/pay/braintree/billable.rb +88 -82
- data/lib/pay/braintree/charge.rb +0 -2
- data/lib/pay/braintree/subscription.rb +74 -72
- data/lib/pay/braintree.rb +6 -6
- data/lib/pay/engine.rb +10 -10
- data/lib/pay/env.rb +0 -1
- data/lib/pay/payment.rb +52 -0
- data/lib/pay/receipts.rb +7 -7
- data/lib/pay/stripe/billable.rb +68 -37
- data/lib/pay/stripe/charge.rb +0 -2
- data/lib/pay/stripe/subscription.rb +7 -6
- data/lib/pay/stripe/webhooks/charge_refunded.rb +0 -2
- data/lib/pay/stripe/webhooks/charge_succeeded.rb +9 -10
- data/lib/pay/stripe/webhooks/customer_deleted.rb +5 -7
- data/lib/pay/stripe/webhooks/customer_updated.rb +0 -2
- data/lib/pay/stripe/webhooks/payment_action_required.rb +27 -0
- data/lib/pay/stripe/webhooks/{source_deleted.rb → payment_method_updated.rb} +1 -3
- data/lib/pay/stripe/webhooks/subscription_created.rb +6 -11
- data/lib/pay/stripe/webhooks/subscription_deleted.rb +1 -3
- data/lib/pay/stripe/webhooks/subscription_renewing.rb +0 -2
- data/lib/pay/stripe/webhooks/subscription_updated.rb +13 -10
- data/lib/pay/stripe/webhooks.rb +17 -11
- data/lib/pay/stripe.rb +5 -5
- data/lib/pay/version.rb +1 -1
- data/lib/pay.rb +42 -15
- metadata +27 -39
@@ -10,12 +10,12 @@ module Pay
|
|
10
10
|
|
11
11
|
if on_trial?
|
12
12
|
gateway.subscription.cancel(processor_subscription.id)
|
13
|
-
update(ends_at: trial_ends_at)
|
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
|
-
update(ends_at: subscription.billing_period_end_date)
|
18
|
+
update(status: :canceled, ends_at: subscription.billing_period_end_date.to_date)
|
19
19
|
end
|
20
20
|
rescue ::Braintree::BraintreeError => e
|
21
21
|
raise Error, e.message
|
@@ -23,13 +23,13 @@ module Pay
|
|
23
23
|
|
24
24
|
def braintree_cancel_now!
|
25
25
|
gateway.subscription.cancel(processor_subscription.id)
|
26
|
-
update(ends_at: Time.zone.now)
|
26
|
+
update(status: :canceled, ends_at: Time.zone.now)
|
27
27
|
rescue ::Braintree::BraintreeError => e
|
28
28
|
raise Error, e.message
|
29
29
|
end
|
30
30
|
|
31
31
|
def braintree_resume
|
32
|
-
if
|
32
|
+
if canceled? && on_trial?
|
33
33
|
duration = trial_ends_at.to_date - Date.today
|
34
34
|
|
35
35
|
owner.subscribe(
|
@@ -45,9 +45,11 @@ 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
|
+
|
52
|
+
update(status: :active)
|
51
53
|
rescue ::Braintree::BraintreeError => e
|
52
54
|
raise Error, e.message
|
53
55
|
end
|
@@ -58,8 +60,8 @@ module Pay
|
|
58
60
|
return
|
59
61
|
end
|
60
62
|
|
61
|
-
|
62
|
-
owner.subscribe(name, plan
|
63
|
+
unless active?
|
64
|
+
owner.subscribe(name: name, plan: plan, trial_period: false)
|
63
65
|
return
|
64
66
|
end
|
65
67
|
|
@@ -79,11 +81,11 @@ module Pay
|
|
79
81
|
number_of_billing_cycles: nil,
|
80
82
|
options: {
|
81
83
|
prorate_charges: prorate?,
|
82
|
-
}
|
84
|
+
},
|
83
85
|
})
|
84
86
|
|
85
87
|
if result.success?
|
86
|
-
update(processor_plan: braintree_plan.id, ends_at: nil)
|
88
|
+
update(status: :active, processor_plan: braintree_plan.id, ends_at: nil)
|
87
89
|
else
|
88
90
|
raise Error, "Braintree failed to swap plans: #{result.message}"
|
89
91
|
end
|
@@ -93,81 +95,81 @@ module Pay
|
|
93
95
|
|
94
96
|
private
|
95
97
|
|
96
|
-
|
97
|
-
|
98
|
-
|
98
|
+
def gateway
|
99
|
+
Pay.braintree_gateway
|
100
|
+
end
|
99
101
|
|
100
|
-
|
101
|
-
|
102
|
-
|
102
|
+
def would_change_billing_frequency?(plan)
|
103
|
+
plan.billing_frequency != find_braintree_plan(processor_plan).billing_frequency
|
104
|
+
end
|
103
105
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
106
|
+
def find_braintree_plan(id)
|
107
|
+
@braintree_plans ||= gateway.plan.all
|
108
|
+
@braintree_plans.find { |p| p.id == id }
|
109
|
+
end
|
108
110
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
111
|
+
# Helper methods for swapping plans
|
112
|
+
def switching_to_monthly_plan?(current_plan, plan)
|
113
|
+
current_plan.billing_frequency == 12 && plan.billing_frequency == 1
|
114
|
+
end
|
113
115
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
116
|
+
def discount_for_switching_to_monthly(current_plan, plan)
|
117
|
+
cycles = (money_remaining_on_yearly_plan(current_plan) / plan.price).floor
|
118
|
+
OpenStruct.new(
|
119
|
+
amount: plan.price,
|
120
|
+
number_of_billing_cycles: cycles
|
121
|
+
)
|
122
|
+
end
|
121
123
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
124
|
+
def money_remaining_on_yearly_plan(current_plan)
|
125
|
+
end_date = processor_subscription.billing_period_end_date.to_date
|
126
|
+
(current_plan.price / 365) * (end_date - Date.today)
|
127
|
+
end
|
126
128
|
|
127
|
-
|
128
|
-
|
129
|
+
def discount_for_switching_to_yearly
|
130
|
+
amount = 0
|
129
131
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
end
|
132
|
+
processor_subscription.discounts.each do |discount|
|
133
|
+
if discount.id == "plan-credit"
|
134
|
+
amount += discount.amount * discount.number_of_billing_cycles
|
134
135
|
end
|
135
|
-
|
136
|
-
OpenStruct.new(
|
137
|
-
amount: amount,
|
138
|
-
number_of_billing_cycles: 1
|
139
|
-
)
|
140
136
|
end
|
141
137
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
else
|
148
|
-
discount_for_switching_to_yearly
|
149
|
-
end
|
150
|
-
|
151
|
-
options = {}
|
152
|
-
|
153
|
-
if discount.amount > 0 && discount.number_of_billing_cycles > 0
|
154
|
-
options = {
|
155
|
-
discounts: {
|
156
|
-
add: [
|
157
|
-
{
|
158
|
-
inherited_from_id: 'plan-credit',
|
159
|
-
amount: discount.amount,
|
160
|
-
number_of_billing_cycles: discount.number_of_billing_cycles
|
161
|
-
}
|
162
|
-
]
|
163
|
-
}
|
164
|
-
}
|
165
|
-
end
|
138
|
+
OpenStruct.new(
|
139
|
+
amount: amount,
|
140
|
+
number_of_billing_cycles: 1
|
141
|
+
)
|
142
|
+
end
|
166
143
|
|
167
|
-
|
144
|
+
def swap_across_frequencies(plan)
|
145
|
+
current_plan = find_braintree_plan(processor_plan)
|
168
146
|
|
169
|
-
|
147
|
+
discount = if switching_to_monthly_plan?(current_plan, plan)
|
148
|
+
discount_for_switching_to_monthly(current_plan, plan)
|
149
|
+
else
|
150
|
+
discount_for_switching_to_yearly
|
170
151
|
end
|
152
|
+
|
153
|
+
options = {}
|
154
|
+
|
155
|
+
if discount.amount > 0 && discount.number_of_billing_cycles > 0
|
156
|
+
options = {
|
157
|
+
discounts: {
|
158
|
+
add: [
|
159
|
+
{
|
160
|
+
inherited_from_id: "plan-credit",
|
161
|
+
amount: discount.amount,
|
162
|
+
number_of_billing_cycles: discount.number_of_billing_cycles,
|
163
|
+
},
|
164
|
+
],
|
165
|
+
},
|
166
|
+
}
|
167
|
+
end
|
168
|
+
|
169
|
+
cancel_now!
|
170
|
+
|
171
|
+
owner.subscribe(options.merge(name: name, plan: plan.id))
|
172
|
+
end
|
171
173
|
end
|
172
174
|
end
|
173
175
|
end
|
data/lib/pay/braintree.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
1
|
+
require "pay/env"
|
2
|
+
require "pay/braintree/billable"
|
3
|
+
require "pay/braintree/charge"
|
4
|
+
require "pay/braintree/subscription"
|
5
5
|
|
6
6
|
module Pay
|
7
7
|
module Braintree
|
@@ -13,7 +13,7 @@ module Pay
|
|
13
13
|
Pay.braintree_gateway = ::Braintree::Gateway.new(
|
14
14
|
environment: environment.to_sym,
|
15
15
|
merchant_id: merchant_id,
|
16
|
-
public_key:
|
16
|
+
public_key: public_key,
|
17
17
|
private_key: private_key
|
18
18
|
)
|
19
19
|
|
@@ -35,7 +35,7 @@ module Pay
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def environment
|
38
|
-
find_value_by_name(:braintree, :environment) ||
|
38
|
+
find_value_by_name(:braintree, :environment) || "sandbox"
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
data/lib/pay/engine.rb
CHANGED
@@ -2,35 +2,35 @@
|
|
2
2
|
|
3
3
|
# rubocop:disable Lint/HandleExceptions
|
4
4
|
begin
|
5
|
-
require
|
5
|
+
require "braintree"
|
6
6
|
rescue LoadError
|
7
7
|
end
|
8
8
|
|
9
9
|
begin
|
10
|
-
require
|
11
|
-
require
|
10
|
+
require "stripe"
|
11
|
+
require "stripe_event"
|
12
12
|
rescue LoadError
|
13
13
|
end
|
14
14
|
# rubocop:enable Lint/HandleExceptions
|
15
15
|
|
16
16
|
module Pay
|
17
17
|
class Engine < ::Rails::Engine
|
18
|
-
engine_name
|
18
|
+
engine_name "pay"
|
19
19
|
|
20
|
-
initializer
|
20
|
+
initializer "pay.processors" do |app|
|
21
21
|
# Include processor backends
|
22
|
-
require
|
23
|
-
require
|
22
|
+
require "pay/stripe" if defined? ::Stripe
|
23
|
+
require "pay/braintree" if defined? ::Braintree
|
24
24
|
|
25
|
-
if Pay.
|
25
|
+
if Pay.automount_routes
|
26
26
|
app.routes.append do
|
27
|
-
mount Pay::Engine, at: Pay.
|
27
|
+
mount Pay::Engine, at: Pay.routes_path, as: "pay"
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
32
|
config.to_prepare do
|
33
|
-
Pay::Stripe.setup
|
33
|
+
Pay::Stripe.setup if defined? ::Stripe
|
34
34
|
Pay::Braintree.setup if defined? ::Braintree
|
35
35
|
|
36
36
|
Pay.charge_model.include Pay::Receipts if defined? ::Receipts::Receipt
|
data/lib/pay/env.rb
CHANGED
data/lib/pay/payment.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
module Pay
|
2
|
+
class Payment
|
3
|
+
attr_reader :intent
|
4
|
+
|
5
|
+
delegate :id, :amount, :client_secret, :status, :confirm, to: :intent
|
6
|
+
|
7
|
+
def self.from_id(id)
|
8
|
+
intent = id.start_with?("seti_") ? ::Stripe::SetupIntent.retrieve(id) : ::Stripe::PaymentIntent.retrieve(id)
|
9
|
+
new(intent)
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(intent)
|
13
|
+
@intent = intent
|
14
|
+
end
|
15
|
+
|
16
|
+
def requires_payment_method?
|
17
|
+
status == "requires_payment_method"
|
18
|
+
end
|
19
|
+
|
20
|
+
def requires_action?
|
21
|
+
status == "requires_action"
|
22
|
+
end
|
23
|
+
|
24
|
+
def canceled?
|
25
|
+
status == "canceled"
|
26
|
+
end
|
27
|
+
|
28
|
+
def cancelled?
|
29
|
+
canceled?
|
30
|
+
end
|
31
|
+
|
32
|
+
def succeeded?
|
33
|
+
status == "succeeded"
|
34
|
+
end
|
35
|
+
|
36
|
+
def payment_intent?
|
37
|
+
intent.is_a?(::Stripe::PaymentIntent)
|
38
|
+
end
|
39
|
+
|
40
|
+
def setup_intent?
|
41
|
+
intent.is_a?(::Stripe::SetupIntent)
|
42
|
+
end
|
43
|
+
|
44
|
+
def validate
|
45
|
+
if requires_payment_method?
|
46
|
+
raise Pay::InvalidPaymentMethod.new(self)
|
47
|
+
elsif requires_action?
|
48
|
+
raise Pay::ActionRequired.new(self)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/pay/receipts.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Pay
|
2
2
|
module Receipts
|
3
3
|
def filename
|
4
|
-
"receipt-#{created_at.strftime(
|
4
|
+
"receipt-#{created_at.strftime("%Y-%m-%d")}.pdf"
|
5
5
|
end
|
6
6
|
|
7
7
|
def product
|
@@ -18,9 +18,9 @@ module Pay
|
|
18
18
|
id: id,
|
19
19
|
product: product,
|
20
20
|
company: {
|
21
|
-
name:
|
21
|
+
name: Pay.business_name,
|
22
22
|
address: Pay.business_address,
|
23
|
-
email:
|
23
|
+
email: Pay.support_email,
|
24
24
|
},
|
25
25
|
line_items: line_items
|
26
26
|
)
|
@@ -28,11 +28,11 @@ module Pay
|
|
28
28
|
|
29
29
|
def line_items
|
30
30
|
line_items = [
|
31
|
-
["Date",
|
31
|
+
["Date", created_at.to_s],
|
32
32
|
["Account Billed", "#{owner.name} (#{owner.email})"],
|
33
|
-
["Product",
|
34
|
-
["Amount",
|
35
|
-
["Charged to",
|
33
|
+
["Product", product],
|
34
|
+
["Amount", ActionController::Base.helpers.number_to_currency(amount / 100.0)],
|
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
|
data/lib/pay/stripe/billable.rb
CHANGED
@@ -14,21 +14,32 @@ module Pay
|
|
14
14
|
raise Error, e.message
|
15
15
|
end
|
16
16
|
|
17
|
+
def create_setup_intent
|
18
|
+
::Stripe::SetupIntent.create(
|
19
|
+
customer: processor_id,
|
20
|
+
usage: :off_session,
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
17
24
|
# Handles Billable#charge
|
18
25
|
#
|
19
26
|
# Returns Pay::Charge
|
20
|
-
def create_stripe_charge(amount, options={})
|
27
|
+
def create_stripe_charge(amount, options = {})
|
28
|
+
customer = stripe_customer
|
21
29
|
args = {
|
22
30
|
amount: amount,
|
23
|
-
|
31
|
+
confirm: true,
|
32
|
+
confirmation_method: :automatic,
|
33
|
+
currency: "usd",
|
24
34
|
customer: customer.id,
|
25
|
-
|
35
|
+
payment_method: customer.invoice_settings.default_payment_method,
|
26
36
|
}.merge(options)
|
27
37
|
|
28
|
-
|
38
|
+
payment_intent = ::Stripe::PaymentIntent.create(args)
|
39
|
+
Pay::Payment.new(payment_intent).validate
|
29
40
|
|
30
|
-
#
|
31
|
-
|
41
|
+
# Create a new charge object
|
42
|
+
Stripe::Webhooks::ChargeSucceeded.new.create_charge(self, payment_intent.charges.first)
|
32
43
|
rescue ::Stripe::StripeError => e
|
33
44
|
raise Error, e.message
|
34
45
|
end
|
@@ -36,10 +47,28 @@ module Pay
|
|
36
47
|
# Handles Billable#subscribe
|
37
48
|
#
|
38
49
|
# Returns Pay::Subscription
|
39
|
-
def create_stripe_subscription(name, plan, options={})
|
40
|
-
opts = {
|
41
|
-
|
42
|
-
|
50
|
+
def create_stripe_subscription(name, plan, options = {})
|
51
|
+
opts = {
|
52
|
+
expand: ["pending_setup_intent", "latest_invoice.payment_intent"],
|
53
|
+
items: [plan: plan],
|
54
|
+
off_session: true,
|
55
|
+
}.merge(options)
|
56
|
+
|
57
|
+
# Inherit trial from plan unless trial override was specified
|
58
|
+
opts[:trial_from_plan] = true unless opts[:trial_period_days]
|
59
|
+
|
60
|
+
stripe_sub = customer.subscriptions.create(opts)
|
61
|
+
subscription = create_subscription(stripe_sub, "stripe", name, plan, status: stripe_sub.status)
|
62
|
+
|
63
|
+
# No trial, card requires SCA
|
64
|
+
if subscription.incomplete?
|
65
|
+
Pay::Payment.new(stripe_sub.latest_invoice.payment_intent).validate
|
66
|
+
|
67
|
+
# Trial, card requires SCA
|
68
|
+
elsif subscription.on_trial? && stripe_sub.pending_setup_intent
|
69
|
+
Pay::Payment.new(stripe_sub.pending_setup_intent).validate
|
70
|
+
end
|
71
|
+
|
43
72
|
subscription
|
44
73
|
rescue ::Stripe::StripeError => e
|
45
74
|
raise Error, e.message
|
@@ -48,17 +77,15 @@ module Pay
|
|
48
77
|
# Handles Billable#update_card
|
49
78
|
#
|
50
79
|
# Returns true if successful
|
51
|
-
def update_stripe_card(
|
80
|
+
def update_stripe_card(payment_method_id)
|
52
81
|
customer = stripe_customer
|
53
|
-
token = ::Stripe::Token.retrieve(token)
|
54
82
|
|
55
|
-
return if
|
83
|
+
return true if payment_method_id == customer.invoice_settings.default_payment_method
|
56
84
|
|
57
|
-
|
58
|
-
customer.
|
59
|
-
customer.save
|
85
|
+
payment_method = ::Stripe::PaymentMethod.attach(payment_method_id, customer: customer.id)
|
86
|
+
::Stripe::Customer.update(customer.id, invoice_settings: {default_payment_method: payment_method.id})
|
60
87
|
|
61
|
-
update_stripe_card_on_file(card)
|
88
|
+
update_stripe_card_on_file(payment_method.card)
|
62
89
|
true
|
63
90
|
rescue ::Stripe::StripeError => e
|
64
91
|
raise Error, e.message
|
@@ -71,13 +98,13 @@ module Pay
|
|
71
98
|
customer.save
|
72
99
|
end
|
73
100
|
|
74
|
-
def stripe_subscription(subscription_id)
|
75
|
-
::Stripe::Subscription.retrieve(subscription_id)
|
101
|
+
def stripe_subscription(subscription_id, options = {})
|
102
|
+
::Stripe::Subscription.retrieve(options.merge(id: subscription_id))
|
76
103
|
end
|
77
104
|
|
78
|
-
def stripe_invoice!
|
105
|
+
def stripe_invoice!(options = {})
|
79
106
|
return unless processor_id?
|
80
|
-
::Stripe::Invoice.create(customer: processor_id).pay
|
107
|
+
::Stripe::Invoice.create(options.merge(customer: processor_id)).pay
|
81
108
|
end
|
82
109
|
|
83
110
|
def stripe_upcoming_invoice
|
@@ -87,18 +114,18 @@ module Pay
|
|
87
114
|
# Used by webhooks when the customer or source changes
|
88
115
|
def sync_card_from_stripe
|
89
116
|
stripe_cust = stripe_customer
|
90
|
-
|
117
|
+
default_payment_method_id = stripe_cust.invoice_settings.default_payment_method
|
91
118
|
|
92
|
-
if
|
93
|
-
|
119
|
+
if default_payment_method_id.present?
|
120
|
+
payment_method = ::Stripe::PaymentMethod.retrieve(default_payment_method_id)
|
94
121
|
update(
|
95
|
-
card_type:
|
96
|
-
card_last4:
|
97
|
-
card_exp_month: card.exp_month,
|
98
|
-
card_exp_year:
|
122
|
+
card_type: payment_method.card.brand,
|
123
|
+
card_last4: payment_method.card.last4,
|
124
|
+
card_exp_month: payment_method.card.exp_month,
|
125
|
+
card_exp_year: payment_method.card.exp_year
|
99
126
|
)
|
100
127
|
|
101
|
-
# Customer has no default payment
|
128
|
+
# Customer has no default payment method
|
102
129
|
else
|
103
130
|
update(card_type: nil, card_last4: nil)
|
104
131
|
end
|
@@ -107,29 +134,33 @@ module Pay
|
|
107
134
|
private
|
108
135
|
|
109
136
|
def create_stripe_customer
|
110
|
-
customer = ::Stripe::Customer.create(email: email,
|
111
|
-
update(processor:
|
137
|
+
customer = ::Stripe::Customer.create(email: email, description: customer_name)
|
138
|
+
update(processor: "stripe", processor_id: customer.id)
|
112
139
|
|
113
140
|
# Update the user's card on file if a token was passed in
|
114
|
-
|
115
|
-
|
116
|
-
|
141
|
+
if card_token.present?
|
142
|
+
::Stripe::PaymentMethod.attach(card_token, {customer: customer.id})
|
143
|
+
customer.invoice_settings.default_payment_method = card_token
|
144
|
+
customer.save
|
145
|
+
|
146
|
+
update_stripe_card_on_file ::Stripe::PaymentMethod.retrieve(card_token).card
|
117
147
|
end
|
118
148
|
|
119
149
|
customer
|
120
150
|
end
|
121
151
|
|
122
152
|
def stripe_trial_end_date(stripe_sub)
|
153
|
+
# Times in Stripe are returned in UTC
|
123
154
|
stripe_sub.trial_end.present? ? Time.at(stripe_sub.trial_end) : nil
|
124
155
|
end
|
125
156
|
|
126
157
|
# Save the card to the database as the user's current card
|
127
158
|
def update_stripe_card_on_file(card)
|
128
159
|
update!(
|
129
|
-
card_type:
|
130
|
-
card_last4:
|
160
|
+
card_type: card.brand.capitalize,
|
161
|
+
card_last4: card.last4,
|
131
162
|
card_exp_month: card.exp_month,
|
132
|
-
card_exp_year:
|
163
|
+
card_exp_year: card.exp_year
|
133
164
|
)
|
134
165
|
|
135
166
|
self.card_token = nil
|
data/lib/pay/stripe/charge.rb
CHANGED
@@ -10,15 +10,15 @@ module Pay
|
|
10
10
|
subscription.cancel_at_period_end = true
|
11
11
|
subscription.save
|
12
12
|
|
13
|
-
new_ends_at
|
14
|
-
update(ends_at: new_ends_at)
|
13
|
+
new_ends_at = on_trial? ? trial_ends_at : Time.at(subscription.current_period_end)
|
14
|
+
update(ends_at: new_ends_at, status: :canceled)
|
15
15
|
rescue ::Stripe::StripeError => e
|
16
16
|
raise Error, e.message
|
17
17
|
end
|
18
18
|
|
19
19
|
def stripe_cancel_now!
|
20
|
-
|
21
|
-
update(ends_at: Time.zone.now)
|
20
|
+
processor_subscription.delete
|
21
|
+
update(ends_at: Time.zone.now, status: :canceled)
|
22
22
|
rescue ::Stripe::StripeError => e
|
23
23
|
raise Error, e.message
|
24
24
|
end
|
@@ -26,7 +26,7 @@ module Pay
|
|
26
26
|
def stripe_resume
|
27
27
|
subscription = processor_subscription
|
28
28
|
subscription.plan = processor_plan
|
29
|
-
subscription.trial_end = on_trial? ? trial_ends_at.to_i :
|
29
|
+
subscription.trial_end = on_trial? ? trial_ends_at.to_i : "now"
|
30
30
|
subscription.cancel_at_period_end = false
|
31
31
|
subscription.save
|
32
32
|
rescue ::Stripe::StripeError => e
|
@@ -35,9 +35,10 @@ module Pay
|
|
35
35
|
|
36
36
|
def stripe_swap(plan)
|
37
37
|
subscription = processor_subscription
|
38
|
+
subscription.cancel_at_period_end = false
|
38
39
|
subscription.plan = plan
|
39
40
|
subscription.prorate = prorate
|
40
|
-
subscription.trial_end = on_trial? ? trial_ends_at.to_i :
|
41
|
+
subscription.trial_end = on_trial? ? trial_ends_at.to_i : "now"
|
41
42
|
subscription.quantity = quantity if quantity?
|
42
43
|
subscription.save
|
43
44
|
rescue ::Stripe::StripeError => e
|
@@ -1,11 +1,10 @@
|
|
1
1
|
module Pay
|
2
2
|
module Stripe
|
3
3
|
module Webhooks
|
4
|
-
|
5
4
|
class ChargeSucceeded
|
6
5
|
def call(event)
|
7
6
|
object = event.data.object
|
8
|
-
user
|
7
|
+
user = Pay.user_model.find_by(
|
9
8
|
processor: :stripe,
|
10
9
|
processor_id: object.customer
|
11
10
|
)
|
@@ -20,17 +19,17 @@ module Pay
|
|
20
19
|
|
21
20
|
def create_charge(user, object)
|
22
21
|
charge = user.charges.find_or_initialize_by(
|
23
|
-
processor:
|
24
|
-
processor_id:
|
22
|
+
processor: :stripe,
|
23
|
+
processor_id: object.id,
|
25
24
|
)
|
26
25
|
|
27
26
|
charge.update(
|
28
|
-
amount:
|
29
|
-
card_last4:
|
30
|
-
card_type:
|
31
|
-
card_exp_month: object.
|
32
|
-
card_exp_year:
|
33
|
-
created_at:
|
27
|
+
amount: object.amount,
|
28
|
+
card_last4: object.payment_method_details.card.last4,
|
29
|
+
card_type: object.payment_method_details.card.brand,
|
30
|
+
card_exp_month: object.payment_method_details.card.exp_month,
|
31
|
+
card_exp_year: object.payment_method_details.card.exp_year,
|
32
|
+
created_at: Time.zone.at(object.created)
|
34
33
|
)
|
35
34
|
|
36
35
|
charge
|