Building for wallets as infrastructure, not as a one-off feature
Scott Twiname
Passform Co-founder and CTO

Adding a pass to Apple Wallet or Google Wallet can look like a simple feature. But once you need to support updates, multiple use cases, changing templates and real operational scale, it quickly becomes an infrastructure problem. In this article, we look at the engineering decisions that matter when building wallet functionality for the long term, and why treating passes as lifecycle-driven products leads to a much more reliable system.
A lot of teams will start in the same place with Google Wallet and Apple Wallet, they have an idea to go with a campaign, an event or loyalty, and they want to add a pass.
Initially this looks straight forward, design and generate a pass. Put a simple button on a page. And let people add it to their wallet.
This is usually the first mistake and from a developer point of view this is the start of “technical debt”. If you treat this like a one-off, you tend to build the minimum to get something out the door and into customers hands. If you treat it as infrastructure, you can build a system that survives real usage: repeated issuance, updates, redesigns, brand changes, and all the edge cases that show up at scale.
This is a distinction that matters more than most teams initially expect.
The “just generate a pass” trap
Like with a lot of things, on the surface the initial implementation usually sounds simple:
Design a pass
Insert the users data or information
Issue the pass
Deliver it to your users
Maybe support pass updates later
That can be enough to get you started. But the issue with this is that its not a file generation problem, it’s a lifecycle problem.
Passes have to have the right data for the right users. It might need updates later such as expiry, reflect redemptions, updates or other new information after they have been installed. And when something goes wrong, the failure usually happens between the business logic, UX, platform constraints and other operations. You need the flexibility and visibility to adapt, thats what the infrastructure provides.
The pass is the easy part
Teams often overestimate the difficulty of producing the pass artifacts and underestimate everything around them.
The hard parts tend to be:
How passes map to existing data
What is the source of truth
How the pass stays in sync with that source of truth
How template level changes apply and roll out safely
Ensure passes are valid and usable
How support teams can inspect what was issued and why
These are not the fun parts to solve, they are the ones that determine whether the feature feels tacked on or a core part of the service.
If you only build for issuance, you get a system that works once. If you build for lifecycle, you get a system that can be depended on.
Wallet passes behave more like stateful products than static documents
This is a mental model that teams should start with. A PDF ticket or a loyalty card is a document. A wallet pass is more like a lightweight stripped down application that happens to live inside Apple or Google wallets.
That changes the state of how you build around passes, now you can care about all these other facets:
Versioning
Identifiers that survive across systems
Event driven changes
Auditability
Delivery and communication
Downstream scanner and redemption behaviour
Once you look at passes this way a lot of integration decisions become clearer. You stop asking about “How can we create a pass?” and start asking “What is the lifecycle of this pass and what guarantees do we need around it?”
That is a much better question.
The abstraction problem
One of the tricker parts of building wallet infrastructure is that Apple and Google wallets have some overlap conceptually and achieve similar goals. But they do not expose the same model. They get to the same point with very different concepts.
If you build too close to each platform, you have complexity everywhere. Everything becomes platform specific, your interfaces become inconsistent and conceptually challenging. Everything just becomes harder.
On the other hand if you abstract to aggressively, you end up hiding the differences and cannot make the best of the systems on offer. This abstraction starts cracking and you either tack on exceptions that you are stuck with.
The right answer is somewhere in between. You normalise the common lifecycle primitives and keep explicit facets where the platform behaviour genuinely differs. That means you care about the core lifecycle concepts:
Pass templates
Pass instances
The issuer
Status
Installs
Barcode/NFC data
Delivery channels
Pass history
These concepts survive product changes and feature much better than directly around the raw passes.
Issuance needs to be idempotent
This is one of those engineering things that is very important to have and can naively be overlooked.
In the real world, things don’t always take the happy path. Requests fail, webhooks arrive twice, jobs are rerun, users click links again. If you’re system treats pass creation as a fire and forget side effect, you will eventually have duplicates, broken references and force support teams to clean up the mess.
Wallet infrastructure needs stable identifiers and strong idempotency semantics. If a customer system asks for the same pass twice you need to know whether that means:
Return the existing pass
Update the existing pass
Issue a replacement
Reject because the activity is invalid
This cannot be ambiguous because once you have real volume, this becomes a requirement.
Updates are where the architecture gets exposed
With a simplistic implementation everything looks fine when passes are issued. The weaknesses show up when passes need to change.
An event time change, a voucher redeemed, a new membership tier, additional loyalty points, name corrections. These are all normal events and updates. This needs to be a core part of the design and not awkward afterthoughts.
If this is part of the core design you tend to build:
Explicit pass state models
Event driven update triggers
Retryable idempotent delivery pipelines
Observability around update success and failure
Audit logs for who changed what and when
If updates are added to the core design later you usually get:
Hidden coupling to upstream systems
Partial state mismatches
Unclear retry behaviour
Fear around making changes to templates or business logic
A lot of the value of wallet passes comes after installation, not at the moment of creation. That only works if the update path is treated as infrastructure.
Templates are software, even when non-engineers use them
As soon as wallets become useful inside an organization, more people want to touch them.
Marketing wants control over branding. Operations wants a new field. Product wants a different status treatment. A partner wants a variant for a campaign. Someone wants to run two active versions at once.
If templates are treated like static creative assets, this becomes chaotic quickly.
Templates need the kind of controls we usually associate with software systems:
Versioning
Preview and validation
Safe rollout
Environment separation
Clear ownership
Constraints around what can change without breaking live passes
This matters even more in a multi-tenant system, where one bad template assumption can affect multiple customers or brands.
“No-code” in this context does not mean “no engineering concerns.” It means the engineering has to be good enough that non-engineers can safely operate within it.
Multi-tenant reality changes the design
Building wallet functionality for one product is different from building a platform that supports many teams, brands, and use cases.
The minute you go multi-tenant, a bunch of concerns become unavoidable:
Tenant isolation
Branding and asset management
Per-tenant credentials or configuration
Rate limits and batch issuance behaviour
Audit trails
Access control
Support tooling
Migration safety
This is the point where a lot of one-off implementations start to bend. The shortcuts that were reasonable for a single campaign become liabilities when the same infrastructure is expected to support vouchers, event tickets, loyalty cards, memberships, and branded activations with different operational needs.
That doesn’t mean the answer is over-engineering from day one. It does mean you should be honest about what kind of system you are actually building.
Infrastructure should reduce repeated decision-making
One of the practical tests I like is this: does the platform reduce the number of wallet-specific decisions a team has to make every time they launch something?
If the answer is no, you probably haven’t built infrastructure yet. You’ve built a thin wrapper.
Good infrastructure turns repeated judgment calls into defaults, constraints, and reusable primitives.
That might mean:
A common issuance model across pass types
Standard delivery flows
Reusable barcode/NFC strategies
Consistent update handling
Tenant-safe template controls
A unified API surface
Dashboard tools for non-technical teams
Audit and observability built in from the start
The goal is not to hide the domain. The goal is to stop re-solving the same operational problems for every campaign or integration.
The build vs buy conversation usually starts too late
By the time teams realize they need wallet infrastructure, they’ve often already built the first version themselves. That is understandable. The first version does not look expensive.
The problem is that the long-tail cost shows up later:
Maintaining signing and credential workflows
Handling edge cases across Apple and Google
Supporting updates properly
Debugging delivery failures
Evolving templates safely
Making the system usable by more than one internal team
Supporting new use cases without rewriting the model each time
This is why “we can build passes ourselves” is often true in the narrow sense and misleading in the useful sense.
Of course you can build pass generation. The question is whether you want to own wallet infrastructure.
Those are different commitments.
What changed my thinking
The biggest shift for me was realizing that wallet passes are rarely most valuable at the moment they are issued.
Their value compounds when they become a reliable channel and a reliable object in the product system. Something that can be installed once, updated later, understood by internal teams, and reused across multiple use cases without rebuilding the plumbing each time.
That only happens when the architecture treats them as infrastructure.
If you are adding wallet support today, I think the right starting question is not “how quickly can we get a pass into Apple Wallet or Google Wallet?”
It is “what lifecycle are we signing up to support?”
If the answer is more than a single isolated campaign, design for infrastructure early. You do not need a huge platform on day one. But you do need the right mental model, because most of the pain comes from getting that wrong.
If you want to add Apple Wallet and Google Wallet to your product without building and maintaining the infrastructure yourself, Passform gives you the tools to create, issue, update and manage passes through a single platform.
Explore Passform and start building with wallets.


