Class: Rack::Payment::Request

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/rack-payment/request.rb

Overview

When you call #call, a new Request instance gets created and it does the actual logic to figure out what to do.

The #finish method “executes” this class (it figures out what to do).

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(env, payment_instance) ⇒ Request

Instantiates a Rack::Payment::Request object which basically wraps a single request and handles all of the logic to determine what to do.

Calling #finish will return the actual Rack response

Parameters:

  • The (Hash)

    Rack Request environment variables

  • The (Rack::Payment)

    instance of Rack::Payment handling this request



80
81
82
83
84
85
86
87
88
# File 'lib/rack-payment/request.rb', line 80

def initialize env, payment_instance
  @payment_instance = payment_instance

  self.env          = env
  self.request      = Rack::Request.new @env

  raw_rack_response = app.call env
  self.app_response = Rack::Response.new raw_rack_response[2], raw_rack_response[0], raw_rack_response[1]
end

Instance Attribute Details

#app_responseObject

Rack::Response that results from calling the actual Rack application.

Rack::Response


38
39
40
# File 'lib/rack-payment/request.rb', line 38

def app_response
  @app_response
end

#envHash

Returns Raw Rack env Hash.

Returns:

  • (Hash)

    Raw Rack env Hash



29
30
31
# File 'lib/rack-payment/request.rb', line 29

def env
  @env
end

#payment_instanceRack::Payment

The instance of Rack::Payment that this Request is for

Returns:



50
51
52
# File 'lib/rack-payment/request.rb', line 50

def payment_instance
  @payment_instance
end

#post_came_from_the_built_in_formstrue, false

Whether or not this request’s POST came from our built in forms.

* If true, we think the POST came from our form.
* If false, we think the POST came from a user's custom form.

Returns:

  • (true, false)


46
47
48
# File 'lib/rack-payment/request.rb', line 46

def post_came_from_the_built_in_forms
  @post_came_from_the_built_in_forms
end

#requestObject

An instance of Rack::Request that wraps our #env. It makes it much easier to access the params, path, method, etc.

Returns:

  • Rack::Request



34
35
36
# File 'lib/rack-payment/request.rb', line 34

def request
  @request
end

Instance Method Details

#amount_in_sessionObject



65
66
67
# File 'lib/rack-payment/request.rb', line 65

def amount_in_session
  session[:amount]
end

#amount_in_session=(value) ⇒ Object



69
70
71
# File 'lib/rack-payment/request.rb', line 69

def amount_in_session= value
  session[:amount] = value
end

#cleanup_env_for_rails(new_env) ⇒ Object

Rails keeps track of its own Request/Response.

If we’re using Rails, we need to delete these variables to trick Rails into thinking that this is a new request.

Kind of icky!



216
217
218
219
220
# File 'lib/rack-payment/request.rb', line 216

def cleanup_env_for_rails new_env
  new_env.delete 'action_controller.rescue.request'
  new_env.delete 'action_controller.rescue.response'
  new_env.delete 'REQUEST_URI'
end

#credit_card_and_billing_info_responseObject



238
239
240
241
242
243
244
245
246
# File 'lib/rack-payment/request.rb', line 238

def credit_card_and_billing_info_response
  css_file  = ::File.dirname(__FILE__) + '/views/credit-card-and-billing-info-form.css'
  form_html = payment.form :post_to => built_in_form_path, :inline_css => ::File.read(css_file)
  layout    = ::File.dirname(__FILE__) + '/views/layout.html'
  html      = ::File.read(layout)
  html      = html.sub 'CONTENT', form_html

  [ 200, {'Content-Type' => 'text/html'}, [html] ]
end

#express_cancel_urlObject



24
25
26
# File 'lib/rack-payment/request.rb', line 24

def express_cancel_url
  ::File.join request.url.sub(request.path_info, ''), express_cancel_path
end

#express_ok_urlObject

TODO test these!!!



21
22
23
# File 'lib/rack-payment/request.rb', line 21

def express_ok_url
  ::File.join request.url.sub(request.path_info, ''), express_ok_path
end

#finishArray

Generates and returns the final rack response.

This “runs” the request. It’s the main logic in Rack::Payment!

