Passgeneratorを使って、Laravelで「Apple Walletに追加」する

Apple WatchユーザーということもありAppleウォレットがあまりに便利すぎるので、布教のため自分でも作ってみた。

Passgeneratorのインストール

PassgeneratorのREADMEを見ながらインストールする。

composer require thenextweb/passgenerator

config/app.phpにプロバイダーを追加する。

'providers' => [
// ...
    Thenextweb\PassGeneratorServiceProvider::class,
],

証明書の準備

Apple DeveloperアカウントのWebサイトにアクセスし、IDページへ。

追加ボタンをクリックし、Pass Type IDsを選択。

DescriptionとIdentifierを入力する。
Identifierはpass.から始まり、そのあとは逆ドメイン名スタイルになる。(例:pass.com.example.appname

次にIDの編集画面に移動し、Create Certificateをクリック。

証明書署名リクエストをアップロードする必要があるので、Macのキーチェーンアクセスで作成する

キーチェーンアクセスを開いたら、メニューバーから「キーチェーンアクセス」>「証明書アシスタント」>「認証局に証明書を要求」を選択。

ユーザのメールアドレスと通称を入力したら、ディスクに保存を選択。
出てきたCertificateSigningRequest.certSigningRequestファイルをアップロードすると、pass.cerがダウンロードできる。

Passgeneratorで使うには、このpass.cerを.p12ファイルに変換する必要がある。
pass.cerを再度キーチェーンアクセスにインポートし、秘密鍵をエクスポートする。
この時設定したパスワードをメモしておくこと。

もう一つ、Apple Worldwide Developer Relations Intermediate Certificateが必要。
これはアプリ開発者ならすでにキーチェーンアクセスに入っているかもしれないが、もしなければAppleのWebサイトから.cerでダウンロードできる。
こちらはキーチェーンアクセスから.pemでエクスポートする。

これらの証明書をstorage/app/keys/に保存し、.envに次を追加する。

CERTIFICATE_PATH="/var/www/html/storage/app/keys/Shugo Matsuzawa Dev Key.p12" #p12証明書の絶対パス
CERTIFICATE_PASS="password" #p12証明書作成字のパスワード
WWDR_CERTIFICATE="/var/www/html/storage/app/keys/Apple Worldwide Developer Relations Certification Authority.pem" #WWDRの絶対パス

パスを発行してみる

Jetstreamで会員登録すると、IDの入ったQRコードを発行するデモを作ってみた。
詳しくはGitHubを見てほしい
これでウォレットパスを発行してみる。

コントローラーは次のようになっている。
app/Http/Controllers/DashboardController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Thenextweb\PassGenerator;
use App\Services\DashboardService;
use Response;

class DashboardController extends Controller
{
    public function index()
    {
        return view('dashboard');
    }

    public function pass()
    {
        $dashboardService = new DashboardService();

        $pass_identifier = auth()->id();  // This, if set, it would allow for retrieval later on of the created Pass
        $pkpass = PassGenerator::getPass($pass_identifier);
        if (!$pkpass) {
            $pkpass = $dashboardService->createWalletPass($pass_identifier);
        }

        $filePath = storage_path('app/passgenerator') . '/' . $pass_identifier . '.pkpass';
        $fileName = 'pass.pkpass';
        $headers = [
            'Content-Type' => PassGenerator::getPassMimeType(),
        ];
        return response()->download($filePath, $fileName, $headers);
    }
}

パスのダウンロードの部分だが、PassgeneratorのREADME通りにやるとうまくいかなかった。
でもパス自体はstorage/app/passgenerator/に生成されているので、(後述)それを普通にダウンロードしている。

サービスは次のようになった。
app/Services/DashboardService.php

<?php

namespace App\Services;

use Thenextweb\PassGenerator;

class DashboardService
{
    public function createWalletPass($pass_identifier)
    {
        $pass = new PassGenerator($pass_identifier);

        $pass_definition = [
            "formatVersion"     => 1,
            "passTypeIdentifier"=> "pass.com.shugomatsuzawa.demo",
            "serialNumber"      => (string)auth()->id(),
            "teamIdentifier"    => "0000AA0000", // https://developer.apple.com/account に載っている

            "organizationName"  => "オーガニックプロデュース",
            "description"       => "オーガニックプロデュース ロイヤリティカード",
            "logoText"          => "Organic Produce",
            "foregroundColor"   => "rgb(255, 255, 255)",
            "backgroundColor"   => "rgb(55, 117, 50)",

            "barcode" => [
                "message"   => (string)auth()->id(),
                "format"    => "PKBarcodeFormatQR",
                "messageEncoding"=> "utf-8",
            ],

            "storeCard" => [
                "headerFields" => [
                    [
                        "key" => "label",
                        "value" => "DEMO",
                    ],
                ],
                "primaryFields" => [
                    [
                        "key" => "member_name",
                        "value" => auth()->user()->name,
                    ],
                ],
                "auxiliaryFields" => [
                    [
                        "key" => "member_id",
                        "label" => "会員番号",
                        "value" => (string)auth()->id(),
                    ],
                    [
                        "key" => "member_since",
                        "label" => "ご登録",
                        "value" => auth()->user()->created_at->format('Y年m月'),
                    ],
                ],
                "backFields" => [
                    [
                        "key" => "phone",
                        "label" => "電話",
                        "value" => "+81 3 1234 5678"
                    ], [
                        "key" => "website",
                        "label" => "サポートWebサイト",
                        "value" => "https://shugomatsuzawa.com"
                    ], [
                        "key" => "privacy",
                        "label" => "プライバシーポリシー",
                        "value" => "https://shugomatsuzawa.com/privacy/"
                    ], [
                        "key" => "terms",
                        "label" => "利用規約",
                        "value" => "この利用規約(以下、「本規約」といいます)は、[お店の名前](以下、「当店」といいます)が提供する会員証サービス(以下、「本サービス」といいます)に関する条件を規定します。本サービスを利用する際には、以下の規約に同意していただく必要があります。\n\n1. 会員資格\n1.1. 本サービスの利用資格は、[条件を記載]となります。\n1.2. 会員は、本サービスを他者に譲渡・移転することはできません。\n\n2. 会員証の発行と利用\n2.1. 会員証は[発行条件を記載]に基づき、当店が発行します。\n2.2. 会員証は本人のみが利用でき、他者に貸与することはできません。\n\n3. ポイントおよび特典\n3.1. 会員は、本サービスの利用に伴い、ポイントや特典を享受できる場合があります。これらの条件は随時変更される可能性があります。\n3.2. ポイントや特典は、[利用条件や期限を記載]に基づき提供されます。\n\n4. プライバシーと個人情報\n4.1. 会員のプライバシーと個人情報は、当店のプライバシーポリシーに基づき取り扱われます。\n\n5. 会員証の紛失・盗難\n5.1. 会員は、会員証の紛失・盗難があった場合、直ちに当店に報告する責任があります。\n\n6. 本規約の変更\n6.1. 当店は、本規約を随時変更できるものとし、変更後の規約は本ウェブサイト上で掲示された時点で効力を発生します。\n\n7. サービスの終了\n7.1. 当店は予告なく本サービスを終了する権利を有します。\n\n8. 免責事項\n 8.1. 当店は、本サービスの利用に伴う一切の損害やトラブルに対して一切の責任を負いません。\n\n9. その他の条件\n9.1. 本規約に定められていない事項については、関係法令および一般的な商慣習に従います。\n\n©︎Shugo Matsuzawa 2023"
                    ]
                ],
                "locations" => [
                    [
                        "latitude" => 37.6189722,
                        "longitude" => -122.3748889,
                    ]
                ],
            ],
        ];

        $pass->setPassDefinition($pass_definition);

        // Definitions can also be set from a JSON string
        // $pass->setPassDefinition(file_get_contents('/path/to/pass.json));

        // Add assets to the PKPass package
        $pass->addAsset(base_path('resources/assets/wallet/icon.png'));
        $pass->addAsset(base_path('resources/assets/wallet/[email protected]'));
        $pass->addAsset(base_path('resources/assets/wallet/logo.png'));
        $pass->addAsset(base_path('resources/assets/wallet/strip.png'));
        $pass->addAsset(base_path('resources/assets/wallet/[email protected]'));

        $pkpass = $pass->create();
        return $pkpass;
    }
}

Appleのサンプルパスを元にしている。
各フィールドの詳細はAppleのドキュメントが参考になる。

完成すると次のようなパスがダウンロードできる。

遭遇したバグ

“Disk [passgenerator] does not have a configured driver.”(2023/11/26時点)

今のところconfig/filesystems.phpにpassgeneratorディスクを追加するしかないらしい。

    'disks' => [

        ...省略...

        'passgenerator' => [
            'driver' => 'local',
            'root' => storage_path('app/passgenerator'),
        ],

    ],

message : “error:0308010C:digital envelope routines::unsupported”(2023/11/26時点)

Laravel SailコンテナのUbuntuがopenssl 3を使用しているため、レガシーオプションを有効にする必要がある。

Dockerfileを作成し、以下を追記する。

RUN sed -i '/^default = default_sect/a legacy = legacy_sect' /etc/ssl/openssl.cnf
RUN sed -i '/^\[default_sect\]/a activate = 1' /etc/ssl/openssl.cnf
RUN printf "[legacy_sect]\nactivate = 1" >> /etc/ssl/openssl.cnf

参考

関連記事

コメント

この記事へのコメントはありません。

TOP