Sunday
Jan042009
Using Amazon Simple Pay from Rails
Sunday, January 4, 2009 at 6:10AM
I've recently been working to integrate Amazon Simple Pay into an application. In general, this has been pretty straightforward, thanks to the simplepay gem, which was recently bumped to version 0.2.0. (Amazon also has their own Ruby sample for ASP integration, which came in handy for a first smoke test - though unfortunately at the moment the Amazon site is having trouble delivering that file).
For the most part, using the simplepay gem is quite straightforward. There's a helper to build a payment form:
[sourcecode language='ruby']
Click here to send us $120 every year
<%= simplepay_form_for(:subscription, {
:amount => 120.00,
:description => "Annual Subscription",
:recurring_frequency => "1 year",
:abandon_url => amaz_abandon_customers_url,
:collect_shipping_address => false,
:immediate_return => false,
:ipn_url => amaz_ipn_customers_url,
:process_immediately => true,
:reference_id => @customer.id,
:return_url => amaz_return_customers_url
}) %>
[/sourcecode]
When Amazon executes callbacks, it sends a signature parameter along so that you can verify that the request is legitimate. This is the only spot where I ran into a little gotcha. The simplepay gem includes a helper to validate the parameters of a request to see if they match the signature, but you have to be sure to take out the parameters that Rails inserts before you call it:
[sourcecode language='ruby']
def amaz_ipn
params.delete('controller')
params.delete('action')
if AWS::SimplePay::IpnValidator.valid_ipn_post?
(Simplepay.aws_secret_access_key, params)
# save the results somewhere
render :action => 'show'
else
logger.info "bad request from Amazon Simple Pay"
render :nothing => true, :status => 200
end
end
[/sourcecode]
Also note that you should send back a 200 response even if you're unable to verify the signature. This will prevent Amazon from sending the same request to you over and over again.
For the most part, using the simplepay gem is quite straightforward. There's a helper to build a payment form:
[sourcecode language='ruby']
Click here to send us $120 every year
<%= simplepay_form_for(:subscription, {
:amount => 120.00,
:description => "Annual Subscription",
:recurring_frequency => "1 year",
:abandon_url => amaz_abandon_customers_url,
:collect_shipping_address => false,
:immediate_return => false,
:ipn_url => amaz_ipn_customers_url,
:process_immediately => true,
:reference_id => @customer.id,
:return_url => amaz_return_customers_url
}) %>
[/sourcecode]
When Amazon executes callbacks, it sends a signature parameter along so that you can verify that the request is legitimate. This is the only spot where I ran into a little gotcha. The simplepay gem includes a helper to validate the parameters of a request to see if they match the signature, but you have to be sure to take out the parameters that Rails inserts before you call it:
[sourcecode language='ruby']
def amaz_ipn
params.delete('controller')
params.delete('action')
if AWS::SimplePay::IpnValidator.valid_ipn_post?
(Simplepay.aws_secret_access_key, params)
# save the results somewhere
render :action => 'show'
else
logger.info "bad request from Amazon Simple Pay"
render :nothing => true, :status => 200
end
end
[/sourcecode]
Also note that you should send back a 200 response even if you're unable to verify the signature. This will prevent Amazon from sending the same request to you over and over again.

Reader Comments (2)
Using Hash#delete right on params seems a little scary, since it's destructive. Might cause hard-to-debug issues under some conditions.
Instead I'd probably use the non-destructive params.except(:controller, :action).
Possibly Rails has some method to get the params without controller and action. I vaguely recall there being one, but I can't find it, so maybe not.
request.query_parameters for GET or request.request_parameters for POST is what I was looking for.