Some Banno software components integrate with client financial institutions through jXchange. This document describes how Banno code within the mobile-data-services repository integrates with jXchange.
- Table of contents {:toc}
What jXchange is
jXhange is a product sold by Jack Henry for integrating with their banking software. It offers a set of web services which use the WSDL/SOAP/XML/HTTP technology stack to make remote procedure calls.
The interfaces are documented here: http://mmojxdn01.jhacorp.com/IDG/jXchange/Home/17
jXchange has a very large surface area, with hundreds of operations, but Banno code uses a small subset of it, about 50 operations, as described below.
How jxChange integration code is built
The jXchange interface jars are built from the SBT project called jxchange-scalaxb within the mobile-data-services repository.
The Scalaxb tool we use generates Scala code for the jars from some of the WSDL and XSD files in that project under this directory: /mobile-data-services/jxchange-scalaxb/R2016.0.10JHAPub/Validation.
That build.sbt file for the jxchange-scalaxb project explicitly lists all the jXchange modules we use and the operations within each module for which we generate Scala code.
What Scalaxb produces
Scalaxb generates a large quantity of Scala source code which is then compiled and packaged in to a collection of JAR files.
To see the generated source code, you can build it as follows in your clone of the mobile-data-service repository:
cd jxchange-scalaxb
sbt
package
After those commands you can browse to see the generated code, for example
- bill-pay/target/scala-2.11/src_managed/main/sbt-scalaxb
- customer/target/scala-2.11/src_managed/main/sbt-scalaxb
As an example, for bill-pay, some of the generated files include:
- TPG_BillPayMaster_skinny_type1.scala
- case classes for the service method input parameters and
responses, e.g.
BilPayChanInqRq_MType,BilPayChanInqRs_MType
- case classes for the service method input parameters and
responses, e.g.
- TPG_BillPay_skinny.scala
- the abstract service trait for bill-pay,
BillPayService
- the abstract service trait for bill-pay,
- xmlprotocol.scala
- implicit definitions to provide XML codecs for the various generated case classes
- a soap implementation,
BillPayServiceSoapBindings, of the abstract service traitBillPayService - a trait called
BillPayServiceSoapBindingswhich is used to bind to an endpoint URI (by overriding itsbaseAddressfield).
The Scala code generated by Scalaxb uses the
Dispatch HTTP client
to make the SOAP requests via HTTP and all service trait methods
return a
scala.concurrent.Future[_].
Package structure of the Scalaxb-generated code
jXchange is divided into several modules, each such module’s operations and message types described in a .wsdl and .xsd file pair, for example TPG_BillPay.wsdl and TPG_BillPayMaster.xsd.
The jXchange interface code is split into these sub-packages, each derived from a sub-module of JXchange as embodied in a corresponding .wsdl and .xsd file pair.
com.banno.jackhenry.jxchange.billpaycom.banno.jackhenry.jxchange.checkimagescom.banno.jackhenry.jxchange.customcom.banno.jackhenry.jxchange.customercom.banno.jackhenry.jxchange.identitymanagementcom.banno.jackhenry.jxchange.inquirycom.banno.jackhenry.jxchange.transaction
Which Banno components use jXchange
All generated jXchange integration code resides beneath the Scala package com.jackhenry.jxchange.
Within the mobile-data-services repo, the following sub-components of mobile-data-servics make use of jXchange:
How the jxChange integration is used
The jXchange jars are not built in the regular mobile-data-services Jenkins build because the repertoire of operations we use changes relatively infrequently. So jars are built only when chages are needed and are staged to Banno’s Nexus jar repository and included as “managed library” dependency jars by SBT.
Given the Scalaxb generated Scala code and some supporting Banno code, you need to supply 3 data in order to actually run a jXchange request:
- a
URIfor the service endpoint - a username
- a password
Ultimately the endpoint URI is bound by implementing the Scalaxb-generated
trait XXXXServiceSoapBindings (where XXXX is for example “BillPay”) and
overriding the baseAddress member.
Ultimately username and password are bound by implementing the trait
JxchangeSoapClients
and overriding the members called username and password.
Prior to those bindings being made, the data are supplied in two different ways, one for the billpay related code and one for the other.
Configuring a non-billpay jXchange client
Like this:
- Read the appropriate row from the database table
called
connection_config_defaults - Instantiate a
ConnectionConfig - Creds are stored in the vault and are decrypted using private
keys are in a
Configvalue.
E.g. See the
ConfiguredJXchangeServices
object where each XXXCServiceSoapBindings has its baseAddress (a URI)
overridden by what is inside an instance of a
JxServiceConfiguration
instance.
Configuring a billpay jXchange client
Configuring the endpoint
There is a Scalaxb-generated BillPayServiceSoapBindings class with an abstract “baseAddress” member.
That is overridden by mixing in an instance of
ConfiguredJxBillPayClient,
in the object called
JxBillPaySoapBindings.
The values come from a config, at paths isb-bill-pay.base-address isb-bill-pay.test-env.base-address
These in turn come from the reference.conf file in the isb-bill-pay-client SBT project.
The values as this is written are:
- for production: https://jxappgtw.jhahosted.com/jxchange/2008/ServiceGateway/ServiceGateway.svc
- for test: https://jxtest.jackhenry.com/jxchange/2008/ServiceGateway/ServiceGateway.svc
Configuring the credentials
There is a class called
ConfiguredJxBillPayClient
which implements
JxchangeSoapClients
, overriding the username and password members with values
obtained from a
Config
value (from the
reference.conf)
at these config paths:
isb-bill-pay.jxchange-username
isb-bill-pay.jxchange-password
Example of a simple jXchange client program
package com.banno.http4sjxclient
import com.jackhenry.jxchange.customer._
import delorean._
import java.net.URI
import org.http4s._
import org.http4s.client.{Client, _}
import org.http4s.client.blaze._
import org.http4s.dsl._
import scala.concurrent.duration._
import scala.concurrent.{Await, Future}
import scala.util.{Failure, Success, Try}
import scala.xml.transform.{RewriteRule, RuleTransformer}
import scala.xml.{Elem, NamespaceBinding, Node, NodeSeq, XML}
import scalaxb.{HttpClientsAsync, Soap11ClientsAsync}
import scalaz.concurrent.Task
/**
* Simple command line app that runs a jXChange request using Http4s.
* Known to work with just these dependencies, and the versions are not crucial:
*
* "com.banno" %% "jxchange-scalaxb-customer" % "1.94.0"
* "io.verizon.delorean" %% "core" % "1.2.42-scalaz-7.2"
* "org.http4s" %% "http4s-blaze-client" % "1.2.42-scalaz-7.2"
*/
object JxCustomerPingOverHttp4s extends App {
private val requestTimeout = 20.seconds
private val username = "jxep-Banno-1-i101@jxtest.local"
private val password = "6y+AZ!4x1Pk~d2#LMg3-"
private val uri = new URI(
"https://jxtest.jackhenry.com/jxchange/2008/ServiceGateway/ServiceGateway.svc"
)
private val client = PooledHttp1Client()
private val service = newCustomerService(username, password, uri, client)
println(
Await.result(
service.ping(pingRq = PingRq_Type("blah"), None),
requestTimeout))
private def newCustomerService(_username: String, _password: String, uri: URI, client: Client): CustomerService =
new CustomerServiceSoapBindings
with HttpClientsAsyncHttp4s
with WssSoapClients {
override val baseAddress: URI = uri
override val username: String = _username
override val password: String = _password
override val http4sClient: Client = client
}.service
trait HttpClientsAsyncHttp4s extends HttpClientsAsync {
def http4sClient: Client
override def httpClient: HttpClient = new HttpClient {
override def request(
requestBodyText: String,
address: URI,
headersMap: Map[String, String]): Future[String] = {
println(s"Headers: $headersMap")
println(s"Request body: \n${attemptPrettyPrint(requestBodyText)} ")
val f = (for {
uri <- Task.fromDisjunction(Uri.fromString(address.toString))
req <- POST(uri, requestBodyText)
.putHeaders(headersMap.map((Header(_, _)).tupled).toSeq: _*)
result <- http4sClient.fetchAs[String](req)
} yield result).unsafeToFuture
f.onComplete {
case Success(s) => println(s"Response text is: \n ${attemptPrettyPrint(s)}")
case Failure(t) => t.printStackTrace()
}
f
}
}
private lazy val pp = new scala.xml.PrettyPrinter(100, 2)
private def attemptPrettyPrint(s: String): String = {
Try { XML.loadString(s) } match {
case Success(xmlElem) =>
pp.format(xmlElem)
case Failure(_) => s
}
}
}
trait NamespaceHoist {
protected def hoistNamespace(prefix: String, uri: String)(xml: NodeSeq): NodeSeq = {
val withoutPrefixes = new RuleTransformer(new StripPrefixRule(prefix))(xml.head)
val rootElement = withoutPrefixes.head.asInstanceOf[Elem]
rootElement.copy(scope = NamespaceBinding(null, uri, rootElement.scope))
}
private[this] class StripPrefixRule(prefix: String) extends RewriteRule {
override def transform(node: Node): Seq[Node] = node match {
case ele: Elem if ele.prefix == prefix =>
ele.copy(prefix = null)
case other => other
}
}
}
trait WssSoapClients extends Soap11ClientsAsync { self: HttpClientsAsync =>
def username: String
def password: String
override lazy val soapClient: Soap11ClientAsync = new WssSoapClient
class WssSoapClient extends Soap11ClientAsync with NamespaceHoist {
// Note: be careful that code reformatting does not introduce leading or trailing
// whitespace around username and password here.
private lazy val wssHeader: NodeSeq =
<wsse:Security soap11:mustUnderstand="1"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/">
<wsse:UsernameToken>
<wsse:Username>{username}</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">{password}</wsse:Password></wsse:UsernameToken>
</wsse:Security>
override def requestResponse(
body: NodeSeq,
headers: NodeSeq,
scope: NamespaceBinding,
address: URI,
webMethod: String,
action: Option[URI]): Future[(NodeSeq, NodeSeq)] = {
val xmlWithHoistedNamespace = hoistNamespace(
"tns",
"http://jackhenry.com/jxchange/TPG/2008")(body)
super.requestResponse(
xmlWithHoistedNamespace,
headers ++ wssHeader,
scope,
address,
webMethod,
action)
}
}
}
}
Tool for testing endpoints and operations (jxplorer)
- A utility for tinkering with JxChange endpoints and operations
- A .zip containing README and jar can be downloaded from Google Drive here or you can get source and build from this repository.
Catalog of all supported operations
(As of the time of writing.)
jxchange-scalaxb-bill-pay.jar
- package: com.jackhenry.jxchange.customer
- operations:
PingBilPayChanInqBilPaySubAddBilPaySubInqBilPaySubSrchBilPaySubConsmCustInqBilPayPayeeSrchBilPayPayeeInqBilPayPayeeAddBilPayPayeeModBilPayPmtHistInqBilPayPmtHistSrchBilPaySchedPmtSrchBilPaySchedPmtInqBilPaySchedPmtAddBilPaySchedPmtMod
jxchange-scalaxb-customer.jar
- package:
com.jackhenry.jxchange.customer - operations
PingCustAddCustInqCustSrchParmValSrchCustModIntnetFinInstIdInqIntnetFinInstIdUsrInqEFTCardModEFTCardSrchEFTCardInq
jxchange-scalaxb-inquiry.jar
- package:
com.jackhenry.jxchange.inquiry - operations:
PingAcctSrchAcctInqAcctHistSrchXferSrchXferSrcDestSrch
jxchange-scalaxb-transaction.jar
- package:
com.jackhenry.jxchange.transaction - operations:
PingTrnAddXferAddXferAddValidateXferMod
jxchange-scalaxb-identity-management.jar
- package:
com.jackhenry.jxchange.identitymanagement - operations:
PingMFAUsrQnAInq
jxchange-scalaxb-deposit.jar
- package:
com.jackhenry.jxchange.deposit - operations:
PingAcctAddAcctMod
jxchange-scalaxb-loan.jar
- package:
com.jackhenry.jxchange.loan - operations:
PingLnAcctAdd
jxchange-scalaxb-custom.jar
- package:
com.jackhenry.jxchange.custom - operations:
IntnetFinInstUsrAuthenInq
jxchange-scalaxb-statement.jar
- package:
com.jackhenry.jxchange.statements - operations:
ChkImgStmtSrchChkImgStmtGenChkImgStmtGenInq
jxchange-scalaxb-check-images.jar
- package:
com.jackhenry.jxchange.checkimages - operations:
ChkImgInq