diff --git a/Gemfile b/Gemfile
index cb3243ef..c79d7cc2 100644
--- a/Gemfile
+++ b/Gemfile
@@ -8,12 +8,13 @@ gem 'unicorn'
gem 'foreman'
gem 'crowdtilt', github: 'Crowdtilt/crowdtilt-gem'
+gem 'stripe'
gem 'devise', '~> 3.2.0'
gem 'nokogiri'
gem 'friendly_id', '~> 4.0.9'
gem 'iso_country_codes'
gem 'paperclip', '~> 3.0'
-gem 'ckeditor'
+gem 'ckeditor', '4.0.4'
gem 'aws-sdk'
gem 'active_model_serializers'
gem 'momentjs-rails'
diff --git a/Gemfile.lock b/Gemfile.lock
index e56ab3ff..23973bc8 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -145,6 +145,7 @@ GEM
net-scp (1.1.1)
net-ssh (>= 2.6.5)
net-ssh (2.6.7)
+ netrc (0.10.3)
newrelic_rpm (3.6.3.111)
nokogiri (1.5.9)
orm_adapter (0.4.0)
@@ -190,6 +191,9 @@ GEM
rake (10.0.4)
rdoc (3.12.2)
json (~> 1.4)
+ rest-client (1.7.3)
+ mime-types (>= 1.16, < 3.0)
+ netrc (~> 0.7)
rspec-core (2.13.1)
rspec-expectations (2.13.0)
diff-lcs (>= 1.1.3, < 2.0)
@@ -219,6 +223,9 @@ GEM
multi_json (~> 1.0)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
+ stripe (1.8.8)
+ multi_json (>= 1.0.4, < 2)
+ rest-client (~> 1.4)
thor (0.18.1)
thread_safe (0.1.3)
atomic
@@ -249,7 +256,7 @@ DEPENDENCIES
aws-sdk
bootstrap-sass (= 2.1)
capybara
- ckeditor
+ ckeditor (= 4.0.4)
coffee-rails (~> 3.2.1)
crowdtilt!
devise (~> 3.2.0)
@@ -273,5 +280,6 @@ DEPENDENCIES
rspec-rails
sass-rails (~> 3.2.3)
shoulda
+ stripe
uglifier (>= 1.0.3)
unicorn
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..59babbf5
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,19 @@
+all:
+ RAILS_ENV=production bundle exec foreman start;
+
+stop:
+ cat tmp/unicorn.pid | xargs kill -QUIT ;
+
+restart:
+ make stop; make;
+
+logs:
+ tail -f log/unicorn.log;
+
+assets:
+ RAILS_ENV=production bundle exec foreman run rake assets:precompile
+
+
+local:
+ bundle exec foreman run unicorn -p 5000 -c ./config/unicorn.rb
+
diff --git a/Procfile b/Procfile
index 9c823741..b7578160 100644
--- a/Procfile
+++ b/Procfile
@@ -1 +1 @@
-web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb
+web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb -D
diff --git a/app/assets/images/app_icon_114px.png b/app/assets/images/app_icon_114px.png
index 581bbd02..7896c64e 100644
Binary files a/app/assets/images/app_icon_114px.png and b/app/assets/images/app_icon_114px.png differ
diff --git a/app/assets/images/app_icon_144px.png b/app/assets/images/app_icon_144px.png
index 8b7e600b..d4817cf8 100644
Binary files a/app/assets/images/app_icon_144px.png and b/app/assets/images/app_icon_144px.png differ
diff --git a/app/assets/images/app_icon_172px.png b/app/assets/images/app_icon_172px.png
index 2bdba397..fb04d1ce 100644
Binary files a/app/assets/images/app_icon_172px.png and b/app/assets/images/app_icon_172px.png differ
diff --git a/app/assets/images/apple-touch-icon-120x120.png b/app/assets/images/apple-touch-icon-120x120.png
new file mode 100644
index 00000000..f358bad2
Binary files /dev/null and b/app/assets/images/apple-touch-icon-120x120.png differ
diff --git a/app/assets/images/apple-touch-icon-152x152.png b/app/assets/images/apple-touch-icon-152x152.png
new file mode 100644
index 00000000..159dd8cc
Binary files /dev/null and b/app/assets/images/apple-touch-icon-152x152.png differ
diff --git a/app/assets/images/apple-touch-icon-57x57.png b/app/assets/images/apple-touch-icon-57x57.png
new file mode 100644
index 00000000..5053e49b
Binary files /dev/null and b/app/assets/images/apple-touch-icon-57x57.png differ
diff --git a/app/assets/images/apple-touch-icon-76x76.png b/app/assets/images/apple-touch-icon-76x76.png
new file mode 100644
index 00000000..db781ff0
Binary files /dev/null and b/app/assets/images/apple-touch-icon-76x76.png differ
diff --git a/app/assets/images/apple-touch-icon.png b/app/assets/images/apple-touch-icon.png
new file mode 100644
index 00000000..5053e49b
Binary files /dev/null and b/app/assets/images/apple-touch-icon.png differ
diff --git a/app/assets/images/favicon.ico b/app/assets/images/favicon.ico
new file mode 100644
index 00000000..55badc64
Binary files /dev/null and b/app/assets/images/favicon.ico differ
diff --git a/app/assets/javascripts/campaigns.js.coffee b/app/assets/javascripts/campaigns.js.coffee
index 1dddf008..6674a4e7 100644
--- a/app/assets/javascripts/campaigns.js.coffee
+++ b/app/assets/javascripts/campaigns.js.coffee
@@ -16,11 +16,39 @@ Crowdhoster.campaigns =
$('html,body').animate({scrollTop: $('#header')[0].scrollHeight})
$('#quantity').on "change", (e) ->
- quantity = $(this).val()
+ unit_price_at_qty = parseFloat($(":selected", this).attr('data-price-at-qty'));
+ quantity = parseInt($(this).val())
$amount = $('#amount')
+ functional_total = quantity + parseInt($(".their-qty").attr("data-campaign-qty"))
new_amount = parseFloat($amount.attr('data-original')) * quantity
+ new_amount_display = unit_price_at_qty * quantity
+ $("#unit-price-at-qty").html("$" + unit_price_at_qty.toFixed(2));
$amount.val(new_amount)
- $('#total').html(new_amount.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");)
+ $('#total').html(new_amount_display.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","))
+ $(".their-qty").html(quantity)
+ $(".total-qty").html(functional_total)
+
+ # loop over the tiers and update them
+ current_tier = null
+ $(".tier-list .tier").each (i, item) ->
+ min = parseInt $(item).attr "data-min-qty"
+ $item = $(item);
+ $item.removeClass "list-group-item-success"
+ $item.removeClass "list-group-item-info"
+ $item.removeClass "active"
+
+ # we havent reached this tier
+ if(functional_total < min)
+ $item.addClass "list-group-item-info"
+ $(".people-needed", $item).html "" + (min - functional_total) + " More Orders to Activate"
+ else
+ $item.addClass "list-group-item-success"
+ $(".people-needed", $item).html "Activated with "+min+" Orders"
+ current_tier = $item
+
+ current_tier.addClass "active"
+ $(".people-needed", current_tier).html "Current Price Activated with "+min+" Orders "
+
$('#amount').on "keyup", (e) ->
$(this).addClass('edited')
diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.scss
similarity index 80%
rename from app/assets/stylesheets/application.css
rename to app/assets/stylesheets/application.scss
index fc74c06a..a20ea0d2 100644
--- a/app/assets/stylesheets/application.css
+++ b/app/assets/stylesheets/application.scss
@@ -14,4 +14,20 @@
*= require jquery.ui.all
*= require main
*= require theme
-*/
\ No newline at end of file
+*/
+
+#header {
+ font-size: 35px;
+
+ div.container {
+ color: #ff8e1f;
+
+ a {
+ color: #ff8e1f;
+ }
+
+ a:hover {
+ text-decoration: none;
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/assets/stylesheets/primitives.css.scss b/app/assets/stylesheets/primitives.css.scss
index fc007540..9e7582b0 100644
--- a/app/assets/stylesheets/primitives.css.scss
+++ b/app/assets/stylesheets/primitives.css.scss
@@ -10,7 +10,7 @@ $text_shadow: 0 1px 0px #fff;
$font_size: 22px;
// Lockitron uses ProximaNova Regular, but that costs money, so we're defaulting to Helveitca Neue instead.
-$primary_font: "Helvetica Neue";
+$primary_font: "proxima-nova-alt";
$secondary_font: "Helvetica";
$tertiary_font: "Arial";
@@ -29,7 +29,7 @@ $tertiary_font: "Arial";
h1 {
margin: 0 auto;
- font-family: Helvetica;
+ font-family: $primary_font;
font-size: 42px;
font-weight: bold;
color: $h1_color;
@@ -76,7 +76,7 @@ h5 {
}
p {
- font-family: "Helvetica Neue","Helvetica","Arial";
+ font-family: $primary_font, $secondary_font, $tertiary_font;
color: #6b6b6b;
font-size: 18px;
line-height: 1.5;
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 484ffdd2..321b058d 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -4,6 +4,15 @@ class ApplicationController < ActionController::Base
before_filter :load_settings, :set_default_mailer_host
after_filter :store_location
+ def capture
+ if(params.has_key?(:referral_code))
+ ReferralCode.create(params[:referral_code]);
+ redirect_to root_url, :flash => { :info => "Thank you for you're interest, we'll contact you before the next sale starts!" }
+ else
+ redirect_to root_url, :flash => { :error => "We're sorry there seems to have been an issue with your submission. Please try again later." }
+ end
+ end
+
def load_settings
@settings = Settings.first
diff --git a/app/controllers/campaigns_controller.rb b/app/controllers/campaigns_controller.rb
index d0093d98..cba004d9 100644
--- a/app/controllers/campaigns_controller.rb
+++ b/app/controllers/campaigns_controller.rb
@@ -62,8 +62,10 @@ def checkout_payment
return
end
+ @display_subtotal = @campaign.price_at_additional_qty(@quantity) * @quantity
@fee = (@campaign.apply_processing_fee)? calculate_processing_fee(@amount * 100)/100.0 : 0
@total = @amount + @fee
+ @display_total = @display_subtotal + @fee
end
@@ -123,53 +125,76 @@ def checkout_process
end
@payment.reward = @reward if @reward
+ @payment.ip_addr = request.remote_ip
@payment.save
# Execute the payment via the Crowdtilt API, if it fails, redirect user
begin
- payment = {
- amount: payment_params[:amount],
- user_fee_amount: user_fee_amount,
- admin_fee_amount: admin_fee_amount,
- user_id: ct_user_id,
- card_id: ct_card_id,
- metadata: {
- fullname: payment_params[:fullname],
- email: payment_params[:email],
- billing_postal_code: payment_params[:billing_postal_code],
- quantity: payment_params[:quantity],
- reward: @reward ? @reward.id : 0,
- additional_info: payment_params[:additional_info]
- }
- }
- @campaign.production_flag ? Crowdtilt.production(@settings) : Crowdtilt.sandbox
-
- logger.info "CROWDTILT API REQUEST: /campaigns/#{@campaign.ct_campaign_id}/payments"
- logger.info payment
- response = Crowdtilt.post('/campaigns/' + @campaign.ct_campaign_id + '/payments', {payment: payment})
- logger.info "CROWDTILT API RESPONSE:"
- logger.info response
- rescue Crowdtilt::ApiError => api_error
- response = api_error.response
- logger.error "API ERROR WITH POST TO /payments: #{response[:status]} #{response[:body]}"
- error_attributes = {status: 'error'}
- error_attributes[:ct_charge_request_id] = response[:body]['request_id'] if response[:body]['request_id']
- error_attributes[:ct_charge_request_error_id] = response[:body]['error_id'] if response[:body]['error_id']
- @payment.update_attributes(error_attributes)
- redirect_to checkout_amount_url(@campaign), flash: { error: "There was an error processing your payment. Please try again or contact support by emailing open@tilt.com" } and return
- rescue StandardError => exception
- @payment.update_attributes({status: 'error'})
- logger.error "ERROR WITH POST TO /payments: #{exception.message}"
- redirect_to checkout_amount_url(@campaign), flash: { error: "There was an error processing your payment. Please try again or contact support by emailing open@tilt.com" } and return
+ require "stripe"
+ Stripe.api_key = ENV['STRIPE_KEY']
+
+ response = Stripe::Charge.create(
+ :amount => payment_params[:amount],
+ :currency => "usd",
+ :source => {
+ :object => "card",
+ :number => params[:card_no],
+ :exp_month => params[:expiration_month],
+ :exp_year => params[:expiration_year],
+ :name => payment_params[:fullname],
+ :cvc => params[:security_code],
+ :address_zip => params[:billing_postal_code]
+ },
+ :description => "Cincodebuyo campaign authorization",
+ :capture => false
+ )
+ rescue => e
+ redirect_to checkout_amount_url(@campaign), flash: { error: "There was an error processing your payment. Please try again or contact support by emailing info@cincodebuyo.com" } and return
+
+
+ # payment = {
+ # amount: payment_params[:amount],
+ # user_fee_amount: user_fee_amount,
+ # admin_fee_amount: admin_fee_amount,
+ # user_id: ct_user_id,
+ # card_id: ct_card_id,
+ # metadata: {
+ # fullname: payment_params[:fullname],
+ # email: payment_params[:email],
+ # billing_postal_code: payment_params[:billing_postal_code],
+ # quantity: payment_params[:quantity],
+ # reward: @reward ? @reward.id : 0,
+ # additional_info: payment_params[:additional_info]
+ # }
+ # }
+ # @campaign.production_flag ? Crowdtilt.production(@settings) : Crowdtilt.sandbox
+
+ # logger.info "CROWDTILT API REQUEST: /campaigns/#{@campaign.ct_campaign_id}/payments"
+ # logger.info payment
+ # response = Crowdtilt.post('/campaigns/' + @campaign.ct_campaign_id + '/payments', {payment: payment})
+ # logger.info "CROWDTILT API RESPONSE:"
+ # logger.info response
+ # rescue Crowdtilt::ApiError => api_error
+ # response = api_error.response
+ # logger.error "API ERROR WITH POST TO /payments: #{response[:status]} #{response[:body]}"
+ # error_attributes = {status: 'error'}
+ # error_attributes[:ct_charge_request_id] = response[:body]['request_id'] if response[:body]['request_id']
+ # error_attributes[:ct_charge_request_error_id] = response[:body]['error_id'] if response[:body]['error_id']
+ # @payment.update_attributes(error_attributes)
+ # redirect_to checkout_amount_url(@campaign), flash: { error: "There was an error processing your payment. Please try again or contact support by emailing open@tilt.com" } and return
+ # rescue StandardError => exception
+ # @payment.update_attributes({status: 'error'})
+ # logger.error "ERROR WITH POST TO /payments: #{exception.message}"
+ # redirect_to checkout_amount_url(@campaign), flash: { error: "There was an error processing your payment. Please try again or contact support by emailing open@tilt.com" } and return
end
# Sync payment data
- @payment.update_api_data(response['payment'])
- @payment.ct_charge_request_id = response['request_id']
+ @payment.update_api_data(response)
+ @payment.ct_charge_request_id = response['id']
@payment.save
# Sync campaign data
- @campaign.update_api_data(response['payment']['campaign'])
+ @campaign.update_api_data(@payment.amount)
@campaign.save
# Send confirmation emails
diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb
index 904e9033..0a910b5c 100644
--- a/app/controllers/pages_controller.rb
+++ b/app/controllers/pages_controller.rb
@@ -2,6 +2,11 @@ class PagesController < ApplicationController
before_filter :check_init
def index
+ # Check for referral code
+ if request[:referral_code]
+ cookies[:referral_code] = request[:referral_code]
+ end
+
if @settings.default_campaign && ((user_signed_in? && current_user.admin?) || @settings.default_campaign.published_flag)
redirect_to campaign_home_url(@settings.default_campaign)
else
diff --git a/app/mixins/checkout_mixin.rb b/app/mixins/checkout_mixin.rb
index f2e242cd..aa2366a6 100644
--- a/app/mixins/checkout_mixin.rb
+++ b/app/mixins/checkout_mixin.rb
@@ -21,6 +21,9 @@ def basic_payment_info(params)
billing_postal_code: params[:billing_postal_code],
quantity: params[:quantity].to_i,
+ #Referral
+ referred_by: params[:referred_by],
+
#Shipping Info
address_one: params.has_key?(:address_one) ? params[:address_one] : '',
address_two: params.has_key?(:address_two) ? params[:address_two] : '',
diff --git a/app/models/campaign.rb b/app/models/campaign.rb
index 5f310f48..c0352aca 100644
--- a/app/models/campaign.rb
+++ b/app/models/campaign.rb
@@ -4,6 +4,7 @@ class Campaign < ActiveRecord::Base
has_many :faqs, dependent: :destroy, :order => 'sort_order'
has_many :payments
has_many :rewards
+ has_many :campaign_tiers, :order => 'min_users ASC'
attr_accessible :name, :goal_type, :goal_dollars, :goal_orders, :expiration_date, :ct_campaign_id, :media_type,
:main_image, :main_image_delete, :video_embed_id, :video_placeholder, :video_placeholder_delete,
@@ -39,15 +40,79 @@ class Campaign < ActiveRecord::Base
before_save :set_min_amount
- def update_api_data(campaign)
- self.ct_campaign_id = campaign['id']
- self.stats_number_of_contributions = campaign['stats']['number_of_contributions']
- self.stats_raised_amount = campaign['stats']['raised_amount']/100.0
- self.stats_tilt_percent = campaign['stats']['tilt_percent']
- self.stats_unique_contributors = campaign['stats']['unique_contributors']
- self.is_tilted = campaign['is_tilted'].to_i == 0 ? false : true
- self.is_expired = campaign['is_expired'].to_i == 0 ? false : true
- self.is_paid = campaign['is_paid'].to_i == 0 ? false : true
+ def update_api_data(amount)
+ # self.ct_campaign_id = campaign['id']
+ # self.stats_raised_amount = amount / 100.0
+ self.stats_number_of_contributions = self.orders
+ self.stats_unique_contributors = self.payments.length
+ self.stats_tilt_percent = 100
+ self.is_tilted = true
+ self.is_paid = false
+ self.is_expired = self.expired?
+ end
+
+ def stats_raised_amount
+ total = 0
+ self.payments.each do |p|
+ if(p.status === "paid")
+ total = total + (p.amount / 100.0)
+ end
+ end
+
+ total + (self.fake_users * self.fixed_payment_amount)
+ end
+
+ def price_at_additional_qty(amount)
+ expected_orders = self.orders + amount
+ price = self.base_price
+ tier = self.tier_for_orders expected_orders
+ if(!tier.nil?)
+ price = tier.price_at_tier
+ end
+
+ price
+ end
+
+ def current_tier_price
+ price = self.base_price;
+ if(!self.current_tier.nil?)
+ price = self.current_tier.price_at_tier
+ end
+
+ price
+ end
+
+ def next_tier
+ tier = self.campaign_tiers.first
+ self.campaign_tiers.each do |t|
+ if(self.orders < t.min_users)
+ tier = t
+ break
+ end
+ end
+
+ tier
+ end
+
+ def until_next_tier
+ self.next_tier.min_users - self.orders
+ end
+
+ def current_tier
+ self.tier_for_orders self.orders
+ end
+
+ def tier_for_orders(order_amount)
+ max_users = 0;
+ tier = nil
+ self.campaign_tiers.each do |t|
+ if(order_amount >= t.min_users && max_users <= t.min_users)
+ tier = t
+ max_users = t.min_users
+ end
+ end
+
+ tier
end
def set_goal
@@ -57,7 +122,7 @@ def set_goal
end
def expired?
- self.expiration_date < Time.current
+ self.expiration_date < Time.current or self.sold_out
end
def orders
@@ -81,7 +146,7 @@ def number_of_contributions
end
def tilt_percent
- (raised_amount / goal_dollars) * 100.0
+ (self.stats_raised_amount / goal_dollars) * 100.0
end
private
diff --git a/app/models/campaign_tier.rb b/app/models/campaign_tier.rb
new file mode 100644
index 00000000..c362eb9d
--- /dev/null
+++ b/app/models/campaign_tier.rb
@@ -0,0 +1,21 @@
+class CampaignTier < ActiveRecord::Base
+ attr_accessible :campaign_id, :min_users, :price_at_tier
+ belongs_to :campaign
+
+ def pct_off
+ ((self.campaign.base_price - self.price_at_tier) / self.campaign.base_price) * 100.0
+ end
+
+ def pct_complete
+ orders = self.campaign.orders
+ (1.0 * [orders, self.min_users].min / self.min_users) * 100.0
+ end
+
+ def complete
+ self.pct_complete >= 100
+ end
+
+ def remaining
+ return [0, self.min_users - self.campaign.orders].max
+ end
+end
diff --git a/app/models/payment.rb b/app/models/payment.rb
index 7cec8d77..481a1d34 100644
--- a/app/models/payment.rb
+++ b/app/models/payment.rb
@@ -5,7 +5,9 @@ class Payment < ActiveRecord::Base
:additional_info, :client_timestamp,
:ct_charge_request_id, :ct_charge_request_error_id,
:ct_tokenize_request_id, :ct_tokenize_request_error_id,
- :ct_user_id
+ :ct_user_id,
+ :referred_by,
+ :ip_addr
validates :fullname, :quantity, presence: true
validates :email, presence: true, email: true
@@ -56,12 +58,12 @@ def update_api_data(payment)
self.ct_payment_id = payment['id']
self.status = payment['status']
self.amount = payment['amount']
- self.user_fee_amount = payment['user_fee_amount']
- self.admin_fee_amount = payment['admin_fee_amount']
- self.card_type = payment['card']['card_type']
- self.card_last_four = payment['card']['last_four']
- self.card_expiration_month = payment['card']['expiration_month']
- self.card_expiration_year = payment['card']['expiration_year']
+ self.user_fee_amount = 0
+ self.admin_fee_amount = 0
+ self.card_type = payment['source']['brand']
+ self.card_last_four = payment['source']['last4']
+ self.card_expiration_month = payment['source']['exp_month']
+ self.card_expiration_year = payment['source']['exp_year']
end
def refund!
@@ -78,4 +80,26 @@ def self.display_date(date)
date.strftime("%m/%d/%Y")
end
+ def referral_code
+ code = ReferralCode.where(email: self.email)
+ if(code.length > 0)
+ return code[0]
+ else
+ code = ReferralCode.create(code:('0'..'9').to_a.concat(('A'..'Z').to_a).concat(('a'..'z').to_a).shuffle[0,8].join, email: self.email, comment:"purchase #" + self.id.to_s)
+ code
+ end
+ end
+
+ # return the referral code for the user who made this payment
+ # returns nil if no user is found or if they do not have a referral
+ # source
+ def get_user_referral_code()
+ user = User.where(email: self.email)
+ if(user.length > 0)
+ return user[0].referred_by
+ else
+ return nil
+ end
+ end
+
end
diff --git a/app/models/referral_code.rb b/app/models/referral_code.rb
new file mode 100644
index 00000000..7b5edb47
--- /dev/null
+++ b/app/models/referral_code.rb
@@ -0,0 +1,9 @@
+class ReferralCode < ActiveRecord::Base
+ attr_accessible :code, :comment, :email
+
+ def before_save
+ if(self.code.nil?)
+ self.code = ('0'..'9').to_a.concat(('A'..'Z').to_a).concat(('a'..'z').to_a).shuffle[0,8].join
+ end
+ end
+end
diff --git a/app/models/user.rb b/app/models/user.rb
index dfe1d134..f6241e0d 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -6,10 +6,9 @@ class User < ActiveRecord::Base
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :fullname,
- :wants_admin_payment_notification
+ :wants_admin_payment_notification, :referred_by
# Validate presence of user inputs.
# (most in this model are handled by Devise -- email, password, and password_confirmation)
validates :fullname, presence: true
-
end
diff --git a/app/views/campaigns/checkout_amount.html.erb b/app/views/campaigns/checkout_amount.html.erb
index 913e2c5c..adab47af 100644
--- a/app/views/campaigns/checkout_amount.html.erb
+++ b/app/views/campaigns/checkout_amount.html.erb
@@ -1,9 +1,9 @@
-
-
-
-
-
+
+
+
+
<% end %>
-
-
Continue to checkout
+
+
Continue to checkout
-
-
-