pci_proxy 1.0.0 → 1.2.2

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: 0d2e838d63aadc950a829e6d3b50e7c6fddcfffdb216ae9f7646d4dc06fbb930
4
- data.tar.gz: 42b52b3106e60c53a75de4424f445edca6ddc9251c899f12f07dfeadee30b722
3
+ metadata.gz: 91dcc3b1b4d3f7b715bf9b97096e309041e263839f0c98b9b605ed6fa4b652d9
4
+ data.tar.gz: 10fe3831fe0b63dccfa142a21bf91983898574245c2c37ea4f62c2729a4f22ed
5
5
  SHA512:
6
- metadata.gz: e9c4197a8b75b8070f5174d0a64ffd3fdb374ce4e279bc45b0f2386be6ccfdda3172baac6b00c8ac795526ac45e92bd4e19f41a23f959c5df803643d0b46562a
7
- data.tar.gz: 11735efca4a2ce834ea0c6a05906b38649796b999eda37e9f6b697d39bce4b494cc1e92ba376d41a67e5ba270376a91388da925173541c2a46a8b09ec76f3b0f
6
+ metadata.gz: 8a7f74d2b7656f9227f8ead57e15ecbb1abf03ce7b6b0b1e7eedb30c5153571b55b7d7dfc13e41405ff16824ca0257600700fd1ae508c5ff7dd82d2930106f45
7
+ data.tar.gz: 5246d64cea9102600e09adfe254d25ea9368e4b1d91842f7feda9f9997ef426d2377b1403e241d80d6319a2e6d29f236e933c3ca764ad341743ece1e70dff05b
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  .DS_Store
2
+ *.gem
2
3
  /.idea/
3
4
  /.bundle/
4
5
  /.yardoc
@@ -1,7 +1,13 @@
1
- # PciProxy
1
+ # PciProxy Change Log
2
2
 
