Flujo de desarrollo del pase 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 define campos que son iguales en todas las entradas, como el nombre, la fecha y la hora del evento.

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 un boleto de 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 un ID único a cada objeto Passes, que se usa para hacer referencia a él cuando se emite 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 ejemplo de objeto Passe objeto

{
  "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 que se emitan 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 ya se emitió el pase, 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 se definen 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 que se envíen a la API de la Billetera de Google para emitir un pase deben firmarse con las credenciales que proporcionaste anteriormente en la Billetera Business de la Billetera de Google. La firma usa tus credenciales para encriptar el JWT para que tus pases permanezcan seguros y para permitir que la API de la Billetera de Google autentique que los detalles codificados en él son válidos y están asociados con la cuenta de la entidad emisora.

Cómo se hace

Las bibliotecas cliente de la Billetera de Google y el SDK de Android proporcionan métodos convenientes para firmar tus JWT. También hay numerosas bibliotecas de código abierto disponibles que se encargan de la complejidad de la firma de código para que elijas la que necesites.

A 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 usan el SDK de Android de la Billetera de Google, el SDK administra automáticamente la firma del JWT con la huella digital SHA-1 del certificado de firma de tu app.

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

Mostrar ejemplos 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, podrás enviar tu pase a un usuario de la Billetera de Google. Para ello, se debe presentar 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 quieres crear el botón “Agregar a la Billetera de Google” en una app para Android, usa el SDK de la Billetera de Google para Android, que proporciona métodos para generar el botón. Para todas las demás plataformas, incluidas la Web, el correo electrónico y los mensajes de texto, crea un hipervínculo con el formato https://pay.google.com/gp/v/save/<signed_jwt>. Cuando sea posible, es mejor enviar 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, este aparecerá en la app de la Billetera de Google junto con cualquier otro pase que haya guardado.

Cómo crear objetos Passes y clases Passes en el JWT

Los Objetos Passes y las clases 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 clases de pases y objetos de pase “justo a tiempo” incorporando 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 los objetos Passes Classes y Passes 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": ""
        }
      }]
    }
  }