2026年5月9日7 分鐘電子發票 · Turnkey · 財政部 · MIG 4.1 · B2B · 系統整合 · Electron · MSSQL

台灣電子發票 Turnkey 4.1 串接的 4 個坑 — 監控目錄不夠用

台灣財政部 Turnkey 4.1 是 B2B 電子發票的事實標準,但實際整合會發現「寫到 SRC 目錄」這條最直覺的路只能做到「送出去」,做不到「知道平台回什麼」。這篇拆 4 個我們踩過的坑跟解法。

陳先生 (Henry)

「我們有套 Web ERP,B2B 開票想自動化 — 員工現在每天手動匯出再丟 Turnkey。」

這幾乎是每個導入 ERP 自動化的客戶都會丟過來的需求。

聽起來簡單:把 XML 寫到 Turnkey 的 SRC 目錄、Turnkey 自己會排程上傳、結束。但實際做下去就會發現 — 「寫到 SRC」只是第一步,做完才知道平台收到沒有、有沒有錯、什麼時候才會送、出錯了拿不拿得到中文錯誤訊息。

這篇把我們做了幾套 B2B 電子發票整合(攤位 POS、總部後台、Electron 桌面 App)踩過的 4 個坑寫出來。寫給準備自己 hack Turnkey 整合的工程師看 — 少踩兩個就值。

先 1 分鐘理解 Turnkey 是什麼 #

