pci_proxy 1.0.1 → 1.2.3

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: b3b3d7fc9150a054af99f760133f3f0e0c61b4101b9c9aaa6226697b2d19fa05
4
- data.tar.gz: 9f0a96730a817e58c296f5c27b338204ba6d8fc007e15b051fa8785670dfc18c
3
+ metadata.gz: 15c3fcaa4725e70d9e90fd5032c5b69195f238a972f01ed3e04000d63261f4b1
4
+ data.tar.gz: '080aeef98013bfbc7bb0ceb10dba0748f7ef494b37cb198ddc6f5a8b515be170'
5
5
  SHA512:
6
- metadata.gz: c48acd895516a36dc3d5d3980b22164db5711e79baec8f96a3268748dc73d00fa220013cd764480eeaf4702787495aeee904e22b86acaf8aa3e008e85f2d0fec
7
- data.tar.gz: 5cfaa045d5167aa2da4a5c6a477ccdc59ed34f7e05af5789fbbffced9d4536335cedf29d4371889d03b237114ed30383fb7f9ebd0764cb0ded29655c406bd468
6
+ metadata.gz: 5c813ade53e12719e369a1acf59877bc5007753209c8b826511d9bb6bdab08d483690b61380aa1a0b2350901df5b0457f7479efb506aedb9210552f95dd5dd4a
7
+ data.tar.gz: '09649352281a2ab57a06f0f366f2f205b50b2ca7ca55488c3dc04f56e692fd0b626d61d06c44d45263e4cb711765d0f830d2010e5168e0993fafec29cdf088f9'
@@ -2,4 +2,16 @@
2
2
 
3
3
  A simple client library for [PCI Proxy](https://pci-proxy.com)'s API
4
4
 
5
- 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)
5
+ v1.2.3 - 26th May 2020 - Add support for masked_pan from tokenised card
6
+
7
+ v1.2.2 - 25th May 2020 - Fix bug in handling of card type where passed as a string rather than a symbol; Require rake 13.0 (CVE-2020-8130)
8
+
9
+ v1.2.1 - 30th January 2020 - Minor tweaks; cleanups; update documentation
10
+
11
+ v1.2.0 - 30th January 2020 - Add support for the [Check API](https://docs.pci-proxy.com/use-stored-cards/check)
12
+
13
+ v1.1.0 - 17th January 2020 - Return an instance of PciProxy::Model::TokenisedCard instead of plain Hash
14
+
15
+ v1.0.1 - 14th January 2020 - Relax dependency version requirements
16
+
17
+ 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.1)
4
+ pci_proxy (1.2.2)
5
5
  faraday (>= 0.8.9)
6
6
  multi_json (>= 1.10.0)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- faraday (1.0.0)
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,11 +35,20 @@ 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
 
@@ -49,6 +58,38 @@ The most likely error is that the transactionId temporary token has expired, res
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,44 @@
1
+ module PciProxy
2
+ module Model
3
+ class TokenisedCard
4
+
5
+ attr_reader :response, :pan_token, :cvv_token, :type_slug, :masked_pan
6
+
7
+ def initialize(response)
8
+ @response = response
9
+ @pan_token = response["aliasCC"]
10
+ @cvv_token = response["aliasCVV"]
11
+ @masked_pan = response["maskedCard"]
12
+ @type_slug = slug_for(response["paymentMethod"])
13
+ end
14
+
15
+ private
16
+
17
+ def slug_for(payment_method)
18
+ return nil if payment_method.nil?
19
+
20
+ case payment_method
21
+ when 'VIS'
22
+ :visa
23
+ when 'ECA'
24
+ :mastercard
25
+ when 'AMX'
26
+ :amex
27
+ when 'DIN'
28
+ :diners
29
+ when 'DIS'
30
+ :discovery
31
+ when 'JCB'
32
+ :jcb
33
+ when 'ELO'
34
+ :elo
35
+ when 'CUP'
36
+ :cup
37
+ else
38
+ :unknown
39
+ end
40
+
41
+ end
42
+ end
43
+ end
44
+ 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.1"
2
+ VERSION = "1.2.3"
3
3
  end
@@ -27,7 +27,7 @@ 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
33
  spec.add_dependency "faraday", ">= 0.8.9"
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.1
4
+ version: 1.2.3
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-26 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
@@ -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