< All Topics
Print

Extensibility, Development and APIs

Extensibility, Development and APIs

API Schema

Blockchain API

Ledger central offers a Codeunit API to expose blockchain related data and function for third party app through Business Central.

How to publish the Blockchain API form Business Central

  1. Navigate to the “Web Service” page
  2. Create a new line and with “Object Type” = Codeunit
  3. In Object ID, open the list and look for « Ledger Central API » object
  4. Fill in “Service Name”, for exemple : “LedgerCentralAPI”.

Build http web request

Follow Microsoft Guide « Interacting with OData unbound action http request » https://learn.microsoft.com/fr-fr/dynamics365/business-central/dev-itpro/developer/devenv-creating-and-interacting-with-odatav4-unbound-action#http-request

Using PostMan you can test the API with following URL

POST {Business-Central-URL}/ODataV4/{serviceName}_{procedureName}?company={companyName} HTTP/1.1
{requestBody}

{Business-Central-URL} : Business Central instance URL (including tenant id on cloud)
Exemple of cloud Business central api URL :
https://api.businesscentral.dynamics.com/v2.0/40287fcd-fa59-4fee-a9a5-aee76e3acc9f/LedgerCentral-DEMO/

{serviceName} : Defined above in step 4.
{procedureName} : Available procedures are listed and described below
{companyName} : Business Central Company name.
{requestBody} : JSON parameters, described under each functions below

Example :

POST https://api.businesscentral.dynamics.com/v2.0/tenantid/MySandbox/ODataV4
/LedgerCentralAPI_GetAccountMainBalance?company=CRONUS%20CH

{
   "blockchainNetwork": "Bitcoin",
    "blockchainPublicAddress": "bc1q8tung..."
}

The parameters can be passed in the Body as Json as above, with header content-type = application/json.
Note that parameters name must alway start with lowercase letter.

Date Format : date passer to Business Central API shall be in xml format : YYYY-MM-DD
Decimals : decimal value shall be separated using dot
Texte/Code : shall be passed inside double quotes

If you’re having any struggle to build the http request, you can refer to the web service definition from Business Central. On the « web service » page use the action « Download Metadata Document » to have the full XML list of avialable function and parameters.

Web service response

The response is JSON formated, include a « value » json object, depending the function called return type. It can be either text, decimal, integer, or even encoded Json text.

Response with single decimal value :

{
"@odata.context": "https://api.businesscentral.dynamics.com/v2.0/40287fcd-fa59-4fee-a9a5-aee76e3acc9f/LedgerCentral-DEMO/ODataV4/$metadata#Edm.Decimal",
"value": 0.851972541653092
}

Response with multiple value in encoded Json :

{
"@odata.context": "https://api.businesscentral.dynamics.com/v2.0/40287fcd-fa59-4fee-a9a5-aee76e3acc9f/LedgerCentral-DEMO/ODataV4/$metadata#Edm.String",
"value": "{\"Balance\":1.21788633,\"BalanceLCY\":0,\"LastDateTimeBalanceUpdated\": 2024-08-22T21:24:46.127Z}"
}

Available API Functions

GenerateNewBlockchainAccount

This function generate a new account for the blockchain mentioned and store it in Business Central then return its public address.

Parameters :

  • BlockchainNetwork : The blockchain network name to generate a new account on, Code 20
  • (Optional) Name : name affect to the newly created account, Text 150

Return Value :

  • PublicAddress : The public address generated

GenerateNewBlockchainAccountPK

This function generate a new account for the blockchain mentioned and store it in Business Central, then return both public address and private key.
Note : This function return an error with default configuration for security reason. Check the option « Disable Private Key Access from API » in Business Central, under Blockchain General Setup page.

Parameters :

  • BlockchainNetwork : The blockchain network name to generate a new account on, Code 20
  • (Optional) Name : name affect to the newly created account, Text 150

Return Value (Json encoded) :

  • PublicAddress : The public address generated
  • PrivateKey : The private key allowing to spend/transfer funds from the address.

GetAccountMainBalance

This function returns the balance of the main account currency, for the specified address that must exist in Business Central.

Parameters :

  • BlockchainNetwork : The blockchain network of the account, Code 20
  • BlockchainPublicAddress : Public address of the account, Text 150

