paysafe 0.9.4 → 0.10.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
  SHA256:
3
- metadata.gz: dc7294b85582b6ee37fcdd12dfcabc56d16090f507fd4d8a939cc66a96df5c4e
4
- data.tar.gz: f4a2779dd998b06c501ca581b4ec9d0ad68cf56b903cc4301a509da825b1026c
3
+ metadata.gz: 4803818bdc78d71bc90d2ae9943edc64b0d0c679bfda7bd67347d15ef8e9af7f
4
+ data.tar.gz: 4dc6d70f206609c6e21e7c1c8020de90d64208e5b5d7f338b78a2a77393431c3
5
5
  SHA512:
6
- metadata.gz: f4f33d5ca79c2090278ce544941b72f773c778d8ef62e590401d1f5f8da15f8e246d40c8c066330989ded3333a3cb14c4b247a7b27093608552096ab80e61421
7
- data.tar.gz: 7a46cdad0e46104317fb942315fa5ea96952b22e0694e182ad053c2747e791015ce2d99a0961d686168730dc8467b9fd9cdf1a984a9f047ac66f737a796cf4b5
6
+ metadata.gz: 29f506be900bebea1edb04c07efbe80bda334d676dada67c75773765d7b2008df5107d3aa96b9ec77c35756da60cfc3aa39679663b1474216d06d812be0dcf7e
7
+ data.tar.gz: b4f9aede128b21ff3e1af4a9b7fb810601f45b2c0de2e768f91d8d9cf53f3eb039d96764bbf4f188c710607eacead7b6c332b1826bb1aa0e3853dbf3f02a5a74
@@ -4,5 +4,12 @@ PAYSAFE_API_SECRET="YOUR_API_SECRET"
4
4
 
5
5
  # This is only needed if you want to generate your own Single
6
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"
7
+ PAYSAFE_SUT_API_KEY="YOUR_SUT_API_KEY"
8
+ PAYSAFE_SUT_API_SECRET="YOUR_SUT_API_SECRET"
9
+
10
+ # These are for the Paysafe Payments API which are part of
11
+ # what Paysafe refers to as the Unity platform.
12
+ PAYSAFE_UNITY_API_KEY="YOUR_UNITY_API_KEY"
13
+ PAYSAFE_UNITY_API_SECRET="YOUR_UNITY_API_SECRET"
14
+ PAYSAFE_UNITY_SUT_API_KEY="YOUR_UNITY_SUT_API_KEY"
15
+ PAYSAFE_UNITY_SUT_API_SECRET="YOUR_UNITY_SUT_API_SECRET"
@@ -0,0 +1,51 @@
1
+ name: Paysafe
2
+
3
+ on: [push]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ matrix:
10
+ ruby: [ '2.4', '2.5', '2.6', '2.7' ]
11
+ name: Ruby ${{ matrix.ruby }}
12
+ steps:
13
+ - uses: actions/checkout@v2
14
+
15
+ - uses: actions/setup-ruby@v1
16
+ with:
17
+ ruby-version: ${{ matrix.ruby }}
18
+
19
+ - uses: actions/cache@v1
20
+ with:
21
+ path: vendor/bundle
22
+ key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
23
+ restore-keys: |
24
+ ${{ runner.os }}-gems-
25
+
26
+ - name: Install rubygems
27
+ run: |
28
+ gem update --system --no-document
29
+
30
+ - name: Install bundler
31
+ run: |
32
+ gem install bundler --no-document
33
+
34
+ - name: Install dependencies
35
+ run: |
36
+ bundle config path vendor/bundle
37
+ bundle install --jobs 4 --retry 3
38
+
39
+ - name: Run Tests
40
+ env:
41
+ PAYSAFE_ACCOUNT_NUMBER: test
42
+ PAYSAFE_API_KEY: test
43
+ PAYSAFE_API_SECRET: test
44
+ PAYSAFE_SUT_API_KEY: test
45
+ PAYSAFE_SUT_API_SECRET: test
46
+ PAYSAFE_UNITY_API_KEY: test
47
+ PAYSAFE_UNITY_API_SECRET: test
48
+ PAYSAFE_UNITY_SUT_API_KEY: test
49
+ PAYSAFE_UNITY_SUT_API_SECRET: test
50
+ run: |
51
+ bundle exec rake
data/.gitignore CHANGED
@@ -1,6 +1,5 @@
1
1
  /.bundle/
2
2
  /.yardoc
3
- /Gemfile.lock
4
3
  /_yardoc/
5
4
  /coverage/
6
5
  /doc/
@@ -1,6 +1,18 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ ## 0.10.0 (2020-02-24)
5
+
6
+ * Removed client configuration using a block.
7
+ * Deprecated `Client` object API methods.
8
+ * All API methods are now grouped by API objects, for example:
9
+ * Customer Vault API: `client.create_profile` is now `client.customer_vault.create_profile`
10
+ * Card Payments API: `client.create_verification` is now `client.card_payments.create_verification`
11
+ * Includes some methods for new Payments API through `client.payments` (aka Paysafe Unity Platform)
12
+ * Requires http gem v4.
13
+ * Requires Ruby 2.4 and up.
14
+ * Added Ruby 2.7 build to CI.
15
+
4
16
  ## 0.9.4 (2019-09-24)
5
17
 
6
18
  * Relax http gem dependency to allow v2 through v4.
