Thursday, January 31, 2013

Targeted Email : ATG

Creating Targeted E-mail

You create targeted e-mail using the atg.userprofiling.email.TemplateEmailInfoImpl class. This class draws the message body of an e-mail from a page template, invokes a MessageContentProcessor component to process the content, and then passes the resulting JavaMail object to the TemplateEmailSender component, which sends the message. The properties of a TemplateEmailInfoImpl object store values that are specific to an individual e-mail campaign, so you should create a separate instance of this class for each campaign.

You can use targeted e-mail to produce e-mail messages that are personalized for different profiles. The e-mail content is based on a JSP or JHTML template, specified with the templateURL property of a TemplateEmailInfoImpl object. Using the dynamic elements of the Page template, you can customize the e-mail for each of your users according to their profile attributes. Here is a JSP example :

<html>
<dsp:importbean bean="/atg/userprofiling/Profile"/>
<p>Dear <dsp:valueof bean="Profile.firstName">Customer</dsp:valueof>,
<p>We're writing to tell you about some exciting new offers ...
</html>



The template yields different message text depending on the attributes of the recipient’s profile. You can use ATG’s targeting servlet beans to personalize the content of the message as well. If the template page contains a targeting servlet bean, the servlet bean behaves just as it would in any other page, rendering content targeted to the current profile.

The TemplateEmailInfoImpl object contains the standard e-mail message attributes as properties. When it invokes the createMessage method, the specified e-mail attributes create and fill in a javax.mail.Message object, which can then be sent to the desired recipient using the TemplateEmailSender service

Note that TemplateEmailInfoImpl is a subclass of the abstract class TemplateEmailInfo. If you want to create your own template e-mail implementation, you can create a subclass of TemplateEmailInfo that implements all of its methods, and use it as you would use TemplateEmailInfoImpl.



Creating a Targeted E-mail Template

The page template for your targeted e-mail is specified by the templateURL property of the TemplateEmailInfoImpl object. You create this template like any other page. Because the template page is passed to the standard ATG servlet pipeline, it can include ATG servlet beans and additional JHTML or JSP tags.

Here is a sample JSP targeted e-mail template. It displays a few profile attributes and contains a targeting servlet bean. Assuming the targeting rules used in the TargetingForEach servlet bean’s targeter service depend on the attributes of the Profile component, the targeting results displayed in the e-mail message will be different for each profile.

<dsp:importbean bean="/atg/userprofiling/Profile"/>
<p>Dear <dsp:valueof bean="Profile.firstName"/>
<dsp:valueof bean="Profile.lastName"/>,


<p>Thank you for your order! It has been shipped today to:

<blockquote><pre>
<dsp:valueof bean="Profile.address"/><br>
<dsp:valueof bean="Profile.city"/>, <dsp:valueof bean="Profile.State"/>
<dsp:valueof bean="Profile.zipCode"/>
</pre></blockquote>

<p>Since your last order, we've introduced some great new products. If you enjoy
your new <dsp:valueof bean="Profile.lastProductShipped"/>, you may also be
interested in ordering some of these great widgets:
<p>

<dsp:droplet name="/atg/targeting/TargetingForEach">
  <dsp:param bean="/targeters/WidgetTargeter" name="targeter"/>
  <dsp:oparam name="output">
    <dsp:valueof param="element.name"/><br>
  </dsp:oparam>
</dsp:droplet>

<p>Thank you for shopping with us.

<p>Sincerely,
The Customer Service Team
help@example.com
http://www.example.com


Note that if your template page contains links to other URLs on your site, you must specify them as absolute URLs; otherwise the e-mail recipients will not be able to access the linked pages. Use the full http://server:port/... form of the URL. For example, in JSP code:

<dsp:img src="http://myserver:80/images/logo.gif">
<dsp:a href="http://myserver:80/help/index.html">help</dsp:a>


The following example will not work, because it uses relative URLs:

<dsp:img src="images/logo.gif">
<dsp:a href="help/index.html">help</dsp:a>


Specifying E-mail Fields in the Template

By default, the Personalization module takes the values for various fields of an e-mail message from properties of the TemplateEmailInfoImpl object. This behavior means that all messages using the same object will have identical properties. For example, suppose the value of the messageSubject property of the TemplateInfoImpl component is “Hello”. All e-mail messages created by this component will contain the subject line “Hello”.

