Python library to read/write Apple Wallet (.pkpass) files
This is a fork of https://github.com/devartis/passbook which doesn't support py3k.
- Create a Pass Type Id:
- Visit the Visit the iOS Provisioning Portal
- On the left, click Identifiers
- From the type drop-down on the right, choose Pass Type IDs
- Next to Identifiers, click the + (plus) button.
- Select Pass Type IDs and click Continue
- Enter a description and an identifier (typically in the form
pass.com.yourcompany.some_name
) and click Continue - Download the generated file
- Double-click the pass file you downloaded to install it to your keychain
- Export the pass certificate as a p12 file:
- Open Keychain Access
- Locate the pass certificate -- it should be under the login keychain, Certificates category.
- Right-click the pass and choose Export
- Make sure the File Format is set to Personal Information Exchange (.p12) and export to a convenient location.
- Generate the necessary certificate and key .pem files
-
Open Terminal and navigate to the folder where you exported the p12 file.
-
Generate the pass pem file:
openssl pkcs12 -in "Certificates.p12" -clcerts -nokeys -out certificate.pem
-
Generate the key pem file:
Note you must set a password for the key pem file or you'll get errors when attempt to generate the pkpass file.openssl pkcs12 -in "Certificates.p12" -nocerts -out key.pem
-
- Generate the pem file for the Apple WWDR certificate (available from developer.apple.com) following the same steps as above.
- Move all the pem files into your project
from wallet.models import Pass, Barcode, StoreCard
pass_type_identifier = "pass.com.yourcompany.some_name"
team_identifier = "ABCDE1234" # Your Apple team ID
cert_pem = "certficate.pem"
key_pem = "key.pem"
wwdr_pem = "AppleWWDRCA.pem"
key_pem_password = "your_password"
cardInfo = StoreCard()
cardInfo.addPrimaryField('name', 'John Doe', 'Name')
passfile = Pass(cardInfo,
passTypeIdentifier=cls.pass_type_identifier,
organizationName=cls.organization_name,
teamIdentifier=cls.team_identifier)
# charge_response.id is trackable via the Stripe dashboard
passfile.serialNumber = charge_response.id
passfile.barcode = Barcode(message = charge_response.id, format=BarcodeFormat.PDF417)
passfile.description = charge_response.description
# Including the icon and logo is necessary for the passbook to be valid.
passfile.addFile("icon.png", open("icon.png", "rb"))
passfile.addFile("logo.png", open("logo.png", "rb"))
_ = passfile.create(cls.cert_pem,
cls.key_pem,
cls.wwdr_pem,
cls.key_pem_password,
"pass_name.pkpass")
- You must use a password for your key.pem file. If you don't, the pass file won't be properly generated. You'll probably see errors like
PEM routines:PEM_read_bio:no start line
in your server's logs. passfile.create()
writes the pass file to your server's filesystem. By default, it's written to the same directory as your script, but you can pass an absolute path (including the file name) to store elsewhere.passfile.create()
returns the name of the generated file, which matches what you pass to it as the fifth parameter.- Valid
cardInfo
constructors mirror the pass types defined by Apple. For example,StoreCard()
,BoardingPass()
,Coupon()
, etc. - The various "add field" methods (e.g.
addPrimaryField()
) take three unnamed parameters in the orderkey
,value
,label
An example Flask route handler to return the generated pass files:
@application.route("/passes/<path:fname>")
def passes_proxy(fname):
"""static passes serve"""
return send_from_directory("passes", fname, mimetype="application/vnd.apple.pkpass")
An example usage in a React app using Stripe and Stripe Elements to process payments and generate a store pass:
paymentRequest.on('token', async (ev) => {
try {
const response = await fetch('https://your_server/charge', {
method: 'POST',
body: JSON.stringify({
token: ev.token.id,
amount: totalInCents,
description: purchasedItems.join(',\n')
}),
headers: {'content-type': 'application/json'},
});
if (!response.ok) {
throw new Error('There was a problem processing your payment.');
}
// Report to the browser that the payment was successful, prompting
// it to download the pass file to the user's Wallet
ev.complete('success');
const pkpass = await response.json();
window.location.href = `https://your_server/passes/${pkpass.filename}`;
} catch (error) {
throw new Error("There was a problem processing your payment.");
}
});