Completed
Push — master ( 330dad...b4ceb2 )
by
unknown
12s queued 10s
created

Response.parse_response()   A

Complexity

Conditions 3

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
dl 0
loc 22
rs 9.2
c 1
b 0
f 0
1
module AuthorizeNet::SIM
2
  # The SIM response class. Handles parsing the response from the gateway. Also
3
  # provides a few relay response helpers used to implement Direct Post Method.
4
  class Response < AuthorizeNet::KeyValueResponse
5
    # Our MD5 digest generator.
6
    @@digest = OpenSSL::Digest.new('md5')
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using a class variable like @@digest is generally not recommended; did you consider
using an class instance variable instead?
Loading history...
7
8
    include AuthorizeNet::SIM::Fields
9
10
    # Constructs a new response object from a +raw_response+. Provides utility methods
11
    # for validating the response as authentic, and for handling the Direct Post Method
12
    # relay response.
13
    #
14
    # +raw_response+:: The raw response, either a string in POST body or GET query string format, or a hash of key/value pairs.
15
    #
16
    # Typical usage:
17
    #   response = AuthorizeNet::SIM::Response("x_first_name=John&x_last_name=Doe")
18
    def initialize(raw_response)
19
      @raw_response = raw_response
20
      @custom_fields = {}
21
      @fields = {}
22
      parse_response(@raw_response)
23
    end
24
25
    # Returns True if the MD5 hash found in the response payload validates using
26
    # the supplied api_login and secret merchant_value (THIS IS NOT YOUR API KEY).
27 View Code Duplication
    def valid_md5?(api_login, merchant_value)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
28
      return false if @fields[:MD5_Hash].nil?
29
      @@digest.hexdigest("#{merchant_value}#{api_login}#{@fields[:trans_id]}#{@fields[:amount]}").casecmp(@fields[:MD5_Hash]).zero?
30
    end
31
32
    # Returns an HTML string that can be returned to the gateway during the Relay Response,
33
    # and will send the user on to URL you specify. Takes a hash of options, currently the
34
    # only option is :include, which can be True to include all fields returned in the response
35
    # as query string parameters, or it can be an array of fields to include.
36
    def direct_post_reply(url, options = {})
37
      url = direct_post_url(url, options[:include]) if options.key?(:include)
38
      js_url = url.tr("'", '\'')
39
      html_url = url.gsub('&', '&amp;').tr('"', "\"")
40
      html = <<-HTML
41
<html><head><script type="text/javascript" charset="utf-8">window.location='#{js_url}';</script><noscript><meta http-equiv="refresh" content="1;url=#{html_url}"></noscript></head><body></body></html>
42
HTML
43
    end
44
45
    # Returns an URL with the fields found in the response and specified in include_fields attached as
46
    # part of the URL's query string. If you pass true instead of an array of fields, all fields will be
47
    # attached.
48
    def direct_post_url(base_url, include_fields = true)
49
      url = base_url
50
      if include_fields
51
        fields = []
52
        case include_fields
53
        when TrueClass
54
          fields = FIELDS.collect do |k|
55
            k_str = k.to_s
56
            k_str[2..k_str.length].to_sym
57
          end
58
        when Array
59
          fields = include_fields
60
        else
61
          fields = include_fields.to_a
62
        end
63
        parsed_url = URI.parse(url)
64
        if parsed_url.query.nil?
65
          parsed_url.query = ''
66
        elsif !parsed_url.query.empty?
67
          parsed_url.query = parsed_url.query.chomp('&') + '&'
68
        end
69
        parsed_url.query += ((fields.select { |k| @fields.key?(k) }).collect { |k| to_param(k, @fields[k]) }).join('&')
70
        parsed_url.query.chomp('&')
71
        url = parsed_url.to_s
72
      end
73
      url
74
    end
75
76
    # Check to see if the response indicated success. Success is defined as a valid MD5 hash
77
    # and an response code of AuthorizeNet::Response::ResponseCode::APPROVED.
78
    def success?(api_login, merchant_value)
79
      valid_md5?(api_login, merchant_value) && approved?
80
    end
81
82
    # Returns the transaction's authorization code. This should be shown to the
83
    # end user.
84
    def authorization_code
85
      @fields[:auth_code]
86
    end
87
88
    # Returns the transaction's authorization id. You will need this for future void, refund
89
    # and prior authorization capture requests.
90
    def transaction_id
91
      @fields[:trans_id]
92
    end
93
94
    # Returns the customer id from the response.
95
    def customer_id
96
      @fields[:cust_id]
97
    end
98
99
    # Returns a response code (from AVSResponseCode) indicating the result of any Address Verification
100
    # Service checks.
101
    def avs_response
102
      @fields[:avs_code]
103
    end
104
105
    #:enddoc:
106
    protected
107
108
    # Internal helper to parse the raw response object. It handles both raw POST bodies and
109
    # hashes.
110
    def parse_response(raw_response)
111
      case raw_response
112
      when Hash
113
        raw_response.each do |k, v|
114
          k = k.to_sym
115
          if FIELDS.include?(k)
116
            @fields[to_internal_field(k)] = v # remove x_ from sym and stick in @fields
117
          else
118
            @custom_fields[k] = v
119
          end
120
        end
121
      when String
122
        # convert to hash and re-parse
123
        hash = CGI.parse(raw_response)
124
        hash.each do |k, v|
125
          hash[k] = v[0] if v.is_a?(Array) && v.length == 1
126
        end
127
        parse_response(hash)
128
      else
129
        parse_response(@raw_response.to_s)
130
      end
131
    end
132
  end
133
end
134