Returns:

  • (Array)

    A Rack response, eg. ‘[200, {}, [“Hello World”]]`



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/rack-payment/request.rb', line 95

def finish
  update_credit_card_and_billing_address_from_params

  # The application returned a 402 ('Payment Required')
  if app_response.status == 402
    self.amount_in_session = payment.amount
    
    return setup_express_purchase if payment.use_express?
    
    if payment.card_or_address_partially_filled_out?
      return process_credit_card
    else
      return credit_card_and_billing_info_response
    end

  # The requested path matches our built-in form
  elsif request.path_info == built_in_form_path
    self.post_came_from_the_built_in_forms = true
    return process_credit_card 

  # The requested path matches our callback for express payments
  elsif request.path_info == express_ok_path
    return process_express_payment_callback
  end

  # If we haven't returned anything, there was no reason for the 
  # middleware to handle this request so we return the real 
  # application's response.
  app_response.finish
end

#paymentObject



56
57
58
# File 'lib/rack-payment/request.rb', line 56

def payment
  env[env_helper_variable] ||= Rack::Payment::Helper.new(payment_instance)
end

#post_came_from_the_built_in_forms?Boolean

Returns:

  • (Boolean)


52
53
54
# File 'lib/rack-payment/request.rb', line 52

def post_came_from_the_built_in_forms?
  post_came_from_the_built_in_forms == true
end

#process_credit_cardObject

Gets parameters, attempts an #authorize call, attempts a #capture call, and renders the results.



157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/rack-payment/request.rb', line 157

def process_credit_card
  payment.amount ||= amount_in_session

  update_credit_card_and_billing_address_from_params

  # Purchase!
  if payment.purchase(:ip => request.ip)
    render_on_success
  else
    render_on_error payment.errors
  end
end

#process_express_payment_callbackObject



248
249
250
251
252
253
254
255
256
257
258
# File 'lib/rack-payment/request.rb', line 248

def process_express_payment_callback
  payment.amount ||= amount_in_session # gets lost because we're coming here directly from PayPal

  details = express_gateway.details_for params['token']

  payment.raw_express_response = express_gateway.purchase payment.amount_in_cents, :ip       => request.ip,
                                                                                   :token    => params['token'],
                                                                                   :payer_id => details.payer_id

  render_on_success
end

#render_on_error(errors) ⇒ Object



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/rack-payment/request.rb', line 186

def render_on_error errors
  if post_came_from_the_built_in_forms?
    # we POSTed from our form, so let's re-render our form
    credit_card_and_billing_info_response
  else
    # pass along the errors to the application's custom page, which should be the current URL
    # so we can actually just re-call the same env (should display the form) using a GET
    payment.errors = errors
    new_env = env.clone
    cleanup_env_for_rails(new_env)
    new_env['REQUEST_METHOD'] = 'GET'

    if request.path_info == express_ok_path # if express, we render on_success
      new_env['PATH_INFO'] = (payment.on_success || payment_instance.on_success)
    elsif payment.on_error
      new_env['PATH_INFO'] = payment.on_error # Specific to this request
    elsif payment_instance.on_error
      new_env['PATH_INFO'] = payment_instance.on_error # Global
    end

    app.call(new_env)
  end
end

#render_on_successObject



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/rack-payment/request.rb', line 222

def render_on_success
  on_success = (payment.on_success || payment_instance.on_success)

  if on_success
    # on_success is overriden ... we #call the main application using the on_success path
    new_env = env.clone
    cleanup_env_for_rails(new_env)
    new_env['PATH_INFO']      = on_success
    new_env['REQUEST_METHOD'] = 'GET'
    app.call new_env
  else
    # on_success has not been overriden ... let's just display out own info
    [ 200, {'Content-Type' => 'text/html'}, ["Order successful.  You should have been charged #{ payment.amount }" ]]
  end
end

#sessionObject



60
61
62
63
# File 'lib/rack-payment/request.rb', line 60

def session
  raise "Rack env['#{rack_session_variable}'] is nil.  Has a Rack::Session middleware be enabled?" if env[rack_session_variable].nil?
  env[rack_session_variable][session_variable] ||= {}
end

#setup_express_purchaseObject



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/rack-payment/request.rb', line 170

def setup_express_purchase
  # TODO we should get the callback URLs to use from the Rack::Purchase
  #      and they should be overridable

  # TODO go BOOM if the express gateway isn't set!

  # TODO catch exceptions

  # TODO catch ! success?
  response = express_gateway.setup_purchase payment.amount_in_cents, :ip                => request.ip, 
                                                                     :return_url        => express_ok_url,
                                                                     :cancel_return_url => express_cancel_url

  [ 302, {'Location' => express_gateway.redirect_url_for(response.token)}, ['Redirecting to PayPal Express Checkout'] ]
end

#update_credit_card_and_billing_address_from_paramsObject

If the params include fields that start with credit_card_ or the params have a hash of credit card variables, we use them to update our credit card information.

We do the same with the billing address.



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/rack-payment/request.rb', line 131

def update_credit_card_and_billing_address_from_params
  # The params *should* be set on the payment data object, but we accept 
  # POST requests too, so we check the POST variables for credit_card 
  # or billing_address fields
  #
  # TODO deprecate this in favor of the more conventional credit_card[number] syntax?
  #
  params.each do |field, value|
    if field =~ /^credit_card_(\w+)/
      payment.credit_card.update $1 => value
    elsif field =~ /billing_address_(\w+)/
      payment.billing_address.update $1 => value
    end 
  end

  # We also accept credit_card[number] style params, which Rack supports
  if params['credit_card'] and params['credit_card'].respond_to?(:each)
    payment.credit_card.update params['credit_card']
  end
  if params['billing_address'] and params['billing_address'].respond_to?(:each)
    payment.billing_address.update params['billing_address']
  end
end