You can override the values of these properties in a specific e-mail template by doing the following:

In the template, include parameters with the same names as the corresponding TemplateEmailInfoImpl properties.

Set the fillFromTemplate value of the TemplateEmailInfoImpl object to true.

For example, you can set a different subject line for a specific mailing by including a statement as follows in the template for that mailing:


JSP example:
<dsp:setvalue value="Your order has shipped" param="messageSubject"/>

JHTML example:

<setvalue param="messageSubject" value="Your order has shipped">

Because these parameters are evaluated for each message individually, they can include dynamic content. For example, you could set the message subject as follows.


JSP example:

<dsp:setvalue value='<%="Order " + request.getParameter("order.id")+
" has shipped"%>' param="messageSubject"/>


JHTML example:

<setvalue param="messageSubject"
  value="Order 'request.getParameter("order.id")' has shipped">


There are seven parameters that you can use in this way to override the corresponding TemplateEmailInfoImpl properties:

mailingName

messageFrommessageTo

messageReplyTo

messageSubject

messageCc

messageBcc



You can use any combination of these parameters in an e-mail template; if any of these parameters is not set, the system uses the corresponding property value instead.

For example, you could globally set the messageCc property to Cc yourself, but you could override this property if you wanted to Cc or Bcc other people to make them aware of a particular e-mail campaign. The messageCc and messageBcc parameters are defined as a string of e-mail addresses separated by commas. For example, you could add the following lines to a template file.


JSP example:

<dsp:setvalue value=lewis@example.com,everyone@example.com
 param="messageCc"/>
<dsp:setvalue value=management@example.com,bob@example.com
 param="messageBcc"/>


JHTML example:

<setvalue param="messageCc" value=lewis@example.com,everyone@example.com>
<setvalue param="messageBcc" value=management@example.com,bob@example.com>



Note: Property values you set through an e-mail template are not persisted to the database. Only the values that are set in the TemplateEmailInfoImpl object are persisted.



Tuesday, January 22, 2013

Repricing Orders : ATG

If your site has any pages where you need to reprice an Order, but you cannot do so through a form action and corresponding handle method, use the RepriceOrderDroplet servlet bean. By default, the servlet bean is configured to invoke the repriceAndUpdateOrder pipeline, which reprices the Order by calling the repriceOrder pipeline and then updates the Order by calling OrderManager.updateOrder().

The RepriceOrderDroplet servlet bean is an instance of atg.commerce.order.purchase.RepriceOrder, which extends atg.service.pipeline.servlet.PipelineChainInvocation. ATG Commerce provides an instance of RepriceOrder, which is located in Nucleus at /atg/commerce/order/purchase/RepriceOrderDroplet.

The RepriceOrder class provides the required objects for executing a repricing pipeline as convenient properties. Typically, execution of a repricing pipeline requires the Order, the Profile, the OrderManager, and the user’s PricingModelHolder. RepriceOrder is conveniently configured to reference these objects, which means that a page developer doesn’t need to supply them as input parameters every time the RepriceOrderDroplet servlet bean is invoked. Consequently, the only required parameter that must be supplied is the pricing operation to execute. Acceptable pricing operations are defined in the atg.commerce.pricing.PricingConstants interface:

They are the following:




Pricing Operation
Pricing Constant
ORDER_TOTAL
PricingConstants.OP_REPRICE_ORDER_TOTAL
ORDER_SUBTOTAL
PricingConstants.OP_REPRICE_ORDER_SUBTOTAL
ORDER_SUBTOTAL_SHIPPING
PricingConstants.OP_REPRICE_ORDER_SUBTOTAL_SHIPPING
ORDER_SUBTOTAL_TAX
PricingConstants.OP_REPRICE_ORDER_SUBTOTAL_TAX
ITEMS
PricingConstants.OP_REPRICE_ITEMS
SHIPPING
PricingConstants.OP_REPRICE_SHIPPING
ORDER
PricingConstants.OP_REPRICE_ORDER
TAX
PricingConstants.OP_REPRICE_TAX
NO_REPRICE
PricingConstants.OP_NO_REPRICE





The following code sample is taken from RepriceOrderDroplet.properties and indicates its default configuration:

$class=atg.commerce.order.purchase.RepriceOrder
$scope=request