Turnkey 是財政部給 B2B / B2C 開立電子發票的標準上傳工具,目前主流版本是 MIG 4.1

  • 跑在 Windows,是 Java 程式(java.exeTurnkeyCmd
  • 設定存在 MSSQL(資料庫名通常叫 EInvoiceTurnKey
  • 寫 XML 到指定目錄 → 它按排程 Pack 成 zip → 上傳到財政部 → 平台回 ProcessResult → 它再下載解開

四種主要 XML 訊息類型(B2B 部分):

Type 用途
F0401 一般稅額發票
F0501 一般稅額發票作廢
G0401 折讓單
G0501 折讓單作廢
E0402 空白未使用發票(期末必交)

OK 背景清楚了。接下來進坑。

坑 1:寫到 SRC 子目錄不夠 — 要走 B2SSTORAGE 完整 4 層 #

最常見的天真做法:

E:\EINVTurnkey\XML\UpCast\F0401\SRC\F0401_xxx.xml

寫進去等 Turnkey 排程 Pack。但這條路有時候會卡住 — 因為 MIG 4.1 的 Pack 邏輯預期你的 XML 是先放到「B2SSTORAGE 完整分層」下:

E:\EINVTurnkey\XML\UpCast\B2SSTORAGE\F0401\SRC\F0401_xxx.xml
E:\EINVTurnkey\XML\UpCast\B2SSTORAGE\F0501\SRC\F0501_xxx.xml
E:\EINVTurnkey\XML\UpCast\B2SSTORAGE\G0401\SRC\G0401_xxx.xml
E:\EINVTurnkey\XML\UpCast\B2SSTORAGE\G0501\SRC\G0501_xxx.xml

注意這層 B2SSTORAGE(B2B 存證類型),跟 B2BEXCHANGE(買賣方訊息交換)、B2PMESSAGE(平台訊息)是平行的三個 CATEGORY。只有 B2SSTORAGE 是「開票實際上傳」用的,其他兩個是訊息交換、不要混。

寫對位置之後,Pack 才會把它包進去。

坑 2:監控 BAK 目錄只能知道「送出去」— 看不到「平台回什麼」 #

第二個常見天真做法:用 chokidar / FileSystemWatcher 監控輸出目錄:

SendFile\B2SSTORAGE\BAK\... → 看到檔案 = 上傳成功

問題:這只代表「Turnkey 把檔案送出去了」,不代表「財政部平台收下了」,更不代表「平台檢查通過、發票合法」。

實際的完整 pipeline 是這樣:

寫入 SRC
   ↓ UpCast 處理
       ├─→ ERR → local_error(XML 格式錯,財政部沒看過)
       └─→ ↓ Pack
              ├─→ ERR → local_error(打包失敗)
              └─→ ↓ SendFile
                     ├─→ ERR → local_error
                     └─→ BAK → uploaded_pending(平台收到,等下回 ProcessResult)

下載
   ReceiveFile → Unpack → DownCast → RecvTarget(平台 ProcessResult)

ProcessResult 才是真相:發票通過 → processSuccess;失敗 → 有 ERRORCODE + MESSAGE1~6 中文錯誤敘述(例如「買方統編檢核碼錯誤」)。

要拿到這層資訊,光看 BAK 不夠 — 你得直連 Turnkey 的 MSSQL

坑 3:直連 Turnkey MSSQL 才能看到完整 4 桶狀態 #

我們最後的做法:用 mssql driver 連到 EInvoiceTurnKey 資料庫,撈 TURNKEY_MESSAGE_LOGTURNKEY_SYSEVENT_LOG

SELECT
  m.SEQNO,
  m.INVOICE_IDENTIFIER,   -- F0401BA80015223 + 20260508 (xml_type + invoice_no + date)
  m.STATUS,
  m.IN_OUT_BOUND,         -- O = 我們送出 / I = 平台回應
  m.UUID,                 -- 同批次共用
  d.TASK,                 -- UpCast / Pack / SendFile / Unpack / DownCast
  d.FILENAME,
  d.STATUS                AS DETAIL_STATUS,
  e.ERRORCODE,
  e.MESSAGE1 + e.MESSAGE2 + e.MESSAGE3 + e.MESSAGE4 + e.MESSAGE5 + e.MESSAGE6 AS ERROR_TEXT
FROM TURNKEY_MESSAGE_LOG m
LEFT JOIN TURNKEY_MESSAGE_LOG_DETAIL d ON d.SEQNO = m.SEQNO
LEFT JOIN TURNKEY_SYSEVENT_LOG e ON e.SEQNO = m.SEQNO
WHERE m.INVOICE_IDENTIFIER LIKE @prefix
ORDER BY m.SEQNO DESC;

一筆 SEQNO 同時會有 UpCast 進 BAK(我們送)+ Unpack ProcessResult(平台回)的紀錄,整個 pipeline 跟平台回應全串起來

拿到資料後,我們在 UI 把所有發票分成 4 桶

對應狀態 UI 怎麼處理
待上傳 還沒寫到 SRC 列表 + checkbox + 「上傳」按鈕
已匯出尚未上傳 在 SRC / Pack 中 顯示卡在哪個 task
已上傳 平台 ProcessResult 為 success 綠燈,可開電子發票證明聯
異常 任何階段 ERR 紅色 badge + ERRORCODE + 中文錯誤

這比「狀態欄位寫個 enum 5 值」實用太多 — 使用者一眼看到的就是「我現在要處理什麼」,不是抽象狀態碼。

坑 4:等排程太慢 — spawn run_start.cmd + 暫改 SCHEDULE_CONFIG 強制觸發 #

預設 Turnkey 排程是 1 小時跑一次。對員工開完一張 B2B 發票要等 1 小時才上傳 — 不能接受。

直接改 Turnkey 排程設定會破壞它,業主下次裝更新也會被覆蓋。我們的做法:暫時改 + 用完還原

完整流程(在 driver 層做 try/finally 包好):

// 1. 偵測 Turnkey 是否在跑(避免重複 spawn)
const isRunning = await detectTurnkeyProcess(); // tasklist | grep java.exe + TurnkeyCmd

if (!isRunning) {
  // 2. 備份原始排程
  const originalSchedule = await mssql.query('SELECT * FROM SCHEDULE_CONFIG WHERE TASK = @t');

  try {
    // 3. 改成 FIX TIME (馬上跑一次)
    await mssql.query(`
      UPDATE SCHEDULE_CONFIG
      SET TYPE = 'FIX', FIX_TIME = @now
      WHERE TASK = @t
    `);

    // 4. spawn 排程程式 detached
    spawn('E:\\EINVTurnkey\\run_start.cmd', [], { detached: true });

    // 5. tail log 等 "Job end" marker
    await waitForJobEnd('E:\\EINVTurnkey\\logs\\turnkey.log', { timeout: 60_000 });
  } finally {
    // 6. 不管成功失敗,都還原原始排程
    await mssql.query('UPDATE SCHEDULE_CONFIG ... SET ... WHERE TASK = @t', originalSchedule);

    // 7. 停 Turnkey
    spawn('E:\\EINVTurnkey\\run_stop.cmd');
  }
}

幾個關鍵點:

  • try/finally 是必要的。如果中途崩潰沒還原,下次正常排程會跑錯時間
  • 偵測 java.exe + TurnkeyCmd,不只看 process name — 那台機器可能還跑別的 Java 程式
  • run_start.cmd 要 detached spawn,不然你的程式結束 Turnkey 也會被連帶 kill
  • tail log 看 Job end,不要靠 sleep N 秒 — 慢機可能跑 30s、快機 5s

做完這套,員工開完發票 5 秒內就上傳,跟「即時開立電子發票」感覺一樣。

沒裝 Turnkey 的情況:CSV fallback 模式 #

不是每個客戶都裝 Turnkey。有的就 1 個月開 20-30 張 B2B 發票,裝一套 Java Turnkey + 後續維護不划算。

這時做第三條路:CSV 匯出 + 開啟財政部上傳頁

// 規格:BTB207W H/M/D 三層 + E0402,UTF-8 with BOM
exportCsv({
  format: 'BTB207W',
  invoices: pendingInvoices,
  output: `${appDataDir}/csv_export/F0401_${yyyymmdd}_${timestamp}.csv`,
});

// 開啟瀏覽器到財政部上傳頁
shell.openExternal('https://einv-platform.einvoice.nat.gov.tw/');

員工只要:點「匯出 CSV」→ 開瀏覽器 → 拖檔進去 → 完成。比印單據掃描上傳省 90% 時間,零 Turnkey 維護成本。

整套系統內部我們稱為「三模式上傳」:本機 Turnkey / 遠端 Turnkey(UNC + FTP)/ CSV 匯出。客戶想用哪種他自己選。

重點整理 #

# 解法
1 XML 寫到 UpCast\F0401\SRC\ 應該寫到 UpCast\B2SSTORAGE\F0401\SRC\ 完整 4 層
2 監控 BAK 以為上傳成功 BAK 只代表送出去,要看 ProcessResult 才知道平台收下
3 用 chokidar 監控檔案系統 直連 MSSQL TURNKEY_MESSAGE_LOG 才能拿完整 4 桶狀態
4 等排程 1 小時太慢 spawn run_start.cmd + 暫改 SCHEDULE_CONFIG,5 秒內上傳

客戶想評估 #

如果你正在做的 ERP 或 POS 要串電子發票,先別自己從零 hack Turnkey 整合 — 上面這些坑都會碰到,每個坑都吃掉 2-3 天 debug。

我們做過 Electron POS、Web ERP、總部後台 3 種不同型態的 Turnkey 整合。可以聊聊:

  • 評估你現在的系統適合本機 Turnkey / 遠端 Turnkey / CSV 哪一種
  • 直接接案做整合(依工時報價,諮詢免費)
  • 賣現成的 emobile-b2b 桌面 App(B2B 開票 + Turnkey 整合一條龍)

聯絡:0912852835 / henryccy@icloud.com / LINE @3q3tw / 線上表單

延伸閱讀:

想聊類似的東西?

諮詢免費,依工時報價。

聯絡我們