銀行口座の残高管理をGolangで自動化した


# 問題点

もうだいぶ前の話ですが，社会人になると銀行の預金残高を確認するのが面倒だと思いました．

- 通帳の記帳が面倒
  - 最後に通帳見たの何年前だろう…
- 口座が増える
  - 会社に銀行を指定されたり，証券会社の口座欲しかったり，手数料が安い銀行の口座が欲しかったり．．．
- ネットバンキングのサイトのUIが凶悪
   - 今なら MoneyForward.com などを使うのもありかも？
   - とはいえ，可視化したいわけじゃないし，パスワードとか情報預けるのもなんだかな感がある
- 数字を見ると増やしたくなる
  - そして雑に投資とかしてすぐ飽きるパターン

(そして会社のLTで発表したネタの再利用記事です)

# 自動化する

やりたいのは以下のことです．

- 給料が振り込まれたら各口座へお金を移動する
- カードの引き落とし口座やATMで現金を引き出す口座の残高が一定範囲に収まるように定期的に調整する
- 口座の取引履歴をスプレッドシートに保存する

銀行が自動送金機能を用意している場合もありますが，今回の用途には「これじゃない」感があったので送金も自前で管理することにしました．

## 作った

Golangで各銀行(みずほ，楽天，新生，SBI)のネットバンキングサイトをスクレイピングするライブラリと自動的に送金を行うツールを書きました．

- ライブラリ: https://github.com/binzume/gobanking
  - 残高・履歴の取得，登録済み口座への送金が出来ます． みずほ，楽天，新生銀行，SBI(予定)に対応．
- 自動化ツール: https://github.com/binzume/bankautomation
  - Googleスプレッドシートへの記帳や，残高を条件にして自動的に送金したりSlackへの通知をします
  - (ほぼ自分のユースケース専用の実装．．．)

送金先が登録済み口座しか対応してないのは，事前に登録しておくと大抵の銀行で認証が簡略化されるためです．

ブラウザとか無い環境でも実行できるように，Pure Golang(JavaScriptエンジンやヘッドレスブラウザに依存してない)です．
ヘッドレスブラウザ使うのが無難な気がしますが，負けた気になるので避けました．
正規表現で雑に処理してるので稀に動かなくなるかもしれませんが，銀行のサイトは滅多に更新されないのであまり問題ないです．

### 利用例

