├── crts └── index.html ├── .gitignore ├── requirements.txt ├── env_variables ├── Dockerfile ├── README.md └── pyawal.py /crts/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | env_variables -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.2 2 | wallet_py3k==0.0.4 3 | python-barcode==0.13.1 4 | Pillow==8.1.2 5 | requests==2.25.1 -------------------------------------------------------------------------------- /env_variables: -------------------------------------------------------------------------------- 1 | export PASS_TYPE_IDENT=pass.identity 2 | export TEAM_IDENT=teamident 3 | export PASS_PASSWORD=certpasswords 4 | export PRD_ADDRESS="https://wallet.domain.com" 5 | export DEV_ADDRESS="http://192.168.0.101" -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-alpine 2 | 3 | RUN apk upgrade --update-cache --available && \ 4 | apk add openssl ca-certificates zlib-dev jpeg-dev gcc musl-dev --no-cache g++ freetype-dev jpeg-dev && \ 5 | rm -rf /var/cache/apk/* 6 | 7 | WORKDIR /app 8 | COPY . . 9 | 10 | RUN pip install -r /app/requirements.txt 11 | 12 | VOLUME /app/crts 13 | 14 | EXPOSE 5002 15 | 16 | CMD [ "python", "/app/pyawal.py" ] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Instructions: 2 | Make sure you have the required certs inside of the directory you mount below named exactly as so 3 | 4 | ``` 5 | certificate.pem 6 | key.pem 7 | wwdr.pem 8 | ``` 9 | 10 | ## Docker Compose Example: 11 | ``` 12 | services: 13 | applewallet: 14 | image: shauder/apple-wallet-shortcut:latest 15 | container_name: applewallet 16 | restart: unless-stopped 17 | networks: 18 | - backend 19 | environment: 20 | - TZ=${TIME_ZONE} 21 | - PASS_TYPE_IDENT=certpassidentity 22 | - TEAM_IDENT=certteamid 23 | - PASS_PASSWORD=mysupersecretpasswordforcert 24 | - RETURN_ADDRESS=https://wallet.shane.app 25 | volumes: 26 | - /etc/localtime:/etc/localtime:ro 27 | - /data/docker/applewallet:/app/crts 28 | ``` -------------------------------------------------------------------------------- /pyawal.py: -------------------------------------------------------------------------------- 1 | import re 2 | import mimetypes 3 | import os 4 | import barcode 5 | import base64 6 | import requests 7 | from urllib.parse import unquote_plus 8 | from io import BytesIO 9 | from PIL import Image 10 | from barcode import Code39 11 | from barcode.writer import ImageWriter 12 | from flask import ( 13 | Flask, 14 | redirect, 15 | request, 16 | render_template, 17 | send_file 18 | ) 19 | from wallet.models import Pass, Barcode, StoreCard 20 | 21 | GENERIC_PASS_URL = 'https://www.icloud.com/shortcuts/70e4911a049045eea3cfd32d3d9908e6' 22 | 23 | pass_type_id = os.environ.get('PASS_TYPE_IDENT') 24 | team_id = os.environ.get('TEAM_IDENT') 25 | pass_passcode = os.environ.get('PASS_PASSWORD') 26 | prd_address = os.environ.get('PRD_ADDRESS') 27 | dev_address = os.environ.get('DEV_ADDRESS') 28 | 29 | if os.environ.get('PRODUCTION'): 30 | develop = False 31 | address = prd_address 32 | app_port = '5002' 33 | return_url = address 34 | else: 35 | develop = True 36 | address = dev_address 37 | app_port = '5002' 38 | return_url = address + ':' + app_port 39 | 40 | # Create the application instance 41 | app = Flask(__name__, template_folder='templates', static_url_path='') 42 | 43 | # Create a URL route in our application for '/' 44 | @app.route('/') 45 | def home(): 46 | return redirect(GENERIC_PASS_URL, code=302) 47 | 48 | @app.route('/api/v1/ip', methods=['POST', 'GET']) 49 | def get_ext_ip(): 50 | return requests.get('https://checkip.amazonaws.com').text.strip() 51 | 52 | @app.route('/api/v1//', methods=['POST', 'GET']) 53 | def gen_apple_wallet(barcode_type, barcode_input): 54 | 55 | if request.method == 'POST': 56 | if request.is_json: 57 | content = request.get_json(silent=False) 58 | else: 59 | content = { 'name': 'Membership Card' } 60 | 61 | elif request.method == 'GET': 62 | if request.is_json: 63 | content = request.get_json(silent=False) 64 | else: 65 | content = request.args 66 | 67 | else: 68 | return 'Bad request.' 69 | 70 | pass_name = decode_input(content.get('name', 'Membership Card')) 71 | pass_description = decode_input(content.get('description', 'My membership card ' + barcode_type + ' pass')) 72 | pass_header_text = decode_input(content.get('header_text')) 73 | pass_header_value = decode_input(content.get('header_value')) 74 | pass_primary_text = decode_input(content.get('primary_text')) 75 | pass_primary_value = decode_input(content.get('primary_value')) 76 | pass_icon = decode_input(content.get('icon', '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')) 77 | pass_logo = decode_input(content.get('logo', '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')) 78 | pass_location = decode_input(content.get('location', False)) 79 | pass_latitude = decode_input(content.get('latitude', '0')) 80 | pass_longitude = decode_input(content.get('longitude', '0')) 81 | pass_relevant_text = decode_input(content.get('relevant_text', 'A pass is available for use here')) 82 | pass_foreground_color = decode_input(content.get('foreground_color', 'rgb(0,0,0)')) 83 | pass_background_color = decode_input(content.get('background_color', 'rgb(37,170,225)')) 84 | pass_label_color = decode_input(content.get('label_color', 'rgb(241,92,34)')) 85 | 86 | pass_debug = content.get('debug', False) 87 | 88 | if pass_debug: 89 | print(pass_name) 90 | print(pass_description) 91 | print(pass_icon) 92 | print(pass_logo) 93 | print(pass_location) 94 | print(pass_latitude) 95 | print(pass_longitude) 96 | print(pass_relevant_text) 97 | print(pass_foreground_color) 98 | print(pass_background_color) 99 | print(pass_label_color) 100 | 101 | simplename = re.sub('[^a-zA-Z0-9]', '', pass_name).lower() 102 | 103 | if barcode_type.upper() == 'C39': 104 | if len(barcode_input) < 44: 105 | pass_barcode_buffer = BytesIO() 106 | AW_C39 = barcode.get_barcode_class('Code39') 107 | AW_C39(barcode_input, writer=ImageWriter(), add_checksum=False).render({'font_size': 0, 'dpi': 300}).save(pass_barcode_buffer, format='PNG') 108 | pass_barcode = base64.b64encode(pass_barcode_buffer.getvalue()) 109 | else: 110 | return 'Bad Code39 code.' 111 | elif barcode_type.upper() == 'C128': 112 | if len(barcode_input) < 129: 113 | pass_barcode_buffer = BytesIO() 114 | AW_C128 = barcode.get_barcode_class('Code128') 115 | AW_C128(barcode_input, writer=ImageWriter()).render({'font_size': 0, 'dpi': 300}).save(pass_barcode_buffer, format='PNG') 116 | pass_barcode = base64.b64encode(pass_barcode_buffer.getvalue()) 117 | else: 118 | return 'Bad Code128 code.' 119 | elif barcode_type.upper() == 'EAN13': 120 | barcode_input = re.sub("\D", "", barcode_input) 121 | if len(barcode_input) == 13: 122 | pass_barcode_buffer = BytesIO() 123 | AW_EAN13 = barcode.get_barcode_class('EAN13') 124 | AW_EAN13(barcode_input, writer=ImageWriter()).render({'font_size': 0, 'dpi': 300}).save(pass_barcode_buffer, format='PNG') 125 | pass_barcode = base64.b64encode(pass_barcode_buffer.getvalue()) 126 | else: 127 | return 'Bad EAN13 code.' 128 | elif barcode_type.upper() == 'EAN8': 129 | barcode_input = re.sub("\D", "", barcode_input) 130 | if len(barcode_input) == 8: 131 | pass_barcode_buffer = BytesIO() 132 | AW_EAN8 = barcode.get_barcode_class('EAN8') 133 | AW_EAN8(barcode_input, writer=ImageWriter()).render({'font_size': 0, 'dpi': 300}).save(pass_barcode_buffer, format='PNG') 134 | pass_barcode = base64.b64encode(pass_barcode_buffer.getvalue()) 135 | else: 136 | return 'Bad EAN8 code.' 137 | elif barcode_type.upper() == 'JAN': 138 | barcode_input = re.sub("\D", "", barcode_input) 139 | if ( barcode_input[0:2] == '45' or barcode_input[0:2] == '49' ) and len(barcode_input) == 13: 140 | pass_barcode_buffer = BytesIO() 141 | AW_JAN = barcode.get_barcode_class('JAN') 142 | AW_JAN(barcode_input, writer=ImageWriter()).render({'font_size': 0, 'dpi': 300}).save(pass_barcode_buffer, format='PNG') 143 | pass_barcode = base64.b64encode(pass_barcode_buffer.getvalue()) 144 | else: 145 | return 'Bad JAN code.' 146 | elif barcode_type.upper() == 'ISBN13': 147 | barcode_input = re.sub("\D", "", barcode_input) 148 | if ( barcode_input[0:3] == '978' or barcode_input[0:3] == '979' ) and len(barcode_input) == 13: 149 | pass_barcode_buffer = BytesIO() 150 | AW_ISBN13 = barcode.get_barcode_class('ISBN13') 151 | AW_ISBN13(barcode_input, writer=ImageWriter()).render({'font_size': 0, 'dpi': 300}).save(pass_barcode_buffer, format='PNG') 152 | pass_barcode = base64.b64encode(pass_barcode_buffer.getvalue()) 153 | else: 154 | return 'Bad ISBN13 code.' 155 | elif barcode_type.upper() == 'ISBN10': 156 | barcode_input = re.sub("\D", "", barcode_input) 157 | if len(barcode_input) == 10: 158 | pass_barcode_buffer = BytesIO() 159 | AW_ISBN10= barcode.get_barcode_class('ISBN10') 160 | AW_ISBN10(barcode_input, writer=ImageWriter()).render({'font_size': 0, 'dpi': 300}).save(pass_barcode_buffer, format='PNG') 161 | pass_barcode = base64.b64encode(pass_barcode_buffer.getvalue()) 162 | else: 163 | return 'Bad ISBN10 code.' 164 | elif barcode_type.upper() == 'ISSN': 165 | barcode_input = re.sub("\D", "", barcode_input) 166 | if len(barcode_input) == 8: 167 | pass_barcode_buffer = BytesIO() 168 | AW_ISSN= barcode.get_barcode_class('ISSN') 169 | AW_ISSN(barcode_input, writer=ImageWriter()).render({'font_size': 0, 'dpi': 300}).save(pass_barcode_buffer, format='PNG') 170 | pass_barcode = base64.b64encode(pass_barcode_buffer.getvalue()) 171 | else: 172 | return 'Bad ISSN code.' 173 | elif barcode_type.upper() == 'NOCODE': 174 | pass_barcode = None 175 | else: 176 | return 'No valid barcode type passed with the request, try again.' 177 | 178 | cardInfo = StoreCard() 179 | 180 | if not barcode_type.upper() == 'NOCODE' and not pass_header_text and not pass_header_value: 181 | pass_header_text = 'Account Number' 182 | pass_header_value = barcode_input 183 | 184 | if pass_header_text and pass_header_value: 185 | cardInfo.addHeaderField('header_field', pass_header_value, pass_header_text) 186 | 187 | if barcode_type.upper() == 'NOCODE' and pass_primary_value and pass_primary_text: 188 | cardInfo.addPrimaryField('primary_field', pass_primary_value, pass_primary_text) 189 | 190 | cardInfo.addBackField('name', pass_name, 'Name: ') 191 | cardInfo.addBackField('description', pass_description, 'Description: ') 192 | cardInfo.addBackField('credit', address, 'Created By: ') 193 | 194 | passfile = Pass(cardInfo, \ 195 | passTypeIdentifier=pass_type_id, \ 196 | organizationName=pass_name, \ 197 | teamIdentifier=team_id) 198 | 199 | passfile.logoText = pass_name 200 | passfile.description = pass_description 201 | passfile.serialNumber = pass_type_id + '.' + simplename + '.' + barcode_input 202 | passfile.barcode = '' 203 | passfile.foregroundColor = pass_foreground_color 204 | passfile.backgroundColor = pass_background_color 205 | passfile.labelColor = pass_label_color 206 | 207 | if pass_location: 208 | passfile.locations = [{'latitude' : float(pass_latitude), 'longitude' : float(pass_longitude), 'relevantText' : pass_relevant_text}] 209 | 210 | passfile.addFile('icon.png', BytesIO(base64.b64decode(str(pass_icon)))) 211 | passfile.addFile('logo.png', BytesIO(base64.b64decode(str(pass_logo)))) 212 | 213 | if not barcode_type.upper() == 'NOCODE': 214 | passfile.addFile('strip.png', BytesIO(base64.b64decode(pass_barcode))) 215 | 216 | pass_encoded = base64.b64encode(passfile.create('crts/certificate.pem', 'crts/key.pem', 'crts/wwdr.pem', pass_passcode).getvalue()) 217 | 218 | return send_file(BytesIO(base64.b64decode(pass_encoded)), mimetype='application/vnd.apple.pkpass', as_attachment=True, attachment_filename='pass.pkpass') 219 | 220 | def decode_input(input_text): 221 | if input_text: 222 | if '%2B' in input_text or '%3D' in input_text or '%2F' in input_text: 223 | return unquote_plus(input_text) 224 | else: 225 | return input_text 226 | 227 | def main(): 228 | mimetypes.add_type('application/vnd.apple.pkpass', '.pkpass') 229 | app.run(host='0.0.0.0', port=app_port, debug=False) 230 | 231 | # If we're running in stand alone mode, run the application 232 | if __name__ == '__main__': 233 | main() 234 | --------------------------------------------------------------------------------