如何在服务器端验证android应用程序的购买(google play in app billing v3)
我有一个简单的应用程序(需要用户登录帐户)。我为付费用户提供了一些高级功能,比如更多的新闻内容 我需要记录用户是否在我的服务器数据库中购买了此项目。当我向用户的设备提供数据内容时,我可以检查用户的状态,并为付费用户提供不同的内容 我查看了Google提供的官方驱动示例,它没有提供任何用于服务器端验证的示例代码,下面是我的问题如何在服务器端验证android应用程序的购买(google play in app billing v3),android,in-app-purchase,in-app-billing,Android,In App Purchase,In App Billing,我有一个简单的应用程序(需要用户登录帐户)。我为付费用户提供了一些高级功能,比如更多的新闻内容 我需要记录用户是否在我的服务器数据库中购买了此项目。当我向用户的设备提供数据内容时,我可以检查用户的状态,并为付费用户提供不同的内容 我查看了Google提供的官方驱动示例,它没有提供任何用于服务器端验证的示例代码,下面是我的问题 我发现这个示例使用我的应用程序的公钥来验证购买,看起来不太好,我想我可以将验证过程移动到我的服务器,并结合用户登录凭据来查看用户购买是否完成,然后更新数据库 此外,我还可以
如果成功,此方法将在响应正文中返回a。听起来您正在寻找的是一种检查用户是否在其帐户上启用了高级功能的方法,因此我将从这里开始 确保数据库上有某种标志,指示用户是否具有高级功能,并在请求帐户信息时将其包含在API响应负载中。此标志将是您“高级功能”的主要权限 当用户进行应用内购买时,在客户端(即应用)本地缓存详细信息(令牌、订单id和产品id),然后将其发送到API 然后,您的API应将
purchaseToken
发送到进行验证
从这里可能会发生一些事情:
premium
标志设置为true
在3的情况下。(5xx状态代码)或网络超时。客户端应继续尝试,直到从API收到2xx或4xx状态代码
根据您的要求,您可以让它在再次发送之前等待几秒钟,或者在再次启动应用程序时将详细信息发送到您的API,或者如果应用程序缓存中存在购买详细信息,则将其发送到后台
这种方法应该考虑网络超时、服务器不可用等问题
现在,您需要考虑以下几个问题:
购买后应该立即发生什么?应用程序应该等到验证成功后再提供高级内容,还是应该暂时授予访问权限并在验证失败时将其带走
授予对高级功能的临时访问权可以使大多数用户的访问过程更加顺畅,但在API验证purchaseToken
时,您也将授予一些欺诈用户的访问权
换言之:在证明欺诈或欺诈之前,购买是有效的;欺诈,直到证明有效
为了在用户的订阅期即将续订时确定用户是否仍然拥有有效的订阅,您需要计划对purchaseToken
进行重新验证,以在中返回的expiryTimeMillis
上运行
如果expiryMemillis
是过去的,您可以将premium
标志设置为false。如果是在将来,请为新的expiryTimeMillis
重新安排
最后,为确保用户具有高级访问权限(或不具有高级访问权限),您的应用程序应在应用程序启动时或在后台查询用户的API详细信息。我回答了这个问题 网络连接已断开或我自己的服务器已断开,用户仅需 在google play中支付了这笔钱,但我没有在我的帐户中记录购买情况 服务器?我该怎么办,我该如何处理这种情况 情况是: 用户使用google play服务购买“abc”项目->返回确定->由于某些原因无法与服务器验证,例如没有Internet连接 解决办法是: 在客户端,在显示“谷歌钱包”按钮之前,检查“abc”项目是否已经拥有
- 如果是,请再次与服务器验证
- 如果没有,则显示“谷歌钱包”按钮李>
$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)