![image.png](https://qiita-image-store.s3.amazonaws.com/0/58515/195576bc-741f-30f3-5a02-a5d7813d5127.png)


1. 会社の給与振込口座が銀行Aなので，給与が振り込まれると一定の残高を残して銀行Bに送金します
2. 銀行Bから更に他の銀行に送金
3. 銀行A,Bの残高が少なくなると補充します

楽天銀行が真ん中にあるのは，月３回まで他行あての振込手数料が無料なのと，ログイン難易度が一番低いからです．
最初は，月10回まで振込手数料が無料な新生銀行を経由していたのですが，ブラウザからログインする難易度が高いので間に楽天銀行を挟むように変更しました．

## ライブラリ

https://github.com/binzume/gobanking

テストは無いに等しいですが，自分の管理下に無いサイトをスクレイピングする処理のテスト書いても無駄なので，実際の実行結果を監視する方針．

### ログイン

jsonファイルを使ってログインする場合．

```go
import "github.com/binzume/gobanking"

func main() {
	acc, err := banking.LoginWithJsonFile("account.json")
	if err != nil {
		log.Fatal(err)
	}
	defer acc.Logout()
	// ...
```

```json:account.json
{
  "bank": "mizuho",
  "id":"1234567890",
  "password": "PASSWORD",
  "options": {
		"質問の部分文字列1": "答え1",
		"質問の部分文字列2": "答え2"
	}
}
```

直接呼ぶ場合は，銀行名のパッケージ下の関数でログインできます．銀行ごとに引数が異なります．

```go
package main

import "github.com/binzume/gobanking/mizuho"

func main() {
	words := map[string]string{
		"質問の部分文字列1": "答え1",
	}
	acc, err := mizuho.Login("1234567890", "password", words)
	if err != nil {
		log.Fatal(err)
	}
	defer acc.Logout()
	// ...
	// T.B.D.
}
```


### 例：残高の取得

```go
	// Print balance.
	total, err := acc.TotalBalance()
	if err != nil {
		fmt.Println("TotalBalance error.", err)
	}
	fmt.Println("Balance:", total)
}
```

完全な実装はexsamples以下のサンプルを参照してください．

```bash
cd examples
go run account_info.go account.json
```

### 例：送金する

事前にネットバンキングのサイト上で送金先が登録されている必要があります．

```bash
cd examples
go run transfer.go account.json binzume 5000000000000000
```

5000兆円とは言わないので誰かください

## 銀行系サイトのスクレイピング面倒

- jsで値が埋め込まれている
- ワンタイムトークン
- html壊れているとか

あとは銀行のhtmlやjsを眺をめていると，たまに面白い物があります．

- 新○銀行： scriptタグが不思議な場所に埋め込まれている(`<html>`～`</html>`の外とか)．そもそもログイン時に全情報を入力させるの何なのか
- み○○銀行： jsでRSA暗号化している．haraidashi (=払い出し？). 一見PKCS#1っぽいけどMGFの実装がなんか不思議なような……

## セキュリティ

とりあえず妥協します．

お金周りを全部握ってるプロセスとか，アカウント情報が書かれたファイルとかを許容する．
「最悪でもお金が無くなるだけ」の気持ちでやっていく．

~~便利そう~~不思議な挙動をする銀行がある気もするけど気軽に変な操作すると怒られそうだなと思って放置．


# 紆余曲折

実際に自動化しはじめたのは2010年頃なので，もう7年以上，色々変えながら動かしています．

最初は，

- Rubyで実装
- 通知先は，Twitter
- 残高の記録先は，はてなグラフ

でした．

### Rubyのライブラリに切り出そうとした残骸

- https://github.com/binzume/mizuhodirect-ruby
- https://github.com/binzume/shinseibank-ruby
- https://github.com/binzume/rakutenbank-ruby
- https://github.com/binzume/sbi-ruby

ほんとはgemとかにしたい気がていたけど，年単位で放置してるので，たぶんもう触ることないです．
Golang版に無い機能として，新生銀行の投資信託の売買が出来たりしますが，最近試したら動かなくなっていた．．．
誰かがforkしてメンテを試みてくれた形跡を見ると非常に申し訳ない気持ちになる…．

### 通知

最初はTwitterのメンションで通知していました．

![notify-twitter.png](https://qiita-image-store.s3.amazonaws.com/0/58515/112d9586-99e6-d5fd-6557-7edcbdda719b.png)

「キユウヨ」と「シヨウヨ」の振込がすぐ分かるので良いですが，DMにしなかったので誰でも見れてました．．．

#### → SlackのDMにしました

振込のたびに通知されると邪魔なので，大きな変化があったときだけ通知するようにしています．

## 残高の可視化

はてなグラフで残高の推移を記録．

![hatena-graph.png](https://qiita-image-store.s3.amazonaws.com/0/58515/56d531dc-c586-bb56-5869-d15fd7ca5637.png)

はてなグラフに公開するのが(ごく狭い範囲で)流行っていたので便乗した形．
残高の推移が一目瞭然で良い．

#### →Googleスプレッドシートに集約

![spreadsheet.png](https://qiita-image-store.s3.amazonaws.com/0/58515/b014cfc0-5e37-9f13-8a81-465686bbf924.png)

だいたい良い感じになりました．

# トラブルあれこれ

基本的には調子よく動いていますが，7年間くらい運用していて遭遇したトラブル．

## お金の流れがいつの間にか止まっている

み○ほダイレクトのシステムの更新
→ スクレイピング失敗
→ 送金が止まる
→ クレジットカードの引き落とし失敗しそうになっていた

１ヶ月以上気づいていませんでした．システムの監視は大事だと痛感しました．

成功のログが一定期間無い場合や残高がおかしいときはSlackで通知するようにしました．
最近は，[Prometheus](https://prometheus.io/) + [Grafana](https://grafana.com/)で色々監視するようにしているので，残高の変化が正常か監視して問題があったらアラートが飛ぶようにもしています．

## お金がどこかに消える

振り込まれても残高が増えない．振込側では成功している．そして残高のチェックで後日に再実行の無限ループに陥りそうになる．

み○○銀行から電話
「資金洗浄対策のため振込を一時的に預かっています．振込内容を説明できるものを持って窓口にお越しください」(要約)

当時PayPal使ってたせいだった．PayPalを経由したお金はチェックされる場合があるらしい．

## ネットバンクにログインできなくなる

何だろう？と思ったら，○ず○銀行からメールが届いていました．
「緊急の要件があるので窓口へお越しになるか担当者まで電話で連絡をお願いします」(要件の内容は何も書いてない)

給与振込口座が使えないとちょっと困るので電話する．

「不正アクセスの可能性があったため，アカウントを凍結しました」らしい．

残高がいくらくらいか，○月○日にどういう操作をしたか，とか色々と質問される．
(数時間おきに何かしらのバッチ処理実行されてるし，ログ見ても正直どれの事かあまりはっきりしない．．．)

海外のプロキシとかVPNとか使ってる人が誤検知される場合があるらしいです．
インターネットとか詳しくないし，プロキシとかVPNとかよく分からないですが，知らないうちに使ってたかもしれない……

とりあえず解除してもらえました．

# 感想

- 預金残高を意識することがほとんどなくなった
  - 最初は可視化を意識してグラフ作ったりしていたけど，最終的に最低限のアラートが飛ばない限り見ない運用にした
- ATMで現金を引き出すとき，いつも同じような残高なのは良い
  - 勝手に補充されるのでお金が無限に湧いてる感じになる (トータルで 支出＜収入 な生活を送っている限りは)
- 色々面倒くさいのでAPI整備して欲しい
  - そもそも，お金が絡むと面倒が増えるのを根本的にどうにかしたい