Salesforce – Quickbooks Online Integration – oAuth 2.O

Hi All, I have come up with another integration. In this post we will make connection between Salesforce and Quickbook System using Apex, VF page.

Earlier Quickbooks uses oAuth 1.0 for the authentication but now it has updated it’s API and using oAuth 2.0 so in this Integration we are going to use oAuth 2.0

Step1 – Create a Custom Object OR Custom Metadata to store the information about Access and Refresh Token. If you are thinking about to use Custom Setting the answer is No because Access Token length is more than 255 Character and in Custom Setting we can store data upto 255 Character.

Setup -> Create -> Objects -> New -> Your Object will look like below image

QuickBooks Infos Object

Step2 – Create a Trail Account of QuickBooks From Here.

Step3 – Create a connected App into QuickBooks Developer account, Follow the steps given into this Link. Please enter a redirect URI. for example if you want to redirect into “QuickbookConnection” VF page and your org base URL is “https://dreamhouse-a-dev-ed.my.salesforce.com” then your Redirect URI will be as given below

https://dreamhouse-a-dev-ed–c.ap5.visual.force.com/apex/QuickbookConnection

Step4 – Now, in this Step get the Consumer Secret and Consumer Key and to get the key follow the steps given in This Link.

Connected App

Step5 – Now, Create a Apex Class. File -> New -> Apex Class -> Name it “QuickbookConnection” -> OK. Use below code the class

public class QuickbookConnection{
 // Replase with your Client Id
 public static String client_Id = 'Q0lUVQVbYhzGMzNuBe2AFc8AiQL82BPOBpIILFlqteae8aTz8H';
 // Replase with your Client Secret
 public static String consumer_Secret = 'qjhhpmfSuB0Yk5X6aQSXc5r3DgYS9jhOLVcbBsHV';
 // Replace with Your Redirect URI
 public static String redirect_URI = 'https://dreamhouse-a-dev-ed--c.ap5.visual.force.com/apex/QuickbookConnection';

 /*
 * @Name - doAuthorizationQuickBooks
 * @Param - None
 * @Description - to get the authentication code from the QuickBooks Account
 * @ReturnType - PageReference
 */


 public static PageReference doAuthorizationQuickBooks(){

 String authorization_endpoint = 'https://appcenter.intuit.com/connect/oauth2';

 String scope = 'com.intuit.quickbooks.accounting';

 String final_EndPoint = authorization_endpoint+'?client_id='+client_Id+'&response_type=code&scope='+
 scope+'&state=123445633443&redirect_uri='+redirect_URI;

 PageReference pageRef = new PageReference(final_EndPoint);
 return pageRef;
 }
 /*
 * @Name - doFetchAccessToken
 * @Param - None
 * @Description - to get the Access Token , Refresh Token and other Information after getting the authentication code
 * @ReturnType - void
 */

 public static void doFetchAccessToken(){

 String encodedString = EncodingUtil.base64Encode(Blob.valueOf(client_Id+':'+consumer_Secret));
 String endPoint = 'https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer';

 String oAuthCode = ApexPages.currentPage().getParameters().get('code');
 String requestBody = 'grant_type=authorization_code&code='+oAuthCode+'&redirect_uri='+redirect_URI;
 String errorMessage ='';

 HttpRequest httpReq = new HttpRequest();
 HttpResponse httpRes = new HttpResponse();
 Http http = new Http();
 httpReq.setMethod('POST');
 httpReq.setEndPoint(endPoint);
 httpReq.setHeader('Authorization' , 'Basic '+encodedString);
 httpReq.setHeader('Content-Type' , 'application/x-www-form-urlencoded');
 httpReq.setBody(requestBody);

 try{
 httpRes = http.send(httpReq);

 if(httpRes.getStatusCode() == 200){
 Map<String, Object> response_Map = (Map<String, Object>)JSON.deserializeUntyped(httpRes.getBody());
 List<Quickbooks_Token_Info__c> connectSettingInfos = new List<Quickbooks_Token_Info__c>();
 connectSettingInfos = [Select Id, Name From Quickbooks_Token_Info__c Where Name ='QuickBooks Setting Info'];
 Quickbooks_Token_Info__c quickBooksSettingInfo = new Quickbooks_Token_Info__c();

 String Name = 'QuickBooks Setting Info';
 String accessToken = (String)response_Map.get('access_token');
 String refreshToken = (String)response_Map.get('refresh_token');
 Decimal expiresIn = (Decimal)response_Map.get('expires_in');
 Decimal expiresInRefToken = (Decimal)response_Map.get('x_refresh_token_expires_in');

 quickBooksSettingInfo.Name = Name;
 quickBooksSettingInfo.Access_Token__c = accessToken;
 quickBooksSettingInfo.Refresh_Token__c = refreshToken;
 quickBooksSettingInfo.Expire_In_Seconds__c = expiresIn;
 quickBooksSettingInfo.Refresh_Token_Expires_In__c = expiresInRefToken;
 if(connectSettingInfos!=null && connectSettingInfos.size() > 0 ) quickBooksSettingInfo.Id = connectSettingInfos[0].Id;

 upsert quickBooksSettingInfo;
 ApexPages.addmessage(new ApexPages.message(ApexPages.severity.Confirm,'Successfully Authenticated with Quickbooks System!!!'));
 }else{
 ApexPages.addmessage(new ApexPages.message(ApexPages.severity.ERROR,'Unexpected Error while communicating with Quickbooks API'+
 'Status '+httpRes.getStatus()+' and Status Code '+httpRes.getStatuscode()));
 }

 }catch(System.Exception e){
 System.debug('#### Exception Executed '+e.getStackTraceString());
 if(String.valueOf(e.getMessage()).startsWith('Unauthorized endpoint')){
 errorMessage = 'Unauthorize endpoint: An Administer must go to Setup -> Administer -> Security Control ->'
 +' Remote Site Setting and add '+' '+ endPoint +' Endpoint';
 ApexPages.addmessage(new ApexPages.message(ApexPages.severity.ERROR,errorMessage));
 //return null;
 }else{
 errorMessage = 'Unexpected Error while communicating with Quickbooks API. '
 +'Status '+httpRes.getStatus()+' and Status Code '+httpRes.getStatuscode();
 ApexPages.addmessage(new ApexPages.message(ApexPages.severity.ERROR,errorMessage));
 //return null;
 }
 }
 }
 /*
 * @Name - doRefreshAccessToken
 * @Param - None
 * @Description - to get the Refresh Token and other Information after access token expires
 * @ReturnType - void
 */

