Definition of the Transaction
List of all transactions are defined in common/tm-client/transactionsDefinitions.yml
.
Each transaction definition consist of multiple types of parameters which are categorised to:
Field | Type | Required | Description |
---|---|---|---|
name | String | true | Name of your transaction (the generated Kotlin class for this transaction will have this class name, e.g., |
transactionType | String | true | Is one of the core transaction types |
staticTypeVariables | Map<variableName, stringValue> | true | Static variables values which are pre-defined for each core transaction type |
staticTransactionVariables | Map<variableName, stringValue> | false | Static variables values specific for the given transaction. These variables are sent to TM in instruction details map. |
runtimeTransactionVariables | Map<variableName, variableType> (supported variable types: | false | Runtime variables specific for the given transaction. You have to pass these as parameters when creating instance of the given transaction. These variables are also sent to TM in instruction details map. |
Example:
transacations: - name: TopUpPaynamics transactionType: InboundHardSettlement staticTypeVariables: internalAccountId: PaynamicsSuspense accountType: MainAccount bankTransactionCode: TRX-TOP-123 staticTransactionVariables: transactionType: BILL_PAYMENT runtimeTransactionVariables: someVariable1: String someVariable2: BigDecimal
Based on this definition a Kotlin class is generated in package: package ph.safibank.tm.model.transaction
(you can see example of creating its instance few lines below on this page)
Basic setup for produce/listen
Setup application properties in micro service for listening to executed transactions
tm-client: posting: client: <PostingsAPIClientId> listener-level: <client|global> consumer: group-id: ${TM_KAFKA_CONSUMER_GROUP} bootstrap: servers: ${TM_KAFKA_URL}
In above configuration it is specified which client is used for generating postings and which listener level should be used:
client
- listens to transactions published by<PostingsAPIClientId>
global
- listens to all transactions which were created in TM (also Smart Contract transactions - interest, taxes, etc.)
Produce new transaction
There are producers generated per each client defined in kafkaClientDefinitions.yml
Package: package ph.safibank.tm.producer
In package above there are generated clients classes which are Kafka producers used for send messages to TM. There is no need to use them directly and they are enabled by application properties. For usage you just need to inject PostingCoreProducer
bean in your service. Producer is enabled by setting application properties:
1. Setup application properties
2. Produce transaction and send to TM
@Inject lateinit var coreProducer: PostingCoreProducer val transaction = TopUpPaynamics( someVariable1 = "SomeVariable1", someVariable2 = BigDecimal("123.45"), transactionParams = InboundHardSettlementRuntimeParams( accountId = UUID(1, 2), amount = BigDecimal("22.33"), denomination = "PHP", clientTransactionId = "ABC-123-XYZ" ) ) coreProducer.process("<requestId>", "<clientBatchId>", listOf(transaction))
Listen to executed transactions
Transactions are sent to TM from micro services through specific channels which are defined as PostingsAPIClients.
Responses from TM Kafka are propagated to specific Kafka topic (per client) or to global topic which receives all transactions that are created in TM.
For most of cases micro service should listen only to client specific transactions, which were sent to TM with client specific producer.
When there is a case in micro service, that it needs to listen to transactions that are not only generated by it’s client or there is need to listen to transactions generated by smart contracts, then it should use global listener which receives all transactions.
Postings API clients
The Postings API implements an asynchronous request/response API over Kafka. Requests are published to one of two topics:
vault.core.postings.requests.v1
for requests with a strict service-level agreement (SLA)vault.core.postings.requests.low_priority.v1
for requests with a generous SLA
To use the Postings API, a PostingsAPIClient
resource is required. It serves two purposes:
Routing of responses
The PostingsAPIClient
resource is used to route responses back to a client of the Postings API. Every payment integration that wants to instruct the movement of funds in Vault must create at least one PostingsAPIClient
resource.
1. Setup application properties
2. Handle listener for some specific transaction
There are generated handlers per each transaction defined in yaml definition list.
Package: package ph.safibank.tm.listener.handler
Generated handlers are abstract classes which will receive specific transactions and requires to implement fun process(batchMetadata: BatchMetadata, instruction: T)
method.
This process method contains
BatchMetadata
which contains common meta data from Posting Instruction Batch:id
: transaction idrequestId
: A unique ID generated by the client (payment processor) that is used for idempotency. The client must ensure a uniquerequest_id
is passed within their namespace (determined byclient_id
). Multiple requests with the same <client_id
-request_id
> will receive the same response.clientId
: Uniquely identifies a client of the Posting API. Used to publish responses to the specified Kafka response topic. Together withrequest_id
it forms a unique key used for idempotency.
In case of Smart Contract generated transaction value is:CoreContracts
clientBatchId
: This must be set by the client, and is used as a correlation ID across different posting instruction batches. The suggested use is to set the sameclient_batch_id
across batches that contain posting instructions for the same financial transaction. The posting service provides indexing on this ID, allowing for efficient queries to return all posting instructions that belong to a client transaction.status
: The status of the processing of the posting instruction batch.
POSTING_INSTRUCTION_BATCH_STATUS_ACCEPTED:
The PostingInstructionBatch has been accepted and Postings have been committed to the ledger.
POSTING_INSTRUCTION_BATCH_STATUS_REJECTED:
The PostingInstructionBatch has been rejected and no Postings have been committed to the ledger.
instruction: T
which is per handler specified and all parameters are known.
Handler class names
Handlers classes are generated in format <TransactionName>Handler
in package: package ph.safibank.tm.listener.handler
.
Parameter instruction
has correct type of transactions with related data in model.
Example for TopUpPaynamics
handler
import jakarta.inject.Singleton import ph.safibank.tm.listener.handler.TopUpPaynamicsHandler import ph.safibank.tm.model.BatchMetadata import ph.safibank.tm.model.PostingBatchStatus import ph.safibank.tm.model.transaction.TopUpPaynamics @Singleton class MyOwnTopUpPaynamicsHandler: TopUpPaynamicsHandler() { override fun process(batchMetadata: BatchMetadata, instruction: TopUpPaynamics) { // check status if (batchMetadata.status == PostingBatchStatus.ACCEPTED){ // do process when transaction was accepted // Mark as paid -> completed etc } else{ // do process when transaction was rejected // notify user, mark as unpaid, retry ? } } }