Created
October 8, 2009 16:01
-
-
Save ohammersmith/205127 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Index: test/unit/integrations/helpers/amazon_paynow_helper_test.rb | |
=================================================================== | |
--- test/unit/integrations/helpers/amazon_paynow_helper_test.rb (revision 0) | |
+++ test/unit/integrations/helpers/amazon_paynow_helper_test.rb (revision 0) | |
@@ -0,0 +1,31 @@ | |
+require File.dirname(__FILE__) + '/../../../test_helper' | |
+ | |
+class AmazonPaynowHelperTest < Test::Unit::TestCase | |
+ include ActiveMerchant::Billing::Integrations | |
+ | |
+ def setup | |
+ @helper = AmazonPaynow::Helper.new('order-500',nil, :aws_access_key_id => 'JDAKJHKASKDJAHSKDJHA', :aws_secret_access_key => 'askdjhdueHAKSUENajskshdyYWJANSKDJ') | |
+ end | |
+ | |
+ | |
+ def test_form_fields | |
+ @helper.description 'Product 500' | |
+ @helper.amount 'USD 10.11' | |
+ @helper.immediate_return false | |
+ @helper.return_url 'http://localhost:3000/amazon_paynow/done' | |
+ @helper.cancel_return_url 'http://localhost:3000/amazon_paynow/cancel' | |
+ | |
+ # calling this function as only then signature gets computed | |
+ form_fields = @helper.form_fields | |
+ | |
+ assert_field 'description', 'Product 500' | |
+ assert_field 'amount', 'USD 10.11' | |
+ assert_field 'referenceId', 'order-500' | |
+ assert_field 'accessKey', 'JDAKJHKASKDJAHSKDJHA' | |
+ assert_field 'immediateReturn', '0' | |
+ assert_field 'accessKey', 'JDAKJHKASKDJAHSKDJHA' | |
+ assert_field 'returnUrl', 'http://localhost:3000/amazon_paynow/done' | |
+ assert_field 'abandonUrl', 'http://localhost:3000/amazon_paynow/cancel' | |
+ assert_field 'signature', 'GQAXF+ww53KnTBOGTapsY+hP12Y=' | |
+ end | |
+end | |
Index: test/unit/integrations/amazon_paynow_module_test.rb | |
=================================================================== | |
--- test/unit/integrations/amazon_paynow_module_test.rb (revision 0) | |
+++ test/unit/integrations/amazon_paynow_module_test.rb (revision 0) | |
@@ -0,0 +1,52 @@ | |
+require File.dirname(__FILE__) + '/../../test_helper' | |
+ | |
+class AmazonPaynowModuleTest < Test::Unit::TestCase | |
+ include ActiveMerchant::Billing::Integrations | |
+ | |
+ def test_test_mode | |
+ ActiveMerchant::Billing::Base.integration_mode = :test | |
+ assert_equal 'https://authorize.payments-sandbox.amazon.com/pba/paypipeline', AmazonPaynow.service_url | |
+ end | |
+ | |
+ def test_production_mode | |
+ ActiveMerchant::Billing::Base.integration_mode = :production | |
+ assert_equal 'https://authorize.payments.amazon.com/pba/paypipeline', AmazonPaynow.service_url | |
+ end | |
+ | |
+ def test_invalid_mode | |
+ ActiveMerchant::Billing::Base.integration_mode = :preprod | |
+ assert_raise(StandardError){ AmazonPaynow.service_url } | |
+ end | |
+ | |
+ def test_ui_return | |
+ query_string = 'status=PS&referenceId=orderid85&transactionId=12ROQMTAFV1V348RVSJTQNPTN48TQ64K9HH&signature=oHK0/MU8coqRkygmMURTHp9Ekzc=' | |
+ | |
+ resp = ActiveMerchant::Billing::Integrations::AmazonPaynow::Return.new(query_string) | |
+ | |
+ assert_equal resp.reference_id, 'orderid85' | |
+ assert_equal resp.transaction_id, '12ROQMTAFV1V348RVSJTQNPTN48TQ64K9HH' | |
+ assert_equal resp.status, 'PS' | |
+ assert_equal resp.acknowledge('dummykey'), true | |
+ end | |
+ | |
+ def test_ui_merchant_error | |
+ query_string = 'status=ME&errorMessage=Merchant+Input+Error%3A+The+following+input+parameters+are+either+invalid+or+absent%3A+%5Bamount%5D&referenceId=orderid904' | |
+ | |
+ resp = ActiveMerchant::Billing::Integrations::AmazonPaynow::Return.new(query_string) | |
+ | |
+ assert_equal resp.reference_id, 'orderid904' | |
+ assert_equal resp.status, 'ME' | |
+ assert_equal resp.error_message, 'Merchant Input Error: The following input parameters are either invalid or absent: [amount]' | |
+ end | |
+ | |
+ | |
+ def test_ui_return_ack_failure | |
+ | |
+ query_string = 'status=PS&referenceId=orderid85&transactionId=12ROQMTAFV1V348RVSJTQNPTN48TQ64K9HH&signature=oHK0/MU8coqRkygmMURTHp9zc=' | |
+ | |
+ resp = ActiveMerchant::Billing::Integrations::AmazonPaynow::Return.new(query_string) | |
+ | |
+ assert_equal resp.acknowledge('dummy_key'), false | |
+ end | |
+ | |
+end | |
Index: lib/active_merchant/billing/integrations/amazon_paynow/helper.rb | |
=================================================================== | |
--- lib/active_merchant/billing/integrations/amazon_paynow/helper.rb (revision 0) | |
+++ lib/active_merchant/billing/integrations/amazon_paynow/helper.rb (revision 0) | |
@@ -0,0 +1,81 @@ | |
+module ActiveMerchant #:nodoc: | |
+ module Billing #:nodoc: | |
+ module Integrations #:nodoc: | |
+ module AmazonPaynow | |
+ class Helper < ActiveMerchant::Billing::Integrations::Helper | |
+ | |
+ mapping :amount, 'amount' | |
+ mapping :reference_id, "referenceId" | |
+ mapping :immediate_return, "immediateReturn" | |
+ mapping :aws_access_key_id, "accessKey" | |
+ mapping :return_url, "returnUrl" | |
+ mapping :cancel_return_url, "abandonUrl" | |
+ mapping :description, "description" | |
+ | |
+ def initialize(order, account, options = {}) | |
+ @aws_access_key_id = options.delete(:aws_access_key_id) | |
+ @aws_secret_access_key = options.delete(:aws_secret_access_key) | |
+ super | |
+ self.aws_access_key_id = @aws_access_key_id | |
+ self.reference_id = order | |
+ end | |
+ | |
+ # This method accepts a boolean | |
+ def immediate_return(immediate_return_flag) | |
+ if immediate_return_flag | |
+ value = 1 | |
+ else | |
+ value = 0 | |
+ end | |
+ add_field mappings[:immediate_return], value | |
+ end | |
+ | |
+ def immediate_return=(immediate_return_flag) | |
+ immediate_return(immediate_return_flag) | |
+ end | |
+ | |
+ # Format amount in format "#{currency} #{amount}" | |
+ def amount(amt) | |
+ unless amt.nil? | |
+ if amt.is_a?(String) | |
+ value = amt | |
+ elsif amt.is_a?(Money) | |
+ value = sprintf("%s %.2f", amt.currency, amt.cents.to_f/100) | |
+ else | |
+ raise ArgumentError, 'money amount must be either a Money object | |
+ or a string like "USD 50"' | |
+ end | |
+ add_field mappings[:amount], value | |
+ end | |
+ end | |
+ | |
+ def amount=(amt) | |
+ amount(amt) | |
+ end | |
+ | |
+ def form_fields | |
+ add_field("signature", Helper::calculate_signature( | |
+ Helper::get_string_to_sign(super), | |
+ @aws_secret_access_key)) | |
+ return super | |
+ end | |
+ | |
+ # Computes RFC 2104-compliant HMAC signature given | |
+ # given data to sign | |
+ def self.calculate_signature(data, secret_key) | |
+ digest = OpenSSL::Digest::Digest.new('sha1') | |
+ hmac = OpenSSL::HMAC.digest(digest, secret_key, data) | |
+ Base64.encode64(hmac).chomp | |
+ end | |
+ | |
+ # Computes the string to sign from the form parameters | |
+ def self.get_string_to_sign(field_values) | |
+ sorted_fields = field_values.sort_by { |field, value| field.downcase } | |
+ sorted_fields.collect {|field, value| field+value}.join | |
+ end | |
+ | |
+ end | |
+ end | |
+ end | |
+ end | |
+end | |
Index: lib/active_merchant/billing/integrations/amazon_paynow/return.rb | |
=================================================================== | |
--- lib/active_merchant/billing/integrations/amazon_paynow/return.rb (revision 0) | |
+++ lib/active_merchant/billing/integrations/amazon_paynow/return.rb (revision 0) | |
@@ -0,0 +1,71 @@ | |
+require 'net/http' | |
+ | |
+module ActiveMerchant #:nodoc: | |
+ module Billing #:nodoc: | |
+ module Integrations #:nodoc: | |
+ module AmazonPaynow | |
+ class Return < ActiveMerchant::Billing::Integrations::Return | |
+ | |
+# def initialize(response_string) | |
+# super | |
+# @params.each_key do |key| | |
+# @params[key] = CGI::unescape(@params[key]) | |
+# end | |
+# end | |
+ | |
+ def params | |
+ @params | |
+ end | |
+ | |
+ def reference_id | |
+ params['referenceId'] | |
+ end | |
+ | |
+ def transaction_id | |
+ params['transactionId'] | |
+ end | |
+ | |
+ def error_message | |
+ params['errorMessage'] | |
+ end | |
+ | |
+ def signature | |
+ params['signature'] | |
+ end | |
+ | |
+ #Status of transaction. List of possible values: | |
+ #<tt>PS</tt>::Indicates that the payment transaction was successful. | |
+ #<tt>PF</tt>::Indicates that the payment transaction has failed and the money was not transferred. | |
+ # You can redirect your customer to the Amazon Payments Payment Authorization page | |
+ # to select a different payment method. | |
+ #<tt>PI</tt>::Indicates the payment has been initiated. It will take between five seconds | |
+ # and 48 hours to complete, based on the availability of external payment networks and | |
+ # the riskiness of the transaction. | |
+ #<tt>A</tt>::Indicates that your customer abandoned the transaction by clicking on the Cancel | |
+ # link during the payment pipeline. | |
+ #<tt>ME</tt>::Indicates that the HTML form received was not constructed properly. Please refer to | |
+ # the Implementation Guide for instructions on constructing Amazon Payments buttons. | |
+ #<tt>SE</tt>::Indicates a temporary system unavailable error and that the payment was not processed. | |
+ def status | |
+ params['status'] | |
+ end | |
+ | |
+ def success | |
+ raise NotImplementedError.new("Use the method status instead") | |
+ end | |
+ | |
+ # Validate that the response is in fact from Amazon Pay now pipeline | |
+ # by verifying the signature | |
+ def acknowledge(aws_secret_access_key) | |
+ return_info = params.clone | |
+ return_info.delete("signature") | |
+ string_to_sign = Helper.get_string_to_sign(return_info) | |
+ calculated_signature = Helper.calculate_signature(string_to_sign, | |
+ aws_secret_access_key) | |
+ signature == calculated_signature | |
+ end | |
+ end | |
+ end | |
+ end | |
+ end | |
+end | |
Index: lib/active_merchant/billing/integrations/amazon_paynow.rb | |
=================================================================== | |
--- lib/active_merchant/billing/integrations/amazon_paynow.rb (revision 0) | |
+++ lib/active_merchant/billing/integrations/amazon_paynow.rb (revision 0) | |
@@ -0,0 +1,88 @@ | |
+require File.dirname(__FILE__) + '/amazon_paynow/helper.rb' | |
+require File.dirname(__FILE__) + '/amazon_paynow/return.rb' | |
+ | |
+module ActiveMerchant #:nodoc: | |
+ module Billing #:nodoc: | |
+ module Integrations #:nodoc: | |
+ | |
+ # To start with Amazon Pay now, follow the instructions for installing | |
+ # ActiveMerchant as a plugin, as described on | |
+ # http://www.activemerchant.org/. | |
+ # | |
+ # The AmazonPaynow helper facilitates creating the Amazon FPS Pay now | |
+ # button/link dymanically for any product discription/price price/dercription | |
+ # | |
+ # Here is the relevent documentation about Amazon Pay now | |
+ # http://docs.amazonwebservices.com/AmazonFPS/2007-01-08/PayNowWidgetImplementationGuide/ | |
+ # | |
+ # The following code renders a html form that redirect to Amazon FPS | |
+ # Pay Now pipeline | |
+ # | |
+ # The syntax of the helper is as follows: | |
+ # | |
+ # <% payment_service_for 'order11', nil, | |
+ # :aws_access_key_id => 'KJAJJJAKSUEKAHCNEKRU', | |
+ # :aws_secret_access_key => 'saskjdhfLKJlkjasdfasfasdksjdfhdksjfha', | |
+ # :service => :amazon_paynow | |
+ # do |service| %> | |
+ # | |
+ # <% service.description "Purchase at Foo bar store" | |
+ # <% service.amount "USD 400" %> | |
+ # <% service.immediate_return false %> | |
+ # <% service.return_url url_for(:only_path => false, :action => 'done') %> | |
+ # <% service.cancel_return_url url_for(:only_path => false, :action => 'cancel') %> | |
+ # <p>Proceed to make payment<p> | |
+ # <%=image_submit_tag 'https://authorize.payments.amazon.com/pba/images/payNowButton.png' %> | |
+ # <% end %> | |
+ # | |
+ # | |
+ # The following code is a quick example (not complete) of how the the response from Amazon FPS | |
+ # Pay Now pipeline is handled using the AmazonPaynow::Return class | |
+ # | |
+ # <% resp = ActiveMerchant::Billing::Integrations::AmazonPaynow::Return.new(request.query_string) | |
+ # | |
+ # status = resp.status | |
+ # | |
+ # if (status == 'PS' || status == 'PI' || status == 'PF') && !resp.acknowledge(@aws_secret_access_key) | |
+ # msg = 'There was a problem while returning back to our web site. There could be a malware in your browser changing the data returned to our website' | |
+ # elsif status == 'PS' | |
+ # msg = 'Thank You for your payment' | |
+ # elsif resp.status == 'PI' | |
+ # msg = 'Your payment is initiated. We will process the order once the payment is complete.' | |
+ # elsif status == 'PF' | |
+ # msg = 'Your payment has failed. Please try again using a different payment method' | |
+ # elsif status == 'SE' | |
+ # msg = 'There is a temporary problem with the payment system. Please try again after some time' | |
+ # elsif status == 'ME' | |
+ # msg = 'There is a problem processing the payment.' | |
+ # error_message = resp.error_message | |
+ # elsif status == 'A' | |
+ # msg = 'You have aborted the payment' | |
+ # end | |
+ # %> | |
+ # <h2><%=msg%></h2> | |
+ # | |
+ | |
+ module AmazonPaynow | |
+ | |
+ mattr_accessor :test_url | |
+ self.test_url = 'https://authorize.payments-sandbox.amazon.com/pba/paypipeline' | |
+ | |
+ mattr_accessor :production_url | |
+ self.production_url = 'https://authorize.payments.amazon.com/pba/paypipeline' | |
+ | |
+ def self.service_url | |
+ mode = ActiveMerchant::Billing::Base.integration_mode | |
+ case mode | |
+ when :production | |
+ self.production_url | |
+ when :test | |
+ self.test_url | |
+ else | |
+ raise StandardError, "Integration mode set to an invalid value: #{mode}" | |
+ end | |
+ end | |
+ end | |
+ end | |
+ end | |
+end | |
Index: lib/active_merchant/billing/integrations.rb | |
=================================================================== | |
--- lib/active_merchant/billing/integrations.rb (revision 497) | |
+++ lib/active_merchant/billing/integrations.rb (working copy) | |
@@ -8,6 +8,7 @@ | |
require 'active_merchant/billing/integrations/gestpay' | |
require 'active_merchant/billing/integrations/two_checkout' | |
require 'active_merchant/billing/integrations/hi_trust' | |
+require 'active_merchant/billing/integrations/amazon_paynow' | |
# make the bogus gateway be classified correctly by the inflector | |
Inflector.inflections do |inflect| |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment