5. Developers guide

-

5.1. Getting started

5.1.1. How to obtain Atomia Billing SDK

Atomia Billing SDK can be downloaded as a compiled set of dlls. The main SDK dll is Atomia.Billing.Core.Sdk.dll . Other dlls in the package are dependencies of the Atomia Billing SDK.

Here you can download the full Atomia Billing SDK: Atomia.Billing.Core.Sdk.zip .

5.1.2. Set up a Visual Studio project

Here you can find basic steps for starting the development of custom plug-ins to use with the Atomia Billing API. The same procedure can be used for creating event handlers.

  1. Create a folder on your hard drive where all code will be placed (e.g. AtomiaBillingSdkExample ). It will be referenced later as the root folder.

  2. Create in root folder subfolder named Solution where solution items will be placed.

  3. Create another subfolder, lib for placing all needed libraries.

  4. Copy the contents of the downloaded Atomia Billing SDK package ( Atomia.Billing.Core.Sdk folder) to lib folder.

  5. Open Visual Studio 2010 and create a blank solution named AtomiaBillingSdkExample and place it in the Solution folder.

  6. Create a new class library project named Plugins and change its assembly name and default namespace to AtomiaBillingSdkExample.Plugins .

  7. Add references to Atomia.Billing.Core.Sdk.dll and Atomia.Billing.Core.DataLayer.dll for the project Plugins .

You are now ready to start writing custom plug-ins by implementing the interfaces defined in the Atomia Billing SDK.

Here is an example of a simple plug-in of type IScheduledEventHandler :

  1. Add a new class with the name NotificationHandler .

  2. Copy the following code as its definition:

    using System.Collections.Generic;
    using Atomia.Billing.Core.Sdk;
    using Atomia.Billing.Core.Sdk.Plugins;
    using Atomia.Billing.Core.Sdk.Utils.Email;
    
    namespace AtomiaBillingSdkExample.Plugins
    {
     /// <summary>
     /// Custom implementation of IScheduledEventHandler.
     /// </summary>
     public class NotificationHandler : IScheduledEventHandler
     {
       /// <summary>
       /// Initializes a new instance of the <see cref="NotificationHandler"/> class.
       /// </summary>
       public NotificationHandler()
       {
         this.SenderAddress = "test@atomia.com";
       }   
       
       /// <summary>
       /// Gets or sets plugin name.
       /// </summary>
       public string Name
       {
         get
         {
           return "NotificationHandler";
         }
         
         set
         {
         }
       }
       
       /// <summary>
       /// Gets or sets IAtomiaBillingApi instance.
       /// </summary>
       public IAtomiaBillingApi AtomiaBillingApi
       {
         get;
         set;
       }
       
       /// <summary>
       /// Gets or sets SenderAddress.
       /// </summary>
       public string SenderAddress
       {
         get;
         set;
       }
       
       /// <summary>
       /// Handles scheduled event.
       /// </summary>
       public void HandleEvent()
       {
         IList<MyNotification> notifications = this.GetNotifications();
         foreach (MyNotification notification in notifications)
         {
           this.SendEmail(this.SenderAddress, notification.To, notification.Subject, notification.Body);
         }
       }
       
       /// <summary>
       /// Send email to recipient.
       /// </summary>
       /// <param name="senderAddress">The sender address.</param>
       /// <param name="recipient">The recipient.</param>
       /// <param name="subject">The subject.</param>
       /// <param name="body">The email body.</param>
       private void SendEmail(string senderAddress, string recipient, string subject, string body)
       {
         this.AtomiaBillingApi.SendMail(senderAddress, recipient, subject, body, new List<MailAttachment>());
       }
       
       /// <summary>
       /// Gets notifications.
       /// </summary>
       /// <returns>
       /// List of notifications.
       /// </returns>
       private IList<MyNotification> GetNotifications()
       {
         IList<MyNotification> notifications = new List<MyNotification>();
         
         // Here should be placed logic for retrieving notifications (using helper class, repository, etc.).
         // For this example, one notification is hardcoded.
         notifications.Add(new MyNotification
         {
           Body = "Test notification body.",
           Subject = "Test notification",
           To = "info@foobar.com"
         });
         return notifications;
       }
     }             
    
    /// <summary>
     /// Notification class.
     /// </summary>
     internal class MyNotification
     {
       /// <summary>
       /// Gets or sets the body.
       /// </summary>
       public string Body { get; set; }
    
       /// <summary>
       /// Gets or sets the subject.
       /// </summary>
       public string Subject { get; set; }            
    
       /// <summary>
       /// Gets or sets recipient.
       /// </summary>
       public string To { get; set; }
     }
    }
  3. You should now be able to build the project.

  4. To test the created class, add a new Test project named UnitTests and change its assembly name and default namespace to AtomiaBillingSdkExample.UnitTests .

  5. Add a unit test class for NotificationHandler (you can name it NotificationHandlerTest ) and add following code:

    using System.Collections.Generic;
    using Atomia.Billing.Core.Sdk;
    using Atomia.Billing.Core.Sdk.Utils.Email;
    using AtomiaBillingSdkExample.Plugins;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Rhino.Mocks;
    
    namespace AtomiaBillingSdkExample.UnitTests
    {
     /// <summary>
     /// NotificationHandler test fixture.
     /// </summary>
     [TestClass]
     public class NotificationHandlerTest
     {
       /// <summary>
       /// Rhino Mocks Repository.
       /// </summary>
       private readonly MockRepository mocks;
       
       /// <summary>
       /// Atomia Billing Api mock.
       /// </summary>
       private readonly IAtomiaBillingApi atomiaBillingApiMock;
       
       /// <summary>
       /// Initializes a new instance of the <see cref="NotificationHandlerTest"/> class.
       /// </summary>
       public NotificationHandlerTest()
       {
         this.mocks = new MockRepository();
         this.atomiaBillingApiMock = this.mocks.StrictMock<IAtomiaBillingApi>();
       }
       
       /// <summary>
       /// Gets or sets the test context which provides
       /// information about and functionality for the current test run.
       /// </summary>
       public TestContext TestContext { get; set; }
       
       #region Additional test attributes
       /// <summary>
       /// Use ClassInitialize to run code before running the first test in the class.
       /// </summary>
       /// <param name="testContext">The test context.</param>
       [ClassInitialize]
       public static void MyClassInitialize(TestContext testContext)
       {
       }
       
       /// <summary>
       /// Use ClassCleanup to run code after all tests in a class have run.
       /// </summary>
       [ClassCleanup]
       public static void MyClassCleanup()
       {
       }
       
       /// <summary>
       /// Use TestInitialize to run code before running each test.
       /// </summary>
       [TestInitialize]
       public void MyTestInitialize()
       {
         this.mocks.BackToRecordAll();
       }
       
       /// <summary>
       /// Use TestCleanup to run code after each test has run.
       /// </summary>
       [TestCleanup]
       public void MyTestCleanup()
       {
         this.mocks.VerifyAll();
       }
       #endregion
       
       /// <summary>
       /// A test for HandleEvent
       /// </summary>
       [TestMethod]
       public void HandleEventTest()
       {
         NotificationHandler notificationHandler = new NotificationHandler
         {
           AtomiaBillingApi = this.atomiaBillingApiMock
         };
         
         Expect.Call(
         () =>
         this.atomiaBillingApiMock.SendMail("test@atomia.com", "info@foobar.com", "Test notification", "Test notification body.", new List<MailAttachment>()))
         .Repeat.Once();
         this.mocks.ReplayAll();
         
         notificationHandler.HandleEvent();
       }
     }
    }
  6. In order to compile the project and run the test you will need Rhino Mocks library for creating the Atomia Billing API mock. You can download Rhino Mocks for free and add a reference to it for your UnitTests project.

You can download the complete example here: AtomiaBillingSdkExample.zip .

5.2. Atomia Billing Plug-ins

5.2.1. Introduction

The base interface for all Atomia Billing plug-ins is IPlugin located in the Atomia.Billing.Core.Sdk.Plugins namespace. All more specific plug-in interfaces must inherit it and add its methods and property definitions.

namespace Atomia.Billing.Core.Sdk.Plugins
{
 /// <summary>
 /// Interface from which all Atomia Billing plug-ins have to inherit or implement.
 /// </summary>
 public interface IPlugin
 {
   /// <summary>
   /// Gets or sets the plug-in name.
   /// </summary>
   string Name { get; set; }
   
   /// <summary>
   /// Gets or sets IAtomiaBillingApi instance.
   /// </summary>
   IAtomiaBillingApi AtomiaBillingApi { get; set; }
 }
}

5.2.2. Plug-in interfaces

Here is list of all currently supported plug-in interfaces:

Interface type

Description

IAddressFetchPlugin

