stripe-rails 1.4.2 → 1.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cc77c2742f14d5d7042f2a3ed06af604144c2d956e0fd5a627f45c01e82a9461
4
- data.tar.gz: 570149b8d68d0429d5257e45099739ba9da0c52fe1c5432251207e98edc2f9e8
3
+ metadata.gz: 8fbadf81c0f8478feeffda1a46ed1b29b7fd5771eec9fbec5f2a8cd0684a051f
4
+ data.tar.gz: baa6413f94ecc16b4ac4ba192e29dc7ef3c5742de759ed1e22397a13e71b764e
5
5
  SHA512:
6
- metadata.gz: f5eb46629a7af4564ce45f3bb4f219f93413c88bc46773b2546fb8032d40441c164d62ac207e868ac2efb46622df0cb1a1256e6b9915bd9cfd3675e6d88d8a1b
7
- data.tar.gz: f865412f3696550203443d037b26fb8472a818049014622b67391b7565c0fe97116391129378734cf7e422c110b0c8a740d1c75e913cf229b203a3315e6647c0
6
+ metadata.gz: ca51fc8a16bac65cd5ecda5d92130ecf18e83870cacc3c422952846d61359f89ecb877151b4ff9ea93c0e1b25746dd9efe6f86d6790a363de0ede13e6727a9c3
7
+ data.tar.gz: 7b380bb3b762c6acc39de20b4d0187c15623dc3141adda0bed62d0017a533ccbb546ba4352682d903dae04bf567f504f0a8eb27487b3ab2327d21f1fe973cb41
data/Changelog.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 1.5.0 (2018-09-27)
2
+
3
+ * Add Webhook Signature Validation - Thanks to @wkirby
4
+ * Include nickname in the payload for plans - Thanks to @jeanmartin
5
+
1
6
  ## 1.4.2 (2018-08-06)
2
7
 
3
8
  * New attributes for Stripe Billing Plans.
data/README.md CHANGED
@@ -212,6 +212,61 @@ config.stripe.endpoint = '/payment/stripe-integration'
212
212
 
213
213
  Your new webhook URL would then be `http://myproductionapp/payment/stripe-integration/events`
214
214
 
