iOS Wallet(旧Passbook)用のPassbookパッケージを生成するための.NET標準ライブラリ
dotnet-passbook を使用している場合は、私にコーヒーをおごってください。
dotnet-passbook は、NuGet からダウンロードすることもできます。
Install-Package dotnet-passbook
Apple の Passbook のパスの作成は非常に簡単ですが、マニフェスト ファイルの署名に PKI を使用する必要があり、これはそれほど簡単ではありません。 PassVerse.com (現在は利用できません) を構築する過程で、すべての手順を実行する .Net ライブラリを作成しました。私は、このライブラリを他の .NET 開発者向けにオープンソース化することにしました。
このソリューションには Visual Studio 2017 以降が必要です。このライブラリは .NET Standard 2.0 用に構築されています。
PassGenerator を実行する前に、必要な証明書がすべてインストールされていることを確認する必要があります。必要なものは 2 つあります。
まず、開発者ポータルから取得した Passbook 証明書が必要です。 iOS 開発者アカウントが必要です。
次に、Apple WWDR (Worldwide Developer Relations) 証明書が必要です。これは http://www.apple.com/certificateauthority/ からダウンロードできます。
Passbook 証明書を生成した時期に応じて、G1 または G4 証明書のいずれかが必要になります。 2022 年 1 月 27 日以前に通帳証明書を生成した場合は、G1 を使用してください。それ以外の場合は G4 を使用します。
ここにリストされている他の「Worldwide Developer Relations」証明書は、Apple Wallet では機能しません。 (「申し訳ありませんが、現時点ではあなたのパスを Passbook にインストールできません。」)
Windows マシンを使用している場合、IIS を使用して証明書を生成する手順が私のブログにあります。
Linux/macOS を使用している場合、または Windows で OpenSSL を使用したい場合は、OpenSSL を使用して必要な証明書を作成する方法について using-openssl.md を確認してください。
証明書を移動するときは、Passbook 証明書に常に秘密キー コンポーネントが含まれていることを確認してください。そうでないと、署名が失敗します。
パスを生成するには、まず PassGenerator を宣言します。
PassGenerator generator = new PassGenerator ( ) ;
次に、PassGeneratorRequest を作成します。これは、作成するパスに必要なすべてのフィールドを追加するための完全な権限を与える生のリクエストです。各パスはいくつかのセクションに分かれています。各セクションは、作成しようとしているパスのスタイルに基づいて、異なる方法でレンダリングされます。詳細については、Apple の『Passbook Programming Guide』を参照してください。以下の例は、非常に基本的な搭乗券を作成する方法を示しています。
各パスには必須データのセットがあるため、最初にそれを入力します。
PassGeneratorRequest request = new PassGeneratorRequest ( ) ;
request . PassTypeIdentifier = " pass.tomsamcguinness.events " ;
request . TeamIdentifier = " RW121242 " ;
request . SerialNumber = " 121212 " ;
request . Description = " My first pass " ;
request . OrganizationName = " Tomas McGuinness " ;
request . LogoText = " My Pass " ;
色はHTML形式またはRGB形式で指定できます。
request . BackgroundColor = " #FFFFFF " ;
request . LabelColor = " #000000 " ;
request . ForegroundColor = " #000000 " ;
request . BackgroundColor = " rgb(255,255,255) " ;
request . LabelColor = " rgb(0,0,0) " ;
request . ForegroundColor = " rgb(0,0,0) " ;
Apple WWDR と Passbook 証明書の両方を X509Certificate インスタンスとして提供する必要があります。注: これは以前のバージョンからの変更です。
request . AppleWWDRCACertificate = new X509Certificate ( .. . ) ;
request . PassbookCertificate = new X509Certificate ( .. . ) ;
クラウドで X509Certificate インスタンスを作成する場合、署名プロセスで問題が発生する可能性があります。 MachineKeySet と Exportable X509KeyStorageFlags を使用することをお勧めします。
X509KeyStorageFlags flags = X509KeyStorageFlags . MachineKeySet | X509KeyStorageFlags . Exportable ;
X509Certificate2 certificate = new X509Certificate2 ( bytes , password , flags ) ;
次に、使用する画像を定義します。常に、標準サイズの画像と Retina サイズの画像の両方を含める必要があります。画像は byte[] として提供されます。
request . Images . Add ( PassbookImage . Icon , System . IO . File . ReadAllBytes ( Server . MapPath ( " ~/Icons/icon.png " ) ) ) ;
request . Images . Add ( PassbookImage . Icon2X , System . IO . File . ReadAllBytes ( Server . MapPath ( " ~/Icons/[email protected] " ) ) ) ;
request . Images . Add ( PassbookImage . Icon3X , System . IO . File . ReadAllBytes ( Server . MapPath ( " ~/Icons/[email protected] " ) ) ) ;
より多くのパス固有の情報を提供できるようになりました。スタイルを設定してから、すべての情報を必須セクションのフィールドに追加する必要があります。搭乗券の場合、フィールドは 3 つのセクションに追加されます。プライマリ、セカンダリ、補助。
request . Style = PassStyle . BoardingPass ;
request . AddPrimaryField ( new StandardField ( " origin " , " San Francisco " , " SFO " ) ) ;
request . AddPrimaryField ( new StandardField ( " destination " , " London " , " LDN " ) ) ;
request . AddSecondaryField ( new StandardField ( " boarding-gate " , " Gate " , " A55 " ) ) ;
request . AddAuxiliaryField ( new StandardField ( " seat " , " Seat " , " G5 " ) ) ;
request . AddAuxiliaryField ( new StandardField ( " passenger-name " , " Passenger " , " Thomas Anderson " ) ) ;
request . TransitType = TransitType . PKTransitTypeAir ;
バーコードを追加できます。
request . AddBarcode ( BarcodeType . PKBarcodeFormatPDF417 , " 01927847623423234234 " , " ISO-8859-1 " , " 01927847623423234234 " ) ;
iOS 9 以降、複数のバーコードがサポートされるようになりました。このヘルパー メソッドは、この新しい機能をサポートします。 iOS 8 以前をサポートしたい場合は、SetBarcode() メソッドを使用できます。
パスを既存のアプリにリンクするには、アプリの Apple ID を AssociatedStoreIdentifiers 配列に追加します。
request . AssociatedStoreIdentifiers . Add ( 551768478 ) ;
最後に、リクエストをジェネレーターのインスタンスに渡してパスを生成します。これにより、署名付きマニフェストが作成され、すべてのイメージ ファイルが zip にパッケージ化されます。
byte [ ] generatedPass = generator . Generate ( request ) ;
たとえば、ASP.NET MVC を使用している場合は、この byte[] を Passbook パッケージとして返すことができます
return new FileContentResult ( generatedPass , " application/vnd.apple.pkpass " ) ;
iOS 15 では、単一の .pkpasses ファイルを使用して複数のパスをバンドルして配布する機能が導入されました。リクエスト値のディクショナリと、個々のリクエストのファイル名を表す文字列キーを渡すことによって、パス バンドルを生成することもできます。
PassGeneratorRequest myFirstRequest = new PassGeneratorRequest ( ) ;
PassGeneratorRequest mySecondRequest = new PassGeneratorRequest ( ) ;
// Build out your requests
List < PassGeneratorRequest > requests = new List < PassGeneratorRequest > ;
requests . Add ( myFirstRequest ) ;
request . Add ( mySecondRequest ) ;
byte [ ] generatedBundle = generator . Generate ( requests ) ;
結果のバイト配列は、単一の.pkpass
ファイルとほぼ同じように扱われますが、拡張子と MIME タイプ ( pkpasses ) が異なります。
return new FileContentResult ( generatedBundle , " application/vnd.apple.pkpasses " )
{
FileDownloadName = " tickets.pkpasses.zip "
} ;
PassGenerator
クラスは、パスの生成に使用されるメソッドを公開するIPassGenerator
インターフェイスに準拠しています。パス生成リクエストを構築するカスタム ロジックがある場合は、任意のモック ライブラリを使用してIPassGenerator
インターフェイスを簡単にモックし、ロジックが正しいジェネレーター リクエストでGenerate
メソッドを呼び出すかどうかをテストできます。
DI を通じてジェネレーター リクエストを受け取り、そのリクエストに基づいてパスを生成するサービスがあるとします。
class PassGeneratorService ( IPassGenerator passGenerator )
{
public byte [ ] GeneratePassWithLogoTextAndBackgroundColor ( String logoText , String backgroundColor )
{
// make a request based on parameters
....
passGenerator . Generate ( request ) ;
}
}
IPassGenerator
インターフェイスをモックし、正しい要求でGenerate
メソッドが呼び出されることを確認することで、このサービスを簡単にテストできます。 NSubstitute と xUnit を使用したサンプルを次に示します。
[ Fact ]
void ServiceUsesPassedParamsForRequest ( )
{
// Arrange
var passGeneratorMock = Substitute . For < IPassGenerator > ( ) ;
var sut = new PassGeneratorService ( passGeneratorMock ) ;
// Act
sut . GeneratePassWithLogoTextAndBackgroundColor ( " Cup'o'Joe " , " #0CAFE0 " ) ;
// Assert/Verify
passGeneratorMock . Received ( ) . Generate ( Arg . Is < PassGeneratorRequest > ( r =>
{
r . LogoText == " Cup'o'Joe " && r . BackgroundColor == " #0CAFE0 "
} ) ) ;
}
ロジックが適切に機能するかどうかをテストするもう 1 つの方法は、作成されたPassGeneratorRequest
オブジェクト自体をテストすることです。リクエスト オブジェクトを作成し、ロジックに基づいてプロパティを設定して、リクエスト オブジェクトが正しく作成されたかどうかをテストできます。次の基本的なリクエスト ビルダーを考えてみましょう。
class PassGeneratorRequestBuilder
{
public PassGeneratorRequest BuildRequestWithLogoTextAndBackgroundColor ( String logoText , String backgroundColor )
{
var request = new PassGeneratorRequest ( ) ;
request . LogoText = logoText ;
request . BackgroundColor = backgroundColor ;
return request ;
}
}
このビルダーをテストするには、リクエスト オブジェクトを作成し、プロパティが正しく設定されているかどうかを確認します。
[ Fact ]
void RequestBuilderSetsPropertiesCorrectly ( )
{
// Arrange
var sut = new PassGeneratorRequestBuilder ( ) ;
// Act
var request = sut . BuildRequestWithLogoTextAndBackgroundColor ( " Cup'o'Joe " , " #0CAFE0 " ) ;
// Assert
Assert . Equal ( " Cup'o'Joe " , request . LogoText ) ;
Assert . Equal ( " #0CAFE0 " , request . BackgroundColor ) ;
}
ここで必要なのは、リクエストがPassGenerator
クラスに到達する途中で変更/変更されていないことを確認することだけです。
作成したパスが iOS またはシミュレーターで開かないようであれば、ペイロードが無効である可能性があります。トラブルシューティングを支援するために、このシンプルなツールを作成しました - https://pkpassvalidator.azurewebsites.net - これを使用してpkpass
ファイルを実行するだけで、何が問題なのかがわかるかもしれません。このツールは新しいもの (2018 年 7 月) であり、完全にすべてをチェックするわけではありません。ジェネレーター自体にさらに検証を追加してみます。
IIS アプリケーション内で署名コードを実行している場合、証明書の秘密キーにアクセスする際に問題が発生する可能性があります。この問題を解決するには、MMC を開きます => 証明書 (ローカル コンピューター) スナップインの追加 => 証明書 (ローカル コンピューター) => 個人 => 証明書 => 対象の証明書を右クリック => すべてのタスク => 秘密キーの管理 => IIS の追加AppPoolAppPoolName に変更し、フル コントロールを付与します。 「AppPoolName」を、アプリが実行されているアプリケーション プールの名前に置き換えます。 (場合によってはIIS_IUSRS
)
パスを更新できるようにするには、パスにコールバックを提供する必要があります。リクエストを生成するときは、AuthenticationToken と WebServiceUrl を指定する必要があります。これらの値は両方とも必須です。 WebServiceUrl はデフォルトで HTTPS である必要がありますが、テストしているデバイスの iOS 開発者向けオプションでこの要件を無効にすることができます。
認証トークンは、API に対して行われるすべてのリクエストのヘッダーに含まれる文字列です。このトークンを検証するのはあなたの責任です。
request . AuthenticationToken = " <a secret to ensure authorized access> " ;
request . WebServiceUrl = " https://<your api> " ;
指定する Web サービスは、PassKit Web サービス リファレンスで概要が説明されている Apple のプロトコルをサポートする必要があります。
バージョン 2.0.1 では、NFC キーがサポートされるようになりました。これらを使用するには、新しい NFC オブジェクトで Nfc プロパティを設定するだけです。メッセージとエンコードされた公開キーの値は両方とも必須です。
PassGeneratorRequest request = new PassGeneratorRequest ( ) ;
request . Nfc = new Nfc ( " THE NFC Message " , " <encoded private key> " ) ;
残念ながら、必要な値は公開されていないため、情報を提供することはできません。ここに何が入っているか知っている人がいたら、このキーをサポートするために喜んでライブラリに変更を追加したいと思います。
すべてのプル リクエストを歓迎します。修正できない問題に遭遇した場合は、問題を提起するか、[email protected] にメールを送信するか、twitter @tomasmcguinness をフォローしてください。
dotnet-passbook は MIT ライセンスに基づいて配布されます: http://tomasmcguinness.mit-license.org/