Xero - beautiful accounting software

Xero Developer Help Center

Xero Developer Community

Community > Wrapper libraries >

Xero API Wrapper Classes for Apex (Salesforce)

Started by Ben Edwards -   in Wrapper libraries

Hi All,

I've started to put together some wrapper classes for Apex (Salesforce), to be able to use the Xero APIs from Salesforce.

It took me some time to get the OAuth 1.0 authentication working from Apex, as there aren't any standard libraries to do it. I hope it helps anyone else trying to do the same.

The wrapper classes currently have a method for authenticating, and also a simple getContacts() method. I plan to add further methods and classes as I go.

You can find the source code and instructions here:
https://github.com/benedwards44/Apex-for-Xero/

Cheers.
Ben,

I wanted to say thanks - I started on my sf/xero project this morning and with just a couple of hours research and an hour of tinkering with your code I have the basics working.

A couple of things that may help future users

1. I initially wasn't clear on how to deploy the source into my dev org. AndyInTheCloud.com provided a very easy method: https://githubsfdeploy.herokuapp.com/app/githubdeploy/benedwards44/Apex-for-Xero

2. If you already have the required certificate in salesforce Setup / Certificate and Key Management then it is not necessary to use OpenSSL to generate intermediate files:
Public key: view the certificate in salesforce and click "download certificate". This gives you a .CRT file that you can upload directly to Xero.
Private key: replace line 85 of XeroOAuthUtility with the following. You'd normally store the name of the certificate in a custom setting.

Blob signatureBlob = System.Crypto.signWithCertificate('RSA-SHA1', Blob.valueOf(baseString), 'nameofcertificategoeshere');

All the best and thanks again
Tim
 

Tim Monks  

Hi Tim,

Thanks for the feedback! I've used Andy's Deploy to Salesforce app on other applications since this one but hadn't gone back and added it to this project. I've added that now.

And same for Salesforce Certificate Management. I wasn't familiar with how Salesforce managed certificates at the time of this project. I've gone back and updated my code with your suggestion, and updated the instructions and removed the Private_Key__c field from the Custom Setting, so it's all managed properly in Salesforce Certificates.

Cheers.
Ben
 

Ben Edwards  

Hello Everyone,

Thank you so much for this given code. I used this at the moment to connect to my salesforce instance. The problem I have at the moment is that when I try to run some update using POST method, It will return an error saying, "Error oauth_problem = signature_invalid & oauth_problem_advice = Failed to validate signature". Do you encounter this problem ? I try to search the problem and it seems there are lots of people who also encounter this problem. Can you please help me out ?

Sincerely,
John
 

John Hope  

Hi John,

I've recently updated this app to use Salesforce Certificates, so installed it into a new Org to make sure all as working as it was prior.

I was able to connect to my Xero account successfully using the XeroAccountingApi.getContacts() call.

The error message you're getting is mostly likely related to your authentication credentials. Can you make sure:
- You've created your Xero application (instructions here: https://developer.xero.com/documentation/getting-started/getting-started-guide/), and taken the Consumer Key, Consumer Secret and Endpoint and entered into your Xero Settings object in Salesforce
- You've created your certificate in Salesforce and loaded the .cer file into your Xero application

And then let me know if you're still receiving the error.

Feel free to email me on ben@edwards.nz if you need more support.
 

Ben Edwards  

Hello Ben,

Thanks for your reply. Actually the code you've given is working well. Sorry that I forgot to mention, that my problem occurs when I need to update some of my xero record using POST method. I didn't have problem using PUT and GET method but the error will show when I try to use POST. Because of that, I guess that I follow the instruction you've given.

Sincerely,
John
 

John Hope  

Hey all, it looks like POST requests weren't working correctly with this library, as the signature generation was using the content of the POST body, which apparently it shouldn't be.

OAuth1.0a is really fiddly!

Anywho, I've updated the library on GitHub and appears to be working now for all GET, PUT and POST requests:
https://github.com/benedwards44/Apex-for-Xero

Ben
 

Ben Edwards  

Good day everyone. I'm currently using your src for testing and now I can get all the xerocontacts, My problem is that the ContactPersons for every line seems to be empty even though some of the Xero Contacts have multiple contact persons. How do I obtain these data?

DEBUG|mScope : ContactPersons=(),
 

michael domz  

Hey Michael, it looks like when getting all contacts for a Xero org, the ContactPersons array is always empty.

However, when returning a specific contact, the ContactPersons array is populated. So it looks like this is only set when querying a specific Contact.

I've added a method called getContact('XERO-CONTACT-ID') to the repository, so using this will allow you to get the ContactPersons for a Contact.
 

Ben Edwards  

