在向用户提供可下载内容之前,我无法找到关于如何验证服务器上的应用内结算购买的直接答案。
我在app-billing版本3中使用。我使用基于TrivialDrive示例代码中的IabHelper类的代码购买托管产品。一切都很好,花花公子,购买成功完成,我得到一个完整的购买对象和以下原始JSON数据:
{
"orderId":"12999763169054705758.1364365967744519",
"packageName":"my package name",
"productId":"77",
"purchaseTime":1366217534000,
"purchaseState":0,
"purchaseToken":"utfwimslnrrwvglktizikdcd.AO-J1OwZ4l5oXz_3d2SAWAAUgFE3QErKoyIX8WuSEnBW26ntsyDmlLgoUd5lshqIY2p2LnlV4tpH4NITB4mJMX98sCtZizH7wGf6Izw3tfW_GflJDKFyb-g"
}
据我所知,我需要传递purchaseToken和我认为是服务器签名的东西。然后,服务器使用私钥来验证购买。它是否正确?如果是这样,我从哪里获得签名,是否真的没有关于购买的服务器端验证的正确文档?
答案 0 :(得分:15)
where do I get the signature from ?
它表示在onActivityResult()
方法中,您可以获得以下数据,如示例
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1001) {
int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");//this is the signature which you want
if (resultCode == RESULT_OK) {
try {
JSONObject jo = new JSONObject(purchaseData);//this is the JSONObject which you have included in Your Question right now
String sku = jo.getString("productId");
String purchaseToken = jo.getString("purchaseToken");
//you need to send sku and purchaseToken to server for verification
}
catch (JSONException e) {
alert("Failed to parse purchase data.");
e.printStackTrace();
}
}
}
}
在服务器端进行验证 看看official docs
如前所述,客户端应用会将sku
和purchaseToken
发送到服务器API。服务器必须接收这些值,并且必须使用android publish api执行检查以验证购买:
服务器可以通过添加必要的参数来调用 GET请求:
https://www.googleapis.com/androidpublisher/v2/applications/的的packageName 强> /购买/产品/的的productId 强> /令牌/的令牌强>
这里,
packageName =客户端应用程序的packageName
productId =从客户端应用程序收到的sku
从客户端应用程序收到的token = purchaseToken
如上所述格式会产生JSONObject
响应:
{
"kind": "androidpublisher#productPurchase",
"purchaseTimeMillis": long,
"purchaseState": integer,
"consumptionState": integer,
"developerPayload": string,
"orderId": string,
"purchaseType": integer
}
此处 purchaseState = 0表示有效购买
我希望它会有所帮助!!
答案 1 :(得分:4)
我在减少应用内购买欺诈方面做出的微薄贡献
在Android代码上的外部服务器上进行签名验证:
<强> verifySignatureOnServer()强>
private boolean verifySignatureOnServer(String data, String signature) {
String retFromServer = "";
URL url;
HttpsURLConnection urlConnection = null;
try {
String urlStr = "https://www.example.com/verify.php?data=" + URLEncoder.encode(data, "UTF-8") + "&signature=" + URLEncoder.encode(signature, "UTF-8");
url = new URL(urlStr);
urlConnection = (HttpsURLConnection) url.openConnection();
InputStream in = urlConnection.getInputStream();
InputStreamReader inRead = new InputStreamReader(in);
retFromServer = convertStreamToString(inRead);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
return retFromServer.equals("good");
}
<强> convertStreamToString()强>
private static String convertStreamToString(java.io.InputStreamReader is) {
java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
网页寄存的根目录verify.php
<?php
// get data param
$data = $_GET['data'];
// get signature param
$signature = $_GET['signature'];
// get key
$key_64 = ".... put here the base64 encoded pub key from google play console , all in one row !! ....";
$key = "-----BEGIN PUBLIC KEY-----\n".
chunk_split($key_64, 64,"\n").
'-----END PUBLIC KEY-----';
//using PHP to create an RSA key
$key = openssl_get_publickey($key);
// state whether signature is okay or not
$ok = openssl_verify($data, base64_decode($signature), $key, OPENSSL_ALGO_SHA1);
if ($ok == 1) {
echo "good";
} elseif ($ok == 0) {
echo "bad";
} else {
die ("fault, error checking signature");
}
// free the key from memory
openssl_free_key($key);
?>
备注:强>
你应该加密你的java代码中的URL,如果不能通过你的解压缩应用程序中的简单文本搜索轻松找到URL apk
最好将php文件名,url参数,好/坏响应更改为无意义的内容。
如果不引发主线程异常上的网络,则verifySignatureOnServer()应该在一个单独的线程中运行。
希望它会有所帮助...
答案 2 :(得分:2)
这个问题太老了,但我希望我的回答可以帮助别人。
您必须验证客户端上的签名,然后您必须将purchaseToken
传递到服务器端,然后服务器将联系 Google的服务器并获取有关购买的所有必要信息,例如purchaseState
和consumptionState
。
https://developers.google.com/android-publisher/api-ref/purchases/products
答案 3 :(得分:1)
1。使用作曲家安装Google客户端PHP库
composer require google/apiclient:"^2.0"
起始代码
require $_SERVER['DOCUMENT_ROOT'] . '/client/vendor/autoload.php';
$packageName='Your Package name'; //com.example.blahblah
$productId='your_product_ID';
$token='Purchase_Token_Form_Payment';
$client = new \Google_Client();
$client->setAuthConfig('credentials.json');
$client->addScope('https://www.googleapis.com/auth/androidpublisher');
$service = new \Google_Service_AndroidPublisher($client);
$purchase = $service->purchases_products->get($packageName, $productId, $token);
//echo $purchase['purchaseState'];
//echo '<br>';
echo json_encode($purchase);
结果将是这样
{"acknowledgementState":1,"consumptionState":1,"developerPayload":"","kind":"androidpublisher#productPurchase","orderId":"GPA.3342-8146-5668-57982","productId":null,"purchaseState":0,"purchaseTimeMillis":"1586978561493","purchaseToken":null,"purchaseType":0,"quantity":null}
答案 4 :(得分:0)
这是一个很老的问题,但我认为它仍然有意义。
我的小贡献。购买对象扩展了一个新参数“确认”,现在看起来:
{
"orderId":"12999763169054705758.1364365967744519",
"packageName":"my package name",
"productId":"77",
"purchaseTime":1366217534000,
"purchaseState":0,
"purchaseToken":"utfwimslnrrwvglktizikdcd.AO-J1OwZ4l5oXz_3d2SAWAAUgFE3QErKoyIX8WuSEnBW26ntsyDmlLgoUd5lshqIY2p2LnlV4tpH4NITB4mJMX98sCtZizH7wGf6Izw3tfW_GflJDKFyb-g",
"acknowledged":true
}
因此在检查签名时添加此额外参数时要小心。