payhyper 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- OGEwMGYyMjgxOWVlOTQ4ZjVlYWYzODVmOTQwZDVhMzU2MDAyYjg4OQ==
5
- data.tar.gz: !binary |-
6
- NzVlNTE5OTdiZGY2MzYyM2Q5YjQ1YjQxMWM3MzUzYjkyY2YxNGE5Mw==
7
- !binary "U0hBNTEy":
8
- metadata.gz: !binary |-
9
- ZTc5ZjgzZDc3Y2IxZWI1N2I1MzAyOTczYjg3ZWYwYjAzYjE1MDY0NjliYzkw
10
- N2VjYWRmZWU5NDc0YWVjMTk1MzNjNDhmZDI0YWFkNjljYmMyYmNmOGI3ZDc5
11
- YjQxNTdmMTZlOTA1YTkyYWFkM2Q4NDUwN2U1MTQxMmM2MGUwZjQ=
12
- data.tar.gz: !binary |-
13
- NzJkYmRhMTZmODBmMzY5YjlmZDY0YWE5Mjk5Y2FlNGQxNmExODBjZDQxOTE1
14
- NmE0YjMzYTY3MTllMWIzYjhlY2I5NTRlNGFhYTEzNDhiMGM1ZWQwMWM5Yzlh
15
- MWIzNzk4N2Y3NGUxZThjOWFiY2E5ZjcxODIwN2FjODkzNzg0YWU=
2
+ SHA1:
3
+ metadata.gz: a0765918e4ad00fc1a1efb9c21030831110d50fe
4
+ data.tar.gz: 5f424f23813782dc5cdc916bd1c7ad03dbc09574
5
+ SHA512:
6
+ metadata.gz: 69080fb75e1f6cfb4eaa815100524d14f2c40c64bff2fc0eec990e1c92126119921259793f3d3b029a280e603f787d5f7cd8f6e146fea156afba1cb7481d3a4a
7
+ data.tar.gz: ee023675b08c6964f5d7644d5bc17516f67dde123760c98d80c9b981c10957c5c80e830399a9d265d94bf0ffcc55e6ad68e804c5f66a8bf804583bf21e97d171
@@ -1,3 +1,3 @@
1
1
  module PayHyper
2
- VERSION = '0.2.3'
2
+ VERSION = '0.3.0'
3
3
  end
data/lib/payhyper.rb CHANGED
@@ -35,14 +35,32 @@ module PayHyper
35
35
  @base_url = BASE_URLS[mode]
36
36
  end
37
37
 
38
- def self.raise_if_not_setup!
39
- raise PayHyperError, "Must call setup() first" if @access_key_id.nil? || @access_key_secret.nil? || @base_url.nil?
38
+ def self.at_door!(name, phone, email, country, amount, currency, address = nil, invoice = nil, tag = nil) # TODO: There is a lot of overlap with "in_store!", refactor.
39
+ raise_if_not_setup!
40
+ # == Clean-up fields ==
41
+ currency = currency.upcase if currency && currency.is_a?(String)
42
+ country = country.upcase if country && country.is_a?(String)
43
+ if phone && phone.is_a?(String)
44
+ phone = phone.gsub(/[^0-9]/, "") # Remove non-numeric characters.
45
+ phone = phone.gsub(/^0*/, "") # Remove leading zeros.
46
+ if country && COUNTRY_CODES[country] && !phone.start_with?(COUNTRY_CODES[country])
47
+ phone = COUNTRY_CODES[country] + phone # Add country code.
48
+ end
49
+ end
50
+ # == Validate ==
51
+ raise ValidationError, "Country specified is incorrect or not supported." unless COUNTRIES.include?(country)
52
+ raise ValidationError, "Incorrect amount, must be positive." if amount.to_i <= 0
53
+ raise ValidationError, "Currency is incorrect or not supported." unless CURRENCIES.include?(currency)
54
+ raise ValidationError, "Incorrect phone, or not in a supported country." if phone.nil? || !phone.match(/\A962[0-9]{8,9}\z/)
55
+ raise ValidationError, "Invalid email." if email.nil? || !email.match(/\A[^@\s]+@([^@\s]+\.)+[^@\s]+\z/)
56
+ raise ValidationError, "Name is mandatory." if name.nil? || name.strip.length == 0
57
+ # == Do the request ==
58
+ make_call("/v1/at-door", { :name => name, :phone => phone, :email => email, :country => country, :amount => amount, :currency => currency, :address => address, :invoice => invoice, :tag => tag })
40
59
  end
