fake_stripe 0.0.8

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.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING.md +33 -0
  3. data/LICENSE +19 -0
  4. data/README.md +66 -0
  5. data/Rakefile +8 -0
  6. data/lib/fake_stripe.rb +49 -0
  7. data/lib/fake_stripe/assets/v1.js +38 -0
  8. data/lib/fake_stripe/assets/v2-mock.js +18 -0
  9. data/lib/fake_stripe/assets/v2.js +757 -0
  10. data/lib/fake_stripe/configuration.rb +15 -0
  11. data/lib/fake_stripe/fixtures/cancel_subscription.json +18 -0
  12. data/lib/fake_stripe/fixtures/cancel_transfer.json +161 -0
  13. data/lib/fake_stripe/fixtures/capture_charge.json +51 -0
  14. data/lib/fake_stripe/fixtures/close_dispute.json +13 -0
  15. data/lib/fake_stripe/fixtures/create_card.json +21 -0
  16. data/lib/fake_stripe/fixtures/create_charge.json +45 -0
  17. data/lib/fake_stripe/fixtures/create_coupon.json +17 -0
  18. data/lib/fake_stripe/fixtures/create_customer.json +51 -0
  19. data/lib/fake_stripe/fixtures/create_invoice.json +52 -0
  20. data/lib/fake_stripe/fixtures/create_invoiceitem.json +15 -0
  21. data/lib/fake_stripe/fixtures/create_plan.json +14 -0
  22. data/lib/fake_stripe/fixtures/create_recipient.json +25 -0
  23. data/lib/fake_stripe/fixtures/create_subscription.json +18 -0
  24. data/lib/fake_stripe/fixtures/create_token.json +26 -0
  25. data/lib/fake_stripe/fixtures/create_transfer.json +161 -0
  26. data/lib/fake_stripe/fixtures/delete_card.json +4 -0
  27. data/lib/fake_stripe/fixtures/delete_coupon.json +4 -0
  28. data/lib/fake_stripe/fixtures/delete_customer.json +4 -0
  29. data/lib/fake_stripe/fixtures/delete_customer_discount.json +4 -0
  30. data/lib/fake_stripe/fixtures/delete_invoiceitem.json +4 -0
  31. data/lib/fake_stripe/fixtures/delete_plan.json +4 -0
  32. data/lib/fake_stripe/fixtures/delete_recipient.json +4 -0
  33. data/lib/fake_stripe/fixtures/delete_subscription_discount.json +4 -0
  34. data/lib/fake_stripe/fixtures/list_application_fees.json +55 -0
  35. data/lib/fake_stripe/fixtures/list_balance_history.json +76 -0
  36. data/lib/fake_stripe/fixtures/list_cards.json +70 -0
  37. data/lib/fake_stripe/fixtures/list_charges.json +146 -0
  38. data/lib/fake_stripe/fixtures/list_coupons.json +58 -0
  39. data/lib/fake_stripe/fixtures/list_customers.json +157 -0
  40. data/lib/fake_stripe/fixtures/list_events.json +172 -0
  41. data/lib/fake_stripe/fixtures/list_invoiceitems.json +52 -0
  42. data/lib/fake_stripe/fixtures/list_invoices.json +199 -0
  43. data/lib/fake_stripe/fixtures/list_plans.json +49 -0
  44. data/lib/fake_stripe/fixtures/list_recipients.json +82 -0
  45. data/lib/fake_stripe/fixtures/list_subscriptions.json +100 -0
  46. data/lib/fake_stripe/fixtures/list_transfers.json +488 -0
  47. data/lib/fake_stripe/fixtures/pay_invoice.json +65 -0
  48. data/lib/fake_stripe/fixtures/refund_application_fee.json +17 -0
  49. data/lib/fake_stripe/fixtures/refund_charge.json +51 -0
  50. data/lib/fake_stripe/fixtures/retrieve_account.json +19 -0
  51. data/lib/fake_stripe/fixtures/retrieve_application_fee.json +17 -0
  52. data/lib/fake_stripe/fixtures/retrieve_balance.json +16 -0
  53. data/lib/fake_stripe/fixtures/retrieve_balance_transaction.json +23 -0
  54. data/lib/fake_stripe/fixtures/retrieve_card.json +21 -0
  55. data/lib/fake_stripe/fixtures/retrieve_charge.json +45 -0
  56. data/lib/fake_stripe/fixtures/retrieve_coupon.json +17 -0
  57. data/lib/fake_stripe/fixtures/retrieve_customer.json +51 -0
  58. data/lib/fake_stripe/fixtures/retrieve_event.json +56 -0
  59. data/lib/fake_stripe/fixtures/retrieve_invoice.json +64 -0
  60. data/lib/fake_stripe/fixtures/retrieve_invoice_line_items.json +61 -0
  61. data/lib/fake_stripe/fixtures/retrieve_invoiceitem.json +15 -0
  62. data/lib/fake_stripe/fixtures/retrieve_plan.json +14 -0
  63. data/lib/fake_stripe/fixtures/retrieve_recipient.json +25 -0
  64. data/lib/fake_stripe/fixtures/retrieve_subscription.json +31 -0
  65. data/lib/fake_stripe/fixtures/retrieve_token.json +26 -0
  66. data/lib/fake_stripe/fixtures/retrieve_transfer.json +161 -0
  67. data/lib/fake_stripe/fixtures/retrieve_upcoming_invoice.json +63 -0
  68. data/lib/fake_stripe/fixtures/update_card.json +21 -0
  69. data/lib/fake_stripe/fixtures/update_charge.json +45 -0
  70. data/lib/fake_stripe/fixtures/update_customer.json +51 -0
  71. data/lib/fake_stripe/fixtures/update_dispute.json +13 -0
  72. data/lib/fake_stripe/fixtures/update_invoice.json +64 -0
  73. data/lib/fake_stripe/fixtures/update_invoiceitem.json +15 -0
  74. data/lib/fake_stripe/fixtures/update_plan.json +14 -0
  75. data/lib/fake_stripe/fixtures/update_recipient.json +25 -0
  76. data/lib/fake_stripe/fixtures/update_subscription.json +18 -0
  77. data/lib/fake_stripe/fixtures/update_transfer.json +161 -0
  78. data/lib/fake_stripe/initializers/webmock.rb +3 -0
  79. data/lib/fake_stripe/stub_app.rb +307 -0
  80. data/lib/fake_stripe/stub_stripe_js.rb +29 -0
  81. data/lib/fake_stripe/version.rb +3 -0
  82. metadata +211 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c7b987c214a1ff6edd50c5b79ba6adb060ccfb3c
