wallet-mongoose

Implementation of Apple Wallet Web Service CRUD operations using mongoose

Usage no npm install needed!

<script type="module">
  import walletMongoose from 'https://cdn.skypack.dev/wallet-mongoose';
</script>

README

This module provides API's for performing CRUD operations listed in Wallet Developer Guide and PassKit Web Service Reference for wallet push notifications.

wallet-mongoose relies on the mongoose module and provides error handling, caching and unit tests.

Plug this in between any Authentication module and a Pass generating module

Steps:

1.Generate a Pass and save user data in the Pass schema

2.Wallet app makes REST calls when user adds Pass to the wallet app

3.Wallet requests -> Authenticate -> wallet-mongoose

Installation

$ npm install wallet-mongoose

Usage :

Sample schema with mandatory fields,

var deviceSchema = new Schema({
    deviceLibraryIdentifier : String,
    pushToken : String
});

var registrationSchema = new Schema({
    deviceLibraryIdentifier : String,
    passTypeIdentifier : String,
    serialNumber : String
});

var passSchema = new Schema({
    passTypeIdentifier : String,
    serialNumber : String,
    lastUpdated : {type : Number , default : 0}, // This can be UNIX time
    userName : String	// (Optional) Add in your custom fields for storing user data
});

var Device = mongoose.model('Device' , deviceSchema);
var Pass = mongoose.model('Pass' , passSchema);
var Registration = mongoose.model('Registration' , registrationSchema);

Generate Pass:

Generate a Pass using any pass generating module and save the document in the Pass schema

Eg:

var now = Date.now()
Pass.update({
        serialNumber : '1234',
        passTypeIdentifier : 'pass.com.company.team'
    },{
        passTypeIdentifier : 'pass.com.company.team',
        serialNumber : '1234',
        lastUpdated : now,
        userName : 'John Doe'
        }
    },{
        upsert : true,
        setDefaultsOnInsert : true
    },function(err , raw){
        if(err){
            ......
        }
   });

   /*
    For caching, wallet-mongoose uses the 'if-modified-since' header in the request to getPass(...) to send 304 if Pass.lastUpdated is <= req.headers['if-modified-since'] .
   */
    res.setHeader('Last-Modified', now);

On clicking Add pass to the wallet app, the following REST calls are used by the wallet app.

REST API's:

1.Register

var express = require('express');
var router = express.Router();
var authenticate = require('./authenticate'); // Plug in your authentication module, using jsonwebtoken for instance
var wallet = require('wallet-mongoose');

router.post('/webServiceURL/*/devices/*/registrations/*/*', authenticate.verify, function(req, res, next){
    wallet.register(req, Device, Registration, function(err, statusCode){
        if(err){
            res.sendStatus(statusCode);	// Send appropriate error status code
        }
        res.sendStatus(statusCode);	// Send appropriate success status code
    });
});

2.Get Serial Numbers

router.get('/webServiceURL/*/devices/*/registrations/*', function(req, res, next){
    wallet.getSerialNumbers(req, Registration, Pass, function(err, statusCode, responseBody){
        if(err){
            res.sendStatus(statusCode);
        }
        res.status(statusCode).send(responseBody);
    });
});

3.Get Pass

router.get('/webServiceURL/*/passes/*/*', authenticate.verify , function(req, res, next){
    wallet.getPass(req, Pass, function(err, statusCode, passData){
        if(err){
            res.sendStatus(statusCode);
        }
        // passData is the document in the Pass schema holding user's data
        /*
         Call next module which uses passData and generates an actual Pass
         The passbook module for example is capable of doing this
        */
        next();
    });
});

4.Unregister

router.delete('/webServiceURL/*/devices/*/registrations/*/*' , authenticate.verify, function(req, res, next){
    wallet.unregister(req, Registration, Device, function(err, statusCode){
        if(err){
            res.sendStatus(statusCode);
        }
        res.sendStatus(statusCode);
    });
});

5.Log

router.post('/webServiceURL/*/log' , function(req, res, next){
    wallet.log(function(err, statusCode, log){
        console.log(log);	// Just logs to console
        res.sendStatus(statusCode);
    });
});

Testing

Run mongod on mongodb://localhost:27017, then

cd wallet-mongoose
npm install
npm test

Sharding

Device collection can be hash sharded on the deviceLibraryIdentifier key.

Pass collection can be sharded on the serialNumber + passTypeIdentifier composite key.