Flujo de desarrollo de pases de la Billetera de Google

La API de la Billetera de Google proporciona un conjunto predefinido de tipos de pases optimizados para casos de uso específicos, como tarjetas de regalo, tarjetas de embarque, entradas para eventos y mucho más. También hay un tipo de pase genérico destinado a casos de uso en los que un tipo de pase específico no está disponible.

El objetivo de este artículo es que te familiarices con los pasos básicos necesarios para crear y emitir un pase con la API de la Billetera de Google. Hay varias maneras de realizar algunos de los pasos que se detallan a continuación, pero, en términos generales, todos los tipos de pases se crean siguiendo el mismo flujo de desarrollo básico.

Si quieres obtener una explicación detallada para crear un pase, consulta las guías de apps web, de correo electrónico y SMS o de apps para Android.

Para qué sirve

Una clase Passes define un conjunto de propiedades que son comunes entre varios pases, similar a una plantilla. Por ejemplo, si emites entradas para un evento, la clase Passes definiría campos que son iguales para todas las entradas, como el nombre del evento, la fecha y la hora.

Cada pase que emitas debe hacer referencia a una clase Passes. También debes asignar un ID único a cada clase de pases que crees, que se usará como referencia cuando crees pases.

Cómo se hace

Una clase Passes se define en formato JSON y se puede crear con la API de REST de la Billetera de Google, el SDK de Android o en Google Wallet Business Console.

Mostrar clase Passes de ejemplo

{
  "id": "ISSUER_ID.EVENT_CLASS_ID",
  "issuerName": "[TEST ONLY] Heraldic Event",
  "localizedIssuerName": {
    "defaultValue": {
      "language": "en-US",
      "value": "[TEST ONLY] Heraldic Event"
    }
  },
  "logo": {
    "sourceUri": {
      "uri": "https://images.unsplash.com/photo-1475721027785-f74eccf877e2?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=660&h=660"
    },
    "contentDescription": {
      "defaultValue": {
        "language": "en-US",
        "value": "LOGO_IMAGE_DESCRIPTION"
      }
    }
  },
  "eventName": {
    "defaultValue": {
      "language": "en-US",
      "value": "Google Live"
    }
  },
  "venue": {
    "name": {
      "defaultValue": {
        "language": "en-US",
        "value": "Shoreline Amphitheater"
      }
    },
    "address": {
      "defaultValue": {
        "language": "en-US",
        "value": "ADDRESS_OF_THE_VENUE"
      }
    }
  },
  "dateTime": {
    "start": "2023-04-12T11:30"
  },
  "reviewStatus": "UNDER_REVIEW",
  "hexBackgroundColor": "#264750",
  "heroImage": {
    "sourceUri": {
      "uri": "https://images.unsplash.com/photo-1501281668745-f7f57925c3b4?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1032&h=336"
    },
    "contentDescription": {
      "defaultValue": {
        "language": "en-US",
        "value": "HERO_IMAGE_DESCRIPTION"
      }
    }
  }
}
    

Para qué sirve

Un objeto Passes define las propiedades de un pase único que se emitirá a un usuario específico. Por ejemplo, el objeto Passes de una entrada para un evento definiría campos que son exclusivos de una entrada específica, como el número de asiento o un código QR de esa entrada.

Cuando se crea un objeto Passes, la API de la Billetera de Google almacena un pase nuevo y lo asocia con la cuenta de la entidad emisora. Este pase almacenado es una combinación de las propiedades únicas del objeto Passes y las propiedades de la plantilla de la clase Passes asociada.

También debes asignar a cada objeto Passes un ID único, que se usa para hacer referencia a él cuando emites un pase.

Cómo se hace

Un objeto Passes tiene un formato JSON definido y se puede crear con la API de REST de la Billetera de Google o el SDK de Android.

Mostrar objeto Passes de ejemplo

{
  "id": "ISSUER_ID.OBJECT_ID",
  "classId": "ISSUER_ID.EVENT_CLASS_ID",
  "state": "ACTIVE",
  "seatInfo": {
    "seat": {
      "defaultValue": {
        "language": "en-us",
        "value": "5"
      }
    },
    "row": {
      "defaultValue": {
        "language": "en-us",
        "value": "G"
      }
    },
    "section": {
      "defaultValue": {
        "language": "en-us",
        "value": "40"
      }
    },
    "gate": {
      "defaultValue": {
        "language": "en-us",
        "value": "3A"
      }
    }
  },
  "barcode": {
    "type": "QR_CODE",
    "value": "BARCODE_VALUE",
    "alternateText": ""
  }
}
    

Para qué sirve

