caboose-cms 0.8.64 → 0.8.65

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2d35930182bb20d54aa4c0b5b5e8904f3ef90681
4
- data.tar.gz: ec38ef84ece6b6ea25ffc3aff9efda36f0d738df
3
+ metadata.gz: e6a4ba5943295734cab32b17d7fdbfa8dd72711d
4
+ data.tar.gz: 88ff9aec8169a808c90b0b9ed97e63816638be5e
5
5
  SHA512:
6
- metadata.gz: b8525496f4bdcebb66cb5da6402250ea8750c94afebbec73bd9f85d75a2980c3a4511f308931b83cce7cde4c840596764c80fe847a681459bae16c90629bd172
7
- data.tar.gz: 5de7d8361b02fd800da40c2e24eb193cf4ba02a82d166394a6fe3c696f3faa0687783237d1b6c2886a11b91003e0b41328058858eec6946c564cfb985f74401a
6
+ metadata.gz: ec41548d7130692a7754d55d62703cf5d17266222a4b2215da0c4bdc5179f8f5722868ad7878d7340a3248de76cfb1c2839e9eed03e21fd12927fc85695d56d8
7
+ data.tar.gz: b50b104307b57b2839efbfc1460be7922d6967150c91e00daf2d455f4d1b27b0afcbf435ed17746094ec3362166f623931c915ed3ced32c5b38303dff4f64b57
@@ -14,6 +14,7 @@ MyAccountInvoiceController.prototype = {
14
14
 
15
15
  var that = this;
16
16
  $('#payment_form').hide();
17
+
17
18
  $(document).ready(function() { that.refresh(); });
18
19
  },
19
20
 
