This commit is contained in:
Seth Call 2015-03-20 08:48:00 -05:00
parent 137c6faedc
commit 661c4ed446
46 changed files with 1431 additions and 362 deletions

View File

@ -64,7 +64,7 @@ ActiveAdmin.register JamRuby::JamTrackRight, :as => 'JamTrackRights' do
begin
client.find_or_create_account(user, billing_info)
client.place_order(user, jam_track)
client.place_order(user, jam_track, nil)
rescue RecurlyClientError=>x
redirect_to admin_jam_track_rights_path, notice: "Could not order #{jam_track} for #{user.to_s}: #{x.errors.inspect}"
else

View File

@ -11,7 +11,7 @@ FactoryGirl.define do
state "NC"
country "US"
terms_of_service true
resue_card true
reuse_card true
factory :admin do

View File

@ -262,4 +262,4 @@ jam_track_importer.sql
jam_track_pro_licensing_update.sql
jam_track_redeemed.sql
shopping_cart_anonymous.sql
user_reuse_card.sql
user_reuse_card_and_reedem.sql

View File

@ -1 +0,0 @@
ALTER TABLE users ADD COLUMN reuse_card BOOLEAN DEFAULT TRUE NOT NULL;

View File

@ -0,0 +1,3 @@
ALTER TABLE users ADD COLUMN reuse_card BOOLEAN DEFAULT TRUE NOT NULL;
ALTER TABLE users ADD COLUMN has_redeemable_jamtrack BOOLEAN DEFAULT TRUE NOT NULL;
ALTER TABLE shopping_carts ADD COLUMN marked_for_redeem INTEGER DEFAULT 0 NOT NULL;

View File

@ -21,5 +21,9 @@ module JamRuby
def admin
false
end
def has_redeemable_jamtrack
false
end
end
end

View File

@ -141,12 +141,14 @@ module JamRuby
recording.recorded_tracks.each do |recorded_track|
manifest["files"] << { "filename" => recorded_track.sign_url(one_day), "codec" => "vorbis", "offset" => 0 }
mix_params << { "level" => 1.0, "balance" => 0 }
mix_params << { "level" => 100, "balance" => 0 }
# change to 1.0 when ready to deploy new audiomixer
end
recording.recorded_backing_tracks.each do |recorded_backing_track|
manifest["files"] << { "filename" => recorded_backing_track.sign_url(one_day), "codec" => "vorbis", "offset" => 0 }
mix_params << { "level" => 1.0, "balance" => 0 }
mix_params << { "level" => 100, "balance" => 0 }
# change to 1.0 when ready to deploy new audiomixer
end
recording.recorded_jam_track_tracks.each do |recorded_jam_track_track|

View File

@ -8,19 +8,32 @@ 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]}
default_scope order('created_at DESC')
def product_info
product = self.cart_product
{name: product.name, price: product.price, product_id: cart_id} unless product.nil?
{name: product.name, price: product.price, product_id: cart_id, plan_code: product.plan_code, total_price: total_price(product), quantity: quantity, marked_for_redeem:marked_for_redeem} unless product.nil?
end
# multiply (quantity - redeemable) by price
def total_price(product)
(quantity - marked_for_redeem) * product.price
end
def cart_product
self.cart_class_name.classify.constantize.find_by_id self.cart_id unless self.cart_class_name.blank?
end
def self.create user, product, quantity = 1
def redeem(mark_redeem)
self.marked_for_redeem = mark_redeem ? 1 : 0
end
def free?
marked_for_redeem == quantity
end
def self.create user, product, quantity = 1, mark_redeem = false
cart = ShoppingCart.new
if user.is_a?(User)
cart.user = user
@ -32,8 +45,27 @@ module JamRuby
cart.cart_class_name = product.class.name
cart.cart_id = product.id
cart.quantity = quantity
cart.redeem(mark_redeem)
cart.save
cart
end
# if the user has a redeemable jam_track still on their account, then also check if any shopping carts have already been marked.
# if no shpping carts have been marked, then mark it redeemable
# should be wrapped in a TRANSACTION
def self.user_has_redeemable_jam_track?(any_user)
mark_redeem = false
if APP_CONFIG.one_free_jamtrack_per_user && any_user.has_redeemable_jamtrack
mark_redeem = true # start out assuming we can redeem...
shopping_carts.each do |shopping_cart|
# but if we find any shopping cart item already marked for redeem, then back out of mark_redeem=true
if shopping_cart.cart_type == product.class::PRODUCT_TYPE && shopping_cart.marked_for_redeem > 0
mark_redeem = false
break
end
end
end
mark_redeem
end
end
end

View File

@ -172,6 +172,7 @@ module JamRuby
validates :terms_of_service, :acceptance => {:accept => true, :on => :create, :allow_nil => false }
validates :reuse_card, :inclusion => {:in => [true, false]}
validates :has_redeemable_jamtrack, :inclusion => {:in => [true, false]}
validates :subscribe_email, :inclusion => {:in => [nil, true, false]}
validates :musician, :inclusion => {:in => [true, false]}
validates :show_whats_next, :inclusion => {:in => [nil, true, false]}

View File

@ -4,7 +4,7 @@ module JamRuby
def initialize()
end
def create_account(current_user, billing_info=nil)
def create_account(current_user, billing_info)
options = account_hash(current_user, billing_info)
account = nil
begin
@ -37,7 +37,7 @@ module JamRuby
end
def get_account(current_user)
(current_user && current_user.recurly_code) ? Recurly::Account.find(current_user.recurly_code) : nil
current_user && current_user.recurly_code ? Recurly::Account.find(current_user.recurly_code) : nil
end
def update_account(current_user, billing_info=nil)
@ -53,12 +53,12 @@ module JamRuby
account
end
def update_billing_info(current_user, billing_info=nil)
def update_billing_info(current_user, billing_info)
account = get_account(current_user)
if (account.present?)
begin
account.billing_info=billing_info
account.billing_info.save
account.billing_info = billing_info
account.billing_info.save
rescue Recurly::Error, NoMethodError => x
raise RecurlyClientError, x.to_s
end
@ -145,7 +145,7 @@ module JamRuby
raise RecurlyClientError.new(plan.errors) if plan.errors.any?
end
def place_order(current_user, jam_track)
def place_order(current_user, jam_track, shopping_cart)
jam_track_right = nil
account = get_account(current_user)
if (account.present?)
@ -163,10 +163,17 @@ module JamRuby
# this means we already have a subscription, so don't try to create a new one for the same plan (Recurly would fail this anyway)
unless recurly_subscription_uuid
subscription = Recurly::Subscription.create(:account=>account, :plan_code=>jam_track.plan_code)
# if the shopping cart was specified, see if the item should be free
free = shopping_cart.nil? ? false : shopping_cart.free?
# and if it's free, squish the charge to 0.
unit_amount_in_cents = free ? 0 : nil
subscription = Recurly::Subscription.create(:account=>account, :plan_code=>jam_track.plan_code, unit_amount_in_cents: unit_amount_in_cents)
raise RecurlyClientError.new(subscription.errors) if subscription.errors.any?
# delete from shopping cart the subscription
shopping_cart.destroy if shopping_cart
# Reload and make sure it went through:
account = get_account(current_user)
@ -180,7 +187,7 @@ module JamRuby
raise RecurlyClientError, "Plan code '#{paid_subscription.plan_code}' doesn't match jam track: '#{jam_track.plan_code}'" unless recurly_subscription_uuid
jam_track_right=JamRuby::JamTrackRight.find_or_create_by_user_id_and_jam_track_id(current_user.id, jam_track.id)
jam_track_right = JamRuby::JamTrackRight.find_or_create_by_user_id_and_jam_track_id(current_user.id, jam_track.id)
if jam_track_right.recurly_subscription_uuid != recurly_subscription_uuid
jam_track_right.recurly_subscription_uuid = recurly_subscription_uuid
jam_track_right.save
@ -198,7 +205,7 @@ module JamRuby
jam_track_right
end
def find_or_create_account(current_user, billing_info=nil)
def find_or_create_account(current_user, billing_info)
account = get_account(current_user)
if(account.nil?)

View File

@ -18,7 +18,7 @@ FactoryGirl.define do
musician true
terms_of_service true
last_jam_audio_latency 5
resue_card true
reuse_card true
#u.association :musician_instrument, factory: :musician_instrument, user: u

View File

@ -89,7 +89,7 @@ describe RecurlyClient do
it "can place order" do
@client.find_or_create_account(@user, @billing_info)
expect{@client.place_order(@user, @jamtrack)}.not_to raise_error()
expect{@client.place_order(@user, @jamtrack, nil)}.not_to raise_error()
subs = @client.get_account(@user).subscriptions
subs.should_not be_nil
subs.should have(1).items
@ -102,7 +102,7 @@ describe RecurlyClient do
@client.find_or_create_account(@user, @billing_info)
# Place order:
expect{@client.place_order(@user, @jamtrack)}.not_to raise_error()
expect{@client.place_order(@user, @jamtrack, nil)}.not_to raise_error()
active_subs=@client.get_account(@user).subscriptions.find_all{|t|t.state=='active'}
@jamtrack.reload
@jamtrack.jam_track_rights.should have(1).items
@ -118,10 +118,10 @@ describe RecurlyClient do
it "detects error on double order" do
@client.find_or_create_account(@user, @billing_info)
jam_track_right = @client.place_order(@user, @jamtrack)
jam_track_right = @client.place_order(@user, @jamtrack, nil)
jam_track_right.recurly_subscription_uuid.should_not be_nil
jam_track_right2 = @client.place_order(@user, @jamtrack)
jam_track_right2 = @client.place_order(@user, @jamtrack, nil)
jam_track_right.should eq(jam_track_right2)
jam_track_right.recurly_subscription_uuid.should eq(jam_track_right.recurly_subscription_uuid)
end

View File

@ -166,6 +166,10 @@ def app_config
20 # 20 seconds
end
def one_free_jamtrack_per_user
true
end
private

View File

