用於 iOS 錢包(以前稱為 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 之前,您需要確保已安裝所有必要的憑證。需要有兩個。
首先,您需要從開發者入口網站取得 Passbook 憑證。您必須擁有 iOS 開發者帳號。
其次,你需要Apple WWDR(全球開發者關係)證書。您可以從這裡下載:http://www.apple.com/certificateauthority/。
根據您產生 Passbook 證書的時間,您將需要 G1 或 G4 證書。如果您的存摺憑證是在 2022 年 1 月 27 日或之前產生的,請使用 G1。否則使用G4。
此處列出的其他「全球開發者關係」證書不適用於 Apple Wallet。 (「抱歉,您的 Pass 目前無法安裝到 Passbook。」)
如果您使用的是 Windows 計算機,我的部落格上有關於使用 IIS 產生憑證的說明
如果您使用的是 Linux/macOS 或偏好在 Windows 上使用 OpenSSL,請查看 using-openssl.md 以取得如何使用 OpenSSL 建立必要憑證的說明。
移動憑證時,請確保您的 Passbook 憑證始終包含私鑰元件,否則簽名將會失敗。
若要產生通行證,請先聲明 PassGenerator。
PassGenerator generator = new PassGenerator ( ) ;
接下來,建立一個 PassGeneratorRequest。這是一個原始請求,您可以完全有權添加您想要建立的通行證所需的所有欄位。每個通道都分為幾個部分。每個部分都以不同的方式呈現,具體取決於您嘗試產生的通道的樣式。有關詳細信息,請參閱 Apple 的 Passbook 編程指南。下面的範例將展示如何建立非常基本的登機證。
由於每遍都有一組必填數據,因此請先填寫。
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 ) ;
接下來,定義要使用的圖像。您必須始終包含標準尺寸和視網膜尺寸的影像。圖像以 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] " ) ) ) ;
您現在可以提供更多通行證特定資訊。必須設定樣式,然後將所有資訊新增到所需部分的欄位中。對於登機證,字段添加到三個部分;主要、次要和輔助。
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 "
} ) ) ;
}
測試邏輯是否正常運作的另一種方法是測試所建立的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 服務必須支援 Apple 的協議,如 PassKit Web 服務參考中所述
從版本 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/