barion 0.2.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.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +158 -0
  4. data/Rakefile +34 -0
  5. data/app/assets/config/barion_manifest.js +1 -0
  6. data/app/assets/stylesheets/barion/application.css +15 -0
  7. data/app/assets/stylesheets/barion/main.css +4 -0
  8. data/app/controllers/barion/application_controller.rb +7 -0
  9. data/app/controllers/barion/main_controller.rb +28 -0
  10. data/app/helpers/barion/application_helper.rb +7 -0
  11. data/app/helpers/barion/main_helper.rb +7 -0
  12. data/app/models/barion/address.rb +60 -0
  13. data/app/models/barion/application_record.rb +10 -0
  14. data/app/models/barion/gift_card_purchase.rb +66 -0
  15. data/app/models/barion/item.rb +88 -0
  16. data/app/models/barion/payer_account.rb +119 -0
  17. data/app/models/barion/payment.rb +348 -0
  18. data/app/models/barion/payment_transaction.rb +134 -0
  19. data/app/models/barion/purchase.rb +107 -0
  20. data/app/models/concerns/barion/currencies.rb +11 -0
  21. data/app/models/concerns/barion/data_formats.rb +12 -0
  22. data/app/models/concerns/barion/json_serializer.rb +106 -0
  23. data/app/views/barion/main/land.html.erb +7 -0
  24. data/config/initializers/barion.rb +9 -0
  25. data/config/routes.rb +10 -0
  26. data/db/migrate/20201222235354_create_barion_payments.rb +59 -0
  27. data/db/migrate/20201223001557_create_barion_addresses.rb +21 -0
  28. data/db/migrate/20201223002427_create_barion_payment_transactions.rb +28 -0
  29. data/db/migrate/20201223003219_create_barion_purchases.rb +24 -0
  30. data/db/migrate/20210111051233_create_barion_payer_accounts.rb +28 -0
  31. data/db/migrate/20210128220347_create_barion_items.rb +21 -0
  32. data/db/migrate/20210201120609_create_barion_gift_card_purchases.rb +14 -0
  33. data/lib/barion.rb +100 -0
  34. data/lib/barion/engine.rb +14 -0
  35. data/lib/barion/version.rb +5 -0
  36. data/lib/tasks/auto_annotate_models.rake +59 -0
  37. data/lib/tasks/barion_tasks.rake +4 -0
  38. metadata +207 -0
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ # == Schema Information
4
+ #
5
+ # Table name: barion_items
6
+ #
7
+ # id :integer not null, primary key
8
+ # description :string(500) not null
9
+ # image_url :string
10
+ # item_total :decimal(, ) not null
11
+ # name :string(250) not null
12
+ # quantity :decimal(, ) not null
13
+ # sku :string(100)
14
+ # unit :string(50) not null
15
+ # unit_price :decimal(, ) not null
16
+ # payment_transaction_id :integer
17
+ #
18
+ # Indexes
19
+ #
20
+ # index_barion_items_on_payment_transaction_id (payment_transaction_id)
21
+ #
22
+ # Foreign Keys
23
+ #
24
+ # payment_transaction_id (payment_transaction_id => barion_payment_transactions.id)
25
+ #
26
+ module Barion
27
+ # Represents an item in a transaction for Barion engine
28
+ class Item < ApplicationRecord
29
+ include ::Barion::JsonSerializer
30
+
31
+ belongs_to :payment_transaction,
32
+ inverse_of: :items
33
+
34
+ attribute :name, :string
35
+ attribute :description, :string
36
+ attribute :unit_price, :decimal, default: 0.0
37
+ attribute :quantity, :decimal, default: 0.0
38
+ attribute :item_total, :decimal, default: 0.0
39
+ attribute :unit, :string
40
+
41
+ validates :name, presence: true, length: { maximum: 256 }
42
+ validates :description, presence: true, length: { maximum: 500 }
43
+ validates :quantity, presence: true
44
+ validates :unit_price, presence: true
45
+ validates :item_total, presence: true
46
+ validates :sku, length: { maximum: 100 }
47
+ validates :unit, presence: true, length: { maximum: 50 }
48
+
49
+ after_initialize :set_defaults
50
+
51
+ def serialize_options
52
+ {
53
+ except: %i[id updated_at created_at],
54
+ map: {
55
+ keys: {
56
+ _all: :camelize,
57
+ sku: 'SKU'
58
+ }
59
+ }
60
+ }
61
+ end
62
+
63
+ def unit_price=(value)
64
+ super(value.to_d)
65
+ calculate_total
66
+ end
67
+
68
+ def quantity=(value)
69
+ super(value.to_d)
70
+ calculate_total
71
+ end
72
+
73
+ def item_total=(value)
74
+ value = calculate_total if value.nil?
75
+ super(value)
76
+ end
77
+
78
+ private
79
+
80
+ def set_defaults
81
+ calculate_total
82
+ end
83
+
84
+ def calculate_total
85
+ self.item_total = (quantity * unit_price.to_d)
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ # == Schema Information
4
+ #
5
+ # Table name: barion_payer_accounts
6
+ #
7
+ # id :integer not null, primary key
8
+ # account_change_indicator :integer
9
+ # account_created :datetime
10
+ # account_creation_indicator :integer
11
+ # account_last_changed :datetime
12
+ # password_change_indicator :integer
13
+ # password_last_changed :datetime
14
+ # payment_method_added :datetime
15
+ # provision_attempts :integer
16
+ # purchases_in_the_last_6_months :integer
17
+ # shipping_address_added :datetime
18
+ # shipping_address_usage_indicator :integer
19
+ # suspicious_activity_indicator :integer default("no_suspicious_activity_observed")
20
+ # transactional_activity_per_day :integer
21
+ # transactional_activity_per_year :integer
22
+ # created_at :datetime not null
23
+ # updated_at :datetime not null
24
+ # account_id :string(64)
25
+ # payment_id :bigint
26
+ #
27
+ # Indexes
28
+ #
29
+ # index_barion_payer_accounts_on_account_id (account_id)
30
+ # index_barion_payer_accounts_on_payment_id (payment_id)
31
+ #
32
+ module Barion
33
+ # Represents a PayerAccount in Barion engine
34
+ class PayerAccount < ApplicationRecord
35
+ include ::Barion::DataFormats
36
+ include ::Barion::JsonSerializer
37
+
38
+ attribute :account_is, :string
39
+ enum account_change_indicator: {
40
+ changed_during_this_transaction: 0,
41
+ less_than_30_days: 10,
42
+ between_30_and_60_days: 20,
43
+ more_than_60_days: 30
44
+ }, _default: 0, _prefix: true
45
+ attribute :account_created, :datetime
46
+ enum account_creation_indicator: {
47
+ no_account: 0,
48
+ created_during_this_transaction: 10,
49
+ less_than_30_days: 20,
50
+ between_30_and_60_days: 30,
51
+ more_than_60_days: 40
52
+ }, _default: 0, _prefix: true
53
+ attribute :account_last_changed, :datetime
54
+ enum password_change_indicator: {
55
+ no_change: 0,
56
+ changed_during_this_transaction: 10,
57
+ less_than_30_days: 20,
58
+ between_30_and_60_days: 30,
59
+ more_than_60_days: 40
60
+ }, _default: 0, _prefix: true
61
+ attribute :password_last_changed, :datetime
62
+ attribute :payment_method_added, :boolean
63
+ attribute :provision_attempts, :integer
64
+ attribute :purchases_in_the_last_6_months, :integer
65
+ attribute :shipping_address_added, :datetime
66
+ enum shipping_address_usage_indicator: {
67
+ this_transaction: 0,
68
+ less_than_30_days: 10,
69
+ between_30_and_60_days: 20,
70
+ more_than_60_days: 30
71
+ }, _default: 0
72
+ enum suspicious_activity_indicator: {
73
+ no_suspicious_activity_observed: 0,
74
+ suspicious_activity_observed: 10
75
+ }, _default: 0
76
+ attribute :transactional_activity_per_day, :integer
77
+ attribute :transactional_activity_per_year, :integer
78
+
79
+ belongs_to :payment, inverse_of: :payer_account, dependent: :delete
80
+
81
+ validates :account_id, length: { maximum: 64 }, allow_nil: true
82
+ validates :provision_attempts,
83
+ numericality: true,
84
+ inclusion: { in: 0..999 },
85
+ allow_nil: true
86
+ validates :transactional_activity_per_day,
87
+ numericality: { only_integer: true },
88
+ inclusion: { in: 0..999 },
89
+ allow_nil: true
90
+ validates :purchases_in_the_last_6_months,
91
+ numericality: { only_integer: true },
92
+ inclusion: { in: 0..9999 },
93
+ allow_nil: true
94
+ validates :transactional_activity_per_year,
95
+ numericality: { only_integer: true },
96
+ inclusion: { in: 0..999 },
97
+ allow_nil: true
98
+
99
+ def serialize_options
100
+ { except: %i[id created_at updated_at],
101
+ map: {
102
+ keys: {
103
+ _all: :camelize
104
+ },
105
+ values: {
106
+ _all: proc { |v| v.respond_to?(:camelize) ? v.camelize : v },
107
+ account_created: :as_datetime,
108
+ account_last_changed: :as_datetime,
109
+ password_last_changed: :as_datetime,
110
+ account_change_indicator: :as_enum_id,
111
+ account_creation_indicator: :as_enum_id,
112
+ password_change_indicator: :as_enum_id,
113
+ shipping_address_usage_indicator: :as_enum_id,
114
+ suspicious_activity_indicator: :as_enum_id
115
+ }
116
+ } }
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,348 @@
1
+ # frozen_string_literal: true
2
+
3
+ # == Schema Information
4
+ #
5
+ # Table name: barion_payments
6
+ #
7
+ # id :integer not null, primary key
8
+ # callback_url :string(2000)
9
+ # card_holder_name_hint :string(45)
10
+ # challenge_preference :integer default("no_preference")
11
+ # checksum :string not null
12
+ # completed_at :datetime
13
+ # created_at_barion :datetime
14
+ # currency :string(3) not null
15
+ # delayed_capture_period :integer
16
+ # delayed_capture_until :datetime
17
+ # fraud_risk_score :integer
18
+ # funding_source :integer
19
+ # funding_sources :integer default("all")
20
+ # gateway_url :string(2000)
21
+ # guest_check_out :boolean
22
+ # initiate_recurrence :boolean
23
+ # locale :string(10) not null
24
+ # order_number :string(100)
25
+ # payer_hint :string(256)
26
+ # payer_home_number :string(30)
27
+ # payer_phone_number :string(30)
28
+ # payer_work_phone_number :string(30)
29
+ # payment_type :integer default("immediate"), not null
30
+ # payment_window :string(6)
31
+ # pos_name :string
32
+ # pos_owner_country :string
33
+ # pos_owner_email :string
34
+ # poskey :string not null
35
+ # qr_url :string(2000)
36
+ # recurrence_result :integer
37
+ # recurrence_type :integer default(NULL)
38
+ # redirect_url :string(2000)
39
+ # reservation_period :integer
40
+ # reserved_until :datetime
41
+ # started_at :datetime
42
+ # status :integer not null
43
+ # suggested_local :string
44
+ # total :decimal(, )
45
+ # valid_until :datetime
46
+ # created_at :datetime not null
47
+ # updated_at :datetime not null
48
+ # payment_id :string
49
+ # payment_request_id :string(100)
50
+ # pos_id :string
51
+ # recurrence_id :string(100)
52
+ # trace_id :string(100)
53
+ #
54
+ # Indexes
55
+ #
56
+ # index_barion_payments_on_order_number (order_number)
57
+ # index_barion_payments_on_payment_id (payment_id)
58
+ # index_barion_payments_on_payment_request_id (payment_request_id)
59
+ # index_barion_payments_on_payment_type (payment_type)
60
+ # index_barion_payments_on_poskey (poskey)
61
+ # index_barion_payments_on_recurrence_id (recurrence_id)
62
+ # index_barion_payments_on_status (status)
63
+ #
64
+ module Barion
65
+ # Represents a payment in Barion engine
66
+ class Payment < ApplicationRecord
67
+ include ::Barion::DataFormats
68
+ include ::Barion::Currencies
69
+ include ::Barion::JsonSerializer
70
+
71
+ enum locale: { 'cs-CZ': 'cs-CZ',
72
+ 'de-DE': 'de-DE',
73
+ 'en-US': 'en-US',
74
+ 'es-ES': 'es-ES',
75
+ 'fr-FR': 'fr-FR',
76
+ 'hu-HU': 'hu-HU',
77
+ 'sk-SK': 'sk-SK',
78
+ 'sl-SI': 'sl-SI' }, _default: 'hu-HU'
79
+ enum challenge_preference: {
80
+ no_preference: 0,
81
+ challenge_required: 10,
82
+ no_challenge_needed: 20
83
+ }, _default: :no_preference
84
+ enum recurrence_result: %i[none successful failed not_found], _prefix: true
85
+ enum payment_type: %i[immediate reservation delayed_capture], _default: :immediate
86
+ enum funding_sources: { all: 0, balance: 1 }, _suffix: true, _default: :all
87
+ enum recurrence_type: { one_click: 1, merchant_initiated: 2, recurring: 3 }
88
+ enum status: {
89
+ initial: 0,
90
+ prepared: 10,
91
+ started: 20,
92
+ in_progress: 21,
93
+ waiting: 22,
94
+ reserved: 25,
95
+ authorized: 26,
96
+ canceled: 30,
97
+ succeeded: 40,
98
+ failed: 50,
99
+ partially_succeeded: 60,
100
+ expired: 70
101
+ }, _default: :initial
102
+ attribute :payment_window, :integer, default: 30.minutes.to_i
103
+ attribute :guest_check_out, :boolean, default: true
104
+ attribute :initiate_recurrence, :boolean, default: false
105
+ attribute :phone_number, :string
106
+ attribute :home_number, :string
107
+ attribute :checksum, :string
108
+ enum funding_source: { balance: 1, bank_card: 2, bank_transfer: 3 }, _suffix: true
109
+
110
+ has_many :payment_transactions,
111
+ inverse_of: :payment,
112
+ dependent: :destroy
113
+
114
+ has_one :shipping_address,
115
+ class_name: '::Barion::Address',
116
+ inverse_of: :payment
117
+
118
+ has_one :billing_address,
119
+ class_name: '::Barion::Address',
120
+ inverse_of: :payment
121
+
122
+ has_one :payer_account, inverse_of: :payment
123
+
124
+ has_one :purchase_information,
125
+ class_name: '::Barion::Purchase',
126
+ inverse_of: :payment
127
+
128
+ validates :payment_type, presence: true
129
+ validates :reservation_period,
130
+ presence: true,
131
+ numericality: { only_integer: true },
132
+ inclusion: { in: 1.minute.to_i..1.year.to_i },
133
+ if: :reservation?
134
+ validates :delayed_capture_period,
135
+ presence: true,
136
+ numericality: { only_integer: true },
137
+ inclusion: { in: 1.minute.to_i..1.week.to_i },
138
+ if: :delayed_capture?
139
+ validates :delayed_capture_period,
140
+ absence: true,
141
+ unless: :delayed_capture?
142
+ validates :payment_window,
143
+ numericality: { only_integer: true },
144
+ inclusion: { in: 1.minute.to_i..1.week.to_i }
145
+ validates_uniqueness_of :payment_request_id
146
+ validates :recurrence_id, length: { maximum: 100 }
147
+ validates :payer_hint, length: { maximum: 256 }
148
+ validates :card_holder_name_hint,
149
+ length: { minimum: 2, maximum: 45 },
150
+ allow_nil: true
151
+ validates :trace_id, length: { maximum: 100 }
152
+ validates :redirect_url, length: { maximum: 2000 }
153
+ validates :callback_url, length: { maximum: 2000 }
154
+ validates :gateway_url, length: { maximum: 2000 }
155
+ validates :qr_url, length: { maximum: 2000 }
156
+ validates :order_number, length: { maximum: 100 }
157
+ validates :payer_hint, length: { maximum: 256 }
158
+ validates :payment_transactions, presence: true
159
+ validates :checksum, presence: true
160
+ validates_associated :payment_transactions
161
+ validates_associated :payer_account
162
+
163
+ after_initialize :set_defaults
164
+ after_initialize :validate_checksum
165
+
166
+ before_validation :create_payment_request_id
167
+ before_validation :refresh_checksum
168
+
169
+ def poskey=(value = nil)
170
+ value = ::Barion.poskey if value.nil?
171
+ super(value)
172
+ end
173
+
174
+ def payment_type=(value)
175
+ case value.to_sym
176
+ when :immediate
177
+ self.reservation_period = nil
178
+ when :reservation
179
+ self.reservation_period = 30.minutes.to_i
180
+ when :delayed_capture
181
+ self.reservation_period = 1.minutes.to_i
182
+ self.delayed_capture_period = 1.week.to_i
183
+ else
184
+ raise ArgumentError, "#{value} is not a valid payment_type"
185
+ end
186
+ super(value)
187
+ end
188
+
189
+ def payer_work_phone_number=(number)
190
+ super(::Barion::DataFormats.phone_number(number))
191
+ end
192
+
193
+ def payer_phone_number=(number)
194
+ super(::Barion::DataFormats.phone_number(number))
195
+ end
196
+
197
+ def payer_home_number=(number)
198
+ super(::Barion::DataFormats.phone_number(number))
199
+ end
200
+
201
+ def readonly?
202
+ return false if @_bypass_readonly
203
+
204
+ !initial?
205
+ end
206
+
207
+ def refresh_checksum
208
+ self.checksum = gen_checksum
209
+ end
210
+
211
+ def execute
212
+ if valid?
213
+ ::Barion.endpoint['v2/Payment/Start'].post(
214
+ as_json.to_json,
215
+ { content_type: :json, accept: :json }
216
+ ) { |response, request, _result| handle_response(response, request) }
217
+ else
218
+ false
219
+ end
220
+ end
221
+
222
+ def refresh_state
223
+ ::Barion.endpoint['v2/Payment/GetPaymentState'].get(
224
+ {
225
+ params: {
226
+ POSKey: poskey,
227
+ PaymentId: payment_id
228
+ }
229
+ }
230
+ ) { |response, request, _| handle_response(response, request) }
231
+ end
232
+
233
+ def serialize_options
234
+ { only: %i[callback_url card_holder_name_hint challenge_preference currency
235
+ delayed_capture_period funding_sources guest_check_out initiate_recurrence
236
+ locale order_number payer_hint payer_home_number payer_phone_number
237
+ payer_work_phone_number payment_type payment_window poskey recurrence_type
238
+ redirect_url reservation_period payment_request_id recurrence_id trace_id],
239
+ include: %i[billing_address shipping_address payment_transactions payer_account purchase_information],
240
+ map: {
241
+ keys: {
242
+ _all: :camelize,
243
+ poskey: 'POSKey',
244
+ payment_transactions: 'Transactions'
245
+ },
246
+ values: {
247
+ _all: proc { |v| v.respond_to?(:camelize) ? v.camelize : v },
248
+ _except: %w[poskey redirect_url callback_url locale payment_request_id payer_hint card_holder_name_hint],
249
+ reservation_period: :as_time,
250
+ delayed_capture_period: :as_time,
251
+ payment_window: :as_time,
252
+ funding_sources: :as_list,
253
+ challenge_preference: :as_enum_id
254
+ }
255
+ } }
256
+ end
257
+
258
+ def deserialize_options
259
+ {
260
+ assoc: {
261
+ payment_transactions: {
262
+ pos_transaction_id: 'POSTransactionId'
263
+ }
264
+ },
265
+ map: {
266
+ keys: {
267
+ _all: :underscore,
268
+ Transactions: 'payment_transactions',
269
+ CreatedAt: 'barion_created_at'
270
+ },
271
+ values: {
272
+ _all: proc { |v| v.respond_to?(:underscore) ? v.underscore : v },
273
+ _except: %w[Locale Currency]
274
+ }
275
+ },
276
+ before_save: :refresh_checksum
277
+ }
278
+ end
279
+
280
+ protected
281
+
282
+ # rubocop:disable Lint/UselessMethodDefinition
283
+ def checksum=(value)
284
+ super(value)
285
+ end
286
+ # rubocop:enable Lint/UselessMethodDefinition
287
+
288
+ def handle_response(response, request)
289
+ case response.code
290
+ when 200
291
+ process_response(JSON.parse(response))
292
+ refresh_checksum
293
+ _bypass_readonly do
294
+ save
295
+ end
296
+ true
297
+ when 301, 302, 307
298
+ raise ::Barion::Error.new(
299
+ { Title: response.description,
300
+ Description: 'No redirection is allowed in communication, please check endpoint!',
301
+ HappenedAt: DateTime.now,
302
+ ErrorCode: response.code,
303
+ EndPoint: request.url }
304
+ )
305
+ when 400
306
+ errors = ::JSON.parse(response)['Errors']
307
+ raise ::Barion::Error.new(
308
+ { Title: response.description,
309
+ Description: 'Request failed, please check errors',
310
+ HappenedAt: DateTime.now,
311
+ Errors: errors,
312
+ ErrorCode: response.code,
313
+ EndPoint: request.url }
314
+ )
315
+ end
316
+ end
317
+
318
+ def set_defaults
319
+ self.poskey = ::Barion.poskey if poskey.nil?
320
+ self.callback_url = ::Barion::Engine.routes.url_helpers.gateway_callback_url
321
+ self.redirect_url = ::Barion::Engine.routes.url_helpers.gateway_back_url
322
+ end
323
+
324
+ def create_payment_request_id
325
+ self.payment_request_id = "#{::Barion.acronym}#{::Time.now.to_f.to_s.gsub('.', '')}" if payment_request_id.nil?
326
+ end
327
+
328
+ def gen_checksum
329
+ ::Digest::SHA512.hexdigest(as_json.to_json)
330
+ end
331
+
332
+ def validate_checksum
333
+ if checksum.present? && checksum != gen_checksum
334
+ raise ::Barion::TamperedData, "checksum: #{refresh_checksum}, json: #{as_json}"
335
+ end
336
+
337
+ true
338
+ end
339
+
340
+ private
341
+
342
+ def _bypass_readonly
343
+ @_bypass_readonly = true
344
+ yield
345
+ @_bypass_readonly = false
346
+ end
347
+ end
348
+ end