Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- diff --git a/api-srv.rb b/api-srv.rb
- index 24042d0..5ee28ea 100644
- --- a/api-srv.rb
- +++ b/api-srv.rb
- @@ -4,13 +4,11 @@ require 'eventmachine'
- require 'thin'
- require 'active_merchant'
- require 'pp'
- -basereq 'db_recurring'
- basereq 'data_types'
- basereq 'log/log'
- basereq 'srv-common'
- basereq 'mail/mail'
- basereq 'recur/billing_logic'
- -basereq 'recur/billing_logic_new'
- basereq 'extn/gateway'
- basereq 'db/log'
- require_goat 'notifications.rb'
- @@ -62,17 +60,13 @@ class APIResponse
- pretty = pretty.gsub(/\[\s+\]/, '[]').gsub(/\{\s+\}/, '{}')
- pretty
- end
- -
- - def add_error_details(dets={})
- - dets.each{|k,v| @error[k] = v} if @error
- - end
- -
- +
- def error=(e)
- raise "We already have an error" if @error
- @error = e
- end
- - def card_error(msg, param, code=nil, note=nil, charge=nil)
- + def card_error(msg, param, code=nil, note=nil)
- h = {
- 'message' => msg.sym_to_s,
- 'type' => 'card_error'
- @@ -81,9 +75,10 @@ class APIResponse
- h['param'] = param.sym_to_s if param
- h['code'] = code.sym_to_s if code
- h['note'] = note if note
- +
- self.error = h
- end
- -
- +
- def card_error_from_validator(er)
- card_error(er[:message], er[:detail], er[:code], er[:note])
- end
- @@ -108,7 +103,6 @@ class APIResponse
- def data=(hash)
- @data = hash
- end
- -
- end
- class APIHacks
- @@ -284,7 +278,7 @@ class PaymentGateway
- end
- @@errors_seen = YAML.load_file('/tmp/responses_seen.yaml') rescue []
- - def self.errors_for_firstdata(response, gateway, charge)
- + def self.errors_for_firstdata(response, gateway)
- errors = []
- params = response.params
- rcode = params['rcode']
- @@ -320,34 +314,32 @@ class PaymentGateway
- :gateway => gateway,
- :mailable => true,
- :response => response.params,
- - :charge => charge.try(:token),
- :rtext => rtext)
- errors << CardValidator::Processing_Error
- end
- # Do this until we have a handle on the kinds of responses to come back
- unless @@errors_seen.include?(rcode)
- - @@errors_seen.push(rcode)
- + @@errors_seen.push rcode
- File.open('/tmp/responses_seen.yaml', 'w') { |f| f.write YAML.dump(@@errors_seen) }
- Log.log_internal_event('new-firsdata-response',
- :message => "Firstdata error codes seen on this server: #{@@errors_seen.inspect}",
- :gateway => gateway,
- :mailable => true,
- :response => response.params,
- - :charge => charge.try(:token),
- :rtext => rtext)
- end
- errors
- end
- - def self.errors_for(response, charge)
- + def self.errors_for(response)
- case response.gateway
- when 'authorize'
- errors_for_authnet(response)
- when 'merchantpartners'
- errors_for_mps(response)
- when 'firstdata_north', 'firstdata_north_test', 'firstdata_nashville', 'firstdata_nashville_test'
- - errors_for_firstdata(response, response.gateway, charge)
- + errors_for_firstdata(response, response.gateway)
- else
- raise "Unknown gateway type #{response.params['gateway'].inspect}"
- end
- @@ -453,11 +445,12 @@ class PaymentGateway
- rh = self.gateway_request(authz, authz.mrchgateway, 'authorization', :authorize, authz.amount, credit_card, opts)
- end
- - def self.attempt_charge(charge, card, opts={})
- + def self.attempt_charge(charge, card)
- credit_card = self.cardify(card)
- - opts.merge!({ :trace => charge.numeric_id })
- - invoice = charge.load_invoice
- - opts[:recurring] = true if invoice && !card.cvc && !opts.key?(:recurring)
- +
- + opts = { :trace => charge.numeric_id }
- + recon = charge.load_recon
- + opts.merge!(:recurring => true) if recon && !card.cvc
- gw = MrchGateway.load(charge.mrchgateway)
- case gw.gateway
- @@ -663,35 +656,6 @@ module CustomerCommon
- end
- end
- -module PlanCommon
- - def load_plan(plan_code)
- - p = Plan.load_alive(:merchant => @merchant.token, :identifier => plan_code)
- - unless p
- - raise DPException.new("No such plan: #{plan_code}", 'identifier', 404)
- - end
- - p
- - end
- -end
- -
- -module InvoiceItemCommon
- - def load_invoice_item(tok)
- - item = InvoiceItem.load_alive(:merchant => @merchant.token, :_id => tok)
- - unless item
- - raise DPException.new("No such invoice item: #{tok}", 'id', 404)
- - end
- - item
- - end
- -end
- -
- -module CouponCommon
- - def load_coupon(code)
- - c = Coupon.load_one(:merchant => @merchant.token, :code => code)
- - raise DPException.new("No such coupon: #{code}", 'code', 404) unless c
- - raise InvalidAPIRequestError.new("Coupon expired: #{code}") unless c.valid?
- - c
- - end
- -end
- -
- module CommonValidations
- ULONG_MAX = 2147483647 # 2**31 - 1. Max 32-bit int in Mongo.
- @@ -821,7 +785,7 @@ module ChargeCommon
- rhash = PaymentGateway.attempt_authorization(charge, card)
- charge.authorized = rhash[:success]
- - charge.auth_errors = charge.authorized ? [] : PaymentGateway.errors_for(rhash[:response], charge)
- + charge.auth_errors = charge.authorized ? [] : PaymentGateway.errors_for(rhash[:response])
- charge.auth_gw_token = rhash[:response].authorization
- charge.auth_avs_result = rhash[:response].avs_result[:code]
- charge.save
- @@ -837,7 +801,7 @@ module ChargeCommon
- charge
- end
- - def attempt_charge(charge, card, opts={})
- + def attempt_charge(charge, card)
- maybe_load_cvc!(card)
- # If we didn't get a CVC with this card, see if we have its CVC in the temp storage (maybe this card was from the JS bindings)
- charge.card = card.token
- @@ -852,12 +816,13 @@ module ChargeCommon
- if card.error
- raise "This card has an error defined -- why are we trying to charge it?"
- end
- +
- # We get an ActiveMerchant::Billing::Response object,
- # annotated with a :gateway param specifying which gateway
- - rhash = PaymentGateway.attempt_charge(charge, card, opts)
- + rhash = PaymentGateway.attempt_charge(charge, card)
- charge.authorized = rhash[:success]
- - charge.auth_errors = charge.authorized ? [] : PaymentGateway.errors_for(rhash[:response], charge)
- + charge.auth_errors = charge.authorized ? [] : PaymentGateway.errors_for(rhash[:response])
- charge.auth_gw_token = rhash[:response].authorization
- charge.auth_avs_result = rhash[:response].avs_result[:code]
- @@ -876,7 +841,7 @@ module ChargeCommon
- # The HTTP_X_STRIPE_REQUEST is set in apiori and can be trusted
- - if (Dynamic.variable?(:request) && request = Dynamic[:request]) && (request.env['HTTP_X_STRIPE_REQUEST'] == 'healthcheck')
- + if (request = Dynamic[:request]) && (request.env['HTTP_X_STRIPE_REQUEST'] == 'healthcheck')
- mailable = false
- else
- mailable = charge.livemode
- @@ -946,7 +911,6 @@ class ExecuteChargeMethod < APIMethod
- optional :id, :string
- optional :customer, :string
- optional :mnemonic, :string
- - optional :recurring, :valid_boolean
- optional :recon, :string
- optional :force_recon, :valid_boolean
- @@ -955,11 +919,16 @@ class ExecuteChargeMethod < APIMethod
- tok = param?[:id]
- cust = param?[:customer]
- card = param?[:card]
- + recon = param?[:recon]
- +
- if amt && cur && !tok
- # ok. we'll need to create a charge object for this transaction
- elsif !amt && !cur && tok
- # ok. we have a charge token already.
- + elsif @key.system && recon && !amt && !cur && !tok
- + # fine, we're paying a recon
- else
- + # raise InvalidAPIRequestError.new('You must supply either charge id or an amount')
- raise InvalidAPIRequestError.new('You must supply an amount and currency.')
- end
- @@ -967,11 +936,14 @@ class ExecuteChargeMethod < APIMethod
- # okay, normal execute_charge customer
- elsif !card && cust
- # grand, we'll use the card on the customer's account
- + elsif @key.system && recon && !card && !cust
- + # fine, we're paying a recon
- else
- raise InvalidAPIRequestError.new('You must supply either a card dictionary or a customer id')
- end
- CommonValidations.validate_card_error(card, @key, "A card can only have the error param with test transactions")
- +
- if @key.publishable && !card
- # Stop people randomly charging a merchant's customers
- @@ -980,15 +952,30 @@ class ExecuteChargeMethod < APIMethod
- end
- end
- + def maybe_load_recon
- + recon = param?[:recon]
- + if recon
- + @recon = CustomerReconciliation.load(recon)
- + raise InvalidAPIRequestError.new('No such CustomerReconciliation') unless @recon && @recon.merchant == @merchant.token
- + end
- + end
- +
- def maybe_load_customer
- mrchid = param?[:customer]
- - if mrchid
- + if @recon
- + @cust = Customer.load(@recon.customer)
- + raise "Trying to execute charge on a recon without a customer" if !@cust
- + elsif mrchid
- @cust = load_customer(mrchid)
- end
- end
- def get_amount
- + if @recon
- + [@recon.charge_amount, @recon.currency]
- + else
- [param?[:amount], param?[:currency]]
- + end
- end
- def load_charge(tok)
- @@ -1006,16 +993,17 @@ class ExecuteChargeMethod < APIMethod
- def get_charge
- ch_tok = param?[:id]
- +
- if ch_tok
- c = load_charge(ch_tok)
- else
- amt, cur = get_amount
- - chargs = {}
- - chargs[:mnemonic] = param?[:mnemonic] if param?[:mnemonic]
- - chargs[:recurring] = param?[:recurring] if param?[:recurring]
- + return nil if amt == 0 && @recon
- +
- + chargs = {:mnemonic => param?[:mnemonic]}
- chargs.merge!(:customer => @cust.token) if @cust
- + chargs.merge!(:recon => @recon.token) if @recon
- c = prepare_charge(@key.livemode, amt, cur, chargs)
- - return nil if amt == 0 && @recon
- end
- c
- @@ -1030,6 +1018,14 @@ class ExecuteChargeMethod < APIMethod
- end
- end
- + def update_recon
- + @recon.paid = true if @ch && @ch.paid
- + @recon.charge = @ch.token if @ch
- + @recon.attempted_at = Time.stamp
- + @recon.settled_at = Time.stamp
- + @recon.save
- + end
- +
- def do_charge_success_tasks
- # Requirements for emailing the receipt:
- # - Merchant has an email address
- @@ -1043,6 +1039,7 @@ class ExecuteChargeMethod < APIMethod
- def respond
- begin
- Merchant.update(@merchant.token) do
- + maybe_load_recon
- maybe_load_customer
- if @recon && (@cust.suspended || (@cust.delinquent && !param?[:force_recon]))
- @@ -1059,6 +1056,12 @@ class ExecuteChargeMethod < APIMethod
- @card = get_card
- if !@card
- + if @recon
- + @recon.charge = @ch.token if @ch
- + @recon.attempted_at = Time.stamp
- + @recon.settled_at = Time.stamp
- + @recon.save
- + end
- return @response.card_error('There is no active card on this account.', nil, 'no_card')
- end
- @@ -1069,25 +1072,40 @@ class ExecuteChargeMethod < APIMethod
- end
- # reasons for not charging
- - if @ch
- - opts = {}
- - opts[:recurring] = param?[:recurring] if param?[:recurring]
- - @ch = attempt_charge(@ch, @card, opts)
- - end
- + if !@ch
- + assert(@recon, "Recon cannot be #{@recon.inspect} if charge is #{@ch.inspect}", true)
- + # no @ch means charge not necessary, because the amount was 0
- + else
- + @ch = attempt_charge(@ch, @card)
- + end
- Merchant.update(@merchant.token) do
- + if @recon
- + # If @ch is nil, then the only action taken will be to do update_recon.
- + # This is equivalent to a serial ordering of these requests, however.
- + # TODO: someone should examine the semantics of recon updating and
- + # things.
- + @recon = CustomerReconciliation.load(@recon.token)
- + @recon.charge = @ch.token if @ch
- + update_recon
- + end
- do_charge_success_tasks if @ch && @ch.paid
- +
- if @ch && @cust
- ccj = {:_id => @ch.token, :customer => @cust.token}
- ccj[:recon] = @recon.token if @recon
- DB.db['charge_customer_join'].insert(ccj)
- end
- -
- - if @ch && @ch.auth_errors.length > 0
- +
- + if @recon
- + @response.data = {:id => @recon.token,
- + :attempted => (@ch ? @ch.attempted : false),
- + :charge => @recon.charge,
- + :paid => (@ch ? @ch.paid : nil)}
- + # TODO fix
- + elsif @ch && @ch.auth_errors.length > 0
- puts "ch is #{@ch.inspect}"
- @response.card_error_from_validator(@ch.auth_errors.first)
- - # if system key, describe charge even when errors for invoice payer to get failed charge tokens
- - @response.add_error_details({:charge => @ch.token}) if @key.system
- else
- @response.data = DataTypes.describe_charge(@ch)
- end
- @@ -1297,9 +1315,6 @@ end
- class UpdateCustomerMethod < APIMethod
- include CustomerCommon
- include ChargeCommon
- - include PlanCommon
- - include CouponCommon
- -
- attr_accessor :should_create # If we're creating customer first
- def validate_request
- @@ -1311,14 +1326,10 @@ class UpdateCustomerMethod < APIMethod
- optional :card, :valid_card
- optional :mnemonic, :string
- - optional :plan, :string
- - optional :coupon, :string
- optional :subscription, :valid_subscription
- optional :schedule
- optional :suspended, :valid_boolean
- optional :validate, :valid_boolean
- - optional :prorate, :valid_boolean
- - optional :email, :string
- if param?[:schedule] and !self.should_create
- raise InvalidAPIRequestError.new('The schedule param can only be set when creating a new customer.', 'schedule')
- @@ -1331,18 +1342,42 @@ class UpdateCustomerMethod < APIMethod
- id ||= String.random(20)
- existing = Customer.load_one(:merchant => @merchant.token, :mrchid => id, :deleted => false)
- raise DPException.new('Customer already exists.') if existing
- +
- + prebill = nil
- + case param?[:schedule]
- + when 'prebill'
- + prebill = true
- + when 'postbill'
- + prebill = false
- + when nil
- + # okay
- + else
- + raise InvalidAPIRequestError.new('Invalid value for schedule')
- + end
- c = Customer.init(id)
- c.livemode = @key.livemode
- c.merchant = @merchant.token
- + c.prebill = prebill
- c
- end
- +
- + def payable_recons
- + # If cr exists, that has to be the only recon
- + return [@cr] if @cr
- - def set_parameters(customer)
- - mnem, susp, email = param?[:mnemonic], param?[:suspended], param?[:email]
- + CustomerReconciliation.load_all(:customer => @cust.token).select{|rec| (rec.charge && !rec.paid) || (!rec.attempted_at && !rec.settled_at) }
- + end
- +
- + def set_basic_params
- + mnem = param?[:mnemonic]
- + susp = param?[:suspended]
- - @cust.mnemonic = mnem if mnem && !mnem.empty?
- - @cust.email = email if email && !email.empty?
- + if mnem && mnem.empty?
- + @cust.mnemonic = nil
- + elsif mnem
- + @cust.mnemonic = mnem
- + end
- if susp == 'true' || susp == 'True'
- @cust.suspended = true
- @@ -1352,68 +1387,48 @@ class UpdateCustomerMethod < APIMethod
- @cust.save if !self.should_create
- end
- -
- - def generate_subscription(plan, previous=nil)
- -
- - sub = Subscription.init
- - sub.status = "active"
- - sub.customer = @cust.token
- - sub.merchant = @merchant.token
- - sub.plan = plan.token
- -
- - if previous
- - interval_change = previous.plan_.interval != sub.plan_.interval
- - if interval_change
- - previous.ended_at = Time.stamp
- - generate_prorations(previous)
- - sub.start = previous.current_period_end = previous.ended_at
- - else
- - sub.current_period_start = previous.current_period_start
- - sub.current_period_end = previous.current_period_end
- - previous.ended_at = sub.start = Time.stamp
- - end
- - else
- - if plan.trial_period_days
- - sub.trial_start = sub.start = Time.stamp
- - sub.trial_end = sub.start_.advance(:days => plan.trial_period_days).to_i
- - else
- - sub.start = Time.stamp
- - end
- +
- + def delete_existing_cb
- + existing = CustomerBill.load_one(:customer => @cust.token, :deleted => {'$ne' => true}, :delayed_start => @cb.start) || CustomerBill.load_one(:customer => @cust.token, :created => @cb.start, :delayed_start => nil, :deleted => {'$ne' => true})
- + if existing
- + existing.deleted = true
- + existing.save
- end
- - prorate = param?[:prorate] == 'false' ? false : true
- - generate_prorations(previous, sub) if previous && prorate && !interval_change
- - sub
- - end
- -
- - def new_proration
- - p = InvoiceItem.proration
- - p.merchant = @merchant.token
- - p.currency = 'usd'
- - p.customer = @cust.token
- - p.created = Time.stamp
- - p.generate_token
- - p
- end
- - def generate_prorations(old_sub, new_sub=nil)
- - period_end = old_sub.current_period_end.to_f
- - period = (period_end - old_sub.current_period_start).to_f
- - old_price = old_sub.plan_.amount.to_f
- - remaining_percent = (period_end - old_sub.ended_at.to_f) / period
- - credit_amount = remaining_percent * old_price
- - credit = new_proration
- - credit.amount = -credit_amount.round
- - credit.description = "Pro-rated credit for remaining unused time on #{old_sub.plan_.name} plan on #{old_sub.end_.pretty_date}"
- - credit.save
- + def generate_customerbill
- + sub = param?[:subscription]
- + @cb = CustomerBill.new
- + @cb.generate_token
- + @cb.created = Time.stamp
- + @cb.delayed_start = sub[:start].to_i if sub[:start]
- + @cb.merchant = @merchant.token
- + @cb.amount = sub[:amount].to_i
- + @cb.currency = sub[:currency]
- + @cb.per = sub[:per]
- + @cb.mrchid = @cust.mrchid
- + @cb.customer = @cust.token
- + end
- - if new_sub
- - new_price = new_sub.plan_.amount.to_f
- - debit_amount = remaining_percent * new_price
- - debit = new_proration
- - debit.amount = debit_amount.round
- - debit.description = "Pro-rated charge for plan change to #{new_sub.plan_.name} on #{new_sub.start_.pretty_date}"
- - debit.save
- - end
- + def maybe_create_recon
- + if @cb && @cust.should_prebill && CustomerBill.load_alives(:customer => @cust.token).empty? && (@cb.start <= Time.stamp)
- + @cr = ReconciliationCalculator.new(@cust, [@cb]).calculate_next_recon
- + end
- + end
- +
- + def maybe_create_charge
- + # ideally, we would pay all the recons, but it gets super messy if
- + # one succeeds but then the next doesn't
- + @recon = payable_recons.first
- + return nil unless @recon && @card
- + return nil if @cust.suspended
- +
- + if @recon.charge
- + charge = Charge.load(@recon.charge)
- + else
- + charge = prepare_charge(@cust.livemode, @recon.amount, @recon.currency)
- + end
- + charge
- end
- def save_db_objects
- @@ -1422,47 +1437,55 @@ class UpdateCustomerMethod < APIMethod
- # call maybe_create_recon again) even though maybe_create_recon uses @cust because it only
- # uses @cust's token and merchant fields, which we know won't change while we released
- # the lock
- - # @recon = payable_recons.first
- + @recon = payable_recons.first
- # Very unlikely, but card might have changed since we made it
- @card = Card.load(@card.token) if @card
- @cust = Customer.load(@cust.token) if !self.should_create
- if @charge
- if @charge.paid || !self.should_create
- - DB.db['charge_customer_join'].insert(:_id => @charge.token, :customer => @cust.token, :invoice => @invoice.token)
- + DB.db['charge_customer_join'].insert(:_id => @charge.token, :customer => @cust.token, :recon => @recon.token)
- + @recon.charge = @charge.token
- + @recon.attempted_at = Time.stamp
- + @recon.settled_at = Time.stamp
- + @recon.save
- end
- - unless @charge.paid
- +
- + if @charge.paid
- + @recon.paid = true and @recon.save
- + NotificationDict.charge_success(@recon, @cust, @charge.token, @card)
- + else
- + NotificationDict.charge_failure(@recon, @cust) unless self.should_create
- err = @charge.errors.first
- raise BadUserInputError.new(err[:message], err[:detail].to_s, err[:code].to_s)
- end
- end
- add_card_to_customer if @card
- - # update card - try to pay latest unpaid invoice
- - if @card && !self.should_create && (current = @cust.current_subscription) && current.past_due?
- - old_invoice = current.latest_invoice(q={:closed_at => nil, :paid => false})
- - InvoicePayer.new(old_invoice).pay
- - end
- -
- - @invoice.paid = @charge.paid and @invoice.charge = @charge.token if @invoice and @charge
- - [@charge, @sub, @invoice, @previous, @discount].each{|obj| obj.save if obj}
- - NotificationDict.recurring_payment_succeeded(@invoice) if @invoice and @invoice.paid
- + @charge.save if @charge
- + # No need to re-fetch as this hasn't been saved yet
- + delete_existing_cb if param?[:subscription]
- + @cb.save if @cb
- + @recon.save if @recon
- @cust.save
- end
- def get_customer(tok)
- - self.should_create ? create_customer(tok) : load_customer(tok)
- + if self.should_create
- + create_customer(tok)
- + else
- + load_customer(tok)
- + end
- end
- - def new_card(c)
- + def maybe_save_card(c)
- return nil unless c
- - card = get_card_obj(c)
- + @card = get_card_obj(c)
- if param?[:validate]
- - err = validate_card(@key.livemode, card).errors.first
- + err = validate_card(@key.livemode, @card).errors.first
- raise BadUserInputError.new(err[:message], err[:detail].to_s, err[:code].to_s) if err
- end
- - card
- end
- def add_card_to_customer
- @@ -1470,62 +1493,32 @@ class UpdateCustomerMethod < APIMethod
- @cust.active_card = @card.token
- end
- - def generate_discount
- - coupon = load_coupon(param?[:coupon])
- - d = Discount.init
- - d.merchant = @merchant.token
- - d.customer = @cust.token
- - d.coupon = coupon.token
- - d
- + def prepare_for_paying_recon
- + @charge = maybe_create_charge
- + if @charge
- + raise "Attempting simultaneous recon pay attempts on the same charge" if @charge.in_use
- + @charge.in_use = true
- + @charge.save
- + end
- end
- def respond
- begin
- Merchant.update(@merchant.token) do
- @cust = get_customer(@params[:id])
- - set_parameters(@cust)
- -
- - @discount = generate_discount if param?[:coupon]
- -
- - if param?[:plan]
- - plan_code = param?[:plan]
- - plan = load_plan(plan_code)
- - @previous = @cust.active_subscription
- - unless @previous and @previous.plan == plan.token
- - @sub = generate_subscription(plan,previous=@previous)
- - # Invoice the customer immediately if new or there's no active subscription or the interval is changing
- - if self.should_create or !@previous or @previous.plan_.free? or @previous.plan_.interval != @sub.plan_.interval
- - @invoice = Invoicer.invoice_new_subscription(@cust, @sub, @discount)
- - end
- - end
- - end
- + set_basic_params
- + generate_customerbill if param?[:subscription]
- + maybe_create_recon
- end
- -
- - @card = new_card(param?[:card]) if param?[:card]
- +
- + maybe_save_card(param?[:card])
- Merchant.update(@merchant.token) do
- - if @invoice
- - if @invoice.amount > 0
- - @invoice.attempted_at = Time.stamp
- - @charge = prepare_charge(@cust.livemode, @invoice.amount, @invoice.currency)
- - else
- - @invoice.closed_at = Time.stamp
- - @invoice.save
- - end
- - end
- -
- - if @charge
- - raise "Attempting simultaneous recon pay attempts on the same charge" if @charge.in_use
- - @charge.in_use = true
- - @charge.save
- - end
- + prepare_for_paying_recon
- end
- # TODO: don't drop lock if you don't have to
- - card_to_use = @card || @cust.current_card
- - raise InvalidAPIRequestError.new('You must supply a valid card') if !card_to_use && @charge
- -
- - @charge = attempt_charge(@charge, card_to_use, {:recurring => true}) if @charge
- + @charge = attempt_charge(@charge, @card) if @charge
- Merchant.update(@merchant.token) do
- save_db_objects
- @@ -1549,7 +1542,9 @@ class PublishablyUpdateCustomerMethod < UpdateCustomerMethod
- def respond
- Merchant.update(@merchant.token) { @cust = get_customer(@params[:id]) }
- - @card = new_card(param?[:card])
- + maybe_save_card(param?[:card])
- + Merchant.update(@merchant.token) { prepare_for_paying_recon }
- +
- p = attempt_charge(@charge, @card) if @charge
- Merchant.update(@merchant.token) { save_db_objects }
- @@ -1557,87 +1552,6 @@ class PublishablyUpdateCustomerMethod < UpdateCustomerMethod
- end
- end
- -class UpdateInvoiceItemMethod < APIMethod
- - include InvoiceItemCommon
- - def validate_request
- - required :id, :string
- - optional :amount, :valid_integer_amount
- - optional :description, :string
- - end
- -
- - def respond
- - Merchant.update(@merchant.token) do
- - item = load_invoice_item(param![:id])
- - item.amount = param?[:amount].to_i if param?[:amount]
- - item.description = param?[:description] if param?[:description]
- - item.save
- - @response.data = DataTypes.describe_invoice_item(item)
- - end
- - end
- -end
- -
- -class DeleteInvoiceItemMethod < APIMethod
- - include InvoiceItemCommon
- - def validate_request
- - required :id, :string
- - end
- -
- - def respond
- - Merchant.update(@merchant.token) do
- - item = load_invoice_item(params[:id])
- - item.deleted_at = Time.stamp
- - @response.data = {:id => item.token, :deleted => true}
- - end
- - end
- -end
- -
- -class CancelSubscriptionMethod <APIMethod
- - include CustomerCommon
- -
- - def validate_request
- - required :id, :string
- - # optional :refund, :string
- - optional :cancel_at_end_of_period, :valid_boolean
- - end
- -
- - def respond
- - Merchant.update(@merchant.token) do
- - c = load_customer(param![:id])
- - current = c.current_subscription
- - if current
- - if param?[:cancel_at_end_of_period] == 'true'
- - current.cancel_at_end_of_period = true
- - current.canceled_at = Time.stamp
- - else
- - current.status = "canceled"
- - current.ended_at = current.canceled_at = Time.stamp
- - end
- - current.save
- - # refund_subscription(current) if param?[:refund]
- - end
- -
- - # close out any pending prorations
- - InvoiceItem.load_all(:customer => c.token, :created => {'$gte' => c.last_assessment_date}).each{|b| b.deleted_at = Time.stamp and b.save }
- - @response.data = DataTypes.describe_customer(c)
- - end
- - end
- -
- - def refund_subscription(canceled_sub)
- - latest = canceled_sub.latest_invoice(:paid => true)
- - charge = Charge.load(latest.charge)
- - if charge && charge.paid
- - refund_amount = case param?[:refund]
- - when 'partial'
- - canceled_sub.unused_amount
- - when 'full'
- - charge.amount
- - else
- - raise InvalidAPIRequestError.new('Invalid value for refund')
- - end
- - end
- - end
- -end
- -
- class DeleteCustomerMethod < APIMethod
- include CustomerCommon
- @@ -1662,25 +1576,22 @@ class BillCustomerMethod < APIMethod
- required :id, :string
- required :amount, :valid_integer_amount
- required :currency, :valid_currency
- - optional :description, :string
- end
- def respond
- Merchant.update(@merchant.token) do
- c = load_customer(param![:id])
- - item = InvoiceItem.init
- - item.generate_token
- - item.description = param?[:description] if param?[:description]
- - item.created = Time.stamp
- - c.assessment_period = [item.created, Time.at(item.created).advance(:months => 1).to_i] if c.next_assessment_date.nil?
- - item.merchant = @merchant.token
- - item.amount = param![:amount].to_i
- - item.currency = param![:currency]
- - item.customer = c.token
- - item.save
- - c.save
- -
- - @response.data = DataTypes.describe_invoice_item(item)
- +
- + cb = CustomerBill.new
- + cb.generate_token
- + cb.created = Time.stamp
- + cb.merchant = @merchant.token
- + cb.amount = param![:amount].to_i
- + cb.currency = param![:currency]
- + cb.mrchid = c.mrchid
- + cb.customer = c.token
- + cb.save
- + @response.data = DataTypes.describe_customer(c)
- end
- end
- end
- @@ -1757,14 +1668,14 @@ class BumpCustomerMonthMethod < APIMethod
- def backshift(obj)
- obj.created = (obj.created_ << 1).to_i
- - obj.delayed_start = (Time.at(obj.delayed_start) << 1).to_i if obj.is_a?(InvoiceItem) && obj.delayed_start
- + obj.delayed_start = (Time.at(obj.delayed_start) << 1).to_i if obj.is_a?(CustomerBill) && obj.delayed_start
- obj.from = (obj.from_ << 1).to_i if obj.is_a?(CustomerReconciliation)
- obj.until = (obj.until_ << 1).to_i if obj.is_a?(CustomerReconciliation)
- obj.save
- end
- def backshift_dates
- - cbs = InvoiceItem.load_all(:customer => @c.token)
- + cbs = CustomerBill.load_all(:customer => @c.token)
- raise BadUserInputError.new("No bills for customer", 'id') if cbs.empty?
- recons = CustomerReconciliation.load_all(:customer => @c.token)
- @@ -1796,23 +1707,16 @@ class API
- "execute_charge" => ExecuteChargeMethod,
- "refund_charge" => RefundChargeMethod,
- "all_charges" => :all_charges,
- - "customer_invoices" => :customer_invoices,
- - "active_subscription" => :active_subscription,
- - "change_subscription" => :change_subscription,
- - "retrieve_invoice" => :retrieve_invoice,
- - "retrieve_invoice_item" => :retrieve_invoice_item,
- - "update_invoice_item" => UpdateInvoiceItemMethod,
- - "delete_invoice_item" => :DeleteInvoiceItemMethod,
- - "customer_charges" => :customer_charges,
- - "upcoming_invoice" => :upcoming_invoice,
- +
- "create_customer" => :create_customer,
- "update_customer" => :update_customer,
- "delete_customer" => DeleteCustomerMethod,
- "bill_customer" => BillCustomerMethod,
- "retrieve_customer" => RetrieveCustomerMethod,
- "all_customers" => :all_customers,
- +
- "create_card" => CreateCardMethod,
- - "cancel_subscription" => CancelSubscriptionMethod,
- +
- 'trigger_error' => :trigger_error,
- 'running' => :current_version
- }
- @@ -1855,80 +1759,7 @@ class API
- resp.data = charges
- resp
- end
- -
- - def customer_invoices(params)
- - cust = Customer.load_one(:mrchid => params[:customer_id])
- - invoices = Invoice._find(:customer => cust.token)
- - invoices = invoices.map {|i| DataTypes.describe_invoice(Invoice.load(i['_id'])) }
- - resp = APIResponse.new
- - resp.data = invoices
- - resp
- - end
- -
- - def customer_charges(params)
- - cust = Customer.load_one(:mrchid => params[:customer_id])
- - charges = cust.load_charges.map{|c| DataTypes.describe_charge(c)}
- - resp = APIResponse.new
- - resp.data = charges
- - resp
- - end
- -
- - def upcoming_invoice(params)
- - cust = Customer.load_one(:mrchid => params[:customer_id])
- - invoice = Invoice.generate(cust, {:calculation_only => true})
- - invoice_desc = DataTypes.describe_invoice(invoice)
- - [:id, :date, :period_start, :period_end].each { |param| invoice_desc.delete(param)}
- - resp = APIResponse.new
- - resp.data = invoice_desc
- - resp
- - end
- -
- - def active_subscription(params)
- - cust = Customer.load_one(:mrchid => params[:customer_id])
- - unless sub = cust.active_subscription
- - raise DPException.new("No active subscription for customer: #{params[:customer_id]}", nil, 404)
- - end
- - resp = APIResponse.new
- - resp.data = DataTypes.describe_subscription(sub)
- - resp
- - end
- -
- - def change_subscription(params)
- - puts "hello odongos: #{id}"
- - cust = Customer.load_one(:mrchid => params[:id])
- - puts "hihihi"
- - update_customer(params, false)
- - resp = APIResponse.new
- - resp.data = DataTypes.describe_subscription(cust.active_subscription)
- - resp
- - end
- -
- - def retrieve_invoice(params)
- - tok = params[:id]
- -
- - i = Invoice.load(tok)
- - if (not i) or i.merchant != self.merchant.token
- - raise DPException.new("Invalid invoice id: #{tok}", nil, 404)
- - end
- -
- - resp = APIResponse.new
- - resp.data = DataTypes.describe_invoice(i)
- - resp
- - end
- -
- - def retrieve_invoice_item(params)
- - tok = params[:id]
- -
- - item = InvoiceItem.load(tok)
- - if (not item) or item.merchant != self.merchant.token
- - raise DPException.new("Invalid invoice item id: #{tok}", nil, 404)
- - end
- - resp = APIResponse.new
- - resp.data = DataTypes.describe_invoice_item(item)
- - resp
- - end
- -
- def all_customers(params)
- per_page = params[:count] ? params[:count].to_i : 10
- offset = params[:offset] ? params[:offset].to_i : 0
- @@ -1995,6 +1826,7 @@ class API
- i = self.new
- i.merchant = mrch
- i.key = MerchantKey.load(params[:key])
- +
- target = self.method(i.key, m)
- raise DPException.new("No such API method: #{m}") unless target
- @@ -2025,12 +1857,11 @@ class APISrv < PaySrv
- # COMPATIBILITY helpers with older API
- helpers do
- def execute_old_style_api_request(name)
- -
- @new_params.merge! params.reject { |key, value|
- ['method', 'key'].include?(key) #we don't want ?key= or ?method=
- }
- -
- +
- v1_common_response_handler()
- raise InvalidAPIRequestError.new("Method not implemented.") if !name
- @@ -2047,7 +1878,6 @@ class APISrv < PaySrv
- def v1_initialize_request_params()
- @new_params = params
- -
- @common_log_params = {}
- reqid = String.randkey('apireq')
- @@ -2124,10 +1954,6 @@ class APISrv < PaySrv
- execute_old_style_api_request('retrieve_charge')
- end
- - get '/v1/customers/:customer_id/charges' do
- - execute_old_style_api_request('customer_charges')
- - end
- -
- post '/v1/charges/:id' do
- if params["action"] == "refund"
- execute_old_style_api_request('refund_charge')
- @@ -2155,78 +1981,35 @@ class APISrv < PaySrv
- end
- post '/v1/customers/:id' do
- - req = case params['action']
- - when 'update'
- - 'update_customer'
- - when 'delete'
- - 'delete_customer'
- - when 'cancel_subscription'
- - 'cancel_subscription'
- - else
- - nil
- - end
- - execute_old_style_api_request(req)
- - end
- -
- - # SUBSCRIPTION API
- -
- - get '/v1/customers/:customer_id/subscription' do
- - execute_old_style_api_request("active_subscription")
- - end
- -
- - post '/v1/customers/:id/subscription' do
- - req = case params['action']
- - when 'cancel'
- - 'cancel_subscription'
- - when 'update', 'create'
- - puts "hihihih"
- - 'change_subscription'
- - else
- - nil
- - end
- - execute_old_style_api_request(req)
- - end
- -
- -
- - # INVOICES API
- - get '/v1/invoices' do
- - execute_old_style_api_request('all_invoices')
- - end
- -
- - get '/v1/invoices/:id' do
- - execute_old_style_api_request('retrieve_invoice')
- - end
- -
- - get '/v1/customers/:customer_id/invoices' do
- - execute_old_style_api_request("customer_invoices")
- - end
- -
- - get '/v1/customers/:customer_id/invoices/upcoming' do
- - execute_old_style_api_request("upcoming_invoice")
- - end
- -
- - post '/v1/customers/:id/invoices/upcoming' do
- - if params["action"] == "add"
- - execute_old_style_api_request('bill_customer')
- - else
- - execute_old_style_api_request(nil)
- - end
- - end
- -
- - get '/v1/customers/:customer_id/invoices/upcoming/items/:id' do
- - execute_old_style_api_request('retrieve_invoice_item')
- - end
- -
- - post '/v1/customers/:customer_id/invoices/upcoming/items/:id' do
- if params["action"] == "update"
- - execute_old_style_api_request('update_invoice_item')
- + execute_old_style_api_request('update_customer')
- elsif params["action"] == "delete"
- - execute_old_style_api_request('delete_invoice_item')
- + execute_old_style_api_request('delete_customer')
- else
- execute_old_style_api_request(nil)
- end
- end
- - before '/v1' do
- +
- + # SUBSCRIPTION API
- +
- + #get '/v1/customers/:customer_id/subscriptions' do
- + #end
- +
- + #post '/v1/customers/:id/bills/create' do
- + # execute_old_style_api_request('bill_customer')
- + #end
- +
- + #get '/v1/customers/:customer_id/subscriptions/:subscription_id' do
- + #end
- +
- + #post '/v1/customers/:customer_id/subscriptions/:subscription_id' do
- + #end
- +
- + #delete '/v1/customers/:customer_id/bills/:id' do
- + #end
- +
- +
- + before '/v1' do
- v1_initialize_request_params()
- end
- @@ -2262,84 +2045,15 @@ class APISrv < PaySrv
- post '/v1' do
- v1_common_response_handler()
- - # convert old params
- - @new_params = convert_from_old_params(@new_params)
- -
- # Create variable in this scope
- @resp = nil
- Dynamic.let :request => request do
- @resp = API.invoke(@new_params[:method], @mrch, @new_params)
- end
- - @resp = convert_to_old_style_response(@new_params[:method], @resp)
- +
- return_response(@resp)
- end
- - def convert_from_old_params(params)
- - # temporarily support old subscription parameters
- - if params[:subscription]
- - sub = params[:subscription]
- - amount, interval, currency, delayed_start = sub[:amount], sub[:per], sub[:currency], sub[:start]
- - q = {:merchant => @mrch.token, :interval => interval, :amount => amount.to_i}
- -
- - if delayed_start
- - trial_days = ((delayed_start.to_i - Time.stamp) / (24 * 60 * 60)).round if delayed_start
- - q.merge!({:trial_period_days => trial_days})
- - end
- -
- - plan = Plan.load_one(q)
- - unless plan
- - count = Plan._find.count
- - plan = Plan.init
- - plan.name = "Plan #{count + 1}"
- - plan.amount = amount.to_i
- - plan.interval = interval
- - plan.currency = currency
- - plan.identifier = "plan#{count + 1}"
- - plan.trial_period_days = trial_days if trial_days
- - plan.merchant = @mrch.token
- - plan.save
- - end
- - params.delete(:subscription)
- - params[:plan] = plan.identifier
- - end
- - params.delete(:schedule)
- - params
- - end
- -
- - def old_style_customer(resp)
- - cust = Customer._find_one(:mrchid => resp.data[:id]).token
- - subscriptions = Subscription.load_all(:customer => cust).sort_by(&:start)
- - resp.data[:subscription_history] = subscriptions.map do |sub|
- - plan = sub.plan_
- - h = {:amount => plan.amount, :per => plan.interval, :currency => plan.currency, :start => sub.start.to_i}
- - h[:end] = sub.ended_at.to_i if sub.ended_at
- - h
- - end
- - resp.data[:schedule] = 'prebill' # force prebill
- - # legacy nonsense from before
- - legacy = @mrch.created < 1294079244
- - resp.data[:subscriptions] = resp.data[:subscription_history] if legacy
- - resp.data.delete(:subscription)
- - resp.data[:bills] = resp.data[:invoiceitems]
- - resp.data.delete(:invoiceitems)
- - resp
- - end
- -
- - def convert_to_old_style_response(method, resp)
- - case method
- - when 'retrieve_charge', 'execute_charge'
- - resp.data.delete(:invoice)
- - when 'bill_customer'
- - c = InvoiceItem.load(resp.data[:id]).customer
- - resp.data = DataTypes.describe_customer(Customer.load(c))
- - resp = old_style_customer(resp)
- - when 'create_customer', 'update_customer', 'retrieve_customer'
- - resp = old_style_customer(resp)
- - end
- - resp
- - end
- -
- -
- error [BadUserInputError, DPException, InvalidAPIRequestError, InvalidAPICredentialsError] do
- @common_log_params ||= { :common_log_params_was_nil => true }
- begin
- @@ -2430,28 +2144,35 @@ class APISrv < PaySrv
- end
- APISrv.configure do |srv|
- - $port = PayConf.get('api-bind-port') || 15003
- + set_svcname PayConf.get('api-srv-svcname') || 'bapi-srv'
- + pidfile
- + standard_log
- +
- + DB::initdb
- + $port = PayConf.get('api-bind-port') || 15003
- +
- srv.set :raise_errors, false
- srv.set :show_exceptions, false
- - srv.set :static, true
- +
- + srv.set :static
- srv.set :public, File.dirname(__FILE__) + '/stripejs/public/'
- srv.set :views, File.dirname(__FILE__) + '/api/views'
- srv.set :port, $port
- srv.set :bind, PayConf.get('api-bind-host') || '127.0.0.1'
- srv.disable_logging # stops using the *normal* logger, instead we put in the NoHealthCheckLogger below
- srv.use NoHealthCheckLogger
- +
- + if $0 == __FILE__ || $0 == File.expand_path(__FILE__)
- + Goat::QueueClient.configure(:host => PayConf.get('queue-host'),
- + :port => PayConf.get('queue-port'),
- + :prefix => PayConf.get('version'))
- + Goat::NotificationCenter.initialize_queue
- + emit_notifications
- + end
- +
- end
- -if Sys.invoked?(__FILE__)
- - set_svcname PayConf.get('api-srv-svcname') || 'bapi-srv'
- - pidfile
- - standard_log
- - Goat::QueueClient.configure(:host => PayConf.get('queue-host'),
- - :port => PayConf.get('queue-port'),
- - :prefix => PayConf.get('version'))
- - Goat::NotificationCenter.initialize_queue
- - emit_notifications
- - DB::initdb
- +if $0 == __FILE__ || $0 == File.expand_path(__FILE__)
- APISrv.run!
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement