diff --git a/ruby/lib/jam_ruby/models/shopping_cart.rb b/ruby/lib/jam_ruby/models/shopping_cart.rb
index 24f4db02c..4faffae08 100644
--- a/ruby/lib/jam_ruby/models/shopping_cart.rb
+++ b/ruby/lib/jam_ruby/models/shopping_cart.rb
@@ -8,7 +8,7 @@ module JamRuby
validates :cart_id, presence: true
validates :cart_type, presence: true
validates :cart_class_name, presence: true
- validates :marked_for_redeem, :inclusion => {:in => [true, false]}
+ validates :marked_for_redeem, numericality: {only_integer: true}
default_scope order('created_at DESC')
diff --git a/ruby/lib/jam_ruby/recurly_client.rb b/ruby/lib/jam_ruby/recurly_client.rb
index 955f702fe..69b8d5a45 100644
--- a/ruby/lib/jam_ruby/recurly_client.rb
+++ b/ruby/lib/jam_ruby/recurly_client.rb
@@ -38,6 +38,8 @@ module JamRuby
def get_account(current_user)
current_user && current_user.recurly_code ? Recurly::Account.find(current_user.recurly_code) : nil
+ rescue Recurly::Error => x
+ raise RecurlyClientError, x.to_s
end
def update_account(current_user, billing_info=nil)
diff --git a/web/app/assets/javascripts/checkout_order.js b/web/app/assets/javascripts/checkout_order.js
index c232f4c07..5ca574c7e 100644
--- a/web/app/assets/javascripts/checkout_order.js
+++ b/web/app/assets/javascripts/checkout_order.js
@@ -221,7 +221,7 @@
else {
$("#order_error").text(xhr.responseText).removeClass("hidden")
}
- $orderContent.find(".place-order").on('click', placeOrder)
+ $screen.find(".place-order").on('click', placeOrder).removeClass('disabled')
}
function moveToThanks(purchaseResponse) {
diff --git a/web/app/assets/javascripts/checkout_payment.js b/web/app/assets/javascripts/checkout_payment.js
index c492ddf3e..69f306b50 100644
--- a/web/app/assets/javascripts/checkout_payment.js
+++ b/web/app/assets/javascripts/checkout_payment.js
@@ -61,13 +61,22 @@
if(user) {
user.done(populateAccountInfo).error(app.ajaxError);
}
+ else {
+ $reuseExistingCardChk.iCheck('uncheck').attr('checked', false)
+ if(gon.global.one_free_jamtrack_per_user) {
+ $freeJamTrackPrompt.removeClass('hidden')
+ }
+ else {
+ $noFreeJamTrackPrompt.removeClass('hidden')
+ }
+ }
})
}
function populateAccountInfo(user) {
userDetail = user;
- $reuseExistingCardChk.iCheck(userDetail.reuse_card ? 'check' : 'uncheck').attr('checked', userDetail.reuse_card)
+ $reuseExistingCardChk.iCheck(userDetail.reuse_card && userDetail.has_recurly_account ? 'check' : 'uncheck').attr('checked', userDetail.reuse_card)
// show appropriate prompt text based on whether user has a free jamtrack
if(user.free_jamtrack) {
@@ -151,7 +160,6 @@
var reuse_card_this_time = $reuseExistingCardChk.is(':checked');
var reuse_card_next_time = $paymentMethod.find('#save-card').is(':checked');
-
// validation
var billing_first_name = $billingInfo.find("#billing-first-name").val();
var billing_last_name = $billingInfo.find("#billing-last-name").val();
@@ -162,13 +170,14 @@
var billing_zip = $billingInfo.find("#billing-zip").val();
var billing_country = $billingInfo.find("#billing-country").val();
+ var billingInfoValid = true;
if (!billing_first_name) {
$billingInfo.find('#divBillingFirstName .error-text').remove();
- $billingInfo.find('#divBillingFirstName').addClass("error");
+ $billingInfo.find('#divBillingFirstName').addClass("error").addClass("transparent");
$billingInfo.find('#billing-first-name').after("
");
logger.info("no billing first name");
- return false;
+ billingInfoValid = false;
}
else {
$billingInfo.find('#divBillingFirstName').removeClass("error");
@@ -176,11 +185,11 @@
if (!billing_last_name) {
$billingInfo.find('#divBillingLastName .error-text').remove();
- $billingInfo.find('#divBillingLastName').addClass("error");
+ $billingInfo.find('#divBillingLastName').addClass("error").addClass("transparent");
$billingInfo.find('#billing-last-name').after("");
logger.info("no billing last name");
- return false;
+ billingInfoValid = false;
}
else {
$billingInfo.find('#divBillingLastName').removeClass("error");
@@ -188,11 +197,11 @@
if (!billing_address1) {
$billingInfo.find('#divBillingAddress1 .error-text').remove();
- $billingInfo.find('#divBillingAddress1').addClass("error");
+ $billingInfo.find('#divBillingAddress1').addClass("error").addClass("transparent");
$billingInfo.find('#billing-address1').after("");
logger.info("no billing address line 1");
- return false;
+ billingInfoValid = false;
}
else {
$billingInfo.find('#divBillingAddress1').removeClass("error");
@@ -200,11 +209,11 @@
if (!billing_zip) {
$billingInfo.find('#divBillingZip .error-text').remove();
- $billingInfo.find('#divBillingZip').addClass("error");
- $billingInfo.find('#billing-zip').after("");
+ $billingInfo.find('#divBillingZip').addClass("error").addClass("transparent");
+ $billingInfo.find('#billing-zip').after("");
- logger.info("no billing address line 2");
- return false;
+ logger.info("no billing zip");
+ billingInfoValid = false;
}
else {
$billingInfo.find('#divBillingZip').removeClass("error");
@@ -212,11 +221,11 @@
if (!billing_state) {
$billingInfo.find('#divBillingState .error-text').remove();
- $billingInfo.find('#divBillingState').addClass("error");
- $billingInfo.find('#billing-zip').after("");
+ $billingInfo.find('#divBillingState').addClass("error").addClass("transparent");
+ $billingInfo.find('#billing-state').after("");
- logger.info("no billing zip");
- return false;
+ logger.info("no billing state");
+ billingInfoValid = false;
}
else {
$billingInfo.find('#divBillingState').removeClass("error");
@@ -224,11 +233,11 @@
if (!billing_city) {
$billingInfo.find('#divBillingCity .error-text').remove();
- $billingInfo.find('#divBillingCity').addClass("error");
+ $billingInfo.find('#divBillingCity').addClass("error").addClass("transparent");
$billingInfo.find('#billing-city').after("");
logger.info("no billing city");
- return false;
+ billingInfoValid = false;
}
else {
$billingInfo.find('#divBillingCity').removeClass("error");
@@ -236,11 +245,11 @@
if (!billing_country) {
$billingInfo.find('#divBillingCountry .error-text').remove();
- $billingInfo.find('#divBillingCountry').addClass("error");
+ $billingInfo.find('#divBillingCountry').addClass("error").addClass("transparent");
$billingInfo.find('#billing-country').after("");
logger.info("no billing country");
- return false;
+ billingInfoValid = false;
}
else {
$billingInfo.find('#divBillingCountry').removeClass("error");
@@ -263,7 +272,7 @@
if (!shipping_first_name) {
$shippingAddress.find('#divShippingFirstName .error-text').remove();
- $shippingAddress.find('#divShippingFirstName').addClass("error");
+ $shippingAddress.find('#divShippingFirstName').addClass("error").addClass("transparent");
$shippingAddress.find('#shipping-first-name').after("");
logger.info("no address first name");
@@ -275,7 +284,7 @@
if (!shipping_last_name) {
$shippingAddress.find('#divShippingLastName .error-text').remove();
- $shippingAddress.find('#divShippingLastName').addClass("error");
+ $shippingAddress.find('#divShippingLastName').addClass("error").addClass("transparent");
$shippingAddress.find('#shipping-last-name').after("");
logger.info("no last name");
@@ -287,7 +296,7 @@
if (!shipping_address1) {
$shippingAddress.find('#divShippingAddress1 .error-text').remove();
- $shippingAddress.find('#divShippingAddress1').addClass("error");
+ $shippingAddress.find('#divShippingAddress1').addClass("error").addClass("transparent");
$shippingAddress.find('#shipping-address1').after("");
logger.info("no shipping address 1");
@@ -299,7 +308,7 @@
if (!shipping_zip) {
$shippingAddress.find('#divShippingZip .error-text').remove();
- $shippingAddress.find('#divShippingZip').addClass("error");
+ $shippingAddress.find('#divShippingZip').addClass("error").addClass("transparent");
$shippingAddress.find('#shipping-zip').after("");
logger.info("no shipping address 2");
@@ -311,7 +320,7 @@
if (!shipping_state) {
$shippingAddress.find('#divShippingState .error-text').remove();
- $shippingAddress.find('#divShippingState').addClass("error");
+ $shippingAddress.find('#divShippingState').addClass("error").addClass("transparent");
$shippingAddress.find('#shipping-zip').after("");
logger.info("no shipping state");
@@ -323,7 +332,7 @@
if (!shipping_city) {
$shippingAddress.find('#divShippingCity .error-text').remove();
- $shippingAddress.find('#divShippingCity').addClass("error");
+ $shippingAddress.find('#divShippingCity').addClass("error").addClass("transparent");
$shippingAddress.find('#shipping-city').after("");
logger.info("no shipping city");
@@ -335,7 +344,7 @@
if (!shipping_country) {
$shippingAddress.find('#divShippingCountry .error-text').remove();
- $shippingAddress.find('#divShippingCountry').addClass("error");
+ $shippingAddress.find('#divShippingCountry').addClass("error").addClass("transparent");
$shippingAddress.find('#shipping-country').after("");
logger.info("no shipping country");
@@ -356,7 +365,7 @@
/**
if (!card_name) {
$paymentMethod.find('#divCardName .error-text').remove();
- $paymentMethod.find('#divCardName').addClass("error");
+ $paymentMethod.find('#divCardName').addClass("error").addClass("transparent");
$paymentMethod.find('#card-name').after("");
return false;
} else {
@@ -367,48 +376,54 @@
if(!reuse_card_this_time) {
if (!card_number) {
$paymentMethod.find('#divCardNumber .error-text').remove();
- $paymentMethod.find('#divCardNumber').addClass("error");
+ $paymentMethod.find('#divCardNumber').addClass("error").addClass("transparent");
$paymentMethod.find('#card-number').after("");
logger.info("no card number");
- return false;
+ billingInfoValid = false;
} else if (!$.payment.validateCardNumber(card_number)) {
$paymentMethod.find('#divCardNumber .error-text').remove();
- $paymentMethod.find('#divCardNumber').addClass("error");
+ $paymentMethod.find('#divCardNumber').addClass("error").addClass("transparent");
$paymentMethod.find('#card-number').after("");
logger.info("invalid card number");
- return false;
+ billingInfoValid = false;
} else {
$paymentMethod.find('#divCardNumber').removeClass("error");
}
if (!$.payment.validateCardExpiry(card_month, card_year)) {
$paymentMethod.find('#divCardExpiry .error-text').remove();
- $paymentMethod.find('#divCardExpiry').addClass("error");
+ $paymentMethod.find('#divCardExpiry').addClass("error").addClass("transparent");
$paymentMethod.find('#card-expiry').after("");
logger.info("invalid card expiry");
- return false;
+ billingInfoValid = false;
} else {
$paymentMethod.find('#divCardExpiry').removeClass("error");
}
if (!card_verify) {
$paymentMethod.find('#divCardVerify .error-text').remove();
- $paymentMethod.find('#divCardVerify').addClass("error");
+ $paymentMethod.find('#divCardVerify').addClass("error").addClass("transparent");
$paymentMethod.find('#card-verify').after("- Card Verification Value is required
");
logger.info("no card verify");
- return false;
+ billingInfoValid = false;
} else if (!$.payment.validateCardCVC(card_verify)) {
$paymentMethod.find('#divCardVerify .error-text').remove();
- $paymentMethod.find('#divCardVerify').addClass("error");
+ $paymentMethod.find('#divCardVerify').addClass("error").addClass("transparent");
$paymentMethod.find('#card-verify').after("- Card Verification Value is not valid.
");
logger.info("bad card CVC");
- return false;
+ billingInfoValid = false;
} else {
$paymentMethod.find('#divCardVerify').removeClass("error");
}
}
+
+ if(!billingInfoValid) {
+ logger.debug("billing info is invalid. returning");
+ return false;
+ }
+
billing_info = {};
shipping_info = {};
billing_info.first_name = billing_first_name;
@@ -480,34 +495,34 @@
$.each(xhr.responseJSON.errors, function(key, error) {
if (key == 'number') {
$paymentMethod.find('#divCardNumber .error-text').remove();
- $paymentMethod.find('#divCardNumber').addClass("error");
+ $paymentMethod.find('#divCardNumber').addClass("error").addClass("transparent");
$paymentMethod.find('#card-number').after("");
}
else if (key == 'verification_value') {
$paymentMethod.find('#divCardVerify .error-text').remove();
- $paymentMethod.find('#divCardVerify').addClass("error");
+ $paymentMethod.find('#divCardVerify').addClass("error").addClass("transparent");
$paymentMethod.find('#card-verify').after("");
}
else if(key == 'email') {
var $email = $accountSignup.find('input[name="email"]')
var $field = $email.closest('.field')
$field.find('.error-text').remove()
- $field.addClass("error");
- $field.append("");
+ $field.addClass("error").addClass("transparent");
+ $email.after("");
}
else if(key == 'password') {
var $password = $accountSignup.find('input[name="password"]')
var $field = $password.closest('.field')
$field.find('.error-text').remove()
- $field.addClass("error");
- $field.append("");
+ $field.addClass("error").addClass("transparent");
+ $password.after("");
}
else if(key == 'terms_of_service') {
var $terms = $accountSignup.find('input[name="terms-of-service"]')
var $field = $terms.closest('.field')
$field.find('.error-text').remove()
- $field.addClass("error");
- $field.append("");
+ $field.addClass("error").addClass("transparent");
+ $accountSignup.find('.terms-of-service-label-holder').append("");
}
});
}
diff --git a/web/app/assets/stylesheets/client/checkout_payment.css.scss b/web/app/assets/stylesheets/client/checkout_payment.css.scss
index 97c17c359..0f86dd0cf 100644
--- a/web/app/assets/stylesheets/client/checkout_payment.css.scss
+++ b/web/app/assets/stylesheets/client/checkout_payment.css.scss
@@ -16,6 +16,16 @@
line-height:125%;
}
+ .field.error {
+ background-color: transparent !important;
+ padding: 0 !important;
+ border-width:0 !important;
+
+ li {
+ list-style:none;
+ }
+ }
+
h2 {
color:white;
background-color:#4d4d4d;
diff --git a/web/app/assets/stylesheets/client/jamkazam.css.scss b/web/app/assets/stylesheets/client/jamkazam.css.scss
index 2aa254437..0269adfd2 100644
--- a/web/app/assets/stylesheets/client/jamkazam.css.scss
+++ b/web/app/assets/stylesheets/client/jamkazam.css.scss
@@ -392,6 +392,24 @@ textarea {
padding:5px;
border: solid 1px #900;
+ .error-text {
+ display:block;
+ font-size:11px;
+ color:#F00;
+ margin:10px 0 0;
+ }
+
+ &.transparent {
+ background-color:transparent;
+ padding:0;
+ border-width:0px;
+
+ .error-text {
+ margin:5px 0 0;
+ font-size:14px;
+ font-weight:bold;
+ }
+ }
}
.error input {
@@ -403,12 +421,6 @@ textarea {
display:none;
}
-.error .error-text {
- display:block;
- font-size:11px;
- color:#F00;
- margin:10px 0 0;
-}
.grey {
color:#999;
}
diff --git a/web/spec/features/checkout_spec.rb b/web/spec/features/checkout_spec.rb
index adff4c750..1099fc0ad 100644
--- a/web/spec/features/checkout_spec.rb
+++ b/web/spec/features/checkout_spec.rb
@@ -17,33 +17,63 @@ describe "Checkout", :js => true, :type => :feature, :capybara_feature => true d
JamTrack.delete_all
JamTrackTrack.delete_all
JamTrackLicensor.delete_all
+ User.delete_all
stub_const("APP_CONFIG", web_config)
end
+ def verify_nav(selected)
+
+ 3.times do |i|
+ badge = i + 1
+
+ if i == selected
+ find('.badge-number', text:badge)
+ else
+ find('.badge-number.disabled', text:badge)
+ end
+ end
+ end
+
describe "Checkout Signin" do
- it "allows user to log in" do
+ it "allows user to log in on the signin page" do
visit '/client#/checkoutSignin'
find('h3', text: 'ALREADY A MEMBER OF THE JAMKAZAM COMMUNITY?')
+ verify_nav(1)
+
# try a bogus user/pass first
fill_in "email", with: user.email
fill_in "password", with: 'wrong'
+ find('.signin-submit').trigger(:click)
find('.login-error-msg', text: 'Invalid login')
# try successfully
fill_in "email", with: user.email
fill_in "password", with: user.password
+ find('.signin-submit').trigger(:click)
-
+ # this should take us to the payment screen
+ find('p.payment-prompt')
+ end
+ it "allows user to skip login and go to payment screen" do
+ visit '/client#/checkoutSignin'
+ find('h3', text: 'ALREADY A MEMBER OF THE JAMKAZAM COMMUNITY?')
+ verify_nav(1)
+
+ # skip to payment without signing in
+ find('a.btnNext').trigger(:click)
+
+ # this should take us to the payment screen
+ find('p.payment-prompt')
end
it "indicates already logged in" do
@@ -51,6 +81,9 @@ describe "Checkout", :js => true, :type => :feature, :capybara_feature => true d
# verify that the signin page shows, but indicates to the user that they are already signed in
find('h3', text: 'YOU ARE ALREADY LOGGED IN')
+
+ verify_nav(1)
+
find('p.carry-on-prompt', text: 'You can move on to the next step of checkout.')
# let them move on to the next step
@@ -59,4 +92,146 @@ describe "Checkout", :js => true, :type => :feature, :capybara_feature => true d
end
end
+ describe "Checkout Payment" do
+ it "allows anonymous to visit" do
+
+ visit '/client#/checkoutPayment'
+
+ find('p.payment-prompt.free-jamtrack')
+
+ find('.jamkazam-account-signup')
+
+ # try to submit, and see slew of errors
+ find('#payment-info-next').trigger(:click)
+
+ find('#divBillingFirstName.error .error-text', text: 'First Name is required')
+ find('#divBillingLastName.error .error-text', text: 'Last Name is required')
+ find('#divBillingAddress1.error .error-text', text: 'Address is required')
+ find('#divBillingCity.error .error-text', text: 'City is required')
+ find('#divBillingState.error .error-text', text: 'State is required')
+ find('#divBillingZip.error .error-text', text: 'Zip Code is required')
+ find('#divCardNumber.error .error-text', text: 'Card Number is required')
+ find('#divCardVerify.error .error-text', text: 'Card Verification Value is required')
+
+ # fill out all billing info, but not account info
+ fill_in 'billing-first-name', with: 'Seth'
+ fill_in 'billing-last-name', with: 'Call'
+ fill_in 'billing-address1', with: '10704 Buckthorn Drive'
+ fill_in 'billing-city', with: 'Austin'
+ fill_in 'billing-state', with: 'Texas'
+ fill_in 'billing-zip', with: '78759'
+ fill_in 'card-number', with: '4111111111111111'
+ fill_in 'card-verify', with: '012'
+
+ # try to submit, and see new account errors
+ find('#payment-info-next').trigger(:click)
+
+ find('#divJamKazamEmail.error .error-text', text: "can't be blank,is invalid")
+ find('#divJamKazamPassword.error .error-text', text: "is too short (minimum is 6 characters)")
+ find('#divJamKazamTos.error .error-text', text: "must be accepted")
+
+ # verify that all filled out fields have had errors removed
+ find('#divBillingFirstName').has_no_css?('.error')
+ find('#divBillingLastName').has_no_css?('.error')
+ find('#divBillingAddress1').has_no_css?('.error')
+ find('#divBillingCity').has_no_css?('.error')
+ find('#divBillingState').has_no_css?('.error')
+ find('#divBillingZip').has_no_css?('.error')
+ find('#divCardNumber').has_no_css?('.error')
+ find('#divCardVerify').has_no_css?('.error')
+
+ # fill in user/email/tos
+ fill_in 'email', with: 'seth@jamkazam.com'
+ fill_in 'password', with: 'jam123'
+ find('#divJamKazamTos ins.iCheck-helper').trigger(:click) # accept TOS
+
+ # try to submit, and see order page
+ find('#payment-info-next').trigger(:click)
+
+ # find empty shopping cart prompt notice
+ find('p.empty-cart-prompt')
+
+ user.reload
+ user.reuse_card.should be_true
+ end
+
+ it "allows billing info submit for existing user" do
+
+ fast_signin(user, '/client#/checkoutPayment')
+
+ find('p.payment-prompt.free-jamtrack')
+
+ expect(page).to_not have_selector('.jamkazam-account-signup')
+
+ # try to submit, and see slew of errors
+ find('#payment-info-next').trigger(:click)
+
+ find('#divBillingAddress1.error .error-text', text: 'Address is required')
+ find('#divBillingZip.error .error-text', text: 'Zip Code is required')
+ find('#divCardNumber.error .error-text', text: 'Card Number is required')
+ find('#divCardVerify.error .error-text', text: 'Card Verification Value is required')
+
+ # fill out all billing info, but not account info
+ fill_in 'billing-first-name', with: 'Seth'
+ fill_in 'billing-last-name', with: 'Call'
+ fill_in 'billing-address1', with: '10704 Buckthorn Drive'
+ fill_in 'billing-city', with: 'Austin'
+ fill_in 'billing-state', with: 'Texas'
+ fill_in 'billing-zip', with: '78759'
+ fill_in 'card-number', with: '4111111111111111'
+ fill_in 'card-verify', with: '012'
+
+ # try to submit, and see order page
+ find('#payment-info-next').trigger(:click)
+
+ # find empty shopping cart prompt notice
+ find('p.empty-cart-prompt')
+
+ user.reload
+ user.reuse_card.should be_true
+ end
+
+ it "allows user to specify don't save card" do
+
+ fast_signin(user, '/client#/checkoutPayment')
+
+ find('p.payment-prompt.free-jamtrack')
+
+ expect(page).to_not have_selector('.jamkazam-account-signup')
+
+ # fill out all billing info, but not account info
+ fill_in 'billing-first-name', with: 'Seth'
+ fill_in 'billing-last-name', with: 'Call'
+ fill_in 'billing-address1', with: '10704 Buckthorn Drive'
+ fill_in 'billing-city', with: 'Austin'
+ fill_in 'billing-state', with: 'Texas'
+ fill_in 'billing-zip', with: '78759'
+ fill_in 'card-number', with: '4111111111111111'
+ fill_in 'card-verify', with: '012'
+ find('.save-card-checkbox ins.iCheck-helper').trigger(:click) # don't accept re-use card default
+
+ # try to submit, and see order page
+ find('#payment-info-next').trigger(:click)
+
+ # find empty shopping cart prompt notice
+ find('p.empty-cart-prompt')
+
+ user.reload
+ user.reuse_card.should be_false
+ end
+
+ it "payment shows saved card info correctly if user has billing info and reuse_card set to true" do
+
+ user.reuse_card = true
+ user.recurly_code = user.id
+ user.has_redeemable_jamtrack = false
+ user.save!
+
+ fast_signin(user, '/client#/checkoutPayment')
+
+ find('p.payment-prompt.no-free-jamtrack')
+
+ end
+ end
+
end