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 +4 -4
- data/CHANGELOG.md +11 -0
- data/CONTRIBUTING.md +4 -12
- data/Gemfile +5 -1
- data/Gemfile.lock +37 -21
- data/README.md +2 -2
- data/bitbucket-pipelines.yml +3 -1
- data/lib/pixelpay_sdk/base/Helpers.rb +55 -2
- data/lib/pixelpay_sdk/base/RequestBehaviour.rb +21 -2
- data/lib/pixelpay_sdk/base/Response.rb +1 -1
- data/lib/pixelpay_sdk/base/ServiceBehaviour.rb +92 -1
- data/lib/pixelpay_sdk/entities/TransactionResult.rb +5 -3
- data/lib/pixelpay_sdk/exceptions/FailedEncryptionException.rb +6 -0
- data/lib/pixelpay_sdk/models/Settings.rb +19 -2
- data/lib/pixelpay_sdk/request/CardTokenization.rb +63 -0
- data/lib/pixelpay_sdk/request/PaymentTransaction.rb +61 -0
- data/lib/pixelpay_sdk/services/Transaction.rb +2 -7
- data/lib/pixelpay_sdk/version.rb +1 -1
- metadata +8 -8
- data/lib/pixelpay_sdk/libraries/RouterManager.rb +0 -52
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d53ee74ca8bbed17b4bec55f9fe0061cdd14f841b5450d7ec8b5aed23b6a974a
|
|
4
|
+
data.tar.gz: a9835dec0d92cc864ec02b694c4b3f7de62345055f0db1529de31b00fe0e2e87
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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
|
|
49
|
-
3. rbenv global
|
|
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.
|
|
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.
|
|
4
|
+
pixelpay_sdk (1.0.5)
|
|
5
5
|
|
|
6
6
|
GEM
|
|
7
7
|
remote: https://rubygems.org/
|
|
8
8
|
specs:
|
|
9
|
-
ast (2.4.
|
|
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 (
|
|
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.
|
|
17
|
-
parser (3.
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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 (>=
|
|
29
|
-
|
|
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, <
|
|
33
|
-
rubocop-ast (1.
|
|
34
|
-
parser (>= 3.
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
69
|
+
rbnacl (~> 7.1)
|
|
70
|
+
rubocop (~> 1.51, >= 1.51.0)
|
|
55
71
|
simplecov
|
|
56
72
|
|
|
57
73
|
BUNDLED WITH
|
|
58
|
-
2.4.
|
|
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.
|
|
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.
|
|
13
|
+
gem 'pixelpay_sdk', '~> 1.0.5'
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
Y luego ejecutar:
|
data/bitbucket-pipelines.yml
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
image: ruby:2.
|
|
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
|
|
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
|
|
@@ -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
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
|
|
@@ -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://
|
|
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
|
-
|
|
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
|
-
|
|
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
|
data/lib/pixelpay_sdk/version.rb
CHANGED
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.
|
|
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:
|
|
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.
|
|
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
|