Return Values (Json encoded) :

  • LastDateTimeBalanceUpdated : Date time the account balance was last updated, DateTime
  • BalanceLCY : the account balance in local currency (according Business Central company setup and latest exchange rate), Decimal
  • Balance : the account balance of it main currency (eg BTC for Bitcoin network), Decimal

GetAccountTokenBalance

This function returns the balance of a specific token, holded by the specified address that must exist in Business Central.

Parameters :

  • BlockchainNetwork : The blockchain network of the account, Code 20
  • BlockchainPublicAddress : Public address of the account, Text 100
  • Token : The symbol/ticker code of the token, eg USDT for tether usd, Code 10

Return Values (Json encoded) :

  • LastDateTimeBalanceUpdated : Date time the account balance was last updated, DateTime
  • TokenBalanceLCY : the token balance in local currency according Business Central company setup and latest exchange rate, Decimal
  • TokenBalance : the token balance, Decimal

GetAccountTotalValuationLCY

Parameters : 

  • BlockchainNetwork : The blockchain network of the account, Code 20
  • BlockchainPublicAddress : Public address of the account, Text 100

Return Values (Json encoded) :

  • LastDateTimeBalanceUpdated : Date time the account balance was last updated, DateTime
  • BalanceLCY : The total account valuation including main currency and any token valued by Business Central, in company local currency according to the latest exchange rate, Decimal

GetTransactionInformation

Parameters : 

  • BlockchainNetwork :  blockchain network of the transaction
  • TransactionID : Unique transaction identifier or Hash to retireve

Return Values (Json encoded) : 

  • status : either « Failed » or « Success »
  • BlockDateTime : the date time at which the transaction was confirmed
  • FromAddress : source address that were debited
  • ToAddress : destination address that were credited
  • Symbol : crypto currency symbol, such as BTC, ETH, USDT ect.
  • NetAmount : net amount of the transfer (excluding fee)
  • NetAmountLCY : net amount of the transfer in company local currency
  • FeeAmount : amount of fee consumemd for the transaction in main blockchain currency

GetLatestExchangeRate

Parameters : 

  • Symbol :  The token or currency code. Eg : “BTC” or “USDT”, Code 10
  • (Optional) FromDate : Date to get the exchange rate. If not mentionned, the latest available is returned, Date

Return Value : 

The exchange rate for provided symbol to company local currency, Decimal

GetQRCodeB64PngFromText

Parameters : 

  • InputText :  text, such as blockchain address to encode as a QR image

Return Value : 

Base 64 text of an QR image in png format
The QR image is 6 pixels per module with ECC level “Q” (25% error correcting).

Application Extensibility for AL Developper

Available Events

Most of the published events are available within the paremeter “IsHandled” passed by reference. This allows developers to rewrite a function and skip original code for the described Event.

Codeunit “Blockchain General Events”

  • OnAfterSubmitedTransactionFailed
      • Happen after a transaction status were updated and the RPC detected the transaction failure (eg : there were not enough fee allocated) 
  • OnBeforeBlockchainGenerateNewAccount
      • Happen right before a public/private key pair is generated to create a new account. Can be triggered manually by users, automatically from crypto. invoices or from web service request
  • OnBeforeBlockchainAccountGetTransactions
      • Happen right before the update of an account transaction is requested. Can be triggered manually by users, automatically by page opening, scheduled job queue or from web service request
  • OnBeforeBlockchainGenerateNewAccountAfterGetKeyPair
      • Happen after a public and private key pair is generated but before it is affected to a new Blockchain Account
  • OnBeforeGetBlockchainAccountBalance
      • Happen right before an account balance update is requested. Can be triggered manually by user, automatically by page opening, scheduled job queue or from web service request
  • OnBeforeGetLastErrorEvent
      • Happen when the system is looking for the last error logged in the table “Blockchain Event Log” in order to return the message to user
  • OnBeforeUpdateAllSubmitedTransactionStatus
      • Happen before all submitted transactions status are updated. Can be triggered manually by user from the journal, automatically on page opening, scheduled job queue or from web service request
  • OnBeforeUpdateBlockchainFees
      • Happen before a request of recommended blockchain fees update. Can be triggered manually by user, automatically on page opening, scheduled job queue or from web service request
  • OnBeforeUpdateSubmitedTransactionStatus
    • Happen before each single submitted transaction status is updated