@ -0,0 +1,368 @@
(function (context, $) {
"use strict";
context.JK = context.JK || {};
context.JK.CheckoutOrderScreen = function (app) {
var EVENTS = context.JK.EVENTS;
var logger = context.JK.logger;
var rest = context.JK.Rest();
var jamTrackUtils = context.JK.JamTrackUtils;
var $screen = null;
var $navigation = null;
var $templateOrderContent = null;
var $templatePurchasedJamTrack = null;
var $orderPanel = null;
var $thanksPanel = null;
var $jamTrackInBrowser = null;
var $purchasedJamTrack = null;
var $purchasedJamTrackHeader = null;
var $purchasedJamTracks = null;
var $orderContent = null;
var userDetail = null;
var step = null;
var downloadJamTracks = [];
var purchasedJamTracks = null;
var purchasedJamTrackIterator = 0;
var $backBtn = null;
var $paymentPrompt = null;
var $emptyCartPrompt = null;
function beforeShow() {
beforeShowOrder();
}
function afterShow(data) {
}
function beforeHide() {
if(downloadJamTracks) {
context._.each(downloadJamTracks, function(downloadJamTrack) {
downloadJamTrack.destroy();
downloadJamTrack.root.remove();
})
downloadJamTracks = [];
}
purchasedJamTracks = null;
purchasedJamTrackIterator = 0;
}
function beforeShowOrder() {
$paymentPrompt.addClass('hidden')
$emptyCartPrompt.addClass('hidden')
$orderPanel.removeClass("hidden")
$thanksPanel.addClass("hidden")
$orderContent.find(".place-order").addClass('disabled').off('click', placeOrder)
step = 3;
renderNavigation();
populateOrderPage();
}
function populateOrderPage() {
clearOrderPage();
rest.getShoppingCarts()
.done(function(carts) {
rest.getBillingInfo()
.done(function(billingInfo) {
renderOrderPage(carts, billingInfo)
})
.fail(function(jqXHR) {
if(jqXHR.status == 404) {
// no account for this user
app.notify({ title: "No account information",
text: "Please restart the checkout process." },
null,
true);
}
})
})
.fail(app.ajaxError);
}
function renderOrderPage(carts, recurlyAccountInfo) {
logger.debug("rendering order page")
var data = {}
var sub_total = 0.0
var taxes = 0.0
$.each(carts, function(index, cart) {
sub_total += parseFloat(cart.product_info.total_price)
});
//data.grand_total = (sub_total + taxes).toFixed(2)
data.sub_total = sub_total.toFixed(2)
//data.taxes = taxes.toFixed(2)
data.carts = carts
data.billing_info = recurlyAccountInfo.billing_info
data.shipping_info = recurlyAccountInfo.address
data.shipping_as_billing = true; //jamTrackUtils.compareAddress(data.billing_info, data.shipping_info);
var orderContentHtml = $(
context._.template(
$templateOrderContent.html(),
data,
{variable: 'data'}
)
)
$orderContent.append(orderContentHtml)
$orderPanel.find(".change-payment-info").on('click', moveToPaymentInfo)
var $placeOrder = $screen.find(".place-order")
if(carts.length == 0) {
$paymentPrompt.addClass('hidden')
$emptyCartPrompt.removeClass('hidden')
$placeOrder.addClass('disabled')
}
else {
logger.debug("cart has " + carts.length + " items in it")
$paymentPrompt.removeClass('hidden')
$emptyCartPrompt.addClass('hidden')
$placeOrder.removeClass('disabled').on('click', placeOrder)
var planPricing = {}
context._.each(carts, function(cart) {
var priceElement = $screen.find('.order-right-page .plan[data-plan-code="' + cart.product_info.plan_code +'"]')
if(priceElement.length == 0) {
logger.error("unable to find price element for " + cart.product_info.plan_code, cart);
app.notify({title: "Error Encountered", text: "Unable to find plan info for " + cart.product_info.plan_code})
return false;
}
logger.debug("creating recurly pricing element for plan: " + cart.product_info.plan_code)
var pricing = context.recurly.Pricing();
pricing.plan_code = cart.product_info.plan_code;
pricing.resolved = false;
pricing.effective_quantity = cart.product_info.quantity - cart.product_info.marked_for_redeem
planPricing[pricing.plan_code] = pricing;
// this is called when the plan is resolved against Recurly. It will have tax info, which is the only way we can get it.
pricing.on('change', function(price) {
var resolvedPrice = planPricing[this.plan_code];
if(!resolvedPrice) {
logger.error("unable to find price info in storage")
app.notify({title: "Error Encountered", text: "Unable to find plan info in storage"})
return;
}
else {
logger.debug("pricing resolved for plan: " + this.plan_code)
}
resolvedPrice.resolved = true;
var allResolved = true;
var totalTax = 0;
var totalPrice = 0;
// let's see if all plans have been resolved via API; and add up total price and taxes for display
$.each(planPricing, function(plan_code, priceObject) {
logger.debug("resolved recurly priceObject", priceObject)
if(!priceObject.resolved) {
allResolved = false;
return false;
}
else {
var unitTax = Number(priceObject.price.now.tax) * priceObject.effective_quantity;
totalTax += unitTax;
var totalUnitPrice = Number(priceObject.price.now.total) * priceObject.effective_quantity;
totalPrice += totalUnitPrice;
}
})
if(allResolved) {
$screen.find('.order-right-page .order-items-value.taxes').text('$' + totalTax.toFixed(2))
$screen.find('.order-right-page .order-items-value.order-total').text('$' + totalPrice.toFixed(2))
}
else
{
logger.debug("still waiting on more plans to resolve")
}
})
pricing.attach(priceElement.eq(0))
})
}
}
function moveToPaymentInfo() {
context.location = '/client#/checkoutPayment';
return false;
}
function placeOrder(e) {
e.preventDefault();
$screen.find(".place-order").off('click').addClass('disabled')
rest.placeOrder()
.done(moveToThanks)
.fail(orderErrorHandling);
}
function orderErrorHandling(xhr, ajaxOptions, thrownError) {
if (xhr && xhr.responseJSON) {
var message = "Error submitting payment: "
$.each(xhr.responseJSON.errors, function (key, error) {
message += key + ": " + error
})
$("#order_error").text(message).removeClass("hidden")
}
else {
$("#order_error").text(xhr.responseText).removeClass("hidden")
}
$orderContent.find(".place-order").on('click', placeOrder)
}
function moveToThanks(purchaseResponse) {
$("#order_error").addClass("hidden")
$orderPanel.addClass("hidden")
$thanksPanel.removeClass("hidden")
jamTrackUtils.checkShoppingCart()
//beforeShowOrder()
handleJamTracksPurchased(purchaseResponse.jam_tracks)
}
function handleJamTracksPurchased(jamTracks) {
// were any JamTracks purchased?
var jamTracksPurchased = jamTracks && jamTracks.length > 0;
if(jamTracksPurchased) {
if(gon.isNativeClient) {
startDownloadJamTracks(jamTracks)
}
else {
$jamTrackInBrowser.removeClass('hidden');
}
}
}
function startDownloadJamTracks(jamTracks) {
// there can be multiple purchased JamTracks, so we cycle through them
purchasedJamTracks = jamTracks;
// populate list of jamtracks purchased, that we will iterate through graphically
context._.each(jamTracks, function(jamTrack) {
var downloadJamTrack = new context.JK.DownloadJamTrack(app, jamTrack, 'small');
var $purchasedJamTrack = $(context._.template(
$templatePurchasedJamTrack.html(),
jamTrack,
{variable: 'data'}
));
$purchasedJamTracks.append($purchasedJamTrack)
// show it on the page
$purchasedJamTrack.append(downloadJamTrack.root)
downloadJamTracks.push(downloadJamTrack)
})
iteratePurchasedJamTracks();
}
function iteratePurchasedJamTracks() {
if(purchasedJamTrackIterator < purchasedJamTracks.length ) {
var downloadJamTrack = downloadJamTracks[purchasedJamTrackIterator++];
// make sure the 'purchasing JamTrack' section can be seen
$purchasedJamTrack.removeClass('hidden');
// the widget indicates when it gets to any transition; we can hide it once it reaches completion
$(downloadJamTrack).on(EVENTS.JAMTRACK_DOWNLOADER_STATE_CHANGED, function(e, data) {
if(data.state == downloadJamTrack.states.synchronized) {
logger.debug("jamtrack " + downloadJamTrack.jamTrack.name + " synchronized;")
//downloadJamTrack.root.remove();
downloadJamTrack.destroy();
// go to the next JamTrack
iteratePurchasedJamTracks()
}
})
logger.debug("jamtrack " + downloadJamTrack.jamTrack.name + " downloader initializing")
// kick off the download JamTrack process
downloadJamTrack.init()
// XXX style-test code
// downloadJamTrack.transitionError("package-error", "The server failed to create your package.")
}
else {
logger.debug("done iterating over purchased JamTracks")
$purchasedJamTrackHeader.text('All purchased JamTracks have been downloaded successfully! You can now play them in a session.')
}
}
function clearOrderPage() {
$orderContent.empty();
}
function renderNavigation() {
$navigation.html("");
var navigationHtml = $(
context._.template(
$('#template-checkout-navigation').html(),
{current: step},
{variable: 'data'}
)
);
$navigation.append(navigationHtml);
}
function events() {
$backBtn.on('click', function(e) {
e.preventDefault();
context.location = '/client#/checkoutPayment'
})
}
function initialize() {
var screenBindings = {
'beforeShow': beforeShow,
'afterShow': afterShow,
'beforeHide': beforeHide
};
app.bindScreen('checkoutOrder', screenBindings);
$screen = $("#checkoutOrderScreen");
$navigation = $screen.find(".checkout-navigation-bar");
$templateOrderContent = $("#template-order-content");
$templatePurchasedJamTrack = $('#template-purchased-jam-track');
$orderPanel = $screen.find(".order-panel");
$thanksPanel = $screen.find(".thanks-panel");
$jamTrackInBrowser = $screen.find(".thanks-detail.jam-tracks-in-browser");
$purchasedJamTrack = $thanksPanel.find(".thanks-detail.purchased-jam-track");
$purchasedJamTrackHeader = $purchasedJamTrack.find(".purchased-jam-track-header");
$purchasedJamTracks = $purchasedJamTrack.find(".purchased-list")
$backBtn = $screen.find('.back');
$paymentPrompt = $screen.find('.payment-prompt');
$emptyCartPrompt = $screen.find('.empty-cart-prompt');
$orderContent = $orderPanel.find(".order-content");
if ($screen.length == 0) throw "$screen must be specified";
if ($navigation.length == 0) throw "$navigation must be specified";
events();
}
this.initialize = initialize;
return this;
}
})
(window, jQuery);