@@ -0,0 +1,69 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ paysafe (0.10.0)
5
+ http (>= 4, < 5)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ addressable (2.7.0)
11
+ public_suffix (>= 2.0.2, < 5.0)
12
+ ansi (1.5.0)
13
+ builder (3.2.4)
14
+ crack (0.4.3)
15
+ safe_yaml (~> 1.0.0)
16
+ domain_name (0.5.20190701)
17
+ unf (>= 0.0.5, < 1.0.0)
18
+ dotenv (2.7.5)
19
+ ffi (1.12.2)
20
+ ffi-compiler (1.0.1)
21
+ ffi (>= 1.0.0)
22
+ rake
23
+ hashdiff (1.0.0)
24
+ http (4.3.0)
25
+ addressable (~> 2.3)
26
+ http-cookie (~> 1.0)
27
+ http-form_data (~> 2.2)
28
+ http-parser (~> 1.2.0)
29
+ http-cookie (1.0.3)
30
+ domain_name (~> 0.5)
31
+ http-form_data (2.2.0)
32
+ http-parser (1.2.1)
33
+ ffi-compiler (>= 1.0, < 2.0)
34
+ minitest (5.14.0)
35
+ minitest-mock_expectations (1.1.1)
36
+ minitest-reporters (1.4.2)
37
+ ansi
38
+ builder
39
+ minitest (>= 5.0)
40
+ ruby-progressbar
41
+ public_suffix (4.0.3)
42
+ rake (13.0.1)
43
+ ruby-progressbar (1.10.1)
44
+ safe_yaml (1.0.5)
45
+ unf (0.1.4)
46
+ unf_ext
47
+ unf_ext (0.0.7.6)
48
+ vcr (5.1.0)
49
+ webmock (3.8.2)
50
+ addressable (>= 2.3.6)
51
+ crack (>= 0.3.2)
52
+ hashdiff (>= 0.4.0, < 2.0.0)
53
+
54
+ PLATFORMS
55
+ ruby
56
+
57
+ DEPENDENCIES
58
+ bundler (~> 2.0)
59
+ dotenv
60
+ minitest
61
+ minitest-mock_expectations
62
+ minitest-reporters
63
+ paysafe!
64
+ rake
65
+ vcr
66
+ webmock
67
+
68
+ BUNDLED WITH
69
+ 2.0.2
data/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # The Paysafe Ruby Gem
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/paysafe.svg)][gem]
4
- [![Build Status](https://travis-ci.org/javierjulio/paysafe.svg?branch=master)][travis]
4
+ ![Build Status](https://github.com/javierjulio/paysafe/workflows/Paysafe/badge.svg?branch=master)
5
5
 
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.
6
+ A well tested Ruby interface to the [Paysafe REST API](paysafe_api_reference) (formerly Optimal Payments). Requires Ruby 2.4 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.
7
7
 
8
8
  ## Installation
9
9
 
@@ -24,23 +24,24 @@ To try out the gem, just follow the Development section instructions as the buil
24
24
  Create and configure a client with your API authentication.
25
25
 
26
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
27
+ client = Paysafe::REST::Client.new(
28
+ api_key: 'your api key',
29
+ api_secret: 'you api secret',
30
+ test_mode: false, # to enable Production requests (default is true)
31
+ account_number: '1234', # used for the Card Payments API
32
+ # Provide optional timeouts
33
+ # timeouts: { connect: 2, read: 5, write: 10 }
34
+ )
36
35
  ```
37
36
 
37
+ At a minimum the following options: `api_key`, `api_secret`, and `test_mode` should be specified. The `account_number` is only necessary for certain API's such as the Card Payments API.
38
+
38
39
  ### Making Requests
39
40
 
40
41
  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.
41
42
 
42
43
  ```ruby
43
- profile = client.create_profile(
44
+ profile = client.customer_vault.create_profile(
44
45
  merchant_customer_id: '123',
45
46
  locale: 'en_US',
46
47
  card: {
@@ -66,16 +67,14 @@ profile.cards.first.card_expiry.year
66
67
  # => 2020
67
68
  ```
68
69
 
69
- Further API methods are provided in the `Paysafe::REST::Client` object.
70
-
71
70
  ## Development
72
71
 
73
72
  1. `git clone https://github.com/javierjulio/paysafe.git`
74
- 2. Run `./bin/setup` to install dependencies and fill out API key info
73
+ 2. Run `./bin/setup` to install dependencies and fill out API key/secret info
75
74
  3. Run `./bin/console` for an interactive prompt with an authenticated client for you to experiment:
76
75
 
77
76
  ```ruby
78
- profile = client.create_profile(merchant_customer_id: '123', locale: 'en_US')
77
+ profile = client.customer_vault.create_profile(merchant_customer_id: SecureRandom.uuid, locale: 'en_US')
79
78
  puts profile.id
80
79
  # => b088ac37...
81
80
  ```
@@ -84,7 +83,7 @@ All code is written in snake_case since requests and responses are converted to
84
83
 
85
84
  ### Tests
86
85
 
87
- Run `bundle exec rake test` or to skip integration tests run with `SKIP_INTEGRATION=true`.
86
+ If the API key/secret info is different from what was used to record the cassettes, you'll need to run `bundle exec rake test RECORD_MODE=all` otherwise run `bundle exec rake test`.
88
87
 
89
88
  ### Releasing
90
89
 
@@ -99,5 +98,4 @@ Bug reports and pull requests for missing API support are welcome on GitHub at h
99
98
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
100
99
 
101
100
  [gem]: https://rubygems.org/gems/paysafe
102
- [travis]: https://travis-ci.org/javierjulio/paysafe
103
101
  [paysafe_api_reference]: https://developer.paysafe.com/en/api-reference/
@@ -1,25 +1,37 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require "bundler/setup"
4
+ require "dotenv/load"
4
5
  require "paysafe"
5
6
 
6
7
  # You can add fixtures and/or initialization code here to make experimenting
7
8
  # with your gem easier. You can also use a different console, if you like.
8
9
 
9
- # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
10
+ def client
11
+ Paysafe::REST::Client.new(
12
+ account_number: ENV['PAYSAFE_ACCOUNT_NUMBER'],
13
+ api_key: ENV['PAYSAFE_API_KEY'],
14
+ api_secret: ENV['PAYSAFE_API_SECRET']
15
+ )
16
+ end
12
17
 
13
- require 'dotenv'
14
- Dotenv.load
18
+ def sut_client
19
+ Paysafe::REST::Client.new(
20
+ api_key: ENV['PAYSAFE_SUT_API_KEY'],
21
+ api_secret: ENV['PAYSAFE_SUT_API_SECRET']
22
+ )
23
+ end
15
24
 
16
- def client
17
- Paysafe::REST::Client.new do |config|
18
- config.account_number = ENV['PAYSAFE_ACCOUNT_NUMBER']
19
- config.api_key = ENV['PAYSAFE_API_KEY']
20
- config.api_secret = ENV['PAYSAFE_API_SECRET']
21
- end
25
+ def unity_client
26
+ Paysafe::REST::Client.new(
27
+ api_key: ENV['PAYSAFE_UNITY_API_KEY'],
28
+ api_secret: ENV['PAYSAFE_UNITY_API_SECRET']
29
+ )
22
30
  end
23
31
 
32
+ # (If you use this, don't forget to add pry to your Gemfile!)
33
+ # require "pry"
34
+ # Pry.start
35
+
24
36
  require "irb"
25
- IRB.start
37
+ IRB.start(__FILE__)
data/bin/setup CHANGED
@@ -23,10 +23,19 @@ echo "Enter your account credentials below."
23
23
  read -p 'Account Number: ' account_number
24
24
  read -p 'API Key: ' api_key
25
25
  read -p 'API Secret: ' api_secret
26
+ read -p 'Single Use Token API Key: ' sut_api_key
27
+ read -p 'Single Use Token API Secret: ' sut_api_secret
28
+ read -p 'Unity API Key: ' unity_api_key
29
+ read -p 'Unity API Secret: ' unity_api_secret
26
30
 
27
31
  cp .env.sample .env
32
+
28
33
  sed -i '' -e "s/YOUR_ACCOUNT_NUMBER/$account_number/g" .env
29
34
  sed -i '' -e "s/YOUR_API_KEY/$api_key/g" .env
30
35
  sed -i '' -e "s/YOUR_API_SECRET/$api_secret/g" .env
36
+ sed -i '' -e "s/YOUR_SUT_API_KEY/$sut_api_key/g" .env
37
+ sed -i '' -e "s/YOUR_SUT_API_SECRET/$sut_api_secret/g" .env
38
+ sed -i '' -e "s/YOUR_UNITY_API_KEY/$unity_api_key/g" .env
39
+ sed -i '' -e "s/YOUR_UNITY_API_SECRET/$unity_api_secret/g" .env
31
40
 
32
41
  echo "Done."
@@ -1,7 +1,15 @@
1
+ require "http"
2
+ require "json"
1
3
  require "paysafe/version"
2
4
  require "paysafe/error"
3
5
  require "paysafe/single_use_token"
4
6
  require "paysafe/profile"
7
+ require "paysafe/customer"
8
+ require "paysafe/payment"
9
+ require "paysafe/gateway_response"
10
+ require "paysafe/payment_processor"
11
+ require "paysafe/payment_methods"
12
+ require "paysafe/payment_method"
5
13
  require "paysafe/birth_date"
6
14
  require "paysafe/address"
7
15
  require "paysafe/card"
@@ -11,3 +19,8 @@ require "paysafe/authorization"
11
19
  require "paysafe/refinements/camel_case"
12
20
  require "paysafe/refinements/snake_case"
13
21
  require "paysafe/rest/client"
22
+ require "paysafe/configuration"
23
+ require "paysafe/api/base_api"
24
+ require "paysafe/api/card_payments_api"
25
+ require "paysafe/api/customer_vault_api"
26
+ require "paysafe/api/payments_api"
@@ -2,7 +2,7 @@ require 'paysafe/result'
2
2
 
3
3
  module Paysafe
4
4
  class Address < Result
5
- attributes :id, :nick_name, :street, :street2, :city,
5
+ attributes :id, :nick_name, :street, :street1, :street2, :city,
6
6
  :country, :state, :zip, :recipient_name, :phone, :status,
7
7
  :default_shipping_address_indicator
8
8
  end
@@ -0,0 +1,70 @@
1
+ module Paysafe
2
+ module Api
3
+ class BaseApi
4
+ HEADERS = {
5
+ 'Content-Type' => 'application/json',
6
+ 'User-Agent' => "PaysafeRubyGem/#{Paysafe::VERSION}",
7
+ 'X-Ruby-Version' => RUBY_VERSION,
8
+ 'X-Ruby-Platform' => RUBY_PLATFORM
9
+ }
10
+
11
+ using Refinements::CamelCase
12
+ using Refinements::SnakeCase
13
+
14
+ def initialize(config)
15
+ @config = config
16
+ end
17
+
18
+ protected
19
+
20
+ # Needed for some API URLs
21
+ def account_number
22
+ @config.account_number
23
+ end
24
+
25
+ def http_client
26
+ HTTP
27
+ .headers(HEADERS)
28
+ .timeout(@config.timeouts ? @config.timeouts : :null)
29
+ .basic_auth(user: @config.api_key, pass: @config.api_secret)
30
+ end
31
+
32
+ def perform_post_with_object(path, data, klass)
33
+ response = http_client.post("#{@config.api_base}#{path}", json: data.to_camel_case)
34
+ process_response(response, klass)
35
+ end
36
+
37
+ def perform_get_with_object(path, klass)
38
+ response = http_client.get("#{@config.api_base}#{path}")
39
+ process_response(response, klass)
40
+ end
41
+
42
+ def perform_delete(path)
43
+ response = http_client.delete("#{@config.api_base}#{path}")
44
+ process_response(response)
45
+ end
46
+
47
+ def perform_put_with_object(path, data, klass)
48
+ response = http_client.put("#{@config.api_base}#{path}", json: data.to_camel_case)
49
+ process_response(response, klass)
50
+ end
51
+
52
+ def process_response(response, klass=nil)
53
+ data = parse_response_body(response.to_s)
54
+
55
+ if response.status.success?
56
+ klass&.new(data)
57
+ else
58
+ fail Error.from_response(data, response.code)
59
+ end
60
+ end
61
+
62
+ def parse_response_body(body)
63
+ return nil if body.strip.empty?
64
+ JSON.parse(body, symbolize_names: true)&.to_snake_case
65
+ rescue JSON::ParserError
66
+ end
67
+
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,23 @@
1
+ module Paysafe
2
+ module Api
3
+ class CardPaymentsApi < BaseApi
4
+
5
+ def create_authorization(**data)
6
+ perform_post_with_object("/cardpayments/v1/accounts/#{account_number}/auths", data, Authorization)
7
+ end
8
+
9
+ def create_verification(**data)
10
+ perform_post_with_object("/cardpayments/v1/accounts/#{account_number}/verifications", data, Verification)
11
+ end
12
+
13
+ def get_authorization(id:)
14
+ perform_get_with_object("/cardpayments/v1/accounts/#{account_number}/auths/#{id}", Authorization)
15
+ end
16
+
17
+ def get_verification(id:)
18
+ perform_get_with_object("/cardpayments/v1/accounts/#{account_number}/verifications/#{id}", Verification)
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,74 @@
1
+ module Paysafe
2
+ module Api
3
+ class CustomerVaultApi < BaseApi
4
+
5
+ def create_address(profile_id:, country:, zip:, **args)
6
+ data = args.merge({ country: country, zip: zip })
7
+ perform_post_with_object("/customervault/v1/profiles/#{profile_id}/addresses", data, Address)
8
+ end
9
+
10
+ def create_card(profile_id:, **data)
11
+ perform_post_with_object("/customervault/v1/profiles/#{profile_id}/cards", data, Card)
12
+ end
13
+
14
+ def create_profile(merchant_customer_id:, locale:, **args)
15
+ data = args.merge({
16
+ merchant_customer_id: merchant_customer_id,
17
+ locale: locale
18
+ })
19
+
20
+ perform_post_with_object("/customervault/v1/profiles", data, Profile)
21
+ end
22
+
23
+ def create_single_use_token(**data)
24
+ perform_post_with_object("/customervault/v1/singleusetokens", data, SingleUseToken)
25
+ end
26
+
27
+ def delete_address(profile_id:, id:)
28
+ perform_delete("/customervault/v1/profiles/#{profile_id}/addresses/#{id}")
29
+ end
30
+
31
+ def delete_card(profile_id:, id:)
32
+ perform_delete("/customervault/v1/profiles/#{profile_id}/cards/#{id}")
33
+ end
34
+
35
+ def delete_profile(id:)
36
+ perform_delete("/customervault/v1/profiles/#{id}")
37
+ end
38
+
39
+ def get_address(profile_id:, id:)
40
+ perform_get_with_object("/customervault/v1/profiles/#{profile_id}/addresses/#{id}", Address)
41
+ end
42
+
43
+ def get_card(profile_id:, id:)
44
+ perform_get_with_object("/customervault/v1/profiles/#{profile_id}/cards/#{id}", Card)
45
+ end
46
+
47
+ def get_profile(id:, fields: [])
48
+ path = "/customervault/v1/profiles/#{id}"
49
+ path += "?fields=#{fields.join(',')}" if !fields.empty?
50
+
51
+ perform_get_with_object(path, Profile)
52
+ end
53
+
54
+ def update_address(profile_id:, id:, country:, zip:, **args)
55
+ data = args.merge({ country: country, zip: zip })
56
+ perform_put_with_object("/customervault/v1/profiles/#{profile_id}/addresses/#{id}", data, Address)
57
+ end
58
+
59
+ def update_card(profile_id:, id:, **data)
60
+ perform_put_with_object("/customervault/v1/profiles/#{profile_id}/cards/#{id}", data, Card)
61
+ end
62
+
63
+ def update_profile(id:, merchant_customer_id:, locale:, **args)
64
+ data = args.merge({
65
+ merchant_customer_id: merchant_customer_id,
66
+ locale: locale
67
+ })
68
+
69
+ perform_put_with_object("/customervault/v1/profiles/#{id}", data, Profile)
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,19 @@
1
+ module Paysafe
2
+ module Api
3
+ class PaymentsApi < BaseApi
4
+
5
+ def get_payment_methods(currency_code:)
6
+ perform_get_with_object("/paymenthub/v1/paymentmethods?currencyCode=#{currency_code}", PaymentMethods)
7
+ end
8
+
9
+ def create_payment(**data)
10
+ perform_post_with_object("/paymenthub/v1/payments", data, Payment)
11
+ end
12
+
13
+ def get_payment(id:)
14
+ perform_get_with_object("/paymenthub/v1/payments/#{id}", Payment)
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -3,5 +3,9 @@ require 'paysafe/result'
3
3
  module Paysafe
4
4
  class BirthDate < Result
5
5
  attributes :year, :month, :day
6
+
7
+ def date
8
+ @date ||= Date.new(year, month, day)
9
+ end
6
10
  end
7
11
  end
@@ -0,0 +1,20 @@
1
+ module Paysafe
2
+ class Configuration
3
+
4
+ API_TEST = 'https://api.test.paysafe.com'
5
+ API_LIVE = 'https://api.paysafe.com'
6
+
7
+ attr_reader :account_number, :api_base, :api_key, :api_secret, :test_mode, :timeouts
8
+
9
+ def initialize(**options)
10
+ @test_mode = true
11
+
12
+ options.each do |key, value|
13
+ instance_variable_set("@#{key}", value)
14
+ end
15
+
16
+ @api_base = test_mode ? API_TEST : API_LIVE
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,11 @@
1
+ require 'paysafe/result'
2
+
3
+ module Paysafe
4
+ class Customer < 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
+ end
11
+ end
@@ -48,7 +48,7 @@ module Paysafe
48
48
  # Raised on the HTTP status code 504
49
49
  GatewayTimeout = Class.new(ServerError)
50
50
 
51
- ERRORS = {
51
+ ERRORS_BY_STATUS = {
52
52
  400 => Paysafe::Error::BadRequest,
53
53
  401 => Paysafe::Error::Unauthorized,
54
54
  402 => Paysafe::Error::RequestDeclined,
@@ -65,24 +65,14 @@ module Paysafe
65
65
  504 => Paysafe::Error::GatewayTimeout,
66
66
  }
67
67
 
68
- # The Paysafe API Error Code
69
- #
70
- # @return [Integer]
71
- attr_reader :code
72
-
73
- # The JSON HTTP response in Hash form
74
- #
75
- # @return [Hash]
76
- attr_reader :response
77
-
78
68
  class << self
79
69
  # Create a new error from an HTTP response
80
70
  #
81
71
  # @param body [String]
82
- # @param code [Integer]
72
+ # @param status [Integer]
83
73
  # @return [Paysafe::Error]
84
- def from_response(body, code)
85
- klass = ERRORS[code] || Paysafe::Error
74
+ def from_response(body, status)
75
+ klass = ERRORS_BY_STATUS[status] || Paysafe::Error
86
76
  message, error_code = parse_error(body)
87
77
  klass.new(message: message, code: error_code, response: body)
88
78
  end
@@ -98,16 +88,64 @@ module Paysafe
98
88
  end
99
89
  end
100
90
 
91
+ # The Paysafe API Error Code
92
+ #
93
+ # @return [String]
94
+ attr_reader :code
95
+
96
+ # The JSON HTTP response in Hash form
97
+ #
98
+ # @return [Hash]
99
+ attr_reader :response
100
+
101
101
  # Initializes a new Error object
102
102
  #
103
103
  # @param message [Exception, String]
104
- # @param code [Integer]
104
+ # @param code [String]
105
105
  # @param response [Hash]
106
106
  # @return [Paysafe::Error]
107
107
  def initialize(message: '', code: nil, response: {})
108
- super(message)
109
108
  @code = code
110
109
  @response = response
110
+ super(message)
111
111
  end
112
+
113
+ def to_s
114
+ [ super, code_text, field_error_text, detail_text ].compact.join(' ')
115
+ end
116
+
117
+ def id
118
+ response.dig(:id) if response.is_a?(Hash)
119
+ end
120
+
121
+ def merchant_ref_num
122
+ response.dig(:merchant_ref_num) if response.is_a?(Hash)
123
+ end
124
+
125
+ private
126
+
127
+ def code_text
128
+ "(Code #{code})" if code
129
+ end
130
+
131
+ def field_error_text
132
+ if field_errors
133
+ msgs = field_errors.map { |f| "The \`#{f[:field]}\` #{f[:error]}." }.join(' ')
134
+ "Field Errors: #{msgs}".strip
135
+ end
136
+ end
137
+
138
+ def field_errors
139
+ response.dig(:error, :field_errors) if response.is_a?(Hash)
140
+ end
141
+
142
+ def detail_text
143
+ "Details: #{details.join('. ')}".strip if details
144
+ end
145
+
146
+ def details
147
+ response.dig(:error, :details) if response.is_a?(Hash)
148
+ end
149
+
112
150
  end
113
151
  end
@@ -0,0 +1,12 @@
1
+ require 'paysafe/result'
2
+
3
+ module Paysafe
4
+ class GatewayResponse < Result
5
+ attributes :processor,
6
+ :balance,
7
+ :merchant_transaction_id,
8
+ :payment_processor_transaction_id,
9
+ :lcp_transaction_id,
10
+ :lcp_encoded_transaction_id
11
+ end
12
+ end
@@ -0,0 +1,27 @@
1
+ require 'paysafe/result'
2
+
3
+ module Paysafe
4
+ class Payment < Result
5
+ attributes :id,
6
+ :payment_type,
7
+ :payment_handle_token,
8
+ :merchant_ref_num,
9
+ :currency_code,
10
+ :settle_with_auth,
11
+ :txn_time,
12
+ :status,
13
+ :gateway_reconciliation_id,
14
+ :amount,
15
+ :available_to_settle,
16
+ :available_to_refund,
17
+ :consumer_ip,
18
+ :live_mode,
19
+ :updated_time,
20
+ :status_time
21
+
22
+ object_attribute :Address, :billing_details
23
+ object_attribute :Customer, :profile
24
+ object_attribute :GatewayResponse, :gateway_response
25
+ object_attribute :PaymentProcessor, :sightline
26
+ end
27
+ end
@@ -0,0 +1,7 @@
1
+ require 'paysafe/result'
2
+
3
+ module Paysafe
4
+ class PaymentMethod < Result
5
+ attributes :payment_method, :currency_code, :currency, :account_id, :mcc
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'paysafe/result'
2
+
3
+ module Paysafe
4
+ class PaymentMethods < Result
5
+ object_attribute :PaymentMethod, :payment_methods
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'paysafe/result'
2
+
3
+ module Paysafe
4
+ class PaymentProcessor < Result
5
+ attributes :consumer_id
6
+ end
7
+ end
@@ -1,40 +1,16 @@
1
- require 'http'
2
- require 'json'
3
-
4
1
  module Paysafe
5
2
  module REST
6
3
  class Client
4
+ extend Forwardable
7
5
 
8
- API_TEST = 'https://api.test.paysafe.com'
9
- API_LIVE = 'https://api.paysafe.com'
10
-
11
- HEADERS = {
12
- 'Content-Type' => 'application/json',
13
- 'User-Agent' => "PaysafeRubyGem/#{Paysafe::VERSION}",
14
- 'X-Ruby-Version' => RUBY_VERSION,
15
- 'X-Ruby-Platform' => RUBY_PLATFORM
16
- }
17
-
18
- using Refinements::CamelCase
19
- using Refinements::SnakeCase
20
-
21
- attr_accessor :account_number, :api_key, :api_secret, :test_mode, :timeouts
22
- attr_reader :api_base
6
+ delegate [:account_number, :api_base, :api_key, :api_secret, :test_mode, :timeouts] => :@config
23
7
 
24
8
  # Initializes a new Client object
25
9
  #
26
10
  # @param options [Hash]
27
11
  # @return [Paysafe::REST::Client]
28
- def initialize(options={})
29
- @test_mode = true
30
-
31
- options.each do |key, value|
32
- instance_variable_set("@#{key}", value)
33
- end
34
-
35
- yield(self) if block_given?
36
-
37
- @api_base = test_mode ? API_TEST : API_LIVE
12
+ def initialize(**options)
13
+ @config = Configuration.new(options)
38
14
  end
39
15
 
40
16
  # @return [Hash]
@@ -47,87 +23,76 @@ module Paysafe
47
23
  credentials.values.all?
48
24
  end
49
25
 
26
+ def customer_vault
27
+ @customer_vault ||= Api::CustomerVaultApi.new(@config)
28
+ end
29
+
30
+ def card_payments
31
+ @card_payments ||= Api::CardPaymentsApi.new(@config)
32
+ end
33
+
34
+ def payments
35
+ @payments ||= Api::PaymentsApi.new(@config)
36
+ end
37
+
50
38
  def create_single_use_token(data)
51
- response = post(path: "/customervault/v1/singleusetokens", data: data.to_camel_case)
52
- process_response(response, SingleUseToken)
39
+ warn "[DEPRECATION] 'create_single_use_token' is deprecated. Please use 'customer_vault.create_single_use_token' instead."
40
+ customer_vault.create_single_use_token(**data)
53
41
  end
54
42
 
55
43
  def create_profile_from_token(data)
56
- response = post(path: "/customervault/v1/profiles", data: data.to_camel_case)
57
- process_response(response, Profile)
44
+ warn "[DEPRECATION] 'create_profile_from_token' is deprecated. Please use 'customer_vault.create_profile' instead."
45
+ customer_vault.create_profile(**data)
58
46
  end
59
47
 
60
48
  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
65
-
66
- response = post(path: "/customervault/v1/profiles", data: data)
67
- process_response(response, Profile)
49
+ warn "[DEPRECATION] 'create_profile' is deprecated. Please use 'customer_vault.create_profile' instead."
50
+ customer_vault.create_profile(merchant_customer_id: merchant_customer_id, locale: locale, **args)
68
51
  end
69
52
 
70
53
  def delete_profile(id:)
71
- response = delete(path: "/customervault/v1/profiles/#{id}")
72
- process_response(response)
54
+ warn "[DEPRECATION] 'delete_profile' is deprecated. Please use 'customer_vault.delete_profile' instead."
55
+ customer_vault.delete_profile(id: id)
73
56
  end
74
57
 
75
58
  def get_profile(id:, fields: [])
76
- path = "/customervault/v1/profiles/#{id}"
77
- path += "?fields=#{fields.join(',')}" if !fields.empty?
78
-
79
- response = get(path: path)
80
- process_response(response, Profile)
59
+ warn "[DEPRECATION] 'get_profile' is deprecated. Please use 'customer_vault.get_profile' instead."
60
+ customer_vault.get_profile(id: id, fields: fields)
81
61
  end
82
62
 
83
63
  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
88
-
89
- response = put(path: "/customervault/v1/profiles/#{id}", data: data)
90
- process_response(response, Profile)
64
+ warn "[DEPRECATION] 'update_profile' is deprecated. Please use 'customer_vault.update_profile' instead."
65
+ customer_vault.update_profile(id: id, merchant_customer_id: merchant_customer_id, locale: locale, **args)
91
66
  end
92
67
 
93
68
  def create_address(profile_id:, country:, zip:, **args)
94
- data = args.merge({ country: country, zip: zip }).to_camel_case
95
- response = post(path: "/customervault/v1/profiles/#{profile_id}/addresses", data: data)
96
- process_response(response, Address)
69
+ warn "[DEPRECATION] 'create_address' is deprecated. Please use 'customer_vault.create_address' instead."
70
+ customer_vault.create_address(profile_id: profile_id, country: country, zip: zip, **args)
97
71
  end
98
72
 
99
73
  def get_address(profile_id:, id:)
100
- response = get(path: "/customervault/v1/profiles/#{profile_id}/addresses/#{id}")
101
- process_response(response, Address)
74
+ warn "[DEPRECATION] 'get_address' is deprecated. Please use 'customer_vault.get_address' instead."
75
+ customer_vault.get_address(profile_id: profile_id, id: id)
102
76
  end
103
77
 
104
78
  def create_card_from_token(profile_id:, token:)
105
- data = { single_use_token: token }.to_camel_case
106
- response = post(path: "/customervault/v1/profiles/#{profile_id}/cards", data: data)
107
- process_response(response, Card)
79
+ warn "[DEPRECATION] 'create_card_from_token' is deprecated. Please use 'customer_vault.create_card' instead."
80
+ customer_vault.create_card(profile_id: profile_id, single_use_token: token)
108
81
  end
109
82
 
110
- def create_card(profile_id:, number:, month:, year:, **args)
111
- data = args.merge({
112
- card_num: number,
113
- card_expiry: {
114
- month: month,
115
- year: year
116
- }
117
- }).reject { |key, value| value.nil? }.to_camel_case
118
-
119
- response = post(path: "/customervault/v1/profiles/#{profile_id}/cards", data: data)
120
- process_response(response, Card)
83
+ def create_card(profile_id:, **data)
84
+ warn "[DEPRECATION] 'create_card' is deprecated. Please use 'customer_vault.create_card' instead."
85
+ customer_vault.create_card(profile_id: profile_id, **data)
121
86
  end
122
87
 
123
88
  def delete_card(profile_id:, id:)
124
- response = delete(path: "/customervault/v1/profiles/#{profile_id}/cards/#{id}")
125
- process_response(response)
89
+ warn "[DEPRECATION] 'delete_card' is deprecated. Please use 'customer_vault.delete_card' instead."
90
+ customer_vault.delete_card(profile_id: profile_id, id: id)
126
91
  end
127
92
 
128
93
  def get_card(profile_id:, id:)
129
- response = get(path: "/customervault/v1/profiles/#{profile_id}/cards/#{id}")
130
- process_response(response, Card)
94
+ warn "[DEPRECATION] 'get_card' is deprecated. Please use 'customer_vault.get_card' instead."
95
+ customer_vault.get_card(profile_id: profile_id, id: id)
131
96
  end
132
97
 
133
98
  def update_card(profile_id:, id:, month:, year:, **args)
@@ -136,10 +101,9 @@ module Paysafe
136
101
  month: month,
137
102
  year: year
138
103
  }
139
- }).reject { |key, value| value.nil? }.to_camel_case
140
-
141
- response = put(path: "/customervault/v1/profiles/#{profile_id}/cards/#{id}", data: data)
142
- process_response(response, Card)
104
+ }).reject { |key, value| value.nil? }
105
+ warn "[DEPRECATION] 'update_card' is deprecated. Please use 'customer_vault.update_card' instead with new params format: #{data}"
106
+ customer_vault.update_card(profile_id: profile_id, id: id, **data)
143
107
  end
144
108
 
145
109
  def purchase(amount:, token:, merchant_ref_num:, **args)
@@ -150,81 +114,21 @@ module Paysafe
150
114
  card: {
151
115
  payment_token: token
152
116
  }
153
- }).to_camel_case
154
-
155
- response = post(path: "/cardpayments/v1/accounts/#{account_number}/auths", data: data)
156
- process_response(response, Authorization)
117
+ })
118
+ warn "[DEPRECATION] 'purchase' is deprecated. Please use 'card_payments.create_authorization' instead with new params format: #{data}"
119
+ card_payments.create_authorization(**data)
157
120
  end
