Salesforce to QuickBooks Online Integration via Apex
Table Of Content
Overview of Integration
If you’re running businesses using Salesforce and QuickBooks Online, you might want to integrate the two systems. The guide below will help you integrate Salesforce with QuickBooks, enabling you to create or read QuickBooks customers and invoices with ease.
To integrate QuickBooks with Salesforce, you’ll need admin access to QuickBooks Online (this will work for all versions of QuickBooks Online) and admin access to Salesforce (Enterprise Edition or higher, or a Dev/Sandbox org for testing). You’ll also need some experience with Apex code. If you lack experience with Apex, you may want to consider using a no-code integration with Zapier or use Breadwinner’s no-code integration between Salesforce and QuickBooks.
Remote Site Settings
When making an Apex callout to an external website, it is important to register the target website within the Remote Site Settings page. This is because Salesforce has strict limitations on calls to unregistered network addresses, and not registering the target site will result in the callout failing.
Please follow the steps below to create two remote site settings in Salesforce before making calls to QuickBooks Endpoints:
- Go to Setup.
- In the Quick Find search bar, type “Remote Site.”
- Click on “Remote Site Settings.”
- Create the following two remote site settings, each serving a specific purpose.
- For authentication and obtaining the Access Token,
URL: https://oauth.platform.intuit.com - For retrieving the Data from QuickBooks,
If connecting to a QuickBooks Online Sandbox: https://sandbox-quickbooks.api.intuit.com (make sure you fully log into to your Sandbox environment as a user, as final setup of the Sandbox environment is performed upon the first login)
If connecting to a QuickBooks Online Production environment: https://quickbooks.api.intuit.com
- For authentication and obtaining the Access Token,
Connecting to QuickBooks Online using OAuth 2.0
Create App
Upon logging into https://developer.intuit.com/, navigate to “Dashboard” and create a new App.
To create the App, click on the button “Create an app” –> Select QuickBooks Online and Payments –> Name your App “My App” –> select the scopes required –> click on “Create app.”
Create a Custom Object in Salesforce
To ensure we can utilize tokens such as Client ID, Client Secret, Access Token, and Refresh Token in future requests or callouts to QuickBooks Online, it’s necessary to store them in Salesforce. This involves creating a Custom Object with the following details:
Custom Object
- Label: QuickBooks Details
- API Name: QuickBooks_Details__c
Custom Fields
Label | API Name | Data Type |
Access Token | Access_Token__c | Long Text Area(4096) |
Access Token Expiry | Access_Token_Expiry__c | Number(18, 0) |
Client Id | Client_Id__c | Text(255) |
Client Secret | Client_Secret__c | Text(255) |
RealmId | RealmId__c | Text(255) |
Refresh Token | Refresh_Token__c | Long Text Area(512) |
Refresh Token Expiry | Refresh_Token_Expiry__c | Number(18, 0) |
Note: Ideally, the preference would be to utilize Custom Settings for storing Tokens rather than Custom Objects. However, we have opted to use Custom Object due to the limitation of a maximum Access Token length of 4096 characters from QuickBooks, and Custom Settings cannotn handle fields of that length.
Get Client Tokens
After creating the App, go to Development Settings in the dashboard and navigate to Keys & Credentials to obtain the “Client ID” and “Client Secret”.
Your unique ID and Secret will be similar to the example below. Please note that your ID and Secret will differ from the values provided.
Client Id: ABsFR*******************************************gjLX
Client Secret: PRJP********************************************pA2To
Add Redirect URI
To connect Salesforce to QuickBooks, QuickBooks will send a code and realmId as a URL Parameter to a specific web address that we provide in the Redirect URI. The code is then used to get Access Tokens.
To do this, we need to create a VF page called “qboOauthRedirect” (the code will be added in later sections).
Once this page is created, we must add the URL “https://<your-domain>/apex/qboOAuthRedirect” as a Redirect URI in QuickBooks for the App created earlier.
Authorize QuickBooks Online and Obtain Tokens
In Salesforce, create a new record in the previously created object. Save the Client ID and Client Secret obtained earlier and integrate the provided code by developing the corresponding VF Page and Apex Class.
qboOauthRedirect.vfp
<apex:page controller="qboOAuthHandlerClass" action="{!getOAuthTokens}" >
<apex:form>
<apex:pageMessage rendered="{!isAuthorizationSuccess}" summary="Authorization is Succesfull" severity="confirm" strength="3"/>
<apex:outputPanel rendered="{!showOAuthButton}">
<apex:commandButton value="Authorize QuickBooks Online" onclick="window.open('{!buttonUrl}', '_blank ');window.close();"/>
</apex:outputPanel>
</apex:form>
</apex:page>
qboOAuthHandlerClass.cls
public class qboOAuthHandlerClass {
public String buttonUrl {get; private set;}
public boolean showOAuthButton {get; private set;}
public boolean isAuthorizationSuccess {get; private set;}
public String code;
public String realmId;
QuickBooks_Details__c qbRec;
FINAL String redirect_uri = 'https://persoanl3-dev-ed--c.develop.vf.force.com/apex/qboOAuthRedirect'; // Replace this with your Org based Domain and Visualforce Page name
FINAL String base_url = 'https://appcenter.intuit.com/connect/oauth2?response_type=code&scope=com.intuit.quickbooks.accounting&state=testStateSecurity';
public qboOAuthHandlerClass(){
showOAuthButton = false;
isAuthorizationSuccess = false;
qbRec = [SELECT Id, Client_Id__c, Client_Secret__c FROM QuickBooks_Details__c LIMIT 1];
Map<String, String> urlParams = ApexPages.currentPage().getParameters();
if(urlParams.containsKey('code')){
code = urlParams.get('code');
realmId = urlParams.get('realmId');
}
else{
showOAuthButton = true;
buttonUrl = base_url + '&client_id='+qbRec.Client_Id__c + '&redirect_uri='+redirect_uri;
}
}
public pageReference getOAuthTokens(){
if(String.isNotBlank(code) && String.isNotBlank(realmId)){
Blob headerValue = Blob.valueOf(qbRec.Client_Id__c + ':' + qbRec.Client_Secret__c);
String authorizationHeader = 'Basic ' + EncodingUtil.base64Encode(headerValue);
String payload = 'grant_type=authorization_code&code='+code+'&redirect_uri='+redirect_uri;
httpRequest req = new HttpRequest();
req.setEndpoint('https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer');
req.setMethod('POST');
req.setHeader('Authorization', authorizationHeader);
req.setHeader('Content-Type', 'application/x-www-form-urlencoded');
req.setHeader('Accept', 'application/json');
req.setBody(payload);
system.debug('req ::: '+req);
Http http = new Http();
HTTPResponse res = http.send(req);
if(res.getStatusCode() == 200 && res.getBody() != NULL){
String responseBody = res.getBody();
isAuthorizationSuccess = true;
updateTokensinSalesforce(responseBody, realmId, qbRec.Id);
}
}
return null;
}
public static void updateTokensinSalesforce(String responseBody, String realmId, Id qbRecId){
Map<String, Object> respMap = (Map<String, Object>)JSON.deserializeUntyped(responseBody);
QuickBooks_Details__c qbRec = new QuickBooks_Details__c(Id = qbRecId);
if(respMap.containsKey('access_token')) qbRec.Access_Token__c = String.valueOf(respMap.get('access_token'));
if(respMap.containsKey('refresh_token')) qbRec.Refresh_Token__c = String.valueOf(respMap.get('refresh_token'));
if(respMap.containsKey('expires_in')) qbRec.Access_Token_Expiry__c = Integer.valueOf(respMap.get('expires_in'));
if(respMap.containsKey('x_refresh_token_expires_in')) qbRec.Refresh_Token_Expiry__c = Integer.valueOf(respMap.get('x_refresh_token_expires_in'));
if(String.isNotBlank(realmId)) qbRec.RealmId__c = realmId;
update qbRec;
}
}
QuickBooks Customers
Read QuickBooks Customers
Here is the sample code that can be used to read all the QuickBooks Customers from Salesforce.
Note: If you wish to get a single QuickBooks Customer, add the WHERE clause to the Query like below
select * from Customer where id = ’29’
qboCustomersClass.cls
public class qboCustomersClass {
public static final string BASE_URL = 'https://sandbox-quickbooks.api.intuit.com/v3/company/';
public static void getQBOCustomers(){
QuickBooks_Details__c qbRec = [SELECT Id, RealmId__c, Access_Token__c FROM QuickBooks_Details__c LIMIT 1];
String reqURL = BASE_URL+qbRec.RealmId__c+'/query?query='+EncodingUtil.urlEncode('select * from Customer', 'UTF-8')+'&minorversion=69';
httpRequest req = new HttpRequest();
req.setEndpoint(reqURL);
req.setMethod('GET');
req.setHeader('Authorization', 'Bearer '+qbRec.Access_Token__c);
req.setHeader('Accept', 'application/json');
Http http = new Http();
HTTPResponse res = http.send(req);
if(res.getStatusCode() == 200 && res.getBody() != NULL){
String responseBody = res.getBody();
system.debug('responseBody ::: '+responseBody);
}
else{
if(res.getStatusCode() == 401){
// Token Expired - Please refresh the token
system.debug('res.getBody() ::: '+res.getBody());
}
}
}
}
Create a QuickBooks Customer
Here is the sample code that can be used to create a QuickBooks Customer from Salesforce.
qboCustomersClass.cls
public class qboCustomersClass {
public static final string BASE_URL = 'https://sandbox-quickbooks.api.intuit.com/v3/company/';
public static void createQBOCustomer(){
QuickBooks_Details__c qbRec = [SELECT Id, RealmId__c, Access_Token__c FROM QuickBooks_Details__c LIMIT 1];
String reqURL = BASE_URL+qbRec.RealmId__c+'/customer?minorversion=69';
String requestBody = '{"FullyQualifiedName":"King Groceries","PrimaryEmailAddr":{"Address":"jdrew@myemail.com"},"DisplayName":"King\'s Groceries","Suffix":"Jr","Title":"Mr","MiddleName":"B","Notes":"Here are other details.","FamilyName":"King","PrimaryPhone":{"FreeFormNumber":"(555) 555-5555"},"CompanyName":"King Groceries","BillAddr":{"CountrySubDivisionCode":"CA","City":"Mountain View","PostalCode":"94042","Line1":"123 Main Street","Country":"USA"},"GivenName":"James"}';
HttpRequest req = new HttpRequest();
req.setEndpoint(reqURL);
req.setMethod('POST');
req.setHeader('Content-Type', 'application/json');
req.setHeader('Authorization', 'Bearer '+qbRec.Access_Token__c);
req.setHeader('Accept', 'application/json');
req.setBody(requestBody);
Http http = new Http();
HTTPResponse res = http.send(req);
if(res.getStatusCode() == 200 && res.getBody() != NULL){
String responseBody = res.getBody();
system.debug('responseBody ::: '+responseBody);
}
else{
if(res.getStatusCode() == 401){
// Token Expired - Please refresh the token
system.debug('res.getBody() ::: '+res.getBody());
}
}
}
}
QuickBooks Invoices
Read QuickBooks Invoices
Here is the sample code that can be used to read all the QuickBooks Invoices from Salesforce.
Note: If you wish to get a single QuickBooks Invoice, add the WHERE clause to the Query like below
select * from Invoice where id = ‘239’
qboInvoicesClass.cls
public class qboInvoicesClass {
public static final string BASE_URL = 'https://sandbox-quickbooks.api.intuit.com/v3/company/';
public static void getQBOInvoices(){
QuickBooks_Details__c qbRec = [SELECT Id, RealmId__c, Access_Token__c FROM QuickBooks_Details__c LIMIT 1];
String reqURL = BASE_URL+qbRec.RealmId__c+'/query?query='+EncodingUtil.urlEncode('select * from Invoice', 'UTF-8')+'&minorversion=69';
httpRequest req = new HttpRequest();
req.setEndpoint(reqURL);
req.setMethod('GET');
req.setHeader('Authorization', 'Bearer '+qbRec.Access_Token__c);
req.setHeader('Accept', 'application/json');
Http http = new Http();
HTTPResponse res = http.send(req);
if(res.getStatusCode() == 200 && res.getBody() != NULL){
String responseBody = res.getBody();
system.debug('responseBody ::: '+responseBody);
}
else{
if(res.getStatusCode() == 401){
// Token Expired - Please refresh the token
system.debug('res.getBody() ::: '+res.getBody());
}
}
}
}
Create a QuickBooks Invoice
Here is the sample code that can be used to create a QuickBooks Invoice from Salesforce.
qboInvoicesClass.cls
public class qboInvoicesClass {
public static final string BASE_URL = 'https://sandbox-quickbooks.api.intuit.com/v3/company/';
public static void createQBOInvoice(){
QuickBooks_Details__c qbRec = [SELECT Id, RealmId__c, Access_Token__c FROM QuickBooks_Details__c LIMIT 1];
String reqURL = BASE_URL+qbRec.RealmId__c+'/invoice?minorversion=69';
String requestBody = '{"Line":[{"DetailType":"SalesItemLineDetail","Amount":100,"SalesItemLineDetail":{"ItemRef":{"name":"Services","value":"1"}}}],"CustomerRef":{"value":"58"}}';
httpRequest req = new HttpRequest();
req.setEndpoint(reqURL);
req.setMethod('POST');
req.setHeader('Content-Type', 'application/json');
req.setHeader('Authorization', 'Bearer '+qbRec.Access_Token__c);
req.setHeader('Accept', 'application/json');
req.setBody(requestBody);
Http http = new Http();
HTTPResponse res = http.send(req);
if(res.getStatusCode() == 200 && res.getBody() != NULL){
String responseBody = res.getBody();
system.debug('responseBody ::: '+responseBody);
}
else{
if(res.getStatusCode() == 401){
// Token Expired - Please refresh the token
system.debug('res.getBody() ::: '+res.getBody());
}
}
}
}
Next Steps
At this point, you can create a QuickBooks Invoice from Salesforce using Apex. You might want to take additional steps or automate what you have built.
- Would you like to learn how to create a QuickBooks Customer and Invoice automatically when an Opportunity is converted? To achieve this, you can create an Apex Method annotated with @InvocableMethod. This method should include the code required to create a QuickBooks Customer and related Invoice. Finally, call this method from a Record Triggered Flow.
- If you want to fetch data from QuickBooks continuously, create a schedulable class and schedule it to run every hour or two.