Codeunit “Blockchain Submit Transaction”

  • OnBeforeCheckAnotherWaitingTransaction
      • Happen before the system verifies if another transaction was submitted with the blockchain account to prevent users for submitting two different transactions with the same blockchain account.
  • OnBeforeSubmitBlockchainTransaction
      • Happen right before a blockchain transaction is submitted or simulated, at the beginning of the process
  • OnBeforeTryTestFieldsOperationJnlLine
      • Happen during a blockchain transaction is submitted or simulated, before the journal line is verified
  • OnBeforeTryVerifyOperationJnlLineBalance
    • Happen during a blockchain transaction is submitted or simulated, before the account balance is verified to be sufficient for the transfer

Codeunit “Blockchain Payable Accounting”

  • OnBeforePostTransactionFeeAccounting
      • Happen when a submitted transaction is validated and the Fees are integrated in the accounting, at the beginning of the process
  • OnBeforePostTransactionFeeBeforePostGLJnlLine
      • Happen when a submitted transaction is validated and the Fees are integrated in the accounting, right before the General Journal Line is posted
  • OnBeforePostTransferAccountingToThirdParty
      • Happen when a submitted transaction is validated and the payable to third party are integrated in the accounting, at the beginning of the process
  • OnBeforePostTransferAccountingToThirdPartyBeforePostGL
    • Happen when a submitted transaction is validated and the payable to third party are integrated in the accounting, right before the General Journal Line is posted

Table “Blockchain Account”

  • OnAfterCalculatePublicAddressFromPrivateKeyBeforeModify
      • Happen after the Private key is defined when a public address is already set so the system verify if they match and trigger an error if they don’t
  • OnBeforeVerifyPublicAddressFromPrivateKey
    • Happen when a private key is defined in order to calculate to public address from it (the term “Derive public address” is used in cryptography)

Table “Blockchain Network” :

  • OnBeforeGetBlockchainAssetGLAccount
      • Happen when the system look for the account to book in the blockchain valuation movments
  • OnBeforeGetBlockchainFeeGLAccount
    • Happen when the system look for the account to book in the blockchain payed fees

Codeunit “Blockchain Update LCY Values”

  • OnBeforeUpdateCryptoLCYValues
      • Happen after Crypto currencies exchange rates are updated in standard Microsoft tables, before the system look in the different blockchain related tables to update LCY Values
  • OnBeforeUpdateCryptoLCYValuesAfterLoadLastCryptoExchRate
      • Happen after all latest crypto currency exchange rates were loaded in memory (temporary record), and before the system look in the different blockchain related tables to update LCY valuations
  • OnAfterUpdateCryptoLCYValues
    • Happen after crypto currencies exchange rate are updated, and after LCY Values are updates on all related blockchain tables : 
      • Blockchain Account
      • Blockchain Account Token
      • Blockchain Operation Jnl.

Blockchain Codeunit extensible Interface

The integration with different blockchains are managed using an AL interface “BlockchainHandler”.

Using an interface allows the application to call the different procedure without hard coded codeunit reference, so blockchain support can be added without recoding all common features.

The codeunit to use for a specific blockchain is defined on the table “Blockchain Network”, in the “RPC Handler” field. This is an enum, referencing codeunit implementing the interface.

Blockchain Interface Handler

Selection on “Blockchain Network” card

  • The enum implementing BlockchainHandler
enum 62702 BlockchainHandler implements BlockchainHandler
{
    Extensible = true;

