简体   繁体   English

Xero:OAuth2:Python3:获取刷新令牌的示例代码

[英]Xero : OAuth2: Python3: Example Code to get the refresh token

Xero has changed its API to require OAuth2 connections instead of OAuth1. Xero 已将其 API 更改为需要 OAuth2 连接而不是 OAuth1。

I had a working solution in OAuth1, but the examples for Oauth2 are scarce at best, but mainly for web sites.我在 OAuth1 中有一个可行的解决方案,但 Oauth2 的示例充其量很少,但主要用于 web 站点。

I'm another developer who managed to create an Oauth1 solution, that successfully worked as a machine to machine solution with no web server involved.我是另一位设法创建 Oauth1 解决方案的开发人员,该解决方案成功地用作机器对机器解决方案,不涉及 web 服务器。

Xero has some examples that run in Postman, that easily work in my test environment. Xero 有一些在 Postman 中运行的示例,可以在我的测试环境中轻松运行。

I'm attempting to reproduce the postman action of refreshing the token in Python3.我正在尝试重现在 Python3 中刷新令牌的 postman 操作。

My basic code below is where I currently am:我下面的基本代码是我目前所在的位置:

#client_id = "xxxxx"
#client_secret = "xxxxx"
callback_url = "https://api.xero.com/connections"
re_directURI = "https://developer.xero.com"
scopes = "offline_access accounting.contacts accounting.transactions"

refresh_url = "https://identity.xero.com/connect/token"

access_token = open('AccessToken.txt').read()
old_refresh_token = open('RefreshToken.txt','r').read()


# Refresh Token code...

import requests

#def refresh_xero_token(refresh_token):

headers = {
'grant_type': 'refresh_token',
'Content-Type': 'application/json',
}
data = {
'grant_type': 'refresh_token',
'refresh_token': old_refresh_token,
'client_id': client_id,
'client_secret': client_secret
}
print(data,headers)
response = requests.post(refresh_url, headers=headers, data=data)

#return response.json()
print(response.text)

I have so far failed to find an example that works without a web server, just using python to communicate with the Xero servers to transfer local data into the Zero API.到目前为止,我还没有找到一个没有 web 服务器的示例,仅使用 python 与 Xero 服务器通信以将本地数据传输到零 API。

Using xoauth,.exe (windows) to get the access_token, and then in postman I can run the examples for refresh token, connections, invoices etc to the demo company.使用 xoauth,.exe (windows) 获取 access_token,然后在 postman 中,我可以运行示例,以获取演示公司的刷新令牌、连接、发票等。

and I believe just being able to replicate these examples will provide me with what I need to get a working solution.而且我相信能够复制这些示例将为我提供获得有效解决方案所需的东西。

currently with this python code I only get {"error":"invalid_request"}目前使用这个 python 代码我只得到 {"error":"invalid_request"}

So, clearly I am missing something.所以,很明显我错过了一些东西。

I'll class myself as a newbie with Python or Oauth2, but have chosen this path due to my previous success with an Oauth1 connected solution.我将 class 作为新手使用 Python 或 Oauth2,但由于我之前使用 Oauth1 连接的解决方案取得了成功,我选择了这条路径。

I would ask the Xero developer community, but I'm writing this for a user of our software to push data into their Xero accounts, and so for testing I only have a trial account, which does not give me access to the Xero developer community.我会问 Xero 开发者社区,但我写这篇文章是为了让我们软件的用户将数据推送到他们的 Xero 帐户中,所以为了测试,我只有一个试用帐户,这不能让我访问 Xero 开发者社区.

Which by itself is really annoying.这本身就很烦人。

Xero support seems of little use also, I tried. Xero支持似乎也没什么用,我试过了。

If there is anyone out there able to assist, that would be fabulous.如果有人可以提供帮助,那将是非常棒的。

Thank you in advance for any help given.预先感谢您提供的任何帮助。

After using the xoauth application and setting up the connection, I have since found that with the refresh token, running this function keeps the connection up and running.使用 xoauth 应用程序并设置连接后,我发现使用刷新令牌,运行此 function 可保持连接正常运行。

def refresh_xero_token():
    refresh_url =       "https://identity.xero.com/connect/token"

    old_refresh_token = open('RefreshToken.txt','r').read()

    tokenb4 = f"{client_id}:{client_secret}"
    basic_token = base64.urlsafe_b64encode(tokenb4.encode()).decode()

    headers = {
      'Authorization': f"Basic {basic_token}",
      'Content-Type': 'application/x-www-form-urlencoded',
    }
    data = {
    'grant_type': 'refresh_token',
    'refresh_token': old_refresh_token
    }

    try:
        response = requests.post(refresh_url, headers=headers, data=data)

        results = response.json()
        open('RefreshToken.txt','w').write(results["refresh_token"])
        open('AccessToken.txt','w').write(results["access_token"])

    except Exception as err:
        print("ERROR ! Refreshing token error?")
        print(response.text)