@@ -21,9 +22,13 @@ MyAccountInvoiceController.prototype = {
21
22
  {
22
23
  var that = this;
23
24
  that.refresh_invoice(function() {
25
+ if (that.invoice.financial_status == 'pending')
26
+ that.payment_method_controller = new MyAccountPaymentMethodController({ ic: that });
27
+
24
28
  $('#invoice_table').html("<p class='loading'>Getting invoice...</p>");
25
29
  that.print();
26
- if (after) after();
30
+
31
+ if (after) after();
27
32
  });
28
33
  },
29
34
 
@@ -79,6 +84,9 @@ MyAccountInvoiceController.prototype = {
79
84
  if (count_downloadable > 0) that.downloadable_line_items_table(table);
80
85
  that.summary_table(table);
81
86
  $('#invoice_table').empty().append(table);
87
+
88
+ if (that.invoice.financial_status == 'pending')
89
+ that.payment_method_controller.print();
82
90
  }
83
91
  else
84
92
  {
@@ -96,7 +104,8 @@ MyAccountInvoiceController.prototype = {
96
104
  var fstatus = $('<div/>').append($('<p/>').html(capitalize_first_letter(that.invoice.financial_status)));
97
105
  if (that.invoice.financial_status == 'pending')
98
106
  {
99
- fstatus.append($('<p/>').append($('<input/>').attr('type', 'button').addClass('btn').val('Pay now').click(function(e) { e.preventDefault(); that.payment_form(); })));
107
+ fstatus.append($('<div/>').attr('id', 'payment_method_container'));
108
+ //fstatus.append($('<p/>').append($('<input/>').attr('type', 'button').addClass('btn').val('Pay now').click(function(e) { e.preventDefault(); that.payment_form(); })));
100
109
  }
101
110
 
102
111
  var table = $('<table/>').addClass('invoice');
@@ -424,32 +433,87 @@ MyAccountInvoiceController.prototype = {
424
433
  },
425
434
 
426
435
  /****************************************************************************/
436
+
437
+ confirm_invoice: function()
438
+ {
439
+ var that = this;
440
+ var cust = that.invoice.customer;
441
+ var form = $('#payment_form');
442
+ var message = $('#payment_message');
443
+
444
+ if (form.is(':visible'))
445
+ {
446
+ form.slideUp(function() { form.empty(); });
447
+ message.empty();
448
+ return;
449
+ }
450
+
451
+ form.append($('<h4/>').append("Confirm payment"));
452
+ form.append($('<p/>').html("A payment of $" + curr(that.invoice.total) + " will be charged to your " + cust.card_brand + ' ending in ' + cust.card_last4 + "."));
453
+
454
+ form.append($('<input/>').attr('type', 'button').addClass('btn').val('Pay Now').click(function(e)
455
+ {
456
+ e.preventDefault();
457
+ message.empty().html("<p class='loading'>Processing payment...</p>");
458
+
459
+ $.ajax({
460
+ url: '/my-account/confirm',
461
+ type: 'post',
462
+ data: { id: that.invoice.id},
463
+ success: function(resp) {
464
+ if (resp.success == true)
465
+ {
466
+ form.slideUp(function() { form.empty(); });
467
+ message.empty().html("<p class='note success'>" + resp.message + "</p>");
468
+ setTimeout(function() { message.empty() }, 5000);
469
+ that.refresh();
470
+ }
471
+ else {
472
+ message.empty().html("<p class='note error'>" + resp.error + "</p>");
473
+ }
474
+ }
475
+ });
476
+ }
477
+ ));
478
+
479
+ form.slideDown();
480
+ },
427
481
 
428
482
  payment_form: function()
429
483
  {
430
- var that = this;
431
- var form = $('#payment_form');
484
+ var that = this;
485
+ var form = $('#payment_form');
486
+ var message = $('#payment_message')
432
487
  if (form.is(':visible'))
433
488
  {
434
489
  form.slideUp(function() { form.empty(); });
435
- $('#payment_message').empty();
490
+ message.empty();
436
491
  return;
437
492
  }
438
493
 
439
- $('#payment_message').empty().html("<p class='loading'>Getting payment form...</p>");
494
+ message.empty().html("<p class='loading'>Processing payment...</p>");
440
495
  $.ajax({
441
496
  url: '/my-account/invoices/' + that.invoice.id + '/payment-form',
442
497
  type: 'get',
443
- success: function(html) {
444
- form.empty().append(html);
445
- form.slideDown();
446
- $('#payment_message').empty();
447
- $('#payment_confirm').click(function(e) {
448
- $('#expiration').val($('#month').val() + $('#year').val());
449
- $('#payment_message').empty().html("<p class='loading'>Processing payment...</p>");
450
- $('#payment_form').slideUp();
451
- $('#payment').submit();
452
- });
498
+ success: function(resp) {
499
+ if (resp.success == true)
500
+ {
501
+ message.empty().html("<p class='note success'>" + resp.message + "</p>");
502
+ setTimeout(function() { message.empty() }, 5000);
503
+ that.refresh();
504
+ }
505
+ else {
506
+ message.empty().html("<p class='note error'>" + resp.error + "</p>");
507
+ }
508
+
509
+ // form.empty().append(html);
510
+ // form.slideDown();
511
+ // $('#payment_confirm').click(function(e) {
512
+ // $('#expiration').val($('#month').val() + $('#year').val());
513
+ // $('#payment_message').empty().html("<p class='loading'>Processing payment...</p>");
514
+ // $('#payment_form').slideUp();
515
+ // $('#payment').submit();
516
+ // });
453
517
  }
454
518
  });
455
519
  },
@@ -0,0 +1,175 @@
1
+
2
+ var MyAccountPaymentMethodController = function(params) { this.init(params); };
3
+
4
+ MyAccountPaymentMethodController.prototype = {
5
+
6
+ container: 'payment_method_container',
7
+ stripe_key: false,
8
+ customer_id: false,
9
+ card_brand: false,
10
+ card_last4: false,
11
+ card_name: false,
12
+ card_zip: false,
13
+ ic: false,
14
+
15
+ init: function(params)
16
+ {
17
+ var that = this;
18
+ for (var i in params)
19
+ that[i] = params[i];
20
+ },
21
+
22
+ refresh: function(after)
23
+ {
24
+ var that = this;
25
+ $.ajax({
26
+ url: '/checkout/stripe/json',
27
+ type: 'get',
28
+ success: function(resp) {
29
+ that.stripe_key = resp.stripe_key;
30
+ that.customer_id = resp.customer_id;
31
+ that.card_brand = resp.card_brand;
32
+ that.card_last4 = resp.card_last4;
33
+ //that.cc.print_ready_message();
34
+ if (after) after();
35
+ }
36
+ });
37
+ },
38
+
39
+ print: function()
40
+ {
41
+ var that = this;
42
+ if (!that.stripe_key)
43
+ {
44
+ that.refresh(function() { that.print(); });
45
+ return;
46
+ }
47
+ var msg = that.card_brand && that.card_last4 ? that.card_brand + ' ending in ' + that.card_last4 : 'You have no card on file.';
48
+ var div = $('<div/>');
49
+
50
+ if (that.ic.invoice.financial_status == 'pending') {
51
+ if (that.card_brand && that.card_last4) {
52
+ div.append($('<p/>').append($('<input/>').attr('type', 'button').addClass('btn').val('Confirm').click(function(e) { e.preventDefault(); that.ic.refresh_invoice( function() { that.ic.confirm_invoice(); }); })));
53
+ }
54
+ }
55
+
56
+ if (that.ic.invoice.total > 0.00)
57
+ {
58
+ var msg = that.card_brand && that.card_last4 ? that.card_brand + ' ending in ' + that.card_last4 : 'You have no card on file.';
59
+ div.append($('<p/>')
60
+ .append(msg).append(' ')
61
+ .append($('<a/>').attr('href', '#').html('Edit').click(function(e) { e.preventDefault(); that.edit(); })
62
+ ));
63
+ }
64
+ else
65
+ {
66
+ div.append($('<p/>')
67
+ .append("No payment is required at this time. ")
68
+ .append($('<a/>').attr('href', '#').html('Edit').click(function(e) { e.preventDefault(); that.edit(); })
69
+ ));
70
+ }
71
+ $('#'+that.container).empty().append(div);
72
+ },
73
+
74
+ edit: function()
75
+ {
76
+ var that = this;
77
+
78
+ if (that.ic.invoice.total <= 0.00)
79
+ {
80
+ that.print();
81
+ return;
82
+ }
83
+
84
+ var form = $('<form/>')
85
+ .attr('action', '')
86
+ .attr('method', 'post')
87
+ .attr('id', 'stripe_form')
88
+ .addClass('stripe_form')
89
+ .submit(function(e) { e.preventDefault(); that.update(); return false; });
90
+
91
+ form.append($('<div/>').addClass('card_number_container')
92
+ .append($('<input/>').attr('id', 'card_number').attr('type', 'tel').attr('autocomplete', 'off').attr('autocorrect', 'off').attr('spellcheck', 'off').attr('autocapitalize', 'off').attr('placeholder', 'Card number'))
93
+ .append($('<div/>').addClass('svg icon').css('width', '30px').css('height', '30px').html('<svg version="1.1" viewBox="0 0 30 30" width="30" height="30" focusable="false"><g fill-rule="evenodd"><path d="M2.00585866,0 C0.898053512,0 0,0.900176167 0,1.99201702 L0,9.00798298 C0,10.1081436 0.897060126,11 2.00585866,11 L11.9941413,11 C13.1019465,11 14,10.0998238 14,9.00798298 L14,1.99201702 C14,0.891856397 13.1029399,0 11.9941413,0 L2.00585866,0 Z M2.00247329,1 C1.44882258,1 1,1.4463114 1,1.99754465 L1,9.00245535 C1,9.55338405 1.45576096,10 2.00247329,10 L11.9975267,10 C12.5511774,10 13,9.5536886 13,9.00245535 L13,1.99754465 C13,1.44661595 12.544239,1 11.9975267,1 L2.00247329,1 Z M1,3 L1,5 L13,5 L13,3 L1,3 Z M11,8 L11,9 L12,9 L12,8 L11,8 Z M9,8 L9,9 L10,9 L10,8 L9,8 Z M9,8" style="fill:#3b6faa" transform="translate(8,10)"></g></svg>')));
94
+ form.append($('<div/>').addClass('card_exp_container')
95
+ .append($('<input/>').attr('id', 'card_exp').attr('type', 'tel').attr('autocomplete', 'off').attr('autocorrect', 'off').attr('spellcheck', 'off').attr('autocapitalize', 'off').attr('placeholder', 'MM / YY').attr('x-autocompletetype', 'off').attr('autocompletetype', 'off'))
96
+ .append($('<div/>').addClass('svg icon').css('width', '30px').css('height', '30px').html('<svg version="1.1" viewBox="0 0 30 30" width="30" height="30" focusable="false"><g fill-rule="evenodd"><path d="M2.0085302,1 C0.899249601,1 0,1.90017617 0,2.99201702 L0,10.007983 C0,11.1081436 0.901950359,12 2.0085302,12 L9.9914698,12 C11.1007504,12 12,11.0998238 12,10.007983 L12,2.99201702 C12,1.8918564 11.0980496,1 9.9914698,1 L2.0085302,1 Z M1.99539757,4 C1.44565467,4 1,4.43788135 1,5.00292933 L1,9.99707067 C1,10.5509732 1.4556644,11 1.99539757,11 L10.0046024,11 C10.5543453,11 11,10.5621186 11,9.99707067 L11,5.00292933 C11,4.44902676 10.5443356,4 10.0046024,4 L1.99539757,4 Z M3,1 L3,2 L4,2 L4,1 L3,1 Z M8,1 L8,2 L9,2 L9,1 L8,1 Z M3,0 L3,1 L4,1 L4,0 L3,0 Z M8,0 L8,1 L9,1 L9,0 L8,0 Z M8,0" style="fill:#3b6faa" transform="translate(8,9)"></g></svg>')));
97
+ form.append($('<div/>').addClass('card_cvc_container')
98
+ .append($('<input>').attr('id', 'card_cvc').attr('type', 'tel').attr('autocomplete', 'off').attr('autocorrect', 'off').attr('spellcheck', 'off').attr('autocapitalize', 'off').attr('placeholder', 'CVC').attr('maxlength', '4'))
99
+ .append($('<div>').addClass('svg icon').css('width', '30px').css('height', '30px').html('<svg version="1.1" viewBox="0 0 30 30" width="30" height="30" focusable="false"><g fill-rule="evenodd"><path d="M8.8,4 C8.8,1.79086089 7.76640339,4.18628304e-07 5.5,0 C3.23359661,-4.1480896e-07 2.2,1.79086089 2.2,4 L3.2,4 C3.2,2.34314567 3.81102123,0.999999681 5.5,1 C7.18897877,1.00000032 7.80000001,2.34314567 7.80000001,4 L8.8,4 Z M1.99201702,4 C0.891856397,4 0,4.88670635 0,5.99810135 L0,10.0018986 C0,11.1054196 0.900176167,12 1.99201702,12 L9.00798298,12 C10.1081436,12 11,11.1132936 11,10.0018986 L11,5.99810135 C11,4.89458045 10.0998238,4 9.00798298,4 L1.99201702,4 Z M1.99754465,5 C1.44661595,5 1,5.45097518 1,5.99077797 L1,10.009222 C1,10.5564136 1.4463114,11 1.99754465,11 L9.00245535,11 C9.55338405,11 10,10.5490248 10,10.009222 L10,5.99077797 C10,5.44358641 9.5536886,5 9.00245535,5 L1.99754465,5 Z M1.99754465,5" style="fill:#3b6faa" transform="translate(9,9)"></g></svg>')));
100
+ form.append($('<div/>').addClass('card_name_container')
101
+ .append($('<input/>').attr('id', 'card_name').attr('type', 'text').attr('autocomplete', 'off').attr('autocorrect', 'off').attr('spellcheck', 'off').attr('autocapitalize', 'on').attr('placeholder', 'Name on card')));
102
+ form.append($('<div/>').addClass('card_zip_container')
103
+ .append($('<input/>').attr('id', 'card_zip').attr('type', 'tel').attr('autocomplete', 'off').attr('autocorrect', 'off').attr('spellcheck', 'off').attr('autocapitalize', 'on').attr('placeholder', 'Zip code')));
104
+ form.append($('<div/>').attr('id', 'payment_message'))
105
+ form.append($('<p/>').addClass('payment_controls')
106
+ .append($('<input/>').attr('type', 'button').attr('id', 'cancel_payment_btn').val('Cancel' ).click(function(e) { that.print(); })).append(' ')
107
+ .append($('<input/>').attr('type', 'submit').attr('id', 'save_payment_btn').val('Save' ))
108
+ );
109
+
110
+ $('#payment_method_container').empty().append(form);
111
+
112
+ $('#stripe_form .card_number_container input').payment('formatCardNumber');
113
+ $('#stripe_form .card_exp_container input').payment('formatCardExpiry');
114
+ $('#stripe_form .card_cvc_container input').payment('formatCardCVC');
115
+ },
116
+
117
+ update: function()
118
+ {
119
+ var that = this;
120
+ var info = {
121
+ number: $('#card_number').val(),
122
+ exp: $('#card_exp').val(),
123
+ cvc: $('#card_cvc').val(),
124
+ name: $('card_name').val(),
125
+ address_zip: $('card_zip').val()
126
+ };
127
+ var exp = info.exp.split('/');
128
+ var m = exp.length > 0 ? exp[0] : '';
129
+ var y = exp.length > 1 ? exp[1] : '';
130
+ var error = false;
131
+ if (!$.payment.validateCardNumber(info.number)) error = "Invalid card number.";
132
+ if (!$.payment.validateCardExpiry(m, y)) error = "Invalid expiration date.";
133
+ if (!$.payment.validateCardCVC(info.cvc)) error = "Invalid CVC.";
134
+ if (error) { $('#payment_message').html("<p class='note error'>" + error + "</p>"); return; }
135
+
136
+ $('#save_payment_btn').attr('disabled', 'true').val('Saving card...');
137
+ Stripe.setPublishableKey(that.stripe_key);
138
+ Stripe.card.createToken(info, function(status, resp) {
139
+ if (resp.error)
140
+ {
141
+ $('#save_payment_btn').attr('disabled', 'false').val('Save Payment Method');
142
+ $('#payment_message').html("<p class='note error'>" + resp.error.message + "</p>");
143
+ }
144
+ else
145
+ {
146
+ that.card_brand = resp.card.brand;
147
+ that.card_last4 = resp.card.last4;
148
+ $.ajax({
149
+ url: '/checkout/stripe-details',
150
+ type: 'put',
151
+ data: { token: resp.id, card: resp.card },
152
+ success: function(resp2) {
153
+ if (resp2.success)
154
+ {
155
+ that.customer_id = resp2.customer_id;
156
+ that.print();
157
+ //that.cc.print_ready_message();
158
+ }
159
+ if (resp2.error) $('#payment_message').html("<p class='note error'>" + resp2.error + "</p>");
160
+ }
161
+ });
162
+ }
163
+ });
164
+ },
165
+
166
+ ready: function()
167
+ {
168
+ var that = this;
169
+ if (that.ic.invoice.total <= 0.00) return true;
170
+ if (!that.customer_id ) return false;
171
+ if (!that.card_brand ) return false;
172
+ if (!that.card_last4 ) return false;
173
+ return true;
174
+ }
175
+ };
@@ -29,7 +29,7 @@ p { margin-bottom: 10px; }
29
29
 
30
30
  #payment_form {
31
31
  margin: 0 0 20px 0;
32
- padding: 0;
32
+ padding: 5px;
33
33
  background: #EFEFEF;
34
34
  -webkit-border-radius: 5px;
35
35
  -moz-border-radius: 5px;
@@ -0,0 +1,464 @@
1
+ require 'authorize_net'
2
+
3
+ module Caboose
4
+ class CheckoutController < Caboose::ApplicationController
5
+
6
+ before_filter :ensure_line_items, :only => [:step_one, :step_two]
7
+ protect_from_forgery
8
+
9
+ def ensure_line_items
10
+ redirect_to '/checkout/empty' if @invoice.line_items.empty?
11
+ end
12
+
13
+ # @route GET /checkout/json
14
+ def invoice_json
15
+ render :json => @invoice.as_json(
16
+ :include => [
17
+ :customer,
18
+ :shipping_address,
19
+ :billing_address,
20
+ :invoice_transactions,
21
+ {
22
+ :line_items => {
23
+ :include => {
24
+ :variant => {
25
+ :include => [
26
+ { :product_images => { :methods => :urls }},
27
+ { :product => { :include => { :product_images => { :methods => :urls }}}}
28
+ ],
29
+ :methods => :title
30
+ }
31
+ }
32
+ }
33
+ },
34
+ { :invoice_packages => { :include => [:shipping_package, :shipping_method] }},
35
+ { :discounts => { :include => :gift_card }}
36
+ ]
37
+ )
38
+ end
39
+
40
+ # @route GET /checkout/stripe/json
41
+ def stripe_json
42
+ sc = @site.store_config
43
+ u = logged_in_user
44
+ render :json => {
45
+ :stripe_key => sc.stripe_publishable_key.strip,
46
+ :customer_id => u.stripe_customer_id,
47
+ :card_last4 => u.card_last4,
48
+ :card_brand => u.card_brand,
49
+ :card_exp_month => u.card_exp_month,
50
+ :card_exp_year => u.card_exp_year
51
+ }
52
+ end
53
+
54
+ #===========================================================================
55
+
56
+ # Step 1 - Login or register
57
+ # @route GET /checkout
58
+ def index
59
+ if logged_in?
60
+ if @invoice.customer_id.nil?
61
+ @invoice.customer_id = logged_in_user.id
62
+ @invoice.save
63
+ end
64
+ #redirect_to '/checkout/addresses'
65
+ #return
66
+
67
+ @invoice.verify_invoice_packages
68
+
69
+ # See if any there are any empty invoice packages
70
+ #@invoice.invoice_packages.each do |op|
71
+ # count = 0
72
+ # @invoice.line_items.each do |li|
73
+ # count = count + 1 if li.invoice_package_id == op.id
74
+ # end
75
+ # op.destroy if count == 0
76
+ #end
77
+ #
78
+ ## See if any line items aren't associated with an invoice package
79
+ #line_items_attached = true
80
+ #@invoice.line_items.each do |li|
81
+ # line_items_attached = false if li.invoice_package_id.nil?
82
+ #end
83
+ #
84
+ #ops = @invoice.invoice_packages
85
+ #if ops.count == 0 || !line_items_attached
86
+ # @invoice.calculate
87
+ # LineItem.where(:invoice_id => @invoice.id).update_all(:invoice_package_id => nil)
88
+ # InvoicePackage.where(:invoice_id => @invoice.id).destroy_all
89
+ # InvoicePackage.create_for_invoice(@invoice)
90
+ #end
91
+
92
+ #render :file => "caboose/checkout/checkout_#{@site.store_config.pp_name}"
93
+ render :file => "caboose/checkout/checkout"
94
+ end
95
+ end
96
+
97
+ # Step 3 - Shipping method
98
+ # @route GET /checkout/shipping/json
99
+ def shipping_json
100
+ render :json => { :error => 'Not logged in.' } and return if !logged_in?
101
+ render :json => { :error => 'No shippable items.' } and return if !@invoice.has_shippable_items?
102
+ render :json => { :error => 'Empty shipping address.' } and return if @invoice.shipping_address.nil?
103
+
104
+ @invoice.calculate
105
+
106
+ #ops = @invoice.invoice_packages
107
+ #if params[:recalculate_invoice_packages] || ops.count == 0
108
+ # # Remove any invoice packages
109
+ # LineItem.where(:invoice_id => @invoice.id).update_all(:invoice_package_id => nil)
110
+ # InvoicePackage.where(:invoice_id => @invoice.id).destroy_all
111
+ #
112
+ # # Calculate what shipping packages we'll need
113
+ # InvoicePackage.create_for_invoice(@invoice)
114
+ #end
115
+
116
+ # Now get the rates for those packages
117
+ rates = ShippingCalculator.rates(@invoice)
118
+ render :json => rates
119
+ end
120
+
121
+ # Step 5 - Update Stripe Details
122
+ # @route PUT /checkout/stripe-details
123
+ def update_stripe_details
124
+ render :json => false and return if !logged_in?
125
+
126
+ sc = @site.store_config
127
+ Stripe.api_key = sc.stripe_secret_key.strip
128
+
129
+ u = logged_in_user
130
+
131
+ c = nil
132
+ if u.stripe_customer_id
133
+ c = Stripe::Customer.retrieve(u.stripe_customer_id)
134
+ begin
135
+ c.source = params[:token]
136
+ c.save
137
+ rescue
138
+ c = nil
139
+ end
140
+ end
141
+
142
+ if c.nil?
143
+ c = Stripe::Customer.create(
144
+ :source => params[:token],
145
+ :email => u.email,
146
+ :metadata => { :user_id => u.id }
147
+ )
148
+ end
149
+
150
+ u.stripe_customer_id = c.id
151
+ u.card_last4 = params[:card][:last4]
152
+ u.card_brand = params[:card][:brand]
153
+ u.card_exp_month = params[:card][:exp_month]
154
+ u.card_exp_year = params[:card][:exp_year]
155
+ u.save
156
+
157
+ render :json => {
158
+ :success => true,
159
+ :customer_id => u.stripe_customer_id
160
+ }
161
+ end
162
+
163
+ # @route POST /checkout/confirm
164
+ def confirm
165
+ render :json => { :error => 'Not logged in.' } and return if !logged_in?
166
+ #render :json => { :error => 'Invalid billing address.' } and return if @invoice.billing_address.nil?
167
+ render :json => { :error => 'Invalid shipping address.' } and return if @invoice.has_shippable_items? && @invoice.shipping_address.nil?
168
+ render :json => { :error => 'Invalid shipping methods.' } and return if @invoice.has_shippable_items? && @invoice.has_empty_shipping_methods?
169
+
170
+ resp = Caboose::StdClass.new
171
+ sc = @site.store_config
172
+
173
+ # Make sure all the variants still exist
174
+ @invoice.line_items.each do |li|
175
+ v = Variant.where(:id => li.variant_id).first
176
+ if v.nil? || v.status == 'Deleted'
177
+ render :json => { :error => 'One or more of the products you are purchasing are no longer available.' }
178
+ return
179
+ end
180
+ end
181
+
182
+ error = false
183
+ requires_payment = @invoice.line_items.count > 0 && @invoice.total > 0 && @invoice.payment_terms == Invoice::PAYMENT_TERMS_PIA
184
+ if requires_payment
185
+
186
+ ot = nil
187
+ case sc.pp_name
188
+ when StoreConfig::PAYMENT_PROCESSOR_AUTHNET
189
+
190
+ when StoreConfig::PAYMENT_PROCESSOR_STRIPE
191
+ Stripe.api_key = sc.stripe_secret_key.strip
192
+ begin
193
+ c = Stripe::Charge.create(
194
+ :amount => (@invoice.total * 100).to_i,
195
+ :currency => 'usd',
196
+ :customer => logged_in_user.stripe_customer_id,
197
+ :capture => false,
198
+ :metadata => { :invoice_id => @invoice.id },
199
+ :statement_descriptor => "Invoice ##{@invoice.id}"
200
+ )
201
+ rescue Exception => ex
202
+ render :json => { :error => ex.message }
203
+ return
204
+ end
205
+ ot = Caboose::InvoiceTransaction.create(
206
+ :invoice_id => @invoice.id,
207
+ :transaction_id => c.id,
208
+ :transaction_type => c.captured ? Caboose::InvoiceTransaction::TYPE_AUTHCAP : Caboose::InvoiceTransaction::TYPE_AUTHORIZE,
209
+ :payment_processor => sc.pp_name,
210
+ :amount => c.amount/100.0,
211
+ :date_processed => DateTime.now.utc,
212
+ :success => c.status == 'succeeded'
213
+ )
214
+ end
215
+
216
+ if !ot.success
217
+ render :json => { :error => error }
218
+ return
219
+ else
220
+ @invoice.financial_status = Invoice::FINANCIAL_STATUS_AUTHORIZED
221
+ @invoice.take_gift_card_funds
222
+ end
223
+ end
224
+
225
+ @invoice.status = Invoice::STATUS_PENDING
226
+ @invoice.invoice_number = @site.store_config.next_invoice_number
227
+
228
+ # Send out emails
229
+ begin
230
+ InvoicesMailer.configure_for_site(@site.id).customer_new_invoice(@invoice).deliver
231
+ InvoicesMailer.configure_for_site(@site.id).fulfillment_new_invoice(@invoice).deliver
232
+ rescue
233
+ puts "=================================================================="
234
+ puts "Error sending out invoice confirmation emails for invoice ID #{@invoice.id}"
235
+ puts "=================================================================="
236
+ end
237
+
238
+ # Emit invoice event
239
+ Caboose.plugin_hook('invoice_authorized', @invoice) if @invoice.total > 0
240
+
241
+ # Save the invoice
242
+ @invoice.save
243
+
244
+ # Decrement quantities of variants
245
+ @invoice.decrement_quantities
246
+
247
+ # Clear the cart and re-initialize
248
+ session[:cart_id] = nil
249
+ init_cart
250
+
251
+ resp.success = true
252
+ resp.redirect = '/checkout/thanks'
253
+ render :json => resp
254
+ end
255
+
256
+ # @route GET /checkout/thanks
257
+ def thanks
258
+ @logged_in_user = logged_in_user
259
+
260
+ # Find the last invoice for the user
261
+ @last_invoice = Invoice.where(:customer_id => @logged_in_user.id).reorder("id desc").limit(1).first
262
+ add_ga_event('Ecommerce', 'Checkout', 'Payment', (@last_invoice.total*100).to_i)
263
+ end
264
+
265
+ #===========================================================================
266
+
267
+ # @route GET /checkout/state-options
268
+ def state_options
269
+ options = Caboose::States.all.collect { |abbr, state| { 'value' => abbr, 'text' => abbr }}
270
+ render :json => options
271
+ end
272
+
273
+ # @route GET /checkout/total
274
+ def verify_total
275
+ total = 0.00
276
+ if logged_in?
277
+ @invoice.calculate
278
+ total = @invoice.total
279
+ end
280
+ render :json => total.to_f
281
+ end
282
+
283
+ # @route GET /checkout/address
284
+ def address
285
+ render :json => {
286
+ :shipping_address => @invoice.shipping_address,
287
+ :billing_address => @invoice.billing_address
288
+ }
289
+ end
290
+
291
+ # @route PUT /checkout/addresses
292
+ def update_addresses
293
+
294
+ # Grab or create addresses
295
+ shipping_address = if @invoice.shipping_address then @invoice.shipping_address else Address.new end
296
+ billing_address = if @invoice.billing_address then @invoice.billing_address else Address.new end
297
+
298
+ has_shippable_items = @invoice.has_shippable_items?
299
+
300
+ # Shipping address
301
+ if has_shippable_items
302
+ shipping_address.first_name = params[:shipping][:first_name]
303
+ shipping_address.last_name = params[:shipping][:last_name]
304
+ shipping_address.company = params[:shipping][:company]
305
+ shipping_address.address1 = params[:shipping][:address1]
306
+ shipping_address.address2 = params[:shipping][:address2]
307
+ shipping_address.city = params[:shipping][:city]
308
+ shipping_address.state = params[:shipping][:state]
309
+ shipping_address.zip = params[:shipping][:zip]
310
+ end
311
+
312
+ # Billing address
313
+ if has_shippable_items && params[:use_as_billing]
314
+ billing_address.update_attributes(shipping_address.attributes)
315
+ else
316
+ billing_address.first_name = params[:billing][:first_name]
317
+ billing_address.last_name = params[:billing][:last_name]
318
+ billing_address.company = params[:billing][:company]
319
+ billing_address.address1 = params[:billing][:address1]
320
+ billing_address.address2 = params[:billing][:address2]
321
+ billing_address.city = params[:billing][:city]
322
+ billing_address.state = params[:billing][:state]
323
+ billing_address.zip = params[:billing][:zip]
324
+ end
325
+
326
+ # Save address info; generate ids
327
+ render :json => { :success => false, :errors => shipping_address.errors.full_messages, :address => 'shipping' } and return if has_shippable_items && !shipping_address.save
328
+ render :json => { :success => false, :errors => billing_address.errors.full_messages, :address => 'billing' } and return if !billing_address.save
329
+
330
+ # Associate address info with invoice
331
+ @invoice.shipping_address_id = shipping_address.id
332
+ @invoice.billing_address_id = billing_address.id
333
+
334
+ #render :json => { :redirect => 'checkout/shipping' }
335
+ render :json => { :success => @invoice.save, :errors => @invoice.errors.full_messages }
336
+ end
337
+
338
+ # @route PUT /checkout/shipping-address
339
+ def update_shipping_address
340
+ resp = Caboose::StdClass.new
341
+
342
+ # Grab or create addresses
343
+ sa = @invoice.shipping_address
344
+ if sa.nil?
345
+ sa = Address.create
346
+ @invoice.shipping_address_id = sa.id
347
+ @invoice.save
348
+ end
349
+
350
+ save = true
351
+ recalc_shipping = false
352
+ params.each do |name, value|
353
+ case name
354
+ when 'address1' then recalc_shipping = true if sa.address1 != value
355
+ when 'address2' then recalc_shipping = true if sa.address2 != value
356
+ when 'city' then recalc_shipping = true if sa.city != value
357
+ when 'state' then recalc_shipping = true if sa.state != value
358
+ when 'zip' then recalc_shipping = true if sa.zip != value
359
+ end
360
+ case name
361
+ when 'name' then sa.name = value
362
+ when 'first_name' then sa.first_name = value
363
+ when 'last_name' then sa.last_name = value
364
+ when 'street' then sa.street = value
365
+ when 'address1' then sa.address1 = value
366
+ when 'address2' then sa.address2 = value
367
+ when 'company' then sa.company = value
368
+ when 'city' then sa.city = value
369
+ when 'state' then sa.state = value
370
+ when 'province' then sa.province = value
371
+ when 'province_code' then sa.province_code = value
372
+ when 'zip' then sa.zip = value
373
+ when 'country' then sa.country = value
374
+ when 'country_code' then sa.country_code = value
375
+ when 'phone' then sa.phone = value
376
+ end
377
+ end
378
+ if recalc_shipping
379
+ @invoice.invoice_packages.each do |op|
380
+ op.shipping_method_id = nil
381
+ op.total = nil
382
+ op.save
383
+ end
384
+ end
385
+
386
+ resp.success = save && sa.save
387
+ render :json => resp
388
+ end
389
+
390
+ # @route PUT /checkout/billing-address
391
+ def update_billing_address
392
+
393
+ # Grab or create addresses
394
+ ba = @invoice.billing_address
395
+ if ba.nil?
396
+ ba = Address.create
397
+ @invoice.billing_address_id = ba.id
398
+ @invoice.save
399
+ end
400
+
401
+ ba.first_name = params[:first_name]
402
+ ba.last_name = params[:last_name]
403
+ ba.company = params[:company]
404
+ ba.address1 = params[:address1]
405
+ ba.address2 = params[:address2]
406
+ ba.city = params[:city]
407
+ ba.state = params[:state]
408
+ ba.zip = params[:zip]
409
+ ba.save
410
+
411
+ render :json => { :success => true }
412
+ end
413
+
414
+ # @route POST /checkout/attach-user
415
+ def attach_user
416
+ render :json => { :success => false, :errors => ['User is not logged in'] } and return if !logged_in?
417
+ @invoice.customer_id = logged_in_user.id
418
+ #Caboose.log("Attaching user to invoice: customer_id = #{@invoice.customer_id}")
419
+ render :json => { :success => @invoice.save, :errors => @invoice.errors.full_messages, :logged_in => logged_in? }
420
+ end
421
+
422
+ # @route POST /checkout/guest
423
+ def attach_guest
424
+ resp = Caboose::StdClass.new
425
+ email = params[:email]
426
+
427
+ if email != params[:confirm_email]
428
+ resp.error = "Emails do not match."
429
+ elsif Caboose::User.where(:email => email, :is_guest => false).exists?
430
+ resp.error = "A user with that email address already exists."
431
+ else
432
+ user = Caboose::User.where(:email => email, :is_guest => true).first
433
+ if user.nil?
434
+ user = Caboose::User.create(:email => email)
435
+ user.is_guest = true
436
+ user.save
437
+ user = Caboose::User.where(:email => email).first
438
+ end
439
+ @invoice.customer_id = user.id
440
+ login_user(user)
441
+
442
+ if !@invoice.valid?
443
+ resp.errors = @invoice.errors.full_messages
444
+ else
445
+ @invoice.save
446
+ resp.redirect = '/checkout/addresses'
447
+ end
448
+ end
449
+ render :json => resp
450
+ end
451
+
452
+ # @route PUT /checkout/shipping
453
+ def update_shipping
454
+ op = InvoicePackage.find(params[:invoice_package_id])
455
+ op.shipping_method_id = params[:shipping_method_id]
456
+ op.total = params[:total]
457
+ op.save
458
+ op.invoice.calculate
459
+
460
+ render :json => { :success => true }
461
+ end
462
+
463
+ end
464
+ end
@@ -279,7 +279,8 @@ module Caboose
279
279
  Invoice::STATUS_CART,
280
280
  Invoice::STATUS_PENDING,
281
281
  Invoice::STATUS_READY_TO_SHIP,
282
- Invoice::STATUS_SHIPPED,
282
+ Invoice::STATUS_SHIPPED,
283
+ Invoice::STATUS_PAID,
283
284
  Invoice::STATUS_CANCELED
284
285
  ]
285
286
  options = statuses.collect{ |s| { 'text' => s.capitalize, 'value' => s }}
@@ -56,10 +56,90 @@ module Caboose
56
56
 
57
57
  when 'stripe'
58
58
  # TODO: Implement manual invoice payment for stripe
59
-
60
59
  end
61
60
  render :layout => false
62
61
  end
62
+
63
+ # @route POST /my-account/confirm
64
+ def confirm
65
+ Caboose::log(params)
66
+ sc = @site.store_config
67
+ @invoice = Invoice.find(params[:id])
68
+
69
+ # Make sure all the variants still exist
70
+ @invoice.line_items.each do |li|
71
+ v = Variant.where(:id => li.variant_id).first
72
+ if v.nil? || v.status == 'Deleted'
73
+ render :json => { :error => 'One or more of the products you are purchasing are no longer available.' }
74
+ return
75
+ end
76
+ end
77
+
78
+ error = false
79
+ requires_payment = @invoice.line_items.count > 0 && @invoice.total > 0
80
+ if requires_payment
81
+ ot = nil
82
+
83
+ Stripe.api_key = sc.stripe_secret_key.strip
84
+ begin
85
+ c = Stripe::Charge.create(
86
+ :amount => (@invoice.total * 100).to_i,
87
+ :currency => 'usd',
88
+ :customer => logged_in_user.stripe_customer_id,
89
+ :capture => false,
90
+ :metadata => { :invoice_id => @invoice.id },
91
+ :statement_descriptor => "Invoice ##{@invoice.id}"
92
+ )
93
+ rescue Exception => ex
94
+ render :json => { :error => ex.message }
95
+ return
96
+ end
97
+ ot = Caboose::InvoiceTransaction.create(
98
+ :invoice_id => @invoice.id,
99
+ :transaction_id => c.id,
100
+ :transaction_type => c.captured ? Caboose::InvoiceTransaction::TYPE_AUTHCAP : Caboose::InvoiceTransaction::TYPE_AUTHORIZE,
101
+ :payment_processor => sc.pp_name,
102
+ :amount => c.amount/100.0,
103
+ :date_processed => DateTime.now.utc,
104
+ :success => c.status == 'succeeded'
105
+ )
106
+
107
+ if !ot.success
108
+ render :json => { :error => error }
109
+ return
110
+ else
111
+ capture_resp = @invoice.capture_funds
112
+ if capture_resp.success == true
113
+ @invoice.take_gift_card_funds
114
+ @invoice.status = Caboose::Invoice::STATUS_PAID
115
+ end
116
+ end
117
+ end
118
+
119
+ @invoice.invoice_number = @site.store_config.next_invoice_number
120
+
121
+ # Send out emails
122
+ begin
123
+ InvoicesMailer.configure_for_site(@site.id).customer_new_invoice(@invoice).deliver
124
+ InvoicesMailer.configure_for_site(@site.id).fulfillment_new_invoice(@invoice).deliver
125
+ rescue
126
+ puts "=================================================================="
127
+ puts "Error sending out invoice confirmation emails for invoice ID #{@invoice.id}"
128
+ puts "=================================================================="
129
+ end
130
+
131
+ # Save the invoice
132
+ @invoice.save
133
+
134
+ # Decrement quantities of variants
135
+ @invoice.decrement_quantities
136
+
137
+ render :json => {
138
+ :success => true,
139
+ :message => "Thank you for your payment!"
140
+ }
141
+ return
142
+ end
63
143
 
64
144
  # @route GET /my-account/invoices/:id/json
65
145
  def invoice_json
@@ -19,9 +19,9 @@ module Caboose
19
19
  #end
20
20
 
21
21
  # Sends a confirmation email to the customer about a new invoice
22
- def customer_new_invoice(invoice)
22
+ def customer_new_invoice(invoice)
23
23
  @invoice = invoice
24
- mail(:to => invoice.customer.email, :subject => 'Thank you for your invoice!')
24
+ mail(:to => invoice.customer.email, :subject => 'Thank you for your order!')
25
25
  end
26
26
 
27
27
  # Sends a notification email to the fulfillment dept about a new invoice
@@ -55,6 +55,7 @@ module Caboose
55
55
  STATUS_CANCELED = 'canceled'
56
56
  STATUS_READY_TO_SHIP = 'ready to ship'
57
57
  STATUS_SHIPPED = 'shipped'
58
+ STATUS_PAID = 'paid'
58
59
  STATUS_TESTING = 'testing'
59
60
 
60
61
  # New
@@ -89,6 +90,7 @@ module Caboose
89
90
  scope :pending , where('status = ?', 'pending')
90
91
  scope :canceled , where('status = ?', 'canceled')
91
92
  scope :shipped , where('status = ?', 'shipped')
93
+ scope :paid , where('status = ?', 'paid')
92
94
  scope :test , where('status = ?', 'testing')
93
95
 
94
96
  scope :authorized , where('financial_status = ?', 'authorized')
@@ -101,7 +103,7 @@ module Caboose
101
103
  #
102
104
 
103
105
  validates :status, :inclusion => {
104
- :in => ['cart', 'pending', 'canceled', 'ready to ship', 'shipped', 'testing'],
106
+ :in => ['cart', 'pending', 'canceled', 'ready to ship', 'shipped', 'paid', 'testing'],
105
107
  :message => "%{value} is not a valid status. Must be either 'pending' or 'shipped'"
106
108
  }
107
109
 
@@ -15,8 +15,11 @@ store_config = @invoice.site.store_config
15
15
  <p><input type='button' value='< Back' class='btn' onclick="window.location='/my-account/invoices';" /></p>
16
16
 
17
17
  <% content_for :caboose_js do %>
18
+ <%= javascript_include_tag 'https://js.stripe.com/v2/' %>
19
+ <%= javascript_include_tag 'caboose/jquery.payment' %>
18
20
  <%= javascript_include_tag 'caboose/model/all' %>
19
21
  <%= javascript_include_tag 'caboose/my_account_edit_invoice' %>
22
+ <%= javascript_include_tag 'caboose/my_account_payment_method_controller' %>
20
23
  <script type='text/javascript'>
21
24
 
22
25
  var controller = false;
@@ -32,4 +35,20 @@ $(document).ready(function() {
32
35
 
33
36
  <% content_for :caboose_css do %>
34
37
  <%= stylesheet_link_tag 'caboose/my_account_edit_invoice' %>
38
+ <style type='text/css'>
39
+ .stripe_form { width: 100%; }
40
+ .stripe_form .card_number_container { position: relative; width: 100%; } .stripe_form .card_number_container input { padding-left: 30px; height: 37px; font-size: 15px; width: 100%; border-color: #b9b9b9; border-style: solid; border-width: 1px 1px 0px 1px; }
41
+ .stripe_form .card_exp_container { position: relative; width: 50% !important; float: left; } .stripe_form .card_exp_container input { padding-left: 30px; height: 37px; font-size: 15px; width: 100%; border-color: #b9b9b9; border-style: solid; border-width: 1px 1px 0px 1px; }
42
+ .stripe_form .card_cvc_container { position: relative; width: 50%; float: left; } .stripe_form .card_cvc_container input { padding-left: 30px; height: 37px; font-size: 15px; width: 100%; border-color: #b9b9b9; border-style: solid; border-width: 1px 1px 0px 0px; }
43
+ .stripe_form .card_name_container { position: relative; width: 50%; float: left; } .stripe_form .card_name_container input { padding-left: 10px; height: 37px !important; font-size: 15px; width: 100%; border-color: #b9b9b9; border-style: solid; border-width: 1px 0px 1px 1px; }
44
+ .stripe_form .card_zip_container { position: relative; width: 50%; float: left; margin-bottom: 4px; } .stripe_form .card_zip_container input { padding-left: 10px; height: 37px; font-size: 15px; width: 100%; border-color: #b9b9b9; border-style: solid; border-width: 1px 1px 1px 0px; }
45
+
46
+ .stripe_form .card_number_container .icon { position: absolute; top: 3px; left: 1px; transform-origin: 50% 50% 0; pointer-events: none; }
47
+ .stripe_form .card_exp_container .icon { position: absolute; top: 3px; left: 1px; transform-origin: 50% 50% 0; pointer-events: none; }
48
+ .stripe_form .card_cvc_container .icon { position: absolute; top: 3px; left: 1px; transform-origin: 50% 50% 0; pointer-events: none; }
49
+
50
+ .stripe_form .note { width: 100%; margin-bottom: 10px !important; text-align: center; }
51
+ .stripe_form .payment_controls { clear: left; margin-top: 4px !important; }
52
+
53
+ </style>
35
54
  <% end %>
@@ -1,3 +1,3 @@
1
1
  module Caboose
2
- VERSION = '0.8.64'
2
+ VERSION = '0.8.65'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: caboose-cms
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.64
4
+ version: 0.8.65
5
5
  platform: ruby
6
6
  authors:
7
7
  - William Barry
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-09-02 00:00:00.000000000 Z
11
+ date: 2016-09-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg
@@ -600,6 +600,7 @@ files:
600
600
  - app/assets/javascripts/caboose/model/pager.js
601
601
  - app/assets/javascripts/caboose/model/s3.js
602
602
  - app/assets/javascripts/caboose/my_account_edit_invoice.js
603
+ - app/assets/javascripts/caboose/my_account_payment_method_controller.js
603
604
  - app/assets/javascripts/caboose/product.js
604
605
  - app/assets/javascripts/caboose/product_new.js
605
606
  - app/assets/javascripts/caboose/product_old.js
@@ -743,6 +744,7 @@ files:
743
744
  - app/assets/templates/caboose/product/images.jst.ejs
744
745
  - app/assets/templates/caboose/product/images_old.jst.ejs
745
746
  - app/assets/templates/caboose/product/options.jst.ejs
747
+ - app/controllers/caboose/#checkout_controller.rb#
746
748
  - app/controllers/caboose/ab_options_controller.rb
747
749
  - app/controllers/caboose/ab_variants_controller.rb
748
750
  - app/controllers/caboose/admin_controller.rb