anchor.diff:        {
           if( not structKeyExists( application, variables.cacheName) or structKeyExists( url, "reload" ))
           {
  -          application[variables.cacheName] = {};
  +          application[variables.cacheName]={};
           }
   
           /* Optionally read config from a file, otherwise, just instantiate the cfc with your options as arguments */
           if( len( trim( config )) and fileExists( config ))
           {
  -          application[variables.cacheName].properties = {};
  -
  -          local.config = fileRead( config );
  +          application[variables.cacheName].properties={};
  +
  +          local.config=fileRead( config );
   
             for( valuePair in listToArray( local.config, "#chr( 13 )##chr( 10 )#" ))
             {
  
positive.code: component accessors = true
            persistent = true
  {
    /*
      Generate ideal certificates like this:
  
      1.  keytool -genkey -keyalg RSA -sigAlg SHA256withRSA -keysize 2048 -validity 1825 -alias {KeyStoreAlias} -keystore {keystoreFileName.ks}
      2.  keytool -export -v -rfc -alias {KeyStoreAlias} -keystore {keystoreFileName.ks} -file {certificateFileName.cer}
  
      Upload ks file to your server ( not to a web accessible directory )
      Upload cer file to ideal dashboard
  
      Requirements:
       - javaloader which is used to compile idealcrypto.class
    */
  
    property name="timestamp" type="date";
    property name="issuerID" type="string";
    property name="merchantID" type="numeric";
    property name="subID" default="0" type="numeric";
    property name="purchaseID" type="numeric"                                     hint="Order ID";
    property name="transactionID" default="" type="string";
    property name="amount" type="numeric";
    property name="currency" type="string" default="EUR";
    property name="language" type="string" default="nl";
    property name="description" type="string"                                     hint="NO HTML ALLOWED!";
    property name="entranceCode" type="string"                                    hint="Session ID";
    property name="expirationPeriod" type="string"                                hint="Optional, date period format: PnYnMnDTnHnMnS";
    property name="defaultCountry" default="Nederland" type="string"              hint="Optional, set to country of website";
    property name="merchantReturnURL" type="string";
    property name="ksFile" type="string";
    property name="ksAlias" type="string";
    property name="ksPassword" type="string";
    property name="idealURL" required="yes" type="string";
    property name="debugIP" default="::1,fe80:0:0:0:0:0:0:1%1,127.0.0.1" required=false type="string";
    property name="debugEmail" default="administrator@your-website-here.nl" required=false type="string";
    property name="debugLog" default="ideal-cfc" required=false type="string";
  
    variables.cacheName = "cache";
  
    if( not structKeyExists( server, 'idealcrypto' ))
    {
      variables.pwd = getDirectoryFromPath( GetCurrentTemplatePath());
      variables.jl = new javaloader.JavaLoader( sourceDirectories = [ '#variables.pwd#\..\java' ]);
      variables.idealcrypto = jl.create( 'idealcrypto' );
      server.idealcrypto = variables.idealcrypto;
    }
  
    variables.idealcrypto = server.idealcrypto;
  
    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
    public ideal function init( string config="", struct initProperties={} )
    {
      try
      {
        var tempfunc = "";
  
        lock  name="lock_#application.applicationname#_init"
              timeout="5"
  
negative.code:               continue;
              }
  
              initProperties[trim( listFirst( valuePair, ' #chr( 9 )#' ))]=trim( listRest( valuePair, ' #chr( 9 )#' ));
            }
          }
        }
  
        for( var key in initProperties )
        {
          tempfunc=this["set#key#"];
          tempfunc( initProperties[key] );
        }
  
        if( not fileExists( getKSFile()))
        {
          throw( message="Missing keystore file (#getKSFile()#)" );
        }
  
        return this;
      }
      catch( Any cfcatch )
      {
        return handleError( cfcatch );
      }
    }
  
    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
    /**
     * Method to generate a <select /> with a list of participating banks
     * @param calss           String    CSS class name(s)
     */
    public Void function directoryRequest( String class="" )
    {
      try
      {
        var cacheName="DR_#dateFormat( now(), 'yyyymmdd' )#";
        var issuerXML="";
        var issuerList="";
        var result="";
  
        // Per ideal request, this is cached for the duration of the application's life.
        if( not structKeyExists( application[variables.cacheName], cacheName ))
        {
          var issuersXML=postRequest( "Directory" );
          var issuers={};
          var issuerLists=issuersXML.DirectoryRes.Directory;
  
          if( structKeyExists( issuersXML.DirectoryRes.Directory, "Country" ))
          {
            issuerLists=issuersXML.DirectoryRes.Directory.Country;
          }
  
          for( var issuerXML in issuerLists.xmlChildren )
          {
            if( issuerXML.xmlName eq "countryNames" )
            {
              issuerList=issuerXML.xmlText;
            }
  
            if( issuerXML.xmlName neq "Issuer" )
            {
              continue;
            }
  
            if( not structKeyExists( issuers, issuerList ))
            {
              issuers[issuerList]=[];
            }
  
            arrayAppend( issuers[issuerList], {
              "id"=issuerXML.xmlChildren[1].xmlText,
              "name"=issuerXML.xmlChildren[2].xmlText
            });
          }
  
          application[variables.cacheName][cacheName]=issuers;
        }
  
        issuers=application[variables.cacheName][cacheName];
        var issuerKeyList=listSort( structKeyList( issuers ), 'text' );
  
        if( len( getDefaultCountry()))
        {
          if( listFindNoCase( issuerKeyList, getDefaultCountry()))
          {
            issuerKeyList=listDeleteAt( issuerKeyList, listFindNoCase( issuerKeyList, getDefaultCountry()));
          }
          issuerKeyList=listPrepend( issuerKeyList, getDefaultCountry());
        }
  
        result &='<select name="issuerID" id="issuerID" style="margin-bottom:10px;" class="#class#">';
        result &='<option value="">Kies uw bank:</option>';
  
        for( var key in issuerKeyList )
        {
          if( not structKeyExists( issuers, key ))
          {
            continue;
          }
  
          result &='<optgroup label="#key#">';
  
          for( var issuer in issuers[key] )
          {
            result &='<option value="#issuer.id#">#issuer.name#</option>';
          }
  
          result &='</optgroup>';
        }
  
        result &='</select>';
  
        return result;
      }
      catch( Any cfcatch )
      {
        return handleError( cfcatch );
      }
    }
  
    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
    /**
     * @param redirect        boolean   if you need to do more processing before
     *                                  being redirected, set this to false.
     */
    public Void function transactionRequest( boolean redirect=true )
    {
      try
      {
        var transactionXML=postRequest( "Transaction" );
  
        if( redirect )
        {
          location( url=transactionXML.AcquirerTrxRes.Issuer.issuerAuthenticationURL.xmlText,
                    addToken=false );
        }
  
        setTransactionID( transactionXML.AcquirerTrxRes.Transaction.transactionID.XmlText );
  
        return transactionXML.AcquirerTrxRes.Issuer.issuerAuthenticationURL.xmlText;
      }
      catch( Any cfcatch )
      {
        return handleError( cfcatch );
      }
    }
  
    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
    /**
     * @return  Returns the status of the current order being processed by ideal
     */
    public Void function statusRequest()
    {
      try
      {
        return postRequest( "Status" );
      }
      catch( Any cfcatch )
      {
        return handleError( cfcatch );
      }
    }
  
    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
    /**
     * Signs the supplied XML requests and appends that signature to the request.
     * @param   strToSign   String  XML String to sign
     * @return              String  Signed XML (including the signature)
     */
    public String function signXML( required String strToSign )
    {
      try
      {
        var XMLSignatureFactory=createObject( "java", "javax.xml.crypto.dsig.XMLSignatureFactory" );
        var DigestMethod=createObject( "java", "javax.xml.crypto.dsig.DigestMethod" );
        var TransformService=createObject( "java", "javax.xml.crypto.dsig.TransformService" );
        var DOMTransform=createObject( "java", "org.jcp.xml.dsig.internal.dom.DOMTransform" );
        var DocumentBuilderFactory=createObject( "java", "javax.xml.parsers.DocumentBuilderFactory" );
        var CanonicalizationMethod=createObject( "java", "javax.xml.crypto.dsig.CanonicalizationMethod" );
        var C14NMethodParameterSpec=createObject( "java", "javax.xml.crypto.dsig.spec.C14NMethodParameterSpec" );
        var InputSource=createObject( "java", "org.xml.sax.InputSource" );
        var StringReader=createObject( "java", "java.io.StringReader" );
        var PKCS8EncodedKeySpec=createObject( "java", "java.security.spec.PKCS8EncodedKeySpec" );
        var KeyFactory=createObject( "java", "java.security.KeyFactory" ).getInstance( "RSA" );
        var DOMSignContext=createObject( "java", "javax.xml.crypto.dsig.dom.DOMSignContext" );
        var DOMSource=createObject( "java", "javax.xml.transform.dom.DOMSource" );
        var TransformerFactory=createObject( "java", "javax.xml.transform.TransformerFactory" );
        var Transformer=createObject( "java", "javax.xml.transform.Transformer" );
        var StringWriter=createObject( "java", "java.io.StringWriter" ).init();
        var StreamResult=createObject( "java", "javax.xml.transform.stream.StreamResult" );
        var DOMStructure=createObject( "java", "javax.xml.crypto.dom.DOMStructure" );
        var KeyStore=createObject( "java", "java.security.KeyStore" );
        var PasswordProtection=createObject( "java", "java.security.KeyStore$PasswordProtection" ).init( getKSPassword().toCharArray());
        var FileInputStream=createObject( "java", "java.io.FileInputStream" );
  
        /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
        /* ~~ Signature creation: Step 1                                  ~~ */
        /* ~~ Is now done in a java file compiled at runtime              ~~ */
        /* ~~ idealcrypto.class                                           ~~ */
        /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
        var facObj=variables.idealcrypto.init();
        var fac=facObj.fac;
        var signedInfo=facObj.signedInfo;
  
        /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
        /* ~~ Signature creation: Step 2                                  ~~ */
        /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  
        /* Load the KeyStore and get the signing key and certificate. */
        var ksfile=FileInputStream.init( getKSFile());
        var ks=KeyStore.getInstance( "JKS" );
        ks.load( ksfile, getKSPassword().toCharArray());
        var keyEntry=ks.getEntry( getKSAlias(), PasswordProtection );
        var cert=keyEntry.getCertificate();
        ksfile.close();
  
        /* Create the KeyInfo containing the X509Data. */
        var kif=fac.getKeyInfoFactory();
        var keyInfo=kif.newKeyInfo([kif.newKeyName( createSHA1Fingerprint( cert ))]);
  
        /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
        /* ~~ Signature creation: Step 3                                  ~~ */
        /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  
        /* Instantiate the document to be signed. */
        var dbf_i=DocumentBuilderFactory.newInstance();
        dbf_i.setNamespaceAware( true );
        var doc=dbf_i.newDocumentBuilder().parse( InputSource.init( StringReader.init( strToSign )));
  
        /* Create a DOMSignContext and specify the RSA PrivateKey and location of
              the resulting XMLSignature's parent element. */
        var dsc=DOMSignContext.init( keyEntry.getPrivateKey(), doc.getDocumentElement());
  
        /* Create the XMLSignature, but don't sign it yet. */
        var signature=fac.newXMLSignature( signedInfo, keyInfo );
  
        /* Marshal, generate, and sign the enveloped signature. */
        signature.sign( dsc );
  
        /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
        /* ~~ Signature creation: Step 4                                  ~~ */
        /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
        /* Output the resulting document. */
  
        var xmlResult=StreamResult.init( StringWriter );
        var ds=DOMSource.init( doc );
        var tf=TransformerFactory.newInstance();
        var trans=tf.newTransformer();
        trans.transform( ds, xmlResult );
  
        return StringWriter.toString();
      }
      catch( Any cfcatch )
      {
        return handleError( cfcatch );
      }
    }
  
    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
    /**
     * @return  String  Returns the fingerprint of the certificate set in the config file.
     */
    public String function getFingerprint()
    {
      try
      {
        var jFileInputStream=createObject( "java", "java.io.FileInputStream" );
        var jKeyStore=createObject( "java", "java.security.KeyStore" );
        var jPasswordProtection=createObject( "java", "java.security.KeyStore$PasswordProtection" );
  
        var ks=jKeyStore.getInstance( "JKS" );
        ks.load( jFileInputStream.init( getKSFile()), getKSPassword().toCharArray());
  
        var keyEntry=ks.getEntry( getKSAlias(), jPasswordProtection.init( getKSPassword().toCharArray()));
  
        return createSHA1Fingerprint( keyEntry.getCertificate());
      }
      catch( Any cfcatch )
      {
        return handleError( cfcatch );
      }
    }
  
  
  
  
    // PRAVIATE METHODS BELOW:
  
    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
    /**
     * Global error handler
     * @param error           Any       Struct containing the thrown error
     */
    private Void function handleError( Any error )
    {
      param name="error.message" default="";
      param name="error.detail" default="";
      param name="error.errorCode" default="";
      param name="error.extendedInfo" default="";
  
      savecontent variable="local.errorDump"
      {
        writeDump( error );
  