paysafe 0.6.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d342047f484e12247acaa0f8763e0cd252228e83
4
- data.tar.gz: b70bf62d28be40941f816525715c6d65fc5a761e
3
+ metadata.gz: d6bbfa2b88d67e16221b48a7fdc9e2048f3e83e2
4
+ data.tar.gz: f4e8670f77a2ca5a406b5a52794a446f87f2dabe
5
5
  SHA512:
6
- metadata.gz: 3fae18580d0b5a010b03a5d0f0c804aac8ebc3b76d1119155f99c01d9d90a020e2ce1b30390b18b1d944c68d155a796b8b46335b37a8c4b16428859eaac40b4a
7
- data.tar.gz: bd5ee3aab0093d1521b7f97ab7df8cea404b60f3dc577369f9e7de32a4bea0e1bedf3af13a03e6d63901cdaa54bfe1a0a610781439b186d11e5fb3d447d9c92e
6
+ metadata.gz: dd143b165e5467ae27d084c9011ce710d80dd40eff19fbd2896a4514b6a3d1dff4ccefea27ee533aa05b2c37825970a3644bd7cd14111ec313ba4192fdd725f6
7
+ data.tar.gz: f577618983eaa4dc510f5fa4def95780be2c85c8ec791d35ea8c300018acbc56780f3f58a7fbf6e5c0558e9ce6d8114cc5fc89c7c8b96d34b69ce77b2205eeb0
data/.env.sample CHANGED
@@ -1,3 +1,8 @@
1
1
  PAYSAFE_ACCOUNT_NUMBER="YOUR_ACCOUNT_NUMBER"
2
2
  PAYSAFE_API_KEY="YOUR_API_KEY"
3
3
  PAYSAFE_API_SECRET="YOUR_API_SECRET"
4
+
5
+ # This is only needed if you want to generate your own Single
6
+ # Use Tokens which utilize a different API key and secret.
7
+ PAYSAFE_SUT_API_KEY="YOUR_SINGLE_USE_TOKEN_API_KEY"
8
+ PAYSAFE_SUT_API_SECRET="YOUR_SINGLE_USE_TOKEN_API_SECRET"
data/.travis.yml CHANGED
@@ -1,13 +1,13 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.1.5
4
- - 2.2.3
5
- before_install: gem install bundler -v 1.10.6
3
+ - 2.3.4
4
+ - 2.4.1
5
+ before_install: gem install bundler
6
6
  cache: bundler
7
7
  sudo: false
8
8
  fast_finish: true
9
9
  env:
10
- - SKIP_INTEGRATION_TESTS=true
10
+ - SKIP_INTEGRATION=true
11
11
  notifications:
12
12
  email:
13
13
  on_success: always
