Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/223.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在服务器端验证android应用程序的购买(google play in app billing v3)_Android_In App Purchase_In App Billing - Fatal编程技术网

如何在服务器端验证android应用程序的购买(google play in app billing v3)

如何在服务器端验证android应用程序的购买(google play in app billing v3),android,in-app-purchase,in-app-billing,Android,In App Purchase,In App Billing,我有一个简单的应用程序(需要用户登录帐户)。我为付费用户提供了一些高级功能,比如更多的新闻内容 我需要记录用户是否在我的服务器数据库中购买了此项目。当我向用户的设备提供数据内容时,我可以检查用户的状态,并为付费用户提供不同的内容 我查看了Google提供的官方驱动示例,它没有提供任何用于服务器端验证的示例代码,下面是我的问题 我发现这个示例使用我的应用程序的公钥来验证购买,看起来不太好,我想我可以将验证过程移动到我的服务器,并结合用户登录凭据来查看用户购买是否完成,然后更新数据库 此外,我还可以

我有一个简单的应用程序(需要用户登录帐户)。我为付费用户提供了一些高级功能,比如更多的新闻内容

我需要记录用户是否在我的服务器数据库中购买了此项目。当我向用户的设备提供数据内容时,我可以检查用户的状态,并为付费用户提供不同的内容

