PO Box 55056 RPO Windermere, Edmonton, AB T6W 5B4, Canada

Build vs Buy: Issuing Wallet Passes from Salesforce

A developer workstation with code on screen, a phone and a wallet, representing the choice between building and buying wallet pass infrastructure

A wallet pass is the card a member holds in the Apple Wallet or Google Wallet app on a phone. It can be issued from a Salesforce record in two ways.

A developer writes the code in Apex, or the org installs an application that generates the pass. The choice between those two routes is a build vs buy decision.

Build vs buy on Salesforce wallet passes reaches two people at the same time. The Salesforce admin prices a license. The engineering lead is asked for a build estimate. This article sets out what each route actually carries, so both answers are costed against the same list of work.

What build vs buy covers for a Salesforce wallet pass

A wallet pass program runs on five pieces of work, whether you have chosen to build or buy. The difference between an in-house build and licensed software is who is responsible for each piece.

Pass file generation

Apple Wallet is handled with a file called a .pkpass. The file holds the card content in pass.json, the images used on the card, a manifest that lists a hash for every file, and a signature made with the organization's certificate. The folder is then zipped, and the extension is changed to .pkpass.

Google Wallet works differently. A pass class and a pass object are created through the Google Wallet API. The save link carries a JWT, which is a signed data format Google checks before the pass is saved. Both formats are produced from the same member record.

Signing credentials for Apple and Google

Apple issues a Pass Type ID certificate, generated from a certificate signing request in the developer account. The private key from that certificate is used to sign every pass file the organization sends out. Google uses a service account key from Google Cloud for the same job. The Apple certificate carries an expiry date, so it gets replaced.

The update service behind a pass

A pass file carries a web service URL and a token. The phone registers itself with the web service after the member saves the pass. When a value on the card changes, a push notification with an empty payload is sent out, and the phone comes back to ask which passes changed. The updated file is downloaded over HTTPS.

Apple's documentation lists five endpoints for the web service — register, list updatable passes, send an updated pass, unregister and log — along with storage for the devices and the passes registered to them. Google is responsible for holding the pass object on its own servers, so an update there is a call to the API.

Scan validation and write-back

The barcode carries a serial number for the pass. The serial number does not carry the member's status, so the status comes from a lookup against the Salesforce record. The result of the scan is written back to the record and used for reporting.

Maintenance

Certificates expire and get replaced. Apple and Google make changes to the pass formats. The web service is expected to answer for as long as members hold passes. A card saved to a phone in year one still calls the same web service in year two.

How to build a wallet pass in Salesforce

The process to build a wallet pass in Salesforce works in a fixed order, from account creation and certificates through to writing Apex.

Start with an Apple Developer account

The first step is enrolling in the Apple Developer Program, followed by opening Certificates, Identifiers & Profiles in that account. A pass type identifier is created, written as a reverse DNS string that names one group of passes, along the lines of com.example.passes.membership.

Next is raising a certificate signing request against the identifier, which Apple answers with a Pass Type ID certificate. The private key from the request is used for signing every pass file the organization issues. Salesforce keeps the certificate and its key in Setup under Certificate and Key Management, and Apex reads them by developer name.

Each pass is given a serial number at issue. The pass type identifier and the serial number together identify one card, so a file carrying the same pair replaces the copy already saved on the phone.

Create a wallet pass for Apple Wallet

A .pkpass file is a folder that gets zipped, then renamed. That folder holds four kinds of content.

  • pass.json. The values printed on the card, read from the member record.
  • The image files. The icon and the logo, along with any strip art in the design.
  • manifest.json. A SHA1 hash for every other file, which Apex generates with Crypto.generateDigest.
  • The signature file. A PKCS #7 detached signature of manifest.json, made with the pass certificate and Apple's WWDR intermediate certificate.

Apex is helpful for three of the four. Building the JSON uses standard methods. Reading the images is a query against Static Resources, and hashing is one call per file. Zipping came with the Compression namespace, where ZipWriter accepts each file as an entry and returns the archive as a blob.

What Apex cannot sign on its own

The signature is the exception. Crypto.sign and Crypto.signWithCertificate return a raw signature over the data given to them, while Apple expects a PKCS #7 container carrying the signature along with the signing certificate and the WWDR intermediate. Apex has no method for building the container.

Two options are left. One is assembling the PKCS #7 structure in Apex as bytes, which means writing ASN.1 encoding by hand. The other is sending manifest.json to a signing service outside Salesforce, which adds a callout for every pass issued, along with an off-platform home for the private key.

A wrong container fails quietly. Wallet declines to add the card without naming a reason, so diagnosis becomes a comparison against a file known to work.

Volume then brings in the platform limits. Apex allows 6 MB of heap in a synchronous transaction and 12 MB in an asynchronous one, while the images, the manifest, the signature, and the finished archive are all held in memory. A transaction is capped at 100 callouts, with a cumulative callout timeout of 120 seconds, and CPU time is capped at 10,000 milliseconds synchronously against 60,000 milliseconds asynchronously. Batch sizes are set by those numbers rather than by the length of the member list.

Create a wallet pass for Google Wallet

Google starts with a Cloud project holding a service account, followed by enabling the Google Wallet API on that project. The service account key is responsible for signing everything sent afterward, in the job Apple's certificate does on the other platform.

Building the pass has two parts. A pass class is created once and describes the design used across the program. A pass object is created for each member and holds that member's values. The save link is a Google URL carrying a signed JWT, which is three base64url strings joined by dots.

Signing is where the platforms differ. The third string is an RS256 signature, meaning RSA with a SHA-256 hash, which Crypto.sign produces with the RSA-SHA256 algorithm. The Google half of a build is doable in Apex from beginning to end.

API calls need an OAuth access token issued to the service account, and Named Credentials are used to hold the connection so no key material goes into code. Creating each pass object is one callout, which moves issuance for a member list into a Queueable or batch job.

Apple's PassKit spec in Salesforce terms

Most of Apple's specification maps across without difficulty. Field values in pass.json come from fields on the Contact or the membership object. Images are stored as Static Resources or in Salesforce Files. The certificate and its key are stored in Certificate and Key Management.

The update service has no clean equivalent. Apple's endpoints answer requests from phones on the public internet, while Apex REST classes expect an authenticated Salesforce session. Publishing them takes a Salesforce Site or an Experience Cloud guest user, with the token check written into the Apex class. Device library identifiers and push tokens become records on custom objects, along with the last-updated tag held for each pass.

The push notification has no equivalent at all. Salesforce push notifications serve Salesforce mobile apps, while the message a wallet pass needs goes to Apple's servers.

Three ways to buy instead of building

Buying means handing the signing key and update services to a vendor. There are three ways to buy, and each one keeps a different amount of the work inside Salesforce.

A pass platform called over an API

A pass platform runs as a separate product with its own database. Salesforce integrates it with a REST callout that sends the values printed on the card, and the platform returns a save link. Pass templates, signing keys, the update service, and the scan endpoints are handled by the vendor.

Integration code is what stays in Salesforce. A Salesforce developer writes the callout, along with the retry and the error handling for a failed response. Field mapping is maintained twice, once in the org and once in the platform, so a new field on the membership object gets added in both. The member list is copied into the platform database, so the org keeps one version of a member and the vendor keeps another — a split set out in digital membership cards for Salesforce.

An app with a Salesforce connector

A connector is a package the vendor supplies for installation in the org. Setup is configuration rather than Apex. Records are synced out to the vendor's platform on a schedule or on a record change, and the pass is generated on the vendor's side.

Two consoles come with the arrangement. Field mapping is set in the connector, while pass design and issue rules are set in the vendor's own interface. Access is granted twice, once in Salesforce and once for the platform login.

A managed package in the org

A managed package installs from AppExchange and runs inside Salesforce. Pass templates and field mappings are stored as Salesforce records, along with the rules that decide when a pass is issued. Generation reads the member record, so no member list is copied to a second database.

