Integrating Apple Wallet and Google Wallet in .NET MAUI: A Production-Grade Cross-Platform Strategy
βοΈ Introduction
Digital wallet ecosystems such as Apple Wallet and Google Wallet have evolved far beyond simple payment tools. Today, they serve as secure containers for:
- Boarding passes
- Event tickets
- Loyalty cards
- Membership credentials
- Access control passes For mobile developers working with .NET MAUI, integrating these capabilities introduces a paradox:
You are building a single cross-platform app, but interacting with two completely different ecosystems.
This guide goes far beyond a basic integration. Weβll design a production-ready architecture, covering:
- Platform abstractions in MAUI
- Backend responsibilities (critical)
- Pass generation pipelines
- Security considerations
- Real-world pitfalls
- Scalability patterns
π§ Understanding the Core Problem
Before writing any code, internalize this: π Apple Wallet and Google Wallet are not UI features
π They are secure, platform-governed ecosystems This means:
- You cannot βjust add a cardβ
- You must follow strict signing + validation flows
- The mobile app is only a delivery mechanism
βοΈ Platform Differences in Depth
| Capability | Apple Wallet | Google Wallet |
|---|---|---|
| Data Format | .pkpass (ZIP package) | JSON (Wallet Objects API) |
| Signing | PKI Certificates | JWT (Signed with Service Account) |
| Distribution | File / URL | API + Save URL |
| Update Mechanism | Push Notification (APNs) | REST API updates |
| Security Model | Certificate trust chain | OAuth2 + JWT |
ποΈ High-Level Architecture
A correct architecture separates concerns clearly:
[ MAUI App ]
|
| (Request pass)
v
[ Backend API ]
|
|-- Apple Pass Generator (.pkpass)
|-- Google Wallet API (JWT)
|
v
[ Wallet Platform ]
π§± Step 1 β MAUI Abstraction Layer
Your MAUI app should never contain platform-specific logic directly in UI code. Define a contract:
public interface IWalletService
{
Task AddPassAsync(WalletRequest request);
}
Where:
public class WalletRequest
{
public string PassId { get; set; }
public string PlatformPayload { get; set; }
}
π± Platform Implementations
π iOS β Apple Wallet (PassKit)
Apple uses the PassKit framework, exposed via native bindings.
using PassKit;
using Foundation;
public class AppleWalletService : IWalletService
{
public async Task AddPassAsync(WalletRequest request)
{
var url = NSUrl.FromString(request.PlatformPayload);
var data = NSData.FromUrl(url);
var pass = new PKPass(data, out NSError error);
if (pass == null)
throw new Exception(error?.LocalizedDescription);
var library = new PKPassLibrary();
library.AddPass(pass);
}
}
π Key Observations
- The app does NOT create the pass
- It only downloads and installs it
- Apple enforces strict signature validation
π€ Android β Google Wallet
Google Wallet works differently: no file, only API + JWT.
public class GoogleWalletService : IWalletService
{
public Task AddPassAsync(WalletRequest request)
{
var intent = new Intent(Intent.ActionView, Android.Net.Uri.Parse(request.PlatformPayload));
Platform.CurrentActivity.StartActivity(intent);
return Task.CompletedTask;
}
}
π Key Observations
- Payload is a JWT URL
- The Wallet UI is handled externally
- No local pass storage like iOS
π Dependency Injection Wiring
#if ANDROID
builder.Services.AddSingleton<IWalletService, GoogleWalletService>();
#elif IOS
builder.Services.AddSingleton<IWalletService, AppleWalletService>();
#endif
βοΈ Step 2 β Backend Responsibilities (Critical Section)
This is where 90% of the complexity lives.
π Apple Wallet Backend Pipeline
### 1. Create `pass.json`
{
"description": "Event Ticket",
"formatVersion": 1,
"organizationName": "Your Company",
"serialNumber": "123456",
"teamIdentifier": "ABCDE12345",
"barcode": {
"format": "PKBarcodeFormatQR",
"message": "USER123"
}
}
2. Add Assets
- icon.png
- logo.png
- background.png
3. Generate Manifest
SHA-1 hashes of all files:
{
"pass.json": "abc123...",
"icon.png": "def456..."
}
4. Sign the Manifest
Using Apple-issued certificate:
openssl smime -sign ...
5. Zip Everything β .pkpass
π€ Google Wallet Backend Pipeline
1. Create Pass Class
{
"id": "issuerId.loyaltyClass",
"programName": "My Loyalty Program"
}
2. Create Pass Object
{
"id": "issuerId.user123",
"classId": "issuerId.loyaltyClass",
"state": "ACTIVE"
}
3. Generate JWT
{
"iss": "service-account@project.iam.gserviceaccount.com",
"aud": "google",
"typ": "savetowallet",
"payload": {
"loyaltyObjects": [ ... ]
}
}
4. Return Save URL
https://pay.google.com/gp/v/save/<JWT>
π Security Considerations
This is not optional β itβs mandatory.
Apple Wallet
- Certificates expire yearly β οΈ
- Private keys must NEVER be exposed
- Use secure backend storage (Key Vault, HSM)
Google Wallet
- JWT must be signed with service account
- Never generate JWT on device
- Use short-lived tokens
π¨ Common Mistakes (Real-World)
β Generating passes in MAUI app
π Wrong. Always backend.
β Embedding certificates in mobile app
π Critical security flaw.
β Treating both platforms the same
π Architecturally incorrect.
β Not handling pass updates
π Breaks real-world scenarios (tickets, loyalty)
π Handling Pass Updates
Apple Wallet
- Uses APNs push notifications
- Device pulls updated pass from your server
Google Wallet
- Update via REST API
- Changes reflect instantly
π Performance Considerations
| Concern | Recommendation |
|---|---|
| Pass generation | Cache static templates |
| Signing | Use background workers |
| API latency | Pre-generate where possible |
| Scaling | Queue-based processing (Azure Queue) |
βοΈ Recommended Backend Stack
Given your ecosystem, a strong setup would be:
- ASP.NET Core API
- Azure Functions (for pass generation)
- Azure Key Vault (certificates)
- Azure Storage (pkpass hosting)
π§ͺ Testing Strategy
iOS
- Test on real device (simulator limited)
- Validate certificate chain
Android
- Test multiple accounts
- Validate JWT expiration
π Advanced Scenarios
ποΈ Dynamic Event Tickets
- QR changes every X minutes
π Loyalty Systems
- Points updated in real-time
π Secure Access Passes
- Time-limited authentication
π Location-Based Triggers
- Wallet surfaces pass automatically
π§Ύ Final Thoughts
Integrating Apple Wallet and Google Wallet in .NET MAUI is less about writing code and more about designing a distributed, secure system. If you approach it correctly: β
Clean MAUI abstraction
β
Strong backend pipeline
β
Proper security handling You end up with a feature that feels native, powerful, and production-grade.
π Closing Insight
The real skill is not βadding a passβ β
itβs designing the system that makes that pass trustworthy.
