pay 2.4.4 → 2.5.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: e84a6d0c51681591ba02b3736bf1009096dc9ebf2c7ae7fa9e7c3771afff96b3
4
- data.tar.gz: '089b14e73b7dffd8ee7e1ea4891adb23ad24fc0eb553a95f9a3e39d59392ffc1'
3
+ metadata.gz: 8aa4f779f21c561b6e8b48df4908424198b09413c0e2b580d7d6d2610e4d7429
4
+ data.tar.gz: c62e136c8ae6ffed79028922aedfe59f14e77ca235ae407aa5acaed3299918d5
5
5
  SHA512:
6
- metadata.gz: 2686b43a90c36dd93cfc2539255af98faf9e8eb1bbf53090639a1446c7561a42fd3f734f01fbff047cf6ac4e34abdd13db2b980616e385f18e8608767d4406ce
7
- data.tar.gz: 308341a4541afae134c4cbe581cd67faff65d0dd3bee7b48d563db00a6d0b63c81dcc501a0adcd840d202bf869ec3bfef15a69ee0e2fee5cd4458f1d0e4b8c9e
6
+ metadata.gz: 2154c6f3551b6089c470c71280c2ba9c810858954290fce62c67f2ca502822a4504447ebca822ac1b01f763c6c94095202693dc5b0d75e6dde4f89464530180e
7
+ data.tar.gz: 4d497228db1c45863ad0157bca179957f875705621b968eb1d4c0318c3e4f7ab35692260bb838a7f6c63aa39e704ac3398de535804d512461db1a8ac568239c8
data/README.md CHANGED
@@ -9,8 +9,8 @@ Pay is a payments engine for Ruby on Rails 4.2 and higher.
9
9
  **Current Payment Providers**
10
10
 
