Salesforce to QuickBooks Online Integration via Apex

i 3 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:

  1. Go to Setup.
  2. In the Quick Find search bar, type “Remote Site.”
  3. Click on “Remote Site Settings.”
  4. Create the following two remote site settings, each serving a specific purpose.
    1. For authentication and obtaining the Access Token,
      URL: https://oauth.platform.intuit.com
    2. 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

 

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.”

QBO Create App

 

 

 

 

 

Select QuickBooks Online And Payments

 

 

 

 

 

QBO App Name and Scope

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.

QBO Redirect Url

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.

 

  1. 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.
  2. If you want to fetch data from QuickBooks continuously, create a schedulable class and schedule it to run every hour or two.