# Web Integration

TIP

The anybill web SDK is based on async/await syntax (opens new window). Although it is technically possible to use Promise based syntax we recommend the async/await syntax in conjunction with try/catch.

# Getting Started

# Resolving the SDK

The anybill SDK for web is hosted in a private npm registry (anybill-npm) that has also access to the public npm registry. This means there is no need to install peer dependecies as they get resolved in the registry itself.
That said here is how to resolve the SDK with scoped access from the anybill-npm registry:

  1. Add a .npmrc file to the root directory of your project containing the following:
/anybill.jfrog.io/artifactory/api/npm/anybill_web_sdk/:_authToken = <AUTH_TOKEN>
email = youremail@email.com
always-auth = true
@anybill:registry = https://anybill.jfrog.io/artifactory/api/npm/anybill_web_sdk/

SECRET

This file should not be checked into version control!

  1. Now you can install modules via:

npm:

npm install @anybill/base

yarn:

yarn add @anybill/base

# Error handling

The anybill web SDK aims to provider the developer with easily understandable and catchable errors. The following two paragraphs introduce the used error model and how to work with it.

# Error object

The error object is based on the internal Error object (opens new window). It has the following structure:


class AnybillError extends Error {
    status: number;
    timestamp: string;
    path?: string;
    details?: any;
}

/// Example error handling
async function getQRData() {
  try {
    const res = await AuthProvider.instance.getQRCodeData();
    /// Present qr code to user
  } catch (ex) {
    const error = ex as AnybillError;
    switch (error.status) {
      case 400:
        /// handle bad request error
        break;
      case 401:
        /// refetch credentials
        break;
      case 500:
        /// handle server error
        break;
    }
  }
}

All errors thrown by the Anybill SDK methods are of type AnybillError, making them easily parsable. The simplest way to handle specific errors is by checking the status property, which reflects the API response code or 0 if the error occurs before a request is sent. For additional context on the error, refer to the details parameter.

# Usage

# Initialization

To initialize the SDK with your clientId you must call the initialize method from the ApiConfig class. Furthermore this method allows you to change the api environment for testing puposes.

import { ApiConfig } from "@anybill/base";

  ApiConfig.initialize(
      "your-client_id",
      ApiEnvironment.TEST_API_URL
    );

# Authentication

To authenticate the anybill SDK you can link an anybill user to your user account by using the linked token user. For detailed instructions, refer to the Partner Platform API documentation.

  • Get a token from the anybill Partner API by linking you account system to an anybill id.
  • Login with the anybill sdk

import { AuthProvider } from "@anybill/base";

try {
  await AuthProvider.instance.loginTokenUser(accessToken, refreshToken);
}  catch (ex) {
    const error = ex as AnybillError;
    switch (error.status) {
      case 400:
        /// handle bad request error
        break;
      case 401:
        /// refetch credentials
        break;
      case 500:
        /// handle server error
        break;
    }
}
  

TIP

When using the Token Based Login you'll have to check for a failing Refresh Token Call on the first anybill API Call you invoke by catching a 401 error. When the error is triggered you'll have to retrieve new authentication information from the anybill Partner Platform API.

Re Auth Sdk

# Getting user information

After logging in an anybill user with the received token, user information can be acquired using following methods:

import { AuthProvider } from "@anybill/auth";

// User Model including id (anybill Id), externalId (usually your id) and more
const user = await AuthProvider.instance.getUserData();

// Data object which can be displayed as QR Code to receive receipts by scanning of the users device
const userQRCodeData = await AuthProvider.instance.getQRCodeData();

# Retrieving receipts

The anybill SDK offers two distinct approaches for fetching user receipts:

  1. Direct API Access: Use the ReceiptProvider.getReceipts() method to directly access the API. This approach allows you to implement custom fetching and pagination logic based on your specific requirements.

  2. Optimized SDK Caching Process: Leverage the SDK's built-in caching and optimized pagination for efficient receipt retrieval by using the initiateReceiptQuery() and continueReceiptQuery() methods. This approach simplifies the retrieval process and reduces the need for manual pagination handling.

Detailed information about both approaches is provided below:

Direct API Access

The getReceipts() method allows you to retrieve user receipts with pagination support. The result includes the receipts, the total count of available receipts, and a continuation token that can be used to fetch subsequent batches.

You can customize the request with the following parameters:

  • take: Specifies the number of receipts to fetch in each batch. The default and maximum value is 100.

  • continuationToken: A nullable token used for paginating through the query results. If null, a new query is initiated. To continue fetching from the previous result, use the continuationToken provided in the last response.

  • orderBy: Specifies the field used for ordering the receipts. Currently, only Date is available.

  • orderDirection: Defines the sort direction for the receipts, either Ascending or Descending.

TIP

Important:

Due to database restrictions, you must specify the orderBy and orderDirection parameters for every page of the query.