4
+ data.tar.gz: 1f279550aa2d4c885c6487528645d396579229db
5
+ SHA512:
6
+ metadata.gz: 3f3653167729c7bc6ef172843c53f9ce0567b3bc71e13503e6632f650b97e8fd1d95c17154ea1e88b3ee81dc15a3e4a69c0cab8912948f0f18f4fee6ad53e78d
7
+ data.tar.gz: d0ab0d0b5dbbf08bdc8e50309e7144991c4e6faf716cc583d44698dec2fbddd5ef9c8e491de60c9d541c56c168b4a8e97606d7adb03fd50cfb215ab499dad1cb
@@ -0,0 +1,33 @@
1
+ ## Steps
2
+
3
+ 1. Fork the repo.
4
+
5
+ 2. Run the tests. We only take pull requests with passing tests, and it's great
6
+ to know that you have a clean slate: `bundle && rake`
7
+
8
+ 3. Create your feature branch (`git checkout -b my-new-feature`).
9
+
10
+ 4. Add a test for your change. Only refactoring and documentation changes
11
+ require no new tests. If you are adding functionality or fixing a bug, we need
12
+ a test!
13
+
14
+ 5. Make the test pass.
15
+
16
+ 6. Commit your changes (`git commit -am 'Add some feature'`).
17
+
18
+ 7. Push to the branch (`git push origin my-new-feature`).
19
+
20
+ 8. Create new Pull Request
21
+
22
+ At this point you're waiting on us. We like to at least comment on, if not
23
+ accept, pull requests within three business days (and, typically, one business
24
+ day). We may suggest some changes or improvements or alternatives.
25
+
26
+ ## Syntax
27
+
28
+ * Two spaces, no tabs.
29
+ * No trailing whitespace. Blank lines should not have any space.
30
+ * Prefer &&/|| over and/or.
31
+ * MyClass.my_method(my_arg) not my_method( my_arg ) or my_method my_arg.
32
+ * a = b and not a=b.
33
+ * Follow the conventions you see used in the source already.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2013 thoughtbot, inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,66 @@
1
+ # fake\_stripe, a Stripe fake
2
+
3
+ This library is a way to test [Stripe](http://www.stripe.com/) code without hitting Stripe's
4
+ servers. It uses
5
+ [Capybara::Server](https://github.com/jnicklas/capybara/blob/master/lib/capybara/server.rb)
6
+ and [Webmock](https://github.com/bblimke/webmock) to intercept all of the calls from Stripe's
7
+ Ruby library and returns JSON that the Stripe library can parse.
8
+
9
+ ## Installation
10
+
11
+ ### Gemfile
12
+
13
+ Add the `fake_stripe` Gem to the `:test` group in your Gemfile:
14
+
15
+ ```ruby
16
+ # Gemfile
17
+ group :test do
18
+ gem 'fake_stripe'
19
+ end
20
+ ```
21
+
22
+ Remember to run `bundle install`.
23
+
24
+ ### Stripe settings
25
+
26
+ Set the `STRIPE_JS_HOST` constant in an initializer:
27
+
28
+ ```ruby
29
+ # config/initilialzers/stripe.rb
30
+ Stripe.api_key = ENV['STRIPE_API_KEY']
31
+
32
+ unless defined? STRIPE_JS_HOST
33
+ STRIPE_JS_HOST = 'https://js.stripe.com'
34
+ end
35
+ ```
36
+
37
+ Include the Stripe JavaScript in your application template:
38
+
39
+ ```rhtml
40
+ # app/views/layouts/application.html.erb
41
+ <%= javascript_include_tag "#{STRIPE_JS_HOST}/v1/" %>
42
+ ```
43
+
44
+ When the test suite runs `fake_stripe` will override the address for
45
+ `STRIPE_JS_HOST` and serve up a local version of [Stripe.js](https://stripe.com/docs/stripe.js).
46
+
47
+ ### In Tests
48
+
49
+ Require the library in your spec support:
50
+
51
+ ```ruby
52
+ # spec/support/fake_stripe.rb
53
+ require 'fake_stripe'
54
+
55
+ RSpec.configure do |config|
56
+ config.before(:each) do
57
+ FakeStripe.stub_stripe
58
+ end
59
+ end
60
+ ```
61
+
62
+ ## Contributing
63
+
64
+ Please see [CONTRIBUTING.md][1] for more details.
65
+
66
+ [1]: https://github.com/thoughtbot/fake_stripe/blob/master/CONTRIBUTING.md
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+
8
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,49 @@
1
+ require 'fake_stripe/configuration'
2
+ require 'fake_stripe/initializers/webmock'
3
+ require 'fake_stripe/stub_app'
4
+ require 'fake_stripe/stub_stripe_js'
5
+
6
+ module FakeStripe
7
+ extend Configuration
8
+
9
+ VALID_CARD_NUMBER = '4242424242424242'
10
+
11
+ def self.charge_count
12
+ @@charge_count
13
+ end
14
+
15
+ def self.charge_count=(charge_count)
16
+ @@charge_count = charge_count
17
+ end
18
+
19
+ def self.refund_count
20
+ @@refund_count
21
+ end
22
+
23
+ def self.refund_count=(refund_count)
24
+ @@refund_count = refund_count
25
+ end
26
+
27
+ def self.customer_count
28
+ @@customer_count
29
+ end
30
+
31
+ def self.customer_count=(customer_count)
32
+ @@customer_count = customer_count
33
+ end
34
+
35
+ def self.reset
36
+ @@charge_count = 0
37
+ @@customer_count = 0
38
+ @@refund_count = 0
39
+ end
40
+
41
+ def self.stub_stripe
42
+ Stripe.api_key = 'FAKE_STRIPE_API_KEY'
43
+ FakeStripe.reset
44
+ stub_request(:any, /api.stripe.com/).to_rack(FakeStripe::StubApp)
45
+ end
46
+ end
47
+
48
+ server = FakeStripe::StubStripeJS.boot
49
+ STRIPE_JS_HOST = "http://#{server.host}:#{server.port}"
@@ -0,0 +1,38 @@
1
+ window.Stripe = {
2
+ createToken: function(creditCard, handler) {
3
+ function isValidCardNumber(number) {
4
+ var validCardNumbers = ['4242424242424242', '4242 4242 4242 4242'];
5
+ return $.inArray(number, validCardNumbers) >= 0;
6
+ }
7
+
8
+ if (isValidCardNumber(creditCard.number)) {
9
+ handler(200, {
10
+ id: 123,
11
+ card: {
12
+ last4: 4242,
13
+ brand: 'Visa',
14
+ exp_month: '12',
15
+ exp_year: '2018'
16
+ }
17
+ });
18
+ } else {
19
+ handler(402, {
20
+ error: {
21
+ code: 'incorrect_number',
22
+ message: 'Your card number is incorrect',
23
+ param: 'number',
24
+ type: 'card_error'
25
+ }
26
+ });
27
+ }
28
+ },
29
+ cardType: function(value) { return 'Visa' },
30
+ setPublishableKey: function() {},
31
+ validateCardNumber: function(value) { return true; },
32
+ validateCVC: function(value) { return true; },
33
+ validateExpiry: function(value) { return true; }
34
+ };
35
+
36
+ window.Stripe.card = {
37
+ createToken: window.Stripe.createToken
38
+ };
@@ -0,0 +1,18 @@
1
+ (function() {
2
+ this.Stripe.ajaxJSONP = function(options) {
3
+ if (options.url.indexOf("tokens") > -1) {
4
+ return options.success({
5
+ id: 123,
6
+ card: {
7
+ last4: 4242,
8
+ brand: 'Visa',
9
+ exp_month: '12',
10
+ exp_year: '2018'
11
+ }
12
+ }, 200);
13
+ } else {
14
+ throw(options.url + " has not been mocked by FakeStripe. " +
15
+ "Please submit a pull request to implement this action.");
16
+ }
17
+ };
18
+ }).call(this);
@@ -0,0 +1,757 @@
1
+ (function() {
2
+ var Stripe, exports, key, _i, _len,
3
+ __hasProp = {}.hasOwnProperty,
4
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
5
+ _this = this;
6
+
7
+ this.Stripe = (function() {
8
+
9
+ function Stripe() {}
10
+
11
+ Stripe.version = 2;
12
+
13
+ Stripe.endpoint = 'https://api.stripe.com/v1';
14
+
15
+ Stripe.setPublishableKey = function(key) {
16
+ Stripe.key = key;
17
+ };
18
+
19
+ Stripe.complete = function(callback, errorMessage) {
20
+ return function(type, xhr, options) {
21
+ var timestamp;
22
+ if (type !== 'success') {
23
+ timestamp = Math.round(new Date().getTime() / 1000);
24
+ (new Image).src = "https://q.stripe.com?event=stripejs-error&type=" + type + "&key=" + Stripe.key + "&timestamp=" + timestamp;
25
+ return typeof callback === "function" ? callback(500, {
26
+ error: {
27
+ code: type,
28
+ type: type,
29
+ message: errorMessage
30
+ }
31
+ }) : void 0;
32
+ }
33
+ };
34
+ };
35
+
36
+ return Stripe;
37
+
38
+ }).call(this);
39
+
40
+ Stripe = this.Stripe;
41
+
42
+ this.Stripe.token = (function() {
43
+
44
+ function token() {}
45
+
46
+ token.validate = function(data, name) {
47
+ if (!data) {
48
+ throw name + ' required';
49
+ }
50
+ if (typeof data !== 'object') {
51
+ throw name + ' invalid';
52
+ }
53
+ };
54
+
55
+ token.formatData = function(data, attrs) {
56
+ if (Stripe.utils.isElement(data)) {
57
+ data = Stripe.utils.paramsFromForm(data, attrs);
58
+ }
59
+ Stripe.utils.underscoreKeys(data);
60
+ return data;
61
+ };
62
+
63
+ token.create = function(params, callback) {
64
+ params.key || (params.key = Stripe.key || Stripe.publishableKey);
65
+ Stripe.utils.validateKey(params.key);
66
+ return Stripe.ajaxJSONP({
67
+ url: "" + Stripe.endpoint + "/tokens",
68
+ data: params,
69
+ method: 'POST',
70
+ success: function(body, status) {
71
+ return typeof callback === "function" ? callback(status, body) : void 0;
72
+ },
73
+ complete: Stripe.complete(callback, "A network error has occurred, and you have not been charged. Please try again."),
74
+ timeout: 40000
75
+ });
76
+ };
77
+
78
+ token.get = function(token, callback) {
79
+ if (!token) {
80
+ throw 'token required';
81
+ }
82
+ Stripe.utils.validateKey(Stripe.key);
83
+ return Stripe.ajaxJSONP({
84
+ url: "" + Stripe.endpoint + "/tokens/" + token,
85
+ data: {
86
+ key: Stripe.key
87
+ },
88
+ success: function(body, status) {
89
+ return typeof callback === "function" ? callback(status, body) : void 0;
90
+ },
91
+ complete: Stripe.complete(callback, "A network error has occurred loading data from Stripe. Please try again."),
92
+ timeout: 40000
93
+ });
94
+ };
95
+
96
+ return token;
97
+
98
+ }).call(this);
99
+
100
+ this.Stripe.card = (function(_super) {
101
+
102
+ __extends(card, _super);
103
+
104
+ function card() {
105
+ return card.__super__.constructor.apply(this, arguments);
106
+ }
107
+
108
+ card.tokenName = 'card';
109
+
110
+ card.whitelistedAttrs = ['number', 'cvc', 'exp_month', 'exp_year', 'name', 'address_line1', 'address_line2', 'address_city', 'address_state', 'address_zip', 'address_country'];
111
+
112
+ card.createToken = function(data, params, callback) {
113
+ var amount;
114
+ if (params == null) {
115
+ params = {};
116
+ }
117
+ Stripe.token.validate(data, 'card');
118
+ if (typeof params === 'function') {
119
+ callback = params;
120
+ params = {};
121
+ } else if (typeof params !== 'object') {
122
+ amount = parseInt(params, 10);
123
+ params = {};
124
+ if (amount > 0) {
125
+ params.amount = amount;
126
+ }
127
+ }
128
+ params[card.tokenName] = Stripe.token.formatData(data, card.whitelistedAttrs);
129
+ return Stripe.token.create(params, callback);
130
+ };
131
+
132
+ card.getToken = function(token, callback) {
133
+ return Stripe.token.get(token, callback);
134
+ };
135
+
136
+ card.validateCardNumber = function(num) {
137
+ num = (num + '').replace(/\s+|-/g, '');
138
+ return num.length >= 10 && num.length <= 16 && card.luhnCheck(num);
139
+ };
140
+
141
+ card.validateCVC = function(num) {
142
+ num = Stripe.utils.trim(num);
143
+ return /^\d+$/.test(num) && num.length >= 3 && num.length <= 4;
144
+ };
145
+
146
+ card.validateExpiry = function(month, year) {
147
+ var currentTime, expiry;
148
+ month = Stripe.utils.trim(month);
149
+ year = Stripe.utils.trim(year);
150
+ if (!/^\d+$/.test(month)) {
151
+ return false;
152
+ }
153
+ if (!/^\d+$/.test(year)) {
154
+ return false;
155
+ }
156
+ if (!(parseInt(month, 10) <= 12)) {
157
+ return false;
158
+ }
159
+ expiry = new Date(year, month);
160
+ currentTime = new Date;
161
+ expiry.setMonth(expiry.getMonth() - 1);
162
+ expiry.setMonth(expiry.getMonth() + 1, 1);
163
+ return expiry > currentTime;
164
+ };
165
+
166
+ card.luhnCheck = function(num) {
167
+ var digit, digits, odd, sum, _i, _len;
168
+ odd = true;
169
+ sum = 0;
170
+ digits = (num + '').split('').reverse();
171
+ for (_i = 0, _len = digits.length; _i < _len; _i++) {
172
+ digit = digits[_i];
173
+ digit = parseInt(digit, 10);
174
+ if ((odd = !odd)) {
175
+ digit *= 2;
176
+ }
177
+ if (digit > 9) {
178
+ digit -= 9;
179
+ }
180
+ sum += digit;
181
+ }
182
+ return sum % 10 === 0;
183
+ };
184
+
185
+ card.cardType = function(num) {
186
+ return card.cardTypes[num.slice(0, 2)] || 'Unknown';
187
+ };
188
+
189
+ card.cardTypes = (function() {
190
+ var num, types, _i, _j;
191
+ types = {};
192
+ for (num = _i = 40; _i <= 49; num = ++_i) {
193
+ types[num] = 'Visa';
194
+ }
195
+ for (num = _j = 50; _j <= 59; num = ++_j) {
196
+ types[num] = 'MasterCard';
197
+ }
198
+ types[34] = types[37] = 'American Express';
199
+ types[60] = types[62] = types[64] = types[65] = 'Discover';
200
+ types[35] = 'JCB';
201
+ types[30] = types[36] = types[38] = types[39] = 'Diners Club';
202
+ return types;
203
+ })();
204
+
205
+ return card;
206
+
207
+ }).call(this, this.Stripe.token);
208
+
209
+ this.Stripe.bankAccount = (function(_super) {
210
+
211
+ __extends(bankAccount, _super);
212
+
213
+ function bankAccount() {
214
+ return bankAccount.__super__.constructor.apply(this, arguments);
215
+ }
216
+
217
+ bankAccount.tokenName = 'bank_account';
218
+
219
+ bankAccount.whitelistedAttrs = ['country', 'routing_number', 'account_number'];
220
+
221
+ bankAccount.createToken = function(data, params, callback) {
222
+ if (params == null) {
223
+ params = {};
224
+ }
225
+ Stripe.token.validate(data, 'bank account');
226
+ if (typeof params === 'function') {
227
+ callback = params;
228
+ params = {};
229
+ }
230
+ params[bankAccount.tokenName] = Stripe.token.formatData(data, bankAccount.whitelistedAttrs);
231
+ return Stripe.token.create(params, callback);
232
+ };
233
+
234
+ bankAccount.getToken = function(token, callback) {
235
+ return Stripe.token.get(token, callback);
236
+ };
237
+
238
+ bankAccount.validateRoutingNumber = function(num, country) {
239
+ num = Stripe.utils.trim(num);
240
+ switch (country) {
241
+ case 'US':
242
+ return /^\d+$/.test(num) && num.length === 9 && bankAccount.routingChecksum(num);
243
+ case 'CA':
244
+ return /\d{5}\-\d{3}/.test(num) && num.length === 9;
245
+ default:
246
+ return true;
247
+ }
248
+ };
249
+
250
+ bankAccount.validateAccountNumber = function(num, country) {
251
+ num = Stripe.utils.trim(num);
252
+ switch (country) {
253
+ case 'US':
254
+ return /^\d+$/.test(num) && num.length >= 1 && num.length <= 17;
255
+ default:
256
+ return true;
257
+ }
258
+ };
259
+
260
+ bankAccount.routingChecksum = function(num) {
261
+ var digits, index, sum, _i, _len, _ref;
262
+ sum = 0;
263
+ digits = (num + '').split('');
264
+ _ref = [0, 3, 6];
265
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
266
+ index = _ref[_i];
267
+ sum += parseInt(digits[index]) * 3;
268
+ sum += parseInt(digits[index + 1]) * 7;
269
+ sum += parseInt(digits[index + 2]);
270
+ }
271
+ return sum !== 0 && sum % 10 === 0;
272
+ };
273
+
274
+ return bankAccount;
275
+
276
+ }).call(this, this.Stripe.token);
277
+
278
+ this.Stripe.bitcoinReceiver = (function() {
279
+
280
+ function bitcoinReceiver() {}
281
+
282
+ bitcoinReceiver._whitelistedAttrs = ['amount', 'currency', 'email', 'description'];
283
+
284
+ bitcoinReceiver.createReceiver = function(data, callback) {
285
+ var params;
286
+ Stripe.token.validate(data, 'bitcoin_receiver data');
287
+ params = Stripe.token.formatData(data, this._whitelistedAttrs);
288
+ params.key = Stripe.key || Stripe.publishableKey;
289
+ Stripe.utils.validateKey(params.key);
290
+ return Stripe.ajaxJSONP({
291
+ url: "" + Stripe.endpoint + "/bitcoin/receivers",
292
+ data: params,
293
+ method: 'POST',
294
+ success: function(body, status) {
295
+ return typeof callback === "function" ? callback(status, body) : void 0;
296
+ },
297
+ complete: Stripe.complete(callback, "A network error has occurred while creating a Bitcoin address. Please try again."),
298
+ timeout: 40000
299
+ });
300
+ };
301
+
302
+ bitcoinReceiver.getReceiver = function(id, callback) {
303
+ var key;
304
+ if (!id) {
305
+ throw 'receiver id required';
306
+ }
307
+ key = Stripe.key || Stripe.publishableKey;
308
+ Stripe.utils.validateKey(key);
309
+ return Stripe.ajaxJSONP({
310
+ url: "" + Stripe.endpoint + "/bitcoin/receivers/" + id,
311
+ data: {
312
+ key: key
313
+ },
314
+ success: function(body, status) {
315
+ return typeof callback === "function" ? callback(status, body) : void 0;
316
+ },
317
+ complete: Stripe.complete(callback, "A network error has occurred loading data from Stripe. Please try again."),
318
+ timeout: 40000
319
+ });
320
+ };
321
+
322
+ bitcoinReceiver._activeReceiverPolls = {};
323
+
324
+ bitcoinReceiver._clearReceiverPoll = function(receiverId) {
325
+ return delete bitcoinReceiver._activeReceiverPolls[receiverId];
326
+ };
327
+
328
+ bitcoinReceiver._pollInterval = 1500;
329
+
330
+ bitcoinReceiver.pollReceiver = function(receiverId, callback) {
331
+ if (this._activeReceiverPolls[receiverId] != null) {
332
+ throw "You are already polling receiver " + receiverId + ". Please cancel that poll before polling it again.";
333
+ }
334
+ this._activeReceiverPolls[receiverId] = {};
335
+ return this._pollReceiver(receiverId, callback);
336
+ };
337
+
338
+ bitcoinReceiver._pollReceiver = function(receiverId, callback) {
339
+ bitcoinReceiver.getReceiver(receiverId, function(status, body) {
340
+ var pollInterval, timeoutId;
341
+ if (bitcoinReceiver._activeReceiverPolls[receiverId] == null) {
342
+ return;
343
+ }
344
+ if (status === 200 && body.filled) {
345
+ bitcoinReceiver._clearReceiverPoll(receiverId);
346
+ return typeof callback === "function" ? callback(status, body) : void 0;
347
+ } else if (status >= 400 && status < 500) {
348
+ bitcoinReceiver._clearReceiverPoll(receiverId);
349
+ return typeof callback === "function" ? callback(status, body) : void 0;
350
+ } else {
351
+ pollInterval = status === 500 ? 5000 : bitcoinReceiver._pollInterval;
352
+ timeoutId = setTimeout(function() {
353
+ return bitcoinReceiver._pollReceiver(receiverId, callback);
354
+ }, pollInterval);
355
+ return bitcoinReceiver._activeReceiverPolls[receiverId]['timeoutId'] = timeoutId;
356
+ }
357
+ });
358
+ };
359
+
360
+ bitcoinReceiver.cancelReceiverPoll = function(receiverId) {
361
+ var activeReceiver;
362
+ activeReceiver = bitcoinReceiver._activeReceiverPolls[receiverId];
363
+ if (activeReceiver == null) {
364
+ throw "You are not polling receiver " + receiverId + ".";
365
+ }
366
+ if (activeReceiver['timeoutId'] != null) {
367
+ clearTimeout(activeReceiver['timeoutId']);
368
+ }
369
+ bitcoinReceiver._clearReceiverPoll(receiverId);
370
+ };
371
+
372
+ return bitcoinReceiver;
373
+
374
+ }).call(this);
375
+
376
+ exports = ['createToken', 'getToken', 'cardType', 'validateExpiry', 'validateCVC', 'validateCardNumber'];
377
+
378
+ for (_i = 0, _len = exports.length; _i < _len; _i++) {
379
+ key = exports[_i];
380
+ this.Stripe[key] = this.Stripe.card[key];
381
+ }
382
+
383
+ if (typeof module !== "undefined" && module !== null) {
384
+ module.exports = this.Stripe;
385
+ }
386
+
387
+ if (typeof define === "function") {
388
+ define('stripe', [], function() {
389
+ return _this.Stripe;
390
+ });
391
+ }
392
+
393
+ }).call(this);
394
+ (function() {
395
+ var e, requestID, serialize,
396
+ __slice = [].slice;
397
+
398
+ e = encodeURIComponent;
399
+
400
+ requestID = new Date().getTime();
401
+
402
+ serialize = function(object, result, scope) {
403
+ var key, value;
404
+ if (result == null) {
405
+ result = [];
406
+ }
407
+ for (key in object) {
408
+ value = object[key];
409
+ if (scope) {
410
+ key = "" + scope + "[" + key + "]";
411
+ }
412
+ if (typeof value === 'object') {
413
+ serialize(value, result, key);
414
+ } else {
415
+ result.push("" + key + "=" + (e(value)));
416
+ }
417
+ }
418
+ return result.join('&').replace(/%20/g, '+');
419
+ };
420
+
421
+ this.Stripe.ajaxJSONP = function(options) {
422
+ var abort, abortTimeout, callbackName, head, script, xhr;
423
+ if (options == null) {
424
+ options = {};
425
+ }
426
+ callbackName = 'sjsonp' + (++requestID);
427
+ script = document.createElement('script');
428
+ abortTimeout = null;
429
+ abort = function(reason) {
430
+ var _ref;
431
+ if (reason == null) {
432
+ reason = 'abort';
433
+ }
434
+ clearTimeout(abortTimeout);
435
+ if ((_ref = script.parentNode) != null) {
436
+ _ref.removeChild(script);
437
+ }
438
+ if (callbackName in window) {
439
+ window[callbackName] = (function() {});
440
+ }
441
+ return typeof options.complete === "function" ? options.complete(reason, xhr, options) : void 0;
442
+ };
443
+ xhr = {
444
+ abort: abort
445
+ };
446
+ script.onerror = function() {
447
+ xhr.abort();
448
+ return typeof options.error === "function" ? options.error(xhr, options) : void 0;
449
+ };
450
+ window[callbackName] = function() {
451
+ var args;
452
+ args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
453
+ clearTimeout(abortTimeout);
454
+ script.parentNode.removeChild(script);
455
+ try {
456
+ delete window[callbackName];
457
+ } catch (e) {
458
+ window[callbackName] = void 0;
459
+ }
460
+ if (typeof options.success === "function") {
461
+ options.success.apply(options, args);
462
+ }
463
+ return typeof options.complete === "function" ? options.complete('success', xhr, options) : void 0;
464
+ };
465
+ options.data || (options.data = {});
466
+ options.data.callback = callbackName;
467
+ if (options.method) {
468
+ options.data._method = options.method;
469
+ }
470
+ script.src = options.url + '?' + serialize(options.data);
471
+ head = document.getElementsByTagName('head')[0];
472
+ head.appendChild(script);
473
+ if (options.timeout > 0) {
474
+ abortTimeout = setTimeout(function() {
475
+ return xhr.abort('timeout');
476
+ }, options.timeout);
477
+ }
478
+ return xhr;
479
+ };
480
+
481
+ }).call(this);
482
+ (function() {
483
+ var __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
484
+
485
+ this.Stripe.utils = (function() {
486
+
487
+ function utils() {}
488
+
489
+ utils.trim = function(str) {
490
+ return (str + '').replace(/^\s+|\s+$/g, '');
491
+ };
492
+
493
+ utils.underscore = function(str) {
494
+ return (str + '').replace(/([A-Z])/g, function($1) {
495
+ return "_" + ($1.toLowerCase());
496
+ }).replace(/-/g, '_');
497
+ };
498
+
499
+ utils.underscoreKeys = function(data) {
500
+ var key, value, _results;
501
+ _results = [];
502
+ for (key in data) {
503
+ value = data[key];
504
+ delete data[key];
505
+ _results.push(data[this.underscore(key)] = value);
506
+ }
507
+ return _results;
508
+ };
509
+
510
+ utils.isElement = function(el) {
511
+ if (typeof el !== 'object') {
512
+ return false;
513
+ }
514
+ if ((typeof jQuery !== "undefined" && jQuery !== null) && el instanceof jQuery) {
515
+ return true;
516
+ }
517
+ return el.nodeType === 1;
518
+ };
519
+
520
+ utils.paramsFromForm = function(form, whitelist) {
521
+ var attr, input, inputs, select, selects, values, _i, _j, _len, _len1;
522
+ if (whitelist == null) {
523
+ whitelist = [];
524
+ }
525
+ if ((typeof jQuery !== "undefined" && jQuery !== null) && form instanceof jQuery) {
526
+ form = form[0];
527
+ }
528
+ inputs = form.getElementsByTagName('input');
529
+ selects = form.getElementsByTagName('select');
530
+ values = {};
531
+ for (_i = 0, _len = inputs.length; _i < _len; _i++) {
532
+ input = inputs[_i];
533
+ attr = this.underscore(input.getAttribute('data-stripe'));
534
+ if (__indexOf.call(whitelist, attr) < 0) {
535
+ continue;
536
+ }
537
+ values[attr] = input.value;
538
+ }
539
+ for (_j = 0, _len1 = selects.length; _j < _len1; _j++) {
540
+ select = selects[_j];
541
+ attr = this.underscore(select.getAttribute('data-stripe'));
542
+ if (__indexOf.call(whitelist, attr) < 0) {
543
+ continue;
544
+ }
545
+ if (select.selectedIndex != null) {
546
+ values[attr] = select.options[select.selectedIndex].value;
547
+ }
548
+ }
549
+ return values;
550
+ };
551
+
552
+ utils.validateKey = function(key) {
553
+ if (!key || typeof key !== 'string') {
554
+ throw new Error('You did not set a valid publishable key. ' + 'Call Stripe.setPublishableKey() with your publishable key. ' + 'For more info, see https://stripe.com/docs/stripe.js');
555
+ }
556
+ if (/\s/g.test(key)) {
557
+ throw new Error('Your key is invalid, as it contains whitespace. ' + 'For more info, see https://stripe.com/docs/stripe.js');
558
+ }
559
+ if (/^sk_/.test(key)) {
560
+ throw new Error('You are using a secret key with Stripe.js, instead of the publishable one. ' + 'For more info, see https://stripe.com/docs/stripe.js');
561
+ }
562
+ };
563
+
564
+ return utils;
565
+
566
+ })();
567
+
568
+ }).call(this);
569
+ (function() {
570
+ var __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
571
+
572
+ this.Stripe.validator = {
573
+ boolean: function(expected, value) {
574
+ if (!(value === 'true' || value === 'false')) {
575
+ return "Enter a boolean string (true or false)";
576
+ }
577
+ },
578
+ integer: function(expected, value) {
579
+ if (!/^\d+$/.test(value)) {
580
+ return "Enter an integer";
581
+ }
582
+ },
583
+ positive: function(expected, value) {
584
+ if (!(!this.integer(expected, value) && parseInt(value, 10) > 0)) {
585
+ return "Enter a positive value";
586
+ }
587
+ },
588
+ range: function(expected, value) {
589
+ var _ref;
590
+ if (_ref = parseInt(value, 10), __indexOf.call(expected, _ref) < 0) {
591
+ return "Needs to be between " + expected[0] + " and " + expected[expected.length - 1];
592
+ }
593
+ },
594
+ required: function(expected, value) {
595
+ if (expected && (!(value != null) || value === '')) {
596
+ return "Required";
597
+ }
598
+ },
599
+ year: function(expected, value) {
600
+ if (!/^\d{4}$/.test(value)) {
601
+ return "Enter a 4-digit year";
602
+ }
603
+ },
604
+ birthYear: function(expected, value) {
605
+ var year;
606
+ year = this.year(expected, value);
607
+ if (year) {
608
+ return year;
609
+ } else if (parseInt(value, 10) > 2000) {
610
+ return "You must be over 18";
611
+ } else if (parseInt(value, 10) < 1900) {
612
+ return "Enter your birth year";
613
+ }
614
+ },
615
+ month: function(expected, value) {
616
+ if (this.integer(expected, value)) {
617
+ return "Please enter a month";
618
+ }
619
+ if (this.range([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], value)) {
620
+ return "Needs to be between 1 and 12";
621
+ }
622
+ },
623
+ choices: function(expected, value) {
624
+ if (__indexOf.call(expected, value) < 0) {
625
+ return "Not an acceptable value for this field";
626
+ }
627
+ },
628
+ email: function(expected, value) {
629
+ if (!/^[^@<\s>]+@[^@<\s>]+$/.test(value)) {
630
+ return "That doesn't look like an email address";
631
+ }
632
+ },
633
+ url: function(expected, value) {
634
+ if (!/^https?:\/\/.+\..+/.test(value)) {
635
+ return "Not a valid url";
636
+ }
637
+ },
638
+ usTaxID: function(expected, value) {
639
+ if (!/^\d{2}-?\d{1}-?\d{2}-?\d{4}$/.test(value)) {
640
+ return "Not a valid tax ID";
641
+ }
642
+ },
643
+ ein: function(expected, value) {
644
+ if (!/^\d{2}-?\d{7}$/.test(value)) {
645
+ return "Not a valid EIN";
646
+ }
647
+ },
648
+ ssnLast4: function(expected, value) {
649
+ if (!/^\d{4}$/.test(value)) {
650
+ return "Not a valid last 4 digits for an SSN";
651
+ }
652
+ },
653
+ ownerPersonalID: function(country, value) {
654
+ var match;
655
+ match = (function() {
656
+ switch (country) {
657
+ case 'CA':
658
+ return /^\d{3}-?\d{3}-?\d{3}$/.test(value);
659
+ case 'US':
660
+ return true;
661
+ }
662
+ })();
663
+ if (!match) {
664
+ return "Not a valid ID";
665
+ }
666
+ },
667
+ bizTaxID: function(country, value) {
668
+ var fieldName, match, regex, regexes, validation, validations, _i, _len;
669
+ validations = {
670
+ 'CA': ['Tax ID', [/^\d{9}$/]],
671
+ 'US': ['EIN', [/^\d{2}-?\d{7}$/]]
672
+ };
673
+ validation = validations[country];
674
+ if (validation != null) {
675
+ fieldName = validation[0];
676
+ regexes = validation[1];
677
+ match = false;
678
+ for (_i = 0, _len = regexes.length; _i < _len; _i++) {
679
+ regex = regexes[_i];
680
+ if (regex.test(value)) {
681
+ match = true;
682
+ break;
683
+ }
684
+ }
685
+ if (!match) {
686
+ return "Not a valid " + fieldName;
687
+ }
688
+ }
689
+ },
690
+ zip: function(country, value) {
691
+ var match;
692
+ match = (function() {
693
+ switch (country.toUpperCase()) {
694
+ case 'CA':
695
+ return /^[\d\w]{6}$/.test(value != null ? value.replace(/\s+/g, '') : void 0);
696
+ case 'US':
697
+ return /^\d{5}$/.test(value) || /^\d{9}$/.test(value);
698
+ }
699
+ })();
700
+ if (!match) {
701
+ return "Not a valid zip";
702
+ }
703
+ },
704
+ bankAccountNumber: function(expected, value) {
705
+ if (!/^\d{1,17}$/.test(value)) {
706
+ return "Invalid bank account number";
707
+ }
708
+ },
709
+ usRoutingNumber: function(value) {
710
+ var index, part1, part2, part3, total, _i, _ref;
711
+ if (!/^\d{9}$/.test(value)) {
712
+ return "Routing number must have 9 digits";
713
+ }
714
+ total = 0;
715
+ for (index = _i = 0, _ref = value.length - 1; _i <= _ref; index = _i += 3) {
716
+ part1 = parseInt(value.charAt(index), 10) * 3;
717
+ part2 = parseInt(value.charAt(index + 1), 10) * 7;
718
+ part3 = parseInt(value.charAt(index + 2), 10);
719
+ total += part1 + part2 + part3;
720
+ }
721
+ if (!(total !== 0 && total % 10 === 0)) {
722
+ return "Invalid routing number";
723
+ }
724
+ },
725
+ caRoutingNumber: function(value) {
726
+ if (!/^\d{5}\-\d{3}$/.test(value)) {
727
+ return "Invalid transit number";
728
+ }
729
+ },
730
+ routingNumber: function(country, value) {
731
+ switch (country.toUpperCase()) {
732
+ case 'CA':
733
+ return this.caRoutingNumber(value);
734
+ case 'US':
735
+ return this.usRoutingNumber(value);
736
+ }
737
+ },
738
+ phoneNumber: function(expected, value) {
739
+ var number;
740
+ number = value.replace(/[^0-9]/g, '');
741
+ if (number.length !== 10) {
742
+ return "Invalid phone number";
743
+ }
744
+ },
745
+ bizDBA: function(expected, value) {
746
+ if (!/^.{1,23}$/.test(value)) {
747
+ return "Statement descriptors can only have up to 23 characters";
748
+ }
749
+ },
750
+ nameLength: function(expected, value) {
751
+ if (value.length === 1) {
752
+ return 'Names need to be longer than one character';
753
+ }
754
+ }
755
+ };
756
+
757
+ }).call(this);