Go言語からPixiv Boothの売上をfreeeに登録

この記事ではPixiv Boothの売上情報をスクレイピングで取得し、売上情報を会計freeeサービスのAPIを利用し登録する流れを解説したいと思います。まだ連携先として追加されていない他のECサービス等を利用している方の参考にもなるかと思います。

同人誌の販売サイトとして非常に人気が高いECサイトPixiv Boothですが、マイナーなのか売上情報の自動取り込みに対応している会計ソフトはざっと調べた所存在しないようです。Pixiv Booth上では注文金額が乗ったCSVをダウンロードする機能が提供されていますが、サービス利用手数料などの詳細情報は取得できず、会計ソフトに登録するためにはかなりの手作業が必要となっていました。APIが提供されていると知ったので、この機会に自動でfreeeへと取り込むツールを作成してみました。

成果物はこちらに掲載しています。

https://github.com/kamijin-fanta/freee-api-example

主な実装箇所はこちらです。

https://github.com/kamijin-fanta/freee-api-example/blob/master/cmd/freee-booth-exporter/freee-booth-exporter.go

## 2021-05-12追記

公式でGoのクライアントライブラリがLayerXより公開されたので、この記事の方法のようにクライアントを自作する必要はなくなりました :tada:

https://tech.layerx.co.jp/entry/2021/05/12/110000


## freeeでの収支の入力方法を理解する

まず普段会計に触れていないエンジニアなので、基礎的な部分から整理していきます。

Pixiv Boothは、基本的に月末締め・翌月20日支払いとなっています。1ヶ月分の売上分が振り込まれたタイミングで合計金額を会計システムに入力出来るなら楽なのですが、日本の法律はそうはなっておらず、収入・費用が発生したタイミングにて計上を行うという事になっています。ですので、売上等発生する度に毎日帳簿に記載する必要があるようです。売上は銀行のようにPixiv Boothの口座に貯められ、それを翌月20日に自分の口座に振替するという風に扱うようです。

![2020-12-12-freee-pixiv.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61226/62190683-53a3-f17d-5da2-f02aae5a9763.png)

既にfreeeにて連携機能が実装されているSquareという決済サービスでの取り込み内容を参考にPixiv Boothでの注文履歴ページからfreeeの取引内容をWeb UIにて入力してみます。参考までに注文履歴ページはこのような表示になっています。

![2020-12-12-freee-pixiv_2020-12-13-00-20-59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61226/cb174269-607a-3c72-1f0f-20f6fac33cf5.png)

取引入力の前に、Pixiv Boothの残高を登録するための口座を作成する必要が有ります。「決済サービス・電子マネーを登録する」→「利用しているPOSレジ・ESサイト（出店）・決済サービスが見つからない場合」と遷移することで、手動で入力が可能な空の口座が作成できます。

![2020-12-12-freee-pixiv_2020-12-13-00-47-27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61226/5c742709-c743-5d2d-e015-d57925c349a4.png)

freeeでの収支入力の例です。

![2020-12-12-freee-pixiv_2020-12-13-00-18-22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61226/806d4de7-acd2-da37-9062-98810c700e3b.png)

今回の例は本1冊500円の売上があり、それに対して18円の手数料が運営側に差し引かれています。収入で売上高として500円を入力し、マイナス収入として手数料の18円を差し引いています。そして、その合計金額482円と同額をPixiv Boothの口座で受け取りします。また、備考欄にてPixiv Boothでの注文IDを記述することで、複数回実行しても重複がないように制御しようかと思います。

さて、これから取引情報をAPI経由で投げ込んでいきます。

参考資料

- Square （スクエア） - 売上データを取り込む https://support.freee.co.jp/hc/ja/articles/202847590
- 【会計の基礎知識】発生主義・現金主義・実現主義の関係 https://www.freee.co.jp/kb/kb-accounting/accounting-principles/


## freee会計APIを呼び出す

APIを呼び出すまでの手順はこちらで解説されているので、登録方法等は割愛したいと思います。開発者アカウントは無料で登録することができ、一定期間毎にデータが消去されるようです。登録が完了すると事業者IDとアクセストークンが画面に表示されるので控えておきます。

