Technical details for Motics tickets in Google Wallet

This page provides the technical details a Public Transit Operator (PTO) and their system integrator need to integrate with Google to provide Motics tickets in Google Wallet. The solution makes use of the Google Wallet API and also relies on the PTO implementing an activation endpoint.

System Architecture

This section shows the system architecture and the Motics save flow.

Motics Ticket Save Flow Figure 1. Motics Ticket Save Flow

Figure 1 shows the flow for creating, activating and pinning a Motics ticket in Google Wallet, across several entities:

  • Google servers
  • PTO (System Integrator) server
  • Motics SCE server
  • Web shop

The following describes the flow in more detail:

  1. In the initial setup phase, the PTO server creates the transitClass, passing the ownerId and activationUrl using the transitClass:Insert Google Wallet API endpoint. This is a one-off activity.
  2. Next, when a user purchases a ticket from the web shop, the PTO server calls transitObject:Insert containing basic ticketing information and some initial fields indicating that this a Motics ticket.
  3. Then the PTO server and web shop work together to render the Add to Google Wallet button and ultimately return the ticket's JWT to Google, using the save link.
  4. Now the ticket pinning phase can begin, when the Google server calls the Activation Endpoint behind the activationUrl.
  5. In response to Step 4, the PTO server generates the signature (sigSTB) containing the SCE_ID signed with the SAM.
  6. Before responding to the activationUrl call, the PTO server should first call transitObject:Patch containing all the necessary Motics information, including the Motics applicationData.
  7. Only after the transitObject:Patch call has been successful, the PTO server should return a success (HTTP-200) response to the activationUrl call.

To provide a good user experience, a user should be able to move their Motics ticket from one device to another, within certain limits defined by the issuer. For this, the issuer has to implement the Move and Unlink Flow.

Activation Endpoint

The issuer/PTO (or their system integrator) needs to implement a ticket activation endpoint which Google will invoke when the ticket is saved. The URL to this endpoint should be provided in the invocation to transitClass:Insert. The activation endpoint will generate the signature (sigSTB) and call the transitObject:Patch method with the parameters defined in the following section.

Request

The request to the activation endpoint has the following format:

Content-Type: application/json
Body: {
  "classId": "123.classId",
  "expTimeMillis": 1669671940735,
  "eventType": "activate",
  "objectId": string - base64 encoded ID of the TransitObject,
  "deviceContext": string - base64 encoded SCE_ID,
}

Response

An HTTP-200 success response with an empty body, should be returned if:

  • The sigSTB containing SCE_ID was generated and signed with the SAM
  • The transitObject:Patch method was called successfully
Status: 200 - OK
Body: {}

Latency Targets

The activation endpoint should adhere to the following latency targets:

  • At least 50% of all requests should be responded to within 200ms
  • At least 95% of all requests should be responded to within 2s
  • There is a hard upper limit of 10s

Google Wallet API changes

The following outlines the changes to the Google Wallet API endpoints in order to support Motics as outlined in the system architecture.

Method: transitClass:insert

This is the Google Wallet API endpoint to create a transitClass on Google's backend. The system integrator needs to invoke this API with the following request parameters along with any other fields which are applicable. Refer to the transitClass & transitClass.Insert API documentation for a full list of (non-Motics) parameters and more details.

POST: https://walletobjects.googleapis.com/walletobjects/v1/transitClass

JSON representation

The Motics integration requires at minimum the following JSON representation of the transitClass in the transitClass:insert request body. Other mandatory transitClass metadata fields will need to be set as well.

{
  "id": string,
  "multipleDevicesAndHoldersAllowedStatus": ONE_USER_ONE_DEVICE (MultipleDevicesAndHoldersAllowedStatus),
  "deviceCertificationSupport": {
     "vdvCertDetails": {
        "ownerId" string,
        "certEnvironment": PRODUCTION/STAGING,
      },
  },
  "activationOptions": {
    "activationUrl": string
  },
  ...
}

When certEnvironment = PRODUCTION the Google server will fetch the certificate from the production Motics server. When certEnvironment = STAGING the Google server will fetch the certificate from the sandbox Motics server.

Method: transitObject:insert

This is the Google Wallet API endpoint to insert the transitObject for the new ticket that a user wants to purchase and add to Google Wallet. The system integrator should pass a transitObject with mainly the ticket information at this point. Refer to the transitObject & transitObject.Insert API documentation for a full list of (non-Motics) parameters and more details.

POST: https://walletobjects.googleapis.com/walletobjects/v1/transitObject

JSON representation

The Motics integration requires at minimum the following JSON representation of the transitObject in the transitObject:insert request body. Other object metadata fields may be set as well and all other mandatory fields should also be included.

{
  "id": string,
  "classId": string,
  "validTimeInterval": {
    object (TimeInterval)
  },
  "activationStatus": {
    "state": NOT_ACTIVATED (State)
  },
  "rotatingBarcode": {
    "type": AZTEC (BarcodeType),
    "valuePattern": "{vdv_barcode}",
    "deviceEntitlementSupport": {
      "vdvEntitlementDetails": {
        "applicationData": "",
      },
    },
  },
  ...
}

Notes:

  • The API requires that the applicationData field is included. At this point in the Motics activation flow, the applicationData value is not yet known, so it has to be set to an empty string.
    • The applicationData will be set later in the transitObject:Patch call.
  • The validTimeInterval DateTime objects must have the timezone offset specified, for example: 2024-04-12T19:20:50.52-04:00.

Method: transitObject:patch

This is the Google Wallet API endpoint to patch the transitObject with data to be used by Google for Motics barcode generation and fetching VDV eTicket Service certificates. Refer to the transitObject & transitObject.Patch API documentation for a full list of (non-Motics) parameters and more details.

PATCH:
https://walletobjects.googleapis.com/walletobjects/v1/transitObject/{resourceId}

JSON representation

The Motics integration requires the following representation of the transitObject in transitObject:patch request body. Note that it is at this point that the applicationData field is populated.

{
  "activationStatus": {
    "state": ACTIVATED (State)
  },
  "rotatingBarcode": {
    "type": AZTEC (BarcodeType),
    "valuePattern": "{vdv_barcode}",
    "deviceEntitlementSupport": {
      "vdvEntitlementDetails": {
        "applicationData": string - Hex encoded,
      },
    },
  }
}

Application Data specifications

The following is the Motics specification for the contents of the applicationData (tag:0x5F07). The applicationData should be generated by the system integrator in tag-length-value (TLV) format. This data is later encapsulated in a larger data structure to be finally encoded as part of the QR code.

Tag Length Value
0x9E 81 80 Signature
OctetString, first 128 bytes of signed entitlement data
Google term: sigSTB
0x9A Varies Residual data
OctetString, residual entitlement data
Google term: sigSTB cont.
0x7F21 81 C8 Certificate of the issuing
OctetString, certificate data
Google term: Cert(puk_SAM)
0x42 08 Certificate Authority Reference (CAR)
OctetString, CAR value
Google term: CAR