import { toBuffer as createZip } from 'do-not-zip' ; import { v4 as uuid4 } from 'uuid' ; import { Constants } from "./constants" ; import { Payload , PayloadBody , PassDictionary } from "./payload" ; import { ValueSets } from "./value_sets" ; const crypto = require ( 'crypto' ) ; enum QrFormat { PKBarcodeFormatQR = 'PKBarcodeFormatQR' } enum Encoding { utf8 = "utf-8" } interface QrCode { message : string ; format : QrFormat ; messageEncoding : Encoding ; altText : string ; } interface SignData { PassJsonHash : string ; useBlackVersion : boolean ; } export class PassData { passTypeIdentifier : string = Constants . PASS_IDENTIFIER ; teamIdentifier : string = Constants . TEAM_IDENTIFIER ; sharingProhibited : boolean = false ; voided : boolean = false ; formatVersion : number = 1 ; logoText : string = Constants . NAME ; organizationName : string = Constants . NAME ; description : string = Constants . NAME ; labelColor : string ; foregroundColor : string ; backgroundColor : string ; serialNumber : string ; barcodes : Array < QrCode > ; barcode : QrCode ; generic : PassDictionary ; private static getBufferHash ( buffer ) { const sha = crypto . createHash ( 'sha1' ) ; sha . update ( buffer ) ; return sha . digest ( 'hex' ) ; } private static async signWithRemote ( signData ) { const configResponse = await fetch ( '/api/config' ) ; const apiBaseUrl = ( await configResponse . json ( ) ) . apiBaseUrl ; const response = await fetch ( `${ apiBaseUrl }/sign` , { method : 'POST' , headers : { 'Accept' : 'application/octet-stream' , 'Content-Type' : 'application/json' } , body : JSON . stringify ( signData ) } ) ; if ( response . status !== 200 ) { throw Error ( 'signatureFailed' ) ; } return await response . arrayBuffer ( ) ; } static async generatePass ( payloadBody ) { const valueSets = await ValueSets . loadValueSets ( ) ; const payload = new Payload ( payloadBody , valueSets ) ; const qrCode = { message : payload . rawData , format : QrFormat . PKBarcodeFormatQR , messageEncoding : Encoding . utf8 , altText : 'SCAN TO VERIFY' , } ; const pass = new PassData ( payload , qrCode ) ; const zip = [ ] as { path : string ; data : Buffer | string ; } [ ] ; const passJson = JSON . stringify ( pass ) ; zip . push ( { path : 'pass.json' , data : Buffer . from ( passJson ) } ) ; zip . push ( { path : 'icon.png' , data : payload . img1x } ) ; zip . push ( { path : 'icon@2x.png' , data : payload . img2x } ) ; zip . push ( { path : 'logo.png' , data : payload . img1x } ) ; zip . push ( { path : 'logo@2x.png' , data : payload . img2x } ) ; const manifestJson = JSON . stringify ( zip . reduce ( ( res , { path , data } ) => { res [ path ] = PassData . getBufferHash ( data ) ; return res ; } , { } ) ) ; zip . push ( { path : 'manifest.json' , data : Buffer . from ( manifestJson ) } ) ; const passHash = PassData . getBufferHash ( Buffer . from ( passJson ) ) ; const manifestSignature = await PassData . signWithRemote ( { PassJsonHash : passHash , useBlackVersion : ! payload . dark , } ) ; zip . push ( { path : 'signature' , data : Buffer . from ( manifestSignature ) } ) ; return createZip ( zip ) ; } private constructor ( payload , qrCode ) { this . labelColor = payload . labelColor ; this . foregroundColor = payload . foregroundColor ; this . backgroundColor = payload . backgroundColor ; this . serialNumber = uuid4 ( ) ; this . barcodes = [ qrCode ] ; this . barcode = qrCode ; this . generic = payload . generic ; } }