caboose-cms 0.8.64 → 0.8.65

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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