comgate_ruby 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|