adyen-ruby-api-library 7.0.2 → 7.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 +4 -4
- data/Makefile +4 -2
- data/README.md +116 -0
- data/lib/adyen/client.rb +12 -0
- data/lib/adyen/services/balancePlatform/network_tokens_api.rb +29 -0
- data/lib/adyen/services/balancePlatform/payment_instruments_api.rb +9 -0
- data/lib/adyen/services/balancePlatform.rb +5 -0
- data/lib/adyen/services/checkout/recurring_api.rb +3 -3
- data/lib/adyen/services/payment.rb +116 -11
- data/lib/adyen/services/terminalCloudAPI.rb +38 -0
- data/lib/adyen/utils/hmac_validator.rb +11 -0
- data/lib/adyen/version.rb +1 -1
- data/lib/adyen-ruby-api-library.rb +1 -0
- data/spec/client_spec.rb +21 -2
- data/spec/mocks/requests/TerminalCloudAPI/connected_terminals.json +3 -0
- data/spec/mocks/requests/TerminalCloudAPI/sync_payment.json +29 -0
- data/spec/mocks/responses/TerminalCloudAPI/connected_terminals.json +5 -0
- data/spec/mocks/responses/TerminalCloudAPI/sync_payment.json +18 -0
- data/spec/payments_spec.rb +1 -1
- data/spec/terminal_cloud_api_spec.rb +91 -0
- metadata +9 -4
- data/lib/adyen/services/payment/general_api.rb +0 -56
- data/lib/adyen/services/payment/modifications_api.rb +0 -83
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e77636d4dad0702e8816c00de1f5cf37ab3d1c841f73b2b20f5e256275458e53
|
4
|
+
data.tar.gz: fc410bcae06fc7a846e1a72edc39d34f838e2253e59c9e315839c9c299723b55
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c79bf2bfd30680ad73ac71f18416436bdfcfeb110260c3f78456aac857acafeb52f915be43fa2f26f9570c1a6846ed02751dd4b147b574ea4ed35d0e6ce7d8f
|
7
|
+
data.tar.gz: 2848c7cba6583e4ed195c71eaf8e736832bf8b55e713d3f9001d71cea4d129471f1da92a3c5d59e88f3c8c592fd8854039c9f7350955f0361bc059422a7289fc
|
data/Makefile
CHANGED
@@ -5,8 +5,8 @@ openapi-generator-jar:=build/openapi-generator-cli.jar
|
|
5
5
|
openapi-generator-cli:=java -jar build/openapi-generator-cli.jar
|
6
6
|
output:=build/out
|
7
7
|
|
8
|
-
services:=balancePlatform checkout legalEntityManagement management
|
9
|
-
singleFileServices:=balanceControlService binLookup dataProtection recurring storedValue posTerminalManagement
|
8
|
+
services:=balancePlatform checkout legalEntityManagement management payout transfers
|
9
|
+
singleFileServices:=balanceControlService binLookup dataProtection recurring storedValue payment posTerminalManagement
|
10
10
|
|
11
11
|
binLookup: spec=BinLookupService-v54
|
12
12
|
checkout: spec=CheckoutService-v70
|
@@ -43,6 +43,8 @@ $(services): build/spec $(openapi-generator-jar)
|
|
43
43
|
|
44
44
|
$(singleFileServices): build/spec
|
45
45
|
wget $(openapi-generator-url) -O build/openapi-generator-cli.jar
|
46
|
+
jq -e 'del(.paths[][].tags)' build/spec/json/$(spec).json > build/spec/json/$(spec).tmp
|
47
|
+
mv build/spec/json/$(spec).tmp build/spec/json/$(spec).json
|
46
48
|
rm -rf $(output)
|
47
49
|
$(openapi-generator-cli) generate \
|
48
50
|
-i build/spec/json/$(spec).json \
|
data/README.md
CHANGED
@@ -21,6 +21,7 @@ This library supports the following:
|
|
21
21
|
| [Recurring API](https://docs.adyen.com/api-explorer/Recurring/68/overview) | v68 | Endpoints for managing saved payment details. | [Recurring](lib/adyen/services/recurring.rb) |
|
22
22
|
| [Stored Value API](https://docs.adyen.com/payment-methods/gift-cards/stored-value-api) | v46 | Manage both online and point-of-sale gift cards and other stored-value cards. | [StoredValue](lib/adyen/services/storedValue.rb) |
|
23
23
|
| [Transfers API](https://docs.adyen.com/api-explorer/transfers/3/overview) | v3 | The Transfers API provides endpoints that can be used to get information about all your transactions, move funds within your balance platform or send funds from your balance platform to a transfer instrument. | [Transfers](lib/adyen/services/transfers.rb) |
|
24
|
+
| [Cloud-based Terminal API](https://docs.adyen.com/point-of-sale/design-your-integration/terminal-api/terminal-api-reference/) | - | Our point-of-sale integration. | [TerminalCloudAPI](lib/adyen/services/terminalCloudAPI.rb) |
|
24
25
|
|
25
26
|
For more information, refer to our [documentation](https://docs.adyen.com/) or the [API Explorer](https://docs.adyen.com/api-explorer/).
|
26
27
|
|
@@ -99,6 +100,121 @@ To run the tests use :
|
|
99
100
|
bundle install --with development
|
100
101
|
~~~~
|
101
102
|
|
103
|
+
## Using the Cloud Terminal API Integration
|
104
|
+
In order to submit In-Person requests with [Terminal API over Cloud](https://docs.adyen.com/point-of-sale/design-your-integration/choose-your-architecture/cloud/) you need to initialize the client in the same way as explained above for Ecommerce transactions:
|
105
|
+
``` ruby
|
106
|
+
# Step 1: Require the parts of the module you want to use
|
107
|
+
require 'adyen-ruby-api-library'
|
108
|
+
|
109
|
+
# Step 2: Initialize the client object
|
110
|
+
adyen = Adyen::Client.new(api_key: 'YOUR_API_KEY', env: :test)
|
111
|
+
|
112
|
+
# Step 3: Create the request
|
113
|
+
serviceID = "123456789"
|
114
|
+
saleID = "POS-SystemID12345"
|
115
|
+
POIID = "Your Device Name(eg V400m-123456789)"
|
116
|
+
|
117
|
+
# Use a unique transaction for every transaction you perform
|
118
|
+
transactionID = "TransactionID"
|
119
|
+
|
120
|
+
request =
|
121
|
+
{
|
122
|
+
"SaleToPOIRequest": {
|
123
|
+
"MessageHeader": {
|
124
|
+
"MessageClass": "Service",
|
125
|
+
"MessageCategory": "Payment",
|
126
|
+
"MessageType": "Request",
|
127
|
+
"ServiceID": serviceID,
|
128
|
+
"SaleID": saleID,
|
129
|
+
"POIID": POIID,
|
130
|
+
"ProtocolVersion": "3.0"
|
131
|
+
},
|
132
|
+
"PaymentRequest": {
|
133
|
+
"SaleData": {
|
134
|
+
"SaleTransactionID": {
|
135
|
+
"TransactionID": transactionID,
|
136
|
+
"TimeStamp": "2023-08-23T09:48:55"
|
137
|
+
},
|
138
|
+
"SaleToAcquirerData": "eyJhcHBsaWNhdGlvbkluZm8iOnsiYWR5ZW5MaWJyYXJ5Ijp7Im5hbWUiOiJhZ....",
|
139
|
+
"TokenRequestedType": "Transaction"
|
140
|
+
},
|
141
|
+
"PaymentTransaction": {
|
142
|
+
"AmountsReq": {
|
143
|
+
"Currency": "EUR",
|
144
|
+
"RequestedAmount": 10
|
145
|
+
}
|
146
|
+
}
|
147
|
+
}
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
# Step 4: Make the request
|
152
|
+
response = adyen.terminal_cloud_api.sync(request)
|
153
|
+
```
|
154
|
+
|
155
|
+
### Optional: perform an abort request
|
156
|
+
|
157
|
+
To perform an [abort request](https://docs.adyen.com/point-of-sale/basic-tapi-integration/cancel-a-transaction/) you can use the following example:
|
158
|
+
``` ruby
|
159
|
+
abortRequest =
|
160
|
+
{
|
161
|
+
"MessageHeader": {
|
162
|
+
"MessageClass": "Service",
|
163
|
+
"MessageCategory": "Abort",
|
164
|
+
"MessageType": "Request",
|
165
|
+
"ServiceID": serviceID,
|
166
|
+
"SaleID": saleID,
|
167
|
+
"POIID": POIID,
|
168
|
+
"ProtocolVersion": "3.0"
|
169
|
+
},
|
170
|
+
"AbortRequest": {
|
171
|
+
"AbortReason": "MerchantAbort",
|
172
|
+
"MessageReference": {
|
173
|
+
"MessageCategory": "Payment",
|
174
|
+
"SaleID": saleID,
|
175
|
+
# Service ID of the payment you're aborting
|
176
|
+
"ServiceID": serviceID,
|
177
|
+
"POIID": POIID
|
178
|
+
}
|
179
|
+
}
|
180
|
+
}
|
181
|
+
|
182
|
+
response = adyen.terminal_cloud_api.sync(abortRequest)
|
183
|
+
```
|
184
|
+
|
185
|
+
### Optional: perform a status request
|
186
|
+
|
187
|
+
To perform a [status request](https://docs.adyen.com/point-of-sale/basic-tapi-integration/verify-transaction-status/) you can use the following example:
|
188
|
+
``` ruby
|
189
|
+
statusRequest =
|
190
|
+
{
|
191
|
+
"MessageHeader": {
|
192
|
+
"MessageClass": "Service",
|
193
|
+
"MessageCategory": "TransactionStatus",
|
194
|
+
"MessageType": "Request",
|
195
|
+
"ServiceID": serviceID,
|
196
|
+
"SaleID": saleID,
|
197
|
+
"POIID": POIID,
|
198
|
+
"ProtocolVersion": "3.0"
|
199
|
+
},
|
200
|
+
"TransactionStatusRequest": {
|
201
|
+
"ReceiptReprintFlag": true,
|
202
|
+
"DocumentQualifier": [
|
203
|
+
"CashierReceipt",
|
204
|
+
"CustomerReceipt"
|
205
|
+
],
|
206
|
+
"MessageReference": {
|
207
|
+
"SaleID": saleID,
|
208
|
+
# serviceID of the transaction you want the status update for
|
209
|
+
"ServiceID": serviceID,
|
210
|
+
"MessageCategory": "Payment"
|
211
|
+
}
|
212
|
+
}
|
213
|
+
}
|
214
|
+
|
215
|
+
response = adyen.terminal_cloud_api.sync(statusRequest)
|
216
|
+
```
|
217
|
+
|
102
218
|
## Feedback
|
103
219
|
We value your input! Help us enhance our API Libraries and improve the integration experience by providing your feedback. Please take a moment to fill out [our feedback form](https://forms.gle/A4EERrR6CWgKWe5r9) to share your thoughts, suggestions or ideas.
|
104
220
|
|
data/lib/adyen/client.rb
CHANGED
@@ -75,6 +75,9 @@ module Adyen
|
|
75
75
|
when 'Management'
|
76
76
|
url = "https://management-#{@env}.adyen.com"
|
77
77
|
supports_live_url_prefix = false
|
78
|
+
when 'TerminalCloudAPI'
|
79
|
+
url = "https://terminal-api-#{@env}.adyen.com"
|
80
|
+
supports_live_url_prefix = false
|
78
81
|
else
|
79
82
|
raise ArgumentError, 'Invalid service specified'
|
80
83
|
end
|
@@ -98,6 +101,8 @@ module Adyen
|
|
98
101
|
def service_url(service, action, version)
|
99
102
|
if service == "Checkout" && @env == :live
|
100
103
|
return "#{service_url_base(service)}/checkout/v#{version}/#{action}"
|
104
|
+
elsif version == nil
|
105
|
+
return "#{service_url_base(service)}/#{action}"
|
101
106
|
else
|
102
107
|
return "#{service_url_base(service)}/v#{version}/#{action}"
|
103
108
|
end
|
@@ -221,6 +226,9 @@ module Adyen
|
|
221
226
|
# delete has no response.body (unless it throws an error)
|
222
227
|
if response.body.nil? || response.body === ''
|
223
228
|
AdyenResult.new('{}', response.headers, response.status)
|
229
|
+
# terminal API async call returns always 'ok'
|
230
|
+
elsif response.body === 'ok'
|
231
|
+
AdyenResult.new('{}', response.headers, response.status)
|
224
232
|
else
|
225
233
|
AdyenResult.new(response.body, response.headers, response.status)
|
226
234
|
end
|
@@ -286,6 +294,10 @@ module Adyen
|
|
286
294
|
def balance_control_service
|
287
295
|
@balance_control_service ||= Adyen::BalanceControlService.new(self)
|
288
296
|
end
|
297
|
+
|
298
|
+
def terminal_cloud_api
|
299
|
+
@terminal_cloud_api ||= Adyen::TerminalCloudAPI.new(self)
|
300
|
+
end
|
289
301
|
end
|
290
302
|
end
|
291
303
|
# rubocop:enable all
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative '../service'
|
2
|
+
module Adyen
|
3
|
+
class NetworkTokensApi < Service
|
4
|
+
attr_accessor :service, :version
|
5
|
+
|
6
|
+
def initialize(client, version = DEFAULT_VERSION)
|
7
|
+
super(client, version, 'BalancePlatform')
|
8
|
+
end
|
9
|
+
|
10
|
+
def get_network_token(network_token_id, headers: {})
|
11
|
+
endpoint = '/networkTokens/{networkTokenId}'.gsub(/{.+?}/, '%s')
|
12
|
+
endpoint = endpoint.gsub(%r{^/}, '')
|
13
|
+
endpoint = format(endpoint, network_token_id)
|
14
|
+
|
15
|
+
action = { method: 'get', url: endpoint }
|
16
|
+
@client.call_adyen_api(@service, action, {}, headers, @version)
|
17
|
+
end
|
18
|
+
|
19
|
+
def update_network_token(request, network_token_id, headers: {})
|
20
|
+
endpoint = '/networkTokens/{networkTokenId}'.gsub(/{.+?}/, '%s')
|
21
|
+
endpoint = endpoint.gsub(%r{^/}, '')
|
22
|
+
endpoint = format(endpoint, network_token_id)
|
23
|
+
|
24
|
+
action = { method: 'patch', url: endpoint }
|
25
|
+
@client.call_adyen_api(@service, action, request, headers, @version)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -16,6 +16,15 @@ module Adyen
|
|
16
16
|
@client.call_adyen_api(@service, action, {}, headers, @version)
|
17
17
|
end
|
18
18
|
|
19
|
+
def list_network_tokens(id, headers: {})
|
20
|
+
endpoint = '/paymentInstruments/{id}/networkTokens'.gsub(/{.+?}/, '%s')
|
21
|
+
endpoint = endpoint.gsub(%r{^/}, '')
|
22
|
+
endpoint = format(endpoint, id)
|
23
|
+
|
24
|
+
action = { method: 'get', url: endpoint }
|
25
|
+
@client.call_adyen_api(@service, action, {}, headers, @version)
|
26
|
+
end
|
27
|
+
|
19
28
|
def get_pan_of_payment_instrument(id, headers: {})
|
20
29
|
endpoint = '/paymentInstruments/{id}/reveal'.gsub(/{.+?}/, '%s')
|
21
30
|
endpoint = endpoint.gsub(%r{^/}, '')
|
@@ -3,6 +3,7 @@ require_relative 'balancePlatform/balance_accounts_api'
|
|
3
3
|
require_relative 'balancePlatform/bank_account_validation_api'
|
4
4
|
require_relative 'balancePlatform/grant_accounts_api'
|
5
5
|
require_relative 'balancePlatform/grant_offers_api'
|
6
|
+
require_relative 'balancePlatform/network_tokens_api'
|
6
7
|
require_relative 'balancePlatform/payment_instrument_groups_api'
|
7
8
|
require_relative 'balancePlatform/payment_instruments_api'
|
8
9
|
require_relative 'balancePlatform/platform_api'
|
@@ -39,6 +40,10 @@ module Adyen
|
|
39
40
|
@grant_offers_api ||= Adyen::GrantOffersApi.new(@client, @version)
|
40
41
|
end
|
41
42
|
|
43
|
+
def network_tokens_api
|
44
|
+
@network_tokens_api ||= Adyen::NetworkTokensApi.new(@client, @version)
|
45
|
+
end
|
46
|
+
|
42
47
|
def payment_instrument_groups_api
|
43
48
|
@payment_instrument_groups_api ||= Adyen::PaymentInstrumentGroupsApi.new(@client, @version)
|
44
49
|
end
|
@@ -7,10 +7,10 @@ module Adyen
|
|
7
7
|
super(client, version, 'Checkout')
|
8
8
|
end
|
9
9
|
|
10
|
-
def delete_token_for_stored_payment_details(
|
11
|
-
endpoint = '/storedPaymentMethods/{
|
10
|
+
def delete_token_for_stored_payment_details(stored_payment_method_id, headers: {}, query_params: {})
|
11
|
+
endpoint = '/storedPaymentMethods/{storedPaymentMethodId}'.gsub(/{.+?}/, '%s')
|
12
12
|
endpoint = endpoint.gsub(%r{^/}, '')
|
13
|
-
endpoint = format(endpoint,
|
13
|
+
endpoint = format(endpoint, stored_payment_method_id)
|
14
14
|
endpoint += create_query_string(query_params)
|
15
15
|
action = { method: 'delete', url: endpoint }
|
16
16
|
@client.call_adyen_api(@service, action, {}, headers, @version)
|
@@ -1,23 +1,128 @@
|
|
1
|
-
require_relative '
|
2
|
-
require_relative 'payment/modifications_api'
|
3
|
-
|
1
|
+
require_relative './service'
|
4
2
|
module Adyen
|
5
|
-
class Payment
|
3
|
+
class Payment < Service
|
6
4
|
attr_accessor :service, :version
|
7
5
|
|
8
6
|
DEFAULT_VERSION = 68
|
9
7
|
def initialize(client, version = DEFAULT_VERSION)
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
super(client, version, 'Payment')
|
9
|
+
end
|
10
|
+
|
11
|
+
def adjust_authorisation(request, headers: {})
|
12
|
+
endpoint = '/adjustAuthorisation'.gsub(/{.+?}/, '%s')
|
13
|
+
endpoint = endpoint.gsub(%r{^/}, '')
|
14
|
+
endpoint = format(endpoint)
|
15
|
+
|
16
|
+
action = { method: 'post', url: endpoint }
|
17
|
+
@client.call_adyen_api(@service, action, request, headers, @version)
|
18
|
+
end
|
19
|
+
|
20
|
+
def authorise(request, headers: {})
|
21
|
+
endpoint = '/authorise'.gsub(/{.+?}/, '%s')
|
22
|
+
endpoint = endpoint.gsub(%r{^/}, '')
|
23
|
+
endpoint = format(endpoint)
|
24
|
+
|
25
|
+
action = { method: 'post', url: endpoint }
|
26
|
+
@client.call_adyen_api(@service, action, request, headers, @version)
|
27
|
+
end
|
28
|
+
|
29
|
+
def authorise3d(request, headers: {})
|
30
|
+
endpoint = '/authorise3d'.gsub(/{.+?}/, '%s')
|
31
|
+
endpoint = endpoint.gsub(%r{^/}, '')
|
32
|
+
endpoint = format(endpoint)
|
33
|
+
|
34
|
+
action = { method: 'post', url: endpoint }
|
35
|
+
@client.call_adyen_api(@service, action, request, headers, @version)
|
36
|
+
end
|
37
|
+
|
38
|
+
def authorise3ds2(request, headers: {})
|
39
|
+
endpoint = '/authorise3ds2'.gsub(/{.+?}/, '%s')
|
40
|
+
endpoint = endpoint.gsub(%r{^/}, '')
|
41
|
+
endpoint = format(endpoint)
|
42
|
+
|
43
|
+
action = { method: 'post', url: endpoint }
|
44
|
+
@client.call_adyen_api(@service, action, request, headers, @version)
|
45
|
+
end
|
46
|
+
|
47
|
+
def cancel(request, headers: {})
|
48
|
+
endpoint = '/cancel'.gsub(/{.+?}/, '%s')
|
49
|
+
endpoint = endpoint.gsub(%r{^/}, '')
|
50
|
+
endpoint = format(endpoint)
|
51
|
+
|
52
|
+
action = { method: 'post', url: endpoint }
|
53
|
+
@client.call_adyen_api(@service, action, request, headers, @version)
|
54
|
+
end
|
55
|
+
|
56
|
+
def cancel_or_refund(request, headers: {})
|
57
|
+
endpoint = '/cancelOrRefund'.gsub(/{.+?}/, '%s')
|
58
|
+
endpoint = endpoint.gsub(%r{^/}, '')
|
59
|
+
endpoint = format(endpoint)
|
60
|
+
|
61
|
+
action = { method: 'post', url: endpoint }
|
62
|
+
@client.call_adyen_api(@service, action, request, headers, @version)
|
63
|
+
end
|
64
|
+
|
65
|
+
def capture(request, headers: {})
|
66
|
+
endpoint = '/capture'.gsub(/{.+?}/, '%s')
|
67
|
+
endpoint = endpoint.gsub(%r{^/}, '')
|
68
|
+
endpoint = format(endpoint)
|
69
|
+
|
70
|
+
action = { method: 'post', url: endpoint }
|
71
|
+
@client.call_adyen_api(@service, action, request, headers, @version)
|
72
|
+
end
|
73
|
+
|
74
|
+
def donate(request, headers: {})
|
75
|
+
endpoint = '/donate'.gsub(/{.+?}/, '%s')
|
76
|
+
endpoint = endpoint.gsub(%r{^/}, '')
|
77
|
+
endpoint = format(endpoint)
|
78
|
+
|
79
|
+
action = { method: 'post', url: endpoint }
|
80
|
+
@client.call_adyen_api(@service, action, request, headers, @version)
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_authentication_result(request, headers: {})
|
84
|
+
endpoint = '/getAuthenticationResult'.gsub(/{.+?}/, '%s')
|
85
|
+
endpoint = endpoint.gsub(%r{^/}, '')
|
86
|
+
endpoint = format(endpoint)
|
87
|
+
|
88
|
+
action = { method: 'post', url: endpoint }
|
89
|
+
@client.call_adyen_api(@service, action, request, headers, @version)
|
90
|
+
end
|
91
|
+
|
92
|
+
def refund(request, headers: {})
|
93
|
+
endpoint = '/refund'.gsub(/{.+?}/, '%s')
|
94
|
+
endpoint = endpoint.gsub(%r{^/}, '')
|
95
|
+
endpoint = format(endpoint)
|
96
|
+
|
97
|
+
action = { method: 'post', url: endpoint }
|
98
|
+
@client.call_adyen_api(@service, action, request, headers, @version)
|
99
|
+
end
|
100
|
+
|
101
|
+
def retrieve3ds2_result(request, headers: {})
|
102
|
+
endpoint = '/retrieve3ds2Result'.gsub(/{.+?}/, '%s')
|
103
|
+
endpoint = endpoint.gsub(%r{^/}, '')
|
104
|
+
endpoint = format(endpoint)
|
105
|
+
|
106
|
+
action = { method: 'post', url: endpoint }
|
107
|
+
@client.call_adyen_api(@service, action, request, headers, @version)
|
13
108
|
end
|
14
109
|
|
15
|
-
def
|
16
|
-
|
110
|
+
def technical_cancel(request, headers: {})
|
111
|
+
endpoint = '/technicalCancel'.gsub(/{.+?}/, '%s')
|
112
|
+
endpoint = endpoint.gsub(%r{^/}, '')
|
113
|
+
endpoint = format(endpoint)
|
114
|
+
|
115
|
+
action = { method: 'post', url: endpoint }
|
116
|
+
@client.call_adyen_api(@service, action, request, headers, @version)
|
17
117
|
end
|
18
118
|
|
19
|
-
def
|
20
|
-
|
119
|
+
def void_pending_refund(request, headers: {})
|
120
|
+
endpoint = '/voidPendingRefund'.gsub(/{.+?}/, '%s')
|
121
|
+
endpoint = endpoint.gsub(%r{^/}, '')
|
122
|
+
endpoint = format(endpoint)
|
123
|
+
|
124
|
+
action = { method: 'post', url: endpoint }
|
125
|
+
@client.call_adyen_api(@service, action, request, headers, @version)
|
21
126
|
end
|
22
127
|
|
23
128
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative './service'
|
2
|
+
module Adyen
|
3
|
+
class TerminalCloudAPI < Service
|
4
|
+
attr_accessor :service
|
5
|
+
|
6
|
+
def initialize(client)
|
7
|
+
super(client, nil ,'TerminalCloudAPI')
|
8
|
+
end
|
9
|
+
|
10
|
+
def connected_terminals(request, headers: {})
|
11
|
+
endpoint = '/connectedTerminals'.gsub(/{.+?}/, '%s')
|
12
|
+
endpoint = endpoint.gsub(%r{^/}, '')
|
13
|
+
endpoint = format(endpoint)
|
14
|
+
|
15
|
+
action = { method: 'post', url: endpoint }
|
16
|
+
@client.call_adyen_api(@service, action, request, headers, @version)
|
17
|
+
end
|
18
|
+
|
19
|
+
def sync(request, headers: {})
|
20
|
+
endpoint = '/sync'.gsub(/{.+?}/, '%s')
|
21
|
+
endpoint = endpoint.gsub(%r{^/}, '')
|
22
|
+
endpoint = format(endpoint)
|
23
|
+
|
24
|
+
action = { method: 'post', url: endpoint }
|
25
|
+
@client.call_adyen_api(@service, action, request, headers, @version)
|
26
|
+
end
|
27
|
+
|
28
|
+
def async(request, headers: {})
|
29
|
+
endpoint = '/async'.gsub(/{.+?}/, '%s')
|
30
|
+
endpoint = endpoint.gsub(%r{^/}, '')
|
31
|
+
endpoint = format(endpoint)
|
32
|
+
|
33
|
+
action = { method: 'post', url: endpoint }
|
34
|
+
@client.call_adyen_api(@service, action, request, headers, @version)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -8,6 +8,11 @@ module Adyen
|
|
8
8
|
amount.value amount.currency eventCode success
|
9
9
|
].freeze
|
10
10
|
|
11
|
+
# <b>DEPRECATED:</b> Please use valid_webhook_hmac?() instead.
|
12
|
+
def valid_notification_hmac?(notification_request_item, hmac_key)
|
13
|
+
valid_webhook_hmac?(notification_request_item, hmac_key)
|
14
|
+
end
|
15
|
+
|
11
16
|
def valid_webhook_hmac?(webhook_request_item, hmac_key)
|
12
17
|
expected_sign = calculate_webhook_hmac(webhook_request_item, hmac_key)
|
13
18
|
merchant_sign = fetch(webhook_request_item, 'additionalData.hmacSignature')
|
@@ -15,6 +20,12 @@ module Adyen
|
|
15
20
|
expected_sign == merchant_sign
|
16
21
|
end
|
17
22
|
|
23
|
+
# <b>DEPRECATED:</b> Please use calculate_webhook_hmac() instead.
|
24
|
+
def calculate_notification_hmac(notification_request_item, hmac_key)
|
25
|
+
calculate_webhook_hmac(notification_request_item, hmac_key)
|
26
|
+
end
|
27
|
+
|
28
|
+
|
18
29
|
def calculate_webhook_hmac(webhook_request_item, hmac_key)
|
19
30
|
data = data_to_sign(webhook_request_item)
|
20
31
|
|
data/lib/adyen/version.rb
CHANGED
data/spec/client_spec.rb
CHANGED
@@ -25,10 +25,10 @@ RSpec.describe Adyen do
|
|
25
25
|
end
|
26
26
|
|
27
27
|
it 'fails payments call without WS user and password' do
|
28
|
-
expect { @shared_values[:client].payment.
|
28
|
+
expect { @shared_values[:client].payment.authorise('{}') }
|
29
29
|
.to raise_error(Adyen::AuthenticationError)
|
30
30
|
@shared_values[:client].ws_user = @shared_values[:ws_user]
|
31
|
-
expect { @shared_values[:client].payment.
|
31
|
+
expect { @shared_values[:client].payment.authorise('{}') }
|
32
32
|
.to raise_error(Adyen::AuthenticationError)
|
33
33
|
end
|
34
34
|
|
@@ -236,4 +236,23 @@ RSpec.describe Adyen do
|
|
236
236
|
expect(client.service_url('PosTerminalManagement', 'assignTerminals', '1'))
|
237
237
|
.to eq('https://postfmapi-test.adyen.com/postfmapi/terminal/v1/assignTerminals')
|
238
238
|
end
|
239
|
+
|
240
|
+
it 'checks the creation of TerminalCloudAPI sync url' do
|
241
|
+
client = Adyen::Client.new(api_key: 'api_key', env: :test)
|
242
|
+
expect(client.service_url('TerminalCloudAPI', 'sync', nil))
|
243
|
+
.to eq('https://terminal-api-test.adyen.com/sync')
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'checks the creation of TerminalCloudAPI async url' do
|
247
|
+
client = Adyen::Client.new(api_key: 'api_key', env: :test)
|
248
|
+
expect(client.service_url('TerminalCloudAPI', 'async', nil))
|
249
|
+
.to eq('https://terminal-api-test.adyen.com/async')
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'checks the creation of TerminalCloudAPI connectedTerminals url' do
|
253
|
+
client = Adyen::Client.new(api_key: 'api_key', env: :test)
|
254
|
+
expect(client.service_url('TerminalCloudAPI', 'connectedTerminals', nil))
|
255
|
+
.to eq('https://terminal-api-test.adyen.com/connectedTerminals')
|
256
|
+
|
257
|
+
end
|
239
258
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
{
|
2
|
+
"SaleToPOIRequest": {
|
3
|
+
"MessageHeader": {
|
4
|
+
"MessageClass": "Service",
|
5
|
+
"MessageCategory": "Payment",
|
6
|
+
"MessageType": "Request",
|
7
|
+
"ServiceID": "1233094855",
|
8
|
+
"SaleID": "POSSystemID12345",
|
9
|
+
"POIID": "V400m-1234123412",
|
10
|
+
"ProtocolVersion": "3.0"
|
11
|
+
},
|
12
|
+
"PaymentRequest": {
|
13
|
+
"SaleData": {
|
14
|
+
"SaleTransactionID": {
|
15
|
+
"TransactionID": "123456764",
|
16
|
+
"TimeStamp": "2023-08-23T09:48:55"
|
17
|
+
},
|
18
|
+
"SaleToAcquirerData": "eyJhcHBsaWNhdGlvbkluZm8iOnsiYWR5ZW5MaWJyYXJ5Ijp7Im5hbWUiOiJhZ....",
|
19
|
+
"TokenRequestedType": "Transaction"
|
20
|
+
},
|
21
|
+
"PaymentTransaction": {
|
22
|
+
"AmountsReq": {
|
23
|
+
"Currency": "EUR",
|
24
|
+
"RequestedAmount": 10
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
{
|
2
|
+
"SaleToPOIResponse":{
|
3
|
+
"MessageHeader":{},
|
4
|
+
"PaymentResponse":{
|
5
|
+
"POIData":{
|
6
|
+
"POITransactionID":{
|
7
|
+
"TransactionID": "oLkO0012498220087000.KHQC5N7G84BLNK43"
|
8
|
+
}
|
9
|
+
},
|
10
|
+
"Response":{
|
11
|
+
"Result":"Success",
|
12
|
+
"AdditionalResponse":"...shopperEmail=shoppersemail%40address.com..."
|
13
|
+
}
|
14
|
+
},
|
15
|
+
"PaymentReceipt":{}
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
data/spec/payments_spec.rb
CHANGED
@@ -28,7 +28,7 @@ RSpec.describe Adyen::Payment, service: 'Payment' do
|
|
28
28
|
body: response_body
|
29
29
|
)
|
30
30
|
|
31
|
-
result = @shared_values[:client].payment.
|
31
|
+
result = @shared_values[:client].payment.adjust_authorisation(request_body)
|
32
32
|
response_hash = result.response
|
33
33
|
|
34
34
|
expect(result.status)
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
RSpec.describe Adyen::TerminalCloudAPI, service: 'TerminalCloudAPI' do
|
5
|
+
before(:all) do
|
6
|
+
@shared_values = {
|
7
|
+
client: create_client(:api_key),
|
8
|
+
service: 'TerminalCloudAPI'
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'makes a connectedTerminals POST call' do
|
13
|
+
request_body = JSON.parse(json_from_file('mocks/requests/TerminalCloudAPI/connected_terminals.json'))
|
14
|
+
|
15
|
+
response_body = json_from_file('mocks/responses/TerminalCloudAPI/connected_terminals.json')
|
16
|
+
|
17
|
+
url = @shared_values[:client].service_url(@shared_values[:service], 'connectedTerminals', nil)
|
18
|
+
WebMock.stub_request(:post, url)
|
19
|
+
.with(
|
20
|
+
headers: {
|
21
|
+
'x-api-key' => @shared_values[:client].api_key
|
22
|
+
}
|
23
|
+
)
|
24
|
+
.to_return(
|
25
|
+
body: response_body
|
26
|
+
)
|
27
|
+
|
28
|
+
result = @shared_values[:client].terminal_cloud_api.connected_terminals(request_body)
|
29
|
+
response_hash = result.response
|
30
|
+
|
31
|
+
expect(result.status)
|
32
|
+
.to eq(200)
|
33
|
+
expect(response_hash)
|
34
|
+
.to eq(JSON.parse(response_body))
|
35
|
+
expect(response_hash)
|
36
|
+
.to be_a Adyen::HashWithAccessors
|
37
|
+
expect(response_hash)
|
38
|
+
.to be_a_kind_of Hash
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'makes a sync payment POST call' do
|
42
|
+
request_body = JSON.parse(json_from_file('mocks/requests/TerminalCloudAPI/sync_payment.json'))
|
43
|
+
|
44
|
+
response_body = json_from_file('mocks/responses/TerminalCloudAPI/sync_payment.json')
|
45
|
+
|
46
|
+
url = @shared_values[:client].service_url(@shared_values[:service], 'sync', nil)
|
47
|
+
WebMock.stub_request(:post, url)
|
48
|
+
.with(
|
49
|
+
headers: {
|
50
|
+
'x-api-key' => @shared_values[:client].api_key
|
51
|
+
}
|
52
|
+
)
|
53
|
+
.to_return(
|
54
|
+
body: response_body
|
55
|
+
)
|
56
|
+
|
57
|
+
result = @shared_values[:client].terminal_cloud_api.sync(request_body)
|
58
|
+
response_hash = result.response
|
59
|
+
|
60
|
+
expect(result.status)
|
61
|
+
.to eq(200)
|
62
|
+
expect(response_hash)
|
63
|
+
.to eq(JSON.parse(response_body))
|
64
|
+
expect(response_hash)
|
65
|
+
.to be_a Adyen::HashWithAccessors
|
66
|
+
expect(response_hash)
|
67
|
+
.to be_a_kind_of Hash
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'makes an async payment POST call' do
|
71
|
+
request_body = JSON.parse(json_from_file('mocks/requests/TerminalCloudAPI/sync_payment.json'))
|
72
|
+
|
73
|
+
url = @shared_values[:client].service_url(@shared_values[:service], 'async', nil)
|
74
|
+
WebMock.stub_request(:post, url)
|
75
|
+
.with(
|
76
|
+
headers: {
|
77
|
+
'x-api-key' => @shared_values[:client].api_key
|
78
|
+
}
|
79
|
+
)
|
80
|
+
.to_return(
|
81
|
+
body: 'ok'
|
82
|
+
)
|
83
|
+
|
84
|
+
result = @shared_values[:client].terminal_cloud_api.async(request_body)
|
85
|
+
response_hash = result.response
|
86
|
+
|
87
|
+
expect(result.status)
|
88
|
+
.to eq(200)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
# rubocop:enable Metrics/BlockLength
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: adyen-ruby-api-library
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.0
|
4
|
+
version: 7.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adyen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-08-
|
11
|
+
date: 2023-08-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -126,6 +126,7 @@ files:
|
|
126
126
|
- lib/adyen/services/balancePlatform/bank_account_validation_api.rb
|
127
127
|
- lib/adyen/services/balancePlatform/grant_accounts_api.rb
|
128
128
|
- lib/adyen/services/balancePlatform/grant_offers_api.rb
|
129
|
+
- lib/adyen/services/balancePlatform/network_tokens_api.rb
|
129
130
|
- lib/adyen/services/balancePlatform/payment_instrument_groups_api.rb
|
130
131
|
- lib/adyen/services/balancePlatform/payment_instruments_api.rb
|
131
132
|
- lib/adyen/services/balancePlatform/platform_api.rb
|
@@ -180,8 +181,6 @@ files:
|
|
180
181
|
- lib/adyen/services/management/webhooks_merchant_level_api.rb
|
181
182
|
- lib/adyen/services/marketpay.rb
|
182
183
|
- lib/adyen/services/payment.rb
|
183
|
-
- lib/adyen/services/payment/general_api.rb
|
184
|
-
- lib/adyen/services/payment/modifications_api.rb
|
185
184
|
- lib/adyen/services/payout.rb
|
186
185
|
- lib/adyen/services/payout/initialization_api.rb
|
187
186
|
- lib/adyen/services/payout/instant_payouts_api.rb
|
@@ -190,6 +189,7 @@ files:
|
|
190
189
|
- lib/adyen/services/recurring.rb
|
191
190
|
- lib/adyen/services/service.rb
|
192
191
|
- lib/adyen/services/storedValue.rb
|
192
|
+
- lib/adyen/services/terminalCloudAPI.rb
|
193
193
|
- lib/adyen/services/transfers.rb
|
194
194
|
- lib/adyen/services/transfers/capital_api.rb
|
195
195
|
- lib/adyen/services/transfers/transactions_api.rb
|
@@ -300,6 +300,8 @@ files:
|
|
300
300
|
- spec/mocks/requests/Terminal/assign_terminals.json
|
301
301
|
- spec/mocks/requests/Terminal/find_terminal.json
|
302
302
|
- spec/mocks/requests/Terminal/get_terminals_under_account.json
|
303
|
+
- spec/mocks/requests/TerminalCloudAPI/connected_terminals.json
|
304
|
+
- spec/mocks/requests/TerminalCloudAPI/sync_payment.json
|
303
305
|
- spec/mocks/requests/Transfers/get_transactions.json
|
304
306
|
- spec/mocks/requests/Transfers/make_transfer.json
|
305
307
|
- spec/mocks/responses/Account/check_account_holder.json
|
@@ -396,6 +398,8 @@ files:
|
|
396
398
|
- spec/mocks/responses/Terminal/assign_terminals.json
|
397
399
|
- spec/mocks/responses/Terminal/find_terminal.json
|
398
400
|
- spec/mocks/responses/Terminal/get_terminals_under_account.json
|
401
|
+
- spec/mocks/responses/TerminalCloudAPI/connected_terminals.json
|
402
|
+
- spec/mocks/responses/TerminalCloudAPI/sync_payment.json
|
399
403
|
- spec/mocks/responses/Transfers/get_transactions.json
|
400
404
|
- spec/mocks/responses/Transfers/make_transfer.json
|
401
405
|
- spec/mocks/responses/Webhooks/backslash_webhook.json
|
@@ -410,6 +414,7 @@ files:
|
|
410
414
|
- spec/service_spec.rb
|
411
415
|
- spec/spec_helper.rb
|
412
416
|
- spec/stored_value_spec.rb
|
417
|
+
- spec/terminal_cloud_api_spec.rb
|
413
418
|
- spec/transfers_spec.rb
|
414
419
|
- spec/utils/hmac_validator_spec.rb
|
415
420
|
- templates/api-single.mustache
|
@@ -1,56 +0,0 @@
|
|
1
|
-
require_relative '../service'
|
2
|
-
module Adyen
|
3
|
-
class GeneralApi < Service
|
4
|
-
attr_accessor :service, :version
|
5
|
-
|
6
|
-
def initialize(client, version = DEFAULT_VERSION)
|
7
|
-
super(client, version, 'Payment')
|
8
|
-
end
|
9
|
-
|
10
|
-
def authorise(request, headers: {})
|
11
|
-
endpoint = '/authorise'.gsub(/{.+?}/, '%s')
|
12
|
-
endpoint = endpoint.gsub(%r{^/}, '')
|
13
|
-
endpoint = format(endpoint)
|
14
|
-
|
15
|
-
action = { method: 'post', url: endpoint }
|
16
|
-
@client.call_adyen_api(@service, action, request, headers, @version)
|
17
|
-
end
|
18
|
-
|
19
|
-
def authorise3d(request, headers: {})
|
20
|
-
endpoint = '/authorise3d'.gsub(/{.+?}/, '%s')
|
21
|
-
endpoint = endpoint.gsub(%r{^/}, '')
|
22
|
-
endpoint = format(endpoint)
|
23
|
-
|
24
|
-
action = { method: 'post', url: endpoint }
|
25
|
-
@client.call_adyen_api(@service, action, request, headers, @version)
|
26
|
-
end
|
27
|
-
|
28
|
-
def authorise3ds2(request, headers: {})
|
29
|
-
endpoint = '/authorise3ds2'.gsub(/{.+?}/, '%s')
|
30
|
-
endpoint = endpoint.gsub(%r{^/}, '')
|
31
|
-
endpoint = format(endpoint)
|
32
|
-
|
33
|
-
action = { method: 'post', url: endpoint }
|
34
|
-
@client.call_adyen_api(@service, action, request, headers, @version)
|
35
|
-
end
|
36
|
-
|
37
|
-
def get_authentication_result(request, headers: {})
|
38
|
-
endpoint = '/getAuthenticationResult'.gsub(/{.+?}/, '%s')
|
39
|
-
endpoint = endpoint.gsub(%r{^/}, '')
|
40
|
-
endpoint = format(endpoint)
|
41
|
-
|
42
|
-
action = { method: 'post', url: endpoint }
|
43
|
-
@client.call_adyen_api(@service, action, request, headers, @version)
|
44
|
-
end
|
45
|
-
|
46
|
-
def retrieve3ds2_result(request, headers: {})
|
47
|
-
endpoint = '/retrieve3ds2Result'.gsub(/{.+?}/, '%s')
|
48
|
-
endpoint = endpoint.gsub(%r{^/}, '')
|
49
|
-
endpoint = format(endpoint)
|
50
|
-
|
51
|
-
action = { method: 'post', url: endpoint }
|
52
|
-
@client.call_adyen_api(@service, action, request, headers, @version)
|
53
|
-
end
|
54
|
-
|
55
|
-
end
|
56
|
-
end
|
@@ -1,83 +0,0 @@
|
|
1
|
-
require_relative '../service'
|
2
|
-
module Adyen
|
3
|
-
class ModificationsApi < Service
|
4
|
-
attr_accessor :service, :version
|
5
|
-
|
6
|
-
def initialize(client, version = DEFAULT_VERSION)
|
7
|
-
super(client, version, 'Payment')
|
8
|
-
end
|
9
|
-
|
10
|
-
def adjust_authorisation(request, headers: {})
|
11
|
-
endpoint = '/adjustAuthorisation'.gsub(/{.+?}/, '%s')
|
12
|
-
endpoint = endpoint.gsub(%r{^/}, '')
|
13
|
-
endpoint = format(endpoint)
|
14
|
-
|
15
|
-
action = { method: 'post', url: endpoint }
|
16
|
-
@client.call_adyen_api(@service, action, request, headers, @version)
|
17
|
-
end
|
18
|
-
|
19
|
-
def cancel(request, headers: {})
|
20
|
-
endpoint = '/cancel'.gsub(/{.+?}/, '%s')
|
21
|
-
endpoint = endpoint.gsub(%r{^/}, '')
|
22
|
-
endpoint = format(endpoint)
|
23
|
-
|
24
|
-
action = { method: 'post', url: endpoint }
|
25
|
-
@client.call_adyen_api(@service, action, request, headers, @version)
|
26
|
-
end
|
27
|
-
|
28
|
-
def cancel_or_refund(request, headers: {})
|
29
|
-
endpoint = '/cancelOrRefund'.gsub(/{.+?}/, '%s')
|
30
|
-
endpoint = endpoint.gsub(%r{^/}, '')
|
31
|
-
endpoint = format(endpoint)
|
32
|
-
|
33
|
-
action = { method: 'post', url: endpoint }
|
34
|
-
@client.call_adyen_api(@service, action, request, headers, @version)
|
35
|
-
end
|
36
|
-
|
37
|
-
def capture(request, headers: {})
|
38
|
-
endpoint = '/capture'.gsub(/{.+?}/, '%s')
|
39
|
-
endpoint = endpoint.gsub(%r{^/}, '')
|
40
|
-
endpoint = format(endpoint)
|
41
|
-
|
42
|
-
action = { method: 'post', url: endpoint }
|
43
|
-
@client.call_adyen_api(@service, action, request, headers, @version)
|
44
|
-
end
|
45
|
-
|
46
|
-
def donate(request, headers: {})
|
47
|
-
endpoint = '/donate'.gsub(/{.+?}/, '%s')
|
48
|
-
endpoint = endpoint.gsub(%r{^/}, '')
|
49
|
-
endpoint = format(endpoint)
|
50
|
-
|
51
|
-
action = { method: 'post', url: endpoint }
|
52
|
-
@client.call_adyen_api(@service, action, request, headers, @version)
|
53
|
-
end
|
54
|
-
|
55
|
-
def refund(request, headers: {})
|
56
|
-
endpoint = '/refund'.gsub(/{.+?}/, '%s')
|
57
|
-
endpoint = endpoint.gsub(%r{^/}, '')
|
58
|
-
endpoint = format(endpoint)
|
59
|
-
|
60
|
-
action = { method: 'post', url: endpoint }
|
61
|
-
@client.call_adyen_api(@service, action, request, headers, @version)
|
62
|
-
end
|
63
|
-
|
64
|
-
def technical_cancel(request, headers: {})
|
65
|
-
endpoint = '/technicalCancel'.gsub(/{.+?}/, '%s')
|
66
|
-
endpoint = endpoint.gsub(%r{^/}, '')
|
67
|
-
endpoint = format(endpoint)
|
68
|
-
|
69
|
-
action = { method: 'post', url: endpoint }
|
70
|
-
@client.call_adyen_api(@service, action, request, headers, @version)
|
71
|
-
end
|
72
|
-
|
73
|
-
def void_pending_refund(request, headers: {})
|
74
|
-
endpoint = '/voidPendingRefund'.gsub(/{.+?}/, '%s')
|
75
|
-
endpoint = endpoint.gsub(%r{^/}, '')
|
76
|
-
endpoint = format(endpoint)
|
77
|
-
|
78
|
-
action = { method: 'post', url: endpoint }
|
79
|
-
@client.call_adyen_api(@service, action, request, headers, @version)
|
80
|
-
end
|
81
|
-
|
82
|
-
end
|
83
|
-
end
|