paysimple-ruby 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a26074b9683b716e2a6db729e0859d11d32179f8
4
+ data.tar.gz: e94335c05375789b558572bbeba8570fccc2effc
5
+ SHA512:
6
+ metadata.gz: 0ce84fc7b7c3124b35b2d83f7608f6ebec246cf54401b9cf4878f3f46b1f21ea4d1a7a3ef955471e07715e6bbd1777c16f3687c09ca5e09c4b3533bb6f1e0256
7
+ data.tar.gz: 6495fbca110a86add9be0c8bfcd0294391459afd15d966d80c6b55169f6609524c72633e963c47718b3c7ed4816aae96f53b540d3993706f8f87815a4e4883f8
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .idea/
11
+ .env
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Konstantin Lebedev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
File without changes
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "paysimple"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,152 @@
1
+ require 'time'
2
+ require 'digest'
3
+ require 'base64'
4
+
5
+ require 'rest-client'
6
+ require 'json'
7
+
8
+ # Common
9
+ require 'paysimple/version'
10
+ require 'paysimple/endpoint'
11
+ require 'paysimple/util'
12
+
13
+ # Resources
14
+ require 'paysimple/resources/customer'
15
+ require 'paysimple/resources/credit_card'
16
+ require 'paysimple/resources/ach'
17
+ require 'paysimple/resources/payment'
18
+
19
+ # Errors
20
+ require 'paysimple/errors/paysimple_error'
21
+ require 'paysimple/errors/api_connection_error'
22
+ require 'paysimple/errors/api_error'
23
+ require 'paysimple/errors/invalid_request_error'
24
+ require 'paysimple/errors/authentication_error'
25
+
26
+ module Paysimple
27
+
28
+ @api_endpoint = Endpoint::PRODUCTION
29
+
30
+ class << self
31
+ attr_accessor :api_key, :api_user, :api_endpoint
32
+ end
33
+
34
+ def self.request(method, url, params={})
35
+
36
+ raise AuthenticationError.new('API key is not provided.') unless @api_key
37
+ raise AuthenticationError.new('API user is not provided.') unless @api_user
38
+
39
+ url = api_url(url)
40
+ case method
41
+ when :get, :head
42
+ url += "#{URI.parse(url).query ? '&' : '?'}#{uri_encode(params)}" if params && params.any?
43
+ payload = nil
44
+ when :delete
45
+ payload = nil
46
+ else
47
+ payload = Util.camelize_and_symbolize_keys(params).to_json
48
+ end
49
+
50
+ request_opts = { headers: request_headers, method: method, open_timeout: 30,
51
+ payload: payload, url: url, timeout: 80 }
52
+
53
+ begin
54
+ response = execute_request(request_opts)
55
+ rescue SocketError => e
56
+ handle_restclient_error(e)
57
+ rescue RestClient::ExceptionWithResponse => e
58
+ if rcode = e.http_code and rbody = e.http_body
59
+ handle_api_error(rcode, rbody)
60
+ else
61
+ handle_restclient_error(e)
62
+ end
63
+ rescue RestClient::Exception, Errno::ECONNREFUSED => e
64
+ handle_restclient_error(e)
65
+ end
66
+
67
+ parse(response)
68
+ end
69
+
70
+ def self.api_url(url='')
71
+ @api_endpoint + '/v4' + url
72
+ end
73
+
74
+ def self.execute_request(opts)
75
+ RestClient::Request.execute(opts)
76
+ end
77
+
78
+ def self.request_headers
79
+ {
80
+ authorization: authorization_header,
81
+ content_type: :json,
82
+ accept: :json
83
+ }
84
+ end
85
+
86
+ def self.uri_encode(params)
87
+ Util.flatten_params(params).map { |k,v| "#{k}=#{Util.url_encode(v)}" }.join('&')
88
+ end
89
+
90
+ def self.parse(response)
91
+ if response.body.empty?
92
+ {}
93
+ else
94
+ response = JSON.parse(response.body)
95
+ Util.underscore_and_symbolize_names(response)[:response]
96
+ end
97
+ rescue JSON::ParserError
98
+ raise general_api_error(response.code, response.body)
99
+ end
100
+
101
+ def self.general_api_error(rcode, rbody)
102
+ APIError.new("Invalid response object from API: #{rbody.inspect} " +
103
+ "(HTTP response code was #{rcode})", rcode, rbody)
104
+ end
105
+
106
+ def self.authorization_header
107
+ utc_timestamp = Time.now.getutc.iso8601.encode(Encoding::UTF_8)
108
+ secret_key = @api_key.encode(Encoding::UTF_8)
109
+ hash = OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha256'), secret_key, utc_timestamp)
110
+ signature = Base64.encode64(hash)
111
+
112
+ "PSSERVER accessid=#{@api_user}; timestamp=#{utc_timestamp}; signature=#{signature}"
113
+ end
114
+
115
+ def self.handle_api_error(rcode, rbody)
116
+ begin
117
+ error_obj = rbody.empty? ? {} : Util.underscore_and_symbolize_names(JSON.parse(rbody))
118
+ rescue JSON::ParserError
119
+ raise general_api_error(rcode, rbody)
120
+ end
121
+
122
+ case rcode
123
+ when 400, 404
124
+ error = error_obj[:meta][:errors][:error_messages].collect { |e| e[:message]}
125
+ raise InvalidRequestError.new(error, nil, rcode, rbody, error_obj)
126
+ when 401
127
+ raise AuthenticationError.new(error, rcode, rbody, error_obj)
128
+ else
129
+ raise APIError.new(error, rcode, rbody, error_obj)
130
+ end
131
+ end
132
+
133
+ def self.handle_restclient_error(e, api_base_url=nil)
134
+ connection_message = 'Please check your internet connection and try again.'
135
+
136
+ case e
137
+ when RestClient::RequestTimeout
138
+ message = "Could not connect to Paysimple (#{@api_endpoint}). #{connection_message}"
139
+ when RestClient::ServerBrokeConnection
140
+ message = "The connection to the server (#{@api_endpoint}) broke before the " \
141
+ "request completed. #{connection_message}"
142
+ when SocketError
143
+ message = 'Unexpected error communicating when trying to connect to Paysimple. ' \
144
+ 'You may be seeing this message because your DNS is not working. '
145
+ else
146
+ message = 'Unexpected error communicating with Paysimple. '
147
+ end
148
+
149
+ raise APIConnectionError.new(message + "\n\n(Network error: #{e.message})")
150
+ end
151
+
152
+ end
@@ -0,0 +1,8 @@
1
+ module Paysimple
2
+
3
+ class Endpoint
4
+ PRODUCTION = 'https://api.paysimple.com'
5
+ SANDBOX = 'https://sandbox-api.paysimple.com'
6
+ end
7
+
8
+ end
@@ -0,0 +1,6 @@
1
+ module Paysimple
2
+
3
+ class APIConnectionError < PaysimpleError
4
+ end
5
+
6
+ end
@@ -0,0 +1,5 @@
1
+ module Paysimple
2
+ class APIError < PaysimpleError
3
+
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ module Paysimple
2
+
3
+ class AuthenticationError < PaysimpleError
4
+ end
5
+
6
+ end
@@ -0,0 +1,12 @@
1
+ module Paysimple
2
+
3
+ class InvalidRequestError < PaysimpleError
4
+ attr_accessor :param
5
+
6
+ def initialize(message, param, http_status=nil, http_body=nil, json_body=nil)
7
+ super(message, http_status, http_body, json_body)
8
+ @param = param
9
+ end
10
+ end
11
+
12
+ end
@@ -0,0 +1,22 @@
1
+ module Paysimple
2
+
3
+ class PaysimpleError < StandardError
4
+ attr_reader :message
5
+ attr_reader :http_status
6
+ attr_reader :http_body
7
+ attr_reader :json_body
8
+
9
+ def initialize(message=nil, http_status=nil, http_body=nil, json_body=nil)
10
+ @message = message
11
+ @http_status = http_status
12
+ @http_body = http_body
13
+ @json_body = json_body
14
+ end
15
+
16
+ def to_s
17
+ status_string = @http_status.nil? ? "" : "(Status #{@http_status}) "
18
+ "#{status_string}#{@message}"
19
+ end
20
+ end
21
+
22
+ end
@@ -0,0 +1,23 @@
1
+ module Paysimple
2
+ class ACH
3
+
4
+ def self.create(opts)
5
+ Paysimple.request(:post, url, opts)
6
+ end
7
+
8
+ def self.update(opts)
9
+ Paysimple.request(:put, url, opts)
10
+ end
11
+
12
+ def self.delete(id)
13
+ Paysimple.request(:delete, "#{url}/#{id}")
14
+ end
15
+
16
+ protected
17
+
18
+ def self.url
19
+ '/account/ach'
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ module Paysimple
2
+ class CreditCard
3
+
4
+ def self.create(opts)
5
+ Paysimple.request(:post, url, opts)
6
+ end
7
+
8
+ def self.update(opts)
9
+ Paysimple.request(:put, url, opts)
10
+ end
11
+
12
+ def self.delete(id)
13
+ Paysimple.request(:delete, "#{url}/#{id}")
14
+ end
15
+
16
+ protected
17
+
18
+ def self.url
19
+ '/account/creditcard'
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ module Paysimple
2
+ class Customer
3
+
4
+ def self.create(opts)
5
+ Paysimple.request(:post, url, opts)
6
+ end
7
+
8
+ def self.update(opts)
9
+ Paysimple.request(:put, url, opts)
10
+ end
11
+
12
+ def self.delete(id)
13
+ Paysimple.request(:delete, "#{url}/#{id}")
14
+ end
15
+
16
+ protected
17
+
18
+ def self.url
19
+ '/customer'
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ module Paysimple
2
+ class Payment
3
+
4
+ def self.create(opts)
5
+ Paysimple.request(:post, url, opts)
6
+ end
7
+
8
+ def self.void(id)
9
+ Paysimple.request(:put, "#{url}/#{id}/void")
10
+ end
11
+
12
+ def self.get(id)
13
+ Paysimple.request(:get, "#{url}/#{id}")
14
+ end
15
+
16
+ protected
17
+
18
+ def self.url
19
+ '/payment'
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,85 @@
1
+ module Paysimple
2
+ module Util
3
+
4
+ # class ::Hash
5
+ # def method_missing(name)
6
+ # return self[name] if key? name
7
+ # self.each { |k,v| return v if k.to_s.to_sym == name }
8
+ # super.method_missing name
9
+ # end
10
+ # end
11
+
12
+ def self.underscore(str)
13
+ str.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
14
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
15
+ tr('-', '_').
16
+ downcase
17
+ end
18
+
19
+ def self.camelize(str)
20
+ str.split('_').collect(&:capitalize).join
21
+ end
22
+
23
+ def self.camelize_and_symbolize_keys(object)
24
+ transform_names object do |key|
25
+ camelize(key.to_s).to_sym
26
+ end
27
+ end
28
+
29
+ def self.underscore_and_symbolize_names(object)
30
+ transform_names object do |key|
31
+ underscore(key).to_sym
32
+ end
33
+ end
34
+
35
+ def self.transform_names(object, &block)
36
+ case object
37
+ when Hash
38
+ new_hash = {}
39
+ object.each do |key, value|
40
+ key = block.call(key) || key
41
+ new_hash[key] = transform_names(value, &block)
42
+ end
43
+ new_hash
44
+ when Array
45
+ object.map { |value| transform_names(value, &block) }
46
+ else
47
+ object
48
+ end
49
+ end
50
+
51
+ def self.flatten_params(params, parent_key=nil)
52
+ result = []
53
+ params.each do |key, value|
54
+ calculated_key = parent_key ? "#{parent_key}[#{url_encode(key)}]" : url_encode(key)
55
+ if value.is_a?(Hash)
56
+ result += flatten_params(value, calculated_key)
57
+ elsif value.is_a?(Array)
58
+ result += flatten_params_array(value, calculated_key)
59
+ else
60
+ result << [calculated_key, value]
61
+ end
62
+ end
63
+ result
64
+ end
65
+
66
+ def self.url_encode(key)
67
+ URI.escape(key.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
68
+ end
69
+
70
+ def self.flatten_params_array(value, calculated_key)
71
+ result = []
72
+ value.each do |elem|
73
+ if elem.is_a?(Hash)
74
+ result += flatten_params(elem, calculated_key)
75
+ elsif elem.is_a?(Array)
76
+ result += flatten_params_array(elem, calculated_key)
77
+ else
78
+ result << ["#{calculated_key}[]", elem]
79
+ end
80
+ end
81
+ result
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,3 @@
1
+ module Paysimple
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,27 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'paysimple/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'paysimple-ruby'
7
+ spec.version = Paysimple::VERSION
8
+ spec.authors = ['Konstantin Lebedev']
9
+ spec.email = ['koss.lebedev@gmail.com']
10
+
11
+ spec.summary = 'Ruby bindings for Paysimple API'
12
+ spec.description = ''
13
+ spec.homepage = 'http://developer.paysimple.com'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = 'exe'
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.8'
22
+ spec.add_development_dependency 'rake', '~> 10.0'
23
+ spec.add_development_dependency 'rspec'
24
+
25
+ spec.add_dependency('rest-client', '~> 1.4')
26
+ spec.add_dependency('json', '~> 1.8.1')
27
+ end
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: paysimple-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Konstantin Lebedev
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-05-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.8'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rest-client
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.4'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '1.4'
69
+ - !ruby/object:Gem::Dependency
70
+ name: json
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 1.8.1
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 1.8.1
83
+ description: ''
84
+ email:
85
+ - koss.lebedev@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - .gitignore
91
+ - .rspec
92
+ - .travis.yml
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - bin/console
98
+ - bin/setup
99
+ - lib/paysimple.rb
100
+ - lib/paysimple/endpoint.rb
101
+ - lib/paysimple/errors/api_connection_error.rb
102
+ - lib/paysimple/errors/api_error.rb
103
+ - lib/paysimple/errors/authentication_error.rb
104
+ - lib/paysimple/errors/invalid_request_error.rb
105
+ - lib/paysimple/errors/paysimple_error.rb
106
+ - lib/paysimple/resources/ach.rb
107
+ - lib/paysimple/resources/credit_card.rb
108
+ - lib/paysimple/resources/customer.rb
109
+ - lib/paysimple/resources/payment.rb
110
+ - lib/paysimple/util.rb
111
+ - lib/paysimple/version.rb
112
+ - paysimple.gemspec
113
+ homepage: http://developer.paysimple.com
114
+ licenses:
115
+ - MIT
116
+ metadata: {}
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - '>='
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 2.0.14
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: Ruby bindings for Paysimple API
137
+ test_files: []