View File

@ -6,6 +6,7 @@
var EVENTS = context.JK.EVENTS;
var logger = context.JK.logger;
var jamTrackUtils = context.JK.JamTrackUtils;
var $screen = null;
var $navigation = null;
@ -21,8 +22,17 @@
var billing_info = null;
var shipping_info = null;
var shipping_as_billing = null;
var $reuseExistingCard = null;
var $reuseExistingCardChk = null;
var $existingCardEndsWith = null;
var $newCardInfo = null;
var selectCountry = null;
var selectCountryLoaded = false;
var $freeJamTrackPrompt = null;
var $noFreeJamTrackPrompt = null;
function afterShow() {
function beforeShow() {
beforeShowPaymentInfo();
}
@ -30,39 +40,76 @@
step = 2;
renderNavigation();
renderAccountInfo();
}
function renderAccountInfo() {
var user = rest.getUserDetail()
$reuseExistingCard.addClass('hidden');
$newCardInfo.removeClass('hidden');
$freeJamTrackPrompt.addClass('hidden');
$noFreeJamTrackPrompt.addClass('hidden');
var selectCountryReady = selectCountry.ready();
if(!selectCountryReady) {
// one time init of country dropdown
selectCountryReady = selectCountry.load('US', null, null);
}
selectCountryReady.done(function() {
var user = rest.getUserDetail()
if(user) {
user.done(populateAccountInfo).error(app.ajaxError);
}
})
}
function populateAccountInfo(user) {
userDetail = user;
$reuseExistingCardChk.iCheck(userDetail.reuse_card ? 'check' : 'uncheck').attr('checked', userDetail.reuse_card)
// show appropriate prompt text based on whether user has a free jamtrack
if(user.free_jamtrack) {
$freeJamTrackPrompt.removeClass('hidden')
}
else {
$noFreeJamTrackPrompt.removeClass('hidden')
}
if (userDetail.has_recurly_account) {
rest.getBillingInfo()
.done(function(response) {
$billingInfo.find("#billing-first-name").val(response.first_name);
$billingInfo.find("#billing-last-name").val(response.last_name);
$billingInfo.find("#billing-address1").val(response.address1);
$billingInfo.find("#billing-address2").val(response.address2);
$billingInfo.find("#billing-city").val(response.city);
$billingInfo.find("#billing-state").val(response.state);
$billingInfo.find("#billing-zip").val(response.zip);
$billingInfo.find("#billing-country").val(response.country);
$shippingAddress.find("#shipping-first-name").val(response.first_name);
$shippingAddress.find("#shipping-last-name").val(response.last_name);
$shippingAddress.find("#shipping-address1").val(response.address1);
$shippingAddress.find("#shipping-address2").val(response.address2);
$shippingAddress.find("#shipping-city").val(response.city);
$shippingAddress.find("#shipping-state").val(response.state);
$shippingAddress.find("#shipping-zip").val(response.zip);
$shippingAddress.find("#shipping-country").val(response.country);
if(userDetail.reuse_card) {
$reuseExistingCard.removeClass('hidden');
toggleReuseExistingCard.call($reuseExistingCardChk)
$existingCardEndsWith.text(response.billing_info.last_four);
}
var isSameAsShipping = true // jamTrackUtils.compareAddress(response.billing_info, response.address);
$shippingAsBilling.iCheck(isSameAsShipping ? 'check' : 'uncheck').attr('checked', isSameAsShipping)
$billingInfo.find("#billing-first-name").val(response.billing_info.first_name);
$billingInfo.find("#billing-last-name").val(response.billing_info.last_name);
$billingInfo.find("#billing-address1").val(response.billing_info.address1);
$billingInfo.find("#billing-address2").val(response.billing_info.address2);
$billingInfo.find("#billing-city").val(response.billing_info.city);
$billingInfo.find("#billing-state").val(response.billing_info.state);
$billingInfo.find("#billing-zip").val(response.billing_info.zip);
$billingInfo.find("#billing-country").val(response.billing_info.country);
//$shippingAddress.find("#shipping-first-name").val(response.billing_info.first_name);
//$shippingAddress.find("#shipping-last-name").val(response.billing_info.last_name);
//$shippingAddress.find("#shipping-address1").val(response.address.address1);
//$shippingAddress.find("#shipping-address2").val(response.address.address2);
//$shippingAddress.find("#shipping-city").val(response.address.city);
//$shippingAddress.find("#shipping-state").val(response.address.state);
//$shippingAddress.find("#shipping-zip").val(response.address.zip);
//$shippingAddress.find("#shipping-country").val(response.address.country);
})
.error(app.ajaxError);
}
@ -81,7 +128,7 @@
}
}
function afterShow(data) {
function beforeShow(data) {
// XXX : style-test code
// moveToThanks({jam_tracks: [{id: 14, jam_track_right_id: 11, name: 'Back in Black'}, {id: 15, jam_track_right_id: 11, name: 'In Bloom'}, {id: 16, jam_track_right_id: 11, name: 'Love Bird Supreme'}]});
}
@ -96,8 +143,14 @@
// TODO: Refactor: this function is long and fraught with many return points.
function next(e) {
$paymentInfoPanel.find('.error-text').remove();
$paymentInfoPanel.find('.error').removeClass('error');
e.preventDefault();
$("#order_error").addClass("hidden")
$("#payment_error").addClass("hidden")
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();
@ -114,6 +167,7 @@
$billingInfo.find('#divBillingFirstName').addClass("error");
$billingInfo.find('#billing-first-name').after("<ul class='error-text'><li>First Name is required</li></ul>");
logger.info("no billing first name");
return false;
}
else {
@ -125,6 +179,7 @@
$billingInfo.find('#divBillingLastName').addClass("error");
$billingInfo.find('#billing-last-name').after("<ul class='error-text'><li>Last Name is required</li></ul>");
logger.info("no billing last name");
return false;
}
else {
@ -136,6 +191,7 @@
$billingInfo.find('#divBillingAddress1').addClass("error");
$billingInfo.find('#billing-address1').after("<ul class='error-text'><li>Address is required</li></ul>");
logger.info("no billing address line 1");
return false;
}
else {
@ -147,6 +203,7 @@
$billingInfo.find('#divBillingZip').addClass("error");
$billingInfo.find('#billing-zip').after("<ul class='error-text'><li>Zip code is required</li></ul>");
logger.info("no billing address line 2");
return false;
}
else {
@ -158,6 +215,7 @@
$billingInfo.find('#divBillingState').addClass("error");
$billingInfo.find('#billing-zip').after("<ul class='error-text'><li>State is required</li></ul>");
logger.info("no billing zip");
return false;
}
else {
@ -169,6 +227,7 @@
$billingInfo.find('#divBillingCity').addClass("error");
$billingInfo.find('#billing-city').after("<ul class='error-text'><li>City is required</li></ul>");
logger.info("no billing city");
return false;
}
else {
@ -180,12 +239,14 @@
$billingInfo.find('#divBillingCountry').addClass("error");
$billingInfo.find('#billing-country').after("<ul class='error-text'><li>Country is required</li></ul>");
logger.info("no billing country");
return false;
}
else {
$billingInfo.find('#divBillingCountry').removeClass("error");
}
/**
shipping_as_billing = $shippingAsBilling.is(":checked");
var shipping_first_name, shipping_last_name, shipping_address1, shipping_address2;
var shipping_city, shipping_state, shipping_zip, shipping_country;
@ -205,6 +266,7 @@
$shippingAddress.find('#divShippingFirstName').addClass("error");
$shippingAddress.find('#shipping-first-name').after("<ul class='error-text'><li>First Name is required</li></ul>");
logger.info("no address first name");
return false;
}
else {
@ -216,6 +278,7 @@
$shippingAddress.find('#divShippingLastName').addClass("error");
$shippingAddress.find('#shipping-last-name').after("<ul class='error-text'><li>Last Name is required</li></ul>");
logger.info("no last name");
return false;
}
else {
@ -227,6 +290,7 @@
$shippingAddress.find('#divShippingAddress1').addClass("error");
$shippingAddress.find('#shipping-address1').after("<ul class='error-text'><li>Address is required</li></ul>");
logger.info("no shipping address 1");
return false;
}
else {
@ -238,6 +302,7 @@
$shippingAddress.find('#divShippingZip').addClass("error");
$shippingAddress.find('#shipping-zip').after("<ul class='error-text'><li>Zip code is required</li></ul>");
logger.info("no shipping address 2");
return false;
}
else {
@ -249,6 +314,7 @@
$shippingAddress.find('#divShippingState').addClass("error");
$shippingAddress.find('#shipping-zip').after("<ul class='error-text'><li>State is required</li></ul>");
logger.info("no shipping state");
return false;
}
else {
@ -260,6 +326,7 @@
$shippingAddress.find('#divShippingCity').addClass("error");
$shippingAddress.find('#shipping-city').after("<ul class='error-text'><li>City is required</li></ul>");
logger.info("no shipping city");
return false;
}
else {
@ -271,12 +338,14 @@
$shippingAddress.find('#divShippingCountry').addClass("error");
$shippingAddress.find('#shipping-country').after("<ul class='error-text'><li>Country is required</li></ul>");
logger.info("no shipping country");
return false;
}
else {
$shippingAddress.find('#divShippingCountry').removeClass("error");
}
}
*/
var card_name = $paymentMethod.find("#card-name").val();
var card_number = $paymentMethod.find("#card-number").val();
@ -284,6 +353,7 @@
var card_month = $paymentMethod.find("#card_expire-date_2i").val();
var card_verify = $paymentMethod.find("#card-verify").val();
/**
if (!card_name) {
$paymentMethod.find('#divCardName .error-text').remove();
$paymentMethod.find('#divCardName').addClass("error");
@ -291,46 +361,54 @@
return false;
} else {
$paymentMethod.find('#divCardName').removeClass("error");
}*/
// don't valid card form fields when reuse card selected
if(!reuse_card_this_time) {
if (!card_number) {
$paymentMethod.find('#divCardNumber .error-text').remove();
$paymentMethod.find('#divCardNumber').addClass("error");
$paymentMethod.find('#card-number').after("<ul class='error-text'><li>Card Number is required</li></ul>");
logger.info("no card number");
return false;
} else if (!$.payment.validateCardNumber(card_number)) {
$paymentMethod.find('#divCardNumber .error-text').remove();
$paymentMethod.find('#divCardNumber').addClass("error");
$paymentMethod.find('#card-number').after("<ul class='error-text'><li>Card Number is not valid</li></ul>");
logger.info("invalid card number");
return 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('#card-expiry').after("<ul class='error-text'><li>Card Number is not valid</li></ul>");
logger.info("invalid card expiry");
return false;
} else {
$paymentMethod.find('#divCardExpiry').removeClass("error");
}
if (!card_verify) {
$paymentMethod.find('#divCardVerify .error-text').remove();
$paymentMethod.find('#divCardVerify').addClass("error");
$paymentMethod.find('#card-verify').after("<ul class='error-text'><li>Card Verification Value is required</li></ul>");
logger.info("no card verify");
return false;
} else if (!$.payment.validateCardCVC(card_verify)) {
$paymentMethod.find('#divCardVerify .error-text').remove();
$paymentMethod.find('#divCardVerify').addClass("error");
$paymentMethod.find('#card-verify').after("<ul class='error-text'><li>Card Verification Value is not valid.</li></ul>");
logger.info("bad card CVC");
return false;
} else {
$paymentMethod.find('#divCardVerify').removeClass("error");
}
}
if (!card_number) {
$paymentMethod.find('#divCardNumber .error-text').remove();
$paymentMethod.find('#divCardNumber').addClass("error");
$paymentMethod.find('#card-number').after("<ul class='error-text'><li>Card Number is required</li></ul>");
return false;
} else if (!$.payment.validateCardNumber(card_number)) {
$paymentMethod.find('#divCardNumber .error-text').remove();
$paymentMethod.find('#divCardNumber').addClass("error");
$paymentMethod.find('#card-number').after("<ul class='error-text'><li>Card Number is not valid</li></ul>");
return 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('#card-expiry').after("<ul class='error-text'><li>Card Number is not valid</li></ul>");
} else {
$paymentMethod.find('#divCardExpiry').removeClass("error");
}
if (!card_verify) {
$paymentMethod.find('#divCardVerify .error-text').remove();
$paymentMethod.find('#divCardVerify').addClass("error");
$paymentMethod.find('#card-verify').after("<ul class='error-text'><li>Card Verification Value is required</li></ul>");
return false;
} else if(!$.payment.validateCardCVC(card_verify)) {
$paymentMethod.find('#divCardVerify .error-text').remove();
$paymentMethod.find('#divCardVerify').addClass("error");
$paymentMethod.find('#card-verify').after("<ul class='error-text'><li>Card Verification Value is not valid.</li></ul>");
return false;
} else {
$paymentMethod.find('#divCardVerify').removeClass("error");
}
billing_info = {};
shipping_info = {};
billing_info.first_name = billing_first_name;
@ -346,6 +424,7 @@
billing_info.year = card_year;
billing_info.verification_value = card_verify;
/**
if (shipping_as_billing) {
shipping_info = $.extend({},billing_info);
delete shipping_info.number;
@ -361,69 +440,83 @@
shipping_info.state = shipping_state;
shipping_info.country = shipping_country;
shipping_info.zip = shipping_zip;
}
$paymentInfoPanel.find("#payment-info-next").addClass("disabled");
$paymentInfoPanel.find("#payment-info-next").off("click");
var reuse_card = $paymentMethod.find('#save-card').is(':checked');
}*/
var email = null;
var password = null;
var terms = false;
var isLoggedIn = context.JK.currentUserId;
if(!isLoggedIn) {
email = $accountSignup.find('input[name="email"]').val()
password = $accountSignup.find('input[name="password"]').val()
terms = $accountSignup.find('input[name="terms-of-service"]').is(':checked');
}
rest.createRecurlyAccount({billing_info: billing_info, terms_of_service: true, email: email, password: password, reuse_card: reuse_card})
$screen.find("#payment-info-next").addClass("disabled");
$screen.find("#payment-info-next").off("click");
rest.createRecurlyAccount({billing_info: billing_info, terms_of_service: terms, email: email, password: password, reuse_card_this_time: reuse_card_this_time, reuse_card_next_time: reuse_card_next_time})
.done(function() {
$paymentInfoPanel.find("#payment-info-next").removeClass("disabled");
$paymentInfoPanel.find("#payment-info-next").on("click", next);
$screen.find("#payment-info-next").on("click", next);
if(isLoggedIn) {
context.location = '/client#checkout_order'
context.location = '/client#/checkoutOrder'
}
else {
// this means the account was created; we need to reload the page for this to take effect
context.location = '/client#checkout_order'
context.JK.currentUserId = 'something' // this is to trick layout.js from getting involved and redirecting to home screen
context.location = '/client#/checkoutOrder'
context.location.reload()
}
})
.fail(errorHandling);
.fail(errorHandling)
.always(function(){
$screen.find("#payment-info-next").removeClass("disabled");
})
}
function errorHandling(xhr, ajaxOptions, thrownError) {
$.each(xhr.responseJSON.errors, function(key, error) {
if (key == 'number') {
$paymentMethod.find('#divCardNumber .error-text').remove();
$paymentMethod.find('#divCardNumber').addClass("error");
$paymentMethod.find('#card-number').after("<ul class='error-text'><li>" + error + "</li></ul>");
}
else if (key == 'verification_value') {
$paymentMethod.find('#divCardVerify .error-text').remove();
$paymentMethod.find('#divCardVerify').addClass("error");
$paymentMethod.find('#card-verify').after("<ul class='error-text'><li>" + error + "</li></ul>");
}
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("<ul class='error-text'><li>" + error + "</li></ul>");
}
else if(key == 'password') {
var $email = $accountSignup.find('input[name="password"]')
var $field = $email.closest('.field')
$field.find('.error-text').remove()
$field.addClass("error");
$field.append("<ul class='error-text'><li>" + error + "</li></ul>");
}
});
logger.debug("error handling", xhr.responseJSON)
if(xhr.responseJSON && xhr.responseJSON.errors) {
$.each(xhr.responseJSON.errors, function(key, error) {
if (key == 'number') {
$paymentMethod.find('#divCardNumber .error-text').remove();
$paymentMethod.find('#divCardNumber').addClass("error");
$paymentMethod.find('#card-number').after("<ul class='error-text'><li>" + error + "</li></ul>");
}
else if (key == 'verification_value') {
$paymentMethod.find('#divCardVerify .error-text').remove();
$paymentMethod.find('#divCardVerify').addClass("error");
$paymentMethod.find('#card-verify').after("<ul class='error-text'><li>" + error + "</li></ul>");
}
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("<ul class='error-text'><li>" + error + "</li></ul>");
}
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("<ul class='error-text'><li>" + error + "</li></ul>");
}
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("<ul class='error-text'><li>" + error + "</li></ul>");
}
});
}
else {
$("#payment_error").text(xhr.responseText)
}
$paymentInfoPanel.find("#payment-info-next").addClass("disabled");
$paymentInfoPanel.find("#payment-info-next").on('click', next);
$screen.find("#payment-info-next").on('click', next);
}
function beforeShowOrder() {
@ -440,10 +533,6 @@
.fail(app.ajaxError);
}
function moveToOrder() {
window.location = '/client#/checkout_order';
}
function toggleShippingAsBilling(e) {
e.preventDefault();
@ -457,9 +546,36 @@
}
}
function toggleReuseExistingCard(e) {
if(e) {
e.preventDefault();
}
logger.debug("toggle reuse existing card")
var reuse_existing = $(this).is(':checked');
$('#billing-first-name').prop('disabled', reuse_existing);
$('#billing-last-name').prop('disabled', reuse_existing);
$('#billing-address1').prop('disabled', reuse_existing);
$('#billing-address2').prop('disabled', reuse_existing);
$('#billing-city').prop('disabled', reuse_existing);
$('#billing-state').prop('disabled', reuse_existing);
$('#billing-zip').prop('disabled', reuse_existing);
$('#billing-country').prop('disabled', reuse_existing);
$('#card-name').prop('disabled', reuse_existing);
$('#card-number').prop('disabled', reuse_existing);
$('#card_expire-date_1i').prop('disabled', reuse_existing);
$('#card_expire-date_2i').prop('disabled', reuse_existing);
$('#card-verify').prop('disabled', reuse_existing);
}
function events() {
$screen.find("#payment-info-next").on('click', next);
$shippingAsBilling.on('ifChanged', toggleShippingAsBilling);
$reuseExistingCardChk.on('ifChanged', toggleReuseExistingCard);
}
function reset() {
@ -489,6 +605,8 @@
// Use jquery.payment to limit characters and length:
$paymentMethod.find("#card-number").payment('formatCardNumber');
$paymentMethod.find("#card-verify").payment('formatCardCVC');
selectCountry = new context.JK.SelectLocation($('#billing-country'), null, null, app, false)
}
function initialize() {
@ -497,7 +615,7 @@
'afterShow': afterShow,
'beforeHide' : beforeHide
};
app.bindScreen('checkout_payment', screenBindings);
app.bindScreen('checkoutPayment', screenBindings);
$screen = $("#checkoutPaymentScreen");
$paymentInfoPanel = $screen.find("#checkout-payment-info");
@ -508,6 +626,13 @@
$accountSignup = $paymentInfoPanel.find('.jamkazam-account-signup')
$shippingAddress = $paymentInfoPanel.find(".shipping-address-detail");
$shippingAsBilling = $paymentInfoPanel.find("#shipping-as-billing");
$reuseExistingCard = $paymentInfoPanel.find('.reuse-existing-card')
$reuseExistingCardChk = $paymentInfoPanel.find('#reuse-existing-card')
$existingCardEndsWith = $paymentInfoPanel.find('.existing-card-ends-with')
$newCardInfo = $paymentInfoPanel.find('.new-card-info')
$freeJamTrackPrompt = $screen.find('.payment-prompt.free-jamtrack')
$noFreeJamTrackPrompt = $screen.find('.payment-prompt.no-free-jamtrack')
if($screen.length == 0) throw "$screen must be specified";
if($navigation.length == 0) throw "$navigation must be specified";

