Skip to content

Commit

Permalink
Simplepay buttons are now signed
Browse files Browse the repository at this point in the history
  • Loading branch information
David Padilla committed Oct 11, 2012
1 parent 78a5f4e commit 15592c1
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 18 deletions.
5 changes: 5 additions & 0 deletions app/helpers/fundraiser/application_helper.rb
Expand Up @@ -7,5 +7,10 @@ def bootstrap_text_field(f, column, input_class = "")
def bootstrap_text_area(f, column, input_class = "")
render "bootstrap_text_area", :f => f, :column => column, :input_class => input_class
end

def simplepay_button(reward)
amount = "USD #{reward.minimum_pledge}"
Amazon::FPS::Widget.widget amount, reward.title, reward.id, fundraiser.rewards_url, fundraiser.ipns_url
end
end
end
19 changes: 1 addition & 18 deletions app/views/fundraiser/rewards/_reward.html.haml
Expand Up @@ -2,21 +2,4 @@
%h2= "#{reward.title} (#{number_to_currency(reward.minimum_pledge)})"
.description.lead= reward.description
.buy
:plain
<form action="#{Fundraiser::Settings.amazon_checkout_url}" method="post">
<input type="hidden" name="ipnUrl" value="#{fundraiser.ipns_url}" >
<input type="hidden" name="returnUrl" value="#{fundraiser.thanks_url}" >
<input type="hidden" name="abandonUrl" value="#{fundraiser.rewards_url}" >
<input type="hidden" name="processImmediate" value="0" >
<input type="hidden" name="accessKey" value="#{Fundraiser::Settings.amazon_access_key}" >
<input type="hidden" name="collectShippingAddress" value="0" >
<input type="hidden" name="isDonationWidget" value="0" >
<input type="hidden" name="amazonPaymentsAccountId" value="#{Fundraiser::Settings.amazon_payments_account_id}" >
<input type="hidden" name="referenceId" value="#{reward.id}" >
<input type="hidden" name="cobrandingStyle" value="logo" >
<input type="hidden" name="immediateReturn" value="1" >
<input type="hidden" name="amount" value="USD #{reward.minimum_pledge}" >
<input type="hidden" name="description" value="#{reward.title}" >
<input type="image" src="http://g-ecx.images-amazon.com/images/G/01/asp/beige_small_paynow_withmsg_whitebg.gif" border="0">
</form>

= simplepay_button(reward)
142 changes: 142 additions & 0 deletions lib/amazon/fps/signature_utils.rb
@@ -0,0 +1,142 @@
###############################################################################
# Copyright 2008-2010 Amazon Technologies, Inc
# Licensed under the Apache License, Version 2.0 (the "License");
#
# You may not use this file except in compliance with the License.
# You may obtain a copy of the License at: http://aws.amazon.com/apache2.0
# This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
##############################################################################
require 'base64'
require 'cgi'
require 'openssl'

module Amazon
module FPS

#
# Copyright:: Copyright (c) 2009 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# RFC 2104-compliant HMAC signature for request parameters
# Implements AWS Signature, as per following spec:
#
# If Signature Version is 1, it performs the following:
#
# Sorts all parameters (including SignatureVersion and excluding Signature,
# the value of which is being created), ignoring case.
#
# Iterate over the sorted list and append the parameter name (in original case)
# and then its value. It will not URL-encode the parameter values before
# constructing this string. There are no separators.
#
# If Signature Version is 2, string to sign is based on following:
#
# 1. The HTTP Request Method followed by an ASCII newline (%0A)
# 2. The HTTP Host header in the form of lowercase host, followed by an ASCII newline.
# 3. The URL encoded HTTP absolute path component of the URI
# (up to but not including the query string parameters);
# if this is empty use a forward '/'. This parameter is followed by an ASCII newline.
# 4. The concatenation of all query string components (names and values)
# as UTF-8 characters which are URL encoded as per RFC 3986
# (hex characters MUST be uppercase), sorted using lexicographic byte ordering.
# Parameter names are separated from their values by the '=' character
# (ASCII character 61), even if the value is empty.
# Pairs of parameter and values are separated by the '&' character (ASCII code 38).
#
class SignatureUtils

SIGNATURE_KEYNAME = "Signature"
SIGNATURE_METHOD_KEYNAME = "SignatureMethod"
SIGNATURE_VERSION_KEYNAME = "SignatureVersion"

HMAC_SHA256_ALGORITHM = "HmacSHA256"
HMAC_SHA1_ALGORITHM = "HmacSHA1"

def self.sign_parameters(args)
signature_version = args[:parameters][SIGNATURE_VERSION_KEYNAME]
string_to_sign = "";
algorithm = 'sha1';
if (signature_version == '1') then
string_to_sign = calculate_string_to_sign_v1(args)
elsif (signature_version == '2') then
algorithm = get_algorithm(args[:parameters][SIGNATURE_METHOD_KEYNAME])
string_to_sign = calculate_string_to_sign_v2(args)
else
raise "Invalid Signature Version specified"
end
return compute_signature(string_to_sign, args[:aws_secret_key], algorithm)
end

# Convert a string into URL encoded form.
def self.urlencode(plaintext)
CGI.escape(plaintext.to_s).gsub("+", "%20").gsub("%7E", "~")
end

