Skip to content

Sample Java application for QuickBooks Sales Tax API demonstrating external invoicing workflows with GraphQL-based tax calculation

License

Notifications You must be signed in to change notification settings

IntuitDeveloper/SampleApp-SalesTax-Java

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

QuickBooks Sales Tax API (Java/Spring Boot)

This Spring Boot app authenticates with QuickBooks via OAuth 2.0 (Java SDK), fetches Customers/Items via the Accounting API, and calculates Sales Tax using the Sales Tax GraphQL API.

Features

  • Complete Sales Tax Operations (UI demo)
    • ✅ Calculate: real-time tax calculations for transactions (GraphQL)
    • ✅ Customer/Item retrieval via Accounting API
    • ✅ Input builder: select customer/item, shipping ZIPs, units, and unit value
  • GraphQL Integration
    • Uses indirectTaxCalculateSaleTransactionTax mutation
    • Variables prepared dynamically from form input
  • OAuth 2.0 (Java SDK)
    • Discovery-based environment handling
    • Secure redirect with configurable URI
  • Diagnostics: GET /debug/config returns non-sensitive runtime config

Prerequisites

  • Java 17 or higher
  • Gradle 7.0 or higher
  • QuickBooks Developer account and a QuickBooks Online company
  • ngrok (for local development)

Endpoints

  • / — Home page
  • /connect — Initiate OAuth flow
  • /callback — OAuth callback
  • /calculate_tax — Run Sales Tax GraphQL calculation
  • /debug/config — Non-sensitive runtime config

Dependencies

Runtime

  • Spring Boot Web (spring-boot-starter-web)
  • Spring Boot Thymeleaf (spring-boot-starter-thymeleaf)
  • Intuit QuickBooks SDKs:
    • ipp-v3-java-devkit (6.7.0)
    • oauth2-platform-api (6.7.0)
    • ipp-v3-java-data (6.7.0)
  • Jackson Databind (JSON)
  • Spring Session Core (for session management)

Testing

  • Spring Boot Starter Test (JUnit 5/Jupiter, AssertJ, Mockito)
  • Spring Test (MockMvc, MockRestServiceServer)

Project Structure

SampleApp-SalesTax-Java/
├── src/main/java/com/quickbooks/demo/
│   ├── Application.java
│   ├── config/
│   ├── controller/
│   └── service/
├── src/main/resources/
│   ├── application.yml
│   ├── graphql/
│   │   ├── sales_tax.graphql
│   │   └── graphql_variables.json
│   ├── static/
│   └── templates/
└── README.md

Required OAuth Scopes

  • com.intuit.quickbooks.accounting — access accounting data (customers, items)
  • indirect-tax.tax-calculation.quickbooks — Sales Tax GraphQL access

Sales tax calculation behavior (nexus)

  • QuickBooks Automated Sales Tax (AST) is nexus-driven. Tax is charged only when your company has nexus (registration) in the ship-to state.
  • The API accepts any US ZIP, but without nexus for that destination you will typically get $0 tax (and in some environments you may see an error).
  • For non-zero tax: enable Sales Tax in QBO, add nexus for destination states, use a taxable item, and provide a valid US ship-to address/ZIP in a nexus state.

Automated Sales Tax (AST) setup (production companies)

AST is US-only and supported in production. To get non-zero tax values:

  1. Enable Sales Tax in QBO
  • Taxes → Sales tax → Set up sales tax
  1. Add nexus (registration) for destination states
  • Taxes → Sales tax → Sales tax settings → Add state
  • Pick the state, set effective date, filing frequency, agency
  1. Create a taxable item
  • Sales → Products and services → New → mark “Is taxable”
  1. Ensure a customer exists with a US address

  2. Re-authenticate if necessary (scopes already include indirect-tax.tax-calculation.quickbooks)

Testing note: The ship-to ZIP must be in a state where you have nexus. Example: if you added nexus for CA, test with 94043 (Mountain View, CA); for UT, test with 84041 (Layton, UT).

Setup

  1. Clone the repository
git clone <repository-url>
cd SampleApp-SalesTax-Java
  1. Start ngrok
