-
Notifications
You must be signed in to change notification settings - Fork 1
/
charges.js
302 lines (259 loc) · 9.6 KB
/
charges.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
$(function () {
/**
* Clean up state when the modal closes
* Hide the spinner and message if they are visible
*/
$('#stripeFormModal').on('hidden.bs.modal', function () {
hideSpinner();
hideMessage();
});
Stripe.setPublishableKey('<insert-your-publishable-stripe-key-here>');
/**
* Take control of the form submission. Prevent the default submission
* behaviour and retrieve a stripe token first
*/
$('#stripe-form').submit(function (e) {
e.preventDefault();
showSpinner();
// Charge the user with their inputted details
createCharge();
$('#submit-btn').prop("disabled", true);
});
/**
* This function creates a stripe charge request using the Stripe object.
* This request will call the stripeResponseHandler on completion
* @method createCharge
*/
function createCharge() {
Stripe.card.createToken({
number: $('#card-number').val(),
cvc: $('#cvc').val(),
exp_month: $('#expiry-month').val(),
exp_year: $('#expiry-year').val()
}, stripeResponseHandler);
}
/**
* Sends a post request to the charges_controller with the amount and token
* attributes.
*
* @method stripeResponseHandler
* @param http_code status The http_code returned with the response
* @param {} response The response from the api
*/
function stripeResponseHandler(status, response) {
if (response.error) {
// Show the errors on the form
$("#form-errors").show();
$('#form-errors').html(response.error.message);
} else {
// Send a post request to the charges route
$.post('/charges', {stripeToken: response.id, amount: $('#amount').val()})
.done(res => showSuccess(res.responseText))
.fail(err => handleErrors(err))
.always(function () {
$('#submit-btn').prop("disabled", false);
});
}
};
function showSuccess(res) {
// Hide the spinner
hideSpinner();
// Show the user the success message
showMessage('The charge was successful, thank you for donating.');
};
/**
* Sorts through the error retrieved from the stripe charge and executes
* the nessecary methods
* @method handleErrors
* @param {} err error object returned from the stripe charge
*/
function handleErrors(err) {
switch (err.type) {
case "card_error":
handleCardErrors(err);
break;
case "invalid_request_error":
// The user sent an invalid request, highlight all fields
fields = [
'card-holder-name',
'card-number',
'cvc',
'expiry-month',
'expiry-year'
];
highlightFields(fields);
break;
case "rate_limit_error":
// Wait two seconds and retry the transaction
retry();
break;
case "authentication_error":
// Wait two seconds and retry the transaction
retry();
break;
case "api_connection_error":
// Wait two seconds and retry the transaction
retry();
break;
case "api_error":
// Wait two seconds and retry the transaction
retry();
break;
default:
// Wait two seconds and retry the transaction
retry();
}
};
/**
* Sorts the possible errors returned from the stripe response which are
* related to the users card.
* @method handleCardErrors
* @param {} err error object returned from the stripe charge
* NOTE : 'invalid_zip' and 'missing' codes are not listed below. We do not take the
* users zip number and missing only applies when using Stripe Customers.
*/
function handleCardErrors(err) {
// Handle the card errors
switch (err.code) {
case ('invalid_number' || 'incorrect_number'):
highlightFields(['card-number']);
break;
case 'invalid_expiry_month':
highlightFields(['expiry-month'])
break;
case 'invalid_expiry_year':
highlightFields(['expiry-year'])
break;
case ('invalid_cvc' || 'incorrect_cvc'):
highlightFields(['cvc'])
break;
case 'card_declined':
showMessage('Sorry but your card was declined, please try again or contact your credit card supplier');
break;
case 'expired_card':
showMessage('Sorry but your card has expired, please try again or contact your credit card supplier');
break;
case 'processing_error':
showMessage('Sorry their was an issue processing your card, please try again');
break;
// For the default just show a processing error
default:
showMessage('Sorry their was an issue processing your card, please try again');
}
};
/**
* Highlight fields with invalid params.
* @method highlightFields
* @return [] fields A list of all the fields to highlight
*/
function highlightFields(fields) {
// Highlight specific/all fields
fields.forEach((element) => {
$(`#${element}`).css('border', '1px solid red');
});
// Hide the spinner if active
hideSpinner();
};
/**
* Retry the transaction from scratch with the users inputted details.
* This method should inform the user that the transaction is taking longer
* than normal/being re-tried.
* @method retry
*/
function retry() {
// Set the amount to a 00 value for a success response
$('#amount').val(1.00);
// Wait two seconds to try and avoid any network issues
setTimeout(() => {
// Change the message that the user sees
$('.payment-text').text('Just a little longer .... ');
createCharge();
}, 2000);
// Put the default text back in the payment text section
$('#payment-text').text('One moment we are processing your request ...');
};
/**
* Show the spinner and hide the form
* @method showSpinner
*/
function showSpinner() {
$('#spinner-container').show();
$('#stripe-form').hide();
};
/**
* Hide the spinner and show the form
* @method hideSpinner
*/
function hideSpinner() {
$('#spinner-container').hide();
$('#stripe-form').show();
};
/**
* Show the user a message based on the error we recieve
* @method showMessage
* @param string message The message to show the user
*/
function showMessage(message) {
$('#message-text').text(message);
$('#stripe-form').hide();
$('#spinner-container').hide();
$('#message-container').show();
};
/**
* Hide the message from the user and show the form again
* @method hideMessage
*/
function hideMessage() {
$('#message-container').hide();
$('#stripe-form').show();
};
});
$(function () {
// This function gets cookie with a given name
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
/*
The functions below will create a header with csrftoken
*/
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
function sameOrigin(url) {
// test that a given url is a same-origin URL
// url could be relative or scheme relative or absolute
var host = document.location.host; // host + port
var protocol = document.location.protocol;
var sr_origin = '//' + host;
var origin = protocol + sr_origin;
// Allow absolute or scheme relative URLs to same origin
return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
(url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
// or any other URL that isn't scheme relative or absolute i.e relative.
!(/^(\/\/|http:|https:).*/.test(url));
}
$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
// Send the token to same-origin, relative URLs only.
// Send the token only if the method warrants CSRF protection
// Using the CSRFToken value acquired earlier
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
});