View File

@ -51,7 +51,7 @@
}
function moveNext() {
window.location = '/client#/checkout_payment';
window.location = '/client#/checkoutPayment';
return false;
}
@ -69,7 +69,7 @@
rest.login({email: email, password: password, remember_me: true})
.done(function() {
window.location = '/client#/checkout_payment'
window.location = '/client#/checkoutPayment'
window.location.reload();
})
.fail(function(jqXHR) {
@ -106,7 +106,7 @@
'beforeShow': beforeShow,
'afterShow': afterShow
};
app.bindScreen('checkout_signin', screenBindings);
app.bindScreen('checkoutSignin', screenBindings);
$screen = $("#checkoutSignInScreen");
$navigation = $screen.find(".checkout-navigation-bar");

View File

@ -215,13 +215,7 @@
}
function initShoppingCart(app) {
var user = app.user()
if(user) {
user.done(function(userProfile) {
context.JK.JamTrackUtils.checkShoppingCart();
})
}
context.JK.JamTrackUtils.checkShoppingCart();
}
})(window, jQuery);

View File

@ -473,7 +473,7 @@
processData: false,
contentType: 'application/json',
data: JSON.stringify(options)
});
})
}
function getUserDetail(options) {
@ -1547,10 +1547,10 @@
});
}
function placeOrder(options) {
function placeOrder() {
return $.ajax({
type: "POST",
url: '/api/recurly/place_order?' + $.param(options),
url: '/api/recurly/place_order',
dataType: "json",
contentType: 'application/json'
});

View File

@ -22,7 +22,12 @@ class JamTrackUtils
else
cartLink.addClass("hidden")
compareAddress: (billing, shipping) =>
billing.address1 == shipping.address1 &&
billing.address2 == shipping.address2 &&
billing.zip == shipping.zip &&
billing.city == shipping.city &&
billing.country == shipping.country;
# global instance

View File

@ -115,19 +115,6 @@
purchasedJamTrackIterator = 0;
}
function beforeHide() {
if(downloadJamTracks) {
context._.each(downloadJamTracks, function(downloadJamTrack) {
downloadJamTrack.destroy();
downloadJamTrack.root.remove();
})
downloadJamTracks = [];
}
purchasedJamTracks = null;
purchasedJamTrackIterator = 0;
}
// TODO: Refactor: this function is long and fraught with many return points.
function next(e) {
e.preventDefault();

View File

@ -6,7 +6,7 @@
context.JK.SelectLocation = Class.extend({
init: function ($countries, $regions, $cities, app) {
init: function ($countries, $regions, $cities, app, useEasyDropdown) {
this.api = context.JK.Rest();
this.logger = context.JK.logger;
this.loadingCitiesData = false;
@ -14,9 +14,12 @@
this.loadingCountriesData = false;
this.nilOptionStr = '<option value=""></option>';
this.nilOptionText = 'n/a';
this.countriesLoaded = false;
this.$countries = $countries;
this.$regions = $regions;
this.$cities = $cities;
this.$deferred = null;
this.useEasyDropdown = useEasyDropdown === undefined ? true : useEasyDropdown;
this.app = app;
$countries.on('change', function (evt) {
@ -24,11 +27,24 @@
this.handleCountryChanged();
return false;
}.bind(this));
$regions.on('change', function (evt) {
evt.stopPropagation();
this.handleRegionChanged();
return false;
}.bind(this));
if($regions) {
$regions.on('change', function (evt) {
evt.stopPropagation();
this.handleRegionChanged();
return false;
}.bind(this));
}
},
selectCountry: function (country) {
if(this.useEasyDropdown) {
this.$countries.easyDropDown('select', country, true)
}
else {
this.$countries.val(country)
}
},
ready: function() {
return this.$deferred;
},
load: function (country, region, city) {
@ -42,13 +58,9 @@
country = 'US';
}
this.loadingCountriesData = true;
this.loadingRegionsData = true;
this.loadingCitiesData = true;
// make the 3 slower requests, which only matter if the user wants to affect their ISP or location
this.api.getCountries()
this.loadingCountriesData = true;
this.$deferred = this.api.getCountries()
.done(function (countriesx) {
this.populateCountriesx(countriesx["countriesx"], country);
}.bind(this))
@ -57,7 +69,9 @@
this.loadingCountriesData = false;
}.bind(this))
if (country) {
if (country && this.$regions) {
this.loadingRegionsData = true;
this.api.getRegions({ country: country })
.done(function (regions) {
this.populateRegions(regions["regions"], region);
@ -67,7 +81,8 @@
this.loadingRegionsData = false;
}.bind(this))
if (region) {
if (region && this.$cities) {
this.loadingCitiesData = true;
this.api.getCities({ country: country, region: region })
.done(function (cities) {
this.populateCities(cities["cities"], this.city)
@ -78,9 +93,15 @@
}.bind(this))
}
}
return this.$deferred;
},
handleCountryChanged: function () {
var selectedCountry = this.$countries.val()
if(!this.$regions) {
return;
}
var selectedRegion = this.$regions.val()
var cityElement = this.$cities
@ -144,7 +165,9 @@
else {
cityElement.children().remove();
cityElement.append($(this.nilOptionStr).text(this.nilOptionText));
context.JK.dropdown(cityElement);
if(this.useEasyDropdown) {
context.JK.dropdown(cityElement);
}
}
},
@ -159,7 +182,7 @@
if (!countryx.countrycode) return;
var option = $(this.nilOptionStr);
option.text(countryx.countryname);
option.text(countryx.countryname ? countryx.countryname : countryx.countrycode);
option.attr("value", countryx.countrycode);
if (countryx.countrycode == this.country) {
@ -170,6 +193,8 @@
},
populateCountriesx: function (countriesx) {
this.countriesLoaded = true;
// countriesx has the format [{countrycode: "US", countryname: "United States"}, ...]
this.foundCountry = false;
@ -194,8 +219,9 @@
this.$countries.val(this.country);
this.$countries.attr("disabled", null).easyDropDown('enable');
context.JK.dropdown(this.$countries);
if(this.useEasyDropdown) {
context.JK.dropdown(this.$countries);
}
},
writeRegion: function (index, region) {
@ -220,7 +246,9 @@
this.$regions.val(userRegion)
this.$regions.attr("disabled", null).easyDropDown('enable');
context.JK.dropdown(this.$regions);
if(this.useEasyDropdown) {
context.JK.dropdown(this.$regions);
}
},
writeCity: function (index, city) {
@ -245,7 +273,9 @@
this.$cities.val(userCity)
this.$cities.attr("disabled", null).easyDropDown('enable');
context.JK.dropdown(this.$cities);
if(this.useEasyDropdown) {
context.JK.dropdown(this.$cities);
}
},
regionListFailure: function (jqXHR, textStatus, errorThrown) {

View File

@ -30,15 +30,15 @@
e.preventDefault();
if (!context.JK.currentUserId) {
window.location = '/client#/checkout_signin';
window.location = '/client#/checkoutSignin';
}
else {
app.user().done(function(user) {
if(user.reuse_card) {
window.location = '/client#/checkout_order';
window.location = '/client#/checkoutOrder';
}
else {
window.location = '/client#/checkout_payment';
window.location = '/client#/checkoutPayment';
}
})

View File

@ -64,88 +64,3 @@
}
}
}
.order-panel {
padding: 30px;
.order-header {
h2 {
font-size: 16px;
}
}
.order-content {
margin-top: 20px;
}
.order-left-page {
float: left;
width: 60%;
.payment-info-page {
padding: 5px;
.info-caption-link {
.caption-text {
float: left;
}
.caption-link {
float: left;
margin-left: 5px;
}
}
.address-info {
width: 50%;
float: left;
}
.payment-method-info {
width: 50%;
float: left;
}
}
.order-items-page {
padding: 5px;
.cart-item-caption {
width: 50%;
text-align: left;
float: left;
}
.cart-item-caption#header {
font-weight: bold;
}
.cart-item-price {
width: 25%;
text-align: right;
float: left;
}
.cart-item-quantity {
width: 25%;
text-align: right;
float: left;
}
.cart-items {
margin-top: 10px;
}
.cart-item {
margin-top: 10px;
}
}
}
.order-right-page {
float: right;
width: 35%;
text-align: center;
.order-total {
color: #ed3618;
}
}
}

View File

@ -0,0 +1,204 @@
@import "client/common.css.scss";
#checkoutOrderScreen {
p {
font-size:12px;
margin:0;
}
.payment-prompt {
color:white;
line-height:125%;
}
h2 {
color:white;
background-color:#4d4d4d;
font-weight:normal;
margin: 0 0 10px 0;
font-size:14px;
padding: 3px 0 3px 10px;
height: 14px;
line-height: 14px;
vertical-align: middle;
text-align:left;
}
.action-bar {
margin-top:20px;
}
.line {
margin:10px 0 10px;
border-width:0 0 1px 0;
border-color:#ccc;
border-style:solid;
}
#checkout-info-help {
margin-right:1px;
}
.billing-info-item {
margin-bottom:3px;
}
.country {
margin-left:15px;
}
.billing-address {
margin-bottom:20px;
}
.order-panel {
padding: 30px;
min-width:730px;
.place-order {
font-size: 14px;
padding: 1px 3px;
line-height: 15px;
}
.place-order-center {
text-align:center;
margin:20px 0 20px;
}
.change-payment-info {
position:absolute;
font-size:12px;
left:180px;
}
.billing-caption {
margin-bottom:5px;
float:left;
position:relative;
}
.order-header {
h2 {
font-size: 16px;
}
}
.shipping-address {
display:none;
}
.order-help {
margin:20px 0 30px;
}
.order-summary {
padding:0 20px;
.billing-caption {
float:none;
margin-bottom:10px;
}
}
.order-items-header {
float:left;
margin-bottom:5px;
}
.order-items-value {
float:right;
}
.order-content {
margin-top: 20px;
background-color:#262626;
}
.order-left-page {
float: left;
width: 65%;
background-color:#262626;
border-width:0 1px 0 0;
border-style:solid;
border-color:#333;
@include border_box_sizing;
.payment-info-page {
.info-caption-link {
.caption-text {
float: left;
}
.caption-link {
float: left;
margin-left: 5px;
}
}
.address-info {
width: 50%;
float: left;
padding:0 10px;
@include border_box_sizing;
margin-bottom:30px;
}
.payment-method-info {
width: 50%;
float: left;
padding:0 10px;
@include border_box_sizing;
}
}
.order-items-page {
.cart-item-caption {
width: 50%;
text-align: left;
float: left;
margin-bottom:10px;
@include border_box_sizing;
}
.cart-item-price {
width: 25%;
text-align: right;
float: left;
padding:0 10px;
margin-bottom:10px;
@include border_box_sizing;
}
.cart-item-quantity {
width: 10%;
text-align: right;
float: left;
padding:0 10px;
margin-bottom:10px;
@include border_box_sizing;
}
.cart-items {
margin-top: 10px;
padding-left:10px;
}
.cart-item {
margin-top: 10px;
}
.no-cart-items {
}
}
}
.order-right-page {
float: left;
width: 35%;
text-align: left;
background-color:#262626;
@include border_box_sizing;
.order-total {
color: #ed3618;
}
}
}
}