ngrok http 8080
  1. Configure your QuickBooks app
  • Visit the Intuit Developer Portal (https://developer.intuit.com/app/developer/myapps)
  • Create an app (or use an existing one)
  • Enable the scopes above
  • Add your redirect URI (e.g., https://your-ngrok-url.ngrok-free.app/callback)
  1. Configure application settings

Option A: Use environment variables

export QB_CLIENT_ID=your_client_id
export QB_CLIENT_SECRET=your_client_secret
export QB_REDIRECT_URI=https://your-ngrok-url.ngrok-free.app/callback
export QB_ENVIRONMENT=production   # or sandbox
export QB_GRAPHQL_URL=https://qb.api.intuit.com/graphql
export QB_BASE_URL=https://quickbooks.api.intuit.com
export QB_MINOR_VERSION=75
export QB_SCOPES="com.intuit.quickbooks.accounting,indirect-tax.tax-calculation.quickbooks"

Option B: Use application.yml (create at src/main/resources/application.yml)

server:
  port: 8080

spring:
  application:
    name: quickbooks-sdk-demo
  thymeleaf:
    cache: false
    
quickbooks:
  # INSTRUCTIONS: Copy this block into application.yml and replace with your credentials
  client-id: ${QB_CLIENT_ID:your_quickbooks_client_id_here}
  client-secret: ${QB_CLIENT_SECRET:your_quickbooks_client_secret_here}
  environment: ${QB_ENVIRONMENT:production}  # Use 'sandbox' for development, 'production' for live
  redirect-uri: ${QB_REDIRECT_URI:https://your-ngrok-url.ngrok-free.app/callback}
  base-url: ${QB_BASE_URL:https://quickbooks.api.intuit.com}  # If using sandbox, set to https://sandbox-quickbooks.api.intuit.com
  graphql-url: ${QB_GRAPHQL_URL:https://qb.api.intuit.com/graphql}
  minor-version: ${QB_MINOR_VERSION:75}
  deep-link-template: ${QB_DEEP_LINK_TEMPLATE:https://app.qbo.intuit.com/app/invoice?txnId=%s}  # sandbox: https://app.sandbox.qbo.intuit.com/app/invoice?txnId=%s
  scopes:
    - com.intuit.quickbooks.accounting
    - project-management.project

logging:
  level:
    com.quickbooks.demo: DEBUG
    org.springframework.graphql: DEBUG

Tip: Use the config template safely

  • Copy the template to your local config file:
    • macOS/Linux: cp src/main/resources/application.yml.template src/main/resources/application.yml
    • Windows (PowerShell): Copy-Item src/main\resources\application.yml.template src/main\resources\application.yml
  • Edit application.yml with your real credentials and URLs.
  • src/main/resources/application.yml is in .gitignore, so your secrets won’t be committed. Keep the .template file in git as a reference only.
  1. Run the application
./gradlew bootRun

Usage

  1. Visit http://localhost:8080
  2. Click "Connect to QuickBooks" to authenticate (redirects to /connect)
  3. After auth, the home page loads Customers and Items from your QBO company
  4. Enter ship-from/to ZIPs, number of units, and unit value (or extend to full free-form addresses)
  5. Click "Calculate Tax" to execute the Sales Tax GraphQL mutation and view the result
  6. Optional: visit /debug/config to verify environment, endpoints, and scopes

Screenshots (expected flow)

Step 1 - OAuth Successful

Step 2 - Sales Tax Calculated

Common Issues

  1. Insufficient scope (403)
  • Ensure both scopes are configured and granted during auth
  • If you change scopes, re-authenticate to obtain a new token
  1. Invalid client or redirect URI
  • Verify Client ID/Secret
  • Ensure redirect-uri exactly matches the Developer Portal configuration
  1. Invalid redirect_uri query parameter
  • The QuickBooks OAuth page shows: "The redirect_uri query parameter value is invalid".
  • Fix by making the values match EXACTLY (scheme, host, path) between:
    • Your Intuit Developer Portal app: Keys & OAuth → Redirect URIs
    • Your app config: src/main/resources/application.ymlquickbooks.redirect-uri
  • Tips:
    • Use HTTPS (required for production); ngrok URL must match the registered value.
    • Include the path (typically /callback). No trailing slashes unless registered that way.
    • After changing the redirect URI in either place, restart the app and redo the OAuth flow.
  1. Environment mismatch
  1. GraphQL limitations (e.g., error -37109)
  • Ensure Sales Tax is enabled for the connected company
  • Use valid QuickBooks customer IDs and product/variant IDs
  • Provide full free-form addresses if ZIP-only returns errors
  1. First-time "double connect" (only on initial login)
  • Cause: If you load the UI on one host (e.g., http://localhost:8080) but your OAuth redirect-uri is on another (e.g., https://<ngrok>.ngrok-free.app/callback), the OAuth callback writes tokens to a different session (ngrok domain), while your visible page is on localhost. The page won’t see the token until you reconnect on the same host.
  • Symptoms: First connect appears successful in logs, but the home page still shows "Connect to QuickBooks". A second click works, and future connects are fine.
  • Fixes:
    • Open the app using the same host as your redirect-uri (visit https://<ngrok>.ngrok-free.app/ instead of http://localhost:8080).
    • Or change quickbooks.redirect-uri to match the host you actually use, and update the same value in the Intuit Developer Portal.
  • Optional hardening (behind proxies):
    • In application.yml, consider:
server:
  forward-headers-strategy: native
  servlet:
    session:
      cookie:
        same-site: Lax
        secure: true
  • Store a random OAuth state in the session on /connect and validate it in /callback.
  • Remove prompt=consent in QuickBooksOAuthService#getAuthorizationUrl if you don’t need forced re-consent.

License

This project is licensed under the Apache License 2.0 — see LICENSE.

Support

For support, visit the Intuit Developer Community (https://help.developer.intuit.com/s/) or open an issue in this repository.

About

Sample Java application for QuickBooks Sales Tax API demonstrating external invoicing workflows with GraphQL-based tax calculation

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published