通过Purchases.products.get验证Android In App Product时,productId将被忽略并返回null. [英] productId is ignored and returned as null when validating Android In App Product via purchases.products.get

查看:870
本文介绍了通过Purchases.products.get验证Android In App Product时,productId将被忽略并返回null.的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用PHP验证在服务器端消耗的Google Play/Android应用内商品.我收到了带有有效收据的回复,但是有两个令人困惑的问题:

I'm trying to validate a Google Play/Android In App Product consumed purchase server-side using PHP. I get a response back with a valid receipt, but there are two confusing issues:

  1. productId始终为null
  2. 如果我将下面示例中的$productId更改为无效的ID,它将返回完全相同的响应.字面上的任何字符串似乎都是这种情况.
  1. The productId is always null
  2. If I change the the $productId in the sample below to an invalid ID, it will return the exact same response. This seems to be the case for literally any string.

这是我的示例代码:

$purchaseToken = 'TEST purchaseToken FROM ANDROID APP';
$appId = 'com.example.myapp';
$productId = 'com.example.myapp.iap1';

$googleClient = new \Google_Client();
$googleClient->setScopes([\Google_Service_AndroidPublisher::ANDROIDPUBLISHER]);
$googleClient->setApplicationName($appId);
$googleClient->setAuthConfig(__DIR__ . '/gp-service.json');
$googleAndroidPublisher = new \Google_Service_AndroidPublisher($googleClient);
$purchase = $googleAndroidPublisher->purchases_products->get($appId, $productId, $purchaseToken);

如果我放弃$purchase,我会得到:

If I dump out $purchase, I get:

=> Google_Service_AndroidPublisher_ProductPurchase {
     +acknowledgementState: 1,
     +consumptionState: 1,
     +developerPayload: "",
     +kind: "androidpublisher#productPurchase",
     +obfuscatedExternalAccountId: null,
     +obfuscatedExternalProfileId: null,
     +orderId: "GPA.XXXX-XXXX-XXXX-XXXXX",
     +productId: null,
     +purchaseState: 0,
     +purchaseTimeMillis: "1602771022178",
     +purchaseToken: null,
     +purchaseType: 0,
     +quantity: null,
     +regionCode: "US",
   }

有人知道我在做什么错吗?它似乎并没有在最后验证productId,也没有为我提供我需要在其最后验证它的数据,这意味着我现在无法验证此IAP.

Does anyone know what I am doing wrong here? It doesn't seem to be validating the productId on its end nor does it provide me the data I would need to validate it on my end, meaning I have no way of validating this IAP right now.

推荐答案

在此处看到Deusald的回复后,开始输入在投诉Google Issues票证的回应中,我有一个顿悟:Google只是在验证该交易对您的帐户有效,并希望您在服务器端验证收据数据.它们在收据数据中以及用于创建该签名的原始数据中包括base64编码的RSA SHA1签名,因此它们为您提供了完成此操作所需的一切.

After seeing Deusald's response here and starting to type up a response to the Google Issues ticket complaining about it, I had an epiphany: Google is just validating that the transaction is valid for your account and expects you to validate the receipt data server-side. They include a base64 encoded RSA SHA1 signature in the receipt data and the original data they used to create that signature, so they give you everything you need to accomplish this.

下面的代码段完成了对PHP的验证,但应该可以轻松移植到其他语言:

The below snippet accomplishes that verification for PHP, but it should be easily portable to other languages:

<?php

// our app's bundle id
$appId = 'com.example.myapp';

// the location of a Service Account JSON file for a Service account that has access to the "Financial Data" permissions in the Play Console
$serviceAccountJson = __DIR__ . '/gp-service.json';

// this is the raw receipt data received on the device from Google Play; this example is obfuscated and only shows the keys for sensitive fields
$googlePlayReceipt = '{"productId": "com.example.myapp.iap1","transactionDate": 1602714720893,"transactionReceipt": "","purchaseToken": "","transactionId": "","dataAndroid": "","signatureAndroid": "","isAcknowledgedAndroid": false,"autoRenewingAndroid": false,"purchaseStateAndroid": 1}';
// decode the json to an array we can use
$decodedGooglePlayReceipt = json_decode($googlePlayReceipt, true);

// the data that was signed for verification purposes
$data = $decodedGooglePlayReceipt['transactionReceipt'];
// the signature that was used to sign the $data
$signature = $decodedGooglePlayReceipt['signatureAndroid'];

// The "Base64-encoded RSA public key" for your app, taken from the Google Play Console
// In the Classic Console: Your App -> Development Tools -> Services & APIs -> Licensing & in-app billing
// In the New Console: Your App -> Monetize -> Monetization Setup -> Licensing
$base64EncodedPublicKeyFromGoogle  = '################';

// Convert the key into the normal public key format
// Just need to split the base64 key into 64 character long lines and add the usual prefix/suffix
$openSslFriendlyKey = "-----BEGIN PUBLIC KEY-----\n" . chunk_split($base64EncodedPublicKeyFromGoogle, 64, "\n") .  "-----END PUBLIC KEY-----";
// Convert the key to the openssl key ID that openssl_verify() expects
// I'm unsure if this step would be necessary on all platforms
$publicKeyId = openssl_get_publickey($openSslFriendlyKey);

// Use openssl_verify() to verify the $signature (which has to be base64 decoded!) against the $data using the public key we have
$result = openssl_verify($data, base64_decode($signature), $publicKeyId, OPENSSL_ALGO_SHA1);
if ($result === 1) {
    // receipt data is valid. now let's grab the productId and purchaseToken
    $decodedData = json_decode($data, true);
    $purchasedProductId = decodedData['productId'];
    $$purchaseToken = decodedData['purchaseToken'];

    // now we'll verify that the receipt is valid for our account
    try {
        $googleClient = new \Google_Client();
        $googleClient->setScopes([\Google_Service_AndroidPublisher::ANDROIDPUBLISHER]);
        $googleClient->setApplicationName($appId);
        $googleClient->setAuthConfig($serviceAccountJson);
        $googleAndroidPublisher = new \Google_Service_AndroidPublisher($googleClient);
        $purchase = $googleAndroidPublisher->purchases_products->get($appId, $purchasedProductId, $purchaseToken);
    } catch (Throwable $exception) {
        // this means the receipt data is unable to be validated by Google
        throw new Exception('Invalid receipt');
    }
} elseif ($result === 0) {
    throw new Exception('Invalid receipt');
} else {
    throw new Exception(openssl_error_string());
}

这篇关于通过Purchases.products.get验证Android In App Product时,productId将被忽略并返回null.的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