freee API スタートガイド https://developer.freee.co.jp/getting-started

上記のガイドに書かれているように、利用可能なAPIはOpenAPIで定義されておりSwaggerUIにて手軽に試すことが出来ます。また、公式でC#とJavaのSDKが提供されているようです。

試しに実際に先程Web UIで入力した項目をAPI経由で投げ入れてみましょう。以下のようなペイロードを投げれば取引一覧ページに追加されていることが分かると思います。

```jsonc
// POST /api/1/deals
{
  "issue_date": "2020-02-23",
  "type": "income",
  "company_id": 2686190,
  "details": [
    {
      "tax_code": 129,                            // 課税売上10%の税区分ID
      "account_item_id": 429799547,               // 売上高の勘定科目ID
      "amount": 500,                              // 売上金額
      "description": "pixivBoothOrder=123456789"  // 注文IDを記載しておく
    },
    {
      "tax_code": 136,                            // 課対仕入10%の税区分ID
      "account_item_id": 429799593,               // 販売手数料の勘定科目ID
      "amount": -18                               // 手数料・金額にはマイナスが使える
    }
  ],
  "payments": [
    {
      "amount": 482,                              // 受け取る金額
      "from_walletable_type": "wallet",
      "from_walletable_id": 123456789,            // Pixiv Boothの残高用に作成した口座ID
      "date": "2020-02-23"
    }
  ]
}
```

- 会計APIリファレンス Version: 2020-06-15 https://developer.freee.co.jp/docs/accounting/reference
- 会計freee API 用のSDKをリリースしました。 https://developer.freee.co.jp/release-note/1920


## Goからfreee APIの呼び出し

残念ながらGoのfreee APIクライアントは公式で提供されていないので、OpenAPIの定義ファイルからコードを自動生成してクライアントを作成します。普段利用しているoapi-codegenにてコード生成を試みたもののエラーが発生してコード生成できなかったので、openapi-generator-cliを利用することにしました。こちらはJava環境で動作するのですが、手元PCにJavaを入れていないのでDockerを利用してコード生成を行いました。以下のようなコマンドで生成が可能です。

```shell
docker run --rm \
  -v `pwd`:/app \
  openapitools/openapi-generator-cli generate \
  -i https://raw.githubusercontent.com/freee/freee-api-schema/master/v2020_06_15/open-api-3/api-schema.json \
  -g go \
  -o /app/pkg/freee \
  --additional-properties=packageName=freee,isGoSubmodule=false
```

ただOpenAPI定義との相性が悪いのか生成したコードをそのままコンパイルすることは出来なかったので、少々生成後のパッケージに手を加えています。型名が誤って生成されていたので、エイリアス型を作成したファイルを置きました。

では実際に生成されたクライアントを利用してみます。このコードでは、利用可能な事業所の一覧を表示しています。非常に手軽に利用できることが分かると思います。一部requirementが指定されていないフィールドがNullableとなってしまい少々扱いが面倒ですが大きな影響は無いでしょう。

```go
apiConfig := freee.NewConfiguration()
client := freee.NewAPIClient(apiConfig)
ctx := context.WithValue(context.TODO(), freee.ContextAccessToken, "accessToken")

res, _, err := client.CompaniesApi.GetCompanies(ctx).Execute()
if err != nil {
  panic(err)
}

fmt.Printf("get %d company\n", len(res.Companies))
for _, company := range res.Companies {
  name := ""
  if company.DisplayName.IsSet() {
    name = *company.DisplayName.Get()
  }
  fmt.Printf("  id=%d name=%s role=%s\n", company.Id, name, company.Role)
}
```

## Pixiv Boothの注文情報をスクレイピング

Pixiv Boothの注文一覧ページ・注文詳細ページをスクレイピングして、注文日・注文価格・手数料・注文IDを取得する必要が有ります。今回はGo言語のchromedpライブラリを使用します。これはChromeを自動操作した上で任意のJavaScriptの実行してGoで値を受け取ることが出来ますので、スクレイピングの用途に非常に適しています。

