Introduction
Quaderno is a SaaS app and set of APIs to automate and improve tax management in businesses of all sizes. Here are some resources that will help you understand the basics of all of them.
The documentation is organized into these major areas:
- Quaderno Sandbox is a safe space to experiment, test new features, and break things while learning about our Quaderno products.
- Quaderno.js is a handy small javascript library to calculate sales taxes in your own checkout form, directly from your frontend.
- Quaderno Checkout is a checkout form that you can use within your site to sell your own products and subscriptions.
- Quaderno Apps allows you to create new partner integrations based on OAuth2 and thus removing the need of using private keys.
- Quaderno API ↗ are a set of APIs that provides your business with billing, reporting and tax management capabilities. With our APIs you can calculate taxes during checkout, validate tax IDs or manage location evidences. You can issue and manage invoices and credit notes or log your expenses, as well as asynchronously get all your tax and business reports programmatically.
- Quaderno Connect API ↗ is our API to provide marketplaces with tax capabilities to their business models.
Not a developer?
A custom Quaderno integration requires you to either have basic coding skills or hire a developer. But we made Quaderno really easy to connect with many third-party platforms without coding experience at all. Check our documentation on one-click integrations, such as for Shopify, Amazon FBA, and WooCommerce.
Need more help?
Just send us an email. Please include your code or the API request and response when possible. We’re always happy to help out with code or any other questions you might have.
Quaderno Sandbox
Quaderno Sandbox is a safe space to experiment with our set of APIs and products, testing new features or fine-tunning your Quaderno workflows and integrations, without the worries of breaking things while learning.
Quaderno Sandbox has feature parity with the real live environment plus all the new features, testable in advance but without the fuss of dealing with real user data! It's perfect for connecting your payment's processor testing accounts, knowing all will behave just as fine on the live production servers.
Quaderno Sandbox is a separate environment, with it's own URL https://sandbox-quadernoapp.com
and credentials. This means your credentials for the Production environment won't work on Quaderno Sandbox. You'll need to create a new Quaderno Sandbox account.
- Emails are delivered to the account email rather than to the specified recipient.
- Tax IDs always return
{ "valid": true }
except for:- DE111111111: acts as an invalid Tax ID, returning
{ "valid": false }
- IE222222222: acts as if the Tax Validator service is down, returning
{ "valid": null }
- DE111111111: acts as an invalid Tax ID, returning
Purge your Quaderno Sandbox documents with:
curl --user sk_your-secret-key:x \
--request POST \
--url http://your-url.sandbox-quadernoapp.com/api/purge
- The Taxes API only returns state tax rates. Local tax rates will not be returned, with the exception of
90049
,10128
,60611
, and33132
. Tax thresholds are not updated.
There's a global 200 documents cap. If you need to create more you'll have to remove your previous documents first, by navigating to You > Purge into the app, or using the
/purge
endpoint as shown on the right panel.
Quaderno.js
Quaderno.js is a handy small javascript library to calculate taxes directly from your frontend. It fits great with our own checkout experience, Quaderno Checkout, as well as with your own forms, to calculate taxes in real time.
To unleash all the power of our server-side APIs like Tax calculations and Tax ID validations, Billing and Tax reporting and tracking, go check our API.
Including Quaderno.js
<script src="https://js.quaderno.io/v4/"></script>
However you’re using Quaderno.js, you always begin by including the library and setting your public API key*.
To get started, include this script on your pages — it should always be loaded directly from https://js.quaderno.io
While the private API key is meant for being used server-side, secured in your backend, the public API key is only used by Quaderno.js, and only allow access to our tax calculations engine. No data can be leaked from your account with the public API key.
Supported browsers
Quaderno.js strives to support all recent versions of major browsers. For the sake of security and providing the best experience to the majority of customers, we do not support browsers that are no longer receiving security updates and represent a small minority of traffic.
- We support Internet Explorer and Edge per Microsoft's lifecycle policy.
- We support Chrome and Safari on all platforms and Firefox on desktop platforms.
- We support the Android native browser on Android 4.4 and later.
- We respond to bug reports but do not proactively test other mobile browsers.
If you have an issue with Quaderno.js on a specific browser, please contact us so we can improve its support.
Quaderno.js methods
When you include the Quaderno.js script in your page, a global Quaderno
object will be available with the following methods:
init( publishableKey )
// never expose your private API key on your frontend, use the public API key instead
Quaderno.init(publishableKey).then(function(){
console.log('Success');
}).catch(function(error){
console.log(error.description, error.messages);
});
Async request. Use this method to initialize the global object Quaderno
.
This method returns a Promise
which resolves if the publishableKey verification was successful. If there's any issue, this method returns a Promise
which rejects with an error
object. This object has either:
- error.description: A full description of the error.
- error.messages: If present, it indicates the affected attribute and an specific error (ie.
{ email: "invalid", businessNumber: "invalid" }
).
bindForm( formId )
// to use along with the deferred automatic init
Quaderno.bindForm("#formId").then(function(){
console.log('Success');
}).catch(function(error){
console.log(error.description, error.messages);
});
Async request. This method also initializes the global object Quaderno
from a form object.
This method returns a Promise
which resolves if the publishableKey verification was successful. If there's any issue, this method returns a Promise
which rejects with an error
object. This object has either:
- error.description: A full description of the error.
- error.messages: A brief description if each invalid attribute.
calculateTaxes( options )
// use `options` along with Custom init
var options = { taxClass: 'eservice', postalCode: '123456', city: 'Dublin', country: 'IE' }
Quaderno.calculateTaxes(options).then(function(taxObject){
console.log(taxObject.name, taxObject.rate, taxObject.country); // ie. "VAT", 23, "IE"
})
Async request. Calculates the taxes based on the billing data from options
object
Available options are documented below:
Attribute | Mandatory | Description |
---|---|---|
taxClass |
No | Indicates the tax class of the transaction. |
postalCode |
No | ZIP or postal code. |
city |
No | The customer's city. Used to make more accurate calculations based on the postal code |
country |
No | ISO 3166-1 alpha-2 |
businessNumber |
No | A valid business number. Quaderno validates EU VAT numbers, ABN, and NZBN. |
<!-- no need to fill the `options` of calculateTaxes() with automatic inits -->
<form id="quaderno-payment-form" data-publishable-key="pk_xxxxxxxx">
<select data-quaderno="country"> … </select>
<input data-quaderno="postalCode" />
...
</form>
This method returns a Promise
which resolves with a tax object.
Note that when you calculate taxes using our Tax Rates API, parameters follow a slighty different naming scheme:
Quaderno.js | Tax Rates API |
---|---|
taxClass |
tax_code |
businessNumber |
tax_id |
taxType |
tax_behavior |
calculatePrice([options])
options = { amount: 100, quantity: 2 }
priceObject = Quaderno.calculatePrice(options);
priceObject.subtotal;
priceObject.taxes;
Helper method which calculates the final price of the product, tax inclusive.
Available options are documented below:
Attribute | Mandatory | Description |
---|---|---|
amount |
No | Amount you are going to charge your customer, in cents. |
quantity |
No | Quantity of the products. |
taxType |
No | If the tax is included or excluded |
The options
can be omitted if you've set a form using bindForm
with its proper data-quaderno
attributes, or if you've retrieved the product information from Quaderno with getProduct
This method returns a price
object with the following attributes:
subtotal
: the price without taxes and discounts.taxes
: the taxes amount.total
: the final amount.discountedAmount
: The discount amount.
destroy()
Quaderno.destroy();
Remove event listeners from the DOM elements. It might be useful if you have single-page apps and have tied the Quaderno object to a form via bindForm
or the simple initialization.
getProduct( productId )
Quaderno.getProduct("productId").then(function(product){
console.log(product.name, product.unitCost, product.code);
});
Async request. Retrieve the product info identified by productId
. Returns a product
object in the resolve function.
You can register your products manually in quadernoapp.com/products or programatically with our API.
getCoupon( couponCode )
Quaderno.getCoupon("couponCode").then(function(product){
console.log(coupon.amountOff);
});
Async request. Retrieve the product info identified by couponCode
. Returns a coupon
object in the resolve function.
You can register your products manually in quadernoapp.com/checkout/coupons or programatically with our API.
getLastTax()
taxes = Quaderno.getLastTax();
taxes.name;
taxes.rate;
taxes.country;
Reads the last calculated tax and returns a tax
object.
validateBusinessNumber(businessNumber, country)
Quaderno.validateBusinessNumber("GB1234567", "GB").then(function(response){
console.log(response.valid);
}).catch(function(error){
console.log(error.descriptions);
});
Async request. This method validates the customer's business number. Currently Quaderno validates EU VAT numbers, ABN, and NZBN.
Please keep in mind that business numbers are not actually validated when using Quaderno Sandbox.
This method returns a result
object with the following attribute:
- result.valid: true (valid business number), false (invalid business number) or null (the external validation service is temporarily unavailable).
Quaderno.js objects
This section documents the objects you'll receive from Quaderno.js.
The Tax object
This object is passed by the resolve function of the Promise
returned by the Quaderno.calculateTaxes
method or by invoking Quaderno.getLastTax
.
Attribute | Description |
---|---|
country |
A valid ISO 3166-1 alpha-2 code. |
region |
State/Region/Province. |
county |
County (only for US sales tax). |
city |
City (only for US sales tax) |
countyTaxCode |
County tax code (if applicable on US sales taxes) |
cityTaxCode |
City tax code (if applicable on US sales taxes) |
name |
Name of the tax. |
rate |
Tax rate in percentage (6.5). |
extraName |
Name of the extra tax. |
extraRate |
Tax rate in percentage of the extra tax. |
taxClass |
The tax class of the transaction. Can be one of the following: standard , eservice , ebook or saas . |
businessNumberValid |
This attribute is only present if you've set a businessNumber during the calculation request. It can be true , null or false . |
The Product object
This object contains the relevant information related to the productID you've set. It's returned by the resolve function of Quaderno.getProduct
.
Attribute | Description |
---|---|
code |
The product code in Quaderno. |
name |
The name of the product. |
image |
URL to the image of the product. |
unitCost |
Unit price of the product. |
currency |
A valid ISO-4271 code. |
kind |
Indicates if the product is either a subscription or a one-off sale. |
taxClass |
The tax class for the product. Can be one of the following: standard , eservice , ebook or saas . |
taxType |
Indicates if the tax is included or excluded from the product price. |
The Coupon object
This object contains the relevant information related to the couponCode you've set. It's returned by the resolve function of Quaderno.getCoupon
.
Attribute | Description |
---|---|
code |
The product code in Quaderno. |
kind |
Indicates if the coupon is percent_off or amount_off |
amountOff |
The amount off. |
currency |
The currency of the amount off. |
percentOff |
The percent off. |
redeemable |
Indicates if the coupon is redeemable |
Initializing Quaderno.js
For your convenience, there are a few different ways to initialize Quaderno.js.
You can avoid this behavior using the custom init method.
Simple automatic init
<head>
<script src="https://js.quaderno.io/v4/"></script>
</head>
<body>
<form id="quaderno-payment-form" data-publishable-key="pk_xxxxxxxx">
<select data-quaderno="country"> … </select>
<input data-quaderno="postalCode" />
...
</form>
</body>
Automatically loads quaderno.js if the form has the quaderno-payment-form
id and a valid data-publishable-key
. Optionally you can also set a data-product-id
.
- data-publishable-key: Your Quaderno public key. Remember to never expose your private API keys.
The input fields will be automatically detected as long as they have the data-quaderno
with their definition:
- postalCode (highly recommendable)
- country (mandatory)
- businessNumber
- city
- productId
- amount
- quantity
- taxType
- taxClass
- productId
- couponCode
Deferred automatic init
<head>
<script src="https://js.quaderno.io/v4/"></script>
</head>
<body>
<form id="payment-form" data-publishable-key="pk_xxxxxxxx" data-product-id="prod_xxxxxxxx">
<select data-quaderno="country"> … </select>
<input data-quaderno="postalCode" />
<!-- … -->
</form>
<script>
Quaderno.bindForm('#payment-form').then(function(response) {
console.log(response.product);
}).catch(function(error){
console.log(error.description, error.messages);
});
</script>
</body>
If you don't want the form to load automatically, you can load it later by not using the "quaderno-payment-form" id, and then call the bindForm
method whenever you want.
Custom init
<label for="country">
<span>Country</span>
<input id="country" name="country" value="ES" />
</label>
<label for="postal-code">
<span>Postal code</span>
<input id="postal-code" name="postal-code" value="35007" />
</label>
<script>
Quaderno.init(publishableKey).then(function() {
console.log('Quaderno.js successfully initialized');
}).catch(function(error) {
console.log(error.description, error.messages);
});
</script>
Futhermore, you can also dissociate Quaderno from the DOM by setting the Quaderno object properties by yourself.
This means you need to explicitly set your publishableKey
as argument for the init
call, instead of using the data-publishable-key
html attribute in your form
.
Afterwards, you'll need to specify all options
for calculateTaxes()
.
Calculating taxes from your frontend with Quaderno.js
In this example, we are going to handle UI updates on the relevant fields to force tax calculations and show it in a
<span>
. We'll be validating Tax IDs (business numbers) too when given one. We'll use the custom init method in this example.
<form>
<label for="country">
Country
<select id="country">
<option value="US" selected>United States</option>
<option value="GB">United Kingdom</option>
<option value="DE">Germany</option>
<option value="FR">France</option>
<option value="IT">Italy</option>
<option value="ES">Spain</option>
<option value="CA">Canada</option>
<option value="AU">Australia</option>
</select>
</label>
<label for="postal-code">
Postal code
<input id="postal-code">
</label>
<label for="business-number">
Business number:
<input id="business-number">
</label>
<label for="tax-class">
Tax Code:
<select id="tax-class">
<option value="standard">standard</option>
<option value="eservice">eservice</option>
<option value="ebook">ebook</option>
<option value="saas">saas</option>
</select>
</label>
</form>
Taxes: <span id="tax-rate"></span>
<script>
// never expose your private API key on your frontend, use the public API key instead
let publishableKey = "pk_test_xxxxxxxxxxxxxxxxxxxx";
// we're not attaching Quaderno.js to our form, so we need to use init:
Quaderno.init(publishableKey).then(function(){
console.log('Quaderno.js successfully initialized');
}).catch(function(error){
console.log(error.description, error.messages);
});
// this is a custom function to be called each time the user introduces new data
function reloadTaxes(options){
console.log("Calling calculateTaxes with params", options)
Quaderno.calculateTaxes(options).then(function(taxObject){
// Remember, you need to be registered on the tax jurisdiction to get the tax rate
console.log("Taxes calculated:", taxObject)
document.querySelector('#tax-rate').innerHTML = taxObject.name + " " + taxObject.rate + "%"; // ie.: VAT 20%
})
}
// Handle change events on the relevant user-inputs so that the Tax Rate it's automatically updated
document.querySelector('#country').addEventListener('change', function () {
country = this.value
postalCode = document.querySelector('#postal-code').value
businessNumber = document.querySelector('#business-number').value
reloadTaxes({ country: country, postalCode: postalCode, businessNumber: businessNumber})
})
document.querySelector('#postal-code').addEventListener('change', function () {
postalCode = this.value
country = document.querySelector('#country').value
businessNumber = document.querySelector('#business-number').value
reloadTaxes({ country: country, postalCode: postalCode, businessNumber: businessNumber })
})
document.querySelector('#business-number').addEventListener('change', function () {
businessNumber = this.value
country = document.querySelector('#country').value
postalCode = document.querySelector('#postal-code').value
// We're also validating Tax ID (business number) when given one
Quaderno.validateBusinessNumber(businessNumber, country).then(function(){
reloadTaxes({ businessNumber: businessNumber, country: country, postalCode: postalCode })
})
})
document.querySelector('#tax-class').addEventListener('change', function () {
taxClass = this.value
country = document.querySelector('#country').value
postalCode = document.querySelector('#postal-code').value
businessNumber = document.querySelector('#business-number').value
reloadTaxes({ taxClass: taxClass, country: country, postalCode: postalCode, businessNumber: businessNumber })
})
</script>
The more common use of Quaderno.js is to calculate taxes by invoking the method Quaderno.calculateTaxes
.
In the example on the right, we're showing a form with different countries and tax codes (called taxClass
in Quaderno.js). Instead of using the super-simple approach of binding to your form by specifying a quaderno-payment-form
id and a valid data-publishable-key
, we're first initializing Quaderno.js with our public API key using the custom init method, and then collecting the data that will form our options
for calculateTaxes(options)
manually, with document.querySelector('#value-id')
, anytime these receive a change
event from the DOM.
The result from calculateTaxes(options)
is then painted on the UI. We're also validating the Tax ID (business number) with validateBusinessNumber
when given one.
On this example, when selecting "United States" and introducing "7501" as postal code, the
console
prints:
Calling calculateTaxes with params:
{
"country": "US",
"postalCode": "7501",
"businessNumber": ""
}
Taxes calculated:
{
country: "US"
extraName: null
extraRate: null
name: "Sales tax"
rate: 6.25
region: "TX"
}
Note that we're not calling the performant getLastTax()
method because in this case the data changes everytime. If you already got your tax rate and need that data again later in your checkout process, you may want to call calculateTaxes(options)
once and after that rely on getLastTax()
.
Remember that Quaderno.js will only return tax rates for those tax jurisdictions where you are registered. Follow the links to learn more on tax jurisdictions and setting up tax jurisdictions.
You can check the code to the right live in this Codepen:
Checkout
Quaderno Checkout is a checkout form that you can use to sell your digital products and subscriptions from any website, email or message.
A Checkout Session represents your customer’s session as they pay for one-off purchases or subscriptions through Checkout.
You can create simple sessions by using Checkout Links.
But if you want to fine tune the Checkout experience for a particular customer or create sessions on-the-fly, you can also create a Checkout Session directly. You can find more information about how to manage the Sessions in our API docs
How to create a Checkout Session and fulfill the order
In this example, you will learn how to create a Checkout Session for your customer and how to asynchrnonously identify the transaction once it has been completed by using the checkout.succeeded
event.
Part 1: Creating the Session:
First you need to dynamically create a Checkout Session for your customer (more details here)
curl -u sk_test123123123123123:x \
-H 'Content-Type: application/json' \
-X POST \
-d '{"billing_details_collection":"auto","cancel_url":"string","coupon_collection":true,"customer":{"first_name":"John","last_name":"Doe","email":"john@doe.com"},"items":[{"product":"prod_61ffa845b4a0b8"}]}' \
'https://demo-account.quadernoapp.com/api/checkout/sessions.json'
This will return a new session like the following:
{
"id":1,
"status":"completed",
"billing_details_collection":"required",
"cancel_url":"http://go.back.com",
"coupon_collection":true,
"locale":"auto",
"payment_methods":["card", "paypal"],
"success_url":"http://success.com?prod=1",
"custom":{},
"items":[
{
"product":"prod_61ffa845b4a0b8",
"amount":999.0,
"name":"A test item",
"description":"This a test item.",
"currency":"EUR",
"quantity":1
}
],
"customer":{
"billing_city":null,
"billing_country":null,
"billing_postal_code":null,
"billing_street_line_1":null,
"billing_street_line_2":null,
"company":"",
"email":"john@doe.com",
"first_name":"John",
"last_name":"Doe",
"tax_id":null,
"business_number":null
},
"permalink":"https://demo-account.quadernoapp.com/checkout/session/8ccf3fdc42b85800188b113b81d3e4212ef094b3"
}
The session's permalink
(e.g https://demo-account.quadernoapp.com/checkout/session/xxxxx) is the URL your customer will use to complete their purchase.
Part 2: Fulfilling the order
Due to the SCA (Strong Customer Authentication) requirements, the only way to guarantee the transaction has succeeded is by checking it asynchrnously via webhooks.
Once your customer visits the permalink and completes the purchase, you need to set up some webhooks to track succeeded sessions.
Quaderno provides a checkout.succeeded
event to send you a notification every time a customer completes a purchase on the Checkout.
You can subscribe to Quaderno webhooks by using our API (more details here):
# body
'{
"url": "http://yourapp.com/notifications",
"events_types": ["checkout.succeed"]
}'
curl -u YOUR_API_KEY:x \
-H 'Content-Type: application/json' \
-X POST \
--data-binary @body.json \
'https://ACCOUNT_NAME.quadernoapp.com/api/webhooks.json'
or by using the web interface (in Developers -> Webhooks -> Create Webhook):
Once you've subscribed to the checkout.succeeded
event you need to create an endpoint in your app to handle the webhooks notifications.
This is a what a checkout.succeeded
notification should look like:
{
"event_type":"checkout.succeeded",
"data":{
"object":{
"message":{
"response_message":"Checkout succeeded",
"status_code":200
},
"transaction_details":{
"session":43,
"session_permalink":"https://demo.quadernoapp.com/checkout/session/8ccf3fdc42b85800188b113b81d3e4212ef094b3",
"gateway":"stripe",
"type":"charge",
"description":"Unicorn",
"customer":"cus_FXesSyaK3CG8Oz",
"email":"john@doe.com",
"transaction":"pi_1F2ZYtEjVHvINKlcq2as8H5V",
"product_id":"prod_61ffa845b4a0b8",
"tax_name":"VAT",
"tax_rate":20.0,
"extra_tax_name":null,
"extra_tax_rate":null,
"iat":1564647784,
"amount_cents":1500,
"amount":"15.00",
"currency":"EUR"
},
"contact":{
"id":547540,
"kind":"company",
"first_name":"John ",
"last_name":"Doe",
"full_name":"John Doe",
"contact_name":null,
"street_line_1":"Fake Street 1",
"postal_code":"SW15 5PU",
"city":null,
"region":null,
"country":"GB",
"email":"john@doe.com",
"web":null,
"language":"EN",
"tax_id":null,
"vat_number":null
}
}
}
}
With the session
id, product_id
and the customer's email
you should be able to identify the customer and fulfill the order.
Apps
With the Quaderno API, it’s easy to build a partner integration to add tax management and billing features to your product.
For example, if you are a marketplace, you can integrate sales tax calculation using the Quaderno API so your users can calculate taxes automatically in their checkout forms depending on their customers' locations.
But it’s not just marketplaces that work great with Quaderno - we’ve seen partner integrations built with all kinds of software from tools to manage sport clubs to WordPress blogs.
Integrating with Quaderno has the power to add huge value for your users, streamlining their businesses processes and providing a great experience.
Every partner integration is different, so this document can’t tell you exactly what to do, but we’ll explain the flows you should handle, and give you some ideas of how to provide the best experience for your users.
Connecting your users' accounts
To calculate taxes or issue receipts for your users, you’ll need access to their Quaderno account so you can make API requests on their behalf.
Our OAuth flow allows you to securely obtain an access token for a Quaderno account without having to have your user’s password or do any manual setup.
There are four key benefits to getting access to your users’ accounts using OAuth:
- It’s secure: Your users have a secure way to grant you access to their account without needing to send you their login credentials or an access token. They only need to tell us that you may have access through a simple online process.
- It’s fast: You only need to generate and send your user to a link to our OAuth flow. From there, they’ll create a Quaderno account or sign in to their existing one, approve your access to their account, and then we’ll send them right back to you with an access token.
- It’s future-proof: As the Quaderno API gets better and better over time and offers new functionality, you’ll be able to stay up-to-date and use all the latest features
The process for your user looks like this:
- Your user clicks a button in your UI to start the process of connecting with Quaderno.
- You generate a link to the authorization page, embedding details of your app (which we’ll set up next) as well as a scope to specify the level of access you want your user to give you.
- You redirect your user to the generated link. Your user lands on our site, where they create a Quaderno account or log in to their existing one, and agree to grant your app access.
- We redirect your user’s browser back to you with a code query parameter.
- You exchange the code for a permanent access token, which you store so that you can use it to make API requests on your user’s behalf.
Creating an app
Before we can get started, you need to create an "app" which represents your partner integration in the Quaderno system. You’ll be issued with a client ID and secret, which you’ll use to identify yourself when sending users to the OAuth flow and swapping codes for access tokens.
First sign up for a Quaderno account in our sandbox testing environment and then create an app.
You’ll need to provide a name and the URL of your website (where users can go to read more about your product or your integration with Quaderno).
Once your user has completed the OAuth flow, they will be redirected to your application using the redirect_url
parameter you provide. For security reasons, this can’t be just any internet URL, so you will also need to configure one callback URL here (which must exactly match the parameter).
Once you’ve created an app, you’ll see it in the list of your apps. Click on your newly-created app, and take a note of the Client ID and Client Secret.
Generating a link to the authorization page
First, you need to install an Oauth client library. For example, in Ruby you can use the oauth2 gem:
echo "gem 'oauth2'" >> Gemfile
bundle install
Once we’ve installed the library, we can build a link to the authorization page. We’ll have to pass in a number of parameters, including the client_id
and client_secret
, as well as a few parameters that will vary by library. These can include:
Option | Required | Description |
---|---|---|
redirect_uri | Yes | The URL to send your users to once they've agreed to connect their account to Quaderno (as well if they deny authorisation or something goes wrong, in which case we'll pass you details of the error). This must exactly match one of the callback URLs you specified above. |
scope | No | The level of access you want to your users' Quaderno accounts - this may either be read_write or read_only . Default is read_only . |
state | No | Any value you pass in here will be included as a querystring parameter when we redirect back to your redirect URL. Please note that this value can be tampered with by your user, and so shouldn't be trusted implicitly. We recommend using this parameter for a CSRF token. |
require 'oauth2'
# You should store your client ID and secret in environment variables rather than
# committing them with your code
oauth = OAuth2::Client.new(ENV['QUADERNO_CLIENT_ID'],
ENV['QUADERNO_CLIENT_SECRET'],
site: 'https://sandbox-quadernoapp.com',
authorize_url: '/oauth/authorize',
token_url: '/oauth/token')
authorize_url = oauth.auth_code.authorize_url(redirect_uri: 'https://yourapp.com/redirect',
scope: 'read_only')
redirect_to authorize_url
Run this code to generate a link to the OAuth flow. Head over to the link you’ve just generated in an Incognito window, and you’ll see our OAuth flow.
Getting an access token
On the signup form, we recommend creating a second sandbox account using a different email address. This will replicate what your users will experience when connecting to your partner integration, and will give you an example account which you can use from the perspective of one of your users.
Once your user has either signed up or logged in, and then approved your app’s access to their account, they’ll be sent to your app’s redirect URI with a temporary code which you’ll see in the query parameters, as well as any state you provided.
You should use the OAuth client library we set up earlier to fetch an access token using the code. This is a permanent access token which allows you to use the API on behalf of your merchant at any time.
require 'oauth2'
oauth = OAuth2::Client.new(ENV['QUADERNO_CLIENT_ID'],
ENV['QUADERNO_CLIENT_SECRET'],
site: 'https://sandbox-quadernoapp.com',
authorize_url: '/oauth/authorize',
token_url: '/oauth/token')
response = oauth.auth_code.get_token(
params[:code], # use exactly the same access token as you did in the last step
redirect_uri: 'https://yourapp.com/redirect'
)
# You should store the user's access token - you'll need it to make API requests on their
# behalf in future. If you want to handle webhooks for your users, you should also store
# their account ID which will allow you to identify webhooks belonging to them.
current_user.update!(quaderno_access_token: response.token,
quaderno_account_id: response['account_id'])
Whether you are developing a mobile, web, or desktop application, it is important not to pass the client secret to your user’s device as it could be used to impersonate your app. The process of exchanging a code for an access token should be done on your server so your client secret can be kept private.
You’ll want to store this access token in your database for use in the future to make requests to the Quaderno API on your user’s behalf. Make sure you keep it safe and secure, as it gives full access to your user’s account.
Revoking access
To disconnect an account from your platform, POST your client_id
, your client_token
, and the user's access token to /oauth/revoke
HTTParty.post('https:/sandbox-quadernoapp.com/oauth/revoke', body: {
client_id: ENV['QUADERNO_CLIENT_ID'],
client_secret: ENV['QUADERNO_CLIENT_SECRET'],
token: USER_TOKEN
})