@Ben Edwards..thanks for the update. I tried it and it works for a specific contact. My problem is I made a batch to retrieve all Xero Contacts which is supposed to be synced in Salesforce Account and if I use this current method with it, it will cause Too many callouts: 1. If I add Database.Allowcallouts I can get results but I know it will produce another error if it reaches 100 call limit per transaction. Is there a possible workaround for this?
 

michael domz  

@michael domz

First thing I would do is confirm with Xero that what I've said is the case, and that the ContactPersons are only included if querying for a single Contact. And ask if there's any way to include ContactPersons in a query all request.

Failing that, you'll need to batch your ContactPersons callout into single batches on the Salesforce side. I would set it up as follows:

1. Callout to Xero to retrieve all contacts for the Org, and save into Salesforce (without the ContactPersons)
2. Execute an asynchronous callout on each Contact to retrieve the ContactPersons

You may choose to do a @future or Apex Queueable callout for each contact, or alternatively execute an Apex Batch class on all contacts with a batch size of 1.

Be sure to read up on:
- @future callouts
- Apex Queueable interface
- Apex Batch interface

 

Ben Edwards  

Hello Ben,
I want to get invoices related to specific ContactID from xero to salesforce.I used Contact.ContactID = Guid("cd09aa49-134d-40fb-a52b-b63c6a91d712")
as filter to get invoices related to specific ContactID. But i got "HTTP Error 400. The request is badly formed" error.
Can you please tell me how to do that?
 

Chaitali Shete  

Hey Chaitali,

The query needs to be added to the endpoint. I have created a new method called getInvoicesForContact(CONTACT_ID) which you should be able to use. It's updated to the GitHub repo:
https://github.com/benedwards44/Apex-for-Xero
 

Ben Edwards  

Hey Ben,
Thanks for your reply.Its helpful to me.

Thank You!
 

Chaitali Shete  

Good day. I have retrieved all xero invoices using your method 'XeroAccountingApi.getInvoices()'. My problem is all the invoices have 'LineItems=()' even though all of them have line items. How do I retrieve them with the invoices as well?
 

michael domz  

Hi Michael,

I believe when using the get all invoices method, only the header-level details of the invoice are returned.

To get the line items, you need to get a specific invoice, or get invoices using the pagination methods.

I've added a new method to the source code called getInvoice(), that accepts a Xero Invoice ID and should returned the line items as well.
 

Ben Edwards  

@Ben Edwards

I already tried adding a method to get the individual line item of each invoice but I end up having an error about 5.0000 which I realized was the quantity of a line item. I tried to find what part of JSONParser caused it. Your update on the XeroInvoice.cls fully solves the problem as the quantity before was of INTEGER type.

Just tried it and it has no errors now.

Thanks so much for the update !!!
 

michael domz  

Yea that's correct, I had the same issue when I tested it. Looks like the JSON2Apex parser I used set all the number based values at Integer, so I manually updated them to Decimal and that seemed to fix the issue.
 

Ben Edwards  

@Ben Edwards

I tried adding TotalDiscount in the XeroInvoice but it always has a null value even though all the line items have a corresponding DiscountRate.

XeroInvoice:SubTotal=180.00, Total=180.00, TotalDiscount=null, TotalTax=0.00, Type=ACCREC

it has line items like this one

LineItem:AccountCode=260, Description=hhhhhh, DiscountRate=3.00, ItemCode=h1, LineAmount=77.60, LineItemID=95156533-bae7-4b94-9b6b-3f74c7bc1c1a, Quantity=4.0000, TaxAmount=0.00, TaxType=OUTPUT, UnitAmount=20.00, ValidationErrors=null
 

michael domz  

@Ben Edwards

Good day Ben. Can the wrapper class work to Sync records from Salesforce to XERO in real-time? i.e Say every time a Salesforce user updates or creates a record from Salesforce. It should reflect immediately in XERO.
 

Jose Ponce  

@Jose Ponce

Yep you can. You would need to write a trigger on the object you want to sync (Account, Invoice__c etc) and then execute the push to Xero asynchronously (using a @future, Queueable or Batch method in Salesforce).

Your async method would call the createInvoice(), createContact() methods from the Wrapper classes, and this would push to Xero on create/edit of records in Salesforce.
 

Ben Edwards  

Hi Ben,

When I try to deploy the code using the below link, the deployment is failing with the below errors. https://githubsfdeploy.herokuapp.com/app/githubdeploy/benedwards44/Apex-for-Xero

classes/XeroAccountingApiTest.cls(22,16):Dependent class is invalid and needs recompilation:
Class XeroInvoice : Identifier name is reserved: Date
classes/XeroAccountingApi.cls(22,16):Dependent class is invalid and needs recompilation:
Class XeroInvoice : Identifier name is reserved: Date
classes/XeroCalloutResponseParser.cls(22,16):Dependent class is invalid and needs recompilation:
Class XeroInvoice : Identifier name is reserved: Date
classes/XeroInvoice.cls(22,16):Identifier name is reserved: Date

