简体   繁体   中英

Error Code 400: Invalid Value - Google Play IAP Verification Failure

I had working code in my server, that verified in-app purchases.

There are already 2 days, that my verification started give me a bad response.

{
    "error": {
        "errors": [
            {
                "domain": "global",
                "reason": "invalid",
                "message": "Invalid Value"
            }
        ],
        "code": 400,
        "message": "Invalid Value"
    }
}

Nothing changed on my side.

(PS I'm able to get an access token via refresh, so, I assume I have no problems with my credentials).

Here is the code, that worked OK before.

$product_sku = $_REQUEST['product_sku'];
$transaction_id = $_REQUEST['transaction_id'];
$transaction_time = $_REQUEST['transaction_time'];
$purchase_data = @$_REQUEST['purchase_data'];
$market = $_REQUEST['market'];

$verified = false;
$test_purchase = false;
if (isset($product_sku) && isset($transaction_id) && isset($transaction_time) && isset($market)) {

    // If it's GOOGLE
    if ($market == '2') {

        // verifies if the IAB is correct
        if (isset($purchase_data) && $purchase_data != "") {

            // Getting necessary data for verification
            $client_id = file_get_contents('google_play_developer_api_client_id');
            $client_secret = file_get_contents('google_play_developer_api_client_secret');
            $refresh_token = file_get_contents('google_play_developer_api_refresh_token');
            $refresh_token_url = 'https://accounts.google.com/o/oauth2/token';
            $verification_url = "https://www.googleapis.com/androidpublisher/v3/applications/mypackage/purchases/products/" . $product_sku . "/tokens/" . $purchase_data;

            // Preparing for the REFRESH_TOKEN request. This need to be changed after Memcache enabling.
            // Will be needed to store the ACCESS_TOKEN in the Memcache for the expiration time and after expiring get new ACCESS_TOKEN with REFRESH_TOKEN

            // constructing the necessary data for Google authentication
            $data_array = array(
                "grant_type" => "refresh_token",
                "client_id" => $client_id,
                "client_secret" => $client_secret,
                "refresh_token" => $refresh_token
            );

            // replacing '\/' with '/' as after json_encode() the '/' in the array values will be replaced with '\/'
            $data_array = str_replace("\/", "/", json_encode($data_array));

            // contracting Headers for the REFRESH_TOKEN request
            $headers = array(
                'APIKEY: 111111111111111111111',
                'Content-Type: application/json'
            );

            // making REFRESH_TOKEN request and getting the new ACCESS_TOKEN
            $make_call = callAPI('POST', $refresh_token_url, $data_array, $headers);

            $response = json_decode($make_call, true);

            if (array_key_exists("access_token", $response)) {
                $accessToken = $response["access_token"];

                // preparing for the Verification request

                // adding necessary headers
                array_push($headers, "Authorization: OAuth " . $accessToken, "Accept: application/json");

                // making Verification request and getting the receipt from Google
                $make_call = callAPI('GET', $verification_url, false, $headers);
                $receipt = json_decode($make_call, true);

                if (array_key_exists("purchaseState", $receipt)) {
                    // checking for the test purchase or for the purchase made using promo code.
                    // if purchaseType exists in the receipt the it is test purchase or the purchase made using promo code
                    // purchaseType = 0 -> Test Purchase, purchaseType = 1 -> Purchase made using promo code
                    if (array_key_exists("purchaseType", $receipt)) {
                        $purchaseType = $receipt["purchaseType"];
                        $test_purchase = $purchaseType == 0;
                    }

                    // Getting the purchaseState from the receipt.
                    // purchaseState = 0 -> Successfull purchase, purchaseState = 0 -> Canceled purchase
                    $purchaseState = $receipt["purchaseState"];

                    // Getting Order Id from the receipt
                    $order_id = $receipt["orderId"];

                    // Getting Purchase Time from the receipt. Time in millis from the Unix Epoch
                    $purchaseTimeMillis = $receipt["purchaseTimeMillis"];

                    // Verifying the purchase
                    // Verification is failed for any of the following reasons
                    // 1. Test purchase or the purchase made using promo code
                    // 2. Canceled Purchase
                    // 3. If the order id from receipt and the transaction id from the mobile app are different
                    // 4. If the PurchaseTime from the receipt and the Transaction Time from the mobile are different
                    // If all conditions are true, the purchase is verified.

                    $verified = ($purchaseState == 0 && $order_id == $transaction_id && $purchaseTimeMillis == $transaction_time);

                } elseif(!array_key_exists("error", $receipt)){
                        // Something went wrong, let's set the verified to true, so we don't know if it is cheating

                        $verified = true;
                }
            } else {
                // Something went wrong, let's set the verified to true, so we don't know if it is cheat
                $verified = true;
            }
        }
    } else {
        // Changed this, while adding verification for other platforms
        $verified = true;
    }

    $verified = $verified ? 1 : 0;
    $test_purchase = $test_purchase ? 1 : 0;
    // Updating verified and test Purchase fields in the payment_transaction table
    // The default value is 1, so no need for updating , if the payment is verified
    if ($verified == 0 || $test_purchase == 1) {
        dbQuery("UPDATE payment_transaction SET verified=$verified, test_purchase=$test_purchase WHERE user_id=$user_id AND txnid='$transaction_id'", $user_id);
    }
    $output['status'] = 'ok';
    $output['verified'] = $verified;
    $output['test_purchase'] = $test_purchase;
}

echo json_encode($output);

function callAPI($method, $url, $data = false, $headers = null)
{
    $curl = curl_init();

    switch ($method) {
        case "POST":
            curl_setopt($curl, CURLOPT_POST, 1);
            if ($data)
                curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
            break;
        case "GET":
            if ($data)
                $url = sprintf("%s?%s", $url, http_build_query($data));
    }

    // OPTIONS:
    curl_setopt($curl, CURLOPT_URL, $url);
    if ($headers) {
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
    }
    curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);

    // EXECUTE:
    $result = curl_exec($curl);

    if (! $result) {
        die("Connection Failure");
    }
    curl_close($curl);

    return $result;
}

?>

Any Ideas what may be the reason for the bad response? I have tried to generate a new refresh token, but the result is the same. (

Ok. I found the problem. The PurchaseToken was incorrect in my case.

BTW, the Error Code 400 means that the authentication is ok , but some data is invalid. In my case, it was the PurchaseToken.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM