Android IAP的Java服务器端验证



我想通过谷歌的API在我的中央游戏服务器上验证Android IAP。

关于这个有很多不完整的信息,这让我很震惊。我没有支付€25成为谷歌开发人员,因为我不确定如果我将能够得到它的工作。

当进行IAP时,返回一个JSON对象。该对象包含几个字段,如purchaseTokenproductId(源)。

我发现您可以通过以下GET请求来请求购买产品的信息:GET https://www.googleapis.com/androidpublisher/v2/applications/packageName/purchases/products/productId/tokens/token .

我可以编程这没有问题,但您需要授权自己:"此请求需要具有以下范围的授权"(源)。这就是我开始感到困惑的地方。

  1. 您需要通过开发控制台(链接)创建某种登录令牌。我不知道是哪一种。OAuth还是服务帐户?
  2. 这个令牌是短期的。你需要刷新它

在互联网上可以找到一些可能工作也可能不工作的大代码片段,但它们都是部分的,并且没有很好的文档。

我找到了谷歌的Java API库:链接。这个API似乎是为了解决OAuth和令牌的所有这些问题。然而,我无法弄清楚如何让这个API工作。

这可能没有那么难,但是有很多不同的方法可以做到这一点,我找不到任何清晰的例子。

DR:我需要验证Google Play IAP服务器端。要做到这一点,我想使用谷歌的Java API。

编辑:这可能是一个更简单的解决方案。将原始JSON和JSON一起传递给服务器可能更容易,因为我可以在服务器端验证非对称签名。

我已经在Scala中做到了这一点,但使用的是Java标准库。我相信将代码转换为Java应该很简单。这种实现的主要优点是它不依赖于Google的库。

  • 首先,您需要一个服务帐户。你可以通过Google Dev控制台创建它。它基本上返回给您一个生成的电子邮件帐户,您将使用该帐户对后端服务进行身份验证并生成令牌。

  • 创建该帐户后,提示您下载私钥。你需要它来签署JWT。

  • 您必须以Google指定的格式生成JWT(我将在下面的代码中向您展示如何生成)。见:https://developers.google.com/identity/protocols/OAuth2ServiceAccount creatingjwt

  • 然后,使用JWT,您可以请求访问令牌

  • 使用访问令牌,您可以请求验证您的购买

/** Generate JWT(JSON Web Token) to request access token
* How to generate JWT: https://developers.google.com/identity/protocols/OAuth2ServiceAccount#creatingjwt
*
* If we need to generate a new Service Account in the Google Developer Console,
* we are going to receive a .p12 file as the private key. We need to convert it to .der.
* That way the standard Java library can handle that.
*
* Covert the .p12 file to .pem with the following command:
* openssl pkcs12 -in <FILENAME>.p12 -out <FILENAME>.pem -nodes
*
* Convert the .pem file to .der with the following command:
* openssl pkcs8 -topk8 -inform PEM -outform DER -in <FILENAME>.pem -out <FILENAME>.der -nocrypt
*
* */
private def generateJWT(): String = {
  // Generating the Header
  val header = Json.obj("alg" -> "RS256", "typ" -> "JWT").toString()
  // Generating the Claim Set
  val currentDate = DateTime.now(DateTimeZone.UTC)
  val claimSet =Json.obj(
    "iss" -> "<YOUR_SERVICE_ACCOUNT_EMAIL>",
    "scope" -> "https://www.googleapis.com/auth/androidpublisher",
    "aud" -> "https://www.googleapis.com/oauth2/v4/token",
    "exp" -> currentDate.plusMinutes(5).getMillis / 1000,
    "iat" -> currentDate.getMillis / 1000
  ).toString()
  // Base64URL encoded body
  val encodedHeader = Base64.getEncoder.encodeToString(header.getBytes(StandardCharsets.UTF_8))
  val encodedClaimSet = Base64.getEncoder.encodeToString(claimSet.getBytes(StandardCharsets.UTF_8))
  // use header and claim set as input for signature in the following format:
  // {Base64url encoded JSON header}.{Base64url encoded JSON claim set}
  val jwtSignatureInput = s"$encodedHeader.$encodedClaimSet"
  // use the private key generated by Google Developer console to sign the content. 
  // Maybe cache this content to avoid unnecessary round-trips to the disk.
  val keyFile = Paths.get("<path_to_google_play_store_api.der>");
  val keyBytes = Files.readAllBytes(keyFile);
  val keyFactory = KeyFactory.getInstance("RSA")
  val keySpec = new PKCS8EncodedKeySpec(keyBytes)
  val privateKey = keyFactory.generatePrivate(keySpec)
  // Sign payload using the private key
  val sign = Signature.getInstance("SHA256withRSA")
  sign.initSign(privateKey)
  sign.update(jwtSignatureInput.getBytes(StandardCharsets.UTF_8))
  val signatureByteArray = sign.sign()
  val signature = Base64.getEncoder.encodeToString(signatureByteArray)
  // Generate the JWT in the following format:
  // {Base64url encoded JSON header}.{Base64url encoded JSON claim set}.{Base64url encoded signature}
  s"$encodedHeader.$encodedClaimSet.$signature"
}

现在已经生成了JWT,可以像这样请求access token:

/** Request the Google Play access token */
private def getAccessToken(): Future[String] = {
  ws.url("https://www.googleapis.com/oauth2/v4/token")
    .withHeaders("Content-Type" -> "application/x-www-form-urlencoded")
    .post(
      Map(
        "grant_type" -> Seq("urn:ietf:params:oauth:grant-type:jwt-bearer"),
        "assertion" -> Seq(generateJWT()))
    ).map {
    response =>
      try {
        (response.json  "access_token").as[String]
      } catch {
        case ex: Exception => throw new IllegalArgumentException("GooglePlayAPI - Invalid response: ", ex)
      }
  }
}

有了访问令牌,您可以自由地验证您的购买。

根据我上面的评论,Google API管理器在设计上有所改变,但过程仍然相同。您想要创建一个服务帐户,创建后您可以下载JSON Web令牌,这是一个简单的JSON文件,包含您需要进行身份验证的凭据。服务帐户应该拥有访问Google Play Developer API所需的所有权限。您需要在Google Play Developer Console> Settings> API access页面中授予对Finance的访问权限。

你可以使用Google API Client Library for Java来验证并向Google Play Developer API发出请求。按照OAuth2服务帐户文档进行设置。数据存储上有一个说明,描述了如何处理刷新令牌。

还有关于如何使用Google Play Developer API Client Library for Java的文档。

对于验证购买JSON的签名,在购买意图的响应有效负载中有一个INAPP_DATA_SIGNATURE。有关如何获得物品的更多信息,请参阅购买物品的文档。您可以通过base64解码签名并使用您的许可密钥验证INAPP_PURCHASE_DATA,在Google Play开发控制台>所有应用程序>[应用程序名称]>服务&;api。确保INAPP_PURCHASE_DATA是完整的,否则你将结束这个问题。

最新更新