Para emitir un pase a un usuario, una clase Passes y un objeto Passes deben estar codificados en un token web JSON (JWT). El formato JWT es un estándar abierto y común para representar reclamaciones entre dos partes. En el caso de emitir pases con la API de la Billetera de Google, los JWT se usan para enviar un reclamo de que un usuario tiene derecho a acceder a un pase específico asociado con la cuenta de la entidad emisora.

Cuando se envía un JWT a la API de la Billetera de Google, los datos codificados se usan para identificar un pase específico y emitirlo al usuario. Si el pase ya se emitió, estos datos también permiten que la API de la Billetera de Google identifique que el pase es un duplicado para que no se agregue a la Billetera de Google del usuario más de una vez.

Cómo se hace

Los JWT están definidos en formato JSON según la especificación de JWT. Si quieres definir un JWT para emitir un pase con la API de la Billetera de Google, debes proporcionar la información sobre el pase que quieres emitir en la propiedad payload del JWT.

Mostrar JWT de ejemplo

{
  "iss": "issuer@example.com",
  "aud": "google",
  "typ": "savetowallet",
  "iat": 1696877738,
  "origins": [
    "www.example.com"
  ],
  "payload": {
    "eventTicketObjects": [
      {
        "id": "ISSUER_ID.LOYALTY_OBJECT_SUFFIX"
      }
    ]
  }
}
    

Para qué sirve

Todos los JWT enviados a la API de Google Wallet para emitir un pase deben firmarse con las credenciales que proporcionaste anteriormente en la Google Wallet Business Console. La firma usa tus credenciales para encriptar el JWT a fin de que tus pases permanezcan seguros y permitir que la API de la Billetera de Google autentique que los detalles del pase codificados son válidos y están asociados con tu cuenta de la entidad emisora.

Cómo se hace

Las bibliotecas cliente de Google Wallet y el SDK de Android proporcionan métodos prácticos para firmar tus JWT. También existen numerosas bibliotecas de código abierto disponibles que pueden elegir entre la complejidad de la firma de código.

Para quienes usen la API de REST de la Billetera de Google para emitir pases, el JWT se firma con una clave de cuenta de servicio de Google Cloud. Para quienes utilizan el SDK de la Billetera de Google para Android, el SDK maneja automáticamente la firma del JWT con la huella digital SHA-1 del certificado de firma de la app.

Para proteger tus credenciales, los JWT solo deben firmarse en tu servidor o con el SDK de Android de la Billetera de Google en tu aplicación.

Mostrar ejemplo de firma de código

Java

  // Create the JWT as a HashMap object
  HashMap claims = new HashMap();
  claims.put("iss", ((ServiceAccountCredentials) credentials).getClientEmail());
  claims.put("aud", "google");
  claims.put("origins", Arrays.asList("www.example.com"));
  claims.put("typ", "savetowallet");

  // Create the Google Wallet payload and add to the JWT
  HashMap payload = new HashMap();
  payload.put("eventTicketObjects", Arrays.asList(newObject));
  claims.put("payload", payload);

  // Google Cloud service account credentials are used to sign the JWT
  Algorithm algorithm =
      Algorithm.RSA256(
          null, (RSAPrivateKey) ((ServiceAccountCredentials) credentials).getPrivateKey());
  String token = JWT.create().withPayload(claims).sign(algorithm);
        

Node.JS

  // Create the JWT claims
  let claims = {
    iss: this.credentials.client_email,
    aud: 'google',
    origins: ['www.example.com'],
    typ: 'savetowallet',
    payload: {
      eventTicketObjects: [newObject]
    },
  };

  // The service account credentials are used to sign the JWT
  let token = jwt.sign(claims, this.credentials.private_key, { algorithm: 'RS256' });
        

Python

  # Create the JWT claims
  claims = {
      'iss': self.credentials.service_account_email,
      'aud': 'google',
      'origins': ['www.example.com'],
      'typ': 'savetowallet',
      'payload': {
          # The listed classes and objects will be created
          'eventTicketObjects': [new_object]
      }
  }

  # The service account credentials are used to sign the JWT
  signer = crypt.RSASigner.from_service_account_file(self.key_file_path)
  token = jwt.encode(signer, claims).decode('utf-8')
        

Para qué sirve

Una vez que hayas creado un JWT firmado, estarás listo para emitir tu pase a un usuario de la Billetera de Google. Para ello, se le presenta al usuario un botón o vínculo “Agregar a la Billetera de Google”. Cuando un usuario hace clic en el botón o hipervínculo, el JWT firmado se envía a la API de la Billetera de Google, que luego lo desencripta con tus credenciales guardadas. Una vez que se autentique la firma JWT, se emitirá el pase al usuario para que lo guarde en su Billetera de Google.

Cómo se hace