215
+ ### Signed Webhooks
216
+
217
+ Validation of your webhook's signature uses your webhook endpoint signing secret.
218
+ Before you can verify signatures, you need to retrieve your endpoint’s secret your
219
+ Stripe Dashboard. Select an endpoint for which you want to obtain
220
+ the secret, then select the Click to reveal button.
221
+
222
+ ```ruby
223
+ # config/application.rb
224
+ # ...
225
+ config.stripe.signing_secret = 'whsec_XXXYYYZZZ'
226
+ ```
227
+
228
+ Each secret is unique to the endpoint to which it corresponds. If you use multiple endpoints,
229
+ you must obtain a secret for each one. After this setup, Stripe starts to sign each webhook
230
+ it sends to the endpoint. Because of this, we recommend setting this variable with an environment
231
+ variable:
232
+
233
+ ```sh
234
+ export STRIPE_SIGNING_SECRET=whsec_XXXYYYZZZ
235
+ ```
236
+
237
+ ```ruby
238
+ config.stripe.signing_secret = ENV.fetch('STRIPE_SIGNING_SECRET')
239
+ ```
240
+
241
+ #### Testing Signed Webhooks Locally
242
+
243
+ In order to test signed webhooks, you'll need to trigger test webhooks from your Stripe dashboard,
244
+ and configure your local environment to receive remote network requests. To do so, we recommend using
245
+ [ngrok](https://ngrok.com/) to configure a secure tunnel to `localhost`.
246
+
247
+ Once configured and running, `ngrok` will give you a unique URL which can be used to set up a webhook
248
+ endpoint. Webhook endpoints are configured in your Dashboard's [Webhook settings](https://dashboard.stripe.com/account/webhooks)
249
+ section. Make sure you are in **Test** mode and click `Add endpoint`, and provide your `ngrok` URL along with the `stripe.endpoint` suffix.
250
+
251
+ An example webhook URL would then be `https://bf2a5d21.ngrok.io/stripe/events`.
252
+
253
+ Once your endpoint is configured, you can reveal the **Signing secret**. This will need to be set
254
+ as documented above:
255
+
256
+ ```ruby
257
+ # config/application.rb
258
+ # ...
259
+ config.stripe.signing_secret = 'whsec_XXXYYYZZZ'
260
+ ```
261
+
262
+ And you'll need to restart your rails server with:
263
+
264
+ ```sh
265
+ rails restart
266
+ ```
267
+
268
+ Now you're ready to click **Send test webhook**, and trigger whichever events you'd like to test from Stripe itself.
269
+
215
270
  ### Disabling auto mount
216
271
 
217
272
  Sometimes, you don't want the stripe engine to be auto-mounted so that
@@ -4,8 +4,14 @@ module Stripe
4
4
  respond_to :json
5
5
 
6
6
  def create
7
- @event = dispatch_stripe_event params
7
+ @event = dispatch_stripe_event(request)
8
8
  head :ok
9
+ rescue JSON::ParserError => e
10
+ ::Rails.logger.error e.message
11
+ head :bad_request, status: 400
12
+ rescue Stripe::SignatureVerificationError => e
13
+ ::Rails.logger.error e.message
14
+ head :bad_request, status: 400
9
15
  end
10
16
  end
11
17
  end
@@ -1,20 +1,29 @@
1
1
  require 'stripe/event'
2
2
  module Stripe
3
3
  module EventDispatch
4
- def dispatch_stripe_event(params)
5
- retrieve_stripe_event(params) do |evt|
4
+ def dispatch_stripe_event(request)
5
+ retrieve_stripe_event(request) do |evt|
6
6
  target = evt.data.object
7
7
  ::Stripe::Callbacks.run_callbacks(evt, target)
8
8
  end
9
9
  end
10
10
 
11
- def retrieve_stripe_event(params)
12
- id = params['id']
13
- if id == 'evt_00000000000000' #this is a webhook test
14
- yield Stripe::Event.construct_from(params.to_unsafe_h)
11
+ def retrieve_stripe_event(request)
12
+ id = request['id']
13
+ body = request.body.read
14
+ sig_header = request.headers['HTTP_STRIPE_SIGNATURE']
15
+ endpoint_secret = ::Rails.application.config.stripe.signing_secret
16
+
17
+ # this is a webhook test
18
+ if id == 'evt_00000000000000'
19
+ event = Stripe::Event.construct_from(JSON.parse(body))
20
+ elsif Object.const_defined?('Stripe::Webhook') && sig_header && endpoint_secret
21
+ event = ::Stripe::Webhook.construct_event(body, sig_header, endpoint_secret)
15
22
  else
16
- yield Stripe::Event.retrieve(id)
23
+ event = Stripe::Event.retrieve(id)
17
24
  end
25
+
26
+ yield event
18
27
  end
19
28
  end
20
29
  end
data/lib/stripe/engine.rb CHANGED
@@ -8,7 +8,7 @@ module Stripe
8
8
  attr_accessor :testing
9
9
  end
10
10
 
11
- stripe_config = config.stripe = Struct.new(:api_base, :api_version, :secret_key, :verify_ssl_certs, :publishable_key, :endpoint, :debug_js, :auto_mount, :eager_load, :open_timeout, :read_timeout).new
11
+ stripe_config = config.stripe = Struct.new(:api_base, :api_version, :secret_key, :verify_ssl_certs, :signing_secret, :publishable_key, :endpoint, :debug_js, :auto_mount, :eager_load, :open_timeout, :read_timeout).new
12
12
 
13
13
  def stripe_config.api_key=(key)
14
14
  warn "[DEPRECATION] to align with stripe nomenclature, stripe.api_key has been renamed to config.stripe.secret_key"
data/lib/stripe/plans.rb CHANGED
@@ -22,10 +22,10 @@ module Stripe
22
22
  validates_presence_of :id, :amount, :currency
23
23
 
24
24
  validates_inclusion_of :interval,
25
- :in => %w(day week month year),
26
- :message => "'%{value}' is not one of 'day', 'week', 'month' or 'year'"
25
+ in: %w(day week month year),
26
+ message: "'%{value}' is not one of 'day', 'week', 'month' or 'year'"
27
27
 
28
- validates :statement_descriptor, :length => { :maximum => 22 }
28
+ validates :statement_descriptor, length: { maximum: 22 }
29
29
 
30
30
  validates :active, inclusion: { in: [true, false] }, allow_nil: true
31
31
  validates :usage_type, inclusion: { in: %w{ metered licensed } }, allow_nil: true
@@ -62,31 +62,32 @@ module Stripe
62
62
 
63
63
  def default_create_options
64
64
  {
65
- :currency => @currency,
65
+ currency: currency,
66
66
  product: product_options,
67
- :amount => @amount,
68
- :interval => @interval,
69
- :interval_count => @interval_count,
70
- :trial_period_days => @trial_period_days,
71
- :metadata => @metadata,
72
- }
67
+ amount: amount,
68
+ interval: interval,
69
+ interval_count: interval_count,
70
+ trial_period_days: trial_period_days,
71
+ metadata: metadata,
72
+ nickname: nickname
73
+ }.delete_if{|_, v| v.nil? }
73
74
  end