41
60
 
42
- def self.create_order!(service, country, amount, currency, phone, email, tag = nil)
61
+ def self.in_store!(name, phone, email, country, amount, currency, tag = nil) # TODO: There is a lot of overlap with "at_door!", refactor.
43
62
  raise_if_not_setup!
44
63
  # == Clean-up fields ==
45
- service = service.to_s.gsub("_", "-") if [:at_door, :in_store].include?(service)
46
64
  currency = currency.upcase if currency && currency.is_a?(String)
47
65
  country = country.upcase if country && country.is_a?(String)
48
66
  if phone && phone.is_a?(String)
@@ -53,20 +71,61 @@ module PayHyper
53
71
  end
54
72
  end
55
73
  # == Validate ==
56
- raise ValidationError, "Incorrect service specified." unless ["at-door", "in-store"].include?(service)
57
74
  raise ValidationError, "Country specified is incorrect or not supported." unless COUNTRIES.include?(country)
58
75
  raise ValidationError, "Incorrect amount, must be positive." if amount.to_i <= 0
59
76
  raise ValidationError, "Currency is incorrect or not supported." unless CURRENCIES.include?(currency)
60
77
  raise ValidationError, "Incorrect phone, or not in a supported country." if phone.nil? || !phone.match(/\A962[0-9]{8,9}\z/)
61
78
  raise ValidationError, "Invalid email." if email.nil? || !email.match(/\A[^@\s]+@([^@\s]+\.)+[^@\s]+\z/)
79
+ raise ValidationError, "Name is mandatory." if name.nil? || name.strip.length == 0
62
80
  # == Do the request ==
63
- body = JSON.dump({ :service => service, :country => country, :amount => amount, :currency => currency, :phone => phone, :email => email, :tag => tag })
81
+ make_call("/v1/in-store", { :name => name, :phone => phone, :email => email, :country => country, :amount => amount, :currency => currency, :tag => tag })
82
+ end
83
+
84
+ def self.parse_notification(request)
85
+ raise_if_not_setup!
86
+ if check_ani_authenticity(request)
87
+ request.env["rack.input"].rewind # In case someone forgot to rewind the input.
88
+ body = request.env["rack.input"].read
89
+ request.env["rack.input"].rewind # Be nice to others.
90
+ JSON.load(body)
91
+ else
92
+ nil
93
+ end
94
+ end
95
+
96
+ class PayHyperError < StandardError; end
97
+ class ValidationError < PayHyperError; end
98
+ class CommunicationError < PayHyperError; end
99
+
100
+ private
101
+
102
+ def self.raise_if_not_setup!
103
+ raise PayHyperError, "Must call setup() first" if @access_key_id.nil? || @access_key_secret.nil? || @base_url.nil?
104
+ end
105
+
106
+ def self.check_ani_authenticity(request)
107
+ authorization = request.env["HTTP_AUTHORIZATION"]
108
+ return false unless authorization
109
+ matches = authorization.match(/\A(.*) (.*):(.*)/)
110
+ return false unless matches
111
+ method, access_key_id, input_signature = matches.captures
112
+ return false unless method == "Hyper" && access_key_id && input_signature && access_key_id == @access_key_id
113
+ request.env["rack.input"].rewind # In case someone forgot to rewind the input.
114
+ body = request.env["rack.input"].read
115
+ request.env["rack.input"].rewind # Be nice to others.
116
+ canonical_request_representation = [request.env["REQUEST_METHOD"], request.env["HTTP_HOST"], request.env["PATH_INFO"], request.env["CONTENT_TYPE"], body].join("\n") # TODO: security bug if the webserver doesn't check host headers.
117
+ correct_signature = Security.sign(@access_key_secret, canonical_request_representation)
118
+ return Security.secure_compare(correct_signature, input_signature)
119
+ end
120
+
121
+ def self.make_call(endpoint, body)
122
+ body = JSON.dump(body)
64
123
  failure = "Unknown error"
65
124
  begin
66
- uri = URI.parse(@base_url + "/v1/orders")
125
+ uri = URI.parse(@base_url + endpoint)
67
126
  content_type = "application/json; charset=utf-8"
