この記事では教育プラットフォームであるMentaの売上情報をダウンロードし、売上情報として会計サービスfreeeのAPIを利用し登録する流れを解説したいと思います。まだ連携先として追加されていない他のECサービス・収益化が行える配信サイト等を利用している方の参考にもなるかと思います。
Mentaとはプログラミング技術などを教える人・学習する人をマッチングするサイトです。私も1年以上メンターを行っており、累計50人近くの方に学習のサポートを行っています。現在調べてみたところ、このサイトの収益情報を自動登録できる会計ソフトは存在しないようなので、APIを通じてfreeeへ取引を登録するための簡単なCLIを作成しました。
成果物はこちらに掲載しています。
freeeでの収支の入力方法を理解する
Mentaは入金から30日以上経過した売上について出金の申請が行なえます。契約者毎に支払タイミングが異なり、毎月支払いが行われるので手動で入力するのはかなり手間です。売上が発生したタイミングでは会計ソフト上で売上を登録します。売上金については一旦Mentaへ銀行口座のように貯められるものと扱い、出金を行ったタイミングでMentaの口座から自分の口座に振替を行うという風に扱います。
売上に対しての手数料は20%に対して消費税を加算した22%で、出金の手数料は1回あたり300円必要です。
参考までにMenta上でダウンロードできる売上情報はこのようなCSVフォーマットでダウンロード可能です。
契約No,日付,ユーザー名,プラン名,招待割引,キャンセル,金額,手数料,売上額,出金日
11111,2021-09-24,Example User,"プラン名",,,1000,220,780,"2021-11-10 12:34:56"
22222,2021-10-24,Example User,"プラン名",,,1000,220,780,
取引入力の前に、Menta上の残高を登録するための口座を作成する必要が有ります。「決済サービス・電子マネーを登録する」→「利用しているPOSレジ・ESサイト(出店)・決済サービスが見つからない場合」と遷移することで、手動で入力が可能な空の口座が作成できます。
freeeでの収支入力の例です。
今回の例は1000円の契約に対しての売上があり、それに対して220円の手数料が運営側に差し引かれています。収入で売上高として1000円を入力し、マイナス収入として手数料の220円を差し引いています。そして、その合計金額780円と同額をMentaの口座で受け取りします。また、備考欄にてMentaでの注文IDを記述しています。
これから取引情報をAPI経由で投げ込んでいきます。一旦Web画面から入力しておくと、Developer Toolsでの通信内容などを覗くことで、APIで投げるJSONペイロードの形がわかりやすくなるのでオススメです。
freeeでOauth2認証を行う
APIを呼び出すまでの手順はこちらで解説されています。開発者アカウントは無料で登録することができ、一定期間毎にデータが消去されるようです。登録が完了すると事業者IDとアクセストークンが画面に表示されます。
freee API スタートガイド https://developer.freee.co.jp/getting-started
しかしながら、今回は開発者アカウントではなく個人事業主として作成した事業所でのAPI呼び出しを行いたいです。この場合はスタートガイドに記述されていない方法を利用する必要がありました。アカウント作成時に表示されたアクセストークンは一定時間で失効し、テスト環境の事業所でしか使えません。実際に本番利用するためには、Oauth2認証を利用し、ClientID,ClientSecretからアクセストークンを取得する必要があります。具体的には以下の方法があります。
- サーバサイドでの標準的な方法。HTTPエンドポイントを立てて、そこにコールバックを受けてアクセストークンを取得。
- 組み込みブラウザ等で認証を行う際の方法。認証後の画面に直接アクセストークンが表示され、それをコピペ/自動読み取り等で認識を行う。リダイレクトURLに
urn:ietf:wg:oauth:2.0:oob
を指定する。 https://app.secure.freee.co.jp/developers/tutorials/3
今回は手元で動くCLIなのでどちらの方法でも構いませんが、より利便性が高くなるように1番の方法で、ローカルサーバを立ててアクセストークンを取得する方法を採用しました。以下のような流れでアクセストークンを取得することができます。’
-
http://localhost:9999/
でサーバを起動 - ブラウザで
https://accounts.secure.freee.co.jp/public_api/authorize
へアクセス その際GETパラメータのredirect_uri=http://localhost:9999/
をつける - freeeのサイト上でログイン等を行う
-
http://localhost:9999/
にリダイレクトされる その際GETパラメータにcode
が付いている - CLIが
https://accounts.secure.freee.co.jp/public_api/token
へGETリクエストを投げる その際GETパラメータにcode
をつけるとレスポンスでアクセストークンが受け取れる
freeeのOauthアプリケーションの作成方法はこちらで紹介されています。
https://app.secure.freee.co.jp/developers/tutorials/2
Oauth2の設定 oauth2.Config
とサーバの待受ポートを渡すことで、HTTPサーバの起動からアクセストークンの取得までを行うコードはこちらです。再利用性が良さそうなので外から使えそうな形にしました。(実際のコードでは攻撃を防ぐためのstateの検証・エラーハンドリング等を行っていますが簡略化したものを掲載しています。)
func Oauth2AuthorizeLocalServer(config *oauth2.Config, port int) (*oauth2.Token, error) {
ctx := context.Background()
// redirect to authorized page
url := config.AuthCodeURL(state)
open.Start(url) // ブラウザが起動する
// ローカルサーバの起動・認証コードの受け取り
code, _ := func() (string, error) {
l, _ := net.Listen("tcp", fmt.Sprintf("localhost:%d", port))
defer l.Close()
quit := make(chan string)
go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
query := req.URL.Query()
w.Write([]byte(`auth success`))
w.(http.Flusher).Flush()
quit <- query.Get("code")
}))
return <-quit, nil
}()
oauth2Token, err := config.Exchange(ctx, code)
return oauth2Token, err
}
func main () {
oauth2Config := &oauth2.Config{
ClientID: os.Getenv("FREEE_CLIENT_ID"),
ClientSecret: os.Getenv("FREEE_CLIENT_SECRET"),
Endpoint: oauth2.Endpoint{
AuthURL: "https://accounts.secure.freee.co.jp/public_api/authorize",
TokenURL: "https://accounts.secure.freee.co.jp/public_api/token",
},
RedirectURL: "http://localhost:9999/callback",
}}
token, _ := Oauth2AuthorizeLocalServer(oauth2Config, 9999)
log.Println(token)
}
受け取った oauth2.Token
構造体にはAccess TokenやRefresh Tokenが入っているので、これを永続化して再利用すればログインを省略することができます。今回は json.Encoder
を利用して secret.json
に保存しました。
Goからfreee APIの呼び出し
対応APIは一部だけですが、Go言語向けのクライアントライブラリがサードパーティによって提供されています。今回はこれを使います。対応していないエンドポイントを利用したい場合は、 https://qiita.com/kamijin_fanta/items/67dfa90675f0d66c9670 で紹介したようにOpenAPI定義を利用したクライアント生成を行うのがオススメです。
READMEにも記載がありますが、Oauth2のClientID,ClientSecret,AccessToken等が有れば呼び出しが可能です。クライアント生成後には以下のような記述で取引をfreeeに登録することが出来ます。
details := []freee.DealCreateParamsDetails{
// 取引の詳細
}
payments := []freee.DealCreateParamsPayments{
// 関連する支払情報
}
client.CreateDeal(ctx, oauth2Token, freee.DealCreateParams{
IssueDate: "yyyy-mm-dd",
Type: "expense", // 収支区分 (収入: income, 支出: expense)
CompanyID: companyId,
Details: details,
Payments: &payments,
})
Mentaの売上情報CSVをパース
先程お見せしましたが、Mentaはダッシュボードから以下のようなフォーマットのCSVを出力できます。
契約No,日付,ユーザー名,プラン名,招待割引,キャンセル,金額,手数料,売上額,出金日
11111,2021-09-24,Example User,"プラン名",,,1000,220,780,"2021-11-10 12:34:56"
22222,2021-10-24,Example User,"プラン名",,,1000,220,780,
Go標準ライブラリの csv.NewReader
を利用することで簡単にパースを行うことが出来ます。難しいことは行っていないのですがコードは長いのでGitHubのリンクを貼り付けておきます。
パーサ: https://github.com/kamijin-fanta/freee-menta-importer/blob/master/parser.go
パーサのテスト: https://github.com/kamijin-fanta/freee-menta-importer/blob/master/parser_test.go
パースにより以下のような構造体のスライスが得られます。
type Sale struct {
ContractNo string // 契約No
Date string // 日付
UserName string // ユーザ名
PlanName string // プラン名
Price int // 金額
Fee int // 手数料
Sales int // 売上額
// ...省略
}
Mentaへ取引登録する
MentaのCSVから注文情報が取得できたので、あとは取引の登録をするだけです。先程作成したAPIクライアントで各種パラメータを埋めた構造体を投げます。descriptionに文字列を指定することで備考として確認が可能となるので、契約IDを指定しました。
description := fmt.Sprintf("menta-contract=%s", sale.ContractNo)
details := []freee.DealCreateParamsDetails{
// 売上の収入登録
{
TaxCode: 129, // sales_with_tax_10: 課税売上10%
AccountItemID: 429799547, // 売上高
Amount: int32(sale.Price),
Description: &description,
},
// 利用手数料の支出登録
{
TaxCode: 136, // purchase_with_tax_10: 課対仕入10%
AccountItemID: 429799593, // 販売手数料
Amount: int32(sale.Fee) * -1,
},
}
payments := []freee.DealCreateParamsPayments{
{
Amount: int32(sale.Sales),
FromWalletableID: walletableId,
FromWalletableType: "wallet",
Date: sale.Date,
},
}
client.CreateDeal(ctx, oauth2Token, freee.DealCreateParams{
IssueDate: date,
Type: "income", // 収支区分 (収入: income, 支出: expense)
CompanyID: companyId,
Details: details,
Payments: &payments,
})
リクエスト後にfreeeのWeb UIを確認すると正常に登録されていることが確認できます。
今回利用した全体のコードはこちらに掲載していますので、興味があれば御覧ください。また、MITライセンスとしましたのでコードの再利用等も歓迎します。 https://github.com/kamijin-fanta/freee-menta-importer
所感
前回のアドベントカレンダーを試した際に便利だと感じたので、今年からは契約を行い個人事業主で実際に利用するユーザとして記事を書かせていただきました。(去年の記事: https://qiita.com/kamijin_fanta/items/67dfa90675f0d66c9670 )
数十件・数百件の売上情報を手入力するのは現実的では無いので、こういった個人でも気軽に利用できる形でAPIが提供されているのは非常に有り難いです。今後も自動入力したいデータが増えたら自作しようと思います。