Also, remember to reset the continuationToken if you modify any query parameters.


   try {
    const continuationList = await ReceiptProvider.instance.getReceipts({
      take: 100,
      orderBy: UserReceiptOrderByFieldDto.Date,
      orderDirection: OrderByDirectionDto.Descending,
    });
    /// handle receipts: continuationList.receipts
    /// save continuation token to fetch the next batch of receitps: continuationList.continuationToken;
  } catch (ex) {
    /// handle error
  }

Optimized SDK Caching Process

The anybill SDK offers an optimized receipt pagination process, providing efficient querying and display of receipts.

Similar to direct API access, you can customize the query with the following parameters:

  • take: Specifies the number of receipts to retrieve in each batch, with a maximum of 100 by default.
  • orderBy: Specifies the field for ordering receipts. Currently, only Date is supported.
  • orderDirection: Defines the sort direction for receipts, either Ascending or Descending.

TIP

Note on Pagination and Sorting

The continuation token and query management are handled internally by the SDK, so there is no need for manual handling to load additional pages.

If you wish to change the sorting or direction of the receipt list, use the initiateReceiptQuery() method again.

Fetch first page / Update receipt list

The initiateReceiptQuery method resets any existing query. We recommend calling this method in the following scenarios to ensure an up-to-date receipt list:

  • Initial Display of Receipt List
    When the receipt list is displayed for the first time in a session (e.g., when the user navigates to the receipt list view), this method should be called to display the latest receipt data.

  • New Receipt Received
    If a new receipt is issued while the user is in the app, the list should be refreshed upon notification of the new receipt (e.g., triggered by a webhook event). This ensures the receipt list reflects the latest transactions.

  • Manual User Update
    For scenarios where a user manually refreshes the list, such as through a "pull-to-refresh" gesture or a refresh button, use this method to re-fetch the latest data for the first page.

  • Change in Sort Order
    When changing the sorting parameters of the receipt list (e.g., switching the sort order), call this method with the new parameters. This will reset the cache to reflect the updated sorting criteria.


   try {
    const continuationList = await ReceiptProvider.instance.initiateReceiptQuery({
      take: 100,
      orderBy: UserReceiptOrderByFieldDto.Date,
      orderDirection: OrderByDirectionDto.Descending,
    });
    /// handle receipts: continuationList.receipts
  } catch (ex) {
    /// handle error
  }

Fetch next page

To retrieve the next batch of receipts in the existing query, use continueReceiptQuery(). This method automatically applies the previously retrieved continuation token to fetch the subsequent set of receipts.


   try {
    const continuationList = await ReceiptProvider.instance.continueReceiptQuery();
    /// handle receipts: continuationList.receipts
  } catch (ex) {
    /// handle error
  }

Querying for single receipts by id can be achieved with the getReceiptByID method of the ReceiptProvider

import { ReceiptsProvider } from "@anybill/base";

const singleReceipt = await ReceiptProvider.instance
    .getReceiptByID("receipt-id");

# Downloading user receipts

The Receipt utility has different methods of providing a PDF version of a receipt.
Following three functions are available and should be used according to your project setup:

import { ReceiptProvider } from "@anybill/base";

// best for client side use
const objectURL = await ReceiptProvider.instance
    .getReceiptPDFasObjectURL(receiptId);

// flexible usage
const blob = await ReceiptProvider.instance
    .getReceiptPDFasBlob(receiptId);

// best for server side use
const file = await ReceiptProvider.instance
    .getReceiptPDFasFile(receiptId);

ObjectURL memory

ObjectURLs generated with getReceiptPDFasObjectURL are not automagically revoked!
To do so use URL.revokeObjectURL (opens new window) after using the URL.

File name

The File returned by getReceiptPDFasFile has a name with template:

`receipt-export_${new Date().toLocaleDateString()}.pdf`

# Deleting users receipts

Using the ReceiptProvider utility you can delete either a single receipt or multiple receipts at once by calling following methods with the corresponding receiptIds.

import { ReceiptProvider } from "@anybill/base";

// Delete a single receipt
await ReceiptProvider.instance.deleteReceipt(receiptId);

// Delete multiple receipts at once
await ReceiptProvider.instance.deleteReceipts([receiptId1, receiptId2]);

# Mark a receipt as favourite

User receipts can be marked as favourite using following function:

import { ReceiptProvider } from "@anybill/base";

// Toggle  isFavourite flag
await ReceiptProvider.instance.toggleIsFavourite(receiptId);

Receipt Model

Boolean value ReceiptDto.misc.isFavourite determines whether a receipt is marked as favourite.

# Update receipt note

Using the ReceiptProvider.updateReceiptNote() method, a custom note can be set for a receipt which can be retrieved in the ReceiptDto.Misc.Note field. This field can later on be used for querying and filtering the receipt list.

import { ReceiptProvider } from "@anybill/base";

// Update note flag
await ReceiptProvider.instance.updateReceiptNote(
      receiptId,
      note
    );