我是否正确地验证了用户的Android应用内订阅?

时间:2012-09-14 15:22:00

标签: android service google-api google-play subscription

我正在创建一个不需要用户帐户/登录的应用程序,并允许用户购买订阅。我想使用Google Play Developer API来验证用户是否有购买/有效订阅。从所有文档中,我收集了以下步骤。

他们是否正确,你能否回答其中的两个问题?

  1. 在Google API控制台中创建Service Account
  2. 保存提供给我的私钥(其中?肯定不在我的代码/设备上this sample code建议)
  3. 使用Google APIs Client Library for Java使用私钥创建和签署JWT(如何?the docs give me this,但这不是Java代码...我该怎么办?)
  4. 构建访问令牌请求,并获取对API的访问权
  5. 应用程序现在可以向API发送GET请求,以确定是否 用户有订阅
  6. 当访问令牌过期时,请返回步骤3.
  7. 另外,我有一个Web服务,虽然我对Web服务或Web服务编程一无所知......我只知道它可能需要在这里使用。

    编辑:这些步骤不正确。请参阅下面的答案,了解正确的步骤。但请注意,这仅适用于使用服务帐户(因为我不想要求用户必须明确允许API访问)

7 个答案:

答案 0 :(得分:39)

事实证明,我的步骤不正确。我花了好几个星期来解决这个问题,似乎没有其他地方记录。不客气:

  1. Google APIs Console中创建 Web应用程序帐户。将任何网站作为“重定向URI”;没关系,因为你不会真正使用它。创建帐户时,您将获得客户端ID和客户端密码。

  2. 在计算机上的浏览器中,转到https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/androidpublisher&response_type=code&access_type=offline&redirect_uri=[YOUR REDIRECT URI]&client_id=[YOUR CLIENT ID]并在出现提示时允许访问。

  3. 查看地址栏。在您最初输入的URI的末尾将是您的刷新令牌。它看起来像1/....您将在下一步中使用此“代码”。刷新令牌永不过期。

  4. 转到https://accounts.google.com/o/oauth2/token?client_id=[YOUR CLIENT ID]&client_secret=[YOUR CLIENT SECRET]&code=[CODE FROM PREVIOUS STEP]&grant_type=authorization_code&redirect_uri=[YOUR REDIRECT URI],将此“代码”转换为“刷新令牌”。您可以将结果值保存在程序中;除非明确撤销,否则它永不过期。 ( @BrianWhite插入此步骤 - 请参阅注释) 确保你使用的是POST。(由Gintas插入)

  5. 在您的代码中,使用BasicNameValuePairs https://accounts.google.com/o/oauth2/token"grant_type","refresh_token""client_id",[YOUR CLIENT ID]"client_secret",[YOUR CLIENT SECRET]"refresh_token",[YOUR REFRESH TOKEN]发送HttpPost请求。例如,here。您需要在单独的线程中执行此操作,可能使用AsyncTask。这将返回一个JSONObject。

  6. 从返回的JSONObject中获取访问令牌。例如,here。您需要获取字符串“access_token”。访问令牌在1小时后到期。

  7. 在您的代码中,向https://www.googleapis.com/androidpublisher/v1/applications/[YOUR APP'S PACKAGE NAME]/subscriptions/[THE ID OF YOUR PUBLISHED SUBSCRIPTION FROM YOUR ANDROID DEVELOPER CONSOLE]/purchases/[THE PURCHASE TOKEN THE USER RECEIVES UPON PURCHASING THE SUBSCRIPTION]?accesstoken="[THE ACCESS TOKEN FROM STEP 4]"发送HttpGet请求。例如,查看here

答案 1 :(得分:12)

.NET用户:我希望这个答案可以让别人感到悲痛。

@Christophe Fondacci在2015年指出,几年前公认的解决方案很有效。现在是2017年,这个过程更容易,更快

我的用例是验证应用内订阅,我的移动应用会将订阅购买信息发送到我的RESTful服务器,后者又与Google联系以验证订阅购买。

