class Rubyqrpay::Generator

Public Class Methods

generate_payload(opts) click to toggle source
# File lib/rubyqrpay/generator.rb, line 11
def self.generate_payload(opts)
  opts = Rubyqrpay::Validator.validate_payload(opts)
  unless opts.nil?
    payload = generation(opts)
    # percent_encode payload # Temporary solution to fix invalid BNB URI decode
  end
end
generate_png(url, payload, **opts) click to toggle source
# File lib/rubyqrpay/generator.rb, line 19
def self.generate_png(url, payload, **opts)
  opts_default = {size: size_func(url, payload).to_i, level: :l}
  qrcode = RQRCode::QRCode.new("#{url}#{payload}", level: opts[:level] || opts_default[:level])
  png = qrcode.as_png(resize_gte_to: false,
                      resize_exactly_to: false,
                      fill: 'white',
                      color: 'black',
                      size: opts[:size] || opts_default[:size],
                      border_modules: 4,
                      module_px_size: 6)
  qrcode_to_base64(png)
end

Private Class Methods

additional_data_field(additional_data) click to toggle source
# File lib/rubyqrpay/generator.rb, line 99
def self.additional_data_field(additional_data)
  additional_data ||= Hash.new
  additional_data = {
    ID_BILL_NUMBER            => additional_data[:bill_number],
    ID_MOBILE_NUMBER          => additional_data[:mobile_number],
    ID_STORE_LABEL            => additional_data[:store_label],
    ID_LOYALTY_NUMBER         => additional_data[:loyalty_number],
    ID_REFERENCE_LABEL        => additional_data[:reference_label],
    ID_CUSTOMER_LABEL         => additional_data[:customer_label],
    ID_TERMINAL_LABEL         => additional_data[:terminal_label],
    ID_PURPOSE_OF_TRANSACTION => additional_data[:purpose_of_transaction],
    ID_CONSUMER_DATA_REQUEST  => additional_data[:consumer_data_request]
  }
  join_hash(additional_data)
end
aep_convert(aep) click to toggle source
# File lib/rubyqrpay/generator.rb, line 77
def self.aep_convert(aep)
  if aep || aep.nil?
    AEP_DEFAULT
  else
    AEP_FALSE
  end
end
convenience_indicator_case(payload_data, opts) click to toggle source
# File lib/rubyqrpay/generator.rb, line 125
def self.convenience_indicator_case(payload_data, opts)
  payload_data.tap do |data|
    case format_indicator(opts[:convenience_indicator])
    when CONVENIENCE_INDICATOR_FIXED
      data[ID_VALUE_OF_CONVENIENCE_FEE_FIXED] = format_amount(opts[:fixed_fee])
    when CONVENIENCE_INDICATOR_PERCENTAGE
      data[ID_VALUE_OF_CONVENIENCE_FEE_PERCENTAGE] = format_amount(opts[:percentage_fee])
    end
  end
end
crc(data) click to toggle source
# File lib/rubyqrpay/generator.rb, line 158
def self.crc(data)
  # === old algorithm
  # data += ID_CRC + CRC_SYMBOL_SIZE
  # x = Digest::CRC16CCITT.new
  # x.update(data)
  # ID_CRC + CRC_SYMBOL_SIZE + x.hexdigest.upcase

  # === updated algorithm
  ID_CRC + CRC_SYMBOL_SIZE + Digest::SHA256.hexdigest(data).slice(-4..-1).upcase
end
format_amount(amount) click to toggle source
# File lib/rubyqrpay/generator.rb, line 146
def self.format_amount(amount)
  "%.2f" % amount.to_f
end
format_indicator(convenience_indicator) click to toggle source
# File lib/rubyqrpay/generator.rb, line 150
def self.format_indicator(convenience_indicator)
  "0#{convenience_indicator}"