Kemicard installs this way, from its listing on Salesforce AppExchange. Certificates are handled by the vendor, which covers the Apple renewal and the Google service account key. Permissions come from the org, so field-level security decides which values are allowed onto a card. Scan and install activity is written back to records, making reporting a standard Salesforce report rather than an export.

Build vs buy wallet pass cost

Cost follows the same split as the work. Building pays in engineering hours and running services. Buying pays a subscription against work the vendor has already done.

Costs of building in-house

Engineering time is the largest line, and it covers more than the pass file. The build pays for assembling pass.json and the images, for getting past the PKCS #7 signing gap, for the Google pass class and object setup, for the update service, and for the scan lookup that validates a card. Each piece is a separate estimate.

Platform accounts come next. The Apple Developer Program is a fixed cost, 99 USD a year, renewing annually. Google's side runs through a Cloud project holding a service account, with an issuer account approved for the Wallet API. Hosting is the third line, since a signing service or an update service placed outside Salesforce runs on infrastructure somebody pays for.

Certificate work is an administrative task on a yearly cycle for Apple, along with key rotation on the Google side, and both belong to a named person rather than to a calendar reminder. Monitoring is the line most often left out of the first estimate. A pass that fails to issue produces no complaint from either wallet, so the org pays for someone to watch a queue.

Costs of a licensed solution

A subscription replaces the engineering estimate. Implementation still takes time, spent on the pass template, the field mapping, and the Flow that decides when a card is issued. Admin training is a smaller line beside it. Kemicard's own tiers are set out on the pricing page.

A managed package that manages the signing credentials carries the platform accounts as well, so the Apple enrollment and the Google service account key move off the org's books. Hosting disappears for the same reason, since the update service belongs to the vendor.

Time to first pass

A build cannot start until Apple has issued the certificate, which puts an account setup between the decision and the first line of Apex. The first pass then arrives once the signing route works, and the rest of the program is still ahead of it. Reaching a demo is not reaching production.

A licensed app is installed, then configured. The first test pass comes out of a template and a field mapping. What differs between the two routes is not only the calendar, since the build occupies developers who would otherwise be doing other Salesforce work.

What changes when passes are issued in bulk

One pass at a time is a small transaction. Bulk issuance is a different job, and it arrives with a member migration, a season launch, or an annual renewal run.

Bulk changes the design rather than the code. Issuance moves into a batch or Queueable job, sized against the heap, callout, and CPU ceilings named earlier. A rerun after a partial failure has to reuse the serial numbers already assigned, because a fresh serial number produces a second card instead of replacing the first.

Updates behave the same way. A value changing on one template reaches every device holding a pass from it, and the update service answers each request individually. The cost is the capacity to answer them, which is a hosting question for a build.

The calculation is short. Build hours multiplied by a loaded developer rate, plus hosting and the yearly renewals, set against the subscription plus implementation time. Running the numbers over three years rather than one is what moves the answer, since the build's first-year total hides the maintenance that starts in year two.

An org with wallet passes at the center of its product has a case for owning the code. A membership program issuing cards from records it already keeps is paying twice for the same result.

Build or buy: which one fits wallet passes on Salesforce?

Both routes issue a working card. The decision turns on what the passes are for, and on who keeps the machinery running once the program is live.

When building in-house fits

Passes that behave in a way no product supports are the clearest case. A scan flow tied to proprietary hardware, a barcode rule written for one venue, or a pass that forms part of a product the company sells to its own customers all sit outside what a packaged app configures.

Key custody is the second case. Rules in some organizations require the private key to stay inside infrastructure the company controls, which rules out a vendor holding it.

A build also holds up where the pass never changes after issue. A static card with no renewal date and no tier carries no update service, which removes the highest running cost from the estimate.

When to buy

Membership programs, loyalty schemes, event check-in, and staff or student ID cards are the programs packaged products are built around. Nothing in them needs custom pass behavior, so the engineering estimate buys a result already available.