策略是创建一个代表您运营的服务帐户。

  1. 登录您的Google Play Dev Console,然后点击您正在设置的应用。
  2. 访问设置 - > API访问
  3. 服务帐户下,点击创建服务帐户按钮。
  4. 自2017年1月起,将显示一个对话框,其中包含有关设置服务帐户的说明。该对话框将您带到Google API控制台;从那里,

    A)点击创建服务帐户

    B)创建有意义的服务帐户名称。由于我们对访问Android Publisher服务感兴趣,因此我选择了“发布者”。

    C)对于角色,只需选择一些东西 - 您可以稍后更改。

    D)为.Net实施选择“提供新私钥”和选择P12 。不要丢失这个文件!

  5. 5)现在你已经完成了#4,你会看到你的新服务帐户被列出;单击“授予访问权限”以启用它。

    6)点击“查看权限”链接。您应该根据您的需求和API修改权限。

    要验证应用内购买,请访问Cog->更改权限并启用GLOBAL“Visiblity”和“Manage Orders”权限。

    好的,此时您已经在Google端配置了所有内容。现在设置服务器到服务器的东西。我建议创建 一个.Net控制台应用程序来测试你的实现,然后在需要的地方卸载它。

    1)从Nuget

    添加Android Publisher客户端库
    PM> Install-Package Google.Apis.AndroidPublisher.v2
    

    2)将P12文件添加到项目根目录

    3)更改P12属性,使“Build Action”为“Content”,“Copy To Output Directory”为“Copy if newer”。

    4)实现类似这样的东西来测试你的访问和微调。

    using System.Threading.Tasks;
    using System.Security.Cryptography.X509Certificates;
    using Google.Apis.Services;
    using Google.Apis.Auth.OAuth2;
    using Google.Apis.AndroidPublisher.v2;
    ...
    public Task<SubscriptionPurchase> GetSubscriptionPurchase(string packageName, string productId, string purchaseToken)
    {
        var certificate = new X509Certificate2(
                        "{{your p12 file name}}",
                        "{{ your p12 secret }}",
                        X509KeyStorageFlags.Exportable
                    );
        var credentials = new ServiceAccountCredential(
                new ServiceAccountCredential.Initializer("{{ your service account email }}")
                {
                    Scopes = new[] { AndroidPublisherService.Scope.Androidpublisher }
                }.FromCertificate(certificate))
                ;
    
        var service = new AndroidPublisherService(new BaseClientService.Initializer()
        {
            HttpClientInitializer = credentials,
            ApplicationName = "my server app name",
        });
        return service.Purchases.Subscriptions.Get(packageName, productId, purchaseToken).ExecuteAsync();
    }
    
    祝你好运,希望这有助于某人。

    来源:

    Using OAuth 2.0 for Server to Server Applications

    .Net Client Library for Google.Apis.AndroidPublisher.v2

答案 2 :(得分:8)

如果你像我一样,并希望在 PHP 中执行此操作,请按照以下步骤操作...感谢Kalina的回答我花了三天时间才弄清楚它是怎么回事工作:)。

这里是:

  1. 转到Google开发者控制台https://console.developers.google.com/并创建网络应用。将'developers.google.com/oauthplayground'设为“重定向URI”;您将在步骤2中使用它。创建帐户时,您将获得客户端ID和客户端密码。确保添加了 Google Play Android Developer API

  2. 转到Google oauth2游乐场https://developers.google.com/oauthplayground/。这个伟大的工具是你未来几天最好的朋友。 现在转到设置:确保已设置使用您自己的OAuth凭据。只有这样,您才能在下面的表单中填写客户端ID 客户端密码

  3. Google oauth2 playground 中,转到第1步选择&amp;授权API 在输入字段 https://www.googleapis.com/auth/androidpublisher 中填写范围。我无法在列表中找到Google Play Android Developer API,也许他们会在稍后添加一些时间。点击 AUTORIZE APIS 。做以下的授权事项。

  4. Google oauth2 playground 中,转到步骤2 令牌的Exchange授权码。如果一切顺利,您将看到以/ 4开头的授权码。如果某些事情没有顺利,请检查右侧的错误消息。现在你点击'刷新访问令牌'。复制刷新令牌......它将以/ 1 ...

  5. 开头
  6. 现在您可以随时获取访问令牌!这是如何:

    $url ="https://accounts.google.com/o/oauth2/token";
    $fields = array(
       "client_id"=>"{your client id}",
       "client_secret"=>"{your client secret}",
       "refresh_token"=>"{your refresh token 1/.....}",
       "grant_type"=>"refresh_token"
    );
    
    $ch = curl_init($url);
    
    //set the url, number of POST vars, POST data
    curl_setopt($ch, CURLOPT_POST,count($fields));
    curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    
    //execute post
    $lResponse_json = curl_exec($ch);
    
    //close connection
    curl_close($ch);
    
  7. 现在你有一个ACCESS TOKEN万岁... JSON将如下所示:

    "access_token" : "{the access token}",  "token_type" : "Bearer",  "expires_in" : 3600
    

    最后你准备问google了!以下是如何做到这一点:

    $lAccessToken = "{The access token you got in}" ;
    $lPackageNameStr = "{your apps package name com.something.something}";
    $lURLStr =  "https://www.googleapis.com/androidpublisher/v1.1/applications/$lPackageNameStr/subscriptions/$pProductIdStr/purchases/$pReceiptStr";
    
    $curl = curl_init($lURLStr);
    
    curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 
    $curlheader[0] = "Authorization: Bearer " . $lAccessToken;
    curl_setopt($curl, CURLOPT_HTTPHEADER, $curlheader);
    
    $json_response = curl_exec($curl);
    curl_close($curl);
    
    $responseObj = json_decode($json_response,true);
    

    返回的JSON将包含两个时间戳, initiateTimestampMsec validUntilTimestampMsec 订阅有效的时间。两者都是将毫秒加到1970年1月1日的毫秒数。

