paysafe 0.9.4 → 0.10.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
  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