74
75
 
75
76
  def product_options
76
- @product_id.presence || { :name => @name, :statement_descriptor => @statement_descriptor }
77
+ product_id.presence || { name: name, statement_descriptor: statement_descriptor }
77
78
  end
78
79
 
79
80
  def create_options_without_products
80
81
  {
81
- :currency => @currency,
82
- :name => @name,
83
- :amount => @amount,
84
- :interval => @interval,
85
- :interval_count => @interval_count,
86
- :trial_period_days => @trial_period_days,
87
- :metadata => @metadata,
88
- :statement_descriptor => @statement_descriptor
89
- }
82
+ currency: currency,
83
+ name: name,
84
+ amount: amount,
85
+ interval: interval,
86
+ interval_count: interval_count,
87
+ trial_period_days: trial_period_days,
88
+ metadata: metadata,
89
+ statement_descriptor: statement_descriptor
90
+ }.delete_if{|_, v| v.nil? }
90
91
  end
91
92
  end
92
93
  end
@@ -1,5 +1,5 @@
1
1
  module Stripe
2
2
  module Rails
3
- VERSION = '1.4.2'
3
+ VERSION = '1.5.0'
4
4
  end
5
5
  end
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  class DummyStripesControllerSpec < ApplicationSystemTestCase
2
4
  setup do
3
5
  Dummy::Application.configure do
@@ -1,7 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Stripe::EventsController do
4
- parallelize_me!
5
4
  include Rack::Test::Methods
6
5
 
7
6
  let(:app) { Rails.application }
@@ -11,7 +10,7 @@ describe Stripe::EventsController do
11
10
  end
12
11
 
13
12
  describe 'the events interface' do
14
- let(:params) {
13
+ let(:params) {
15
14
  {
16
15
  id: 'evt_00000000000000',
17
16
  type: 'customer.updated',
@@ -22,4 +21,36 @@ describe Stripe::EventsController do
22
21
 
23
22
  it { subject.must_be :ok? }
24
23
  end
24
+
25
+ describe 'signed webhooks' do
26
+ before do
27
+ header 'Stripe-Signature', 't=1537832721,v1=123,v0=123'
28
+ app.config.stripe.signing_secret = 'SECRET'
29
+ end
30
+
31
+ after { app.config.stripe.signing_secret = nil }
32
+
33
+ let(:params) {
34
+ {
35
+ id: 'evt_00000000000001',
36
+ type: 'customer.updated',
37
+ data: {
38
+ object: 'customer',
39
+ fingerprint: 'xxxyyyzzz'
40
+ },
41
+ }
42
+ }
43
+
44
+ subject { post '/stripe/events', params.to_json }
45
+
46
+ it 'returns bad_request when invalid' do
47
+ Stripe::Webhook.expects(:construct_event).raises(Stripe::SignatureVerificationError.new('msg', 'sig_header'))
48
+ subject.must_be :bad_request?
49
+ end
50
+
51
+ it 'returns ok when valid' do
52
+ Stripe::Webhook.expects(:construct_event).returns(Stripe::Event.construct_from(params))
53
+ subject.must_be :ok?
54
+ end
55
+ end
25
56
  end
@@ -218,9 +218,7 @@ describe 'building plans' do
218
218
  :amount => 699,
219
219
  :interval => 'month',
220
220
  :interval_count => 1,
221
- :trial_period_days => 0,
222
- :metadata => nil,
223
- :statement_descriptor => nil
221
+ :trial_period_days => 0
224
222
  )
225
223
  Stripe::Plans::GOLD.put!
226
224
  end
@@ -233,9 +231,7 @@ describe 'building plans' do
233
231
  :amount => 699,
234
232
  :interval => 'month',
235
233
  :interval_count => 1,
236
- :trial_period_days => 0,
237
- :metadata => nil,
238
- :statement_descriptor => nil
234
+ :trial_period_days => 0
239
235
  )
240
236
  Stripe::Plans::ALTERNATIVE_CURRENCY.put!
241
237
  end
@@ -255,8 +251,7 @@ describe 'building plans' do
255
251
  :amount => 699,
256
252
  :interval => 'month',
257
253
  :interval_count => 1,
258
- :trial_period_days => 0,
259
- :metadata => nil,
254
+ :trial_period_days => 0
260
255
  )