答案 3 :(得分:6)

我不知道2012年,但在2015年你不应该手动执行这些步骤。我很难找到文档,所以我在这里发帖以防万一。

  1. 出于安全原因,您应该只从服务器查询应用内购买,否则您无法信任购买过程的两个端点。
  2. 现在在服务器端(我认为如果你绝对需要,你仍然可以使用你的应用程序中的相同代码),将google-api-services-androidpublisher客户端库包含在你的项目中(参见https://developers.google.com/api-client-library/java/apis/androidpublisher/v1

    正如您所提到的,您需要一个带有P12文件的服务帐户(客户端库只接受P12文件)。

    然后,以下代码将验证并很好地获取购买信息:

        HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
        JsonFactory jsonFactory = new JacksonFactory();
    
        List<String> scopes = new ArrayList<String>();
        scopes.add(AndroidPublisherScopes.ANDROIDPUBLISHER);
    
        Credential credential = new GoogleCredential.Builder().setTransport(httpTransport).setJsonFactory(jsonFactory)
                .setServiceAccountId(googleServiceAccountId)
                .setServiceAccountPrivateKeyFromP12File(new File(googleServicePrivateKeyPath))
                .setServiceAccountScopes(scopes).build();
        AndroidPublisher publisher = new AndroidPublisher.Builder(httpTransport, jsonFactory, credential).build();
        AndroidPublisher.Purchases purchases = publisher.purchases();
        final Get request = purchases.get(packageName, productId, token);
        final SubscriptionPurchase purchase = request.execute();
    
        // Do whatever you want with the purchase bean
    

    有关Java客户端身份验证的信息,请访问: https://developers.google.com/identity/protocols/OAuth2ServiceAccount

答案 4 :(得分:2)

我可能会误解您的问题,但我认为您没有理由使用您引用的链接来获取Android应用的应用内结算功能。这个页面更有帮助:

http://developer.android.com/guide/google/play/billing/index.html

您可以试用它们包含的演示应用程序(Dungeons - http://developer.android.com/guide/google/play/billing/billing_integrate.html#billing-download)。这使用产品(一次性购买)而不是订阅,但您应该能够修改以测试您想要的产品。

我认为,对于您来说,关键是他们在示例中提供的restoreTransactions方法,以查看Google Play帐户是否有您的应用的任何订阅:

@Override
public void onRestoreTransactionsResponse(RestoreTransactions request, int responseCode) {
    if (responseCode == BillingVars.OK) {                        
        // Update the shared preferences so that we don't perform a RestoreTransactions again.
        // This is also where you could save any existing subscriptions/purchases the user may have.
        SharedPreferences prefs = getSharedPreferences(my_prefs_file, Context.MODE_PRIVATE);
        SharedPreferences.Editor edit = prefs.edit();
        edit.putBoolean(DB_INITIALIZED, true);
        edit.commit();
    } else {
        Log.e(TAG, "RestoreTransactions error: " + responseCode);
    }
}

答案 5 :(得分:2)

如果有人在接受的帖子最后一步(#7)中遇到问题,我发现?access_token=代替?accessToken=

工作

太糟糕的堆栈溢出不会让我直接向线程发表评论......

答案 6 :(得分:1)

由于您的应用可以拨打的网络服务,我建议您将私钥安全地存储在服务器上。您应该尽可能多地将应用程序内容移动到服务调用中,请参阅this link。我已经实现了应用内订阅,但是在此部分API出来之前。我必须自己进行注册和安全验证,但看起来这个API使用OAuth为您完成了大部分工作,尽管看起来您仍然负责存储订阅请求/验证。

在谈到使用现有库签署JWT时,它们似乎确实为您提供了到java库,Python库和PHP库的链接 - 它取决于您的Web服务或服务器组件的编写方式(我的是C#,所以我使用RSACryptoServiceProvider来验证签名购买。他们使用JSON对象进行实际的数据传输。