defaultPipelineManager=/atg/commerce/PipelineManager
defaultChainId=repriceAndUpdateOrder
order^=/atg/commerce/ShoppingCart.current
profile=/atg/userprofiling/Profile
orderManager=/atg/commerce/order/OrderManager
userPricingModels=/atg/commerce/pricing/UserPricingModels

This default configuration enables a page developer to include the RepriceOrderDroplet servlet bean on any shopping cart page that requires the repricing and updating of Orders with the following JSP code:

<dsp:droplet name="RepriceOrderDroplet">
  <dsp:param value="ORDER_SUBTOTAL" name="pricingOp"/>
</dsp:droplet>

Friday, January 18, 2013

Modifying the Current Order : ATG

To modify an Order, you must supply either a CatalogRefId of a CommerceItem or a ShippingGroupCommerceItemRelationship ID. It is recommended that you modify an Order by ShippingGroupCommerceItemRelationship ID, especially if you intend to support complex product-SKU relationships, such as multiple CommerceItems with the same catalogRefId (SKU ID) or multiple shipping groups. For example, a customer could order 5 of a given item and choose to ship a quantity of 3 to a home address and the remaining 2 to a work address. In this example, to remove the items being shipped to the work address, you would modify (and ultimately remove) the ShippingGroupCommerceItemRelationship instead of modifying the CommerceItem.

    The following subsections describes both order modification methods:

    1.Modifying an Order by catalogRefId

    2.Modifying an Order by ShippingGroupCommerceItemRelationship ID
   
   
1.Modifying an Order by catalogRefId:


Modifying orders by catalogRefId works for very simple sites. Because it does not provide the granularity necessary to delete just a part of a CommerceItem, it is not recommended for sites with complex features, such as multiple CommerceItems with the same catalogRefId or multiple shipping groups.

You can use the following CartModifierFormHandler methods to modify an Order by catalogRefId:

    1.handleSetOrder()

    2.handleRemoveItemFromOrder()

    3.handleMoveToPurchaseInfo()

Refer to Understanding the CartModifierFormHandler for more information on these handle methods.

To change the quantities of items in an Order using the catalogRefIds of CommerceItems, call the CartModifierFormHandler.handleSetOrder() method for each CommerceItem whose quantity you want to change and pass in the catalogRefId and quantity for the CommerceItem. This is illustrated in the following JSP code:

<dsp:input value='<dsp:valueof param="CommerceItem.quantity"/>'
type="text" name='<dsp:valueof param="CommerceItem.catalogRefId"/>'>


Note that if no quantity is found for a CommerceItem, then the CommerceItem is removed from the Order.

To remove items from an Order using the catalogRefIds of CommerceItems, edit the JSPs that invoke the CartModifierFormHandler handle methods that delete items from the Order. Populate the form handler’s removalCatalogRefIds array with the catalogRefIds of the CommerceItems to be removed. For example, you can populate the array using following JSP code:

<dsp:input bean="CartModifierFormHandler.removalCatalogRefIds"
paramvalue="CommerceItem.catalogRefId" type="checkbox"/>



Modifying an Order by ShippingGroupCommerceItemRelationship ID:


If your site supports complex product-SKU relationships (for example, multiple CommerceItems with the same catalogRefId) or multiple shipping groups, then it is recommended that you modify an Order using the IDs of the ShippingGroupCommerceItemRelationship objects in the Order. Doing so makes the changes at the CommerceItem-to-ShippingGroup level.

You can use the following CartModifierFormHandler methods to modify an Order by ShippingGroupCommerceItemRelationship ID:

    1.handleSetOrderByRelationshipId()

    2.handleRemoveItemFromOrderByRelationshipId()

    3.handleMoveToPurchaseInfoByRelId()

Refer to Understanding the CartModifierFormHandler for more information on these handle methods.

To change the quantity of an item in the Order, pass the new quantity into the ShippingpingGroupCommerceItemRelationship, as shown in the following JSP example:

<dsp:input value='<dsp:valueof param="SgCiRelationship.quantity"/>'
type="text" name='<dsp:valueof param="SgCiRelationship.Id"/>'>


To delete an item from the Order, pass the ID of the associated ShippingGroupCommerceItemRelationship into the form handler’s removalRelationshipIds property, as shown in the following JSP example:

