Building custom configurable service actions

FTM for Immediate Payments delivers a number of models to configure and control the application processes. Some of these models use sets of configurable services that are invoked at different steps of the process. This topic provides guidance on how to implement custom built actions that can be included in these service sets.

The Configuring Immediate Payments for the customer and Configuration Values topics explain how to edit the configurable service set definitions. As part of this configuration, it is also possible to add customized actions and not just reuse the ones provided. These customized actions can be used to make service invocations for:
  • Request/Response services
  • Fire and Forget services

Coding the action

These customized actions are almost identical to standard FTM actions but the action code must adhere to a couple of extra rules:
  1. Request the outbound transaction class by calling GetDoChecksTxnClass(IN rEnv REFERENCE, IN cBaseClass CHAR).
  2. Call RegisterCheck(IN rEnv REFERENCE, IN cRequestType CHAR, IN bReserved BOOLEAN) for each object
The following code example shows these two calls in bold text:

	-- general Persistence API initialization
	CALL Persistence.InitPersist(rPersist);
	-- init for data set / rows
	CALL Persistence.InitPersistSet(rPersist, rAccDataSet, 'TXN', BC_IP_RESERVE_FUNDS_REQUEST, 'TRANSACTION_V');	
	CALL Persistence.SetTxnRouteInfo(rPersist, 'TXN', BC_IP_RESERVE_FUNDS_REQUEST, 'OUT_TXN_WAIT', BC_IP_RESERVE_FUNDS_REQUEST, 'PRIMARY', NULL, cCacheId);
	
	-- get correct class for outbound Txn
	SET cClass = GetDoChecksTxnClass(rEnv, 'OUT_TXN_WAIT');                                    -- (1) <<<  'OUT_TXN_WAIT' or 'OUT_TXN_NOWAIT'
	-----------------------------------
	-- Action Init : End
	-----------------------------------

	--------------------------------------------------
	-- OBJ_ITER loop runs for each object transitioned
	--------------------------------------------------
	OBJ_ITER: WHILE LASTMOVE(rObj) DO
		
		-------------------------------
		-- Action Object : Begin
		-------------------------------
		SET nCurrentTxnId = CAST(rObj.ID AS INTEGER);
		-- this provides diagnostic information in the Env in the event of an Exception
		SET rPMPVars.CURRENT_ID = nCurrentTxnId;
		
		-- Applying the master transaction's priority to the outbound transaction.
		
		SET nOutTechPriority = rObj.TECH_PRIORITY;
		
		-- Get ID
		MOVE rAccTxnDataRow TO rPersist;
		SET nNewId = Persistence.InitObjectRow(rPersist, rAccDataSet, rAccTxnDataRow);
		-- Get the Data for this new transaction
		CALL GetResFundsReqTxnData(cEventType);
		
		CALL RegisterCheck(rEnv, BC_IP_RESERVE_FUNDS_REQUEST, TRUE);                       -- (2) <<<
		
		-- Create the TRANSACTION record
		CALL CreateTxnRecord();
		
		-- Create the relationship record
		CALL Persistence.RelateObj1ToObj2(rPersist, nCurrentTxnId, nNewId, 'CAUSE');
		
		IF nNewId > 0 THEN
			CALL RaiseTxnCreatedEvent(nNewId, CAST(rObj.TECH_PRIORITY AS CHAR), ACTION_NAME, rEnv); -- Actions Common (this sample)
		END IF;
		-------------------------------
		-- Action Object : End
		-------------------------------
		
		-- Go on to the next object
		MOVE rObj NEXTSIBLING REPEAT NAME;
		
	END WHILE OBJ_ITER;
	
	-----------------------------------
	-- Action Finalize : Begin
	-----------------------------------
	DELETE FIELD rAckTxns;
	-----------------------------------
	-- Action Finalize : End
	-----------------------------------
The value obtained for cClass should be used when creating the transaction record as shown in bold text in this example:

CREATE PROCEDURE CreateTxnRecord()
BEGIN
	
	CALL Persistence.SetColValInt(rAccDataSet, rAccTxnDataRow, 'APP_ID', Common.GetApplicationId());
	
	CALL Persistence.SetTxnStatus(rAccDataSet, rAccTxnDataRow, 'S_OutTxnCreated');
	
	CALL Persistence.SetColValChar(rAccDataSet, rAccTxnDataRow, 'MASTER_FLAG', 'N');
	CALL Persistence.SetColValChar(rAccDataSet, rAccTxnDataRow, 'SUBTYPE', BC_IP_RESERVE_FUNDS_REQUEST);
	CALL Persistence.SetColValChar(rAccDataSet, rAccTxnDataRow, 'CID', rObj.CID);
	
	CALL Persistence.SetTxnISFBLOB(rAccDataSet, rAccTxnDataRow, nNewId, rCachedTxn, 'ISFBLOB');
	CALL Persistence.SetColValChar(rAccDataSet, rAccTxnDataRow, 'OBJ_CLASS', cClass);                          -- (3)  <<<
	
	CALL Persistence.SetColValInt(rAccDataSet, rAccTxnDataRow, 'TECH_PRIORITY', nOutTechPriority);

	-- extend the ISF_Cache for this Transaction
	-- include extra info that can be used by core/generic code
	-- to save later database access
	SET rCachedTxn.MASTER_TYPE = 'TRANSACTION';
	SET rCachedTxn.MASTER_PT_ID = CAST(rObj.TRANSMISSION_ID AS INTEGER);
	
