Xero: OAuth2: Python3: пример кода для получения токена обновления

#python #api #oauth-2.0 #xero

#python #API #oauth-2.0 #xero

Вопрос:

Xero изменил свой API, чтобы требовать подключения OAuth2 вместо OAuth1.

У меня было рабочее решение в OAuth1, но примеров для Oauth2 в лучшем случае мало, но в основном для веб-сайтов.

Я еще один разработчик, которому удалось создать решение Oauth1, которое успешно работало как межмашинное решение без участия веб-сервера.

У Xero есть несколько примеров, которые выполняются в Postman, которые легко работают в моей тестовой среде.

Я пытаюсь воспроизвести действие postman по обновлению токена в Python3.

Мой базовый код ниже — это то, где я сейчас нахожусь:

 #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)
 

До сих пор мне не удалось найти пример, который работает без веб-сервера, просто используя python для связи с серверами Xero для передачи локальных данных в нулевой API.

Используя xoauth, .exe (windows), чтобы получить access_token, а затем в postman я могу запустить примеры для токена обновления, соединений, счетов и т. Д. Для демонстрационной компании.

и я считаю, что просто возможность воспроизвести эти примеры даст мне то, что мне нужно, чтобы получить рабочее решение.

в настоящее время с этим кодом python я получаю только {«ошибка»:»invalid_request»}

Итак, очевидно, я что-то упускаю.

Я буду считать себя новичком в Python или Oauth2, но выбрал этот путь из-за моего предыдущего успеха с подключенным решением Oauth1.

Я бы попросил сообщество разработчиков Xero, но я пишу это для пользователя нашего программного обеспечения, чтобы он загружал данные в свои учетные записи Xero, и поэтому для тестирования у меня есть только пробная учетная запись, которая не дает мне доступа к сообществу разработчиков Xero.

Что само по себе действительно раздражает.

Поддержка Xero тоже кажется малопригодной, я пробовал.

Если есть кто-то, кто может помочь, это было бы потрясающе.

Заранее благодарю вас за любую оказанную помощь.

Ответ №1:

После использования приложения xoauth и настройки соединения я обнаружил, что с помощью токена обновления выполнение этой функции поддерживает работоспособность соединения.

 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)
 

Ответ №2:

В качестве дополнительной информации я также могу использовать это соединение, например, для создания контакта в Xero:

В этом примере irContact представляет собой строку данных SQLAlchemy из таблицы SQL.

определение 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)
 

Я полагаю, что с таким объемом информации любой может создать свой собственный интерфейс Xero machine to machine…
Понимая, что другие записи могут быть прочитаны и созданы с использованием минимальных настроек заголовка и / или элемента данных вызова запросов.

Меня очень расстраивало отсутствие этой информации, и если люди смогут найти это, это может помочь им в будущем.