pixelpay_sdk 1.0.3 → 1.0.5

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: f3fad8309071227abe8c5b9440d6ede64798fed39278bbcf9c3b9c34d415c0f0
4
- data.tar.gz: f077574ba276639f53e92e75139c43de83741be6aec7e66dc9ad641c59279bb7
3
+ metadata.gz: d53ee74ca8bbed17b4bec55f9fe0061cdd14f841b5450d7ec8b5aed23b6a974a
4
+ data.tar.gz: a9835dec0d92cc864ec02b694c4b3f7de62345055f0db1529de31b00fe0e2e87
5
5
  SHA512:
6
- metadata.gz: b7e3c0014095332a1150f864edf943b6f0c4d97d59af2fd411fd10cbd60538766fd95dcc84723caa4401f86e1f1559e5dcb0b562f1a9a980f5b3a3397c652f7c
7
- data.tar.gz: ddbc8ad2636efa52dd36944cac62499dafb81badd69cb58446f96357448b8c117fe68179fc8797cea6778b8dd65ef1a78eba6ce3fb4dd556b7bd7c19b4fc2422
6
+ metadata.gz: 511b116d1b5281bf5009cce39f0c8cf6456b0faa34abc3e99a473e3e400c03b35e784450fc12612350fcf9272de3c49a953854d64e2f3b19e23d33f617f3a2ab
7
+ data.tar.gz: 53c3ad088779920e9842a187c15b006ce794836ce132242d7030b25f5b0c3b12c784cabb816d4e2fb7ce23f08ce0fa55fdf8a267f1f05d3641f0f6aa0b16338a
data/CHANGELOG.md CHANGED
@@ -5,6 +5,17 @@ El formato se basa en [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  y este proyecto se adhiere a [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
  Tipos de cambios: `Added`, `Changed`, `Deprecated`, `Removed`, `Fixed`, `Security`.
7
7
 
8
+ ## [v1.0.5] - 2026-10-30
9
+ ### Added
10
+ - Se restauro lógica de encriptación de datos de tarjeta
11
+
12
+ ### Removed
13
+ - Se elimina lógica de enrutador de transacción.
14
+
15
+ ## [v1.0.4] - 2025-12-23
16
+ ### Added
17
+ - Se agregó headers a settings
18
+
8
19
  ## [v1.0.3] - 2025-06-04
9
20
  ### Added
10
21
  - Se agrego la configuración de router a las transacciones con `RouterManager`
data/CONTRIBUTING.md CHANGED
@@ -33,19 +33,11 @@ ruby test/services/test_transaction.rb -n test_success_sale_transaction
33
33
 
34
34
  El sistema macOS ya trae una versión de **Ruby** instalada, pero es posible que no sea completamente compatible con el **SDK**. Por ello, es mejor instalar otra versión.
35
35
 
36
- 1. Instalar Ruby Version Manager para gestionar versiones de Ruby: [RVM](https://rvm.io/).
37
- 2. Instalar la versión de ruby: ```rvm install 2.7.6 --with-openssl-dir=$(brew --prefix openssl@1.1) ```
38
- 3. Correr ```gem install bundler```
39
-
40
- **Nota**: ```openssl@1.1``` ya deberia estar en homebrew. Revisar con ```brew list```. Si no está, instalarlo antes de ejecutar el paso 2.
41
-
42
- **Opción alternativa**
43
- Es posible que no se permita instalar ```openssl@1.1``` ya que fue deshabilitado por homebrew. En ese caso se puede utilizar otro manejador de versiones de **Ruby** como [rbenv](https://github.com/rbenv/rbenv).
36
+ Utilizar manejador de versiones de **Ruby** como [rbenv](https://github.com/rbenv/rbenv).
44
37
 
45
38
  Pasos de instalación:
46
39
 
47
40
  1. brew install rbenv
48
- 2. rbenv install 2.7.6
49
- 3. rbenv global 2.7.6 (Configura la versión de Ruby por defecto de la computadora)
50
-
51
- **Nota** Si se intento la instalación previa de RVM y no se puedo debido al elemento de **openssl@1.1**, comentar en archivo de variables de entorno .zshrc los elementos de RVM.
41
+ 2. rbenv install 3.7.2
42
+ 3. rbenv global 3.7.2 (Configura la versión de Ruby por defecto de la computadora)
43
+ 4. rbenv local 3.7.2 (Configura la versión de Ruby local en el proyecto)
data/Gemfile CHANGED
@@ -9,8 +9,12 @@ gem "rake", "~> 13.0"
9
9
 
10
10
  gem "minitest", "~> 5.0"
11
11
 
12
- gem "rubocop", "~> 1.21"
12
+ gem "rubocop", "~> 1.51", ">= 1.51.0"
13
13
 
14
14
  gem "i18n", "~> 1.13"
15
15
 
16
+ gem "rbnacl", "~> 7.1"
17
+
16
18
  gem "simplecov", require: false, group: :test
19
+
20
+ gem 'dotenv'
data/Gemfile.lock CHANGED
@@ -1,37 +1,47 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pixelpay_sdk (1.0.3)
4
+ pixelpay_sdk (1.0.5)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- ast (2.4.2)
9
+ ast (2.4.3)
10
10
  concurrent-ruby (1.2.2)
11
11
  docile (1.4.0)
12
+ dotenv (3.2.0)
13
+ ffi (1.17.4)
12
14
  i18n (1.13.0)
13
15
  concurrent-ruby (~> 1.0)
14
- json (2.6.3)
16
+ json (3.1.0)
17
+ language_server-protocol (3.17.0.6)
18
+ lint_roller (1.1.0)
15
19
  minitest (5.18.0)
16
- parallel (1.23.0)
17
- parser (3.2.2.1)
20
+ parallel (1.28.0)
21
+ parser (3.3.13.0)
18
22
  ast (~> 2.4.1)
23
+ racc
24
+ prism (1.9.0)
25
+ racc (1.8.1)
19
26
  rainbow (3.1.1)
20
27
  rake (13.0.6)
21
- regexp_parser (2.8.0)
22
- rexml (3.2.5)
23
- rubocop (1.50.2)
24
- json (~> 2.3)
25
- parallel (~> 1.10)
26
- parser (>= 3.2.0.0)
28
+ rbnacl (7.1.2)
29
+ ffi (~> 1)
30
+ regexp_parser (2.13.1)
31
+ rubocop (1.92.2)
32
+ json (>= 2.3)
33
+ language_server-protocol (~> 3.17.0.2)
34
+ lint_roller (~> 1.1.0)
35
+ parallel (>= 1.10)
36
+ parser (>= 3.3.0.2)
27
37
  rainbow (>= 2.2.2, < 4.0)
28
- regexp_parser (>= 1.8, < 3.0)
29
- rexml (>= 3.2.5, < 4.0)
30
- rubocop-ast (>= 1.28.0, < 2.0)
38
+ regexp_parser (>= 2.11.3, < 3.0)
39
+ rubocop-ast (>= 1.49.0, < 2.0)
31
40
  ruby-progressbar (~> 1.7)
32
- unicode-display_width (>= 2.4.0, < 3.0)
33
- rubocop-ast (1.28.0)
34
- parser (>= 3.2.1.0)
41
+ unicode-display_width (>= 2.4.0, < 4.0)
42
+ rubocop-ast (1.50.0)
43
+ parser (>= 3.3.7.2)
44
+ prism (~> 1.7)
35
45
  ruby-progressbar (1.13.0)
36
46
  simplecov (0.22.0)
37
47
  docile (~> 1.1)
@@ -39,20 +49,26 @@ GEM
39
49
  simplecov_json_formatter (~> 0.1)
40
50
  simplecov-html (0.12.3)
41
51
  simplecov_json_formatter (0.1.4)
42
- unicode-display_width (2.4.2)
52
+ unicode-display_width (3.3.0)
53
+ unicode-emoji (~> 4.3)
54
+ unicode-emoji (4.3.0)
43
55
 
44
56
  PLATFORMS
45
57
  arm64-darwin-22
46
- ruby
58
+ arm64-darwin-23
59
+ arm64-darwin-25
47
60
  x64-mingw32
61
+ x86_64-linux
48
62
 
49
63
  DEPENDENCIES
64
+ dotenv
50
65
  i18n (~> 1.13)
51
66
  minitest (~> 5.0)
52
67
  pixelpay_sdk!
53
68
  rake (~> 13.0)
54
- rubocop (~> 1.21)
69
+ rbnacl (~> 7.1)
70
+ rubocop (~> 1.51, >= 1.51.0)
55
71
  simplecov
56
72
 
57
73
  BUNDLED WITH
58
- 2.4.12
74
+ 2.4.22
data/README.md CHANGED
@@ -5,12 +5,12 @@
5
5
  Para obtener la versión del SDK de PixelPay con Ruby puedes utilizar este comando para instalar directamente:
6
6
 
7
7
  ``` bash
8
- gem install pixelpay_sdk-1.0.3
8
+ gem install pixelpay_sdk-1.0.5
9
9
  ```
10
10
 
11
11
  O puedes colocar en el archivo Gemfile la siguiente línea:
12
12
  ``` bash
13
- gem 'pixelpay_sdk', '~> 1.0.3'
13
+ gem 'pixelpay_sdk', '~> 1.0.5'
14
14
  ```
15
15
 
16
16
  Y luego ejecutar:
@@ -1,4 +1,4 @@
1
- image: ruby:2.7
1
+ image: ruby:3.2.6
2
2
 
3
3
  pipelines:
4
4
  tags:
@@ -6,6 +6,8 @@ pipelines:
6
6
  - step:
7
7
  name: Build and test
8
8
  script:
9
+ # Install system dependency required by rbnacl
10
+ - apt-get update && apt-get install -y libsodium23
9
11
  # Install gems
10
12
  - bundle install
11
13
  # Run tests
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "rbnacl"
4
+ require "base64"
3
5
  require "digest"
4
6
  require "json"
5
7
  # Helpers
@@ -7,13 +9,17 @@ module Helpers
7
9
  # Serialize object to JSON string without empties.
8
10
  # Args:
9
11
  # value (object): object to serialize.
12
+ # ignore (array): key values that will be excluded from final JSON.
10
13
  # Returns
11
14
  # string: JSON string.
12
- def self.object_to_json(value)
15
+ def self.object_to_json(value, ignore = [])
13
16
  verify_type(Object, value.is_a?(Object))
14
17
 
15
18
  hash = {}
16
- value.instance_variables.each { |var| hash[var.to_s.delete("@")] = value.instance_variable_get(var) }
19
+ value.instance_variables.each do |var|
20
+ var_name = var.to_s.delete("@")
21
+ hash[var_name] = value.instance_variable_get(var) unless ignore.include?(var_name)
22
+ end
17
23
 
18
24
  hash.each do |attr_name, attr_value|
19
25
  if hash[attr_name].nil? || hash[attr_name].to_s.empty? || (attr_value.is_a?(Array) && attr_value.empty?) || (attr_value.is_a?(Hash) && attr_value.empty?)
@@ -95,4 +101,51 @@ module Helpers
95
101
  def self.verify_type(expected, flag)
96
102
  raise TypeError, "This function only accepts parameters of type #{expected}." unless flag
97
103
  end
104
+
105
+ # Generate encryption key pair.
106
+ # Returns:
107
+ # Hash: encryption key pair.
108
+ def self.get_key_pair
109
+ private_key = RbNaCl::PrivateKey.generate
110
+ public_key = private_key.public_key
111
+
112
+ {
113
+ private_key: private_key,
114
+ public_key: public_key
115
+ }
116
+ end
117
+
118
+ # Encrypt message.
119
+ # Args:
120
+ # message (string): raw message.
121
+ # public_key (string): merchant public key.
122
+ # key_pair (hash): encryption key pair
123
+ # Returns:
124
+ # string: encrypted value concatenated with base64 nonce.
125
+ def self.encrypt(message, public_key, key_pair)
126
+ verify_type(Hash, key_pair.is_a?(Hash))
127
+
128
+ return nil if message.nil? || message.empty?
129
+
130
+ return message if message.include?("|")
131
+
132
+ return message if public_key.nil? || public_key.empty?
133
+
134
+ merchant_public_key = Base64.strict_decode64(public_key)
135
+
136
+ return message if merchant_public_key.bytesize != 32
137
+
138
+ sdk_private_key = key_pair[:private_key]
139
+
140
+ box = RbNaCl::Box.new(merchant_public_key, sdk_private_key)
141
+
142
+ nonce = RbNaCl::Random.random_bytes(box.nonce_bytes)
143
+
144
+ encrypted_message = box.encrypt(nonce, message)
145
+
146
+ box_b64 = Base64.strict_encode64(encrypted_message)
147
+ nonce_b64 = Base64.strict_encode64(nonce)
148
+
149
+ "#{box_b64}|#{nonce_b64}"
150
+ end
98
151
  end
@@ -6,7 +6,7 @@ require_relative "../version"
6
6
 
7
7
  # RequestBehaviour defines the structure for the request behaviour.
8
8
  class RequestBehaviour
9
- attr_accessor :env, :lang, :from, :sdk_version
9
+ attr_accessor :env, :lang, :from, :sdk_version, :sdk_key_pair
10
10
 
11
11
  # Initialize request
12
12
  def initialize
@@ -23,12 +23,31 @@ class RequestBehaviour
23
23
  @sdk_version = PixelpaySdk::VERSION
24
24
 
25
25
  @lang = "es" if @lang != "es" && @lang != "en"
26
+
27
+ # For internal use to prevent encrypting same request instance twice
28
+ @sdk_key_pair = nil
26
29
  end
27
30
 
28
31
  # Serialize object to JSON string.
29
32
  # Returns:
30
33
  # string: JSON string.
31
34
  def object_to_json
32
- Helpers.object_to_json(self)
35
+ Helpers.object_to_json(self, ["sdk_key_pair"])
36
+ end
37
+
38
+ # Check if current request needs encryption, must be overriden by inherited classes.
39
+ # Returns:
40
+ # bool: true if request contains parameters that mush be encrypted.
41
+ def is_encryptable?
42
+ false
43
+ end
44
+
45
+ # Encrypt fields with sensitive data, must be overriden by inherited classes.
46
+ # Args:
47
+ # public_key (string): merchant public key.
48
+ # Returns:
49
+ # nil: this base method always returns nil.
50
+ def with_encryption(public_key)
51
+ nil
33
52
  end
34
53
  end
@@ -64,7 +64,7 @@ class Response
64
64
  # Returns:
65
65
  # Any: data or nil.
66
66
  def get_data(key)
67
- return nil if @data.nil? && @data.key?(key.to_s)
67
+ return nil if @data.nil? || !@data.key?(key.to_s)
68
68
 
69
69
  @data[key.to_s]
70
70
  end
@@ -7,6 +7,7 @@ require "uri"
7
7
  require_relative "../base/Helpers"
8
8
  require_relative "../base/RequestBehaviour"
9
9
  require_relative "../exceptions/InvalidCredentialsException"
10
+ require_relative "../exceptions/FailedEncryptionException"
10
11
  require_relative "../responses/ErrorResponse"
11
12
  require_relative "../responses/FailureResponse"
12
13
  require_relative "../responses/InputErrorResponse"
@@ -21,7 +22,9 @@ require_relative "../responses/TimeoutResponse"
21
22
 
22
23
  # ServiceBehaviour defines the structure for the service behaviour.
23
24
  class ServiceBehaviour
24
- attr_accessor :settings
25
+ PUBLIC_KEY_PATH = "api/v2/merchant/public_key"
26
+
27
+ attr_accessor :settings, :sdk_public_key
25
28
 
26
29
  # Initialize service.
27
30
  def initialize(settings)
@@ -29,6 +32,9 @@ class ServiceBehaviour
29
32
 
30
33
  # Settings service model
31
34
  @settings = settings
35
+
36
+ # Public key from pair generated by current instance.
37
+ @sdk_public_key = nil
32
38
  rescue StandardError => e
33
39
  warn("An exception occurred: #{e.message}")
34
40
  end
@@ -136,6 +142,14 @@ class ServiceBehaviour
136
142
 
137
143
  request["x-auth-user"] = @settings.auth_user if @settings.auth_user != ""
138
144
 
145
+ request["x-auth-secure"] = @sdk_public_key if @sdk_public_key
146
+
147
+ if @settings.headers
148
+ @settings.headers.each do |key, value|
149
+ request[key] = value
150
+ end
151
+ end
152
+
139
153
  request
140
154
  rescue StandardError => e
141
155
  exception_response(e)
@@ -161,6 +175,7 @@ class ServiceBehaviour
161
175
  raise TypeError, "This function only accepts parameters of type RequestBehaviour."
162
176
  end
163
177
 
178
+ validate_encryption(body)
164
179
  uri = get_route(url)
165
180
  req = build_request(uri, "POST", body)
166
181
 
@@ -184,6 +199,7 @@ class ServiceBehaviour
184
199
  def put(url, body)
185
200
  Helpers.verify_type(RequestBehaviour, body.is_a?(RequestBehaviour))
186
201
 
202
+ validate_encryption(body)
187
203
  uri = get_route(url)
188
204
  req = build_request(uri, "PUT", body)
189
205
 
@@ -205,6 +221,7 @@ class ServiceBehaviour
205
221
  def delete(url, body)
206
222
  Helpers.verify_type(RequestBehaviour, body.is_a?(RequestBehaviour))
207
223
 
224
+ validate_encryption(body)
208
225
  uri = get_route(url)
209
226
  req = build_request(uri, "DELETE", body)
210
227
 
@@ -226,6 +243,7 @@ class ServiceBehaviour
226
243
  def get(url, body)
227
244
  Helpers.verify_type(RequestBehaviour, body.is_a?(RequestBehaviour))
228
245
 
246
+ validate_encryption(body) if url != PUBLIC_KEY_PATH
229
247
  uri = get_route(url)
230
248
  req = build_request(uri, "GET", body)
231
249
 
@@ -237,4 +255,77 @@ class ServiceBehaviour
237
255
  rescue StandardError => e
238
256
  exception_response(e)
239
257
  end
258
+
259
+ # Verify and set request encryption values.
260
+ # Args:
261
+ # request (RequestBehaviour): unencrypted request.
262
+ # Raises:
263
+ # InvalidCredentialsException: When invalid or no credentials are provided.
264
+ # FailedEncryptionException: When encryption process fails.
265
+ def validate_encryption(request)
266
+ Helpers.verify_type(RequestBehaviour, request.is_a?(RequestBehaviour))
267
+
268
+ return unless request.is_encryptable?
269
+
270
+ validate_merchant_public_key
271
+ validate_sdk_public_key(request)
272
+ end
273
+
274
+ # Resiliently fetch merchant public key, trying at least 3 times before failing.
275
+ # Raises:
276
+ # InvalidCredentialsException: When invalid or no credentials are provided.
277
+ def validate_merchant_public_key
278
+ tries = 0
279
+
280
+ while @settings.public_key.nil? && tries < 3
281
+ @settings.public_key = fetch_merchant_public_key
282
+ tries += 1
283
+ end
284
+
285
+ if @settings.public_key.nil?
286
+ raise InvalidCredentialsException, "Could not obtain necessary credentials for transaction."
287
+ end
288
+ end
289
+
290
+ # Resiliently generate keys and encrypt request, trying at least 3 times before failing.
291
+ # Args:
292
+ # request (RequestBehaviour): unencrypted request.
293
+ # Raises:
294
+ # FailedEncryptionException: When encryption process fails.
295
+ def validate_sdk_public_key(request)
296
+
297
+ Helpers.verify_type(RequestBehaviour, request.is_a?(RequestBehaviour))
298
+
299
+ tries = 0
300
+ last_error = nil
301
+
302
+ while @sdk_public_key.nil? && tries < 3 || tries.zero?
303
+ begin
304
+ @sdk_public_key = request.with_encryption(@settings.public_key)
305
+ rescue StandardError => e
306
+ last_error = e
307
+ ensure
308
+ tries += 1
309
+ end
310
+ end
311
+
312
+ raise last_error if @sdk_public_key.nil? && last_error
313
+
314
+ if @sdk_public_key.nil?
315
+ raise FailedEncryptionException, "Could not process encryption, please try again."
316
+ end
317
+ end
318
+
319
+ # Fetch merchant public key for encryption.
320
+ # Returns:
321
+ # string: merchant public key or None.
322
+ # Raises:
323
+ # InvalidCredentialsException: When no credentials are provided.
324
+ def fetch_merchant_public_key
325
+ response = get(PUBLIC_KEY_PATH, RequestBehaviour.new)
326
+
327
+ return response.get_data("public_key") if response.success
328
+
329
+ nil
330
+ end
240
331
  end
@@ -103,9 +103,11 @@ class TransactionResult
103
103
  def self.from_response(response)
104
104
  entity = TransactionResult.new
105
105
 
106
- entity.instance_variables.each do |var_name|
107
- if response.data.key?(var_name.to_s.delete("@"))
108
- entity.instance_variable_set(var_name, response.data[var_name.to_s.delete("@")])
106
+ if response.respond_to?(:data) && response.data.is_a?(Hash)
107
+ entity.instance_variables.each do |var_name|
108
+ if response.data.key?(var_name.to_s.delete("@"))
109
+ entity.instance_variable_set(var_name, response.data[var_name.to_s.delete("@")])
110
+ end
109
111
  end
110
112
  end
111
113
 
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # FailedEncryptionException provides the possibility to create an exception of
4
+ # it's type
5
+ class FailedEncryptionException < StandardError
6
+ end
@@ -4,7 +4,7 @@ require_relative "../base/Helpers"
4
4
  require_relative "../resources/Environment"
5
5
  # Settings represents the settings information for a request.
6
6
  class Settings
7
- attr_accessor :auth_key, :auth_hash, :auth_user, :endpoint, :environment, :lang
7
+ attr_accessor :auth_key, :auth_hash, :auth_user, :endpoint, :environment, :lang, :headers, :public_key
8
8
 
9
9
  # Initialize model
10
10
  def initialize
@@ -25,6 +25,12 @@ class Settings
25
25
 
26
26
  # Settings response messages language
27
27
  @lang = nil
28
+
29
+ # Request headers
30
+ @headers = nil
31
+
32
+ # Public encryption key
33
+ @public_key = nil
28
34
  end
29
35
 
30
36
  # Setup API endpoint URL.
@@ -75,7 +81,7 @@ class Settings
75
81
 
76
82
  # Setup defaults to Sandbox credentials.
77
83
  def setup_sandbox
78
- @endpoint = "https://pixel-pay.com"
84
+ @endpoint = "https://pixelpay.dev"
79
85
  @auth_key = "1234567890"
80
86
  @auth_hash = Helpers.hash("MD5", "@s4ndb0x-abcd-1234-n1l4-p1x3l")
81
87
  @environment = Environment::SANDBOX
@@ -91,4 +97,15 @@ class Settings
91
97
 
92
98
  self
93
99
  end
100
+
101
+ # Setup request headers
102
+ # Args:
103
+ # headers (Hash): request headers to be used.
104
+ # Returns:
105
+ # Settings: self
106
+ def setup_headers(headers)
107
+ @headers = headers
108
+
109
+ self
110
+ end
94
111
  end
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "base64"
3
4
  require_relative "../base/Helpers"
4
5
  require_relative "../base/RequestBehaviour"
6
+ require_relative "../exceptions/FailedEncryptionException"
5
7
 
6
8
  # CardTokenization defines a card tokenization request.
7
9
  class CardTokenization < RequestBehaviour
@@ -94,4 +96,65 @@ class CardTokenization < RequestBehaviour
94
96
 
95
97
  self
96
98
  end
99
+
100
+ # Check if current request needs encryption.
101
+ # Returns:
102
+ # bool: true if request contains parameters that must be encrypted.
103
+ def is_encryptable?
104
+ !(@number.nil? || @number.empty?) ||
105
+ !(@cvv2.nil? || @cvv2.empty?) ||
106
+ !(@expire_month.nil? || @expire_month.empty?) ||
107
+ !(@expire_year.nil? || @expire_year.empty?) ||
108
+ !(@cardholder.nil? || @ardholder.empty?) ||
109
+ !(@email.nil? || @email.empty?)
110
+ end
111
+
112
+ # Encrypt fields with sensitive data.
113
+ # Args:
114
+ # public_key (string): merchant public key.
115
+ # Returns:
116
+ # string: SDK public key.
117
+ # Raises:
118
+ # FailedEncryptionException: When public key is empty or encryption fails.
119
+ def with_encryption(public_key)
120
+ if public_key.nil? || public_key.empty?
121
+ raise FailedEncryptionException, "Could not process transaction without merchant public key."
122
+ end
123
+
124
+ begin
125
+ key_pair = !@sdk_key_pair.nil? ? @sdk_key_pair : Helpers.get_key_pair
126
+
127
+ if !(@number.nil? || @number.empty?)
128
+ @number = Helpers.encrypt(@number, public_key, key_pair)
129
+ end
130
+
131
+ if !(@cvv2.nil? || @cvv2.empty?)
132
+ @cvv2 = Helpers.encrypt(@cvv2, public_key, key_pair)
133
+ end
134
+
135
+ if !(@expire_month.nil? || @expire_month.empty?)
136
+ @expire_month = Helpers.encrypt(@expire_month, public_key, key_pair)
137
+ end
138
+
139
+ if !(@expire_year.nil? || @expire_year.empty?)
140
+ @expire_year = Helpers.encrypt(@expire_year, public_key, key_pair)
141
+ end
142
+
143
+ if !(@cardholder.nil? || @cardholder.empty?)
144
+ @cardholder = Helpers.encrypt(@cardholder, public_key, key_pair)
145
+ end
146
+
147
+ if !(@email.nil? || @email.empty?)
148
+ @email = Helpers.encrypt(@email, public_key, key_pair)
149
+ end
150
+
151
+ sdk_public_key = Base64.strict_encode64(key_pair[:public_key])
152
+
153
+ @sdk_key_pair = key_pair
154
+
155
+ sdk_public_key
156
+ rescue StandardError
157
+ raise FailedEncryptionException, "Could not process transaction without merchant public key."
158
+ end
159
+ end
97
160
  end
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "base64"
3
4
  require_relative "../base/Helpers"
4
5
  require_relative "../base/RequestBehaviour"
6
+ require_relative "../exceptions/FailedEncryptionException"
5
7
 
6
8
  # PaymentTransaction defines the structure for a payment transaction request.
7
9
  class PaymentTransaction < RequestBehaviour
@@ -179,4 +181,63 @@ class PaymentTransaction < RequestBehaviour
179
181
  def with_authentication_request
180
182
  @authentication_request = true
181
183
  end
184
+
185
+ # Check if current request needs encryption.
186
+ # Returns:
187
+ # bool: true if request contains parameters that must be encrypted.
188
+ def is_encryptable?
189
+ !(@card_number.nil? || @card_number.empty?) ||
190
+ !(@card_cvv.nil? || @card_cvv.empty?) ||
191
+ !(@card_expire.nil? || @card_expire.empty?) ||
192
+ !(@card_holder.nil? || @card_holder.empty?) ||
193
+ !(@customer_name.nil? || @customer_name.empty?) ||
194
+ !(@customer_email.nil? || @customer_email.empty?)
195
+ end
196
+
197
+ # Encrypt fields with sensitive data.
198
+ # Args:
199
+ # public_key (string): merchant public key.
200
+ # Returns:
201
+ # string: SDK public key or nil.
202
+ def with_encryption(public_key)
203
+ if public_key.nil? || public_key.empty?
204
+ raise FailedEncryptionException, "Could not process transaction without merchant public key."
205
+ end
206
+
207
+ begin
208
+ key_pair = !@sdk_key_pair.nil? ? @sdk_key_pair : Helpers.get_key_pair
209
+
210
+ if !(@card_number.nil? || @card_number.empty?)
211
+ @card_number = Helpers.encrypt(@card_number, public_key, key_pair)
212
+ end
213
+
214
+ if !(@card_cvv.nil? || @card_cvv.empty?)
215
+ @card_cvv = Helpers.encrypt(@card_cvv, public_key, key_pair)
216
+ end
217
+
218
+ if !(@card_expire.nil? || @card_expire.empty?)
219
+ @card_expire = Helpers.encrypt(@card_expire, public_key, key_pair)
220
+ end
221
+
222
+ if !(@card_holder.nil? || @card_holder.empty?)
223
+ @card_holder = Helpers.encrypt(@card_holder, public_key, key_pair)
224
+ end
225
+
226
+ if !(@customer_name.nil? || @customer_name.empty?)
227
+ @customer_name = Helpers.encrypt(@customer_name, public_key, key_pair)
228
+ end
229
+
230
+ if !(@customer_email.nil? || @customer_email.empty?)
231
+ @customer_email = Helpers.encrypt(@customer_email, public_key, key_pair)
232
+ end
233
+
234
+ sdk_public_key = Base64.strict_encode64(key_pair[:public_key])
235
+
236
+ @sdk_key_pair = key_pair
237
+
238
+ sdk_public_key
239
+ rescue StandardError
240
+ raise FailedEncryptionException, "Could not process transaction without merchant public key."
241
+ end
242
+ end
182
243
  end
@@ -6,7 +6,6 @@ require_relative "../request/StatusTransaction"
6
6
  require_relative "../request/AuthTransaction"
7
7
  require_relative "../request/CaptureTransaction"
8
8
  require_relative "../request/VoidTransaction"
9
- require_relative "../libraries/RouterManager"
10
9
  require_relative "../exceptions/InvalidTransactionTypeException"
11
10
  require_relative "../exceptions/RunningTransactionException"
12
11
 
@@ -39,9 +38,7 @@ class Transaction < ServiceBehaviour
39
38
  response = post("api/v2/transaction/sale", transaction)
40
39
 
41
40
  if response.instance_of?(PayloadResponse)
42
- if response.data["transaction_type"] == "router"
43
- return RouterManager.new(transaction).init_manager(response.data["payload"])
44
- end
41
+ raise RunningTransactionException, "This type of transaction is invalid."
45
42
  end
46
43
 
47
44
  return response
@@ -63,9 +60,7 @@ class Transaction < ServiceBehaviour
63
60
  response = post("api/v2/transaction/auth", transaction)
64
61
 
65
62
  if response.instance_of?(PayloadResponse)
66
- if response.data["transaction_type"] == "router"
67
- return RouterManager.new(transaction).init_manager(response.data["payload"])
68
- end
63
+ raise RunningTransactionException, "This type of transaction is invalid"
69
64
  end
70
65
 
71
66
  return response
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PixelpaySdk
4
- VERSION = "1.0.3"
4
+ VERSION = "1.0.5"
5
5
  end
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pixelpay_sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sonia Villeda
8
8
  - Oscar Gómez
9
- autorequire:
9
+ autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2025-08-15 00:00:00.000000000 Z
12
+ date: 2026-10-31 00:00:00.000000000 Z
13
13
  dependencies: []
14
- description:
14
+ description:
15
15
  email:
16
16
  - sonia@pixel.hn
17
17
  - oscar@pixel.hn
@@ -38,10 +38,10 @@ files:
38
38
  - lib/pixelpay_sdk/base/ServiceBehaviour.rb
39
39
  - lib/pixelpay_sdk/entities/CardResult.rb
40
40
  - lib/pixelpay_sdk/entities/TransactionResult.rb
41
+ - lib/pixelpay_sdk/exceptions/FailedEncryptionException.rb
41
42
  - lib/pixelpay_sdk/exceptions/InvalidCredentialsException.rb
42
43
  - lib/pixelpay_sdk/exceptions/InvalidTransactionTypeException.rb
43
44
  - lib/pixelpay_sdk/exceptions/RunningTransactionException.rb
44
- - lib/pixelpay_sdk/libraries/RouterManager.rb
45
45
  - lib/pixelpay_sdk/models/Billing.rb
46
46
  - lib/pixelpay_sdk/models/Card.rb
47
47
  - lib/pixelpay_sdk/models/Item.rb
@@ -77,7 +77,7 @@ licenses:
77
77
  - MIT
78
78
  metadata:
79
79
  homepage_uri: https://pixel.hn
80
- post_install_message:
80
+ post_install_message:
81
81
  rdoc_options: []
82
82
  require_paths:
83
83
  - lib
@@ -92,8 +92,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
92
92
  - !ruby/object:Gem::Version
93
93
  version: '0'
94
94
  requirements: []
95
- rubygems_version: 3.1.6
96
- signing_key:
95
+ rubygems_version: 3.4.19
96
+ signing_key:
97
97
  specification_version: 4
98
98
  summary: PixelPay SDK toolkit.
99
99
  test_files: []
@@ -1,52 +0,0 @@
1
- require "json"
2
- require "base64"
3
-
4
- require_relative "../services/Transaction"
5
- require_relative "../request/PaymentTransaction"
6
- require_relative "../request/SaleTransaction"
7
- require_relative "../models/Settings"
8
-
9
- class RouterManager
10
- attr_accessor :transaction, :identifier
11
-
12
- def initialize(request)
13
- # Payment transaction
14
- @transaction = request
15
-
16
- #Payment transaction source
17
- @transaction.source = 'router'
18
-
19
- # PixelPay transaction authentication identifier
20
- @identifier = nil
21
- end
22
-
23
- # Initialize manager
24
- def init_manager(payload)
25
- service = Transaction.new(parse_payload(payload))
26
-
27
- if @transaction.is_a?(SaleTransaction)
28
- return service.do_sale(@transaction);
29
- else
30
- return service.do_auth(@transaction)
31
- end
32
- end
33
-
34
- # Parse the payload string to Object Settings
35
- def parse_payload(payload)
36
- decoded_payload = Base64.decode64(payload)
37
- obj_payload = JSON.parse(decoded_payload)
38
-
39
- settings = Settings.new
40
-
41
- settings.setup_environment(obj_payload["env"])
42
- settings.setup_credentials(obj_payload["auth_key"], obj_payload["auth_hash"])
43
- settings.setup_endpoint(obj_payload["endpoint"]);
44
- settings.setup_language(@transaction.lang);
45
-
46
- if obj_payload["auth_user"]
47
- settings.setup_platform_user(obj_payload["auth_user"])
48
- end
49
-
50
- return settings
51
- end
52
- end