wechatpay: WeChat Bill Analyzer Turns Export Chaos Into a Private Financial X-Ray

A lean Electron app that finds the real table inside messy WeChat Pay spreadsheets, checks the totals, and turns local-only financial data into usable insight.

8 min read • View on GitHub • More from run-liyi

A wide editorial scene shows a cluttered spreadsheet being pulled through a magnifying lens on a desktop analyst's desk. On one side of the lens the file is tangled with metadata lines and misaligned rows. On the other side it resolves into a clean ledger and chart fragments, showing the idea of recovering structure from chaos.
The app’s core promise is not prettier charts. It is turning a hostile export into something trustworthy enough to analyze.
Key Takeaways

The export is the enemy

WeChat Pay gives you data, but not a clean analysis surface. The export arrives with metadata blocks, separator rows, locale quirks, and a table that only becomes useful after the app reconstructs its boundaries. That is why this repo feels less like a dashboard and more like a recovery tool.

The important move is philosophical. It does not trust the file just because it opened. It inspects the export first, then decides where the real dataset begins.

A close editorial scene shows a spreadsheet grid with noise rows, metadata fragments, and a visible transaction header being sorted into place. The image explains that the application does not start with clean data. It has to discover where the real table begins before analysis can happen.
The hard part is not rendering charts. It is separating the actual bill table from the export wrapper around it.

How the parser finds the real table

The repo’s most interesting move is a simple anchor search. In the parsing flow, the code scans the rows until it finds 交易时间, then treats that row as the real header. Everything above it becomes metadata to extract. Everything below it becomes candidate transaction data.

for (let i = 0; i < rawData.length; i++) {
  if (rawData[i][0] === '交易时间') {
    headerRowIndex = i;
    break;
  }
}

const metadata = extractMetadata(rawData.slice(0, headerRowIndex));
const billRows = rawData.slice(headerRowIndex + 1);

That split matters. It means the app can recover user identity fields, export timestamps, and summary totals before it cleans a single transaction row. Once the metadata is separated, the app can compare its own computed sums with the bill summary WeChat included in the file.

The whole pipeline is a state transition: discover the header, clean the rows, then trust the result enough to analyze it.

Why local-first changes the product

This app’s privacy story is not a marketing layer. It is the architecture. The data stays on the machine, which means users are not forced to hand financial history to a web service just to understand where their money went.

That changes the trust equation. A cloud finance product asks for faith up front. This one earns trust by never asking for the upload in the first place.

The repo’s small stack reinforces that promise. Electron handles the desktop shell, SheetJS handles spreadsheet parsing, Chart.js handles the visuals, and vanilla JavaScript ties the flow together. There is no extra platform to explain and no backend to defend.

The renderer is a controller, not just a view

The renderer does more than paint the screen. It manages view switching, stores the parsed bill data, and drives the analysis functions that feed the charts. In practice, it behaves like a lightweight client-side controller.

let billData = [];

function switchView(viewName) {
  // toggle overview, analysis, trend, and other panels
}

function updateViewData(data) {
  billData = data;
  analyzeOverview(billData);
  renderCharts(billData);
}

That design keeps the mental model tight. Parse once, store locally, and transform the same dataset into multiple views without sending it anywhere else.

Dirty data is the real edge case

The cleanup logic is where the app proves it understands reality. Empty rows, separator rows, and date formatting edge cases are not cosmetic problems. They are the reason many exports feel unusable in the first place.

A fragile parser can handle the happy path. A useful parser has to survive the junk between the useful rows.

What this tool is better than

WorkflowPrivacyEffortRobustnessTrust in totalsBest use case
Raw WeChat exportHigh, but unreadableLow to start, high to inspectPoorManual onlyGlancing at the file
Manual spreadsheet cleanupHighHighMediumDepends on the userOne-off investigation
Local Electron analyzerHigh and retainedLow after importHighAutomated verificationPrivate forensic analysis

The comparison makes the niche obvious. This is not a general accounting suite, and it is not trying to replace a bank dashboard. It is a local forensic tool for one specific export format, and that narrowness is its strength.

Why the stack is intentionally small

The stack is almost a product statement. Electron keeps the app cross-platform. SheetJS handles the ugly file format. Chart.js gives the analysis a visual layer without pulling in a framework. Vanilla JS keeps the control flow legible.

That restraint matters. For a utility like this, complexity is a liability unless it directly improves the trust chain from file import to verified analysis.