end
generation(opts) click to toggle source
# File lib/rubyqrpay/generator.rb, line 44
def self.generation(opts)
  payload_data = {
    ID_PAYLOAD_FORMAT                => PAYLOAD_FORMAT_EMV_QRCPS_MERCHANT_PRESENTED_MODE,
    ID_POI_METHOD                    => opts[:amount] ? POI_METHOD_DYNAMIC : POI_METHOD_STATIC,
    ID_MERCHANT_INFORMATION_32       => merchant_account_32_data(opts[:merchant_account_32]),
    ID_MERCHANT_INFORMATION_33       => merchant_account_33_data(opts[:agregator_id], opts[:merchant_account_33]),
    ID_MERCHANT_CATEGORY_CODE        => mcc_format(opts[:merchant_category_code]),
    ID_TRANSACTION_CURRENCY          => opts[:currency],
    ID_TRANSACTION_AMOUNT            => format_amount(opts[:amount]),
    ID_TIP_OF_CONVENIENCE_INDICATOR  => format_indicator(opts[:convenience_indicator]),
    ID_COUNTRY                       => opts[:country],
    ID_MERCHANT_NAME                 => opts[:merchant_name],
    ID_MERCHANT_CITY                 => opts[:merchant_city],
    ID_POSTAL_CODE                   => opts[:postal_code],
    ID_ADDITIONAL_DATA_FIELD         => additional_data_field(opts[:additional_data]),
    ID_MERCHANT_INFORMATION_LANGUAGE => merchant_language_data(opts[:merchant_information_language])
  }
  payload_data = convenience_indicator_case(payload_data, opts)
  payload = join_hash(payload_data)
  payload += crc(payload)
end
join_hash(hash) click to toggle source
# File lib/rubyqrpay/generator.rb, line 136
def self.join_hash(hash)
  hash.map do |id, value|
    value = percent_encode(value.to_s).gsub(/\%.{2}/, '')
    unless value.empty?
      len = "00#{value.size}".slice(-2..-1)
      id + len + value
    end
  end.join
end
mcc_format(mcc) click to toggle source
# File lib/rubyqrpay/generator.rb, line 169
def self.mcc_format(mcc)
  "%.4d" % mcc unless mcc.to_i == 0
end
merchant_account_32_data(merchant_account) click to toggle source
# File lib/rubyqrpay/generator.rb, line 66
def self.merchant_account_32_data(merchant_account)
  merchant_account = {
    MERCHANT_INFORMATION_TEMPLATE_ID_GUID => GUID_PROMPTPAY_32,
    ID_SERVICE_CODE_ERIP       => merchant_account[:service_code_erip],
    ID_PAYER_UNIQUE            => merchant_account[:payer_unique_id],
    ID_PAYER_NUMBER            => merchant_account[:payer_number],
    ID_AMOUNT_EDIT_POSSIBILITY => aep_convert(merchant_account[:amount_edit_possibility])
  }
  join_hash(merchant_account)
end
merchant_account_33_data(agregator_id, merchant_account) click to toggle source
# File lib/rubyqrpay/generator.rb, line 85
def self.merchant_account_33_data(agregator_id, merchant_account)
  unless agregator_id.nil?
    merchant_account ||= Hash.new
    merchant_account = {
        MERCHANT_INFORMATION_TEMPLATE_ID_GUID => "#{GUID_PROMPTPAY_33}#{agregator_id}",
        ID_SERVICE_PRODUCER_CODE => merchant_account[:service_producer_code],
        ID_SERVICE_CODE          => merchant_account[:service_code],
        ID_OUTLET                => merchant_account[:outlet],
        ID_ORDER_CODE            => merchant_account[:order_code]
      }
    join_hash(merchant_account)
  end
end
merchant_language_data(merchant_information_language) click to toggle source
# File lib/rubyqrpay/generator.rb, line 115
def self.merchant_language_data(merchant_information_language)
  merchant_information_language ||= Hash.new
  merchant_information_language = {
    ID_LANGUAGE_REFERENCE      => merchant_information_language[:language_reference],
    ID_MERCHANT_NAME_ALTERNATE => merchant_information_language[:name_alternate],
    ID_MERCHANT_CITY_ALTERNATE => merchant_information_language[:city_alternate]
  }
  join_hash(merchant_information_language)
end
percent_encode(str) click to toggle source
# File lib/rubyqrpay/generator.rb, line 154
def self.percent_encode(str)
  URI.escape(str)
end
qrcode_to_base64(png) click to toggle source
# File lib/rubyqrpay/generator.rb, line 39
def self.qrcode_to_base64(png)
  base64 = Base64.encode64(png.to_s)
  base64.split("\n").join
end
size_func(url, payload) click to toggle source
# File lib/rubyqrpay/generator.rb, line 34
def self.size_func(url, payload)
  x = "#{url}#{payload}".size
  K_SIZE_FUNC * x + B_SIZE_FUNC # linear function (kx + b)
end