11
11
  - Stripe ([supports SCA](https://stripe.com/docs/strong-customer-authentication) using API version `2020-08-27`)
12
- - Braintree
13
- - Paddle
12
+ - Braintree (supports PayPal)
13
+ - Paddle (supports PayPal)
14
14
 
15
15
  Want to add a new payment provider? Contributions are welcome and the instructions [are here](https://github.com/jasoncharnes/pay/wiki/New-Payment-Provider).
16
16
 
@@ -31,7 +31,6 @@ gem 'pay', '~> 2.0'
31
31
 
32
32
  # To use Stripe, also include:
33
33
  gem 'stripe', '< 6.0', '>= 2.8'
34
- gem 'stripe_event', '~> 2.3'
35
34
 
36
35
  # To use Braintree + PayPal, also include:
37
36
  gem 'braintree', '< 3.0', '>= 2.92.0'
@@ -6,66 +6,20 @@ module Pay
6
6
  end
7
7
 
8
8
  def create
9
- case webhook_notification.kind
10
- when "subscription_charged_successfully"
11
- subscription_charged_successfully(webhook_notification)
12
- when "subscription_canceled"
13
- subscription_canceled(webhook_notification)
14
- when "subscription_trial_ended"
15
- subscription_trial_ended(webhook_notification)
16
- end
17
-
18
- render json: {success: true}, status: :ok
19
- rescue ::Braintree::InvalidSignature
9
+ delegate_event(verified_event)
20
10
  head :ok
11
+ rescue ::Braintree::InvalidSignature
12
+ head :bad_request
21
13
  end
22
14
 
23
15
  private
24
16
 
25
- def subscription_charged_successfully(event)
26
- subscription = event.subscription
27
- return if subscription.nil?
28
-
29
- pay_subscription = Pay.subscription_model.find_by(processor: :braintree, processor_id: subscription.id)
30
- return unless pay_subscription.present?
31
-
32
- billable = pay_subscription.owner
33
- charge = billable.save_braintree_transaction(subscription.transactions.first)
34
-
35
- if Pay.send_emails
36
- Pay::UserMailer.with(billable: billable, charge: charge).receipt.deliver_later
37
- end
38
- end
39
-
40
- def subscription_canceled(event)
41
- subscription = event.subscription
42
- return if subscription.nil?
43
-
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?
49
-
50
- # User canceled or failed to make payments
51
- billable.update(braintree_subscription_id: nil)
52
- end
53
-
54
- def subscription_trial_ended(event)
55
- subscription = event.subscription
56
- return if subscription.nil?
57
-
58
- pay_subscription = Pay.subscription_model.find_by(processor: :braintree, processor_id: subscription.id)
59
- return unless pay_subscription.present?
60
-
61
- pay_subscription.update(trial_ends_at: Time.zone.now)
17
+ def delegate_event(event)
18
+ Pay::Webhooks.instrument type: "braintree.#{event.kind}", event: event
62
19
  end
63
20
 
64
- def webhook_notification
65
- @webhook_notification ||= Pay.braintree_gateway.webhook_notification.parse(
66
- params[:bt_signature],
67
- params[:bt_payload]
68
- )
21
+ def verified_event
22
+ Pay.braintree_gateway.webhook_notification.parse(params[:bt_signature], params[:bt_payload])
69
23
  end
70
24
  end
71
25
  end
@@ -6,28 +6,29 @@ module Pay
6
6
  end
7
7
 
8
8
  def create
9
- verifier = Pay::Paddle::Webhooks::SignatureVerifier.new(check_params.as_json)
10
- if verifier.verify
11
- case params["alert_name"]
12
- when "subscription_created"
13
- Pay::Paddle::Webhooks::SubscriptionCreated.new(check_params.as_json)
14
- when "subscription_updated"
15
- Pay::Paddle::Webhooks::SubscriptionUpdated.new(check_params.as_json)
16
- when "subscription_cancelled"
17
- Pay::Paddle::Webhooks::SubscriptionCancelled.new(check_params.as_json)
18
- when "subscription_payment_succeeded"
19
- Pay::Paddle::Webhooks::SubscriptionPaymentSucceeded.new(check_params.as_json)
20
- when "subscription_payment_refunded"
21
- Pay::Paddle::Webhooks::SubscriptionPaymentRefunded.new(check_params.as_json)
22
- end
23
- render json: {success: true}, status: :ok
24
- else
25
- head :ok
26
- end
9
+ delegate_event(verified_event)
10
+ head :ok
11
+ rescue Pay::Paddle::Error
12
+ head :bad_request
27
13
  end
28
14
 
29
15
  private
30
16
 
17
+ def delegate_event(event)
18
+ Pay::Webhooks.instrument type: "paddle.#{type}", event: event
19
+ end
20
+
21
+ def type
22
+ params[:alert_name]
23
+ end
24
+
25
+ def verified_event
26
+ event = check_params.as_json
27
+ verifier = Pay::Paddle::Webhooks::SignatureVerifier.new(event)
28
+ return event if verifier.verify
29
+ raise Pay::Paddle::Error, "Unable to verify Paddle webhook event"
30
+ end
31
+
31
32
  def check_params
32
33
  params.except(:action, :controller).permit!
33
34
  end
@@ -0,0 +1,47 @@
1
+ module Pay
2
+ module Webhooks
3
+ class StripeController < Pay::ApplicationController
4
+ if Rails.application.config.action_controller.default_protect_from_forgery
5
+ skip_before_action :verify_authenticity_token
6
+ end
7
+
8
+ def create
9
+ delegate_event(verified_event)
10
+ head :ok
11
+ rescue ::Stripe::SignatureVerificationError => e
12
+ log_error(e)
13
+ head :bad_request
14
+ end
15
+
16
+ private
17
+
18
+ def delegate_event(event)
19
+ Pay::Webhooks.instrument type: "stripe.#{event.type}", event: event
20
+ end
21
+
22
+ def verified_event
23
+ payload = request.body.read
24
+ signature = request.headers["Stripe-Signature"]
25
+ possible_secrets = secrets(payload, signature)
26
+
27
+ possible_secrets.each_with_index do |secret, i|
28
+ return ::Stripe::Webhook.construct_event(payload, signature, secret.to_s)
29
+ rescue ::Stripe::SignatureVerificationError
30
+ raise if i == possible_secrets.length - 1
31
+ next
32
+ end
33
+ end
34
+
35
+ def secrets(payload, signature)
36
+ secret = Pay::Stripe.signing_secret
37
+ return Array.wrap(secret) if secret
38
+ raise ::Stripe::SignatureVerificationError.new("Cannot verify signature without a Stripe signing secret", signature, http_body: payload)
39
+ end
40
+
41
+ def log_error(e)
42
+ logger.error e.message
43
+ e.backtrace.each { |line| logger.error " #{line}" }
44
+ end
45
+ end
46
+ end
47
+ end
data/config/routes.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Pay::Engine.routes.draw do
4
4
  resources :payments, only: [:show], module: :pay
5
- post "webhooks/stripe", to: "stripe_event/webhook#event"
5
+ post "webhooks/stripe", to: "pay/webhooks/stripe#create"
6
6
  post "webhooks/braintree", to: "pay/webhooks/braintree#create"
7
7
  post "webhooks/paddle", to: "pay/webhooks/paddle#create"
8
8
  end
data/lib/pay.rb CHANGED
@@ -6,6 +6,18 @@ require "pay/payment"
6
6
  require "pay/errors"
7
7
 
8
8
  module Pay
9
+ module Webhooks
10
+ autoload :Delegator, "pay/webhooks/delegator"
11
+
12
+ class << self
13
+ delegate :configure, :instrument, to: :delegator
14
+
15
+ def delegator
16
+ @delegator ||= Delegator.new
17
+ end
18
+ end
19
+ end
20
+
9
21
  # Define who owns the subscription
10
22
  mattr_accessor :billable_class
11
23
  mattr_accessor :billable_table
@@ -37,15 +49,6 @@ module Pay
37
49
  mattr_accessor :send_emails
38
50
  @@send_emails = true
39
51
 
40
- mattr_accessor :email_receipt_subject
41
- @@email_receipt_subject = "Payment receipt"
42
- mattr_accessor :email_refund_subject
43
- @@email_refund_subject = "Payment refunded"
44
- mattr_accessor :email_renewing_subject
45
- @@email_renewing_subject = "Your upcoming subscription renewal"
46
- mattr_accessor :email_action_required_subject
47
- @@email_action_required_subject = "Confirm your payment"
48
-
49
52
  mattr_accessor :automount_routes
50
53
  @@automount_routes = true
51
54
 
data/lib/pay/braintree.rb CHANGED
@@ -2,6 +2,7 @@ require "pay/env"
2
2
  require "pay/braintree/billable"
3
3
  require "pay/braintree/charge"
4
4
  require "pay/braintree/subscription"
5
+ require "pay/braintree/webhooks"
5
6
 
6
7
  module Pay
7
8
  module Braintree
@@ -0,0 +1,11 @@
1
+ Dir[File.join(__dir__, "webhooks", "**", "*.rb")].sort.each { |file| require file }
2
+
3
+ Pay::Webhooks.configure do |events|
4
+ events.subscribe "braintree.subscription_canceled", Pay::Braintree::Webhooks::SubscriptionCanceled.new
5
+ events.subscribe "braintree.subscription_charged_successfully", Pay::Braintree::Webhooks::SubscriptionChargedSuccessfully.new
6
+ events.subscribe "braintree.subscription_charged_unsuccessfully", Pay::Braintree::Webhooks::SubscriptionChargedUnsuccessfully.new
7
+ events.subscribe "braintree.subscription_expired", Pay::Braintree::Webhooks::SubscriptionExpired.new
8
+ events.subscribe "braintree.subscription_trial_ended", Pay::Braintree::Webhooks::SubscriptionTrialEnded.new
9
+ events.subscribe "braintree.subscription_went_active", Pay::Braintree::Webhooks::SubscriptionWentActive.new
10
+ events.subscribe "braintree.subscription_went_past_due", Pay::Braintree::Webhooks::SubscriptionWentPastDue.new
11
+ end
@@ -0,0 +1,19 @@
1
+ # A subscription is canceled.
2
+
3
+ module Pay
4
+ module Braintree
5
+ module Webhooks
6
+ class SubscriptionCanceled
7
+ def call(event)
8
+ subscription = event.subscription
9
+ return if subscription.nil?
10
+
11
+ pay_subscription = Pay.subscription_model.find_by(processor: :braintree, processor_id: subscription.id)
12
+ return unless pay_subscription.present?
13
+
14
+ pay_subscription.update!(ends_at: Time.current, status: :canceled)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,24 @@
1
+ # A subscription successfully moves to the next billing cycle. This will also occur when either a new transaction is created mid-cycle due to proration on an upgrade or a billing cycle is skipped due to the presence of a negative balance that covers the cost of the subscription.
2
+
3
+ module Pay
4
+ module Braintree
5
+ module Webhooks
6
+ class SubscriptionChargedSuccessfully
7
+ def call(event)
8
+ subscription = event.subscription
9
+ return if subscription.nil?
10
+
11
+ pay_subscription = Pay.subscription_model.find_by(processor: :braintree, processor_id: subscription.id)
12
+ return unless pay_subscription.present?
13
+
14
+ billable = pay_subscription.owner
15
+ charge = billable.save_braintree_transaction(subscription.transactions.first)
16
+
17
+ if Pay.send_emails
18
+ Pay::UserMailer.with(billable: billable, charge: charge).receipt.deliver_later
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ # A subscription successfully moves to the next billing cycle. This will also occur when either a new transaction is created mid-cycle due to proration on an upgrade or a billing cycle is skipped due to the presence of a negative balance that covers the cost of the subscription.
2
+
3
+ module Pay
4
+ module Braintree
5
+ module Webhooks
6
+ class SubscriptionChargedUnsuccessfully
7
+ def call(event)
8
+ subscription = event.subscription
9
+ return if subscription.nil?
10
+
11
+ pay_subscription = Pay.subscription_model.find_by(processor: :braintree, processor_id: subscription.id)
12
+ return unless pay_subscription.present?
13
+
14
+ # billable = pay_subscription.owner
15
+ # charge = billable.save_braintree_transaction(subscription.transactions.first)
16
+
17
+ # if Pay.send_emails
18
+ # Pay::UserMailer.with(billable: billable, charge: charge).receipt.deliver_later
19
+ # end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ # A subscription reaches the specified number of billing cycles and expires.
2
+
3
+ module Pay
4
+ module Braintree
5
+ module Webhooks
6
+ class SubscriptionExpired
7
+ def call(event)
8
+ subscription = event.subscription
9
+ return if subscription.nil?
10
+
11
+ pay_subscription = Pay.subscription_model.find_by(processor: :braintree, processor_id: subscription.id)
12
+ return unless pay_subscription.present?
13
+
14
+ pay_subscription.update!(ends_at: Time.current, status: :canceled)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # A subscription's trial period ends.
2
+
3
+ module Pay
4
+ module Braintree
5
+ module Webhooks
6
+ class SubscriptionTrialEnded
7
+ def call(event)
8
+ subscription = event.subscription
9
+ return if subscription.nil?
10
+
11
+ pay_subscription = Pay.subscription_model.find_by(processor: :braintree, processor_id: subscription.id)
12
+ return unless pay_subscription.present?
13
+
14
+ pay_subscription.update(trial_ends_at: Time.zone.now)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # A subscription's first authorized transaction is created, or a successful transaction moves a subscription from the Past Due status to the Active status. Subscriptions with trial periods will not trigger this notification when they move from the trial period into the first billing cycle.
2
+
3
+ module Pay
4
+ module Braintree
5
+ module Webhooks
6
+ class SubscriptionWentActive
7
+ def call(event)
8
+ subscription = event.subscription
9
+ return if subscription.nil?
10
+
11
+ pay_subscription = Pay.subscription_model.find_by(processor: :braintree, processor_id: subscription.id)
12
+ return unless pay_subscription.present?
13
+
14
+ pay_subscription.update!(status: :active)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # A subscription has moved from the Active status to the Past Due status. This will only be triggered when the initial transaction in a billing cycle is declined. Once the status moves to past due, it will not be triggered again in that billing cycle.
2
+
3
+ module Pay
4
+ module Braintree
5
+ module Webhooks
6
+ class SubscriptionWentPastDue
7
+ def call(event)
8
+ subscription = event.subscription
9
+ return if subscription.nil?
10
+
11
+ pay_subscription = Pay.subscription_model.find_by(processor: :braintree, processor_id: subscription.id)
12
+ return unless pay_subscription.present?
13
+
14
+ pay_subscription.update!(status: :past_due)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
data/lib/pay/engine.rb CHANGED
@@ -8,7 +8,6 @@ end
8
8
 
9
9
  begin
10
10
  require "stripe"
11
- require "stripe_event"
12
11
  rescue LoadError
13
12
  end
14
13
 
@@ -1 +1,9 @@
1
1
  Dir[File.join(__dir__, "webhooks", "**", "*.rb")].sort.each { |file| require file }
2
+
3
+ Pay::Webhooks.configure do |events|
4
+ events.subscribe "paddle.subscription_created", Pay::Paddle::Webhooks::SubscriptionCreated.new
5
+ events.subscribe "paddle.subscription_updated", Pay::Paddle::Webhooks::SubscriptionUpdated.new
6
+ events.subscribe "paddle.subscription_cancelled", Pay::Paddle::Webhooks::SubscriptionCancelled.new
7
+ events.subscribe "paddle.subscription_payment_succeeded", Pay::Paddle::Webhooks::SubscriptionPaymentSucceeded.new
8
+ events.subscribe "paddle.subscription_payment_refunded", Pay::Paddle::Webhooks::SubscriptionPaymentRefunded.new
9
+ end
@@ -2,15 +2,15 @@ module Pay
2
2
  module Paddle
3
3
  module Webhooks
4
4
  class SubscriptionCancelled
5
- def initialize(data)
6
- subscription = Pay.subscription_model.find_by(processor: :paddle, processor_id: data["subscription_id"])
5
+ def call(event)
6
+ subscription = Pay.subscription_model.find_by(processor: :paddle, processor_id: event["subscription_id"])
7
7
 
8
8
  # We couldn't find the subscription for some reason, maybe it's from another service
9
9
  return if subscription.nil?
10
10
 
11
11
  # User canceled subscriptions have an ends_at
12
12
  # Automatically canceled subscriptions need this value set
13
- subscription.update!(ends_at: Time.zone.parse(data["cancellation_effective_date"])) if subscription.ends_at.blank? && data["cancellation_effective_date"].present?
13
+ subscription.update!(ends_at: Time.zone.parse(event["cancellation_effective_date"])) if subscription.ends_at.blank? && event["cancellation_effective_date"].present?
14
14
  end
15
15
  end
16
16
  end
@@ -2,42 +2,42 @@ module Pay
2
2
  module Paddle
3
3
  module Webhooks
4
4
  class SubscriptionCreated
5
- def initialize(data)
5
+ def call(event)
6
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"])
7
+ subscription = Pay.subscription_model.find_by(processor: :paddle, processor_id: event["subscription_id"])
8
8
 
9
9
  # Create the subscription in the database if we don't have it already
10
10
  if subscription.nil?
11
11
 
12
12
  # The customer could already be in the database
13
- owner = Pay.find_billable(processor: :paddle, processor_id: data["user_id"])
13
+ owner = Pay.find_billable(processor: :paddle, processor_id: event["user_id"])
14
14
 
15
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"])
16
+ owner = owner_by_passtrough(event["passthrough"], event["subscription_plan_id"])
17
+ owner&.update!(processor: "paddle", processor_id: event["user_id"])
18
18
  end
19
19
 
20
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(", ")}")
21
+ Rails.logger.error("[Pay] Unable to find Pay::Billable with owner: '#{event["passthrough"]}'. Searched these models: #{Pay.billable_models.join(", ")}")
22
22
  return
23
23
  end
24
24
 
25
- subscription = Pay.subscription_model.new(owner: owner, name: Pay.default_product_name, processor: "paddle", processor_id: data["subscription_id"], status: :active)
25
+ subscription = Pay.subscription_model.new(owner: owner, name: Pay.default_product_name, processor: "paddle", processor_id: event["subscription_id"], status: :active)
26
26
  end
27
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"
28
+ subscription.quantity = event["quantity"]
29
+ subscription.processor_plan = event["subscription_plan_id"]
30
+ subscription.paddle_update_url = event["update_url"]
31
+ subscription.paddle_cancel_url = event["cancel_url"]
32
+ subscription.trial_ends_at = Time.zone.parse(event["next_bill_date"]) if event["status"] == "trialing"
33
33
 
34
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?
35
+ subscription.ends_at = if ["paused", "deleted"].include?(event["status"]) && subscription.on_trial?
36
36
  subscription.trial_ends_at
37
37
 
38
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"])
39
+ elsif ["paused", "deleted"].include?(event["status"])
40
+ Time.zone.parse(event["next_bill_date"])
41
41
 
42
42
  # Subscription isn't marked to cancel at period end
43
43
  end
@@ -2,11 +2,11 @@ module Pay
2
2
  module Paddle
3
3
  module Webhooks
4
4
  class SubscriptionPaymentRefunded
5
- def initialize(data)
6
- charge = Pay.charge_model.find_by(processor: :paddle, processor_id: data["subscription_payment_id"])
5
+ def call(event)
6
+ charge = Pay.charge_model.find_by(processor: :paddle, processor_id: event["subscription_payment_id"])
7
7
  return unless charge.present?
8
8
 
9
- charge.update(amount_refunded: Integer(data["gross_refund"].to_f * 100))
9
+ charge.update(amount_refunded: Integer(event["gross_refund"].to_f * 100))
10
10
  notify_user(charge.owner, charge)
11
11
  end
12
12
 
@@ -2,29 +2,29 @@ module Pay
2
2
  module Paddle
3
3
  module Webhooks
4
4
  class SubscriptionPaymentSucceeded
5
- def initialize(data)
6
- billable = Pay.find_billable(processor: :paddle, processor_id: data["user_id"])
5
+ def call(event)
6
+ billable = Pay.find_billable(processor: :paddle, processor_id: event["user_id"])
7
7
  return unless billable.present?
8
- return if billable.charges.where(processor_id: data["subscription_payment_id"]).any?
8
+ return if billable.charges.where(processor_id: event["subscription_payment_id"]).any?
9
9
 
10
- charge = create_charge(billable, data)
10
+ charge = create_charge(billable, event)
11
11
  notify_user(billable, charge)
12
12
  end
13
13
 
14
- def create_charge(user, data)
14
+ def create_charge(user, event)
15
15
  charge = user.charges.find_or_initialize_by(
16
16
  processor: :paddle,
17
- processor_id: data["subscription_payment_id"]
17
+ processor_id: event["subscription_payment_id"]
18
18
  )
19
19
 
20
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: Time.zone.parse(data["event_time"])
21
+ amount: Integer(event["sale_gross"].to_f * 100),
22
+ card_type: event["payment_method"],
23
+ paddle_receipt_url: event["receipt_url"],
24
+ created_at: Time.zone.parse(event["event_time"])
25
25
  }
26
26
 
27
- payment_information = user.paddle_payment_information(data["subscription_id"])
27
+ payment_information = user.paddle_payment_information(event["subscription_id"])
28
28
 
29
29
  charge.update(params.merge(payment_information))
30
30
  user.update(payment_information)
@@ -2,29 +2,29 @@ module Pay
2
2
  module Paddle
3
3
  module Webhooks
4
4
  class SubscriptionUpdated
5
- def initialize(data)
6
- subscription = Pay.subscription_model.find_by(processor: :paddle, processor_id: data["subscription_id"])
5
+ def call(event)
6
+ subscription = Pay.subscription_model.find_by(processor: :paddle, processor_id: event["subscription_id"])
7
7
 
8
8
  return if subscription.nil?
9
9
 
10
- case data["status"]
10
+ case event["status"]
11
11
  when "deleted"
12
12
  subscription.status = "canceled"
13
- subscription.ends_at = Time.zone.parse(data["next_bill_date"]) || Time.zone.now if subscription.ends_at.blank?
13
+ subscription.ends_at = Time.zone.parse(event["next_bill_date"]) || Time.zone.now if subscription.ends_at.blank?
14
14
  when "trialing"
15
15
  subscription.status = "trialing"
16
- subscription.trial_ends_at = Time.zone.parse(data["next_bill_date"])
16
+ subscription.trial_ends_at = Time.zone.parse(event["next_bill_date"])
17
17
  when "active"
18
18
  subscription.status = "active"
19
- subscription.paddle_paused_from = Time.zone.parse(data["paused_from"]) if data["paused_from"].present?
19
+ subscription.paddle_paused_from = Time.zone.parse(event["paused_from"]) if event["paused_from"].present?
20
20
  else
21
- subscription.status = data["status"]
21
+ subscription.status = event["status"]
22
22
  end
23
23
 
24
- subscription.quantity = data["new_quantity"]
25
- subscription.processor_plan = data["subscription_plan_id"]
26
- subscription.paddle_update_url = data["update_url"]
27
- subscription.paddle_cancel_url = data["cancel_url"]
24
+ subscription.quantity = event["new_quantity"]
25
+ subscription.processor_plan = event["subscription_plan_id"]
26
+ subscription.paddle_update_url = event["update_url"]
27
+ subscription.paddle_cancel_url = event["cancel_url"]
28
28
 
29
29
  # If user was on trial, their subscription ends at the end of the trial
30
30
  subscription.ends_at = subscription.trial_ends_at if subscription.on_trial?
data/lib/pay/stripe.rb CHANGED
@@ -13,7 +13,6 @@ module Pay
13
13
  def setup
14
14
  ::Stripe.api_key = private_key
15
15
  ::Stripe.api_version = "2020-08-27"
16
- ::StripeEvent.signing_secret = signing_secret
17
16
 
18
17
  # Used by Stripe to identify Pay for support
19
18
  ::Stripe.set_app_info("PayRails", partner_id: "pp_partner_IqhY0UExnJYLxg", version: Pay::VERSION, url: "https://github.com/pay-rails/pay")
@@ -1,39 +1,38 @@
1
- require "stripe_event"
2
1
  Dir[File.join(__dir__, "webhooks", "**", "*.rb")].sort.each { |file| require file }
3
2
 
4
- StripeEvent.configure do |events|
3
+ Pay::Webhooks.configure do |events|
5
4
  # Listen to the charge event to make sure we get non-subscription
6
5
  # purchases as well. Invoice is only for subscriptions and manual creation
7
6
  # so it does not include individual charges.
8
- events.subscribe "charge.succeeded", Pay::Stripe::Webhooks::ChargeSucceeded.new
9
- events.subscribe "charge.refunded", Pay::Stripe::Webhooks::ChargeRefunded.new
7
+ events.subscribe "stripe.charge.succeeded", Pay::Stripe::Webhooks::ChargeSucceeded.new
8
+ events.subscribe "stripe.charge.refunded", Pay::Stripe::Webhooks::ChargeRefunded.new
10
9
 
11
10
  # Warn user of upcoming charges for their subscription. This is handy for
12
11
  # notifying annual users their subscription will renew shortly.
13
12
  # This probably should be ignored for monthly subscriptions.
14
- events.subscribe "invoice.upcoming", Pay::Stripe::Webhooks::SubscriptionRenewing.new
13
+ events.subscribe "stripe.invoice.upcoming", Pay::Stripe::Webhooks::SubscriptionRenewing.new
15
14
 
16
15
  # Payment action is required to process an invoice
17
- events.subscribe "invoice.payment_action_required", Pay::Stripe::Webhooks::PaymentActionRequired.new
16
+ events.subscribe "stripe.invoice.payment_action_required", Pay::Stripe::Webhooks::PaymentActionRequired.new
18
17
 
19
18
  # If a subscription is manually created on Stripe, we want to sync
20
- events.subscribe "customer.subscription.created", Pay::Stripe::Webhooks::SubscriptionCreated.new
19
+ events.subscribe "stripe.customer.subscription.created", Pay::Stripe::Webhooks::SubscriptionCreated.new
21
20
 
22
21
  # If the plan, quantity, or trial ending date is updated on Stripe, we want to sync
23
- events.subscribe "customer.subscription.updated", Pay::Stripe::Webhooks::SubscriptionUpdated.new
22
+ events.subscribe "stripe.customer.subscription.updated", Pay::Stripe::Webhooks::SubscriptionUpdated.new
24
23
 
25
24
  # When a customers subscription is canceled, we want to update our records
26
- events.subscribe "customer.subscription.deleted", Pay::Stripe::Webhooks::SubscriptionDeleted.new
25
+ events.subscribe "stripe.customer.subscription.deleted", Pay::Stripe::Webhooks::SubscriptionDeleted.new
27
26
 
28
27
  # Monitor changes for customer's default card changing
29
- events.subscribe "customer.updated", Pay::Stripe::Webhooks::CustomerUpdated.new
28
+ events.subscribe "stripe.customer.updated", Pay::Stripe::Webhooks::CustomerUpdated.new
30
29
 
31
30
  # If a customer was deleted in Stripe, their subscriptions should be cancelled
32
- events.subscribe "customer.deleted", Pay::Stripe::Webhooks::CustomerDeleted.new
31
+ events.subscribe "stripe.customer.deleted", Pay::Stripe::Webhooks::CustomerDeleted.new
33
32
 
34
33
  # If a customer's payment source was deleted in Stripe, we should update as well
35
- events.subscribe "payment_method.attached", Pay::Stripe::Webhooks::PaymentMethodUpdated.new
36
- events.subscribe "payment_method.updated", Pay::Stripe::Webhooks::PaymentMethodUpdated.new
37
- events.subscribe "payment_method.card_automatically_updated", Pay::Stripe::Webhooks::PaymentMethodUpdated.new
38
- events.subscribe "payment_method.detached", Pay::Stripe::Webhooks::PaymentMethodUpdated.new
34
+ events.subscribe "stripe.payment_method.attached", Pay::Stripe::Webhooks::PaymentMethodUpdated.new
35
+ events.subscribe "stripe.payment_method.updated", Pay::Stripe::Webhooks::PaymentMethodUpdated.new
36
+ events.subscribe "stripe.payment_method.card_automatically_updated", Pay::Stripe::Webhooks::PaymentMethodUpdated.new
37
+ events.subscribe "stripe.payment_method.detached", Pay::Stripe::Webhooks::PaymentMethodUpdated.new
39
38
  end
@@ -22,6 +22,7 @@ module Pay
22
22
  end
23
23
 
24
24
  subscription.quantity = object.quantity
25
+ subscription.status = object.status
25
26
  subscription.processor_plan = object.plan.id
26
27
  subscription.trial_ends_at = Time.at(object.trial_end) if object.trial_end.present?
27
28
 
data/lib/pay/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Pay
2
- VERSION = "2.4.4"
2
+ VERSION = "2.5.0"
3
3
  end
@@ -0,0 +1,61 @@
1
+ module Pay
2
+ module Webhooks
3
+ class Delegator
4
+ attr_reader :backend
5
+
6
+ def initialize
7
+ @backend = ActiveSupport::Notifications
8
+ end
9
+
10
+ # Configure DSL
11
+ def configure(&block)
12
+ raise ArgumentError, "must provide a block" unless block
13
+ block.arity.zero? ? instance_eval(&block) : yield(self)
14
+ end
15
+
16
+ # Subscribe to specific events
17
+ def subscribe(name, callable = nil, &block)
18
+ callable ||= block
19
+ backend.subscribe to_regexp(name), NotificationAdapter.new(callable)
20
+ end
21
+
22
+ # Listen to all events
23
+ def all(callable = nil, &block)
24
+ callable ||= block
25
+ subscribe nil, callable
26
+ end
27
+
28
+ # Unsubscribe
29
+ def unsubscribe(name)
30
+ backend.unsubscribe name
31
+ end
32
+
33
+ # Called to process an event
34
+ def instrument(event:, type:)
35
+ backend.instrument name_with_namespace(type), event
36
+ end
37
+
38
+ # Strips down to event data only
39
+ class NotificationAdapter
40
+ def initialize(subscriber)
41
+ @subscriber = subscriber
42
+ end
43
+
44
+ def call(*args)
45
+ payload = args.last
46
+ @subscriber.call(payload)
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def to_regexp(name)
53
+ %r{^#{Regexp.escape name_with_namespace(name)}}
54
+ end
55
+
56
+ def name_with_namespace(name, delimiter: ".")
57
+ [:pay, name].join(delimiter)
58
+ end
59
+ end
60
+ end
61
+ 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.4.4
4
+ version: 2.5.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: 2021-02-04 00:00:00.000000000 Z
12
+ date: 2021-02-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -59,20 +59,6 @@ dependencies:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '2.8'
62
- - !ruby/object:Gem::Dependency
63
- name: stripe_event
64
- requirement: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '2.3'
69
- type: :development
70
- prerelease: false
71
- version_requirements: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '2.3'
76
62
  - !ruby/object:Gem::Dependency
77
63
  name: paddle_pay
78
64
  requirement: !ruby/object:Gem::Requirement
@@ -181,6 +167,7 @@ files:
181
167
  - app/controllers/pay/payments_controller.rb
182
168
  - app/controllers/pay/webhooks/braintree_controller.rb
183
169
  - app/controllers/pay/webhooks/paddle_controller.rb
170
+ - app/controllers/pay/webhooks/stripe_controller.rb
184
171
  - app/helpers/pay/application_helper.rb
185
172
  - app/jobs/pay/application_job.rb
186
173
  - app/jobs/pay/email_sync_job.rb
@@ -214,6 +201,14 @@ files:
214
201
  - lib/pay/braintree/billable.rb
215
202
  - lib/pay/braintree/charge.rb
216
203
  - lib/pay/braintree/subscription.rb
204
+ - lib/pay/braintree/webhooks.rb
205
+ - lib/pay/braintree/webhooks/subscription_canceled.rb
206
+ - lib/pay/braintree/webhooks/subscription_charged_successfully.rb
207
+ - lib/pay/braintree/webhooks/subscription_charged_unsuccessfully.rb
208
+ - lib/pay/braintree/webhooks/subscription_expired.rb
209
+ - lib/pay/braintree/webhooks/subscription_trial_ended.rb
210
+ - lib/pay/braintree/webhooks/subscription_went_active.rb
211
+ - lib/pay/braintree/webhooks/subscription_went_past_due.rb
217
212
  - lib/pay/engine.rb
218
213
  - lib/pay/env.rb
219
214
  - lib/pay/errors.rb
@@ -246,6 +241,7 @@ files:
246
241
  - lib/pay/stripe/webhooks/subscription_renewing.rb
247
242
  - lib/pay/stripe/webhooks/subscription_updated.rb
248
243
  - lib/pay/version.rb
244
+ - lib/pay/webhooks/delegator.rb
249
245
  homepage: https://github.com/jasoncharnes/pay
250
246
  licenses:
251
247
  - MIT