Production timing is the second reason. A licensed app issues a test pass on the day it is configured, while a build reaches the same point after the certificate, the signing route, and the update service are working.

Maintenance is the third. Certificate renewal, key rotation, and the changes Apple and Google publish to their formats all move to the vendor, along with the servers answering update requests. Distribution follows the routes the org already uses, covered in sending wallet passes from Salesforce.

How to use build and buy together

Both options can be used together. Pass generation, signing, and the update service can be licensed while the automation around them is built in the org.

Flows that decide when a card is issued are written by the admin. A check-in screen for staff can be built on Experience Cloud against the scan records. Analytics beyond the built-in reports are standard Salesforce reports and dashboards, since the pass activity is already stored as records.

Extending a licensed app takes an API, which is why the developer documentation matters when a product is chosen. Kemicard publishes its developer reference alongside the platform and API for developers, so the automation written around it uses supported calls rather than workarounds.

An org choosing this route pays for the part that is hard to build, then spends its engineering time on the part that is specific to its own program.

Final take

Building a wallet pass in Salesforce is possible. Apex assembles the file, and the Google side signs without outside help. What the estimate has to carry is the PKCS #7 container, the update service, the yearly certificate work, and the monitoring that starts once passes are issued in bulk.

Buying moves that list to a vendor. A managed package keeps the member list in the org, so the pass is generated from the record already held.

Kemicard is built for that route. Passes for Apple Wallet and Google Wallet are issued from Salesforce records, with the signing certificates managed by Kemicard rather than by an admin holding a private key. Install and scan activity is written back to the record, which keeps the reporting in standard Salesforce reports.

Frequently asked questions

How does an Apple pass certificate get into Salesforce?

Apple returns the Pass Type ID certificate to the machine that raised the certificate signing request, where it pairs with the private key. Salesforce imports a key pair through Setup under Certificate and Key Management, using a Java keystore rather than the .p12 file directly. Converting the .p12 into a keystore with the keytool utility is the step between the two. Once imported, Apex reads the certificate by its developer name rather than by handling the key itself.

Can Apex make the callouts a wallet pass build needs from inside a trigger?

Not in the same transaction as the record change. Salesforce documents that a callout is blocked when operations are pending, and it names DML statements among them, so a trigger that updates a record then calls a signing service fails at the callout. Wallet pass work therefore runs in a future method, a Queueable, or a batch job started by the trigger. The design matters for issuance timing, since a card is generated after the transaction commits rather than during it.

What happens to passes already on phones if an org moves from a build to a licensed app?

The pass type identifier and the serial number decide the outcome. A licensed app issuing under the same pass type identifier, with the same serial numbers, sends a file that replaces the card already saved. Issuing under a new identifier produces a second card, which leaves the member holding both until the old one is removed. Migration planning starts with who owns the identifier, since that answer decides whether members are asked to install anything again.

Does an org still need its own Apple Developer account after buying a wallet pass app?

It depends on whose certificate signs the pass. A managed package that manages signing credentials issues under the vendor's arrangement, so the org carries no enrollment or renewal. A platform configured to sign with the organization's own pass type identifier keeps the Apple Developer Program membership on the org's books, along with the yearly renewal. Asking which model a product uses is worth doing before the contract, because it changes what happens if the product is replaced later.

Can wallet passes be tested in a Salesforce sandbox before going live?

Yes, with one caution about identifiers. Certificates are stored per org, so a sandbox needs its own copy of the signing material to produce a valid file. Test passes are real passes and installed on real phones, and a test card sharing a pass type identifier and serial number with a production card replaces it. Using a separate pass type identifier for sandbox work keeps the two sets apart.

Sources: Apple's Building a Pass and Adding a Web Service to Update Passes; Google's Working with JSON Web Tokens (JWT); Salesforce Help: Generate a Salesforce Compatible JKS From PFX or P12.

Explore Kemicard

Product & solutions

Stop building .pkpass infrastructure

See a pass issued from your own Salesforce record, with the signing certificates managed for you. Or install from AppExchange and issue a test card yourself.