END;
Additional working examples are found by reviewing the following actions in the FTM IP Common LIB project:
  • A_DoFundsReservation.esql
  • A_DoNotifyChannel.esql
  • A_DoSendAccountInquiry.esql
  • A_DoSendDebitToAccounting.esql
  • A_DoSendToAccounting.esql
  • A_DoSendToFraudCheck.esql
  • A_DoSendToSanctScreen.esql

Object selector data available to the actions

Typically, actions require access to data selected on the FSM transition. If you are implementing an FSM, you normally specify the object selector based on the data required by the action(s) on the transition.

In this case, the object selector is predefined and depends on which service definition set you are extending. Normally, object selectors are defined directly on the FSM model or on the FSM transition as an override, but for these transitions the object selector is defined as an Alias referenced on the transition override. In the context of a single configurable service set, two aliases are in use. An example is shown in the following diagram.

The aliases are defined as Configuration Values under the Category 'O_SEL_ALIAS'.
Table 1. Example object selector alias
Alias key (Name) Description Example value
IN_PRE_CHECK Basic selector, which is enough to facilitate the evaluation of the status for the current services. That is, it selects the data required by A_CheckInPreCheckResults or an equivalent.

SELECT T.ID, T.SUBTYPE, T.TECH_PRIORITY, 
       V.KEY PROCESS_STEP, V.SMALL_VALUE CHECK_STATUS 
    FROM $DBSchema.OBJ_BASE T 
         LEFT OUTER JOIN $DBSchema.OBJ_VALUE V 
            ON T.ID = V.OBJ_ID 
            AND V.CATEGORY = 'IP_PROCESS' 
            AND V.KEY LIKE 'IP_INCOMING_PRE_CHECK%' 
    WHERE SUBTYPE IN ('IP_FROM_CSM_INSTR','IP_FROM_CSM_INSTR_RPT') 
      AND STATUS=?
IN_PRE_CHECK_ISF Enhanced selector to facilitate most actions that require access to the ISF, Payment, or both, fields. It is a super-set of the basic selector. Additional data selected is highlighted in bold.

SELECT T.ID, T.SUBTYPE, T.CID, T.ALT_ID, T.BATCH_ID, T.TRANSMISSION_ID, 
       T.ISF_DATA, T.ACCOUNT, T.AMOUNT, T.CURRENCY, T.VALUE_DATE, 
       T.SENDER, T.RECEIVER, T.PAYMENT_TYPE, T.TXN_DATA1, 
       T.SETTLEMENT_SYSTEM, T.BANK_CODE, T.DEST_BANK_CODE, 
       T.TECH_PRIORITY, 
       V.KEY PROCESS_STEP, V.SMALL_VALUE CHECK_STATUS 
    FROM $DBSchema.TXN_PAYMENT_V T 
         LEFT OUTER JOIN $DBSchema.OBJ_VALUE V 
            ON T.ID = V.OBJ_ID 
            AND V.CATEGORY = 'IP_PROCESS' 
            AND V.KEY LIKE 'IP_INCOMING_PRE_CHECK%' 
    WHERE SUBTYPE IN ('IP_FROM_CSM_INSTR','IP_FROM_CSM_INSTR_RPT') 
      AND STATUS=?

Customizing the object selector aliases

Note: Customers should never need to customize the basic selectors, only the enhanced variants. Typically, it is not expected that implementations should need to do this.
The following steps can be followed to customize an object selector alias:
  1. Copy the applicable alias entry from the applicable FTM for Immediate Payments model into the customer model.
  2. Enhance the value to include the additional select columns, join tables, or both, and conditions.
  3. Do not, in any way, reduce the scope of the data selected because it could cause the application to break.

Deploying the action

As for all standard FTM actions, these code artifacts must be deployed in the scope of an event processing wrapper msgflow. It is recommended that these custom actions be deployed onto the customized event processing wrapper of the customer; either directly or by using an appropriate subflow container. Remember to promote the Datasouce property and connect it to all other Datasouce properties.

Transaction routing

It is important to correctly configure the outbound transaction routing such that it reaches the correct service. These actions can use standard FTM routing mechanisms as described in the Routing outbound transmissions topic in the base FTM documentation. Standard actions delivered with FTM for Immediate Payments currently use ROLE_FOR_TXN_TYPE. Remember to add an appropriate configuration value entry into the Customer IP Model if the action is not specifying the Service Participant itself.

Completion events

The FSMs that trigger these actions to invoke the services through the configurable lists use the following standardized events that must be used if the FSM is to correctly react to the service completion:
  • E_CheckRequestComplete
  • E_CheckRequestFailed
The actions and services supplied with FTM for Immediate Payments all use E_CheckRequestComplete for a final good response as well as a pending response. The following table shows example configurations for actions that create transactions with SUBTYPE = 'MY_TXN_REQUEST' and where response transactions map to SUBTYPE = 'MY_TXN_RESPONSE...'
Table 2. Example completion event entries
Value category Service interaction Value key Value
COMPLETION_EVENT_FOR_TXN_TYPES Request / Reply MY_TXN_REQUEST_MY_TXN_RESPONSE E_CheckRequestComplete
MY_TXN_REQUEST_MY_TXN_RESPONSE_PENDING
MY_TXN_REQUEST_MY_TXN_RESPONSE_REJECT E_CheckRequestFailed
Fire and Forget MY_TXN_REQUEST E_CheckRequestComplete

Include the action label in the service definition list

For the deployed action to be invoked, it must be added to a valid service definition set. Don't forget to update or override the configuration value that defines this. For more information, see the Configuration Values and Configuring Immediate Payments for the customer topics.