View File

@ -3,6 +3,7 @@
.payment-wrapper {
padding:10px 30px;
min-width:600px;
}
p {
@ -41,6 +42,12 @@
input[type="text"], input[type="password"] {
width: 90%;
@include border_box_sizing;
}
select#billing-country {
width:90%;
@include border_box_sizing;
}
&.signed-in {
@ -53,6 +60,21 @@
}
}
}
&.not-signed-in {
.row.second {
.left-side {
display:none;
}
.right-side {
width:100%;
}
}
}
#divShippingFirstName, #divShippingLastName {
display:none;
}
.row {
margin-top:20px;
@ -87,6 +109,23 @@
float: left;
@include border_box_sizing;
}
div.terms-of-service.ichecbuttons {
margin-left:5px;
.icheckbox_minimal {
float: left;
display: block;
margin: 5px 5px 0 0;
}
}
.terms-of-service-label-holder {
font-size:12px;
line-height:18px;
top:4px;
position:relative;
float:left;
}
}
.hint {
@ -131,11 +170,20 @@
@include border_box_sizing;
}
.save-card-checkbox {
.save-card-checkbox, .reuse-existing-card-checkbox {
float:left;
display:block;
margin-right:5px;
}
label[for="reuse-existing-card"], label[for="save-card"] {
line-height: 18px;
vertical-align: middle;
}
.reuse-existing-card-helper {
margin-bottom:10px;
}
}
.shipping-address {

View File

@ -55,6 +55,7 @@
*= require ./checkout
*= require ./checkout_signin
*= require ./checkout_payment
*= require ./checkout_order
*= require ./genreSelector
*= require ./sessionList
*= require ./searchResults

View File

@ -12,6 +12,7 @@ $ColorLinkHover: #82AEAF;
$ColorSidebarText: #a0b9bd;
$ColorScreenBackground: lighten($ColorUIBackground, 10%);
$ColorTextBoxBackground: #c5c5c5;
$ColorTextBoxDisabledBackground: #999;
$ColorRecordingBackground: #471f18;
$ColorTextHighlight: white;

View File

@ -222,6 +222,10 @@
border:none;
-webkit-box-shadow: inset 2px 2px 3px 0px #888;
box-shadow: inset 2px 2px 3px 0px #888;
&:disabled {
background-color: $ColorTextBoxDisabledBackground;
}
}
}