<dsp:input bean="CartModifierFormHandler.removalRelationshipIds"
paramvalue="SgCiRelationship.Id" type="checkbox"/>

Wednesday, January 16, 2013

Setting Up Access Control : ATG CSC


When ATG Commerce Service Center is installed, it is preconfigured with various access rights, global roles, and access controllers. These elements are used to restrict access to certain pages in the Commerce Service Center.

1. Default Access Control Configuration:


The default access control configuration provided with ATG Commerce Service Center includes a number of access rights, roles, and access controllers. The access controllers are Nucleus components, which are added to your ATG installation when you install ATG Commerce Service Center. The access rights and roles are repository data that you import into your database (from supplied XML files) as a configuration step after you install ATG Commerce Service Center. For more information about installing and configuring ATG Commerce Service Center.


1.1 Access Rights:


The basic security unit is the access right. The access rights for ATG Commerce Service Center are subdivided according to the following classification:

a) Tab Security - Security for the Commerce Service Center tab accessed through the Service Framework
     tabs

b) Panel Security - Security for a panel

c) Element Security - Security for a data field or action element within a panel

ATG Commerce Service Center comes preconfigured with access rights that have been designed based on specific CSR agent activities. A subset of these rights is assigned to each Commerce Service Center role, and you assign the appropriate roles to agents to give them the access rights they need.

Add additional information and descriptions on these preconfigured tables. The following table summarizes the preconfigured Commerce Service Center access rights:


ID                                                   Name

issueCredit                                       commerce-custsvc-issue-credit-privilege
adjustPrice                                       commerce-custsvc-adjust-price-priviege.



1.2 Global Roles:


ATG Commerce Service Center comes preconfigured with four global roles for controlling the access rights granted to CSR agents. The Commerce Service Center roles use template roles to simplify their configuration:

a) csrTicketing - Includes the access rights necessary to use the Ticketing UI

b) csrOrders - Includes the access rights needed to create and modify orders. In addition,
    this role includes csrTicketing as a template role, so all ticketing access rights are included

c) csrProfiles - Includes the access rights needed to create and modify customer profiles.
    In addition, this role includes csrTicketing as a template role, so all ticketing access rights
    are included

d) csrManager - Includes csrOrders (and thus csrTicketing) and csrProfiles as template roles

For a list of all access rights for each role, and a description of the access right
When you create an agent’s profile in the Internal User Profile Repository, you assign the agent a role that corresponds to the tasks the agent is authorized to perform. For example, a typical agent may be able to create and modify orders, but only a manager can override prices and issue credits.


1.3 Creating New Roles:


If you have requirements that none of the existing roles meet, you can create new roles.

To create new roles:

1. Open the BCC > Personalization page.

2. Select Internal Users.

3. Use the Show menu to select Organizations and Roles.

4. Open the Global Roles folder. Identify the location to store the role.

5. Click the Create New icon to create the new role.

6. Enter the name of the new role.

7. Select the Access Rights tab to add existing access rights, or to create new access rights by
    specify Direct Access Rights or incorporating the access rights from existing roles by using
    the Template Role field.

8. Once you are finished, click Create to save your settings.



2. Creating Agent Profiles:



By default, ATG Commerce Service Center is not preconfigured with any agent profiles. As part of setting up ATG Commerce Service Center, you need to:

1. Create a profile in the Internal User Profile Repository for each agent.

2. Assign each profile a global role.

If you want to create a sample account for evaluation purposes, ATG Commerce Service Center provides a file named csrEvalUser.xml. Importing the data from this file creates an account whose username and password are both csr. This account is assigned the csrManager role, which means it has access to all areas and activities in the Commerce Service Center and the Ticketing UI.

Note: This account should be added only to the database that is provided with the ATG platform for evaluation purposes. You should not include it in a production database, as this is a serious security risk.





Configuring Return Shipping Addresses : ATG CSC


To configure the shipping address for returns, perform the following steps:


Create a JSP file that contains your shipping address. 


For example:


<ul class="atg_commerce_csr_simpleList">
<li><strong>Ship return items to:</strong></li>
<li>My Company</li>
<li>Attn:Returns</li>
<li>100 Main Street</li>
<li>My City, My State</li>
<li>My Zip</li>
</ul>


Open the atg/commerce/custsvc/ui/renderers/
ReturnShippingAddressRenderer.properties file and provide the location of the new JSP file and the contextRoot variable.


