class AmazonPay::IpnHandler
AmazonPay
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
- MSG_CERTIFICATE
- MSG_HEADER
- MSG_KEY
- 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, log_enabled: false, log_file_name: nil, log_level: :DEBUG )
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/amazon_pay/ipn_handler.rb, line 54 def initialize( headers, body, proxy_addr: :ENV, proxy_port: nil, proxy_user: nil, proxy_pass: nil, log_enabled: false, log_file_name: nil, log_level: :DEBUG ) @body = body @raw = parse_from(@body) @headers = headers @proxy_addr = proxy_addr @proxy_port = proxy_port @proxy_user = proxy_user @proxy_pass = proxy_pass @log_enabled = log_enabled @logger = AmazonPay::LogInitializer.new(log_file_name, log_level).create_logger if @log_enabled 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/amazon_pay/ipn_handler.rb, line 81 def authentic? decoded_from_base64 = Base64.decode64(signature) validate_header validate_subject(certificate.subject) public_key = public_key_from(certificate) verify_public_key(public_key, decoded_from_base64, canonical_string) return true rescue IpnWasNotAuthenticError => e raise e.message end
environment()
click to toggle source
# File lib/amazon_pay/ipn_handler.rb, line 137 def environment parse_from(@raw['Message'])['ReleaseEnvironment'] end
message()
click to toggle source
# File lib/amazon_pay/ipn_handler.rb, line 105 def message @raw['Message'] end
message_id()
click to toggle source
# File lib/amazon_pay/ipn_handler.rb, line 97 def message_id @raw['MessageId'] end
message_timestamp()
click to toggle source
# File lib/amazon_pay/ipn_handler.rb, line 149 def message_timestamp parse_from(@raw['Message'])['Timestamp'] end
notification_data()
click to toggle source
# File lib/amazon_pay/ipn_handler.rb, line 145 def notification_data parse_from(@raw['Message'])['NotificationData'] end
notification_type()
click to toggle source
# File lib/amazon_pay/ipn_handler.rb, line 129 def notification_type parse_from(@raw['Message'])['NotificationType'] end
parse_from(json)
click to toggle source
# File lib/amazon_pay/ipn_handler.rb, line 153 def parse_from(json) JSON.parse(json) end
seller_id()
click to toggle source
# File lib/amazon_pay/ipn_handler.rb, line 133 def seller_id parse_from(@raw['Message'])['SellerId'] end
signature()
click to toggle source
# File lib/amazon_pay/ipn_handler.rb, line 113 def signature @raw['Signature'] end
signature_version()
click to toggle source
# File lib/amazon_pay/ipn_handler.rb, line 117 def signature_version @raw['SignatureVersion'] end
signing_cert_url()
click to toggle source
# File lib/amazon_pay/ipn_handler.rb, line 121 def signing_cert_url @raw['SigningCertURL'] end
timestamp()
click to toggle source
# File lib/amazon_pay/ipn_handler.rb, line 109 def timestamp @raw['Timestamp'] end
topic_arn()
click to toggle source
# File lib/amazon_pay/ipn_handler.rb, line 101 def topic_arn @raw['TopicArn'] end
type()
click to toggle source
# File lib/amazon_pay/ipn_handler.rb, line 93 def type @raw['Type'] end
unsubscribe_url()
click to toggle source
# File lib/amazon_pay/ipn_handler.rb, line 125 def unsubscribe_url @raw['UnsubscribeURL'] end
version()
click to toggle source
# File lib/amazon_pay/ipn_handler.rb, line 141 def version parse_from(@raw['Message'])['Version'] end
Protected Instance Methods
canonical_string()
click to toggle source
# File lib/amazon_pay/ipn_handler.rb, line 168 def canonical_string text = '' SIGNABLE_KEYS.each do |key| value = @raw[key] next if value.nil? || value.empty? text << key << "\n" text << value << "\n" end text end
certificate()
click to toggle source
# File lib/amazon_pay/ipn_handler.rb, line 159 def certificate cert_pem = download_cert(signing_cert_url) OpenSSL::X509::Certificate.new(cert_pem) end
download_cert(url)
click to toggle source
# File lib/amazon_pay/ipn_handler.rb, line 179 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' msg = "Error - certificate is not hosted at AWS URL (https): #{url}" raise IpnWasNotAuthenticError, msg end tries = 0 begin resp = https_get(url) if @log_enabled data = AmazonPay::Sanitize.new(resp.body) @logger.debug(data.sanitize_response_data) end resp.body rescue StandardError => error tries += 1 retry if tries < 3 raise error end end
https_get(url)
click to toggle source
# File lib/amazon_pay/ipn_handler.rb, line 202 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
public_key_from(certificate)
click to toggle source
# File lib/amazon_pay/ipn_handler.rb, line 164 def public_key_from(certificate) OpenSSL::PKey::RSA.new(certificate.public_key) end
validate_header()
click to toggle source
# File lib/amazon_pay/ipn_handler.rb, line 213 def validate_header raise IpnWasNotAuthenticError, MSG_HEADER unless @headers['x-amz-sns-message-type'] == 'Notification' end
validate_subject(certificate_subject)
click to toggle source
# File lib/amazon_pay/ipn_handler.rb, line 217 def validate_subject(certificate_subject) subject = certificate_subject.to_a raise IpnWasNotAuthenticError, MSG_CERTIFICATE unless subject.rassoc(COMMON_NAME) end
verify_public_key(public_key, decoded_signature, signed_string)
click to toggle source
# File lib/amazon_pay/ipn_handler.rb, line 222 def verify_public_key(public_key, decoded_signature, signed_string) raise IpnWasNotAuthenticError, MSG_KEY unless public_key.verify(OpenSSL::Digest::SHA1.new, decoded_signature, signed_string) end