我查看了Google提供的官方驱动示例,它没有提供任何用于服务器端验证的示例代码,下面是我的问题

  • 我发现这个示例使用我的应用程序的公钥来验证购买,看起来不太好,我想我可以将验证过程移动到我的服务器,并结合用户登录凭据来查看用户购买是否完成,然后更新数据库
  • 此外,我还可以使用它进行查询,我需要的是将用户的purchaseToken传递到服务器
  • 我不确定我应该采取什么方法来验证用户的购买,并在我的数据库中标记用户的状态,也许两者都可以

    我担心有一种情况,如果一个用户从google play购买了这个物品,但由于某种原因,就在那时,当我的应用程序向我的服务器启动验证时,网络连接断开,或者我自己的服务器断开,,用户只是在google play中付款,而我没有在我的服务器上记录购买的物品?我应该怎么做,如何处理这种情况。

    您可以尝试使用服务器端。它将packageName、subscriptionId和token作为参数,并需要

    检查用户的订阅购买是否有效并返回其 有效期


    如果成功,此方法将在响应正文中返回a。

    听起来您正在寻找的是一种检查用户是否在其帐户上启用了高级功能的方法,因此我将从这里开始

    确保数据库上有某种标志,指示用户是否具有高级功能,并在请求帐户信息时将其包含在API响应负载中。此标志将是您“高级功能”的主要权限

    当用户进行应用内购买时,在客户端(即应用)本地缓存详细信息(令牌、订单id和产品id),然后将其发送到API

    然后,您的API应将
    purchaseToken
    发送到进行验证

    从这里可能会发生一些事情:

  • 收据是有效的,您的API以200 Ok状态代码响应客户端
  • 收据无效,API以400错误请求状态代码响应客户端
  • Google Play API关闭,您的API响应为502错误网关状态代码
  • 在1的情况下。或2。(2xx或4xx状态代码)您的客户端清除购买详细信息的缓存,因为它不再需要它,因为API已指示它已收到

    成功验证后(案例1),您应该为用户将
    premium
    标志设置为true

    在3的情况下。(5xx状态代码)或网络超时。客户端应继续尝试,直到从API收到2xx或4xx状态代码

    根据您的要求,您可以让它在再次发送之前等待几秒钟,或者在再次启动应用程序时将详细信息发送到您的API,或者如果应用程序缓存中存在购买详细信息,则将其发送到后台

    这种方法应该考虑网络超时、服务器不可用等问题

    现在,您需要考虑以下几个问题:

    购买后应该立即发生什么?应用程序应该等到验证成功后再提供高级内容,还是应该暂时授予访问权限并在验证失败时将其带走

    授予对高级功能的临时访问权可以使大多数用户的访问过程更加顺畅,但在API验证
    purchaseToken
    时,您也将授予一些欺诈用户的访问权

    换言之:在证明欺诈或欺诈之前,购买是有效的;欺诈,直到证明有效

    为了在用户的订阅期即将续订时确定用户是否仍然拥有有效的订阅,您需要计划对
    purchaseToken
    进行重新验证,以在中返回的
    expiryTimeMillis
    上运行

    如果
    expiryMemillis
    是过去的,您可以将
    premium
    标志设置为false。如果是在将来,请为新的
    expiryTimeMillis
    重新安排


    最后,为确保用户具有高级访问权限(或不具有高级访问权限),您的应用程序应在应用程序启动时或在后台查询用户的API详细信息。

    我回答了这个问题

    网络连接已断开或我自己的服务器已断开,用户仅需 在google play中支付了这笔钱,但我没有在我的帐户中记录购买情况 服务器?我该怎么办,我该如何处理这种情况

    情况是:

    用户使用google play服务购买“abc”项目->返回确定->由于某些原因无法与服务器验证,例如没有Internet连接

    解决办法是:

    在客户端,在显示“谷歌钱包”按钮之前,检查“abc”项目是否已经拥有

    • 如果是,请再次与服务器验证
    • 如果没有,则显示“谷歌钱包”按钮
    采购=mInventory.getPurchase('abc')

    完整的使用示例:

  • 按照Marc的回答,为您的服务帐户设置谷歌项目,并访问谷歌播放

  • 安装库:

  • 现在,您可以通过以下方式验证您的收据:

    $client = new \Google_Client();
    $client->setAuthConfig('/path/to/service/account/credentials.json');
    $client->addScope('https://www.googleapis.com/auth/androidpublisher');
    $service = new \Google_Service_AndroidPublisher($client);
    $purchase = $service->purchases_subscriptions->get($packageName, $productId, $token);
    
    在那之后,$purchase是Google\u Service\u AndroidPublisher\u SubscriptionPurchase的实例

    $purchase->getAutoRenewing();
    $purchase->getCancelReason();
    ...
    

  • 关于这一点的文档令人困惑和奇怪
    $purchase->getAutoRenewing();
    $purchase->getCancelReason();
    ...
    
    $ pipenv install google-api-python-client
    
    credentials = service_account.Credentials.from_service_account_file("service_account.json")
    
    #Build the "service" interface to the API you want
    service = googleapiclient.discovery.build("androidpublisher", "v3", credentials=credentials)
    
    #Use the token your API got from the app to verify the purchase
    result = service.purchases().subscriptions().get(packageName="your.app.package.id", subscriptionId="sku.name", token="token-from-app").execute()
    #result is a python object that looks like this ->
    # {'kind': 'androidpublisher#subscriptionPurchase', 'startTimeMillis': '1534326259450', 'expiryTimeMillis': '1534328356187', 'autoRenewing': False, 'priceCurrencyCode': 'INR', 'priceAmountMicros': '70000000', 'countryCode': 'IN', 'developerPayload': '', 'cancelReason': 1, 'orderId': 'GPA.1234-4567-1234-1234..5', 'purchaseType': 0}
    
        import requests
        import datetime
        import json
        import base64
        from Crypto.Signature import PKCS1_v1_5 as Signature_pkcs1_v1_5
        from Crypto.Hash import SHA256 
        from Crypto.PublicKey import RSA
    
        jwtheader64 = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9"
        #SERVICE_ACCOUNT_FILE: full path to the json key file obtained from google
        with open(SERVICE_ACCOUNT_FILE) as json_file:
            authinfo = json.load(json_file)
    
        packageName = #your package name
        product = #your inapp id
        token = #your purchase token
        
        #create the JWT to use for authentication
        now = datetime.datetime.now()
        now1970 = (now - datetime.datetime(1970,1,1)).total_seconds()
        jwtclaim = {"iss":authinfo["client_email"],"scope":"https://www.googleapis.com/auth/androidpublisher","aud": "https://oauth2.googleapis.com/token","iat":now1970,"exp":now1970+1800,"sub":authinfo["client_email"]}
        jwtclaimstring = json.dumps(jwtclaim).encode(encoding='UTF-8')
        jwtclaim64 = base64.urlsafe_b64encode(jwtclaimstring).decode(encoding='UTF-8')
        tosign = (jwtheader64+"."+jwtclaim64).encode(encoding='UTF-8')
    
        #sign it with your private key
        private = authinfo["private_key"].encode(encoding='UTF-8')
        signingkey = RSA.importKey(private)
        signer = Signature_pkcs1_v1_5.new(signingkey)
        digest = SHA256.new()
        digest.update(tosign)
        signature = signer.sign(digest)
        res = base64.urlsafe_b64encode(signature).decode(encoding='UTF-8')
    
        #send it to Google authentication server to obtain your access token
        headers = {'Content-Type': 'mapplication/x-www-form-urlencoded'}
        payload = "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion="+jwtheader64+"."+jwtclaim64+"."+res
        r = requests.post("https://oauth2.googleapis.com/token",headers=headers,data=payload)
        if r.status_code == 200:
            authdata = json.loads(r.text)
            accesstoken = authdata['access_token']
            bearerheader = {'Authorization':'Bearer '+authdata['access_token']}
            #Now you have at last your authentication token, so you can use it to make calls. In this example we want to verify a subscription
            url = "https://androidpublisher.googleapis.com/androidpublisher/v3/applications/"+packageName+"/purchases/subscriptions/"+product+"/tokens/"+token
            subscription = requests.get(url,headers=bearerheader)