For example:


# This is the default renderer for the returns line item page, default
# renderers will all have their id property set to "default". This
# property is primarily useful in targeting rules.
id=default


# The JSP that renders the returns line item
url=/panels/order/returns/NewreturnShippingAddress.jsp
contextRoot=/NewDCS-CSR


Save the ReturnShippingAddressRenderer.properties file.

Moving Properties to a Different Database Table : ATG



This is an example of what can go wrong if you do not understand how the Dynamo Application Framework combines XML files. Suppose you extend the userProfile.xml definition file with your own file in the localconfig configuration layer. You want to use the receiveEmail property that appears in the standard user profile repository definition, but you want this property to be stored in the my_user table, rather than the dps_user table. The receiveEmail property is defined in the standard profile repository definition like this:

<!-- DPS userProfile.xml -->
  <item-descriptor name="user" ...>
    <table name="dps_user" ...>
...
      <property category="Email" name="receiveEmail" data-type="enumerated"
                default="yes" column-name="receive_email"
                display-name="Receive email">
        <attribute name="useCodeForValue" value="false"/>
        <option value="yes" code="1"/>
        <option value="no" code="0"/>
      </property>...
    </table>
  </item-descriptor>


To replace this property with a similar property that is stored in the my_user table rather than the dps_user table, you might try to define this property in a /atg/userprofiling/userProfile.xml file in your application module or your localconfig directory, using the xml-combine="replace" directive, like this:

Bad Example 1:


<!-- my userProfile.xml -->
  <item-descriptor name="user" ...>
    <table name="my_user" ...>
      <property category="Email" name="receiveEmail" data-type="enumerated"
                default="yes" column-name="receive_email"
                display-name="Receive email" xml-combine="replace">
        <attribute name="useCodeForValue" value="false"/>
        <option value="yes" code="1"/>
        <option value="no" code="0"/>
      </property>
    </table>
  </item-descriptor>

This approach won’t work. In the Personalization module’s userProfile.xml, the receiveEmail property is defined under the dps_user table. But in the second userProfile.xml file, that property is defined under the my_user table. XML combination operates recursively starting from the outermost tags and working inwards. In this case, the <table> tags are matched by name. Since the table names dps_user and my_user don’t match, these two <table> tags are not combined. And since the <table> tags don’t match, the tags inside the <table> tags will not match up against each other. When the two userProfile.xml files are combined, you end up with two properties in the user item descriptor, each with the same property name: receiveEmail. So you end up with:


Bad Example 2:



<item-descriptor name="user" ...>
  <table name="dps_user" ...>
    <property name="receiveEmail" ... />
  </table>
  <table name="my_user" ...>
    <property name="receiveEmail" ... />
  </table>
</item-descriptor>

Since the resulting user item descriptor has two properties named receiveEmail, errors result.

To fix this is easy. Use the xml-combine="remove" directive in the dps_user table, rather than the xml-combine="replace" directive in the my_user table, as in this example:

Perfect Example:


<item-descriptor name="user" ...>
  <table name="dps_user" ...>
    <property name="receiveEmail" xml-combine="remove" />
  </table>
  <table name="my_user" ...>
    <property category="Email" name="receiveEmail" data-type="enumerated"
              default="yes" column-name="receive_email"
              display-name="Receive email" xml-combine="replace">
       <attribute name="useCodeForValue" value="false"/>
       <option value="yes" code="1"/>
       <option value="no" code="0"/>
    </property>
  </table>
</item-descriptor>


The xml-combine="remove" directive ensures that the old receiveEmail property is eliminated from the profile repository definition.

Difference Between Shopping Cart FormHandler & CartModifierFormHandler : ATG

CartModifierFormHandler - used to modify the shopping cart by adding items, deleting items and modifying the quantities of items in cart and preparing for checkout. Typically when using this you would have to handle checkout flow yourself by using ExpressCheckoutFormHandler in case of express checkout and in case of full checkout by using ShippingGroup & PaymentGroup form handlers and finally CommitFormHandler to confirm and commit the order.


ShoppingCartFormHandler - used for handling cart management (add/remove/adjust item quantity) and checkout process (shipping, billing, committing order). This provides an easier and simpler interface than using CartModifierFormHandler along other form handlers because in ShoppingCartFormHandler various functions for cart management to checkout are exposed via different handleXXX methods each having a preXXX and postXXX to extend the functionality if required. There is one FullShoppingCartFormHandler also which extends ShoppingCartFormHandler and adds the functionality like handling multiple payment groups, split shipping, express checkout.


