はじめに
家計簿アプリ「Zaim」の入力履歴を
Laravel上からoAuth経由のAPIから取得して
自分用の日々のやりくり管理アプリに連携させてみました。
経緯
私は日々のお買い物やお金の管理などの管理に、
長らく「Zaim」というアプリを使ってきました。
レシートを撮影するだけで読み取ってレシート撮影のみでほぼ入力が終わります。
めんどくさがりな人でもきちんと家計簿がつけられるようになる素晴らしいアプリです。
また、今年「365日貯金」というものを始めましたので
これと別の自作アプリケーションに「いつ何、何円入れたか」を記録していました。
図にするとこんな感じになります。
つまるところ、Zaimと自作システムの両方に登録を行っていました。
となると、どうなるでしょう?
そうです、面倒で入力を溜め込むことになります(特に自作システム)
そこで、「この2つのどちらかに集約したいなァ!」と思い調べていると
Zaim大先生にはAPIがあるご様子でしたので、
Zaimに一本化させることにしました。
図にするとこんな感じ。
仕様(実現したいこと)
- PHP 8.2/Laravel 10で実装する
- 自作システムからZaim APIを呼び出して、Zaimの入力データを取得する
※今回は同期のたびに都度、全レコードを削除して再登録を行う - アクセストークンの取得は同期の際に自動で行い、
.env
の更新作業などは不要とする
実装
情報源が少なかったこともあり、けっこう苦戦しました。
Zaim APIはoAuth 1.0aに対応しているのですが、
少し前の方式ということもあり、PHPでの事例も古かったり、
Laravelでサクッと使えそうなライブラリの情報源がなかなか見つかりませんでした。
最終的に落ち着いたライブラリが以下の2つです。
(1本化できたりなど、他に良いやり方があるかもしれません)
- APIを実行して入力データを取得する
GuzzleHttp\Subscriber\Oauth\Oauth1
- APIを経由してアクセストークンを発行する
https://github.com/thephpleague/oauth1-client
何はともあれ、一旦APIを叩いてみましょう
PostmanでZaim APIを使ってみるの記事を参考にさせていただき、
Zaim APIのコンシュマーキーの発行からアクセストークンの発行まで行っておきます。
ここまでで発行したパラメータは後で使うので
.env
ファイルに記載しておきます
ZAIM_API_CONSUMER_ID=
ZAIM_API_CONSUMER_SECRET=
ZAIM_API_OAUTH_ACCESS_TOKEN=
ZAIM_API_OAUTH_ACCESS_TOKEN_SECRET=
APIを実行して入力データを取得
-
composerで「oauth-subscriber」をインストール
composer require guzzlehttp/oauth-subscriber
-
ライブラリのReadmeを参考に、下記のコードを実行します
※今回はサンプルコードのためコントローラに実装しました。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Subscriber\Oauth\Oauth1;
class TestController extends Controller
{
public function index ()
{
$stack = HandlerStack::create();
$middleware = new Oauth1([
'consumer_key' => env('ZAIM_API_CONSUMER_ID'),
'consumer_secret' => env('ZAIM_API_CONSUMER_SECRET'),
'token' => env('ZAIM_API_OAUTH_ACCESS_TOKEN'),
'token_secret' => env('ZAIM_API_OAUTH_ACCESS_TOKEN_SECRET'),
]);
$stack->push($middleware);
$client = new Client([
'base_uri' => 'https://api.zaim.net/v2/',
'handler' => $stack,
'auth' => 'oauth',
]);
// 取得コードを実行
$response = $client->get('home/money');
return $response->getBody()->getContents();
}
}
APIを経由してアクセストークンを発行
先ほどの実装でアクセストークンがあれば叩くことができるようになりました。
では続いて、アクセストークンを発行できるようにしていきましょう。
認証時にコールバックが発生するので
コールバック先のURLを.env
に追加しておきましょう。
ZAIM_CALLBACK_URL=
-
composerで「socialite」をインストール
※socialiteのインストール時に一緒についてくるleague/oauth1-client
を使用します。
composer require laravel/socialite
-
League\OAuth1\Client\Server\Server
クラスを継承させたServerクラスを作成します
※一部のメソッドはオーバライドさせています。
<?php
namespace App\Services\External\OAuth;
use League\OAuth1\Client\Credentials\TokenCredentials;
use League\OAuth1\Client\Server\Server;
use League\OAuth1\Client\Server\User;
use League\OAuth1\Client\Signature\HmacSha1Signature;
class TestServer extends Server
{
public function __construct()
{
$clientCredentials = $this->createClientCredentials([
'identifier' => env('ZAIM_API_CONSUMER_ID'),
'secret' => env('ZAIM_API_CONSUMER_SECRET'),
'callback_uri'=> env('ZAIM_CALLBACK_URL'),
]);
$this->clientCredentials = $clientCredentials;
$this->signature = new HmacSha1Signature($clientCredentials);
}
public function authorize($temporaryIdentifier)
{
return
$this->getAuthorizationUrl($temporaryIdentifier);
}
public function getUserDetails(TokenCredentials $tokenCredentials, $force = false)
{
$data = $this->fetchUserDetails($tokenCredentials, $force);
return $this->userDetails($data, $tokenCredentials);
}
public function getUserUid(TokenCredentials $tokenCredentials, $force = false)
{
$data = $this->fetchUserDetails($tokenCredentials, $force);
return $this->userUid($data, $tokenCredentials);
}
public function getUserEmail(TokenCredentials $tokenCredentials, $force = false)
{
$data = $this->fetchUserDetails($tokenCredentials, $force);
return $this->userEmail($data, $tokenCredentials);
}
public function getUserScreenName(TokenCredentials $tokenCredentials, $force = false)
{
$data = $this->fetchUserDetails($tokenCredentials, $force);
return $this->userScreenName($data, $tokenCredentials);
}
public function urlTemporaryCredentials(){
return "https://api.zaim.net/v2/auth/request";
}
public function urlAuthorization(){
return 'https://auth.zaim.net/users/auth';
}
public function urlTokenCredentials(){
return 'https://api.zaim.net/v2/auth/access';
}
public function urlUserDetails(){
return 'https://api.zaim.net/v2/home/user/verify';
}
public function userDetails($data, TokenCredentials $tokenCredentials){
return $user = new User();
}
public function userUid($data, TokenCredentials $tokenCredentials){
return null;
}
public function userEmail($data, TokenCredentials $tokenCredentials){
return null;
}
public function userScreenName($data, TokenCredentials $tokenCredentials){
return null;
}
}
3.ルーティングを追加しておきます
Route::get('test/', 'App\Http\Controllers\TestController@index')->name('test.index');
Route::get('test/zaim', 'App\Http\Controllers\TestController@zaim')->name('test.zaim');
Route::get('test/callback', 'App\Http\Controllers\TestController@callback')->name('test.callback');
4.コントローラに連携時の処理、コールバック時の処理を以下のように実装します
<?php
namespace App\Http\Controllers;
use App\Services\External\OAuth\ZaimServer;
use Illuminate\Http\Request;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Subscriber\Oauth\Oauth1;
use App\Services\External\OAuth\TestServer;
use Illuminate\Support\Facades\Session;
class TestController extends Controller
{
public function index()
{
return view('test');
}
public function zaim(TestServer $server)
{
// リクエストトークンを発行
$temporaryCredentials = $server->getTemporaryCredentials();
// 認証URL
$urlAuthorize = $server->authorize($temporaryCredentials);
// セッションにリクエストトークンを保存
Session::put('zaim_temporary_credentials', serialize($temporaryCredentials));
// 認証URLにリダイレクト
return redirect($urlAuthorize);
}
public function callback(Request $request, ZaimServer $zaimServer)
{
// セッションからリクエストトークンを取得
$temporaryCredentials = unserialize(Session::pull('zaim_temporary_credentials'));
// アクセストークンを発行する
$tokenCredentials = $zaimServer->getTokenCredentials(
$temporaryCredentials,
$temporaryCredentials->getIdentifier(),
$request->input('oauth_verifier')
);
$stack = HandlerStack::create();
$middleware = new Oauth1([
'consumer_key' => env('ZAIM_API_CONSUMER_ID'),
'consumer_secret' => env('ZAIM_API_CONSUMER_SECRET'),
'token' => $tokenCredentials->getIdentifier(),
'token_secret' => $tokenCredentials->getSecret(),
]);
$stack->push($middleware);
$client = new Client([
'base_uri' => 'https://api.zaim.net/v2/',
'handler' => $stack,
'auth' => 'oauth',
]);
$response = $client->get('home/money');
$contents = json_decode($response->getBody()->getContents(), true);
dd($contents);
return $contents;
}
}
これで組み込みは完成です。
こんな感じになりました
結論
無事に叩くことができました!
Zaimユーザの方はAPIを使って楽しい家計簿ライフをお送りくださいね!
おわりに
最後にお知らせとなりますが、イーディーエーでは一緒に働くエンジニアを募集しております。
詳しくは採用情報ページをご確認ください。
https://eda-inc.jp/recruit/
みなさまからのご応募をお待ちしております。