Class: EInvoiceQREncryptor::Cipher

Inherits:
Object
  • Object
show all
Defined in:
lib/einvoice-qr-encryptor/client.rb

Overview

:nodoc:

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(aes_key = nil) ⇒ Cipher

Returns a new instance of Cipher.



11
12
13
14
15
# File 'lib/einvoice-qr-encryptor/client.rb', line 11

def initialize(aes_key = nil)
  @aes_key = aes_key
  @aes_iv = '0EDF25C93A28D7B5FF5E45DA42F8A1B8'
  @cipher = OpenSSL::Cipher::AES.new(128, :CBC)
end

Instance Attribute Details

#aes_keyObject (readonly)

Returns the value of attribute aes_key.



9
10
11
# File 'lib/einvoice-qr-encryptor/client.rb', line 9

def aes_key
  @aes_key
end

Instance Method Details

#decrypt(cipher_text) ⇒ String

Decrypt cipher text

Examples:

decrypt('73UqXrAk5DsVNv2VEvIFkQ==') #=> 'AA123456781234'

Parameters:

  • cipher_text (String)

    Cipher text to decrypt

Returns:



34
35
36
37
# File 'lib/einvoice-qr-encryptor/client.rb', line 34

def decrypt(cipher_text)
  init_cipher(:decrypt)
  @cipher.update(Base64.decode64(cipher_text)) + @cipher.final
end

#encrypt(plain_text) ⇒ String

Encrypt plain text

Examples:

encrypt('AA123456781234') #=> '73UqXrAk5DsVNv2VEvIFkQ=='

Parameters:

  • plain_text (String)

    Plain text to encrypt

Returns:



23
24
25
26
# File 'lib/einvoice-qr-encryptor/client.rb', line 23

def encrypt(plain_text)
  init_cipher(:encrypt)
  Base64.encode64(@cipher.update(plain_text) + @cipher.final).strip
end

#gen_barcode_qrcode_information(invoice_number:, invoice_date:, invoice_time:, random_number:, sales_amount:, tax_amount:, total_amount:, buyer_identifier:, represent_identifier:, seller_identifier:, business_identifier:, product_array:) ⇒ Hash

Generate Barcode and QR code information for EInvoice

> {

  :barcode=>"10405AA123456781234",
  :qrcode_left=>"AA12345678104051112340000006400000064000000000000000073UqXrAk5DsVNv2VEvIFkQ==:**********:0:2:1",
  :qrcode_right=>"**"
}

Examples:

gen_barcode_qrcode_information(
  invoice_number: 'AA12345678',
  invoice_date: '1040511',
  invoice_time: '090000',
  random_number: '1234',
  sales_amount: 100,
  tax_amount: 0,
  total_amount: 100,
  buyer_identifier: '00000000',
  represent_identifier: '00000000',
  seller_identifier: '00000000',
  business_identifier: '00000000',
  product_array: [{
    product_code: '4713546575601',
    product_name: 'Product 1',
    product_qty: '1',
    product_sale_amount: '60',
    product_tax_amount: '0',
    product_amount: '60'
  }, {
    product_code: '4713546575602',
    product_name: 'Product 2',
    product_qty: '2',
    product_sale_amount: '20',
    product_tax_amount: '0',
    product_amount: '20'
  }]
)

Parameters:

Returns:

  • (Hash)

    Barcode and QR code information for EInvoice



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/einvoice-qr-encryptor/client.rb', line 210

def gen_barcode_qrcode_information(invoice_number:, invoice_date:, invoice_time:, random_number:, sales_amount:, tax_amount:, total_amount:, buyer_identifier:, represent_identifier:, seller_identifier:, business_identifier:, product_array:)
  first_77_bits_qrcode_info = gen_qrcode_information(
    invoice_number: invoice_number,
    invoice_date: invoice_date,
    invoice_time: invoice_time,
    random_number: random_number,
    sales_amount: sales_amount,
    tax_amount: tax_amount,
    total_amount: total_amount,
    buyer_identifier: buyer_identifier,
    represent_identifier: represent_identifier,
    seller_identifier: seller_identifier,
    business_identifier: business_identifier,
    product_array: product_array
  )

  # 營業人自行使用區
  bussiness_custom_block = '*' * 10

  # 二維條碼記載完整品目筆數
  product_count_in_qrcode = 0

  # 該張發票交易品目總筆數
  product_count_in_einvoice = product_array.length

  # 中文編碼參數
  chinese_encoding = 1

  {
    barcode: "#{invoice_date[0..4]}#{invoice_number}#{random_number}",
    qrcode_left: "#{first_77_bits_qrcode_info}:#{bussiness_custom_block}:#{product_count_in_qrcode}:#{product_count_in_einvoice}:#{chinese_encoding}",
    qrcode_right: '**'
  }
