paycertify 0.0.5 → 0.0.6

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
  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