Can you have a look into it please.

Thanks in advance.
 

Rinu George  

Hey Rinu, just fixed this now.

Try again!
 

Ben Edwards  

Hey Ben, Thanks so much for your quick response.
Its working perfectly fine now.

Many thanks,

Rinu
 

Rinu George  

Hi Ben, do you know if we can sort the invoice data using duedate (while making REST API call)

Thanks!
 

Rinu George  

@Rinu

According to the Xero API, it's just a parameter to add to the endpoint:
https://developer.xero.com/documentation/api/requests-and-responses#ordering

I've created a new method getInvoices(String orderBy, XeroAccountingApi.SortOrder sortOrder), to allow you to set an order. So you can run it like:

List<XeroInvoice> myXeroInvoices = XeroAccountingApi.getInvoices('DueDate', XeroAccountingApi.SortOrder.DESCENDING);
 

Ben Edwards  

Thanks Ben!

Thanks for sharing the doc link as well. Got the details from the shared doc.

I was trying orderby and sort instead of order.

Many thanks,

Rinu
 

Rinu George  

Hello Ben,

Good day. How are you ? Hope you are well. It's me again. I just have some question about this. I've tried to create new contact which name has an asterisk(*) . First I checked if it exist and then I insert it to xero. I get an error. "401: oauth_problem=signature_invalid&oauth_problem_advice=Failed%20to%20validate%20signature" . Another thing ben, when I've tried to add website, it doesn't populate the website in my xero org. Can I ask your idea about this ?

Sincerely,
John
 

John Hope  

Hi John,

I haven't come across that issue with the asterisk(*). Is the entire name just as asterisk? I wonder if this needs to be encoded or escaped in some way before sending to Xero.

On the 2nd issue, Website is not a writeable field in the Xero API. You can see this in the documentation:
https://developer.xero.com/documentation/api/contacts

It is available in the GET request, but not the POST request.
 

Ben Edwards  

Hello Ben,

I believe the error does not come with my post parameter. At the moment I already escaped the post string but I believe the error comes with my query. My query is something like this : Select Name from Contact where Name="sample name *test*". Please give me an insight on what is the possible change I need for my query.

Sincerely,
John
 

John Hope  

I'm confused as to where you are running this query or exactly what you're trying to do. Is this a query against the Salesforce database or against the Xero API? If it's for Salesforce, then not really an issue related to this API wrapper.
 

Ben Edwards  

I am trying to check if my salesforce record already exist in Xero. My salesforce record is named as "sample name *test*" that is why I did that query.

Sincerely,
John
 

John Hope  

Hi Ben.
I really confuse how to deploy the app in Salesforce. After I create the certificate I create the private App with the certificate.
And now, how can I deploy it in SalesForce?
So, I guess I have to use the consumer Key and consumer secret to connect both, but how?
Does anyone can helpme?.

Thanks a lot.
 

Freddy VillaLeal  

Click the "Deploy to Org" link in the Quick Setup
 

Ben Edwards  

Hi Ben.
Thanks for your answer.

Now I have another doubt.

When I save a record in xero and then when I get all these records, I don't get the record saved. I just get all the records saved in the XeroContactsMock resource file. I couldn't tested in postman because the "Private App". But I could tested this case with a public app and all right.

In another hand, What is the relationship between XeroContactsMock resource file and the rest of the app? because if I delete all the data saved in this file and the I get all the contacts from xero, I don't get anything. And if I save more records in this file, I get all this records saved.
Is not the program supposed to read the records from xero and not from this file?
I read about "Test.setMock()" and "HttpCalloutMock", in Salesforce documentation, but is not clear.

I'd really appreciate your answer.

Thanks a lot.
 

Freddy VillaLeal  

Hi Freddy,

The Apex Wrapper Classes are designed as an SDK for people already with a good base knowledge of Apex and Salesforce to enable integration to Xero (specifically around the OAuth 1.0 authentication as this is rather difficult using Apex).

Based on your comments, I really don't think you have the necessary knowledge of Apex in order to leverage this SDK. You should instead learn Apex and Salesforce first:
https://trailhead.salesforce.com/
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_dev_guide.htm

And then look to use the wrapper classes. I would happy support you in any questions specifically around the wrapper classes, but I'm not sure to teach you how to write code or build Apex applications.

Ben
 

Ben Edwards  

Thanks
 

Freddy VillaLeal  

Hi Ben,
Do you have any document to get 30 min access token for a public app using apex?
 

SFDC Dev