【问题标题】:Is there a simple way to access the Quickbooks API using OAuth2?有没有一种使用 OAuth2 访问 Quickbooks API 的简单方法?
【发布时间】:2019-01-26 17:54:38
【问题描述】:

我只需要使用带有 C# 的 API 访问我的 Quickbooks Online (QBO) 公司中的数据。但是现在他们已经迁移到 OAuth2,它看起来非常复杂。例如,它建议我需要一个重定向 URI 来接收授权代码,但我正在编写一个简单的控制台应用程序来将数据导出到 QBO,并且不想托管一个 URI 端点来执行此操作。

知道如何获取和管理以下内容也令人困惑:

  • 客户 ID
  • 客户端密码
  • 领域ID
  • 授权码
  • 刷新令牌
  • 访问令牌

必须有一个简单的方法来做到这一点。例如,使用 Stripe,您只需管理一个 API 密钥。

【问题讨论】:

  • 您可以找到示例here。我希望它能有所帮助。
  • 我认为该示例使用 OAuth 1.0。一切都必须在 2019 年 12 月之前迁移到 OAuth 2.0

标签: c# quickbooks-online


【解决方案1】:

经过一些研究,我发现这可以通过简单的方式完成。您只需要保留刷新令牌的副本(可能在读/写文件中)。当您想要访问 API 时,只需调用 OAuth2Client.RefreshTokenAsync() 即可获取访问令牌。可以使用 OAuth2 游乐场找到所有其他所需的项目。

然后,访问令牌最多可与 API 一起使用一小时。您还可以取回更新的刷新令牌。如果发生这种情况,只需将其存储起来以备将来使用。在您必须使用从 Refresh Token 操作返回的较新版本之前,刷新令牌最多可以持续 100 天。