View File

@ -322,6 +322,10 @@ input[type="text"], input[type="password"]{
border:none;
padding:3px;
font-size:15px;
&:disabled {
background-color: $ColorTextBoxDisabledBackground;
}
}
textarea {

View File

@ -8,10 +8,18 @@ class ApiRecurlyController < ApiController
def create_account
billing_info = params[:billing_info]
shipping_info = params[:shipping_info]
# should we let the user reuse this card next time?
reuse_card_next_time = params[:reuse_card_next_time] == "true"
# should we update the card info, or use what's on file this time?
reuse_card_this_time = params[:reuse_card_this_time] == "true"
# terms of service accepted?
terms_of_service = params[:terms_of_service] == "true"
if current_user
# keep reuse card up-to-date
User.where(id: current_user.id).update_all(reuse_card: params[:reuse_card])
User.where(id: current_user.id).update_all(reuse_card: params[:reuse_card_next_time])
else
options = {
remote_ip: request.remote_ip,
@ -20,17 +28,17 @@ class ApiRecurlyController < ApiController
email: params[:email],
password: params[:password],
password_confirmation: params[:password],
terms_of_service: params[:terms_of_service],
instruments: [{ :instrument_id => 'other', :proficiency_level => 3, :priority => 1 }],
terms_of_service: terms_of_service,
instruments: [{ :instrument_id => 'other', :proficiency_level => 1, :priority => 1 }],
birth_date: nil,
location: { :country => billing_info[:country], :state => billing_info[:state], :city => billing_info[:city]},
musician: true,
recaptcha_failed: false,
skip_recaptcha: true,
invited_user: nil,
fb_signup: nil,
signup_confirm_url: ApplicationHelper.base_uri(request) + "/confirm",
any_user: any_user,
reuse_card: params[:reuse_card]
reuse_card: reuse_card_next_time
}
user = UserManager.new.signup(options)
@ -45,7 +53,14 @@ class ApiRecurlyController < ApiController
end
begin
@account = @client.find_or_create_account(current_user, billing_info)
billing_info[:ip_address] = request.remote_ip if billing_info
if reuse_card_this_time
# do not attempt to update any billing/shipping info unless the user re-inputs their info!
@account = @client.get_account(current_user)
else
@account = @client.find_or_create_account(current_user, billing_info)
end
render :json=>account_json(@account)
rescue RecurlyClientError => x
render json: { :message => x.inspect, errors: x.errors }, :status => 404
@ -61,7 +76,7 @@ class ApiRecurlyController < ApiController
# get Recurly account
def get_account
@account=@client.get_account(current_user)
@account = @client.get_account(current_user)
render :json=>account_json(@account)
rescue RecurlyClientError => e
@ -79,9 +94,11 @@ class ApiRecurlyController < ApiController
# get Billing Information
def billing_info
@account = @client.get_account(current_user)
# @billing = @account.billing_info
# @billing ||= @account
render :json=> account_json(@account)
if @account
render :json=> account_json(@account)
else
render :json=> {}, :status => 404
end
rescue RecurlyClientError => x
render json: { message: x.inspect, errors: x.errors}, :status => 404
end
@ -96,33 +113,20 @@ class ApiRecurlyController < ApiController
def place_order
error=nil
puts "PLACING ORDER #{params.inspect}"
response = {jam_tracks:[]}
# 1st confirm that all specified JamTracks exist
jam_tracks = []
current_user.shopping_carts.each do |shopping_cart|
jam_track = shopping_cart.cart_product
params[:jam_tracks].each do |jam_track_id|
jam_track = JamTrack.where("id=?", jam_track_id).first
if jam_track
jam_tracks << jam_track
else
error="JamTrack not found for '#{jam_track_id}'"
break
end
# if shopping_cart has any marked_for_redeem, then we zero out the price by passing in 'free'
# NOTE: shopping_carts have the idea of quantity, but you should only be able to buy at most one JamTrack. So anything > 0 is considered free for a JamTrack
jam_track_right = @client.place_order(current_user, jam_track, shopping_cart)
# build up the response object with JamTracks that were purchased.
# if this gets more complicated, we should switch to RABL
response[:jam_tracks] << {name: jam_track.name, id: jam_track.id, jam_track_right_id: jam_track_right.id, version: jam_track.version}
end
# then buy each
unless error
jam_tracks.each do |jam_track|
jam_track_right = @client.place_order(current_user, jam_track)
# build up the response object with JamTracks that were purchased.
# if this gets more complicated, we should switch to RABL
response[:jam_tracks] << {name: jam_track.name, id: jam_track.id, jam_track_right_id: jam_track_right.id, version: jam_track.version}
end
end
if error
render json: { errors: {message:error}}, :status => 404
else
@ -138,16 +142,21 @@ private
end
def account_json(account)
billing_info = account.billing_info.nil? ? nil : {
:first_name => account.billing_info.first_name,
:last_name => account.billing_info.last_name,
:address1 => account.billing_info.address1,
:address2 => account.billing_info.address2,
:city => account.billing_info.city,
:state => account.billing_info.state,
:zip => account.billing_info.zip,
:country => account.billing_info.country,
:last_four => account.billing_info.last_four
}
{
:first_name => account.first_name,
:last_name => account.last_name,
:email => account.email,
:address1 => account.billing_info ? account.billing_info.address1 : nil,
:address2 => account.billing_info ? account.billing_info.address2 : nil,
:city => account.billing_info ? account.billing_info.city : nil,
:state => account.billing_info ? account.billing_info.state : nil,
:zip => account.billing_info ? account.billing_info.zip : nil,
:country => account.billing_info ? account.billing_info.country : nil
billing_info: billing_info
}
end

View File

@ -16,7 +16,11 @@ class ApiShoppingCartsController < ApiController
raise StateError, "Invalid JamTrack."
end
@cart = ShoppingCart.create any_user, jam_track
ShoppingCart.transaction do
mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(any_user)
@cart = ShoppingCart.create(any_user, jam_track, 1, mark_redeem)
end
if @cart.errors.any?
response.status = :unprocessable_entity
@ -46,7 +50,21 @@ class ApiShoppingCartsController < ApiController
@cart = any_user.shopping_carts.find_by_id(params[:id])
raise StateError, "Invalid Cart." if @cart.nil?
@cart.destroy
ShoppingCart.transaction do
@cart.destroy
# check if we should move the redemption
mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(any_user)
carts = any_user.shopping_carts
# if we find any carts on the account, mark one redeemable
if mark_redeem && carts.length > 0
carts[0].redeem(mark_redeem)
carts[0].save
end
end
respond_with responder: ApiResponder, :status => 204
end

View File

@ -17,6 +17,10 @@ if @user == current_user
geoiplocation.info if geoiplocation
end
node :free_jamtrack do |user|
Rails.application.config.one_free_jamtrack_per_user && user.has_redeemable_jamtrack
end
node :mods do |user|
user.mods_json
end

View File

@ -0,0 +1,154 @@
div layout="screen" layout-id="checkoutOrder" id="checkoutOrderScreen" class="screen secondary"
.content
.content-head
.content-icon= image_tag("content/icon_shopping_cart.png", {:height => 19, :width => 19})
h1 check out
= render "screen_navigation"
.content-body
#order_error.error.hidden
.content-body-scroller
.content-wrapper
.checkout-navigation-bar
.order-panel
.payment-wrapper
p.payment-prompt.hidden
| Please review your order, and if everything looks correct, click the PLACE YOUR ORDER button. Thank you!
p.empty-cart-prompt.hidden
| You have nothing in your cart. You can go browse for JamTracks&nbsp;
a href="/client#/jamtrack" here
| .
.order-content
.clearall
.action-bar
.right
a.button-grey href="#" id="checkout-info-help" HELP
a.button-grey.back href="#" BACK
a.button-orange.place-order href="#" PLACE YOUR ORDER
.clearall
.thanks-panel
h2 Thank you for your order!
br
.thanks-detail We'll send you an email confirming your order shortly.
br
.thanks-detail.jam-tracks-in-browser.hidden
| To play your purchased JamTrack, launch the JamKazam application and open the JamTrack while in a session.
.thanks-detail.purchased-jam-track.hidden
h2.purchased-jam-track-header Downloading Your Purchased JamTracks
span Each JamTrack will be downloaded sequentially.
br
span.notice Note that you do not have to wait for this to complete in order to use your JamTrack later.
br.clear
ul.purchased-list
script type='text/template' id='template-order-content'
.order-left-page
.payment-info-page
h2 ADDRESS & PAYMENT
.address-info
.billing-address
.billing-caption
| BILLING ADDRESS:
a.change-payment-info href="#" change
.clearall
.billing-info-item= "{{data.billing_info.address1}}"
.billing-info-item= "{{data.billing_info.address2}}"
.billing-info-item
| {{data.billing_info.city}}, {{data.billing_info.state}} {{data.billing_info.zip}}
span.country= "{{data.billing_info.country}}"
.shipping-address
.billing-caption
| SHIPPING ADDRESS:
a.change-payment-info href="#" change
.clearall
= "{% if (data.shipping_as_billing) { %}"
.billing-info-item Same as billing address
= "{% } else { %}"
.billing-info-item= "{{data.shipping_info.address1}}"
.billing-info-item= "{{data.shipping_info.address2}}"
.billing-info-item
| {{data.shipping_info.city}}, {{data.shipping_info.state}} {{data.shipping_info.zip}}
span.country= "{{data.shipping_info.country}}"
= "{% } %}"
br
.payment-method-info
.billing-caption
| PAYMENT METHOD:
a.change-payment-info href="#" change
.clearall
/= image_tag ''
="Credit card ending {{data.billing_info.last_four}}"
.clearall
.order-items-page
h2 ORDER DETAILS
.cart-items
.cart-item-caption
span YOUR ORDER INCLUDES:
.cart-item-price
span PRICE
.cart-item-quantity
span QUANTITY
.clearall
= "{% if (data.carts.length == 0) { %}"
.no-cart-items You have no orders now.
= "{% } %}"
= "{% _.each(data.carts, function(cart) { %}"
.cart-item cart-id="{{cart.id}}"
.cart-item-caption
= "{{cart.cart_type}}: {{cart.product_info.name}}"
.cart-item-price
= "$ {{Number(cart.product_info.total_price).toFixed(2)}}"
.cart-item-quantity
= "{{cart.quantity}}"
.clearall
= "{% }); %}"
.clearall
.order-right-page
h2 PLACE ORDER
.recurly-data.hidden
= "{% _.each(data.carts, function(cart) { %}"
.plan data-plan-code="{{cart.product_info.plan_code}}"
input data-recurly="plan" type="text" value="{{cart.product_info.plan_code}}"
= "{% }); %}"
.order-summary
.place-order-center
a.button-orange.place-order href="#" PLACE YOUR ORDER
.clearall
.billing-caption ORDER SUMMARY:
.order-items-header Order items:
.order-items-value ${{data.sub_total}}
.clearall
.order-items-header Shipping & handling:
.order-items-value $0.00
.clearall
.line
.order-items-header Total before tax:
.order-items-value ${{data.sub_total}}
.clearall
.order-items-header.taxes Taxes:
.order-items-value.taxes Calculating...
.clearall
.line
.order-items-header.order-total Order total:
.order-items-value.order-total Calculating...
.clearall
.order-help
span By placing your order, you agree to JamKazam's
'
a href="http://www.jamkazam.com/corp/terms" rel="external" terms of service
'
span and
'
a href="http://www.jamkazam.com/corp/returns" rel="external" returns policy
span .
.clearall
script type='text/template' id='template-purchased-jam-track'
li data-jam-track-id="{{data.jam_track_id}}"

View File

@ -1,20 +1,22 @@
div layout="screen" layout-id="checkout_payment" id="checkoutPaymentScreen" class="screen secondary no-login-required"
div layout="screen" layout-id="checkoutPayment" id="checkoutPaymentScreen" class="screen secondary no-login-required"
.content
.content-head
.content-icon= image_tag("content/icon_shopping_cart.png", {:height => 19, :width => 19})
h1 check out
= render "screen_navigation"
.content-body
#order_error.error.hidden
#payment_error.error.hidden
.content-body-scroller
.content-wrapper
.checkout-navigation-bar
.payment-wrapper
p.payment-prompt
| Please enter you billing address and payment information below. You will not be billed for your first JamTrack, which is 100% free.&nbsp;
p.payment-prompt.free-jamtrack.hidden
| Please enter your billing address and payment information below. You will not be billed for your first JamTrack, which is 100% free.&nbsp;
| But we need this data to prevent fraud/abuse of those who would create multiple accounts to collect multiple free JamTracks.&nbsp;
| You will not be billed for any charges of any kind without your explicit authorization.&nbsp;
| There are no "hidden" charges or fees, thank you!
p.payment-prompt.no-free-jamtrack.hidden
| Please enter your billing address and payment information below.&nbsp;
form class="payment-info" id="checkout-payment-info"
.row.first
@ -67,37 +69,49 @@ div layout="screen" layout-id="checkout_payment" id="checkoutPaymentScreen" clas
.billing-label
label for="billing-country" Country:
.billing-value
input type="text" id="billing-country"
select id="billing-country"
option value="US" US
.clearall
.right-side
.payment-method
h2.payment-method-caption PAYMENT METHOD
#divCardName.field
.new-card-info
#divCardName.field.hidden
.card-label
label for="card-name" Name of Card:
.card-value
input type="text" id="card-name"
.clearall
#divCardNumber.field
.card-label
label for="card-number" Card Number:
.card-value
input type="text" id="card-number"
.clearall
#divCardExpiry.field
.card-label Expiration Date:
.card-value
=date_select("card", "expire-date", use_two_digit_numbers: true, discard_day: true, :start_year => Time.now.year, :end_year => Time.now.year + 18, :order => [:month, :day, :year], :default => -25.years.from_now, :html => {:class => "account-profile-birthdate", :id => "card-expiry"})
.clearall
#divCardVerify.field
.card-label
label for="card-verify"
| CVV Code:
.hint.cvv
| (back of card)
.card-value
input type="text" id="card-verify"
.clearall
.reuse-existing-card
.card-label
label for="card-name" Name of Card:
.card-value
input type="text" id="card-name"
.clearall
#divCardNumber.field
.card-label
label for="card-number" Card Number:
.card-value
input type="text" id="card-number"
.clearall
#divCardExpiry.field
.card-label Expiration Date:
.card-value
=date_select("card", "expire-date", use_two_digit_numbers: true, discard_day: true, :start_year => Time.now.year, :end_year => Time.now.year + 18, :order => [:month, :day, :year], :default => -25.years.from_now, :html => {:class => "account-profile-birthdate", :id => "card-expiry"})
.clearall
#divCardVerify.field
.card-label
label for="card-verify"
| CVV Code:
.hint.cvv
| (back-of-card)
.card-value
input type="text" id="card-verify"
.clearall
.reuse-existing-card-checkbox.ichecbuttons
input type="checkbox" id="reuse-existing-card" name="reuse-existing-card" checked="checked"
.reuse-existing-card-helper
label for="reuse-existing-card"
| Use card ending in&nbsp;
span.existing-card-ends-with
.clearall
.card-label
.card-value
.save-card-checkbox.ichecbuttons
@ -108,7 +122,7 @@ div layout="screen" layout-id="checkout_payment" id="checkoutPaymentScreen" clas
.clearall
.clearall
.row.second
.left-side
left-side.hidden
.shipping-address
h2.shipping-address-label SHIPPING ADDRESS
.shipping-as-billing.ichecbuttons
@ -180,6 +194,14 @@ div layout="screen" layout-id="checkout_payment" id="checkoutPaymentScreen" clas
.account-value
input name="password" type="password"
.clearall
#divJamKazamTos.field
.terms-of-service.ichecbuttons
input type="checkbox" name="terms-of-service"
.terms-of-service-label-holder
label for="terms-of-service"
| I have read and agree to the JamKazam&nbsp;
a rel="external" href=corp_terms_path terms of service
.clearall
.clearall

View File

@ -1,4 +1,4 @@
div layout="screen" layout-id="checkout_signin" id="checkoutSignInScreen" class="screen secondary no-login-required"
div layout="screen" layout-id="checkoutSignin" id="checkoutSignInScreen" class="screen secondary no-login-required"
.content
.content-head
.content-icon= image_tag("content/icon_shopping_cart.png", {:height => 19, :width => 19})

View File

@ -41,6 +41,7 @@
<%= render "shopping_cart" %>
<%= render "checkout_signin" %>
<%= render "checkout_payment" %>
<%= render "checkout_order" %>
<%= render "order" %>
<%= render "feed" %>
<%= render "bands" %>
@ -241,7 +242,10 @@
checkoutSignInScreen.initialize();
var checkoutPaymentScreen = new JK.CheckoutPaymentScreen(JK.app);
checkoutPaymentScreen.initialize();
checkoutPaymentScreen.initialize();
var checkoutOrderScreen = new JK.CheckoutOrderScreen(JK.app);
checkoutOrderScreen.initialize();
// var OrderScreen = new JK.OrderScreen(JK.app);
// OrderScreen.initialize();

View File

@ -35,5 +35,7 @@
<body class="jam" data-client-type="<%= @nativeClient ? 'client' : 'browser' %>">
<%= yield %>
<%= render "shared/ga" %>
<%= render "shared/recurly" %>
</body>
</html>

View File

@ -0,0 +1,4 @@
script src="https://js.recurly.com/v3/recurly.js"
javascript:
recurly.configure(gon.global.recurly_public_api_key)

View File

@ -318,5 +318,6 @@ if defined?(Bundler)
config.show_jamblaster_kickstarter_link = true
config.metronome_available = true
config.backing_tracks_available = true
config.one_free_jamtrack_per_user = true
end
end

View File

@ -10,4 +10,6 @@ Gon.global.influxdb_port = Rails.application.config.influxdb_port
Gon.global.influxdb_database = Rails.application.config.influxdb_database
Gon.global.influxdb_username = Rails.application.config.influxdb_unsafe_username
Gon.global.influxdb_password = Rails.application.config.influxdb_unsafe_password
Gon.global.recurly_public_api_key = Rails.application.config.recurly_public_api_key
Gon.global.one_free_jamtrack_per_user = Rails.application.config.one_free_jamtrack_per_user
Gon.global.env = Rails.env

View File

@ -26,8 +26,12 @@ class UserManager < BaseManager
fb_signup = options[:fb_signup]
signup_confirm_url = options[:signup_confirm_url]
affiliate_referral_id = options[:affiliate_referral_id]
recaptcha_failed=(fb_signup) ? false : !@google_client.verify_recaptcha(options[:recaptcha_response])
any_user = options[:any_user]
recaptcha_failed = false
unless options[:skip_recaptcha] # allow callers to opt-of recaptcha
recaptcha_failed = fb_signup ? false : !@google_client.verify_recaptcha(options[:recaptcha_response])
end
user = User.new
# check if we have disabled open signup for this site. open == invited users can still get in
@ -62,11 +66,9 @@ class UserManager < BaseManager
invited_user: invited_user,
fb_signup: fb_signup,
signup_confirm_url: signup_confirm_url,
affiliate_referral_id: affiliate_referral_id)
return user
#end
affiliate_referral_id: affiliate_referral_id,
any_user: any_user)
user
end
def signup_confirm(signup_token, remote_ip=nil)

