### Install dotnet-passbook Package Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/README.md Use this command to install the dotnet-passbook library via NuGet Package Manager. ```powershell Install-Package dotnet-passbook ``` -------------------------------- ### NumberField Examples Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/fields.md Demonstrates creating and configuring NumberFields for displaying prices with currency codes and discounts as percentages. ```csharp // Price field var priceField = new NumberField("price", "Total", 99.99m, FieldNumberStyle.PKNumberStyleDecimal); priceField.CurrencyCode = "USD"; request.AddAuxiliaryField(priceField); // Discount percentage var discountField = new NumberField("discount", "Discount", 15, FieldNumberStyle.PKNumberStylePercent); request.AddAuxiliaryField(discountField); ``` -------------------------------- ### Complete Field Configuration Example Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/fields.md Demonstrates how to add various types of fields (Header, Primary, Secondary, Auxiliary, Back) to a PassGeneratorRequest. Includes examples of StandardField and NumberField with specific formatting. ```csharp var request = new PassGeneratorRequest { /* ... */ }; // Header request.AddHeaderField(new StandardField("event", "Concert 2024", "Summer Series")); // Primary request.AddPrimaryField(new StandardField("artist", "Artist", "The Beatles Experience")); request.AddPrimaryField(new StandardField("date", "Date", "July 15, 2024")); // Secondary request.AddSecondaryField(new StandardField("time", "Time", "8:00 PM")); request.AddSecondaryField(new StandardField("venue", "Venue", "Central Park")); // Auxiliary request.AddAuxiliaryField(new StandardField("section", "Section", "A")); request.AddAuxiliaryField(new StandardField("row", "Row", "12")); request.AddAuxiliaryField(new StandardField("seat", "Seat", "42")); var priceField = new NumberField("price", "Price", 99.99m, FieldNumberStyle.PKNumberStyleDecimal); priceField.CurrencyCode = "USD"; request.AddAuxiliaryField(priceField); // Back request.AddBackField(new StandardField("terms", "Terms", "Non-transferable. One use only.")); request.AddBackField(new StandardField("contact", "Contact", "support@example.com or 1-800-CONCERT")); ``` -------------------------------- ### PKCS12 Export Password Prompts Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/using-openssl.md Example of the password prompts encountered during the PKCS12 export process. ```html Enter pass phrase for passbook.key: Enter Export Password: Verifying - Enter Export Password: ``` -------------------------------- ### OpenSSL Key Generation Output Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/using-openssl.md Example output showing the RSA key generation process and passphrase prompts. ```bash Generating a RSA private key ............+++++ .................................................+++++ writing new private key to 'passbook.key' Enter PEM pass phrase: Verifying - Enter PEM pass phrase: ``` -------------------------------- ### FieldTextAlignment Example Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/fields.md Shows how to set the text alignment for a standard field to center. ```csharp var field = new StandardField("seat", "Seat", "42A"); field.TextAlignment = FieldTextAlignment.PKTextAlignmentCenter; request.AddAuxiliaryField(field); ``` -------------------------------- ### DateField Creation Example Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/types.md Demonstrates creating a DateField with specified keys, labels, and date-time styles for both date and time components. ```csharp var field = new DateField( "departure", "Departs", FieldDateTimeStyle.PKDateStyleMedium, FieldDateTimeStyle.PKDateStyleShort ); ``` -------------------------------- ### Certificate Setup for Cloud Deployments Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/errors.md Illustrates how to load X.509 certificates with specific key storage flags suitable for cloud or Windows Server environments, ensuring proper access to private keys. ```csharp // Load with appropriate flags for cloud/Windows Server environments X509KeyStorageFlags flags = X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable; var passbookCert = new X509Certificate2( System.IO.File.ReadAllBytes("passbook.pfx"), "certificate-password", flags ); var wwdrCert = new X509Certificate2( System.IO.File.ReadAllBytes("apple_wwdr.cer"), "", flags ); ``` -------------------------------- ### StandardField - Text Examples Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/quick-reference.md Demonstrates creating a StandardField for simple text values, enabling change notifications, and using HTML for attributed values. ```csharp // Simple text var field = new StandardField("key", "Label", "Value"); request.AddPrimaryField(field); // With change notification field.ChangeMessage = "Gate changed to %@"; // With HTML link field.AttributedValue = "Click here"; ``` -------------------------------- ### PassStyle Usage Example Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/types.md Demonstrates how to set the PassStyle when creating a PassGeneratorRequest. This example sets the style to EventTicket. ```csharp var request = new PassGeneratorRequest { Style = PassStyle.EventTicket, // ... }; ``` -------------------------------- ### StandardField Example Usage Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/fields.md Demonstrates creating and adding StandardFields to a request, including fields with change messages and HTML links. ```csharp // Simple field var field = new StandardField("passenger-name", "Passenger", "John Doe"); request.AddPrimaryField(field); // Field with change notification var gateField = new StandardField("gate", "Gate", "A12"); gateField.ChangeMessage = "Gate changed to %@"; request.AddSecondaryField(gateField); // Field with HTML link var linkField = new StandardField("ticket-details", "Details", "https://example.com"); linkField.AttributedValue = "View Ticket"; request.AddBackField(linkField); ``` -------------------------------- ### Add Barcode Example Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/types.md Example of how to add a barcode to a pass request using a specified BarcodeType, data, and encoding. ```csharp request.AddBarcode(BarcodeType.PKBarcodeFormatQR, "ticket-data", "iso-8859-1"); ``` -------------------------------- ### Example of DuplicateFieldKeyException Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/errors.md This example demonstrates how adding a field with a duplicate key triggers a DuplicateFieldKeyException. Ensure field keys are unique. ```csharp var request = new PassGeneratorRequest { /* ... */ }; request.AddPrimaryField(new StandardField("origin", "From", "SFO")); request.AddPrimaryField(new StandardField("origin", "Depart", "SFO")); // Throws! ``` -------------------------------- ### Example Scenarios for ManifestSigningException Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/errors.md Demonstrates scenarios that trigger a ManifestSigningException, such as setting the Passbook or Apple WWDR certificates to null before generating a pass. ```csharp var request = new PassGeneratorRequest { /* ... */ }; request.PassbookCertificate = null; // Throws ManifestSigningException request.AppleWWDRCACertificate = null; // Throws ManifestSigningException var generator = new PassGenerator(); byte[] result = generator.Generate(request); ``` -------------------------------- ### Usage of FieldNumberStyle for Price Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/types.md Example of creating a NumberField with a specific currency and decimal style. ```csharp var priceField = new NumberField("total", "Total", 99.99m, FieldNumberStyle.PKNumberStyleDecimal); priceField.CurrencyCode = "USD"; ``` -------------------------------- ### Complete Pass Generation Example with Relevance Data Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/relevance.md Demonstrates how to configure a `PassGeneratorRequest` with relevant dates, locations, and beacons, and set a maximum distance for relevance. ```csharp var request = new PassGeneratorRequest { PassTypeIdentifier = "pass.example.com.eventticket", // ... other configuration }; // Pass relevant during event hours request.AddRelevantDate( new DateTimeOffset(2024, 7, 15, 18, 00, 0, TimeSpan.Zero), new DateTimeOffset(2024, 7, 15, 23, 59, 0, TimeSpan.Zero) ); // Venue location request.AddLocation( 40.7849, -73.9754, "Show your pass at the north entrance" ); // Venue beacon request.AddBeacon( "F7826DA6-4FA2-4E98-8024-BC5B71E0893E", "You are near the entrance", major: 1, minor: 1 ); // Relevant within 500 meters of venue request.MaxDistance = 500; var generator = new PassGenerator(); byte[] passBytes = generator.Generate(request); ``` -------------------------------- ### Service Using IPassGenerator Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/ipass-generator.md Example of a service class that depends on the IPassGenerator interface. It demonstrates how to inject IPassGenerator and use it to create an event pass. ```csharp public class PassService { private readonly IPassGenerator _generator; public PassService(IPassGenerator generator) { _generator = generator; } public byte[] CreateEventPass(string eventName, string venue) { var request = new PassGeneratorRequest { PassTypeIdentifier = "pass.example.com.eventticket", TeamIdentifier = "ABCD123456", SerialNumber = Guid.NewGuid().ToString(), Description = eventName, OrganizationName = "Example Events", Style = PassStyle.EventTicket, PassbookCertificate = _certificateService.GetPassbookCert(), AppleWWDRCACertificate = _certificateService.GetAppleCert() }; // Configure request... return _generator.Generate(request); } } ``` -------------------------------- ### TransitType Usage Example Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/types.md Shows how to set both PassStyle to BoardingPass and TransitType when creating a PassGeneratorRequest. This example sets the transit type to Air. ```csharp var request = new PassGeneratorRequest { Style = PassStyle.BoardingPass, TransitType = TransitType.PKTransitTypeAir, // ... }; ``` -------------------------------- ### Initialize Pass Generator Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/README.md Start by declaring a PassGenerator instance to begin the pass creation process. ```cs PassGenerator generator = new PassGenerator(); ``` -------------------------------- ### Standard Field Creation Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/00-START-HERE.md Example of creating a standard text-based field for a pass. Use this for general information display. ```csharp new StandardField("key", "Label", "Value") ``` -------------------------------- ### DateField Example Usage Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/fields.md Demonstrates creating and adding DateFields to a request, including flight departure times and events with relative dates. ```csharp // Flight departure time var departureField = new DateField( "departure-time", "Departs", FieldDateTimeStyle.PKDateStyleMedium, FieldDateTimeStyle.PKDateStyleShort, new DateTime(2024, 7, 15, 14, 30, 0) ); request.AddPrimaryField(departureField); // Event with relative date var eventField = new DateField("event-date", "When", FieldDateTimeStyle.PKDateStyleLong, FieldDateTimeStyle.PKDateStyleNone); eventField.Value = new DateTime(2024, 7, 15); eventField.IsRelative = true; request.AddSecondaryField(eventField); ``` -------------------------------- ### Date/Time Field Creation Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/00-START-HERE.md Example of creating a date and/or time field. Supports different styling options for display. ```csharp new DateField("key", "Label", dateStyle, timeStyle, dateTime) ``` -------------------------------- ### Instantiate Nfc Object Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/types.md Example of how to create an Nfc object with message and encryption public key data. Consult Apple's PassKit documentation for current implementation details. ```csharp request.Nfc = new Nfc( message: "NFC_MESSAGE_DATA", encryptionPublicKey: "ENCODED_PUBLIC_KEY_BASE64" ); ``` -------------------------------- ### Number Field Creation Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/00-START-HERE.md Example of creating a numeric field. Allows for currency or other numerical values with specified styling. ```csharp new NumberField("key", "Label", 99.99m, numberStyle) ``` -------------------------------- ### OpenSSL CSR Details Prompt Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/using-openssl.md Example of the interactive prompts for Distinguished Name (DN) fields when generating a CSR. ```bash ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [XX]:GB State or Province Name (full name) []:London Locality Name (eg, city) [Default City]:London Organization Name (eg, company) [Default Company Ltd]:Ursatile Ltd Organizational Unit Name (eg, section) []:iOS Development Common Name (eg, your name or your server hostname) []:passbook.ursatile.com Email Address []:passbook@ursatile.com Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []: ``` -------------------------------- ### Initialize PassGenerator Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/pass-generator.md Creates a new instance of the PassGenerator class. No specific setup is required for this constructor. ```csharp public class PassGenerator : IPassGenerator { public PassGenerator() } ``` -------------------------------- ### Example: StandardField without value Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/errors.md Demonstrates adding a StandardField without a value, which will trigger RequiredFieldValueMissingException during pass generation. ```csharp // StandardField without value var field = new StandardField("seat", "Seat"); // No value request.AddPrimaryField(field); var generator = new PassGenerator(); byte[] result = generator.Generate(request); // Throws RequiredFieldValueMissingException ``` -------------------------------- ### ASP.NET MVC Controller for Pass Generation Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/ipass-generator.md An example of an ASP.NET MVC controller that uses the IPassGenerator to create and serve event passes and pass bundles. It handles pass generation requests and returns the generated pass files. ```csharp [ApiController] [Route("api/[controller]")] public class PassesController : ControllerBase { private readonly IPassGenerator _generator; private readonly ICertificateService _certificates; public PassesController(IPassGenerator generator, ICertificateService certificates) { _generator = generator; _certificates = certificates; } [HttpGet("event/{eventId}")] public IActionResult GetEventPass(string eventId) { try { var eventData = _eventService.GetEvent(eventId); var request = new PassGeneratorRequest { PassTypeIdentifier = "pass.example.com.eventticket", TeamIdentifier = "ABCD123456", SerialNumber = eventId, Description = eventData.Title, OrganizationName = "Example Events", Style = PassStyle.EventTicket, PassbookCertificate = _certificates.GetPassbookCert(), AppleWWDRCACertificate = _certificates.GetAppleCert() }; // Configure fields, images, etc... byte[] passBytes = _generator.Generate(request); return File(passBytes, "application/vnd.apple.pkpass", $"event-{eventId}.pkpass"); } catch (ManifestSigningException ex) { return BadRequest($"Pass generation failed: {ex.Message}"); } } [HttpPost("bundle")] public IActionResult GetPassBundle([FromBody] BundleRequest bundleRequest) { try { var requests = bundleRequest.EventIds .Select(id => BuildPassRequest(id)) .ToList(); byte[] bundleBytes = _generator.Generate(requests); return File(bundleBytes, "application/vnd.apple.pkpasses", "events.pkpasses"); } catch (ArgumentException ex) { return BadRequest("Bundle must contain at least one pass"); } } } ``` -------------------------------- ### Define Boarding Pass Fields Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/README.md Set the pass style to BoardingPass and add specific fields to the primary, secondary, and auxiliary sections. This example shows how to add origin, destination, gate, seat, and passenger name. ```cs 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; ``` -------------------------------- ### Pass Generator Request Builder Example Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/README.md A simple builder class to construct PassGeneratorRequest objects. This allows for testing the request object's properties directly. ```csharp class PassGeneratorRequestBuilder { public PassGeneratorRequest BuildRequestWithLogoTextAndBackgroundColor(String logoText, String backgroundColor) { var request = new PassGeneratorRequest(); request.LogoText = logoText; request.BackgroundColor = backgroundColor; return request; } } ``` -------------------------------- ### Applying DataDetectorTypes to a Field Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/fields.md Example of how to apply a DataDetectorTypes enum value to a 'notes' field to enable phone number detection. Also shows how to combine multiple detectors using the bitwise OR operator. ```csharp var field = new StandardField("notes", "Notes", "Contact us at 555-1234"); field.DataDetectorTypes = DataDetectorTypes.PKDataDetectorTypePhoneNumber; request.AddBackField(field); // Multiple detectors field.DataDetectorTypes = DataDetectorTypes.PKDataDetectorTypeLink | DataDetectorTypes.PKDataDetectorTypePhoneNumber; ``` -------------------------------- ### Usage of FieldTextAlignment for Seat Number Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/types.md Example of setting the text alignment for a standard field, such as a seat number. ```csharp var field = new StandardField("seat", "Seat", "42A"); field.TextAlignment = FieldTextAlignment.PKTextAlignmentCenter; ``` -------------------------------- ### ASP.NET Integration with PassGenerator Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/00-START-HERE.md Demonstrates how to register PassGenerator for dependency injection in ASP.NET and how to generate and return a pass file from a controller. ```csharp // Startup services.AddScoped(); ``` ```csharp // Controller [HttpGet("pass/{id}")] public IActionResult GetPass(string id) { var request = new PassGeneratorRequest { /* ... */ }; byte[] pass = _generator.Generate(request); return File(pass, "application/vnd.apple.pkpass", $"{id}.pkpass"); } ``` -------------------------------- ### Add Time Relevance to Pass Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/00-START-HERE.md Adds time-based relevance to the pass by specifying a start and end date. ```csharp request.AddRelevantDate(startTime, endTime); ``` -------------------------------- ### DateField - Date and Time Formatting Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/quick-reference.md Shows how to create a DateField, specifying date and time styles, and enabling relative time display. ```csharp var field = new DateField( "arrival-time", "Arrives", FieldDateTimeStyle.PKDateStyleMedium, // Date format FieldDateTimeStyle.PKDateStyleShort, // Time format DateTime.Now.AddHours(2) ); field.IsRelative = true; // Show as "in 2 hours" instead of date request.AddPrimaryField(field); ``` -------------------------------- ### Configure Web Service Integration Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/configuration.md Set the authentication token and the HTTPS URL for the web service that will provide updates for the pass. Both properties must be provided together if either is set. ```csharp request.AuthenticationToken = "secret-token-12345"; request.WebServiceUrl = "https://api.example.com/passes"; ``` -------------------------------- ### Example: Missing required request property Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/errors.md Illustrates a PassGeneratorRequest missing a required property (PassTypeIdentifier), which will lead to RequiredFieldValueMissingException. ```csharp // Missing required request property var request = new PassGeneratorRequest { // Missing PassTypeIdentifier SerialNumber = "123", Description = "Test", TeamIdentifier = "ABC", OrganizationName = "Example" }; // Throws RequiredFieldValueMissingException during generation ``` -------------------------------- ### Testing IPassGenerator with Moq Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/ipass-generator.md Demonstrates how to mock the IPassGenerator interface using Moq for unit testing a service. It verifies that the Generate method is called with the correct arguments. ```csharp using Moq; using Xunit; public class PassServiceTests { [Fact] public void GeneratePass_CallsGeneratorWithCorrectRequest() { // Arrange var generatorMock = new Mock(); generatorMock .Setup(g => g.Generate(It.IsAny())) .Returns(new byte[] { 1, 2, 3 }); var service = new PassService(generatorMock.Object); // Act var result = service.CreateEventPass("Concert", "Central Park"); // Assert generatorMock.Verify( g => g.Generate(It.Is(r => r.Description == "Concert")), Times.Once ); Assert.NotNull(result); } } ``` -------------------------------- ### Load X509Certificate2 from PFX Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/configuration.md Load a certificate from a .pfx file using its password. Ensure the .pfx file contains the private key. ```csharp var passbookCert = new X509Certificate2( File.ReadAllBytes("passbook.pfx"), "password" ); ``` -------------------------------- ### Export Certificate to PKCS12 Format Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/using-openssl.md Bundles the private key and PEM certificate into a PFX file for .NET compatibility. ```bash openssl pkcs12 -export -out passbook.pfx -inkey passbook.key -in passbook.pem ``` -------------------------------- ### Basic Pass Generation Flow Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/quick-reference.md This snippet demonstrates the fundamental steps to generate a pass, including creating a generator, setting up the request with required fields, adding images and fields, and finally generating the pass bytes. ```csharp var generator = new PassGenerator(); var request = new PassGeneratorRequest { PassTypeIdentifier = "pass.example.com.eventticket", TeamIdentifier = "ABCD123456", SerialNumber = Guid.NewGuid().ToString(), Description = "Event Ticket", OrganizationName = "My Organization", Style = PassStyle.EventTicket, // Load certificates PassbookCertificate = LoadCertificate("passbook.pfx", "password"), AppleWWDRCACertificate = LoadCertificate("apple_wwdr.cer", "") }; request.Images.Add(PassbookImage.Icon, File.ReadAllBytes("icon.png")); request.Images.Add(PassbookImage.Icon2X, File.ReadAllBytes("icon@2x.png")); request.Images.Add(PassbookImage.Icon3X, File.ReadAllBytes("icon@3x.png")); request.AddPrimaryField(new StandardField("event", "Event", "Concert 2024")); request.AddPrimaryField(new StandardField("venue", "Venue", "Central Park")); request.AddBarcode(BarcodeType.PKBarcodeFormatQR, "ticket-123", "utf-8"); byte[] passBytes = generator.Generate(request); return File(passBytes, "application/vnd.apple.pkpass", "ticket.pkpass"); ``` -------------------------------- ### Load X509Certificate2 with Key Storage Flags Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/configuration.md Load a certificate with specific X509KeyStorageFlags, useful for cloud or IIS deployments where key storage needs to be managed. ```csharp // For cloud/IIS deployment X509KeyStorageFlags flags = X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable; var cert = new X509Certificate2(certBytes, password, flags); ``` -------------------------------- ### NumberField - Decimal and Percentage Formatting Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/quick-reference.md Illustrates creating NumberFields for decimal currency values and percentage values, specifying currency codes and styles. ```csharp // Decimal var priceField = new NumberField("price", "Price", 99.99m, FieldNumberStyle.PKNumberStyleDecimal); priceField.CurrencyCode = "USD"; request.AddAuxiliaryField(priceField); // Percent var discountField = new NumberField("discount", "Discount", 15, FieldNumberStyle.PKNumberStylePercent); request.AddAuxiliaryField(discountField); ``` -------------------------------- ### Testing IPassGenerator with NSubstitute Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/ipass-generator.md Demonstrates how to mock the IPassGenerator interface using NSubstitute for unit testing a service. It verifies that the Generate method is called with the correct arguments. ```csharp using NSubstitute; using Xunit; public class PassServiceTests { [Fact] public void GeneratePass_CallsGeneratorWithCorrectRequest() { // Arrange var generatorMock = Substitute.For(); generatorMock .Generate(Arg.Any()) .Returns(new byte[] { 1, 2, 3 }); var service = new PassService(generatorMock); // Act var result = service.CreateEventPass("Concert", "Central Park"); // Assert generatorMock.Received(1).Generate(Arg.Is(r => r.Description == "Concert" )); Assert.NotNull(result); } } ``` -------------------------------- ### Load PFX Certificate for Passbook Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/quick-reference.md Helper method to load a PFX certificate with or without a password. Supports machine key set and exportable flags for cloud deployments. ```csharp private X509Certificate2 LoadCertificate(string filename, string password) { byte[] certBytes = File.ReadAllBytes(filename); if (string.IsNullOrEmpty(password)) { return new X509Certificate2(certBytes); } else { // For cloud deployment X509KeyStorageFlags flags = X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable; return new X509Certificate2(certBytes, password, flags); } } // Usage request.PassbookCertificate = LoadCertificate("passbook.pfx", "password"); request.AppleWWDRCACertificate = LoadCertificate("apple_wwdr.cer", ""); ``` -------------------------------- ### Configure Web Service Updates Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/00-START-HERE.md Configures the pass to use a web service for updates. Set the authentication token and the web service URL. ```csharp request.AuthenticationToken = "secret"; request.WebServiceUrl = "https://api.example.com/passes"; ``` -------------------------------- ### Mock Pass Generator for Testing Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/00-START-HERE.md Shows how to use Moq to mock the IPassGenerator interface for unit testing services that depend on it. ```csharp // With Moq var mock = new Mock(); mock.Setup(g => g.Generate(It.IsAny())) .Returns(new byte[] { 1, 2, 3 }); var service = new MyService(mock.Object); ``` -------------------------------- ### Configure PassGeneratorRequest in .NET Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/using-openssl.md Sets up the certificate and password in the dotnet-passbook generator request. ```csharp var request = new PassGeneratorRequest(); request.AppleWWDRCACertificate = File.ReadAllBytes("AppleWWDRCA.cer"); request.Certificate = File.ReadAllBytes("passbook.pfx"); request.CertificatePassword = ""; ``` -------------------------------- ### Build Event Ticket Pass Request Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/ipass-generator.md Demonstrates how to build a pass generation request for an event ticket. This is useful for testing the request building logic without generating actual passes. ```csharp public class PassRequestBuilderTests { [Fact] public void BuildRequest_SetsEventTicketStyle() { // Arrange var builder = new PassRequestBuilder(); // Act var request = builder.BuildEventTicket("Concert", "Central Park"); // Assert Assert.Equal(PassStyle.EventTicket, request.Style); Assert.Equal("Concert", request.Description); } } public class PassRequestBuilder { public PassGeneratorRequest BuildEventTicket(string title, string venue) { var request = new PassGeneratorRequest { PassTypeIdentifier = "pass.example.com.eventticket", SerialNumber = Guid.NewGuid().ToString(), Description = title, TeamIdentifier = "ABCD123456", OrganizationName = "Example Events", Style = PassStyle.EventTicket }; request.AddPrimaryField(new StandardField("venue", "Venue", venue)); return request; } } ``` -------------------------------- ### Set DataDetectorTypes for a Field Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/types.md Demonstrates how to set the DataDetectorTypes for a StandardField, including setting a single detector and combining multiple detectors. ```csharp var field = new StandardField("notes", "Notes", "Contact: 555-1234"); field.DataDetectorTypes = DataDetectorTypes.PKDataDetectorTypePhoneNumber; // Multiple detectors field.DataDetectorTypes = DataDetectorTypes.PKDataDetectorTypeLink | DataDetectorTypes.PKDataDetectorTypePhoneNumber; ``` -------------------------------- ### Add Pass Images Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/README.md Define the images for the pass, including standard and retina (2x, 3x) versions. Images should be supplied as byte arrays. ```cs 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/icon@2x.png"))); request.Images.Add(PassbookImage.Icon3X, System.IO.File.ReadAllBytes(Server.MapPath("~/Icons/icon@3x.png"))); ``` -------------------------------- ### EventTicket Pass Configuration Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/quick-reference.md This configuration sets up an EventTicket pass with specific styling, header, primary, secondary, and auxiliary fields, along with a QR barcode. Ensure you have the necessary certificates and images loaded. ```csharp var request = new PassGeneratorRequest { PassTypeIdentifier = "pass.example.com.eventticket", TeamIdentifier = "ABCD123456", SerialNumber = Guid.NewGuid().ToString(), Description = "Concert Ticket", OrganizationName = "Example Events", Style = PassStyle.EventTicket, BackgroundColor = "rgb(23, 187, 82)", ForegroundColor = "rgb(255, 255, 255)", PassbookCertificate = LoadCertificate("passbook.pfx", "password"), AppleWWDRCACertificate = LoadCertificate("apple_wwdr.cer", "") }; request.Images.Add(PassbookImage.Icon, LoadImage("icon.png")); request.Images.Add(PassbookImage.Icon2X, LoadImage("icon@2x.png")); request.Images.Add(PassbookImage.Icon3X, LoadImage("icon@3x.png")); request.AddHeaderField(new StandardField("event", "Concert 2024", "")); request.AddPrimaryField(new StandardField("artist", "Artist", "The Beatles Experience")); request.AddPrimaryField(new StandardField("venue", "Venue", "Central Park")); request.AddSecondaryField(new StandardField("date", "Date", "July 15, 2024")); request.AddSecondaryField(new StandardField("time", "Time", "8:00 PM")); request.AddAuxiliaryField(new StandardField("section", "Section", "A")); request.AddAuxiliaryField(new StandardField("row", "Row", "12")); request.AddAuxiliaryField(new StandardField("seat", "Seat", "42")); request.AddBarcode(BarcodeType.PKBarcodeFormatQR, "TICKET-12345", "utf-8"); var generator = new PassGenerator(); byte[] passBytes = generator.Generate(request); ``` -------------------------------- ### Provide Certificates Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/README.md You must provide both the Apple WWDR and your Passbook certificate as X509Certificate instances. Ensure correct X509KeyStorageFlags are used for cloud-based certificate generation to avoid signing issues. ```cs request.AppleWWDRCACertificate = new X509Certificate(...); request.PassbookCertificate = new X509Certificate(...); ``` ```cs X509KeyStorageFlags flags = X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable; X509Certificate2 certificate = new X509Certificate2(bytes, password, flags); ``` -------------------------------- ### Load X509Certificate2 from CER Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/configuration.md Load a certificate from a .cer file. These typically contain only the public key and do not require a password. ```csharp var wwdrCert = new X509Certificate2( File.ReadAllBytes("apple_wwdr.cer") ); ``` -------------------------------- ### Catching FileNotFoundException Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/errors.md Shows how to specifically catch a FileNotFoundException during pass generation and log a user-friendly message. ```csharp try { byte[] result = generator.Generate(request); } catch (FileNotFoundException ex) { Console.WriteLine($"Certificate not found: {ex.Message}"); } ``` -------------------------------- ### Set Background and Foreground Colors Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/quick-reference.md Use hex or RGB formats to define the background and foreground colors of the pass. Ensure colors are valid hex codes or RGB strings. ```csharp request.BackgroundColor = "#FF6600"; // Hex request.BackgroundColor = "rgb(255, 102, 0)"; // RGB request.ForegroundColor = "#000000"; // Black hex request.ForegroundColor = "rgb(0, 0, 0)"; // Black RGB ``` -------------------------------- ### Verifying Service Calls with NSubstitute and xUnit Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/README.md Use NSubstitute to mock IPassGenerator and xUnit to assert that the Generate method is called with the correct PassGeneratorRequest arguments. ```csharp [Fact] void ServiceUsesPassedParamsForRequest() { // Arrange var passGeneratorMock = Substitute.For(); var sut = new PassGeneratorService(passGeneratorMock); // Act sut.GeneratePassWithLogoTextAndBackgroundColor("Cup'o'Joe", "#0CAFE0"); // Assert/Verify passGeneratorMock.Received().Generate(Arg.Is(r => { r.LogoText == "Cup'o'Joe" && r.BackgroundColor == "#0CAFE0" })); } ``` -------------------------------- ### Return Passbook File in ASP.NET MVC Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/quick-reference.md Generates the passbook file and returns it as a downloadable file with the correct MIME type. ```csharp byte[] passBytes = generator.Generate(request); return File( passBytes, "application/vnd.apple.pkpass", "ticket.pkpass" ); ``` -------------------------------- ### Generate Coupon Pass Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/quick-reference.md Use this snippet to generate a coupon pass. It includes setting pass details, images, header, primary, and auxiliary fields, as well as a barcode. ```csharp var request = new PassGeneratorRequest { PassTypeIdentifier = "pass.example.com.coupon", TeamIdentifier = "ABCD123456", SerialNumber = Guid.NewGuid().ToString(), Description = "Summer Discount", OrganizationName = "Example Store", Style = PassStyle.Coupon, BackgroundColor = "rgb(255, 200, 0)", ForegroundColor = "rgb(0, 0, 0)", PassbookCertificate = LoadCertificate("passbook.pfx", "password"), AppleWWDRCACertificate = LoadCertificate("apple_wwdr.cer", "") }; request.Images.Add(PassbookImage.Icon, LoadImage("icon.png")); request.Images.Add(PassbookImage.Icon2X, LoadImage("icon@2x.png")); request.Images.Add(PassbookImage.Icon3X, LoadImage("icon@3x.png")); request.AddHeaderField(new StandardField("offer", "Offer", "50% Off")); request.AddPrimaryField(new StandardField("description", "Offer", "Summer Sale")); request.AddAuxiliaryField(new StandardField("code", "Code", "SUMMER50")); request.AddBarcode(BarcodeType.PKBarcodeFormatCode128, "9876543210", "iso-8859-1"); var generator = new PassGenerator(); byte[] passBytes = generator.Generate(request); ``` -------------------------------- ### AddBeacon(string, string) Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/relevance.md Adds a beacon to the pass without major/minor identifiers, but with display text. ```APIDOC ## AddBeacon(string, string) ### Description Adds a beacon without major/minor identifiers. ### Method `AddBeacon(string proximityUUID, string relevantText)` ``` -------------------------------- ### Minimal PassGeneratorRequest Configuration Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/configuration.md This snippet shows the essential properties to set for generating a Passbook pass, including identifiers, descriptions, team details, pass style, certificates, images, and primary fields. All configuration is applied programmatically. ```csharp var request = new PassGeneratorRequest { PassTypeIdentifier = "pass.example.com.generic", SerialNumber = "001", Description = "My Pass", TeamIdentifier = "ABCD123456", OrganizationName = "My Organization", Style = PassStyle.Generic, PassbookCertificate = LoadCertificate("passbook.pfx", "password"), AppleWWDRCACertificate = LoadCertificate("apple_wwdr.cer", "") }; request.Images.Add(PassbookImage.Icon, File.ReadAllBytes("icon.png")); request.Images.Add(PassbookImage.Icon2X, File.ReadAllBytes("icon@2x.png")); request.Images.Add(PassbookImage.Icon3X, File.ReadAllBytes("icon@3x.png")); request.AddPrimaryField(new StandardField("title", "Title", "Sample Pass")); var generator = new PassGenerator(); byte[] passBytes = generator.Generate(request); ``` -------------------------------- ### Triggering FileNotFoundException Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/errors.md This snippet demonstrates how a FileNotFoundException can be triggered when certificate files are not properly loaded or are missing. ```csharp var request = new PassGeneratorRequest { /* ... */ }; // Certificates not loaded, left as null var generator = new PassGenerator(); byte[] result = generator.Generate(request); // Throws FileNotFoundException ``` -------------------------------- ### Add Passbook Images to a Request Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/types.md Shows how to add various passbook image types, including different scales, to a request object by reading image files. ```csharp request.Images.Add(PassbookImage.Icon, File.ReadAllBytes("icon.png")); request.Images.Add(PassbookImage.Icon2X, File.ReadAllBytes("icon@2x.png")); request.Images.Add(PassbookImage.Icon3X, File.ReadAllBytes("icon@3x.png")); request.Images.Add(PassbookImage.Logo, File.ReadAllBytes("logo.png")); request.Images.Add(PassbookImage.Logo2X, File.ReadAllBytes("logo@2x.png")); request.Images.Add(PassbookImage.Logo3X, File.ReadAllBytes("logo@3x.png")); ``` -------------------------------- ### Comprehensive Exception Handling for Pass Generation Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/errors.md Illustrates a robust approach to handling various exceptions that may occur during pass generation, re-throwing them as InvalidOperationException with context. ```csharp public byte[] GeneratePass(PassGeneratorRequest request) { var generator = new PassGenerator(); try { return generator.Generate(request); } catch (ArgumentNullException ex) { // Invalid input throw new InvalidOperationException("Pass generation request is invalid", ex); } catch (DuplicateFieldKeyException ex) { // Configuration error throw new InvalidOperationException($"Field configuration error: {ex.Message}", ex); } catch (RequiredFieldValueMissingException ex) { // Missing required data throw new InvalidOperationException($"Missing required data: {ex.Message}", ex); } catch (FileNotFoundException ex) { // Certificate setup issue throw new InvalidOperationException("Certificate configuration error", ex); } catch (ManifestSigningException ex) { // Signing failure throw new InvalidOperationException("Pass signing failed", ex); } catch (Exception ex) { // Unexpected error throw new InvalidOperationException("Unexpected error during pass generation", ex); } } ``` -------------------------------- ### Load Image for Passbook Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/quick-reference.md Helper method to load image files from the local file system. Assumes images are stored in an 'images' subdirectory. ```csharp private byte[] LoadImage(string filename) { string imagePath = Path.Combine("images", filename); return File.ReadAllBytes(imagePath); } // Usage request.Images.Add(PassbookImage.Icon, LoadImage("icon.png")); ``` -------------------------------- ### Generate CSR and Private Key with OpenSSL Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/using-openssl.md Creates a new 2048-bit RSA key and a corresponding certificate signing request. Requires a passphrase for the private key. ```bash openssl req -new -newkey rsa:2048 -out passbook.csr -keyout passbook.key ``` -------------------------------- ### Convert Certificate to PEM Format Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/using-openssl.md Converts a DER binary-encoded certificate file into a PEM text format. ```bash openssl x509 -in pass.cer -inform der -out passbook.pem ``` -------------------------------- ### AddBeacon(string, string, int, int) Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/relevance.md Adds a beacon to the pass with major and minor identifiers, and display text. ```APIDOC ## AddBeacon(string, string, int, int) ### Description Adds a beacon with major and minor identifiers. ### Method `AddBeacon(string proximityUUID, string relevantText, int major, int minor)` ``` -------------------------------- ### Catching ManifestSigningException Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/errors.md Shows how to catch a ManifestSigningException and access its message and inner exception details for debugging signing failures. ```csharp try { var passBytes = generator.Generate(request); } catch (ManifestSigningException ex) { Console.WriteLine($"Manifest signing failed: {ex.Message}"); if (ex.InnerException != null) { Console.WriteLine($"Reason: {ex.InnerException.Message}"); } } ``` -------------------------------- ### Return Pass Bundle in ASP.NET MVC Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/README.md Return the generated pass bundle byte array as a FileContentResult. The MIME type should be 'application/vnd.apple.pkpasses' and the FileDownloadName should end with '.pkpasses.zip'. ```cs return new FileContentResult(generatedBundle, "application/vnd.apple.pkpasses") { FileDownloadName = "tickets.pkpasses.zip" }; ``` -------------------------------- ### Set Authentication Token and Web Service URL for Pass Updates Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/README.md Configure pass updates by providing an AuthenticationToken and a WebServiceUrl. The WebServiceUrl must be HTTPS by default. The authentication token is included in request headers for API authorization. ```csharp request.AuthenticationToken = ""; request.WebServiceUrl = "https://"; ``` -------------------------------- ### Add Semantic Tags to Request Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/types.md Demonstrates adding semantic tags like boarding group and seat information to a request object. ```csharp request.SemanticTags.Add(new boardingGroup { value = "A" }); request.SemanticTags.Add(new seat { value = "12A" }); ``` -------------------------------- ### Add Beacon with Major and Minor Identifiers Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/pass-generator-request.md Adds a beacon location, including the proximity UUID, relevant text, and both major and minor identifiers. ```csharp public void AddBeacon(string proximityUUID, string relevantText, int major, int minor) ``` -------------------------------- ### AddBeacon(string, string, int) Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/relevance.md Adds a beacon to the pass with a major identifier and display text. ```APIDOC ## AddBeacon(string, string, int) ### Description Adds a beacon with major identifier only. ### Method `AddBeacon(string proximityUUID, string relevantText, int major)` ``` -------------------------------- ### Generate Single Pass Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/ipass-generator.md Generates a single Apple Wallet pass (.pkpass file). Ensure certificates are found and field keys/values are valid to avoid exceptions. ```csharp byte[] Generate(PassGeneratorRequest generatorRequest) ``` -------------------------------- ### BoardingPass Configuration Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/quick-reference.md This configuration sets up a BoardingPass with specific transit type, colors, images (including logo), and fields relevant to air travel. It also includes a PDF417 barcode. Ensure the TransitType is set for boarding passes. ```csharp var request = new PassGeneratorRequest { PassTypeIdentifier = "pass.example.com.boardingpass", TeamIdentifier = "ABCD123456", SerialNumber = Guid.NewGuid().ToString(), Description = "Boarding Pass", OrganizationName = "Example Airlines", Style = PassStyle.BoardingPass, TransitType = TransitType.PKTransitTypeAir, // Required for boarding passes BackgroundColor = "rgb(0, 100, 200)", ForegroundColor = "rgb(255, 255, 255)", PassbookCertificate = LoadCertificate("passbook.pfx", "password"), AppleWWDRCACertificate = LoadCertificate("apple_wwdr.cer", "") }; request.Images.Add(PassbookImage.Icon, LoadImage("icon.png")); request.Images.Add(PassbookImage.Icon2X, LoadImage("icon@2x.png")); request.Images.Add(PassbookImage.Icon3X, LoadImage("icon@3x.png")); request.Images.Add(PassbookImage.Logo, LoadImage("logo.png")); request.Images.Add(PassbookImage.Logo2X, LoadImage("logo@2x.png")); request.Images.Add(PassbookImage.Logo3X, LoadImage("logo@3x.png")); request.AddPrimaryField(new StandardField("departure-airport", "From", "SFO")); request.AddPrimaryField(new StandardField("arrival-airport", "To", "LHR")); request.AddSecondaryField(new StandardField("gate", "Gate", "A55")); request.AddAuxiliaryField(new StandardField("seat", "Seat", "12A")); request.AddAuxiliaryField(new StandardField("passenger", "Passenger", "John Doe")); request.AddBarcode(BarcodeType.PKBarcodeFormatPDF417, "*E0ABC123...*0", "iso-8859-1"); var generator = new PassGenerator(); byte[] passBytes = generator.Generate(request); ``` -------------------------------- ### StandardField Constructors Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/fields.md Defines the constructors for creating a StandardField, used for arbitrary string values. ```csharp public class StandardField : Field { public StandardField(); public StandardField(string key, string label, string value); public StandardField(string key, string label, string value, string attributedValue, DataDetectorTypes dataDetectorTypes); } ``` -------------------------------- ### Barcode Class Constructor Source: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/_autodocs/api-reference/barcodes.md Represents a single barcode to be displayed on a pass. This constructor allows specifying the barcode type, message, and encoding. ```APIDOC ## Barcode Constructor ### Description Initializes a new instance of the Barcode class with the specified type, message, and encoding. ### Method Constructor ### Parameters - **type** (BarcodeType) - Required - Format of barcode (QR, PDF417, Aztec, Code128) - **message** (string) - Required - Data to encode in barcode - **encoding** (string) - Required - Text encoding (typically "iso-8859-1" or "utf-8") ```