3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

家計簿アプリ「Zaim」のAPIをLaravelに組み込んでみよう

Last updated at Posted at 2023-09-21

はじめに

家計簿アプリ「Zaim」の入力履歴を
Laravel上からoAuth経由のAPIから取得して
自分用の日々のやりくり管理アプリに連携させてみました。

経緯

私は日々のお買い物やお金の管理などの管理に、
長らく「Zaim」というアプリを使ってきました。

レシートを撮影するだけで読み取ってレシート撮影のみでほぼ入力が終わります。
めんどくさがりな人でもきちんと家計簿がつけられるようになる素晴らしいアプリです。

また、今年「365日貯金」というものを始めましたので
これと別の自作アプリケーションに「いつ何、何円入れたか」を記録していました。

図にするとこんな感じになります。

image.png

つまるところ、Zaimと自作システムの両方に登録を行っていました。
となると、どうなるでしょう?

そうです、面倒で入力を溜め込むことになります(特に自作システム)

そこで、「この2つのどちらかに集約したいなァ!」と思い調べていると
Zaim大先生にはAPIがあるご様子でしたので、
Zaimに一本化させることにしました。

図にするとこんな感じ。

image.png

仕様(実現したいこと)

  • PHP 8.2/Laravel 10で実装する
  • 自作システムからZaim APIを呼び出して、Zaimの入力データを取得する
    ※今回は同期のたびに都度、全レコードを削除して再登録を行う
  • アクセストークンの取得は同期の際に自動で行い、.envの更新作業などは不要とする

実装

情報源が少なかったこともあり、けっこう苦戦しました。

Zaim APIoAuth 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を実行して入力データを取得

  1. composerで「oauth-subscriber」をインストール
    composer require guzzlehttp/oauth-subscriber

  2. ライブラリの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=
  1. composerで「socialite」をインストール
    ※socialiteのインストール時に一緒についてくるleague/oauth1-clientを使用します。
    composer require laravel/socialite

  2. 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;
    }
}

これで組み込みは完成です。

こんな感じになりました

Sep-19-2023 14-13-47.gif

結論

無事に叩くことができました!
Zaimユーザの方はAPIを使って楽しい家計簿ライフをお送りくださいね!

おわりに

最後にお知らせとなりますが、イーディーエーでは一緒に働くエンジニアを募集しております。
詳しくは採用情報ページをご確認ください。
https://eda-inc.jp/recruit/
みなさまからのご応募をお待ちしております。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?