paysafe 0.6.2 → 0.9.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,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: []