158
121
 
159
122
  def create_verification_from_token(merchant_ref_num:, token:, **args)
123
+ warn "[DEPRECATION] 'create_verification_from_token' is deprecated. Please use 'card_payments.create_verification' instead with new params format."
160
124
  data = args.merge({
161
125
  merchant_ref_num: merchant_ref_num,
162
126
  card: {
163
127
  payment_token: token
164
128
  }
165
- }).to_camel_case
166
-
167
- response = post(path: "/cardpayments/v1/accounts/#{account_number}/verifications", data: data)
168
- process_response(response, Verification)
169
- end
170
-
171
- def verify_card(merchant_ref_num:, number:, month:, year:, cvv:, address:, **args)
172
- data = args.merge({
173
- merchant_ref_num: merchant_ref_num,
174
- billing_details: address,
175
- card: {
176
- card_num: number,
177
- cvv: cvv,
178
- card_expiry: {
179
- month: month,
180
- year: year
181
- }
182
- }
183
- }).to_camel_case
184
-
185
- response = post(path: "/cardpayments/v1/accounts/#{account_number}/verifications", data: data)
186
- process_response(response, Verification)
187
- end
188
-
189
- private
190
-
191
- def http_client
192
- HTTP
193
- .headers(HEADERS)
194
- .timeout(timeouts ? timeouts : :null)
195
- .basic_auth(user: api_key, pass: api_secret)
196
- end
197
-
198
- def post(path:, data:)
199
- http_client.post("#{api_base}#{path}", json: data)
200
- end
201
-
202
- def get(path:)
203
- http_client.get("#{api_base}#{path}")
204
- end
205
-
206
- def delete(path:)
207
- http_client.delete("#{api_base}#{path}")
208
- end
209
-
210
- def put(path:, data:)
211
- http_client.put("#{api_base}#{path}", json: data)
212
- end
213
-
214
- def process_response(response, klass=nil)
215
- data = parse_response_body(response.to_s)
216
-
217
- if response.status.success?
218
- klass&.new(data)
219
- else
220
- fail Error.from_response(data, response.code)
221
- end
222
- end
129
+ })
223
130
 
