Steps to Integrate Quickbooks with Salesforce
- 9 minutes
- 2582
Quickbooks is an accounting software package built and marketed by intuit. Quickbooks products are geared mainly toward small and medium sized organizations and offer on-premises accounting applications as well as cloud-based versions that accept business payments, manage and pay bills, and payroll functions.
Quickbooks and Salesforce are two cloud based software and they are widely used by small to large companies. With the help of integration between these two can save hours in manual data migration and give information about how your business is operating and where it can be optimized. It will also help to share information among accounting and sales, client data, sales order, costs, and invoicing.
Key Features of Salesforce and Quickbooks Integration
- It will synchronize Salesforce items with Quickbooks items
- It will synchronize the update Quickbooks lists with Salesforce items
- New client in Quickbooks makes a record in Salesforce
- It will synchronise account with clients in Quickbooks on the web
- Salesforce cloud-won have opportunities automatically make Quickbooks invoices/receipt
Below are the steps of Salesforce Integration with Quickbooks
Step 1: Create an visualforce page to authorize the quickbook for the access token
Visualforce Page:
<apex:page id="pg" controller="QuickbooksConnector" action="{!getAccessToken}" showHeader="false" sidebar="false" lightningStylesheets="true">
<html>
<head>
<title>Quickbook Integration</title>
<apex:slds />
</head>
<body>
<div class="slds-scope slds-box slds-box_x-small">
<apex:form id="theForm">
<apex:actionFunction action="{!GetAuthorization}" oncomplete="overridePageMessages()" name="Auth" reRender="theForm" />
<apex:actionstatus id="status">
<apex:facet name="start">
<div id="spinner" class="slds-spinner_container slds-is-relative" style="position: fixed;">
<div role="status" class="slds-spinner slds-spinner--large slds-spinner--brand">
<div class="slds-spinner__dot-a"></div>
<div class="slds-spinner__dot-b"></div>
</div>
</div>
</apex:facet>
</apex:actionstatus>
<div class="slds-page-header " role="banner">
<div class="slds-grid slds-grid_vertical">
<div class="slds-col slds-has-flexi-truncate" align="center">
<h1 class="slds-page-header__title slds-truncate h1-size" title="Quickbook Authentication">Quickbook Authentication</h1>
<hr/>
<div class="slds-col slds-medium-size" align="center">
<apex:pageMessages id="showmsg"></apex:pageMessages>
</div>
<div class="slds-col slds-medium-size" align="center">
<table align="center" style="width:80%">
<tr>
<td>
<button class=" buttonWidth slds-button slds-button_brand slds-m-top_large " align="center" onclick="Auth(); return false;"
reRender="theForm">
Authorization
</button>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</apex:form>
Apex Class:
public String authToken { get; set; }
public String QBPageURL { get; set; }
private static QuickBookIntuit__c serviceObject;
public pagereference GetAuthorization() {
serviceObject = getServiceSettings();
String timestamp = string.valueof(dateTime.now().getTime()/1000);
Map<String,String> parameters = new Map<String,String>();
parameters.put('client_id', serviceObject.Consumer_Key__c);
parameters.put('scope', 'com.intuit.quickbooks.accounting');
parameters.put('redirect_uri', EncodingUtil.urlEncode(URL.getSalesforceBaseUrl().toExternalForm() + '/apex/QuickbooksConnectorPage', 'UTF-8'));
parameters.put('response_type', 'code');
parameters.put('state', timestamp);
String url = serviceObject.Authorization_URL__c+'?client_id=' +
serviceObject.Consumer_Key__c + '&scope=com.intuit.quickbooks.accounting&redirect_uri=' +
EncodingUtil.urlEncode(URL.getSalesforceBaseUrl().toExternalForm() + '/apex/QuickbooksConnectorPage', 'UTF-8') +
'&response_type=code&state=' + timestamp;
HttpRequest req = new HttpRequest();
HttpResponse res;
req.setEndpoint(url);
req.setMethod(serviceObject.Http_Request_Type__c);
req.setHeader('Content-Length', '146');
QBPageURL = 'https://c71.qbo.intuit.com/Connect/Begin?oauth_token=' + authToken;
pagereference redirect = new PageReference( url );
return redirect.setRedirect(true);
}
/* =============================================================================
//
// Function: Get Authorization Code With Accounting Online Scope
//
/*=============================================================================*/
public pagereference GetAuthorization() {
serviceObject = getServiceSettings();
String timestamp = string.valueof(dateTime.now().getTime()/1000);
Map<String,String> parameters = new Map<String,String>();
parameters.put('client_id', serviceObject.Consumer_Key__c);
parameters.put('scope', 'com.intuit.quickbooks.accounting');
parameters.put('redirect_uri', EncodingUtil.urlEncode(URL.getSalesforceBaseUrl().toExternalForm() + '/apex/QuickbooksConnectorPage', 'UTF-8'));
parameters.put('response_type', 'code');
parameters.put('state', timestamp);
String url = serviceObject.Authorization_URL__c+'?client_id=' +
serviceObject.Consumer_Key__c + '&scope=com.intuit.quickbooks.accounting&redirect_uri=' +
EncodingUtil.urlEncode(URL.getSalesforceBaseUrl().toExternalForm() + '/apex/QuickbooksConnectorPage', 'UTF-8') +
'&response_type=code&state=' + timestamp;
HttpRequest req = new HttpRequest();
HttpResponse res;
req.setEndpoint(url);
req.setMethod(serviceObject.Http_Request_Type__c);
req.setHeader('Content-Length', '146');
QBPageURL = 'https://c71.qbo.intuit.com/Connect/Begin?oauth_token=' + authToken;
pagereference redirect = new PageReference( url );
return redirect.setRedirect(true);
}
public void getAccessToken(){
serviceObject = getServiceSettings();
if( !ApexPages.currentPage().getParameters().containskey('code') )
return;
String encodedString = EncodingUtil.base64Encode(Blob.valueOf(serviceObject.Consumer_Key__c+':'+serviceObject.Consumer_Secret__C));
String endPoint = 'https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer';
String redirectUrl = EncodingUtil.urlEncode(URL.getSalesforceBaseUrl().toExternalForm() + '/apex/QuickbooksConnectorPage', 'UTF-8');
String oAuthCode = ApexPages.currentPage().getParameters().get('code');
String companyId = ApexPages.currentPage().getParameters().get('realmId');
String stateID = ApexPages.currentPage().getParameters().get('state');
String requestBody = 'grant_type=authorization_code&code='+oAuthCode+'&redirect_uri='+redirectUrl;
String errorMessage ='';
HttpRequest httpReq = new HttpRequest();
HttpResponse httpRes = new HttpResponse();
Http http = new Http();
httpReq.setMethod( serviceObject.Http_Request_Type__c );
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());
serviceObject.StateID__c = stateID;
serviceObject.CompanyId__c = companyId;
serviceObject.OAuth_Token_Secret__c = oAuthCode;
serviceObject.OAuth_Token__c = string.valueof(response_Map.get('access_token'));
serviceObject.Temporary_Token_Secret__c = string.valueof(response_Map.get('refresh_token'));
serviceObject.Expire_In_Seconds__c = (Decimal)response_Map.get('expires_in');
serviceObject.Refresh_Token_Expires_In__c = (Decimal)response_Map.get('x_refresh_token_expires_in');
serviceObject.Expires_In_Time__c = System.Now().addSeconds(Integer.valueOf((Decimal)response_Map.get('expires_in')));
ApexPages.addmessage( new ApexPages.message(ApexPages.severity.Confirm,+' '+'Successfully Authenticated with Quickbooks System!!!') );
update serviceObject;
} 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) {
if(String.valueOf(e.getMessage()).startsWith('Unauthorized endpoint')){
errorMessage = 'Unauthorize endpoint: An Administrator must go to Setup -> Administer -> Security Control ->'
+' Remote Site Setting and add '+' '+ endPoint +' Endpoint';
ApexPages.addmessage(new ApexPages.message(ApexPages.severity.ERROR,errorMessage));
}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));
}
}
}
}
Steps 2: Click On Authorization
It will redirect you to quickbook login.
Create an account in the intuit and use the credential.
Configure all details in the Intuit related to your company and other information.
Create a company record and manage all the financial account details in the Quickbook.
If you have a multiple company details in quickbook then select the company.
On select you will get an authorization code and store the authorization all details in the Salesforce Object.
You can create the other button for the Sync Account, Product & Customers details.
Step 4: Get the refresh token.
Create an Apex Batch class and call the method from apex class at the 15 min interval so you will get an refresh token on the 15 min interval.
Apex Method:
public void doRefreshAccessToken(){
serviceObject = getServiceSettings();
String encodedString = EncodingUtil.base64Encode( Blob.valueOf(serviceObject.Consumer_Key__c + ':' + serviceObject.Consumer_Secret__c));
String endPoint = 'https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer';
String requestBody = 'grant_type=refresh_token&refresh_token=';
if(serviceObject.Temporary_Token_Secret__c != null){
requestBody += serviceObject.Temporary_Token_Secret__c;
} else {
}
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());
serviceObject.OAuth_Token__c = string.valueof(response_Map.get('access_token'));
serviceObject.Temporary_Token_Secret__c = string.valueof(response_Map.get('refresh_token'));
serviceObject.Expire_In_Seconds__c = (Decimal)response_Map.get('expires_in');
serviceObject.Refresh_Token_Expires_In__c = (Decimal)response_Map.get('x_refresh_token_expires_in');
serviceObject.Expires_In_Time__c = System.Now().addSeconds(Integer.valueOf((Decimal)response_Map.get('expires_in')));
update serviceObject;
}
} catch ( System.Exception e ){
if(String.valueOf(e.getMessage()).startsWith('Unauthorized endpoint')){
errorMessage = 'Unauthorize endpoint: An Administrator must go to Setup -> Administer -> Security Control ->'
+' Remote Site Setting and add '+' '+ endPoint +' Endpoint';
}else{
errorMessage = 'Unexpected Error while communicating with Quickbooks API. '
+'Status '+httpRes.getStatus()+' and Status Code '+httpRes.getStatuscode();
}
}
}
Note: After the Authorization if you want to Sync other Details from the Quickbook then you can create the custom button on the record level so you can manually sync the single records with the Quickbook.
Useful Link for the Quickbook Integration:
https://developer.intuit.com/app/developer/qbpayments/docs/develop/authentication-and-authorization
https://developer.intuit.com/app/developer/qbpayments/docs/develop/explore-the-quickbooks-payments-api/postman#using-postman
https://developer.intuit.com/app/developer/qbo/docs/api/accounting/most-commonly-used/account