 public static void doRefreshAccessToken(){
 String encodedString = EncodingUtil.base64Encode(Blob.valueOf(client_Id+':'+consumer_Secret));
 String endPoint = 'https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer';

 QuickBookIntegrationInfo__c QBInfo = QuickBookIntegrationInfo__c.getAll().get('QuickBooks Setting Info');

 String oAuthCode = ApexPages.currentPage().getParameters().get('code');
 String requestBody = 'grant_type=refresh_token&refresh_token=';
 if(QBInfo!=null && QBInfo.Refresh_Token__c !=null){
 requestBody+= QBInfo.Refresh_Token__c;
 }else{
 ApexPages.addmessage(new ApexPages.message(ApexPages.severity.ERROR,'Refresh Token is NULL'));
 }
 String errorMessage ='';

 HttpRequest httpReq = new HttpRequest();
 HttpResponse httpRes = new HttpResponse();
 Http http = new Http();
 httpReq.setMethod('POST');
 httpReq.setEndPoint(endPoint);
 httpReq.setHeader('Authorization' , 'Basic '+encodedString);
 httpReq.setHeader('Content-Type' , 'application/x-www-form-urlencoded');
 httpReq.setBody(requestBody);

 try{

 }catch(System.Exception e){
 System.debug('#### Exception Executed '+e.getStackTraceString());
 if(String.valueOf(e.getMessage()).startsWith('Unauthorized endpoint')){
 errorMessage = 'Unauthorize endpoint: An Administer must go to Setup -> Administer -> Security Control ->'
 +' Remote Site Setting and add '+' '+ endPoint +' Endpoint';
 ApexPages.addmessage(new ApexPages.message(ApexPages.severity.ERROR,errorMessage));
 //return null;
 }else{
 errorMessage = 'Unexpected Error while communicating with Quickbooks API. '
 +'Status '+httpRes.getStatus()+' and Status Code '+httpRes.getStatuscode();
 ApexPages.addmessage(new ApexPages.message(ApexPages.severity.ERROR,errorMessage));
 //return null;
 }
 }
 }
}

 

Step5 – Create a VF page. File -> New -> Visualforce Page -> Name “QuickbookConnection” -> OK. and use below code for this page

<apex:page controller="QuickbookConnection">
<apex:slds />
<apex:form id="theForm" >
<apex:pageblock >
<apex:pageMessages ></apex:pageMessages>
<apex:actionstatus id="statusAuthQuickBooks">
<apex:facet name="start">
<div class="waitingSearchDiv" id="el_loading" style="background-color: #fbfbfb; height:100%;opacity:0.65;width:100%;">
<div class="waitingHolder" style="top: 100px; width: 91px;">
<img class="waitingImage" src="/img/loading.gif" title="Please Wait..." />
<span class="waitingDescription">Loading...</span></div>
</div>
</apex:facet>
</apex:actionstatus>
<apex:pageBlockButtons location="top">
<apex:commandButton action="{!doAuthorizationQuickBooks}" value="Authorize with Quickbooks" />
<apex:commandButton action="{!doFetchAccessToken}" value="Complete Authorzation" status="statusAuthQuickBooks"
reRender="theForm" />
</apex:pageBlockButtons>
</apex:pageblock>
</apex:form>
</apex:page>

tada we have done with the coding and configuration part, now time to test the functionality. Click preview on the VF page. Click on Authorize with QuickBooks it will take you to login page of QuickBooks login with your username and password.

If you have more than 1 Sandbox it will list all the sandbox over there select one which you want to integrate.

Authorize the Application and you will be redirected to the same page now click on Complete Authorization it will create a record for the custom Object that you have created with success message

See the working in below gif Image

Salesforce - Quickbooks Working.gif

Now, We have access token we can make the callout to get the data from QuickBooks into Salesforce. You can find the complete code form Here.

If you have any problem then please come up into comment section.

Happy Learning 🙂 😉

Resources –

 

4 thoughts on “Salesforce – Quickbooks Online Integration – oAuth 2.O

  1. Hi

    Can you provide additional Salesforce -integration scenarios/examples as described in your previous posts like
    LinkedIn -sales force integration

  2. Hi,

    There is nowhere QuickBookIntegrationInfo__c mentioned in the description other than the code.

    As per your description, it has to be custom object/custom metadata to store the refresh token. But I am getting the following error (not of type settings). If I am using custom setting, then I am not getting errors. But the Refresh_Token__C can be set with the length of 255.

    Can you please clarify.

    1. Hi Alagu,

      We have to use the Custom object to Store the Access Token no Refresh Token. Because the Length of Access Token is more than 255 Characters. In the blog I specified the Object and also attached the screen.

      Hope this is clear to you.

      Please let me know if you have any query.

      Regards,
      SFDCPanter

Leave a Reply

Your email address will not be published. Required fields are marked *