https://github.com/chromedp/chromedp

Googleの検索を行うサンプルコードがこちらに掲載されていますが、 `chromedp.Navigate(url)` でページの遷移・ `chromedp.WaitVisible(selector)` で要素を待機・ `chromedp.SendKeys(selector, inputs)` でキー入力等、分かりやすいコードでChromeを自動操作出来ることが分かると思います。

https://github.com/chromedp/examples/blob/master/submit/main.go

コード上では以下のような事を行っています。

1. Pixiv Boothへのログイン
2. 注文一覧ページへ移動
3. 注文詳細画面のURLを収集する
4. 注文詳細画面を順に巡回する
5. 各注文について注文金額・サービス利用手数料・取引日・注文IDをJavaScript実行によって取得する

また、最初にPixivの利用規約をざっと確認しましたが、スクレイピングでの情報収集を禁じる項目は無いようでした。以下のような記述がありましたが、今回は作品を収集しない・通常のブラウザを利用した低頻度なアクセスのため、規約に反さないと判断しました。

> クローラーなどのプログラムを使って作品を収集する行為、サーバに極端な負荷をかける行為は禁止します。また、それらに違反しない場合でも、当社はその停止を要求する場合がございます。
> https://policies.pixiv.net/


## freeeへ取引登録する

Pixiv Boothより注文情報が取得できたので、あとは取引の登録をするだけです。先程の項目で作成したOpenAPIのクライアントを作成し、SwaggerUI上で試したJSONフィールドを再現します。

```go
// 取引登録を行う
params := freee.DealCreateParams{
	IssueDate: order.Date,
	Type:      "income",
	CompanyId: companyId,
	Details: []freee.DealCreateParamsDetails{
		// 売上の収入登録
		{
			TaxCode:       129,       // 課税売上10%
			AccountItemId: 429799547, // 売上高
			Amount:        total,     // 合計金額
		},

		// サービス利用料金の支出登録
		{
			TaxCode:       136,       // 課対仕入10%
			AccountItemId: 429799593, // 販売手数料
			Amount:        -fee,      // 手数料の支払い
		},
	},
	Payments: &[]freee.DealCreateParamsPayments{
		{
			Amount:             total - fee,
			FromWalletableType: "wallet",
			FromWalletableId:   walletableId,
			Date:               order.Date,
		},
	},
}
_, _, err := client.DealsApi.CreateDeal(ctx).
	DealCreateParams(params).
  Execute()
```

リクエスト後にfreeeのWeb UIを確認すると正常に登録されていることが確認できます。

![2020-12-12-freee-pixiv_2020-12-13-10-09-55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61226/130fbc79-1439-5958-5763-7fe835b88c4c.png)


## 所感

今回はじめて会計ソフトを触ったのですが、1日程度で取引履歴を自動入力するコードを記述できました。こういった業務アプリケーションでAPI提供されているのは珍しく感じるので好感が持てます。以下は利用している中で感じた事です。


- OpenAPI周り
  - 公式のSwagger UIではモデルなどの一部の上が表示されていないので、フィールドに入れるべき値が分かりにくい・自分でJSONをSwagger UI入力して内容を確かめた
  - フィールドの扱い方(from_walletable_typeの値やマイナス収支の入力方法等)はドキュメント化されておらず手探りで記述した・Web UIのAPIと公開APIは内容が微妙に異なり混乱した
  - 取引履歴の検索は、APIよりUI上の方が豊富なオプションが用意されている
  - openapi-generator-cliで生成される事を前提に書かれたOpenAPI定義ファイルではなかったので、少し記述が難しい箇所が多かった　理由がない限り公式SDKを使ったほうが良さそう
- 全体
  - 会計知識がないと少し手間取るかなと感じる
  - 仕分け形式プレビューも表示されて面白い


権利情報

- 図に利用しているアイコンはFont AwesomeによってCC BY 4.0 Licenseで提供されています https://fontawesome.com/license/free