summaryrefslogtreecommitdiff
path: root/engines/billing/app
diff options
context:
space:
mode:
Diffstat (limited to 'engines/billing/app')
-rw-r--r--engines/billing/app/controllers/billing_admin_controller.rb29
-rw-r--r--engines/billing/app/controllers/billing_base_controller.rb22
-rw-r--r--engines/billing/app/controllers/credit_card_info_controller.rb35
-rw-r--r--engines/billing/app/controllers/customer_controller.rb64
-rw-r--r--engines/billing/app/controllers/payments_controller.rb34
-rw-r--r--engines/billing/app/controllers/subscriptions_controller.rb63
-rw-r--r--engines/billing/app/helpers/billing_helper.rb51
-rw-r--r--engines/billing/app/helpers/braintree_form_helper.rb64
-rw-r--r--engines/billing/app/helpers/braintree_helper.rb5
-rw-r--r--engines/billing/app/models/customer.rb58
-rw-r--r--engines/billing/app/views/billing_admin/show.html.haml7
-rw-r--r--engines/billing/app/views/credit_card_info/confirm.html.haml5
-rw-r--r--engines/billing/app/views/credit_card_info/edit.html.haml17
-rw-r--r--engines/billing/app/views/customer/_customer_data.html.haml16
-rw-r--r--engines/billing/app/views/customer/_transaction.html.haml0
-rw-r--r--engines/billing/app/views/customer/confirm.html.haml14
-rw-r--r--engines/billing/app/views/customer/edit.html.haml23
-rw-r--r--engines/billing/app/views/customer/new.html.haml24
-rw-r--r--engines/billing/app/views/customer/show.html.haml27
-rw-r--r--engines/billing/app/views/payments/_non_customer_fields.html.haml16
-rw-r--r--engines/billing/app/views/payments/_transaction_details.html.haml15
-rw-r--r--engines/billing/app/views/payments/confirm.html.haml26
-rw-r--r--engines/billing/app/views/payments/index.html.haml5
-rw-r--r--engines/billing/app/views/payments/new.html.haml17
-rw-r--r--engines/billing/app/views/subscriptions/_subscription_details.html.haml26
-rw-r--r--engines/billing/app/views/subscriptions/create.html.haml9
-rw-r--r--engines/billing/app/views/subscriptions/destroy.html.haml7
-rw-r--r--engines/billing/app/views/subscriptions/index.html.haml8
-rw-r--r--engines/billing/app/views/subscriptions/new.html.haml15
-rw-r--r--engines/billing/app/views/subscriptions/show.html.haml6
30 files changed, 708 insertions, 0 deletions
diff --git a/engines/billing/app/controllers/billing_admin_controller.rb b/engines/billing/app/controllers/billing_admin_controller.rb
new file mode 100644
index 0000000..e11d4ee
--- /dev/null
+++ b/engines/billing/app/controllers/billing_admin_controller.rb
@@ -0,0 +1,29 @@
+class BillingAdminController < BillingBaseController
+ before_filter :require_admin
+
+ def show
+
+ br_atleast_90_days = Braintree::Subscription.search do |search|
+ search.days_past_due >= 90
+ end
+ @past_due_atleast_90_days = braintree_resource_collection_to_array(br_atleast_90_days)
+
+ br_all_past_due = Braintree::Subscription.search do |search|
+ search.status.is Braintree::Subscription::Status::PastDue
+ #cannot search by balance.
+ end
+ @all_past_due = braintree_resource_collection_to_array(br_all_past_due)
+
+ end
+
+ private
+
+ def braintree_resource_collection_to_array(braintree_resource_collection)
+ array = []
+ braintree_resource_collection.each do |object|
+ array << object
+ end
+ array
+ end
+
+end
diff --git a/engines/billing/app/controllers/billing_base_controller.rb b/engines/billing/app/controllers/billing_base_controller.rb
new file mode 100644
index 0000000..0453677
--- /dev/null
+++ b/engines/billing/app/controllers/billing_base_controller.rb
@@ -0,0 +1,22 @@
+class BillingBaseController < ApplicationController
+ before_filter :assign_user
+
+ helper 'billing'
+
+ # required for navigation to work.
+ def assign_user
+ if params[:user_id]
+ @user = User.find(params[:user_id])
+ elsif params[:action] == "confirm"# confirms will come back with different ID set, so check for this first
+ # This is only for cases where an admin cannot apply action for customer, but should be all confirms
+ @user = current_user
+ elsif params[:id]
+ @user = User.find(params[:id])
+ else
+ # TODO
+ # hacky, what are cases where @user hasn't yet been set? certainly some cases with subscriptions and payments
+ @user = current_user
+ end
+ end
+
+end
diff --git a/engines/billing/app/controllers/credit_card_info_controller.rb b/engines/billing/app/controllers/credit_card_info_controller.rb
new file mode 100644
index 0000000..fbaa6f1
--- /dev/null
+++ b/engines/billing/app/controllers/credit_card_info_controller.rb
@@ -0,0 +1,35 @@
+class CreditCardInfoController < ApplicationController
+ before_filter :require_login, :set_user
+
+ def edit
+ @credit_card = Braintree::CreditCard.find(params[:id])
+ customer = Customer.find_by_user_id(@user.id)
+ if customer and customer.braintree_customer_id == @credit_card.customer_id
+ @tr_data = Braintree::TransparentRedirect.
+ update_credit_card_data(:redirect_url => confirm_credit_card_info_url,
+ :payment_method_token => @credit_card.token)
+ else
+ access_denied
+ end
+
+ end
+
+ def confirm
+ @result = Braintree::TransparentRedirect.confirm(request.query_string)
+ if @result.success?
+ render :action => "confirm"
+ else
+ @credit_card = Braintree::CreditCard.find(@result.params[:payment_method_token])
+ render :action => "edit"
+ end
+ end
+
+
+ private
+
+ def set_user
+ # this assumes anybody, even an admin, will not access for another user.
+ @user = current_user
+ end
+
+end
diff --git a/engines/billing/app/controllers/customer_controller.rb b/engines/billing/app/controllers/customer_controller.rb
new file mode 100644
index 0000000..6cbcb44
--- /dev/null
+++ b/engines/billing/app/controllers/customer_controller.rb
@@ -0,0 +1,64 @@
+class CustomerController < BillingBaseController
+ before_filter :require_login, :fetch_customer
+
+ def show
+ if @customer
+ @customer.with_braintree_data!
+ @default_cc = @customer.default_credit_card
+ @active_subscription = @customer.subscriptions
+ @transactions = @customer.braintree_customer.transactions
+ end
+ end
+
+ def new
+ if @customer.has_payment_info?
+ redirect_to edit_customer_path(@user), :notice => 'Here is your saved customer data'
+ else
+ fetch_new_transparent_redirect_data
+ end
+ end
+
+ def edit
+ fetch_edit_transparent_redirect_data
+ end
+
+ def confirm
+ @result = Braintree::TransparentRedirect.confirm(request.query_string)
+ if @result.success?
+ @customer.braintree_customer = @result.customer
+ @customer.save
+ render :action => "confirm"
+ elsif @customer.has_payment_info?
+ fetch_edit_transparent_redirect_data
+ render :action => "edit"
+ else
+ fetch_new_transparent_redirect_data
+ render :action => "new"
+ end
+ end
+
+ protected
+
+ def fetch_new_transparent_redirect_data
+ access_denied unless @user == current_user # admins cannot do this for others
+ @tr_data = Braintree::TransparentRedirect.
+ create_customer_data(:redirect_url => confirm_customer_url)
+ end
+
+ def fetch_edit_transparent_redirect_data
+ access_denied unless @user == current_user # admins cannot do this for others
+ @customer.with_braintree_data!
+ @default_cc = @customer.default_credit_card
+ @tr_data = Braintree::TransparentRedirect.
+ update_customer_data(:redirect_url => confirm_customer_url,
+ :customer_id => @customer.braintree_customer_id) ##??
+ end
+
+ def fetch_customer
+ @customer = Customer.find_by_user_id(@user.id)
+ if @user == current_user
+ @customer ||= Customer.new(user: @user)
+ end
+ access_denied unless (@customer and (@customer.user == current_user)) or admin?
+ end
+end
diff --git a/engines/billing/app/controllers/payments_controller.rb b/engines/billing/app/controllers/payments_controller.rb
new file mode 100644
index 0000000..fce6570
--- /dev/null
+++ b/engines/billing/app/controllers/payments_controller.rb
@@ -0,0 +1,34 @@
+class PaymentsController < BillingBaseController
+ before_filter :require_login, :only => [:index]
+
+ def new
+ fetch_transparent_redirect
+ end
+
+ def confirm
+ @result = Braintree::TransparentRedirect.confirm(request.query_string)
+ if @result.success?
+ render :action => "confirm"
+ else
+ fetch_transparent_redirect
+ render :action => "new"
+ end
+ end
+
+ def index
+ access_denied unless admin? or (@user == current_user)
+ customer = Customer.find_by_user_id(@user.id)
+ braintree_data = Braintree::Customer.find(customer.braintree_customer_id)
+ # these will be ordered by created_at descending, per http://stackoverflow.com/questions/16425475/
+ @transactions = braintree_data.transactions
+ end
+
+ protected
+
+
+ def fetch_transparent_redirect
+ @tr_data = Braintree::TransparentRedirect.transaction_data redirect_url: confirm_payment_url,
+ transaction: { type: "sale", options: {submit_for_settlement: true } }
+ end
+
+end
diff --git a/engines/billing/app/controllers/subscriptions_controller.rb b/engines/billing/app/controllers/subscriptions_controller.rb
new file mode 100644
index 0000000..f066b3c
--- /dev/null
+++ b/engines/billing/app/controllers/subscriptions_controller.rb
@@ -0,0 +1,63 @@
+class SubscriptionsController < BillingBaseController
+ before_filter :require_login
+ before_filter :fetch_subscription, :only => [:show, :destroy]
+ before_filter :confirm_cancel_subscription, :only => [:destroy]
+ before_filter :confirm_self_or_admin, :only => [:index]
+ before_filter :confirm_no_pending_active_pastdue_subscription, :only => [:new, :create]
+ # for now, admins cannot create or destroy subscriptions for others:
+ before_filter :confirm_self, :only => [:new, :create]
+
+ def new
+ # don't show link to subscribe if they are already subscribed?
+ credit_card = @customer.default_credit_card #safe to assume default?
+ @payment_method_token = credit_card.token
+ @plans = Braintree::Plan.all
+ end
+
+ # show has no content, so not needed at this point.
+
+ def create
+ @result = Braintree::Subscription.create( :payment_method_token => params[:payment_method_token], :plan_id => params[:plan_id] )
+ #if you want to test pastdue, can add :price => '2001', :trial_period => true,:trial_duration => 1,:trial_duration_unit => "day" and then wait a day
+ end
+
+ def destroy
+ @result = Braintree::Subscription.cancel params[:id]
+ end
+
+ def index
+ customer = Customer.find_by_user_id(@user.id)
+ @subscriptions = customer.subscriptions(nil, false)
+ end
+
+ private
+
+ def fetch_subscription
+ @subscription = Braintree::Subscription.find params[:id]
+ @credit_card = Braintree::CreditCard.find @subscription.payment_method_token
+ @subscription_customer_id = @credit_card.customer_id
+ current_user_customer = Customer.find_by_user_id(current_user.id)
+ access_denied unless admin? or (current_user_customer and current_user_customer.braintree_customer_id == @subscription_customer_id)
+
+ end
+
+ def confirm_cancel_subscription
+ access_denied unless view_context.allow_cancel_subscription(@subscription)
+ end
+
+ def confirm_no_pending_active_pastdue_subscription
+ @customer = Customer.find_by_user_id(@user.id)
+ if subscription = @customer.subscriptions # will return pending, active or pastdue subscription, if it exists
+ redirect_to user_subscription_path(@user, subscription.id), :notice => 'You already have a subscription'
+ end
+ end
+
+ def confirm_self
+ @user == current_user
+ end
+
+ def confirm_self_or_admin
+ access_denied unless confirm_self or admin?
+ end
+
+end
diff --git a/engines/billing/app/helpers/billing_helper.rb b/engines/billing/app/helpers/billing_helper.rb
new file mode 100644
index 0000000..b9e5e2e
--- /dev/null
+++ b/engines/billing/app/helpers/billing_helper.rb
@@ -0,0 +1,51 @@
+module BillingHelper
+
+ def braintree_form_for(object, options = {}, &block)
+ options.reverse_merge! params: @result && @result.params[object],
+ errors: @result && @result.errors.for(object),
+ builder: BraintreeFormHelper::BraintreeFormBuilder,
+ url: Braintree::TransparentRedirect.url
+
+ form_for object, options, &block
+ end
+
+ def billing_top_link(user)
+ # for admins, top link will show special admin information, which has link to show their own customer information
+ if (admin? and user == current_user)
+ billing_admin_path
+ else
+ show_or_new_customer_link(user)
+ end
+ end
+
+ def show_or_new_customer_link(user)
+ # Link to show if user is admin viewing another user, or user is already a customer.
+ # Otherwise link to create a new customer.
+ if (admin? and (user != current_user)) or ((customer = Customer.find_by_user_id(user.id)) and customer.has_payment_info?)
+ show_customer_path(user)
+ else
+ new_customer_path
+ end
+ end
+
+ # a bit strange to put here, but we don't have a subscription model
+ def user_for_subscription(subscription)
+
+ if (transaction = subscription.transactions.first)
+ # much quicker, but will only work if there is already a transaction associated with subscription (should generally be)
+ braintree_customer_id = transaction.customer_details.id
+ else
+ credit_card = Braintree::CreditCard.find(subscription.payment_method_token)
+ braintree_customer_id = credit_card.customer_id
+ end
+
+ customer = Customer.find_by_braintree_customer_id(braintree_customer_id)
+ user = User.find(customer.user_id)
+
+ end
+
+ def allow_cancel_subscription(subscription)
+ ['Active', 'Pending'].include? subscription.status or (admin? and subscription.status == 'Past Due')
+ end
+
+end
diff --git a/engines/billing/app/helpers/braintree_form_helper.rb b/engines/billing/app/helpers/braintree_form_helper.rb
new file mode 100644
index 0000000..cb322fa
--- /dev/null
+++ b/engines/billing/app/helpers/braintree_form_helper.rb
@@ -0,0 +1,64 @@
+module BraintreeFormHelper
+ class BraintreeFormBuilder < ActionView::Helpers::FormBuilder
+ include ActionView::Helpers::AssetTagHelper
+ include ActionView::Helpers::TagHelper
+
+ def initialize(object_name, object, template, options, proc)
+ super
+ @braintree_params = @options[:params]
+ @braintree_errors = @options[:errors]
+ @braintree_existing = @options[:existing]
+ end
+
+ def fields_for(record_name, *args, &block)
+ options = args.extract_options!
+ options[:builder] = BraintreeFormBuilder
+ options[:params] = @braintree_params && @braintree_params[record_name]
+ options[:errors] = @braintree_errors && @braintree_errors.for(record_name)
+ new_args = args + [options]
+ super record_name, *new_args, &block
+ end
+
+ def text_field(method, options = {})
+ has_errors = @braintree_errors && @braintree_errors.on(method).any?
+ field = super(method, options.merge(:value => determine_value(method)))
+ result = content_tag("div", field, :class => has_errors ? "fieldWithErrors" : "")
+ result.safe_concat validation_errors(method)
+ result
+ end
+
+ protected
+
+ def determine_value(method)
+ if @braintree_params
+ @braintree_params[method]
+ elsif @braintree_existing
+
+ if @braintree_existing.kind_of?(Braintree::CreditCard)
+
+ case method
+ when :number
+ method = :masked_number
+ when :cvv
+ return nil
+ end
+ end
+
+ @braintree_existing.send(method)
+ else
+ nil
+ end
+ end
+
+ def validation_errors(method)
+ if @braintree_errors && @braintree_errors.on(method).any?
+ @braintree_errors.on(method).map do |error|
+ content_tag("div", ERB::Util.h(error.message), {:style => "color: red;"})
+ end.join
+ else
+ ""
+ end
+ end
+ end
+end
+
diff --git a/engines/billing/app/helpers/braintree_helper.rb b/engines/billing/app/helpers/braintree_helper.rb
new file mode 100644
index 0000000..2d18b6c
--- /dev/null
+++ b/engines/billing/app/helpers/braintree_helper.rb
@@ -0,0 +1,5 @@
+module BraintreeHelper
+
+
+end
+
diff --git a/engines/billing/app/models/customer.rb b/engines/billing/app/models/customer.rb
new file mode 100644
index 0000000..1acc7a5
--- /dev/null
+++ b/engines/billing/app/models/customer.rb
@@ -0,0 +1,58 @@
+class Customer < CouchRest::Model::Base
+
+ FIELDS = [:first_name, :last_name, :phone, :website, :company, :fax, :addresses, :credit_cards, :custom_fields]
+ attr_accessor *FIELDS
+
+ use_database "customers"
+ belongs_to :user
+ belongs_to :braintree_customer
+
+ # Braintree::Customer - stored on braintrees servers - we only have the id.
+ def braintree_customer
+ @braintree_customer ||= Braintree::Customer.find(braintree_customer_id)
+ end
+
+ validates :user, presence: true
+
+ design do
+ view :by_user_id
+ view :by_braintree_customer_id
+ end
+
+ def has_payment_info?
+ !!braintree_customer_id
+ end
+
+ # from braintree_ruby_examples/rails3_tr_devise and should be tweaked
+ def with_braintree_data!
+ return self unless has_payment_info?
+
+ FIELDS.each do |field|
+ send(:"#{field}=", braintree_customer.send(field))
+ end
+ self
+ end
+
+ def default_credit_card
+ return unless has_payment_info?
+
+ credit_cards.find { |cc| cc.default? }
+ end
+
+ # based on 2nd parameter, either returns the single active subscription (or nil if there isn't one), or an array of all subsciptions
+ def subscriptions(braintree_data=nil, only_pending_active_pastdue=true)
+ self.with_braintree_data!
+ return unless has_payment_info?
+
+ subscriptions = []
+ self.default_credit_card.subscriptions.each do |sub|
+ if only_pending_active_pastdue and ['Pending', 'Active','Past Due'].include? sub.status
+ return sub
+ else
+ subscriptions << sub
+ end
+ end
+ only_pending_active_pastdue ? nil : subscriptions
+ end
+
+end
diff --git a/engines/billing/app/views/billing_admin/show.html.haml b/engines/billing/app/views/billing_admin/show.html.haml
new file mode 100644
index 0000000..0382cf0
--- /dev/null
+++ b/engines/billing/app/views/billing_admin/show.html.haml
@@ -0,0 +1,7 @@
+%legend= t(:more_than_90_days_past_due)
+= render(:partial => "subscriptions/subscription_details", :collection => @past_due_atleast_90_days, :as => 'subscription', :locals => {:show_user => true}) || t(:none)
+%legend= t(:all_past_due)
+= render(:partial => "subscriptions/subscription_details", :collection => @all_past_due, :as => 'subscription', :locals => {:show_user => true}) || t(:none)
+
+%legend= t(:your_settings)
+= link_to 'view own billing settings', show_or_new_customer_link(current_user) \ No newline at end of file
diff --git a/engines/billing/app/views/credit_card_info/confirm.html.haml b/engines/billing/app/views/credit_card_info/confirm.html.haml
new file mode 100644
index 0000000..9dd8176
--- /dev/null
+++ b/engines/billing/app/views/credit_card_info/confirm.html.haml
@@ -0,0 +1,5 @@
+%h1 Payment Info Confirmation
+%p Your payment information was successfully saved.
+%dl
+ %dt Credit Card
+ %dd= @result.credit_card.masked_number
diff --git a/engines/billing/app/views/credit_card_info/edit.html.haml b/engines/billing/app/views/credit_card_info/edit.html.haml
new file mode 100644
index 0000000..bd86a4c
--- /dev/null
+++ b/engines/billing/app/views/credit_card_info/edit.html.haml
@@ -0,0 +1,17 @@
+%h1 Change Credit Card
+- if @result
+ #total-errors{:style => "color:red;"}
+ = h(@result.errors.size)
+ error(s)
+= braintree_form_for :credit_card, :existing => @credit_card do |f|
+ = field_set_tag "Credit Card" do
+ %dl
+ %dt= f.label :number, 'Number'
+ %dd= f.text_field :number
+ %dt= f.label :expiration_date, 'Expiration Date (MM/YY)'
+ %dd= f.text_field :expiration_date
+ %dt= f.label :cvv, 'CVV'
+ %dd= f.text_field :cvv
+ = hidden_field_tag :tr_data, @tr_data
+ = f.submit 'Save Payment Info', :class => :btn
+ = link_to t(:cancel), edit_customer_path(@user.id), :class => :btn
diff --git a/engines/billing/app/views/customer/_customer_data.html.haml b/engines/billing/app/views/customer/_customer_data.html.haml
new file mode 100644
index 0000000..e9df040
--- /dev/null
+++ b/engines/billing/app/views/customer/_customer_data.html.haml
@@ -0,0 +1,16 @@
+%legend= t(:customer_information)
+%dl
+ %dt First Name
+ %dd= @customer.first_name
+ %dt Last Name
+ %dd= @customer.last_name
+ %dt Phone
+ %dd= @customer.phone
+%legend= t(:credit_card_information)
+%dl
+ %dt Number
+ %dd= @default_cc.masked_number
+ %dt Expiration Date
+ %dd= @default_cc.expiration_date
+ - if current_user == @user
+ = link_to t(:edit_saved_data), edit_customer_path(@user.id), :class => :btn
diff --git a/engines/billing/app/views/customer/_transaction.html.haml b/engines/billing/app/views/customer/_transaction.html.haml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/engines/billing/app/views/customer/_transaction.html.haml
diff --git a/engines/billing/app/views/customer/confirm.html.haml b/engines/billing/app/views/customer/confirm.html.haml
new file mode 100644
index 0000000..877a8ac
--- /dev/null
+++ b/engines/billing/app/views/customer/confirm.html.haml
@@ -0,0 +1,14 @@
+%h1 Payment Info Confirmation
+%p Your payment information was successfully saved.
+%dl
+ %dt First Name
+ %dd= @result.customer.first_name
+ %dt Last Name
+ %dd= @result.customer.last_name
+ %dt Phone
+ %dd= @result.customer.phone
+ %dt Credit Card
+ - @result.customer.credit_cards.each do |cc|
+ %dd= cc.masked_number
+- customer = Customer.find_by_user_id(@user.id)
+= link_to 'View Customer Info', show_customer_path(@user.id), :class=> :btn \ No newline at end of file
diff --git a/engines/billing/app/views/customer/edit.html.haml b/engines/billing/app/views/customer/edit.html.haml
new file mode 100644
index 0000000..e882d53
--- /dev/null
+++ b/engines/billing/app/views/customer/edit.html.haml
@@ -0,0 +1,23 @@
+- if @result
+ #total-errors{:style => "color:red;"}
+ = h(@result.errors.size)
+ error(s)
+= braintree_form_for :customer, existing: @customer do |f|
+ = field_set_tag "Customer" do
+ %dl
+ %dt= f.label :first_name, 'First Name'
+ %dd= f.text_field :first_name
+ %dt= f.label :last_name, 'Last Name'
+ %dd= f.text_field :last_name
+ %dt= f.label :phone, 'Phone'
+ %dd= f.text_field :phone
+ - if @default_cc
+ = # todo, as they will need a credit card, so not sure about conditional?
+ %dt= t(:stored_credit_card)
+ %dd
+ = @default_cc.masked_number
+ = link_to t(:change_credit_card), edit_credit_card_info_path(:id => @default_cc.token), :class => :btn
+ = hidden_field_tag :tr_data, @tr_data
+ .form-actions
+ = f.submit t(:save_customer_info), :class => 'btn btn-primary'
+ = link_to t(:cancel), show_customer_path(@user), :class=> :btn
diff --git a/engines/billing/app/views/customer/new.html.haml b/engines/billing/app/views/customer/new.html.haml
new file mode 100644
index 0000000..e1f5ba9
--- /dev/null
+++ b/engines/billing/app/views/customer/new.html.haml
@@ -0,0 +1,24 @@
+- if @result
+ #total-errors{:style => "color:red;"}
+ = h(@result.errors.size)
+ error(s)
+= braintree_form_for :customer do |f|
+ = field_set_tag "Customer" do
+ %dl
+ %dt= f.label :first_name, 'First Name'
+ %dd= f.text_field :first_name
+ %dt= f.label :last_name, 'Last Name'
+ %dd= f.text_field :last_name
+ %dt= f.label :phone, 'Phone'
+ %dd= f.text_field :phone
+ = field_set_tag "Credit Card" do
+ - f.fields_for :credit_card do |cc|
+ %dl
+ %dt= cc.label :number, 'Number'
+ %dd= cc.text_field :number
+ %dt= cc.label :expiration_date, 'Expiration Date (MM/YY)'
+ %dd= cc.text_field :expiration_date
+ %dt= cc.label :cvv, 'CVV'
+ %dd= cc.text_field :cvv
+ = hidden_field_tag :tr_data, @tr_data
+ = f.submit 'Save Payment Info'
diff --git a/engines/billing/app/views/customer/show.html.haml b/engines/billing/app/views/customer/show.html.haml
new file mode 100644
index 0000000..ec1779c
--- /dev/null
+++ b/engines/billing/app/views/customer/show.html.haml
@@ -0,0 +1,27 @@
+- if admin? and !@customer
+ = t(:no_saved_customer)
+- else
+ = render :partial => 'customer_data'
+ %legend= t(:last_three_transactions)
+ - counter = 0
+ = # these will be ordered with most recently created first, per http://stackoverflow.com/questions/16425475/
+ - @transactions.each do |t|
+ - break if counter > 2 # not ruby-like, but object is a Braintree::ResourceCollection so limited methods available
+ = render :partial => "payments/transaction_details", :locals => {:transaction => t}
+ - counter += 1
+ = link_to t(:transaction_history), user_payments_path(@user)
+ %legend= t(:subscriptions)
+ - if @active_subscription
+ = render :partial => "subscriptions/subscription_details", :locals => {:subscription => @active_subscription}
+ - else
+ %p
+ = t(:no_relevant_subscription)
+ - if current_user == @user
+ %p
+ .form-actions
+ = link_to t(:subscribe_to_plan), new_subscription_path, :class => :btn
+ %p
+ = link_to t(:all_subscriptions), user_subscriptions_path(@user)
+
+.form-actions
+ = link_to t(:make_donation), new_payment_path, :class => 'btn btn-primary'
diff --git a/engines/billing/app/views/payments/_non_customer_fields.html.haml b/engines/billing/app/views/payments/_non_customer_fields.html.haml
new file mode 100644
index 0000000..77cfe95
--- /dev/null
+++ b/engines/billing/app/views/payments/_non_customer_fields.html.haml
@@ -0,0 +1,16 @@
+= field_set_tag "Personal Information" do
+ = f.fields_for :customer do |c|
+ %div= c.label :first_name, "First Name"
+ %div= c.text_field :first_name
+ %div= c.label :last_name, "Last Name"
+ %div= c.text_field :last_name
+ %div= c.label :email, "Email"
+ %div= c.text_field :email
+= field_set_tag "Credit Card" do
+ = f.fields_for :credit_card do |c|
+ %div= c.label :number, "Number"
+ %div= c.text_field :number
+ %div= c.label :expiration_date, "Expiration Date (MM/YY)"
+ %div= c.text_field :expiration_date
+ %div= c.label :cvv, "CVV"
+ %div= c.text_field :cvv \ No newline at end of file
diff --git a/engines/billing/app/views/payments/_transaction_details.html.haml b/engines/billing/app/views/payments/_transaction_details.html.haml
new file mode 100644
index 0000000..85e4f6a
--- /dev/null
+++ b/engines/billing/app/views/payments/_transaction_details.html.haml
@@ -0,0 +1,15 @@
+%p
+ = transaction.id
+ Type:
+ = transaction.type
+ Amount:
+ = number_to_currency(transaction.amount)
+ Status:
+ = transaction.status
+ Date
+ = transaction.created_at.strftime("%Y-%m-%d")
+ - if sub_start = transaction.subscription_details.billing_period_start_date
+ From subscription which started
+ = sub_start
+ - else # should not have any of these
+ Not paid as part of subscription \ No newline at end of file
diff --git a/engines/billing/app/views/payments/confirm.html.haml b/engines/billing/app/views/payments/confirm.html.haml
new file mode 100644
index 0000000..45af3c9
--- /dev/null
+++ b/engines/billing/app/views/payments/confirm.html.haml
@@ -0,0 +1,26 @@
+%h1 Payment Result
+%div Thank you for your donation.
+%h2 Transaction Details
+%table
+ %tr
+ %td Amount
+ %td
+ $#{@result.transaction.amount}
+ %tr
+ %td Transaction ID:
+ %td= @result.transaction.id
+ %tr
+ %td First Name:
+ %td= h @result.transaction.customer_details.first_name
+ %tr
+ %td Last Name:
+ %td= h @result.transaction.customer_details.last_name
+ %tr
+ %td Email:
+ %td= h @result.transaction.customer_details.email
+ %tr
+ %td Credit Card:
+ %td= h @result.transaction.credit_card_details.masked_number
+ %tr
+ %td Card Type:
+ %td= h @result.transaction.credit_card_details.card_type \ No newline at end of file
diff --git a/engines/billing/app/views/payments/index.html.haml b/engines/billing/app/views/payments/index.html.haml
new file mode 100644
index 0000000..7a89917
--- /dev/null
+++ b/engines/billing/app/views/payments/index.html.haml
@@ -0,0 +1,5 @@
+%h2=t :transaction_history
+- if (@transactions.count == 0)
+ = t(:no_transaction_history)
+- @transactions.each do |t|
+ = render :partial => "transaction_details", :locals => {:transaction => t} \ No newline at end of file
diff --git a/engines/billing/app/views/payments/new.html.haml b/engines/billing/app/views/payments/new.html.haml
new file mode 100644
index 0000000..e9a8273
--- /dev/null
+++ b/engines/billing/app/views/payments/new.html.haml
@@ -0,0 +1,17 @@
+%h1
+ = t(:Donation)
+- if logged_in?
+ = t(:donation_not_payment)
+- if @result and @result.errors.size > 0
+ %div{:style => "color: red;"}
+ = h @result.errors.size
+ error(s)
+- if @result and @result.transaction and @result.transaction.status != 'success'
+ %div{:style => "color: red;"}
+ = t(:processor_declined)
+= braintree_form_for :transaction, :html => {:autocomplete => "off"} do |f|
+ = f.label :amount, t(:amount)
+ = f.text_field :amount
+ = render :partial => 'non_customer_fields', :locals => {:f => f}
+ = hidden_field_tag :tr_data, @tr_data
+ = f.submit "Submit Donation", :class => 'btn btn-primary'
diff --git a/engines/billing/app/views/subscriptions/_subscription_details.html.haml b/engines/billing/app/views/subscriptions/_subscription_details.html.haml
new file mode 100644
index 0000000..6145c95
--- /dev/null
+++ b/engines/billing/app/views/subscriptions/_subscription_details.html.haml
@@ -0,0 +1,26 @@
+%p
+ - if local_assigns[:show_user]
+ User:
+ - user_to_show = user_for_subscription(subscription)
+ = link_to user_to_show.login, user_overview_path(user_to_show)
+ ID:
+ = link_to subscription.id, user_subscription_path(@user, subscription.id)
+ Balance:
+ - color = (subscription.balance > 0) ? "red" : ""
+ %font{:color => color}
+ = number_to_currency(subscription.balance)
+ Bill on:
+ = subscription.billing_day_of_month
+ Start date:
+ = subscription.first_billing_date
+ Paid through:
+ = subscription.paid_through_date
+ Plan:
+ = subscription.plan_id
+ Price:
+ = number_to_currency(subscription.price)
+ - color = (subscription.status == 'Active') ? "green" : "red"
+ Status:
+ %font{:color => color}
+ = subscription.status
+ - # would be good to get plan name but not sure if that is possible? \ No newline at end of file
diff --git a/engines/billing/app/views/subscriptions/create.html.haml b/engines/billing/app/views/subscriptions/create.html.haml
new file mode 100644
index 0000000..2b6c5e9
--- /dev/null
+++ b/engines/billing/app/views/subscriptions/create.html.haml
@@ -0,0 +1,9 @@
+- if @result.success?
+ %h1
+ Subscription Status
+ = @result.subscription.status
+ = render :partial => "subscription_details", :locals => {:subscription => @result.subscription}
+- else
+ %h1
+ Error:
+ = @result.message \ No newline at end of file
diff --git a/engines/billing/app/views/subscriptions/destroy.html.haml b/engines/billing/app/views/subscriptions/destroy.html.haml
new file mode 100644
index 0000000..44b4333
--- /dev/null
+++ b/engines/billing/app/views/subscriptions/destroy.html.haml
@@ -0,0 +1,7 @@
+- if @result.success?
+ Subscription destroyed
+- else
+ Error:
+ = @result.message
+%p
+ = link_to 'Customer Information', show_customer_path(@user), :class=> :btn \ No newline at end of file
diff --git a/engines/billing/app/views/subscriptions/index.html.haml b/engines/billing/app/views/subscriptions/index.html.haml
new file mode 100644
index 0000000..3d4e8fd
--- /dev/null
+++ b/engines/billing/app/views/subscriptions/index.html.haml
@@ -0,0 +1,8 @@
+%h2=t :all_subscriptions
+- pending_active_pastdue = false
+- @subscriptions.each do |s|
+ - if ['Pending', 'Active','Past Due'].include? s.status
+ - pending_active_pastdue = true
+ = render :partial => "subscription_details", :locals => {:subscription => s}
+- if !pending_active_pastdue and @user == current_user
+ = link_to 'subscribe to plan', new_subscription_path, :class => :btn \ No newline at end of file
diff --git a/engines/billing/app/views/subscriptions/new.html.haml b/engines/billing/app/views/subscriptions/new.html.haml
new file mode 100644
index 0000000..4183458
--- /dev/null
+++ b/engines/billing/app/views/subscriptions/new.html.haml
@@ -0,0 +1,15 @@
+- if @payment_method_token
+ %h1
+ Subscribe to plan
+ = #currently just one plan
+ = @plans[0].name
+ = number_to_currency(@plans[0].price)
+ = simple_form_for :subscription, :url => :subscriptions do |f|
+ = hidden_field_tag :payment_method_token, @payment_method_token
+ = hidden_field_tag :plan_id, @plans[0].id
+ .form-actions
+ = f.submit t(:subscribe), :class => 'btn btn-primary'
+- else
+ = t(:must_create_customer)
+ %p
+ = link_to t(:create_new_customer), new_customer_path
diff --git a/engines/billing/app/views/subscriptions/show.html.haml b/engines/billing/app/views/subscriptions/show.html.haml
new file mode 100644
index 0000000..2699db9
--- /dev/null
+++ b/engines/billing/app/views/subscriptions/show.html.haml
@@ -0,0 +1,6 @@
+%h1
+ - if @subscription.status == 'Active'
+ Current
+ Subscription
+= render :partial => "subscription_details", :locals => {:subscription => @subscription}
+= link_to t(:cancel_subscription), user_subscription_path(@user, @subscription.id), :confirm => t(:are_you_sure), :method => :delete, :class => 'btn btn-danger' if allow_cancel_subscription(@subscription)