private # All the methods below are private

def self.calculate_string_to_sign_v1(args)
parameters = args[:parameters]

# exclude any existing Signature parameter from the canonical string
sorted = (parameters.reject { |k, v| k == SIGNATURE_KEYNAME }).sort { |a,b| a[0].downcase <=> b[0].downcase }

canonical = ''
sorted.each do |v|
canonical << v[0]
canonical << v[1] unless(v[1].nil?)
end

return canonical
end

def self.calculate_string_to_sign_v2(args)
parameters = args[:parameters]

uri = args[:uri]
uri = "/" if uri.nil? or uri.empty?
uri = urlencode(uri).gsub("%2F", "/")

verb = args[:verb]
host = args[:host].downcase


# exclude any existing Signature parameter from the canonical string
sorted = (parameters.reject { |k, v| k == SIGNATURE_KEYNAME }).sort

canonical = "#{verb}\n#{host}\n#{uri}\n"
isFirst = true

sorted.each { |v|
if(isFirst) then
isFirst = false
else
canonical << '&'
end

canonical << urlencode(v[0])
unless(v[1].nil?) then
canonical << '='
canonical << urlencode(v[1])
end
}

return canonical
end

def self.get_algorithm(signature_method)
return 'sha256' if (signature_method == HMAC_SHA256_ALGORITHM);
return 'sha1'
end

def self.compute_signature(canonical, aws_secret_key, algorithm = 'sha1')
digest = OpenSSL::Digest::Digest.new(algorithm)
return Base64.encode64(OpenSSL::HMAC.digest(digest, aws_secret_key, canonical)).chomp
end

end

end
end


58 changes: 58 additions & 0 deletions lib/amazon/fps/widget.rb
@@ -0,0 +1,58 @@
module Amazon
module FPS
class Widget
@@app_name = "ASP"
@@http_method = "POST"
@@service_end_point = "https://authorize.payments-sandbox.amazon.com/pba/paypipeline"

@@access_key = "<Paste your access key id here>"
@@secret_key = "<Paste your secret key here>"

def self.get_paynow_widget_params(amount, description, reference_id, immediate_return,
return_url, abandon_url, process_immediate, ipn_url, cobranding_style, signature_version,
signatureMethod)
form_hidden_inputs = {}
form_hidden_inputs["accessKey"] = Fundraiser::Settings.aws_access_key
form_hidden_inputs["amount"] = amount
form_hidden_inputs["description"] = description

form_hidden_inputs["referenceId"] = reference_id unless reference_id.nil?
form_hidden_inputs["immediateReturn"] = immediate_return unless immediate_return.nil?
form_hidden_inputs["returnUrl"] = return_url unless return_url.nil?
form_hidden_inputs["abandonUrl"] = abandon_url unless abandon_url.nil?
form_hidden_inputs["processImmediate"] = process_immediate unless process_immediate.nil?
form_hidden_inputs["ipnUrl"] = ipn_url unless ipn_url.nil?
form_hidden_inputs["cobrandingStyle"] = cobranding_style unless cobranding_style.nil?
form_hidden_inputs[Amazon::FPS::SignatureUtils::SIGNATURE_VERSION_KEYNAME] = signature_version unless signature_version.nil?
form_hidden_inputs[Amazon::FPS::SignatureUtils::SIGNATURE_METHOD_KEYNAME] = signatureMethod unless signatureMethod.nil?

form_hidden_inputs
end

def self.get_paynow_widget_form(form_hidden_inputs)
form = "<form action=\"" + @@service_end_point + "\" method=\"" + @@http_method + "\">\n"
form += "<input type=\"image\" src=\"https://authorize.payments-sandbox.amazon.com/pba/images/payNowButton.png\" border=\"0\">\n"
form_hidden_inputs.each { |k,v|
form += "<input type=\"hidden\" name=\"" + k + "\" value=\"" + v + "\" >\n"
}
form += "</form>\n"
end

def self.widget(amount, title, reward_id, return_url, ipn_url)
uri = URI.parse(@@service_end_point)
params = get_paynow_widget_params(amount, title, reward_id.to_s, "1",
return_url, nil, "0",
ipn_url, "logo", "2", Amazon::FPS::SignatureUtils::HMAC_SHA256_ALGORITHM)

signature = Amazon::FPS::SignatureUtils.sign_parameters({:parameters => params,
:aws_secret_key => Fundraiser::Settings.aws_secret_key,
:host => uri.host,
:verb => @@http_method,
:uri => uri.path })
params[Amazon::FPS::SignatureUtils::SIGNATURE_KEYNAME] = signature
paynow_widget_form = get_paynow_widget_form(params)
paynow_widget_form.html_safe
end
end
end
end
2 changes: 2 additions & 0 deletions lib/fundraiser.rb
Expand Up @@ -2,7 +2,9 @@
require "devise"
require "haml-rails"
require "persistent_settings"
require "amazon/fps/signature_utils"
require "amazon/fps/signature_utils_for_outbound"
require "amazon/fps/widget"

#TODO: This line is here due a Heroku issue:
#
Expand Down

0 comments on commit 15592c1

Please sign in to comment.