Use this interface to write a plugin for fetching customer address information from an address service based on the personal or corporate identification number.

ICampaignPlugin

Used for applying specific campaign rules and discounts for an order.

IConditionHandler

Used for creating custom conditions for campaign discounts.

IInvoiceTemplating

Used for creating string representations of invoices for e-mails, PDF documents etc.

IMessagingPlugin

Used for implementing message sending via specific protocols (email, SMS etc.).

IPayFileProcessorPlugin

Used for parsing payments from payment report files.

IPaymentMethodPlugin

Used for implementing payment operations (transaction handling) for payment gateways.

IPaymentRulePlugin

Used for applying custom rules on payments (e.g. check balance or payment status, check if currency matches invoice etc.).

IProvisioningPlugin

Used for defining provisioning operations (package/subscription handling, order validation etc.).

IScheduledEventHandler

Used for handling scheduled tasks when "heart beat" occurs.

ITaxPlugin

Used for applying tax to invoiced item (s).

Plug-in interfaces are placed in Atomia.Billing.Core.Sdk.Plugins . The documentation for plugin interfaces can be found in the API and SDK reference .

5.2.3. Campaign plug-in

5.2.3.1. Introduction

The main object for campaigns is Campaign . It defines the campaign name, the period it is valid, codes and discounts. Campaign can have one or more campaign codes which are defined using CampaignCode class. Each code has its own validity period and maximum number of usages. Campaign is bound to an order by setting its custom attribute {{CampaignCode to a valid campaign code.

Campaign can have one or more discounts (defined by CampaignDiscount class). Discount can be specified using percentage or currency amount. It can have a list of items (identified by ItemNumber ) with a specific discount for every item. In this case, the item discount overrides the general discount. Also, discount limitations can be specified. This is done by specifying properties of the CampaignDiscountLimits class. A list of campaign discount conditions is defined for every discount. It contains the name of the condition handler (name of type which implements IConditionHandler interface) and a list of CampaignDiscountConditionParam objects. These parameters are used to check whether the order satisfies the discount conditions or not. In order to apply a discount to an order, every condition must be met. This logic should be implemented in plugins of type ICampaignPlugin and IConditionHandler .

A class diagram for mentioned types can be found here .

5.2.3.2. ICampaignPlugin example
5.2.3.2.1. ApplyCampaignRules method

ApplyCampaignRules is called when an order is created and it can be used to change order items, add more items (e.g. get three for two) etc.

public Order ApplyCampaignRules(Order order)
{
 OrderCustomAttribute codeAttribute = order.CustomAttributes.Find(x => x.Name == "CampaignCode");
 
 if (codeAttribute != null)
 {
   // Retreive campaign code object for checking if it can be used
   CampaignCode campaignCode = CampaignsHelper.GetCampaignCode(codeAttribute.Value);
   if (this.ValidateCampaignCode(campaignCode))
   {
     // Put logic for applying rules (change order data) here.
     // ...
     
     campaignCode.UsedTimes = campaignCode.UsedTimes + 1;
     CampaignsHelper.SaveCampaignCode(campaignCode);
   }
 }
 return order;
}

private bool ValidateCampaignCode(CampaignCode campaignCode)
{
 if (campaignCode == null)
 {
   return false;
 }
 return (campaignCode.CanBeUsedTimes > campaignCode.UsedTimes) &&
   (campaignCode.ValidFrom <= DateTime.Now) &&
   (campaignCode.ValidThru >= DateTime.Now);
}
5.2.3.2.2. ApplyCampaignDiscounts method

ApplyCampaignDiscounts is called when order totals are calculated. It should check if the order satisfies the campaign conditions and implement logic for changing order data by applying discount rules.

public Order ApplyCampaignDiscounts(Order order)
{
 OrderCustomAttribute codeAttribute = codeAttribute = order.CustomAttributes.Find(x => x.Name == "CampaignCode");
 
 if (codeAttribute != null)
 {
   // There is campaign code in order so its data should be retreived and discounts should be applied.
   CampaignCode campaignCode = CampaignsHelper.GetCampaignCode(codeAttribute.Value);
   if (this.ValidateCampaignCode(campaignCode))
   {
     // Put logic for applying discounts here
   }
 }
 
 return order;
}

private bool ValidateCampaignCode(CampaignCode campaignCode)
{
 if (campaignCode == null)
 {
   return false;
 }
 
  return (campaignCode.CanBeUsedTimes > campaignCode.UsedTimes) &&
    (campaignCode.ValidFrom <= DateTime.Now) &&
    (campaignCode.ValidThru >= DateTime.Now);
}

5.2.3.3. IConditionHandler example
5.2.3.3.1. TestCondition method

TestCondition can be called after an order is created to check if it satisfies the conditions for applying campaign discounts. These conditions can be number of ordered items, list of specific items, new customer etc. Here is an example of an implementation which checks if an order contains items needed for a discount.

public class CampaignDiscountConditionParam
{
  public Guid Id { get; set; }

  public CampaignDiscountCondition CampaignDiscountCondition { get; set; }

  public string Name { get; set; }

  public string Value { get; set; }
}

public bool TestCondition(Order order, List<CampaignDiscountConditionParam> campaignDiscountConditionParams)
{
  List<OrderLine> orderLines = new List<OrderLine>();
  orderLines.AddRange(order.OrderLines);
  
  CampaignDiscountConditionParam operationParam = campaignDiscountConditionParams.Find(x => x.Name == "Operation");
  bool ok = operationParam == null || operationParam.Value == "AND";
  
  foreach (CampaignDiscountConditionParam campaignDiscountConditionParam in campaignDiscountConditionParams)
  {
    if (operationParam == null || operationParam.Value == "AND")
    {
      ok = ok && orderLines.Exists(x => x.ItemNumber == campaignDiscountConditionParam.Value);
    }
    else
    {
      ok = ok || orderLines.Exists(x => x.ItemNumber == campaignDiscountConditionParam.Value);
    }
  }
  
  return ok;
}

5.2.4. PayFileProcessor plug-in

5.2.4.1. Introduction

Payment files are used for importing payments from various payment processors. Atomia Billing API allows customers to implement plugins for parsing different payment file formats. These plugins are used to parse file stream into defined sections and to create payments from them. After that, parsed payments are imported into the system by the API.

5.2.4.2. ParseFile method

ParseFile is used for parsing payments from a payments report file. These payments are later imported into the system.

public IList<Payment> ParseFile(Stream fileStream)
{
  // PaymentLine is custom type which represents one line from payment file.
  IList<PaymentLine> paymentLines = this.ParseLines(fileStream);
  
  IList<Payment> payments = new List<Payment>();
  
  // Payment is represented by PaymentLine.
  foreach (PaymentLine paymentLine in paymentLines)
  {
    Payment payment = new Payment();
    payment.Amount = paymentLine.IsCreditDeduction ? -paymentLine.Amount : paymentLine.Amount;
    payment.Currency = new Currency { Code = paymentLine.Currency };
    payment.InvoiceReferenceNumber = paymentLine.CustomerReference;
    payment.CreatedTime = paymentLine.AccountingDate;
    payment.LastChangedTime = DateTime.Now;
    payment.DocumentId = paymentLine.ReportNumber;
    
    payments.Add(payment);
  }
  
  return payments;
}
            
private IList<PaymentLine> ParseLines(Stream fileStream)
{
  // Logic for parsing file lines to custom PaymentLine type.
}
5.2.4.3. DetectFile method

DetectFile is used to check whether the pay file plugin can parse the specified file or not. When parsing payments from an uploaded file, Atomia Billing API loads the configured pay file plugins and uses DetectFile to select a suitable plugin for the uploaded file.

5.2.5. Payment method plug-in

Payment method plug-ins are used for performing processing of payment transactions by calling operations on various payment processing systems (payment gateways). Interface for payment method plug-ins,  IPaymentMethodPlugin , defines two standard operations for processing payment transactions (BeginTransaction and CloseTransaction ) with one additional method for getting transaction status (ProbeTransactionStatus). First two methods should perform needed transformation of transaction data to the format expected by payment gateway and call proper operations. On some payment gateways, where it is possible to do processing in one step (charge without some kind of authorization), implementing of BeginTransaction should be sufficient.

5.2.5.1. Authorize.Net example

In the following section, code for processing transaction on Authorize.Net payment gateway can be found. On Authorize.Net, it is possible to to authorize and capture in one step so implementation of CloseTransaction method is simple. In this example, Authorize.Net SDK is used for creating requests, sending them and getting response. In the end, payment transaction (with updated statuses) is saved using Atomia Billing SDK's PaymentMethodsHelper.

/// <summary>
/// Begins the transaction on the payment gateway/engine.
/// </summary>
/// <param name="transaction">The transaction.</param>
/// <returns>Updated transaction.</returns>
/// <remarks>
/// It gets transaction object and according to the data stored in transaction object it creates transaction on the payment gateway.
/// For synchronous payments, it returns new transaction object with filled TransactionAttributes and other properties. Status of the transaction is set to "Completed"
/// For async payment, it also fills the properties, and sets that transaction is in progress.
/// It can receive PaymentProfile in the transaction. In that case, it should use that payment profile, otherwise, it should store payment profile and then use it.
/// </remarks>
public PaymentTransaction BeginTransaction(PaymentTransaction transaction)
{
  // Validate transactions's currency. Here only USD is supported.
  if (transaction.CurrencyCode != "USD")
  {
    throw new ArgumentException("CurrencyCode");
  }
  
  // Validate that all required credit card data.
  if (!transaction.Attributes.ContainsKey("cc_number") || !transaction.Attributes.ContainsKey("expires_month") ||
    !transaction.Attributes.ContainsKey("expires_year") || !transaction.Attributes.ContainsKey("ccv"))
  {
    throw new ArgumentException("CCData");
  }
  
  // Set defaults for transaction details.
  transaction.TransactionId = string.Empty;
  transaction.StatusCode = string.Empty;
  transaction.StatusCodeDescription = string.Empty;
  
  Dictionary<string, string> customAttributes = transaction.Attributes.Keys.ToDictionary(key => key, key => transaction.Attributes[key]);
  transaction.Attributes.Clear();
  
  // Get CC data.
  string cardNumber = customAttributes["cc_number"];
  string cardNumberExpireMonth = customAttributes["expires_month"];
  string cardNumberExpireYear = customAttributes["expires_year"];
  string cardNumberCvc = customAttributes["ccv"];
  
  // Create the request and add customer data.
  AuthorizationRequest request = new AuthorizationRequest(cardNumber, cardNumberExpireMonth + cardNumberExpireYear, transaction.Amount, string.Empty, true);
  request.AddCardCode(cardNumberCvc);
  SetCustomerData(request, transaction);
  
  // Create the gateway using credentials from plugin configuration.
  // Logic for storing and getting data should be implemented.
  ApiData apiData = this.GetApiData();
  Gateway gate = new Gateway(this.Decrypt(apiData.ApiLogin), this.Decrypt(apiData.TransactionKey), apiData.TestMode);
  
  // Send request and set transaction status based on response.
  IGatewayResponse response = gate.Send(request);
  GatewayResponse gatewayResponse = response as GatewayResponse;
  
  transaction.Status = response.Approved ? PaymentTransaction.Ok : PaymentTransaction.Failed;
  transaction.StatusCode = response.ResponseCode;
  transaction.StatusCodeDescription = response.Message;
  transaction.TransactionId = response.TransactionID;
  
  // Save transaction using helper from SDK.
  PaymentMethodsHelper.AddPaymentTransaction(transaction);
  return transaction;
}

/// <summary>
/// Closes the transaction.
/// </summary>
/// <param name="transactionId">The transaction id.</param>
/// <returns>Updated transaction</returns>
/// <remarks>
/// If some payment gateways need some actions taken when transaction is performed, this method does that.
/// It is called by the engine when payment on the payment gateway is successfull.
/// </remarks>
public PaymentTransaction CloseTransaction(string transactionId)
{
  return PaymentMethodsHelper.GetPaymentTransactionById(transactionId);
}

/// <summary>
/// Probes for transaction status. It is called from the outside periodically, to fetch new status of the transaction.
/// Plugin should save this transaction status in the database, too.
/// </summary>
/// <param name="transactionId">The transaction id.</param>
/// <returns>Updated transaction.</returns>
public PaymentTransaction ProbeTransactionStatus(string transactionId)
{
  return PaymentMethodsHelper.GetPaymentTransactionById(transactionId);
}

5.2.6. Provisioning plug-in

5.2.6.1. Introduction

The provisioning plug-in is a common name for a family of plug-ins that represent connectors between Atomia Billing and the Atomia Automation Server. Their basic purpose is to convert billing objects into corresponding provisioning objects, perform provisioning operations by calling the Atomia Provisioning Core API and return information about the result of the operation.

Provisioning plug-ins allows different provisioning systems to be used with Atomia Billing.

5.2.6.2. IProvisioningPlugin interface

The provisioning plug-in must implement the IProvisioningPlugin interface. Here is a list of all methods of this interface with a short explanation of their purpose.

/// Gets the packages available in the provisioning service.
List<ProvisioningUnit> GetPackages();

/// Adds the package for the specified subscription.
void AddPackage(Subscription subscription, Dictionary<string, object> additionalData);

/// Renews the package for the specified subscription.
void RenewPackage(Subscription subscription, Dictionary<string, object> additionalData);

/// Prepares the provisioning.
void PrepareProvisioning(Subscription subscription, Dictionary<string, object> additionalData);

/// Deletes the package.
void DeletePackage(Subscription subscription, Dictionary<string, object> additionalData);

/// Change package status.
void ChangePackageStatus(Subscription subscription, string statusName, Dictionary<string, object> additionalData);

/// Changes the package.
void ChangePackage(Subscription fromSubscription, Subscription toSubscription, Dictionary<string, object> additionalData);

/// Gets the service status.
string GetPackageStatus(Subscription subscription, Dictionary<string, object> additionalData);

/// Validates the order.
void ValidateOrder(Order order);

/// Determines whether [is posible to change package] [the specified subscription].
bool IsPossibleToChangePackage(Subscription mainSubscription, Subscription newSubscription, Dictionary<string, object> additionalData);

/// Determines whether [is subscription termination possible] [the specified subscription].
List<bool> IsSubscriptionTerminationPossible(List<Subscription> subscriptions, string accountName);

/// Executes the custom action, and returns results.
object ExecuteCustomAction(string actionName, Dictionary<string, object> properties);

The Subscription object contains all necessary data needed for provisioning. For more details on the Subscription object, please see the API and SDK reference .

5.2.7. GetTax method

GetTax is used for getting tax details for an order when it is created or when its totals are calculated. It can be used to apply custom tax rules based on a reseller's or customer's data, ordered item or legal number.

public class TaxPlugin : ITaxPlugin
{
  public TaxRuleResult GetTax(Guid resellerId, Guid customerId, Guid itemId, string countryCode, string legalNumber)
  {
    TaxRuleResult taxRule = new TaxRuleResult();
    
    // Place here custom code which sets taxRule properties based on arguments.
    
    return taxRule;
  }
}

public class TaxRuleResult
{
  /// <summary>
  /// Gets or sets the percentage.
  /// </summary>
  public decimal Percentage { get; set; }
  
  /// <summary>
  /// Gets or sets the description.
  /// </summary>
  public string Description { get; set; }
  
  /// <summary>
  /// Gets or sets a value indicating whether this tax is cumulative.
  /// </summary>
  public bool IsCumulative { get; set;  }
}

5.2.8. Template plug-in

5.2.8.1. Introduction

IInvoiceTemplating is used for getting custom formatted string representations of invoices. It uses invoice data to fill a template's placeholders and returns the formatted string. Templates are stored in a database as HTML documents with defined placeholders for each invoice property.

5.2.8.2. Example

Here is simple example of the Template plug-in used for generating an HTML document of an invoice:

public class InvoiceTemplating : IInvoiceTemplating
{
  /// <summary>
  /// Template name.
  /// </summary>
  private string templateName;
  
  /// <summary>
  /// Initializes a new instance of the <see cref="AtomiaInvoiceTemplating"/> class.
  /// </summary>
  public AtomiaInvoiceTemplating()
  {
    this.templateName = "InvoiceTemplate"; // Load from configuration.
  }
  
  public string FillInvoice(Invoice invoice, CultureInfo culture)
  {
    string dateFormat = culture.DateTimeFormat.ShortDatePattern;
    int totalPages = (int)Math.Ceiling(invoice.InvoiceLines.Count / 5.0f);
    
    // Set parameters of custom TemplateData type.
    //It is used to store data needed for template's placeholders.
    TemplateData data = new TemplateData()
      {
        InvoiceReferenceNumber = invoice.ReferenceNumber,
        InvoiceNumber = invoice.Number,
        DocumentDate = invoice.InvoiceDate.ToString(dateFormat),
        CustomerNumber = invoice.CustomerName,
        CustomerCompanyName = invoice.CustomerCompanyName,
        CustomerFirstName = invoice.CustomerFirstName,
        CustomerLastName = invoice.CustomerLastName,
        CustomerAddress = invoice.CustomerAddress,
        CustomerZip = invoice.CustomerZip,
        CustomerCity = invoice.CustomerCity,
        CustomerCountryName = invoice.CustomerCountry,
        Pages = new List<Page>(),
        Tax1Total = invoice.Tax1Total.ToString(CultureHelper.CURRENCY_FORMAT, culture),
        Subtotal = invoice.Subtotal.ToString(CultureHelper.CURRENCY_FORMAT, culture),
        Total = invoice.Total.ToString(CultureHelper.CURRENCY_FORMAT, culture),
        Currency = invoice.Currency,
        IsCompany = !string.IsNullOrEmpty(invoice.CustomerCompanyName)
      };
    
    int i = 0;
    foreach (InvoiceLine invoiceLine in invoice.InvoiceLines)
    {
      // Check if new page should be added
      if ((i % 5) == 0)
      {
        data.Pages.Add(new Page
        {
        PageNumber = ((i / 5) + 1).ToString(),
        Items = new List<Item>(),
        LastPage = totalPages == ((i / 5) + 1)
        });
      }
      
      // Add Item data here
      // ...
      
      i++;
    }
    
    return this.FillTemplate(new Dictionary<string, object> { { "data", data } });
  }
  
  private string FillTemplate(Dictionary<string, object> values)
  {
    string template = this.GetTemplate(this.templateName);
    
    if (!string.IsNullOrEmpty(template))
    {
      // Here is used StringTemplate template engine.
      StringTemplate templateEngine = new StringTemplate(template);
      
      foreach (string key in values.Keys)
      {
        templateEngine.SetAttribute(key, values[key]);
      }
      
      return templateEngine.ToString();
    }
    else
    {
      return string.Empty;
    }
  }
  
  private string GetTemplate(string templateName)
  {
    // Put logic for getting template by name here
    // ...
  }
}

Here is the template that is used in this example:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
  <meta http-equiv="X-UA-COMPATIBLE" content="IE=8" />
  <title>Invoice</title>
  <meta http-equiv="content-type" content="text/html;charset=utf-8" />
  </head>
  <body>
  $data.Pages:{
  <div>
  <div>
  <div>
  <strong>Invoice reference number:</strong> $data.InvoiceReferenceNumber$
  </div>
  </div>
  <div>
  <div>
  <b>$if(data.IsCompany)$     $data.CustomerCompanyName$     $else$     $data.CustomerFirstName$ $data.CustomerLastName$     $endif$</b>
  <br />$data.CustomerAddress$
  <br />$data.CustomerZip$, $data.CustomerCity$
  </div>
  </div>
  <div>
  <table>
  <tr>
  <th style="width: 34%;">Invoice date</th>
  <th style="width: 34%;">Customer number</th>
  <th>Invoice number:</th>
  </tr>
  <tr>
  <td>$data.DocumentDate$</td>
  <td>$data.CustomerNumber$</td>
  <td>$data.InvoiceNumber$</td>
  </tr>
  </table>
  <table>
  <tr>
  <th>Item</th>
  <th>Price</th>
  </tr>
  $it.Items:{
  <tr>
  <td>$it.ItemName$</td>
  <td>$it.Price$ $data.Currency$</td>
  </tr>
  }$
  $if(it.LastPage)$
  <tr>
  <td><strong>Subtotal</strong></td>
  <td>$data.Subtotal$ $data.Currency$</td>
  </tr>
  <tr>
  <td>Tax</td>
  <td>$data.Tax1Total$ $data.Currency$</td>
  </tr>
  <tr>
  <td><strong>Total</strong></td>
  <td><strong>$data.Total$ $data.Currency$</strong></td>
  </tr>
  $endif$
  </table>
  </div>
  </div>
  <div></div>
  }$
  </body>
</html>

5.3. Atomia Billing Event Handlers

5.3.1. Introduction

The Atomia Billing API defines a set of events that can be used to customize predefined workflows or add executions of scheduled tasks. Writing handlers for what these events should provide means inserting custom business logic into workflows. The API provides pre- and post- events for all essential workflows. The base interface for API event handlers is IApiEventListenerPlugin located in the Atomia.Billing.Core.Sdk.Plugins namespace. All API event handlers should implement this interface and add custom logic for handling defined events.

5.3.2. Workflow event handlers

Here is a list of defined events which can be used for adding custom logic into workflows:

Event name

Description

AccountAddressUpdated

Occurs when account address is updated.

AccountCreated

Occurs when account is created.

AccountPreCreated

Occurs when account is about to be created.

AccountPreTerminated

Occurs before account is terminated.

AccountPreUpdated

Occurs when account is about to be updated.

AccountTerminated

Occurs when account is terminated.

AccountTerminationRequestPostCreate

Occurs whent account termination request is created.

AccountTerminationRequestPreCreate

Occurs whent account termination request is about to be created.

AccountUpdated

Occurs when account is updated.

CreditInvoicePreSent

Occurs when single credit invoice is about to be sent.

CustomerCreated

Occurs when customer is created.

CustomerStatusChanged

Occurs when customer status is changed.

HeartBeatEvent

Occurs when Ticker sends heart beat signal.

InvoicePaid

Occurs when invoice is paid.

InvoicePreCreated

Occurs before invoice is created.

InvoicePreSent

Occurs when single invoice is about to be sent.

InvoiceSending

Occurs before invoices are sent.

OrderCreated

Occurs when order is created.

OrderPreCalculate

Occurs when order totals are about to be calculated.

OrderPreCreated

Occurs when order is about to be created.

OrderPreProcessed

Occurs before order is processed.

OrderProcessed

Occurs when order is processed.

OrderProcessedPreCreateInvoice

Occurs when order is processed, and before we should create an invoice for each subscription.

PaymentCreated

Occurs when payment is created.

PaymentPreCreated

Occurs when payment is about to be created.

SubscriptionChangedStatus

Occurs when Subscription is about to be updated (status changed).

SubscriptionCreated

Occurs when Subscription is created.

SubscriptionExtended

Occurs when subscription is extended.

SubscriptionPreChangedStatus

Occurs when Subscription is updated (status changed).

SubscriptionPreCreated

Occurs when Subscription is about to be created.

SubscriptionPreTerminated

Occurs when Subscription is about to be terminated.

SubscriptionPreUpdated

Occurs when Subscription is about to be updated.

SubscriptionProvisionedOnPayment

Occurs when subscription is provisioned when payment is created.

SubscriptionProvisioningStatusChanged

Occurs when Subscription provisioning status is changed.

SubscriptionTerminated

Occurs when Subscription is terminated.

SubscriptionUpdated

Occurs when Subscription is updated.

UpgradeOrderCreated

Occurs when upgrade order is created.

Complete documentation can be found in the Atomia Billing API reference .

5.3.2.1. Example event handler implementation

 

class MyOrderPreCalcualteEventHandler : IApiEventListenerPlugin
{
  public string Name{get; set;}
  
  private IAtomiaBillingApi billingApi;
  public IAtomiaBillingApi AtomiaBillingApi
  {
    get
    {
      return this.billingApi;
    }
    set
    {
      this.billingApi = value;
      // So, you only need to add this part
      this.billingApi.OrderPreCalculate += this.OnOrderPreCalculate;
    }
  }
  
  // and this part
  private void OnOrderPreCalculate(Object sender, OrderCalculateEventArgs args)
  {
    // Here implement your logic that occurs before Atomia Billing re-calculates the order totals.
  }
}

5.3.3. Scheduled task handlers

5.3.3.1. Defining tasks

The Atomia Billing API allows its users to define scheduled tasks and specific handlers for them. Scheduled tasks are represented by the type ScheduledTask and are defined by adding a record to the scheduled_tasks table in the Atomia Billing database. Every scheduled task has an execution period, expiration period and whether weekend and holidays should be skipped. Every scheduled task has its handler which is specified by setting Name to the name of plug-in which is defined in the plugin table.

public class ScheduledTask
{
  /// <summary>
  /// Gets or sets the id.
  /// </summary>
  public Guid Id { get; set; }
  
  /// <summary>
  /// Gets or sets the last run time.
  /// </summary>
  public DateTime LastRunTime { get; set; }
  
  /// <summary>
  /// Gets or sets the next run time.
  /// </summary>
  public DateTime NextRunTime { get; set; }
  
  /// <summary>
  /// Gets or sets the expires after.
  /// </summary>
  public TimeSpan ExpiresAfter { get; set; }
  
  /// <summary>
  /// Gets or sets the name of the scheduled task.
  /// </summary>
  public string Name { get; set; }
  
  /// <summary>
  /// Gets or sets a value indicating whether task should be ran once.
  /// </summary>
  public bool RunOnce { get; set; }
  
  /// <summary>
  /// Gets or sets a value indicating whether holidays should be skipped.
  /// </summary>
  public bool SkipHolidays { get; set; }
  
  /// <summary>
  /// Gets or sets a value indicating whether weekends should be skipped.
  /// </summary>
  public bool SkipWeekend { get; set; }
  
  /// <summary>
  /// Gets or sets the execution period.
  /// </summary>
  public TimeSpan Period { get; set; }
  
  /// <summary>
  /// Gets or sets a value indicating whether this <see cref="ScheduledTask"/> is finished.
  /// </summary>
  public bool Finished { get; set; }
}

Here is an example of a task definition in the scheduled_tasks table:

name

NotificationHandler

last_run_time

2010-05-25 21:06:10.000

next_run_time

2010-05-25 22:06:07.000

expires_after

86400000000000

run_once

0

period

36000000000

finished

1

skip_holidays

0

skip_weekend

0

5.3.3.2. Creating scheduled task handlers

Custom scheduled task handlers can be created by implementing the interface IScheduledEventHandler defined in the Atomia.Billing.Core.Sdk.Plugins namespace. The type of handler for a specific task is defined by the Name property and it is instantiated when a task is about to be run. The scheduled task doesn't have any input parameters for its handler because it is used only for scheduling execution. All logic for getting needed data and its manipulation should be in the handler's HandleEvent method implementation.

5.3.3.2.1. Simple notifications handler example
public class NotificationHandler : IScheduledEventHandler
{
  /// <summary>
  /// Initializes a new instance of the <see cref="NotificationHandler"/> class.
  /// </summary>
  public NotificationHandler()
  {
    // Load SenderAddress value from configuration
    this.SenderAddress = "foo@bar.com";
  }
  
  /// <summary>
  /// Gets or sets SenderAddress.
  /// </summary>
  public string SenderAddress
  {
    get;
    set;
  }
  
  /// <summary>
  /// Gets or sets plugin name.
  /// </summary>
  public string Name
  {
    get
    {
      return "NotificationHandler";
    }
    
    set
    {
    }
  }
  
  /// <summary>
  /// Gets or sets IAtomiaBillingApi instance.
  /// </summary>
  public IAtomiaBillingApi AtomiaBillingApi
  {
    get;
    set;
  }
  
  /// <summary>
  /// Handles scheduled event.
  /// </summary>
  public void HandleEvent()
  {
    IList<MyNotification> notifications = this.GetNotifications();
    foreach (MyNotification notification in notifications)
    {
      this.SendEmail(this.SenderAddress, notification.To, notification.Subject, notification.Body);
    }
    
    // Do some more work...
  }
  
  private void SendEmail(string senderAddress, string recipient, string subject, string body)
  {
    this.AtomiaBillingApi.SendMail(senderAddress, recipient, subject, body, new List<MailAttachment>());
  }
  
  private IList<MyNotification> GetNotifications()
  {
    IList<MyNotification> notifications = new List<MyNotification>();
  
    // Place logic for getting notifications here.
    // ...
    return notifications;
  }
}

public class MyNotification
{
  /// <summary>
  /// Gets or sets recipient's email-address.
  /// </summary>
  public string To
  {
    get;
    set;
  }
  
  /// <summary>
  /// Gets or sets Subject.
  /// </summary>
  public string Subject
  {
    get;
    set;
  }
  
  /// <summary>
  /// Gets or sets Body.
  /// </summary>
  public string Body
  {
    get;
    set;
  }
}

5.4. API and SDK reference

Here you can find links to the Atomia Billing API and the SDK reference documentation.

5.4.1. Atomia Billing API and SDK

The Atomia Billing API exposes a set of methods for managing accounts, orders, subscriptions, invoices, payments, items, bulk jobs and scheduled events. A list of all methods can be found in the Atomia Billing API reference .

5.4.2. Atomia Order API

Atomia Order API reference links:

  • For Atomia Order API documentation check Atomia.Billing.Services.PublicOrderService namespace in Atomia Billing API reference.

5.5. Admin Panel scripting guide

The Admin Panel contains a scripting facility allowing you to run short scripts written in IronPython with easy access to the Atomia APIs.

5.5.1. Script console

The script console allows you to type and run the scripts in the browser without saving them anywhere. The script can be an arbitrary IronPyton script. When the script starts API proxy objects for easily accessing Atomia APIs are available without doing anything special. These objects are currently:

  • accountApi

  • billingApi

  • coreApi

  • userApi

Often an API method will take some class defined in the .NET class library as parameter, since we are using the .NET proxy objects generated from the WSDL of the APIs. Some examples for how to create such parameters are:

  • Guid

    import System
    
    guidvar = System.Guid("ae20d2c2-4bf5-46b7-ba5c-95b37fa28507")
                            
  • Dictionary<string,string>

    from System.Collections.Generic import Dictionary
    dictvar = Dictionary[str,str](somepythondict)
                            
  • List<string>

    from System.Collections.Generic import List
    listvar = List[str](['a','list','of','strings'])
                            

A simple example script showing how to use the scripting system follows, you can find more examples at the script archive:

import System

accountId = System.Guid("dd20d2c2-4bf5-46b7-ba5c-95b37fa28507")
subscriptionId = System.Guid("ae20d2c2-4bf5-46b7-ba5c-95b37fa28507")
account = billingApi.GetAccountById(accountId)
subscription = billingApi.GetSubscriptionById(subscriptionId, account)

print "Subscription status is " + subscription.State
                

5.5.2. Local scripts

In the local script section of the scripting facility all scripts stored on the local drive of the Admin Panel server is showed and can be easily executed. The files should be placed in C:\Program Files (x86)\Atomia\AdminPanel\App_Data\LocalScripts, and should contain a header with some metadata for the script, such as author and description. This header also contains a list of the parameters that the script expects. When the script is executed the user is asked for values for these parameters and the parameters are then available as variables with the same name once the script executes.

You can find an example script in the script library.

5.5.3. Script library

The script library works exactly like the local scripts section, i.e. the parameters are populated in the same way, the metadata is stored in the same, etc. The only difference is that the available scripts are found by accessing https://github.com/atomia/atomia-script-library in realtime. You are welcome to submit your scripts as pull requests for this repository to make them available to other Atomia users.

5.6. Code examples

5.6.1. How to place an order using the Atomia Billing API

To place an order, create an order object and then pass it to the CreateOrder API method. The following code demonstrates both steps.

5.6.1.1. Create an order object

To create an order set all customer's details, create lists of order lines and custom attributes.

// Fetch customer and desired product to be purchased
Account customer = AccountsHelper.GetAccount(customerId);
Item item = ItemHelper.GetItem(itemId);
Currency currency = CurrencyHelper.GetAccountDefaultCurrency(customerId)

Order order = new Order
{
  // specify customer's details
  CustomerId = customerId,
  CustomerFirstName = "John",
  // continue and add all customer details
  //...
  
  Currency = currency.Code,
  OrderDate = DateTime.Today,
  OrderType = Order.OrderTypeNormal,
  OrderLines = new List<OrderLine>
  {
    new OrderLine
    {
      ItemId = item.Id,
      ItemNumber = item.ArticleNumber,
      ItemName = item.Name,
      Price = item.Prices.Where(price => price.CurrencyId == currency.Id).First.Value,
      Discount = 0.00,
      Quantity = 1.00,
      Taxes = new List<OrderLineTax>
      {
        new OrderLineTax { Name = "VAT 25%", Percent = 25.00, ApplyToAmountOnly = true }
      },
      CustomAttributes = new List<OrderLineCustomAttribute>
      {
        new OrderLineCustomAttribute { Name = "attribute_name", Value = "attribute_value" }
      }
    }
  },
  CustomAttributes = new List<OrderCustomAttribute>
  {
    new OrderCustomAttribute { Name = "attribute_name", Value = "attribute_value" }
  }
};
5.6.1.2. Placing an order

To place an order simply call the CreateOrder API method with an order object.

public Order PlaceOrder(Order order)
{
using (IAtomiaBillingApi atomiaBillingApi = GetAtomiaBillingApiChannel())
  {
    order = atomiaBillingApi.CalculateTotals(order);
    return atomiaBillingApi.CreateOrder(order);
  }
}

public IAtomiaBillingApi GetAtomiaBillingApiChannel()
{
  // add logic to obtain channel
}

5.6.2. Process an order

The following code shows how to initiate order processing. The resulting order's status contains information on whether the processing was successful or not.

using (IAtomiaBillingApi atomiaBillingApi = GetAtomiaBillingApiChannel())
{
  return atomiaBillingApi.ProcessOrder(order); // returns processed order
}

5.6.3. Define a new product

The following code shows how to add a new product to the system:

Item item = new Item
{
  Category = @"item's category",
  ArticleNumber = "ITEM_1",            // unique name (or number) of the product
  Name = "article name",               //
  Recurring = true,                    // true if the product is recurring, otherwise false
  RenewalPeriod = 1,                   // period of renewal (number of months or years - defined by RenewalPeriodUnit)
  RenewalPeriodUnit = "Year",          // "Month" or "Year"
  RenewalItemId = Guid.Empty,          // if Guid.Empty, product will be renewed with the same product, otherwise product with the given id will be used for renewal
  ReProvision = false,                 // if true, reprovisioning should be done
  DeliveryDate = 0,                    // indicating whether this product should be delivered (when invoiced (0), or when invoiced + renewal period (1))
  AllowToSubresellers = true,          // indicating whether this product should be allowed to subresellers
  ProvisioningAllowedType = 1,         // should this product be provisioned without payment (refer to SDK reference for possible values)
  RenewingAllowedType = 1,             // should this product be renewed without payment (refer to SDK reference for possible values)
  ProvisioningService = "BasePackage", // name of the service used for provisioning
  Tax1 = "Swedish Tax",                // name of the tax plug-in used to get tax rules
  Tax2 = null,                         // name of the tax plug-in used to get tax rules
  Tax2Mode = 0,                        // indicates whether tax2 is cumulative
  RenewalInvoiceSendingPeriod = 30,    // number of days prior to renewal when invoice should be sent
};

Guid englishLang = Guid.Empty;
Guid swedishLang = Guid.Empty;

using (AtomiaAccountApiClient accountApi = new AtomiaAccountApiClient())
{
  englishLang = accountApi.GetLanguageByCode("EN").Id;
  swedishLang = accountApi.GetLanguageByCode("SV").Id;
}

item.MultilanguageNames = new Dictionary<Guid, string>
{
  new KeyValuePair(new Guid(englishLang), "ITEM_1_EN"),
  new KeyValuePair(new Guid(swedishLang), "ITEM_1_SV")
};

item.MultilanguageDescriptions = new Dictionary<Guid, string>
{
  new KeyValuePair(new Guid(englishLang), "Description of ITEM_1_EN"),
  new KeyValuePair(new Guid(swedishLang), "Description of ITEM_1_SV")
};

decimal defaultCurrencyPrice = 100;

item.Prices = new List<ItemPrice>
{
  new ItemPrice
  {
    ItemId = item.Id,
    CurrencyId = this.GetDefaultCurrency().Id, // GetDefaultCurrency should implement logic for getting currency for logged reseller
    Description = string.Empty,
    ResellerId = this.GetCurrentReseller().Id, // GetCurrentReseller should implement logic for getting currently logged in reseller
    Value = defaultCurrencyPrice;
  },
  new ItemPrice
  {
    ItemId = item.Id,
    CurrencyId = this.GetAdditionalCurrency().Id, // GetAdditionalCurrency should implement logic for getting additional (secondary) currency for logged reseller
    Description = string.Empty,
    ResellerId = this.GetCurrentReseller().Id, // GetCurrentReseller should implement logic for getting currently logged in reseller
    Value = this.CurrencyConverter(this.GetDefaultCurrency(), this.GetAdditionalCurrency(), defaultCurrencyPrice); // Implement conversion of amount from one currency to another
  }
}

using (IAtomiaBillingApi atomiaBillingApi = GetAtomiaBillingApiChannel())
{
  return atomiaBillingApi.CreateItem(item, this.GetCurrentReseller()); // returns created item
}

5.7. Templates

This section describes each template type and what placeholders that is available for them.

5.7.1. Add reverse DNS

The following placeholders are available for Add reverse DNS:

  • $Customer.<Member Name>$ The customer documentation is available here.

  • $Subscription.<Member Name>$ The properties are named equally as the properties that seen at our API documentation. Click here and navigate to Atomia.Billing.Core.Sdk.BusinessObjects -> Subscription and find the Properties part.

5.7.2. Add reverse delegation

The following placeholders are available for Add reverse delegation:

  • $Customer.<Member Name>$ The customer documentation is available here.

  • $Subscription.<Member Name>$ The properties are named equally as the properties that seen at our API documentation. Click here and navigate to Atomia.Billing.Core.Sdk.BusinessObjects -> Subscription and find the Properties part.

5.7.3. Add user

The following placeholders are available for Add user:

  • $Reseller.Name$ The reseller name.

  • $CustomerFirstName$ The customer first name.

  • $CustomerFirstName$ The customer first name.

  • $Username$ The username.

  • $Password$ The password.

  • $Reseller.HcpUrl$ Reseller HCP Url.

  • $Year$ Current year.

  • $ParentAccount.Name$ Parent account name.

5.7.4. Contact update reminder

The following placeholders are available for Contact update reminder:

  • $Customer.<Member Name>$ The customer documentation is available here.

  • $Environment.BillingUrl$ The Billing URL.

5.7.5. Credit invoice PDF

The following placeholders are available for Credit invoice:

  • $Reseller.<Member Name>$ The reseller data documentation is available here.

  • $data.CreditInvoiceReferenceNumber$ Credited invoice reference number.

  • $data.CreditInvoiceNumber$ Credited invoice number.

  • $data.InvoiceReferenceNumber$ Invoice reference number.

  • $data.InvoiceNumber$ Invoice number.

  • $data.DocumentDate$ Invoice date.

  • $data.CustomerNumber$ Invoice customer name.

  • $data.CustomerCompanyName$ Invoice customer company name.

  • $data.CustomerLegalNumber$ Invoice customer legal number.

  • $data.CustomerFirstName$ Invoice customer first name.

  • $data.CustomerLastName$ Invoice customer last name.

  • $data.CustomerAddress$ Invoice customer address.

  • $data.CustomerZip$ Invoice customer zip.

  • $data.CustomerCity$ Invoice customer city.

  • $data.CustomerCountryName$ Invoice customer country name.

  • $data.TotalPages$ Total pages.

  • $data.Tax1CreditedSum$ Tax 1 credited sum.

  • $data.Tax1Name$ Tax 1 name.

  • $data.Subtotal$ Subtotal.

  • $data.Total$ Total.

  • $data.Currency$ Invoice currency.

  • $data.IsCompany$ True if the invoice is for a company, otherwise false.

  • $data.ShowTax$ Show tax boolean.

  • $data.Pages[].Items[].Amount$ Amount.

  • $data.Pages[].Items[].Description$ Description.

  • $data.Pages[].Items[].Discount$ Discount.

  • $data.Pages[].Items[].ItemName$ Item name.

  • $data.Pages[].Items[].ItemNumber$ Item number.

  • $data.Pages[].Items[].Price$ Price.

  • $data.Pages[].Items[].Quantity$ Quantity.

  • $data.Pages[].Items[].Credited$ Credited.

  • $data.Pages[].Items[].Subtotal$ Subtotal.

5.7.6. Credit invoice email body

The following placeholders are available for Credit invoice email body:

  • $CustomerFirstName$ Customer first name.

  • $CustomerLastName$ Customer last name.

  • $CustomerNumber$ Customer number.

  • $InvoiceNumber$ Invoice number.

  • $InvoiceReferenceNumber$ Invoice reference number.

  • $InvoiceDate$ Invoice date.

  • $InvoiceDueDate$ Invoice due date.

  • $CreditedInvoiceNumber$ Credited invoice number.

  • $Subtotal$ Subtotal.

  • $Total$ Total.

  • $Moms$ Moms.

  • $Rabat$ Discount.

  • $ShowTax$ Show tax.

  • $items[].Name$ Item name.

  • $items[].Renewal$ Renewal period.

  • $items[].RenewalWithoutUnit$ Only the renewal period, no unit string included.

  • $items[].Price$ Price.

  • $items[].Qty$ Quantity.

  • $items[].Discount$ Discount.

  • $items[].Total$ Total.

  • $Reseller.<Member Name>$ The reseller data documentation is available here.

5.7.7. Credit invoice email subject

The following placeholders are available for Credit invoice email subject:

  • $InvoiceNumber$ Credited invoice reference number.

  • $Reseller.<Member Name>$ The reseller data documentation is available here.

5.7.8. Customer welcome

The following placeholders are available for Customer welcome:

  • $Username$ Username.

  • $ProductName$ Name of the product.

  • $Link$ Reset password link.

  • $CustomerId$ Customer name.

5.7.9. Invoice PDF

The following placeholders are available for Invoice PDF:

  • $data.InvoiceReferenceNumber$ Invoice reference number.

  • $data.InvoiceNumber$ Invoice reference number.

  • $data.DocumentDate$ Invoice date.

  • $data.CustomerNumber$ Customer number.

  • $data.PaymentDue$ Due date.

  • $data.CustomerCompanyName$ Customer company name.

  • $data.CustomerCompanyNumber$ Customer company number.

  • $data.CustomerFirstName$ Customer first name.

  • $data.CustomerLastName$ Customer last name.

  • $data.CustomerAddress$ Customer address.

  • $data.CustomerAddress2$ Customer address 2.

  • $data.CustomerAddress2Present$ True if customer address 2 is present.

  • $data.CustomerZip$ Customer zip.

  • $data.CustomerCity$ Customer city.

  • $data.CustomerCountryName$ Customer country name.

  • $data.TotalPages$ Total pages.

  • $data.Tax1Total$ Tax 1 total.

  • $data.Tax1Name$ Tax 1 name.

  • $data.Subtotal$ Subtotal.

  • $data.TaxTotal$ Tax total.

  • $data.TaxRate$ Tax rate.

  • $data.Total$ Total

  • $data.Currency$ Currency

  • $data.IsCompany$ True if the invoice is sent to a company.

  • $data.ShowTax$ Show tax.

  • $data.Autopay$ Autopay.

  • $data.AutopayDate$ Autopay date.

  • $data.InvoicePaid$ True if invoice status is Closed or ClosedUnsent.

  • $data.OnlinePaymentUrl$ Online payment URL.

  • $data.PayByInvoice$ Pay by invoice.

  • $data.InvoicePaymentPeriod$ Invoice payment period.

  • $data.PaymentMethod$ Used payment method if invoice is paid.

  • $data.TransactionId$ Transaction id if invoice is paid.

  • $data.TransactionTimestamp$ Transaction time if invoice is paid.

  • $data.HasPeriodData$ Specifies whether invoice has $data.StartTime$ and $data.EndTime$ values.

  • $data.StartTime$ Start time of postpaid invoice.

  • $data.EndTime$ End time of postpaid invoice.

  • $data.DefaultCountry$ Default country.

  • $data.Pages[].Items[].Amount$ Amount.

  • $data.Pages[].Items[].Description$ Description.

  • $data.Pages[].Items[].Discount$ Discount.

  • $data.Pages[].Items[].ItemName$ Item name.

  • $data.Pages[].Items[].ItemNumber$ Item number.

  • $data.Pages[].Items[].Recurring$ True if recurring, otherwise false.

  • $data.Pages[].Items[].RenewalPeriod$ Renewal period.

  • $data.Pages[].Items[].RenewalPeriodUnit$ Renewal period unit.

  • $data.Pages[].Items[].RenewalInYears$ Renewal in years.

  • $data.Pages[].Items[].Period$ Period.

  • $data.Pages[].Items[].Price$ Price.

  • $data.Pages[].Items[].Quantity$ Quantity.

  • $data.Pages[].Items[].Taxes$ Taxes.

  • $data.Pages[].Items[].ExpiryDate$ Expire date.

  • $data.Pages[].Items[].Usages$ Usages.

  • $data.Pages[].Items[].HasUsages$ Has usages.

  • $data.Pages[].Items[].Total$ Total.

  • $data.Pages[].Items[].SubscriptionNumber$ Invoice line's subscription number.

  • $data.Pages[].Items[].IsSubscriptionPresent$ True if the invoice line has a matching subscription, otherwise false.

  • $data.Pages[].Items[].IsCompany$ True if the customer is a company, otherwise false.

  • $data.Pages[].Items[].CustomerAddress2Present$ True if CustomerAddress2 exists, otherwise false.

  • $data.Pages[].Items[].CustomerNumber$ The customer number.

  • $data.Pages[].Items[].CustomerCompanyNumber$ The customer company number.

  • $data.Pages[].Items[].CustomerCompanyName$ The customer company name.

  • $data.Pages[].Items[].CustomerFirstName$ The customer first name.

  • $data.Pages[].Items[].CustomerLastName$ The customer last name.

  • $data.Pages[].Items[].CustomerAddress$ The customer address.

  • $data.Pages[].Items[].CustomerAddress2$ The second customer address.

  • $data.Pages[].Items[].CustomerZip$ The customer zip.

  • $data.Pages[].Items[].CustomerCity$ The customer city.

  • $data.Pages[].Items[].CustomerCountryName$ The customer country name.

  • $Reseller.<Member Name>$ The reseller data documentation is available here.

5.7.10. Invoice email

The following placeholders are available for Invoice email:

  • $CustomerFirstName$ Customer first name.

  • $CustomerLastName$ Customer last name.

  • $InvoiceNumber$ Invoice number.

  • $InvoiceReferenceNumber$ Invoice reference number.

  • $InvoiceDate$ Invoice date.

  • $InvoiceDueDate$ Invoice due date.

  • $Subtotal$ Subtotal.

  • $Subtotal$ Subtotal.

  • $Total$ Total.

  • $Username$ Username.

  • $Moms$ VAT.

  • $TaxRate$ Tax rate.

  • $RabatExists$ True if discount exists.

  • $Rabat$ Discount.

  • $ShowTax$ Show tax.

  • $OnlinePaymentUrl$ Online payment URL.

  • $Autopay$ Autopay.

  • $AutopayDate$ Autopay date.

  • $InvoicePaid$ Invoice paid.

  • $Currency$ Currency.

  • $PaymentMethod$ Payment method.

  • $TransactionId$ Transaction ID.

  • $TransactionTimestamp$ Transaction timestamp.

  • $HasPeriodData$ Specifies whether invoice has $StartTime$ and $EndTime$ values.

  • $StartTime$ Start time of postpaid invoice.

  • $EndTime$ End time of postpaid invoice.

  • $items[].Name$ Item name.

  • $items[].Renewal$ Renewal period.

  • $items[].RenewalWithoutUnit$ Only the renewal period, no unit string included.

  • $items[].Price$ Price.

  • $items[].Qty$ Quantity.

  • $items[].Discount$ Discount.

  • $items[].Total$ Total.

  • $items[].SubscriptionNumber$ Invoice line's subscription number.

  • $items[].IsSubscriptionPresent$ True if the invoice line has a matching subscription, otherwise false.

  • $items[].IsCompany$ True if the customer is a company, otherwise false.

  • $items[].CustomerAddress2Present$ True if CustomerAddress2 exists, otherwise false.

  • $items[].CustomerNumber$ The customer number.

  • $items[].CustomerCompanyNumber$ The customer company number.

  • $items[].CustomerCompanyName$ The customer company name.

  • $items[].CustomerFirstName$ The customer first name.

  • $items[].CustomerLastName$ The customer last name.

  • $items[].CustomerAddress$ The customer address.

  • $items[].CustomerAddress2$ The second customer address.

  • $items[].CustomerZip$ The customer zip.

  • $items[].CustomerCity$ The customer city.

  • $items[].CustomerCountryName$ The customer country name.

  • $Customer.<Member Name>$ The customer documentation is available here.

  • $Reseller.<Member Name>$ The reseller data documentation is available here.

5.7.11. Invoice reminder

The following placeholders are available for Invoice reminder:

  • $CustomerFirstName$ Customer first name.

  • $CustomerLastName$ Customer last name.

  • $CustomerCompanyName$ Customer company name.

  • $IsCompany$ True if the customer is a company, otherwise false.

  • $CustomerAddress$ Customer address.

  • $CustomerAddress2$ Customer address 2.

  • $CustomerAddress2Present$ True if CustomerAddress2 exists, otherwise false.

  • $CustomerZip$ Customer zip.

  • $CustomerCity$ Customer city.

  • $CustomerCountryName$ Customer country name.

  • $InvoiceNumber$ Invoice number.

  • $InvoiceReferenceNumber$ Invoice reference number.

  • $InvoiceDate$ Invoice date.

  • $InvoiceDueDate$ Invoice due date.

  • $DaysPastDue$ Days past due date.

  • $Subtotal$ Subtotal.

  • $Total$ Total.

  • $Moms$ VAT.

  • $TaxRate$ VAT rate.

  • $Rabat$ Discount.

  • $ShowTax$ Show tax.

  • $TotalPaid$ Total amount of payments made for invoice.

  • $OutstandingAmount$ Remaining unpaid amount for invoice.

  • $ReminderDate$ Reminder creation date.

  • $items[].Name$ Item name.

  • $items[].Renewal$ Renewal period.

  • $items[].RenewalWithoutUnit$ Only the renewal period, no unit string included.

  • $items[].Price$ Price.

  • $items[].Qty$ Quantity.

  • $items[].Discount$ Discount.

  • $items[].Total$ Total.

  • $items[].IsSubscriptionPresent$ True if the invoice line has a matching subscription, otherwise false.

  • $items[].IsCompany$ True if the customer is a company, otherwise false.

  • $items[].CustomerAddress2Present$ True if CustomerAddress2 exists, otherwise false.

  • $items[].CustomerNumber$ The customer number.

  • $items[].CustomerCompanyNumber$ The customer company number.

  • $items[].CustomerCompanyName$ The customer company name.

  • $items[].CustomerFirstName$ The customer first name.

  • $items[].CustomerLastName$ The customer last name.

  • $items[].CustomerAddress$ The customer address.

  • $items[].CustomerAddress2$ The second customer address.

  • $items[].CustomerZip$ The customer zip.

  • $items[].CustomerCity$ The customer city.

  • $items[].CustomerCountryName$ The customer country name.

  • $OnlinePaymentUrl$ Online payment URL.

  • $Customer.<Member Name>$ The customer documentation is available here.

  • $Reseller.<Member Name>$ The reseller data documentation is available here.

5.7.12. Invoice reminder SMS

The following placeholders are available for Invoice reminder SMS:

  • $InvoiceId$ Invoice reference number.

  • $CustomerNumber$ Cusomer number.

  • $Days$ Days past due date.

  • $Reseller.<Member Name>$ The reseller data documentation is available here.

5.7.13. Order additional mail

The following placeholders are available for Order additional mail:

  • $name$ Name.

  • $company$ Company.

  • $orgid$ Organization ID.

  • $domain$ Domain.

  • $time$ Time.

  • $version$ Version.

5.7.14. Order confirmation

The following placeholders are available for Order confirmation:

  • $OrderNumber$ Order number.

  • $CustomerFirstName$ Customer first name.

  • $CustomerLastName$ Customer last name.

  • $Rabat$ Discount.

  • $Moms$ VAT.

  • $TaxRate$ VAT rate.

  • $Total$ Total.

  • $ShowTax$ Show tax.

  • $PayableDomains$ Payable domains.

  • $Currency$ Currency.

  • $OverDebtLimit$ Flag which is set to true if order is not going to be processed because customer is over debt limit.

  • $items[].Name$ Item name.

  • $items[].Qty$ Quantity.

  • $items[].Price$ Price.

  • $items[].Discount$ Discount.

  • $items[].Total$ Total.

  • $items[].Renwal$ Renewal period.

  • $items[].RenewalWithoutUnit$ Only the renewal period, no unit string included.

  • $Reseller.<Member Name>$ The reseller data documentation is available here.

5.7.15. Post subscription provisioning

The following placeholders are available for Post subscription provisioning:

  • $Customer.<Member Name>$ The customer documentation is available here.

  • $Subscription.<Member Name>$ The properties are named equally as the properties that seen at our API documentation. Click here and navigate to Atomia.Billing.Core.Sdk.BusinessObjects -> Subscription and find the Properties part.

5.7.16. Recurring payment core

The following placeholders are available for Recurring payment core:

  • $CustomerFirstName$ Customer first name.

  • $InvoiceNumber$ Invoice reference number.

  • $InvoiceDueDate$ Due date.

  • $Total$ Total.

  • $Currency$ Currency.

  • $RetryScheduled$ Retry scheduled.

  • $RetryTime$ Retry time.

  • $Reseller.<Member Name>$ The customer documentation is available here.

5.7.17. Recurring payment generic workflow

The following placeholders are available for Recurring payment generic workflow:

  • $CustomerFirstName$ Customer first name.

  • $CustomerNumber$ Customer number.

  • $InvoiceNumber$ Invoice number.

  • $InvoiceReferenceNumber$ Invoice reference number.

  • $InvoiceDueDate$ Due date.

  • $Total$ Total.

  • $Currency$ Currency.

  • $RetryScheduled$ Retry scheduled.

  • $RetryTime$ Retry time.

  • $PaymentMethod$ Payment method.

  • $Reseller.<Member Name>$ The reseller data documentation is available here.

5.7.18. Renewal heads up nofication

The following placeholders are available for Renewal heads up nofication:

  • $CustomerFirstName$ Customer first name.

  • $customerLastName$ Customer last name.

  • $items[].Domain$ Domain.

  • $items[].InvoiceDate$ Invoice date.

  • $items[].RenewalDate$ Renewal date.

  • $Reseller.<Member Name>$ The reseller data documentation is available here.

5.7.19. Subscription provision status changed

The following placeholders are available for Subscription provision status changed:

  • $CustomerFirstName$ Customer first name.

  • $CustomerLastName$ Customer last name.

  • $CustomerNumber$ Customer number.

  • $domainName$ Domain name.

  • $Reseller.<Member Name>$ The reseller data documentation is available here.

5.7.20. Termination

The following placeholders are available for Termination:

  • Not available at this moment.

5.7.21. Transfer in notification

The following placeholders are available for Transfer in notification:

  • $CustomerFirstName$ Customer first name.

  • $CustomerLastName$ Customer last name.

  • $DomainName$ Domain name.

  • $Reseller.<Member Name>$ The reseller data documentation is available here.

5.7.22. Transfer out notification

The following placeholders are available for Transfer out notification:

  • $CustomerFirstName$ Customer first name.

  • $CustomerLastName$ Customer last name.

  • $DomainName$ Domain name.

  • $Reseller.<Member Name>$ The reseller data documentation is available here.

5.7.23. Downgrade refund email

The following placeholders are available for Downgrade refund emails:

  • $CustomerId$ Customer id.

  • $BankAccount$ The bank account specified by user.

  • $NewItemName$ Item that the user downgraded to.

  • $OldItemName$ Item that the user downgraded from.

5.7.24. Reseller data structure

This is the parameters available in the reseller structure.

  • $Reseller.Name$ Reseller name.

  • $Reseller.HomeUrl$ Home URL.

  • $Reseller.BillingUrl$ Billing URL.

  • $Reseller.OrderUrl$ Order URL.

  • $Reseller.HcpUrl$ Hosting control panel URL.

  • $Reseller.SupportUrl$ Support URL.

  • $Reseller.ContactUrl$ Contact URL.

  • $Reseller.SupportEmailAddress$ Support email address.

  • $Reseller.SupportPhoneNumber$ Support phone number.

  • $Reseller.SalesEmailAddress$ Sales email address.

  • $Reseller.SalesPhoneNumber$ Sales phone number.

  • $Reseller.FacebookUrl$ Facebook URL.

  • $Reseller.TwitterUrl$ Twitter URL.

  • $Reseller.BloggerUrl$ Blogger URL.

  • $Reseller.BankName$ Bank name.

  • $Reseller.BankAccNo$ Bank account number.

  • $Reseller.BankIdNumber$ Bank ID number.

  • $Reseller.IBAN$ IBAN.

  • $Reseller.SWIFT$ SWIFT.

  • $Reseller.CompanyNumber$ Company number.

  • $Reseller.GooglePlusUrl$ Google plus URL.

  • $Reseller.LinkedInUrl$ LinkedIn URL.

  • $Reseller.Address$ Reseller main address.

  • $Reseller.Address2$ Reseller main address 2.

  • $Reseller.City$ Reseller main address city.

  • $Reseller.Zip$ Reseller main address zip.

  • $Reseller.Phone$ Reseller main address phone.

  • $Reseller.Fax$ Reseller main address fax.

  • $Reseller.Country$ Reseller main address country.

  • $Reseller.LegalNumber$ Legal number.

5.7.25. Customer data structure

This is the parameters available in the customer structure.

  • $Customer.Id$ The customer id.

  • $Customer.ParentAccountId$ The customers parent account id.

  • $Customer.Name$ The customer name.

  • $Customer.Description$ The customer description.

  • $Customer.State$ The customer state.

  • $Customer.Type$ The customer type.

  • $Customer.DefaultLanguage.Id$ The customer language id.

  • $Customer.DefaultLanguage.Name$ The customer language name.

  • $Customer.DefaultLanguage.Iso639Name$ The customer language Iso639Name.

  • $Customer.DefaultLanguage.Culture$ The customer language culture.

  • $Customer.ParentAccountName$ The parent account name of the customer.

  • $Customer.MainAddress.<Member Name>$ The address data documentation is available here.

  • $Customer.ShippingAddress.<Member Name>$ The address data documentation is available here.

  • $Customer.BillingAddress.<Member Name>$ The address data documentation is available here.

  • $Customer.Terminated$ True if the customer is terminated, otherwise false.

5.7.26. Address data structure

This is the parameters available in the address structure.

  • $Address.Id$ The address id.

  • $Address.CompanyName$ The company name.

  • $Address.CompanyNumber$ The company number.

  • $Address.FirstName$ The first name.

  • $Address.LastName$ The last name.

  • $Address.Address$ The address.

  • $Address.Address2$ The secondary address.

  • $Address.City$ The city.

  • $Address.Zip$ The zip code.

  • $Address.Country.Name$ The country name.

  • $Address.Country.Code$ The country code.

  • $Address.State$ The state.

  • $Address.Email$ The email.

  • $Address.Phone$ The phone.

  • $Address.Fax$ The fax.

  • $Address.Mobile$ The mobile.