stripe-rails 0.1.0 → 0.2.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.
- data/.gitignore +1 -0
- data/Changelog.md +6 -0
- data/README.md +176 -7
- data/app/controllers/stripe/application_controller.rb +5 -0
- data/app/controllers/stripe/events_controller.rb +11 -0
- data/app/controllers/stripe/pings_controller.rb +10 -0
- data/app/helpers/stripe/javascript_helper.rb +7 -0
- data/app/models/stripe/event_dispatch.rb +20 -0
- data/app/models/stripe/ping.rb +9 -0
- data/app/views/stripe/_js.html.erb +8 -0
- data/config/routes.rb +8 -0
- data/lib/stripe-rails.rb +1 -3
- data/lib/stripe/callbacks.rb +73 -0
- data/lib/stripe/callbacks/builder.rb +58 -0
- data/lib/{stripe-rails → stripe}/engine.rb +21 -9
- data/lib/{stripe-rails → stripe}/plans.rb +2 -2
- data/lib/stripe/rails.rb +4 -0
- data/lib/{stripe-rails → stripe/rails}/tasks.rake +1 -1
- data/lib/{stripe-rails → stripe/rails}/version.rb +1 -1
- data/stripe-rails.gemspec +3 -2
- data/test/callbacks_spec.rb +171 -0
- data/test/invoice_payment_succeeded.json +65 -0
- data/test/spec_helper.rb +1 -1
- data/test/stripe_rails_spec.rb +1 -1
- metadata +38 -11
- data/test/dummy/log/test.log +0 -0
- data/vendor/assets/javascripts/stripe-debug.js +0 -279
- data/vendor/assets/javascripts/stripe.js.erb +0 -3
data/.gitignore
CHANGED
data/Changelog.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
## 0.2.0 (2012-12-13)
|
2
|
+
|
3
|
+
* out of the box support for webhooks and critical/non-critical event handlers
|
4
|
+
* add :only guards for which webhooks you respond to-
|
5
|
+
* move stripe.js out of asset pipeline, and insert it with utility functions
|
6
|
+
|
1
7
|
## 0.1.0 (2012-11-14)
|
2
8
|
|
3
9
|
* add config/stripe/plans.rb to define and create plans
|
data/README.md
CHANGED
@@ -1,6 +1,11 @@
|
|
1
|
-
# Stripe::Rails: A
|
1
|
+
# Stripe::Rails: A Rails Engine for use with [stripe.com](https://stripe.com)
|
2
2
|
|
3
|
-
|
3
|
+
This gem can help your rails application integrate with Stripe in the following ways
|
4
|
+
|
5
|
+
* manage stripe configurations in a single place.
|
6
|
+
* makes stripe.js available from the asset pipeline.
|
7
|
+
* manage plans and coupons from within your app.
|
8
|
+
* painlessly receive and validate webhooks from stripe.
|
4
9
|
|
5
10
|
## Installation
|
6
11
|
|
@@ -9,9 +14,23 @@ Add this line to your application's Gemfile:
|
|
9
14
|
gem 'stripe-rails'
|
10
15
|
|
11
16
|
If you are going to be using [stripe.js][1] to securely collect credit card information
|
12
|
-
on the client, then add the
|
17
|
+
on the client, then you will need to add the stripe javascript tags into your template.
|
18
|
+
stripe-rails provides a helper to make this easy:
|
19
|
+
|
20
|
+
<%= stripe_javascript_tag %>
|
21
|
+
|
22
|
+
or, you can render it as a partial:
|
23
|
+
|
24
|
+
<%= render :partial => 'stripe/js' %>
|
13
25
|
|
14
|
-
|
26
|
+
In both cases, stripe-rails will choose a version of stripe.js appropriate for your
|
27
|
+
development environment and automatically configure it to use
|
28
|
+
your publishable API key. By default it uses `stripe-debug.js` for your `development`
|
29
|
+
environment and `stripe.js` for everything else, but you can manually configure it
|
30
|
+
per environment
|
31
|
+
|
32
|
+
config.stripe.debug_js = true # use stripe-debug.js
|
33
|
+
config.stripe.debug_js = false # use stripe.js
|
15
34
|
|
16
35
|
### Setup your API keys.
|
17
36
|
|
@@ -20,7 +39,7 @@ using [your api key][1]. There are two methods to do this, you can either set th
|
|
20
39
|
variable `STRIPE_API_KEY`, or use the rails configuration setting `config.stripe.api_key`.
|
21
40
|
In either case, it is recommended that you *not* check in this value into source control.
|
22
41
|
|
23
|
-
|
42
|
+
You can verify that your api is set up and functioning properly by running the following command:
|
24
43
|
|
25
44
|
rake stripe:verify
|
26
45
|
|
@@ -83,7 +102,157 @@ plans that do, so you can run this command safely as many times as you wish.
|
|
83
102
|
|
84
103
|
NOTE: You must destroy plans manually from your stripe dashboard.
|
85
104
|
|
86
|
-
##
|
105
|
+
## Webhooks
|
106
|
+
|
107
|
+
Stripe::Rails automatically sets up your application to receive webhooks from stripe.com whenever
|
108
|
+
an payment event is generated. To enable this, you will need to configure your [stripe webooks][3] to
|
109
|
+
point back to your application. By default, the webhook controller is mounted at '/stripe/events' so
|
110
|
+
you would want to enter in `http://myproductionapp.com/stripe/events` as your url for live mode,
|
111
|
+
and `http://mystagingapp.com/stripe/events` for your test mode.
|
112
|
+
|
113
|
+
If you want to mount the stripe engine somewhere else, you can do so by setting the `stripe.endpoint`
|
114
|
+
parameter. E.g.
|
115
|
+
|
116
|
+
config.stripe.endpoint = '/payment/stripe-integration'
|
117
|
+
|
118
|
+
Your new webook URL would then be `http://myproductionapp/payment/strip-integration/events`
|
119
|
+
|
120
|
+
### Responding to webhooks
|
121
|
+
|
122
|
+
Once you have your webhook URL configured you can respond to a stripe webhook *anywhere* in your
|
123
|
+
application just by including the Stripe::Callbacks module into your class and declaring a
|
124
|
+
callback with one of the callback methods. For example, to update a customer's payment status:
|
125
|
+
|
126
|
+
class User < ActiveRecord::Base
|
127
|
+
include Stripe::Callbacks
|
128
|
+
|
129
|
+
after_customer_updated! do |customer, event|
|
130
|
+
user = User.find_by_stripe_customer_id(customer.id)
|
131
|
+
if customer.delinquent
|
132
|
+
user.is_account_current = false
|
133
|
+
user.save!
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
or to send an email with one of your customer's monthly invoices
|
139
|
+
|
140
|
+
class InvoiceMailer < ActionMailer::Base
|
141
|
+
include Stripe::Callbacks
|
142
|
+
|
143
|
+
after_invoice_created! do |invoice, event|
|
144
|
+
user = User.find_by_stripe_customer(invoice.customer)
|
145
|
+
new_invoice(user, invoice).deliver
|
146
|
+
end
|
147
|
+
|
148
|
+
def new_invoice(user, invoice)
|
149
|
+
@user = user
|
150
|
+
@invoice = invoice
|
151
|
+
mail :to => user.email, :subject => '[Acme.com] Your new invoice'
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
The naming convention for the callback events is after__{callback_name}! where `callback_name`
|
156
|
+
is name of the stripe event with all `.` characters substituted with underscores. So, for
|
157
|
+
example, the stripe event `customer.discount.created` can be hooked by `after_customer_discount_created!`
|
158
|
+
and so on...
|
159
|
+
|
160
|
+
Each web hook is passed an instance of the stripe object to which the event corresponds
|
161
|
+
([`Stripe::Customer`][8], [`Stripe::Invoice`][9], [`Stripe::Charge`][10], etc...) as well as the [`Stripe::Event`][4] which contains metadata about the event being raised.
|
162
|
+
|
163
|
+
By default, the event is re-fetched securely from stripe.com to prevent damage to your system by
|
164
|
+
a malicious system spoofing real stripe events.
|
165
|
+
|
166
|
+
### Critical and non-critical hooks
|
167
|
+
|
168
|
+
So far, the examples have all used critical hooks, but in fact, each callback method comes in two flavors: "critical",
|
169
|
+
specified with a trailing `!` character, and "non-critical", which has no "bang" character at all. What
|
170
|
+
distinguishes one from the other is that _if an exception is raised in a critical callback, it will cause the entire webhook to fail_.
|
171
|
+
|
172
|
+
This will indicate to stripe.com that you did not receive the webhook at all, and that it should retry it again later until it
|
173
|
+
receives a successful response. On the other hand, there are some tasks that are more tangential to the payment work flow and aren't
|
174
|
+
such a big deal if they get dropped on the floor. For example, A non-critical hook can be used to do things like have a bot
|
175
|
+
notify your company's chatroom that something a credit card was successfully charged:
|
176
|
+
|
177
|
+
class AcmeBot
|
178
|
+
include Stripe::Callbacks
|
179
|
+
|
180
|
+
after_charge_succeeded do |charge|
|
181
|
+
announce "Attention all Dudes and Dudettes. Ya'll are so PAID!!!"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
Chances are that if you experience a momentary failure in connectivity to your chatroom, you don't want the whole payment notification to fail.
|
186
|
+
|
187
|
+
|
188
|
+
### Filtering Callbacks
|
189
|
+
|
190
|
+
Certain stripe events represent updates to existing data. You may want to only fire the event when certain attributes of that data
|
191
|
+
are updated. You can pass an `:only` option to your callback to filter to specify which attribute updates you're interested in. For
|
192
|
+
example, to warn users whenever their credit card has changed:
|
193
|
+
|
194
|
+
class StripeMailer
|
195
|
+
include Stripe::Callbacks
|
196
|
+
|
197
|
+
after_customer_updated! :only => :active_card do |customer, evt|
|
198
|
+
your_credit_card_on_file_was_updated_are_you_sure_this_was_you(customer).deliver
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
Filters can be specified as an array as well:
|
204
|
+
|
205
|
+
module Accounting
|
206
|
+
include Stripe::Callbacks
|
207
|
+
|
208
|
+
after_invoice_updated! :only => [:amount, :subtotal] do
|
209
|
+
# update our records
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
|
214
|
+
Alternatively, you can just pass a proc to filter the event manually. It will receive an instance of [`Stripe::Event`][4] as
|
215
|
+
its parameter:
|
216
|
+
|
217
|
+
module StagingOnly
|
218
|
+
include Stripe::Callbacks
|
219
|
+
|
220
|
+
after_charge_created! :only => proc {|charge, evt| unless evt.livemode} do |charge|
|
221
|
+
puts "FAKE DATA, PLEASE IGNORE!"
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
|
226
|
+
### Catchall Callback
|
227
|
+
|
228
|
+
The special 'stripe.event' callback will be invoked for every single event received from stripe.com. This can be useful for things
|
229
|
+
like logging and analytics:
|
230
|
+
|
231
|
+
class StripeFirehose
|
232
|
+
include Stripe::Callbacks
|
233
|
+
|
234
|
+
after_stripe_event do |target, event|
|
235
|
+
# do something useful
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
|
240
|
+
See the [complete listing of all stripe events][5], and the [webhook tutorial][6] for more great information on this subject.
|
241
|
+
|
242
|
+
## Thanks
|
243
|
+
|
244
|
+
<a href="http://thefrontside.net"></a>
|
245
|
+
|
246
|
+
`Stripe::Rails` was developed fondly by your friends at [The FrontSide][7]. They are available for your custom software development
|
247
|
+
needs, including integration with stripe.com.
|
87
248
|
|
88
249
|
[1]: https://stripe.com/docs/stripe.js
|
89
|
-
[2]: https://manage.stripe.com/#account/apikeys
|
250
|
+
[2]: https://manage.stripe.com/#account/apikeys
|
251
|
+
[3]: https://manage.stripe.com/#account/webhooks
|
252
|
+
[4]: https://stripe.com/docs/api?lang=ruby#events
|
253
|
+
[5]: https://stripe.com/docs/api?lang=ruby#event_types
|
254
|
+
[6]: https://stripe.com/docs/webhooks
|
255
|
+
[7]: http://thefrontside.net
|
256
|
+
[8]: https://stripe.com/docs/api?lang=ruby#customers
|
257
|
+
[9]: https://stripe.com/docs/api?lang=ruby#invoices
|
258
|
+
[10]: https://stripe.com/docs/api?lang=ruby#charges
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'stripe/event'
|
2
|
+
module Stripe
|
3
|
+
module EventDispatch
|
4
|
+
def dispatch_stripe_event(params)
|
5
|
+
retrieve_stripe_event(params) do |evt|
|
6
|
+
target = evt.data.object
|
7
|
+
::Stripe::Callbacks.run_callbacks(evt, target)
|
8
|
+
end
|
9
|
+
end
|
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)
|
15
|
+
else
|
16
|
+
yield Stripe::Event.retrieve(id)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<% if Rails.application.config.stripe.debug_js %>
|
2
|
+
<script type="text/javascript" src="https://js.stripe.com/v1/stripe-debug.js"></script>
|
3
|
+
<% else %>
|
4
|
+
<script type="text/javascript" src="https://js.stripe.com/v1/"></script>
|
5
|
+
<% end %>
|
6
|
+
<script type="text/javascript">
|
7
|
+
Stripe.setPublishableKey("<%= Rails.application.config.stripe.publishable_key or fail 'No stripe.com publishable key found. Please set config.stripe.publishable_key in config/application.rb to one of your publishable keys, which can be found here: https://manage.stripe.com/#account/apikeys' %>")
|
8
|
+
</script>
|
data/config/routes.rb
ADDED
data/lib/stripe-rails.rb
CHANGED
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'stripe/callbacks/builder'
|
2
|
+
|
3
|
+
module Stripe
|
4
|
+
module Callbacks
|
5
|
+
include Callbacks::Builder
|
6
|
+
|
7
|
+
callback 'account.updated'
|
8
|
+
callback 'account.application.deauthorized'
|
9
|
+
callback 'charge.succeeded'
|
10
|
+
callback 'charge.failed'
|
11
|
+
callback 'charge.refunded'
|
12
|
+
callback 'charge.dispute.created'
|
13
|
+
callback 'charge.dispute.updated'
|
14
|
+
callback 'charge.dispute.closed'
|
15
|
+
callback 'customer.created'
|
16
|
+
callback 'customer.updated'
|
17
|
+
callback 'customer.deleted'
|
18
|
+
callback 'customer.subscription.created'
|
19
|
+
callback 'customer.subscription.updated'
|
20
|
+
callback 'customer.subscription.deleted'
|
21
|
+
callback 'customer.subscription.trial_will_end'
|
22
|
+
callback 'customer.discount.created'
|
23
|
+
callback 'customer.discount.updated'
|
24
|
+
callback 'customer.discount.deleted'
|
25
|
+
callback 'invoice.created'
|
26
|
+
callback 'invoice.updated'
|
27
|
+
callback 'invoice.payment_succeeded'
|
28
|
+
callback 'invoice.payment_failed'
|
29
|
+
callback 'invoiceitem.created'
|
30
|
+
callback 'invoiceitem.updated'
|
31
|
+
callback 'invoiceitem.deleted'
|
32
|
+
callback 'plan.created'
|
33
|
+
callback 'plan.updated'
|
34
|
+
callback 'plan.deleted'
|
35
|
+
callback 'coupon.created'
|
36
|
+
callback 'coupon.updated'
|
37
|
+
callback 'coupon.deleted'
|
38
|
+
callback 'transfer.created'
|
39
|
+
callback 'transfer.updated'
|
40
|
+
callback 'transfer.failed'
|
41
|
+
callback 'ping'
|
42
|
+
callback 'stripe.event'
|
43
|
+
|
44
|
+
class << self
|
45
|
+
def run_callbacks(evt, target)
|
46
|
+
_run_callbacks evt.type, evt, target
|
47
|
+
_run_callbacks 'stripe.event', evt, target
|
48
|
+
end
|
49
|
+
|
50
|
+
def _run_callbacks(type, evt, target)
|
51
|
+
run_critical_callbacks type, evt, target
|
52
|
+
run_noncritical_callbacks type, evt, target
|
53
|
+
end
|
54
|
+
|
55
|
+
def run_critical_callbacks(type, evt, target)
|
56
|
+
::Stripe::Callbacks::critical_callbacks[type].each do |callback|
|
57
|
+
callback.call(target, evt)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def run_noncritical_callbacks(type, evt, target)
|
62
|
+
::Stripe::Callbacks::noncritical_callbacks[type].each do |callback|
|
63
|
+
begin
|
64
|
+
callback.call(target, evt)
|
65
|
+
rescue Exception => e
|
66
|
+
::Rails.logger.error e.message
|
67
|
+
::Rails.logger.error e.backtrace.join("\n")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Stripe
|
2
|
+
module Callbacks
|
3
|
+
module Builder
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
@critical_callbacks = Hash.new do |h, k|
|
9
|
+
h[k] = []
|
10
|
+
end
|
11
|
+
@noncritical_callbacks = Hash.new do |h, k|
|
12
|
+
h[k] = []
|
13
|
+
end
|
14
|
+
module ClassMethods
|
15
|
+
end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
attr_reader :critical_callbacks, :noncritical_callbacks
|
19
|
+
|
20
|
+
def clear_callbacks!
|
21
|
+
critical_callbacks.clear
|
22
|
+
noncritical_callbacks.clear
|
23
|
+
end
|
24
|
+
|
25
|
+
def callback(name)
|
26
|
+
method_name = "after_#{name.gsub('.', '_')}"
|
27
|
+
|
28
|
+
self::ClassMethods.send(:define_method, method_name) do |options = {}, &block|
|
29
|
+
::Stripe::Callbacks::noncritical_callbacks[name] << ::Stripe::Callbacks.callback_matcher(options, block)
|
30
|
+
end
|
31
|
+
self::ClassMethods.send(:define_method, "#{method_name}!") do |options = {}, &block|
|
32
|
+
::Stripe::Callbacks::critical_callbacks[name] << ::Stripe::Callbacks.callback_matcher(options, block)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def callback_matcher(options, block)
|
37
|
+
case only = options[:only]
|
38
|
+
when Proc, Method
|
39
|
+
proc do |target, evt|
|
40
|
+
block.call(target, evt) if only.call(target, evt)
|
41
|
+
end
|
42
|
+
when Array, Set
|
43
|
+
stringified_keys = only.map(&:to_s)
|
44
|
+
proc do |target, evt|
|
45
|
+
intersection = evt.data.previous_attributes.keys - stringified_keys
|
46
|
+
block.call(target, evt) if intersection != evt.data.previous_attributes.keys
|
47
|
+
end
|
48
|
+
when nil
|
49
|
+
block
|
50
|
+
else
|
51
|
+
callback_matcher options.merge(:only => [only]), block
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -1,16 +1,22 @@
|
|
1
1
|
require 'stripe'
|
2
2
|
|
3
|
-
module Stripe
|
4
|
-
class << self
|
5
|
-
attr_accessor :testing
|
6
|
-
end
|
7
|
-
|
3
|
+
module Stripe
|
8
4
|
class Engine < ::Rails::Engine
|
5
|
+
isolate_namespace Stripe
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_accessor :testing
|
9
|
+
end
|
9
10
|
|
10
|
-
config.stripe = Struct.new(:api_base, :api_key, :verify_ssl_certs, :publishable_key).new
|
11
|
+
config.stripe = Struct.new(:api_base, :api_key, :verify_ssl_certs, :publishable_key, :endpoint, :debug_js).new
|
11
12
|
|
12
|
-
initializer 'stripe.configure.
|
13
|
-
app.config.stripe
|
13
|
+
initializer 'stripe.configure.defaults', :before => 'stripe.configure' do |app|
|
14
|
+
stripe = app.config.stripe
|
15
|
+
stripe.api_key ||= ENV['STRIPE_API_KEY']
|
16
|
+
stripe.endpoint ||= '/stripe'
|
17
|
+
if stripe.debug_js.nil?
|
18
|
+
stripe.debug_js = ::Rails.env.development?
|
19
|
+
end
|
14
20
|
end
|
15
21
|
|
16
22
|
initializer 'stripe.configure' do |app|
|
@@ -26,13 +32,19 @@ environment file directly.
|
|
26
32
|
MSG
|
27
33
|
end
|
28
34
|
|
35
|
+
initializer 'stripe.javascript_helper' do
|
36
|
+
ActiveSupport.on_load :action_controller do
|
37
|
+
helper Stripe::JavascriptHelper
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
29
41
|
initializer 'stripe.plans' do |app|
|
30
42
|
path = app.root.join('config/stripe/plans.rb')
|
31
43
|
load path if path.exist?
|
32
44
|
end
|
33
45
|
|
34
46
|
rake_tasks do
|
35
|
-
load 'stripe
|
47
|
+
load 'stripe/rails/tasks.rake'
|
36
48
|
end
|
37
49
|
end
|
38
50
|
end
|
@@ -57,7 +57,7 @@ module Stripe
|
|
57
57
|
|
58
58
|
def put!
|
59
59
|
if exists?
|
60
|
-
puts "[EXISTS] - #{@id}" unless Stripe::
|
60
|
+
puts "[EXISTS] - #{@id}" unless Stripe::Engine.testing
|
61
61
|
else
|
62
62
|
plan = Stripe::Plan.create(
|
63
63
|
:id => @id,
|
@@ -68,7 +68,7 @@ module Stripe
|
|
68
68
|
:interval_count => @interval_count,
|
69
69
|
:trial_period_days => @trial_period_days
|
70
70
|
)
|
71
|
-
puts "[CREATE] - #{plan}" unless Stripe::
|
71
|
+
puts "[CREATE] - #{plan}" unless Stripe::Engine.testing
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
data/lib/stripe/rails.rb
ADDED
data/stripe-rails.gemspec
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
require File.expand_path('../lib/stripe
|
2
|
+
require File.expand_path('../lib/stripe/rails/version', __FILE__)
|
3
3
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
5
|
gem.authors = ["rubygeek"]
|
@@ -14,9 +14,10 @@ Gem::Specification.new do |gem|
|
|
14
14
|
gem.name = "stripe-rails"
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
gem.version = Stripe::Rails::VERSION
|
17
|
-
gem.add_dependency '
|
17
|
+
gem.add_dependency 'rails', '~> 3.0'
|
18
18
|
gem.add_dependency 'stripe'
|
19
19
|
|
20
20
|
gem.add_development_dependency 'tzinfo'
|
21
21
|
gem.add_development_dependency 'mocha'
|
22
|
+
gem.add_development_dependency 'rack-test'
|
22
23
|
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Stripe::Callbacks do
|
5
|
+
include Rack::Test::Methods
|
6
|
+
|
7
|
+
def app
|
8
|
+
Rails.application
|
9
|
+
end
|
10
|
+
|
11
|
+
before do
|
12
|
+
header 'Accept', 'application/json'
|
13
|
+
header 'Content-Type', 'application/json'
|
14
|
+
@observer = Class.new.tap do |cls|
|
15
|
+
cls.class_eval do
|
16
|
+
include Stripe::Callbacks
|
17
|
+
end
|
18
|
+
end
|
19
|
+
@content = JSON.parse(File.read File.expand_path('../invoice_payment_succeeded.json', __FILE__))
|
20
|
+
self.type = @content['type']
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
def type=(type)
|
25
|
+
@content['type'] = type
|
26
|
+
@stubbed_event = Stripe::Event.construct_from(@content)
|
27
|
+
Stripe::Event.stubs(:retrieve).returns(@stubbed_event)
|
28
|
+
end
|
29
|
+
|
30
|
+
after do
|
31
|
+
::Stripe::Callbacks.clear_callbacks!
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'has a ping interface just to make sure that everything is working just fine' do
|
35
|
+
get '/stripe/ping'
|
36
|
+
assert last_response.ok?
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'defined with a bang' do
|
40
|
+
code = nil
|
41
|
+
before do
|
42
|
+
code = proc {|target, e| @event = e; @target = target}
|
43
|
+
@observer.class_eval do
|
44
|
+
after_invoice_payment_succeeded! do |evt, target|
|
45
|
+
code.call(evt, target)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
it 'is invoked for the invoice.payment_succeeded event' do
|
50
|
+
post 'stripe/events', JSON.pretty_generate(@content)
|
51
|
+
@event.wont_be_nil
|
52
|
+
@event.type.must_equal 'invoice.payment_succeeded'
|
53
|
+
@target.total.must_equal 6999
|
54
|
+
end
|
55
|
+
it 'is not invoked for other types of events' do
|
56
|
+
self.type = 'invoked.payment_failed'
|
57
|
+
post 'stripe/events/', JSON.pretty_generate(@content)
|
58
|
+
end
|
59
|
+
describe 'if it raises an exception' do
|
60
|
+
before do
|
61
|
+
code = proc {fail 'boom!'}
|
62
|
+
end
|
63
|
+
it 'causes the whole webhook to fail' do
|
64
|
+
proc {post 'stripe/events', JSON.pretty_generate(@content)}.must_raise RuntimeError
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe 'defined without a bang and raising an exception' do
|
70
|
+
before do
|
71
|
+
@observer.class_eval do
|
72
|
+
after_invoice_payment_succeeded do |evt|
|
73
|
+
fail 'boom!'
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'does not cause the webhook to fail' do
|
79
|
+
post 'stripe/events', JSON.pretty_generate(@content)
|
80
|
+
last_response.status.must_be :>=, 200
|
81
|
+
last_response.status.must_be :<, 300
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe 'designed to catch any event' do
|
86
|
+
events = nil
|
87
|
+
before do
|
88
|
+
events = []
|
89
|
+
@observer.class_eval do
|
90
|
+
after_stripe_event do |target, evt|
|
91
|
+
events << evt
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
it 'gets invoked for any standard event' do
|
96
|
+
self.type = 'invoice.payment_failed'
|
97
|
+
post 'stripe/events/', JSON.pretty_generate(@content)
|
98
|
+
events.first.type.must_equal 'invoice.payment_failed'
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'gets invoked for any event whatsoever' do
|
102
|
+
self.type = 'foo.bar.baz'
|
103
|
+
post 'stripe/events/', JSON.pretty_generate(@content)
|
104
|
+
events.first.type.must_equal 'foo.bar.baz'
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe 'filtering on specific changed attributes' do
|
109
|
+
events = nil
|
110
|
+
before do
|
111
|
+
events = []
|
112
|
+
self.type = 'invoice.updated'
|
113
|
+
@stubbed_event.data.previous_attributes = {}
|
114
|
+
end
|
115
|
+
describe 'specified as an single symbol' do
|
116
|
+
before do
|
117
|
+
@observer.class_eval do
|
118
|
+
after_invoice_updated! :only => :closed do |invoice, evt|
|
119
|
+
events << evt
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
it 'does not fire events for with a prior attribute was specified' do
|
124
|
+
post 'stripe/events', JSON.pretty_generate(@content)
|
125
|
+
events.length.must_equal 0
|
126
|
+
end
|
127
|
+
it 'does fire events for which the prior attribute was specified' do
|
128
|
+
@stubbed_event.data.previous_attributes['closed'] = true
|
129
|
+
post 'stripe/events', JSON.pretty_generate(@content)
|
130
|
+
events.length.must_equal 1
|
131
|
+
end
|
132
|
+
end
|
133
|
+
describe 'specified as an array' do
|
134
|
+
before do
|
135
|
+
@observer.class_eval do
|
136
|
+
after_invoice_updated! :only => [:currency, :subtotal] do |invoice, evt|
|
137
|
+
events << evt
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
it 'does not fire events for which prior attributes were not specified' do
|
142
|
+
post 'stripe/events', JSON.pretty_generate(@content)
|
143
|
+
events.length.must_equal 0
|
144
|
+
end
|
145
|
+
it 'does fire events for which prior attributes were specified' do
|
146
|
+
@stubbed_event.data.previous_attributes['subtotal'] = 699
|
147
|
+
post 'stripe/events', JSON.pretty_generate(@content)
|
148
|
+
events.length.must_equal 1
|
149
|
+
end
|
150
|
+
end
|
151
|
+
describe 'specified as a lambda' do
|
152
|
+
before do
|
153
|
+
@observer.class_eval do
|
154
|
+
after_invoice_updated :only => proc {|target, evt| evt.data.previous_attributes.has_key? "closed"} do |i,e|
|
155
|
+
events << e
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
it 'does not fire events for which the lambda is not true' do
|
160
|
+
post 'stripe/events', JSON.pretty_generate(@content)
|
161
|
+
events.length.must_equal 0
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'does fire events for when the lambda is true' do
|
165
|
+
@stubbed_event.data.previous_attributes['closed'] = 'false'
|
166
|
+
post 'stripe/events', JSON.pretty_generate(@content)
|
167
|
+
events.length.must_equal 1
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
{
|
2
|
+
"type": "invoice.payment_succeeded",
|
3
|
+
"livemode": false,
|
4
|
+
"pending_webhooks": 0,
|
5
|
+
"created": 1352910508,
|
6
|
+
"object": "event",
|
7
|
+
"id": "evt_0jcGFxHNsrDTj4",
|
8
|
+
"data": {
|
9
|
+
"object": {
|
10
|
+
"starting_balance": 0,
|
11
|
+
"ending_balance": 0,
|
12
|
+
"paid": true,
|
13
|
+
"attempted": true,
|
14
|
+
"total": 6999,
|
15
|
+
"lines": {
|
16
|
+
"object": "list",
|
17
|
+
"count": 1,
|
18
|
+
"data": [
|
19
|
+
{
|
20
|
+
"type": "subscription",
|
21
|
+
"plan": {
|
22
|
+
"livemode": false,
|
23
|
+
"object": "plan",
|
24
|
+
"interval_count": 1,
|
25
|
+
"amount": 6999,
|
26
|
+
"name": "BandFrame PRO Annual",
|
27
|
+
"currency": "usd",
|
28
|
+
"id": "yearly",
|
29
|
+
"interval": "year",
|
30
|
+
"trial_period_days": null
|
31
|
+
},
|
32
|
+
"livemode": false,
|
33
|
+
"object": "line_item",
|
34
|
+
"description": null,
|
35
|
+
"amount": 6999,
|
36
|
+
"currency": "usd",
|
37
|
+
"period": {
|
38
|
+
"end": 1384446507,
|
39
|
+
"start": 1352910507
|
40
|
+
},
|
41
|
+
"id": "su_0jcGpDYAkeNj0a",
|
42
|
+
"proration": false,
|
43
|
+
"quantity": 1
|
44
|
+
}
|
45
|
+
],
|
46
|
+
"url": "/v1/invoices/in_0jcG7zz2RtscPB/lines"
|
47
|
+
},
|
48
|
+
"subtotal": 6999,
|
49
|
+
"livemode": false,
|
50
|
+
"period_start": 1352910503,
|
51
|
+
"charge": "ch_0jcGRmThyuRYyh",
|
52
|
+
"object": "invoice",
|
53
|
+
"customer": "cus_0jcFnB3UIIiicv",
|
54
|
+
"period_end": 1352910507,
|
55
|
+
"discount": null,
|
56
|
+
"next_payment_attempt": null,
|
57
|
+
"currency": "usd",
|
58
|
+
"date": 1352910507,
|
59
|
+
"closed": true,
|
60
|
+
"id": "in_0jcG7zz2RtscPB",
|
61
|
+
"attempt_count": 0,
|
62
|
+
"amount_due": 6999
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|
data/test/spec_helper.rb
CHANGED
data/test/stripe_rails_spec.rb
CHANGED
@@ -11,7 +11,7 @@ end
|
|
11
11
|
|
12
12
|
describe 'initializing plans' do
|
13
13
|
require 'rake'
|
14
|
-
load 'stripe
|
14
|
+
load 'stripe/rails/tasks.rake'
|
15
15
|
it 'creates any plans that do not exist on stripe.com' do
|
16
16
|
Stripe::Plans.expects(:put!)
|
17
17
|
Rake::Task['stripe:plans:prepare'].invoke
|
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: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,10 +9,10 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-12-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
15
|
+
name: rails
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
@@ -75,6 +75,22 @@ dependencies:
|
|
75
75
|
- - ! '>='
|
76
76
|
- !ruby/object:Gem::Version
|
77
77
|
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rack-test
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
78
94
|
description: A gem to integrate stripe into your rails app
|
79
95
|
email:
|
80
96
|
- nola@rubygeek.com
|
@@ -88,15 +104,27 @@ files:
|
|
88
104
|
- LICENSE
|
89
105
|
- README.md
|
90
106
|
- Rakefile
|
107
|
+
- app/controllers/stripe/application_controller.rb
|
108
|
+
- app/controllers/stripe/events_controller.rb
|
109
|
+
- app/controllers/stripe/pings_controller.rb
|
110
|
+
- app/helpers/stripe/javascript_helper.rb
|
111
|
+
- app/models/stripe/event_dispatch.rb
|
112
|
+
- app/models/stripe/ping.rb
|
113
|
+
- app/views/stripe/_js.html.erb
|
114
|
+
- config/routes.rb
|
91
115
|
- lib/generators/stripe/install_generator.rb
|
92
116
|
- lib/generators/templates/plans.rb
|
93
117
|
- lib/stripe-rails.rb
|
94
|
-
- lib/stripe
|
95
|
-
- lib/stripe
|
96
|
-
- lib/stripe
|
97
|
-
- lib/stripe
|
118
|
+
- lib/stripe/callbacks.rb
|
119
|
+
- lib/stripe/callbacks/builder.rb
|
120
|
+
- lib/stripe/engine.rb
|
121
|
+
- lib/stripe/plans.rb
|
122
|
+
- lib/stripe/rails.rb
|
123
|
+
- lib/stripe/rails/tasks.rake
|
124
|
+
- lib/stripe/rails/version.rb
|
98
125
|
- stripe-rails.gemspec
|
99
126
|
- test/.DS_Store
|
127
|
+
- test/callbacks_spec.rb
|
100
128
|
- test/dummy/README.rdoc
|
101
129
|
- test/dummy/Rakefile
|
102
130
|
- test/dummy/app/assets/javascripts/application.js
|
@@ -125,17 +153,15 @@ files:
|
|
125
153
|
- test/dummy/config/stripe/plans.rb
|
126
154
|
- test/dummy/lib/assets/.gitkeep
|
127
155
|
- test/dummy/log/.gitkeep
|
128
|
-
- test/dummy/log/test.log
|
129
156
|
- test/dummy/public/404.html
|
130
157
|
- test/dummy/public/422.html
|
131
158
|
- test/dummy/public/500.html
|
132
159
|
- test/dummy/public/favicon.ico
|
133
160
|
- test/dummy/script/rails
|
161
|
+
- test/invoice_payment_succeeded.json
|
134
162
|
- test/plan_builder_spec.rb
|
135
163
|
- test/spec_helper.rb
|
136
164
|
- test/stripe_rails_spec.rb
|
137
|
-
- vendor/assets/javascripts/stripe-debug.js
|
138
|
-
- vendor/assets/javascripts/stripe.js.erb
|
139
165
|
homepage: ''
|
140
166
|
licenses: []
|
141
167
|
post_install_message:
|
@@ -162,6 +188,7 @@ specification_version: 3
|
|
162
188
|
summary: A gem to integrate stripe into your rails app
|
163
189
|
test_files:
|
164
190
|
- test/.DS_Store
|
191
|
+
- test/callbacks_spec.rb
|
165
192
|
- test/dummy/README.rdoc
|
166
193
|
- test/dummy/Rakefile
|
167
194
|
- test/dummy/app/assets/javascripts/application.js
|
@@ -190,12 +217,12 @@ test_files:
|
|
190
217
|
- test/dummy/config/stripe/plans.rb
|
191
218
|
- test/dummy/lib/assets/.gitkeep
|
192
219
|
- test/dummy/log/.gitkeep
|
193
|
-
- test/dummy/log/test.log
|
194
220
|
- test/dummy/public/404.html
|
195
221
|
- test/dummy/public/422.html
|
196
222
|
- test/dummy/public/500.html
|
197
223
|
- test/dummy/public/favicon.ico
|
198
224
|
- test/dummy/script/rails
|
225
|
+
- test/invoice_payment_succeeded.json
|
199
226
|
- test/plan_builder_spec.rb
|
200
227
|
- test/spec_helper.rb
|
201
228
|
- test/stripe_rails_spec.rb
|
data/test/dummy/log/test.log
DELETED
File without changes
|
@@ -1,279 +0,0 @@
|
|
1
|
-
(function() {
|
2
|
-
var _this = this;
|
3
|
-
|
4
|
-
this.Stripe = (function() {
|
5
|
-
|
6
|
-
function Stripe() {}
|
7
|
-
|
8
|
-
Stripe.version = 2;
|
9
|
-
|
10
|
-
Stripe.endpoint = 'https://api.stripe.com/v1';
|
11
|
-
|
12
|
-
Stripe.validateCardNumber = function(num) {
|
13
|
-
num = (num + '').replace(/\s+|-/g, '');
|
14
|
-
return num.length >= 10 && num.length <= 16 && Stripe.luhnCheck(num);
|
15
|
-
};
|
16
|
-
|
17
|
-
Stripe.validateCVC = function(num) {
|
18
|
-
num = Stripe.trim(num);
|
19
|
-
return /^\d+$/.test(num) && num.length >= 3 && num.length <= 4;
|
20
|
-
};
|
21
|
-
|
22
|
-
Stripe.validateExpiry = function(month, year) {
|
23
|
-
var currentTime, expiry;
|
24
|
-
month = Stripe.trim(month);
|
25
|
-
year = Stripe.trim(year);
|
26
|
-
if (!/^\d+$/.test(month)) {
|
27
|
-
return false;
|
28
|
-
}
|
29
|
-
if (!/^\d+$/.test(year)) {
|
30
|
-
return false;
|
31
|
-
}
|
32
|
-
if (!(parseInt(month, 10) <= 12)) {
|
33
|
-
return false;
|
34
|
-
}
|
35
|
-
expiry = new Date(year, month);
|
36
|
-
currentTime = new Date;
|
37
|
-
expiry.setMonth(expiry.getMonth() - 1);
|
38
|
-
expiry.setMonth(expiry.getMonth() + 1, 1);
|
39
|
-
return expiry > currentTime;
|
40
|
-
};
|
41
|
-
|
42
|
-
Stripe.cardType = function(num) {
|
43
|
-
return Stripe.cardTypes[num.slice(0, 2)] || 'Unknown';
|
44
|
-
};
|
45
|
-
|
46
|
-
Stripe.setPublishableKey = function(key) {
|
47
|
-
Stripe.key = key;
|
48
|
-
};
|
49
|
-
|
50
|
-
Stripe.createToken = function(card, params, callback) {
|
51
|
-
var amount, key, value;
|
52
|
-
if (params == null) {
|
53
|
-
params = {};
|
54
|
-
}
|
55
|
-
if (!card) {
|
56
|
-
throw 'card required';
|
57
|
-
}
|
58
|
-
if (typeof card !== 'object') {
|
59
|
-
throw 'card invalid';
|
60
|
-
}
|
61
|
-
if (typeof params === 'function') {
|
62
|
-
callback = params;
|
63
|
-
params = {};
|
64
|
-
} else if (typeof params !== 'object') {
|
65
|
-
amount = parseInt(params, 10);
|
66
|
-
params = {};
|
67
|
-
if (amount > 0) {
|
68
|
-
params.amount = amount;
|
69
|
-
}
|
70
|
-
}
|
71
|
-
for (key in card) {
|
72
|
-
value = card[key];
|
73
|
-
delete card[key];
|
74
|
-
card[Stripe.underscore(key)] = value;
|
75
|
-
}
|
76
|
-
params.card = card;
|
77
|
-
params.key || (params.key = Stripe.key || Stripe.publishableKey);
|
78
|
-
Stripe.validateKey(params.key);
|
79
|
-
return Stripe.ajaxJSONP({
|
80
|
-
url: "" + Stripe.endpoint + "/tokens",
|
81
|
-
data: params,
|
82
|
-
method: 'POST',
|
83
|
-
success: function(body, status) {
|
84
|
-
return typeof callback === "function" ? callback(status, body) : void 0;
|
85
|
-
},
|
86
|
-
complete: Stripe.complete(callback),
|
87
|
-
timeout: 40000
|
88
|
-
});
|
89
|
-
};
|
90
|
-
|
91
|
-
Stripe.getToken = function(token, callback) {
|
92
|
-
if (!token) {
|
93
|
-
throw 'token required';
|
94
|
-
}
|
95
|
-
Stripe.validateKey(Stripe.key);
|
96
|
-
return Stripe.ajaxJSONP({
|
97
|
-
url: "" + Stripe.endpoint + "/tokens/" + token,
|
98
|
-
data: {
|
99
|
-
key: Stripe.key
|
100
|
-
},
|
101
|
-
success: function(body, status) {
|
102
|
-
return typeof callback === "function" ? callback(status, body) : void 0;
|
103
|
-
},
|
104
|
-
complete: Stripe.complete(callback),
|
105
|
-
timeout: 40000
|
106
|
-
});
|
107
|
-
};
|
108
|
-
|
109
|
-
Stripe.complete = function(callback) {
|
110
|
-
return function(type, xhr, options) {
|
111
|
-
if (type !== 'success') {
|
112
|
-
return typeof callback === "function" ? callback(500, {
|
113
|
-
error: {
|
114
|
-
code: type,
|
115
|
-
type: type,
|
116
|
-
message: 'An unexpected error has occured.\nWe have been notified of the problem.'
|
117
|
-
}
|
118
|
-
}) : void 0;
|
119
|
-
}
|
120
|
-
};
|
121
|
-
};
|
122
|
-
|
123
|
-
Stripe.validateKey = function(key) {
|
124
|
-
if (!key || typeof key !== 'string') {
|
125
|
-
throw new Error('You did not set a valid publishable key.\nCall Stripe.setPublishableKey() with your publishable key.\nFor more info, see https://stripe.com/docs/stripe.js');
|
126
|
-
}
|
127
|
-
if (/^sk_/.test(key)) {
|
128
|
-
throw new Error('You are using a secret key with Stripe.js, instead of the publishable one.\nFor more info, see https://stripe.com/docs/stripe.js');
|
129
|
-
}
|
130
|
-
};
|
131
|
-
|
132
|
-
return Stripe;
|
133
|
-
|
134
|
-
}).call(this);
|
135
|
-
|
136
|
-
if (typeof module !== "undefined" && module !== null) {
|
137
|
-
module.exports = this.Stripe;
|
138
|
-
}
|
139
|
-
|
140
|
-
if (typeof define === "function") {
|
141
|
-
define('stripe', [], function() {
|
142
|
-
return _this.Stripe;
|
143
|
-
});
|
144
|
-
}
|
145
|
-
|
146
|
-
}).call(this);
|
147
|
-
(function() {
|
148
|
-
var e, requestID, serialize,
|
149
|
-
__slice = [].slice;
|
150
|
-
|
151
|
-
e = encodeURIComponent;
|
152
|
-
|
153
|
-
requestID = new Date().getTime();
|
154
|
-
|
155
|
-
serialize = function(object, result, scope) {
|
156
|
-
var key, value;
|
157
|
-
if (result == null) {
|
158
|
-
result = [];
|
159
|
-
}
|
160
|
-
for (key in object) {
|
161
|
-
value = object[key];
|
162
|
-
if (scope) {
|
163
|
-
key = "" + scope + "[" + key + "]";
|
164
|
-
}
|
165
|
-
if (typeof value === 'object') {
|
166
|
-
serialize(value, result, key);
|
167
|
-
} else {
|
168
|
-
result.push("" + key + "=" + (e(value)));
|
169
|
-
}
|
170
|
-
}
|
171
|
-
return result.join('&').replace(/%20/g, '+');
|
172
|
-
};
|
173
|
-
|
174
|
-
this.Stripe.ajaxJSONP = function(options) {
|
175
|
-
var abort, abortTimeout, callbackName, head, script, xhr;
|
176
|
-
if (options == null) {
|
177
|
-
options = {};
|
178
|
-
}
|
179
|
-
callbackName = 'sjsonp' + (++requestID);
|
180
|
-
script = document.createElement('script');
|
181
|
-
abortTimeout = null;
|
182
|
-
abort = function() {
|
183
|
-
var _ref;
|
184
|
-
if ((_ref = script.parentNode) != null) {
|
185
|
-
_ref.removeChild(script);
|
186
|
-
}
|
187
|
-
if (callbackName in window) {
|
188
|
-
window[callbackName] = (function() {});
|
189
|
-
}
|
190
|
-
return typeof options.complete === "function" ? options.complete('abort', xhr, options) : void 0;
|
191
|
-
};
|
192
|
-
xhr = {
|
193
|
-
abort: abort
|
194
|
-
};
|
195
|
-
script.onerror = function() {
|
196
|
-
xhr.abort();
|
197
|
-
return typeof options.error === "function" ? options.error(xhr, options) : void 0;
|
198
|
-
};
|
199
|
-
window[callbackName] = function() {
|
200
|
-
var args;
|
201
|
-
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
202
|
-
clearTimeout(abortTimeout);
|
203
|
-
script.parentNode.removeChild(script);
|
204
|
-
try {
|
205
|
-
delete window[callbackName];
|
206
|
-
} catch (e) {
|
207
|
-
window[callbackName] = void 0;
|
208
|
-
}
|
209
|
-
if (typeof options.success === "function") {
|
210
|
-
options.success.apply(options, args);
|
211
|
-
}
|
212
|
-
return typeof options.complete === "function" ? options.complete('success', xhr, options) : void 0;
|
213
|
-
};
|
214
|
-
options.data || (options.data = {});
|
215
|
-
options.data.callback = callbackName;
|
216
|
-
if (options.method) {
|
217
|
-
options.data._method = options.method;
|
218
|
-
}
|
219
|
-
script.src = options.url + '?' + serialize(options.data);
|
220
|
-
head = document.getElementsByTagName('head')[0];
|
221
|
-
head.appendChild(script);
|
222
|
-
if (options.timeout > 0) {
|
223
|
-
abortTimeout = setTimeout(function() {
|
224
|
-
xhr.abort();
|
225
|
-
return typeof options.complete === "function" ? options.complete('timeout', xhr, options) : void 0;
|
226
|
-
}, options.timeout);
|
227
|
-
}
|
228
|
-
return xhr;
|
229
|
-
};
|
230
|
-
|
231
|
-
}).call(this);
|
232
|
-
(function() {
|
233
|
-
|
234
|
-
this.Stripe.trim = function(str) {
|
235
|
-
return (str + '').replace(/^\s+|\s+$/g, '');
|
236
|
-
};
|
237
|
-
|
238
|
-
this.Stripe.underscore = function(str) {
|
239
|
-
return (str + '').replace(/([A-Z])/g, function($1) {
|
240
|
-
return "_" + ($1.toLowerCase());
|
241
|
-
});
|
242
|
-
};
|
243
|
-
|
244
|
-
this.Stripe.luhnCheck = function(num) {
|
245
|
-
var digit, digits, odd, sum, _i, _len;
|
246
|
-
odd = true;
|
247
|
-
sum = 0;
|
248
|
-
digits = (num + '').split('').reverse();
|
249
|
-
for (_i = 0, _len = digits.length; _i < _len; _i++) {
|
250
|
-
digit = digits[_i];
|
251
|
-
digit = parseInt(digit, 10);
|
252
|
-
if ((odd = !odd)) {
|
253
|
-
digit *= 2;
|
254
|
-
}
|
255
|
-
if (digit > 9) {
|
256
|
-
digit -= 9;
|
257
|
-
}
|
258
|
-
sum += digit;
|
259
|
-
}
|
260
|
-
return sum % 10 === 0;
|
261
|
-
};
|
262
|
-
|
263
|
-
this.Stripe.cardTypes = (function() {
|
264
|
-
var num, types, _i, _j;
|
265
|
-
types = {};
|
266
|
-
for (num = _i = 40; _i <= 49; num = ++_i) {
|
267
|
-
types[num] = 'Visa';
|
268
|
-
}
|
269
|
-
for (num = _j = 50; _j <= 59; num = ++_j) {
|
270
|
-
types[num] = 'MasterCard';
|
271
|
-
}
|
272
|
-
types[34] = types[37] = 'American Express';
|
273
|
-
types[60] = types[62] = types[64] = types[65] = 'Discover';
|
274
|
-
types[35] = 'JCB';
|
275
|
-
types[30] = types[36] = types[38] = types[39] = 'Diners Club';
|
276
|
-
return types;
|
277
|
-
})();
|
278
|
-
|
279
|
-
}).call(this);
|
@@ -1,3 +0,0 @@
|
|
1
|
-
//= require stripe-debug
|
2
|
-
|
3
|
-
Stripe.publishableKey = "<%= Rails.application.config.stripe.publishable_key or fail 'No stripe.com publishable key found. Please set config.stripe.publishable_key in config/application.rb to one of your publishable keys, which can be found here: https://manage.stripe.com/#account/apikeys' %>"
|