← Mobile Data Services

jXchange Integration Guide

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
  • TPG_BillPay_skinny.scala
    • the abstract service trait for bill-pay, BillPayService
  • xmlprotocol.scala
    • implicit definitions to provide XML codecs for the various generated case classes
    • a soap implementation, BillPayServiceSoapBindings, of the abstract service trait BillPayService
    • a trait called BillPayServiceSoapBindings which is used to bind to an endpoint URI (by overriding its baseAddress field).

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.billpay
  • com.banno.jackhenry.jxchange.checkimages
  • com.banno.jackhenry.jxchange.custom
  • com.banno.jackhenry.jxchange.customer
  • com.banno.jackhenry.jxchange.identitymanagement
  • com.banno.jackhenry.jxchange.inquiry
  • com.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 URI for 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:

  1. Read the appropriate row from the database table called connection_config_defaults
  2. Instantiate a ConnectionConfig
  3. Creds are stored in the vault and are decrypted using private keys are in a Config value.

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:

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:
    • Ping
    • BilPayChanInq
    • BilPaySubAdd
    • BilPaySubInq
    • BilPaySubSrch
    • BilPaySubConsmCustInq
    • BilPayPayeeSrch
    • BilPayPayeeInq
    • BilPayPayeeAdd
    • BilPayPayeeMod
    • BilPayPmtHistInq
    • BilPayPmtHistSrch
    • BilPaySchedPmtSrch
    • BilPaySchedPmtInq
    • BilPaySchedPmtAdd
    • BilPaySchedPmtMod

jxchange-scalaxb-customer.jar

  • package: com.jackhenry.jxchange.customer
  • operations
    • Ping
    • CustAdd
    • CustInq
    • CustSrch
    • ParmValSrch
    • CustMod
    • IntnetFinInstIdInq
    • IntnetFinInstIdUsrInq
    • EFTCardMod
    • EFTCardSrch
    • EFTCardInq

jxchange-scalaxb-inquiry.jar

  • package: com.jackhenry.jxchange.inquiry
  • operations:
    • Ping
    • AcctSrch
    • AcctInq
    • AcctHistSrch
    • XferSrch
    • XferSrcDestSrch

jxchange-scalaxb-transaction.jar

  • package: com.jackhenry.jxchange.transaction
  • operations:
    • Ping
    • TrnAdd
    • XferAdd
    • XferAddValidate
    • XferMod

jxchange-scalaxb-identity-management.jar

  • package: com.jackhenry.jxchange.identitymanagement
  • operations:
    • Ping
    • MFAUsrQnAInq

jxchange-scalaxb-deposit.jar

  • package: com.jackhenry.jxchange.deposit
  • operations:
    • Ping
    • AcctAdd
    • AcctMod

jxchange-scalaxb-loan.jar

  • package: com.jackhenry.jxchange.loan
  • operations:
    • Ping
    • LnAcctAdd

jxchange-scalaxb-custom.jar

  • package: com.jackhenry.jxchange.custom
  • operations:
    • IntnetFinInstUsrAuthenInq

jxchange-scalaxb-statement.jar

  • package: com.jackhenry.jxchange.statements
  • operations:
    • ChkImgStmtSrch
    • ChkImgStmtGen
    • ChkImgStmtGenInq

jxchange-scalaxb-check-images.jar

  • package: com.jackhenry.jxchange.checkimages
  • operations:
    • ChkImgInq