Design of the Payment Service Block

Document #: 221ZR053
Authors: J. L. Abad Peiro, N. Asokan, M. Steiner, M. Waidner (ZRL)
Editor: N. Asokan (ZRL)
Reviewer: CRM, DIC, KPN
Status: Draft Version. 2, SEMPER internal
Date: January 23, 1997


Introduction

The purpose of the payment service block is to provide a unified way to access various different payment systems are available to a user. The design described here is based on the concepts described in the SEMPER activity paper 212ZR055: Designing a Generic Payment Service.

This supersedes the old SEMPER activity paper, 212ZR054: Payment Manager Overview.

An active instance of the payment service block will contain the following objects:

Payment Service Block in Action

Figure 1: A Payment Service Block in Action
(Courtesy Günter Karjoth)

Class and Interface Hierarchies

The following class and interface hierarchies in the architecture are significant ("Abc" is used as the name of an example payment system):

The functionality expected from payment systems is captured in the PurseServices interface hierarchy. The root PurseServices interface contains methods corresponding to services expected from all payment systems. Additional services that are specific to various payment models is captured in sub-interfaces that extend the root interface.

Interface hierarchy: PurseServices

 - PurseServices -+--- AccountBasedPurseServices
                  |
                  |
                  |
                  +--- CashLikePurseServices
                  |
                  |
These interfaces describe the generic purse services required by the various payment models. These will be implemented by: the PaymentTransaction subclass (see below) corresponding to each payment system of the appropriate model.

Purse services that are optional are defined separately. An example is the MicropaymentServices interface. Each category of optional services will be in the form of a parallel interface hierarchy similar to the PurseServices hierarchy. It is not mandatory for the subclasses of the Transaction class to implement the optional part of the purse services interface.

Class hierarchy: Purse

 - Purse-+--- AccountBasedPurse -+---AbcPurse
         |                       |
         |                       +---....
         |
         +--- CashLikePurse  -------+---....
         |                          |
         |                          +---....

A purse is a repository of information related an installed payment system. As with the PurseServices hierarchy, the Purse class hierarchy has a root class and model-specific subclasses of it. The root class defines a set of services. Some are in the form of abstract method definitions which need to be implemented by the Purse subclasses. For the rest, default implementations are provided; the subclasses may override them if needed.

Class hierarchy: PaymentTransaction

 - PaymentTransaction -+--- AbcTransaction
              |
              |
              |
              +--- ....
              |
Objects of this class hierarchy are the primary objects in the payment service block for providing payment services. A PaymentTransaction object can be thought of as an abstraction of a "transaction." The root class simply provides the functionality of a generic "transaction" (e.g.: methods to abort and to status check). Each payment system is required to have a subclass of the PaymentTransaction class that implement the PurseServices interfaces for its payment model.

Note that we could have abstract subclasses AccountBasedTransaction and CashLikeTransaction and then make AbcTransaction a subclass of one of these classes. We have not done this at this time because there doesn't seem to be a need for it (if defined, such classes would be empty at least as of now). If it becomes necessary, they can always be defined.

Currently, the model-specific functionality provided by the transaction objects is expressed by the corresponding sub-interface in the PurseServices interface hierarchy. Functionality expected from a generic transaction is expressed by the PaymentTransaction class. The adapter of a payment system will subclass the PaymentTransaction class to inherit specifications of the generic transaction functionality and implement the model-specific PurseServices sub interface to inherit specifications of the model-specific functionality. Hence there is no need for the aforementioned intermediate subclasses of PaymentTransaction.

Finally, note that subclass designers should ensure that only methods corresponding to a single transaction are executed on an PaymentTransaction object. (e.g. if someone creates an PaymentTransaction object, invokes pay() on it and then attempts to make another invocation of a purse service on the same object, the second attempt must be disallowed; however multiple invocations of doMicroPayment() or capture() on the same PaymentTransaction object is legal).

Class (hierarchy): PaymentTransactionRecord

Each PaymentTransaction object keeps any data with long-term relevance in a corresponding PaymentTransactionRecord object. Recall that the latter object is the one that is archived after the transaction is complete. Typically, the designer of the adapter for a payment system will subclass the PaymentTransactionRecord class in order to keep information specific to that payment system. The startTransaction() method in the Purse subclass should do the following:

Class (hierarchy): ServiceType

This class hierarchy is used to represent the services defined in the PurseServices interface hierarchy. For example, the PaymentTransactionRecord object has an attribute of this class to denote the service that was provided during the corresponding transaction. Each model may have an optional subclass of the root ServiceType class. Individual adapters are free to extend the hierarchy further. The various methods in the transaction class of an adapter should use these service type identifiers to keep the transaction record up-to-date.

