comgate_ruby 0.5.0
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 +7 -0
- data/.rubocop.yml +26 -0
- data/.tool-versions +3 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +60 -0
- data/LICENSE +21 -0
- data/LICENSE.txt +21 -0
- data/README.md +185 -0
- data/Rakefile +16 -0
- data/comgate_ruby.gemspec +40 -0
- data/lib/base_service.rb +34 -0
- data/lib/comgate/api_caller.rb +180 -0
- data/lib/comgate/gateway.rb +214 -0
- data/lib/comgate/response.rb +142 -0
- data/lib/comgate/version.rb +5 -0
- data/lib/comgate_ruby.rb +8 -0
- data/lib/hash_enhancements.rb +37 -0
- data/sig/comgate_ruby.rbs +4 -0
- metadata +66 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a7d8a030a101f774292a7f3214a1d3338238afa11fbc9c98e348db15259d6fde
|
4
|
+
data.tar.gz: 6253ba9e4d7e3a1737f491b3efccbd82bd85afc1c802c97c4939e3114dcc88ed
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 47713672ee0beed059c9e26b90bbaebded0cdc29db78f08e20c9e406678c2ffd59562a439f81c8b5a2a9d0716ab7b637af9dcc86068a9010df0e0350c4984ff9
|
7
|
+
data.tar.gz: 9d2a09ac2489566918c5f131764636c34113bfb92cc57be659b72d349bbd78c7ebbec0fb7c2d5108e28261a4f728dc6ef4ca312dc684f331a12981ad7c6cf988
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
AllCops:
|
2
|
+
NewCops: enable
|
3
|
+
TargetRubyVersion: 3.0
|
4
|
+
|
5
|
+
Style/StringLiterals:
|
6
|
+
Enabled: true
|
7
|
+
EnforcedStyle: double_quotes
|
8
|
+
|
9
|
+
Style/StringLiteralsInInterpolation:
|
10
|
+
Enabled: true
|
11
|
+
EnforcedStyle: double_quotes
|
12
|
+
|
13
|
+
Layout/LineLength:
|
14
|
+
Max: 120
|
15
|
+
|
16
|
+
Style/Documentation:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
Metrics/ClassLength:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
Metrics/MethodLength:
|
23
|
+
Enabled: false
|
24
|
+
|
25
|
+
Metrics/AbcSize:
|
26
|
+
Max: 25
|
data/.tool-versions
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in comgate_ruby.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
gem "minitest", "~> 5.0"
|
9
|
+
gem "pry-byebug"
|
10
|
+
gem "rake", "~> 13.0"
|
11
|
+
gem "rspec-expectations"
|
12
|
+
gem "rubocop", "~> 1.21"
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
comgate_ruby (0.5.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
ast (2.4.2)
|
10
|
+
byebug (11.1.3)
|
11
|
+
coderay (1.1.3)
|
12
|
+
diff-lcs (1.5.0)
|
13
|
+
json (2.6.3)
|
14
|
+
method_source (1.0.0)
|
15
|
+
minitest (5.18.0)
|
16
|
+
parallel (1.22.1)
|
17
|
+
parser (3.2.0.0)
|
18
|
+
ast (~> 2.4.1)
|
19
|
+
pry (0.14.2)
|
20
|
+
coderay (~> 1.1)
|
21
|
+
method_source (~> 1.0)
|
22
|
+
pry-byebug (3.10.1)
|
23
|
+
byebug (~> 11.0)
|
24
|
+
pry (>= 0.13, < 0.15)
|
25
|
+
rainbow (3.1.1)
|
26
|
+
rake (13.0.6)
|
27
|
+
regexp_parser (2.6.2)
|
28
|
+
rexml (3.2.5)
|
29
|
+
rspec-expectations (3.12.2)
|
30
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
31
|
+
rspec-support (~> 3.12.0)
|
32
|
+
rspec-support (3.12.0)
|
33
|
+
rubocop (1.44.0)
|
34
|
+
json (~> 2.3)
|
35
|
+
parallel (~> 1.10)
|
36
|
+
parser (>= 3.2.0.0)
|
37
|
+
rainbow (>= 2.2.2, < 4.0)
|
38
|
+
regexp_parser (>= 1.8, < 3.0)
|
39
|
+
rexml (>= 3.2.5, < 4.0)
|
40
|
+
rubocop-ast (>= 1.24.1, < 2.0)
|
41
|
+
ruby-progressbar (~> 1.7)
|
42
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
43
|
+
rubocop-ast (1.24.1)
|
44
|
+
parser (>= 3.1.1.0)
|
45
|
+
ruby-progressbar (1.11.0)
|
46
|
+
unicode-display_width (2.4.2)
|
47
|
+
|
48
|
+
PLATFORMS
|
49
|
+
x86_64-linux
|
50
|
+
|
51
|
+
DEPENDENCIES
|
52
|
+
comgate_ruby!
|
53
|
+
minitest (~> 5.0)
|
54
|
+
pry-byebug
|
55
|
+
rake (~> 13.0)
|
56
|
+
rspec-expectations
|
57
|
+
rubocop (~> 1.21)
|
58
|
+
|
59
|
+
BUNDLED WITH
|
60
|
+
2.3.22
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2023 Sinfin
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2023 Petr Mlčoch
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
# comgate_ruby
|
2
|
+
Client for Comgate payment gateway
|
3
|
+
|
4
|
+
## API docs for Comgate
|
5
|
+
https://help.comgate.cz/docs/api-protokol
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Install the gem and add to the application's Gemfile by executing:
|
10
|
+
|
11
|
+
$ bundle add comgate_ruby
|
12
|
+
|
13
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
14
|
+
|
15
|
+
$ gem install comgate_ruby
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
### 1) set gateway object
|
19
|
+
As singleton on app init or for each transaction:
|
20
|
+
```ruby
|
21
|
+
gateway = Commgate::Gateway.new(merchant_gateway_id: ":comgate_id",
|
22
|
+
test_calls: false,
|
23
|
+
secret: ":comgate_secret")
|
24
|
+
```
|
25
|
+
|
26
|
+
### 2) prepare endpoint
|
27
|
+
Comgate sends POST requests to your app about transactions updates. The URL of it needs to be setup in Comgate Client portal. At endpoint, just call `gateway.process_payment_callback(params)`, which will return
|
28
|
+
`{state: :paid, transaction_id: ":transID"}` (at least). See bullets 4) and 5) in Single payment process bellow.
|
29
|
+
### 3) call appropriate method
|
30
|
+
(see bellow)
|
31
|
+
|
32
|
+
## Usecases
|
33
|
+
### Single payment process
|
34
|
+
1) Start transaction by `gateway.start_transaction(payment_data)`. Response is
|
35
|
+
```ruby
|
36
|
+
#<Comgate::Response:0x00007f56800295a8
|
37
|
+
@array=nil,
|
38
|
+
@errors=nil,
|
39
|
+
@hash={:code=>0, :message=>"OK", :transaction_id=>"AB12-CD34-EF56", :redirect_to=>"https://payments.comgate.cz/client/instructions/index?id=AB12-CD34-EF56"},
|
40
|
+
@http_code=200,
|
41
|
+
@params_conversion_hash=
|
42
|
+
{ .... }
|
43
|
+
@redirect_to="https://payments.comgate.cz/client/instructions/index?id=AB12-CD34-EF56">
|
44
|
+
```
|
45
|
+
Important part is `response.hash` :
|
46
|
+
```ruby
|
47
|
+
{
|
48
|
+
code: 0,
|
49
|
+
message: "OK",
|
50
|
+
transaction_id: "AB12-CD34-EF56"
|
51
|
+
redirect_to: "https://payments.comgate.cz/client/instructions/index?id=AB12-CD34-EF56",
|
52
|
+
}
|
53
|
+
```
|
54
|
+
2) Redirect user to `response.redirect_to` page (=> display Comgate form).
|
55
|
+
3) Client will (not) pay.
|
56
|
+
4) Comgate will send request to your defined endpoint about status change of transaction. Call `gateway.process_state_change(payload)`, which will return
|
57
|
+
`{state: :paid, transaction_id: ":transID"}`(and maybe some more informations).
|
58
|
+
5) Now is Your time to handle payment (or other state like `cancelled`, `authorized`).
|
59
|
+
|
60
|
+
### Recurring payments
|
61
|
+
1) Use `gateway.start_recurring_transaction(payment_data)` and store `transaction_id`.
|
62
|
+
2) Create following payments `gateway.repeat_recurring_transaction(payment_data: new_payment_data.merge({transaction_id: ":transID"}) }})`. No redirection here. Price can change in each payment.
|
63
|
+
3) Handle status change like bullets 4) and 5) in single payment
|
64
|
+
|
65
|
+
### Preauthorized payments
|
66
|
+
1) Use `gateway.start_preauthorized_transaction(payment_data)` and store `transaction_id`.
|
67
|
+
2a) Confirm payment by `gateway.confirm_preauthorized_transaction(payment_data.merge({transaction_id: ":transID"}))` (price cannot exceed preauthorized amount)
|
68
|
+
2b) Cancel payment by `gateway.cancel_preauthorized_transaction(transaction_id: ":transID")`
|
69
|
+
3) Handle status change like bullets 4) and 5) in single payment
|
70
|
+
|
71
|
+
### Verification payments
|
72
|
+
1) Use `gateway.start_verification_transaction(payment_data)` and store `transaction_id`.
|
73
|
+
2) If payment is succesfull, bank will refund payment immediatelly.
|
74
|
+
3) Then you can create (repeat) payments like reccuring payments.
|
75
|
+
|
76
|
+
### Refund payment
|
77
|
+
1) Call `gateway.refund_transaction(payment_data.merge({transaction_id: ":transID"}))` (refunded value cannot exceed paid amount)
|
78
|
+
2) Handle status change like bullets 4) and 5) in single payment
|
79
|
+
|
80
|
+
### Cancel payment
|
81
|
+
1) Call `gateway.cancel_transaction(transaction_id: ":transID")`
|
82
|
+
2) Handle status change like bullets 4) and 5) in single payment
|
83
|
+
|
84
|
+
### Check payment state (ad-hoc)
|
85
|
+
0) The endpoint must be always implemented, this is just additional way to check payment state
|
86
|
+
1) Call `gateway.check_state(transaction_id: ":transID")`. It will return `{state: :paid, transaction_id: ":transID"}` and some more infos.
|
87
|
+
2) Handle status change like bullet 5) in single payment
|
88
|
+
|
89
|
+
### Get payment methods allowed to merchant
|
90
|
+
1) Call `gateway.allowed_payment_methods(params)`. It will return array of allowed payment methods in `response.array`.
|
91
|
+
```ruby
|
92
|
+
[
|
93
|
+
{ id: "BANK_CZ_CS_P",
|
94
|
+
name: "Česká spořitelna - PLATBA 24",
|
95
|
+
description: "On-line platba pro majitele účtu u České spořitelny.",
|
96
|
+
logo_url: "https://payments.comgate.cz/assets/images/logos/BANK_CZ_CS_P.png" },
|
97
|
+
{ id: "BANK_CZ_FB_P",
|
98
|
+
name: "Fio banka - PayMyway",
|
99
|
+
description: "On-line platba pro majitele účtu u Fio banky.",
|
100
|
+
logo_url: "https://payments.comgate.cz/assets/images/logos/BANK_CZ_FB.png" }
|
101
|
+
]
|
102
|
+
```
|
103
|
+
|
104
|
+
### Get list of transfers for date
|
105
|
+
1) Call `gateway.transfers_from(date)`. Array of transfers will be returned in `response.array`.
|
106
|
+
```ruby
|
107
|
+
[
|
108
|
+
{ transfer_id: 1234567,
|
109
|
+
transfer_date: date,
|
110
|
+
account_counter_party: "0/0000",
|
111
|
+
account_outgoing: "123456789/0000",
|
112
|
+
variable_symbol: "12345678"}
|
113
|
+
]
|
114
|
+
```
|
115
|
+
|
116
|
+
## Parameters
|
117
|
+
Structure of parameters is unchanged across most of methods, but you can leave out unused keys. You will get error if You do not pass required key.
|
118
|
+
See `test/comgate/test_gateway.rb` for examples.
|
119
|
+
Also returned hash have consistent structure (very similar to input params)
|
120
|
+
Maximal mixed version looks like:
|
121
|
+
```ruby
|
122
|
+
{
|
123
|
+
code: 0, # output
|
124
|
+
message: "OK", # output
|
125
|
+
transfer_id: "1234-abcd-45678", # input/output
|
126
|
+
test: true, # input (handle as test call)/ output (created by test call)
|
127
|
+
state: :paid, # output (:pending, :paid, :cancelled, :authorized)
|
128
|
+
merchant: {
|
129
|
+
gateway_id: "some_id_from_comgate", # output (input is set at gateway init)
|
130
|
+
target_shop_account: "12345678/1234", # input (change against default)/ output
|
131
|
+
},
|
132
|
+
payment: {
|
133
|
+
amount_in_cents: 12_900, # input/output
|
134
|
+
currency: "CZK", # input/output
|
135
|
+
label: "Payment for 2 straws", # input/output
|
136
|
+
reference_id: "our eshop order #1 reference", # input/output
|
137
|
+
method: "CARD_CZ_CSOB_2", # input (selected method; or use "ALL") / output
|
138
|
+
product_name: "product name ABC", # input/output
|
139
|
+
fee: nil, # output ( if automatic deduction of the payment fee is set at Comgate)
|
140
|
+
variable_symbol: 739_689_656, # output (so I acctually do not know where it came from)
|
141
|
+
apple_pay_payload: "raw apple pay payload", # input
|
142
|
+
dynamic_expiration: false, # input (see https://help.comgate.cz/v1/docs/expirace-plateb )
|
143
|
+
expiration_time: "10h", # input ( use "m" or "h" or "d", but only one of them; allowed rage "30m".."7d")
|
144
|
+
},
|
145
|
+
payer: {
|
146
|
+
email: "payer1@gmail.com", # input/output
|
147
|
+
phone: "+420778899", # input/output
|
148
|
+
account_number: "account_num", # output
|
149
|
+
account_name: "payer account name" # output
|
150
|
+
},
|
151
|
+
options: {
|
152
|
+
country_code: "DE", # input (can restrict allowed payment methods)
|
153
|
+
language_code: "sk" # input
|
154
|
+
},
|
155
|
+
headers: {} # not actually used now
|
156
|
+
}
|
157
|
+
```
|
158
|
+
## Response
|
159
|
+
Response returned from `gateway` call is `Comgate::Response` instance.
|
160
|
+
You can check redirection `response.redirect? ? response.redirect_to : nil`.
|
161
|
+
Most of the time, the response shoul be hash-like, stored in `response.hash`. But for lists there will be array in `response.array`.
|
162
|
+
If there are errors from API call , they will be in `response.errors`. But note, that gateway will raise them and not return `Comgate::Response` instance.
|
163
|
+
And you can also check `result.http_code` (which is surprisingly 200 from API errors)
|
164
|
+
|
165
|
+
## Errors
|
166
|
+
Connection errors or API error responses are raised as RuntimeError with message like ` "{:api=>[\"[Error #1309] incorrect amount\"]}"`.
|
167
|
+
Error Number and text can be found in `lib/comgate/response.rb`.
|
168
|
+
This may be refactored in future.
|
169
|
+
|
170
|
+
## One more thing
|
171
|
+
This gem extends `Hash` with methods `deep_symbolize_keys` and `deep_merge` (if needed).
|
172
|
+
|
173
|
+
## Development
|
174
|
+
|
175
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
176
|
+
|
177
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
178
|
+
|
179
|
+
## Contributing
|
180
|
+
|
181
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/comgate_ruby.
|
182
|
+
|
183
|
+
## License
|
184
|
+
|
185
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "rake/testtask"
|
5
|
+
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
7
|
+
t.libs << "test"
|
8
|
+
t.libs << "lib"
|
9
|
+
t.test_files = FileList["test/**/test_*.rb"]
|
10
|
+
end
|
11
|
+
|
12
|
+
require "rubocop/rake_task"
|
13
|
+
|
14
|
+
RuboCop::RakeTask.new
|
15
|
+
|
16
|
+
task default: %i[test rubocop]
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/comgate/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "comgate_ruby"
|
7
|
+
spec.version = Comgate::VERSION
|
8
|
+
spec.authors = ["Petr Mlčoch"]
|
9
|
+
spec.email = ["foton@centrum.cz"]
|
10
|
+
|
11
|
+
spec.summary = "Client for Comgate payment gateway"
|
12
|
+
spec.description = "Write a longer description or delete this line."
|
13
|
+
spec.homepage = "https://github.com/sinfin/comgate_ruby"
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.required_ruby_version = ">= 3.0"
|
16
|
+
|
17
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
18
|
+
|
19
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
20
|
+
spec.metadata["source_code_uri"] = "https://github.com/sinfin/comgate_ruby"
|
21
|
+
spec.metadata["changelog_uri"] = "https://github.com/sinfin/comgate_ruby/blob/main/CHANGELOG.md"
|
22
|
+
|
23
|
+
# Specify which files should be added to the gem when it is released.
|
24
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
25
|
+
spec.files = Dir.chdir(__dir__) do
|
26
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
27
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
28
|
+
end
|
29
|
+
end
|
30
|
+
spec.bindir = "exe"
|
31
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
32
|
+
spec.require_paths = ["lib"]
|
33
|
+
|
34
|
+
# Uncomment to register a new dependency of your gem
|
35
|
+
# spec.add_dependency "example-gem", "~> 1.0"
|
36
|
+
|
37
|
+
# For more information and examples about making a new gem, check out our
|
38
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
39
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
40
|
+
end
|
data/lib/base_service.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class BaseService
|
4
|
+
attr_reader :result, :errors
|
5
|
+
|
6
|
+
def self.call(*args, **keyword_args)
|
7
|
+
if args.empty?
|
8
|
+
new(**keyword_args).call
|
9
|
+
elsif keyword_args.empty?
|
10
|
+
new(*args).call
|
11
|
+
else
|
12
|
+
new(*args, **keyword_args).call
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
build_result
|
18
|
+
self # always returnning service itself, to get to `errors`, `result`
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(*_args, **_keyword_args)
|
22
|
+
@result = nil
|
23
|
+
@errors = {}
|
24
|
+
end
|
25
|
+
|
26
|
+
def success?
|
27
|
+
errors.empty?
|
28
|
+
end
|
29
|
+
|
30
|
+
def failure?
|
31
|
+
!success?
|
32
|
+
end
|
33
|
+
alias failed? failure?
|
34
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "openssl"
|
4
|
+
require "net/http"
|
5
|
+
require "json"
|
6
|
+
|
7
|
+
module Comgate
|
8
|
+
class ApiCaller < BaseService
|
9
|
+
KNOWN_CONNECTION_ERRORS = [
|
10
|
+
Timeout::Error,
|
11
|
+
Errno::EINVAL,
|
12
|
+
Errno::ECONNRESET,
|
13
|
+
EOFError,
|
14
|
+
SocketError,
|
15
|
+
Net::ReadTimeout,
|
16
|
+
Net::HTTPBadResponse,
|
17
|
+
Net::HTTPHeaderSyntaxError,
|
18
|
+
Net::ProtocolError
|
19
|
+
].freeze
|
20
|
+
|
21
|
+
attr_reader :payload, :url
|
22
|
+
|
23
|
+
def initialize(url:, payload:, test_call: false)
|
24
|
+
super()
|
25
|
+
@url = url
|
26
|
+
@payload = payload
|
27
|
+
@payload.merge!(test: "true") if test_call
|
28
|
+
end
|
29
|
+
|
30
|
+
def build_result
|
31
|
+
call_api
|
32
|
+
process_response
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
attr_accessor :response
|
38
|
+
|
39
|
+
def call_api
|
40
|
+
self.response = https_conn.request(request)
|
41
|
+
rescue *KNOWN_CONNECTION_ERRORS => e
|
42
|
+
handle_connection_error(e)
|
43
|
+
end
|
44
|
+
|
45
|
+
def process_response
|
46
|
+
return unless errors.empty?
|
47
|
+
|
48
|
+
api_log(:debug, "Comgate API RESPONSE: #{response} with body:\n#{response.body}")
|
49
|
+
|
50
|
+
@result = {
|
51
|
+
http_code: response.code.to_i,
|
52
|
+
redirect_to: find_redirect_to,
|
53
|
+
response_body: decoded_response_body
|
54
|
+
}
|
55
|
+
|
56
|
+
record_api_error
|
57
|
+
end
|
58
|
+
|
59
|
+
def https_conn
|
60
|
+
@https_conn ||= Net::HTTP.start(service_uri.host, service_uri.port, connection_options)
|
61
|
+
end
|
62
|
+
|
63
|
+
def request
|
64
|
+
request = Net::HTTP::Post.new service_uri.request_uri, headers
|
65
|
+
request.body = encoded_request_body
|
66
|
+
|
67
|
+
debug_msg = "Commgate API REQUEST: #{request} to #{service_uri} " \
|
68
|
+
"with headers: #{headers}\n and body:\n#{request.body}"
|
69
|
+
api_log(:debug, debug_msg)
|
70
|
+
|
71
|
+
request
|
72
|
+
end
|
73
|
+
|
74
|
+
def service_uri
|
75
|
+
@service_uri ||= URI.parse(url)
|
76
|
+
end
|
77
|
+
|
78
|
+
def headers
|
79
|
+
{ "Content-Type" => "application/x-www-form-urlencoded" }
|
80
|
+
end
|
81
|
+
|
82
|
+
def connection_options
|
83
|
+
{
|
84
|
+
use_ssl: true,
|
85
|
+
verify_mode: OpenSSL::SSL::VERIFY_PEER,
|
86
|
+
keep_alive_timeout: 30
|
87
|
+
# ciphers: secure_and_available_ciphers,
|
88
|
+
# cert: OpenSSL::X509::Certificate.new(File.read(configuration.certificate_path)),
|
89
|
+
# cert_password: configuration.certificate_password,
|
90
|
+
# key: OpenSSL::PKey::RSA.new(File.read(configuration.private_key_path), configuration.private_key_password),
|
91
|
+
# cert_store: post_signum_ca_store
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
def find_redirect_to
|
96
|
+
case response&.code&.to_i
|
97
|
+
when 302
|
98
|
+
response_location
|
99
|
+
when 200
|
100
|
+
decoded_response_body.is_a?(Hash) ? decoded_response_body["redirect"] : nil
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def response_redirect?
|
105
|
+
!redirect_to.nil?
|
106
|
+
end
|
107
|
+
|
108
|
+
def record_api_error
|
109
|
+
return unless api_error?
|
110
|
+
|
111
|
+
errors[:api] = ["[Error ##{result[:response_body]["error"]}] #{result[:response_body]["message"]}"]
|
112
|
+
@result[:errors] = { api: { code: result[:response_body]["error"].to_i,
|
113
|
+
message: result[:response_body]["message"] } }
|
114
|
+
end
|
115
|
+
|
116
|
+
def api_error?
|
117
|
+
return false unless decoded_response_body.is_a?(Hash)
|
118
|
+
|
119
|
+
decoded_response_body["error"].to_i.positive?
|
120
|
+
end
|
121
|
+
|
122
|
+
def handle_connection_error(error)
|
123
|
+
errors[:connection] = ["#{error.class} > #{service_uri} - #{error}"]
|
124
|
+
|
125
|
+
@result = {
|
126
|
+
http_code: 500,
|
127
|
+
errors: { connection: [{ code: 500, message: "#{error.class} > #{service_uri} - #{error}" }] }
|
128
|
+
}
|
129
|
+
end
|
130
|
+
|
131
|
+
def encoded_request_body
|
132
|
+
URI.encode_www_form(payload)
|
133
|
+
end
|
134
|
+
|
135
|
+
def decoded_response_body
|
136
|
+
@decoded_response_body ||= case response_content_type
|
137
|
+
when :url_encoded
|
138
|
+
URI.decode_www_form(response.body).to_h
|
139
|
+
when :json
|
140
|
+
JSON.parse(response.body)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def api_log(level, message)
|
145
|
+
if defined?(Rails) && Rails.respond_to?(:logger)
|
146
|
+
Rails.logger.send(forced_log_level(level), message)
|
147
|
+
else
|
148
|
+
puts("#{Time.now} [#{forced_log_level(level)}] #{message}")
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def forced_log_level(original_level)
|
153
|
+
levels = { debug: 0, info: 1, error: 2 }
|
154
|
+
minimal_level = :error
|
155
|
+
levels[original_level] > levels[minimal_level] ? original_level : minimal_level
|
156
|
+
end
|
157
|
+
|
158
|
+
def response_location
|
159
|
+
path_or_url = response["location"]
|
160
|
+
return nil if path_or_url == ""
|
161
|
+
|
162
|
+
response_uri = URI.parse(path_or_url)
|
163
|
+
response_uri = URI.join(url, response_uri) if response_uri.relative?
|
164
|
+
response_uri.to_s
|
165
|
+
end
|
166
|
+
|
167
|
+
def response_content_type
|
168
|
+
rct = response["content-type"]
|
169
|
+
return nil if rct.nil?
|
170
|
+
|
171
|
+
if rct.include?("json")
|
172
|
+
:json
|
173
|
+
elsif rct.include?("form-urlencoded")
|
174
|
+
:url_encoded
|
175
|
+
else
|
176
|
+
raise "Uncaptured content type: '#{rct}'"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,214 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "base64"
|
4
|
+
|
5
|
+
module Comgate
|
6
|
+
class Gateway
|
7
|
+
BASE_URL = "https://payments.comgate.cz/v1.0"
|
8
|
+
DATA_CONVERSION_HASH = {
|
9
|
+
curr: %i[payment currency],
|
10
|
+
email: %i[payer email],
|
11
|
+
label: %i[payment label],
|
12
|
+
method: %i[payment method],
|
13
|
+
price: %i[payment amount_in_cents],
|
14
|
+
amount: %i[payment amount_in_cents],
|
15
|
+
refId: %i[payment reference_id],
|
16
|
+
account: %i[merchant target_shop_account],
|
17
|
+
applePayPayload: %i[payment apple_pay_payload],
|
18
|
+
country: %i[options country_code],
|
19
|
+
dynamicExpiration: %i[payment dynamic_expiration],
|
20
|
+
embedded: %i[options embedded_iframe],
|
21
|
+
expirationTime: %i[payment expiration_time],
|
22
|
+
lang: %i[options language_code],
|
23
|
+
name: %i[payment product_name],
|
24
|
+
phone: %i[payer phone],
|
25
|
+
preauth: %i[payment preauthorization],
|
26
|
+
verification: %i[payment verification_payment],
|
27
|
+
|
28
|
+
# responses
|
29
|
+
transId: %i[transaction_id],
|
30
|
+
transferId: %i[transfer_id],
|
31
|
+
code: %i[code],
|
32
|
+
message: %i[message],
|
33
|
+
payerId: %i[payer id],
|
34
|
+
payerName: %i[payer account_name],
|
35
|
+
payer_name: %i[payer account_name],
|
36
|
+
payerAcc: %i[payer account_number],
|
37
|
+
payer_acc: %i[payer account_number],
|
38
|
+
fee: %i[payment fee],
|
39
|
+
methods: %i[methods],
|
40
|
+
vs: %i[payment variable_symbol],
|
41
|
+
variableSymbol: %i[variable_symbol],
|
42
|
+
transferDate: %i[transfer_date],
|
43
|
+
accountCounterparty: %i[account_counterparty],
|
44
|
+
accountOutgoing: %i[account_outgoing],
|
45
|
+
status: %i[state],
|
46
|
+
test: %i[test],
|
47
|
+
merchant: %i[merchant gateway_id],
|
48
|
+
secret: %i[secret],
|
49
|
+
redirect: %i[redirect_to]
|
50
|
+
}.freeze
|
51
|
+
|
52
|
+
attr_reader :options
|
53
|
+
|
54
|
+
def initialize(options)
|
55
|
+
@options = options
|
56
|
+
return unless options[:merchant_gateway_id].nil? || options[:secret].nil? || options[:test_calls].nil?
|
57
|
+
|
58
|
+
raise ArgumentError "options have to include :merchant_gateway_id, :secret and :test_calls"
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_calls_used?
|
62
|
+
options[:test_calls] == true
|
63
|
+
end
|
64
|
+
|
65
|
+
def start_transaction(payment_data)
|
66
|
+
make_call(url: "#{BASE_URL}/create",
|
67
|
+
payload: single_payment_payload(payment_data),
|
68
|
+
test_call: test_call?(payment_data[:test]))
|
69
|
+
end
|
70
|
+
|
71
|
+
def start_recurring_transaction(payment_data)
|
72
|
+
make_call(url: "#{BASE_URL}/create",
|
73
|
+
payload: single_payment_payload(payment_data).merge(initRecurring: true),
|
74
|
+
test_call: test_call?(payment_data[:test]))
|
75
|
+
end
|
76
|
+
|
77
|
+
def repeat_recurring_transaction(payment_data)
|
78
|
+
transaction_id = payment_data.delete(:transaction_id)
|
79
|
+
make_call(url: "#{BASE_URL}/recurring",
|
80
|
+
payload: single_payment_payload(payment_data).merge(initRecurringId: transaction_id),
|
81
|
+
test_call: test_call?(payment_data[:test]))
|
82
|
+
end
|
83
|
+
|
84
|
+
def start_verification_transaction(payment_data)
|
85
|
+
make_call(url: "#{BASE_URL}/create",
|
86
|
+
payload: single_payment_payload(payment_data).merge(verification: true),
|
87
|
+
test_call: test_call?(payment_data[:test]))
|
88
|
+
end
|
89
|
+
|
90
|
+
def start_preauthorized_transaction(payment_data)
|
91
|
+
make_call(url: "#{BASE_URL}/create",
|
92
|
+
payload: single_payment_payload(payment_data).merge(preauth: true),
|
93
|
+
test_call: test_call?(payment_data[:test]))
|
94
|
+
end
|
95
|
+
|
96
|
+
def confirm_preauthorized_transaction(payment_data)
|
97
|
+
params = convert_data_to_comgate_params(%i[transId amount], payment_data, required: true)
|
98
|
+
|
99
|
+
make_call(url: "#{BASE_URL}/capturePreauth",
|
100
|
+
payload: gateway_params.merge(params),
|
101
|
+
test_call: false)
|
102
|
+
end
|
103
|
+
|
104
|
+
def cancel_preauthorized_transaction(transaction_id:)
|
105
|
+
make_call(url: "#{BASE_URL}/cancelPreauth",
|
106
|
+
payload: gateway_params.merge(transId: transaction_id),
|
107
|
+
test_call: false)
|
108
|
+
end
|
109
|
+
|
110
|
+
def refund_transaction(payment_data)
|
111
|
+
refund_params = convert_data_to_comgate_params(%i[transId amount], payment_data, required: true)
|
112
|
+
refund_params.merge!(convert_data_to_comgate_params(%i[curr refId], payment_data, required: false))
|
113
|
+
|
114
|
+
make_call(url: "#{BASE_URL}/refund",
|
115
|
+
payload: gateway_params.merge(refund_params),
|
116
|
+
test_call: test_call?(payment_data[:test]))
|
117
|
+
end
|
118
|
+
|
119
|
+
def cancel_transaction(transaction_id:)
|
120
|
+
make_call(url: "#{BASE_URL}/cancel",
|
121
|
+
payload: gateway_params.merge(transId: transaction_id),
|
122
|
+
test_call: false)
|
123
|
+
end
|
124
|
+
|
125
|
+
def check_state(transaction_id:)
|
126
|
+
make_call(url: "#{BASE_URL}/status",
|
127
|
+
payload: gateway_params.merge(transId: transaction_id),
|
128
|
+
test_call: false)
|
129
|
+
end
|
130
|
+
|
131
|
+
def process_payment_callback(comgate_params)
|
132
|
+
Comgate::Response.new({ response_body: comgate_params }, DATA_CONVERSION_HASH)
|
133
|
+
end
|
134
|
+
|
135
|
+
def allowed_payment_methods(payment_data)
|
136
|
+
ph = gateway_params.merge(convert_data_to_comgate_params(%i[curr lang country], payment_data, required: false))
|
137
|
+
|
138
|
+
response = make_call(url: "#{BASE_URL}/methods",
|
139
|
+
payload: ph,
|
140
|
+
test_call: false,
|
141
|
+
conversion_hash: { name: [:name] })
|
142
|
+
response.array = response.hash[:methods]
|
143
|
+
response.hash = nil
|
144
|
+
response
|
145
|
+
end
|
146
|
+
|
147
|
+
def transfers_from(date_or_time)
|
148
|
+
date_str = date_or_time.strftime("%Y-%m-%d")
|
149
|
+
|
150
|
+
make_call(url: "#{BASE_URL}/transferList",
|
151
|
+
payload: gateway_params.merge({ date: date_str }),
|
152
|
+
test_call: false)
|
153
|
+
end
|
154
|
+
|
155
|
+
private
|
156
|
+
|
157
|
+
attr_reader :payment_data
|
158
|
+
|
159
|
+
def make_call(url:, payload:, test_call:, conversion_hash: DATA_CONVERSION_HASH)
|
160
|
+
raise "There are errors in pre-api-call phase: #{payload[:errors]}" unless payload[:errors].nil?
|
161
|
+
|
162
|
+
srv = Comgate::ApiCaller.call(url: url, payload: payload, test_call: test_call)
|
163
|
+
if srv.success?
|
164
|
+
Comgate::Response.new(srv.result, conversion_hash)
|
165
|
+
else
|
166
|
+
handle_failure_from(srv.errors)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def test_call?(test_from_data = nil)
|
171
|
+
test_from_data.nil? ? test_calls_used? : (test_from_data == true)
|
172
|
+
end
|
173
|
+
|
174
|
+
def handle_failure_from(errors)
|
175
|
+
raise errors.to_s
|
176
|
+
end
|
177
|
+
|
178
|
+
def single_payment_payload(payment_data)
|
179
|
+
required_keys = %i[curr email label method price refId]
|
180
|
+
optional_keys = %i[account applePayPayload country dynamicExpiration embedded expirationTime lang name phone
|
181
|
+
preauth verification]
|
182
|
+
|
183
|
+
ph = gateway_params.merge({ prepareOnly: true })
|
184
|
+
ph.merge!(convert_data_to_comgate_params(required_keys, payment_data, required: true))
|
185
|
+
ph.merge!(convert_data_to_comgate_params(optional_keys, payment_data, required: false))
|
186
|
+
ph[:applePayPayload] = Base64.encode64(ph[:applePayPayload]) unless ph[:applePayPayload].nil?
|
187
|
+
ph
|
188
|
+
end
|
189
|
+
|
190
|
+
def gateway_params
|
191
|
+
{ merchant: options[:merchant_gateway_id],
|
192
|
+
secret: options[:secret] }
|
193
|
+
end
|
194
|
+
|
195
|
+
def convert_data_to_comgate_params(comgate_keys, data, required:)
|
196
|
+
h = {}
|
197
|
+
comgate_keys.each do |comg_key|
|
198
|
+
dig_keys = DATA_CONVERSION_HASH[comg_key.to_sym]
|
199
|
+
raise "comgate key '#{comg_key}' is not setup in conversion_hash" if dig_keys.nil?
|
200
|
+
|
201
|
+
value = data.dig(*dig_keys)
|
202
|
+
if value.nil?
|
203
|
+
if required
|
204
|
+
h[:errors] = [] if h[:errors].nil?
|
205
|
+
h[:errors] << "Missing value for `params#{dig_keys.collect { |k| "[:#{k}]" }.join}` in `#{data}`"
|
206
|
+
end
|
207
|
+
else
|
208
|
+
h[comg_key] = value
|
209
|
+
end
|
210
|
+
end
|
211
|
+
h
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Comgate
|
4
|
+
class Response
|
5
|
+
RESPONSE_CODES = {
|
6
|
+
0 => "OK",
|
7
|
+
1100 => "unknown error",
|
8
|
+
1102 => "the specified language is not supported",
|
9
|
+
1103 => "method incorrectly specified",
|
10
|
+
1104 => "unable to load payment",
|
11
|
+
1107 => "payment price is not supported",
|
12
|
+
1200 => "database error",
|
13
|
+
1301 => "unknown e-shop",
|
14
|
+
1303 => "the link or language is missing",
|
15
|
+
1304 => "invalid category",
|
16
|
+
1305 => "product description is missing",
|
17
|
+
1306 => "select the correct method",
|
18
|
+
1308 => "the selected payment method is not allowed",
|
19
|
+
1309 => "incorrect amount",
|
20
|
+
1310 => "unknown currency",
|
21
|
+
1311 => "invalid e-shop bank account identifier",
|
22
|
+
1316 => "e-shop does not allow recurring payments",
|
23
|
+
1317 => "invalid method - does not support recurring payments",
|
24
|
+
1318 => "initial payment not found",
|
25
|
+
1319 => "can not create a payment, a problem on the part of the bank",
|
26
|
+
1399 => "unexpected result from database",
|
27
|
+
1400 => "wrong query",
|
28
|
+
1401 => "the refunded payment is in the CANCELED state",
|
29
|
+
# or 1401 =>"Transaction FVN0-NS40-NA5B has not been authorized, current status: READY"
|
30
|
+
1402 => "refund amount higher than allowed",
|
31
|
+
1500 => "unexpected error"
|
32
|
+
}.freeze
|
33
|
+
|
34
|
+
attr_accessor :http_code, :redirect_to, :hash, :array, :errors
|
35
|
+
attr_reader :params_conversion_hash
|
36
|
+
|
37
|
+
def initialize(caller_result, params_conversion_hash = {})
|
38
|
+
@params_conversion_hash = params_conversion_hash
|
39
|
+
|
40
|
+
@http_code = caller_result[:http_code].to_i
|
41
|
+
@errors = fill_error_messages(caller_result[:errors])
|
42
|
+
@redirect_to = caller_result[:redirect_to]
|
43
|
+
|
44
|
+
converted_body = convert_comgate_params_to_data(caller_result[:response_body])
|
45
|
+
|
46
|
+
case converted_body
|
47
|
+
when Hash
|
48
|
+
@hash = converted_body
|
49
|
+
@array = nil
|
50
|
+
when Array
|
51
|
+
@array = converted_body
|
52
|
+
@hash = nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def redirect?
|
57
|
+
!redirect_to.nil?
|
58
|
+
end
|
59
|
+
|
60
|
+
def error?
|
61
|
+
!errors.nil?
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def convert_comgate_params_to_data(comgate_params)
|
67
|
+
h = transform_comgate_params(comgate_params)
|
68
|
+
return h unless h.is_a?(Hash)
|
69
|
+
|
70
|
+
cleanup_hash(h)
|
71
|
+
end
|
72
|
+
|
73
|
+
def cleanup_hash(rsp_hash) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
74
|
+
rsp_hash.delete(:secret)
|
75
|
+
|
76
|
+
rsp_hash[:state] = rsp_hash[:state].to_s.downcase.to_sym unless rsp_hash[:state].nil?
|
77
|
+
unless rsp_hash.dig(:payment, :variable_symbol).nil?
|
78
|
+
rsp_hash[:payment][:variable_symbol] = rsp_hash.dig(:payment, :variable_symbol).to_i
|
79
|
+
end
|
80
|
+
unless rsp_hash.dig(:payment, :amount_in_cents).nil?
|
81
|
+
rsp_hash[:payment][:amount_in_cents] = rsp_hash.dig(:payment, :amount_in_cents).to_i
|
82
|
+
end
|
83
|
+
rsp_hash[:variable_symbol] = rsp_hash[:variable_symbol].to_i unless rsp_hash[:variable_symbol].nil?
|
84
|
+
if (fee = rsp_hash.dig(:payment, :fee))
|
85
|
+
rsp_hash[:payment][:fee] = fee == "unknown" ? nil : fee.to_f
|
86
|
+
end
|
87
|
+
|
88
|
+
rsp_hash[:code] = rsp_hash[:code].to_i if rsp_hash[:code]
|
89
|
+
rsp_hash[:error] = rsp_hash[:error].to_i if rsp_hash[:error]
|
90
|
+
|
91
|
+
rsp_hash[:message] = RESPONSE_CODES[rsp_hash[:code]] if rsp_hash[:message].to_s == "" && rsp_hash[:code]
|
92
|
+
rsp_hash
|
93
|
+
end
|
94
|
+
|
95
|
+
def transform_comgate_params(comgate_params)
|
96
|
+
case comgate_params
|
97
|
+
when Hash
|
98
|
+
transform_comgate_hash(comgate_params)
|
99
|
+
when Array
|
100
|
+
comgate_params.collect { |item| transform_comgate_params(item) }
|
101
|
+
when "true"
|
102
|
+
true
|
103
|
+
when "false"
|
104
|
+
false
|
105
|
+
else
|
106
|
+
comgate_params
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def transform_comgate_hash(comgate_params)
|
111
|
+
h = {}
|
112
|
+
comgate_params.each_pair do |k, v|
|
113
|
+
build_keys = params_conversion_hash[k.to_sym]&.dup
|
114
|
+
transformed_value = transform_comgate_params(v)
|
115
|
+
|
116
|
+
if build_keys.nil? # not covered in params_conversion_hash
|
117
|
+
h[k.to_sym] = transformed_value
|
118
|
+
else
|
119
|
+
last_key = build_keys.delete(build_keys.last)
|
120
|
+
hash_at_level = h
|
121
|
+
build_keys.each do |bk|
|
122
|
+
hash_at_level[bk] = {} if hash_at_level[bk].nil?
|
123
|
+
hash_at_level = hash_at_level[bk]
|
124
|
+
end
|
125
|
+
hash_at_level[last_key] = transformed_value
|
126
|
+
end
|
127
|
+
end
|
128
|
+
h
|
129
|
+
end
|
130
|
+
|
131
|
+
def fill_error_messages(caller_errors)
|
132
|
+
return nil if caller_errors.nil?
|
133
|
+
return caller_errors if caller_errors[:api].nil?
|
134
|
+
|
135
|
+
caller_errors[:api] = caller_errors[:api].collect do |err_h|
|
136
|
+
err_h[:message] = RESPONSE_CODES[err_h[:code]] if err_h[:message].to_s == ""
|
137
|
+
err_h
|
138
|
+
end
|
139
|
+
caller_errors
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
data/lib/comgate_ruby.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
unless {}.respond_to?(:deep_merge)
|
4
|
+
class Hash
|
5
|
+
def deep_merge(other_hash)
|
6
|
+
result = dup
|
7
|
+
other_hash.each do |key, value|
|
8
|
+
existing_value = result[key]
|
9
|
+
result[key] = if existing_value.is_a?(Hash)
|
10
|
+
existing_value.deep_merge(value)
|
11
|
+
else
|
12
|
+
value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
result
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
unless {}.respond_to?(:deep_symbolize_keys)
|
21
|
+
class Hash
|
22
|
+
def deep_symbolize_keys
|
23
|
+
result = {}
|
24
|
+
each_pair do |k, v|
|
25
|
+
result[k.to_sym] = case v
|
26
|
+
when Hash
|
27
|
+
v.deep_symbolize_keys
|
28
|
+
when Array
|
29
|
+
v.collect { |item| item.is_a?(Hash) ? item.deep_symbolize_keys : item }
|
30
|
+
else
|
31
|
+
v
|
32
|
+
end
|
33
|
+
end
|
34
|
+
result
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: comgate_ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Petr Mlčoch
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-04-20 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Write a longer description or delete this line.
|
14
|
+
email:
|
15
|
+
- foton@centrum.cz
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- ".rubocop.yml"
|
21
|
+
- ".tool-versions"
|
22
|
+
- CHANGELOG.md
|
23
|
+
- Gemfile
|
24
|
+
- Gemfile.lock
|
25
|
+
- LICENSE
|
26
|
+
- LICENSE.txt
|
27
|
+
- README.md
|
28
|
+
- Rakefile
|
29
|
+
- comgate_ruby.gemspec
|
30
|
+
- lib/base_service.rb
|
31
|
+
- lib/comgate/api_caller.rb
|
32
|
+
- lib/comgate/gateway.rb
|
33
|
+
- lib/comgate/response.rb
|
34
|
+
- lib/comgate/version.rb
|
35
|
+
- lib/comgate_ruby.rb
|
36
|
+
- lib/hash_enhancements.rb
|
37
|
+
- sig/comgate_ruby.rbs
|
38
|
+
homepage: https://github.com/sinfin/comgate_ruby
|
39
|
+
licenses:
|
40
|
+
- MIT
|
41
|
+
metadata:
|
42
|
+
allowed_push_host: https://rubygems.org
|
43
|
+
homepage_uri: https://github.com/sinfin/comgate_ruby
|
44
|
+
source_code_uri: https://github.com/sinfin/comgate_ruby
|
45
|
+
changelog_uri: https://github.com/sinfin/comgate_ruby/blob/main/CHANGELOG.md
|
46
|
+
rubygems_mfa_required: 'true'
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options: []
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '3.0'
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
requirements: []
|
62
|
+
rubygems_version: 3.3.7
|
63
|
+
signing_key:
|
64
|
+
specification_version: 4
|
65
|
+
summary: Client for Comgate payment gateway
|
66
|
+
test_files: []
|