paycertify 0.0.5 → 0.0.6

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
  SHA1:
3
- metadata.gz: 73394e0bc6f1d06f09f82d27c78190e885e02942
4
- data.tar.gz: 1059912be2fe0e25f280d539c2e28a3a72781c2b
3
+ metadata.gz: e7d46ae5ef73791cdde93a470cf6021b0c56317c
4
+ data.tar.gz: b8ecdc243b35a74967e187817f8f4f177518b63e
5
5
  SHA512:
6
- metadata.gz: a4723260c7476caac031a519c7dcdfd6b89ac6676a9303b820e4c97f51d53e27ce5881af5fcb98b77d8c46813f7af7408d698cea578620a042924def88db5630
7
- data.tar.gz: 45dde778b7a4bd12f436932a878b14ae1ea077d83636ff58aead9ab984b5b736334dab3e11b4536c63cc9bdc44ab0b9df9820aeb19498d7134799c78c5f12831
6
+ metadata.gz: df5acf381b3a1f0a80eef90542f4ab79c00128383d5d43b0705c326c6c7deafa8ab9004c7ceec966bef0950963073f8dc1b6e3a104b41a0135c38de5df04228c
7
+ data.tar.gz: bf269069f26803f15a4d64de3607b7e285bf25dae42f3b17be4f431b582964a0d2276bfe6dd8f0e9d66f489ab15ab6f1d64da4a38e688645b54a43b3ec5dde3a
data/lib/paycertify.rb CHANGED
@@ -2,5 +2,6 @@ require 'active_support/rails'
2
2
  require 'active_support/core_ext'
3
3
 
4
4
  require 'paycertify/three_ds'
5
+ require 'paycertify/gateway'
5
6
 
6
7
  module PayCertify; end