As additional information, I can then also use this connection to, for example create a contact in Xero:作为附加信息,我还可以使用此连接,例如在 Xero 中创建联系人:

In this example the irContact is a SQLAlchemy row of data from a SQL table.在此示例中,irContact 是来自 SQL 表的 SQLAlchemy 数据行。

def create_contact( connection, irContact, access_token): def create_contact(连接,irContact,access_token):

#Setup new contact
address1 = {"AddressType": "POBOX"}

if irContact['addressline1'] is not None: address1.update({"AddressLine1": irContact['addressline1']})
if irContact['addressline2'] is not None: address1.update({"AddressLine2": irContact['addressline2']})
if irContact['addressline3'] is not None: address1.update({"AddressLine3": irContact['addressline3']})
if irContact['addressline4'] is not None: address1.update({"AddressLine4": irContact['addressline4']})
if irContact['city'] is not None: address1.update({"City": irContact['city']})
if irContact['region'] is not None: address1.update({"Region": irContact['region']})
if irContact['postalcode'] is not None: address1.update({"PostalCode": irContact['postalcode']})
if irContact['country'] is not None: address1.update({"Country": irContact['country']})
if irContact['attentionto'] is not None: address1.update({"AttentionTo": irContact['attentionto']})

#print (address1.values())

addresses = []
addresses.append(address1)

phones = []
if irContact['phonenumber'] is not None:
    phone1 = {"PhoneType": "DEFAULT"}
    #phone1.update({"PhoneType": "DEFAULT"})
    if irContact['phonenumber'] is not None: phone1.update({"PhoneNumber": irContact['phonenumber']})
    if irContact['phoneareacode'] is not None: phone1.update({"PhoneAreaCode": irContact['phoneareacode']})
    if irContact['phonecountrycode'] is not None: phone1.update({"PhoneCountryCode": irContact['phonecountrycode']})

    phones.append(phone1)
    #print (phone1.values())
    

if irContact['mobilenumber'] is not None:
    phone2 = {"PhoneType": "MOBILE"}
    if irContact['phonenumber'] is not None: phone2.update({"PhoneNumber": irContact['mobilenumber']})
    if irContact['phoneareacode'] is not None: phone2.update({"PhoneAreaCode": irContact['mobileareacode']})
    if irContact['phonecountrycode'] is not None: phone2.update({"PhoneCountryCode": irContact['mobilecountrycode']})

    phones.append(phone2)
    #print (phone2.values())
    

contact = { "Name": irContact['name'],
             "ContactNumber": irContact['contactnumber'],
             "AccountNumber": irContact['accountnumber'],
             #"ContactStatus": "ACTIVE",
             "FirstName": irContact['firstname'],
             "LastName": irContact['lastname'],
             #"EmailAddress": irContact['emailaddress'],
             "Addresses": addresses,
             #"Phones":phones
            }

contacts = [contact]
#print(contacts)

contacts_url = "https://api.xero.com/api.xro/2.0/Contacts"

headers = {
  'Authorization': f"Bearer {access_token}",
  'Accept': 'application/json',
  'Content-Type': 'application/json',
  'xero-tenant-id': tenant_id,
}
data = {
'Contacts': [contact],
}
#print(data)

try:
    response = requests.post(contacts_url, headers=headers, json=data)
except Exception as err:
    print("ERROR! Contact: %s" % (str(err) ))
    print(response.text)
    return 0

#print(response.text)

results = response.json()

if 'Contacts' in results:
    newcontacts = results['Contacts']
    for newcontact in newcontacts: break

    query = "update xero_contact set errortext='', ContactID='%s' where id=%d" % (newcontact["ContactID"], irContact['id'])
    connection.execute(query)

    query = "update xero_invoice_header set ContactID='%s' where OurContactID=%d and (InvoiceID='' or InvoiceID is null ) " % ( newcontact["ContactID"], irContact['id']  )
    connection.execute(query)

I believe, with this amount of information, anyone can be capable of creating their own Xero machine to machine interface... Realising that other records can be read and created using minimal tweaks to the header and or data element of the requests call.我相信,有了这么多信息,任何人都可以创建自己的 Xero 机器对机器接口......意识到可以通过对 header 和/或请求调用的数据元素进行最小调整来读取和创建其他记录。

I found the lack of this information so very frustrating, if people can find this, it may help them in future.我发现缺乏这些信息非常令人沮丧,如果人们能找到它,它可能会在未来帮助他们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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