Perpustakaan Standar .NET untuk menghasilkan paket Passbook untuk iOS Wallet (sebelumnya Passbook)
Jika Anda menggunakan dotnet-passbook, mohon pertimbangkan untuk membelikan saya secangkir kopi
dotnet-passbook juga tersedia untuk diunduh dari NuGet
Install-Package dotnet-passbook
Membuat pass untuk Apple's Passbook cukup sederhana, namun memerlukan penggunaan PKI untuk menandatangani file manifes, dan ini tidak sesederhana itu! Selama pembuatan PassVerse.com (tidak lagi tersedia), saya membuat perpustakaan .Net yang melakukan semua langkah. Saya memutuskan untuk membuka sumber perpustakaan ini untuk pengembang .NET lainnya.
Solusinya memerlukan Visual Studio 2017 atau lebih tinggi. Perpustakaan ini dibuat untuk .NET Standard 2.0.
Sebelum menjalankan PassGenerator, Anda perlu memastikan bahwa Anda telah menginstal semua sertifikat yang diperlukan. Ada dua yang diperlukan.
Pertama, Anda memerlukan sertifikat Passbook Anda, yang Anda dapatkan dari Portal Pengembang. Anda harus memiliki akun pengembang iOS.
Kedua, Anda memerlukan sertifikat Apple WWDR (WorldWide Developer Relations). Anda dapat mengunduhnya dari sini http://www.apple.com/certificateauthority/.
Tergantung pada kapan Anda membuat sertifikat Passbook, Anda memerlukan sertifikat G1 atau G4. Jika Anda membuat sertifikat buku tabungan pada atau sebelum tanggal 27 Januari 2022, gunakan G1. Kalau tidak, gunakan G4.
Sertifikat "Hubungan Pengembang Seluruh Dunia" lainnya yang tercantum di sini tidak akan berfungsi dengan Apple Wallet. ("Maaf, Pass Anda tidak dapat diinstal ke Passbook saat ini.")
Ada instruksi di blog saya untuk membuat sertifikat menggunakan IIS jika Anda menggunakan mesin Windows
Jika Anda menggunakan Linux/macOS atau lebih suka menggunakan OpenSSL di Windows, lihat using-openssl.md untuk petunjuk tentang cara membuat sertifikat yang diperlukan menggunakan OpenSSL.
Saat memindahkan sertifikat, pastikan sertifikat Passbook Anda selalu menyertakan komponen kunci pribadi, jika tidak maka penandatanganan akan gagal.
Untuk menghasilkan pass, mulailah dengan mendeklarasikan PassGenerator.
PassGenerator generator = new PassGenerator ( ) ;
Selanjutnya, buat PassGeneratorRequest. Ini adalah permintaan mentah yang memberi Anda kekuatan penuh untuk menambahkan semua kolom yang diperlukan untuk pass yang ingin Anda buat. Setiap lintasan dipecah menjadi beberapa bagian. Tiap bagian dirender dengan cara berbeda, berdasarkan gaya operan yang ingin Anda hasilkan. Untuk informasi lebih lanjut mengenai hal ini, silakan baca Panduan Pemrograman Passbook Apple. Contoh di bawah ini akan menunjukkan cara membuat boarding pass yang sangat mendasar.
Karena setiap pass memiliki sekumpulan data wajib, isilah terlebih dahulu.
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 " ;
Warna dapat ditentukan dalam format HTML atau format 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) " ;
Anda harus memberikan Apple WWDR dan sertifikat Passbook Anda sebagai instans X509Certificate. CATATAN: Ini merupakan perubahan dari versi sebelumnya.
request . AppleWWDRCACertificate = new X509Certificate ( .. . ) ;
request . PassbookCertificate = new X509Certificate ( .. . ) ;
Saat Anda membuat instans X509Certificate di cloud, Anda mungkin mengalami masalah dengan proses penandatanganan. Saya menyarankan Anda menggunakan MachineKeySet dan X509KeyStorageFlags yang Dapat Diekspor.
X509KeyStorageFlags flags = X509KeyStorageFlags . MachineKeySet | X509KeyStorageFlags . Exportable ;
X509Certificate2 certificate = new X509Certificate2 ( bytes , password , flags ) ;
Selanjutnya, tentukan gambar yang ingin Anda gunakan. Anda harus selalu menyertakan gambar berukuran standar dan retina. Gambar disediakan sebagai 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] " ) ) ) ;
Anda sekarang dapat memberikan informasi yang lebih spesifik. Gaya harus diatur dan kemudian semua informasi ditambahkan ke bidang pada bagian yang diperlukan. Untuk boarding pass, kolomnya ditambah menjadi tiga bagian; primer, sekunder dan tambahan.
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 ;
Anda dapat menambahkan BarCode.
request . AddBarcode ( BarcodeType . PKBarcodeFormatPDF417 , " 01927847623423234234 " , " ISO-8859-1 " , " 01927847623423234234 " ) ;
Dimulai dengan iOS 9, beberapa kode batang kini didukung. Metode pembantu ini mendukung fitur baru ini. Jika Anda ingin mendukung iOS 8 dan versi lebih lama, Anda dapat menggunakan metode SetBarcode().
Untuk menautkan pass ke aplikasi yang sudah ada, Anda dapat menambahkan ID Apple aplikasi tersebut ke array AssociatedStoreIdentifiers.
request . AssociatedStoreIdentifiers . Add ( 551768478 ) ;
Terakhir, hasilkan pass dengan meneruskan permintaan ke instance Generator. Ini akan membuat manifes yang ditandatangani dan mengemas semua file gambar ke dalam zip.
byte [ ] generatedPass = generator . Generate ( request ) ;
Jika Anda menggunakan ASP.NET MVC misalnya, Anda dapat mengembalikan byte[] ini sebagai paket Passbook
return new FileContentResult ( generatedPass , " application/vnd.apple.pkpass " ) ;
iOS 15 memperkenalkan kemampuan untuk menggabungkan dan mendistribusikan beberapa pass menggunakan satu file .pkpasses. Anda juga dapat membuat bundel pass dengan meneruskan kamus nilai permintaan dan kunci string yang mewakili nama file untuk setiap permintaan individual.
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 ) ;
Array byte yang dihasilkan diperlakukan hampir sama dengan file .pkpass
tunggal, tetapi dengan ekstensi dan tipe MIME yang berbeda ( pkpasses )
return new FileContentResult ( generatedBundle , " application/vnd.apple.pkpasses " )
{
FileDownloadName = " tickets.pkpasses.zip "
} ;
Kelas PassGenerator
sesuai dengan antarmuka IPassGenerator
yang memperlihatkan metode yang digunakan untuk menghasilkan pass. Jika Anda memiliki logika khusus yang membuat permintaan pembuatan pass, Anda dapat menggunakan antarmuka IPassGenerator
tiruan dengan mudah menggunakan pustaka tiruan apa pun dan menguji apakah logika Anda memanggil metode Generate
dengan permintaan generator yang benar.
Katakanlah Anda memiliki layanan yang menerima permintaan generator melalui DI dan menghasilkan pass berdasarkan permintaan tersebut.
class PassGeneratorService ( IPassGenerator passGenerator )
{
public byte [ ] GeneratePassWithLogoTextAndBackgroundColor ( String logoText , String backgroundColor )
{
// make a request based on parameters
....
passGenerator . Generate ( request ) ;
}
}
Anda dapat dengan mudah menguji layanan ini dengan meniru antarmuka IPassGenerator
dan memverifikasi bahwa metode Generate
dipanggil dengan permintaan yang benar. Berikut contoh penggunaan NSubstitute dan 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 "
} ) ) ;
}
Cara lain untuk menguji apakah logika Anda berfungsi dengan baik adalah dengan menguji objek PassGeneratorRequest
yang dibuat itu sendiri. Anda dapat membuat objek permintaan dan mengatur properti berdasarkan logika Anda dan kemudian menguji apakah objek permintaan dibuat dengan benar. Pertimbangkan pembuat permintaan dasar ini:
class PassGeneratorRequestBuilder
{
public PassGeneratorRequest BuildRequestWithLogoTextAndBackgroundColor ( String logoText , String backgroundColor )
{
var request = new PassGeneratorRequest ( ) ;
request . LogoText = logoText ;
request . BackgroundColor = backgroundColor ;
return request ;
}
}
Anda dapat menguji pembuat ini dengan membuat objek permintaan dan memverifikasi apakah properti telah disetel dengan benar.
[ 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 ) ;
}
Sekarang Anda hanya perlu memastikan bahwa permintaan tersebut tidak diubah/dimutasi dalam perjalanannya ke kelas PassGenerator
.
Jika pass yang Anda buat sepertinya tidak terbuka di iOS atau di simulator, payloadnya mungkin tidak valid. Untuk membantu pemecahan masalah, saya telah membuat alat sederhana ini - https://pkpassvalidator.azurewebsites.net - cukup jalankan file pkpass
Anda melalui ini dan mungkin akan memberikan gambaran apa yang salah. Alat ini baru (18 Juli) dan tidak memeriksa semuanya sepenuhnya. Saya akan mencoba dan menambahkan lebih banyak validasi ke generator itu sendiri.
Jika Anda menjalankan kode penandatanganan dalam aplikasi IIS, Anda mungkin mengalami beberapa masalah saat mengakses kunci pribadi sertifikat Anda. Untuk mengatasinya buka MMC => Tambahkan snap-in Sertifikat (Komputer lokal) => Sertifikat (Komputer Lokal) => Pribadi => Sertifikat => Klik kanan sertifikat yang diminati => Semua tugas => Kelola kunci pribadi => Tambahkan IIS AppPoolAppPoolName dan berikan kontrol penuh. Ganti "AppPoolName" dengan nama kumpulan aplikasi tempat aplikasi Anda berjalan. (terkadang IIS_IUSRS
)
Untuk dapat memperbarui pass Anda, Anda harus memberikannya panggilan balik. Saat membuat permintaan, Anda harus menyediakannya dengan AuthenticationToken dan WebServiceUrl. Kedua nilai ini diperlukan. WebServiceUrl harus berupa HTTPS secara default, namun Anda dapat menonaktifkan persyaratan ini di opsi pengembang iOS pada perangkat apa pun yang Anda uji.
Token autentikasi adalah string yang akan disertakan dalam header semua permintaan yang dibuat ke API Anda. Anda bertanggung jawab untuk memvalidasi token ini.
request . AuthenticationToken = " <a secret to ensure authorized access> " ;
request . WebServiceUrl = " https://<your api> " ;
Layanan web yang Anda tunjuk harus mendukung protokol Apple, yang diuraikan dalam Referensi Layanan Web PassKit
Pada versi 2.0.1, kunci NFC kini didukung. Untuk menggunakannya, cukup atur properti NFC dengan objek NFC baru. Nilai pesan dan kunci publik yang dikodekan bersifat wajib.
PassGeneratorRequest request = new PassGeneratorRequest ( ) ;
request . Nfc = new Nfc ( " THE NFC Message " , " <encoded private key> " ) ;
Sayangnya, saya tidak dapat memberikan informasi apa pun mengenai nilai yang diperlukan karena tidak tersedia untuk umum. Jika ada yang tahu apa yang terjadi di sini, saya akan dengan senang hati menambahkan perubahan ke perpustakaan saya untuk mendukung kunci ini.
Semua permintaan tarik disambut! Jika Anda menemukan masalah yang tidak dapat diperbaiki, silakan sampaikan masalah tersebut atau kirimkan email kepada saya di [email protected] atau ikuti saya di twitter @tomasmcguinness
dotnet-passbook didistribusikan di bawah lisensi MIT: http://tomasmcguinness.mit-license.org/