@@ -0,0 +1,35 @@
1
+ require_relative './gateway/attribute_mapping'
2
+ require_relative './gateway/client'
3
+ require_relative './gateway/response'
4
+ require_relative './gateway/transaction'
5
+ require_relative './gateway/validation'
6
+
7
+ require 'json'
8
+
9
+ module PayCertify
10
+ class Gateway
11
+
12
+ attr_accessor :client, :attributes
13
+
14
+ delegate :api_key, :mode, to: :class
15
+
16
+ def initialize(attributes)
17
+ self.attributes = attributes
18
+ self.client = PayCertify::Gateway::Client.new(api_key: api_key, mode: mode)
19
+ end
20
+
21
+
22
+ def process_transaction!
23
+ transaction = PayCertify::Gateway::Transaction.new(client, attributes)
24
+ transaction.process
25
+ end
26
+
27
+ class << self
28
+ cattr_accessor :api_key, :mode
29
+
30
+ def configure(&block)
31
+ yield self if block_given?
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,42 @@
1
+ module PayCertify
2
+ class Gateway
3
+ module AttributeMapping
4
+ module_function
5
+ def wrapper_to_gateway
6
+ {
7
+ 'Amount' => :amount,
8
+ 'Currency' => :currency,
9
+ 'CardNum' => :card_number,
10
+ 'NameOnCard' => :name_on_card,
11
+ 'CVNum' => :cvv,
12
+ 'InvNum' => :transaction_id,
13
+ 'PNRef' => :transaction_id,
14
+ 'Street' => :billing_address,
15
+ 'City' => :billing_city,
16
+ 'State' => :billing_state,
17
+ 'Zip' => :billing_zip,
18
+ 'Country' => :billing_country,
19
+ 'ShippingStreet' => :billing_address,
20
+ 'ShippingCity' => :billing_city,
21
+ 'ShippingState' => :billing_state,
22
+ 'ShippingZip' => :billing_zip,
23
+ 'ShippingCountry' => :billing_country,
24
+ 'MobilePhone' => :phone,
25
+ 'Email' => :email,
26
+ 'Description' => :order_description,
27
+ 'CustomerID' => :customer_id,
28
+ 'ServerID' => :ip,
29
+ 'ExtData' => :metadata
30
+ }
31
+ end
32
+
33
+ def expiration_date(transaction)
34
+ { 'ExpDate' => [transaction.expiration_month, transaction.expiration_year].join }
35
+ end
36
+
37
+ def transaction_type(transaction)
38
+ { 'TransType' => transaction.type.to_s.capitalize }
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,55 @@
1
+ require 'faraday'
2
+
3
+ module PayCertify
4
+ class Gateway
5
+ class Client
6
+
7
+ attr_accessor :api_key, :mode, :response
8
+
9
+ def initialize(api_key:, mode:)
10
+ self.api_key = api_key
11
+ self.mode = mode.to_s.to_sym
12
+ end
13
+
14
+ def live?
15
+ mode.to_sym == :live
16
+ end
17
+
18
+ def api_endpoint
19
+ @api_endpoint ||= 'https://'+ (live?? 'gateway' : 'demo') +'.paycertify.net'
20
+ end
21
+
22
+ def post(path:, data:)
23
+ body = data.merge('ApiToken' => api_key)
24
+
25
+ response = connection.post do |request|
26
+ request.url path
27
+ request.body = body
28
+ end
29
+
30
+ respond_with response
31
+ end
32
+
33
+ def success?
34
+ response.status < 400
35
+ end
36
+
37
+ def error?
38
+ !success?
39
+ end
40
+
41
+ private
42
+ def connection
43
+ @connection ||= Faraday.new(url: api_endpoint, ssl: {verify: false}) do |faraday|
44
+ faraday.request :url_encoded
45
+ faraday.response :logger
46
+ faraday.adapter Faraday.default_adapter
47
+ end
48
+ end
49
+
50
+ def respond_with(response)
51
+ self.response = PayCertify::Gateway::Response.new(response)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,31 @@
1
+ module PayCertify
2
+ class Gateway
3
+ class Response < HashWithIndifferentAccess
4
+
5
+ APPROVED = '0'.freeze
6
+
7
+ attr_accessor :status, :original_body
8
+
9
+ def initialize(response)
10
+ self.status = response.status
11
+ self.original_body = Hash.from_xml(response.body)['Response']
12
+
13
+ super(
14
+ id: original_body['PNRef'],
15
+ status: response.status,
16
+ status_string: original_body['Result'] == APPROVED ? 'approved' : 'declined',
17
+ message: [original_body['Message'], original_body['Message1'], original_body['Message2']].compact.join(' / '),
18
+ amount: original_body['Amount'].to_d,
19
+ auth_code: original_body['AuthCode'],
20
+ payment_type: original_body['PaymentType'],
21
+ card_number: original_body['Account'],
22
+ created_at: DateTime.strptime([original_body['TransDate'], original_body['TransTime']].join, '%m%d%Y%H%M%s')
23
+ )
24
+ end
25
+
26
+ def success?
27
+ self['status_string'] == 'approved'
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,54 @@
1
+ module PayCertify
2
+ class Gateway
3
+ class Transaction
4
+
5
+ TRANSACTION_PATH = '/ws/encgateway2.asmx/ProcessCreditCard'
6
+
7
+ attr_accessor :client, :original_attributes
8
+
9
+ attr_accessor :type, :amount, :currency, :card_number, :expiration_month, :expiration_year,
10
+ :name_on_card, :cvv, :transaction_id, :billing_address, :billing_city, :billing_state, :billing_country, :billing_zip,
11
+ :shipping_address, :shipping_city, :shipping_state, :shipping_country,
12
+ :shipping_zip, :email, :phone, :ip, :order_description, :customer_id, :metadata
13
+
14
+ def initialize(client, attributes)
15
+ self.original_attributes = attributes
16
+ self.client = client
17
+
18
+ validation.attributes.each do |key, value|
19
+ self.send("#{key}=", value)
20
+ end
21
+ end
22
+
23
+ def validation
24
+ @validation ||= PayCertify::Gateway::Validation.new(original_attributes)
25
+ end
26
+
27
+ def process
28
+ client.post(
29
+ path: TRANSACTION_PATH,
30
+ data: attributes_to_gateway_format
31
+ )
32
+ end
33
+
34
+ private
35
+ def attributes_to_gateway_format
36
+ {}.tap do |formatted|
37
+ attribute_mapping = PayCertify::Gateway::AttributeMapping
38
+
39
+ attribute_mapping.wrapper_to_gateway.each do |key, value|
40
+ [value].flatten.tap do |method_chain|
41
+ new_value = method_chain.map { |method_name| self.send(method_name) }.join
42
+ formatted[key] = new_value
43
+ end
44
+ end
45
+
46
+ formatted.merge! attribute_mapping.expiration_date(self)
47
+ formatted.merge! attribute_mapping.transaction_type(self)
48
+
49
+ formatted
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,174 @@
1
+ require 'ipaddr'
2
+
3
+ module PayCertify
4
+ class Gateway
5
+ class Validation
6
+
7
+ ATTRIBUTES = [
8
+ # Mandatory fields
9
+ { name: :type, validation: :type_validation, required: true },
10
+ { name: :amount, validation: :amount_validation, required: true },
11
+ { name: :currency, validation: :currency_validation, required: true },
12
+ { name: :card_number, validation: :card_number_validation, required: true },
13
+ { name: :expiration_month, validation: :expiration_month_validation, required: true },
14
+ { name: :expiration_year, validation: :expiration_year_validation, required: true },
15
+ { name: :name_on_card, validation: :no_validation, required: true },
16
+ { name: :cvv, validation: :no_validation, required: true },
17
+ { name: :transaction_id, validation: :no_validation, required: true },
18
+ { name: :billing_city, validation: :no_validation, required: true },
19
+ { name: :billing_state, validation: :no_validation, required: true },
20
+ { name: :billing_country, validation: :no_validation, required: true },
21
+ { name: :billing_zip, validation: :zip_validation, required: true },
22
+
23
+ # Optional fields
24
+ { name: :billing_address, validation: :no_validation, required: false },
25
+ { name: :shipping_address, validation: :no_validation, required: false },
26
+ { name: :shipping_city, validation: :no_validation, required: false },
27
+ { name: :shipping_state, validation: :no_validation, required: false },
28
+ { name: :shipping_country, validation: :no_validation, required: false },
29
+ { name: :shipping_zip, validation: :zip_validation, required: false },
30
+ { name: :email, validation: :email_validation, required: false },
31
+ { name: :phone, validation: :no_validation, required: false },
32
+ { name: :ip, validation: :ip_validation, required: false },
33
+ { name: :order_description, validation: :no_validation, required: false },
34
+ { name: :customer_id, validation: :no_validation, required: false },
35
+ { name: :metadata, validation: :no_validation, required: false }
36
+ ]
37
+
38
+ ALLOWED_TYPES = %w(sale auth refund void force recurring)
39
+ ALLOWED_CURRENCIES = %w(USD EUR)
40
+ CREDIT_CARD_REGEX = /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/i
41
+ EMAIL_REGEX = /\A([\w+\-].?)+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
42
+
43
+ class ValidationError < StandardError; end
44
+
45
+ attr_accessor :attributes, :errors
46
+
47
+ def initialize(attributes={})
48
+ self.attributes = attributes
49
+
50
+ ATTRIBUTES.each do |attribute|
51
+ presence_validation(attribute) if attribute[:required]
52
+ send(attribute[:validation], attribute) if value_for(attribute).present?
53
+ end
54
+ end
55
+
56
+ protected
57
+ def presence_validation(attribute)
58
+ if value_for(attribute).blank?
59
+ raise ValidationError, "Required attribute not present: #{attribute[:name]}."
60
+ end
61
+ end
62
+
63
+ def no_validation(_); end
64
+
65
+ def type_validation(attribute)
66
+ unless value_for(attribute).try(:to_s).in?(ALLOWED_TYPES)
67
+ raise ValidationError, error_message(attribute, "Must be one of #{ALLOWED_TYPES.join(', ')}")
68
+ end
69
+ end
70
+
71
+ def amount_validation(attribute)
72
+ set_attribute(attribute, Float(value_for(attribute)))
73
+ rescue ArgumentError
74
+ raise ValidationError, error_message(attribute, "Must be a float, integer or decimal")
75
+ end
76
+
77
+ def currency_validation(attribute)
78
+ set_attribute(attribute, value_for(attribute).upcase)
79
+
80
+ unless value_for(attribute).try(:to_s).in?(ALLOWED_CURRENCIES)
81
+ raise ValidationError, error_message(attribute, "Must be one of #{ALLOWED_CURRENCIES.join(', ')}")
82
+ end
83
+ end
84
+
85
+ def card_number_validation(attribute)
86
+ # Non decimal numbers should be stripped to match.
87
+ set_attribute(attribute, value_for(attribute).gsub(/\D/, ''))
88
+
89
+ unless value_for(attribute) =~ CREDIT_CARD_REGEX
90
+ raise ValidationError, error_message(attribute, "Doesn't validate as a credit card.")
91
+ end
92
+ end
93
+
94
+ def zip_validation
95
+ # Check if it's a number
96
+ set_attribute(attribute, Integer(value_for(attribute)).to_s)
97
+
98
+ if value_for(attribute).length > 5
99
+ # Raise validation error
100
+ end
101
+ end
102
+
103
+ def expiration_month_validation(attribute)
104
+ # if a string, check if length = 2 and smaller than int 12
105
+ # if int, transform into string with zero pad and check if smaller than int 12
106
+ integer = Integer(value_for(attribute))
107
+
108
+ if integer > 12
109
+ raise ValidationError, error_message(attribute, "Must be smaller than 12.")
110
+ end
111
+
112
+ set_attribute(attribute, integer.to_s.rjust(2, '0'))
113
+
114
+ rescue ArgumentError
115
+ raise ValidationError, error_message(attribute, "Must be an integer.")
116
+ end
117
+
118
+ def expiration_year_validation(attribute)
119
+ # if length = 4, strip to 2;
120
+ # if a string, check if length = 2 and smaller than int 12
121
+ # if int, transform into string with zero pad and check if smaller than int 12
122
+
123
+
124
+ is_four_digit = if value_for(attribute).is_a?(String)
125
+ value_for(attribute).length == 4
126
+ else
127
+ value_for(attribute) > 999
128
+ end
129
+
130
+ integer_value = Integer(value_for(attribute))
131
+
132
+ set_attribute(attribute, integer_value.to_s.last(2))
133
+ rescue
134
+ raise ValidationError, error_message(attribute, "Must be a 2 to 4-digit string.")
135
+ end
136
+
137
+ def zip_validation(attribute)
138
+ set_attribute(attribute, Integer(value_for(attribute)).to_s)
139
+
140
+ unless value_for(attribute).length == 5
141
+ raise ValidationError, error_message(attribute, "Must be a 5-digit string that can evaluate to a number.")
142
+ end
143
+
144
+ rescue
145
+ raise ValidationError, error_message(attribute, "Must be a 5-digit string that can evaluate to a number.")
146
+ end
147
+
148
+ def email_validation(attribute)
149
+ unless value_for(attribute) =~ EMAIL_REGEX
150
+ raise ValidationError, error_message(attribute, "Doesn't validate as an email.")
151
+ end
152
+ end
153
+
154
+ def ip_validation(attribute)
155
+ IPAddr.new(value_for(attribute))
156
+ rescue IPAddr::InvalidAddressError
157
+ raise ValidationError, error_message(attribute, "Doesn't validate as an IP.")
158
+ end
159
+
160
+ private
161
+ def value_for(attribute)
162
+ attributes[attribute[:name]]
163
+ end
164
+
165
+ def set_attribute(attribute, value)
166
+ self.attributes[attribute[:name]] = value
167
+ end
168
+
169
+ def error_message(attribute, message)
170
+ "Attribute #{attribute[:name]} passed as: #{value_for(attribute)}. #{message}"
171
+ end
172
+ end
173
+ end
174
+ end
@@ -1,5 +1,3 @@
1
- require 'faraday'
2
-
3
1
  module PayCertify
