A payment processor (ex. PayPal) faces several monetary and legal risks. A transaction may be fraudulent, or otherwise illegal, opening them up to legal action. A merchant might not ship a product after accepting money, leading the consumer to demand their money back. And so on. For this reason, risk assessment and management is a big part of payments business. Risk assessment can often be very complex, with many statistical variables as well as hard laws that must be complied with. For this reason, it is often implemented as a combination of a machine learning and rule-based system. In this project, I have implemented an example in which I will weed out transactions that are too risky before adding them. I have used a rule-based programming in Drools to do that.
There are two entities this project :-
A bank:
- Can process multiple transactions
- Is either local or international
- Can be blacklisted, in which case it can no longer process transactions
This is a representation of a transaction before we have decided if it’s too risky to add to our ontology. A transaction request:
- Is associated with a single bank
- Has a specific amount
- Has a sender and a receiver, identified by their IDs as added to the OWL ontology
- Has a single category: Medical, Dining, Gambling, Wages, Weapons, Other.
- Has a timestamp (generated by system)
We will use our knowledge base about the banks and the incoming transaction request to decide whether we should insert the transaction into our Ontology, or reject it. This should be done according to the following rules:
- A bank, once blacklisted, can no longer process any transactions
- A transaction request belonging to the category of “Medical”, must go through, regardless of what the following rules evaluate to (unless the bank is blacklisted)
- A transaction request with category “Weapons” requires that (necessary, but not sufficient conditions): a. Both the sender and the receiver be trusted (note that only merchants can be trusted, but you just need to check for membership in the class Trusted in your ontology) b. The bank must be local
- A transaction request with amount >$100,000 must have at least one of the participants Trusted.
- A transaction request with amount >10 times the average amount for the given bank should be rejected (except if it’s the bank’s first transaction).
- If <25% of a bank’s past transaction involved a trusted participant, then the bank can no longer process transactions that don’t involve a trusted participant (until the fraction goes up to 25% again).
- If a bank suffers 3 transaction rejections in a row, blacklist the bank.
Two log files are maintained, one for accepted transactions, and one for rejected ones. The acceptance log should have the following info per entry:
- Transaction ID
- Bank ID
- Sender ID
- Receiver ID
- Amount
- Category
- Timestamp The rejection log should have the above, and in addition list the Rule number that it was rejected because of. The rule numbers are as in the above list of rules. These log files are just be txt’s, and should be accessible through the API as detailed in the following section.
Request API | Respone | Description |
---|---|---|
POST /addmerchant/:uniqueID | { “status”: “success” } | Add an individual to Merchant class |
POST /addconsumer/:uniqueID | { “status”: “success” } | Add an individual to Consumer class |
POST /addbank/:nationality/:bankID | { “status”: “success” } | Nationality will be one of “local” or “international”, in lowercase. ID will be unique. |
POST/transactionrequest/:senderID/ :receiverID/:bankID/:category/ :amount/:transactionRequestID | {“status”: “success”} OR { “status”: “failure”, “reason”: “” } | Try to add a transaction. If successful, use the transactionRequestID as the transactionID. If unsuccessful, use the ID in the log anyway. A failure should specify the rule number that caused the failure. Category will be one of the ones listed before, in all lowercase. Amount will be a whole number. |
GET /iscommercial/:transactionID | { “status”: “success”, “result”: “[true|false]” } or { “status”: “failure”, “reason”: “not a transaction” } | Return whether a transaction is commercial. |
GET /ispersonal/:transactionID | { “status”: “success”, “result”: “[true|false]” } or { “status”: “failure”, “reason”: “not a transaction” } | Return whether a transaction is personal. |
GET /ispurchase/:transactionID | { “status”: “success”, “result”: “[true|false]” } or { “status”: “failure”, “reason”: “not a transaction” } | Return whether a transaction is a purchase transaction. |
GET /isrefund/:transactionID | { “status”: “success”, “result”: “[true|false]” } or { “status”: “failure”, “reason”: “not a transaction” } | Return whether a transaction is a refund transaction. |
GET /isblacklisted/:bankID | { “status”: “success”, “result”: “[true|false]” } or { “status”: “failure”, “reason”: “not a bank” } | Returns whether a bank isblacklisted. |
GET /bankrejections/:bankID | { “status”: “success”, “rejections”: “” } or { “status”: “failure”, “reason”: “not a bank” } | Returns the number of rejections suffered by a bank. |
POST /reset | { “result”: “success” } | Reload and start afresh |
GET /acceptancelog | acceptance log contents | Refer to the logging section |
GET /rejectionlog | rejection log contents | Refer to the logging section |