224
- def parse_response_body(body)
225
- return nil if body.strip.empty?
226
- JSON.parse(body, symbolize_names: true)&.to_snake_case
227
- rescue JSON::ParserError
131
+ card_payments.create_verification(**data)
228
132
  end
229
133
 
230
134
  end
@@ -12,13 +12,13 @@ module Paysafe
12
12
 
13
13
  [:unknown, :not_processed, :no_match, :match, :match_address_only, :match_zip_only].each do |key|
14
14
  define_method("avs_#{key}?") do
15
- avs_response == key.to_s.upcase
15
+ avs_response.to_s.upcase == key.to_s.upcase
16
16
  end
17
17
  end
18
18
 
19
19
  [:unknown, :match, :no_match, :not_processed].each do |key|
20
20
  define_method("cvv_#{key}?") do
21
- cvv_verification == key.to_s.upcase
21
+ cvv_verification.to_s.upcase == key.to_s.upcase
22
22
  end
23
23
  end
24
24
  end
@@ -1,3 +1,3 @@
1
1
  module Paysafe
2
- VERSION = "0.9.4"
2
+ VERSION = "0.10.0"
3
3
  end
@@ -19,15 +19,16 @@ 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'
22
+ spec.required_ruby_version = '>= 2.4'
23
23
 