Si deseas crear un botón "Agregar a la Billetera de Google" para una aplicación de Android, usa el SDK de Android de la Billetera de Google, que proporciona métodos para generar el botón. Para todas las demás plataformas, como la Web, los correos electrónicos y los mensajes de texto, crea un hipervínculo con el formato https://pay.google.com/gp/v/save/<signed_jwt>. Siempre que sea posible, se recomienda entregar este vínculo al usuario como un botón "Agregar a la Billetera de Google".

Para obtener más información sobre el uso del botón “Agregar a la Billetera de Google”, consulta los Lineamientos de marca de la API de la Billetera de Google

Mostrar código de ejemplo

  https://pay.google.com/gp/v/save/<signed_jwt>
        

SDK de Android

  private lateinit var walletClient: PayClient
  private val addToGoogleWalletRequestCode = 1000
  private lateinit var addToGoogleWalletButton: View

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    walletClient = Pay.getClient(this)
    addToGoogleWalletButton.setOnClickListener {
      walletClient.savePasses(newObjectJson, this, addToGoogleWalletRequestCode)
    }
  }
        

Una vez que el usuario haya guardado el pase emitido, aparecerá en la app de la Billetera de Google junto con cualquier otro pase que haya guardado.

Crea pases de objetos y clases pases en el JWT

Las clases Passes y los Objetos Passes se pueden crear con anticipación mediante la API de REST de la Billetera de Google o el SDK de Android. Una vez creados, se usan para emitir pases haciendo referencia a su ID.

Como alternativa, también puedes crear objetos Passes Classes y Passes Objects “justo a tiempo” si incorporas su JSON directamente en el JWT que se usa para emitir el pase a un usuario. En este método, la API de la Billetera de Google crea las clases Passes y Passes Objects cuando se envía el JWT firmado con un botón o vínculo “Agregar a la Billetera de Google”.

Por ejemplo, a continuación se muestra un JWT con una clase Passes y un objeto Passes nuevos definidos mediante las propiedades payload.eventTicketClasses y payload.eventTicketObjects. Ten en cuenta que estas propiedades son arrays, por lo que pueden aceptar una o más clases Passes o Passes Objects. También puedes especificar solo un nuevo objeto Passes en el JWT que haga referencia a una clase Passes existente por su ID.

Mostrar JWT de ejemplo

  {
    "iss": "issuer@example.com",
    "aud": "google",
    "typ": "savetowallet",
    "iat": 1696877738,
    "origins": [
      "www.example.com"
    ],
    "payload": {
      "eventTicketClasses": [{
        "id": "ISSUER_ID.EVENT_CLASS_ID",
        "issuerName": "[TEST ONLY] Heraldic Event",
        "localizedIssuerName": {
          "defaultValue": {
            "language": "en-US",
            "value": "[TEST ONLY] Heraldic Event"
          }
        },
        "logo": {
          "sourceUri": {
            "uri": "https://images.unsplash.com/photo-1475721027785-f74eccf877e2?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=660&h=660"
          },
          "contentDescription": {
            "defaultValue": {
              "language": "en-US",
              "value": "LOGO_IMAGE_DESCRIPTION"
            }
          }
        },
        "eventName": {
          "defaultValue": {
            "language": "en-US",
            "value": "Google Live"
          }
        },
        "venue": {
          "name": {
            "defaultValue": {
              "language": "en-US",
              "value": "Shoreline Amphitheater"
            }
          },
          "address": {
            "defaultValue": {
              "language": "en-US",
              "value": "ADDRESS_OF_THE_VENUE"
            }
          }
        },
        "dateTime": {
          "start": "2023-04-12T11:30"
        },
        "reviewStatus": "UNDER_REVIEW",
        "hexBackgroundColor": "#264750",
        "heroImage": {
          "sourceUri": {
            "uri": "https://images.unsplash.com/photo-1501281668745-f7f57925c3b4?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1032&h=336"
          },
          "contentDescription": {
            "defaultValue": {
              "language": "en-US",
              "value": "HERO_IMAGE_DESCRIPTION"
            }
          }
        }
      }],
      "eventTicketObjects": [{
        "id": "ISSUER_ID.OBJECT_ID",
        "classId": "ISSUER_ID.EVENT_CLASS_ID",
        "state": "ACTIVE",
        "seatInfo": {
          "seat": {
            "defaultValue": {
              "language": "en-us",
              "value": "5"
            }
          },
          "row": {
            "defaultValue": {
              "language": "en-us",
              "value": "G"
            }
          },
          "section": {
            "defaultValue": {
              "language": "en-us",
              "value": "40"
            }
          },
          "gate": {
            "defaultValue": {
              "language": "en-us",
              "value": "3A"
            }
          }
        },
        "barcode": {
          "type": "QR_CODE",
          "value": "BARCODE_VALUE",
          "alternateText": ""
        }
      }]
    }
  }