Class (hierarchy): PaymentTransactionState

 - PaymentTransactionState-+--- AccountBasedTransactionState-+--...
         |                                                   |
         |                                                   +---....
         |
         +--------------------- CashLikeTransactionState ----+---....
         |                                                   |
         |                                                   +---....

This class hierarchy is used to represent the states of a transaction. Again, the PaymentTransactionRecord object has an attribute of this class to denote the state of the corresponding transaction. The root class is extended for each model and adapters are free to extend it further. Each class in this hierarchy provides the following: The various methods in the transaction class of an adapter should use these status values to keep the transaction record up-to-date. It is left to the adapter writer to decide how to mix and match the different state values at various levels of the above hierarchy.

Class: PaymentManager

The payment manager object: The PaymentManager class is an implementation of the above.

Incorporating a Payment System into SEMPER

To incorporate a payment system into the payment service block, the following are necessary:

Together, these can be considered to constitute the "adapter" for that payment system. In addition, the following are required:
  • any special business applications: there will be a standard special business application for the payment service block. It would provide generic services to enable user-interaction with the payment service block. For example, it will provide a default way to create a new purse (i.e. by invoking CreatePurse method of the PaymentManager) or to "install" a new payment module into the payment service block. The class PurseManagement provides a set of methods to facilitate the creation and configuration of purses using interaction via Tinguin. Each adapter will have to provide certain standard methods that are used by the PaymentManager and PurseManagement classes. Currently, these are all expected from the subclass of the Purse class. They are:
  • A constructor method,
  • A method init() to initialise the purse during startup, and
  • A method setup() to perform configuration of a purse via user interaction. Default implementations are available in the root Purse class. Also, if a payment system provides more functionality than the what is assumed by the SEMPER payment service block model, special business applications will be necessary to configure, and use such functionality.

    If a payment system does not provide a particular service required by the corresponding interface, the adapter still has to implement a cover method that returns a "not implemented" error/exception.

    The Lifecycle of Purses

    A payment system adapter contains a sub-class of the Purse class. Whenever a new adapter is installed, the name of the the Purse subclass must be registered with the PaymentManager using the registerPurseClassName service. During startup, the PaymentManager will poll each known purse class to see if it is enabled: i.e. whether a purse object of this class can be instantiated. Each purse provides a isEnabled method using which the PaymentManager can check if a Purse class is enabled. The official way to create and delete purses is to use the API provided by the PaymentManager in the form of the createPurse and deletePurse services.

    Each purse also provides an initialisation service. During startup, the PaymentManager will attempt to initialise each purse. The purses that are successfully initialised are called active. Inactive purses cannot be used in any value transfer transaction. A purse can be disabled using the disable (internal to the payment block) method. Before a purse is deleted, the PaymentManager will attempt to disable it first.

    Each Purse class also provides a setup service to configure purses. This service must take care of all the necessary configuration before a purse can be used in value transfer transactions. Purses that can not configured properly should fail their initialisation.

    Finally, the PurseReferences class provides a standard and permanent way to refer to purses (in the absence of persistent storage, java object references are transient).

    Purse Transactions

    For each transaction, there will be a corresponding PaymentTransaction object. The caller would do something like:
    
    	abcTr1 = (AbcTransaction) abcPurse1.startTransaction(<parameters>);
    	abcTr1.pay(<parameters>);
    	abcTr1.endTransaction();
    
    
    Here are some examples of usage:

    Protocols

    Initially at least, only a simple negotiation protocol will be implemented. Messages in the negotiation protocol will be in the form of a list of lists. Each secondary list is used to negotiate the value of a single parameter (e.g. "payment service name"). The head of this list will be an object of the MessageListHead class. It contains the following attributes: The PaymentManager provides various methods such as choosePS and requestPSChoice to negotiate the selection of payment systems. Further, there are methods for local selection as well (for example, based on preferences, or interaction with the user). Different purse selection policies can be implemented by combining these methods in different ways. A pair of methods selectPayingPurse and selectReceivingPurse implementing a particular policy are also provided in the PaymentManager class. Currently, we plan for synchronous negotiations between two payment managers. Later, we may extend this to asynchronous negotiations, and negotiations between two PaymentTransaction objects.

    Interaction with the User

    [to be done]

    Notes

    Interactions with Other Blocks

    Issues

    In this section, we list proposed or forthcoming changes to the design and issues that need to be discussed:

    Using the Payment Service Block

    Transfer Manager in the SEMPER transfer layer is likely to be the most common user of services from the Payment Service Block. However, as mentioned before special Business Applications that manipulate the configuration of the payment service block will interact directly with objects in the payment service block. Also, in principle, there is nothing to prevent normal Business Applications or the Commerce Layer to interact directly with these objects as well.

    In this section, we give brief examples of usage:

    Simple Payment

    Scenario: A user "Alice" wants to make a payment of 100 PST to a shop, "Bob Inc.". The shop is at bob.com. The user and the shop have already started a commerce session. They agree to use a common external reference string (we assume that this is available in the variable ext_ref) to identify this session. They have also not selected any specific payment mechanisms but would like to leave it to their respective payment service blocks to negotiate it.

    Here is what the user does:

    // seller's address
    // The port number and path have no relevance -- so we just use placeholders
    ComPointAddress seller_addr = new ComPointAddress ("tcp", "bob.com", 0, "");
    PaymentEntity seller = new PaymentEntity ("Bob Inc.", seller_addr);
    
    // We don't want to use any specific alias (just the name we have on
    // the selected purse)
    String my_alias = null;
    
    // We have no preferences about the means of payment
    Vector preferred_payment_systems = null;
    Vector preferred_purses = null;
    
    // We have no security options
    Vector options = null;
    
    // External reference (already agreed upon between payer and payee)
    String ext_ref = "order-# 1233";
    
    // Now select a purse -- this involves negotiation with peer
    PurseReference chosen_purse_ref = PaymentManager.selectPayingPurse
    	(seller, my_alias, amount, preferred_payment_systems,
    	 preferred_purses, options, ext_ref);
    Purse chosen_purse = chosen_purse_ref.getPurse();
    
    // carry out the payment
    PaymentTransaction tr = chosen_purse.startTransaction();
    tr.pay(seller, amount, options, ext_ref);
    tr.endTransaction();
    

    Simultaneously, the shop does the following:

    // our address (PaymentManager.init() had already registered this address
    // with the communication manager
    // The port number and path have no relevance -- so we just use placeholders
    ComPointAddress seller_addr = new ComPointAddress ("tcp", "bob.com", 0, "");
    PaymentEntity seller = new PaymentEntity ("Bob Inc.", seller_addr);
    
    // We don't particularly care for the buyer's address since they will open
    // a channel to us with the agreed upon "ext_ref"
    PaymentEntity buyer = null;
    // The rest, as with the buyer side
    Vector preferred_payment_systems = null;
    Vector preferred_purses = null;
    Vector options = null;
    String ext_ref = "order-# 1233";
    
    // Now select a purse -- this involves negotiation with peer
    PurseReference chosen_purse_ref = PaymentManager.selectReceivingPurse
    	(buyer, seller, amount, preferred_payment_systems,
    	 preferred_purses, options, ext_ref);
    Purse chosen_purse = chosen_purse_ref.getPurse();
    
    // receive the payment
    PaymentTransaction tr = chosen_purse.startTransaction();
    tr.receivePayment(buyer, amount, options, ext_ref);
    tr.endTransaction();
    

    The above assumes that

    Since these are not yet available, any test program should do the necessary initialisation by itself. The test programs Seller.java and Buyer.java in the Test directory of the source are examples of such test programs.

    Configuration and Preferences

    Preferences: The init() method of the PaymentManager> will attempt to load the its preference group from the preference manager; if none exists, one will be created with initial defaults. Each purse should also do the same. Currently, the Payment Manager understands the following preferences:

    Configuration: The following configuration variables are understood by the payment block:

    Token-based Definitions of Interfaces to Protocols

    In defining the interfaces, we have assumed that the payment block is responsible for running its protocols: the payment manager carries out the negotiation protocol and adapters of payment systems take care of their respective payment protocols. An alternate approach, in the style of GSS-API, is to define the interface in terms of tokens. The payment block is still responsible for constructing and interpreting protocol messages; but the actual communication between peers is left to the caller.

    To this end, we define a token-based PurseServices hierarchy rooted at the TPurseServices interface. The token-based interface has a start<service> method corresponding to every <service> method in the PurseServices interface hierarchy. The sub-classes of the PaymentTransaction will implement the token-based interface as well.

    The SEMPER model was assumed to be synchronous in that both the payer and payee is expected to have already started business sessions and therefore, for example, invoke pay() and receivePayment() synchronously. With the token-based interface, we can also support an asynchronous model.

    Forthcoming Changes

    Miscellany

    The payment service block will be in package semper.payment. The documentation includes an index and a tree depicting the class structure.

    Individual payment modules should be in packages like semper.payment.ecash. The semper.payment package is unaware of the various individual modules. Each payment module may provide an "installer/configurator" application which can use the services that have been already provided,such as createPurse and those in the PurseManagement class as well as any additional services speicific to that payment module.