24
- spec.add_dependency "http", '>= 2.2.1', '< 5'
24
+ spec.add_dependency "http", '>= 4', '< 5'
25
25
 
26
26
  spec.add_development_dependency "rake"
27
27
  spec.add_development_dependency "bundler", "~> 2.0"
28
28
  spec.add_development_dependency "dotenv"
29
29
  spec.add_development_dependency "minitest"
30
30
  spec.add_development_dependency "minitest-reporters"
31
+ spec.add_development_dependency "minitest-mock_expectations"
31
32
  spec.add_development_dependency "vcr"
32
33
  spec.add_development_dependency "webmock"
33
34
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paysafe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.4
4
+ version: 0.10.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: 2019-09-24 00:00:00.000000000 Z
11
+ date: 2020-02-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http
@@ -16,7 +16,7 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 2.2.1
19
+ version: '4'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
22
  version: '5'
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: 2.2.1
29
+ version: '4'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: '5'
@@ -100,6 +100,20 @@ dependencies:
100
100
  - - ">="
101
101
  - !ruby/object:Gem::Version
102
102
  version: '0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: minitest-mock_expectations
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
103
117
  - !ruby/object:Gem::Dependency
104
118
  name: vcr
105
119
  requirement: !ruby/object:Gem::Requirement
@@ -136,11 +150,12 @@ extensions: []
136
150
  extra_rdoc_files: []
