40

在向用户提供可下载内容之前,我无法找到关于如何在服务器上验证应用内结算购买的直接答案。

我在应用计费版本 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 和我看到的称为签名的东西传递给服务器。然后服务器使用私钥来验证购买。这个对吗?如果是这样,我从哪里获得签名,是否真的没有关于服务器端购买验证的体面文档?

4

5 回答 5

23
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();
          }
      }
   }
}

对于服务器端的验证, 请查看官方文档

如前所述,客户端应用程序将发送sku和发送purchaseToken到服务器 API。服务器必须接收这些值,并且必须使用 android publish api 执行检查以验证购买:

服务器可以通过添加必要的参数来调用以下GET 请求:

https://www.googleapis.com/androidpublisher/v2/applications/ packageName /purchases/products/ productId /tokens/ token

这里,
packageName = 客户端应用程序的 packageName
productId = 从客户端应用程序收到的 sku
令牌 = 从客户端应用程序收到的 purchaseToken

它将导致JSONObject上述格式的响应:

{
  "kind": "androidpublisher#productPurchase",
  "purchaseTimeMillis": long,
  "purchaseState": integer,
  "consumptionState": integer,
  "developerPayload": string,
  "orderId": string,
  "purchaseType": integer
}

这里,purchaseState = 0 表示有效购买

我希望它会有所帮助!

于 2013-04-17T18:37:23.237 回答
7

我对减少应用内购买欺诈的小贡献

外部服务器上的签名验证,在您的 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");
    }

转换流到字符串()

 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,如果不是,则可以通过在解压缩的应用程序 apk 中进行简单的文本搜索轻松找到该 URL

  • 最好将 php 文件名、url 参数、好/坏响应更改为毫无意义的东西。

  • verifySignatureOnServer() 应该在一个单独的线程中运行,如果不是主线程上的网络将抛出异常。

希望它会有所帮助...

于 2018-02-06T14:22:43.723 回答
3

这个问题太老了,但我希望我的回答可以帮助别人。

您必须在客户端验证签名,然后您必须传递purchaseToken服务器端,然后服务器将联系谷歌的服务器并获取有关购买的所有必要信息,例如purchaseStateconsumptionState

https://developers.google.com/android-publisher/api-ref/purchases/products

于 2016-03-30T10:14:53.220 回答
3

1.用composer安装谷歌客户端PHP库

composer require google/apiclient:"^2.0"
  1. 创建服务帐户,它将为您提供 json 并保存为 credentials.json

在此处输入图像描述

  1. 起始码

    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}
于 2020-04-16T04:23:07.003 回答
0

这是一个非常古老的问题,但我认为它仍然是相关的。

我的小贡献。购买对象扩展了一个新参数“已确认”,现在它看起来:

{
  "orderId":"12999763169054705758.1364365967744519",
  "packageName":"my package name",
  "productId":"77",
  "purchaseTime":1366217534000,
  "purchaseState":0,
  "purchaseToken":"utfwimslnrrwvglktizikdcd.AO-J1OwZ4l5oXz_3d2SAWAAUgFE3QErKoyIX8WuSEnBW26ntsyDmlLgoUd5lshqIY2p2LnlV4tpH4NITB4mJMX98sCtZizH7wGf6Izw3tfW_GflJDKFyb-g",
  "acknowledged":true
}

所以在检查签名时要小心添加这个额外的参数。

于 2019-05-06T22:39:26.950 回答