zaala 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 23d2b71b2a1a767732f1e981ec456ee03d30dfa9aae6da0b331d7f19aee61050
4
+ data.tar.gz: 060b17d5d44dda277f3c463358f94083dc03e8f55ae75715ea13322b37298208
5
+ SHA512:
6
+ metadata.gz: 91778343fc0ee1a12d34e217e72b6486330a4533d28fd0654a0a9ad900b12769731bcd88e453d49a48c17d7686201c148d1cb473abf46498ffb0d82186f1fa86
7
+ data.tar.gz: '085382d92b9b1e165d10e071fa1fe0fa5486c3024b8a8848b6074507ee5b775ed95e88437fda5b07e8dd34cc89a90bf58ff7aaf5f80e0b4de6c6b35cf1d02a4c'
@@ -0,0 +1,151 @@
1
+ module Zaala::API
2
+ require 'savon'
3
+
4
+ require_relative "error"
5
+ require_relative "types"
6
+
7
+ class Client
8
+ # * *Args* :
9
+ # - +wsdl+ -> URL that points to the WSDL file
10
+ # - +username+ -> Username (optional)
11
+ # - +password+ -> Password (optional)
12
+ def initialize(wsdl:, username: '', password: '', log: false, proxy: nil)
13
+ symbolize_keys = lambda { |key| key.to_sym }
14
+ params = {
15
+ wsdl: wsdl,
16
+ open_timeout: 30, # in seconds
17
+ read_timeout: 30, # in seconds
18
+ convert_response_tags_to: symbolize_keys,
19
+ log: log,
20
+ proxy: proxy
21
+ }
22
+
23
+ @kar = if username != '' && password != ''
24
+ Savon.client(params.merge({
25
+ basic_auth: [username, password],
26
+ }))
27
+ else
28
+ Savon.client(params)
29
+ end
30
+ end
31
+
32
+ # Authenticate API calls
33
+ #
34
+ # * *Args* :
35
+ # - +username+ -> Username
36
+ # - +password+ -> Password
37
+ def add_login_credentials(username, password)
38
+ @kar = Savon.client(wsdl: wsdl, basic_auth: [username, password])
39
+ end
40
+
41
+ # Authorizes a reservation of funds for a future purchase.
42
+ def pre_authorize(req)
43
+ ensure_operation_exists(:pre_authorize)
44
+ raise BadRequestError.new("pre_authorize operation requires an AuthorizationRequest parameter") unless req.is_a?(AuthorizationRequest)
45
+
46
+ @kar.call(:pre_authorize) do
47
+ message(authorizationRequest: req.to_message)
48
+ end
49
+ rescue Savon::SOAPFault => e
50
+ # IS0003
51
+ # TODO: Extract error code from response body
52
+ # e.g. (The content of at least one field is invalid and did not pass the validation)
53
+ raise BadRequestError.new(e)
54
+ end
55
+
56
+ # Authorizes a purchase transaction.
57
+ def authorize(req)
58
+ ensure_operation_exists(:authorize)
59
+ raise BadRequestError.new("authorize operation requires an AuthorizationRequest parameter") unless req.is_a?(AuthorizationRequest)
60
+
61
+ res = @kar.call(:authorize) do
62
+ message(authorizationRequest: req.to_message)
63
+ end
64
+ AuthorizationResponse.from_message(res.body[:authorizeResponse][:return])
65
+ rescue Savon::SOAPFault => e
66
+ # e.g. (The content of at least one field is invalid and did not pass the validation)
67
+ raise BadRequestError.new(e)
68
+ end
69
+
70
+ # Cancel a previous reservation of funds.
71
+ def cancel_authorization(req)
72
+ ensure_operation_exists(:cancel_authorization)
73
+ raise BadRequestError.new("cancel_authorization operation requires a CancellationRequest parameter") unless req.is_a?(CancellationRequest)
74
+
75
+ @kar.call(:cancel_authorization) do
76
+ message(cancellationRequest: req.to_message)
77
+ end
78
+ rescue Savon::SOAPFault => e
79
+ # e.g. (The content of at least one field is invalid and did not pass the validation)
80
+ raise BadRequestError.new(e)
81
+ end
82
+
83
+ # Perform a solvency check.
84
+ def check(req)
85
+ ensure_operation_exists(:check)
86
+ raise BadRequestError.new("check operation requires a CheckRequest parameter") unless req.is_a?(CheckRequest)
87
+
88
+ @kar.call(:check) do
89
+ message(checkRequest: req.to_message)
90
+ end
91
+ rescue Savon::SOAPFault => e
92
+ # e.g. (The content of at least one field is invalid and did not pass the validation)
93
+ raise BadRequestError.new(e)
94
+ end
95
+
96
+ # Returns available information about an authorization-ID.
97
+ def info(req)
98
+ ensure_operation_exists(:info)
99
+ raise BadRequestError.new("info operation requires an InfoRequest parameter") unless req.is_a?(InfoRequest)
100
+
101
+ @kar.call(:info) do
102
+ message(infoRequest: req.to_message)
103
+ end
104
+ rescue Savon::SOAPFault => e
105
+ # TODO: Extract error code from response body
106
+ # e.g. (The content of at least one field is invalid and did not pass the validation)
107
+ raise BadRequestError.new(e)
108
+ end
109
+
110
+ # Verifies the given MTAN.
111
+ def verify(req)
112
+ ensure_operation_exists(:verify)
113
+ raise BadRequestError.new("info operation requires a VerifyRequest parameter") unless req.is_a?(VerifyRequest)
114
+
115
+ @kar.call(:verify) do
116
+ message(verifyRequest: req.to_message)
117
+ end
118
+ rescue Savon::SOAPFault => e
119
+ # TODO: Extract error code from response body
120
+ # e.g. (The content of at least one field is invalid and did not pass the validation)
121
+ raise BadRequestError.new(e)
122
+ end
123
+
124
+ # Submits a purchase transaction with a previous authorization of funds.
125
+ def submit_authorization(req)
126
+ ensure_operation_exists(:submit_authorization)
127
+ raise BadRequestError.new("info operation requires a SubmissionRequest parameter") unless req.is_a?(SubmissionRequest)
128
+
129
+ res = @kar.call(:submit_authorization) do
130
+ message(submissionRequest: req.to_message)
131
+ end
132
+ SubmissionResponse.from_message(res.body[:submitAuthorizationResponse][:return])
133
+ rescue Savon::SOAPFault => e
134
+ # TODO: Extract error code from response body
135
+ # e.g. (The content of at least one field is invalid and did not pass the validation)
136
+ raise BadRequestError.new(e)
137
+ end
138
+
139
+ # Authorizes a credit transaction.
140
+ def credit_advice(req)
141
+ ensure_operation_exists(:credit_advice)
142
+ raise StandardError, 'unimplemented operation'
143
+ end
144
+
145
+ private
146
+
147
+ def ensure_operation_exists(op)
148
+ raise(UnsupportedServiceEndpoint, op) unless @kar.operations.include?(op)
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,19 @@
1
+ module Zaala::API
2
+ class MissingError < StandardError
3
+ end
4
+ class BadRequestError < StandardError
5
+ end
6
+ class PermissionError < StandardError
7
+ end
8
+ class UnauthenticatedError < StandardError
9
+ end
10
+ class ConflictError < StandardError
11
+ # TODO: Add resource
12
+ end
13
+
14
+ class UnsupportedServiceEndpoint < StandardError
15
+ def initialize(op)
16
+ super("unsupported \"#{op}\" operation. Is it the correct WSDL file? This client supports KarApiV50.")
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,833 @@
1
+ module Zaala::API
2
+ require 'dry-types'
3
+ require 'dry-struct'
4
+
5
+ module Types
6
+ include Dry.Types()
7
+ end
8
+
9
+ # The class ClientInfoEntry represents a key-value pair holding information of the service's client (e.g. IPv4 address).
10
+ class ClientInfo < Dry::Struct
11
+ # the IP v4 address of the end customer doing the ckeckout, if available
12
+ attribute :ipv4, Types::Strict::String.optional.default(nil)
13
+ # the IP v6 address of the end customer doing the ckeckout, if available
14
+ attribute :ipv6, Types::Strict::String.optional.default(nil)
15
+ # set to true if the integration supports the verification process
16
+ attribute :feature_mtan, Types::Strict::Bool.optional.default(nil)
17
+ # set to true if the integration supports the verification process
18
+ attribute :spec_version, Types::Strict::String.optional.default(nil)
19
+ # name of the implementor / integrator, e.g. Customweb, Datatrans, Manor, ...
20
+ attribute :impl_vendor, Types::Strict::String.optional.default(nil)
21
+ # version of the KaR API client implementation, e.g. "Magento/1.25.32"
22
+ attribute :impl_version, Types::Strict::String.optional.default(nil)
23
+ end
24
+
25
+ # The class RequestIdentifier contains data to determine the origin of a web service call.
26
+ class RequestIdentifier < Dry::Struct
27
+ # ID of the web shop. Min. value: 1
28
+ attribute :web_shop_id, Types::Strict::Integer
29
+ # Authorization type of the request. Field can't be null.
30
+ attribute :authorization_type, Types::Strict::String
31
+
32
+ # ID of a previous preAuthorization. Mandatory for the following transaction request types (IF0003):
33
+ # SubmissionRequest
34
+ # CreditRequest
35
+ # CancellationRequest
36
+ # InfoRequest
37
+ # VerifyRequest
38
+ attribute :authorization_id, Types::Strict::Integer.optional.default(nil)
39
+ # Unique identifier of the POS (Point Of Sale) within the web shop. Mandatory in case of a POS sale. (IF0004)
40
+ attribute :pos_id, Types::Strict::String.optional.default(nil)
41
+
42
+ def self.from_message(h)
43
+ RequestIdentifier.new({
44
+ web_shop_id: h[:webShopId],
45
+ authorization_type: h[:authorizationType],
46
+ authorization_id: h[:authorizationId],
47
+ pos_id: h[:posId],
48
+ })
49
+ end
50
+
51
+ def to_message
52
+ m = {
53
+ webShopId: web_shop_id,
54
+ authorizationType: authorization_type
55
+ }
56
+ m[:authorizationId] = authorization_id unless authorization_id.nil?
57
+ m[:posId] = pos_id unless pos_id.nil?
58
+ m
59
+ end
60
+ end
61
+
62
+ # The class PersonalData represents the detail information about the customer.
63
+ class PersonalData < Dry::Struct
64
+ # Type of the customer.
65
+ attribute :customer_type, Types::Strict::String
66
+ # Language of the customer.
67
+ attribute :language, Types::Strict::String
68
+
69
+ # The gender of the customer. Mandatory for country 'Private'. (IF0025)
70
+ attribute :gender, Types::Strict::String.optional.default(nil)
71
+ # Title of the customer.
72
+ attribute :title, Types::Strict::String.optional.default(nil)
73
+ # ID of the customer within the web shop.
74
+ attribute :customer_id, Types::Strict::String.optional.default(nil)
75
+ # First name of the customer. Mandatory for CustomerType 'Private'. (IF0020)
76
+ attribute :first_name, Types::Strict::String.optional.default(nil)
77
+ # Last name of the customer. Mandatory for CustomerType 'Private'. (IF0021)
78
+ attribute :name, Types::Strict::String.optional.default(nil)
79
+ # Customer's date of birth. Mandatory for CustomerType 'Private'.
80
+ # (IF0022) If so, it must contain a date between 12 and 120 years in the past. (IF0023)
81
+ attribute :date_of_birth, Types::Strict::Date.optional.default(nil)
82
+ # Fixnet phone number of the customer.
83
+ attribute :fixnet_phone_number, Types::Strict::String.optional.default(nil)
84
+ # Mobile phone number of the customer.
85
+ attribute :mobile_phone_number, Types::Strict::String.optional.default(nil)
86
+ # EMail address of the customer. Field must contain a valid email address.
87
+ attribute :email, Types::Strict::String.optional.default(nil)
88
+ # Company name. Mandatory for CustomerType 'Company'. (IF0018)
89
+ attribute :company_name, Types::Strict::String.optional.default(nil)
90
+ # Company account number.
91
+ attribute :company_number, Types::Strict::String.optional.default(nil)
92
+ # Company UID (https://www.uid.admin.ch).
93
+ attribute :company_uid, Types::Strict::String.optional.default(nil)
94
+ # Company legal form. Mandatory for CustomerType 'Company'. (IF0019)
95
+ attribute :company_legal_form, Types::Strict::String.optional.default(nil)
96
+ # Company legal form. Has to contain a value if member companyLegalForm equals 'other'.
97
+ # (IF0002) Mandatory for CustomerType 'Company'.
98
+ attribute :company_legal_form_other, Types::Strict::String.optional.default(nil)
99
+ # Company foundation date.
100
+ attribute :company_foundation_date, Types::Strict::Date.optional.default(nil)
101
+
102
+ def self.from_message(h)
103
+ PersonalData.new({
104
+ customer_type: h[:customerType],
105
+ language: h[:language],
106
+ gender: h[:gender],
107
+ title: h[:title],
108
+ customer_id: h[:customerID],
109
+ first_name: h[:firstName],
110
+ name: h[:name],
111
+ date_of_birth: h[:dateOfBirth],
112
+ fixnet_phone_number: h[:fixnetPhoneNumber],
113
+ mobile_phone_number: h[:mobilePhoneNumber],
114
+ email: h[:eMail],
115
+ company_name: h[:companyName],
116
+ company_number: h[:companyNumber],
117
+ company_uid: h[:companyUid],
118
+ company_legal_form: h[:companyLegalForm],
119
+ company_legal_form_other: h[:companyLegalFormOther],
120
+ company_foundation_date: h[:companyFoundationDate],
121
+ })
122
+ end
123
+
124
+ def to_message
125
+ m = {
126
+ customerType: customer_type,
127
+ language: language
128
+ }
129
+ m[:gender] = gender unless gender.nil?
130
+ m[:title] = title unless title.nil?
131
+ m[:customerID] = customer_id unless customer_id.nil?
132
+ m[:firstName] = first_name unless first_name.nil?
133
+ m[:name] = name unless name.nil?
134
+ m[:dateOfBirth] = date_of_birth unless date_of_birth.nil?
135
+ m[:fixnetPhoneNumber] = fixnet_phone_number unless fixnet_phone_number.nil?
136
+ m[:mobilePhoneNumber] = mobile_phone_number unless mobile_phone_number.nil?
137
+ m[:eMail] = email unless email.nil?
138
+ m[:companyName] = company_name unless company_name.nil?
139
+ m[:companyNumber] = company_number unless company_number.nil?
140
+ m[:companyUid] = company_uid unless company_uid.nil?
141
+ m[:companyLegalForm] = company_legal_form unless company_legal_form.nil?
142
+ m[:companyLegalFormOther] = company_legal_form_other unless company_legal_form_other.nil?
143
+ m[:companyFoundationDate] = company_foundation_date unless company_foundation_date.nil?
144
+ m
145
+ end
146
+ end
147
+
148
+ # The class InvoiceAddress represents the invoice address of a purchase.
149
+ class InvoiceAddress < Dry::Struct
150
+ # Country code according to [ISO3166-1_alpha-2] Field can't be blank.
151
+ attribute :country, Types::Strict::String
152
+ # City Field can't be blank.
153
+ attribute :city, Types::Strict::String
154
+ # ZipCode Field can't be blank.
155
+ attribute :zip_code, Types::Strict::String
156
+
157
+ # Additional address information (contact person or division).
158
+ attribute :address_add_on, Types::Strict::String.optional.default(nil)
159
+ # House number
160
+ attribute :house_number, Types::Strict::String.optional.default(nil)
161
+ # Street
162
+ attribute :street, Types::Strict::String.optional.default(nil)
163
+ # Additional street information.
164
+ attribute :street_extension, Types::Strict::String.optional.default(nil)
165
+
166
+ def self.from_message(h)
167
+ InvoiceAddress.new({
168
+ country: h[:country],
169
+ city: h[:city],
170
+ zip_code: h[:zipCode],
171
+ address_add_on: h[:addressAddOn],
172
+ house_number: h[:houseNumber],
173
+ street: h[:street],
174
+ street_extension: h[:streetExtension],
175
+ })
176
+ end
177
+
178
+ def to_message
179
+ m = {
180
+ country: country,
181
+ city: city,
182
+ zipCode: zip_code
183
+ }
184
+ m[:addressAddOn] = address_add_on unless address_add_on.nil?
185
+ m[:houseNumber] = house_number unless house_number.nil?
186
+ m[:street] = street unless street.nil?
187
+ m[:streetExtension] = street_extension unless street_extension.nil?
188
+ m
189
+ end
190
+ end
191
+
192
+ # The class DeliveryAddress represents the delivery address of a purchase.
193
+ class DeliveryAddress < Dry::Struct
194
+ # Country code according to [ISO3166-1_alpha-2] Field can't be blank.
195
+ attribute :country, Types::Strict::String
196
+ # City Field can't be blank.
197
+ attribute :city, Types::Strict::String
198
+ # ZipCode Field can't be blank.
199
+ attribute :zip_code, Types::Strict::String
200
+
201
+ # Additional address information (contact person or division).
202
+ attribute :address_add_on, Types::Strict::String.optional.default(nil)
203
+ # House number
204
+ attribute :house_number, Types::Strict::String.optional.default(nil)
205
+ # Street
206
+ attribute :street, Types::Strict::String.optional.default(nil)
207
+ # Additional street information.
208
+ attribute :street_extension, Types::Strict::String.optional.default(nil)
209
+ # Name of the company.
210
+ attribute :company_name, Types::Strict::String.optional.default(nil)
211
+ # Title of the goods receiving customer.
212
+ attribute :title, Types::Strict::String.optional.default(nil)
213
+ # First name of the goods receiving customer.
214
+ attribute :first_name, Types::Strict::String.optional.default(nil)
215
+ # Last name of the goods receiving customer.
216
+ attribute :name, Types::Strict::String.optional.default(nil)
217
+
218
+ def self.from_message(h)
219
+ DeliveryAddress.new({
220
+ country: h[:country],
221
+ city: h[:city],
222
+ zip_code: h[:zipCode],
223
+ address_add_on: h[:addressAddOn],
224
+ house_number: h[:houseNumber],
225
+ street: h[:street],
226
+ street_extension: h[:streetExtension],
227
+ company_name: h[:companyName],
228
+ title: h[:title],
229
+ first_name: h[:firstName],
230
+ name: h[:name],
231
+ })
232
+ end
233
+
234
+ def to_message
235
+ m = {
236
+ country: country,
237
+ city: city,
238
+ zipCode: zip_code
239
+ }
240
+ m[:addressAddOn] = address_add_on unless address_add_on.nil?
241
+ m[:houseNumber] = house_number unless house_number.nil?
242
+ m[:street] = street unless street.nil?
243
+ m[:streetExtension] = street_extension unless street_extension.nil?
244
+ m[:companyName] = company_name unless company_name.nil?
245
+ m[:title] = title unless title.nil?
246
+ m[:firstName] = first_name unless first_name.nil?
247
+ m[:name] = name unless name.nil?
248
+ m
249
+ end
250
+ end
251
+
252
+ class DetailRecord < Dry::Struct
253
+ # Quantity ordered. Decimal places: max. = 6
254
+ attribute :quantity, Types::Strict::Float
255
+ # Total amount of the product charged to the customer.
256
+ # This amount has to consider rebatePercentage, if given.
257
+ # Amount = AmountWithoutVat + VatAmount(IF0009)
258
+ # Amount = SingleArticleAmount * Quantity * (100% - RebatePercentage) (IF0026) Decimal places: max. = 2
259
+ attribute :amount, Types::Strict::Float
260
+ # Percentage value of the VAT. Allowed values: 0, 2.5, 3.7, 3.8, 7,7 and 8 (IF0012)
261
+ attribute :vat_percent, Types::Strict::Float
262
+ # Amount of the VAT. Decimal places: max. = 2
263
+ attribute :vat_amount, Types::Strict::Float
264
+ # Total amount of the product without VAT.
265
+ # AmountWithoutVat = SingleArticleAmountWithoutVat * Quantity * (100% - RebatePercentage) (IF0008)
266
+ # AmountWithoutVat ~= Amount / (100 + VatPercent) * 100
267
+ # VatAmount ~= Amount / (100 + VatPercent) * VatPercent(IF0011) Decimal places: max. = 2
268
+ attribute :amount_without_vat, Types::Strict::Float
269
+ # Product description. Field can't be blank.
270
+ attribute :product_text, Types::Strict::String
271
+
272
+ # Unique identifier of the product (e.g. Article number).
273
+ attribute :product_id, Types::Strict::String.optional.default(nil)
274
+ # Product category.
275
+ attribute :product_category, Types::Strict::String.optional.default(nil)
276
+ # Percentage value of the rebate given for this line item.
277
+ # This percentage is already reflected in amount.
278
+ # If given, and also singleArticleAmountWithoutVat is given, the following formula
279
+ # has to apply: AmountWithoutVat = SingleArticleAmountWithoutVat * Quantity * (100% - RebatePercentage) (IF0008)
280
+ # If given, and also singleArticleAmount is given, the following formula has to apply:
281
+ # Amount = SingleArticleAmount * Quantity * (100% - RebatePercentage) (IF0026) Range: min. = 0, max. = 100
282
+ attribute :rebate_percentage, Types::Strict::Float.optional.default(nil)
283
+ # Amount of a single product including VAT. Decimal places: max. = 6
284
+ attribute :single_article_amount, Types::Strict::Float.optional.default(nil)
285
+ # Amount of a single product without VAT. Decimal places: max. = 6
286
+ attribute :single_article_amount_without_vat, Types::Strict::Float.optional.default(nil)
287
+ # Identifies the type of the detail record line. Allows to distinguish between goods, fees, other payment means etc.
288
+ attribute :type, Types::Strict::String.optional.default(nil)
289
+ # Quantity related unit of the product (Pieces, Meters, Liters etc.).
290
+ attribute :quantity_unit, Types::Strict::String.optional.default(nil)
291
+
292
+ def self.from_message(h)
293
+ DetailRecord.new({
294
+ quantity: h[:quantity],
295
+ amount: h[:amount],
296
+ vat_percent: h[:vatPercent],
297
+ vat_amount: h[:vatAmount],
298
+ amount_without_vat: h[:amountWithoutVat],
299
+ product_text: h[:productText],
300
+ product_id: h[:productId],
301
+ product_category: h[:productCategory],
302
+ rebate_percentage: h[:rebatePercentage],
303
+ single_article_amount: h[:singleArticleAmount],
304
+ single_article_amount_without_vat: h[:singleArticleAmountWithoutVat],
305
+ type: h[:type],
306
+ quantity_unit: h[:quantityUnit],
307
+ })
308
+ end
309
+
310
+ def to_message
311
+ m = {
312
+ quantity: quantity,
313
+ amount: amount,
314
+ vatPercent: vat_percent,
315
+ vatAmount: vat_amount,
316
+ amountWithoutVat: amount_without_vat,
317
+ productText: product_text
318
+ }
319
+ m[:productId] = product_id unless product_id.nil?
320
+ m[:productCategory] = product_category unless product_category.nil?
321
+ m[:rebatePercentage] = rebate_percentage unless rebate_percentage.nil?
322
+ m[:singleArticleAmount] = single_article_amount unless single_article_amount.nil?
323
+ m[:singleArticleAmountWithoutVat] = single_article_amount_without_vat unless single_article_amount_without_vat.nil?
324
+ m[:type] = type unless type.nil?
325
+ m[:quantityUnit] = quantity_unit unless quantity_unit.nil?
326
+ m
327
+ end
328
+ end
329
+
330
+ # The Basket class contains all financial amounts and information about its products.
331
+ class Basket < Dry::Struct
332
+ # Goods total value minus other payment amounts.
333
+ # This amount is billed towards the end customer.
334
+ # If the basket has detailRecords, the sum of their amount fields has to be
335
+ # equal to the basket's amount + otherPaymentMeansAmount, and thus also equal
336
+ # to the basket's grossAmount. (IF0006) Decimal places: max. = 2
337
+ attribute :amount, Types::Strict::Float
338
+ # Currency of all amounts in the basket.
339
+ attribute :currency, Types::Strict::String.default('CHF'.freeze)
340
+ # Collection of the basket's records with detail information.
341
+ # At least one DetailRecord is mandatory for the following transaction request types (IF0024):
342
+ # * SubmissionRequest
343
+ # * CreditRequest
344
+ attribute :detail_records, Types::Strict::Array.of(DetailRecord)
345
+
346
+ # Total value of the purchase including otherPaymentMeansAmount.
347
+ # Field is optional, if otherPaymentMeansAmount is zero or left out.
348
+ # Consequently, if otherPaymentMeansAmount is set to any non-zero value,
349
+ # grossAmount is mandatory. grossAmount = amount + otherPaymentMeansAmount(IF0007) Decimal places: max. = 2
350
+ attribute :gross_amount, Types::Strict::Float.optional.default(nil)
351
+ # Already payed amount e.g. via gift card. If not set, assumed to be 0.
352
+ # If set to any other value than zero, then also grossAmount must be set correctly. Decimal places: max. = 2
353
+ attribute :other_payment_means_amount, Types::Strict::Float.optional.default(nil)
354
+ # Order number defined by the end customer.
355
+ # Can be useful e.g. for B2B customers in order to provide a reference number to
356
+ # theorder in the customer's finance department, for easier correlation.
357
+ # Length limited to 64 chars, rest will be truncated.
358
+ attribute :customer_order_number, Types::Strict::String.optional.default(nil)
359
+ # Order number. ID of the order on merchant side. Will be printed on the invoice t othe customer,
360
+ # and reported on the merchant statement. Length limited to 30 chars, rest will be truncated.
361
+ attribute :order_number, Types::Strict::String.optional.default(nil)
362
+
363
+ def self.from_message(h)
364
+ Basket.new({
365
+ amount: h[:amount],
366
+ currency: h[:amountCurrency],
367
+ detail_records: h[:detailRecords].map { |r| DetailRecord.from_message(r) },
368
+ gross_amount: h[:grossAmount],
369
+ other_payment_means_amount: h[:otherPaymentMeansAmount],
370
+ customer_order_number: h[:customerOrderNumber],
371
+ order_number: h[:orderNumber],
372
+ })
373
+ end
374
+
375
+ def to_message
376
+ m = {
377
+ amount: amount,
378
+ amountCurrency: currency,
379
+ detailRecords: detail_records.map(&:to_message)
380
+ }
381
+ m[:grossAmount] = gross_amount unless gross_amount.nil?
382
+ m[:otherPaymentMeansAmount] = other_payment_means_amount unless other_payment_means_amount.nil?
383
+ m[:customerOrderNumber] = customer_order_number unless customer_order_number.nil?
384
+ m[:orderNumber] = order_number unless order_number.nil?
385
+ m
386
+ end
387
+ end
388
+
389
+ class AdditionalData < Dry::Struct
390
+ # Describes requested method of payment. Field can't be null.
391
+ attribute :requested_payment_method, Types::Strict::String
392
+
393
+ # Describes the distribution channel of a purchase.
394
+ attribute :sales_channel, Types::Strict::String.optional.default(nil)
395
+ # Date of invoice (Submission date + X days).
396
+ attribute :invoice_date, Types::Strict::Integer.optional.default(nil)
397
+ # Free text field 1.
398
+ attribute :free_text1, Types::Strict::String.optional.default(nil)
399
+ # Free text field 2.
400
+ attribute :free_text2, Types::Strict::String.optional.default(nil)
401
+ # The preferred rate model, e.g. "3x3" or "4x12".
402
+ attribute :preferred_rate_model, Types::Strict::String.optional.default(nil)
403
+ # Allows to influence the behaviour of an authorization concerning risk taker shift.
404
+ attribute :risk_taker_shift, Types::Strict::String.optional.default(nil)
405
+ # Determines the delivery channel for the invoice. Replaces physicalInvoice flag.
406
+ # If invoiceChannel is set, physicalInvoice flag will be ignored, i.e. invoiceChannel takes precedence.
407
+ attribute :invoice_channel, Types::Strict::String.optional.default(nil)
408
+
409
+ def self.from_message(h)
410
+ AdditionalData.new({
411
+ requested_payment_method: h[:requestedPaymentMethod],
412
+ sales_channel: h[:salesChannel],
413
+ invoice_date: h[:invoiceDate],
414
+ free_text1: h[:freeText1],
415
+ free_text2: h[:freeText2],
416
+ preferred_rate_model: h[:preferredRateModel],
417
+ risk_taker_shift: h[:riskTakerShift],
418
+ invoice_channel: h[:invoiceChannel],
419
+ })
420
+ end
421
+
422
+ def to_message
423
+ m = {
424
+ requestedPaymentMethod: requested_payment_method,
425
+ }
426
+ m[:salesChannel] = sales_channel unless sales_channel.nil?
427
+ m[:invoiceDate] = invoice_date unless invoice_date.nil?
428
+ m[:freeText1] = free_text1 unless free_text1.nil?
429
+ m[:freeText2] = free_text2 unless free_text2.nil?
430
+ m[:preferredRateModel] = preferred_rate_model unless preferred_rate_model.nil?
431
+ m[:riskTakerShift] = risk_taker_shift unless risk_taker_shift.nil?
432
+ m[:invoiceChannel] = invoice_channel unless invoice_channel.nil?
433
+ m
434
+ end
435
+ end
436
+
437
+ # Contains token information for authorizations with payment method ONACCOUNT.
438
+ class Token < Dry::Struct
439
+ # ID of the token. Field can't be blank.
440
+ attribute :id, Types::Strict::String
441
+ # Secret of the token. Also know as CVV. Field can't be blank.
442
+ attribute :secret, Types::Strict::String
443
+
444
+ # Expiration date of the token, in format 'YYYYMM'.
445
+ attribute :expiry_date, Types::Strict::String.optional.default(nil)
446
+
447
+ def self.from_message(h)
448
+ Token.new({
449
+ id: h[:tokenId],
450
+ secret: h[:tokenSecret],
451
+ expiry_date: h[:tokenExpiryDate],
452
+ })
453
+ end
454
+
455
+ def to_message
456
+ m = {
457
+ tokenId: id,
458
+ tokenSecret: secret,
459
+ }
460
+ m[:tokenExpiryDate] = expiry_date unless expiry_date.nil?
461
+ m
462
+ end
463
+ end
464
+
465
+ class Attachment < Dry::Struct
466
+ # Name of the attachment.
467
+ attribute :name, Types::Strict::String
468
+ # The mime type of the attachment.
469
+ attribute :mime_type, Types::Strict::String
470
+ # The content of the attachment (base64 encoded).
471
+ attribute :value, Types::Strict::String
472
+
473
+ def from_file
474
+ end
475
+
476
+ def self.from_message(h)
477
+ Attachment.new({
478
+ name: h[:name],
479
+ mime_type: h[:mimeType],
480
+ value: h[:value],
481
+ })
482
+ end
483
+
484
+ def to_message
485
+ {
486
+ name: name,
487
+ mimeType: mime_type,
488
+ value: value
489
+ }
490
+ end
491
+ end
492
+
493
+ class ResponseIdentifier < Dry::Struct
494
+ # Unique transaction id provided by the KaR system.
495
+ # With this id a specific request-response pair can be identified.
496
+ # This id is a valuable information for the KaR support team in case of a technical problem.
497
+ # Field can't be blank.
498
+ attribute :guardean_order_id, Types::Coercible::String
499
+ # Authorization id. Max. length of value is 30
500
+ attribute :authorization_id, Types::Coercible::Integer.optional.default(nil)
501
+
502
+ def self.from_message(h)
503
+ ResponseIdentifier.new({
504
+ guardean_order_id: h[:guardeanOrderId],
505
+ authorization_id: h[:authorizationId],
506
+ })
507
+ end
508
+ end
509
+
510
+ class Rate < Dry::Struct
511
+ # Amount of an installment.
512
+ attribute :amount, Types::Coercible::String
513
+ # Due date of the rate.
514
+ attribute :date, Types::Coercible::String
515
+ # Amount of interest.
516
+ attribute :interest_amount, Types::Coercible::String
517
+
518
+ def self.from_message(h)
519
+ Rate.new({
520
+ amount: h[:rateAmount],
521
+ date: h[:rateDate],
522
+ interest_amount: h[:interestAmount],
523
+ })
524
+ end
525
+ end
526
+
527
+ class InstallmentPlan < Dry::Struct
528
+ # Total amount of the installments including fees.
529
+ attribute :total_rate_amount, Types::Coercible::String
530
+ # Interest per annum.
531
+ attribute :interest_rate, Types::Coercible::String
532
+ # Date of the invoice.
533
+ attribute :billing_date, Types::Coercible::String
534
+ # Rate plan model, e.g. "3x3" or "4x12".
535
+ attribute :rate_model, Types::Coercible::String.optional.default(nil)
536
+ # Collection of the rates for the installments.
537
+ attribute :rates, Types::Coercible::Array.of(Rate).optional.default([].freeze)
538
+
539
+ def self.from_message(h)
540
+ InstallmentPlan.new({
541
+ total_rate_amount: h[:totalRateAmount],
542
+ interest_rate: h[:interestRate],
543
+ billing_date: h[:billingDate],
544
+ rate_model: h[:rateModel],
545
+ rates: h[:rates] ? h[:rates].map { |r| Rate.from_message(r) } : [],
546
+ })
547
+ end
548
+ end
549
+
550
+ class Info < Dry::Struct
551
+ # Current status of the transaction.
552
+ attribute :status, Types::Coercible::String.optional.default(nil)
553
+ # Current status of the transaction.
554
+ attribute :requested, Types::Coercible::String.optional.default(nil)
555
+ # Current status of the transaction.
556
+ attribute :valid_until, Types::Coercible::String.optional.default(nil)
557
+ # Current status of the transaction.
558
+ attribute :is_suitable_for_delivery, Types::Coercible::String.optional.default(nil)
559
+ # Current status of the transaction.
560
+ attribute :invoice_status, Types::Coercible::String.optional.default(nil)
561
+ # Current status of the transaction.
562
+ attribute :payment_status, Types::Coercible::String.optional.default(nil)
563
+ # Current status of the transaction.
564
+ attribute :dunning_level, Types::Coercible::String.optional.default(nil)
565
+ # Current status of the transaction.
566
+ attribute :current_balance, Types::Coercible::String.optional.default(nil)
567
+ # Current status of the transaction.
568
+ attribute :attachments, Types::Coercible::Array.of(Attachment).optional.default([].freeze)
569
+
570
+ # TODO: Enum?
571
+
572
+ def self.from_message(h)
573
+ Info.new({
574
+ status: h[:status],
575
+ requested: h[:requested],
576
+ valid_until: h[:validUntil],
577
+ is_suitable_for_delivery: h[:isSuitableForDelivery],
578
+ invoice_status: h[:invoiceStatus],
579
+ payment_status: h[:paymentStatus],
580
+ dunning_level: h[:dunningLevel],
581
+ current_balance: h[:currentBalance],
582
+ attachments: h[:attachments],
583
+ })
584
+ end
585
+ end
586
+
587
+ class Decision < Dry::Struct
588
+ # Indicates if the customer is allowed to pay its purchase by invoice.
589
+ attribute :invoice, Types::Coercible::String.optional.default(nil)
590
+ # Indicates if the customer is allowed to pay its purchase in installments.
591
+ attribute :installment, Types::Coercible::String.optional.default(nil)
592
+ # Indicates if the customer is allowed to pay its purchase by revolving account.
593
+ attribute :on_account, Types::Coercible::String.optional.default(nil)
594
+ # Indicates, who will take the financial risk of this transaction (see RiskTaker)
595
+ attribute :risk_taker, Types::Coercible::String.optional.default(nil)
596
+ # Only present in response of check operation. Indicates the result of the check.
597
+ attribute :traffic_light, Types::Coercible::String.optional.default(nil)
598
+
599
+ # TODO: Enum?
600
+
601
+ def self.from_message(h)
602
+ Decision.new({
603
+ invoice: h[:decisionInvoice],
604
+ installment: h[:decisionInstallment],
605
+ on_account: h[:decisionOnAccount],
606
+ risk_taker: h[:riskTaker],
607
+ traffic_light: h[:trafficLight],
608
+ })
609
+ end
610
+ end
611
+
612
+ # The AuthorizationRequest class represents the request parameter of the PreAuthorize and the Authorize service call.
613
+ # It contains all information needed to make a preauthorization/authorization.
614
+ class AuthorizationRequest < Dry::Struct
615
+ # Environment parameters and information to identify the caller.
616
+ attribute :identifier, Zaala::API::RequestIdentifier
617
+ # Personal data of the customer.
618
+ attribute :personal_data, Zaala::API::PersonalData
619
+ # Invoice address of the purchase.
620
+ attribute :invoice_address, Zaala::API::InvoiceAddress
621
+ # Additional purchase related information.
622
+ attribute :additional_data, Zaala::API::AdditionalData
623
+
624
+ # Key-value pairs holding information of the service's client (e.g. IPv4 address).
625
+ # attribute :client_info, Zaala::API::ClientInfo
626
+ # Requested amount of the preauthorization/authorization.
627
+ # It corresponds to the total amount, which has to be authorized.
628
+ # Mandatory if no Basket is provided in the request.
629
+ # (IF0001) If a Basket is provided in the request, this value is ignored.
630
+ attribute :requested_amount, Types::Strict::Float.optional.default(nil)
631
+ # Delivery address of the purchase.
632
+ attribute :delivery_address, Zaala::API::DeliveryAddress.optional.default(nil)
633
+ # Basket of the purchase. Mandatory if the requestdAmount is not provided in the request. (IF0001)
634
+ attribute :basket, Zaala::API::Basket.optional.default(nil)
635
+ # Token for authorizations with payment method ONACCOUNT.
636
+ # Mandatory if the the requested PaymentMethod equals ONACCOUNT. (IF0033)
637
+ attribute :token, Zaala::API::Token.optional.default(nil)
638
+ # Allows to provide additional information for the KaR process.
639
+ attribute :attachments, Types::Strict::Array.of(Zaala::API::Attachment).optional.default([].freeze)
640
+
641
+ def to_message
642
+ m = {
643
+ identifier: identifier.to_message,
644
+ personalData: personal_data.to_message,
645
+ invoiceAddress: invoice_address.to_message,
646
+ additionalData: additional_data.to_message,
647
+ }
648
+ m[:requestedAmount] = requested_amount unless requested_amount.nil?
649
+ m[:deliveryAddress] = delivery_address unless delivery_address.nil?
650
+ m[:basket] = basket unless basket.nil?
651
+ m[:token] = token unless token.nil?
652
+ m[:attachments] = attachments.map(&:to_message) unless attachments.nil?
653
+ m
654
+ end
655
+ end
656
+
657
+ class AuthorizationResponse < Dry::Struct
658
+ # Contains information to identify the authorization in a future request.
659
+ attribute :identifier, Zaala::API::ResponseIdentifier
660
+ # Contains additional information concerning the authorization.
661
+ attribute :decision, Zaala::API::Decision
662
+
663
+ # Contains the details of the payment by installments.
664
+ attribute :installment_plan, Zaala::API::InstallmentPlan.optional.default(nil)
665
+ # Contains additional information concerning the authorization.
666
+ attribute :info, Zaala::API::Info.optional.default(nil)
667
+
668
+ def self.from_message(h)
669
+ AuthorizationResponse.new({
670
+ identifier: Zaala::API::ResponseIdentifier.from_message(h[:identifier]),
671
+ decision: Zaala::API::Decision.from_message(h[:decision]),
672
+ installment_plan: h[:installmentPlan] ? Zaala::API::InstallmentPlan.from_message(h[:installmentPlan]) : nil,
673
+ info: h[:info] ? Zaala::API::Info.from_message(h[:info]) : nil,
674
+ })
675
+ end
676
+ end
677
+
678
+ # The SubmissionRequest class represents the request parameter of the SubmitAuthorization service call.
679
+ # It contains all information needed to submit a previous authorization and triggering the issuing of the invoice.
680
+ class SubmissionRequest < Dry::Struct
681
+ # Key-value pairs holding information of the service's client (e.g. IPv4 address).
682
+ # attribute :client_info, []ClientInfo
683
+
684
+ # Environment parameters and information to identify the caller.
685
+ attribute :identifier, Zaala::API::RequestIdentifier
686
+ # Basket of the purchase. Mandatory if the requestdAmount is not provided in the request. (IF0001)
687
+ attribute :basket, Zaala::API::Basket
688
+ # Additional purchase related information.
689
+ attribute :additional_data, Zaala::API::AdditionalData
690
+ # Allows to provide additional information for the KaR process.
691
+ attribute :attachments, Types::Strict::Array.of(Zaala::API::Attachment).optional.default([].freeze)
692
+
693
+ def to_message
694
+ {
695
+ identifier: identifier.to_message,
696
+ basket: basket.to_message,
697
+ additionalData: additional_data.to_message,
698
+ attachments: attachments.map(&:to_message),
699
+ }
700
+ end
701
+ end
702
+
703
+ class SubmissionResponse < Dry::Struct
704
+ # Contains information to identify the authorization in a future request.
705
+ attribute :identifier, Zaala::API::ResponseIdentifier
706
+ # Contains additional information concerning the authorization.
707
+ attribute :decision, Zaala::API::Decision
708
+
709
+ # Contains the details of the payment by installments.
710
+ attribute :installment_plan, Zaala::API::InstallmentPlan.optional.default(nil)
711
+ # Contains additional information concerning the authorization.
712
+ attribute :info, Zaala::API::Info.optional.default(nil)
713
+
714
+ def self.from_message(h)
715
+ SubmissionResponse.new({
716
+ identifier: Zaala::API::ResponseIdentifier.from_message(h[:identifier]),
717
+ decision: Zaala::API::Decision.from_message(h[:decision]),
718
+ installment_plan: h[:installmentPlan] ? Zaala::API::InstallmentPlan.from_message(h[:installmentPlan]) : nil,
719
+ info: h[:info] ? Zaala::API::Info.from_message(h[:info]) : nil,
720
+ })
721
+ end
722
+ end
723
+
724
+ # The CancellationRequest class represents the request parameter of the CancelAuthorization service call.
725
+ # It contains all information needed to cancel an previous authorization.
726
+ class CancellationRequest < Dry::Struct
727
+ # Key-value pairs holding information of the service's client (e.g. IPv4 address).
728
+ # attribute :client_info, []ClientInfo
729
+ # Environment parameters and information to identify the caller.
730
+ attribute :identifier, Zaala::API::RequestIdentifier
731
+
732
+ def to_message
733
+ {
734
+ identifier: identifier.to_message,
735
+ }
736
+ end
737
+ end
738
+
739
+ class CancellationResponse < Dry::Struct
740
+ # Contains information to identify the authorization in a future request.
741
+ attribute :identifier, Zaala::API::ResponseIdentifier
742
+
743
+ # Contains additional information concerning the authorization.
744
+ attribute :info, Zaala::API::Info.optional.default(nil)
745
+
746
+ def self.from_message(h)
747
+ CancellationResponse.new({
748
+ identifier: Zaala::API::ResponseIdentifier.from_message(h[:identifier]),
749
+ info: h[:info] ? Zaala::API::Info.from_message(h[:info]) : nil,
750
+ })
751
+ end
752
+ end
753
+
754
+ # The InfoRequest class represents the request parameter of the Info service call.
755
+ # It contains all information needed to retrieve all available information about an authorization-id.
756
+ class InfoRequest < Dry::Struct
757
+ # Key-value pairs holding information of the service's client (e.g. IPv4 address).
758
+ # attribute :client_info, []ClientInfo
759
+ # Environment parameters and information to identify the caller.
760
+ attribute :identifier, Zaala::API::RequestIdentifier
761
+
762
+ def to_message
763
+ {
764
+ identifier: identifier.to_message,
765
+ }
766
+ end
767
+ end
768
+
769
+ # The class InfoResponse represents the response of the Info service call.
770
+ class InfoResponse < Dry::Struct
771
+ # Contains information to identify the authorization in a future request.
772
+ attribute :identifier, Zaala::API::ResponseIdentifier
773
+ # Contains additional information concerning the authorization.
774
+ attribute :decision, Zaala::API::Decision
775
+
776
+ # Contains additional information concerning the authorization.
777
+ attribute :info, Zaala::API::Info.optional.default(nil)
778
+ # Contains the details of the payment by installments.
779
+ attribute :installment_plan, Zaala::API::InstallmentPlan.optional.default(nil)
780
+
781
+ def self.from_message(h)
782
+ InfoResponse.new({
783
+ identifier: Zaala::API::ResponseIdentifier.from_message(h[:identifier]),
784
+ decision: Zaala::API::Decision.from_message(h[:decision]),
785
+ installment_plan: h[:installmentPlan] ? Zaala::API::InstallmentPlan.from_message(h[:installmentPlan]) : nil,
786
+ info: h[:info] ? Zaala::API::Info.from_message(h[:info]) : nil,
787
+ })
788
+ end
789
+ end
790
+
791
+ # The VerifyRequest class represents the request parameter of the Verify service call.
792
+ # It contains all information needed to verify an authorization with an MTAN.
793
+ class VerifyRequest < Dry::Struct
794
+ # Key-value pairs holding information of the service's client (e.g. IPv4 address).
795
+ # attribute :client_info, []ClientInfo
796
+ # Environment parameters and information to identify the caller.
797
+ attribute :identifier, Zaala::API::RequestIdentifier
798
+ # Additional purchase related information.
799
+ attribute :additional_data, Zaala::API::AdditionalData
800
+ # The verificationCode value to check against.
801
+ attribute :verification_code, Types::Strict::String
802
+
803
+ def to_message
804
+ {
805
+ identifier: identifier.to_message,
806
+ additionalData: additional_data.to_message,
807
+ verificationCode: verification_code,
808
+ }
809
+ end
810
+ end
811
+
812
+ # The class VerifyResponse represents the response of the Verify service call.
813
+ class VerifyResponse < Dry::Struct
814
+ # Contains information to identify the authorization in a future request.
815
+ attribute :identifier, Zaala::API::ResponseIdentifier
816
+ # Contains additional information concerning the authorization.
817
+ attribute :decision, Zaala::API::Decision
818
+
819
+ # Contains additional information concerning the authorization.
820
+ attribute :info, Zaala::API::Info.optional.default(nil)
821
+ # Contains the details of the payment by installments.
822
+ attribute :installment_plan, Zaala::API::InstallmentPlan.optional.default(nil)
823
+
824
+ def self.from_message(h)
825
+ VerifyResponse.new({
826
+ identifier: Zaala::API::ResponseIdentifier.from_message(h[:identifier]),
827
+ decision: Zaala::API::Decision.from_message(h[:decision]),
828
+ installment_plan: h[:installmentPlan] ? Zaala::API::InstallmentPlan.from_message(h[:installmentPlan]) : nil,
829
+ info: h[:info] ? Zaala::API::Info.from_message(h[:info]) : nil,
830
+ })
831
+ end
832
+ end
833
+ end
data/lib/zaala/api.rb ADDED
@@ -0,0 +1,5 @@
1
+ module Zaala::API
2
+ require 'zaala/api/error'
3
+ require 'zaala/api/types'
4
+ require 'zaala/api/client'
5
+ end
data/lib/zaala.rb ADDED
@@ -0,0 +1,3 @@
1
+ module Zaala
2
+ require 'zaala/api'
3
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zaala
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Denteo AG
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-10-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dry-types
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.5.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 1.5.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: dry-struct
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 1.4.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 1.4.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: savon
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 2.12.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 2.12.0
55
+ description: Ruby client interacting with Zaala
56
+ email: simon@denteo.ch
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - lib/zaala.rb
62
+ - lib/zaala/api.rb
63
+ - lib/zaala/api/client.rb
64
+ - lib/zaala/api/error.rb
65
+ - lib/zaala/api/types.rb
66
+ homepage: https://rubygems.org/gems/zaala
67
+ licenses:
68
+ - MIT
69
+ metadata: {}
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubygems_version: 3.1.2
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: Ruby client interacting with Zaala
89
+ test_files: []