#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…
Понимая, что другие записи могут быть прочитаны и созданы с использованием минимальных настроек заголовка и / или элемента данных вызова запросов.
Меня очень расстраивало отсутствие этой информации, и если люди смогут найти это, это может помочь им в будущем.