This supersedes the old SEMPER activity paper, 212ZR054: Payment Manager Overview.
An active instance of the payment service block will contain the following objects:
PaymentManager object
(in reality, the PaymentManager is a static class.)
Purse objects
PaymentTransaction objects (each corresponding
to an ongoing purse transaction)
PaymentTransactionRecord for each
PaymentTransaction object. (Note: this is
created each time a
PaymentTransaction is created. But the
PaymentTransaction
object is transient (i.e. discarded at the end of a session or
at the completion of the transaction) while the
PaymentTransactionRecord is stored for longer periods.
Figure 1: A Payment Service Block in Action
(Courtesy Günter Karjoth)
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.
- 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.
- 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.
- 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).
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:
getNewTXRHandle() method in the Purse
class,
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.
- 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:
TS_INITIATED [in
PaymentTransactionState] and
TS_AB_CAPTURED [in
AccountBasedTransactionState])
setState[in
PaymentTransactionState]
setAccountBasedState [in
AccountBasedTransactionState] etc.)
exchangeCurrency), and
PaymentManager class is an implementation of the above.
To incorporate a payment system into the payment service block, the following are necessary:
PaymentTransaction class which
implements the purse services defined in the interface corresponding
to the appropriate payment model, AbcTransaction class that implements:
AccountBasedPurseServices interface, and
MicropaymentServices interface)
When an object of this subclass is created, it should create a
corresponding object of the PaymentTransactionRecord
class. If necessary, a subclass of the
PaymentTransactionRecord class may be defined and
used to suit the needs of specific payment systems. The
PaymentTransactionRecord uses objects of other
classes to record information about the transaction. Examples
are:
Purse class,
implementing all abstract methods in the Purse class.
AbcPurse class that extends:
AccountBasedPurse class.
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:
init() to initialise the purse during
startup, and
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
Note that if trusted I/O is required by an object in the payment service
block during the execution of such a special business application, it
may have to deal directly with TINGUIN (and not via the access control
manager).
In this section, we give brief examples of usage:
Here is what the user does:
Simultaneously, the shop does the following:
The above assumes that
Configuration:
The following configuration variables are understood by the payment
block:
To this end, we define a token-based PurseServices hierarchy rooted at
the TPurseServices
interface. The token-based interface has a
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 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:
Here are some examples of usage:
abcTr1 = (AbcTransaction) abcPurse1.startTransaction(<parameters>);
abcTr1.pay(<parameters>);
abcTr1.endTransaction();
abcTr1 = (AbcTransaction) abcPurse1.startTransaction(<parameters>);
abcTr1.startMicropayment (<parameters>);
abcTr1.doMicropayment (<parameters>);
[...]
abcTr1.doMicropayment (<parameters>);
abcTr1.stopMicropayment (<parameters>);
abcTr1.endTransaction();
state = abcTr1.getState();
or
abcTr1.abort();
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
PurseManagement
class.
In addition,
each payment system will come with special business applications to
create a purse and perform additional system-specific tasks.
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.
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.
// 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();
// 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();
Since these are not yet available, any test program should do the
necessary initialisation by itself. The test programs
purse
PaymentManager and
each purse has already been executed. This is typically done
by Library.init()
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:
Default: Prompt the user to break ties.
Default: English.
Default: False.
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.
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.
pay() and
receivePayment() synchronously. With the token-based
interface, we can also support an asynchronous model.
Forthcoming Changes
Negotiation class to do this.
PaymentTransactionRecord class hierarchy similar to
the Purse class hierarchy (e.g. with model-specific
intermediate classes)
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.