    value(0; None)
   {
        Caption = 'No Handler';
       Implementation = BlockchainHandler = "Blockchain Empty RPC";
   }
    value(XX; "Bitcoin Blockcstream RPC")
   {
        Caption = 'Bitcoin - Blockstream API';
       Implementation = BlockchainHandler = "Blockchain Bitcoin Blockstream";
    }
...
  • Function defined in the interface

Procedures listed here must be implemented in any new blockchain handler. If the procedure isn’t supported, the function must be present anyway and return false.

“SupportedFeature” is used by core pages and codeunit to display and use only the defined features of the blockchain handler.

interface BlockchainHandler
{
procedure getAssetTransfers(Blockchain: Record "Blockchain Network"; BlockchainAccount: Record "Blockchain Account"; var BlockchainTransactions: Record "Blockchain Transactions") Sucess: Boolean

procedure getBalance(Blockchain: Record "Blockchain Network"; BlockchainAccount: Record "Blockchain Account"; var Balance: Decimal) Sucess: Boolean

procedure getFees(Blockchain: Record "Blockchain Network") Sucess: Boolean

procedure getPublicAddressFromPK(Blockchain: Record "Blockchain Network"; PrivateKey: Text; var PublicAddress: Text) Sucess: Boolean;

procedure getRandomKeyPair(Blockchain: Record "Blockchain Network"; var PrivateKey: Text; var PublicAddress: Text);

procedure getTransactionSignedData(var BlockchainOperationJnl: Record "Blockchain Operation Jnl."; var TXRawData: Text) Sucess: Boolean

procedure getTXStatus(Blockchain: Record "Blockchain Network"; var BlockchainOperationJnl: Record "Blockchain Operation Jnl.") Sucess: Boolean

procedure SubmitTransaction(Blockchain: Record "Blockchain Network"; var TXRawData: Text; var ResultTXid: Text; var ErrMsg: Text) Sucess: Boolean

procedure SupportedAddressFormat(BlockchainAddressType: Enum "Blockchain Address Type"): Boolean

procedure SupportedFeatures(RPCFeature: Enum "Blockchain RPC Features"): Boolean
}

How to add a new Blockchain Network Handler :

  1. Create a code unit implementing the interface, eg :
    codeunit 123456 CustomBlockchain implements BlockchainHandler
  2. Add required functions
  • See details about BlockchainHandler functions in the next chapter
  • Business Central does not support advanced cryptographic computation, required to sign transactions or generate blockchain key pairs.

    You can create a DotNet library to do that or deploy the project in a Private Azure Function for cloud usage.
    Deploying an Azure Function is simple and cheap, the first million executions is free. It can be called from Business Central with simple https GET requests.
    How to create an Azure Function using C#
    Calling Azure Functions from AL
    • If you use Azure function please make sure to use Isolated mode and disable telemetry. Critical information such as private key shall not be logged on Microsoft servers.
  • Contact us if you need help implementing your own blockchain handler : contact@ledgercentral.ch

     3. Extend the Enum “BlockchainHandler”

  • Add a new value which implement newly created codeunit in 2.

    4. Optional – Customize pages if the blockchain need additionnal fields or actions

Once above steps are done, you’ll be able to configure a new “Blockchain Network”, set your new handler then test the different features.

Blockchain Handler functions

