p24 0.1.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 +33 -0
- data/LICENSE +21 -0
- data/LICENSE.txt +21 -0
- data/README.md +203 -0
- data/Rakefile +12 -0
- data/lib/p24/api/v1/request/transaction_register.rb +100 -0
- data/lib/p24/api/v1/request/transaction_verify.rb +51 -0
- data/lib/p24/api/v1/response/payment_method/availability_hours.rb +35 -0
- data/lib/p24/api/v1/response/payment_method/data.rb +50 -0
- data/lib/p24/api/v1/response/payment_methods.rb +33 -0
- data/lib/p24/api/v1/response/test_access.rb +31 -0
- data/lib/p24/api/v1/response/transaction_register.rb +33 -0
- data/lib/p24/api/v1/response/transaction_verify.rb +33 -0
- data/lib/p24/api/v1/transaction_notification.rb +79 -0
- data/lib/p24/client.rb +142 -0
- data/lib/p24/version.rb +5 -0
- data/lib/p24.rb +28 -0
- data/lib/przelewy24.rb +3 -0
- data/sig/p24.rbs +4 -0
- metadata +76 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: fe475892d3ce9320eda7573e4fbbf8b7296708d5505700ad80300bcae324f4c4
|
|
4
|
+
data.tar.gz: fcae282e5de5d184b27d24456aa53a2cd4996808fa9b76910597d4ba67156b66
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 1890ad413b885b269ad7db9afd04b795a27208c8c6d629539ffaaca431611c8a81fa3a94524146128375673ff392eda9823bae7a5bdb51f8d0375005e56a3813
|
|
7
|
+
data.tar.gz: 5875fbd4044a7f9e34dbdc0090a7a14e44dba89ab64072b92aaf6dcee83eb8c316bfc4abd9f771b6e01eee305295892c8c4770d424bc7b4845c92d005dc54831
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require:
|
|
2
|
+
- rubocop-minitest
|
|
3
|
+
- rubocop-rake
|
|
4
|
+
Style/Documentation:
|
|
5
|
+
Enabled: false
|
|
6
|
+
AllCops:
|
|
7
|
+
TargetRubyVersion: 3.2
|
|
8
|
+
NewCops: disable
|
|
9
|
+
Exclude:
|
|
10
|
+
- "bin/**/*"
|
|
11
|
+
- "vendor/**/*"
|
|
12
|
+
Metrics/MethodLength:
|
|
13
|
+
Enabled: false
|
|
14
|
+
Metrics/BlockLength:
|
|
15
|
+
Exclude:
|
|
16
|
+
- 'config/environments/**/*'
|
|
17
|
+
Security/MarshalLoad:
|
|
18
|
+
Enabled: false
|
|
19
|
+
Lint/MissingSuper:
|
|
20
|
+
Enabled: false
|
|
21
|
+
Style/FrozenStringLiteralComment:
|
|
22
|
+
Enabled: true
|
|
23
|
+
EnforcedStyle: always
|
|
24
|
+
Metrics/ClassLength:
|
|
25
|
+
Max: 150
|
|
26
|
+
Exclude:
|
|
27
|
+
- 'test/**/*'
|
|
28
|
+
Layout/LineLength:
|
|
29
|
+
Max: 150
|
|
30
|
+
Metrics/ParameterLists:
|
|
31
|
+
Enabled: false
|
|
32
|
+
Metrics/AbcSize:
|
|
33
|
+
Enabled: false
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Konrad Makowski
|
|
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) 2024 Konrad Makowski
|
|
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,203 @@
|
|
|
1
|
+
# P24 == Przelewy24
|
|
2
|
+
|
|
3
|
+
A Ruby gem for integrating with Przelewy24 (P24), a popular Polish payment gateway. This library provides a simple and intuitive interface for handling online payments through the Przelewy24 API v1.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Transaction registration and verification
|
|
8
|
+
- Payment method discovery
|
|
9
|
+
- Transaction notification handling with signature verification
|
|
10
|
+
- Support for both sandbox and production environments
|
|
11
|
+
- Built-in authentication and request signing
|
|
12
|
+
- Comprehensive error handling
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
Add this line to your application's Gemfile:
|
|
17
|
+
|
|
18
|
+
```ruby
|
|
19
|
+
gem 'p24'
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
And then execute:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
bundle install
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Or install it yourself as:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
gem install p24
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Configuration
|
|
35
|
+
|
|
36
|
+
Initialize the client with your Przelewy24 credentials:
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
client = P24::Client.new(
|
|
40
|
+
user: 'your_merchant_id',
|
|
41
|
+
secret_id: 'your_api_secret',
|
|
42
|
+
crc: 'your_crc_key',
|
|
43
|
+
base_url: 'https://sandbox.przelewy24.pl/api/v1', # Use production URL for live transactions
|
|
44
|
+
timeout: 30,
|
|
45
|
+
encoding: 'UTF-8',
|
|
46
|
+
debug: false
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Alternatively, you can use the Przelewy24 alias
|
|
50
|
+
client = Przelewy24.new(
|
|
51
|
+
user: 'your_merchant_id',
|
|
52
|
+
secret_id: 'your_api_secret',
|
|
53
|
+
crc: 'your_crc_key'
|
|
54
|
+
)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Configuration Options
|
|
58
|
+
|
|
59
|
+
- `user` (required): Your Przelewy24 merchant ID
|
|
60
|
+
- `secret_id` (required): Your API secret/password
|
|
61
|
+
- `crc` (required): Your CRC key for request signing
|
|
62
|
+
- `base_url` (optional): API endpoint URL (defaults to sandbox)
|
|
63
|
+
- `timeout` (optional): Request timeout in seconds (default: 30)
|
|
64
|
+
- `encoding` (optional): Character encoding (default: 'UTF-8')
|
|
65
|
+
- `debug` (optional): Enable debug output (default: false)
|
|
66
|
+
|
|
67
|
+
## Usage
|
|
68
|
+
|
|
69
|
+
### Test API Access
|
|
70
|
+
|
|
71
|
+
Verify your credentials and API connectivity:
|
|
72
|
+
|
|
73
|
+
```ruby
|
|
74
|
+
response = client.test_access
|
|
75
|
+
puts response.error_code # Should be 0 for successful connection
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Register a Transaction
|
|
79
|
+
|
|
80
|
+
Create a new payment transaction:
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
response = client.transaction_register(
|
|
84
|
+
merchant_id: 12345,
|
|
85
|
+
pos_id: 12345,
|
|
86
|
+
session_id: 'unique_session_id',
|
|
87
|
+
amount: 1000, # Amount in grosze (1000 = 10.00 PLN)
|
|
88
|
+
currency: 'PLN',
|
|
89
|
+
description: 'Order #123',
|
|
90
|
+
email: 'customer@example.com',
|
|
91
|
+
country: 'PL',
|
|
92
|
+
language: 'pl',
|
|
93
|
+
url_return: 'https://yoursite.com/payment/return',
|
|
94
|
+
url_status: 'https://yoursite.com/payment/status'
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# Get the payment token
|
|
98
|
+
token = response.token
|
|
99
|
+
|
|
100
|
+
# Redirect user to payment page
|
|
101
|
+
redirect_url = client.redirect_url(token)
|
|
102
|
+
# => "https://sandbox.przelewy24.pl/trnRequest/{token}"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Verify a Transaction
|
|
106
|
+
|
|
107
|
+
Verify transaction status after payment:
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
response = client.transaction_verify(
|
|
111
|
+
merchant_id: 12345,
|
|
112
|
+
pos_id: 12345,
|
|
113
|
+
session_id: 'unique_session_id',
|
|
114
|
+
amount: 1000,
|
|
115
|
+
currency: 'PLN',
|
|
116
|
+
order_id: 98765 # Received from P24 notification
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
puts response.error_code # 0 means successful verification
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Handle Payment Notifications
|
|
123
|
+
|
|
124
|
+
Verify incoming payment notifications from Przelewy24:
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
# In your webhook/callback endpoint
|
|
128
|
+
is_valid = client.transaction_notification(
|
|
129
|
+
merchant_id: params[:merchantId],
|
|
130
|
+
pos_id: params[:posId],
|
|
131
|
+
session_id: params[:sessionId],
|
|
132
|
+
amount: params[:amount],
|
|
133
|
+
origin_amount: params[:originAmount],
|
|
134
|
+
currency: params[:currency],
|
|
135
|
+
order_id: params[:orderId],
|
|
136
|
+
method_id: params[:methodId],
|
|
137
|
+
statement: params[:statement],
|
|
138
|
+
sign: params[:sign]
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
if is_valid
|
|
142
|
+
# Process successful payment
|
|
143
|
+
else
|
|
144
|
+
# Invalid signature - potential fraud attempt
|
|
145
|
+
end
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Get Available Payment Methods
|
|
149
|
+
|
|
150
|
+
Retrieve available payment methods for your customers:
|
|
151
|
+
|
|
152
|
+
```ruby
|
|
153
|
+
methods = client.payment_methods(
|
|
154
|
+
'pl', # Language
|
|
155
|
+
amount: 1000, # Optional: filter by amount
|
|
156
|
+
currency: 'PLN' # Optional: filter by currency
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
methods.each do |method|
|
|
160
|
+
puts "#{method.name} - #{method.status}"
|
|
161
|
+
end
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## API Reference
|
|
165
|
+
|
|
166
|
+
### Client Methods
|
|
167
|
+
|
|
168
|
+
#### `transaction_register(**kwargs)`
|
|
169
|
+
Registers a new transaction and returns a token for payment redirection.
|
|
170
|
+
|
|
171
|
+
#### `transaction_verify(**kwargs)`
|
|
172
|
+
Verifies a completed transaction.
|
|
173
|
+
|
|
174
|
+
#### `transaction_notification(**kwargs)`
|
|
175
|
+
Validates the signature of a payment notification from P24.
|
|
176
|
+
|
|
177
|
+
#### `payment_methods(lang, amount: nil, currency: nil)`
|
|
178
|
+
Retrieves available payment methods.
|
|
179
|
+
|
|
180
|
+
#### `test_access`
|
|
181
|
+
Tests API credentials and connectivity.
|
|
182
|
+
|
|
183
|
+
#### `redirect_url(token)`
|
|
184
|
+
Generates the payment page URL for a given token.
|
|
185
|
+
|
|
186
|
+
## Development
|
|
187
|
+
|
|
188
|
+
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.
|
|
189
|
+
|
|
190
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
191
|
+
|
|
192
|
+
## Contributing
|
|
193
|
+
|
|
194
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/DeVeLo/p24.
|
|
195
|
+
|
|
196
|
+
## License
|
|
197
|
+
|
|
198
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
199
|
+
|
|
200
|
+
## Resources
|
|
201
|
+
|
|
202
|
+
- [Przelewy24 Official Documentation](https://www.przelewy24.pl/do-pobrania#dokumentacja-techniczna)
|
|
203
|
+
- [Przelewy24 API v1 Reference](https://developers.przelewy24.pl/)
|
data/Rakefile
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module P24
|
|
4
|
+
module Api
|
|
5
|
+
module V1
|
|
6
|
+
module Request
|
|
7
|
+
class TransactionRegister
|
|
8
|
+
attr_reader :merchant_id, :pos_id, :session_id, :amount, :currency, :description, :email,
|
|
9
|
+
:client, :address, :zip, :city, :country, :phone, :language, :method, :url_return,
|
|
10
|
+
:url_status, :time_limit, :channel, :wait_for_result, :regulation_accept, :shipping,
|
|
11
|
+
:transfer_label, :mobile_lib, :sdk_version, :encoding, :method_ref_id, :crc
|
|
12
|
+
|
|
13
|
+
def initialize(merchant_id:, pos_id:, session_id:, amount:, currency:, description:, email:,
|
|
14
|
+
country:, language:, url_return:, crc:, client: nil, address: nil, zip: nil, city: nil,
|
|
15
|
+
phone: nil, method: nil, url_status: nil, time_limit: nil, channel: nil,
|
|
16
|
+
wait_for_result: nil, regulation_accept: nil, shipping: nil, transfer_label: nil,
|
|
17
|
+
mobile_lib: nil, sdk_version: nil, encoding: nil, method_ref_id: nil)
|
|
18
|
+
@merchant_id = merchant_id
|
|
19
|
+
@pos_id = pos_id
|
|
20
|
+
@session_id = session_id
|
|
21
|
+
@amount = amount
|
|
22
|
+
@currency = currency
|
|
23
|
+
@description = description
|
|
24
|
+
@email = email
|
|
25
|
+
@client = client
|
|
26
|
+
@address = address
|
|
27
|
+
@zip = zip
|
|
28
|
+
@city = city
|
|
29
|
+
@country = country
|
|
30
|
+
@phone = phone
|
|
31
|
+
@language = language
|
|
32
|
+
@method = method
|
|
33
|
+
@url_return = url_return
|
|
34
|
+
@url_status = url_status
|
|
35
|
+
@time_limit = time_limit
|
|
36
|
+
@channel = channel
|
|
37
|
+
@wait_for_result = wait_for_result
|
|
38
|
+
@regulation_accept = regulation_accept
|
|
39
|
+
@shipping = shipping
|
|
40
|
+
@transfer_label = transfer_label
|
|
41
|
+
@mobile_lib = mobile_lib
|
|
42
|
+
@sdk_version = sdk_version
|
|
43
|
+
@encoding = encoding
|
|
44
|
+
@method_ref_id = method_ref_id
|
|
45
|
+
@crc = crc
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def to_json(*_args)
|
|
49
|
+
{
|
|
50
|
+
merchantId: merchant_id,
|
|
51
|
+
posId: pos_id,
|
|
52
|
+
sessionId: session_id,
|
|
53
|
+
amount:,
|
|
54
|
+
currency:,
|
|
55
|
+
description:,
|
|
56
|
+
email:,
|
|
57
|
+
client:,
|
|
58
|
+
address:,
|
|
59
|
+
zip:,
|
|
60
|
+
city:,
|
|
61
|
+
country:,
|
|
62
|
+
phone:,
|
|
63
|
+
language:,
|
|
64
|
+
method:,
|
|
65
|
+
urlReturn: url_return,
|
|
66
|
+
urlStatus: url_status,
|
|
67
|
+
timeLimit: time_limit,
|
|
68
|
+
channel:,
|
|
69
|
+
waitForResult: wait_for_result,
|
|
70
|
+
regulationAccept: regulation_accept,
|
|
71
|
+
shipping:,
|
|
72
|
+
transferLabel: transfer_label,
|
|
73
|
+
mobileLib: mobile_lib,
|
|
74
|
+
sdkVersion: sdk_version,
|
|
75
|
+
sign:,
|
|
76
|
+
encoding:,
|
|
77
|
+
methodRefId: method_ref_id
|
|
78
|
+
}.compact.to_json
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def sign_params
|
|
84
|
+
{
|
|
85
|
+
sessionId: session_id,
|
|
86
|
+
merchantId: merchant_id,
|
|
87
|
+
amount:,
|
|
88
|
+
currency:,
|
|
89
|
+
crc:
|
|
90
|
+
}
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def sign
|
|
94
|
+
Digest::SHA384.hexdigest sign_params.to_json
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module P24
|
|
4
|
+
module Api
|
|
5
|
+
module V1
|
|
6
|
+
module Request
|
|
7
|
+
class TransactionVerify
|
|
8
|
+
attr_reader :merchant_id, :pos_id, :session_id, :amount, :currency, :order_id, :crc
|
|
9
|
+
|
|
10
|
+
def initialize(merchant_id:, pos_id:, session_id:, amount:, currency:, order_id:, crc:)
|
|
11
|
+
@merchant_id = merchant_id
|
|
12
|
+
@pos_id = pos_id
|
|
13
|
+
@session_id = session_id
|
|
14
|
+
@amount = amount
|
|
15
|
+
@currency = currency
|
|
16
|
+
@order_id = order_id
|
|
17
|
+
@crc = crc
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_json(*_args)
|
|
21
|
+
{
|
|
22
|
+
merchantId: merchant_id,
|
|
23
|
+
posId: pos_id,
|
|
24
|
+
orderId: order_id,
|
|
25
|
+
sessionId: session_id,
|
|
26
|
+
amount:,
|
|
27
|
+
currency:,
|
|
28
|
+
sign:
|
|
29
|
+
}.compact.to_json
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def sign_params
|
|
35
|
+
{
|
|
36
|
+
sessionId: session_id,
|
|
37
|
+
orderId: order_id,
|
|
38
|
+
amount:,
|
|
39
|
+
currency:,
|
|
40
|
+
crc:
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def sign
|
|
45
|
+
Digest::SHA384.hexdigest sign_params.to_json
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module P24
|
|
4
|
+
module Api
|
|
5
|
+
module V1
|
|
6
|
+
module Response
|
|
7
|
+
module PaymentMethod
|
|
8
|
+
class AvailabilityHours
|
|
9
|
+
attr_reader :monday_to_friday, :saturday, :sunday
|
|
10
|
+
|
|
11
|
+
def initialize(monday_to_friday:, saturday:, sunday:)
|
|
12
|
+
@monday_to_friday = monday_to_friday
|
|
13
|
+
@saturday = saturday
|
|
14
|
+
@sunday = sunday
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_json(options = {})
|
|
18
|
+
{ monday_to_friday:, saturday:, sunday: }.to_json(options)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class << self
|
|
22
|
+
def from_json(json)
|
|
23
|
+
new(
|
|
24
|
+
monday_to_friday: json['mondayToFriday'],
|
|
25
|
+
saturday: json['saturday'],
|
|
26
|
+
sunday: json['sunday']
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module P24
|
|
4
|
+
module Api
|
|
5
|
+
module V1
|
|
6
|
+
module Response
|
|
7
|
+
module PaymentMethod
|
|
8
|
+
class Data
|
|
9
|
+
attr_reader :name, :id, :group, :subgroup, :status, :img_url,
|
|
10
|
+
:mobile_img_url, :mobile, :availability_hours
|
|
11
|
+
|
|
12
|
+
def initialize(name:, id:, group:, subgroup:, status:, img_url:,
|
|
13
|
+
mobile_img_url:, mobile:, availability_hours:)
|
|
14
|
+
@name = name
|
|
15
|
+
@id = id
|
|
16
|
+
@group = group
|
|
17
|
+
@subgroup = subgroup
|
|
18
|
+
@status = status
|
|
19
|
+
@img_url = img_url
|
|
20
|
+
@mobile_img_url = mobile_img_url
|
|
21
|
+
@mobile = mobile
|
|
22
|
+
@availability_hours = AvailabilityHours.from_json(availability_hours)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def to_json(options = {})
|
|
26
|
+
{ name:, id:, group:, subgroup:, status:, img_url:,
|
|
27
|
+
mobile_img_url:, mobile:, availability_hours: }.to_json(options)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class << self
|
|
31
|
+
def from_json(json)
|
|
32
|
+
new(
|
|
33
|
+
name: json['name'],
|
|
34
|
+
id: json['id'],
|
|
35
|
+
group: json['group'],
|
|
36
|
+
subgroup: json['subgroup'],
|
|
37
|
+
status: json['status'],
|
|
38
|
+
img_url: json['imgUrl'],
|
|
39
|
+
mobile_img_url: json['mobileImgUrl'],
|
|
40
|
+
mobile: json['mobile'],
|
|
41
|
+
availability_hours: json['availabilityHours']
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module P24
|
|
4
|
+
module Api
|
|
5
|
+
module V1
|
|
6
|
+
module Response
|
|
7
|
+
class PaymentMethods
|
|
8
|
+
attr_reader :data, :agreements, :response_code
|
|
9
|
+
|
|
10
|
+
def initialize(data:, agreements:, response_code:)
|
|
11
|
+
@data = data.map { |d| PaymentMethod::Data.from_json(d) }
|
|
12
|
+
@agreements = agreements
|
|
13
|
+
@response_code = response_code
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def to_json(options = {})
|
|
17
|
+
{ data:, agreements:, response_code: }.to_json(options)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class << self
|
|
21
|
+
def from_json(json)
|
|
22
|
+
new(
|
|
23
|
+
data: json['data'],
|
|
24
|
+
agreements: json['agreements'],
|
|
25
|
+
response_code: json['responseCode']
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module P24
|
|
4
|
+
module Api
|
|
5
|
+
module V1
|
|
6
|
+
module Response
|
|
7
|
+
class TestAccess
|
|
8
|
+
attr_reader :data, :error
|
|
9
|
+
|
|
10
|
+
def initialize(data:, error:)
|
|
11
|
+
@data = data
|
|
12
|
+
@error = error
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def to_json(options = {})
|
|
16
|
+
{ data:, error: }.to_json(options)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class << self
|
|
20
|
+
def from_json(json)
|
|
21
|
+
new(
|
|
22
|
+
data: json['data'],
|
|
23
|
+
error: json['error']
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module P24
|
|
4
|
+
module Api
|
|
5
|
+
module V1
|
|
6
|
+
module Response
|
|
7
|
+
class TransactionRegister
|
|
8
|
+
attr_reader :data, :response_code
|
|
9
|
+
|
|
10
|
+
Data = Struct.new(:token)
|
|
11
|
+
|
|
12
|
+
def initialize(data:, response_code:)
|
|
13
|
+
@data = Data.new(data['token'])
|
|
14
|
+
@response_code = response_code
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_json(options = {})
|
|
18
|
+
{ data: data.to_h, response_code: }.to_json(options)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class << self
|
|
22
|
+
def from_json(json)
|
|
23
|
+
new(
|
|
24
|
+
data: json['data'],
|
|
25
|
+
response_code: json['responseCode']
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module P24
|
|
4
|
+
module Api
|
|
5
|
+
module V1
|
|
6
|
+
module Response
|
|
7
|
+
class TransactionVerify
|
|
8
|
+
attr_reader :data, :response_code
|
|
9
|
+
|
|
10
|
+
Data = Struct.new(:status)
|
|
11
|
+
|
|
12
|
+
def initialize(data:, response_code:)
|
|
13
|
+
@data = Data.new(data['status'])
|
|
14
|
+
@response_code = response_code
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_json(options = {})
|
|
18
|
+
{ data: data.to_h, response_code: }.to_json(options)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class << self
|
|
22
|
+
def from_json(json)
|
|
23
|
+
new(
|
|
24
|
+
data: json['data'],
|
|
25
|
+
response_code: json['responseCode']
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module P24
|
|
4
|
+
module Api
|
|
5
|
+
module V1
|
|
6
|
+
class TransactionNotification
|
|
7
|
+
attr_reader :merchant_id, :pos_id, :session_id, :amount, :origin_amount, :currency,
|
|
8
|
+
:order_id, :method_id, :statement, :crc, :sign
|
|
9
|
+
|
|
10
|
+
def initialize(merchant_id:, pos_id:, session_id:, amount:, origin_amount:, currency:,
|
|
11
|
+
order_id:, method_id:, statement:, crc:, sign: nil)
|
|
12
|
+
@merchant_id = merchant_id
|
|
13
|
+
@pos_id = pos_id
|
|
14
|
+
@session_id = session_id
|
|
15
|
+
@amount = amount
|
|
16
|
+
@origin_amount = origin_amount
|
|
17
|
+
@currency = currency
|
|
18
|
+
@order_id = order_id
|
|
19
|
+
@method_id = method_id
|
|
20
|
+
@statement = statement
|
|
21
|
+
@sign = sign
|
|
22
|
+
@crc = crc
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def to_json(*_args)
|
|
26
|
+
{
|
|
27
|
+
merchantId: merchant_id,
|
|
28
|
+
posId: pos_id,
|
|
29
|
+
sessionId: session_id,
|
|
30
|
+
amount:,
|
|
31
|
+
originAmount: origin_amount,
|
|
32
|
+
currency:,
|
|
33
|
+
orderId: order_id,
|
|
34
|
+
methodId: method_id,
|
|
35
|
+
statement:,
|
|
36
|
+
sign:
|
|
37
|
+
}.compact.to_json
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def from_json(json, crc)
|
|
41
|
+
new(
|
|
42
|
+
merchant_id: json['merchantId'],
|
|
43
|
+
pos_id: json['posId'],
|
|
44
|
+
session_id: json['sessionId'],
|
|
45
|
+
amount: ['amount'],
|
|
46
|
+
origin_amount: json['originAmount'],
|
|
47
|
+
currency: json['currency'],
|
|
48
|
+
order_id: json['orderId'],
|
|
49
|
+
method_id: json['methodId'],
|
|
50
|
+
statement: json['statement'],
|
|
51
|
+
sign: json['sign'],
|
|
52
|
+
crc:
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def correct?
|
|
57
|
+
sign == Digest::SHA384.hexdigest(sign_params.to_json)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
def sign_params
|
|
63
|
+
{
|
|
64
|
+
merchantId: merchant_id,
|
|
65
|
+
posId: pos_id,
|
|
66
|
+
sessionId: session_id,
|
|
67
|
+
amount:,
|
|
68
|
+
originAmount: origin_amount,
|
|
69
|
+
currency:,
|
|
70
|
+
orderId: order_id,
|
|
71
|
+
methodId: method_id,
|
|
72
|
+
statement:,
|
|
73
|
+
crc:
|
|
74
|
+
}
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
data/lib/p24/client.rb
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module P24
|
|
4
|
+
class Client
|
|
5
|
+
attr_reader :user, :secret_id, :crc, :base_url, :timeout, :debug, :encoding
|
|
6
|
+
|
|
7
|
+
def initialize(user:, secret_id:, crc:, base_url: 'https://sandbox.przelewy24.pl/api/v1', timeout: 30,
|
|
8
|
+
encoding: 'UTF-8', debug: false)
|
|
9
|
+
@user = user
|
|
10
|
+
@secret_id = secret_id
|
|
11
|
+
@crc = crc
|
|
12
|
+
@timeout = timeout
|
|
13
|
+
@base_url = base_url
|
|
14
|
+
@debug = debug
|
|
15
|
+
@encoding = encoding
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def transaction_register(**kwargs)
|
|
19
|
+
generic_post_json(
|
|
20
|
+
endpoint('/transaction/register'),
|
|
21
|
+
Api::V1::Request::TransactionRegister.new(**kwargs.merge({ crc:, encoding: })).to_json,
|
|
22
|
+
Api::V1::Response::TransactionRegister
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def transaction_verify(**kwargs)
|
|
27
|
+
generic_put_json(
|
|
28
|
+
endpoint('/transaction/verify'),
|
|
29
|
+
Api::V1::Request::TransactionVerify.new(**kwargs.merge({ crc: })).to_json,
|
|
30
|
+
Api::V1::Response::TransactionVerify
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def transaction_notification(**kwargs)
|
|
35
|
+
Api::V1::TransactionNotification.new(**kwargs.merge({ crc: })).correct?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def payment_methods(lang, amount: nil, currency: nil)
|
|
39
|
+
generic_json endpoint(
|
|
40
|
+
"/payment/methods/#{lang}",
|
|
41
|
+
**keyword_args(__method__, binding)
|
|
42
|
+
), Api::V1::Response::PaymentMethods
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_access
|
|
46
|
+
generic_json endpoint(
|
|
47
|
+
'/testAccess'
|
|
48
|
+
), Api::V1::Response::TestAccess
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def redirect_url(token)
|
|
52
|
+
"#{uri(base_url)}/trnRequest/#{token}"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def endpoint(url, *_args, **params)
|
|
58
|
+
uri = URI "#{base_url}#{url}"
|
|
59
|
+
uri.query = URI.encode_www_form(convert_keys_to_camelcase(params)).gsub('%2C', ',')
|
|
60
|
+
uri.to_s
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def generic_json(uri, type)
|
|
64
|
+
response = HTTParty.get(uri, timeout:, basic_auth:, headers:, debug_output: debug ? $stdout : false)
|
|
65
|
+
handle_response(uri, response, type)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def generic_post_json(uri, body, type)
|
|
69
|
+
response = HTTParty.post(uri, timeout:, basic_auth:, headers:, body:, debug_output: debug ? $stdout : false)
|
|
70
|
+
handle_response(uri, response, type)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def generic_put_json(uri, body, type)
|
|
74
|
+
response = HTTParty.put(uri, timeout:, basic_auth:, headers:, body:, debug_output: debug ? $stdout : false)
|
|
75
|
+
handle_response(uri, response, type)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def handle_response(uri, response, type)
|
|
79
|
+
if response.success?
|
|
80
|
+
json_response = JSON.parse(response.body)
|
|
81
|
+
response_container = json_response.is_a?(Array) ? json_response : json_response['response']
|
|
82
|
+
if response_container.is_a?(Array)
|
|
83
|
+
response_container.map do |response_json|
|
|
84
|
+
type.from_json(response_json)
|
|
85
|
+
end
|
|
86
|
+
else
|
|
87
|
+
type.from_json(json_response)
|
|
88
|
+
end
|
|
89
|
+
else
|
|
90
|
+
raise_error(response, uri)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def raise_error(response, uri)
|
|
95
|
+
raise "Invalid HTTP response code: #{response.code}; " \
|
|
96
|
+
"Response body: #{response.body}; " \
|
|
97
|
+
"Request URI: #{uri};"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def convert_keys_to_camelcase(hash)
|
|
101
|
+
hash.transform_keys { |key| camelcase(key.to_s) }
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def basic_auth
|
|
105
|
+
{ username: user, password: secret_id }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def headers
|
|
109
|
+
{ 'Content-Type' => 'application/json' }
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def pids(array)
|
|
113
|
+
return if array.count.zero?
|
|
114
|
+
|
|
115
|
+
array.join(',')
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def handle_args(arg)
|
|
119
|
+
case arg
|
|
120
|
+
when Array
|
|
121
|
+
pids(arg)
|
|
122
|
+
else
|
|
123
|
+
arg
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def camelcase(str)
|
|
128
|
+
str.split('_').inject([]) { |buffer, e| buffer.push(buffer.empty? ? e : e.capitalize) }.join
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def keyword_args(meth, bind)
|
|
132
|
+
method(meth).parameters.select { |type, _| %i[key keyreq].include? type }.map do |_, name|
|
|
133
|
+
[name, handle_args(bind.local_variable_get(name))]
|
|
134
|
+
end.to_h.compact
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def uri(url)
|
|
138
|
+
purl = URI.parse(url)
|
|
139
|
+
"#{purl.scheme}://#{purl.host}"
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
data/lib/p24/version.rb
ADDED
data/lib/p24.rb
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'uri'
|
|
5
|
+
require 'httparty'
|
|
6
|
+
require 'digest'
|
|
7
|
+
|
|
8
|
+
require_relative 'p24/version'
|
|
9
|
+
require_relative 'p24/client'
|
|
10
|
+
|
|
11
|
+
require_relative 'przelewy24'
|
|
12
|
+
|
|
13
|
+
require_relative 'p24/api/v1/response/test_access'
|
|
14
|
+
|
|
15
|
+
require_relative 'p24/api/v1/request/transaction_register'
|
|
16
|
+
|
|
17
|
+
require_relative 'p24/api/v1/response/payment_methods'
|
|
18
|
+
require_relative 'p24/api/v1/response/payment_method/data'
|
|
19
|
+
require_relative 'p24/api/v1/response/payment_method/availability_hours'
|
|
20
|
+
require_relative 'p24/api/v1/response/transaction_register'
|
|
21
|
+
|
|
22
|
+
require_relative 'p24/api/v1/request/transaction_verify'
|
|
23
|
+
require_relative 'p24/api/v1/response/transaction_verify'
|
|
24
|
+
|
|
25
|
+
require_relative 'p24/api/v1/transaction_notification'
|
|
26
|
+
|
|
27
|
+
module P24
|
|
28
|
+
end
|
data/lib/przelewy24.rb
ADDED
data/sig/p24.rbs
ADDED
metadata
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: p24
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Konrad Makowski
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: httparty
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
description: A Ruby gem for integrating with Przelewy24 (P24), a popular Polish payment
|
|
27
|
+
gateway. Provides transaction registration, verification, payment method discovery,
|
|
28
|
+
and notification handling with signature verification.
|
|
29
|
+
email:
|
|
30
|
+
- konrad@snopkow.eu
|
|
31
|
+
executables: []
|
|
32
|
+
extensions: []
|
|
33
|
+
extra_rdoc_files: []
|
|
34
|
+
files:
|
|
35
|
+
- ".rubocop.yml"
|
|
36
|
+
- LICENSE
|
|
37
|
+
- LICENSE.txt
|
|
38
|
+
- README.md
|
|
39
|
+
- Rakefile
|
|
40
|
+
- lib/p24.rb
|
|
41
|
+
- lib/p24/api/v1/request/transaction_register.rb
|
|
42
|
+
- lib/p24/api/v1/request/transaction_verify.rb
|
|
43
|
+
- lib/p24/api/v1/response/payment_method/availability_hours.rb
|
|
44
|
+
- lib/p24/api/v1/response/payment_method/data.rb
|
|
45
|
+
- lib/p24/api/v1/response/payment_methods.rb
|
|
46
|
+
- lib/p24/api/v1/response/test_access.rb
|
|
47
|
+
- lib/p24/api/v1/response/transaction_register.rb
|
|
48
|
+
- lib/p24/api/v1/response/transaction_verify.rb
|
|
49
|
+
- lib/p24/api/v1/transaction_notification.rb
|
|
50
|
+
- lib/p24/client.rb
|
|
51
|
+
- lib/p24/version.rb
|
|
52
|
+
- lib/przelewy24.rb
|
|
53
|
+
- sig/p24.rbs
|
|
54
|
+
homepage: https://github.com/DeVeLo/p24
|
|
55
|
+
licenses:
|
|
56
|
+
- MIT
|
|
57
|
+
metadata:
|
|
58
|
+
homepage_uri: https://github.com/DeVeLo/p24
|
|
59
|
+
rdoc_options: []
|
|
60
|
+
require_paths:
|
|
61
|
+
- lib
|
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
63
|
+
requirements:
|
|
64
|
+
- - ">="
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: 3.2.4
|
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
68
|
+
requirements:
|
|
69
|
+
- - ">="
|
|
70
|
+
- !ruby/object:Gem::Version
|
|
71
|
+
version: '0'
|
|
72
|
+
requirements: []
|
|
73
|
+
rubygems_version: 4.0.0
|
|
74
|
+
specification_version: 4
|
|
75
|
+
summary: Ruby client for Przelewy24 payment gateway API
|
|
76
|
+
test_files: []
|