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 +4 -4
- data/CHANGELOG.md +13 -1
- data/Gemfile.lock +5 -5
- data/README.md +44 -3
- data/lib/pci_proxy.rb +4 -0
- data/lib/pci_proxy/base.rb +24 -6
- data/lib/pci_proxy/check.rb +62 -0
- data/lib/pci_proxy/model/check_result.rb +18 -0
- data/lib/pci_proxy/model/tokenised_card.rb +44 -0
- data/lib/pci_proxy/token.rb +16 -5
- data/lib/pci_proxy/version.rb +1 -1
- data/pci_proxy.gemspec +1 -1
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 15c3fcaa4725e70d9e90fd5032c5b69195f238a972f01ed3e04000d63261f4b1
|
4
|
+
data.tar.gz: '080aeef98013bfbc7bb0ceb10dba0748f7ef494b37cb198ddc6f5a8b515be170'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c813ade53e12719e369a1acf59877bc5007753209c8b826511d9bb6bdab08d483690b61380aa1a0b2350901df5b0457f7479efb506aedb9210552f95dd5dd4a
|
7
|
+
data.tar.gz: '09649352281a2ab57a06f0f366f2f205b50b2ca7ca55488c3dc04f56e692fd0b626d61d06c44d45263e4cb711765d0f830d2010e5168e0993fafec29cdf088f9'
|
data/CHANGELOG.md
CHANGED
@@ -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.
|
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)
|
data/Gemfile.lock
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
pci_proxy (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.
|
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 (
|
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 (~>
|
26
|
+
rake (~> 13.0)
|
27
27
|
|
28
28
|
BUNDLED WITH
|
29
|
-
2.1.
|
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,
|
38
|
+
In the event of a 200 OK response, an instance of PciProxy::Model::TokenisedCard is returned:
|
39
39
|
|
40
40
|
```ruby
|
41
|
-
|
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
|
|
data/lib/pci_proxy.rb
CHANGED
@@ -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
|
data/lib/pci_proxy/base.rb
CHANGED
@@ -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
|
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 +
|
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
|
-
|
30
|
-
|
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
|
data/lib/pci_proxy/token.rb
CHANGED
@@ -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
|
-
|
7
|
-
|
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 [
|
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
|
-
|
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
|
data/lib/pci_proxy/version.rb
CHANGED
data/pci_proxy.gemspec
CHANGED
@@ -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", "~>
|
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.
|
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-
|
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: '
|
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: '
|
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
|