omnikassa2 1.0.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/README.md +102 -0
- data/lib/omnikassa2.rb +112 -0
- data/lib/omnikassa2/helpers/access_token_provider.rb +34 -0
- data/lib/omnikassa2/helpers/csv_serializer.rb +54 -0
- data/lib/omnikassa2/helpers/signature_service.rb +15 -0
- data/lib/omnikassa2/models/access_token.rb +34 -0
- data/lib/omnikassa2/models/merchant_order.rb +53 -0
- data/lib/omnikassa2/models/money.rb +11 -0
- data/lib/omnikassa2/models/notification.rb +53 -0
- data/lib/omnikassa2/models/order_result.rb +23 -0
- data/lib/omnikassa2/models/order_result_set.rb +80 -0
- data/lib/omnikassa2/requests/base_request.rb +105 -0
- data/lib/omnikassa2/requests/order_announce_request.rb +49 -0
- data/lib/omnikassa2/requests/refresh_request.rb +21 -0
- data/lib/omnikassa2/requests/status_pull_request.rb +32 -0
- data/lib/omnikassa2/responses/base_response.rb +35 -0
- data/lib/omnikassa2/responses/order_announce_response.rb +26 -0
- data/lib/omnikassa2/responses/refresh_response.rb +9 -0
- data/lib/omnikassa2/responses/status_pull_response.rb +29 -0
- data/lib/omnikassa2/version.rb +3 -0
- metadata +94 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f48d6d341c45e5536653e3bbf82952731907457113ea69ac1465f2ad4e8c92c8
|
|
4
|
+
data.tar.gz: 47b2a46a7e7e7e84cec95c14f2b787b0740e70b87a0680e596e4d7c07b1195e0
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 649bb08ac160481cc24de3b765cf61f2dd0b0560fd407394ad71ca58d7df2f1a66ee7c66b12631fb923fedc19b018f740708c1c5ab7045f6a0c5d7a9ce53d557
|
|
7
|
+
data.tar.gz: 494ff7a965a8f99ca3ca32ada837ca991a4ace7248e3b7e87ba1e8e3b93273c3f53f1fce80006466f3e352b4ffb3c4643ffcd4df4cd8279b83f2306ba4658fe7
|
data/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Omnikassa2
|
|
2
|
+
|
|
3
|
+
This Gem provides the Ruby integration for the new Omnikassa 2.0 JSON API from the
|
|
4
|
+
Rabobank. The documentation for this API is currently here:
|
|
5
|
+
[Rabobank.nl](https://www.rabobank.nl/images/handleiding-merchant-shop_29920545.pdf)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
Add this line to your application's Gemfile:
|
|
11
|
+
|
|
12
|
+
```ruby
|
|
13
|
+
gem 'omnikassa2'
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
And then execute:
|
|
17
|
+
|
|
18
|
+
$ bundle
|
|
19
|
+
|
|
20
|
+
Or install it yourself as:
|
|
21
|
+
|
|
22
|
+
$ gem install omnikassa2
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## Configuration
|
|
26
|
+
You can find your `refresh_token` and `signing_key` in Omnikassa's dashboard. The `base_url` corresponds with the base_url of the Omnikassa2 API. You can use `:sandbox` or `:production` as well.
|
|
27
|
+
|
|
28
|
+
```ruby
|
|
29
|
+
Omnikassa2.config(
|
|
30
|
+
refresh_token: 'my_refresh_token',
|
|
31
|
+
signing_key: 'my_signing_key',
|
|
32
|
+
base_url: :sandbox # Shortcut for 'https://betalen.rabobank.nl/omnikassa-api-sandbox'
|
|
33
|
+
)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
For [Status Pull](#status-pull), it is required to configure a webhook as well (see official documentation).
|
|
37
|
+
|
|
38
|
+
## Announce order
|
|
39
|
+
```ruby
|
|
40
|
+
response = Omnikassa2.announce_order(
|
|
41
|
+
Omnikassa2::MerchantOrder.new(
|
|
42
|
+
merchant_order_id: 'order123',
|
|
43
|
+
amount: Money.new(
|
|
44
|
+
amount: 4999,
|
|
45
|
+
currency: 'EUR'
|
|
46
|
+
),
|
|
47
|
+
merchant_return_url: 'https://www.example.org/my-webshop'
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
redirect_url = response.redirect_url
|
|
52
|
+
|
|
53
|
+
# Send client to 'redirect_url'
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Omnikassa will now allow the user to pay. When the payment is finished or terminated, the user will be redirected to the given `merchant_return_url`. These query parameters are `order_id`, `status` and `signature`. We must validate the signature in order to trust the provided parameters:
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
# pseudocode
|
|
60
|
+
class MyLandingPageController
|
|
61
|
+
def get(request)
|
|
62
|
+
params = request.params
|
|
63
|
+
|
|
64
|
+
# Validate passed parameters
|
|
65
|
+
valid_params = Omnikassa2::SignatureService.validate(
|
|
66
|
+
params[:order_id] + ',' + params[:status],
|
|
67
|
+
params[:signature]
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if valid_params
|
|
71
|
+
# Params are trusted
|
|
72
|
+
render 'landing_page', order_status: params[:order_status]
|
|
73
|
+
else
|
|
74
|
+
# Params are not trusted
|
|
75
|
+
render 'error_page'
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Status pull
|
|
82
|
+
Performing a status pull is only possible when notified by Omnikassa through a configured webhook in the dashboard.
|
|
83
|
+
|
|
84
|
+
```ruby
|
|
85
|
+
# pseudocode
|
|
86
|
+
class MyOmnikassaWebhookController
|
|
87
|
+
def post(request)
|
|
88
|
+
# Create notification object
|
|
89
|
+
notification = Omnikassa2::Notification.from_json request.body
|
|
90
|
+
|
|
91
|
+
# Use notification object to retrieve statusses
|
|
92
|
+
Omnikassa2.status_pull(notification) do |order_status|
|
|
93
|
+
# Do something
|
|
94
|
+
puts "Order: #{ order_status.merchant_order_id}"
|
|
95
|
+
puts "Paid amount: #{ order_status.paid_amount.amount }"
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Contributing
|
|
102
|
+
Bug reports, suggestions, questions and pull requests are welcome on GitHub at https://github.com/kabisa/omnikassa2.
|
data/lib/omnikassa2.rb
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
require 'openssl'
|
|
2
|
+
require 'net/http'
|
|
3
|
+
require 'base64'
|
|
4
|
+
|
|
5
|
+
require 'omnikassa2/version'
|
|
6
|
+
|
|
7
|
+
require 'omnikassa2/helpers/access_token_provider'
|
|
8
|
+
require 'omnikassa2/helpers/csv_serializer'
|
|
9
|
+
require 'omnikassa2/helpers/signature_service'
|
|
10
|
+
|
|
11
|
+
require 'omnikassa2/models/access_token'
|
|
12
|
+
require 'omnikassa2/models/merchant_order'
|
|
13
|
+
require 'omnikassa2/models/money'
|
|
14
|
+
require 'omnikassa2/models/notification'
|
|
15
|
+
require 'omnikassa2/models/order_result_set'
|
|
16
|
+
require 'omnikassa2/models/order_result'
|
|
17
|
+
|
|
18
|
+
require 'omnikassa2/requests/base_request'
|
|
19
|
+
require 'omnikassa2/requests/order_announce_request'
|
|
20
|
+
require 'omnikassa2/requests/refresh_request'
|
|
21
|
+
require 'omnikassa2/requests/status_pull_request'
|
|
22
|
+
|
|
23
|
+
require 'omnikassa2/responses/base_response'
|
|
24
|
+
require 'omnikassa2/responses/order_announce_response'
|
|
25
|
+
require 'omnikassa2/responses/refresh_response'
|
|
26
|
+
require 'omnikassa2/responses/status_pull_response'
|
|
27
|
+
|
|
28
|
+
module Omnikassa2
|
|
29
|
+
@@configured = false
|
|
30
|
+
|
|
31
|
+
SETTINGS = :refresh_token, :signing_key, :base_url
|
|
32
|
+
|
|
33
|
+
def self.config(settings)
|
|
34
|
+
for setting in SETTINGS
|
|
35
|
+
value = settings[setting.to_sym]
|
|
36
|
+
raise ConfigError, "config setting '#{setting}' missing" if value.nil?
|
|
37
|
+
|
|
38
|
+
class_variable_set '@@' + setting.to_s, value
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
@@configured = true
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.configured?
|
|
45
|
+
@@configured
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.refresh_token
|
|
49
|
+
@@refresh_token
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def self.signing_key
|
|
53
|
+
Base64.decode64(@@signing_key)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.base_url
|
|
57
|
+
case @@base_url
|
|
58
|
+
when :production
|
|
59
|
+
'https://betalen.rabobank.nl/omnikassa-api'
|
|
60
|
+
when :sandbox
|
|
61
|
+
'https://betalen.rabobank.nl/omnikassa-api-sandbox'
|
|
62
|
+
else
|
|
63
|
+
@@base_url
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def self.announce_order(order_announcement)
|
|
68
|
+
response = Omnikassa2::OrderAnnounceRequest.new(order_announcement).send
|
|
69
|
+
|
|
70
|
+
raise Omnikassa2::HttpError, response.to_s unless response.success?
|
|
71
|
+
raise Omnikassa2::InvalidSignatureError unless response.valid_signature?
|
|
72
|
+
|
|
73
|
+
response
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def self.status_pull(notification)
|
|
77
|
+
more_results_available = true
|
|
78
|
+
while(more_results_available) do
|
|
79
|
+
raise Omnikassa2::InvalidSignatureError unless notification.valid_signature?
|
|
80
|
+
raise Omnikassa2::ExpiringNotificationError if notification.expiring?
|
|
81
|
+
|
|
82
|
+
response = Omnikassa2::StatusPullRequest.new(notification).send
|
|
83
|
+
|
|
84
|
+
raise Omnikassa2::HttpError, response.to_s unless response.success?
|
|
85
|
+
raise Omnikassa2::InvalidSignatureError unless response.valid_signature?
|
|
86
|
+
|
|
87
|
+
result_set = response.order_result_set
|
|
88
|
+
result_set.order_results.each do |order_result|
|
|
89
|
+
yield order_result
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
more_results_available = result_set.more_order_results_available
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# The common base class for all exceptions raised by OmniKassa
|
|
97
|
+
class OmniKassaError < StandardError
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Raised if something is wrong with the configuration parameters
|
|
101
|
+
class ConfigError < OmniKassaError
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
class InvalidSignatureError < OmniKassaError
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
class ExpiringNotificationError < OmniKassaError
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
class HttpError < OmniKassaError
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Omnikassa2
|
|
2
|
+
class AccessTokenProvider
|
|
3
|
+
def self.instance
|
|
4
|
+
@@instance ||= AccessTokenProvider.new
|
|
5
|
+
@@instance
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def access_token
|
|
9
|
+
refresh_token if token_needs_refresh?
|
|
10
|
+
@access_token
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def to_s
|
|
14
|
+
access_token.to_s
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def initialize
|
|
20
|
+
@access_token = nil
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def token_needs_refresh?
|
|
24
|
+
@access_token.nil? || @access_token.expiring?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def refresh_token
|
|
28
|
+
response = Omnikassa2::RefreshRequest.new.send
|
|
29
|
+
raise Omnikassa2::HttpError, response.to_s unless response.success?
|
|
30
|
+
|
|
31
|
+
@access_token = response.access_token
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module Omnikassa2
|
|
2
|
+
class CSVSerializer
|
|
3
|
+
def initialize(config)
|
|
4
|
+
@config = config
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def serialize(object)
|
|
8
|
+
objects = object.kind_of?(Array) ? object : [object]
|
|
9
|
+
parts = []
|
|
10
|
+
objects.each do |object|
|
|
11
|
+
parts << extract_fields(object).join(',')
|
|
12
|
+
end
|
|
13
|
+
parts.join(',')
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def extract_fields(object)
|
|
19
|
+
parts = []
|
|
20
|
+
@config.each do |config_hash|
|
|
21
|
+
value = extract_field(object, config_hash)
|
|
22
|
+
parts << value unless value.nil?
|
|
23
|
+
end
|
|
24
|
+
parts
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def extract_field(object, config_hash)
|
|
28
|
+
field = config_hash.fetch(:field)
|
|
29
|
+
include_if_nil = config_hash.fetch(:include_if_nil, false)
|
|
30
|
+
nested_fields = config_hash.fetch(:nested_fields, nil)
|
|
31
|
+
|
|
32
|
+
value = extract_value object, field
|
|
33
|
+
if(value.kind_of?(Time))
|
|
34
|
+
value = value.iso8601(3)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
if value.nil?
|
|
38
|
+
include_if_nil ? '' : nil
|
|
39
|
+
elsif nested_fields.nil?
|
|
40
|
+
value
|
|
41
|
+
else
|
|
42
|
+
CSVSerializer.new(nested_fields).serialize(value)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def extract_value(object, field)
|
|
47
|
+
if(object.kind_of?(Hash))
|
|
48
|
+
object.fetch(field, nil)
|
|
49
|
+
else
|
|
50
|
+
object.respond_to?(field) ? object.public_send(field) : nil
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Omnikassa2
|
|
2
|
+
class SignatureService
|
|
3
|
+
def self.sign(string)
|
|
4
|
+
OpenSSL::HMAC.hexdigest(
|
|
5
|
+
OpenSSL::Digest.new('sha512'),
|
|
6
|
+
Omnikassa2.signing_key,
|
|
7
|
+
string
|
|
8
|
+
)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.validate(string, signature)
|
|
12
|
+
sign(string) == signature
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'time'
|
|
2
|
+
|
|
3
|
+
module Omnikassa2
|
|
4
|
+
class AccessToken
|
|
5
|
+
EXPIRATION_MARGIN_SECONDS = 300
|
|
6
|
+
|
|
7
|
+
attr_reader :token
|
|
8
|
+
attr_reader :valid_until
|
|
9
|
+
attr_reader :duration_in_millis
|
|
10
|
+
|
|
11
|
+
def initialize(params)
|
|
12
|
+
@token = params.fetch(:token)
|
|
13
|
+
@valid_until = params.fetch(:valid_until)
|
|
14
|
+
@duration_in_millis = params.fetch(:duration_in_millis)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.from_json(json)
|
|
18
|
+
hash = JSON.parse(json)
|
|
19
|
+
AccessToken.new(
|
|
20
|
+
token: hash['token'],
|
|
21
|
+
valid_until: Time.parse(hash['validUntil']),
|
|
22
|
+
duration_in_millis: hash['durationInMillis']
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def expiring?
|
|
27
|
+
(Time.now + EXPIRATION_MARGIN_SECONDS) - @valid_until > 0
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def to_s
|
|
31
|
+
token
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Omnikassa2
|
|
2
|
+
class MerchantOrder
|
|
3
|
+
attr_reader :merchant_return_url
|
|
4
|
+
attr_reader :merchant_order_id
|
|
5
|
+
attr_reader :amount
|
|
6
|
+
|
|
7
|
+
attr_reader :payment_brand
|
|
8
|
+
attr_reader :payment_brand_force
|
|
9
|
+
|
|
10
|
+
def initialize(params)
|
|
11
|
+
@merchant_return_url = params.fetch(:merchant_return_url)
|
|
12
|
+
@merchant_order_id = params.fetch(:merchant_order_id)
|
|
13
|
+
@amount = params.fetch(:amount)
|
|
14
|
+
|
|
15
|
+
@payment_brand = params.fetch(:payment_brand, nil)
|
|
16
|
+
@payment_brand_force = params.fetch(:payment_brand_force, nil)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def timestamp
|
|
20
|
+
@timestamp ||= Time.now.iso8601(3)
|
|
21
|
+
@timestamp
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def signature
|
|
25
|
+
SignatureService.sign to_s
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def to_s
|
|
29
|
+
MerchantOrder.csv_serializer.serialize(self)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def self.csv_serializer
|
|
35
|
+
Omnikassa2::CSVSerializer.new([
|
|
36
|
+
{ field: :timestamp },
|
|
37
|
+
{ field: :merchant_order_id },
|
|
38
|
+
{
|
|
39
|
+
field: :amount,
|
|
40
|
+
nested_fields: [
|
|
41
|
+
{ field: :currency },
|
|
42
|
+
{ field: :amount }
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
{ field: :language, include_if_nil: true },
|
|
46
|
+
{ field: :description, include_if_nil: true },
|
|
47
|
+
{ field: :merchant_return_url },
|
|
48
|
+
{ field: :payment_brand },
|
|
49
|
+
{ field: :payment_brand_force }
|
|
50
|
+
])
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Omnikassa2
|
|
2
|
+
class Notification
|
|
3
|
+
EXPIRATION_MARGIN_SECONDS = 30
|
|
4
|
+
|
|
5
|
+
attr_reader :authentication
|
|
6
|
+
attr_reader :expiry
|
|
7
|
+
attr_reader :event_name
|
|
8
|
+
attr_reader :poi_id
|
|
9
|
+
attr_reader :signature
|
|
10
|
+
|
|
11
|
+
def initialize(params)
|
|
12
|
+
@authentication = params.fetch(:authentication)
|
|
13
|
+
@expiry = params.fetch(:expiry)
|
|
14
|
+
@event_name = params.fetch(:event_name)
|
|
15
|
+
@poi_id = params.fetch(:poi_id)
|
|
16
|
+
@signature = params.fetch(:signature)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.from_json(json)
|
|
20
|
+
hash = JSON.parse(json)
|
|
21
|
+
Notification.new(
|
|
22
|
+
authentication: hash['authentication'],
|
|
23
|
+
expiry: Time.parse(hash['expiry']),
|
|
24
|
+
event_name: hash['eventName'],
|
|
25
|
+
poi_id: hash['poiId'],
|
|
26
|
+
signature: hash['signature']
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def expiring?
|
|
31
|
+
(Time.now + EXPIRATION_MARGIN_SECONDS) - @expiry > 0
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def valid_signature?
|
|
35
|
+
SignatureService.validate(to_s, @signature)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def to_s
|
|
39
|
+
Notification.csv_serializer.serialize(self)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def self.csv_serializer
|
|
45
|
+
CSVSerializer.new([
|
|
46
|
+
{ field: :authentication },
|
|
47
|
+
{ field: :expiry },
|
|
48
|
+
{ field: :event_name },
|
|
49
|
+
{ field: :poi_id }
|
|
50
|
+
])
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Omnikassa2
|
|
2
|
+
class OrderResult
|
|
3
|
+
attr_reader :merchant_order_id
|
|
4
|
+
attr_reader :omnikassa_order_id
|
|
5
|
+
attr_reader :poi_id
|
|
6
|
+
attr_reader :order_status
|
|
7
|
+
attr_reader :order_status_date_time
|
|
8
|
+
attr_reader :error_code
|
|
9
|
+
attr_reader :paid_amount
|
|
10
|
+
attr_reader :total_amount
|
|
11
|
+
|
|
12
|
+
def initialize(params)
|
|
13
|
+
@merchant_order_id = params.fetch(:merchant_order_id)
|
|
14
|
+
@omnikassa_order_id = params.fetch(:omnikassa_order_id)
|
|
15
|
+
@poi_id = params.fetch(:poi_id)
|
|
16
|
+
@order_status = params.fetch(:order_status)
|
|
17
|
+
@order_status_date_time = params.fetch(:order_status_date_time)
|
|
18
|
+
@error_code = params.fetch(:error_code)
|
|
19
|
+
@paid_amount = params.fetch(:paid_amount)
|
|
20
|
+
@total_amount = params.fetch(:total_amount)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
module Omnikassa2
|
|
2
|
+
class OrderResultSet
|
|
3
|
+
attr_reader :more_order_results_available
|
|
4
|
+
attr_reader :order_results
|
|
5
|
+
attr_reader :signature
|
|
6
|
+
|
|
7
|
+
def initialize(params)
|
|
8
|
+
@more_order_results_available = params.fetch(:more_order_results_available)
|
|
9
|
+
@order_results = params.fetch(:order_results)
|
|
10
|
+
@signature = params.fetch(:signature)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def valid_signature?
|
|
14
|
+
SignatureService.validate(to_s, @signature)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_s
|
|
18
|
+
OrderResultSet.csv_serializer.serialize(self)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.from_json(json)
|
|
22
|
+
hash = JSON.parse(json)
|
|
23
|
+
OrderResultSet.new(
|
|
24
|
+
more_order_results_available: hash['moreOrderResultsAvailable'],
|
|
25
|
+
order_results: hash['orderResults'].map do |order|
|
|
26
|
+
OrderResult.new(
|
|
27
|
+
merchant_order_id: order['merchantOrderId'],
|
|
28
|
+
omnikassa_order_id: order['omnikassaOrderId'],
|
|
29
|
+
poi_id: order['poiId'],
|
|
30
|
+
order_status: order['orderStatus'],
|
|
31
|
+
order_status_date_time: Time.parse(order['orderStatusDateTime']),
|
|
32
|
+
error_code: order['errorCode'],
|
|
33
|
+
paid_amount: Money.new(
|
|
34
|
+
amount: order['paidAmount']['amount'].to_i,
|
|
35
|
+
currency: order['paidAmount']['currency']
|
|
36
|
+
),
|
|
37
|
+
total_amount: Money.new(
|
|
38
|
+
amount: order['totalAmount']['amount'].to_i,
|
|
39
|
+
currency: order['totalAmount']['currency']
|
|
40
|
+
)
|
|
41
|
+
)
|
|
42
|
+
end,
|
|
43
|
+
signature: hash['signature']
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def self.csv_serializer
|
|
50
|
+
Omnikassa2::CSVSerializer.new([
|
|
51
|
+
{ field: :more_order_results_available },
|
|
52
|
+
{
|
|
53
|
+
field: :order_results,
|
|
54
|
+
nested_fields: [
|
|
55
|
+
{ field: :merchant_order_id },
|
|
56
|
+
{ field: :omnikassa_order_id },
|
|
57
|
+
{ field: :poi_id },
|
|
58
|
+
{ field: :order_status },
|
|
59
|
+
{ field: :order_status_date_time },
|
|
60
|
+
{ field: :error_code },
|
|
61
|
+
{
|
|
62
|
+
field: :paid_amount,
|
|
63
|
+
nested_fields: [
|
|
64
|
+
{ field: :currency },
|
|
65
|
+
{ field: :amount }
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
field: :total_amount,
|
|
70
|
+
nested_fields: [
|
|
71
|
+
{ field: :currency },
|
|
72
|
+
{ field: :amount }
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
])
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
module Omnikassa2
|
|
2
|
+
class BaseRequest
|
|
3
|
+
def initialize(config = {})
|
|
4
|
+
@access_token = config.fetch(:access_token, Omnikassa2::AccessTokenProvider.instance)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def http_method
|
|
8
|
+
:get
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def authorization_method
|
|
12
|
+
nil
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def custom_token
|
|
16
|
+
nil
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def content_type
|
|
20
|
+
nil
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def path
|
|
24
|
+
'/'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def body
|
|
28
|
+
nil
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def response_decorator
|
|
32
|
+
Omnikassa2::BaseResponse
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def send
|
|
36
|
+
request = request_class.new(uri, headers)
|
|
37
|
+
request.body = body_raw
|
|
38
|
+
|
|
39
|
+
http_response = Net::HTTP.start(
|
|
40
|
+
uri.hostname,
|
|
41
|
+
uri.port,
|
|
42
|
+
use_ssl: true
|
|
43
|
+
) do |http|
|
|
44
|
+
http.request(request)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
response_decorator.nil? ? http_response : response_decorator.new(http_response)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def headers
|
|
51
|
+
value = {}
|
|
52
|
+
add_authorization_header value
|
|
53
|
+
add_content_type_header value
|
|
54
|
+
value
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def body_raw
|
|
60
|
+
return nil if body.nil?
|
|
61
|
+
return body if content_type.nil?
|
|
62
|
+
|
|
63
|
+
case content_type
|
|
64
|
+
when :json
|
|
65
|
+
body.to_json
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def request_class
|
|
70
|
+
case http_method
|
|
71
|
+
when :get
|
|
72
|
+
Net::HTTP::Get
|
|
73
|
+
when :post
|
|
74
|
+
Net::HTTP::Post
|
|
75
|
+
when :put
|
|
76
|
+
Net::HTTP::Put
|
|
77
|
+
when :delete
|
|
78
|
+
Net::HTTP::Delete
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def uri
|
|
83
|
+
tmp_url = Omnikassa2.base_url + path
|
|
84
|
+
URI(tmp_url)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def add_authorization_header(value)
|
|
88
|
+
case authorization_method
|
|
89
|
+
when :refresh_token
|
|
90
|
+
value['Authorization'] = "Bearer #{Omnikassa2.refresh_token}"
|
|
91
|
+
when :access_token
|
|
92
|
+
value['Authorization'] = "Bearer #{@access_token}"
|
|
93
|
+
when :custom_token
|
|
94
|
+
value['Authorization'] = "Bearer #{custom_token}"
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def add_content_type_header(value)
|
|
99
|
+
case content_type
|
|
100
|
+
when :json
|
|
101
|
+
value['Content-Type'] = 'application/json'
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require 'omnikassa2/requests/base_request'
|
|
2
|
+
require 'time'
|
|
3
|
+
|
|
4
|
+
module Omnikassa2
|
|
5
|
+
class OrderAnnounceRequest < BaseRequest
|
|
6
|
+
def initialize(merchant_order, config = {})
|
|
7
|
+
super(config)
|
|
8
|
+
@merchant_order = merchant_order
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def http_method
|
|
12
|
+
:post
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def content_type
|
|
16
|
+
:json
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def authorization_method
|
|
20
|
+
:access_token
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def path
|
|
24
|
+
'/order/server/api/order'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def body
|
|
28
|
+
result = {
|
|
29
|
+
'timestamp' => @merchant_order.timestamp,
|
|
30
|
+
'merchantOrderId' => @merchant_order.merchant_order_id,
|
|
31
|
+
'amount' => {
|
|
32
|
+
'amount' => @merchant_order.amount.amount.to_s,
|
|
33
|
+
'currency' => @merchant_order.amount.currency
|
|
34
|
+
},
|
|
35
|
+
'merchantReturnURL' => @merchant_order.merchant_return_url,
|
|
36
|
+
'signature' => @merchant_order.signature
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
result['paymentBrand'] = @merchant_order.payment_brand unless @merchant_order.payment_brand.nil?
|
|
40
|
+
result['paymentBrandForce'] = @merchant_order.payment_brand_force unless @merchant_order.payment_brand_force.nil?
|
|
41
|
+
|
|
42
|
+
result
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def response_decorator
|
|
46
|
+
OrderAnnounceResponse
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'omnikassa2/requests/base_request'
|
|
2
|
+
|
|
3
|
+
module Omnikassa2
|
|
4
|
+
class RefreshRequest < BaseRequest
|
|
5
|
+
def http_method
|
|
6
|
+
:get
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def authorization_method
|
|
10
|
+
:refresh_token
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def path
|
|
14
|
+
'/gatekeeper/refresh'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def response_decorator
|
|
18
|
+
RefreshResponse
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require 'omnikassa2/requests/base_request'
|
|
2
|
+
require 'omnikassa2'
|
|
3
|
+
|
|
4
|
+
module Omnikassa2
|
|
5
|
+
class StatusPullRequest < BaseRequest
|
|
6
|
+
def initialize(notification, config = {})
|
|
7
|
+
super(config)
|
|
8
|
+
|
|
9
|
+
@notification = notification
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def http_method
|
|
13
|
+
:get
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def authorization_method
|
|
17
|
+
:custom_token
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def custom_token
|
|
21
|
+
@notification.authentication
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def path
|
|
25
|
+
'/order/server/api/events/results/merchant.order.status.changed'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def response_decorator
|
|
29
|
+
StatusPullResponse
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Omnikassa2
|
|
2
|
+
class BaseResponse
|
|
3
|
+
def initialize(http_response)
|
|
4
|
+
@http_response = http_response
|
|
5
|
+
@body = @http_response.body ? JSON.parse(@http_response.body) : nil
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def json_body
|
|
9
|
+
@http_response.body
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def body
|
|
13
|
+
@body
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def code
|
|
17
|
+
@http_response.code.to_i
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def message
|
|
21
|
+
@http_response.message
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def success?
|
|
25
|
+
code >= 200 && code < 300
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def to_s
|
|
29
|
+
value = ''
|
|
30
|
+
value += "Status: #{code}: #{message}\n"
|
|
31
|
+
value += "Body: #{(body ? body.to_s : 'nil')}"
|
|
32
|
+
value
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'omnikassa2/responses/base_response'
|
|
2
|
+
|
|
3
|
+
module Omnikassa2
|
|
4
|
+
class OrderAnnounceResponse < BaseResponse
|
|
5
|
+
def signature
|
|
6
|
+
body['signature']
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def redirect_url
|
|
10
|
+
body['redirectUrl']
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def valid_signature?
|
|
14
|
+
string = OrderAnnounceResponse.csv_serializer.serialize(self)
|
|
15
|
+
SignatureService.validate(string, signature)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def self.csv_serializer
|
|
21
|
+
CSVSerializer.new([
|
|
22
|
+
{ field: :redirect_url }
|
|
23
|
+
])
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'omnikassa2/responses/base_response'
|
|
2
|
+
|
|
3
|
+
module Omnikassa2
|
|
4
|
+
class StatusPullResponse < BaseResponse
|
|
5
|
+
|
|
6
|
+
def to_csv
|
|
7
|
+
csv_serializer.serialize self
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def order_result_set
|
|
11
|
+
OrderResultSet.from_json(json_body)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def valid_signature?
|
|
15
|
+
order_result_set.valid_signature?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def self.csv_serializer
|
|
21
|
+
Omnikassa2::CSVSerializer.new([
|
|
22
|
+
{ field: :authentication },
|
|
23
|
+
{ field: :expiry },
|
|
24
|
+
{ field: :event_name },
|
|
25
|
+
{ field: :description }
|
|
26
|
+
])
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: omnikassa2
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Aike de Jongste
|
|
8
|
+
- Arnout de Mooij
|
|
9
|
+
- Luc Zwanenberg
|
|
10
|
+
autorequire:
|
|
11
|
+
bindir: exe
|
|
12
|
+
cert_chain: []
|
|
13
|
+
date: 2019-05-24 00:00:00.000000000 Z
|
|
14
|
+
dependencies:
|
|
15
|
+
- !ruby/object:Gem::Dependency
|
|
16
|
+
name: bundler
|
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
|
18
|
+
requirements:
|
|
19
|
+
- - "~>"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '1.16'
|
|
22
|
+
type: :development
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
requirements:
|
|
26
|
+
- - "~>"
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
version: '1.16'
|
|
29
|
+
- !ruby/object:Gem::Dependency
|
|
30
|
+
name: rake
|
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
|
32
|
+
requirements:
|
|
33
|
+
- - "~>"
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: '10.0'
|
|
36
|
+
type: :development
|
|
37
|
+
prerelease: false
|
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
39
|
+
requirements:
|
|
40
|
+
- - "~>"
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '10.0'
|
|
43
|
+
description: Omnikassa2 is a gem for Rabobank's Omnikassa 2.0
|
|
44
|
+
email:
|
|
45
|
+
- luc.zwanenberg@kabisa.nl
|
|
46
|
+
executables: []
|
|
47
|
+
extensions: []
|
|
48
|
+
extra_rdoc_files: []
|
|
49
|
+
files:
|
|
50
|
+
- README.md
|
|
51
|
+
- lib/omnikassa2.rb
|
|
52
|
+
- lib/omnikassa2/helpers/access_token_provider.rb
|
|
53
|
+
- lib/omnikassa2/helpers/csv_serializer.rb
|
|
54
|
+
- lib/omnikassa2/helpers/signature_service.rb
|
|
55
|
+
- lib/omnikassa2/models/access_token.rb
|
|
56
|
+
- lib/omnikassa2/models/merchant_order.rb
|
|
57
|
+
- lib/omnikassa2/models/money.rb
|
|
58
|
+
- lib/omnikassa2/models/notification.rb
|
|
59
|
+
- lib/omnikassa2/models/order_result.rb
|
|
60
|
+
- lib/omnikassa2/models/order_result_set.rb
|
|
61
|
+
- lib/omnikassa2/requests/base_request.rb
|
|
62
|
+
- lib/omnikassa2/requests/order_announce_request.rb
|
|
63
|
+
- lib/omnikassa2/requests/refresh_request.rb
|
|
64
|
+
- lib/omnikassa2/requests/status_pull_request.rb
|
|
65
|
+
- lib/omnikassa2/responses/base_response.rb
|
|
66
|
+
- lib/omnikassa2/responses/order_announce_response.rb
|
|
67
|
+
- lib/omnikassa2/responses/refresh_response.rb
|
|
68
|
+
- lib/omnikassa2/responses/status_pull_response.rb
|
|
69
|
+
- lib/omnikassa2/version.rb
|
|
70
|
+
homepage: https://github.com/kabisa/omnikassa2
|
|
71
|
+
licenses:
|
|
72
|
+
- MIT
|
|
73
|
+
metadata: {}
|
|
74
|
+
post_install_message:
|
|
75
|
+
rdoc_options: []
|
|
76
|
+
require_paths:
|
|
77
|
+
- lib
|
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
84
|
+
requirements:
|
|
85
|
+
- - ">="
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: '0'
|
|
88
|
+
requirements: []
|
|
89
|
+
rubyforge_project:
|
|
90
|
+
rubygems_version: 2.7.7
|
|
91
|
+
signing_key:
|
|
92
|
+
specification_version: 4
|
|
93
|
+
summary: Omnikassa2 is a gem for Rabobank's Omnikassa 2.0
|
|
94
|
+
test_files: []
|