class PayWithAmazon::IpnHandler
Pay with Amazon Ipn Handler
This class authenticates an sns message sent from Amazon. It will validate the header, subject, and certificate. After validation there are many helper methods in place to extract information received from the ipn notification.
Constants
- COMMON_NAME
- SIGNABLE_KEYS
Attributes
body[R]
headers[R]
proxy_addr[RW]
proxy_pass[RW]
proxy_port[RW]
proxy_user[RW]
Public Class Methods
new( headers, body, proxy_addr: :ENV, proxy_port: nil, proxy_user: nil, proxy_pass: nil)
click to toggle source
@param headers [request.headers] @param body [request.body.read] @optional proxy_addr
[String] @optional proxy_port
[String] @optional proxy_user
[String] @optional proxy_pass
[String]
# File lib/pay_with_amazon/ipn_handler.rb, line 40 def initialize( headers, body, proxy_addr: :ENV, proxy_port: nil, proxy_user: nil, proxy_pass: nil) @body = body @raw = parse_from(@body) @headers = headers @proxy_addr = proxy_addr @proxy_port = proxy_port @proxy_user = proxy_user @proxy_pass = proxy_pass end
Public Instance Methods
authentic?()
click to toggle source
This method will authenticate the ipn message sent from Amazon. It will return true if everything is verified. It will raise an error message if verification fails.
# File lib/pay_with_amazon/ipn_handler.rb, line 60 def authentic? begin decoded_from_base64 = Base64.decode64(signature) validate_header validate_subject(get_certificate.subject) public_key = get_public_key_from(get_certificate) verify_public_key(public_key, decoded_from_base64, canonical_string) return true rescue IpnWasNotAuthenticError => e raise e.message end end
environment()
click to toggle source
# File lib/pay_with_amazon/ipn_handler.rb, line 118 def environment parse_from(@raw['Message'])["ReleaseEnvironment"] end
message()
click to toggle source
# File lib/pay_with_amazon/ipn_handler.rb, line 86 def message @raw['Message'] end
message_id()
click to toggle source
# File lib/pay_with_amazon/ipn_handler.rb, line 78 def message_id @raw['MessageId'] end
message_timestamp()
click to toggle source
# File lib/pay_with_amazon/ipn_handler.rb, line 130 def message_timestamp parse_from(@raw['Message'])["Timestamp"] end
notification_data()
click to toggle source
# File lib/pay_with_amazon/ipn_handler.rb, line 126 def notification_data parse_from(@raw['Message'])["NotificationData"] end
notification_type()
click to toggle source
# File lib/pay_with_amazon/ipn_handler.rb, line 110 def notification_type parse_from(@raw['Message'])["NotificationType"] end
parse_from(json)
click to toggle source
# File lib/pay_with_amazon/ipn_handler.rb, line 134 def parse_from(json) JSON.parse(json) end
seller_id()
click to toggle source
# File lib/pay_with_amazon/ipn_handler.rb, line 114 def seller_id parse_from(@raw['Message'])["SellerId"] end
signature()
click to toggle source
# File lib/pay_with_amazon/ipn_handler.rb, line 94 def signature @raw['Signature'] end
signature_version()
click to toggle source
# File lib/pay_with_amazon/ipn_handler.rb, line 98 def signature_version @raw['SignatureVersion'] end
signing_cert_url()
click to toggle source
# File lib/pay_with_amazon/ipn_handler.rb, line 102 def signing_cert_url @raw['SigningCertURL'] end
timestamp()
click to toggle source
# File lib/pay_with_amazon/ipn_handler.rb, line 90 def timestamp @raw['Timestamp'] end
topic_arn()
click to toggle source
# File lib/pay_with_amazon/ipn_handler.rb, line 82 def topic_arn @raw['TopicArn'] end
type()
click to toggle source
# File lib/pay_with_amazon/ipn_handler.rb, line 74 def type @raw['Type'] end
unsubscribe_url()
click to toggle source
# File lib/pay_with_amazon/ipn_handler.rb, line 106 def unsubscribe_url @raw['UnsubscribeURL'] end
version()
click to toggle source
# File lib/pay_with_amazon/ipn_handler.rb, line 122 def version parse_from(@raw['Message'])["Version"] end
Protected Instance Methods
canonical_string()
click to toggle source
# File lib/pay_with_amazon/ipn_handler.rb, line 149 def canonical_string text = '' SIGNABLE_KEYS.each do |key| value = @raw[key] next if value.nil? or value.empty? text << key << "\n" text << value << "\n" end text end
download_cert(url)
click to toggle source
# File lib/pay_with_amazon/ipn_handler.rb, line 160 def download_cert(url) uri = URI.parse(url) unless uri.scheme == 'https' && uri.host.match(/^sns\.[a-zA-Z0-9\-]{3,}\.amazonaws\.com(\.cn)?$/) && File.extname(uri.path) == '.pem' then msg = "Error - certificate is not hosted at AWS URL (https): #{url}" raise IpnWasNotAuthenticError, msg end tries = 0 begin resp = https_get(url) resp.body rescue => error tries += 1 retry if tries < 3 raise error end end
get_certificate()
click to toggle source
# File lib/pay_with_amazon/ipn_handler.rb, line 140 def get_certificate cert_pem = download_cert(signing_cert_url) OpenSSL::X509::Certificate.new(cert_pem) end
get_public_key_from(certificate)
click to toggle source
# File lib/pay_with_amazon/ipn_handler.rb, line 145 def get_public_key_from(certificate) OpenSSL::PKey::RSA.new(certificate.public_key) end
https_get(url)
click to toggle source
# File lib/pay_with_amazon/ipn_handler.rb, line 181 def https_get(url) uri = URI.parse(url) http = Net::HTTP.new(uri.host, uri.port, @proxy_addr, @proxy_port, @proxy_user, @proxy_pass) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_PEER http.start resp = http.request(Net::HTTP::Get.new(uri.request_uri)) http.finish resp end
validate_header()
click to toggle source
# File lib/pay_with_amazon/ipn_handler.rb, line 192 def validate_header unless @headers['x-amz-sns-message-type'] == 'Notification' then msg = "Error - Header does not contain x-amz-sns-message-type header" raise IpnWasNotAuthenticError, msg end end
validate_subject(certificate_subject)
click to toggle source
# File lib/pay_with_amazon/ipn_handler.rb, line 201 def validate_subject(certificate_subject) subject = certificate_subject.to_a unless subject[4][1] == COMMON_NAME then msg = "Error - Unable to verify certificate subject issued by Amazon" raise IpnWasNotAuthenticError, msg end end
verify_public_key(public_key, decoded_signature, signed_string)
click to toggle source
# File lib/pay_with_amazon/ipn_handler.rb, line 211 def verify_public_key(public_key, decoded_signature, signed_string) unless public_key.verify(OpenSSL::Digest::SHA1.new, decoded_signature, signed_string) then msg = "Error - Unable to verify public key with signature and signed string" raise IpnWasNotAuthenticError, msg end end