data/CHANGELOG.md ADDED
@@ -0,0 +1,28 @@
1
+ Changelog
2
+ =========
3
+
4
+ ## 0.9.0 (2017-04-19)
5
+
6
+ This is a major update with breaking changes so moving the version up quite a bit since I'd also like to move to a 1.0 release soon after.
7
+
8
+ * Requires Ruby 2.3 and up.
9
+ * All API methods now accept params in snake_case and are converted to camelCase.
10
+ * Responses are now typed with classes using snake_case properties (converted from camelCase) handling nested complex objects and lazily loaded. No more hashes.
11
+ * Field names match Paysafe API docs except when making requests and working with responses, its all in snake_case.
12
+ * Renamed config `timeout_options` renamed to `timeouts`.
13
+ * Removed default value for `timeouts` config.
14
+ * Added new methods to work with single use tokens:
15
+ * `create_single_use_token` (requires different API key and secret)
16
+ * `create_verification_from_token`
17
+ * `create_profile_from_token`
18
+ * `create_card_from_token`
19
+ * Updated `bin/setup` script.
20
+ * Updated API base domain to paysafe.com.
21
+
22
+ ## 0.6.2 (2016-04-25)
23
+
24
+ * Upgrade http gem to v2.0
25
+
26
+ ## 0.6.1 (2016-03-02)
27
+
28
+ * Add `update_profile` method to client
data/README.md CHANGED
@@ -3,54 +3,88 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/paysafe.svg)][gem]
4
4
  [![Build Status](https://travis-ci.org/javierjulio/paysafe.svg?branch=master)][travis]
5
5
 
6
- [gem]: https://rubygems.org/gems/paysafe
7
- [travis]: https://travis-ci.org/javierjulio/paysafe
8
-
9
- A tested Ruby interface to the Paysafe REST API (formerly Optimal Payments). Note: requires Ruby 2.1 and up. Not all API actions are supported yet.
6
+ A well tested Ruby interface to the [Paysafe REST API](paysafe_api_reference) (formerly Optimal Payments). Requires Ruby 2.3 and up. Not all API actions are supported yet. Since the Paysafe API uses camelCase, this gem will handle converting to and from snake_case for you.
10
7
 
11
8
  ## Installation
12
9
 
13
- Add this line to your application's Gemfile:
10
+ Add the following to your application's Gemfile:
14
11
 
15
12
  ```ruby
16
13
  gem 'paysafe'
17
14
  ```
18
15
 
19
- And then execute:
16
+ Or install directly with `gem install paysafe`.
20
17
 
21
- $ bundle
18
+ To try out the gem, just follow the Development section instructions as the built in setup script will direct you on how to provide the necessary API info.
22
19
 
23
- Or install it yourself as:
20
+ ## Usage
24
21
 
25
- $ gem install paysafe
22
+ ### Client Configuration
26
23
 
27
- To try out the gem and experiment, you're better off following the development instructions.
24
+ Create and configure a client with your API authentication.
28
25
 
29
- ## Usage
26
+ ```ruby
27
+ client = Paysafe::REST::Client.new do |config|
28
+ config.account_number = '1234567890'
29
+ config.api_key = 'api_key'
30
+ config.api_secret = 'api_secret'
31
+ # Enable production requests
32
+ # config.test_mode = false
33
+ # Provide explicit timeouts
34
+ # config.timeouts = { connect: 2, read: 5, write: 10 }
35
+ end
36
+ ```
30
37
 
31
- TODO: Write usage instructions here
38
+ ### Making Requests
32
39
 
33
- ## Development
40
+ Make an API request with a payload in the structure documented by the [Paysafe REST API](paysafe_api_reference) but using snake_case. The request payload will be converted to camelCase for you.
34
41
 
35
- 1. Clone the repo: `git clone https://github.com/javierjulio/paysafe.git`
36
- 2. Use Ruby 2.1 and up. If you need to install use [rbenv](https://github.com/sstephenson/rbenv) and [ruby-build](https://github.com/sstephenson/ruby-build) and then run:
42
+ ```ruby
43
+ profile = client.create_profile(
44
+ merchant_customer_id: '123',
45
+ locale: 'en_US',
46
+ card: {
47
+ card_num: '4111111111111111',
48
+ card_expiry: {
49
+ month: 12,
50
+ year: 2020
51
+ }
52
+ }
53
+ )
54
+ ```
37
55
 
38
- gem update --system
39
- gem update
40
- gem install bundler --no-rdoc --no-ri
56
+ ### Handling Responses
41
57
 
42
- 3. From project root run `./bin/setup` script
43
- 4. Run `./bin/console` for an interactive prompt with an authenticated client for you to experiment:
58
+ Response data is in snake_case (converted from camelCase) typed with individual methods and predicates, including nested complex objects and array of objects as shown below:
44
59
 
45
- ```ruby
46
- profile = client.create_profile(merchantCustomerId: '123', locale: 'en_US')
47
- puts profile[:id]
48
- # => b088ac37...
49
- ```
60
+ ```ruby
61
+ profile.id?
62
+ # => true
63
+ profile.id
64
+ # => b088ac37...
65
+ profile.cards.first.card_expiry.year
66
+ # => 2020
67
+ ```
68
+
69
+ Further API methods are provided in the `Paysafe::REST::Client` object.
70
+
71
+ ## Development
72
+
73
+ 1. `git clone https://github.com/javierjulio/paysafe.git`
74
+ 2. Run `./bin/setup` to install dependencies and fill out API key info
75
+ 3. Run `./bin/console` for an interactive prompt with an authenticated client for you to experiment:
76
+
77
+ ```ruby
78
+ profile = client.create_profile(merchant_customer_id: '123', locale: 'en_US')
79
+ puts profile.id
80
+ # => b088ac37...
81
+ ```
82
+
83
+ All code is written in snake_case since requests and responses are converted to and from camelCase for you.
50
84
 
51
85
  ### Tests
52
86
 
53
- Run `bundle exec rake test` or to skip integration tests run with `SKIP_INTEGRATION_TESTS=true`.
87
+ Run `bundle exec rake test` or to skip integration tests run with `SKIP_INTEGRATION=true`.
54
88
 
55
89
  ### Releasing
56
90
 
@@ -63,3 +97,7 @@ Bug reports and pull requests for missing API support are welcome on GitHub at h
63
97
  ## License
64
98
 
65
99
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
100
+
101
+ [gem]: https://rubygems.org/gems/paysafe
102
+ [travis]: https://travis-ci.org/javierjulio/paysafe
103
+ [paysafe_api_reference]: https://developer.paysafe.com/en/api-reference/
data/bin/setup CHANGED
@@ -4,15 +4,16 @@ IFS=$'\n\t'
4
4
 
5
5
  bundle install
6
6
 
7
- read -r -p 'Do you have an Optimal Payments Developer Centre account? (y/n) ' has_account
7
+ read -r -p 'Do you have an Paysafe Developer account? (y/n) ' has_account
8
8
 
9
9
  if [[ "$has_account" =~ ^(Yes|yes|Y|y)$ ]]; then
10
10
  echo "Great! You can get your account credentials from:"
11
- echo " https://developer.optimalpayments.com/en/my-account/"
11
+ echo " https://login.test.netbanx.com/office/public/preLogin.htm"
12
12
  echo
13
13
  else
14
- echo "Then you'll need to create an account first at:"
15
- echo " https://developer.optimalpayments.com/en/get-started/"
14
+ echo "Then you'll need to create an account first."
15
+ echo "Click the 'Sign Up' button on the following page:"
16
+ echo " https://developer.paysafe.com/"
16
17
  echo
17
18
  read -s -p "Press Enter key if you're ready to continue..."
18
19
  echo
@@ -24,8 +25,8 @@ read -p 'API Key: ' api_key
24
25
  read -p 'API Secret: ' api_secret
25
26
 
26
27
  cp .env.sample .env
27
- sed -i -e s/YOUR_ACCOUNT_NUMBER/"$account_number"/g .env
28
- sed -i -e s/YOUR_API_KEY/"$api_key"/g .env
29
- sed -i -e s/YOUR_API_SECRET/"$api_secret"/g .env
28
+ sed -i '' -e "s/YOUR_ACCOUNT_NUMBER/$account_number/g" .env
29
+ sed -i '' -e "s/YOUR_API_KEY/$api_key/g" .env
30
+ sed -i '' -e "s/YOUR_API_SECRET/$api_secret/g" .env
30
31
 
31
32
  echo "Done."
@@ -0,0 +1,9 @@
1
+ require 'paysafe/result'
2
+
3
+ module Paysafe
4
+ class Address < Result
5
+ attributes :id, :nick_name, :street, :street2, :city,
6
+ :country, :state, :zip, :recipient_name, :phone, :status,
7
+ :default_shipping_address_indicator
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ require 'paysafe/result'
2
+
3
+ module Paysafe
4
+ class Authorization < Result
5
+ attributes :id, :merchant_ref_num, :amount, :settle_with_auth,
6
+ :pre_auth, :available_to_settle, :child_account_num, :auth_code,
7
+ :recurring, :customer_ip, :dup_check, :description, :txn_time,
8
+ :currency_code, :avs_response, :cvv_verification, :status, :keywords
9
+
10
+ object_attribute :Card, :card
11
+ object_attribute :Profile, :profile
12
+ object_attribute :Address, :billing_details
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ require 'paysafe/result'
2
+
3
+ module Paysafe
4
+ class BirthDate < Result
5
+ attributes :year, :month, :day
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ require 'paysafe/result'
2
+
3
+ module Paysafe
4
+ class Card < Result
5
+ attributes :id, :nick_name, :merchant_ref_num, :holder_name,
6
+ :card_num, :card_bin, :last_digits, :card_type, :billing_address_id,
7
+ :default_card_indicator, :payment_token, :single_use_token, :status,
8
+ :cvv, :track1, :track2, :type
9
+
10
+ object_attribute :CardExpiry, :card_expiry
11
+ object_attribute :Address, :billing_address
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ require 'paysafe/result'
2
+
3
+ module Paysafe
4
+ class CardExpiry < Result
5
+ attributes :year, :month
6
+ end
7
+ end
data/lib/paysafe/error.rb CHANGED
@@ -81,22 +81,21 @@ module Paysafe
81
81
  # @param body [String]
82
82
  # @param code [Integer]
83
83
  # @return [Paysafe::Error]
84
- def error_from_response(body, code)
84
+ def from_response(body, code)
85
85
  klass = ERRORS[code] || Paysafe::Error
86
86
  message, error_code = parse_error(body)
87
87
  klass.new(message: message, code: error_code, response: body)
88
88
  end
89
89
 
90
- private
90
+ private
91
91
 
92
92
  def parse_error(body)
93
- if body.nil? || body.empty?
94
- ['', nil]
95
- elsif body.is_a?(Hash) && body[:error].is_a?(Hash)
96
- [body[:error][:message], body[:error][:code]]
93
+ if body.is_a?(Hash)
94
+ [ body.dig(:error, :message), body.dig(:error, :code) ]
95
+ else
96
+ [ '', nil ]
97
97
  end
98
98
  end
99
-
100
99
  end
101
100
 
102
101
  # Initializes a new Error object
@@ -0,0 +1,13 @@
1
+ require 'paysafe/result'
2
+
3
+ module Paysafe
4
+ class Profile < Result
5
+ attributes :id, :status, :merchant_customer_id, :locale,
6
+ :ip, :first_name, :middle_name, :last_name, :gender,
7
+ :nationality, :email, :phone, :cell_phone, :payment_token
8
+
9
+ object_attribute :BirthDate, :date_of_birth
10
+ object_attribute :Card, :cards
11
+ object_attribute :Address, :addresses
12
+ end
13
+ end
@@ -0,0 +1,40 @@
1
+ module Paysafe
2
+ module Refinements
3
+ module CamelCase
4
+ refine Hash do
5
+
6
+ def to_camel_case(data = self)
7
+ case data
8
+ when Array
9
+ data.map { |value| to_camel_case(value) }
10
+ when Hash
11
+ data.map { |key, value| [camel_case_key(key), to_camel_case(value)] }.to_h
12
+ else
13
+ data
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def camel_case_key(key)
20
+ case key
21
+ when Symbol
22
+ camel_case(key.to_s).to_sym
23
+ when String
24
+ camel_case(key).to_sym
25
+ else
26
+ key
27
+ end
28
+ end
29
+
30
+ def camel_case(string)
31
+ @__memoize_camelcase ||= {}
32
+ return @__memoize_camelcase[string] if @__memoize_camelcase[string]
33
+ @__memoize_camelcase[string] = string.gsub(/(?:_+)([a-z])/) { $1.upcase }
34
+ @__memoize_camelcase[string]
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,45 @@
1
+ module Paysafe
2
+ module Refinements
3
+ module SnakeCase
4
+ refine Hash do
5
+
6
+ def to_snake_case(data = self)
7
+ case data
8
+ when Array
9
+ data.map { |value| to_snake_case(value) }
10
+ when Hash
11
+ data.map { |key, value| [underscore_key(key), to_snake_case(value)] }.to_h
12
+ else
13
+ data
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def underscore_key(key)
20
+ case key
21
+ when Symbol
22
+ underscore(key.to_s).to_sym
23
+ when String
24
+ underscore(key).to_sym
25
+ else
26
+ key
27
+ end
28
+ end
29
+
30
+ def underscore(string)
31
+ @__memoize_underscore ||= {}
32
+ return @__memoize_underscore[string] if @__memoize_underscore[string]
33
+ @__memoize_underscore[string] =
34
+ string.gsub(/::/, '/')
35
+ .gsub(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
36
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
37
+ .tr('-', '_')
38
+ .downcase
39
+ @__memoize_underscore[string]
40
+ end
41
+
42
+ end
43
+ end
44
+ end
45
+ end
@@ -1,11 +1,12 @@
1
1
  require 'http'
2
+ require 'json'
2
3
 
3
4
  module Paysafe
4
5
  module REST
5
6
  class Client
6
7
 
7
- API_TEST = 'https://api.test.netbanx.com'
8
- API_LIVE = 'https://api.netbanx.com'
8
+ API_TEST = 'https://api.test.paysafe.com'
9
+ API_LIVE = 'https://api.paysafe.com'
9
10
 
10
11
  HEADERS = {
11
12
  'Content-Type' => 'application/json',
@@ -14,7 +15,10 @@ module Paysafe
14
15
  'X-Ruby-Platform' => RUBY_PLATFORM
15
16
  }
16
17
 
17
- attr_accessor :account_number, :api_key, :api_secret, :test_mode, :timeout_options
18
+ using Refinements::CamelCase
19
+ using Refinements::SnakeCase
20
+
21
+ attr_accessor :account_number, :api_key, :api_secret, :test_mode, :timeouts
18
22
  attr_reader :api_base
19
23
 
20
24
  # Initializes a new Client object
@@ -23,7 +27,6 @@ module Paysafe
23
27
  # @return [Paysafe::REST::Client]
24
28
  def initialize(options={})
25
29
  @test_mode = true
26
- @timeout_options = { write: 2, connect: 5, read: 10 }
27
30
 
28
31
  options.each do |key, value|
29
32
  instance_variable_set("@#{key}", value)
@@ -36,11 +39,7 @@ module Paysafe
36
39
 
37
40
  # @return [Hash]
38
41
  def credentials
39
- {
40
- account_number: account_number,
41
- api_key: api_key,
42
- api_secret: api_secret
43
- }
42
+ { api_key: api_key, api_secret: api_secret }
44
43
  end
45
44
 
46
45
  # @return [Boolean]
@@ -48,157 +47,132 @@ module Paysafe
48
47
  credentials.values.all?
49
48
  end
50
49
 
51
- def create_profile(merchantCustomerId:, locale:, **args)
52
- data = {
53
- merchantCustomerId: merchantCustomerId,
54
- locale: locale,
55
- firstName: args[:firstName],
56
- lastName: args[:lastName],
57
- email: args[:email],
58
- card: args[:card]
59
- }
50
+ def create_single_use_token(data)
51
+ response = post(path: "/customervault/v1/singleusetokens", data: data.to_camel_case)
52
+ process_response(response, SingleUseToken)
53
+ end
54
+
55
+ def create_profile_from_token(data)
56
+ response = post(path: "/customervault/v1/profiles", data: data.to_camel_case)
57
+ process_response(response, Profile)
58
+ end
59
+
60
+ def create_profile(merchant_customer_id:, locale:, **args)
61
+ data = args.merge({
62
+ merchant_customer_id: merchant_customer_id,
63
+ locale: locale
64
+ }).to_camel_case
60
65
 
61
66
  response = post(path: "/customervault/v1/profiles", data: data)
62
- response_body = symbolize_keys!(response.parse)
63
- fail_or_return_response_body(response.code, response_body)
67
+ process_response(response, Profile)
64
68
  end
65
69
 
66
70
  def delete_profile(id:)
67
71
  response = delete(path: "/customervault/v1/profiles/#{id}")
68
- fail_or_return_response_body(response.code, (response.code == 200))
72
+ process_response(response)
69
73
  end
70
74
 
71
75
  def get_profile(id:, fields: [])
72
- fields.keep_if { |field| field == :cards || field == :addresses }
73
-
74
76
  path = "/customervault/v1/profiles/#{id}"
75
-
76
- if !fields.empty?
77
- path += "?fields=#{fields.join(',')}"
78
- end
77
+ path += "?fields=#{fields.join(',')}" if !fields.empty?
79
78
 
80
79
  response = get(path: path)
81
- response_body = symbolize_keys!(response.parse)
82
- fail_or_return_response_body(response.code, response_body)
80
+ process_response(response, Profile)
83
81
  end
84
82
 
85
- def update_profile(id:, merchantCustomerId:, locale:, **args)
86
- data = {
87
- merchantCustomerId: merchantCustomerId,
88
- locale: locale,
89
- firstName: args[:firstName],
90
- lastName: args[:lastName],
91
- email: args[:email]
92
- }
83
+ def update_profile(id:, merchant_customer_id:, locale:, **args)
84
+ data = args.merge({
85
+ merchant_customer_id: merchant_customer_id,
86
+ locale: locale
87
+ }).to_camel_case
93
88
 
94
89
  response = put(path: "/customervault/v1/profiles/#{id}", data: data)
95
- response_body = symbolize_keys!(response.parse)
96
- fail_or_return_response_body(response.code, response_body)
90
+ process_response(response, Profile)
97
91
  end
98
92
 
99
93
  def create_address(profile_id:, country:, zip:, **args)
100
- data = {
101
- nickName: args[:nickName],
102
- street: args[:street],
103
- city: args[:city],
104
- country: country,
105
- state: args[:state],
106
- zip: zip
107
- }
108
-
94
+ data = args.merge({ country: country, zip: zip }).to_camel_case
109
95
  response = post(path: "/customervault/v1/profiles/#{profile_id}/addresses", data: data)
110
- response_body = symbolize_keys!(response.parse)
111
- fail_or_return_response_body(response.code, response_body)
96
+ process_response(response, Address)
97
+ end
98
+
99
+ def create_card_from_token(profile_id:, token:)
100
+ data = { single_use_token: token }.to_camel_case
101
+ response = post(path: "/customervault/v1/profiles/#{profile_id}/cards", data: data)
102
+ process_response(response, Card)
112
103
  end
113
104
 
114
105
  def create_card(profile_id:, number:, month:, year:, **args)
115
- data = {
116
- cardNum: number,
117
- cardExpiry: {
106
+ data = args.merge({
107
+ card_num: number,
108
+ card_expiry: {
118
109
  month: month,
119
110
  year: year
120
- },
121
- nickName: args[:nickName],
122
- holderName: args[:holderName],
123
- merchantRefNum: args[:merchantRefNum],
124
- billingAddressId: args[:billingAddressId],
125
- defaultCardIndicator: args[:defaultCardIndicator]
126
- }.reject { |key, value| value.nil? }
111
+ }
112
+ }).reject { |key, value| value.nil? }.to_camel_case
127
113
 
128
114
  response = post(path: "/customervault/v1/profiles/#{profile_id}/cards", data: data)
129
- response_body = symbolize_keys!(response.parse)
130
- fail_or_return_response_body(response.code, response_body)
115
+ process_response(response, Card)
131
116
  end
132
117
 
133
118
  def delete_card(profile_id:, id:)
134
119
  response = delete(path: "/customervault/v1/profiles/#{profile_id}/cards/#{id}")
135
- response_body = symbolize_keys!(response.parse)
136
- fail_or_return_response_body(response.code, response_body)
120
+ process_response(response)
137
121
  end
138
122
 
139
123
  def get_card(profile_id:, id:)
140
124
  response = get(path: "/customervault/v1/profiles/#{profile_id}/cards/#{id}")
141
- response_body = symbolize_keys!(response.parse)
142
- fail_or_return_response_body(response.code, response_body)
125
+ process_response(response, Card)
143
126
  end
144
127
 
145
128
  def update_card(profile_id:, id:, month:, year:, **args)
146
- data = {
147
- cardExpiry: {
129
+ data = args.merge({
130
+ card_expiry: {
148
131
  month: month,
149
132
  year: year
150
- },
151
- nickName: args[:nickName],
152
- holderName: args[:holderName],
153
- merchantRefNum: args[:merchantRefNum],
154
- billingAddressId: args[:billingAddressId],
155
- defaultCardIndicator: args[:defaultCardIndicator]
156
- }.reject { |key, value| value.nil? }
133
+ }
134
+ }).reject { |key, value| value.nil? }.to_camel_case
157
135
 
158
136
  response = put(path: "/customervault/v1/profiles/#{profile_id}/cards/#{id}", data: data)
159
- response_body = symbolize_keys!(response.parse)
160
- fail_or_return_response_body(response.code, response_body)
137
+ process_response(response, Card)
161
138
  end
162
139
 
163
- def purchase(amount:, token:, merchantRefNum:, **args)
140
+ def purchase(amount:, token:, merchant_ref_num:, **args)
164
141
  data = {
165
142
  amount: amount,
166
- merchantRefNum: merchantRefNum,
167
- settleWithAuth: true,
143
+ merchant_ref_num: merchant_ref_num,
144
+ settle_with_auth: true,
168
145
  card: {
169
- paymentToken: token
146
+ payment_token: token
170
147
  }
171
- }
148
+ }.to_camel_case
172
149
 
173
150
  response = post(path: "/cardpayments/v1/accounts/#{account_number}/auths", data: data)
174
- response_body = symbolize_keys!(response.parse)
175
- fail_or_return_response_body(response.code, response_body)
151
+ process_response(response, Authorization)
176
152
  end
177
153
 
178
- def verify_card(merchantRefNum:, number:, month:, year:, **args)
179
- data = {
180
- merchantRefNum: merchantRefNum,
154
+ def create_verification_from_token(merchant_ref_num:, token:)
155
+ data = { merchant_ref_num: merchant_ref_num, card: { payment_token: token } }.to_camel_case
156
+ response = post(path: "/cardpayments/v1/accounts/#{account_number}/verifications", data: data)
157
+ process_response(response, Verification)
158
+ end
159
+
160
+ def verify_card(merchant_ref_num:, number:, month:, year:, cvv:, address:, **args)
161
+ data = args.merge({
162
+ merchant_ref_num: merchant_ref_num,
163
+ billing_details: address,
181
164
  card: {
182
- cardNum: number,
183
- cardExpiry: {
165
+ card_num: number,
166
+ cvv: cvv,
167
+ card_expiry: {
184
168
  month: month,
185
169
  year: year
186
- },
187
- cvv: args[:cvv]
188
- },
189
- profile: {
190
- firstName: args[:firstName],
191
- lastName: args[:lastName],
192
- email: args[:email]
193
- },
194
- billingDetails: args[:address],
195
- customerIp: args[:customerIp],
196
- description: args[:description]
197
- }
170
+ }
171
+ }
172
+ }).to_camel_case
198
173
 
199
174
  response = post(path: "/cardpayments/v1/accounts/#{account_number}/verifications", data: data)
200
- response_body = symbolize_keys!(response.parse)
201
- fail_or_return_response_body(response.code, response_body)
175
+ process_response(response, Verification)
202
176
  end
203
177
 
204
178
  private
@@ -206,7 +180,7 @@ module Paysafe
206
180
  def http_client
207
181
  HTTP
208
182
  .headers(HEADERS)
209
- .timeout(@timeout_options)
183
+ .timeout(@timeouts ? @timeouts : :null)
210
184
  .basic_auth(user: @api_key, pass: @api_secret)
211
185
  end
212
186
 
@@ -226,25 +200,20 @@ module Paysafe
226
200
  http_client.put("#{api_base}#{path}", json: data)
227
201
  end
228
202
 
229
- def symbolize_keys!(object)
230
- if object.is_a?(Array)
231
- object.each_with_index do |val, index|
232
- object[index] = symbolize_keys!(val)
233
- end
234
- elsif object.is_a?(Hash)
235
- object.keys.each do |key|
236
- object[key.to_sym] = symbolize_keys!(object.delete(key))
237
- end
203
+ def process_response(response, klass=nil)
204
+ data = parse_response_body(response.to_s)
205
+
206
+ if response.status.success?
207
+ klass&.new(data)
208
+ else
209
+ fail Error.from_response(data, response.code)
238
210
  end
239
- object
240
211
  end
241
212
 
242
- def fail_or_return_response_body(code, body)
243
- if code < 200 || code >= 206
244
- error = Paysafe::Error.error_from_response(body, code)
245
- fail(error)
246
- end
247
- body
213
+ def parse_response_body(body)
214
+ return nil if body.strip.empty?
215
+ JSON.parse(body, symbolize_names: true)&.to_snake_case
216
+ rescue JSON::ParserError
248
217
  end
249
218
 
250
219
  end
@@ -0,0 +1,86 @@
1
+ module Paysafe
2
+ class Result
3
+
4
+ class << self
5
+ # Define methods that retrieve the value from attributes
6
+ #
7
+ # @param attrs [Array, Symbol]
8
+ def attributes(*attrs)
9
+ attrs.each do |attr|
10
+ define_attribute_method(attr)
11
+ define_predicate_method(attr)
12
+ end
13
+ end
14
+
15
+ # Define object methods from attributes
16
+ #
17
+ # @param klass [Symbol]
18
+ # @param key1 [Symbol]
19
+ def object_attribute(klass, key1)
20
+ define_attribute_method(key1, klass)
21
+ define_predicate_method(key1)
22
+ end
23
+
24
+ # Dynamically define a method for an attribute
25
+ #
26
+ # @param key1 [Symbol]
27
+ # @param klass [Symbol]
28
+ def define_attribute_method(key1, klass = nil)
29
+ define_method(key1) do
30
+ if instance_variable_defined?("@#{key1}")
31
+ return instance_variable_get("@#{key1}")
32
+ end
33
+ instance_variable_set("@#{key1}", get_value_by(key1, klass))
34
+ end
35
+ end
36
+
37
+ # Dynamically define a predicate method for an attribute
38
+ #
39
+ # @param key1 [Symbol]
40
+ # @param key2 [Symbol]
41
+ def define_predicate_method(key1, key2 = key1)
42
+ define_method(:"#{key1}?") do
43
+ !attr_falsey_or_empty?(key2)
44
+ end
45
+ end
46
+ end
47
+
48
+ # Initializes a new object
49
+ #
50
+ # @param attributes [Hash]
51
+ # @return [Paysafe::Result]
52
+ def initialize(attributes={})
53
+ @attributes = attributes || {}
54
+ end
55
+
56
+ # @return [Hash]
57
+ attr_reader :attributes
58
+
59
+ # @return [Boolean]
60
+ def empty?
61
+ @attributes.empty?
62
+ end
63
+
64
+ private
65
+
66
+ def attr_falsey_or_empty?(key)
67
+ !@attributes[key] || @attributes[key].respond_to?(:empty?) && @attributes[key].empty?
68
+ end
69
+
70
+ def get_value_by(key, klass = nil)
71
+ return @attributes[key] if klass.nil?
72
+
73
+ case @attributes[key]
74
+ when Hash
75
+ Paysafe.const_get(klass).new(@attributes[key])
76
+ when Array
77
+ @attributes[key].map do |value|
78
+ Paysafe.const_get(klass).new(value)
79
+ end
80
+ else
81
+ @attributes[key]
82
+ end
83
+ end
84
+
85
+ end
86
+ end
@@ -0,0 +1,10 @@
1
+ require 'paysafe/result'
2
+
3
+ module Paysafe
4
+ class SingleUseToken < Result
5
+ attributes :id, :payment_token, :time_to_live_seconds
6
+
7
+ object_attribute :Card, :card
8
+ object_attribute :Address, :billing_address
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ require 'paysafe/result'
2
+
3
+ module Paysafe
4
+ class Verification < Result
5
+ attributes :id, :merchant_ref_num, :child_account_num,
6
+ :auth_code, :customer_ip, :dup_check, :description, :txn_time,
7
+ :currency_code, :avs_response, :cvv_verification, :status
8
+
9
+ object_attribute :Profile, :profile
10
+ object_attribute :Card, :card
11
+ object_attribute :Address, :billing_details
12
+ end
13
+ end
@@ -1,3 +1,3 @@
1
1
  module Paysafe
2
- VERSION = "0.6.2"
2
+ VERSION = "0.9.0"
3
3
  end
data/lib/paysafe.rb CHANGED
@@ -1,3 +1,13 @@
1
1
  require "paysafe/version"
2
2
  require "paysafe/error"
3
+ require "paysafe/single_use_token"
4
+ require "paysafe/profile"
5
+ require "paysafe/birth_date"
6
+ require "paysafe/address"
7
+ require "paysafe/card"
8
+ require "paysafe/card_expiry"
9
+ require "paysafe/verification"
10
+ require "paysafe/authorization"
11
+ require "paysafe/refinements/camel_case"
12
+ require "paysafe/refinements/snake_case"
3
13
  require "paysafe/rest/client"
data/paysafe.gemspec CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Javier Julio"]
10
10
  spec.email = ["jjfutbol@gmail.com"]
11
11
 
12
- spec.summary = %q{Gem to wrap Paysafe REST API}
13
- spec.description = %q{Gem to wrap Paysafe REST API}
12
+ spec.summary = "A Ruby interface to the Paysafe REST API."
13
+ spec.description = "A Ruby interface to the Paysafe REST API."
14
14
  spec.homepage = "https://github.com/javierjulio/paysafe"
15
15
  spec.license = "MIT"
16
16
 
@@ -19,6 +19,10 @@ Gem::Specification.new do |spec|
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ["lib"]
21
21
 
22
+ spec.required_ruby_version = '>= 2.3.0'
23
+
24
+ spec.add_dependency "http", '~> 2.0'
25
+
22
26
  spec.add_development_dependency "bundler", "~> 1.10"
23
27
  spec.add_development_dependency "rake", "~> 10.0"
24
28
  spec.add_development_dependency "dotenv"
@@ -27,6 +31,4 @@ Gem::Specification.new do |spec|
27
31
  spec.add_development_dependency "vcr"
28
32
  spec.add_development_dependency "webmock"
29
33
 
30
- spec.add_dependency "http", '~> 2.0'
31
-
32
34
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paysafe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Javier Julio
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-04-25 00:00:00.000000000 Z
11
+ date: 2017-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: http
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -108,21 +122,7 @@ dependencies:
108
122
  - - ">="
109
123
  - !ruby/object:Gem::Version
110
124
  version: '0'
111
- - !ruby/object:Gem::Dependency
112
- name: http
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - "~>"
116
- - !ruby/object:Gem::Version
117
- version: '2.0'
118
- type: :runtime
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - "~>"
123
- - !ruby/object:Gem::Version
124
- version: '2.0'
125
- description: Gem to wrap Paysafe REST API
125
+ description: A Ruby interface to the Paysafe REST API.
126
126
  email:
127
127
  - jjfutbol@gmail.com
128
128
  executables: []
@@ -132,6 +132,7 @@ files:
132
132
  - ".env.sample"
133
133
  - ".gitignore"
134
134
  - ".travis.yml"
135
+ - CHANGELOG.md
135
136
  - CODE_OF_CONDUCT.md
136
137
  - Gemfile
137
138
  - LICENSE.txt
@@ -140,8 +141,19 @@ files:
140
141
  - bin/console
141
142
  - bin/setup
142
143
  - lib/paysafe.rb
144
+ - lib/paysafe/address.rb
145
+ - lib/paysafe/authorization.rb
146
+ - lib/paysafe/birth_date.rb
147
+ - lib/paysafe/card.rb
148
+ - lib/paysafe/card_expiry.rb
143
149
  - lib/paysafe/error.rb
150
+ - lib/paysafe/profile.rb
151
+ - lib/paysafe/refinements/camel_case.rb
152
+ - lib/paysafe/refinements/snake_case.rb
144
153
  - lib/paysafe/rest/client.rb
154
+ - lib/paysafe/result.rb
155
+ - lib/paysafe/single_use_token.rb
156
+ - lib/paysafe/verification.rb
145
157
  - lib/paysafe/version.rb
146
158
  - paysafe.gemspec
147
159
  homepage: https://github.com/javierjulio/paysafe
@@ -156,7 +168,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
156
168
  requirements:
157
169
  - - ">="
158
170
  - !ruby/object:Gem::Version
159
- version: '0'
171
+ version: 2.3.0
160
172
  required_rubygems_version: !ruby/object:Gem::Requirement
161
173
  requirements:
162
174
  - - ">="
@@ -164,8 +176,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
164
176
  version: '0'
165
177
  requirements: []
166
178
  rubyforge_project:
167
- rubygems_version: 2.5.0
179
+ rubygems_version: 2.6.8
168
180
  signing_key:
169
181
  specification_version: 4
170
- summary: Gem to wrap Paysafe REST API
182
+ summary: A Ruby interface to the Paysafe REST API.
171
183
  test_files: []