View File

@ -35,7 +35,7 @@ describe ApiRecurlyController, :type=>:controller do
it "should send correct error" do
@billing_info[:number]='121'
post :create_account, {:format => 'json', :billing_info=>@billing_info}
post :create_account, {:format => 'json', :billing_info=>@billing_info, reuse_card_this_time: false, reuse_card_next_time: false}
response.status.should == 404
body = JSON.parse(response.body)
body['errors'].should have(1).items
@ -43,36 +43,39 @@ describe ApiRecurlyController, :type=>:controller do
end
it "should create account" do
post :create_account, {:format => 'json'}
response.should be_success
post :create_account, {:format => 'json',billing_info: @billing_info, reuse_card_this_time: false, reuse_card_next_time: false}
response.should be_success
body = JSON.parse(response.body)
response.should be_success
body['billing_info']['first_name'].should eq(@user.first_name)
end
it "should retrieve account" do
post :create_account, {:format => 'json'}
it "should retrieve account with no billing info" do
post :create_account, {:format => 'json', reuse_card_this_time: false, reuse_card_next_time: false}
response.should be_success
get :get_account
body = JSON.parse(response.body)
response.should be_success
body['email'].should eq(@user.email)
body['billing_info'].should be_nil
end
it "should update account" do
post :create_account
post :create_account, {:format => 'json', billing_info: @billing_info, reuse_card_this_time: false, reuse_card_next_time: false}
response.should be_success
body = JSON.parse(response.body)
body['first_name'].should eq("Person")
body['billing_info']['first_name'].should eq("Person")
@user.update_attribute(:first_name, "Thing")
controller.current_user = @user
put :update_account
@billing_info[:first_name] = "Thing"
put :update_account, {:format => 'json', billing_info: @billing_info}
body = JSON.parse(response.body)
body['first_name'].should eq("Thing")
body['billing_info']['first_name'].should eq("Thing")
get :get_account, { :format => 'json'}
response.should be_success
body = JSON.parse(response.body)
body['first_name'].should eq("Thing")
body['billing_info']['first_name'].should eq("Thing")
end
# Note: We don't have any subscriptions yet:
@ -95,13 +98,13 @@ describe ApiRecurlyController, :type=>:controller do
# $enable_tracing = true
post :create_account
post :create_account, {:format => 'json', billing_info: @billing_info, reuse_card_this_time: false, reuse_card_next_time: false}
response.should be_success
body = JSON.parse(response.body)
body['first_name'].should eq("Person")
body['billing_info']['first_name'].should eq("Person")
@billing_info[:state] = "NE"
put :update_billing_info, {:format => 'json', :billing_info=>@billing_info}
put :update_billing_info, {format: 'json', billing_info: @billing_info}
response.should be_success
body = JSON.parse(response.body)

