Initial commit
This commit is contained in:
216
app/models/beneficiary.rb
Normal file
216
app/models/beneficiary.rb
Normal file
@@ -0,0 +1,216 @@
|
||||
# encoding: UTF-8
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Beneficiary < ApplicationRecord
|
||||
|
||||
# == Constants ============================================================
|
||||
|
||||
extend Enumerize
|
||||
|
||||
include Vault::EncryptedModel
|
||||
|
||||
vault_lazy_decrypt!
|
||||
|
||||
acts_as_eventable prefix: 'beneficiary', on: %i[create update]
|
||||
|
||||
include AASM
|
||||
include AASM::Locking
|
||||
|
||||
STATES_MAPPING = { pending: 0, active: 1, archived: 2, aml_processing: 3, aml_suspicious: 4 }.freeze
|
||||
|
||||
STATES = %i[pending aml_processing aml_suspicious active archived].freeze
|
||||
STATES_AVAILABLE_FOR_MEMBER = %i[pending active]
|
||||
|
||||
PIN_LENGTH = 6
|
||||
PIN_RANGE = 10**5..10**Beneficiary::PIN_LENGTH
|
||||
|
||||
INVALID_ADDRESS_SYMBOLS = /[\<\>\'\,\[\]\}\{\"\)\(\*\&\^\%\$\#\`\~\{\}\@]/.freeze
|
||||
|
||||
# == Attributes ===========================================================
|
||||
|
||||
vault_attribute :data, serialize: :json, default: {}
|
||||
|
||||
# == Extensions ===========================================================
|
||||
|
||||
enumerize :state, in: STATES_MAPPING, scope: true
|
||||
|
||||
aasm column: :state, enum: :states_mapping, whiny_transitions: false do
|
||||
state :pending, initial: true
|
||||
state :active
|
||||
state :aml_processing
|
||||
state :aml_suspicious
|
||||
state :archived
|
||||
|
||||
event :activate do
|
||||
if Peatio::AML.adapter.present?
|
||||
transitions from: :pending, to: :aml_processing, guard: :valid_pin?
|
||||
after do
|
||||
enable! if aml_check!
|
||||
end
|
||||
else
|
||||
transitions from: :pending, to: :active, guard: :valid_pin?
|
||||
end
|
||||
end
|
||||
|
||||
event :enable do
|
||||
transitions from: :aml_processing, to: :active
|
||||
end if Peatio::AML.adapter.present?
|
||||
|
||||
event :aml_suspicious do
|
||||
transitions from: :aml_processing, to: :aml_suspicious
|
||||
end if Peatio::AML.adapter.present?
|
||||
|
||||
event :archive do
|
||||
transitions from: %i[pending aml_processing aml_suspicious active], to: :archived
|
||||
end
|
||||
end
|
||||
|
||||
# == Relationships ========================================================
|
||||
|
||||
belongs_to :currency, required: true
|
||||
belongs_to :member, required: true
|
||||
|
||||
# == Validations ==========================================================
|
||||
|
||||
validates :pin, presence: true, numericality: { only_integer: true }
|
||||
|
||||
validates :data, presence: true
|
||||
|
||||
# Validates that data contains address field which is required for coin.
|
||||
validate if: ->(b) { b.currency.present? && b.currency.coin? } do
|
||||
errors.add(:data, 'address can\'t be blank') if data.blank? || data.symbolize_keys[:address].blank?
|
||||
end
|
||||
|
||||
# Validates address field which is required for coin.
|
||||
validate if: ->(b) { b.currency.present? && b.currency.coin? } do
|
||||
errors.add(:data, 'invlalid address') if data.present? && data.symbolize_keys[:address].present? && data.symbolize_keys[:address].match?(INVALID_ADDRESS_SYMBOLS)
|
||||
end
|
||||
|
||||
# Validates that data contains full_name field which is required for fiat.
|
||||
validate if: ->(b) { b.currency.present? && b.currency.fiat? } do
|
||||
errors.add(:data, 'full_name can\'t be blank') if data.blank? || data.symbolize_keys[:full_name].blank?
|
||||
end
|
||||
|
||||
# == Scopes ===============================================================
|
||||
|
||||
scope :available_to_member, -> { with_state(:pending, :active) }
|
||||
scope :active_to_member, -> { with_state(:active) }
|
||||
# == Callbacks ============================================================
|
||||
|
||||
before_validation(on: :create) do
|
||||
# Truncate spaces
|
||||
data['address'] = data['address'].gsub(/\s+/, '') if data.present? && data['address'].present?
|
||||
|
||||
# Generate Beneficiary Pin
|
||||
self.pin ||= self.class.generate_pin
|
||||
# Record time when we send event to Event API
|
||||
self.sent_at = Time.now
|
||||
end
|
||||
|
||||
# == Class Methods ========================================================
|
||||
|
||||
class << self
|
||||
def generate_pin
|
||||
SecureRandom.rand(Beneficiary::PIN_RANGE)
|
||||
end
|
||||
|
||||
# Method used for passing states mapping to AASM.
|
||||
def states_mapping
|
||||
STATES_MAPPING
|
||||
end
|
||||
end
|
||||
|
||||
# == Instance Methods =====================================================
|
||||
|
||||
def as_json_for_event_api
|
||||
{ user: { uid: member.uid, email: member.email },
|
||||
currency: currency_id,
|
||||
name: name,
|
||||
description: description,
|
||||
data: data,
|
||||
pin: pin,
|
||||
state: state,
|
||||
sent_at: sent_at.iso8601,
|
||||
created_at: created_at.iso8601,
|
||||
updated_at: updated_at.iso8601 }
|
||||
end
|
||||
|
||||
def aml_check!
|
||||
result = Peatio::AML.check!(rid, currency_id, member.uid)
|
||||
if result.risk_detected
|
||||
b.aml_suspicious!
|
||||
return nil
|
||||
end
|
||||
return nil if result.pending
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def valid_pin?(user_pin)
|
||||
case user_pin
|
||||
when Integer
|
||||
return pin == user_pin
|
||||
when String
|
||||
return pin == user_pin.to_i
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
def rid
|
||||
currency.coin? ? coin_rid : fiat_rid
|
||||
end
|
||||
|
||||
def regenerate_pin!
|
||||
update(pin: self.class.generate_pin, sent_at: Time.now)
|
||||
end
|
||||
|
||||
def masked_account_number
|
||||
account_number = data.symbolize_keys[:account_number]
|
||||
|
||||
if data.present? && account_number.present?
|
||||
account_number.sub(/(?<=\A.{2})(.*)(?=.{4}\z)/) { |match| '*' * match.length }
|
||||
end
|
||||
end
|
||||
|
||||
def masked_data
|
||||
data.merge(account_number: masked_account_number).compact if data.present?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def coin_rid
|
||||
return unless currency.coin?
|
||||
|
||||
data.symbolize_keys[:address]
|
||||
end
|
||||
|
||||
def fiat_rid
|
||||
return unless currency.fiat?
|
||||
|
||||
"%s-%s-%08d" % [data.symbolize_keys[:full_name].downcase.split.join('-'), currency_id.downcase, id]
|
||||
end
|
||||
end
|
||||
|
||||
# == Schema Information
|
||||
# Schema version: 20201125134745
|
||||
#
|
||||
# Table name: beneficiaries
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# member_id :bigint not null
|
||||
# currency_id :string(10) not null
|
||||
# name :string(64) not null
|
||||
# description :string(255) default("")
|
||||
# data_encrypted :string(1024)
|
||||
# pin :integer unsigned, not null
|
||||
# sent_at :datetime
|
||||
# state :integer default("pending"), unsigned, not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_beneficiaries_on_currency_id (currency_id)
|
||||
# index_beneficiaries_on_member_id (member_id)
|
||||
#
|
||||
Reference in New Issue
Block a user