Software as a Service with Amazon FPS using Remit for Ruby
For those who actually want people to pay to use their web applications, and don’t want the headache of accepting credit card payments themselves, the payment process can be outsourced with such services as Paypal Website Payments Standard and Amazon Flexible Payments Service (FPS). Amazon’s FPS looked very developer-oriented, and I like the customer experience they provide, so I went that route. Thankfully someone had already written a Ruby API for Amazon FPS, called Remit. It’s very well crafted, and I was able to glean everything I needed to know from the source code, but it still would have been nice to have a few examples to work from as I started using it to do the software-as-a-service dirtywork I needed to get done. So for those who follow in this path, here are a few tidbits. Note that a lot of the work in setting up a payment system is properly handling all the ways that payments can fail, which I’m not going into here.
Most Remit operations require an instance of Remit::API, so here’s how to set one up. The first two arguments for Remit::API.new are the access key and secret key that you can obtain when logged in to your Amazon Web Services account, and the third argument indicates whether or not to use sandbox mode:
require 'remit' api = Remit::API.new( "my access key", "my secret key", true )
Here’s the one-time process of creating and installing your recipient and caller tokens, which are needed to receive payments. The arguments for the request here are:
- payment_instruction – see the Amazon FPS docs for customizing this
- caller_reference – a reference ID that you may or may not want to store
- token_friendly_name – optional friendly name for the token
- token_type – see the Amazon FPS docs for customizing this
request = Remit::InstallPaymentInstruction::Request.new( :payment_instruction => "MyRole == 'Recipient' orSay 'Role does not match';", :caller_reference => Time.now.to_i.to_s, :token_friendly_name => "Friendly Name For Your Token", :token_type => "Unrestricted" ) install_recipient_response = api.install_payment_instruction(request) install_recipient_response.token_id # hold on to this request = Remit::InstallPaymentInstruction::Request.new( :payment_instruction => "MyRole == 'Caller' orSay 'Role does not match';", :caller_reference => Time.now.to_i.to_s, :token_friendly_name => "TriggerFood Caller Token", :token_type => "Unrestricted" ) install_caller_response = api.install_payment_instruction(request) install_caller_response.token_id # hold on to this
Now you can start directing users to Amazon to pay you. If you’re not really familiar with Amazon FPS yet, what happens is that you redirect your users to the URL this following code provides, where they have to log in or register for an Amazon account, and then specify their payment method. No actual payment is collected though, you just get a payment token back when the pipeline successfully redirects the user back to your site. Here are the arguments for getting the pipeline URL:
- caller_reference – a reference ID that you may or may not want to store
- recipient_token – the token you just produced above
- transaction_amount – per-month price
- recurring_period – length of recurring time period
- return_URL – the URL you want users to be sent back to after they’ve gone through the Amazon “pipeline”
- caller_key – same as access the key when creating the Remit::API instance
recurring_use_pipeline = api.get_recurring_use_pipeline( :caller_reference => Time.now.to_i.to_s, :recipient_token => install_recipient_response.token_id, :transaction_amount => price, :recurring_period => "1 Month", :return_URL => return_url, :caller_key => "my access key" ) recurring_use_pipeline.url # this is the URL you want to send your users to
When users are sent back to your site at the :return_URL you provide, you’ll need to process that URL to get the information about how what happened in the pipeline. You’ll need to provide both the URL from Amazon when they redirected the user to your site, as well as your secret yet (the same as used above in the API step).
pipeline_response = Remit::PipelineResponse.new( url, "my secret key" )
There are 3 essential pieces of information you’ll be able to glean from the pipeline response:
- pipeline_response.valid? – if this isn’t true, then the URL isn’t signed properly, so you don’t want to use it
- pipeline_response.success? – make sure this is also true before trying to request payment
- pipeline_response.tokenID – the token for the sender–you’ll use this when requesting payment.
Now to make the user actually pay. For a web app with monthly billing, you’ll probably request payment immediately after running the pipeline or after some trial period, and then again once every month. Setting a temporary decline policy of implicit retry here will make Amazon do the work of retrying the transaction until it is either successful or permanently failed. This requires handling instant payment notification requests, which we’ll get to next. Here are the arguments you’ll need to provide and an example:
- caller_token_id – install_caller_response.token_id from above
- recipient_token_id – install_recipient_response.token_id from above
- sender_token_id – the pipeline_response.tokenID from the last step
- transaction_amount – a Remit::RequestTypes::Amount object, which takes an :amount and :currency_code (see example)
- charge_fee_to – should be “Recipient”, assuming you want Amazon’s fee to come out of the charge to the user
- caller_reference – a reference ID that you may or may not want to store
- temporary_decline_policy – needs to specify implicit retry if you want Amazon to do the dirty work (see example)
request = Remit::Pay::Request.new(
:caller_token_id => "my caller token",
:recipient_token_id => "my recipient token",
:sender_token_id => pipeline_response.tokenID,
:transaction_amount => Remit::RequestTypes::Amount.new(
:amount => price,
:currency_code => "USD"
),
:charge_fee_to => "Recipient",
:caller_reference => Time.now.to_i.to_s,
:temporary_decline_policy => Remit::RequestTypes::TemporaryDeclinePolicy.new(
:temporary_decline_policy_type => Remit::TemporaryDeclinePolicyType::IMPLICIT_RETRY
)
)
pay_response = api.pay(request)
The pay response returns the following essential information:
- pay_response.successful? – false indicates that the request failed, true indicates that the request was successful, but not necessarily the transaction
- pay_response.transaction_response.success? – true if the payment transaction was a success
- pay_response.transaction_response.initiated? – true if payment has been initiated, but needs to be handled asynchronously
- pay_response.transaction_response.reinitiated? – true if transaction was temporarily declined, but has been initiated again
- pay_response.transaction_response.temporary_decline? – true if the transaction is temporarily declined
- pay_response.transaction_response.failure? – true if it’s a complete failure
For transactions that are of ‘initiated’, ‘reinitiated’ or ‘temporary_decline’ status, Amazon will keep trying the transactions until they complete, assuming the temporary decline policy has been set to implicit retry. I handle these by using the Amazon FPS Instant Payment Notification (IPN) service, which can be enabled and set to hit a URL of your choice in your Amazon Payments settings. In order to process these notifications, you just need to provide Remit with the request parameters and your secret key:
ipn_request = Remit::IpnRequest.new( params, "my secret key" )
The IPN request has the following useful properties:
- ipn_request.valid? – true if the IPN request was properly signed and had all the necessary info
- ipn_request.status – if this is “SUCCESS”, then the payment went through, otherwise it has permanently failed.
So that’s the bare bones of what’s needed to use Amazon FPS to handle recurring payments. There’s a lot missing here, such as (assuming a Ruby on Rails app) the actions that will send the user to your Amazon FPS pipeline, receive the responses from the Amazon FPS pipeline and IPN service, and the code to handle recurring payments. Writing lots of tests for these models and controllers is highly recommended, as well as quite a bit of subsequent integration testing in sandbox mode. Enjoy!

