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 .
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.
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.
Create in root folder subfolder named
Solution
where solution items will be placed.
Create another subfolder,
lib
for placing all needed libraries.
Copy the contents of the downloaded Atomia Billing SDK package (
Atomia.Billing.Core.Sdk
folder) to
lib
folder.
Open Visual Studio 2010 and create a blank solution named
AtomiaBillingSdkExample
and place it in the
Solution
folder.
Create a new class library project named
Plugins
and change its assembly name and default namespace to
AtomiaBillingSdkExample.Plugins
.
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
:
Add a new class with the name
NotificationHandler
.
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; } } }
You should now be able to build the project.
To test the created class, add a new Test project named
UnitTests
and change its assembly name and default namespace to
AtomiaBillingSdkExample.UnitTests
.
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(); } } }
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 .
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; } } }
Here is list of all currently supported plug-in interfaces:
Interface type | Description |
| Use this interface to write a plugin for fetching customer address information from an address service based on the personal or corporate identification number. |
| Used for applying specific campaign rules and discounts for an order. |
| Used for creating custom conditions for campaign discounts. |
| Used for creating string representations of invoices for e-mails, PDF documents etc. |
| Used for implementing message sending via specific protocols (email, SMS etc.). |
| Used for parsing payments from payment report files. |
| Used for implementing payment operations (transaction handling) for payment gateways. |
| Used for applying custom rules on payments (e.g. check balance or payment status, check if currency matches invoice etc.). |
| Used for defining provisioning operations (package/subscription handling, order validation etc.). |
| Used for handling scheduled tasks when "heart beat" occurs. |
| 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
.
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 .
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); }
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); }
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; }
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.
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. }
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.
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.
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); }
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.
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 .
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; } }
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.
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>
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.
Here is a list of defined events which can be used for adding custom logic into workflows:
Event name | Description |
| Occurs when account address is updated. |
| Occurs when account is created. |
| Occurs when account is about to be created. |
| Occurs before account is terminated. |
| Occurs when account is about to be updated. |
| Occurs when account is terminated. |
| Occurs whent account termination request is created. |
| Occurs whent account termination request is about to be created. |
| Occurs when account is updated. |
| Occurs when single credit invoice is about to be sent. |
| Occurs when customer is created. |
| Occurs when customer status is changed. |
| Occurs when Ticker sends heart beat signal. |
| Occurs when invoice is paid. |
| Occurs before invoice is created. |
| Occurs when single invoice is about to be sent. |
| Occurs before invoices are sent. |
| Occurs when order is created. |
| Occurs when order totals are about to be calculated. |
| Occurs when order is about to be created. |
| Occurs before order is processed. |
| Occurs when order is processed. |
| Occurs when order is processed, and before we should create an invoice for each subscription. |
| Occurs when payment is created. |
| Occurs when payment is about to be created. |
| Occurs when Subscription is about to be updated (status changed). |
| Occurs when Subscription is created. |
| Occurs when subscription is extended. |
| Occurs when Subscription is updated (status changed). |
| Occurs when Subscription is about to be created. |
| Occurs when Subscription is about to be terminated. |
| Occurs when Subscription is about to be updated. |
| Occurs when subscription is provisioned when payment is created. |
| Occurs when Subscription provisioning status is changed. |
| Occurs when Subscription is terminated. |
| Occurs when Subscription is updated. |
| Occurs when upgrade order is created. |
Complete documentation can be found in the Atomia Billing API reference .
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. } }
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 |
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.
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; } }
Here you can find links to the Atomia Billing API and the SDK reference documentation.
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 .
Atomia Order API reference links:
For Atomia Order API documentation check Atomia.Billing.Services.PublicOrderService namespace in Atomia Billing API reference.
The Admin Panel contains a scripting facility allowing you to run short scripts written in IronPython with easy access to the Atomia APIs.
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
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.
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.
To place an order, create an order object and then pass it to the
CreateOrder
API method. The following code demonstrates both steps.
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" } } };
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 }
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 }
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 }
This section describes each template type and what placeholders that is available for them.
The following placeholders are available for Add reverse DNS:
The following placeholders are available for Add reverse delegation:
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.
The following placeholders are available for Contact update reminder:
$Customer.<Member Name>$ The customer documentation is available here.
$Environment.BillingUrl$ The Billing URL.
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.
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.
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.
The following placeholders are available for Customer welcome:
$Username$ Username.
$ProductName$ Name of the product.
$Link$ Reset password link.
$CustomerId$ Customer name.
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.
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.
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.
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.
The following placeholders are available for Order additional mail:
$name$ Name.
$company$ Company.
$orgid$ Organization ID.
$domain$ Domain.
$time$ Time.
$version$ Version.
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.
The following placeholders are available for Post subscription provisioning:
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.
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.
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.
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.
The following placeholders are available for Termination:
Not available at this moment.
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.
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.
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.
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.
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.
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.