下面是如何在 C# 中使用 API 的更长版本:

  1. 创建一个应用,但不要在 QBO 应用商店中发布它。要做到这一点登录到 developer.intuit.com 使用您的 QBO 帐户。转到“我的应用程序”然后创建一个应用程序(例如,名为“MyQBOApiApp”)。这只需要执行一次。将默认重定向 URL 设置为 OAuth2 游乐场,因为这是唯一需要的重定向 URL。

  2. 从应用的“OAuth 2.0 密钥”选项卡的“生产密钥”部分获取生产客户端 ID 和 客户端密钥。 (记录这些以在您的 C# 程序中使用,因为它们不会改变)

  3. 转到 https://developer.intuit.com/v2/ui#/playground 的 OAuth 2.0 游乐场

  4. 在第 1 步“获取授权码”中,从下拉框列表中选择 MyQBOApiApp(Production)

  5. 如果您只需要向 QBO 公司读取/写入数据,请在“选择范围”列表中选择“会计”

  6. 点击“获取授权码”

  7. 将您的 QBO 公司连接到 MyQBOApiApp 应用程序

  8. 在 Playground 页面上的第 2 步“从身份验证代码中获取 OAuth 2.0 令牌”中,单击 “获取令牌”。这将为您的 API 访问您的公司获得一个刷新令牌。

  9. 跳到 Playground 页面上的第 4 步“刷新访问令牌”。访问令牌只能使用 59 分钟 所以只需保留 'Refresh Token',因为它可以使用 100 天来获取新的访问令牌和刷新令牌。将其存储在 C# 程序可以读取和写入的位置(例如文件或数据库)

  10. realmID 可从第 3 步“进行 API 调用”中获得。 (记录下来以便在您的 C# 程序中使用,因为它不会改变)

  11. 将 IppDotNetSdkForQuickBooksApiV3 NuGet 包添加到您的 C# 程序。提供对 API 的轻松访问。

  12. 确保您使用的是 .Net Framework 4.6.1 或更高版本,因为 QBO 需要 TLS 1.2 连接

  13. 很遗憾,.Net 控制台应用默认不使用 TLS 1.2。因此,在你的 C# 程序启动的某处添加这行代码:

    // Have to explicitly set TLS 1.2 as QBO APIs require it
    System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
    

在访问 API 之前,您需要这样的代码来获取访问令牌:

public static string GetAccessToken()
{
    var oauth2Client = new OAuth2Client(CLIENTID_FROM_STEP_2, 
            CLIENT_SECRET_FROM_STEP_2,
            // Redirect not used but matches entry for app
            "https://developer.intuit.com/v2/OAuth2Playground/RedirectUrl",
            "production"); // environment is “sandbox” or “production”

    var previousRefreshToken = ReadRefreshTokenFromWhereItIsStored();
    var tokenResp = oauth2Client.RefreshTokenAsync(previousRefreshToken );
    tokenResp.Wait();
    var data = tokenResp.Result;

    if ( !String.IsNullOrEmpty(data.Error) || String.IsNullOrEmpty(data.RefreshToken) || 
          String.IsNullOrEmpty(data.AccessToken))
    {
        throw new Exception("Refresh token failed - " + data.Error);
    }

    // If we've got a new refresh_token store it in the file
    if (previousRefreshToken != data.RefreshToken)
    {
        Console.WriteLine("Writing new refresh token : " + data.RefreshToken);
        WriteNewRefreshTokenToWhereItIsStored(data.RefreshToken)

    }
    return data.AccessToken;
}

您需要编写函数 ReadRefreshTokenFromWhereItIsStored() 和 WriteNewRefreshTokenToWhereItIsStored() 以从持久存储中加载和保存刷新令牌。

QBO 中的所有 API 访问都以服务上下文开始。您可以使用如下代码创建一个:

static public ServiceContext GetServiceContext()
{
    var accessToken = GetAccessToken(); // Code from above
    var oauthValidator = new OAuth2RequestValidator(accessToken);

    ServiceContext qboContext = new ServiceContext(REALMID_PROD_FROM_STEP10,
            IntuitServicesType.QBO, oauthValidator);

    return qboContext;
}

要访问数据,您可以像这样创建数据服务:

var service = new DataService(GetServiceContext());

【讨论】:

  • 谢谢,看来其他人对此讨论不感兴趣。我能问一下你为什么将 RefreshToken 存储在一个文件中而不是仅仅将它存储在一个字符串中吗?另外,这段代码编译了吗?对RefreshTokenAsyc() 的调用不应该使用await吗?
  • 是的,代码编译和工作如图所示。我将刷新令牌存储在一个文件中,因为如果您丢失它(例如您的程序结束),那么您必须在操场页面上再次手动获取一个。我的客户端 exe 仅在收到新订单或按小时运行时运行。因此,它需要将刷新令牌存储在某处。这段代码现在已经运行了 6 个月以上,没有任何问题
  • 我明白了。对我来说,RefreshTokenAsync() 失败了。但也许你只是解释了原因。我只是第一次通过null。我想我必须找到一个初始访问令牌,即使我知道它在我使用它时已经过期。我想它需要能够刷新它。
  • 我一直将刷新令牌存储在 Azure KeyVault 中。如果在本地存储它不起作用,这是人们的一个选择。
  • 你有没有相同进程的代码使用JAVA
【解决方案2】:
[HttpPost("qb/refresh-token")]
        public async Task<ActionResult> GetRefressToken()
        {
            JObject result = new JObject();
            //Request Oauth2 tokens
            var tokenClient = new OAuth2Client(
                "your client id",
                "secret",
                "https://developer.intuit.com/v2/OAuth2Playground/RedirectUrl",
                "sandbox"); // environment is “sandbox” or “production”

                    
            var previousRefreshToken = "your refresh token id ";Get it from Playgroud for first time**strong text**//ReadRefreshTokenFromWhereItIsStored();
            var tokenResp = await tokenClient.RefreshTokenAsync(previousRefreshToken);
           

            if (!String.IsNullOrEmpty(tokenResp.Error) || String.IsNullOrEmpty(tokenResp.RefreshToken) ||
                  String.IsNullOrEmpty(tokenResp.AccessToken))
            {
                throw new Exception("Refresh token failed - " + tokenResp.Error);
            }

            // If we've got a new refresh_token store it in the file
            if (previousRefreshToken != tokenResp.RefreshToken)
            {
                Console.WriteLine("Writing new refresh token : " + tokenResp.RefreshToken);
                //WriteNewRefreshTokenToWhereItIsStored(data.RefreshToken);
             
            }
            return Ok(tokenResp.AccessToken);
            //return new Tuple<string, string>(tokenResp.RefreshToken, tokenResp.AccessToken);
        }   

【讨论】:

  • 请不要只发布代码作为答案,还要解释您的代码的作用以及它如何解决问题的问题。带有解释的答案通常更有帮助、质量更好,并且更有可能吸引投票
  • 这看起来就像 stackoverflow.com/a/54381096/323058 中的 GetAccessToken() 函数已被重新打包为 Web API 端点。我真的不明白这对这个问题有什么帮助
猜你喜欢
  • 1970-01-01
  • 2019-03-10
  • 1970-01-01
  • 2022-10-13
  • 2011-06-29
  • 1970-01-01
  • 2010-09-19
  • 2011-04-16
  • 1970-01-01
相关资源
最近更新 更多