3
3
  A simple client library for [PCI Proxy](https://pci-proxy.com)'s API
4
4
 
5
- #CHANGE LOG
5
+ v1.2.1 - 30th January 2020 - Minor tweaks; cleanups; update documentation
6
6
 
7
- v1.0.0 - 14th January 2020 - Initial release - covers the [Token API](https://docs.pci-proxy.com/collect-and-store-cards/capture-iframes/token-api)
7
+ v1.2.0 - 30th January 2020 - Add support for the [Check API](https://docs.pci-proxy.com/use-stored-cards/check)
8
+
9
+ v1.1.0 - 17th January 2020 - Return an instance of PciProxy::Model::TokenisedCard instead of plain Hash
10
+
11
+ v1.0.1 - 14th January 2020 - Relax dependency version requirements
12
+
13
+ v1.0.0 - 14th January 2020 - Initial release - covering the [Token API](https://docs.pci-proxy.com/collect-and-store-cards/capture-iframes/token-api)
@@ -1,19 +1,19 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pci_proxy (1.0.0)
5
- faraday (~> 0.17.3)
6
- multi_json (~> 1.14.1)
4
+ pci_proxy (1.2.2)
5
+ faraday (>= 0.8.9)
6
+ multi_json (>= 1.10.0)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- faraday (0.17.3)
11
+ faraday (1.0.1)
12
12
  multipart-post (>= 1.2, < 3)
13
13
  minitest (5.13.0)
14
14
  multi_json (1.14.1)
15
15
  multipart-post (2.1.1)
16
- rake (10.5.0)
16
+ rake (13.0.1)
17
17
 
18
18
  PLATFORMS
19
19
  java
@@ -23,7 +23,7 @@ DEPENDENCIES
23
23
  bundler (~> 2.0)
24
24
  minitest (~> 5.0)
25
25
  pci_proxy!
26
- rake (~> 10.0)
26
+ rake (~> 13.0)
27
27
 
28
28
  BUNDLED WITH
29
- 2.1.2
29
+ 2.1.4
data/README.md CHANGED
@@ -20,7 +20,7 @@ Or install it yourself as:
20
20
 
21
21
  ## Usage
22
22
 
23
- Initially, this gem only covers the [Token API](https://docs.pci-proxy.com/collect-and-store-cards/capture-iframes/token-api), which converts a transactionId from the [secure fields](https://docs.pci-proxy.com/collect-and-store-cards/capture-iframes) mechanism into tokenised card PAN and CVV.
23
+ Initially, this gem only covers the [Token API](https://docs.pci-proxy.com/collect-and-store-cards/capture-iframes/token-api), which converts a transactionId from the [secure fields](https://docs.pci-proxy.com/collect-and-store-cards/capture-iframes) mechanism into tokenised card PAN and CVV, and the [Check API](https://docs.pci-proxy.com/use-stored-cards/check) which allows verification of a card token.
24
24
 
25
25
  Pull requests are most welcome for coverage of other PCI Proxy APIs :)
26
26
  ### Token API - Usage
@@ -35,20 +35,61 @@ And execute a token exchange like so:
35
35
  client.execute(transaction_id: '1234567890')
36
36
  ```
37
37
 
38
- In the event of a 200 OK response, the JSON response body is returned as a hash, for example:
38
+ In the event of a 200 OK response, an instance of PciProxy::Model::TokenisedCard is returned:
39
39
 
40
40
  ```ruby
41
- {"aliasCC"=>"411111GGCMUJ1111", "aliasCVV"=>"vCslSwP0SQ9JXJy-nDzLKHaS"}
41
+ #<PciProxy::Model::TokenisedCard:0x00007fda073453f8 @pan_token="411111GGCMUJ1111", @cvv_token="b8XeAbhQQES6OVWTpOCaAscj", @type_slug=:visa>
42
42
  ```
43
+ (```response``` attr_reader value omitted for clarity)
44
+
45
+ This object has attr_readers for ```pan_token```, ```cvv_token``` and ```type_slug``` which will return one of the following symbols:
46
+
47
+ ```ruby
48
+ [:visa, :mastercard, :amex, :diners, :discovery, :jcb, :elo, :cup, :unknown]
49
+ ```
50
+
51
+ It also has an attr_reader for ```response``` which is the raw parsed JSON response, as a hash.
43
52
 
44
53
  In the event of an error, a subclass of ```PciProxyAPIError``` will be raised.
45
54
 
46
55
  The most likely error is that the transactionId temporary token has expired, resulting in:
47
56
 
48
- ```ruby
57
+ ```
49
58
  PciProxy::BadRequestError (HTTP status: 400, Response: Tokenization not found)
50
59
  ```
51
60
 
61
+ ### Check API - Usage
62
+
63
+ Create an instance of ```PciProxy::Check``` and call ```execute``` as follows:
64
+
65
+ ```ruby
66
+ client = PciProxy::Check.new(api_username: 'username', api_password: 'password')
67
+ ```
68
+
69
+ And execute a card verification like so:
70
+
71
+ ```ruby
72
+ client.execute(reference: 'foo', card_token: '411111GGCMUJ1111', card_type: :visa, expiry_month: 1, expiry_year: 2022)
73
+ ```
74
+
75
+ In all cases (success, denoted by 200 OK from the API, or error, denoted by non-200 response), an instance of ```PciProxy::Model::CheckResult``` is returned.
76
+
77
+ This object has attr_readers for ```auth_code```, ```transaction_id```, ```status``` and ```error```
78
+
79
+ It also has an attr_reader for ```response``` which is the raw parsed JSON response, as a hash.
80
+
81
+ With a successful response, the object's ```status``` attribute will have the value ```:success```, and the ```auth_code``` and ```transaction_id``` values will be available:
82
+ ```ruby
83
+ #<PciProxy::Model::CheckResult:0x00007fbda2186fe8 @auth_code="124101", @transaction_id="190828124101219812", @error=nil, @status=:success>
84
+ ```
85
+ (```response``` attr_reader value omitted for clarity)
86
+
87
+ With an unsuccessful response, the object's ```status``` attribute will have the value ```:error```:
88
+ ```ruby
89
+ #<PciProxy::Model::CheckResult:0x00007fbda2186fe8 @auth_code=nil, @transaction_id=nil, @error={"code"=>"UNAUTHORIZED", "message"=>"The account is not configured to use this API."}, @status=:error>
90
+ ```
91
+ (```response``` attr_reader value omitted for clarity)
92
+
52
93
  ## Changes
53
94
  See [Changelog](CHANGELOG.md)
54
95
 
@@ -2,7 +2,10 @@ require 'bundler'
2
2
  require "pci_proxy/version"
3
3
 
4
4
  require 'pci_proxy/base'
5
+ require 'pci_proxy/check'
5
6
  require 'pci_proxy/token'
7
+ require 'pci_proxy/model/check_result'
8
+ require 'pci_proxy/model/tokenised_card'
6
9
 
7
10
  module PciProxy
8
11
  PciProxyAPIError = Class.new(StandardError)
@@ -19,4 +22,5 @@ module PciProxy
19
22
  HTTP_FORBIDDEN_CODE = 403
20
23
  HTTP_NOT_FOUND_CODE = 404
21
24
  HTTP_UNPROCESSABLE_ENTITY_CODE = 429
25
+
22
26
  end
@@ -4,6 +4,8 @@ require 'multi_json'
4
4
  module PciProxy
5
5
  class Base
6
6
 
7
+ JSON_UTF8_CONTENT_TYPE = 'application/json; charset=UTF-8'.freeze
8
+
7
9
  attr_reader :api_endpoint, :api_username, :api_password
8
10
 
9
11
  ##
@@ -17,19 +19,35 @@ module PciProxy
17
19
  end
18
20
 
19
21
  ##
20
- # Perform the API request
22
+ # Perform an API request via HTTP GET
21
23
  #
22
24
  # @param +endpoint+ [String] (Optional) - the API endpoint to hit - defaults to the value of the api_endpoint reader
23
- # @param +http_method+ [Symbol] (Optional) - the HTTP method to use - defaults to GET
24
- # @param +params+ [Hash] (Optional) - any parameters to supply to the API call
25
+ # @param +params+ [Hash] (Optional) - any URL parameters to supply to the API call
25
26
  #
26
27
  # @raise [PciProxyAPIError] in cases where the API responds with a non-200 response code
27
28
  # @return [Hash] parsed JSON response
29
+ def api_get(endpoint: @api_endpoint, params: {}, raise_on_error: true)
30
+ response = client.get(endpoint, params)
31
+
32
+ if raise_on_error == false || response.status == HTTP_OK_CODE
33
+ return MultiJson.load(response.body)
34
+ end
35
+
36
+ raise error_class(response), "HTTP status: #{response.status}, Response: #{response.body}"
37
+ end
38
+
39
+ ##
40
+ # Perform an API request via HTTP POST
41
+ #
42
+ # @param +endpoint+ [String] (Optional) - the API endpoint to hit - defaults to the value of the api_endpoint reader
43
+ # @param +body+ [Hash] (Optional) - the API request payload (will be converted to JSON)
28
44
  #
29
- def request(endpoint: @api_endpoint, http_method: :get, params: {})
30
- response = client.public_send(http_method, endpoint, params)
45
+ # @raise [PciProxyAPIError] in cases where the API responds with a non-200 response code
46
+ # @return [Hash] parsed JSON response
47
+ def api_post(endpoint: @api_endpoint, body: {}, raise_on_error: true)
48
+ response = client.post(endpoint, MultiJson.dump(body), "Content-Type" => JSON_UTF8_CONTENT_TYPE)
31
49
 
32
- if response.status == HTTP_OK_CODE
50
+ if raise_on_error == false || response.status == HTTP_OK_CODE
33
51
  return MultiJson.load(response.body)
34
52
  end
35
53
 
@@ -0,0 +1,62 @@
1
+ module PciProxy
2
+ class Check < Base
3
+
4
+ SANDBOX_ENDPOINT = 'https://api.sandbox.datatrans.com/v1/transactions/validate'.freeze
5
+ LIVE_ENDPOINT = 'https://api.datatrans.com/v1/transactions/validate'.freeze
6
+
7
+ CHF = 'CHF'.freeze
8
+ EUR = 'EUR'.freeze
9
+
10
+ # error codes
11
+ # "UNKNOWN_ERROR", "UNRECOGNIZED_PROPERTY", "INVALID_PROPERTY", "INVALID_TRANSACTION_STATUS", "TRANSACTION_NOT_FOUND", "INVALID_JSON_PAYLOAD", "UNAUTHORIZED", "EXPIRED_CARD", "INVALID_CARD", "UNSUPPORTED_CARD", "DUPLICATED_REFNO", "DECLINED", "BLOCKED_BY_VELOCITY_CHECKER", "CLIENT_ERROR" , "SERVER_ERROR"
12
+
13
+ ##
14
+ # Initialise with the specified +api_username+ and +api_password+ from PCI Proxy.
15
+ #
16
+ # Defaults to the sandbox API endpoint - to use the live environment,
17
+ # supply the LIVE_ENDPOINT constant as the value of the +endpoint+ kwarg
18
+ def initialize(api_username:, api_password:, endpoint: SANDBOX_ENDPOINT)
19
+ @api_endpoint = endpoint
20
+ @api_username = api_username
21
+ @api_password = api_password
22
+ end
23
+
24
+ ##
25
+ # Perform a check API request to verify the card token
26
+ #
27
+ # @param +reference+ [String] - caller's unique reference (any non-empty string value)
28
+ # @param +card_token+ [String] - card token obtained from the Token API - see PciProxy::Token
29
+ # @param +card_type+ [Symbol / String] - one of 'visa', 'mastercard' or 'amex'
30
+ # @param +expiry_month+ [Integer / String] - integer 1-12, or string '01' - '12'
31
+ # @param +expiry_year+ [Integer / String] - two or four digit year as a string or integer
32
+ # @param +currency+ [Integer / String] (Optional) - one of 'CHF' or 'EUR' - will default by card type where not specified
33
+ #
34
+ # @raise [PciProxyAPIError] in cases where the API responds with a non-200 response code
35
+ # @return [Hash] result from PCI Proxy, decoded from JSON
36
+ def execute(reference:, card_token:, card_type:, expiry_month:, expiry_year:, currency: nil)
37
+ raise "reference is required" if reference.empty?
38
+ raise "card_token is required" unless card_token && !card_token.empty?
39
+ raise "card_type must be one of 'visa', 'mastercard' or 'amex'" unless [:visa, :mastercard, :amex].include?(card_type.to_sym)
40
+ raise "invalid expiry_month" unless (1..12).include?(expiry_month.to_i)
41
+ raise "invalid expiry_year" unless expiry_year.to_i > 0
42
+
43
+ # default the currency where not specified - according to the documentation Amex should use EUR, Visa and MC should use CHF
44
+ currency ||= :amex == card_type.to_sym ? EUR : CHF
45
+
46
+ card = {
47
+ alias: card_token,
48
+ expiryMonth: expiry_month.to_s,
49
+ expiryYear: expiry_year.to_s.chars.last(2).join
50
+ }
51
+
52
+ body = {
53
+ refno: reference,
54
+ currency: currency,
55
+ card: card
56
+ }
57
+
58
+ PciProxy::Model::CheckResult.new(api_post(endpoint: @api_endpoint, body: body, raise_on_error: false))
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,18 @@
1
+ module PciProxy
2
+ module Model
3
+ class CheckResult
4
+
5
+ attr_reader :response, :status, :error, :auth_code, :transaction_id
6
+
7
+ def initialize(response)
8
+ @response = response
9
+ @auth_code = response["acquirerAuthorizationCode"]
10
+ @transaction_id = response["transactionId"]
11
+ @error = response["error"]
12
+
13
+ @status = @auth_code && @transaction_id && !@error ? :success : :error
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,43 @@
1
+ module PciProxy
2
+ module Model
3
+ class TokenisedCard
4
+
5
+ attr_reader :response, :pan_token, :cvv_token, :type_slug
6
+
7
+ def initialize(response)
8
+ @response = response
9
+ @pan_token = response["aliasCC"]
10
+ @cvv_token = response["aliasCVV"]
11
+ @type_slug = slug_for(response["paymentMethod"])
12
+ end
13
+
14
+ private
15
+
16
+ def slug_for(payment_method)
17
+ return nil if payment_method.nil?
18
+
19
+ case payment_method
20
+ when 'VIS'
21
+ :visa
22
+ when 'ECA'
23
+ :mastercard
24
+ when 'AMX'
25
+ :amex
26
+ when 'DIN'
27
+ :diners
28
+ when 'DIS'
29
+ :discovery
30
+ when 'JCB'
31
+ :jcb
32
+ when 'ELO'
33
+ :elo
34
+ when 'CUP'
35
+ :cup
36
+ else
37
+ :unknown
38
+ end
39
+
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,24 +1,35 @@
1
1
  module PciProxy
2
2
  class Token < Base
3
3
 
4
+ SANDBOX_ENDPOINT = 'https://api.sandbox.datatrans.com/upp/services/v1/inline/token'.freeze
5
+ LIVE_ENDPOINT = 'https://api.datatrans.com/upp/services/v1/inline/token'.freeze
6
+
4
7
  ##
5
8
  # Initialise with the specified +api_username+ and +api_password+ from PCI Proxy.
6
- def initialize(api_username:, api_password:)
7
- @api_endpoint = 'https://api.sandbox.datatrans.com/upp/services/v1/inline/token'.freeze
9
+ #
10
+ # Defaults to the sandbox API endpoint - to use the live environment,
11
+ # supply the LIVE_ENDPOINT constant as the value of the +endpoint+ kwarg
12
+ def initialize(api_username:, api_password:, endpoint: SANDBOX_ENDPOINT)
13
+ @api_endpoint = endpoint
8
14
  @api_username = api_username
9
15
  @api_password = api_password
10
16
  end
11
17
 
12
18
  ##
13
- # Perform a token request to turn the specified +transaction_id+ into card and CVV tokens
19
+ # Perform a token API request to turn the specified +transaction_id+ into card and CVV tokens
14
20
  #
15
21
  # @param +return_payment_method+ (true/false) - whether or not to return the identified payment method (default: true)
16
22
  # @param +cvv_mandatory+ (true/false) - whether or not to consider the CVV alias should be mandatory (default: false)
17
23
  #
18
24
  # @raise [PciProxyAPIError] in cases where the API responds with a non-200 response code
19
- # @return [Hash] result from PCI Proxy, decoded from JSON
25
+ # @return [PciProxy::Model::TokenisedCard] wrapper object around the JSON response
20
26
  def execute(transaction_id:, return_payment_method: true, cvv_mandatory: false)
21
- request(params: { transactionId: transaction_id, returnPaymentMethod: return_payment_method, mandatoryAliasCVV: cvv_mandatory })
27
+ raise "transaction_id is required" unless transaction_id && !transaction_id.empty?
28
+
29
+ PciProxy::Model::TokenisedCard.new(api_get(params: {
30
+ transactionId: transaction_id,
31
+ returnPaymentMethod: return_payment_method,
32
+ mandatoryAliasCVV: cvv_mandatory }))
22
33
  end
23
34
 
24
35
  end
@@ -1,3 +1,3 @@
1
1
  module PciProxy
2
- VERSION = "1.0.0"
2
+ VERSION = "1.2.2"
3
3
  end
@@ -27,9 +27,9 @@ Gem::Specification.new do |spec|
27
27
  spec.require_paths = ["lib"]
28
28
 
29
29
  spec.add_development_dependency "bundler", "~> 2.0"
30
- spec.add_development_dependency "rake", "~> 10.0"
30
+ spec.add_development_dependency "rake", "~> 13.0"
31
31
  spec.add_development_dependency "minitest", "~> 5.0"
32
32
 
33
- spec.add_dependency "faraday", "~> 0.17.3"
34
- spec.add_dependency "multi_json", "~> 1.14.1"
33
+ spec.add_dependency "faraday", ">= 0.8.9"
34
+ spec.add_dependency "multi_json", ">= 1.10.0"
35
35
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pci_proxy
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rory Sinclair
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-01-14 00:00:00.000000000 Z
11
+ date: 2020-05-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: '13.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: '13.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: minitest
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -56,30 +56,30 @@ dependencies:
56
56
  name: faraday
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: 0.17.3
61
+ version: 0.8.9
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: 0.17.3
68
+ version: 0.8.9
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: multi_json
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - "~>"
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: 1.14.1
75
+ version: 1.10.0
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - "~>"
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
- version: 1.14.1
82
+ version: 1.10.0
83
83
  description: A ruby library covering the server-server aspects of the PCI-Proxy API
84
84
  email:
85
85
  - rory@mungler.com
@@ -100,6 +100,9 @@ files:
100
100
  - bin/setup
101
101
  - lib/pci_proxy.rb
102
102
  - lib/pci_proxy/base.rb
103
+ - lib/pci_proxy/check.rb
104
+ - lib/pci_proxy/model/check_result.rb
105
+ - lib/pci_proxy/model/tokenised_card.rb
103
106
  - lib/pci_proxy/token.rb
104
107
  - lib/pci_proxy/version.rb
105
108
  - pci_proxy.gemspec