4
2
  class ThreeDS
5
3
  class Callback < HashWithIndifferentAccess
@@ -66,10 +66,6 @@ module PayCertify
66
66
  end
67
67
 
68
68
  def respond_with(response)
69
- 3.times{puts}
70
- puts 'Response -----'
71
- p response.body
72
- 10.times{puts}
73
69
  self.response = response
74
70
  JSON.parse(response.body)
75
71
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paycertify
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - PayCertify Engineering Team
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '5.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: '5.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: faraday
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: 0.11.0
34
34
  type: :runtime
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: '0'
40
+ version: 0.11.0
41
41
  description: Interact with the Gateway, 3DS, Kount, and FraudPortal
42
42
  email: engineering@paycertify.com
43
43
  executables: []
@@ -45,6 +45,12 @@ extensions: []
45
45
  extra_rdoc_files: []
46
46
  files:
47
47
  - lib/paycertify.rb
48
+ - lib/paycertify/gateway.rb
49
+ - lib/paycertify/gateway/attribute_mapping.rb
50
+ - lib/paycertify/gateway/client.rb
51
+ - lib/paycertify/gateway/response.rb
52
+ - lib/paycertify/gateway/transaction.rb
53
+ - lib/paycertify/gateway/validation.rb
48
54
  - lib/paycertify/three_ds.rb
49
55
  - lib/paycertify/three_ds/callback.rb
50
56
  - lib/paycertify/three_ds/client.rb