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 no está disponible un tipo de pase específico.

Este artículo te ayudará a familiarizarte con los pasos básicos necesarios para crear y emitir un pase con la API de la Billetera de Google. Existen 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 para usar la API de REST o el SDK de Android de la Billetera de Google.

Para qué sirve

Una clase Passes define un conjunto de propiedades que son comunes en varios pases, de manera similar a una plantilla. Por ejemplo, si emitas entradas para un evento, la clase Passes definiría 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 Passes que crees, que se usará para hacer referencia a ella 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 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 tu 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 emite un pase.

Cómo se hace

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

Mostrar objeto de pase 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, un objeto Passes Class and Passes Object se debe codificar en un JSON Web Token (JWT). El formato JWT es un estándar común y abierto 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 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. Para definir un JWT a fin de emitir un pase con la API de la Billetera de Google, debes proporcionar la información sobre el pase que deseas emitir en la propiedad payload de 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 la Billetera de Google para emitir un pase se deben firmar con las credenciales que proporcionaste anteriormente en Google Wallet Business Console. La firma usa tus credenciales para encriptar el JWT a fin de que tus pases permanezcan seguros y para permitir que la API de la Billetera de Google autentique que los detalles del pase codificados en él son válidos y están asociados con tu cuenta de la entidad emisora.

Cómo se hace

Las bibliotecas de cliente de Google Wallet y el SDK de Android proporcionan métodos convenientes para firmar tus JWT. También existen varias bibliotecas de código abierto disponibles que pueden elegir entre las complejidades de la firma de código.

Para quienes usan 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 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 tu app.

A fin de 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 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, podrás emitir tu pase a un usuario de Google Wallet. Para ello, se le muestra 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

A fin de crear el botón “Agregar a la Billetera de Google” en una app para 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, 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>. Siempre que sea posible, es mejor 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

Los objetos Passes y las clases se pueden crear con anticipación mediante la API de REST o el SDK de Android de la Billetera de Google. 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 pases “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 “Add to Google Wallet”.

Por ejemplo, a continuación se muestra un JWT con una nueva clase Passes y Passes Object 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": ""
        }
      }]
    }
  }