在通过 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
我正在尝试使用 PHP 验证 Google Play/Android 在应用程序产品消费购买服务器端。我收到带有有效收据的回复,但有两个令人困惑的问题:
productId
总是null
- 如果我将下面示例中的
$productId
更改为无效 ID,它会 return 完全相同的响应。这似乎是任何字符串的情况。
这是我的示例代码:
$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
,我得到:
=> 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。
https://issuetracker.google.com/issues/169870112
我在 google 问题跟踪器上发现忽略 productId 是一种预期行为。我写了 post 询问我们在响应中收到的 null 是否也是预期的行为。我希望不会,因为正如您所写,如果 productId 始终为空,我们现在无法完全验证 IAP。
在看到 Deusald 的回复 并开始输入对 Google 问题票的回复后,我顿悟了:Google 只是在验证交易对您的帐户有效,并希望您验证收据数据 server-side。它们在收据数据中包含一个 base64 编码的 RSA SHA1 签名以及用于创建该签名的原始数据,因此它们为您提供了完成此操作所需的一切。
下面的代码片段完成了 PHP 的验证,但它应该很容易移植到其他语言:
<?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());
}
$purchaseToken 在 Josh 的回答中未定义。
<?php
$appId = "com.example.myapp";
$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 === 0) {
throw new Exception("Invalid receipt");
}
if ($result !== 1) {
throw new Exception(openssl_error_string());
}
// 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");
}
我正在尝试使用 PHP 验证 Google Play/Android 在应用程序产品消费购买服务器端。我收到带有有效收据的回复,但有两个令人困惑的问题:
productId
总是null
- 如果我将下面示例中的
$productId
更改为无效 ID,它会 return 完全相同的响应。这似乎是任何字符串的情况。
这是我的示例代码:
$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
,我得到:
=> 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。
https://issuetracker.google.com/issues/169870112
我在 google 问题跟踪器上发现忽略 productId 是一种预期行为。我写了 post 询问我们在响应中收到的 null 是否也是预期的行为。我希望不会,因为正如您所写,如果 productId 始终为空,我们现在无法完全验证 IAP。
在看到 Deusald 的回复
下面的代码片段完成了 PHP 的验证,但它应该很容易移植到其他语言:
<?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());
}
$purchaseToken 在 Josh 的回答中未定义。
<?php
$appId = "com.example.myapp";
$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 === 0) {
throw new Exception("Invalid receipt");
}
if ($result !== 1) {
throw new Exception(openssl_error_string());
}
// 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");
}