  • getAssetTransfers : 
    • Retrieve transactions related to a specific public address and store it in the table “Blockchain Transaction”. You may only fetch latest transaction from existing one to optimize the procedure.
      Insert() and Modify() trigger must not be FALSE when writing blockchain transaction.
  • getBalance
    • Retrieve the main balance of a specific public address, then return it in the “Balance” parameter by reference. 
    • If the Blockchain support tokens, the balance of any holded token on the account should be also updated, on the table “Blockchain Account Token” and stored using Modify/Insert. 
    • Shall use Validate() on “Balance” fields to let the system store the last update date time and update the LCY valuation.
  • getFees
    • Retrieve blockchain recommended fee amount and update the table “Blockchain Network Fee”
    • You can use available methods on the table “Blockchain Network Fee” to upsert the data :
      UpsertFeeAvgSize, UpsertFeeGas, UpsertFeeFixedPerOperation or UpsertFeePerByte.
      Depending the fee calculation method use on the blockchain
    • If the blockchain as fixed fee, you can just insert or modify Blockchain Fee with hardcoded value.
  • getPublicAddressFromPK
    • Return a public address calculated from a provided private key
    • This is not doable with AL code and may need DotNet library (Azure function or OnPrem DLL)
  • getRandomKeyPair
    • Return a set of randomly generated Private Key + Public Address
    • This is not doable with AL code and may need DotNet library (Azure function or OnPrem DLL)
  • getTransactionSignedData
    • Return raw blockchain transaction data from a “Blockchain Operation Jnl.”, signed with the “From Account” private key.
    • This data is then used to broadcast the transaction to blockchain. Usually as base58 or base64 encoded.
    • This is not doable with AL code and may need DotNet library (Azure function or OnPrem DLL)
  • getTXStatus
    • Update the status of a submitted transaction from “Blockchain Operation Jnl.”
    • Field “Transaction Status”, “Transaction Block Height” and “Transaction Block Time” should be updated if the transaction have been confirmed or failed. The field “Fee Amount” may also be updated for blockchain with flexible fee consumption.
    • Do not use Modify() on BlockchainOperationJnl, it is already handled in above calling function
    • System handle the accounting after this function is executed: if the status has been marked as confirmed or failed. You don’t need to code the accounting.
  • SubmitTransaction
    • Submit a transaction raw data to the blockchain network and return the transaction hash in parameter “ResultTXId” in order to track the status
    • Fill the “ErrMsg” parameter if the transaction is rejected by the RPC, in order to make the message visible on the journal factbox
  • SupportedAddressFormat
    • Return what type/encoding of blockchain address is supported.
      Return true or false according the input enum, below example for Bitcoin :
if BlockchainAddressType in [BlockchainAddressType::"Bech32 (Segwit)",
BlockchainAddressType::"Bech32m (Taproot)",
BlockchainAddressType::"Base58 (Legacy)",
BlockchainAddressType::"Base58 (P2SH-Segwit)"] then
exit(true)
else
exit(false);
  • SupportedFeatures
    • Return what features are supported by the blockchain and RPC. Return true or false according the input enum, see below example :
if RPCFeature in [RPCFeature::Balance,
                 RPCFeature::"Transactions History",
                 RPCFeature::"Generate Key Pair",
                 RPCFeature::UTXO,
                 RPCFeature::"Submit Transaction",
                 RPCFeature::"Tx Fee - Per Byte Amount",
                  RPCFeature::"Derive Public Address From Private Key"] then
exit(true)
else
exit(false);

Create additional Blockchain Address format

The applciation alway try to detect and validate a blockchain address format in order to prevent any user mistake when importing existing address.

To Add new address type from the existing list :

  1. Extend enum “Blockchain Address Type”
  2. Use the Event “OnBeforeGetaddressType” of the codeunit “Blockchain Address Helper”, and return your new enum value if the input address match the format
    • Functions related to Hexadecimal, Base64 and 58 encoding can be used within the codeunit « Blockchain Encoding Helper »

Create Blockchain Features

The application behavious and pages show different fields and actions regarding what a blockchain features are supported.

  • To add your own feature or additional parameter on the blockchain network card/account :
  1. Extend the enum “Blockchain RPC Feature” to add new values
  2. Extend the pages such as “Blockchain Network Card” or “Blockchain Account Card”, to add fields or actions to show according to the blockchain supported feature
  3. Extend the table « Blockchain Network » and add a function which return if a feature is supported
  4. On the different page, you can hide or show custom fields and actions using this function from the « Blockchain Network »

procedure IsTokenSupported(): Boolean
var
RPCFeature: Enum "Blockchain RPC Features";
RPCHandler: Interface BlockchainHandler;
begin
RPCHandler := Rec."RPC Handler";
exit(RPCHandler.SupportedFeatures(RPCFeature::Tokens));
end;

Used on pages as :

action(...)
{
Visible = ShowToken;
...

var
ShowToken: Boolean
BlockchainNetwork: Record "Blockchain Network";
...

trigger OnAfterGetRecord()
begin
BlockchainNetwork.get("Blockchain Network");
ShowToken := BlockchainNetwork.IsTokenSupported();
...

Test Tool

The application come with a Test library for core function who can be run on will during development and testing phases.

It include but is not limited to :

  • Blockchain related encoding and decoding tests (Base64, Base58, Hexadecimal)
  • Private Key access tests and encryption/decryption proficiency tests
  • Blockchain account signature and corruption tests
  • Blockchain Address recognition tests
  • Basic Blockchain account and blockchain token handling
Test library

Run the test library : 

  1. Navigate to the Microsoft “Test Tools” page
  2. If “Blockchain LedgerCentral Tests” is not present, use the “Get Test Codeunits” action
  3. Press on “Run” or “Run Selected” to execute the tests
Table of Contents