137
151
  files:
138
152
  - ".env.sample"
153
+ - ".github/workflows/ci.yml"
139
154
  - ".gitignore"
140
- - ".travis.yml"
141
155
  - CHANGELOG.md
142
156
  - CODE_OF_CONDUCT.md
143
157
  - Gemfile
158
+ - Gemfile.lock
144
159
  - LICENSE.txt
145
160
  - README.md
146
161
  - Rakefile
@@ -148,11 +163,22 @@ files:
148
163
  - bin/setup
149
164
  - lib/paysafe.rb
150
165
  - lib/paysafe/address.rb
166
+ - lib/paysafe/api/base_api.rb
167
+ - lib/paysafe/api/card_payments_api.rb
168
+ - lib/paysafe/api/customer_vault_api.rb
169
+ - lib/paysafe/api/payments_api.rb
151
170
  - lib/paysafe/authorization.rb
152
171
  - lib/paysafe/birth_date.rb
153
172
  - lib/paysafe/card.rb
154
173
  - lib/paysafe/card_expiry.rb
174
+ - lib/paysafe/configuration.rb
175
+ - lib/paysafe/customer.rb
155
176
  - lib/paysafe/error.rb
177
+ - lib/paysafe/gateway_response.rb
178
+ - lib/paysafe/payment.rb
179
+ - lib/paysafe/payment_method.rb
180
+ - lib/paysafe/payment_methods.rb
181
+ - lib/paysafe/payment_processor.rb
156
182
  - lib/paysafe/profile.rb
157
183
  - lib/paysafe/refinements/camel_case.rb
158
184
  - lib/paysafe/refinements/snake_case.rb
@@ -174,14 +200,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
174
200
  requirements:
175
201
  - - ">="
176
202
  - !ruby/object:Gem::Version
177
- version: 2.3.0
203
+ version: '2.4'
178
204
  required_rubygems_version: !ruby/object:Gem::Requirement
179
205
  requirements:
180
206
  - - ">="
181
207
  - !ruby/object:Gem::Version
182
208
  version: '0'
183
209
  requirements: []
184
- rubygems_version: 3.0.4
210
+ rubygems_version: 3.0.6
185
211
  signing_key:
186
212
  specification_version: 4
187
213
  summary: A Ruby interface to the Paysafe REST API.
@@ -1,18 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.3.4
4
- - 2.4.7
5
- - 2.5.6
6
- - 2.6.4
7
- before_install:
8
- - gem update --system
9
- - gem install bundler
10
- cache: bundler
11
- sudo: false
12
- fast_finish: true
13
- env:
14
- - SKIP_INTEGRATION=true
15
- notifications:
16
- email:
17
- on_success: always
18
- on_failure: always