View File

@ -20,7 +20,7 @@ FactoryGirl.define do
terms_of_service true
subscribe_email true
last_jam_audio_latency 5
resue_card true
reuse_card true
factory :fan do
musician false

View File

@ -0,0 +1,102 @@
require 'spec_helper'
describe "Checkout", :js => true, :type => :feature, :capybara_feature => true do
let(:user) { FactoryGirl.create(:user) }
let(:jamtrack_acdc_backinblack) { FactoryGirl.create(:jam_track, name: 'Back in Black', original_artist: 'AC/DC', sales_region: 'United States', make_track: true, plan_code: 'jamtrack-acdc-backinblack') }
before(:all) do
Capybara.javascript_driver = :poltergeist
Capybara.current_driver = Capybara.javascript_driver
Capybara.default_wait_time = 30 # these tests are SLOOOOOW
end
before(:each) do
ShoppingCart.delete_all
JamTrack.delete_all
JamTrackTrack.delete_all
JamTrackLicensor.delete_all
stub_const("APP_CONFIG", web_config)
end
describe "Shopping" do
before(:each) do
visit "/client#/jamtrack"
find('h1', text: 'jamtracks')
find('a', text: 'What is a JamTrack?')
jk_select('Any', '#jamtrack-find-form #jamtrack_availability')
end
it "shows all JamTracks" do
find_jamtrack jt_us
find_jamtrack jt_ww
find_jamtrack jt_rock
end
it "filters with availability" do
jk_select('Worldwide', '#jamtrack-find-form #jamtrack_availability')
find_jamtrack jt_ww
not_find_jamtrack jt_us
not_find_jamtrack jt_rock
end
it "filters with genre" do
jk_select('Blues', '#jamtrack-find-form #jamtrack_genre')
find_jamtrack jt_blues
not_find_jamtrack jt_rock
not_find_jamtrack jt_us
not_find_jamtrack jt_ww
end
it "filters with instrument" do
jk_select('Electric Guitar', '#jamtrack-find-form #jamtrack_instrument')
find_jamtrack jt_us
find_jamtrack jt_ww
find_jamtrack jt_rock
end
end
describe "Shopping Carts" do
before(:each) do
visit "/client#/jamtrack"
find('h1', text: 'jamtracks')
find('a', text: 'What is a JamTrack?')
jk_select('Any', '#jamtrack-find-form #jamtrack_availability')
end
it "adds/deletes JamTrack to/from Cart" do
find("a.jamtrack-add-cart[data-jamtrack-id=\"#{jt_us.id}\"]").trigger(:click)
find('h1', text: 'shopping cart')
find('.cart-item-caption', text: "JamTrack: #{jt_us.name}")
find('.cart-item-price', text: "$ #{jt_us.price}")
find('a.button-orange', text: 'CONTINUE SHOPPING').trigger(:click)
jk_select('Any', '#jamtrack-find-form #jamtrack_availability')
find_jamtrack jt_us, {added_cart: true}
find('a.header-shopping-cart').trigger(:click)
find("a.remove-cart").trigger(:click)
find('a.button-orange', text: 'CONTINUE SHOPPING').trigger(:click)
jk_select('Any', '#jamtrack-find-form #jamtrack_availability')
find_jamtrack jt_us
find("a.jamtrack-add-cart[data-jamtrack-id=\"#{jt_us.id}\"]").trigger(:click)
find('a.button-orange', text: 'CONTINUE SHOPPING').trigger(:click)
find("a.jamtrack-add-cart[data-jamtrack-id=\"#{jt_ww.id}\"]").trigger(:click)
find('a.button-orange', text: 'CONTINUE SHOPPING').trigger(:click)
find('.shopping-sub-total', text: "Subtotal: $ #{jt_us.price + jt_ww.price}")
end
end
end

View File

@ -78,6 +78,10 @@ def web_config
def signing_job_queue_max_time
20 # 20 seconds
end
def one_free_jamtrack_per_user
true
end
end
klass.new
end

View File

@ -11,7 +11,7 @@ FactoryGirl.define do
state "NC"
country "US"
terms_of_service true
resue_card true
reuse_card true
factory :admin do