end

#gen_qrcode_information(invoice_number:, invoice_date:, invoice_time:, random_number:, sales_amount:, tax_amount:, total_amount:, buyer_identifier:, represent_identifier:, seller_identifier:, business_identifier:, product_array:) ⇒ String

Generate QR code information for EInvoice

Examples:

gen_qrcode_information(
  invoice_number: 'AA12345678',
  invoice_date: '1040511',
  invoice_time: '090000',
  random_number: '1234',
  sales_amount: 100,
  tax_amount: 0,
  total_amount: 100,
  buyer_identifier: '00000000',
  represent_identifier: '00000000',
  seller_identifier: '00000000',
  business_identifier: '00000000',
  product_array: []
) #=> 'AA12345678104051112340000006400000064000000000000000073UqXrAk5DsVNv2VEvIFkQ=='

Parameters:

Returns:

  • (String)

    QR code information for EInvoice



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/einvoice-qr-encryptor/client.rb', line 69

def gen_qrcode_information(invoice_number:, invoice_date:, invoice_time:, random_number:, sales_amount:, tax_amount:, total_amount:, buyer_identifier:, represent_identifier:, seller_identifier:, business_identifier:, product_array:)
  # check all arguments are valid
  # 發票字軌號碼共 10 碼
  raise ErrorMessage.generate(msg: :field_should_be, field: :invoice_number, data: String) unless invoice_number.is_a? String
  raise ErrorMessage.generate(msg: :fixed_length, field: :invoice_number, length: 10) if invoice_number.length != 10

  # 發票開立年月日(中華民國年份月份日期)共 7 碼
  raise ErrorMessage.generate(msg: :field_should_be, field: :invoice_date, data: String) unless invoice_date.is_a? String
  raise ErrorMessage.generate(msg: :fixed_length, field: :invoice_date, length: 7) if invoice_date.length != 7

  /\A\d{3}(?<month>\d{2})(?<day>\d{2})\Z/ =~ invoice_date
  raise ErrorMessage.generate(msg: :field_should_be, field: 'month of invoice_date', data: 'between 01 ~ 12') unless ('01'..'12').cover? month
  raise ErrorMessage.generate(msg: :field_should_be, field: 'day of invoice_date', data: 'between 01 ~ 31') unless ('01'..'31').cover? day

  # 發票開立時間 (24 小時制) 共 6 碼,以時時分分秒秒方式之字串表示
  # raise ErrorMessage.generate(msg: :field_should_be, field: :invoice_time, data: String) unless invoice_time.is_a? String
  # raise ErrorMessage.generate(msg: :fixed_length, field: :invoice_time, length: 6) if invoice_time.length != 6

  # 4 位隨機碼
  raise ErrorMessage.generate(msg: :field_should_be, field: :random_number, data: String) unless random_number.is_a? String
  raise ErrorMessage.generate(msg: :fixed_length, field: :random_number, length: 4) if random_number.length != 4

  # 銷售額(未稅)
  raise ErrorMessage.generate(msg: :field_should_be, field: :sales_amount, data: Integer) unless sales_amount.is_a? Integer
  raise ErrorMessage.generate(msg: :field_should_be, field: :sales_amount, data: 'greater than or equal to 0') if sales_amount < 0

  # 稅額
  raise ErrorMessage.generate(msg: :field_should_be, field: :tax_amount, data: Integer) unless tax_amount.is_a? Integer
  raise ErrorMessage.generate(msg: :field_should_be, field: :tax_amount, data: 'greater than or equal to 0') if tax_amount < 0

  # 總計金額(含稅)
  raise ErrorMessage.generate(msg: :field_should_be, field: :total_amount, data: Integer) unless total_amount.is_a? Integer
  raise ErrorMessage.generate(msg: :field_should_be, field: :total_amount, data: 'greater than or equal to 0') if total_amount < 0

  # 買受人統一編號
  raise ErrorMessage.generate(msg: :field_should_be, field: :buyer_identifier, data: String) unless buyer_identifier.is_a? String
  raise ErrorMessage.generate(msg: :fixed_length, field: :buyer_identifier, length: 8) if buyer_identifier.length != 8

  # 代表店統一編號,目前電子發票證明聯二維條碼規格已不使用代表店
  # raise ErrorMessage.generate(msg: :field_should_be, field: :represent_identifier, data: String) unless represent_identifier.is_a? String
  # raise ErrorMessage.generate(msg: :fixed_length, field: :represent_identifier, length: 8) if represent_identifier.length != 8

  # 銷售店統一編號
  raise ErrorMessage.generate(msg: :field_should_be, field: :seller_identifier, data: String) unless seller_identifier.is_a? String
  raise ErrorMessage.generate(msg: :fixed_length, field: :seller_identifier, length: 8) if seller_identifier.length != 8

  # 總公司統一編號,如無總公司請填入銷售店統一編號
  raise ErrorMessage.generate(msg: :field_should_be, field: :business_identifier, data: String) unless business_identifier.is_a? String
  raise ErrorMessage.generate(msg: :fixed_length, field: :business_identifier, length: 8) if business_identifier.length != 8

  # 商品資訊
  raise ErrorMessage.generate(msg: :field_should_be, field: :product_array, data: Array) unless product_array.is_a? Array

  product_array.each do |product|
    # 透過條碼槍所掃出之條碼資訊
    raise ErrorMessage.generate(msg: :field_should_be, field: 'product_array.product_code', data: String) unless product[:product_code].is_a? String
    raise ErrorMessage.generate(msg: :cannot_be_empty, field: 'product_array.product_code') if product[:product_code].empty?

    # 商品名稱
    raise ErrorMessage.generate(msg: :field_should_be, field: 'product_array.product_name', data: String) unless product[:product_name].is_a? String
    raise ErrorMessage.generate(msg: :cannot_be_empty, field: 'product_array.product_name') if product[:product_name].empty?

    # 商品數量
    raise ErrorMessage.generate(msg: :field_should_be, field: 'product_array.product_qty', data: String) unless product[:product_qty].is_a? String
    raise ErrorMessage.generate(msg: :cannot_be_empty, field: 'product_array.product_qty') if product[:product_qty].empty?
    raise ErrorMessage.generate(msg: :field_should_be, field: 'product_array.product_qty', data: 'number string') unless product[:product_qty].numeric?

    # 商品銷售額(整數未稅),若無法分離稅項則記載為字串 0
    raise ErrorMessage.generate(msg: :field_should_be, field: 'product_array.product_sale_amount', data: String) unless product[:product_sale_amount].is_a? String
    raise ErrorMessage.generate(msg: :cannot_be_empty, field: 'product_array.product_sale_amount') if product[:product_sale_amount].empty?
    raise ErrorMessage.generate(msg: :field_should_be, field: 'product_array.product_sale_amount', data: 'number string') unless product[:product_sale_amount].numeric?

    # 商品稅額(整數),若無法分離稅項則記載為字串 0
    raise ErrorMessage.generate(msg: :field_should_be, field: 'product_array.product_tax_amount', data: String) unless product[:product_tax_amount].is_a? String
    raise ErrorMessage.generate(msg: :cannot_be_empty, field: 'product_array.product_tax_amount') if product[:product_tax_amount].empty?
    raise ErrorMessage.generate(msg: :field_should_be, field: 'product_array.product_tax_amount', data: 'number string') unless product[:product_tax_amount].numeric?

    # 商品金額(整數含稅)
    raise ErrorMessage.generate(msg: :field_should_be, field: 'product_array.product_amount', data: String) unless product[:product_amount].is_a? String
    raise ErrorMessage.generate(msg: :cannot_be_empty, field: 'product_array.product_amount') if product[:product_amount].empty?
    raise ErrorMessage.generate(msg: :field_should_be, field: 'product_array.product_amount', data: 'number string') unless product[:product_amount].numeric?
  end

  # generate cipher text
  cipher_text = encrypt("#{invoice_number}#{random_number}")

  # return QR Code information for EInvoice
  "#{invoice_number}#{invoice_date}#{random_number}" \
  "#{sales_amount.to_8bit_hex_string}#{total_amount.to_8bit_hex_string}" \
  "#{buyer_identifier}#{seller_identifier}#{cipher_text}"
end