Hey, I’m having some trouble saving a URL for the Amazon FPS Instant Payment Notification in my development environment. I get an error if I try any version of localhost, and have tried localhost running on port 80, but still no go. Do you have to forward from a site of the form http://www.blah.com? or is there a way to use a localhost route for the callback IPN URL?
Dave: This is one of the things that makes developing and testing with external services a bit of a pain. In order to get the IPN callbacks from Amazon, you have to have a public-facing server. So you’ll need to expose your application with a publicly-accessible URL in order to get it to work. In my case I tested my code thoroughly in isolation (model and controller tests), and then set it up on my staging server that’s exposed to the web to test it in conjunction with the actual Amazon FPS service.
Thanks for the help, I was able to setup a dynamic dns (http://www.dyndns.com/services/dns/dyndns/) that routes back to my server and allows me to receive IPN. My question now is what is the best way to notify a customer using IPN, do you just send out an email when you get the IPN information, or what about displaying a flash that says, “finishing transaction” and then when I recieve the IPN, update that flash accordingly?
Dave: I handle this with TriggerFood by showing a page after I make the pay request that tells the user their payment is pending, and then sending an email once I get the IPN that the payment is successful. Having a flash message that shows up once the IPN comes back sounds like a nice touch, though!
I am having trouble with the pipelineresponse. When you call new below:
Remit::PipelineResponse.new(
url,
“my secret key”
)
Is the url parameter the output of request.request_uri? The uri I am sending does not pass the validation test. The valid function always returns false.
Rolin: I’m using request.url, which appears to include the full “http://…”. Hopefully that will do the trick.
Nice, thanks for the info. Cool site! I’m curious, do you use GetResults = Remit::GetResults::Request.new(:max_results_count => ’25′)
to poll amazon for requests that did not get processed by IPN? And if so, are you allowed to pass an array of transactionIds instead of individually using remit?
Dave: I’m currently relying entirely on IPN, so I’m not doing any polling. Amazon’s IPN is supposed to be pretty robust and keep retrying the request until it gets a successful response from your server. So far I haven’t had any trouble with this, but polling would be a good fallback.
Thanks bruz for the help, but I just cannot get it to work. I am using remit (0.0.2) with relax (0.0.6) and I am stuck calling the pay function. One of the issue is the Remit::TemporaryDeclinePolicyType::IMPLICIT_RETRY is not defined anywhere. So, I hardcode a value by setting :temporary_decline_policy_type => ‘ImplicitRetry’. I was successfully able to create all 3 tokens and recurring payment authorizations in the sandbox. However, the api.pay just doesn’t work. I wonder if the required parameters to the API has changed. Looking at the pay module code, it doesn’t show that it requires any of the parameters. The interesting thing is, it is as if the pay never makes the http call (pay_response.successful? is always false). I don’t see any errors in the log. I’ve enclosed portion of the code.
def remit
@remit ||= begin
sandbox = !Rails.env.production?
Remit::API.new(FPS_ACCESS_KEY, FPS_SECRET_KEY, sandbox)
end
end
request = Remit::Pay::Request.new(
:caller_token_id => session[:install_caller_response_token_id],
:recipient_token_id => session[:install_recipient_response_token_id],
:sender_token_id => pipeline_response.tokenID,
:transaction_amount => Remit::RequestTypes::Amount.new(
:amount => @amount,
:currency_code => “USD”),
:charge_fee_to => “Recipient”,
:caller_reference => ‘lottokeeper_pay-’ + Time.now.to_i.to_s,
:temporary_decline_policy => Remit::RequestTypes::TemporaryDeclinePolicy.new(
:temporary_decline_policy_type => ‘ImplicitRetry’,
:implicit_retry_timeout_in_mins => 5))
puts ‘Before calling pay_request.’
pay_response = remit.pay(request)
Hmm… It looks like Remit has undergone a few changes (http://github.com/tylerhunt/remit/commits) since I started using it and wrote this post, so that’s one possible problem. What does the ‘pay_response’ object that you get back look like?
I’m still running payments through Amazon using this same code, so I don’t think it’s an API change. Are you able use other parts of the API? Does something like get_tokens work?
Thanks bruz for helping. I am able to call the get_tokens function with no problems. In fact, since I been trying this for several days, the number of tokens returned is always 100. I am not sure if that is the maximum amazon will return or something else. However, I am a bit new to ruby and I am not sure how to print the structure of the pay_response message that is returned. I tried to stick a to_s to the expected fields and they are alway blank. I suspect because pay_response.successful is false they are not being populated. I would think some error field would be populated if something failed. But then again, it looks like the call to amazon is not occurring.
Nice Writing!
I have a problem here generating caller and recipient token. I am using remit version 0.0.4. Every time I try to generate caller/recipient token, The results returns a empty string. I am not able to track what’s going wrong.. Can anybody please help me with this?
Mohan: Since remit has had some changes since I wrote this post, I installed the latest 0.0.4 version, but the code I posted is still working fine. I also checked to see if invalid AWS credentials would cause your problem, but I still get a response object, just one that indicates errors. It doesn’t seem like remit should have any platform-specific issues, but just in case, what OS and Ruby version are you running? I’ve only used it in Linux(Ubuntu) with Ruby 1.8.6.
Thanks… The code is working fine for me now. Nice writing…
Could you please tell me how can I parse the xml response by the amazon. Which is something like below..
#<Remit::GetTokens::Response:0x481d3d8 @raw="\n<ns3:GetTokensResponse xmlns:ns3=\……….
I am using ruby REXML to parse it, but getting some error. Could you please suggest how to do it?
Hi bruz…
I figured it out. I was able to solve the problem. There was some problem with my code.
Thanks once again for the nice writeup.
This code is working perfectly for me.
Hi,
I have a problem while try to generate multi use tokens using MultiUsePipeline, In case of this pipeline Iam passing an array of recipient token list as an input to the :recipient_token_list parameter as below.
:recipient_token_list => [recipient_token1, recipient_token2]
but every time it shows me an error as
Caller Input Exception: The following input(s) are not well formed: [recipientTokenList]
How can I pass the list of recipient token ids to this parameter? Please suggest.
Mohan: Looking at Amazon’s FPS documentation, it looks like what they’re expecting is a comma-separated list of tokens, so this ought to work (although I haven’t tried it myself):
:recipient_token_list => “#{recipient_token1}, #{recipient_token2}”
Hi, I’m bewildered trying to get this to work. I don’t understand how to read the remit rdocs. I tried going from this gist (http://gist.github.com/46941), as well as your instructions. So far I can only get to the Amazon portal. After the redirect nothing works. Here is everything that I can think of that is relevant. http://pastie.org/582453
I hope there is enough information. Thanks for keeping track of this thread. You seem to have the only tutorial available.
It looks like Mohan had the same problem above with getting blank string responses, and was able to resolve it, but unfortunately he didn’t mention what the fix was. I was able to use the code you posted and get a response for an install_payment_request call, so I’m not sure what’s going wrong. Here’s what I tried, does it work? http://pastie.org/582668
Where is the best place for the install_* code? Do they both belong in the response method? One and the other? I’m confused.
For the following, I still get a blank string for the @payment.caller_token_id
http://pastie.org/582795
In the console, I get this error. http://pastie.org/582849 I don’t know what to make of it.
Jesse: It looks like FPS doesn’t like your :payment_instruction. Payment instructions have to follow Amazon’s “GateKeeper language”, which is documented at http://docs.amazonwebservices.com/AmazonFPS/2007-01-08/FPSDeveloperGuide/index.html?GateKeeperLanguage.html. Perhaps what you need is :payment_instruction => “MyRole == ‘Recipient’ orSay ‘Role does not match’;” instead of just :payment_instruction => ‘Caller’
Success!- says the flash notice. Thanks so much for helping. I was really stuck.
I’ve gotten the payment system to work, except I don’t know how to validate the response. Please see line 53, http://pastie.org/596506
Hpricot grabs any incoming XML, and pretty much seems to do all the work. (I don’t know how it does, but it does.)
Good news for me, I have a cofounder for this site, and partner in general. He’s working on this ticket now, but I’d like to ask if this is something more trivial than involved, so I can ask him to work on something more fun.
See my reply at http://groups.google.com/group/remit/browse_thread/thread/96f85b6fab3dc3b4
Please see line 32. This only works half the time. It’s very
puzzling to me. I’m tempted to run it through a loop until it makes
the receiver token, but I fear that it might sprawl. Can you help?
http://pastie.org/private/um69rf6pgrpjtq4nl25ndw
See discussion at http://groups.google.com/group/remit/browse_thread/thread/6d36c2c7576b6740
I’m making slow progress with this, but am not able to figure out why the response I get (after going to Amazon and coming back) has a false response to the valid? method, and no success? method. Is there any way to get more information from the response? The params I’m getting look something like this:
Parameters: {“tokenID”=>”blah blah blah”, “signatureVersion”=>”2″, “callerReference”=>”1268126836″, “action”=>”process_response”, “signature”=>”blah blah blah”, “certificateUrl”=>”https://fps.sandbox.amazonaws.com/certs/090909/PKICert.pem”, “signatureMethod”=>”RSA-SHA1″, “controller”=>”coffee”, “expiry”=>”08/2015″, “status”=>”SC”}
Anybody have any suggestions? Thanks.
Remit only currently supports signature version 1 (see line 88 in lib/remit.rb), so I’m guessing it can’t handle the signature version 2 response you’re getting from Amazon and that’s why valid? is false. What’s unclear is why you’re getting a response with signature version 2. What does your code to generate the pipeline URL at Amazon look like? Perhaps somehow you’re using a newer version of the API (remit uses the 2007-01-08 version)?