einvoice-tw
Parse Taiwan e-invoice (電子發票) QR codes into structured data — and check them against the uniform-invoice lottery. Pure Python, zero dependencies, follows the Ministry of Finance barcode specification.
Every receipt printed in Taiwan carries two QR codes (and a Code 39 barcode)
that encode the invoice number, date, random code, amounts, buyer/seller tax
IDs and the line items. einvoice-tw turns the text your scanner reads out
of them into a tidy Invoice object, JSON or CSV — the building block for
expense trackers, bookkeeping imports, receipt archives and lottery checkers.
Install
pip install einvoice-tw
Python 3.9+. No dependencies. Scanning the image is not part of this
package — point any QR reader (phone app, zbar, pyzbar, OpenCV…) at the
receipt and feed the resulting text in.
Python API
import einvoice_tw
left = "AB112233441020523999900000144000001540000000001234567ydXZt4LAN1UHN/j1juVcRA==:**********:3:3:0:乾電池:1:105:"
right = "**口罩:1:210:牛奶:1:25" # the second QR code, always starts with **
inv = einvoice_tw.parse(left, right) # order does not matter; right is optional
inv.number # 'AB11223344' inv.track 'AB', inv.serial '11223344'
inv.date # datetime.date(2013, 5, 23) (converted from ROC 1020523)
inv.random_code # '9999'
inv.sales_amount # 324 (未稅銷售額, 0 when the issuer did not separate it)
inv.tax_amount # 16 (None when sales_amount is 0)
inv.total_amount # 340
inv.buyer_ubn # None (consumer) or '22099131'
inv.seller_ubn # '01234567'
inv.items # [Item(name='乾電池', quantity=Decimal('1'), unit_price=Decimal('105')), ...]
inv.items[0].amount # Decimal('105')
inv.items_total # 3 how many items the whole invoice has
inv.is_complete # True the QR codes contain every item
inv.period # '10206' lottery period (102年05-06月), inv.period_label
inv.barcode # '10206AB112233449999' content of the Code 39 barcode
inv.encoding # 'big5' | 'utf-8' | 'base64'
inv.to_dict() # JSON-ready dict with everything above
inv.to_json()
inv.item_rows() # one flat dict per item -> csv.DictWriter / pandas.DataFrame
print(inv) # readable summary
einvoice_tw.parse_barcode("*10404UZ176908720122*") # Barcode(period='10404', number='UZ17690872', random_code='0122')
einvoice_tw.parse_any(code1, code2) # auto-detects barcode / left / right
Lottery (統一發票對獎)
from einvoice_tw import WinningNumbers, check
numbers = WinningNumbers(
period="10206", # 102年05-06月
special="12345678", # 特別獎 1,000 萬
grand="87654321", # 特獎 200 萬
first=["11223344", "55667788", "99887766"], # 頭獎 (prizes 2-6 by suffix)
additional=["007"], # 增開六獎 (3 digits), optional
)
check(inv, numbers) # Prize(name='頭獎', name_en='First Prize', amount=200000, matched='11223344', digits=8)
check("AB00000344", numbers) # Prize(name='六獎', amount=200, matched='344', ...)
check("AB00000000", numbers) # None
Passing a parsed Invoice (or Barcode) also verifies that it belongs to the
same period as the winning numbers, raising PeriodMismatchError otherwise
(verify_period=False to skip). WinningNumbers.from_json("11306.json")
loads the same fields from a file.
統一編號 checksum
einvoice_tw.is_valid_ubn("04595257") # True — implements the rule in force since 2023-04
Command line
einvoice-tw parse "<left QR text>" "<right QR text>" # readable summary
einvoice-tw parse --json "<left QR text>" # JSON
einvoice-tw parse --csv < scans.txt > items.csv # one CSV row per item, many invoices
einvoice-tw parse "*10404UZ176908720122*" # barcode
einvoice-tw check AB11223344 --period 11306 \
--special 12345678 --grand 87654321 --first 11111111 22222222 33333333 --additional 123
einvoice-tw check --numbers 11306.json < scans.txt # numbers from JSON; exit code 1 if any could not be checked
einvoice-tw ubn 04595257 12345678 # validate 統一編號
stdin takes one scanned code per line; a line starting with ** is the
right QR code of the invoice next to it, so you can dump a whole scanning
session into one file and convert it in one go. --json prints an object
when a single invoice is given as arguments and a list in every other case
(stdin input always yields a list), so scripts get a predictable shape.
What it handles
- The full left-QR header (77 characters): invoice number, ROC date, random code, hex sales/total amounts, buyer and seller UBN, encryption field — then the seller area, item counts, encoding flag and the items.
- All three item encodings: Big5 (
0), UTF-8 (1, and3for cross-border e-commerce) and Base64 (2) — including Base64 that is encoded per code (as in the official examples) or encoded once and split across both codes, with or without padding. - Items split across the two codes, trailing colons, leftover fields,
incomplete item lists (
items_in_qr<items_total), decimal quantities, thousands separators, and non-numeric quantities (kept as text initem.raw). - Real-world sloppiness in the default lenient mode: swapped codes,
lowercase track letters, a seller area shorter than 10 characters, a right
code missing its
**, invalid Base64.strict=True/--strictrejects all of these. - Mojibake repair: Big5/UTF-8 bytes that a scanner decoded as Latin-1
(
°®¹q¦Àor乾黿±) are restored to乾電池automatically — only when the result actually contains CJK, so genuine30°CorCaféstay as they are (fix_mojibake=False/--no-fixdisables it). - 統一編號 helpers:
is_valid_ubn()rejects malformed numbers, the00000000placeholder and non-ASCII digits. - Lottery rules: 特別獎, 特獎, 頭獎 and the 7- to 3-digit suffix prizes (二獎 to 六獎), 增開六獎, best prize across all 頭獎 numbers, period check.
Notes
- The 24-character
encryptfield is AES-encrypted with a key only the issuer and the Ministry of Finance hold; it is exposed verbatim but cannot be verified here. sales_amountis frequently0on B2C receipts because many issuers do not separate tax for consumers;total_amountis always filled.- Winning numbers are not downloaded automatically (the official API needs a registered app ID). Type them in or keep them in a small JSON file.
- Specification: 財政部財政資訊中心《電子發票證明聯一維及二維條碼規格說明》.
Development
git clone https://github.com/Aaron-lab-c/einvoice-tw.git
cd einvoice-tw
pip install -e . pytest
pytest
Releases are published to PyPI automatically by GitHub Actions when a v*
tag is pushed (see .github/workflows/publish.yml).
Bug reports and ideas: https://github.com/Aaron-lab-c/einvoice-tw/issues
License
MIT © ARON
中文說明
解析台灣電子發票證明聯上的 QR code 與一維條碼,並支援統一發票對獎。 純 Python、零相依套件,依財政部《電子發票證明聯一維及二維條碼規格說明》實作。
每張電子發票下方的兩個 QR code 裡藏著發票號碼、日期、隨機碼、銷售額、總計、買賣方統編和品項明細。
einvoice-tw 把掃描器讀出來的文字變成乾淨的 Invoice 物件、JSON 或 CSV,拿來做記帳 App、報帳匯入、發票存檔、自動對獎都很方便。
安裝
pip install einvoice-tw
掃描影像不在本套件範圍:用手機、zbar、pyzbar、OpenCV 等任何 QR 讀取器把文字讀出來後丟進來即可。
Python 用法
import einvoice_tw
inv = einvoice_tw.parse(左邊QR文字, 右邊QR文字) # 右邊可省略,順序顛倒也會自動修正
inv.number, inv.date, inv.random_code # 'AB11223344', date(2013, 5, 23), '9999'
inv.sales_amount, inv.tax_amount, inv.total_amount
inv.buyer_ubn, inv.seller_ubn # 買方統編(一般消費者為 None)、賣方統編
inv.items # [Item(name='乾電池', quantity=Decimal('1'), unit_price=Decimal('105')), ...]
inv.period # '10206' → 對獎期別 102年05-06月
inv.to_json() # 全部欄位的 JSON
inv.item_rows() # 每個品項一列,可直接丟 csv / pandas
einvoice_tw.parse_barcode("*10404UZ176908720122*") # 一維條碼:期別 + 發票號碼 + 隨機碼
對獎:
from einvoice_tw import WinningNumbers, check
本期 = WinningNumbers(period="11306", special="12345678", grand="87654321",
first=["11111111", "22222222", "33333333"], additional=["123"])
check(inv, 本期) # Prize(name='頭獎', amount=200000, ...) 或 None
check("AB00000123", 本期) # 也可以直接給發票號碼
給 Invoice 時會順便檢查期別是否相符,不符會丟 PeriodMismatchError。
中獎號碼不會自動下載(財政部 API 需要申請 AppID),請自行輸入或存成 JSON 用 WinningNumbers.from_json() 載入。
指令列
einvoice-tw parse "<左QR>" "<右QR>" # 易讀摘要
einvoice-tw parse --json "<左QR>" # JSON
einvoice-tw parse --csv < 掃描紀錄.txt > 品項.csv # 一次轉很多張,每個品項一列
einvoice-tw check AB11223344 --period 11306 --special ... --grand ... --first ... ... ...
einvoice-tw check --numbers 11306.json < 掃描紀錄.txt
einvoice-tw ubn 04595257 12345678 # 統一編號檢查碼(含 2023 年新規則)
stdin 一行一個掃描結果,以 ** 開頭的行會自動接到上一行的發票。
特色
- 完整解析 77 碼固定表頭、營業人自行使用區、品目筆數與中文編碼參數。
- 支援 Big5、UTF-8(含境外電商的
3)、Base64 三種編碼;Base64 不論左右各自編碼或整段切開都能還原。 - 容錯:左右 QR 順序顛倒、小寫字軌、自行使用區長度不足、右 QR 缺
**、Base64 損壞;--strict可改為嚴格模式。 - 自動修復掃描器把 Big5 / UTF-8 當成 Latin-1 解出來的亂碼。
- 對獎涵蓋特別獎、特獎、頭獎、二獎至六獎(末 7 至 3 碼)、增開六獎,並檢查期別。
MIT 授權。問題回報與建議:https://github.com/Aaron-lab-c/einvoice-tw/issues
Metadata
Release files for einvoice-tw 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| einvoice_tw-0.1.0.tar.gz | 29.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| einvoice_tw-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 55.3 kB
Release files / einvoice_tw-0.1.0.tar.gz
| Download URL | einvoice_tw-0.1.0.tar.gz |
|---|---|
| Size | 29.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3bbbf147b1f1afe9c1b872ee84331b8bce6926c75f53bcd9e82143052682538d
|
|
BLAKE2b-256 checksum How to use checksums |
040e8d7fc100b6a0225c43b5218788857b4d60f748565cb8212eaf478d37dddb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.
Transparency logRelease files / einvoice_tw-0.1.0-py3-none-any.whl
| Download URL | einvoice_tw-0.1.0-py3-none-any.whl |
|---|---|
| Size | 25.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
78165b50bd085647bbb3b56c7b69565d79d642a85cbafcda77aee2e4b3e8d569
|
|
BLAKE2b-256 checksum How to use checksums |
0c9a5301a457194c0b7fb05a2ed835b38eff0cdef648142aa8a0d9a888338bb1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.
Transparency log