pay 2.2.2 → 2.3.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 +137 -7
- data/Rakefile +2 -4
- data/app/controllers/pay/payments_controller.rb +2 -0
- data/app/controllers/pay/webhooks/paddle_controller.rb +36 -0
- data/app/models/pay/application_record.rb +6 -1
- data/app/models/pay/charge.rb +7 -0
- data/app/models/pay/subscription.rb +24 -3
- data/config/routes.rb +1 -0
- data/db/migrate/20200603134434_add_data_to_pay_models.rb +17 -0
- data/lib/pay.rb +3 -0
- data/lib/pay/billable.rb +5 -0
- data/lib/pay/braintree/billable.rb +10 -4
- data/lib/pay/braintree/charge.rb +4 -0
- data/lib/pay/braintree/subscription.rb +6 -0
- data/lib/pay/engine.rb +7 -0
- data/lib/pay/paddle.rb +38 -0
- data/lib/pay/paddle/billable.rb +66 -0
- data/lib/pay/paddle/charge.rb +39 -0
- data/lib/pay/paddle/subscription.rb +59 -0
- data/lib/pay/paddle/webhooks.rb +1 -0
- data/lib/pay/paddle/webhooks/signature_verifier.rb +115 -0
- data/lib/pay/paddle/webhooks/subscription_cancelled.rb +18 -0
- data/lib/pay/paddle/webhooks/subscription_created.rb +59 -0
- data/lib/pay/paddle/webhooks/subscription_payment_refunded.rb +21 -0
- data/lib/pay/paddle/webhooks/subscription_payment_succeeded.rb +64 -0
- data/lib/pay/paddle/webhooks/subscription_updated.rb +34 -0
- data/lib/pay/stripe/billable.rb +6 -0
- data/lib/pay/stripe/charge.rb +4 -0
- data/lib/pay/stripe/subscription.rb +6 -0
- data/lib/pay/stripe/webhooks/charge_succeeded.rb +7 -7
- data/lib/pay/stripe/webhooks/payment_action_required.rb +7 -8
- data/lib/pay/stripe/webhooks/subscription_created.rb +1 -1
- data/lib/pay/version.rb +1 -1
- metadata +59 -4
@@ -0,0 +1,18 @@
|
|
1
|
+
module Pay
|
2
|
+
module Paddle
|
3
|
+
module Webhooks
|
4
|
+
class SubscriptionCancelled
|
5
|
+
def initialize(data)
|
6
|
+
subscription = Pay.subscription_model.find_by(processor: :paddle, processor_id: data["subscription_id"])
|
7
|
+
|
8
|
+
# We couldn't find the subscription for some reason, maybe it's from another service
|
9
|
+
return if subscription.nil?
|
10
|
+
|
11
|
+
# User canceled subscriptions have an ends_at
|
12
|
+
# Automatically canceled subscriptions need this value set
|
13
|
+
subscription.update!(ends_at: DateTime.parse(data["cancellation_effective_date"])) if subscription.ends_at.blank? && data["cancellation_effective_date"].present?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Pay
|
2
|
+
module Paddle
|
3
|
+
module Webhooks
|
4
|
+
class SubscriptionCreated
|
5
|
+
def initialize(data)
|
6
|
+
# We may already have the subscription in the database, so we can update that record
|
7
|
+
subscription = Pay.subscription_model.find_by(processor: :paddle, processor_id: data["subscription_id"])
|
8
|
+
|
9
|
+
# Create the subscription in the database if we don't have it already
|
10
|
+
if subscription.nil?
|
11
|
+
|
12
|
+
# The customer could already be in the database
|
13
|
+
owner = Pay.find_billable(processor: :paddle, processor_id: data["user_id"])
|
14
|
+
|
15
|
+
if owner.nil?
|
16
|
+
owner = owner_by_passtrough(data["passthrough"], data["subscription_plan_id"])
|
17
|
+
owner&.update!(processor: "paddle", processor_id: data["user_id"])
|
18
|
+
end
|
19
|
+
|
20
|
+
if owner.nil?
|
21
|
+
Rails.logger.error("[Pay] Unable to find Pay::Billable with owner: '#{data["passthrough"]}'. Searched these models: #{Pay.billable_models.join(", ")}")
|
22
|
+
return
|
23
|
+
end
|
24
|
+
|
25
|
+
subscription = Pay.subscription_model.new(owner: owner, name: "default", processor: "paddle", processor_id: data["subscription_id"], status: :active)
|
26
|
+
end
|
27
|
+
|
28
|
+
subscription.quantity = data["quantity"]
|
29
|
+
subscription.processor_plan = data["subscription_plan_id"]
|
30
|
+
subscription.paddle_update_url = data["update_url"]
|
31
|
+
subscription.paddle_cancel_url = data["cancel_url"]
|
32
|
+
subscription.trial_ends_at = Time.zone.parse(data["next_bill_date"]) if data["status"] == "trialing"
|
33
|
+
|
34
|
+
# If user was on trial, their subscription ends at the end of the trial
|
35
|
+
subscription.ends_at = if ["paused", "deleted"].include?(data["status"]) && subscription.on_trial?
|
36
|
+
subscription.trial_ends_at
|
37
|
+
|
38
|
+
# User wasn't on trial, so subscription ends at period end
|
39
|
+
elsif ["paused", "deleted"].include?(data["status"])
|
40
|
+
Time.zone.parse(data["next_bill_date"])
|
41
|
+
|
42
|
+
# Subscription isn't marked to cancel at period end
|
43
|
+
end
|
44
|
+
|
45
|
+
subscription.save!
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def owner_by_passtrough(passthrough, product_id)
|
51
|
+
passthrough_json = JSON.parse(passthrough)
|
52
|
+
GlobalID::Locator.locate_signed(passthrough_json["owner_sgid"])
|
53
|
+
rescue JSON::ParserError
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Pay
|
2
|
+
module Paddle
|
3
|
+
module Webhooks
|
4
|
+
class SubscriptionPaymentRefunded
|
5
|
+
def initialize(data)
|
6
|
+
charge = Pay.charge_model.find_by(processor: :paddle, processor_id: data["subscription_payment_id"])
|
7
|
+
return unless charge.present?
|
8
|
+
|
9
|
+
charge.update(amount_refunded: Integer(data["gross_refund"].to_f * 100))
|
10
|
+
notify_user(charge.owner, charge)
|
11
|
+
end
|
12
|
+
|
13
|
+
def notify_user(user, charge)
|
14
|
+
if Pay.send_emails
|
15
|
+
Pay::UserMailer.refund(user, charge).deliver_later
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Pay
|
2
|
+
module Paddle
|
3
|
+
module Webhooks
|
4
|
+
class SubscriptionPaymentSucceeded
|
5
|
+
def initialize(data)
|
6
|
+
billable = Pay.find_billable(processor: :paddle, processor_id: data["user_id"])
|
7
|
+
return unless billable.present?
|
8
|
+
return if billable.charges.where(processor_id: data["subscription_payment_id"]).any?
|
9
|
+
|
10
|
+
charge = create_charge(billable, data)
|
11
|
+
notify_user(billable, charge)
|
12
|
+
end
|
13
|
+
|
14
|
+
def create_charge(user, data)
|
15
|
+
charge = user.charges.find_or_initialize_by(
|
16
|
+
processor: :paddle,
|
17
|
+
processor_id: data["subscription_payment_id"]
|
18
|
+
)
|
19
|
+
|
20
|
+
params = {
|
21
|
+
amount: Integer(data["sale_gross"].to_f * 100),
|
22
|
+
card_type: data["payment_method"],
|
23
|
+
paddle_receipt_url: data["receipt_url"],
|
24
|
+
created_at: DateTime.parse(data["event_time"])
|
25
|
+
}.merge(payment_params(data["subscription_id"]))
|
26
|
+
|
27
|
+
charge.update(params)
|
28
|
+
|
29
|
+
charge
|
30
|
+
end
|
31
|
+
|
32
|
+
def notify_user(user, charge)
|
33
|
+
if Pay.send_emails && charge.respond_to?(:receipt)
|
34
|
+
Pay::UserMailer.receipt(user, charge).deliver_later
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def payment_params(subscription_id)
|
41
|
+
subscription_user = PaddlePay::Subscription::User.list({subscription_id: subscription_id}).try(:first)
|
42
|
+
payment_information = subscription_user ? subscription_user[:payment_information] : nil
|
43
|
+
return {} if payment_information.nil?
|
44
|
+
|
45
|
+
case payment_information[:payment_method]
|
46
|
+
when "card"
|
47
|
+
{
|
48
|
+
card_type: payment_information[:card_type],
|
49
|
+
card_last4: payment_information[:last_four_digits],
|
50
|
+
card_exp_month: payment_information[:expiry_date].split("/").first,
|
51
|
+
card_exp_year: payment_information[:expiry_date].split("/").last
|
52
|
+
}
|
53
|
+
when "paypal"
|
54
|
+
{
|
55
|
+
card_type: "PayPal"
|
56
|
+
}
|
57
|
+
else
|
58
|
+
{}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Pay
|
2
|
+
module Paddle
|
3
|
+
module Webhooks
|
4
|
+
class SubscriptionUpdated
|
5
|
+
def initialize(data)
|
6
|
+
subscription = Pay.subscription_model.find_by(processor: :paddle, processor_id: data["subscription_id"])
|
7
|
+
|
8
|
+
return if subscription.nil?
|
9
|
+
|
10
|
+
subscription.status = data["status"] == "deleted" ? "canceled" : data["status"]
|
11
|
+
subscription.quantity = data["new_quantity"]
|
12
|
+
subscription.processor_plan = data["subscription_plan_id"]
|
13
|
+
subscription.paddle_update_url = data["update_url"]
|
14
|
+
subscription.paddle_cancel_url = data["cancel_url"]
|
15
|
+
|
16
|
+
subscription.trial_ends_at = DateTime.parse(data["next_bill_date"]) if data["status"] == "trialing"
|
17
|
+
|
18
|
+
# If user was on trial, their subscription ends at the end of the trial
|
19
|
+
subscription.ends_at = if ["paused", "deleted"].include?(data["status"]) && subscription.on_trial?
|
20
|
+
subscription.trial_ends_at
|
21
|
+
|
22
|
+
# User wasn't on trial, so subscription ends at period end
|
23
|
+
elsif ["paused", "deleted"].include?(data["status"])
|
24
|
+
DateTime.parse(data["next_bill_date"])
|
25
|
+
|
26
|
+
# Subscription isn't marked to cancel at period end
|
27
|
+
end
|
28
|
+
|
29
|
+
subscription.save!
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/pay/stripe/billable.rb
CHANGED
data/lib/pay/stripe/charge.rb
CHANGED
@@ -9,13 +9,11 @@ module Pay
|
|
9
9
|
return unless billable.present?
|
10
10
|
return if billable.charges.where(processor_id: object.id).any?
|
11
11
|
|
12
|
-
|
13
|
-
notify_user(billable, charge)
|
14
|
-
charge
|
12
|
+
create_charge(billable, object)
|
15
13
|
end
|
16
14
|
|
17
|
-
def create_charge(
|
18
|
-
charge =
|
15
|
+
def create_charge(billable, object)
|
16
|
+
charge = billable.charges.find_or_initialize_by(
|
19
17
|
processor: :stripe,
|
20
18
|
processor_id: object.id
|
21
19
|
)
|
@@ -29,12 +27,14 @@ module Pay
|
|
29
27
|
created_at: Time.zone.at(object.created)
|
30
28
|
)
|
31
29
|
|
30
|
+
notify_user(billable, charge)
|
31
|
+
|
32
32
|
charge
|
33
33
|
end
|
34
34
|
|
35
|
-
def notify_user(
|
35
|
+
def notify_user(billable, charge)
|
36
36
|
if Pay.send_emails && charge.respond_to?(:receipt)
|
37
|
-
Pay::UserMailer.receipt(
|
37
|
+
Pay::UserMailer.receipt(billable, charge).deliver_later
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
@@ -6,19 +6,18 @@ module Pay
|
|
6
6
|
# Event is of type "invoice" see:
|
7
7
|
# https://stripe.com/docs/api/invoices/object
|
8
8
|
|
9
|
-
|
9
|
+
object = event.data.object
|
10
10
|
|
11
|
-
subscription = Pay.subscription_model.find_by(
|
12
|
-
|
13
|
-
|
14
|
-
)
|
11
|
+
subscription = Pay.subscription_model.find_by(processor: :stripe, processor_id: object.subscription)
|
12
|
+
return if subscription.nil?
|
13
|
+
billable = subscription.owner
|
15
14
|
|
16
|
-
notify_user(
|
15
|
+
notify_user(billable, event.data.object.payment_intent, subscription)
|
17
16
|
end
|
18
17
|
|
19
|
-
def notify_user(
|
18
|
+
def notify_user(billable, payment_intent_id, subscription)
|
20
19
|
if Pay.send_emails
|
21
|
-
Pay::UserMailer.payment_action_required(
|
20
|
+
Pay::UserMailer.payment_action_required(billable, payment_intent_id, subscription).deliver_later
|
22
21
|
end
|
23
22
|
end
|
24
23
|
end
|
@@ -18,7 +18,7 @@ module Pay
|
|
18
18
|
return
|
19
19
|
end
|
20
20
|
|
21
|
-
subscription = Pay.subscription_model.new(owner: owner)
|
21
|
+
subscription = Pay.subscription_model.new(name: "default", owner: owner, processor: :stripe, processor_id: object.id)
|
22
22
|
end
|
23
23
|
|
24
24
|
subscription.quantity = object.quantity
|
data/lib/pay/version.rb
CHANGED
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.
|
4
|
+
version: 2.3.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-
|
12
|
+
date: 2020-12-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -34,7 +34,7 @@ dependencies:
|
|
34
34
|
version: 2.92.0
|
35
35
|
- - "<"
|
36
36
|
- !ruby/object:Gem::Version
|
37
|
-
version: '
|
37
|
+
version: '4.0'
|
38
38
|
type: :development
|
39
39
|
prerelease: false
|
40
40
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -44,7 +44,7 @@ dependencies:
|
|
44
44
|
version: 2.92.0
|
45
45
|
- - "<"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '4.0'
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
49
|
name: stripe
|
50
50
|
requirement: !ruby/object:Gem::Requirement
|
@@ -73,6 +73,20 @@ dependencies:
|
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: '2.3'
|
76
|
+
- !ruby/object:Gem::Dependency
|
77
|
+
name: paddle_pay
|
78
|
+
requirement: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.0.1
|
83
|
+
type: :development
|
84
|
+
prerelease: false
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.0.1
|
76
90
|
- !ruby/object:Gem::Dependency
|
77
91
|
name: byebug
|
78
92
|
requirement: !ruby/object:Gem::Requirement
|
@@ -157,6 +171,34 @@ dependencies:
|
|
157
171
|
- - "~>"
|
158
172
|
- !ruby/object:Gem::Version
|
159
173
|
version: '1.4'
|
174
|
+
- !ruby/object:Gem::Dependency
|
175
|
+
name: mysql2
|
176
|
+
requirement: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
type: :development
|
182
|
+
prerelease: false
|
183
|
+
version_requirements: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - ">="
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0'
|
188
|
+
- !ruby/object:Gem::Dependency
|
189
|
+
name: pg
|
190
|
+
requirement: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ">="
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0'
|
195
|
+
type: :development
|
196
|
+
prerelease: false
|
197
|
+
version_requirements: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - ">="
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '0'
|
160
202
|
- !ruby/object:Gem::Dependency
|
161
203
|
name: vcr
|
162
204
|
requirement: !ruby/object:Gem::Requirement
|
@@ -202,6 +244,7 @@ files:
|
|
202
244
|
- app/controllers/pay/application_controller.rb
|
203
245
|
- app/controllers/pay/payments_controller.rb
|
204
246
|
- app/controllers/pay/webhooks/braintree_controller.rb
|
247
|
+
- app/controllers/pay/webhooks/paddle_controller.rb
|
205
248
|
- app/helpers/pay/application_helper.rb
|
206
249
|
- app/jobs/pay/application_job.rb
|
207
250
|
- app/jobs/pay/email_sync_job.rb
|
@@ -221,6 +264,7 @@ files:
|
|
221
264
|
- db/migrate/20170205020145_create_pay_subscriptions.rb
|
222
265
|
- db/migrate/20170727235816_create_pay_charges.rb
|
223
266
|
- db/migrate/20190816015720_add_status_to_pay_subscriptions.rb
|
267
|
+
- db/migrate/20200603134434_add_data_to_pay_models.rb
|
224
268
|
- lib/generators/active_record/pay_generator.rb
|
225
269
|
- lib/generators/active_record/templates/migration.rb
|
226
270
|
- lib/generators/pay/email_views_generator.rb
|
@@ -236,6 +280,17 @@ files:
|
|
236
280
|
- lib/pay/braintree/subscription.rb
|
237
281
|
- lib/pay/engine.rb
|
238
282
|
- lib/pay/env.rb
|
283
|
+
- lib/pay/paddle.rb
|
284
|
+
- lib/pay/paddle/billable.rb
|
285
|
+
- lib/pay/paddle/charge.rb
|
286
|
+
- lib/pay/paddle/subscription.rb
|
287
|
+
- lib/pay/paddle/webhooks.rb
|
288
|
+
- lib/pay/paddle/webhooks/signature_verifier.rb
|
289
|
+
- lib/pay/paddle/webhooks/subscription_cancelled.rb
|
290
|
+
- lib/pay/paddle/webhooks/subscription_created.rb
|
291
|
+
- lib/pay/paddle/webhooks/subscription_payment_refunded.rb
|
292
|
+
- lib/pay/paddle/webhooks/subscription_payment_succeeded.rb
|
293
|
+
- lib/pay/paddle/webhooks/subscription_updated.rb
|
239
294
|
- lib/pay/payment.rb
|
240
295
|
- lib/pay/receipts.rb
|
241
296
|
- lib/pay/stripe.rb
|