261
256
  Stripe::Plans::GOLD.put!
262
257
  end
@@ -279,8 +274,7 @@ describe 'building plans' do
279
274
  :amount => 699,
280
275
  :interval => 'month',
281
276
  :interval_count => 1,
282
- :trial_period_days => 0,
283
- :metadata => nil,
277
+ :trial_period_days => 0
284
278
  )
285
279
  Stripe::Plans::GOLD.put!
286
280
  end
@@ -303,8 +297,7 @@ describe 'building plans' do
303
297
  :amount => 699,
304
298
  :interval => 'month',
305
299
  :interval_count => 1,
306
- :trial_period_days => 0,
307
- :metadata => nil,
300
+ :trial_period_days => 0
308
301
  )
309
302
 
310
303
  subject
data/test/spec_helper.rb CHANGED
@@ -8,7 +8,13 @@ require 'minitest/autorun'
8
8
  require 'webmock/minitest'
9
9
  WebMock.disable_net_connect!(allow_localhost: true)
10
10
 
11
+ # Chrome Setup
11
12
  require 'selenium-webdriver'
13
+ require 'capybara'
14
+ require 'chromedriver-helper'
15
+ Capybara.register_driver :selenium do |app|
16
+ Capybara::Selenium::Driver.new(app, :browser => :chrome)
17
+ end
12
18
 
13
19
  # Configure Rails Environment
14
20
  ENV["RAILS_ENV"] = "test"
@@ -34,6 +34,7 @@ describe "Configuring the stripe engine" do
34
34
  subject do
35
35
  app.config.stripe.api_base = 'http://localhost:5000'
36
36
  app.config.stripe.secret_key = 'SECRET_XYZ'
37
+ app.config.stripe.signing_secret = 'SIGNING_SECRET_XYZ'
37
38
  app.config.stripe.verify_ssl_certs = false
38
39
  app.config.stripe.api_version = '2015-10-16'
39
40
  app.config.stripe.open_timeout = 33
@@ -50,6 +51,8 @@ describe "Configuring the stripe engine" do
50
51
  Stripe.api_version.must_equal '2015-10-16'
51
52
  Stripe.open_timeout.must_equal 33
52
53
  Stripe.read_timeout.must_equal 88
54
+
55
+ app.config.stripe.signing_secret.must_equal 'SIGNING_SECRET_XYZ'
53
56
  end
54
57
  end
55
58
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stripe-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.2
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charles Lowell
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2018-09-06 00:00:00.000000000 Z
13
+ date: 2018-09-27 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rails
@@ -178,7 +178,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
178
178
  version: '0'
179
179
  requirements: []
180
180
  rubyforge_project:
181
- rubygems_version: 2.7.3
181
+ rubygems_version: 2.7.7
182
182
  signing_key:
183
183
  specification_version: 4
184
184
  summary: A gem to integrate stripe into your rails app