recurly-js-rails 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +37 -0
- data/Rakefile +2 -0
- data/lib/recurly-js-rails.rb +6 -0
- data/lib/recurly-js-rails/engine.rb +6 -0
- data/lib/recurly-js-rails/version.rb +6 -0
- data/recurly-js-rails.gemspec +17 -0
- data/vendor/assets/javascripts/recurly.js +2243 -0
- data/vendor/assets/stylesheets/images/check.png +0 -0
- data/vendor/assets/stylesheets/images/coupon_check.png +0 -0
- data/vendor/assets/stylesheets/images/coupon_checking.gif +0 -0
- data/vendor/assets/stylesheets/images/coupon_invalid.png +0 -0
- data/vendor/assets/stylesheets/images/coupon_valid.png +0 -0
- data/vendor/assets/stylesheets/images/credit_cards/amex.png +0 -0
- data/vendor/assets/stylesheets/images/credit_cards/discover.png +0 -0
- data/vendor/assets/stylesheets/images/credit_cards/mastercard.png +0 -0
- data/vendor/assets/stylesheets/images/credit_cards/visa.png +0 -0
- data/vendor/assets/stylesheets/images/dash.png +0 -0
- data/vendor/assets/stylesheets/images/due_now.png +0 -0
- data/vendor/assets/stylesheets/images/error.png +0 -0
- data/vendor/assets/stylesheets/images/loading.gif +0 -0
- data/vendor/assets/stylesheets/images/submitting.gif +0 -0
- data/vendor/assets/stylesheets/images/uncheck.png +0 -0
- data/vendor/assets/stylesheets/recurly.css +701 -0
- metadata +71 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Chris McGrath
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# RecurlyJs::Rails
|
2
|
+
|
3
|
+
Early alpha version of a Rails 3.1+ asset gem for recurly-js
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'recurly-js-rails'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install recurly-js-rails
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
To include the js and css everywhere add:
|
22
|
+
|
23
|
+
`//= require recurly` to both your application.js and application.css
|
24
|
+
|
25
|
+
To just include on your subscription pages, add:
|
26
|
+
|
27
|
+
`config.assets.precompile += %w{recurly.css recurly.js}`
|
28
|
+
|
29
|
+
To your application.rb and use `stylesheet_link_tag "recurly"` and `javascript_include_tag "recurly"` where appropriate
|
30
|
+
|
31
|
+
## Contributing
|
32
|
+
|
33
|
+
1. Fork it
|
34
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
35
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
36
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
37
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/recurly-js-rails/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Chris McGrath"]
|
6
|
+
gem.email = ["chris@octopod.info"]
|
7
|
+
gem.description = %q{Rails 3.1+ asset gem for recurly-js}
|
8
|
+
gem.summary = %q{Rails 3.1+ asset gem for recurly-js}
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.name = "recurly-js-rails"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = RecurlyJs::Rails::VERSION
|
17
|
+
end
|
@@ -0,0 +1,2243 @@
|
|
1
|
+
// Recurly.js - v2.1.3
|
2
|
+
//
|
3
|
+
// Communicates with Recurly <https://recurly.com> via a JSONP API,
|
4
|
+
// generates UI, handles user error, and passes control to the client
|
5
|
+
// to handle the successful events such as subscription creation.
|
6
|
+
//
|
7
|
+
// Example Site: https://js.recurly.com
|
8
|
+
//
|
9
|
+
// (MIT License)
|
10
|
+
//
|
11
|
+
// Copyright (C) 2012 by Recurly, Inc.
|
12
|
+
//
|
13
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
14
|
+
// of this software and associated documentation files (the "Software"), to deal
|
15
|
+
// in the Software without restriction, including without limitation the rights
|
16
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
17
|
+
// copies of the Software, and to permit persons to whom the Software is
|
18
|
+
// furnished to do so, subject to the following conditions:
|
19
|
+
//
|
20
|
+
// The above copyright notice and this permission notice shall be included in
|
21
|
+
// all copies or substantial portions of the Software.
|
22
|
+
//
|
23
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
24
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
25
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
26
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
27
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
28
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
29
|
+
// THE SOFTWARE.
|
30
|
+
|
31
|
+
|
32
|
+
(function($) {
|
33
|
+
"use strict";
|
34
|
+
|
35
|
+
//////////////////////////////////////////////////
|
36
|
+
// Compiled from src/js/core.js
|
37
|
+
//////////////////////////////////////////////////
|
38
|
+
|
39
|
+
// Non-intrusive Object.create
|
40
|
+
function createObject(o) {
|
41
|
+
function F() {}
|
42
|
+
F.prototype = o || this;
|
43
|
+
return new F();
|
44
|
+
};
|
45
|
+
|
46
|
+
var R = {};
|
47
|
+
R.settings = {
|
48
|
+
enableGeoIP: true
|
49
|
+
, acceptedCards: ['american_express', 'discover', 'mastercard', 'visa']
|
50
|
+
, oneErrorPerField: true
|
51
|
+
};
|
52
|
+
|
53
|
+
R.version = '2.1.3';
|
54
|
+
|
55
|
+
R.dom = {};
|
56
|
+
|
57
|
+
R.Error = {
|
58
|
+
toString: function() {
|
59
|
+
return 'RecurlyJS Error: ' + this.message;
|
60
|
+
}
|
61
|
+
};
|
62
|
+
|
63
|
+
R.raiseError = function(message) {
|
64
|
+
var e = createObject(R.Error);
|
65
|
+
e.message = message;
|
66
|
+
throw e;
|
67
|
+
};
|
68
|
+
|
69
|
+
|
70
|
+
R.config = function(settings) {
|
71
|
+
$.extend(true, R.settings, settings);
|
72
|
+
|
73
|
+
if(!settings.baseURL) {
|
74
|
+
R.settings.baseURL = 'https://api.recurly.com/jsonp/';
|
75
|
+
var subdomain = R.settings.subdomain || R.raiseError('company subdomain not configured');
|
76
|
+
R.settings.baseURL += subdomain + '/';
|
77
|
+
}
|
78
|
+
};
|
79
|
+
|
80
|
+
|
81
|
+
function pluralize(count, term) {
|
82
|
+
if(count == 1) {
|
83
|
+
return term.substr(0,term.length-1);
|
84
|
+
}
|
85
|
+
|
86
|
+
return '' + count + ' ' + term;
|
87
|
+
}
|
88
|
+
|
89
|
+
// Immutable currency-amount object
|
90
|
+
// This will eventually handle multi-currency
|
91
|
+
// where it will store a list of costs per currency
|
92
|
+
// and accessors will return the appropriate one
|
93
|
+
// based on the current currency
|
94
|
+
//
|
95
|
+
|
96
|
+
(R.Cost = function(cents) {
|
97
|
+
this._cents = cents || 0;
|
98
|
+
}).prototype = {
|
99
|
+
toString: function() {
|
100
|
+
return R.formatCurrency(this.dollars());
|
101
|
+
}
|
102
|
+
, cents: function(val) {
|
103
|
+
if(val === undefined)
|
104
|
+
return this._cents;
|
105
|
+
|
106
|
+
return new Cost(val);
|
107
|
+
}
|
108
|
+
, dollars: function(val) {
|
109
|
+
if(val === undefined)
|
110
|
+
return this._cents/100;
|
111
|
+
|
112
|
+
return new R.Cost(val*100);
|
113
|
+
}
|
114
|
+
, mult: function(n) {
|
115
|
+
return new R.Cost(this._cents * n);
|
116
|
+
}
|
117
|
+
, add: function(n) {
|
118
|
+
if(n.cents) n = n.cents();
|
119
|
+
return new R.Cost(this._cents + n);
|
120
|
+
}
|
121
|
+
, sub: function(n) {
|
122
|
+
if(n.cents) n = n.cents();
|
123
|
+
return new R.Cost(this._cents - n);
|
124
|
+
}
|
125
|
+
};
|
126
|
+
|
127
|
+
R.Cost.FREE = new R.Cost(0);
|
128
|
+
|
129
|
+
(R.TimePeriod = function(length,unit) {
|
130
|
+
this.length = length;
|
131
|
+
this.unit = unit;
|
132
|
+
}).prototype = {
|
133
|
+
toString: function() {
|
134
|
+
return '' + pluralize(this.length, this.unit);
|
135
|
+
}
|
136
|
+
, toDate: function() {
|
137
|
+
var d = new Date();
|
138
|
+
switch(this.unit) {
|
139
|
+
case 'month':
|
140
|
+
d.setMonth( d.getMonth() + this.length );
|
141
|
+
break;
|
142
|
+
case 'day':
|
143
|
+
d.setDay( d.getDay() + this.length );
|
144
|
+
break;
|
145
|
+
}
|
146
|
+
return d;
|
147
|
+
}
|
148
|
+
, clone: function() {
|
149
|
+
return new R.TimePeriod(this.length,this.unit);
|
150
|
+
}
|
151
|
+
};
|
152
|
+
|
153
|
+
(R.RecurringCost = function(cost,interval) {
|
154
|
+
this.cost = cost;
|
155
|
+
this.interval = interval;
|
156
|
+
}).prototype = {
|
157
|
+
toString: function() {
|
158
|
+
return '' + this.cost + ' every ' + this.interval;
|
159
|
+
}
|
160
|
+
, clone: function() {
|
161
|
+
return new R.TimePeriod(this.length,this.unit);
|
162
|
+
}
|
163
|
+
};
|
164
|
+
|
165
|
+
R.RecurringCost.FREE = new R.RecurringCost(0,null);
|
166
|
+
|
167
|
+
(R.RecurringCostStage = function(recurringCost, duration) {
|
168
|
+
this.recurringCost = recurringCost;
|
169
|
+
this.duration = duration;
|
170
|
+
}).prototype = {
|
171
|
+
toString: function() {
|
172
|
+
this.recurringCost.toString() + ' for ' + this.duration.toString();
|
173
|
+
}
|
174
|
+
};
|
175
|
+
|
176
|
+
|
177
|
+
|
178
|
+
|
179
|
+
//////////////////////////////////////////////////
|
180
|
+
// Compiled from src/js/locale.js
|
181
|
+
//////////////////////////////////////////////////
|
182
|
+
|
183
|
+
R.locale = {};
|
184
|
+
|
185
|
+
R.locale.errors = {
|
186
|
+
emptyField: 'Required field'
|
187
|
+
, missingFullAddress: 'Please enter your full address.'
|
188
|
+
, invalidEmail: 'Invalid'
|
189
|
+
, invalidCC: 'Invalid'
|
190
|
+
, invalidCVV: 'Invalid'
|
191
|
+
, invalidCoupon: 'Invalid'
|
192
|
+
, cardDeclined: 'Transaction declined'
|
193
|
+
, acceptTOS: 'Please accept the Terms of Service.'
|
194
|
+
, invalidQuantity: 'Invalid quantity'
|
195
|
+
};
|
196
|
+
|
197
|
+
R.locale.currencies = {};
|
198
|
+
|
199
|
+
R.locale.currency = {
|
200
|
+
format: "%u%n"
|
201
|
+
, separator: "."
|
202
|
+
, delimiter: ","
|
203
|
+
, precision: 2
|
204
|
+
};
|
205
|
+
|
206
|
+
function C(key, def) {
|
207
|
+
var c = R.locale.currencies[key] = createObject(R.locale.currency);
|
208
|
+
for(var p in def) {
|
209
|
+
c[p] = def[p];
|
210
|
+
}
|
211
|
+
};
|
212
|
+
|
213
|
+
C('USD', {
|
214
|
+
symbol: '$'
|
215
|
+
});
|
216
|
+
|
217
|
+
C('AUD', {
|
218
|
+
symbol: '$'
|
219
|
+
});
|
220
|
+
|
221
|
+
C('CAD', {
|
222
|
+
symbol: '$'
|
223
|
+
});
|
224
|
+
|
225
|
+
C('EUR', {
|
226
|
+
symbol: '\u20ac'
|
227
|
+
});
|
228
|
+
|
229
|
+
C('GBP', {
|
230
|
+
symbol: '\u00a3'
|
231
|
+
});
|
232
|
+
|
233
|
+
C('CZK', {
|
234
|
+
symbol: '\u004b'
|
235
|
+
});
|
236
|
+
|
237
|
+
C('DKK', {
|
238
|
+
symbol: '\u006b\u0072'
|
239
|
+
});
|
240
|
+
|
241
|
+
C('HUF', {
|
242
|
+
symbol: 'Ft'
|
243
|
+
});
|
244
|
+
|
245
|
+
C('JPY', {
|
246
|
+
symbol: '\u00a5'
|
247
|
+
});
|
248
|
+
|
249
|
+
C('NOK', {
|
250
|
+
symbol: 'kr'
|
251
|
+
});
|
252
|
+
|
253
|
+
C('NZD', {
|
254
|
+
symbol: '$'
|
255
|
+
});
|
256
|
+
|
257
|
+
C('PLN', {
|
258
|
+
symbol: '\u007a'
|
259
|
+
});
|
260
|
+
|
261
|
+
C('SGD', {
|
262
|
+
symbol: '$'
|
263
|
+
});
|
264
|
+
|
265
|
+
C('SEK', {
|
266
|
+
symbol: 'kr'
|
267
|
+
});
|
268
|
+
|
269
|
+
C('CHF', {
|
270
|
+
symbol: 'Fr'
|
271
|
+
});
|
272
|
+
|
273
|
+
C('ZAR', {
|
274
|
+
symbol: 'R'
|
275
|
+
});
|
276
|
+
|
277
|
+
|
278
|
+
|
279
|
+
R.settings.locale = R.locale;
|
280
|
+
|
281
|
+
|
282
|
+
//////////////////////////////////////////////////
|
283
|
+
// Compiled from src/js/utils.js
|
284
|
+
//////////////////////////////////////////////////
|
285
|
+
|
286
|
+
R.knownCards = {
|
287
|
+
'visa': {
|
288
|
+
prefixes: [4]
|
289
|
+
, name: 'Visa'
|
290
|
+
}
|
291
|
+
, 'mastercard': {
|
292
|
+
prefixes: [51, 52, 53, 54, 55]
|
293
|
+
, name: 'MasterCard'
|
294
|
+
}
|
295
|
+
, 'american_express': {
|
296
|
+
prefixes: [34, 37]
|
297
|
+
, name: 'American Express'
|
298
|
+
}
|
299
|
+
, 'discover': {
|
300
|
+
prefixes: [6011, 62, 64, 65]
|
301
|
+
, name: 'Discover'
|
302
|
+
}
|
303
|
+
, 'diners_club': {
|
304
|
+
prefixes: [305, 36, 38]
|
305
|
+
, name: 'Diners Club'
|
306
|
+
}
|
307
|
+
, 'carte_blanche': {
|
308
|
+
prefixes: [300, 301, 302, 303, 304, 305]
|
309
|
+
}
|
310
|
+
, 'jcb': {
|
311
|
+
prefixes: [35]
|
312
|
+
, name: 'JCB'
|
313
|
+
}
|
314
|
+
, 'enroute': {
|
315
|
+
prefixes: [2014, 2149]
|
316
|
+
, name: 'EnRoute'
|
317
|
+
}
|
318
|
+
, 'maestro': {
|
319
|
+
prefixes: [5018, 5020, 5038, 6304, 6759, 6761]
|
320
|
+
, name: 'Maestro'
|
321
|
+
}
|
322
|
+
, 'laser': {
|
323
|
+
prefixes: [6304, 6706, 6771, 6709]
|
324
|
+
, name: 'Laser'
|
325
|
+
}
|
326
|
+
};
|
327
|
+
|
328
|
+
// Credit card type functions
|
329
|
+
R.detectCardType = function(cardNumber) {
|
330
|
+
cardNumber = cardNumber.replace(/\D/g, '');
|
331
|
+
var cards = R.knownCards;
|
332
|
+
|
333
|
+
for(var ci in cards) {
|
334
|
+
if(cards.hasOwnProperty(ci)) {
|
335
|
+
var c = cards[ci];
|
336
|
+
for(var pi=0,pl=c.prefixes.length; pi < pl; ++pi) {
|
337
|
+
if(c.prefixes.hasOwnProperty(pi)) {
|
338
|
+
var p = c.prefixes[pi];
|
339
|
+
if (new RegExp('^' + p.toString()).test(cardNumber))
|
340
|
+
return ci;
|
341
|
+
}
|
342
|
+
}
|
343
|
+
}
|
344
|
+
}
|
345
|
+
|
346
|
+
return false;
|
347
|
+
};
|
348
|
+
|
349
|
+
|
350
|
+
// Formats currency amount in the current denomination or one provided
|
351
|
+
// based on R.locale.currencies rules
|
352
|
+
R.formatCurrency = function(num,denomination) {
|
353
|
+
|
354
|
+
if(num < 0) {
|
355
|
+
num = -num;
|
356
|
+
var negative = true;
|
357
|
+
}
|
358
|
+
else {
|
359
|
+
var negative = false;
|
360
|
+
}
|
361
|
+
|
362
|
+
denomination = denomination || R.settings.currency ||
|
363
|
+
R.raiseError('currency not configured');
|
364
|
+
|
365
|
+
var langspec = R.locale.currency;
|
366
|
+
var currencyspec = R.locale.currencies[denomination];
|
367
|
+
|
368
|
+
// Format to precision
|
369
|
+
var str = num.toFixed(currencyspec.precision);
|
370
|
+
|
371
|
+
// Replace default period with format separator
|
372
|
+
if(langspec.separator != '.') {
|
373
|
+
str = str.replace(/\./g, langspec.separator);
|
374
|
+
}
|
375
|
+
|
376
|
+
function insertDelimiters(str) {
|
377
|
+
var sRegExp = new RegExp('(-?[0-9]+)([0-9]{3})');
|
378
|
+
while(sRegExp.test(str)) {
|
379
|
+
str = str.replace(sRegExp, '$1'+langspec.delimiter+'$2');
|
380
|
+
}
|
381
|
+
return str;
|
382
|
+
}
|
383
|
+
|
384
|
+
// Apply thousands delimiter
|
385
|
+
str = insertDelimiters(str);
|
386
|
+
|
387
|
+
// Format unit/number order
|
388
|
+
var format = langspec.format;
|
389
|
+
format = format.replace(/%u/g, currencyspec.symbol);
|
390
|
+
format = format.replace(/%n/g, str);
|
391
|
+
str = format;
|
392
|
+
|
393
|
+
if(negative) {
|
394
|
+
str = '-' + str;
|
395
|
+
}
|
396
|
+
|
397
|
+
return str;
|
398
|
+
};
|
399
|
+
|
400
|
+
var euCountries = ["AT","BE","BG","CY","CZ","DK","EE","FI","FR","DE","GR","HU","IE","IT","LV","LT","LU","MT","NL","PL","PT","RO","SK","SI","ES","SE","GB"];
|
401
|
+
R.isCountryInEU = function(country) {
|
402
|
+
return $.inArray(country, euCountries) !== -1;
|
403
|
+
}
|
404
|
+
|
405
|
+
R.isVATNumberApplicable = function(buyerCountry, sellerCountry) {
|
406
|
+
if(!R.settings.VATPercent) return false;
|
407
|
+
|
408
|
+
if(!R.settings.country) {
|
409
|
+
R.raiseError('you must configure a country for VAT to work');
|
410
|
+
}
|
411
|
+
|
412
|
+
if(!R.isCountryInEU(R.settings.country)) {
|
413
|
+
R.raiseError('you cannot charge VAT outside of the EU');
|
414
|
+
}
|
415
|
+
|
416
|
+
// Outside of EU don't even show the number
|
417
|
+
if(!R.isCountryInEU(buyerCountry)) {
|
418
|
+
return false;
|
419
|
+
}
|
420
|
+
|
421
|
+
return true;
|
422
|
+
}
|
423
|
+
|
424
|
+
R.isVATChargeApplicable = function(buyerCountry, vatNumber) {
|
425
|
+
// We made it so the VAT Number is collectable in any case
|
426
|
+
// where it could be charged, so this is logically sound:
|
427
|
+
if(!R.isVATNumberApplicable(buyerCountry)) return false;
|
428
|
+
|
429
|
+
var sellerCountry = R.settings.country;
|
430
|
+
|
431
|
+
// 1) Outside EU never pays
|
432
|
+
// 2) Same country in EU always pays
|
433
|
+
// 3) Different countries in EU, pay only without vatNumber
|
434
|
+
return (sellerCountry == buyerCountry || !vatNumber);
|
435
|
+
};
|
436
|
+
|
437
|
+
R.flattenErrors = function(obj, attr) {
|
438
|
+
var arr = [];
|
439
|
+
|
440
|
+
var baseErrorKeys = ['base','account_id'];
|
441
|
+
|
442
|
+
var attr = attr || '';
|
443
|
+
|
444
|
+
if( typeof obj == 'string'
|
445
|
+
|| typeof obj == 'number'
|
446
|
+
|| typeof obj == 'boolean') {
|
447
|
+
|
448
|
+
/*
|
449
|
+
* erroneous code, commented out here in case similar logic is needed later
|
450
|
+
if($.inArray(baseErrorKeys, attr)) {
|
451
|
+
return [obj];
|
452
|
+
}
|
453
|
+
*/
|
454
|
+
|
455
|
+
return ['' + attr + ' ' + obj];
|
456
|
+
}
|
457
|
+
|
458
|
+
for(var k in obj) {
|
459
|
+
// console.log(k);
|
460
|
+
if(obj.hasOwnProperty(k)) {
|
461
|
+
// Inherit parent attribute names when property key
|
462
|
+
// is a numeric string; how we deal with arrays
|
463
|
+
attr = (parseInt(k).toString() == k) ? attr : k;
|
464
|
+
var children = R.flattenErrors(obj[k], attr);
|
465
|
+
for(var i=0, l=children.length; i < l; ++i) {
|
466
|
+
arr.push(children[i]);
|
467
|
+
}
|
468
|
+
}
|
469
|
+
}
|
470
|
+
|
471
|
+
return arr;
|
472
|
+
};
|
473
|
+
|
474
|
+
// Very small function, but defining for D.R.Y.ness
|
475
|
+
R.getToken = function(response) {
|
476
|
+
var token = response.token || 'INVALIDTOKEN';
|
477
|
+
return token;
|
478
|
+
}
|
479
|
+
|
480
|
+
// POST the results from Recurly to the merchant's webserver
|
481
|
+
R.postResult = function(url, originalResponse, options) {
|
482
|
+
var token = R.getToken(originalResponse);
|
483
|
+
|
484
|
+
var form = $('<form />').hide();
|
485
|
+
form.attr('action', url)
|
486
|
+
.attr('method', 'POST')
|
487
|
+
.attr('enctype', 'application/x-www-form-urlencoded');
|
488
|
+
|
489
|
+
$('<input type="hidden" />').attr({name: 'recurly_token', value: token}).appendTo(form);
|
490
|
+
|
491
|
+
$('body').append(form);
|
492
|
+
form.submit();
|
493
|
+
};
|
494
|
+
|
495
|
+
function jsonToSelect(obj) {
|
496
|
+
var $select = $('<select>');
|
497
|
+
|
498
|
+
for(var k in obj) {
|
499
|
+
if(obj.hasOwnProperty(k)) {
|
500
|
+
var v = obj[k];
|
501
|
+
$select.append('<option value='+k+'>'+v+'</option>');
|
502
|
+
}
|
503
|
+
}
|
504
|
+
|
505
|
+
return $select;
|
506
|
+
};
|
507
|
+
|
508
|
+
R.enforce = function(obj) {
|
509
|
+
return {
|
510
|
+
enforced: obj
|
511
|
+
, hidden: false
|
512
|
+
, hide: function() {
|
513
|
+
this.hidden = true;
|
514
|
+
return this;
|
515
|
+
}
|
516
|
+
};
|
517
|
+
};
|
518
|
+
|
519
|
+
|
520
|
+
function cc2lcu(obj) {
|
521
|
+
obj = obj || this;
|
522
|
+
|
523
|
+
if(typeof obj == 'string') {
|
524
|
+
return obj.replace(/([a-z])([A-Z])/g, function (a, l, u) {
|
525
|
+
return l+'_'+u;
|
526
|
+
}).toLowerCase();
|
527
|
+
}
|
528
|
+
else {
|
529
|
+
for(var k in obj) {
|
530
|
+
if(obj.hasOwnProperty(k)) {
|
531
|
+
|
532
|
+
}
|
533
|
+
}
|
534
|
+
}
|
535
|
+
}
|
536
|
+
|
537
|
+
|
538
|
+
R.ajax = function(options) {
|
539
|
+
options.data = $.extend({js_version: R.version}, options.data);
|
540
|
+
return $.ajax(options);
|
541
|
+
};
|
542
|
+
|
543
|
+
|
544
|
+
function errorDialog(message) {
|
545
|
+
$('body').append(R.dom.error_dialog);
|
546
|
+
}
|
547
|
+
|
548
|
+
|
549
|
+
//////////////////////////////////////////////////
|
550
|
+
// Compiled from src/js/validators.js
|
551
|
+
//////////////////////////////////////////////////
|
552
|
+
|
553
|
+
|
554
|
+
(R.isValidCC = function($input) {
|
555
|
+
var v = $input.val();
|
556
|
+
|
557
|
+
// Strip out all non digits
|
558
|
+
v = v.replace(/\D/g, "");
|
559
|
+
|
560
|
+
if(v == "") return false;
|
561
|
+
|
562
|
+
var nCheck = 0,
|
563
|
+
nDigit = 0,
|
564
|
+
bEven = false;
|
565
|
+
|
566
|
+
|
567
|
+
for (var n = v.length - 1; n >= 0; n--) {
|
568
|
+
var cDigit = v.charAt(n);
|
569
|
+
var nDigit = parseInt(cDigit, 10);
|
570
|
+
if (bEven) {
|
571
|
+
if ((nDigit *= 2) > 9)
|
572
|
+
nDigit -= 9;
|
573
|
+
}
|
574
|
+
nCheck += nDigit;
|
575
|
+
bEven = !bEven;
|
576
|
+
}
|
577
|
+
|
578
|
+
return (nCheck % 10) == 0;
|
579
|
+
}).defaultErrorKey = 'invalidCC';
|
580
|
+
|
581
|
+
(R.isValidEmail = function($input) {
|
582
|
+
var v = $input.val();
|
583
|
+
return /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(v);
|
584
|
+
}).defaultErrorKey = 'invalidEmail';
|
585
|
+
|
586
|
+
function wholeNumber(val) {
|
587
|
+
return /^[0-9]+$/.test(val);
|
588
|
+
}
|
589
|
+
|
590
|
+
(R.isValidCVV = function($input) {
|
591
|
+
var v = $input.val();
|
592
|
+
return (v.length == 3 || v.length == 4) && wholeNumber(v);
|
593
|
+
}).defaultErrorKey = 'invalidCVV';
|
594
|
+
|
595
|
+
(R.isNotEmpty = function($input) {
|
596
|
+
var v = $input.val();
|
597
|
+
if($input.is('select')) {
|
598
|
+
if(v == '-' || v == '--') return false;
|
599
|
+
}
|
600
|
+
return !!v;
|
601
|
+
}).defaultErrorKey = 'emptyField';
|
602
|
+
// State is required if its a dropdown, it is not required if it is an input box
|
603
|
+
(R.isNotEmptyState = function($input) {
|
604
|
+
var v = $input.val();
|
605
|
+
if($input.is('select')) {
|
606
|
+
if(v == '-' || v == '--') return false;
|
607
|
+
}
|
608
|
+
return true;
|
609
|
+
}).defaultErrorKey = 'emptyField';
|
610
|
+
|
611
|
+
(R.isChecked = function($input) {
|
612
|
+
return $input.is(':checked');
|
613
|
+
}).defaultErrorKey = 'acceptTOS';
|
614
|
+
|
615
|
+
(R.isValidQuantity = function($input) {
|
616
|
+
return /^[0-9]*$/.test($input.val());
|
617
|
+
}).defaultErrorKey = 'invalidQuantity';
|
618
|
+
|
619
|
+
|
620
|
+
|
621
|
+
//////////////////////////////////////////////////
|
622
|
+
// Compiled from src/js/plan.js
|
623
|
+
//////////////////////////////////////////////////
|
624
|
+
|
625
|
+
R.Plan = {
|
626
|
+
create: createObject
|
627
|
+
, fromJSON: function(json) {
|
628
|
+
var p = this.create();
|
629
|
+
|
630
|
+
p.name = json.name;
|
631
|
+
p.code = json.plan_code;
|
632
|
+
p.currency = json.currency;
|
633
|
+
p.cost = new R.Cost(json.unit_amount_in_cents);
|
634
|
+
|
635
|
+
p.displayQuantity = json.display_quantity;
|
636
|
+
|
637
|
+
p.interval = new R.TimePeriod(
|
638
|
+
json.plan_interval_length,
|
639
|
+
json.plan_interval_unit
|
640
|
+
);
|
641
|
+
|
642
|
+
if(json.trial_interval_length) {
|
643
|
+
p.trial = new R.TimePeriod(
|
644
|
+
json.trial_interval_length,
|
645
|
+
json.trial_interval_unit
|
646
|
+
);
|
647
|
+
}
|
648
|
+
|
649
|
+
if(json.setup_fee_in_cents) {
|
650
|
+
p.setupFee = new R.Cost(json.setup_fee_in_cents);
|
651
|
+
}
|
652
|
+
|
653
|
+
if (json.vat_percentage) {
|
654
|
+
R.settings.VATPercent = parseFloat(json.vat_percentage);
|
655
|
+
}
|
656
|
+
|
657
|
+
if (json.merchant_country) {
|
658
|
+
R.settings.country = json.merchant_country;
|
659
|
+
}
|
660
|
+
|
661
|
+
p.addOns = [];
|
662
|
+
if(json.add_ons) {
|
663
|
+
for(var l=json.add_ons.length, i=0; i < l; ++i) {
|
664
|
+
var a = json.add_ons[i];
|
665
|
+
p.addOns.push(R.AddOn.fromJSON(a));
|
666
|
+
}
|
667
|
+
}
|
668
|
+
|
669
|
+
return p;
|
670
|
+
}
|
671
|
+
, get: function(plan_code, currency, callback) {
|
672
|
+
$.ajax({
|
673
|
+
url: R.settings.baseURL+'plans/'+plan_code+"?currency="+currency,
|
674
|
+
// data: params,
|
675
|
+
dataType: "jsonp",
|
676
|
+
jsonp: "callback",
|
677
|
+
timeout: 10000,
|
678
|
+
success: function(data) {
|
679
|
+
var plan = R.Plan.fromJSON(data);
|
680
|
+
callback(plan);
|
681
|
+
}
|
682
|
+
});
|
683
|
+
}
|
684
|
+
, createSubscription: function() {
|
685
|
+
var s = createObject(R.Subscription);
|
686
|
+
s.plan = createObject(this);
|
687
|
+
s.plan.quantity = 1;
|
688
|
+
s.addOns = [];
|
689
|
+
return s;
|
690
|
+
}
|
691
|
+
};
|
692
|
+
|
693
|
+
R.AddOn = {
|
694
|
+
fromJSON: function(json) {
|
695
|
+
var a = createObject(R.AddOn);
|
696
|
+
a.name = json.name;
|
697
|
+
a.code = json.add_on_code;
|
698
|
+
a.cost = new R.Cost(json.default_unit_amount_in_cents);
|
699
|
+
a.displayQuantity = json.display_quantity;
|
700
|
+
return a;
|
701
|
+
}
|
702
|
+
|
703
|
+
, toJSON: function() {
|
704
|
+
return {
|
705
|
+
name: this.name
|
706
|
+
, add_on_code: this.code
|
707
|
+
, default_unit_amount_in_cents: this.default_unit_amount_in_cents
|
708
|
+
};
|
709
|
+
}
|
710
|
+
};
|
711
|
+
|
712
|
+
|
713
|
+
//////////////////////////////////////////////////
|
714
|
+
// Compiled from src/js/account.js
|
715
|
+
//////////////////////////////////////////////////
|
716
|
+
|
717
|
+
R.Account = {
|
718
|
+
create: createObject
|
719
|
+
, toJSON: function() {
|
720
|
+
return {
|
721
|
+
first_name: this.firstName
|
722
|
+
, last_name: this.lastName
|
723
|
+
, company_name: this.companyName
|
724
|
+
, account_code: this.code
|
725
|
+
, email: this.email
|
726
|
+
};
|
727
|
+
}
|
728
|
+
};
|
729
|
+
|
730
|
+
|
731
|
+
|
732
|
+
//////////////////////////////////////////////////
|
733
|
+
// Compiled from src/js/billing_info.js
|
734
|
+
//////////////////////////////////////////////////
|
735
|
+
|
736
|
+
R.BillingInfo = {
|
737
|
+
create: createObject
|
738
|
+
, toJSON: function() {
|
739
|
+
return {
|
740
|
+
first_name: this.firstName
|
741
|
+
, last_name: this.lastName
|
742
|
+
, month: this.month
|
743
|
+
, year: this.year
|
744
|
+
, number: this.number
|
745
|
+
, verification_value: this.cvv
|
746
|
+
, address1: this.address1
|
747
|
+
, address2: this.address2
|
748
|
+
, city: this.city
|
749
|
+
, state: this.state
|
750
|
+
, zip: this.zip
|
751
|
+
, country: this.country
|
752
|
+
, phone: this.phone
|
753
|
+
, vat_number: this.vatNumber
|
754
|
+
};
|
755
|
+
}
|
756
|
+
, save: function(options) {
|
757
|
+
var json = {
|
758
|
+
billing_info: this.toJSON()
|
759
|
+
, signature: options.signature
|
760
|
+
};
|
761
|
+
|
762
|
+
// Save first/last name on the account
|
763
|
+
// if not distinguished
|
764
|
+
if(!options.distinguishContactFromBillingInfo) {
|
765
|
+
json.account = {
|
766
|
+
account_code: options.accountCode
|
767
|
+
, first_name: this.firstName
|
768
|
+
, last_name: this.lastName
|
769
|
+
};
|
770
|
+
}
|
771
|
+
|
772
|
+
R.ajax({
|
773
|
+
url: R.settings.baseURL+'accounts/'+options.accountCode+'/billing_info/update'
|
774
|
+
, data: json
|
775
|
+
, dataType: 'jsonp'
|
776
|
+
, jsonp: 'callback'
|
777
|
+
, timeout: 60000
|
778
|
+
, success: function(data) {
|
779
|
+
if(data.success && options.success) {
|
780
|
+
options.success(data.success);
|
781
|
+
}
|
782
|
+
else if(data.errors && options.error) {
|
783
|
+
options.error( R.flattenErrors(data.errors) );
|
784
|
+
}
|
785
|
+
}
|
786
|
+
, error: function() {
|
787
|
+
if(options.error) {
|
788
|
+
options.error(['Unknown error processing transaction. Please try again later.']);
|
789
|
+
}
|
790
|
+
}
|
791
|
+
, complete: options.complete || $.noop
|
792
|
+
});
|
793
|
+
}
|
794
|
+
};
|
795
|
+
|
796
|
+
|
797
|
+
|
798
|
+
//////////////////////////////////////////////////
|
799
|
+
// Compiled from src/js/subscription.js
|
800
|
+
//////////////////////////////////////////////////
|
801
|
+
|
802
|
+
// Base Subscription prototype
|
803
|
+
R.Subscription = {
|
804
|
+
create: createObject
|
805
|
+
, plan: R.Plan
|
806
|
+
, addOns: []
|
807
|
+
|
808
|
+
, calculateTotals: function() {
|
809
|
+
var totals = {
|
810
|
+
stages: {}
|
811
|
+
};
|
812
|
+
|
813
|
+
// PLAN
|
814
|
+
totals.plan = this.plan.cost.mult(this.plan.quantity);
|
815
|
+
|
816
|
+
// ADD-ONS
|
817
|
+
totals.allAddOns = new R.Cost(0);
|
818
|
+
totals.addOns = {};
|
819
|
+
for(var l=this.addOns.length, i=0; i < l; ++i) {
|
820
|
+
var a = this.addOns[i],
|
821
|
+
c = a.cost.mult(a.quantity);
|
822
|
+
totals.addOns[a.code] = c;
|
823
|
+
totals.allAddOns = totals.allAddOns.add(c);
|
824
|
+
}
|
825
|
+
|
826
|
+
totals.stages.recurring = totals.plan.add(totals.allAddOns);
|
827
|
+
|
828
|
+
totals.stages.now = totals.plan.add(totals.allAddOns);
|
829
|
+
|
830
|
+
// FREE TRIAL
|
831
|
+
if(this.plan.trial) {
|
832
|
+
totals.stages.now = R.Cost.FREE;
|
833
|
+
}
|
834
|
+
|
835
|
+
// COUPON
|
836
|
+
if(this.coupon) {
|
837
|
+
var beforeDiscount = totals.stages.now;
|
838
|
+
var afterDiscount = totals.stages.now.discount(this.coupon);
|
839
|
+
totals.coupon = afterDiscount.sub(beforeDiscount);
|
840
|
+
totals.stages.now = afterDiscount;
|
841
|
+
}
|
842
|
+
|
843
|
+
// SETUP FEE
|
844
|
+
if(this.plan.setupFee) {
|
845
|
+
totals.stages.now = totals.stages.now.add(this.plan.setupFee);
|
846
|
+
}
|
847
|
+
|
848
|
+
// VAT
|
849
|
+
if(this.billingInfo && R.isVATChargeApplicable(this.billingInfo.country,this.billingInfo.vatNumber)) {
|
850
|
+
totals.vat = totals.stages.now.mult( (R.settings.VATPercent/100) );
|
851
|
+
totals.stages.now = totals.stages.now.add(totals.vat);
|
852
|
+
}
|
853
|
+
|
854
|
+
return totals;
|
855
|
+
}
|
856
|
+
, redeemAddOn: function(addOn) {
|
857
|
+
var redemption = addOn.createRedemption();
|
858
|
+
this.addOns.push(redemption);
|
859
|
+
return redemption;
|
860
|
+
}
|
861
|
+
|
862
|
+
, removeAddOn: function(code) {
|
863
|
+
for(var a=this.addOns, l=a.length, i=0; i < l; ++i) {
|
864
|
+
if(a[i].code == code) {
|
865
|
+
return a.splice(i,1);
|
866
|
+
}
|
867
|
+
}
|
868
|
+
}
|
869
|
+
|
870
|
+
, findAddOnByCode: function(code) {
|
871
|
+
for(var l=this.addOns.length, i=0; i < l; ++i) {
|
872
|
+
if(this.addOns[i].code == code) {
|
873
|
+
return this.addOns[i];
|
874
|
+
}
|
875
|
+
}
|
876
|
+
return false;
|
877
|
+
}
|
878
|
+
|
879
|
+
, toJSON: function() {
|
880
|
+
var json = {
|
881
|
+
plan_code: this.plan.code
|
882
|
+
, quantity: this.plan.quantity
|
883
|
+
, currency: this.plan.currency
|
884
|
+
, coupon_code: this.coupon ? this.coupon.code : undefined
|
885
|
+
, add_ons: []
|
886
|
+
};
|
887
|
+
|
888
|
+
for(var i=0, l=this.addOns.length, a=json.add_ons, b=this.addOns; i < l; ++i) {
|
889
|
+
a.push({
|
890
|
+
add_on_code: b[i].code
|
891
|
+
, quantity: b[i].quantity
|
892
|
+
});
|
893
|
+
}
|
894
|
+
|
895
|
+
return json;
|
896
|
+
}
|
897
|
+
|
898
|
+
, save: function(options) {
|
899
|
+
var json = {
|
900
|
+
subscription: this.toJSON()
|
901
|
+
, account: this.account.toJSON()
|
902
|
+
, billing_info: this.billingInfo.toJSON()
|
903
|
+
, signature: options.signature
|
904
|
+
};
|
905
|
+
|
906
|
+
R.ajax({
|
907
|
+
url: R.settings.baseURL+'subscribe',
|
908
|
+
data: json,
|
909
|
+
dataType: "jsonp",
|
910
|
+
jsonp: "callback",
|
911
|
+
timeout: 60000,
|
912
|
+
success: function(data) {
|
913
|
+
if(data.success && options.success) {
|
914
|
+
options.success(data.success);
|
915
|
+
}
|
916
|
+
else if(data.errors && options.error) {
|
917
|
+
var errorCode = data.errors.error_code;
|
918
|
+
delete data.errors.error_code;
|
919
|
+
options.error( R.flattenErrors(data.errors), errorCode );
|
920
|
+
}
|
921
|
+
},
|
922
|
+
error: function() {
|
923
|
+
if(options.error) {
|
924
|
+
options.error(['Unknown error processing transaction. Please try again later.']);
|
925
|
+
}
|
926
|
+
},
|
927
|
+
complete: options.complete
|
928
|
+
});
|
929
|
+
|
930
|
+
}
|
931
|
+
};
|
932
|
+
|
933
|
+
R.AddOn.createRedemption = function(qty) {
|
934
|
+
var r = createObject(this);
|
935
|
+
r.quantity = qty || 1;
|
936
|
+
return r;
|
937
|
+
};
|
938
|
+
|
939
|
+
R.Coupon = {
|
940
|
+
fromJSON: function(json) {
|
941
|
+
var c = createObject(R.Coupon);
|
942
|
+
|
943
|
+
if(json.discount_in_cents)
|
944
|
+
c.discountCost = new R.Cost(-json.discount_in_cents);
|
945
|
+
else if(json.discount_percent)
|
946
|
+
c.discountRatio = json.discount_percent/100;
|
947
|
+
|
948
|
+
c.description = json.description;
|
949
|
+
|
950
|
+
return c;
|
951
|
+
}
|
952
|
+
|
953
|
+
, toJSON: function() {
|
954
|
+
}
|
955
|
+
};
|
956
|
+
|
957
|
+
R.Cost.prototype.discount = function(coupon){
|
958
|
+
if(coupon.discountCost)
|
959
|
+
return this.add(coupon.discountCost);
|
960
|
+
|
961
|
+
var ret = this.sub( this.mult(coupon.discountRatio) );
|
962
|
+
if(ret.cents() < 0) {
|
963
|
+
return R.Cost.FREE;
|
964
|
+
}
|
965
|
+
|
966
|
+
return ret;
|
967
|
+
};
|
968
|
+
|
969
|
+
R.Subscription.getCoupon = function(couponCode, successCallback, errorCallback) {
|
970
|
+
|
971
|
+
if(!R.settings.baseURL) { R.raiseError('Company subdomain not configured'); }
|
972
|
+
|
973
|
+
var couponCurrencyQuery = (R.settings.currency !== undefined ? '?currency='+R.settings.currency : '');
|
974
|
+
|
975
|
+
return R.ajax({
|
976
|
+
url: R.settings.baseURL+'plans/'+this.plan.code+'/coupons/'+couponCode+couponCurrencyQuery,
|
977
|
+
// data: params,
|
978
|
+
dataType: "jsonp",
|
979
|
+
jsonp: "callback",
|
980
|
+
timeout: 10000,
|
981
|
+
success: function(data) {
|
982
|
+
if(data.valid) {
|
983
|
+
var coupon = R.Coupon.fromJSON(data);
|
984
|
+
coupon.code = couponCode;
|
985
|
+
successCallback(coupon);
|
986
|
+
}
|
987
|
+
else {
|
988
|
+
errorCallback();
|
989
|
+
}
|
990
|
+
},
|
991
|
+
error: function() {
|
992
|
+
errorCallback();
|
993
|
+
}
|
994
|
+
});
|
995
|
+
};
|
996
|
+
|
997
|
+
|
998
|
+
|
999
|
+
//////////////////////////////////////////////////
|
1000
|
+
// Compiled from src/js/transaction.js
|
1001
|
+
//////////////////////////////////////////////////
|
1002
|
+
|
1003
|
+
|
1004
|
+
R.Transaction = {
|
1005
|
+
// Note - No toJSON function for this object, all parameters must be signed.
|
1006
|
+
create: createObject
|
1007
|
+
, save: function(options) {
|
1008
|
+
var json = {
|
1009
|
+
account: this.account ? this.account.toJSON() : undefined
|
1010
|
+
, billing_info: this.billingInfo.toJSON()
|
1011
|
+
, signature: options.signature
|
1012
|
+
};
|
1013
|
+
|
1014
|
+
R.ajax({
|
1015
|
+
url: R.settings.baseURL+'transactions/create'
|
1016
|
+
, data: json
|
1017
|
+
, dataType: 'jsonp'
|
1018
|
+
, jsonp: 'callback'
|
1019
|
+
, timeout: 60000
|
1020
|
+
, success: function(data) {
|
1021
|
+
if(data.success && options.success) {
|
1022
|
+
options.success(data.success);
|
1023
|
+
}
|
1024
|
+
else if(data.errors && options.error) {
|
1025
|
+
options.error( R.flattenErrors(data.errors) );
|
1026
|
+
}
|
1027
|
+
}
|
1028
|
+
, error: function() {
|
1029
|
+
if(options.error) {
|
1030
|
+
options.error(['Unknown error processing transaction. Please try again later.']);
|
1031
|
+
}
|
1032
|
+
}
|
1033
|
+
, complete: options.complete || $.noop
|
1034
|
+
});
|
1035
|
+
}
|
1036
|
+
};
|
1037
|
+
|
1038
|
+
|
1039
|
+
|
1040
|
+
|
1041
|
+
//////////////////////////////////////////////////
|
1042
|
+
// Compiled from src/js/ui.js
|
1043
|
+
//////////////////////////////////////////////////
|
1044
|
+
|
1045
|
+
R.UserError = {};
|
1046
|
+
|
1047
|
+
function raiseUserError(validation, elem) {
|
1048
|
+
var e = createObject(R.UserError);
|
1049
|
+
e.validation = validation;
|
1050
|
+
e.element = elem;
|
1051
|
+
throw e;
|
1052
|
+
}
|
1053
|
+
|
1054
|
+
function invalidMode(e) {
|
1055
|
+
var $input = e.element;
|
1056
|
+
var message = R.locale.errors[e.validation.errorKey];
|
1057
|
+
var validator = e.validation.validator;
|
1058
|
+
|
1059
|
+
var $e = $('<div class="error">');
|
1060
|
+
$e.text(message);
|
1061
|
+
$e.appendTo($input.parent());
|
1062
|
+
|
1063
|
+
$input.addClass('invalid');
|
1064
|
+
$input.bind('change keyup', function handler(e) {
|
1065
|
+
|
1066
|
+
if(validator($input)) {
|
1067
|
+
$input.removeClass('invalid');
|
1068
|
+
$e.remove();
|
1069
|
+
$input.unbind(e);
|
1070
|
+
}
|
1071
|
+
});
|
1072
|
+
}
|
1073
|
+
|
1074
|
+
function validationGroup(pull,success) {
|
1075
|
+
var anyErrors = false;
|
1076
|
+
var puller = {
|
1077
|
+
field: function($form, fieldSel, validations) {
|
1078
|
+
validations = Array.prototype.slice.call(arguments, 2);
|
1079
|
+
return pullField($form, fieldSel, validations, function onError(error) {
|
1080
|
+
if(!anyErrors) error.element.focus();
|
1081
|
+
invalidMode(error);
|
1082
|
+
anyErrors = true;
|
1083
|
+
|
1084
|
+
if(R.settings.oneErrorPerForm)
|
1085
|
+
throw {stopPulling:true};
|
1086
|
+
});
|
1087
|
+
}
|
1088
|
+
};
|
1089
|
+
|
1090
|
+
|
1091
|
+
try {
|
1092
|
+
pull(puller);
|
1093
|
+
}
|
1094
|
+
catch(e) {
|
1095
|
+
if(!e.stopPulling) {
|
1096
|
+
throw e;
|
1097
|
+
}
|
1098
|
+
}
|
1099
|
+
|
1100
|
+
if(!anyErrors) {
|
1101
|
+
success();
|
1102
|
+
}
|
1103
|
+
}
|
1104
|
+
|
1105
|
+
|
1106
|
+
function pullField($form, fieldSel, validations, onError) {
|
1107
|
+
// Try text input
|
1108
|
+
var $input = $form.find(fieldSel + ' input');
|
1109
|
+
|
1110
|
+
// Try as select
|
1111
|
+
if($input.length == 0) {
|
1112
|
+
$input = $form.find(fieldSel + ' select');
|
1113
|
+
}
|
1114
|
+
|
1115
|
+
// Treat nonexistence as removed deliberately
|
1116
|
+
if($input.length == 0) return undefined;
|
1117
|
+
|
1118
|
+
var val = $input.val();
|
1119
|
+
|
1120
|
+
for(var i=0,l=validations.length; i < l; ++i) {
|
1121
|
+
var v = validations[i];
|
1122
|
+
|
1123
|
+
if(!v.validator($input)) {
|
1124
|
+
onError({
|
1125
|
+
element: $input
|
1126
|
+
, validation: v
|
1127
|
+
});
|
1128
|
+
|
1129
|
+
if(R.settings.oneErrorPerField)
|
1130
|
+
break;
|
1131
|
+
}
|
1132
|
+
}
|
1133
|
+
|
1134
|
+
return val;
|
1135
|
+
}
|
1136
|
+
|
1137
|
+
|
1138
|
+
// Make a 'validation' from validator / errorKey
|
1139
|
+
function V(v,k) {
|
1140
|
+
return {
|
1141
|
+
validator: v,
|
1142
|
+
errorKey: k || v.defaultErrorKey
|
1143
|
+
};
|
1144
|
+
}
|
1145
|
+
|
1146
|
+
|
1147
|
+
// == SERVER ERROR UI METHODS
|
1148
|
+
|
1149
|
+
function clearServerErrors($form) {
|
1150
|
+
var $serverErrors = $form.find('.server_errors');
|
1151
|
+
$serverErrors.removeClass('any').addClass('none');
|
1152
|
+
$serverErrors.empty();
|
1153
|
+
}
|
1154
|
+
|
1155
|
+
function displayServerErrors($form, errors) {
|
1156
|
+
var $serverErrors = $form.find('.server_errors');
|
1157
|
+
clearServerErrors($form);
|
1158
|
+
|
1159
|
+
var l = errors.length;
|
1160
|
+
if(l) {
|
1161
|
+
$serverErrors.removeClass('none').addClass('any');
|
1162
|
+
for(var i=0; i < l; ++i) {
|
1163
|
+
var $e = $('<div class="error">');
|
1164
|
+
$e.text(errors[i]);
|
1165
|
+
$serverErrors.append($e);
|
1166
|
+
}
|
1167
|
+
}
|
1168
|
+
}
|
1169
|
+
|
1170
|
+
|
1171
|
+
var preFillMap = {
|
1172
|
+
account: {
|
1173
|
+
firstName: '.contact_info > .full_name > .first_name > input'
|
1174
|
+
, lastName: '.contact_info > .full_name > .last_name > input'
|
1175
|
+
, email: '.contact_info > .email > input'
|
1176
|
+
, phone: '.contact_info > .phone > input'
|
1177
|
+
, companyName: '.contact_info > .company_name > input'
|
1178
|
+
}
|
1179
|
+
, billingInfo: {
|
1180
|
+
firstName: '.billing_info > .credit_card > .first_name > input'
|
1181
|
+
, lastName: '.billing_info > .credit_card > .last_name > input'
|
1182
|
+
, address1: '.billing_info > .address > .address1 > input'
|
1183
|
+
, address2: '.billing_info > .address > .address2 > input'
|
1184
|
+
, country: '.billing_info > .address > .country > select'
|
1185
|
+
, city: '.billing_info > .address > .city > input'
|
1186
|
+
, state: '.billing_info > .address > .state_zip > .state > input'
|
1187
|
+
, zip: '.billing_info > .address > .state_zip > .zip > input'
|
1188
|
+
, vatNumber: '.billing_info > .vat_number > input'
|
1189
|
+
|
1190
|
+
, cardNumber: '.billing_info .card_number > input'
|
1191
|
+
, CVV: '.billing_info .cvv > input'
|
1192
|
+
}
|
1193
|
+
, subscription: {
|
1194
|
+
couponCode: '.subscription > .coupon > .coupon_code > input'
|
1195
|
+
}
|
1196
|
+
};
|
1197
|
+
|
1198
|
+
function preFillValues($form, options, mapObject) {
|
1199
|
+
|
1200
|
+
(function recurse(preFill,mapObject,keypath) {
|
1201
|
+
|
1202
|
+
if(!preFill) return;
|
1203
|
+
|
1204
|
+
for(var k in preFill) {
|
1205
|
+
if(preFill.hasOwnProperty(k) && mapObject.hasOwnProperty(k)) {
|
1206
|
+
|
1207
|
+
var v = preFill[k];
|
1208
|
+
var selectorOrNested = mapObject[k];
|
1209
|
+
var lcuk = cc2lcu(k);
|
1210
|
+
var keypath2 = keypath ? (keypath+'.'+lcuk) : lcuk;
|
1211
|
+
|
1212
|
+
|
1213
|
+
// jquery selector
|
1214
|
+
if(typeof selectorOrNested == 'string') {
|
1215
|
+
|
1216
|
+
var $input = $form.find(selectorOrNested);
|
1217
|
+
$input.val(v).change();
|
1218
|
+
}
|
1219
|
+
// nested mapping
|
1220
|
+
else if(typeof selectorOrNested == 'object') {
|
1221
|
+
recurse(v, selectorOrNested, keypath2);
|
1222
|
+
}
|
1223
|
+
}
|
1224
|
+
}
|
1225
|
+
})(options,mapObject);
|
1226
|
+
}
|
1227
|
+
|
1228
|
+
|
1229
|
+
function initCommonForm($form, options) {
|
1230
|
+
|
1231
|
+
if(!options.collectPhone) {
|
1232
|
+
$form.find('.phone').remove();
|
1233
|
+
}
|
1234
|
+
|
1235
|
+
if(!options.collectCompany) {
|
1236
|
+
$form.find('.company_name').remove();
|
1237
|
+
}
|
1238
|
+
|
1239
|
+
$form.delegate('.placeholder', 'click', function() {
|
1240
|
+
var $label = $(this);
|
1241
|
+
var $li = $(this).parent();
|
1242
|
+
$li.find('input').focus();
|
1243
|
+
});
|
1244
|
+
|
1245
|
+
$form.delegate('input', 'change keyup', function() {
|
1246
|
+
var $input = $(this);
|
1247
|
+
var $li = $(this).parent();
|
1248
|
+
|
1249
|
+
if($input.val().length > 0) {
|
1250
|
+
$li.find('.placeholder').hide();
|
1251
|
+
}
|
1252
|
+
else {
|
1253
|
+
$li.find('.placeholder').show();
|
1254
|
+
}
|
1255
|
+
});
|
1256
|
+
|
1257
|
+
|
1258
|
+
$form.delegate('input', 'focus', function() {
|
1259
|
+
$(this).parent().addClass('focus');
|
1260
|
+
});
|
1261
|
+
|
1262
|
+
$form.delegate('input', 'blur', function() {
|
1263
|
+
$(this).parent().removeClass('focus');
|
1264
|
+
});
|
1265
|
+
|
1266
|
+
$form.delegate('input', 'keydown', function(e) {
|
1267
|
+
if(e.keyCode >= 48 && e.keyCode <= 90) {
|
1268
|
+
$(this).parent().find('.placeholder').hide();
|
1269
|
+
}
|
1270
|
+
});
|
1271
|
+
|
1272
|
+
preFillValues($form, options, preFillMap);
|
1273
|
+
}
|
1274
|
+
|
1275
|
+
function initContactInfoForm($form, options) {
|
1276
|
+
|
1277
|
+
// == FIRSTNAME / LASTNAME REDUNDANCY
|
1278
|
+
if(options.distinguishContactFromBillingInfo) {
|
1279
|
+
var $contactFirstName = $form.find('.contact_info .first_name input');
|
1280
|
+
var $contactLastName = $form.find('.contact_info .last_name input');
|
1281
|
+
var prevFirstName = $contactFirstName.val();
|
1282
|
+
var prevLastName = $contactLastName.val();
|
1283
|
+
$form.find('.contact_info .first_name input').change(function() {
|
1284
|
+
var $billingFirstName = $form.find('.billing_info .first_name input');
|
1285
|
+
if($billingFirstName.val() == prevFirstName) {
|
1286
|
+
$billingFirstName.val( $(this).val() ).change();
|
1287
|
+
}
|
1288
|
+
prevFirstName = $contactFirstName.val();
|
1289
|
+
});
|
1290
|
+
$form.find('.contact_info .last_name input').change(function() {
|
1291
|
+
var $billingLastName = $form.find('.billing_info .last_name input');
|
1292
|
+
if($billingLastName.val() == prevLastName) {
|
1293
|
+
$billingLastName.val( $(this).val() ).change();
|
1294
|
+
}
|
1295
|
+
prevLastName = $contactLastName.val();
|
1296
|
+
});
|
1297
|
+
|
1298
|
+
}
|
1299
|
+
else {
|
1300
|
+
$form.find('.billing_info .first_name, .billing_info .last_name').remove();
|
1301
|
+
}
|
1302
|
+
|
1303
|
+
}
|
1304
|
+
|
1305
|
+
function initBillingInfoForm($form, options) {
|
1306
|
+
|
1307
|
+
// == SWAPPING OF STATE INPUT WITH SELECT
|
1308
|
+
var $countrySelect = $form.find('.country select');
|
1309
|
+
var $state = $form.find('.state');
|
1310
|
+
var $stateInput = $state.find('input');
|
1311
|
+
var $manualStateInput = $state.children();
|
1312
|
+
var $savedStateSelect = {};
|
1313
|
+
var knownStates = R.states;
|
1314
|
+
var prevCountry = $countrySelect.val();
|
1315
|
+
|
1316
|
+
function matchKnownStateWithInput(country, stateStr) {
|
1317
|
+
var ref = knownStates[country];
|
1318
|
+
// Normalize stateStr
|
1319
|
+
stateStr = $.trim(stateStr.toUpperCase());
|
1320
|
+
|
1321
|
+
// Is a state code
|
1322
|
+
if(ref.hasOwnProperty(stateStr)) {
|
1323
|
+
return stateStr;
|
1324
|
+
}
|
1325
|
+
|
1326
|
+
// Search through state names to find the code
|
1327
|
+
for(var k in ref) {
|
1328
|
+
if(ref.hasOwnProperty(k)) {
|
1329
|
+
var v = ref[k];
|
1330
|
+
if(stateStr == v.toUpperCase()) {
|
1331
|
+
return k;
|
1332
|
+
}
|
1333
|
+
}
|
1334
|
+
}
|
1335
|
+
|
1336
|
+
return false;
|
1337
|
+
}
|
1338
|
+
|
1339
|
+
function swapStateSelectOrInput(country, state) {
|
1340
|
+
var inSelectMode = $state.hasClass('select_mode');
|
1341
|
+
if(country == 'US' || country == 'CA') {
|
1342
|
+
if(!inSelectMode || prevCountry != country) {
|
1343
|
+
var manualVal = $state.find('input').val();
|
1344
|
+
|
1345
|
+
if(manualVal != undefined && manualVal != '') {
|
1346
|
+
state = matchKnownStateWithInput(country, manualVal);
|
1347
|
+
|
1348
|
+
if(!state) return false;
|
1349
|
+
}
|
1350
|
+
|
1351
|
+
// Change to select mode
|
1352
|
+
$state.addClass('select_mode');
|
1353
|
+
// Detatch manual-input children from field
|
1354
|
+
$state.children().detach();
|
1355
|
+
// Instantiate HTML DOM only now, and cache it
|
1356
|
+
$savedStateSelect[country] = $savedStateSelect[country] || jsonToSelect(knownStates[country]);
|
1357
|
+
// Insert select into field
|
1358
|
+
$state.append($savedStateSelect[country]);
|
1359
|
+
// Set known state, if provided
|
1360
|
+
if(state) $state.find('select').val(state);
|
1361
|
+
}
|
1362
|
+
|
1363
|
+
}
|
1364
|
+
else if(inSelectMode) {
|
1365
|
+
// Restore original manual state input field
|
1366
|
+
$state.empty().append($manualStateInput).removeClass('select_mode');
|
1367
|
+
}
|
1368
|
+
}
|
1369
|
+
|
1370
|
+
$stateInput.bind('change keyup', function() {
|
1371
|
+
swapStateSelectOrInput(prevCountry);
|
1372
|
+
});
|
1373
|
+
|
1374
|
+
$countrySelect.change(function() {
|
1375
|
+
var country = $(this).val();
|
1376
|
+
swapStateSelectOrInput(country);
|
1377
|
+
prevCountry = country;
|
1378
|
+
});
|
1379
|
+
|
1380
|
+
// == GEOIP
|
1381
|
+
function niceSet($jq, v) {
|
1382
|
+
var cur = $jq.val();
|
1383
|
+
if(!v || v == '') return false;
|
1384
|
+
if(cur && cur != '' && cur != '-') return false;
|
1385
|
+
|
1386
|
+
return $jq.val(v);
|
1387
|
+
}
|
1388
|
+
|
1389
|
+
if(options.enableGeoIP) {
|
1390
|
+
$.ajax({
|
1391
|
+
url: R.settings.baseURL+'location',
|
1392
|
+
dataType: "jsonp",
|
1393
|
+
jsonp: "callback",
|
1394
|
+
success: function(data) {
|
1395
|
+
if(data.country) {
|
1396
|
+
niceSet($countrySelect, data.country);
|
1397
|
+
swapStateSelectOrInput(data.country, data.state);
|
1398
|
+
}
|
1399
|
+
}
|
1400
|
+
});
|
1401
|
+
}
|
1402
|
+
else {
|
1403
|
+
// == DEFAULT BUYER TO SELLER COUNTRY
|
1404
|
+
if(R.settings.country) {
|
1405
|
+
var $countryOpt = $form.find('.country option[value='+R.settings.country+']');
|
1406
|
+
if($countryOpt.length) {
|
1407
|
+
$countryOpt.attr('selected', true).change();
|
1408
|
+
}
|
1409
|
+
}
|
1410
|
+
}
|
1411
|
+
|
1412
|
+
var now = new Date();
|
1413
|
+
var year = now.getFullYear();
|
1414
|
+
var month = now.getMonth();
|
1415
|
+
var $yearSelect = $form.find('.year select');
|
1416
|
+
var $monthSelect = $form.find('.month select');
|
1417
|
+
|
1418
|
+
// == GENERATE YEAR SELECT OPTIONS
|
1419
|
+
for(var i=year; i <= year+10; ++i) {
|
1420
|
+
var $yearOpt = $('<option name="'+i+'">'+i+'</option>');
|
1421
|
+
$yearOpt.appendTo($yearSelect);
|
1422
|
+
}
|
1423
|
+
$yearSelect.val(year+1);
|
1424
|
+
|
1425
|
+
|
1426
|
+
// == DISABLE INVALID MONTHS, SELECT CURRENT
|
1427
|
+
function updateMonths() {
|
1428
|
+
if($yearSelect.val() == year) {
|
1429
|
+
var foundSelected = false; // If we've set a selection yet
|
1430
|
+
|
1431
|
+
if($monthSelect.val() > month) {
|
1432
|
+
// We know the current selection is already valid
|
1433
|
+
foundSelected = true;
|
1434
|
+
}
|
1435
|
+
|
1436
|
+
$monthSelect.find('option').each(function(){
|
1437
|
+
if($(this).val() <= month) {
|
1438
|
+
$(this).attr('disabled', true);
|
1439
|
+
}
|
1440
|
+
else {
|
1441
|
+
$(this).removeAttr('disabled');
|
1442
|
+
|
1443
|
+
if(!foundSelected) {
|
1444
|
+
$(this).attr('selected', true);
|
1445
|
+
foundSelected = true;
|
1446
|
+
}
|
1447
|
+
}
|
1448
|
+
});
|
1449
|
+
}
|
1450
|
+
else {
|
1451
|
+
$monthSelect.find('option').removeAttr('disabled');
|
1452
|
+
}
|
1453
|
+
};
|
1454
|
+
updateMonths();
|
1455
|
+
$yearSelect.change(updateMonths);
|
1456
|
+
|
1457
|
+
|
1458
|
+
// == HIDE UNNECESSARY ADDRESS FIELDS
|
1459
|
+
|
1460
|
+
if(options.addressRequirement == 'none') {
|
1461
|
+
$form.find('.address').remove();
|
1462
|
+
}
|
1463
|
+
else if(options.addressRequirement == 'zip') {
|
1464
|
+
$form.find('.address').addClass('only_zip');
|
1465
|
+
$form.find('.address1, .address2, .city, .state').remove();
|
1466
|
+
|
1467
|
+
// Only remove country if no VAT support
|
1468
|
+
if(!R.settings.VATPercent) {
|
1469
|
+
$form.find('.country').remove();
|
1470
|
+
}
|
1471
|
+
}
|
1472
|
+
else if(options.addressRequirement == 'zipstreet') {
|
1473
|
+
$form.find('.address').addClass('only_zipstreet');
|
1474
|
+
$form.find('.city, .state').remove();
|
1475
|
+
|
1476
|
+
// Only remove country if no VAT support
|
1477
|
+
if(!R.settings.VATPercent) {
|
1478
|
+
$form.find('.country').remove();
|
1479
|
+
}
|
1480
|
+
}
|
1481
|
+
else if(options.addressRequirement == 'full') {
|
1482
|
+
$form.find('.address').addClass('full');
|
1483
|
+
}
|
1484
|
+
// == BUILD ACCEPTED CARDS DOM
|
1485
|
+
var $acceptedCards = $form.find('.accepted_cards');
|
1486
|
+
|
1487
|
+
if(options.acceptedCards) {
|
1488
|
+
var a = options.acceptedCards
|
1489
|
+
, l = a.length;
|
1490
|
+
|
1491
|
+
for(var i=0; i < l; ++i) {
|
1492
|
+
var cardId = a[i];
|
1493
|
+
var $card = $('<div class="card '+cardId+'">');
|
1494
|
+
var card = R.knownCards[cardId];
|
1495
|
+
if(card && card.name) {
|
1496
|
+
$card.text(card.name);
|
1497
|
+
}
|
1498
|
+
$acceptedCards.append($card);
|
1499
|
+
}
|
1500
|
+
}
|
1501
|
+
|
1502
|
+
// == SHOW/HIDE CARD TYPES
|
1503
|
+
$form.find('.card_number input').bind('change keyup', function() {
|
1504
|
+
var type = R.detectCardType( $(this).val() );
|
1505
|
+
if(type) {
|
1506
|
+
$acceptedCards.find('.card').each(function(){
|
1507
|
+
$(this).toggleClass('match', $(this).hasClass(type));
|
1508
|
+
$(this).toggleClass('no_match', !$(this).hasClass(type));
|
1509
|
+
});
|
1510
|
+
}
|
1511
|
+
else {
|
1512
|
+
$acceptedCards.find('.card').removeClass('match no_match');
|
1513
|
+
}
|
1514
|
+
});
|
1515
|
+
}
|
1516
|
+
|
1517
|
+
|
1518
|
+
function pullAccountFields($form, account, options, pull) {
|
1519
|
+
account.firstName = pull.field($form, '.contact_info .first_name', V(R.isNotEmpty));
|
1520
|
+
account.lastName = pull.field($form, '.contact_info .last_name', V(R.isNotEmpty));
|
1521
|
+
account.companyName = pull.field($form, '.contact_info .company_name');
|
1522
|
+
account.email = pull.field($form, '.email', V(R.isNotEmpty), V(R.isValidEmail));
|
1523
|
+
account.code = options.accountCode;
|
1524
|
+
}
|
1525
|
+
|
1526
|
+
|
1527
|
+
function pullBillingInfoFields($form, billingInfo, options, pull) {
|
1528
|
+
billingInfo.firstName = pull.field($form, '.billing_info .first_name', V(R.isNotEmpty));
|
1529
|
+
billingInfo.lastName = pull.field($form, '.billing_info .last_name', V(R.isNotEmpty));
|
1530
|
+
billingInfo.number = pull.field($form, '.card_number', V(R.isNotEmpty), V(R.isValidCC));
|
1531
|
+
billingInfo.cvv = pull.field($form, '.cvv', V(R.isNotEmpty), V(R.isValidCVV));
|
1532
|
+
|
1533
|
+
billingInfo.month = pull.field($form, '.month');
|
1534
|
+
billingInfo.year = pull.field($form, '.year');
|
1535
|
+
|
1536
|
+
billingInfo.phone = pull.field($form, '.phone');
|
1537
|
+
billingInfo.address1 = pull.field($form, '.address1', V(R.isNotEmpty));
|
1538
|
+
billingInfo.address2 = pull.field($form, '.address2');
|
1539
|
+
billingInfo.city = pull.field($form, '.city', V(R.isNotEmpty));
|
1540
|
+
billingInfo.state = pull.field($form, '.state', V(R.isNotEmptyState));
|
1541
|
+
billingInfo.zip = pull.field($form, '.zip', V(R.isNotEmpty));
|
1542
|
+
billingInfo.country = pull.field($form, '.country', V(R.isNotEmpty));
|
1543
|
+
}
|
1544
|
+
|
1545
|
+
|
1546
|
+
function pullPlanQuantity($form, plan, options, pull) {
|
1547
|
+
var qty = pull.field($form, '.plan .quantity', V(R.isValidQuantity));
|
1548
|
+
// An empty quantity field indicates 1
|
1549
|
+
plan.quantity = qty || 1;
|
1550
|
+
}
|
1551
|
+
|
1552
|
+
|
1553
|
+
function verifyTOSChecked($form, pull) {
|
1554
|
+
pull.field($form, '.accept_tos', V(R.isChecked));
|
1555
|
+
}
|
1556
|
+
|
1557
|
+
|
1558
|
+
R.buildBillingInfoUpdateForm = function(options) {
|
1559
|
+
var defaults = {
|
1560
|
+
addressRequirement: 'full'
|
1561
|
+
, distinguishContactFromBillingInfo: true
|
1562
|
+
};
|
1563
|
+
|
1564
|
+
options = $.extend(createObject(R.settings), defaults, options);
|
1565
|
+
|
1566
|
+
if(!options.accountCode) R.raiseError('accountCode missing');
|
1567
|
+
if(!options.signature) R.raiseError('signature missing');
|
1568
|
+
|
1569
|
+
var billingInfo = R.BillingInfo.create();
|
1570
|
+
|
1571
|
+
var $form = $(R.dom.update_billing_info_form);
|
1572
|
+
$form.find('.billing_info').html(R.dom.billing_info_fields);
|
1573
|
+
|
1574
|
+
|
1575
|
+
initCommonForm($form, options);
|
1576
|
+
initBillingInfoForm($form, options);
|
1577
|
+
|
1578
|
+
|
1579
|
+
$form.submit(function(e) {
|
1580
|
+
e.preventDefault();
|
1581
|
+
|
1582
|
+
clearServerErrors($form);
|
1583
|
+
|
1584
|
+
$form.find('.error').remove();
|
1585
|
+
$form.find('.invalid').removeClass('invalid');
|
1586
|
+
|
1587
|
+
validationGroup(function(puller) {
|
1588
|
+
pullBillingInfoFields($form, billingInfo, options, puller);
|
1589
|
+
}
|
1590
|
+
, function() {
|
1591
|
+
$form.addClass('submitting');
|
1592
|
+
$form.find('button.submit').attr('disabled', true).text('Please Wait');
|
1593
|
+
|
1594
|
+
billingInfo.save({
|
1595
|
+
signature: options.signature
|
1596
|
+
, distinguishContactFromBillingInfo: options.distinguishContactFromBillingInfo
|
1597
|
+
, accountCode: options.accountCode
|
1598
|
+
, success: function(response) {
|
1599
|
+
if(options.successHandler) {
|
1600
|
+
options.successHandler(R.getToken(response));
|
1601
|
+
}
|
1602
|
+
|
1603
|
+
if(options.successURL) {
|
1604
|
+
var url = options.successURL;
|
1605
|
+
R.postResult(url, response, options);
|
1606
|
+
}
|
1607
|
+
}
|
1608
|
+
, error: function(errors) {
|
1609
|
+
if(!options.onError || !options.onError(errors)) {
|
1610
|
+
displayServerErrors($form, errors);
|
1611
|
+
}
|
1612
|
+
}
|
1613
|
+
, complete: function() {
|
1614
|
+
$form.removeClass('submitting');
|
1615
|
+
$form.find('button.submit').removeAttr('disabled').text('Update');
|
1616
|
+
}
|
1617
|
+
});
|
1618
|
+
});
|
1619
|
+
});
|
1620
|
+
|
1621
|
+
if(options.beforeInject) {
|
1622
|
+
options.beforeInject($form.get(0));
|
1623
|
+
}
|
1624
|
+
|
1625
|
+
$(function() {
|
1626
|
+
var $container = $(options.target);
|
1627
|
+
$container.html($form);
|
1628
|
+
|
1629
|
+
if(options.afterInject) {
|
1630
|
+
options.afterInject($form.get(0));
|
1631
|
+
}
|
1632
|
+
});
|
1633
|
+
|
1634
|
+
};
|
1635
|
+
|
1636
|
+
|
1637
|
+
function initTOSCheck($form, options) {
|
1638
|
+
|
1639
|
+
if(options.termsOfServiceURL || options.privacyPolicyURL) {
|
1640
|
+
var $tos = $form.find('.accept_tos').html(R.dom.terms_of_service);
|
1641
|
+
|
1642
|
+
// If only one, remove 'and'
|
1643
|
+
if(!(options.termsOfServiceURL && options.privacyPolicyURL)) {
|
1644
|
+
$tos.find('span.and').remove();
|
1645
|
+
}
|
1646
|
+
|
1647
|
+
// set href or remove tos_link
|
1648
|
+
if(options.termsOfServiceURL) {
|
1649
|
+
$tos.find('a.tos_link').attr('href', options.termsOfServiceURL);
|
1650
|
+
}
|
1651
|
+
else {
|
1652
|
+
$tos.find('a.tos_link').remove();
|
1653
|
+
}
|
1654
|
+
|
1655
|
+
// set href or remove pp_link
|
1656
|
+
if(options.privacyPolicyURL) {
|
1657
|
+
$tos.find('a.pp_link').attr('href', options.privacyPolicyURL);
|
1658
|
+
}
|
1659
|
+
else {
|
1660
|
+
$tos.find('a.pp_link').remove();
|
1661
|
+
}
|
1662
|
+
|
1663
|
+
}
|
1664
|
+
else {
|
1665
|
+
$form.find('.accept_tos').remove();
|
1666
|
+
}
|
1667
|
+
|
1668
|
+
}
|
1669
|
+
|
1670
|
+
R.buildTransactionForm = function(options) {
|
1671
|
+
var defaults = {
|
1672
|
+
addressRequirement: 'full'
|
1673
|
+
, distinguishContactFromBillingInfo: true
|
1674
|
+
, collectContactInfo: true
|
1675
|
+
};
|
1676
|
+
|
1677
|
+
options = $.extend(createObject(R.settings), defaults, options);
|
1678
|
+
|
1679
|
+
|
1680
|
+
if(!options.collectContactInfo && !options.accountCode) {
|
1681
|
+
R.raiseError('collectContactInfo is false, but no accountCode provided');
|
1682
|
+
}
|
1683
|
+
|
1684
|
+
if(!options.signature) R.raiseError('signature missing');
|
1685
|
+
|
1686
|
+
|
1687
|
+
var billingInfo = R.BillingInfo.create()
|
1688
|
+
, account = R.Account.create()
|
1689
|
+
, transaction = R.Transaction.create();
|
1690
|
+
|
1691
|
+
|
1692
|
+
transaction.account = account;
|
1693
|
+
transaction.billingInfo = billingInfo;
|
1694
|
+
transaction.currency = options.currency;
|
1695
|
+
transaction.cost = new R.Cost(options.amountInCents);
|
1696
|
+
|
1697
|
+
var $form = $(R.dom.one_time_transaction_form);
|
1698
|
+
$form.find('.billing_info').html(R.dom.billing_info_fields);
|
1699
|
+
|
1700
|
+
if(options.collectContactInfo) {
|
1701
|
+
$form.find('.contact_info').html(R.dom.contact_info_fields);
|
1702
|
+
}
|
1703
|
+
else {
|
1704
|
+
$form.find('.contact_info').remove();
|
1705
|
+
}
|
1706
|
+
|
1707
|
+
|
1708
|
+
initCommonForm($form, options);
|
1709
|
+
initContactInfoForm($form, options);
|
1710
|
+
initBillingInfoForm($form, options);
|
1711
|
+
initTOSCheck($form, options);
|
1712
|
+
|
1713
|
+
$form.submit(function(e) {
|
1714
|
+
e.preventDefault();
|
1715
|
+
|
1716
|
+
clearServerErrors($form);
|
1717
|
+
|
1718
|
+
$form.find('.error').remove();
|
1719
|
+
$form.find('.invalid').removeClass('invalid');
|
1720
|
+
|
1721
|
+
validationGroup(function(puller) {
|
1722
|
+
pullAccountFields($form, account, options, puller);
|
1723
|
+
pullBillingInfoFields($form, billingInfo, options, puller);
|
1724
|
+
verifyTOSChecked($form, puller);
|
1725
|
+
}
|
1726
|
+
, function() {
|
1727
|
+
$form.addClass('submitting');
|
1728
|
+
$form.find('button.submit').attr('disabled', true).text('Please Wait');
|
1729
|
+
|
1730
|
+
transaction.save({
|
1731
|
+
signature: options.signature
|
1732
|
+
, accountCode: options.accountCode
|
1733
|
+
, success: function(response) {
|
1734
|
+
if(options.successHandler) {
|
1735
|
+
options.successHandler(R.getToken(response));
|
1736
|
+
}
|
1737
|
+
|
1738
|
+
if(options.successURL) {
|
1739
|
+
var url = options.successURL;
|
1740
|
+
R.postResult(url, response, options);
|
1741
|
+
}
|
1742
|
+
}
|
1743
|
+
, error: function(errors) {
|
1744
|
+
if(!options.onError || !options.onError(errors)) {
|
1745
|
+
displayServerErrors($form, errors);
|
1746
|
+
}
|
1747
|
+
}
|
1748
|
+
, complete: function() {
|
1749
|
+
$form.removeClass('submitting');
|
1750
|
+
$form.find('button.submit').removeAttr('disabled').text('Pay');
|
1751
|
+
}
|
1752
|
+
});
|
1753
|
+
});
|
1754
|
+
});
|
1755
|
+
|
1756
|
+
if(options.beforeInject) {
|
1757
|
+
options.beforeInject($form.get(0));
|
1758
|
+
}
|
1759
|
+
|
1760
|
+
$(function() {
|
1761
|
+
var $container = $(options.target);
|
1762
|
+
$container.html($form);
|
1763
|
+
|
1764
|
+
if(options.afterInject) {
|
1765
|
+
options.afterInject($form.get(0));
|
1766
|
+
}
|
1767
|
+
});
|
1768
|
+
|
1769
|
+
};
|
1770
|
+
|
1771
|
+
|
1772
|
+
R.buildSubscriptionForm = function(options) {
|
1773
|
+
var defaults = {
|
1774
|
+
enableAddOns: true
|
1775
|
+
, enableCoupons: true
|
1776
|
+
, addressRequirement: 'full'
|
1777
|
+
, distinguishContactFromBillingInfo: false
|
1778
|
+
};
|
1779
|
+
|
1780
|
+
options = $.extend(createObject(R.settings), defaults, options);
|
1781
|
+
|
1782
|
+
if(!options.signature) R.raiseError('signature missing');
|
1783
|
+
|
1784
|
+
var $form = $(R.dom.subscribe_form);
|
1785
|
+
$form.find('.contact_info').html(R.dom.contact_info_fields);
|
1786
|
+
$form.find('.billing_info').html(R.dom.billing_info_fields);
|
1787
|
+
|
1788
|
+
|
1789
|
+
if(options.planCode)
|
1790
|
+
R.Plan.get(options.planCode, options.currency, gotPlan);
|
1791
|
+
else if(options.plan) {
|
1792
|
+
// this should never be called
|
1793
|
+
// the api does not have it, nor does anywhere else in the program refer to it
|
1794
|
+
gotPlan(options.plan);
|
1795
|
+
}
|
1796
|
+
|
1797
|
+
initCommonForm($form, options);
|
1798
|
+
initContactInfoForm($form, options);
|
1799
|
+
initBillingInfoForm($form, options);
|
1800
|
+
initTOSCheck($form, options);
|
1801
|
+
|
1802
|
+
function gotPlan(plan) {
|
1803
|
+
|
1804
|
+
if(options.filterPlan)
|
1805
|
+
plan = options.filterPlan(plan) || plan;
|
1806
|
+
|
1807
|
+
|
1808
|
+
var subscription = plan.createSubscription(),
|
1809
|
+
account = R.Account.create(),
|
1810
|
+
billingInfo = R.BillingInfo.create();
|
1811
|
+
|
1812
|
+
subscription.account = account;
|
1813
|
+
subscription.billingInfo = billingInfo;
|
1814
|
+
|
1815
|
+
if(options.filterSubscription)
|
1816
|
+
subscription = options.filterSubscription(subscription) || subscription;
|
1817
|
+
|
1818
|
+
// == EDITABLE PLAN QUANTITY
|
1819
|
+
if(!plan.displayQuantity) {
|
1820
|
+
$form.find('.plan .quantity').remove();
|
1821
|
+
}
|
1822
|
+
|
1823
|
+
// == SETUP FEE
|
1824
|
+
if(plan.setupFee) {
|
1825
|
+
$form.find('.subscription').addClass('with_setup_fee');
|
1826
|
+
$form.find('.plan .setup_fee .cost').text('' + plan.setupFee);
|
1827
|
+
}
|
1828
|
+
else {
|
1829
|
+
$form.find('.plan .setup_fee').remove();
|
1830
|
+
}
|
1831
|
+
|
1832
|
+
// == FREE TRIAL
|
1833
|
+
if(plan.trial) {
|
1834
|
+
$form.find('.subscription').addClass('with_trial');
|
1835
|
+
|
1836
|
+
$form.find('.plan .free_trial').text('First ' + plan.trial + ' free');
|
1837
|
+
}
|
1838
|
+
else {
|
1839
|
+
$form.find('.plan .free_trial').remove();
|
1840
|
+
}
|
1841
|
+
|
1842
|
+
|
1843
|
+
// == UPDATE ALL UI TOTALS via subscription.calculateTotals() results
|
1844
|
+
function updateTotals() {
|
1845
|
+
var totals = subscription.calculateTotals();
|
1846
|
+
|
1847
|
+
$form.find('.plan .recurring_cost .cost').text('' + totals.plan);
|
1848
|
+
$form.find('.due_now .cost').text('' + totals.stages.now);
|
1849
|
+
$form.find('.coupon .discount').text('' + (totals.coupon || ''));
|
1850
|
+
$form.find('.vat .cost').text('' + (totals.vat || ''));
|
1851
|
+
|
1852
|
+
$form.find('.add_ons .add_on').each(function() {
|
1853
|
+
var addOn = $(this).data('add_on');
|
1854
|
+
if($(this).hasClass('selected')) {
|
1855
|
+
var cost = totals.addOns[addOn.code];
|
1856
|
+
$(this).find('.cost').text('+ '+cost);
|
1857
|
+
}
|
1858
|
+
else {
|
1859
|
+
$(this).find('.cost').text('+ '+addOn.cost);
|
1860
|
+
}
|
1861
|
+
});
|
1862
|
+
}
|
1863
|
+
|
1864
|
+
$form.find('.plan .quantity input').bind('change keyup', function() {
|
1865
|
+
subscription.plan.quantity = parseInt($(this).val(), 10) || 1;
|
1866
|
+
updateTotals();
|
1867
|
+
});
|
1868
|
+
|
1869
|
+
// == SUBSCRIPTION PLAN GENERAL
|
1870
|
+
$form.find('.plan .name').text(plan.name);
|
1871
|
+
$form.find('.plan .recurring_cost .cost').text(''+plan.cost);
|
1872
|
+
$form.find('.plan .recurring_cost .interval').text('every '+plan.interval);
|
1873
|
+
|
1874
|
+
|
1875
|
+
// == GENERATE ADD-ONS
|
1876
|
+
var $addOnsList = $form.find('.add_ons');
|
1877
|
+
if(options.enableAddOns) {
|
1878
|
+
var l = plan.addOns.length;
|
1879
|
+
if(l) {
|
1880
|
+
$addOnsList.removeClass('none').addClass('any');
|
1881
|
+
for(var i=0; i < l; ++i) {
|
1882
|
+
var addOn = plan.addOns[i];
|
1883
|
+
|
1884
|
+
var classAttr = 'add_on add_on_'+ addOn.code + (i % 2 ? ' even' : ' odd');
|
1885
|
+
if(i == 0) classAttr += ' first';
|
1886
|
+
if(i == l-1) classAttr += ' last';
|
1887
|
+
|
1888
|
+
var $addOn = $('<div class="'+classAttr+'">' +
|
1889
|
+
'<div class="name">'+addOn.name+'</div>' +
|
1890
|
+
'<div class="field quantity">' +
|
1891
|
+
'<div class="placeholder">Qty</div>' +
|
1892
|
+
'<input type="text">' +
|
1893
|
+
'</div>' +
|
1894
|
+
'<div class="cost"/>' +
|
1895
|
+
'</div>');
|
1896
|
+
if(!addOn.displayQuantity) {
|
1897
|
+
$addOn.find('.quantity').remove();
|
1898
|
+
}
|
1899
|
+
$addOn.data('add_on', addOn);
|
1900
|
+
$addOn.appendTo($addOnsList);
|
1901
|
+
}
|
1902
|
+
|
1903
|
+
// Quantity Change
|
1904
|
+
$addOnsList.delegate('.add_ons .quantity input', 'change keyup', function(e) {
|
1905
|
+
var $addOn = $(this).closest('.add_on');
|
1906
|
+
var addOn = $addOn.data('add_on');
|
1907
|
+
var newQty = parseInt($(this).val(),10) || 1;
|
1908
|
+
subscription.findAddOnByCode(addOn.code).quantity = newQty;
|
1909
|
+
updateTotals();
|
1910
|
+
});
|
1911
|
+
|
1912
|
+
$addOnsList.bind('selectstart', function(e) {
|
1913
|
+
if($(e.target).is('.add_on')) {
|
1914
|
+
e.preventDefault();
|
1915
|
+
}
|
1916
|
+
});
|
1917
|
+
|
1918
|
+
// Add-on click
|
1919
|
+
$addOnsList.delegate('.add_ons .add_on', 'click', function(e) {
|
1920
|
+
if($(e.target).closest('.quantity').length) return;
|
1921
|
+
|
1922
|
+
var selected = !$(this).hasClass('selected');
|
1923
|
+
$(this).toggleClass('selected', selected);
|
1924
|
+
|
1925
|
+
var addOn = $(this).data('add_on');
|
1926
|
+
|
1927
|
+
if(selected) {
|
1928
|
+
// add
|
1929
|
+
var sa = subscription.redeemAddOn(addOn);
|
1930
|
+
var $qty = $(this).find('.quantity input');
|
1931
|
+
sa.quantity = parseInt($qty.val(),10) || 1;
|
1932
|
+
$qty.focus();
|
1933
|
+
}
|
1934
|
+
else {
|
1935
|
+
// remove
|
1936
|
+
subscription.removeAddOn(addOn.code);
|
1937
|
+
}
|
1938
|
+
|
1939
|
+
updateTotals();
|
1940
|
+
});
|
1941
|
+
}
|
1942
|
+
}
|
1943
|
+
else {
|
1944
|
+
$addOnsList.remove();
|
1945
|
+
}
|
1946
|
+
|
1947
|
+
// == COUPON REDEEMER
|
1948
|
+
var $coupon = $form.find('.coupon');
|
1949
|
+
var lastCode = null;
|
1950
|
+
|
1951
|
+
function updateCoupon() {
|
1952
|
+
|
1953
|
+
var code = $coupon.find('input').val();
|
1954
|
+
if(code == lastCode) {
|
1955
|
+
return;
|
1956
|
+
}
|
1957
|
+
|
1958
|
+
lastCode = code;
|
1959
|
+
|
1960
|
+
if(!code) {
|
1961
|
+
$coupon.removeClass('invalid').removeClass('valid');
|
1962
|
+
$coupon.find('.description').text('');
|
1963
|
+
subscription.coupon = undefined;
|
1964
|
+
updateTotals();
|
1965
|
+
return;
|
1966
|
+
}
|
1967
|
+
|
1968
|
+
$coupon.addClass('checking');
|
1969
|
+
subscription.getCoupon(code, function(coupon) {
|
1970
|
+
|
1971
|
+
$coupon.removeClass('checking');
|
1972
|
+
|
1973
|
+
subscription.coupon = coupon;
|
1974
|
+
$coupon.removeClass('invalid').addClass('valid');
|
1975
|
+
$coupon.find('.description').text(coupon.description);
|
1976
|
+
|
1977
|
+
updateTotals();
|
1978
|
+
}, function() {
|
1979
|
+
|
1980
|
+
subscription.coupon = undefined;
|
1981
|
+
|
1982
|
+
$coupon.removeClass('checking');
|
1983
|
+
$coupon.removeClass('valid').addClass('invalid');
|
1984
|
+
$coupon.find('.description').text('Not Found');
|
1985
|
+
|
1986
|
+
updateTotals();
|
1987
|
+
});
|
1988
|
+
}
|
1989
|
+
|
1990
|
+
if(options.enableCoupons) {
|
1991
|
+
$coupon.find('input').bind('keyup change', function(e) {
|
1992
|
+
});
|
1993
|
+
|
1994
|
+
$coupon.find('input').keypress(function(e) {
|
1995
|
+
if(e.charCode == 13) {
|
1996
|
+
e.preventDefault();
|
1997
|
+
updateCoupon();
|
1998
|
+
}
|
1999
|
+
});
|
2000
|
+
|
2001
|
+
|
2002
|
+
$coupon.find('.check').click(function() {
|
2003
|
+
updateCoupon();
|
2004
|
+
});
|
2005
|
+
|
2006
|
+
$coupon.find('input').blur(function() { $coupon.find('.check').click(); });
|
2007
|
+
}
|
2008
|
+
else {
|
2009
|
+
$coupon.remove();
|
2010
|
+
}
|
2011
|
+
|
2012
|
+
|
2013
|
+
// == VAT
|
2014
|
+
var $vat = $form.find('.vat');
|
2015
|
+
var $vatNumber = $form.find('.vat_number');
|
2016
|
+
var $vatNumberInput = $vatNumber.find('input');
|
2017
|
+
|
2018
|
+
$vat.find('.title').text('VAT at ' + R.settings.VATPercent + '%');
|
2019
|
+
function showHideVAT() {
|
2020
|
+
var buyerCountry = $form.find('.country select').val();
|
2021
|
+
var vatNumberApplicable = R.isVATNumberApplicable(buyerCountry);
|
2022
|
+
|
2023
|
+
// VAT Number is applicable to collection in any EU country
|
2024
|
+
$vatNumber.toggleClass('applicable', vatNumberApplicable);
|
2025
|
+
$vatNumber.toggleClass('inapplicable', !vatNumberApplicable);
|
2026
|
+
|
2027
|
+
var vatNumber = $vatNumberInput.val();
|
2028
|
+
|
2029
|
+
// Only applicable to charge if isVATApplicable()
|
2030
|
+
var chargeApplicable = R.isVATChargeApplicable(buyerCountry, vatNumber);
|
2031
|
+
$vat.toggleClass('applicable', chargeApplicable);
|
2032
|
+
$vat.toggleClass('inapplicable', !chargeApplicable);
|
2033
|
+
}
|
2034
|
+
// showHideVAT();
|
2035
|
+
$form.find('.country select').change(function() {
|
2036
|
+
billingInfo.country = $(this).val();
|
2037
|
+
updateTotals();
|
2038
|
+
showHideVAT();
|
2039
|
+
}).change();
|
2040
|
+
$vatNumberInput.bind('keyup change', function() {
|
2041
|
+
billingInfo.vatNumber = $(this).val();
|
2042
|
+
updateTotals();
|
2043
|
+
showHideVAT();
|
2044
|
+
});
|
2045
|
+
|
2046
|
+
// SUBMIT HANDLER
|
2047
|
+
$form.submit(function(e) {
|
2048
|
+
e.preventDefault();
|
2049
|
+
|
2050
|
+
clearServerErrors($form);
|
2051
|
+
|
2052
|
+
|
2053
|
+
$form.find('.error').remove();
|
2054
|
+
$form.find('.invalid').removeClass('invalid');
|
2055
|
+
|
2056
|
+
validationGroup(function(puller) {
|
2057
|
+
pullPlanQuantity($form, subscription.plan, options, puller);
|
2058
|
+
pullAccountFields($form, account, options, puller);
|
2059
|
+
pullBillingInfoFields($form, billingInfo, options, puller);
|
2060
|
+
verifyTOSChecked($form, puller);
|
2061
|
+
}, function() {
|
2062
|
+
|
2063
|
+
$form.addClass('submitting');
|
2064
|
+
$form.find('button.submit').attr('disabled', true).text('Please Wait');
|
2065
|
+
|
2066
|
+
subscription.save({
|
2067
|
+
|
2068
|
+
signature: options.signature
|
2069
|
+
, success: function(response) {
|
2070
|
+
if(options.successHandler) {
|
2071
|
+
options.successHandler(R.getToken(response));
|
2072
|
+
}
|
2073
|
+
if(options.successURL) {
|
2074
|
+
var url = options.successURL;
|
2075
|
+
R.postResult(url, response, options);
|
2076
|
+
}
|
2077
|
+
}
|
2078
|
+
, error: function(errors) {
|
2079
|
+
if(!options.onError || !options.onError(errors)) {
|
2080
|
+
displayServerErrors($form, errors);
|
2081
|
+
}
|
2082
|
+
}
|
2083
|
+
, complete: function() {
|
2084
|
+
$form.removeClass('submitting');
|
2085
|
+
$form.find('button.submit').removeAttr('disabled').text('Subscribe');
|
2086
|
+
}
|
2087
|
+
});
|
2088
|
+
});
|
2089
|
+
|
2090
|
+
});
|
2091
|
+
|
2092
|
+
// FINALLY - UPDATE INITIAL TOTALS AND INJECT INTO DOM
|
2093
|
+
updateTotals();
|
2094
|
+
|
2095
|
+
if(options.beforeInject) {
|
2096
|
+
options.beforeInject($form.get(0));
|
2097
|
+
}
|
2098
|
+
|
2099
|
+
$(function() {
|
2100
|
+
var $container = $(options.target);
|
2101
|
+
$container.html($form);
|
2102
|
+
|
2103
|
+
if(options.afterInject) {
|
2104
|
+
options.afterInject($form.get(0));
|
2105
|
+
}
|
2106
|
+
});
|
2107
|
+
|
2108
|
+
}
|
2109
|
+
|
2110
|
+
};
|
2111
|
+
|
2112
|
+
|
2113
|
+
|
2114
|
+
//////////////////////////////////////////////////
|
2115
|
+
// Compiled from src/js/states.js
|
2116
|
+
//////////////////////////////////////////////////
|
2117
|
+
|
2118
|
+
R.states = {};
|
2119
|
+
R.states.US = {
|
2120
|
+
"-": "Select State"
|
2121
|
+
, "--": "------------"
|
2122
|
+
, "AK": "Alaska"
|
2123
|
+
, "AL": "Alabama"
|
2124
|
+
, "AP": "Armed Forces Pacific"
|
2125
|
+
, "AR": "Arkansas"
|
2126
|
+
, "AS": "American Samoa"
|
2127
|
+
, "AZ": "Arizona"
|
2128
|
+
, "CA": "California"
|
2129
|
+
, "CO": "Colorado"
|
2130
|
+
, "CT": "Connecticut"
|
2131
|
+
, "DC": "District of Columbia"
|
2132
|
+
, "DE": "Delaware"
|
2133
|
+
, "FL": "Florida"
|
2134
|
+
, "FM": "Federated States of Micronesia"
|
2135
|
+
, "GA": "Georgia"
|
2136
|
+
, "GU": "Guam"
|
2137
|
+
, "HI": "Hawaii"
|
2138
|
+
, "IA": "Iowa"
|
2139
|
+
, "ID": "Idaho"
|
2140
|
+
, "IL": "Illinois"
|
2141
|
+
, "IN": "Indiana"
|
2142
|
+
, "KS": "Kansas"
|
2143
|
+
, "KY": "Kentucky"
|
2144
|
+
, "LA": "Louisiana"
|
2145
|
+
, "MA": "Massachusetts"
|
2146
|
+
, "MD": "Maryland"
|
2147
|
+
, "ME": "Maine"
|
2148
|
+
, "MH": "Marshall Islands"
|
2149
|
+
, "MI": "Michigan"
|
2150
|
+
, "MN": "Minnesota"
|
2151
|
+
, "MO": "Missouri"
|
2152
|
+
, "MP": "Northern Mariana Islands"
|
2153
|
+
, "MS": "Mississippi"
|
2154
|
+
, "MT": "Montana"
|
2155
|
+
, "NC": "North Carolina"
|
2156
|
+
, "ND": "North Dakota"
|
2157
|
+
, "NE": "Nebraska"
|
2158
|
+
, "NH": "New Hampshire"
|
2159
|
+
, "NJ": "New Jersey"
|
2160
|
+
, "NM": "New Mexico"
|
2161
|
+
, "NV": "Nevada"
|
2162
|
+
, "NY": "New York"
|
2163
|
+
, "OH": "Ohio"
|
2164
|
+
, "OK": "Oklahoma"
|
2165
|
+
, "OR": "Oregon"
|
2166
|
+
, "PA": "Pennsylvania"
|
2167
|
+
, "PR": "Puerto Rico"
|
2168
|
+
, "PW": "Palau"
|
2169
|
+
, "RI": "Rhode Island"
|
2170
|
+
, "SC": "South Carolina"
|
2171
|
+
, "SD": "South Dakota"
|
2172
|
+
, "TN": "Tennessee"
|
2173
|
+
, "TX": "Texas"
|
2174
|
+
, "UT": "Utah"
|
2175
|
+
, "VA": "Virginia"
|
2176
|
+
, "VI": "Virgin Islands"
|
2177
|
+
, "VT": "Vermont"
|
2178
|
+
, "WA": "Washington"
|
2179
|
+
, "WV": "West Virginia"
|
2180
|
+
, "WI": "Wisconsin"
|
2181
|
+
, "WY": "Wyoming"
|
2182
|
+
};
|
2183
|
+
|
2184
|
+
R.states.CA = {
|
2185
|
+
"-": "Select State"
|
2186
|
+
, "--": "------------"
|
2187
|
+
, "AB": "Alberta"
|
2188
|
+
, "BC": "British Columbia"
|
2189
|
+
, "MB": "Manitoba"
|
2190
|
+
, "NB": "New Brunswick"
|
2191
|
+
, "NL": "Newfoundland"
|
2192
|
+
, "NS": "Nova Scotia"
|
2193
|
+
, "NU": "Nunavut"
|
2194
|
+
, "ON": "Ontario"
|
2195
|
+
, "PE": "Prince Edward Island"
|
2196
|
+
, "QC": "Quebec"
|
2197
|
+
, "SK": "Saskatchewan"
|
2198
|
+
, "NT": "Northwest Territories"
|
2199
|
+
, "YT": "Yukon Territory"
|
2200
|
+
, "AA": "Armed Forces Americas"
|
2201
|
+
, "AE": "Armed Forces Europe, Middle East, & Canada"
|
2202
|
+
};
|
2203
|
+
|
2204
|
+
|
2205
|
+
|
2206
|
+
|
2207
|
+
//////////////////////////////////////////////////
|
2208
|
+
// Compiled from src/dom/contact_info_fields.jade
|
2209
|
+
//////////////////////////////////////////////////
|
2210
|
+
|
2211
|
+
R.dom['contact_info_fields'] = '<div class="title">Contact Info</div><div class="full_name"><div class="field first_name"><div class="placeholder">First Name </div><input type="text"/></div><div class="field last_name"><div class="placeholder">Last Name </div><input type="text"/></div></div><div class="field email"><div class="placeholder">Email </div><input type="text"/></div><div class="field phone"><div class="placeholder">Phone Number</div><input type="text"/></div><div class="field company_name"><div class="placeholder">Company/Organization Name</div><input type="text"/></div>';
|
2212
|
+
|
2213
|
+
//////////////////////////////////////////////////
|
2214
|
+
// Compiled from src/dom/billing_info_fields.jade
|
2215
|
+
//////////////////////////////////////////////////
|
2216
|
+
|
2217
|
+
R.dom['billing_info_fields'] = '<div class="title">Billing Info</div><div class="accepted_cards"></div><div class="credit_card"><div class="field first_name"><div class="placeholder">First Name </div><input type="text"/></div><div class="field last_name"><div class="placeholder">Last Name </div><input type="text"/></div><div class="card_cvv"><div class="field card_number"><div class="placeholder">Credit Card Number </div><input type="text"/></div><div class="field cvv"><div class="placeholder">CVV </div><input type="text"/></div></div><div class="field expires"><div class="title">Expires </div><div class="month"><select><option value="1">01 - January</option><option value="2">02 - February</option><option value="3">03 - March</option><option value="4">04 - April</option><option value="5">05 - May</option><option value="6">06 - June</option><option value="7">07 - July</option><option value="8">08 - August</option><option value="9">09 - September</option><option value="10">10 - October</option><option value="11">11 - November</option><option value="12">12 - December</option></select></div><div class="year"><select></select></div></div></div><div class="address"><div class="field address1"><div class="placeholder">Address</div><input type="text"/></div><div class="field address2"><div class="placeholder">Apt/Suite</div><input type="text"/></div><div class="field city"><div class="placeholder">City</div><input type="text"/></div><div class="state_zip"><div class="field state"><div class="placeholder">State/Province</div><input type="text"/></div><div class="field zip"><div class="placeholder">Zip/Postal</div><input type="text"/></div></div><div class="field country"><select><option value="-">Select Country</option><option value="-">--------------</option><option value="AF">Afghanistan</option><option value="AL">Albania</option><option value="DZ">Algeria</option><option value="AS">American Samoa</option><option value="AD">Andorra</option><option value="AO">Angola</option><option value="AI">Anguilla</option><option value="AQ">Antarctica</option><option value="AG">Antigua and Barbuda</option><option value="AR">Argentina</option><option value="AM">Armenia</option><option value="AW">Aruba</option><option value="AC">Ascension Island</option><option value="AU">Australia</option><option value="AT">Austria</option><option value="AZ">Azerbaijan</option><option value="BS">Bahamas</option><option value="BH">Bahrain</option><option value="BD">Bangladesh</option><option value="BB">Barbados</option><option value="BE">Belgium</option><option value="BZ">Belize</option><option value="BJ">Benin</option><option value="BM">Bermuda</option><option value="BT">Bhutan</option><option value="BO">Bolivia</option><option value="BA">Bosnia and Herzegovina</option><option value="BW">Botswana</option><option value="BV">Bouvet Island</option><option value="BR">Brazil</option><option value="BQ">British Antarctic Territory</option><option value="IO">British Indian Ocean Territory</option><option value="VG">British Virgin Islands</option><option value="BN">Brunei</option><option value="BG">Bulgaria</option><option value="BF">Burkina Faso</option><option value="BI">Burundi</option><option value="KH">Cambodia</option><option value="CM">Cameroon</option><option value="CA">Canada</option><option value="IC">Canary Islands</option><option value="CT">Canton and Enderbury Islands</option><option value="CV">Cape Verde</option><option value="KY">Cayman Islands</option><option value="CF">Central African Republic</option><option value="EA">Ceuta and Melilla</option><option value="TD">Chad</option><option value="CL">Chile</option><option value="CN">China</option><option value="CX">Christmas Island</option><option value="CP">Clipperton Island</option><option value="CC">Cocos [Keeling] Islands</option><option value="CO">Colombia</option><option value="KM">Comoros</option><option value="CD">Congo [DRC]</option><option value="CK">Cook Islands</option><option value="CR">Costa Rica</option><option value="HR">Croatia</option><option value="CU">Cuba</option><option value="CY">Cyprus</option><option value="CZ">Czech Republic</option><option value="DK">Denmark</option><option value="DG">Diego Garcia</option><option value="DJ">Djibouti</option><option value="DM">Dominica</option><option value="DO">Dominican Republic</option><option value="NQ">Dronning Maud Land</option><option value="DD">East Germany</option><option value="TL">East Timor</option><option value="EC">Ecuador</option><option value="EG">Egypt</option><option value="SV">El Salvador</option><option value="EE">Estonia</option><option value="ET">Ethiopia</option><option value="EU">European Union</option><option value="FK">Falkland Islands [Islas Malvinas]</option><option value="FO">Faroe Islands</option><option value="FJ">Fiji</option><option value="FI">Finland</option><option value="FR">France</option><option value="GF">French Guiana</option><option value="PF">French Polynesia</option><option value="TF">French Southern Territories</option><option value="FQ">French Southern and Antarctic Territories</option><option value="GA">Gabon</option><option value="GM">Gambia</option><option value="GE">Georgia</option><option value="DE">Germany</option><option value="GH">Ghana</option><option value="GI">Gibraltar</option><option value="GR">Greece</option><option value="GL">Greenland</option><option value="GD">Grenada</option><option value="GP">Guadeloupe</option><option value="GU">Guam</option><option value="GT">Guatemala</option><option value="GG">Guernsey</option><option value="GW">Guinea-Bissau</option><option value="GY">Guyana</option><option value="HT">Haiti</option><option value="HM">Heard Island and McDonald Islands</option><option value="HN">Honduras</option><option value="HK">Hong Kong</option><option value="HU">Hungary</option><option value="IS">Iceland</option><option value="IN">India</option><option value="ID">Indonesia</option><option value="IE">Ireland</option><option value="IM">Isle of Man</option><option value="IL">Israel</option><option value="IT">Italy</option><option value="JM">Jamaica</option><option value="JP">Japan</option><option value="JE">Jersey</option><option value="JT">Johnston Island</option><option value="JO">Jordan</option><option value="KZ">Kazakhstan</option><option value="KE">Kenya</option><option value="KI">Kiribati</option><option value="KW">Kuwait</option><option value="KG">Kyrgyzstan</option><option value="LA">Laos</option><option value="LV">Latvia</option><option value="LS">Lesotho</option><option value="LY">Libya</option><option value="LI">Liechtenstein</option><option value="LT">Lithuania</option><option value="LU">Luxembourg</option><option value="MO">Macau</option><option value="MK">Macedonia [FYROM]</option><option value="MG">Madagascar</option><option value="MW">Malawi</option><option value="MY">Malaysia</option><option value="MV">Maldives</option><option value="ML">Mali</option><option value="MT">Malta</option><option value="MH">Marshall Islands</option><option value="MQ">Martinique</option><option value="MR">Mauritania</option><option value="MU">Mauritius</option><option value="YT">Mayotte</option><option value="FX">Metropolitan France</option><option value="MX">Mexico</option><option value="FM">Micronesia</option><option value="MI">Midway Islands</option><option value="MD">Moldova</option><option value="MC">Monaco</option><option value="MN">Mongolia</option><option value="ME">Montenegro</option><option value="MS">Montserrat</option><option value="MA">Morocco</option><option value="MZ">Mozambique</option><option value="NA">Namibia</option><option value="NR">Nauru</option><option value="NP">Nepal</option><option value="NL">Netherlands</option><option value="AN">Netherlands Antilles</option><option value="NT">Neutral Zone</option><option value="NC">New Caledonia</option><option value="NZ">New Zealand</option><option value="NI">Nicaragua</option><option value="NE">Niger</option><option value="NG">Nigeria</option><option value="NU">Niue</option><option value="NF">Norfolk Island</option><option value="VD">North Vietnam</option><option value="MP">Northern Mariana Islands</option><option value="NO">Norway</option><option value="OM">Oman</option><option value="QO">Outlying Oceania</option><option value="PC">Pacific Islands Trust Territory</option><option value="PK">Pakistan</option><option value="PW">Palau</option><option value="PS">Palestinian Territories</option><option value="PA">Panama</option><option value="PZ">Panama Canal Zone</option><option value="PY">Paraguay</option><option value="YD">People\'s Democratic Republic of Yemen</option><option value="PE">Peru</option><option value="PH">Philippines</option><option value="PN">Pitcairn Islands</option><option value="PL">Poland</option><option value="PT">Portugal</option><option value="PR">Puerto Rico</option><option value="QA">Qatar</option><option value="RO">Romania</option><option value="RU">Russia</option><option value="RW">Rwanda</option><option value="RE">R\u00e9union</option><option value="BL">Saint Barth\u00e9lemy</option><option value="SH">Saint Helena</option><option value="KN">Saint Kitts and Nevis</option><option value="LC">Saint Lucia</option><option value="MF">Saint Martin</option><option value="PM">Saint Pierre and Miquelon</option><option value="VC">Saint Vincent and the Grenadines</option><option value="WS">Samoa</option><option value="SM">San Marino</option><option value="SA">Saudi Arabia</option><option value="SN">Senegal</option><option value="RS">Serbia</option><option value="CS">Serbia and Montenegro</option><option value="SC">Seychelles</option><option value="SL">Sierra Leone</option><option value="SG">Singapore</option><option value="SK">Slovakia</option><option value="SI">Slovenia</option><option value="SB">Solomon Islands</option><option value="ZA">South Africa</option><option value="GS">South Georgia and the South Sandwich Islands</option><option value="KR">South Korea</option><option value="ES">Spain</option><option value="LK">Sri Lanka</option><option value="SR">Suriname</option><option value="SJ">Svalbard and Jan Mayen</option><option value="SZ">Swaziland</option><option value="SE">Sweden</option><option value="CH">Switzerland</option><option value="ST">S\u00e3o Tom\u00e9 and Pr\u00edncipe</option><option value="TW">Taiwan</option><option value="TJ">Tajikistan</option><option value="TZ">Tanzania</option><option value="TH">Thailand</option><option value="TG">Togo</option><option value="TK">Tokelau</option><option value="TO">Tonga</option><option value="TT">Trinidad and Tobago</option><option value="TA">Tristan da Cunha</option><option value="TN">Tunisia</option><option value="TR">Turkey</option><option value="TM">Turkmenistan</option><option value="TC">Turks and Caicos Islands</option><option value="TV">Tuvalu</option><option value="UM">U.S. Minor Outlying Islands</option><option value="PU">U.S. Miscellaneous Pacific Islands</option><option value="VI">U.S. Virgin Islands</option><option value="UG">Uganda</option><option value="UA">Ukraine</option><option value="AE">United Arab Emirates</option><option value="GB">United Kingdom</option><option value="US">United States</option><option value="UY">Uruguay</option><option value="UZ">Uzbekistan</option><option value="VU">Vanuatu</option><option value="VA">Vatican City</option><option value="VE">Venezuela</option><option value="VN">Vietnam</option><option value="WK">Wake Island</option><option value="WF">Wallis and Futuna</option><option value="EH">Western Sahara</option><option value="YE">Yemen</option><option value="ZM">Zambia</option><option value="AX">\u00c5land Islands</option></select></div></div><div class="field vat_number"><div class="placeholder">VAT Number</div><input type="text"/></div>';
|
2218
|
+
|
2219
|
+
//////////////////////////////////////////////////
|
2220
|
+
// Compiled from src/dom/subscribe_form.jade
|
2221
|
+
//////////////////////////////////////////////////
|
2222
|
+
|
2223
|
+
R.dom['subscribe_form'] = '<form class="recurly subscribe"><!--[if lt IE 7]><div class="iefail"><div class="chromeframe"><p class="blast">Your browser is not supported by Recurly.js.</p><p><a href="http://browsehappy.com/">Upgrade to a different browser</a></p><p>or</p><p><a href="http://www.google.com/chromeframe/?redirect=true">install Google Chrome Frame</a></p><p>to use this site.</p></div></div><![endif]--><div class="subscription"><div class="plan"><div class="name"></div><div class="field quantity"><div class="placeholder">Qty</div><input type="text"/></div><div class="recurring_cost"><div class="cost"></div><div class="interval"></div></div><div class="free_trial"></div><div class="setup_fee"><div class="title">Setup Fee</div><div class="cost"></div></div></div><div class="add_ons none"></div><div class="coupon"><div class="coupon_code field"><div class="placeholder">Coupon Code</div><input type="text" class="coupon_code"/></div><div class="check"></div><div class="description"></div><div class="discount"></div></div><div class="vat"><div class="title">VAT</div><div class="cost"></div></div></div><div class="due_now"><div class="title">Order Total</div><div class="cost"></div></div><div class="server_errors none"></div><div class="contact_info"></div><div class="billing_info"></div><div class="accept_tos"></div><div class="footer"><button type="submit" class="submit">Subscribe</button></div></form>';
|
2224
|
+
|
2225
|
+
//////////////////////////////////////////////////
|
2226
|
+
// Compiled from src/dom/update_billing_info_form.jade
|
2227
|
+
//////////////////////////////////////////////////
|
2228
|
+
|
2229
|
+
R.dom['update_billing_info_form'] = '<form class="recurly update_billing_info"><div class="server_errors none"></div><div class="billing_info"></div><div class="footer"><button type="submit" class="submit">Update</button></div></form>';
|
2230
|
+
|
2231
|
+
//////////////////////////////////////////////////
|
2232
|
+
// Compiled from src/dom/one_time_transaction_form.jade
|
2233
|
+
//////////////////////////////////////////////////
|
2234
|
+
|
2235
|
+
R.dom['one_time_transaction_form'] = '<form class="recurly update_billing_info"><div class="server_errors none"></div><div class="contact_info"></div><div class="billing_info"></div><div class="accept_tos"></div><div class="footer"><button type="submit" class="submit">Pay</button></div></form>';
|
2236
|
+
|
2237
|
+
//////////////////////////////////////////////////
|
2238
|
+
// Compiled from src/dom/terms_of_service.jade
|
2239
|
+
//////////////////////////////////////////////////
|
2240
|
+
|
2241
|
+
R.dom['terms_of_service'] = '<input id="tos_check" type="checkbox"/><label id="accept_tos" for="tos_check">I accept the <a target="_blank" class="tos_link">Terms of Service</a><span class="and"> and </span><a target="_blank" class="pp_link">Privacy Policy</a></label>';
|
2242
|
+
window.Recurly = R;
|
2243
|
+
})(jQuery);
|