This application implements the creation of signed .pkpass files and API endpoints for pass registration, updates and logging.
- Build .pkpass with the
PassBuilder
class - Sign .pkpass with SMIME (as apple describes in their documentation)
- Server implementation for store, registration, update and logging
- Push notifications (APNs) on pass update
- Individual storage backend setting
- Support for mime-type upload using django-storages S3
- Django >=3.2.9
- Python >= 3.10
- pyca/cryptography (for .pkpass SMIME sign)
- djangorestframework >= 3.8
$ pip install django-walletpass
Add 'django_walletpass' to you installed apps in the settings.py file.
Load the content of your cert.pem and key.pem in your settings.py file. This is required for signing the .pkpass file.
WALLETPASS = {
'CERT_PATH': 'path/to/your/cert.pem',
'KEY_PATH': 'path/to/your/key.pem',
# (None if isn't protected)
# MUST be in bytes-like
'KEY_PASSWORD': b'1234',
}
Add extra needed conf to your settings.py file.
WALLETPASS = {
'CERT_PATH': 'path/to/your/cert.pem',
'KEY_PATH': 'path/to/your/key.pem',
# (None if isn't protected)
# MUST be in bytes-like
'KEY_PASSWORD': b'1234',
'PASS_TYPE_ID': 'pass.io.develat.devpubs.example',
'TEAM_ID': '123456',
'SERVICE_URL': 'https://example.com/passes/',
}
Add token JWT config data to allow APNs push:
WALLETPASS = {
'PUSH_AUTH_STRATEGY': 'token',
'TOKEN_AUTH_KEY_PATH': 'path/to/your/key.p8',
'TOKEN_AUTH_KEY_ID': 'key_id',
'PASS_TYPE_ID': 'pass.io.develat.devpubs.example',
'TEAM_ID': '123456',
'SERVICE_URL': 'https://example.com/passes/',
}
You should also import the urls into your site urls.
urlpatterns = [
url(r'^api/passes/', include('django_walletpass.urls')),
django-walletpass signals certain events that might come handy in your application.
from django_walletpass.classviews import PASS_REGISTERED, PASS_UNREGISTERED
@receiver(PASS_REGISTERED)
def pass_registered(sender, **kwargs):
pass
@receiver(PASS_UNREGISTERED)
def pass_unregistered(sender, **kwargs):
pass
Signal TOKEN_UNREGISTERED
has a default handler. It can be disconnected and replaced.
Default: DEFAULT_FILE_STORAGE
WALLETPASS_CONF = {
# Defaults to DEFAULT_FILE_STORAGE
'STORAGE_CLASS': 'my.custom.storageclass,
'UPLOAD_TO': 'passes'
'STORAGE_BACKEND': 's3storage' # Dj4.2+ only
}
STORAGE_BACKEND
is the key of the django settings STORAGES
storage config. e.g
STORAGES['s3storage']['BACKEND'] = 'some.s3.backend.class'
Default: False
WALLETPASS_CONF = {
'PUSH_SANDBOX': False,
}
WALLETPASS_CONF = {
# Cert in pem format.
'APPLE_WWDRCA_PEM_PATH': 'path/to/cert.pem',
}
Usefull if you are using django-storages
and you want to serve your .pkpass
files from s3
.
Default: False
WALLETPASS_CONF = {
STORAGE_HTTP_REDIRECT: True,
}
Init empty builder
from django_walletpass.models import PassBuilder
builder = PassBuilder()
Init builder usign a directory as base
from django_walletpass.models import PassBuilder
builder = PassBuilder(directory='/path/to/your.pass/')
If the base directory contains a pass.json
it will be loaded, but remember
that required attributes of pass.json
will be overwritten during build process
using this values:
{
"passTypeIdentifier": WALLETPASS_CONF['PASS_TYPE_ID'],
"serialNumber": secrets.token_urlsafe(20),
"teamIdentifier": WALLETPASS_CONF['TEAM_ID'],
"webServiceURL": WALLETPASS_CONF['SERVICE_URL'],
"authenticationToken": crypto.gen_random_token(),
}
To handle pass.json
data, there is a dict inside your builder instance, you
can manage it like a normal python dictionary.
Update some attrs:
builder.pass_data.update({
"barcode": {
"message": "123456789",
"format": "PKBarcodeFormatPDF417",
"messageEncoding": "iso-8859-1"
},
"organizationName": "Organic Produce",
"description": "Organic Produce Loyalty Card",
})
Update one attr:
builder.pass_data['description'] = "Organic Produce Loyalty Card"
builder.pass_data_required.update({
"passTypeIdentifier": "customvalue",
"serialNumber": "customvalue",
"teamIdentifier": "customvalue",
"webServiceURL": "customvalue",
"authenticationToken": "customvalue",
})
you can overwrite individual attributes:
builder.pass_data_required.update({
"serialNumber": "customvalue",
})
builder.pass_data_required['serialNumber] = 'cutomvalue'
file_content = open('myfile', 'rb').read()
builder.add_file('image.png', file_content)
You can also add files to directories:
file_content = open('myfile', 'rb').read()
builder.add_file('en.lproj/pass.strings', file_content)
Build the content of .pkpass
pkpass_content = builder.build()
Write to file:
pkpass_file = open('mypass.pkpass', 'wb')
pkpass_file.write(pkpass_content)
Save to new record in DB:
pass_instance = builder.write_to_model()
pass_instance.save()
Save to existent record in DB:
builder.write_to_model(pass_instance)
pass_instance.save()
builder = pass_instance.get_pass_builder()
builder.pass_data.update({'field': 'value'})
builder.build()
builder.save_to_db(pass_instance)
Checkout source and run from source root directory
docker run -it --rm -v "$(pwd):/app" python:3.8 bash -c "cd /app; python setup.py install; ./example/manage.py test django_walletpass"