68
127
  signature = Security.sign(@access_key_secret, ["POST", uri.port == uri.default_port ? uri.host : "#{uri.host}:#{uri.port}", uri.request_uri, content_type, body.encode("UTF-8")].join("\n")) # Would be great if this could read directly from the request's headers.
69
- res = RestClient.post(@base_url + "/v1/orders", body, :content_type => content_type, :authorization => "Hyper #{@access_key_id}:#{signature}")
128
+ res = RestClient.post(@base_url + endpoint, body, :content_type => content_type, :authorization => "Hyper #{@access_key_id}:#{signature}")
70
129
  failure = false
71
130
  rescue RestClient::Exception => e # HTTP status codes not in 200-207, 301-303 and 307 result in a RestClient::Exception.
72
131
  if e.http_code && e.http_code.to_i == 422 && e.http_body
@@ -96,35 +155,4 @@ module PayHyper
96
155
  end
97
156
  end
98
157
 
99
- def self.check_ani_authenticity(request)
100
- raise_if_not_setup!
101
- authorization = request.env["HTTP_AUTHORIZATION"]
102
- return false unless authorization
103
- matches = authorization.match(/\A(.*) (.*):(.*)/)
104
- return false unless matches
105
- method, access_key_id, input_signature = matches.captures
106
- return false unless method == "Hyper" && access_key_id && input_signature && access_key_id == @access_key_id
107
- request.env["rack.input"].rewind # In case someone forgot to rewind the input.
108
- body = request.env["rack.input"].read
109
- request.env["rack.input"].rewind # Be nice to others.
110
- canonical_request_representation = [request.env["REQUEST_METHOD"], request.env["HTTP_HOST"], request.env["PATH_INFO"], request.env["CONTENT_TYPE"], body].join("\n") # TODO: security bug if the webserver doesn't check host headers.
111
- correct_signature = Security.sign(@access_key_secret, canonical_request_representation)
112
- return Security.secure_compare(correct_signature, input_signature)
113
- end
114
-
115
- def self.parse_notification(request)
116
- raise_if_not_setup!
117
- if check_ani_authenticity(request)
118
- request.env["rack.input"].rewind # In case someone forgot to rewind the input.
119
- body = request.env["rack.input"].read
120
- request.env["rack.input"].rewind # Be nice to others.
121
- JSON.load(body)
122
- else
123
- nil
124
- end
125
- end
126
-
127
- class PayHyperError < StandardError; end
128
- class ValidationError < PayHyperError; end
129
- class CommunicationError < PayHyperError; end
130
158
  end
data/payhyper.gemspec CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |s|
6
6
 
7
7
  s.name = 'payhyper'
8
8
  s.version = PayHyper::VERSION
9
- s.date = '2013-08-07'
9
+ s.date = '2014-04-26'
10
10
  s.summary = 'The Ruby bindings of the Hyper API'
11
11
  s.description = 'Hyper is an API for cash collection. See http://payhyper.com for more.'
12
12
  s.authors = ["Sinan Taifour"]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: payhyper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sinan Taifour
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-08-07 00:00:00.000000000 Z
11
+ date: 2014-04-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rest-client
@@ -28,7 +28,7 @@ dependencies:
28
28
  name: multi_json
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ! '>='
31
+ - - '>='
32
32
  - !ruby/object:Gem::Version
33
33
  version: 1.0.4
34
34
  - - <
@@ -38,7 +38,7 @@ dependencies:
38
38
  prerelease: false
39
39
  version_requirements: !ruby/object:Gem::Requirement
40
40
  requirements:
41
- - - ! '>='
41
+ - - '>='
42
42
  - !ruby/object:Gem::Version
43
43
  version: 1.0.4
44
44
  - - <
@@ -65,17 +65,17 @@ require_paths:
65
65
  - lib
66
66
  required_ruby_version: !ruby/object:Gem::Requirement
67
67
  requirements:
68
- - - ! '>='
68
+ - - '>='
69
69
  - !ruby/object:Gem::Version
70
70
  version: '0'
71
71
  required_rubygems_version: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ! '>='
73
+ - - '>='
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
76
  requirements: []
77
77
  rubyforge_project:
78
- rubygems_version: 2.0.7
78
+ rubygems_version: 2.2.2
79
79
  signing_key:
80
80
  specification_version: 4
81
81
  summary: The Ruby bindings of the Hyper API