stripe-rails 1.4.2 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
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