To summarize, ShoppingCartFormHandler provides simpler cart management and checkout implementation all in itself. Whereas CartModifierFormHandler doesn't provide you the checkout flow and therefore CartModifierFormHandler based implementation would not be that simple and require other form handlers. But CartModifierFormHandler would provide some benefits like better handling and security for concurrent updates of orders, a more flexible payment/shipping group management for use with multiple shipping or payment groups along with other things.


Note:

ShoppingCartFormHandler is deprecated form handler from ATG2006.
It was common formhandler right from adding items to cart to submitting the order including shipping and billing.

From ATG2006, ATG introduced new formhandlers like CartModifierForHandler, ShippingGroupFormHandler, PaymentGroupFormHandler, CommitOrderFormHandler etc which has better control on transaction and module and handle complex checkout.

CartModifierFormhandler is restricted until Checkout, after checkout you need to use other form handler as mentioned.

ATG doesn't recommend using ShoppingCartFormHandler after ATG2006 and its there for back-ward compatibility

Wednesday, January 2, 2013

Invoice Payment : ATG

 

Enabling the InvoiceRequestProcessor:


The InvoiceRequestProcessor does the work of creating invoices, based on the InvoiceRequestInfo object it receives. InvoiceRequestProcessor is then used by the ATG Business Commerce PaymentManager, just like the credit card and gift certificate processors.

The InvoiceRequestProcessor holds authorize(), debit(), and credit() methods. Authorize() and credit() are empty; you can add any business logic your site needs for these procedures. The debit() method invokes the InvoiceManager’s createInvoice() method, which creates a new invoice from the order and other information, then sends a JMS message indicating that the invoice was created.

To enable invoice payment, add a line to the PaymentNameToChainNameMap configuration file, located at /atg/commerce/payment/PaymentManager:

paymentGroupToChainNameMap=\
  atg.commerce.order.CreditCard=creditCardProcessorChain,\
  atg.commerce.order.GiftCertificate=giftCertificateProcessorChain,\
  atg.commerce.order.StoreCredit=storeCreditProcessorChain,\
  atg.commerce.order.Invoice=invoiceRequestProcessorChain

 
If you want to add further validation logic to your invoice processing, you should extend the InvoiceRequestProcessor.authorize() method.

Using the Invoice Manager:


The InvoiceManager class provides high-level access for creating, manipulating, saving and deleting Invoice objects in the Invoice Repository. Each action leads to execution of a pipeline chain, described later in this section. The InvoiceManager methods are:

addInvoice:
Adds a new Invoice repository item to the repository.

createInvoice:
Creates a new Invoice repository item.

findInvoicesByPropertyValue:
earches the repository for invoices having a particular set of property values.

getInvoiceForUpdate:
Gets the invoice with the specified repository id.

getInvoicesForInvoiceNumber:
Loads all invoices with a given invoice number. This method returns a list because an invoice number may not be unique. It is possible to have several invoices with the same invoice number but different repository id’s, for example if a new invoice is generated to reflect partial payment of an order and a reduction in the balance due. In most cases, however, this method returns a list containing only a single invoice.

getNextInvoiceNumber:
Generates the next unique invoice number to use.

loadInvoice:
Loads the invoice identified by a given repository id and executes the loadInvoice pipeline chain. See Invoice Pipelines for more information.

removeInvoice:
Removes the specified invoice from the repository.

updateInvoice:
Updates invoice properties in the invoice repository and sets the repository item’s lastModified property to the current date and time.

Invoice Pipelines


The InvoiceManager class executes pipelines whenever an invoice is created, loaded, updated, or removed. Each pipeline chain receives an object of type InvoicePipelineArgs as a parameter. InvoicePipelineArgs provides getInvoice() and getInvoiceManager() methods. You can also subclass InvoicePipelineArgs and alter the pipeline’s expected type if you need additional methods.

The chains are defined in the PipelineManager at /atg/commerce/invoice/pipeline/InvoicePipelineManager. Out of the box

addInvoice, updateInvoice, removeInvoice are important pipelineChains for invoice payment.

Popular Posts