diff --git a/Harmony_Individual_Contributor_Assignment_Agreement.txt b/Harmony_Individual_Contributor_Assignment_Agreement.txt index 26226395f..d3eb7ae9c 100644 --- a/Harmony_Individual_Contributor_Assignment_Agreement.txt +++ b/Harmony_Individual_Contributor_Assignment_Agreement.txt @@ -91,9 +91,9 @@ Address: ________________________ Us ________________________ Name: Simon Redfern -Title: CEO TESOBE / Music Pictures Ltd +Title: CEO TESOBE Ltd Address: Osloerstrasse 16/17 - Berlin 13359, German + Berlin 13359, Germany @@ -125,4 +125,4 @@ Address: Osloerstrasse 16/17 -This work is licensed under a Creative Commons Attribution 3.0 Unported License. \ No newline at end of file +This work is licensed under a Creative Commons Attribution 3.0 Unported License. diff --git a/README.md b/README.md index be99f8906..22ac38280 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,7 @@ Please refer to the [wiki](https://github.com/OpenBankProject/OBP-API/wiki) to s ## LICENSE -This project is dual licensed under the AGPL V3 (see NOTICE) and a commercial license from TESOBE -Some files (OAuth related) are licensed under the Apache 2 license. +This project is dual licensed under the AGPL V3 (see NOTICE) and commercial licenses from TESOBE Ltd. ## SETUP @@ -85,7 +84,33 @@ To compile and run jetty, install Maven 3 and execute: ---- +## Ubuntu + +If you use Ubuntu (or a derivate) and encrypted home directories (e.g. you have ~/.Private), you might run into the following error when the project is built: + + uncaught exception during compilation: java.io.IOException + [ERROR] File name too long + [ERROR] two errors found + [DEBUG] Compilation failed (CompilerInterface) + +The current workaround is to move the project directory onto a different partition, e.g. under /opt/ . + + # Databases: The default database for testing etc is H2. PostgreSQL is used for the sandboxes (user accounts, metadata, transaction cache). + +# Kafka (optional): + +If Kafka connector is selected in props (connector=kafka), Kafka and Zookeeper have to be installed, as well as OBP-Kafka-Python (which can be either running from command-propmpt or from inside Docker container): + +* Kafka and Zookeeper can be installed using system's default installer or by unpacking the archives (http://apache.mirrors.spacedump.net/kafka/ and http://apache.mirrors.spacedump.net/zookeeper/) + +* OBP-Kafka-Python can be downloaded from https://github.com/OpenBankProject/OBP-Kafka-Python + + +# Lift +# We use liftweb http://www.liftweb.net/ +# Advanced architecture: http://exploring.liftweb.net/master/index-9.html + diff --git a/pom.xml b/pom.xml index a1e03fb28..7d259c4d1 100644 --- a/pom.xml +++ b/pom.xml @@ -161,6 +161,11 @@ pegdown 1.6.0 + + com.jason-goodwin + authentikat-jwt_2.10 + 0.4.1 + @@ -224,7 +229,7 @@ -DpackageLinkDefs=file://${project.build.directory}/packageLinkDefs.properties - ${scala.version} + ${scala.compiler} ${scala.version} incremental true diff --git a/project/build.scala b/project/build.scala index 39fb7bc15..9dc1a79d4 100755 --- a/project/build.scala +++ b/project/build.scala @@ -38,14 +38,8 @@ object LiftProjectBuild extends Build { }).toMap private lazy val PropertiesExpr = """.*\$\{(.*?)\}.*""".r - private lazy val TwoPropertiesExpr = """.*\$\{(.*?)\}.*\$\{(.*?)\}.*""".r def populateProps(t: String) = t match { - case TwoPropertiesExpr(p, q) => { - t - .replace("${"+p+"}", pomProperties.get(p).getOrElse(throw new Exception("Cannot find property: '" + p + "' required by: '" + t + "' in: " + pomFile))) - .replace("${"+q+"}", pomProperties.get(q).getOrElse(throw new Exception("Cannot find property: '" + q + "' required by: '" + t + "' in: " + pomFile))) - } case PropertiesExpr(p) => { val replaceWith = pomProperties.get(p) t.replace("${"+p+"}", replaceWith.getOrElse(throw new Exception("Cannot find property: '" + p + "' required by: '" + t + "' in: " + pomFile))) diff --git a/src/main/resources/props/sample.props.template b/src/main/resources/props/sample.props.template index 02628b31b..d70eb829f 100644 --- a/src/main/resources/props/sample.props.template +++ b/src/main/resources/props/sample.props.template @@ -30,7 +30,7 @@ db.url=jdbc:postgresql://localhost:5432/dbname?user=dbusername&password=thepassw #our own remotely accessible URL #this is needed for oauth to work. it's important to access the api over this url, e.g. # if this is 127.0.0.1 don't use localhost to access it. -# (this needs to be an URL) +# (this needs to be a URL) hostname=http://127.0.0.1:8080 #this is only useful for running the api locally via RunWebApp @@ -38,6 +38,14 @@ hostname=http://127.0.0.1:8080 #if you want to change the port when running via the command line, use "mvn -Djetty.port=8089 jetty:run" instead dev.port=8080 + +#The start of the api path (before the version) +#It is *strongly* recommended not to change this - since Apps will be expecting the api at /obp/+version +#Including it here so we have a canonical source of the value +#This was introduced March 2016, some code might use hardcoded value instead. +#Default value is obp (highly recomended) +apiPathZero=obp + #sending mail out #not need in dev mode, but important for production mail.api.consumer.registered.sender.address=no-reply@example.com @@ -88,7 +96,7 @@ connection.password=thepassword importer_secret=change_me #set this to true if you want to have the api send a message to the hbci project to refresh transactions for an account -messageQueue.updateBankAccountsTransaction=true +messageQueue.updateBankAccountsTransaction=false #the minimum time between updates in hours messageQueue.updateTransactionsInterval=1 @@ -114,13 +122,25 @@ webui_index_page_about_section_text =

\

-# API Explorer url. Change this to the instance of Sofi linked to this API. e.g. sofi.openbankproject.com/api-explorer -webui_api_explorer_url = http://sofi.openbankproject.com/api-explorer +# API Explorer url. Change to your instance +webui_api_explorer_url = http://apiexplorer.openbankproject.com + +# Sofi url. (AKA Social Finance) Change to your instance +webui_sofi_url = http://sofi.openbankproject.com # Starting page of documentation. Change this if you have a specific landing page. webui_api_documentation_url = https://github.com/OpenBankProject/OBP-API/wiki +# To display a custom message above the username / password box +# We currently use this to display example customer login in sandbox etc. +webui_login_page_special_instructions= + +# Link for SDKs +webui_sdks_url = https://github.com/OpenBankProject/OBP-API/wiki/OAuth-Client-SDKS + + + ## For partner logos and links webui_main_partners=[\ {"logoUrl":"http://www.example.com/images/logo.png", "homePageUrl":"http://www.example.com", "altText":"Example 1"},\ @@ -131,3 +151,13 @@ webui_main_style_sheet = /media/css/website.css # Override certain elements (with important styles) webui_override_style_sheet = + + +## API Options +apiOptions.getBranchesIsPublic = true +apiOptions.getAtmsIsPublic = true +apiOptions.getProductsIsPublic = true + +# Default Bank. Incase the server wants to support a default bank so developers don't have to specify BANK_ID +# e.g. developers could use /my/accounts as well as /my/banks/BANK_ID/accounts +defaultBank.bank_id=THE_DEFAULT_BANK_ID diff --git a/src/main/scala/bootstrap/liftweb/Boot.scala b/src/main/scala/bootstrap/liftweb/Boot.scala index 20b70a600..cbe1955e9 100755 --- a/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/src/main/scala/bootstrap/liftweb/Boot.scala @@ -31,41 +31,45 @@ Berlin 13359, Germany */ package bootstrap.liftweb +import java.io.{File, FileInputStream} +import java.util.Locale +import javax.mail.internet.MimeMessage + +import code.api.ResourceDocs1_4_0.ResourceDocs +import code.api._ import code.api.sandbox.SandboxApiCalls +import code.atms.MappedAtm +import code.branches.MappedBranch import code.crm.MappedCrmEvent -import code.management.ImporterAPI -import code.management.AccountsAPI +import code.customer.{MappedCustomer, MappedCustomerMessage} +import code.kycdocuments.MappedKycDocument +import code.kycmedias.MappedKycMedia +import code.kycchecks.MappedKycCheck +import code.kycstatuses.MappedKycStatus +import code.socialmedia.MappedSocialMedia +import code.management.{AccountsAPI, ImporterAPI} import code.metadata.comments.MappedComment -import code.metadata.counterparties.{MappedCounterpartyWhereTag, MappedCounterpartyMetadata} +import code.metadata.counterparties.{MappedCounterpartyMetadata, MappedCounterpartyWhereTag} import code.metadata.narrative.MappedNarrative import code.metadata.tags.MappedTag import code.metadata.transactionimages.MappedTransactionImage import code.metadata.wheretags.MappedWhereTag import code.metrics.MappedMetric -import code.branches.{MappedBranch} -import code.atms.{MappedAtm} -import code.customer.{MappedCustomerMessage, MappedCustomer} -import code.products.MappedProduct -import code.tesobe.CashAccountAPI -import code.transactionrequests.MappedTransactionRequest -import net.liftweb._ -import util._ -import common._ -import http._ -import sitemap._ -import Loc._ -import mapper._ -import net.liftweb.util.Helpers._ -import net.liftweb.util.Schedule -import net.liftweb.util.Helpers -import java.util.Locale -import java.io.FileInputStream -import java.io.File -import javax.mail.internet.MimeMessage import code.model._ import code.model.dataAccess._ -import code.api._ +import code.products.MappedProduct import code.snippet.{OAuthAuthorisation, OAuthWorkedThanks} +import code.tesobe.CashAccountAPI +import code.transactionrequests.MappedTransactionRequest +import net.liftweb.common._ +import net.liftweb.http._ +import net.liftweb.mapper._ +import net.liftweb.sitemap.Loc._ +import net.liftweb.sitemap._ +import net.liftweb.util.Helpers._ +import net.liftweb.util.{Helpers, Schedule, _} +import code.api.Constant._ + /** * A class that's instantiated early and run. It allows the application @@ -175,6 +179,7 @@ class Boot extends Loggable{ } logger.info("running mode: " + runningMode) + logger.info(s"ApiPathZero (the bit before version) is $ApiPathZero") // where to search snippets LiftRules.addToPackages("code") @@ -182,13 +187,20 @@ class Boot extends Loggable{ //OAuth API call LiftRules.statelessDispatch.append(OAuthHandshake) + // JWT auth endpoints + if(Props.getBool("allow_direct_login", true)) { + LiftRules.statelessDispatch.append(DirectLogin) + } + // Add the various API versions LiftRules.statelessDispatch.append(v1_0.OBPAPI1_0) LiftRules.statelessDispatch.append(v1_1.OBPAPI1_1) LiftRules.statelessDispatch.append(v1_2.OBPAPI1_2) + // Can we depreciate the above? LiftRules.statelessDispatch.append(v1_2_1.OBPAPI1_2_1) LiftRules.statelessDispatch.append(v1_3_0.OBPAPI1_3_0) LiftRules.statelessDispatch.append(v1_4_0.OBPAPI1_4_0) + LiftRules.statelessDispatch.append(v2_0_0.OBPAPI2_0_0) //add management apis LiftRules.statelessDispatch.append(ImporterAPI) @@ -198,6 +210,11 @@ class Boot extends Loggable{ LiftRules.statelessDispatch.append(CashAccountAPI) LiftRules.statelessDispatch.append(BankMockAPI) + + + // Add Resource Docs + LiftRules.statelessDispatch.append(ResourceDocs) + // LiftRules.statelessDispatch.append(Metrics) TODO: see metric menu entry below //add sandbox api calls only if we're running in sandbox mode @@ -282,13 +299,13 @@ class Boot extends Loggable{ // Make a transaction span the whole HTTP request S.addAround(DB.buildLoanWrapper) - val useMessageQueue = Props.getBool("messageQueue.createBankAccounts", false) - if(useMessageQueue) - try { + try { + val useMessageQueue = Props.getBool("messageQueue.createBankAccounts", false) + if(useMessageQueue) BankAccountCreationListener.startListen - } catch { - case e: java.lang.ExceptionInInitializerError => logger.warn(s"BankAccountCreationListener Exception: $e") - } + } catch { + case e: java.lang.ExceptionInInitializerError => logger.warn(s"BankAccountCreationListener Exception: $e") + } Mailer.devModeSend.default.set( (m : MimeMessage) => { logger.info("Would have sent email if not in dev mode: " + m.getContent) @@ -327,8 +344,8 @@ class Boot extends Loggable{ } private def sendExceptionEmail(exception: Throwable): Unit = { + import Mailer.{From, PlainMailBodyType, Subject, To} import net.liftweb.util.Helpers.now - import Mailer.{From, To, Subject, PlainMailBodyType} val outputStream = new java.io.ByteArrayOutputStream val printStream = new java.io.PrintStream(outputStream) @@ -390,5 +407,10 @@ object ToSchemify { MappedBranch, MappedAtm, MappedProduct, - MappedCrmEvent) + MappedCrmEvent, + MappedKycDocument, + MappedKycMedia, + MappedKycCheck, + MappedKycStatus, + MappedSocialMedia) } diff --git a/src/main/scala/code/api/OBPRestHelper.scala b/src/main/scala/code/api/OBPRestHelper.scala index fe22f121a..f1ee71f43 100644 --- a/src/main/scala/code/api/OBPRestHelper.scala +++ b/src/main/scala/code/api/OBPRestHelper.scala @@ -28,7 +28,7 @@ Berlin 13359, Germany Everett Sochowski : everett AT tesobe DOT com Ayoub Benali: ayoub AT tesobe DOT com - */ + */ package code.api @@ -43,6 +43,8 @@ import code.model.User import code.api.OAuthHandshake._ import net.liftweb.json.JsonAST.JValue import net.liftweb.json.Extraction +import net.liftweb.util.Props +import code.api.Constant._ trait APIFailure{ val msg : String @@ -61,7 +63,7 @@ object APIFailure { //that all stable versions retain the same behavior case class UserNotFound(providerId : String, userId: String) extends APIFailure { val responseCode = 400 //TODO: better as 404? -> would break some backwards compatibility (or at least the tests!) - + //to reiterate the comment about preserving backwards compatibility: //consider the case that an app may be parsing this string to decide what message to show their users //e.g. when granting view permissions, an app may not give their users a choice of provider and only @@ -79,7 +81,8 @@ trait OBPRestHelper extends RestHelper with Loggable { val VERSION : String def vPlusVersion = "v" + VERSION - def apiPrefix = ("obp" / vPlusVersion).oPrefix(_) + + def apiPrefix = (ApiPathZero / vPlusVersion).oPrefix(_) /* An implicit function to convert magically between a Boxed JsonResponse and a JsonResponse @@ -135,16 +138,25 @@ trait OBPRestHelper extends RestHelper with Loggable { case Failure(msg, _, _) => errorJsonResponse(msg) case _ => errorJsonResponse("oauth error") } - } else fn(Empty) + } else if (Props.getBool("allow_direct_login", true) && isThereDirectLoginHeader) { + val user = DirectLogin.getUser + if (user.isDefined) { + fn(user) + } else { + fn(Empty) + } + } else { + fn(Empty) + } } class RichStringList(list: List[String]) { val listLen = list.length /** - * Normally we would use ListServeMagic's prefix function, but it works with PartialFunction[Req, () => Box[LiftResponse]] - * instead of the PartialFunction[Req, Box[User] => Box[JsonResponse]] that we need. This function does the same thing, really. - */ + * Normally we would use ListServeMagic's prefix function, but it works with PartialFunction[Req, () => Box[LiftResponse]] + * instead of the PartialFunction[Req, Box[User] => Box[JsonResponse]] that we need. This function does the same thing, really. + */ def oPrefix(pf: PartialFunction[Req, Box[User] => Box[JsonResponse]]): PartialFunction[Req, Box[User] => Box[JsonResponse]] = new PartialFunction[Req, Box[User] => Box[JsonResponse]] { def isDefinedAt(req: Req): Boolean = diff --git a/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocs.scala b/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocs.scala new file mode 100644 index 000000000..5fd207836 --- /dev/null +++ b/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocs.scala @@ -0,0 +1,23 @@ +package code.api.ResourceDocs1_4_0 + +import code.api.OBPRestHelper + + +import net.liftweb.common.Loggable + + +object ResourceDocs extends OBPRestHelper with ResourceDocsAPIMethods with Loggable { + + val VERSION = "1.4.0" + + + val routes = List( + ImplementationsResourceDocs.getResourceDocsObp, + ImplementationsResourceDocs.getResourceDocsSwagger + ) + + routes.foreach(route => { + oauthServe(apiPrefix{route}) + }) + +} diff --git a/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala b/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala new file mode 100644 index 000000000..ff5219f54 --- /dev/null +++ b/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala @@ -0,0 +1,236 @@ +package code.api.ResourceDocs1_4_0 + +import code.api.v1_4_0.{APIMethods140, JSONFactory1_4_0, OBPAPI1_4_0} +import net.liftweb.common.{Box, Full, Loggable} +import net.liftweb.http.rest.RestHelper +import net.liftweb.http.{JsonResponse, Req} +import net.liftweb.json.Extraction +import net.liftweb.json.JsonAST.JValue +import net.liftweb.json.JsonDSL._ +import net.liftweb.util.Props + +import scala.collection.immutable.Nil + +// JObject creation +import code.api.v1_2_1.{APIInfoJSON, APIMethods121, HostedBy, OBPAPI1_2_1} +import code.api.v1_3_0.{APIMethods130, OBPAPI1_3_0} +import code.api.v2_0_0.{APIMethods200, OBPAPI2_0_0} + +import scala.collection.mutable.ArrayBuffer + +// So we can include resource docs from future versions +import java.text.SimpleDateFormat + +import code.api.util.APIUtil.{ResourceDoc, _} +import code.model._ + +trait ResourceDocsAPIMethods extends Loggable with APIMethods200 with APIMethods140 with APIMethods130 with APIMethods121{ + //needs to be a RestHelper to get access to JsonGet, JsonPost, etc. + // We add previous APIMethods so we have access to the Resource Docs + self: RestHelper => + + val ImplementationsResourceDocs = new Object() { + + val resourceDocs = ArrayBuffer[ResourceDoc]() + val emptyObjectJson : JValue = Nil + val apiVersion : String = "1_4_0" + + val exampleDateString : String ="22/08/2013" + val simpleDateFormat : SimpleDateFormat = new SimpleDateFormat("dd/mm/yyyy") + val exampleDate = simpleDateFormat.parse(exampleDateString) + + + def getResourceDocsList(requestedApiVersion : String) : Option[List[ResourceDoc]] = + { + // Return a different list of resource docs depending on the version being called. + // For instance 1_3_0 will have the docs for 1_3_0 and 1_2_1 (when we started adding resource docs) etc. + + logger.info(s"getResourceDocsList says requestedApiVersion is $requestedApiVersion") + + val resourceDocs = requestedApiVersion match { + case "2.0.0" => Implementations2_0_0.resourceDocs ++ Implementations1_4_0.resourceDocs ++ Implementations1_3_0.resourceDocs ++ Implementations1_2_1.resourceDocs + case "1.4.0" => Implementations1_4_0.resourceDocs ++ Implementations1_3_0.resourceDocs ++ Implementations1_2_1.resourceDocs + case "1.3.0" => Implementations1_3_0.resourceDocs ++ Implementations1_2_1.resourceDocs + case "1.2.1" => Implementations1_2_1.resourceDocs + } + + logger.info(s"There are ${resourceDocs.length} resource docs available to $requestedApiVersion") + + val versionRoutes = requestedApiVersion match { + case "2.0.0" => OBPAPI2_0_0.routes + case "1.4.0" => OBPAPI1_4_0.routes + case "1.3.0" => OBPAPI1_3_0.routes + case "1.2.1" => OBPAPI1_2_1.routes + } + + logger.info(s"There are ${versionRoutes.length} routes available to $requestedApiVersion") + + + // We only want the resource docs for which a API route exists else users will see 404s + // Get a list of the partial function classes represented in the routes available to this version. + val versionRoutesClasses = versionRoutes.map { vr => vr.getClass } + + // Only return the resource docs that have available routes + val activeResourceDocs = resourceDocs.filter(rd => versionRoutesClasses.contains(rd.partialFunction.getClass)) + + logger.info(s"There are ${activeResourceDocs.length} resource docs available to $requestedApiVersion") + + // Sort by endpoint, verb. Thus / is shown first then /accounts and /banks etc. Seems to read quite well like that. + Some(activeResourceDocs.toList.sortBy(rd => (rd.requestUrl, rd.requestVerb))) + } + + + + resourceDocs += ResourceDoc( + getResourceDocsObp, + apiVersion, + "getResourceDocsObp", + "GET", + "/resource-docs/obp", + "Get Resource Documentation in OBP format.", + """Returns documentation about the RESTful resources on this server including example body for POST or PUT requests. + | Thus the OBP API Explorer (and other apps) can display and work with the API documentation. + | In the future this information will be used to create Swagger (WIP) and RAML files. + | + """, + emptyObjectJson, + emptyObjectJson, + emptyObjectJson :: Nil, + false, + false, + List(apiTagApiInfo) + ) + + // Provides resource documents so that API Explorer (or other apps) can display API documentation + // Note: description uses html markup because original markdown doesn't easily support "_" and there are multiple versions of markdown. + + + // TODO constrain version? + // strip the leading v + def cleanApiVersionString (version: String) : String = {version.stripPrefix("v").stripPrefix("V")} + + def getResourceDocsObp : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + case "resource-docs" :: requestedApiVersion :: "obp" :: Nil JsonGet _ => { + user => { + for { + rd <- getResourceDocsList(cleanApiVersionString(requestedApiVersion)) + } yield { + // Format the data as json + val json = JSONFactory1_4_0.createResourceDocsJson(rd) + // Return + successJsonResponse(Extraction.decompose(json)) + } + } + } + } + + + resourceDocs += ResourceDoc( + getResourceDocsSwagger, + apiVersion, + "getResourceDocsSwagger", + "GET", + "/resource-docs/swagger", + "Get Resource Documentation in Swagger format. Work In Progress!", + """Returns documentation about the RESTful resources on this server in Swagger format. + | Currently this is incomplete. + """, + emptyObjectJson, + emptyObjectJson, + emptyObjectJson :: Nil, + false, + false, + List(apiTagApiInfo) + ) + + def getResourceDocsSwagger : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + case "resource-docs" :: requestedApiVersion :: "swagger" :: Nil JsonGet _ => { + user => { + for { + rd <- getResourceDocsList(cleanApiVersionString(requestedApiVersion)) + } yield { + // Format the data as json + val json = SwaggerJSONFactory.createSwaggerResourceDoc(rd) + // Return + successJsonResponse(Extraction.decompose(json)) + } + } + } + } + + + + + + + if (Props.devMode) { + resourceDocs += ResourceDoc( + dummy(apiVersion), + apiVersion, + "testResourceDoc", + "GET", + "/dummy", + "I am only a test resource Doc", + """ + | + |#This should be H1 + | + |##This should be H2 + | + |###This should be H3 + | + |####This should be H4 + | + |Here is a list with two items: + | + |* One + |* Two + | + |There are underscores by them selves _ + | + |There are _underscores_ around a word + | + |There are underscores_in_words + | + |There are 'underscores_in_words_inside_quotes' + | + |There are (underscores_in_words_in_brackets) + | + |_etc_...""", + emptyObjectJson, + emptyObjectJson, + emptyObjectJson :: Nil, + false, + false, + List(apiTagApiInfo)) + } + + + + def dummy(apiVersion : String) : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + case "dummy" :: Nil JsonGet json => { + user => + val apiDetails: JValue = { + val hostedBy = new HostedBy("TESOBE", "contact@tesobe.com", "+49 (0)30 8145 3994") + val apiInfoJSON = new APIInfoJSON(apiVersion, gitCommit, hostedBy) + Extraction.decompose(apiInfoJSON) + } + + Full(successJsonResponse(apiDetails, 200)) + } + } + + } + + + +} + + diff --git a/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerJSONFactory.scala b/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerJSONFactory.scala new file mode 100644 index 000000000..552890968 --- /dev/null +++ b/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerJSONFactory.scala @@ -0,0 +1,88 @@ +package code.api.ResourceDocs1_4_0 + +import code.api.util.APIUtil.ResourceDoc +import code.api.Constant._ +import net.liftweb.json._ +import net.liftweb.util.Props + +import scala.collection.immutable.ListMap + +object SwaggerJSONFactory { + + case class ContactJson( + name: String, + url: String + ) + + case class InfoJson( + title: String, + description: String, + contact: ContactJson, + version: String + ) + + case class ResponseObjectSchemaJson(`$ref`: String) + case class ResponseObjectJson(description: Option[String], schema: Option[ResponseObjectSchemaJson]) + + case class MethodJson(tags: List[String], + summary: String, + operationId: String, + responses: Map[String, ResponseObjectJson]) + + case class PathsJson(get: MethodJson) + + case class MessageJson(`type`: String) + + case class CodeJson(`type`: String, format: String) + + case class PropertiesJson(code: CodeJson, message: MessageJson) + + case class ErrorDefinitionJson(`type`: String, required: List[String], properties: PropertiesJson) + + case class DefinitionsJson(Error: ErrorDefinitionJson) + + case class SwaggerResourceDoc(swagger: String, + info: InfoJson, + host: String, + basePath: String, + schemes: List[String], + paths: Map[String, Map[String, MethodJson]], + definitions: DefinitionsJson + ) + + def createSwaggerResourceDoc(resourceDocList: List[ResourceDoc]): SwaggerResourceDoc = { + implicit val formats = DefaultFormats + + val contact = ContactJson("OBP", "https://openbankproject.com/") + val apiVersion = "v1.4.0" + val title = "Open Bank Project API" + val description = "An open source API for banks." + val info = InfoJson(title, description, contact, apiVersion) + // TODO check / improve host, basePath and version + val host = Props.get("hostname", "unknown host").replaceFirst("http://", "") + val basePath = s"/$ApiPathZero/" + apiVersion + val schemas = List("http") + val paths: ListMap[String, Map[String, MethodJson]] = resourceDocList.groupBy(x => x.requestUrl).toSeq.sortBy(x => x._1).map { mrd => + val methods: Map[String, MethodJson] = mrd._2.map(rd => + (rd.requestVerb, + MethodJson( + List(s"${rd.apiVersion.toString}"), + rd.summary, + s"${rd.apiVersion.toString}-${rd.apiFunction.toString}", + Map("200" -> ResponseObjectJson(Some("Success") , None), "400" -> ResponseObjectJson(Some("Error"), Some(ResponseObjectSchemaJson("#/definitions/Error")))))) + ).toMap + (mrd._1, methods.toSeq.sortBy(m => m._1).toMap) + }(collection.breakOut) + + val `type` = "object" + val required = List("code", "message") + val code = CodeJson("integer", "int32") + val message = MessageJson("string") + val properties = PropertiesJson(code, message) + val errorDef = ErrorDefinitionJson(`type`, required, properties) + val defs = DefinitionsJson(errorDef) + + SwaggerResourceDoc("2.0", info, host, basePath, schemas, paths, defs) + } + +} \ No newline at end of file diff --git a/src/main/scala/code/api/constant/constant.scala b/src/main/scala/code/api/constant/constant.scala new file mode 100644 index 000000000..9f5f3d63f --- /dev/null +++ b/src/main/scala/code/api/constant/constant.scala @@ -0,0 +1,17 @@ +package code.api + +import code.api.util.ErrorMessages +import net.liftweb.common.Loggable +import net.liftweb.util.Props + + +// Note: Import this with: import code.api.Constant._ +object Constant extends Loggable { + logger.info("Instantiating Constants") + + final val HostName = Props.get("hostname").openOrThrowException(ErrorMessages.HostnameNotSpecified) + + // This is the part before the version. Do not change this default! + final val ApiPathZero = Props.get("apiPathZero", "obp") + +} diff --git a/src/main/scala/code/api/directlogin.scala b/src/main/scala/code/api/directlogin.scala new file mode 100644 index 000000000..d104afd20 --- /dev/null +++ b/src/main/scala/code/api/directlogin.scala @@ -0,0 +1,313 @@ +/** +Open Bank Project + +Copyright 2011,2012 TESOBE / Music Pictures Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + Open Bank Project (http://www.openbankproject.com) + Copyright 2011,2012 TESOBE / Music Pictures Ltd + + This product includes software developed at + TESOBE (http://www.tesobe.com/) + by + Simon Redfern : simon AT tesobe DOT com + Everett Sochowski: everett AT tesobe DOT com + Ayoub Benali : ayoub AT tesobe Dot com + */ +package code.api + +import java.util.Date + +import authentikat.jwt.{JsonWebToken, JwtClaimsSet, JwtHeader} +import code.api.util.APIUtil._ +import code.model.dataAccess.OBPUser +import code.model.{Consumer, Token, TokenType, User} +import net.liftweb.common._ +import net.liftweb.http._ +import net.liftweb.http.rest.RestHelper +import net.liftweb.json.Extraction +import net.liftweb.mapper.By +import net.liftweb.util.{Props, Helpers} +import net.liftweb.util.Helpers._ + +import scala.compat.Platform +import code.api.util.ErrorMessages + +/** +* This object provides the API calls necessary to +* authenticate users using JSON Web Tokens (http://jwt.io). +*/ + +object JSONFactory { + case class TokenJSON( token : String ) + + def stringOrNull(text: String) = + if (text == null || text.isEmpty) + null + else + text + + def stringOptionOrNull(text: Option[String]) = + text match { + case Some(t) => stringOrNull(t) + case _ => null + } + + def createTokenJSON(token: String): TokenJSON = { + new TokenJSON( + stringOrNull(token) + ) + } +} + +object DirectLogin extends RestHelper with Loggable { + + // Our version of serve + def dlServe(handler : PartialFunction[Req, JsonResponse]) : Unit = { + val obpHandler : PartialFunction[Req, () => Box[LiftResponse]] = { + new PartialFunction[Req, () => Box[LiftResponse]] { + def apply(r : Req) = { + handler(r) + } + def isDefinedAt(r : Req) = handler.isDefinedAt(r) + } + } + super.serve(obpHandler) + } + + dlServe + { + //Handling get request for a token + case Req("my" :: "logins" :: "direct" :: Nil,_ , PostRequest|GetRequest) => { + + //Extract the directLogin parameters from the header and test if the request is valid + var (httpCode, message, directLoginParameters) = validator("authorizationToken", getHttpMethod) + //Test if the request is valid + if(httpCode==200) + { + val claims = Map("" -> "") + val (token,secret) = generateTokenAndSecret(claims) + + //Save the token that we have generated + if(saveAuthorizationToken(directLoginParameters,token, secret)) + message = token + else + message = "invalid" + } + val tokenJSON = JSONFactory.createTokenJSON(message) + successJsonResponse(Extraction.decompose(tokenJSON)) + } + } + + def getHttpMethod = S.request match { + case Full(s) => s.post_? match { + case true => "POST" + case _ => "GET" + } + case _ => "ERROR" + } + + //Check if the request (access token or request token) is valid and return a tuple + def validator(requestType : String, httpMethod : String) : (Int, String, Map[String,String]) = { + //return a Map containing the directLogin parameters : prameter -> value + def getAllParameters: Map[String, String] = { + def toMap(parametersList: String) = { + //transform the string "directLogin_prameter="value"" + //to a tuple (directLogin_parameter,Decoded(value)) + def dynamicListExtract(input: String) = { + val directLoginPossibleParameters = + List( + "consumer_key", + "token", + "username", + "password" + ) + if (input contains "=") { + val split = input.split("=", 2) + val parameterValue = split(1).replace("\"", "") + //add only OAuth parameters and not empty + if (directLoginPossibleParameters.contains(split(0)) && !parameterValue.isEmpty) + Some(split(0), parameterValue) // return key , value + else + None + } + else + None + } + //we delete the "DirectLogin" prefix and all the white spaces that may exist in the string + val cleanedParameterList = parametersList.stripPrefix("DirectLogin").replaceAll("\\s", "") + val params = Map(cleanedParameterList.split(",").flatMap(dynamicListExtract _): _*) + params + } + + S.request match { + case Full(a) => a.header("Authorization") match { + case Full(header) => { + if (header.contains("DirectLogin")) + toMap(header) + else + Map("error" -> "header incorrect") + } + case _ => Map("error" -> "missing header") + } + case _ => Map("error" -> "request incorrect") + } + + } + + def registeredApplication(consumerKey: String): Boolean = { + Consumer.find(By(Consumer.key, consumerKey)) match { + case Full(application) => application.isActive + case _ => false + } + } + + def validAccessToken(tokenKey: String) = { + Token.find(By(Token.key, tokenKey), By(Token.tokenType, TokenType.Access)) match { + case Full(token) => token.isValid + case _ => false + } + } + + //@return the missing parameters depending of the request type + def missingDirectLoginParameters(parameters: Map[String, String], requestType: String): Set[String] = { + requestType match { + case "authorizationToken" => + ("username" :: "password" :: "consumer_key" :: List()).toSet diff parameters.keySet + case "protectedResource" => + ("token" :: List()).toSet diff parameters.keySet + case _ => + parameters.keySet + } + } + + var message = "" + var httpCode: Int = 500 + + val parameters = getAllParameters + + //are all the necessary directLogin parameters present? + val missingParams = missingDirectLoginParameters(parameters, requestType) + if (missingParams.nonEmpty) { + message = ErrorMessages.DirectLoginMissingParameters + missingParams.mkString(", ") + httpCode = 400 + } + else if ( + requestType == "protectedResource" && + !validAccessToken(parameters.get("token").getOrElse("")) + ) { + message = ErrorMessages.DirectLoginInvalidToken + parameters.get("token").getOrElse("") + httpCode = 401 + } + //check if the application is registered and active + else if ( + requestType == "authorizationToken" && + Props.getBool("direct_login_consumer_key_mandatory", true) && + !registeredApplication(parameters.get("consumer_key").getOrElse(""))) { + + logger.error("application: " + parameters.get("consumer_key").getOrElse("") + " not found") + message = ErrorMessages.InvalidLoginCredentials + httpCode = 401 + } + //checking if the signature is correct + //else if(! correctSignature(parameters, httpMethod)) + //{ + // message = "Invalid signature" + // httpCode = 401 + //} + else + httpCode = 200 + if(message.nonEmpty) + logger.error("error message : " + message) + (httpCode, message, parameters) + } + + private def generateTokenAndSecret(claims: Map[String,String]) = + { + // generate random string + val secret_message = Helpers.randomString(40) + // jwt header + val header = JwtHeader("HS256") + // generate jwt token + val token_message = JsonWebToken(header, JwtClaimsSet(claims), secret_message) + (token_message, secret_message) + } + + private def saveAuthorizationToken(directLoginParameters : Map[String, String], tokenKey : String, tokenSecret : String) = + { + import code.model.{Token, TokenType} + val token = Token.create + token.tokenType(TokenType.Access) + Consumer.find(By(Consumer.key, directLoginParameters.get("consumer_key").getOrElse(""))) match { + case Full(consumer) => token.consumerId(consumer.id) + case _ => None + } + val username = directLoginParameters.get("username").getOrElse("").toString + val password = directLoginParameters.get("password").getOrElse("") match { + case p: String => p + case _ => "error" + } + val userId = OBPUser.getUserId(username, password) + token.userForeignKey(userId) + token.key(tokenKey) + token.secret(tokenSecret) + val currentTime = Platform.currentTime + val tokenDuration : Long = Helpers.weeks(4) + token.duration(tokenDuration) + token.expirationDate(new Date(currentTime+tokenDuration)) + token.insertDate(new Date(currentTime)) + val tokenSaved = token.save() + tokenSaved + } + + def getUser : Box[User] = { + val httpMethod = S.request match { + case Full(r) => r.request.method + case _ => "GET" + } + val (httpCode, message, directLoginParameters) = validator("protectedResource", httpMethod) + val user = getUser(200, directLoginParameters.get("token")) + if (user != Empty ) { + val res = Full(user.get) + res + } else { + ParamFailure(message, Empty, Empty, APIFailure(message, httpCode)) + } + } + + def getUser(httpCode : Int, tokenID : Box[String]) : Box[User] = + if(httpCode==200) + { + import code.model.Token + logger.info("DirectLogin header correct ") + Token.find(By(Token.key, tokenID.getOrElse(""))) match { + case Full(token) => { + logger.info("access token: "+ token + " found") + val user = token.user + //just a log + user match { + case Full(u) => logger.info("user " + u.emailAddress + " was found from the DirectLogin token") + case _ => logger.info("no user was found for the DirectLogin token") + } + user + } + case _ =>{ + logger.warn("no token " + tokenID.getOrElse("") + " found") + Empty + } + } + } + else + Empty +} diff --git a/src/main/scala/code/api/oauth1.0.scala b/src/main/scala/code/api/oauth1.0.scala index be49fa2c9..95ee88664 100644 --- a/src/main/scala/code/api/oauth1.0.scala +++ b/src/main/scala/code/api/oauth1.0.scala @@ -250,7 +250,7 @@ object OAuthHandshake extends RestHelper with Loggable { parameters } - //prepare the base string + //prepare the base string (should we really have openOr here?) var baseString = httpMethod+"&"+URLEncoder.encode(Props.get("hostname").openOr(S.hostAndPath) + S.uri,"UTF-8")+"&" baseString+= generateOAuthParametersString(OAuthparameters) diff --git a/src/main/scala/code/api/sandbox/example_data/example_import.json b/src/main/scala/code/api/sandbox/example_data/example_import.json new file mode 100644 index 000000000..e12926ec4 --- /dev/null +++ b/src/main/scala/code/api/sandbox/example_data/example_import.json @@ -0,0 +1,12424 @@ + { + "banks":[{ + "id":"obp-bank-x-gh", + "short_name":"Bank X", + "full_name":"The Bank of X", + "logo":"https://static.openbankproject.com/images/sandbox/bank_x.png", + "website":"https://www.example.com" + },{ + "id":"obp-bank-y-gh", + "short_name":"Bank Y", + "full_name":"The Bank of Y", + "logo":"https://static.openbankproject.com/images/sandbox/bank_y.png", + "website":"https://www.example.com" + }], + "users":[{ + "email":"robert.x.0.gh@example.com", + "password":"3e3a3102", + "display_name":"Robert X.0.GH" + },{ + "email":"susan.x.0.gh@example.com", + "password":"58da7854", + "display_name":"Susan X.0.GH" + },{ + "email":"anil.x.0.gh@example.com", + "password":"90a66977", + "display_name":"Anil X.0.GH" + },{ + "email":"ellie.x.0.gh@example.com", + "password":"a2953da3", + "display_name":"Ellie X.0.GH" + },{ + "email":"robert.y.9.gh@example.com", + "password":"c9a641df", + "display_name":"Robert Y.9.GH" + },{ + "email":"susan.y.9.gh@example.com", + "password":"b7c78269", + "display_name":"Susan Y.9.GH" + },{ + "email":"anil.y.9.gh@example.com", + "password":"fec9556f", + "display_name":"Anil Y.9.GH" + },{ + "email":"ellie.y.9.gh@example.com", + "password":"6f821300", + "display_name":"Ellie Y.9.GH" + }], + "accounts":[{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh", + "label":"Susan X.0.GH Current Plus ..898", + "number":"18449011898", + "type":"CURRENT PLUS", + "balance":{ + "currency":"GBP", + "amount":"6599.63" + }, + "IBAN":"BA12 1234 5123 4518 4490 1189 877", + "owners":["susan.x.0.gh@example.com"], + "generate_public_view":true, + "generate_accountants_view":true, + "generate_auditors_view":true + },{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh", + "label":"Robert X.0.GH Current Plus ..251", + "number":"11875446251", + "type":"CURRENT PLUS", + "balance":{ + "currency":"GBP", + "amount":"6379.63" + }, + "IBAN":"BA12 1234 5123 4511 8754 4625 177", + "owners":["robert.x.0.gh@example.com"], + "generate_public_view":true, + "generate_accountants_view":true, + "generate_auditors_view":true + },{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh", + "label":"Anil X.0.GH Current Plus ..996", + "number":"10433713996", + "type":"CURRENT PLUS", + "balance":{ + "currency":"GBP", + "amount":"7588.25" + }, + "IBAN":"BA12 1234 5123 4510 4337 1399 677", + "owners":["anil.x.0.gh@example.com"], + "generate_public_view":true, + "generate_accountants_view":true, + "generate_auditors_view":true + },{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh", + "label":"Robert X.0.GH Current Plus ..849", + "number":"14444021849", + "type":"CURRENT PLUS", + "balance":{ + "currency":"GBP", + "amount":"6662.05" + }, + "IBAN":"BA12 1234 5123 4514 4440 2184 977", + "owners":["robert.x.0.gh@example.com"], + "generate_public_view":true, + "generate_accountants_view":true, + "generate_auditors_view":true + },{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh", + "label":"Ellie X.0.GH Business Current ..272", + "number":"18953434272", + "type":"BUSINESS CURRENT", + "balance":{ + "currency":"GBP", + "amount":"3748.57" + }, + "IBAN":"BA12 1234 5123 4518 9534 3427 277", + "owners":["ellie.x.0.gh@example.com"], + "generate_public_view":true, + "generate_accountants_view":true, + "generate_auditors_view":true + },{ + "id":"213527de-c423-452a-b2f8-8475e4cc2cfe", + "bank":"obp-bank-x-gh", + "label":"Anil X.0.GH Business Current ..015", + "number":"12195723015", + "type":"BUSINESS CURRENT", + "balance":{ + "currency":"GBP", + "amount":"15860.50" + }, + "IBAN":"BA12 1234 5123 4512 1957 2301 577", + "owners":["anil.x.0.gh@example.com"], + "generate_public_view":true, + "generate_accountants_view":true, + "generate_auditors_view":true + },{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-x-gh", + "label":"Anil X.0.GH Red Mastercard ..869", + "number":"12691485869", + "type":"Red Mastercard", + "balance":{ + "currency":"GBP", + "amount":"7724.41" + }, + "IBAN":"BA12 1234 5123 4512 6914 8586 977", + "owners":["anil.x.0.gh@example.com"], + "generate_public_view":true, + "generate_accountants_view":true, + "generate_auditors_view":true + },{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh", + "label":"Susan Y.9.GH Current Plus ..898", + "number":"18449011898", + "type":"CURRENT PLUS", + "balance":{ + "currency":"GBP", + "amount":"6599.63" + }, + "IBAN":"BA12 1234 5123 4518 4490 1189 877", + "owners":["susan.y.9.gh@example.com"], + "generate_public_view":true, + "generate_accountants_view":true, + "generate_auditors_view":true + },{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh", + "label":"Robert Y.9.GH Current Plus ..251", + "number":"11875446251", + "type":"CURRENT PLUS", + "balance":{ + "currency":"GBP", + "amount":"6379.63" + }, + "IBAN":"BA12 1234 5123 4511 8754 4625 177", + "owners":["robert.y.9.gh@example.com"], + "generate_public_view":true, + "generate_accountants_view":true, + "generate_auditors_view":true + },{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh", + "label":"Anil Y.9.GH Current Plus ..996", + "number":"10433713996", + "type":"CURRENT PLUS", + "balance":{ + "currency":"GBP", + "amount":"7588.25" + }, + "IBAN":"BA12 1234 5123 4510 4337 1399 677", + "owners":["anil.y.9.gh@example.com"], + "generate_public_view":true, + "generate_accountants_view":true, + "generate_auditors_view":true + },{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh", + "label":"Robert Y.9.GH Current Plus ..849", + "number":"14444021849", + "type":"CURRENT PLUS", + "balance":{ + "currency":"GBP", + "amount":"6662.05" + }, + "IBAN":"BA12 1234 5123 4514 4440 2184 977", + "owners":["robert.y.9.gh@example.com"], + "generate_public_view":true, + "generate_accountants_view":true, + "generate_auditors_view":true + },{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh", + "label":"Ellie Y.9.GH Business Current ..272", + "number":"18953434272", + "type":"BUSINESS CURRENT", + "balance":{ + "currency":"GBP", + "amount":"3748.57" + }, + "IBAN":"BA12 1234 5123 4518 9534 3427 277", + "owners":["ellie.y.9.gh@example.com"], + "generate_public_view":true, + "generate_accountants_view":true, + "generate_auditors_view":true + },{ + "id":"213527de-c423-452a-b2f8-8475e4cc2cfe", + "bank":"obp-bank-y-gh", + "label":"Anil Y.9.GH Business Current ..015", + "number":"12195723015", + "type":"BUSINESS CURRENT", + "balance":{ + "currency":"GBP", + "amount":"15860.50" + }, + "IBAN":"BA12 1234 5123 4512 1957 2301 577", + "owners":["anil.y.9.gh@example.com"], + "generate_public_view":true, + "generate_accountants_view":true, + "generate_auditors_view":true + },{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-y-gh", + "label":"Anil Y.9.GH Red Mastercard ..869", + "number":"12691485869", + "type":"Red Mastercard", + "balance":{ + "currency":"GBP", + "amount":"7724.41" + }, + "IBAN":"BA12 1234 5123 4512 6914 8586 977", + "owners":["anil.y.9.gh@example.com"], + "generate_public_view":true, + "generate_accountants_view":true, + "generate_auditors_view":true + }], + "transactions":[{ + "id":"d23e864c-c696-4188-9721-93838b3eddad", + "this_account":{ + "id":"213527de-c423-452a-b2f8-8475e4cc2cfe", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"" + }, + "details":{ + "type":"Transfer", + "description":"Paid Invoice Mr Folk", + "posted":"2015-09-02T00:00:00.000Z", + "completed":"2015-09-02T00:00:00.000Z", + "new_balance":"27916.81", + "value":"-351.25" + } + },{ + "id":"39711f6e-20a0-4044-ae05-4f6e5af8eeb9", + "this_account":{ + "id":"213527de-c423-452a-b2f8-8475e4cc2cfe", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"" + }, + "details":{ + "type":"Transfer", + "description":"Paid Invoice Mr Green", + "posted":"2015-09-02T00:00:00.000Z", + "completed":"2015-09-02T00:00:00.000Z", + "new_balance":"27103.46", + "value":"-813.35" + } + },{ + "id":"c2477fb3-7f95-4a88-9e46-bbf6c5e8265e", + "this_account":{ + "id":"213527de-c423-452a-b2f8-8475e4cc2cfe", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"" + }, + "details":{ + "type":"Transfer", + "description":"Paid Invoice Miss Dean", + "posted":"2015-09-03T00:00:00.000Z", + "completed":"2015-09-03T00:00:00.000Z", + "new_balance":"23305.43", + "value":"-3798.03" + } + },{ + "id":"75d63449-9328-4d57-867b-51d2bc56dc20", + "this_account":{ + "id":"213527de-c423-452a-b2f8-8475e4cc2cfe", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"" + }, + "details":{ + "type":"Direct Debit", + "description":"Vodafone", + "posted":"2015-09-03T00:00:00.000Z", + "completed":"2015-09-03T00:00:00.000Z", + "new_balance":"23252.06", + "value":"-53.37" + } + },{ + "id":"9dc1437a-8049-4b07-bb2c-ba6fe9ce4687", + "this_account":{ + "id":"213527de-c423-452a-b2f8-8475e4cc2cfe", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"" + }, + "details":{ + "type":"Debit Card", + "description":"Shell Filling Station", + "posted":"2015-09-03T00:00:00.000Z", + "completed":"2015-09-03T00:00:00.000Z", + "new_balance":"23193.46", + "value":"-58.60" + } + },{ + "id":"77c2f8c9-3f5d-4859-b0bb-409ec3d6e1b3", + "this_account":{ + "id":"213527de-c423-452a-b2f8-8475e4cc2cfe", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"" + }, + "details":{ + "type":"Transfer", + "description":"Paid Invoice 76542 Mr Green", + "posted":"2015-09-04T00:00:00.000Z", + "completed":"2015-09-04T00:00:00.000Z", + "new_balance":"22801.62", + "value":"-391.84" + } + },{ + "id":"e371d8da-aa48-453c-a3e4-18d177886e6a", + "this_account":{ + "id":"213527de-c423-452a-b2f8-8475e4cc2cfe", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"The Food Place" + }, + "details":{ + "type":"Direct Debit", + "description":"The Food Place", + "posted":"2015-09-07T00:00:00.000Z", + "completed":"2015-09-07T00:00:00.000Z", + "new_balance":"22749.85", + "value":"-51.77" + } + },{ + "id":"38ef2c03-465e-47d2-b31d-bd818999f0cd", + "this_account":{ + "id":"213527de-c423-452a-b2f8-8475e4cc2cfe", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"" + }, + "details":{ + "type":"Direct Debit", + "description":"Business insuance", + "posted":"2015-09-09T00:00:00.000Z", + "completed":"2015-09-09T00:00:00.000Z", + "new_balance":"21936.50", + "value":"-813.35" + } + },{ + "id":"ee0ff6b6-3966-40e1-8455-97b68ee786cf", + "this_account":{ + "id":"213527de-c423-452a-b2f8-8475e4cc2cfe", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"" + }, + "details":{ + "type":"Transfer", + "description":"Paid Invoice 986 Mr Khan", + "posted":"2015-09-16T00:00:00.000Z", + "completed":"2015-09-16T00:00:00.000Z", + "new_balance":"21092.71", + "value":"-843.79" + } + },{ + "id":"3b6479d0-e228-472e-a2cf-67629183b387", + "this_account":{ + "id":"213527de-c423-452a-b2f8-8475e4cc2cfe", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"" + }, + "details":{ + "type":"Transfer", + "description":"Paid Invoice 2297 Miss Dean", + "posted":"2015-09-25T00:00:00.000Z", + "completed":"2015-09-25T00:00:00.000Z", + "new_balance":"20248.92", + "value":"-843.79" + } + },{ + "id":"6aa7c77b-d715-4b30-8c21-791bf02efc13", + "this_account":{ + "id":"213527de-c423-452a-b2f8-8475e4cc2cfe", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"" + }, + "details":{ + "type":"Direct Debit", + "description":"Transfer Salary", + "posted":"2015-09-30T00:00:00.000Z", + "completed":"2015-09-30T00:00:00.000Z", + "new_balance":"16995.86", + "value":"-3253.06" + } + },{ + "id":"18dccfa8-d197-40bb-869a-224fcc41e908", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"ING Mortgage" + }, + "details":{ + "type":"Direct Debit", + "description":"Mortgage", + "posted":"2015-07-01T00:00:00.000Z", + "completed":"2015-07-01T00:00:00.000Z", + "new_balance":"7667.42", + "value":"-703.99" + } + },{ + "id":"f27d50fc-2381-4526-add3-022eed204b48", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Directline" + }, + "details":{ + "type":"Direct Debit", + "description":"Insurance", + "posted":"2015-07-01T00:00:00.000Z", + "completed":"2015-07-01T00:00:00.000Z", + "new_balance":"7643.11", + "value":"-24.31" + } + },{ + "id":"93500326-f8d4-472d-9033-9706f2bbf5ec", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Seven Trent Water" + }, + "details":{ + "type":"Direct Debit", + "description":"Water", + "posted":"2015-07-01T00:00:00.000Z", + "completed":"2015-07-01T00:00:00.000Z", + "new_balance":"7617.03", + "value":"-26.08" + } + },{ + "id":"c5f8dab3-0331-4d5d-9a8c-d11cd2b1659b", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"SSE" + }, + "details":{ + "type":"Direct Debit", + "description":"Gas/Elec", + "posted":"2015-07-01T00:00:00.000Z", + "completed":"2015-07-01T00:00:00.000Z", + "new_balance":"7573.51", + "value":"-43.52" + } + },{ + "id":"04ae0400-bea4-43be-913e-bbc1fdf03270", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Council Tax" + }, + "details":{ + "type":"Direct Debit", + "description":"Council Tax", + "posted":"2015-07-01T00:00:00.000Z", + "completed":"2015-07-01T00:00:00.000Z", + "new_balance":"7472.51", + "value":"-101.00" + } + },{ + "id":"8db0e273-3fab-4991-b11e-0bde1873ac2d", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Sky" + }, + "details":{ + "type":"Direct Debit", + "description":"Monthly Sky", + "posted":"2015-07-02T00:00:00.000Z", + "completed":"2015-07-02T00:00:00.000Z", + "new_balance":"7396.38", + "value":"-76.13" + } + },{ + "id":"0536d14c-f0e0-4a52-a739-fb50ee6615ba", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Bambo Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-07-03T00:00:00.000Z", + "completed":"2015-07-03T00:00:00.000Z", + "new_balance":"7329.90", + "value":"-66.48" + } + },{ + "id":"850617c4-b361-43e8-b2ad-a894abb0e225", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Takeway King" + }, + "details":{ + "type":"Debit Card", + "description":"Takeway", + "posted":"2015-07-04T00:00:00.000Z", + "completed":"2015-07-04T00:00:00.000Z", + "new_balance":"7295.18", + "value":"-34.72" + } + },{ + "id":"7ecb99f9-c5a0-4cde-9500-70064568ffd4", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Tesco" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-05T00:00:00.000Z", + "completed":"2015-07-05T00:00:00.000Z", + "new_balance":"7250.11", + "value":"-45.07" + } + },{ + "id":"8663199d-270a-43a5-8b81-509102005048", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Tesco Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling Station", + "posted":"2015-07-05T00:00:00.000Z", + "completed":"2015-07-05T00:00:00.000Z", + "new_balance":"7197.09", + "value":"-53.02" + } + },{ + "id":"91b6d79e-3a2b-4b06-bf83-dfc7e2ec242e", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"coffee", + "posted":"2015-07-06T00:00:00.000Z", + "completed":"2015-07-06T00:00:00.000Z", + "new_balance":"7192.56", + "value":"-4.53" + } + },{ + "id":"7639ffd6-2302-42b9-b3c8-9926709c7c6b", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Liquid" + }, + "details":{ + "type":"Debit Card", + "description":"Club", + "posted":"2015-07-11T00:00:00.000Z", + "completed":"2015-07-11T00:00:00.000Z", + "new_balance":"7148.35", + "value":"-44.21" + } + },{ + "id":"1c275d5e-cf16-445a-b172-448d53749427", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"The choach and horses" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-07-12T00:00:00.000Z", + "completed":"2015-07-12T00:00:00.000Z", + "new_balance":"7125.08", + "value":"-23.27" + } + },{ + "id":"356a196a-c904-458e-b245-a64a058b0df5", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Amazon" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-14T00:00:00.000Z", + "completed":"2015-07-14T00:00:00.000Z", + "new_balance":"7109.06", + "value":"-16.02" + } + },{ + "id":"83b7c3a1-69d6-40a7-907f-71ffeb9b5503", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"coffee", + "posted":"2015-07-16T00:00:00.000Z", + "completed":"2015-07-16T00:00:00.000Z", + "new_balance":"7099.80", + "value":"-9.26" + } + },{ + "id":"7c0121e9-e3c4-47db-9f65-6af38bd650bd", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Bambo Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-07-18T00:00:00.000Z", + "completed":"2015-07-18T00:00:00.000Z", + "new_balance":"7066.94", + "value":"-32.86" + } + },{ + "id":"26cd124d-e34a-47ab-9c6d-8c00d2a127b7", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"The sandwich Factory" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant/Takeway", + "posted":"2015-07-20T00:00:00.000Z", + "completed":"2015-07-20T00:00:00.000Z", + "new_balance":"7060.87", + "value":"-6.07" + } + },{ + "id":"f6244692-9e83-4ddf-afca-b2c8a07cb4ef", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"High Street ATM" + }, + "details":{ + "type":"Cash Withdrawal", + "description":"Cash Withdrawal", + "posted":"2015-07-20T00:00:00.000Z", + "completed":"2015-07-20T00:00:00.000Z", + "new_balance":"6779.82", + "value":"-281.05" + } + },{ + "id":"762e770e-8078-4631-aa8d-0c9216f74248", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Champagne Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-07-24T00:00:00.000Z", + "completed":"2015-07-24T00:00:00.000Z", + "new_balance":"6744.12", + "value":"-35.70" + } + },{ + "id":"f11b4746-0326-4a87-b885-837f712dd1f8", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Quick Bit" + }, + "details":{ + "type":"Debit Card", + "description":"Takeway", + "posted":"2015-07-24T00:00:00.000Z", + "completed":"2015-07-24T00:00:00.000Z", + "new_balance":"6729.00", + "value":"-15.12" + } + },{ + "id":"788fa20d-714a-4173-826e-04eb47db8745", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Nando's" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-07-25T00:00:00.000Z", + "completed":"2015-07-25T00:00:00.000Z", + "new_balance":"6705.59", + "value":"-23.41" + } + },{ + "id":"dc1278c2-464a-409e-9a35-ca41433e2e36", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Tesco" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-25T00:00:00.000Z", + "completed":"2015-07-25T00:00:00.000Z", + "new_balance":"6650.01", + "value":"-55.58" + } + },{ + "id":"b29bd7c7-bdb4-41d7-95b2-7b877cd9e012", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Tesco Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling Station", + "posted":"2015-07-26T00:00:00.000Z", + "completed":"2015-07-26T00:00:00.000Z", + "new_balance":"6590.90", + "value":"-59.11" + } + },{ + "id":"ddbde74b-d9d5-4b2e-b74d-4680e009358e", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"coffee", + "posted":"2015-07-27T00:00:00.000Z", + "completed":"2015-07-27T00:00:00.000Z", + "new_balance":"6584.83", + "value":"-6.07" + } + },{ + "id":"72afe548-b7a7-48b6-a04f-a2cecac5f5d4", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Salary" + }, + "details":{ + "type":"Paid In", + "description":"Salary", + "posted":"2015-08-01T00:00:00.000Z", + "completed":"2015-08-01T00:00:00.000Z", + "new_balance":"8608.77", + "value":"2023.94" + } + },{ + "id":"e370fb72-0b74-4df7-ab09-49522ccfbff6", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"ING Mortgage" + }, + "details":{ + "type":"Direct Debit", + "description":"Mortgage", + "posted":"2015-08-01T00:00:00.000Z", + "completed":"2015-08-01T00:00:00.000Z", + "new_balance":"7904.78", + "value":"-703.99" + } + },{ + "id":"da22b2e6-1471-4e6e-83f8-bbaa6baaa43c", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Directline" + }, + "details":{ + "type":"Direct Debit", + "description":"Insurance", + "posted":"2015-08-01T00:00:00.000Z", + "completed":"2015-08-01T00:00:00.000Z", + "new_balance":"7880.47", + "value":"-24.31" + } + },{ + "id":"d6166f95-15d3-42a2-82c8-d2d8eabfd14c", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Seven Trent Water" + }, + "details":{ + "type":"Direct Debit", + "description":"Water", + "posted":"2015-08-01T00:00:00.000Z", + "completed":"2015-08-01T00:00:00.000Z", + "new_balance":"7854.39", + "value":"-26.08" + } + },{ + "id":"d4e69013-b682-43fe-a8b7-321ed9ab593c", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"SSE" + }, + "details":{ + "type":"Direct Debit", + "description":"Gas/Elec", + "posted":"2015-08-01T00:00:00.000Z", + "completed":"2015-08-01T00:00:00.000Z", + "new_balance":"7810.87", + "value":"-43.52" + } + },{ + "id":"5e8afd22-c3a1-4016-a7e8-6eb866c37f94", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Council Tax" + }, + "details":{ + "type":"Direct Debit", + "description":"Council Tax", + "posted":"2015-08-01T00:00:00.000Z", + "completed":"2015-08-01T00:00:00.000Z", + "new_balance":"7709.87", + "value":"-101.00" + } + },{ + "id":"4f3fe009-7c94-4316-822d-6d0af868c9c9", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Sky" + }, + "details":{ + "type":"Direct Debit", + "description":"Monthly Sky", + "posted":"2015-08-02T00:00:00.000Z", + "completed":"2015-08-02T00:00:00.000Z", + "new_balance":"7633.74", + "value":"-76.13" + } + },{ + "id":"b03b1976-7c3b-4483-b3b8-b29ee7e5b4e9", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"coffee", + "posted":"2015-08-03T00:00:00.000Z", + "completed":"2015-08-03T00:00:00.000Z", + "new_balance":"7620.87", + "value":"-12.87" + } + },{ + "id":"535f7e8f-1840-415f-bf05-b8816ccf923a", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Quick Bit" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant/Takeway", + "posted":"2015-08-04T00:00:00.000Z", + "completed":"2015-08-04T00:00:00.000Z", + "new_balance":"7601.32", + "value":"-19.55" + } + },{ + "id":"42487581-cbfb-4f7a-8c66-55bc58627582", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Nando's" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-08-07T00:00:00.000Z", + "completed":"2015-08-07T00:00:00.000Z", + "new_balance":"7573.41", + "value":"-27.91" + } + },{ + "id":"48301430-ec64-4256-a4a3-ecd9c3aa15c0", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"The choach and horses" + }, + "details":{ + "type":"Debit Card", + "description":"resturant", + "posted":"2015-08-09T00:00:00.000Z", + "completed":"2015-08-09T00:00:00.000Z", + "new_balance":"7545.03", + "value":"-28.38" + } + },{ + "id":"ff103f99-e15f-4950-95d4-30975379a04f", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Showcase Cinema" + }, + "details":{ + "type":"Debit Card", + "description":"Cinema", + "posted":"2015-08-09T00:00:00.000Z", + "completed":"2015-08-09T00:00:00.000Z", + "new_balance":"7521.88", + "value":"-23.15" + } + },{ + "id":"2a205016-08c9-4869-90de-a7819d3af0e1", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Shell Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling Station", + "posted":"2015-08-10T00:00:00.000Z", + "completed":"2015-08-10T00:00:00.000Z", + "new_balance":"7468.86", + "value":"-53.02" + } + },{ + "id":"af3fb9c6-cc18-45e0-a1ae-539a86dab395", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"coffee", + "posted":"2015-08-14T00:00:00.000Z", + "completed":"2015-08-14T00:00:00.000Z", + "new_balance":"7463.90", + "value":"-4.96" + } + },{ + "id":"ce60e702-d898-4345-9139-4f45065e3f32", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Pizza Express" + }, + "details":{ + "type":"Debit Card", + "description":"restaurant", + "posted":"2015-08-15T00:00:00.000Z", + "completed":"2015-08-15T00:00:00.000Z", + "new_balance":"7442.90", + "value":"-21.00" + } + },{ + "id":"236037ff-04b9-4fab-bfe0-acfa5d563040", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Bambo Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-08-15T00:00:00.000Z", + "completed":"2015-08-15T00:00:00.000Z", + "new_balance":"7402.74", + "value":"-40.16" + } + },{ + "id":"07f8c4aa-c34b-4dac-9b6b-53e244a9e1d7", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Dominos" + }, + "details":{ + "type":"Debit Card", + "description":"Takeway", + "posted":"2015-08-16T00:00:00.000Z", + "completed":"2015-08-16T00:00:00.000Z", + "new_balance":"7363.12", + "value":"-39.62" + } + },{ + "id":"aaf2b649-bfc9-4a5a-832d-211717255a72", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Sainsbury's" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-17T00:00:00.000Z", + "completed":"2015-08-17T00:00:00.000Z", + "new_balance":"7328.91", + "value":"-34.21" + } + },{ + "id":"eff26ed5-17a8-465f-87b8-7c2ae8b963d6", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"coffee", + "posted":"2015-08-20T00:00:00.000Z", + "completed":"2015-08-20T00:00:00.000Z", + "new_balance":"7321.93", + "value":"-6.98" + } + },{ + "id":"bd0223e8-03aa-4773-8917-bd45a932bfa4", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"The sandwich Company" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant/Takeway", + "posted":"2015-08-20T00:00:00.000Z", + "completed":"2015-08-20T00:00:00.000Z", + "new_balance":"7315.70", + "value":"-6.23" + } + },{ + "id":"756ab4d4-d36d-443c-bb75-e0d01b4c252a", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Liquid" + }, + "details":{ + "type":"Debit Card", + "description":"Club", + "posted":"2015-08-22T00:00:00.000Z", + "completed":"2015-08-22T00:00:00.000Z", + "new_balance":"7358.90", + "value":"43.20" + } + },{ + "id":"ffd3d8b2-110b-4e07-81be-0d4f2843e14f", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Tesco" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-23T00:00:00.000Z", + "completed":"2015-08-23T00:00:00.000Z", + "new_balance":"7288.65", + "value":"-70.25" + } + },{ + "id":"46c2363d-8cbb-4315-92c8-7b1640705856", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"The choach and horses" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-08-23T00:00:00.000Z", + "completed":"2015-08-23T00:00:00.000Z", + "new_balance":"7252.27", + "value":"-36.38" + } + },{ + "id":"f85e10d0-4450-407c-ab4e-d9f16c07b883", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Bambo Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-08-28T00:00:00.000Z", + "completed":"2015-08-28T00:00:00.000Z", + "new_balance":"7229.84", + "value":"-22.43" + } + },{ + "id":"dcb14507-c0ae-4f18-a70a-28f84fa3d6ad", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Hollywood Bowl" + }, + "details":{ + "type":"Debit Card", + "description":"Bowling", + "posted":"2015-08-28T00:00:00.000Z", + "completed":"2015-08-28T00:00:00.000Z", + "new_balance":"7203.48", + "value":"-26.36" + } + },{ + "id":"533b3faa-3bf1-475d-9966-24544f84723d", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Salary" + }, + "details":{ + "type":"Paid In", + "description":"Salary", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"9227.42", + "value":"2023.94" + } + },{ + "id":"8b4298f2-302e-4a79-a98b-42f2b439a58d", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"ING Mortgage" + }, + "details":{ + "type":"Direct Debit", + "description":"Mortgage", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"8523.43", + "value":"-703.99" + } + },{ + "id":"e2d06446-976e-4f68-8dda-43968f669642", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Directline" + }, + "details":{ + "type":"Direct Debit", + "description":"Insurance", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"8499.12", + "value":"-24.31" + } + },{ + "id":"c180c364-079d-4d97-9d0c-8118f1f0de1a", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Seven Trent Water" + }, + "details":{ + "type":"Direct Debit", + "description":"Water", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"8473.04", + "value":"-26.08" + } + },{ + "id":"a8e9b945-1e98-4ec1-932b-7730f4e030c2", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"SSE" + }, + "details":{ + "type":"Direct Debit", + "description":"Gas/Elec", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"8429.52", + "value":"-43.52" + } + },{ + "id":"ad964ce6-0a8e-4a0e-87ce-203e3a40cb3c", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Council Tax" + }, + "details":{ + "type":"Direct Debit", + "description":"Council Tax", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"8328.52", + "value":"-101.00" + } + },{ + "id":"936495ce-e931-4c1d-90af-0c7f38ad9dff", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Sky" + }, + "details":{ + "type":"Direct Debit", + "description":"Monthly Sky", + "posted":"2015-09-02T00:00:00.000Z", + "completed":"2015-09-02T00:00:00.000Z", + "new_balance":"8252.39", + "value":"-76.13" + } + },{ + "id":"0bb1b43d-1286-408a-9bda-dfe55ebd1c8d", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Shell Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling Station", + "posted":"2015-09-04T00:00:00.000Z", + "completed":"2015-09-04T00:00:00.000Z", + "new_balance":"8183.86", + "value":"-68.53" + } + },{ + "id":"86cdb706-f2f4-41de-9e70-49500e2e1810", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"The Sandwich Factory" + }, + "details":{ + "type":"Debit Card", + "description":"Resaurant/Takeway", + "posted":"2015-09-04T00:00:00.000Z", + "completed":"2015-09-04T00:00:00.000Z", + "new_balance":"8176.75", + "value":"-7.11" + } + },{ + "id":"e5fa5335-0399-41a0-8e24-ad4d994b8268", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Bambo Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-09-04T00:00:00.000Z", + "completed":"2015-09-04T00:00:00.000Z", + "new_balance":"8135.59", + "value":"-41.16" + } + },{ + "id":"fb267d89-5d72-45ae-962a-53c07643b23a", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Quick Bit" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurtant/Takeway", + "posted":"2015-09-04T00:00:00.000Z", + "completed":"2015-09-04T00:00:00.000Z", + "new_balance":"8122.02", + "value":"-13.57" + } + },{ + "id":"f13670ed-a594-4286-a0fc-a4500812a5ae", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"coffee", + "posted":"2015-09-05T00:00:00.000Z", + "completed":"2015-09-05T00:00:00.000Z", + "new_balance":"8116.42", + "value":"-5.60" + } + },{ + "id":"70366a84-b4b3-4b05-b881-e71559db5008", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Nando's" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-09-05T00:00:00.000Z", + "completed":"2015-09-05T00:00:00.000Z", + "new_balance":"8086.74", + "value":"-29.68" + } + },{ + "id":"24e42efb-e277-42f7-b596-56945b77f873", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Tesco" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-06T00:00:00.000Z", + "completed":"2015-09-06T00:00:00.000Z", + "new_balance":"8019.79", + "value":"-66.95" + } + },{ + "id":"c4cb6daa-cd5a-4807-8252-c94248ea9da7", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Harvey Nichols" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-06T00:00:00.000Z", + "completed":"2015-09-06T00:00:00.000Z", + "new_balance":"7951.26", + "value":"-68.53" + } + },{ + "id":"9020b361-aaf0-4c4f-83b8-02dc5d0f6660", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Tesco" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-08T00:00:00.000Z", + "completed":"2015-09-08T00:00:00.000Z", + "new_balance":"7884.31", + "value":"-66.95" + } + },{ + "id":"8fb3cff0-27ea-4fde-b0fe-45f0d349435b", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Amazon" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-08T00:00:00.000Z", + "completed":"2015-09-08T00:00:00.000Z", + "new_balance":"7849.37", + "value":"-34.94" + } + },{ + "id":"10bdd8f3-da7f-433e-abc5-ff4f66d9bd18", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"coffee", + "posted":"2015-09-10T00:00:00.000Z", + "completed":"2015-09-10T00:00:00.000Z", + "new_balance":"7844.47", + "value":"-4.90" + } + },{ + "id":"30c42a0e-cfe4-4d3a-a309-71011290919b", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Topman" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-10T00:00:00.000Z", + "completed":"2015-09-10T00:00:00.000Z", + "new_balance":"7765.87", + "value":"-78.60" + } + },{ + "id":"427d852c-8637-4139-9d5f-0edbf7b5e026", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Mcdonalds" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-09-11T00:00:00.000Z", + "completed":"2015-09-11T00:00:00.000Z", + "new_balance":"7758.90", + "value":"-6.97" + } + },{ + "id":"d7825a81-e540-4c0a-bd3d-289a0e002817", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Nando's" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-09-12T00:00:00.000Z", + "completed":"2015-09-12T00:00:00.000Z", + "new_balance":"7744.81", + "value":"-14.09" + } + },{ + "id":"e7528faa-29c5-403d-bb3d-ab423417f40a", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Hollywood Bowling" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-09-12T00:00:00.000Z", + "completed":"2015-09-12T00:00:00.000Z", + "new_balance":"7714.14", + "value":"-30.67" + } + },{ + "id":"3a060ae1-8b2b-4db8-aa0e-c8c1c70f9a59", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Tesco" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-13T00:00:00.000Z", + "completed":"2015-09-13T00:00:00.000Z", + "new_balance":"7673.56", + "value":"-40.58" + } + },{ + "id":"410309a6-247d-465a-bcf2-0b7b775501ea", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"coffee", + "posted":"2015-09-16T00:00:00.000Z", + "completed":"2015-09-16T00:00:00.000Z", + "new_balance":"7668.30", + "value":"-5.26" + } + },{ + "id":"10acf185-7dbf-4d99-9750-c56577b2219d", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Champagne Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-09-17T00:00:00.000Z", + "completed":"2015-09-17T00:00:00.000Z", + "new_balance":"7569.02", + "value":"-99.28" + } + },{ + "id":"0151682f-d75f-422b-89c3-f13ad4d013ce", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Pizza King" + }, + "details":{ + "type":"Debit Card", + "description":"Takeway", + "posted":"2015-09-17T00:00:00.000Z", + "completed":"2015-09-17T00:00:00.000Z", + "new_balance":"7516.12", + "value":"-52.90" + } + },{ + "id":"7d007b17-2d2d-4d0c-bf06-e5d958ece7ce", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"coffee", + "posted":"2015-09-18T00:00:00.000Z", + "completed":"2015-09-18T00:00:00.000Z", + "new_balance":"7510.86", + "value":"-5.26" + } + },{ + "id":"5e19c531-eae9-4f64-bbb9-14326ee7cd5a", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Chimichanga" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-09-19T00:00:00.000Z", + "completed":"2015-09-19T00:00:00.000Z", + "new_balance":"7457.90", + "value":"-52.96" + } + },{ + "id":"966dff13-2205-459b-a5c2-78d66799bb25", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Liquid" + }, + "details":{ + "type":"Debit Card", + "description":"Club", + "posted":"2015-09-19T00:00:00.000Z", + "completed":"2015-09-19T00:00:00.000Z", + "new_balance":"7410.43", + "value":"-47.47" + } + },{ + "id":"8a09dfb4-d1c6-4904-842f-62342d65a063", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Liquid" + }, + "details":{ + "type":"Debit Card", + "description":"Club", + "posted":"2015-09-19T00:00:00.000Z", + "completed":"2015-09-19T00:00:00.000Z", + "new_balance":"7372.80", + "value":"-37.63" + } + },{ + "id":"95bb6ccf-001e-4170-8536-3a092c07ada1", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"The coach and horses" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-09-20T00:00:00.000Z", + "completed":"2015-09-20T00:00:00.000Z", + "new_balance":"7315.80", + "value":"-57.00" + } + },{ + "id":"fa612f4b-de3d-4953-8f96-1a297166f197", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Dominos" + }, + "details":{ + "type":"Debit Card", + "description":"Takeway", + "posted":"2015-09-20T00:00:00.000Z", + "completed":"2015-09-20T00:00:00.000Z", + "new_balance":"7262.44", + "value":"-53.36" + } + },{ + "id":"a325035c-4fc3-4298-8f42-df4c06163485", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"coffee", + "posted":"2015-09-21T00:00:00.000Z", + "completed":"2015-09-21T00:00:00.000Z", + "new_balance":"7255.33", + "value":"-7.11" + } + },{ + "id":"b1bd6813-6d05-4ec1-8b5f-468602953125", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"The Sandwich Factory" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant/Takeway", + "posted":"2015-09-21T00:00:00.000Z", + "completed":"2015-09-21T00:00:00.000Z", + "new_balance":"7250.01", + "value":"-5.32" + } + },{ + "id":"960397b6-20c2-4a70-a88a-4adb1414691d", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"R J Dentist" + }, + "details":{ + "type":"Debit Card", + "description":"Dentist", + "posted":"2015-09-23T00:00:00.000Z", + "completed":"2015-09-23T00:00:00.000Z", + "new_balance":"7204.68", + "value":"-45.33" + } + },{ + "id":"82f2458b-3ff0-43b3-8543-37213db3389d", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Patels Parmacy" + }, + "details":{ + "type":"Debit Card", + "description":"Parmacy", + "posted":"2015-09-23T00:00:00.000Z", + "completed":"2015-09-23T00:00:00.000Z", + "new_balance":"7196.26", + "value":"-8.42" + } + },{ + "id":"f2b9a6a3-0a4f-433c-9122-16eb6e7109e0", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Tesco" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-24T00:00:00.000Z", + "completed":"2015-09-24T00:00:00.000Z", + "new_balance":"7166.84", + "value":"-29.42" + } + },{ + "id":"d78a3a7a-65e1-4313-b36a-2861eb0a08d7", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Bambo Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-09-25T00:00:00.000Z", + "completed":"2015-09-25T00:00:00.000Z", + "new_balance":"7136.53", + "value":"-30.31" + } + },{ + "id":"2c49ea60-194f-412d-b05d-975deea0a5c5", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Takeway King" + }, + "details":{ + "type":"Debit Card", + "description":"Takeway", + "posted":"2015-09-25T00:00:00.000Z", + "completed":"2015-09-25T00:00:00.000Z", + "new_balance":"7121.29", + "value":"-15.24" + } + },{ + "id":"899b6971-41b8-4344-b463-a8ec57834ff3", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Mr D Moda" + }, + "details":{ + "type":"Tranfer", + "description":"Transfer", + "posted":"2015-09-27T00:00:00.000Z", + "completed":"2015-09-27T00:00:00.000Z", + "new_balance":"6840.24", + "value":"-281.05" + } + },{ + "id":"eb667ae9-8067-49d4-939b-1579bb0865c4", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Bambo Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-09-29T00:00:00.000Z", + "completed":"2015-09-29T00:00:00.000Z", + "new_balance":"6811.92", + "value":"-28.32" + } + },{ + "id":"dc830888-450e-4f11-bcff-8817af235702", + "this_account":{ + "id":"213527de-c423-452a-b2f8-8475e4cc2cfe", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"" + }, + "details":{ + "type":"Transfer", + "description":"Paid Invoice Mr Folk", + "posted":"2015-09-02T00:00:00.000Z", + "completed":"2015-09-02T00:00:00.000Z", + "new_balance":"27944.92", + "value":"-323.14" + } + },{ + "id":"8c948fef-5b3b-43a2-889a-59d2ea73e8d7", + "this_account":{ + "id":"213527de-c423-452a-b2f8-8475e4cc2cfe", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"" + }, + "details":{ + "type":"Transfer", + "description":"Paid Invoice Mr Green", + "posted":"2015-09-02T00:00:00.000Z", + "completed":"2015-09-02T00:00:00.000Z", + "new_balance":"26826.75", + "value":"-1118.17" + } + },{ + "id":"7d3c3354-d359-4bbe-ae22-2cf3ca762d31", + "this_account":{ + "id":"213527de-c423-452a-b2f8-8475e4cc2cfe", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"" + }, + "details":{ + "type":"Transfer", + "description":"Paid Invoice Miss Dean", + "posted":"2015-09-03T00:00:00.000Z", + "completed":"2015-09-03T00:00:00.000Z", + "new_balance":"23063.72", + "value":"-3763.03" + } + },{ + "id":"24bfdcc1-d1de-4a86-813c-edac3aeee37e", + "this_account":{ + "id":"213527de-c423-452a-b2f8-8475e4cc2cfe", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"" + }, + "details":{ + "type":"Direct Debit", + "description":"Vodafone", + "posted":"2015-09-03T00:00:00.000Z", + "completed":"2015-09-03T00:00:00.000Z", + "new_balance":"23010.70", + "value":"-53.02" + } + },{ + "id":"22dd5046-6191-4380-a3e3-a925af23427d", + "this_account":{ + "id":"213527de-c423-452a-b2f8-8475e4cc2cfe", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"" + }, + "details":{ + "type":"Debit Card", + "description":"Shell Filling Station", + "posted":"2015-09-03T00:00:00.000Z", + "completed":"2015-09-03T00:00:00.000Z", + "new_balance":"22934.57", + "value":"-76.13" + } + },{ + "id":"5e7d2840-0b2c-4e39-973d-2d0337e82b37", + "this_account":{ + "id":"213527de-c423-452a-b2f8-8475e4cc2cfe", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"" + }, + "details":{ + "type":"Transfer", + "description":"Paid Invoice 76542 Mr Green", + "posted":"2015-09-04T00:00:00.000Z", + "completed":"2015-09-04T00:00:00.000Z", + "new_balance":"22555.45", + "value":"-379.12" + } + },{ + "id":"99dbc10e-9212-4d8a-ac74-255f16020bb0", + "this_account":{ + "id":"213527de-c423-452a-b2f8-8475e4cc2cfe", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"The Food Place" + }, + "details":{ + "type":"Direct Debit", + "description":"The Food Place", + "posted":"2015-09-07T00:00:00.000Z", + "completed":"2015-09-07T00:00:00.000Z", + "new_balance":"22511.93", + "value":"-43.52" + } + },{ + "id":"45446654-6328-4437-b63b-ff10a37bfc8e", + "this_account":{ + "id":"213527de-c423-452a-b2f8-8475e4cc2cfe", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"" + }, + "details":{ + "type":"Direct Debit", + "description":"Business insuance", + "posted":"2015-09-09T00:00:00.000Z", + "completed":"2015-09-09T00:00:00.000Z", + "new_balance":"21393.76", + "value":"-1118.17" + } + },{ + "id":"9418b854-7cea-48d4-9e6a-1d8b98a5cab7", + "this_account":{ + "id":"213527de-c423-452a-b2f8-8475e4cc2cfe", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"" + }, + "details":{ + "type":"Transfer", + "description":"Paid Invoice 986 Mr Khan", + "posted":"2015-09-16T00:00:00.000Z", + "completed":"2015-09-16T00:00:00.000Z", + "new_balance":"20726.30", + "value":"-667.46" + } + },{ + "id":"d5f75348-f687-44bd-aaa1-502e75ca356f", + "this_account":{ + "id":"213527de-c423-452a-b2f8-8475e4cc2cfe", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"" + }, + "details":{ + "type":"Transfer", + "description":"Paid Invoice 2297 Miss Dean", + "posted":"2015-09-25T00:00:00.000Z", + "completed":"2015-09-25T00:00:00.000Z", + "new_balance":"20058.84", + "value":"-667.46" + } + },{ + "id":"2bd352ad-7c20-4526-b054-7982a6e85705", + "this_account":{ + "id":"213527de-c423-452a-b2f8-8475e4cc2cfe", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"" + }, + "details":{ + "type":"Direct Debit", + "description":"Transfer Salary", + "posted":"2015-09-30T00:00:00.000Z", + "completed":"2015-09-30T00:00:00.000Z", + "new_balance":"15860.50", + "value":"-4198.34" + } + },{ + "id":"fea9810e-d916-404c-93be-f92ed3b1f108", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Rent" + }, + "details":{ + "type":"Direct Debit", + "description":"", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"4814.30", + "value":"-487.59" + } + },{ + "id":"5b09cc34-fa03-48a2-b5cc-48697e51fa8f", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Rates" + }, + "details":{ + "type":"Direct Debit", + "description":"", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"4577.14", + "value":"-237.16" + } + },{ + "id":"7723f637-1adc-4cca-b75b-b9afcfe0c102", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Southern Electric" + }, + "details":{ + "type":"Direct Debit", + "description":"", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"4315.90", + "value":"-261.24" + } + },{ + "id":"50e488b0-fb4b-4869-bdb7-30cd53f2a154", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"BT" + }, + "details":{ + "type":"Direct Debit", + "description":"", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"4186.66", + "value":"-129.24" + } + },{ + "id":"aba96e57-1361-4cbf-987f-b09fba793a32", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Salary Mr R Turner" + }, + "details":{ + "type":"Transfer", + "description":"", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"-11.68", + "value":"-4198.34" + } + },{ + "id":"2b94f45f-4968-42c0-a9fb-5548c4cf1549", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Salary Mrs F White" + }, + "details":{ + "type":"Transfer", + "description":"", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"-3521.15", + "value":"-3509.47" + } + },{ + "id":"c39da90e-8da4-43ee-9313-6ecf0d7c7601", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"The George Hotel" + }, + "details":{ + "type":"Debit Card", + "description":"", + "posted":"2015-09-12T00:00:00.000Z", + "completed":"2015-09-12T00:00:00.000Z", + "new_balance":"-4008.74", + "value":"-487.59" + } + },{ + "id":"97b1fac7-5730-4ffc-829d-46bf39dac53d", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"The Catering Company" + }, + "details":{ + "type":"Debit Card", + "description":"", + "posted":"2015-09-12T00:00:00.000Z", + "completed":"2015-09-12T00:00:00.000Z", + "new_balance":"-4269.98", + "value":"-261.24" + } + },{ + "id":"d5af4382-73f4-448f-81f6-00a2eee5d90f", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Shell Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"", + "posted":"2015-09-12T00:00:00.000Z", + "completed":"2015-09-12T00:00:00.000Z", + "new_balance":"-4313.50", + "value":"-43.52" + } + },{ + "id":"a108aec3-9eeb-49c5-bef2-03cf7770d33d", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Tesco" + }, + "details":{ + "type":"Debit Card", + "description":"", + "posted":"2015-09-12T00:00:00.000Z", + "completed":"2015-09-12T00:00:00.000Z", + "new_balance":"-4368.38", + "value":"-54.88" + } + },{ + "id":"4e775735-1e62-495e-8076-b2b1613cc0a7", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Donation Mr and Mrs Cooper" + }, + "details":{ + "type":"Paid in", + "description":"", + "posted":"2015-09-15T00:00:00.000Z", + "completed":"2015-09-15T00:00:00.000Z", + "new_balance":"-2033.41", + "value":"2334.97" + } + },{ + "id":"f54a1557-353d-404d-b6e5-c882d8858195", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Donation The Corner Store" + }, + "details":{ + "type":"Paid in", + "description":"", + "posted":"2015-09-15T00:00:00.000Z", + "completed":"2015-09-15T00:00:00.000Z", + "new_balance":"-1803.11", + "value":"230.30" + } + },{ + "id":"faa813b8-6914-420c-b220-2e8452cedf63", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Greggs" + }, + "details":{ + "type":"Debit Card", + "description":"", + "posted":"2015-09-17T00:00:00.000Z", + "completed":"2015-09-17T00:00:00.000Z", + "new_balance":"-1861.81", + "value":"-58.70" + } + },{ + "id":"aa59f956-fd30-4c71-977a-0ae3394c9e0a", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Trainline" + }, + "details":{ + "type":"Debit Card", + "description":"", + "posted":"2015-09-17T00:00:00.000Z", + "completed":"2015-09-17T00:00:00.000Z", + "new_balance":"-1949.00", + "value":"-87.19" + } + },{ + "id":"5b79fc43-445e-4956-9fd2-9b1810fa8a15", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Donation" + }, + "details":{ + "type":"Paid in", + "description":"", + "posted":"2015-09-17T00:00:00.000Z", + "completed":"2015-09-17T00:00:00.000Z", + "new_balance":"-845.81", + "value":"1103.19" + } + },{ + "id":"792e53de-7f94-48e6-b15d-56bd07829f44", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Donation The Eye Company" + }, + "details":{ + "type":"Paid in", + "description":"", + "posted":"2015-09-19T00:00:00.000Z", + "completed":"2015-09-19T00:00:00.000Z", + "new_balance":"-417.82", + "value":"427.99" + } + },{ + "id":"7d59352a-a679-4b5f-b103-3860fa0e54f9", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Orange" + }, + "details":{ + "type":"Direct Debit", + "description":"", + "posted":"2015-09-21T00:00:00.000Z", + "completed":"2015-09-21T00:00:00.000Z", + "new_balance":"-470.84", + "value":"-53.02" + } + },{ + "id":"8a1fa0b0-710e-461e-ab4c-8eeab29ade08", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Donation Page Family" + }, + "details":{ + "type":"Paid in", + "description":"", + "posted":"2015-09-21T00:00:00.000Z", + "completed":"2015-09-21T00:00:00.000Z", + "new_balance":"-401.07", + "value":"69.77" + } + },{ + "id":"8b823eae-6b19-4e8e-b272-1f91345d1fdb", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Royal Edinburgh hospital" + }, + "details":{ + "type":"Transfer", + "description":"", + "posted":"2015-09-24T00:00:00.000Z", + "completed":"2015-09-24T00:00:00.000Z", + "new_balance":"-842.40", + "value":"-441.33" + } + },{ + "id":"a932f778-18e3-405f-8e19-1ffd285132d6", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Donation Mr and Mrs Waterhouse" + }, + "details":{ + "type":"Paid in", + "description":"", + "posted":"2015-09-24T00:00:00.000Z", + "completed":"2015-09-24T00:00:00.000Z", + "new_balance":"-612.10", + "value":"230.30" + } + },{ + "id":"0600caa9-e19f-4618-9109-7a08ae96927e", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Donation The Union" + }, + "details":{ + "type":"Paid in", + "description":"", + "posted":"2015-09-25T00:00:00.000Z", + "completed":"2015-09-25T00:00:00.000Z", + "new_balance":"-121.15", + "value":"490.95" + } + },{ + "id":"aebf5dc5-f8b0-4db4-ac03-a81d7a486a7c", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Rainbow hospice" + }, + "details":{ + "type":"Transfer", + "description":"", + "posted":"2015-09-25T00:00:00.000Z", + "completed":"2015-09-25T00:00:00.000Z", + "new_balance":"-965.55", + "value":"-844.40" + } + },{ + "id":"71276069-8c01-4397-9956-96aac798fcfb", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Donation Mr James" + }, + "details":{ + "type":"Paid in", + "description":"", + "posted":"2015-09-28T00:00:00.000Z", + "completed":"2015-09-28T00:00:00.000Z", + "new_balance":"1369.42", + "value":"2334.97" + } + },{ + "id":"fa785750-34eb-4597-ae9a-b75504415461", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Donation The Keepers inn" + }, + "details":{ + "type":"Paid in", + "description":"", + "posted":"2015-09-28T00:00:00.000Z", + "completed":"2015-09-28T00:00:00.000Z", + "new_balance":"5647.90", + "value":"4278.48" + } + },{ + "id":"65aba0b6-330f-42c3-8716-b3955e64b8ac", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Donation Smiles" + }, + "details":{ + "type":"Paid in", + "description":"", + "posted":"2015-09-30T00:00:00.000Z", + "completed":"2015-09-30T00:00:00.000Z", + "new_balance":"5878.20", + "value":"230.30" + } + },{ + "id":"7a918c54-3cb4-4fcb-99c9-4cbd388e1941", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Donation Miss Heath" + }, + "details":{ + "type":"Paid in", + "description":"", + "posted":"2015-09-30T00:00:00.000Z", + "completed":"2015-09-30T00:00:00.000Z", + "new_balance":"6098.86", + "value":"220.66" + } + },{ + "id":"1e74486f-8cca-4964-bd02-7f43cf03f742", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"British Gas " + }, + "details":{ + "type":"Direct Debit", + "description":"Gas/Elec", + "posted":"2015-07-01T00:00:00.000Z", + "completed":"2015-07-01T00:00:00.000Z", + "new_balance":"2353.27", + "value":"-84.91" + } + },{ + "id":"02a65724-869e-4475-b007-b0515f343ee6", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Seven Trent Water" + }, + "details":{ + "type":"Direct Debit", + "description":"Water", + "posted":"2015-07-01T00:00:00.000Z", + "completed":"2015-07-01T00:00:00.000Z", + "new_balance":"2320.35", + "value":"-32.92" + } + },{ + "id":"f9b33664-1ab8-4531-b885-0fb328ec8e13", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Duddingston Golf Club" + }, + "details":{ + "type":"Direct Debit", + "description":"Monthly Golf membership", + "posted":"2015-07-01T00:00:00.000Z", + "completed":"2015-07-01T00:00:00.000Z", + "new_balance":"2274.47", + "value":"-45.88" + } + },{ + "id":"1d0d872e-d389-42a0-8b1d-6b163eb0ed93", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Council Tax" + }, + "details":{ + "type":"Direct Debit", + "description":"Council Tax", + "posted":"2015-07-01T00:00:00.000Z", + "completed":"2015-07-01T00:00:00.000Z", + "new_balance":"2241.55", + "value":"-32.92" + } + },{ + "id":"62ade710-8303-419d-9e3b-8bd88e967896", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Marks and Spencers" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-05T00:00:00.000Z", + "completed":"2015-07-05T00:00:00.000Z", + "new_balance":"2136.53", + "value":"-105.02" + } + },{ + "id":"60d73107-6aa6-40f9-b407-a1fb1279e524", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Shell Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling Station", + "posted":"2015-07-05T00:00:00.000Z", + "completed":"2015-07-05T00:00:00.000Z", + "new_balance":"2069.31", + "value":"-67.22" + } + },{ + "id":"2f53149e-3f5e-4c90-a0d9-234064d40e79", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-07-08T00:00:00.000Z", + "completed":"2015-07-08T00:00:00.000Z", + "new_balance":"2059.11", + "value":"-10.20" + } + },{ + "id":"d9bd880d-03d9-4124-9581-dee618985ee5", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Prezzo" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurants", + "posted":"2015-07-10T00:00:00.000Z", + "completed":"2015-07-10T00:00:00.000Z", + "new_balance":"2012.31", + "value":"-46.80" + } + },{ + "id":"d959c54e-350e-497f-a5f5-610cbc940c2c", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Sainsbury's" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-12T00:00:00.000Z", + "completed":"2015-07-12T00:00:00.000Z", + "new_balance":"1970.07", + "value":"-42.24" + } + },{ + "id":"b743e72a-bf79-4e07-94b4-2a4d7e0387db", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"The Florist" + }, + "details":{ + "type":"Debit Card", + "description":"Florist", + "posted":"2015-07-12T00:00:00.000Z", + "completed":"2015-07-12T00:00:00.000Z", + "new_balance":"1937.15", + "value":"-32.92" + } + },{ + "id":"ea3cd082-e52f-47e2-a029-4c29e31fe977", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-07-13T00:00:00.000Z", + "completed":"2015-07-13T00:00:00.000Z", + "new_balance":"1932.54", + "value":"-4.61" + } + },{ + "id":"674178d2-c3f0-4ff1-a05f-d30545fe72ab", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"NCP Car Park" + }, + "details":{ + "type":"Debit Card", + "description":"Parking", + "posted":"2015-07-13T00:00:00.000Z", + "completed":"2015-07-13T00:00:00.000Z", + "new_balance":"1928.56", + "value":"-3.98" + } + },{ + "id":"739bec6b-2694-44f5-92ae-8d26926ff194", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"The Kitchin" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurants", + "posted":"2015-07-18T00:00:00.000Z", + "completed":"2015-07-18T00:00:00.000Z", + "new_balance":"1835.27", + "value":"-93.29" + } + },{ + "id":"728112c8-56b8-4174-baf6-2074c4685080", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Marks and Spencers" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-20T00:00:00.000Z", + "completed":"2015-07-20T00:00:00.000Z", + "new_balance":"1783.87", + "value":"-51.40" + } + },{ + "id":"763241b7-3431-4ce3-8836-e1326cff0766", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Shell Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling Station", + "posted":"2015-07-20T00:00:00.000Z", + "completed":"2015-07-20T00:00:00.000Z", + "new_balance":"1716.65", + "value":"-67.22" + } + },{ + "id":"dbcae7e9-79c3-4df5-a0c6-8ce056efcad7", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Bar Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-07-24T00:00:00.000Z", + "completed":"2015-07-24T00:00:00.000Z", + "new_balance":"1674.04", + "value":"-42.61" + } + },{ + "id":"3902f092-ab75-4f72-97f8-6e673514d140", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"British Telecom" + }, + "details":{ + "type":"Direct Debit", + "description":"BT", + "posted":"2015-07-29T00:00:00.000Z", + "completed":"2015-07-29T00:00:00.000Z", + "new_balance":"1650.33", + "value":"-23.71" + } + },{ + "id":"47bd17e5-47ed-481f-be05-f91dbcb5727f", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Paid in" + }, + "details":{ + "type":"Paid in", + "description":"Income", + "posted":"2015-07-29T00:00:00.000Z", + "completed":"2015-07-29T00:00:00.000Z", + "new_balance":"3947.71", + "value":"2297.38" + } + },{ + "id":"e736a34c-250c-435a-9b0f-ae60808d4ebd", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"RBS Savings" + }, + "details":{ + "type":"Standing Order", + "description":"Savings", + "posted":"2015-07-30T00:00:00.000Z", + "completed":"2015-07-30T00:00:00.000Z", + "new_balance":"3579.70", + "value":"-368.01" + } + },{ + "id":"b0def39a-7be8-43df-aee7-2807fbbd985d", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"British Gas " + }, + "details":{ + "type":"Direct Debit", + "description":"Gas/Elec", + "posted":"2015-08-01T00:00:00.000Z", + "completed":"2015-08-01T00:00:00.000Z", + "new_balance":"3494.79", + "value":"-84.91" + } + },{ + "id":"54d8fd71-eb8f-4817-b1f8-c7c1c66f4bae", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Seven Trent Water" + }, + "details":{ + "type":"Direct Debit", + "description":"Water", + "posted":"2015-08-01T00:00:00.000Z", + "completed":"2015-08-01T00:00:00.000Z", + "new_balance":"3461.87", + "value":"-32.92" + } + },{ + "id":"d930534b-eeb7-4920-9452-0741f947caa9", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Duddingston Golf Club" + }, + "details":{ + "type":"Direct Debit", + "description":"Monthly Golf membership", + "posted":"2015-08-01T00:00:00.000Z", + "completed":"2015-08-01T00:00:00.000Z", + "new_balance":"3415.99", + "value":"-45.88" + } + },{ + "id":"11558c49-92d9-459b-9eec-eafac67de628", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Council Tax" + }, + "details":{ + "type":"Direct Debit", + "description":"Council Tax", + "posted":"2015-08-01T00:00:00.000Z", + "completed":"2015-08-01T00:00:00.000Z", + "new_balance":"3383.07", + "value":"-32.92" + } + },{ + "id":"3e6fafb8-946e-4023-b3d6-0e50bde25fe7", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"The News Shop" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-03T00:00:00.000Z", + "completed":"2015-08-03T00:00:00.000Z", + "new_balance":"3377.18", + "value":"-5.89" + } + },{ + "id":"39da0fa8-d355-4604-be1c-6d12b8b4386f", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Shell Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling Station", + "posted":"2015-08-04T00:00:00.000Z", + "completed":"2015-08-04T00:00:00.000Z", + "new_balance":"3309.96", + "value":"-67.22" + } + },{ + "id":"2ee35cfc-fef9-4b79-9874-e7f7843c9013", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-08-08T00:00:00.000Z", + "completed":"2015-08-08T00:00:00.000Z", + "new_balance":"3297.64", + "value":"-12.32" + } + },{ + "id":"35f89966-286c-40d5-ac58-b30ec2dd5b63", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Next" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-08T00:00:00.000Z", + "completed":"2015-08-08T00:00:00.000Z", + "new_balance":"3266.15", + "value":"-31.49" + } + },{ + "id":"29989501-6a6f-4314-a3df-86d699843c75", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"House of Fraser" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-08T00:00:00.000Z", + "completed":"2015-08-08T00:00:00.000Z", + "new_balance":"3163.27", + "value":"-102.88" + } + },{ + "id":"64432adf-8e00-4597-9769-acf032ee67a2", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"La Favorita Restaurant" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurants", + "posted":"2015-08-08T00:00:00.000Z", + "completed":"2015-08-08T00:00:00.000Z", + "new_balance":"3086.56", + "value":"-76.71" + } + },{ + "id":"3acbf423-8d3c-4451-84a5-19a974810c7c", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Marks and Spencers" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-09T00:00:00.000Z", + "completed":"2015-08-09T00:00:00.000Z", + "new_balance":"3037.79", + "value":"-48.77" + } + },{ + "id":"db764abb-aeff-467c-861e-164013ddc7b7", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Bar Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-08-14T00:00:00.000Z", + "completed":"2015-08-14T00:00:00.000Z", + "new_balance":"3009.91", + "value":"-27.88" + } + },{ + "id":"e4ec77b1-d4d4-4035-8ecf-a227565bd149", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Prezzo" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurants", + "posted":"2015-08-15T00:00:00.000Z", + "completed":"2015-08-15T00:00:00.000Z", + "new_balance":"2978.51", + "value":"-31.40" + } + },{ + "id":"9d1b5222-af5a-4da5-9fa3-626390fcf1d1", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-08-19T00:00:00.000Z", + "completed":"2015-08-19T00:00:00.000Z", + "new_balance":"2973.37", + "value":"-5.14" + } + },{ + "id":"38af4651-f4ea-4f3d-971e-128dbaaa15a6", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Marks and Spencers" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-22T00:00:00.000Z", + "completed":"2015-08-22T00:00:00.000Z", + "new_balance":"2921.05", + "value":"-52.32" + } + },{ + "id":"550028d2-c0af-4aec-be22-41fde336d713", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Shell Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling Station", + "posted":"2015-08-26T00:00:00.000Z", + "completed":"2015-08-26T00:00:00.000Z", + "new_balance":"2853.83", + "value":"-67.22" + } + },{ + "id":"3f6b8355-4ab1-4272-895c-ce56922237b7", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"British Telecom" + }, + "details":{ + "type":"Direct Debit", + "description":"BT", + "posted":"2015-08-29T00:00:00.000Z", + "completed":"2015-08-29T00:00:00.000Z", + "new_balance":"2830.12", + "value":"-23.71" + } + },{ + "id":"244fce61-b9e7-489d-9ed5-bf3f6f62783c", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Paid in" + }, + "details":{ + "type":"Paid in", + "description":"Income", + "posted":"2015-08-29T00:00:00.000Z", + "completed":"2015-08-29T00:00:00.000Z", + "new_balance":"5127.50", + "value":"2297.38" + } + },{ + "id":"162fb4a5-48c0-4c53-817b-90ddd0437d68", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"RBS Savings" + }, + "details":{ + "type":"Standing Order", + "description":"Savings", + "posted":"2015-08-30T00:00:00.000Z", + "completed":"2015-08-30T00:00:00.000Z", + "new_balance":"4759.49", + "value":"-368.01" + } + },{ + "id":"9cbb9fa8-e9a4-4f28-95fc-4fcfedc0521c", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"British Gas" + }, + "details":{ + "type":"Direct Debit", + "description":"Gas/Elec", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"4674.58", + "value":"-84.91" + } + },{ + "id":"39541afe-ed7e-4b0f-b433-fa6b10e01e9f", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Seven Trent Water" + }, + "details":{ + "type":"Direct Debit", + "description":"Water", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"4641.66", + "value":"-32.92" + } + },{ + "id":"4641d5ef-3995-43d5-8390-fe1d3e546a9e", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Duddingston Golf Club" + }, + "details":{ + "type":"Direct Debit", + "description":"Monthly Golf membership", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"4595.78", + "value":"-45.88" + } + },{ + "id":"57b0f7f6-04f7-4c8b-ae94-b07e525f1d74", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Council Tax" + }, + "details":{ + "type":"Direct Debit", + "description":"Council Tax", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"4562.86", + "value":"-32.92" + } + },{ + "id":"fa647c36-ae45-4cba-82e1-184924124036", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Marks and Spencers" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-09T00:00:00.000Z", + "completed":"2015-09-09T00:00:00.000Z", + "new_balance":"4510.75", + "value":"-52.11" + } + },{ + "id":"f8d2e8f2-e2d6-4f68-9a33-e7429758b5f2", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Shell Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling Station", + "posted":"2015-09-09T00:00:00.000Z", + "completed":"2015-09-09T00:00:00.000Z", + "new_balance":"4436.97", + "value":"-73.78" + } + },{ + "id":"d62da697-3b57-4ebb-868d-37dbe96ca3d2", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"The Cellar Door" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurants", + "posted":"2015-09-17T00:00:00.000Z", + "completed":"2015-09-17T00:00:00.000Z", + "new_balance":"4343.76", + "value":"-93.21" + } + },{ + "id":"de73f9b1-5884-4626-a712-3247904708ee", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-09-18T00:00:00.000Z", + "completed":"2015-09-18T00:00:00.000Z", + "new_balance":"4336.65", + "value":"-7.11" + } + },{ + "id":"aac84f86-1e83-4a37-baef-bbe98e83601b", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Debenhams" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-18T00:00:00.000Z", + "completed":"2015-09-18T00:00:00.000Z", + "new_balance":"4257.75", + "value":"-78.90" + } + },{ + "id":"47fa7045-33d1-4efe-8fdd-df9ff06e4a6d", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Next" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-18T00:00:00.000Z", + "completed":"2015-09-18T00:00:00.000Z", + "new_balance":"4208.54", + "value":"-49.21" + } + },{ + "id":"00fe8a25-444c-4330-9391-6dd1c049e75d", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Pizza Express" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurants", + "posted":"2015-09-18T00:00:00.000Z", + "completed":"2015-09-18T00:00:00.000Z", + "new_balance":"4177.31", + "value":"-31.23" + } + },{ + "id":"4c96bc6f-f855-42f1-bcd9-9b5564dd7470", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Marks and Spencers" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-22T00:00:00.000Z", + "completed":"2015-09-22T00:00:00.000Z", + "new_balance":"4105.42", + "value":"-71.89" + } + },{ + "id":"692c2fcc-2aa1-40fd-bc21-7eb2d526d3ea", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"British Telecom" + }, + "details":{ + "type":"Direct Debit", + "description":"BT", + "posted":"2015-09-29T00:00:00.000Z", + "completed":"2015-09-29T00:00:00.000Z", + "new_balance":"4081.71", + "value":"-23.71" + } + },{ + "id":"51b9aa86-009f-4005-9d01-d8240109e4eb", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Paid in" + }, + "details":{ + "type":"Paid in", + "description":"Income", + "posted":"2015-09-29T00:00:00.000Z", + "completed":"2015-09-29T00:00:00.000Z", + "new_balance":"6379.09", + "value":"2297.38" + } + },{ + "id":"8a27641b-a866-4081-a8f8-ac53171af5fa", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"RBS Savings" + }, + "details":{ + "type":"Standing Order", + "description":"Savings", + "posted":"2015-09-30T00:00:00.000Z", + "completed":"2015-09-30T00:00:00.000Z", + "new_balance":"6011.08", + "value":"-368.01" + } + },{ + "id":"d94dd841-038f-45ce-b1c5-81da8b419673", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Bambo Bar" + }, + "details":{ + "type":"Credit Card", + "description":"Bar", + "posted":"2010-09-27T00:00:00.000Z", + "completed":"2010-09-27T00:00:00.000Z", + "new_balance":"8396.28", + "value":"-14.76" + } + },{ + "id":"9285476b-9d50-4d65-9697-9a662f959dd8", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Tesco" + }, + "details":{ + "type":"Credit Card", + "description":"Shopping", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"8330.72", + "value":"-65.56" + } + },{ + "id":"33a4f625-73b0-495c-a404-b711e665cd35", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Tesco Filling Station" + }, + "details":{ + "type":"Credit Card", + "description":"Filling Station", + "posted":"2015-09-02T00:00:00.000Z", + "completed":"2015-09-02T00:00:00.000Z", + "new_balance":"8277.35", + "value":"-53.37" + } + },{ + "id":"2f5bf0e6-eadd-4a15-9a61-9e443263bdb7", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Bambo Bar" + }, + "details":{ + "type":"Credit Card", + "description":"Bar", + "posted":"2015-09-07T00:00:00.000Z", + "completed":"2015-09-07T00:00:00.000Z", + "new_balance":"8207.55", + "value":"-69.80" + } + },{ + "id":"4a582e83-e2b2-426e-9308-f8afcfd4c256", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Dominos" + }, + "details":{ + "type":"Credit Card", + "description":"Takeway", + "posted":"2015-09-11T00:00:00.000Z", + "completed":"2015-09-11T00:00:00.000Z", + "new_balance":"8200.47", + "value":"-7.08" + } + },{ + "id":"ec77fefe-a8de-4b1c-9f54-adbbc65cf99b", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Credit Card", + "description":"coffee", + "posted":"2015-09-15T00:00:00.000Z", + "completed":"2015-09-15T00:00:00.000Z", + "new_balance":"8197.13", + "value":"-3.34" + } + },{ + "id":"58eab15b-3ad1-4708-8632-fc6e8f3a18d7", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Prezzo" + }, + "details":{ + "type":"Credit Card", + "description":"resturant", + "posted":"2015-09-17T00:00:00.000Z", + "completed":"2015-09-17T00:00:00.000Z", + "new_balance":"8169.90", + "value":"-27.23" + } + },{ + "id":"a6bf0706-64b3-49c7-bb78-c812ebce4111", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"H Samuals" + }, + "details":{ + "type":"Credit Card", + "description":"Shopping", + "posted":"2015-09-17T00:00:00.000Z", + "completed":"2015-09-17T00:00:00.000Z", + "new_balance":"7801.89", + "value":"-368.01" + } + },{ + "id":"f163dd22-f4ae-419a-8f93-28f753b2be33", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Zara" + }, + "details":{ + "type":"Credit Card", + "description":"Shopping", + "posted":"2015-09-17T00:00:00.000Z", + "completed":"2015-09-17T00:00:00.000Z", + "new_balance":"7687.30", + "value":"-114.59" + } + },{ + "id":"0b3471b2-a33e-4f7f-a95c-463cf6c65ced", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Credit Card", + "description":"coffee", + "posted":"2015-09-19T00:00:00.000Z", + "completed":"2015-09-19T00:00:00.000Z", + "new_balance":"7683.96", + "value":"-3.34" + } + },{ + "id":"696c0dc4-0dd6-4ee8-a28b-2590e0c0fd69", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Showcase Cinema" + }, + "details":{ + "type":"Credit Card", + "description":"Cinema", + "posted":"2015-09-21T00:00:00.000Z", + "completed":"2015-09-21T00:00:00.000Z", + "new_balance":"7662.07", + "value":"-21.89" + } + },{ + "id":"ca214ab9-3cbb-4778-9e3e-98c3a9bdb161", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Tesco" + }, + "details":{ + "type":"Credit Card", + "description":"Shopping", + "posted":"2015-09-23T00:00:00.000Z", + "completed":"2015-09-23T00:00:00.000Z", + "new_balance":"7596.51", + "value":"-65.56" + } + },{ + "id":"b88d2abe-2e6a-4f02-be72-0607615c21d5", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"NCP Car Park" + }, + "details":{ + "type":"Credit Card", + "description":"Parking", + "posted":"2015-09-24T00:00:00.000Z", + "completed":"2015-09-24T00:00:00.000Z", + "new_balance":"7589.43", + "value":"-7.08" + } + },{ + "id":"2d72f6eb-98c5-47bf-bf3b-2c3329c42c82", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Council Tax" + }, + "details":{ + "type":"Direct Debit", + "description":"Council Tax", + "posted":"2015-07-01T00:00:00.000Z", + "completed":"2015-07-01T00:00:00.000Z", + "new_balance":"7580.99", + "value":"-90.58" + } + },{ + "id":"b47a522a-38df-4dc3-90ba-19a535385f1d", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Rent" + }, + "details":{ + "type":"Direct Debit", + "description":"Rent", + "posted":"2015-07-03T00:00:00.000Z", + "completed":"2015-07-03T00:00:00.000Z", + "new_balance":"6877.00", + "value":"-703.99" + } + },{ + "id":"2882a8be-0b36-4218-937d-a12fca1e38cd", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"EON" + }, + "details":{ + "type":"Direct Debit", + "description":"Gas/Elec", + "posted":"2015-07-03T00:00:00.000Z", + "completed":"2015-07-03T00:00:00.000Z", + "new_balance":"6823.98", + "value":"-53.02" + } + },{ + "id":"18d872ea-09dc-43fd-83e7-7b0dd64c7344", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Seven Trent Water" + }, + "details":{ + "type":"Direct Debit", + "description":"Water", + "posted":"2015-07-03T00:00:00.000Z", + "completed":"2015-07-03T00:00:00.000Z", + "new_balance":"6799.67", + "value":"-24.31" + } + },{ + "id":"c50427f0-b65d-4b91-a7c0-67f272629ba4", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Talk Talk" + }, + "details":{ + "type":"Direct Debit", + "description":"Telephone", + "posted":"2015-07-03T00:00:00.000Z", + "completed":"2015-07-03T00:00:00.000Z", + "new_balance":"6768.72", + "value":"-30.95" + } + },{ + "id":"7b4a9679-eba7-42b2-8dce-e53b423adff4", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Asda" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-05T00:00:00.000Z", + "completed":"2015-07-05T00:00:00.000Z", + "new_balance":"6723.17", + "value":"-45.55" + } + },{ + "id":"d8008dc6-b9b6-41d7-a288-d4008af079c4", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Asda ATM" + }, + "details":{ + "type":"Cash withdrawal", + "description":"Cash withdrawal", + "posted":"2015-07-05T00:00:00.000Z", + "completed":"2015-07-05T00:00:00.000Z", + "new_balance":"6679.65", + "value":"-43.52" + } + },{ + "id":"fc879488-4189-41d5-bf6a-7dbaab9035ce", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Poundland" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-05T00:00:00.000Z", + "completed":"2015-07-05T00:00:00.000Z", + "new_balance":"6666.29", + "value":"-13.36" + } + },{ + "id":"e73d9b77-aeb9-4f0c-90f1-f6a19dfdae06", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Specsavers" + }, + "details":{ + "type":"Debit Card", + "description":"Optician", + "posted":"2015-07-06T00:00:00.000Z", + "completed":"2015-07-06T00:00:00.000Z", + "new_balance":"6640.21", + "value":"-26.08" + } + },{ + "id":"e02f3e08-78db-433d-895b-536b5fb16d45", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"mcdonalds" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-07-06T00:00:00.000Z", + "completed":"2015-07-06T00:00:00.000Z", + "new_balance":"6612.06", + "value":"-28.15" + } + },{ + "id":"0df0e27d-692d-4418-a25f-32bc4d0b6692", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"High Street ATM" + }, + "details":{ + "type":"Cash withdrawal", + "description":"Cash withdrawal", + "posted":"2015-07-10T00:00:00.000Z", + "completed":"2015-07-10T00:00:00.000Z", + "new_balance":"6568.54", + "value":"-43.52" + } + },{ + "id":"de1fc929-226e-44b1-9e2e-65c97703249a", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"CO-OP" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-10T00:00:00.000Z", + "completed":"2015-07-10T00:00:00.000Z", + "new_balance":"6541.37", + "value":"-27.17" + } + },{ + "id":"90855b41-d5c6-418a-8863-aae7fd2b833b", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Asda" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-12T00:00:00.000Z", + "completed":"2015-07-12T00:00:00.000Z", + "new_balance":"6503.24", + "value":"-38.13" + } + },{ + "id":"ef5c95bc-816f-4189-9eaa-8a7d51f11c27", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Clarks" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-12T00:00:00.000Z", + "completed":"2015-07-12T00:00:00.000Z", + "new_balance":"6472.29", + "value":"-30.95" + } + },{ + "id":"18ed6025-e0f6-422f-a030-7ab7d7b76fdc", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Next" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-12T00:00:00.000Z", + "completed":"2015-07-12T00:00:00.000Z", + "new_balance":"6445.88", + "value":"-26.41" + } + },{ + "id":"17ca9b0b-574a-4c37-bcd4-15747c758d7c", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"mcdonalds" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-07-15T00:00:00.000Z", + "completed":"2015-07-15T00:00:00.000Z", + "new_balance":"6425.13", + "value":"-20.75" + } + },{ + "id":"0dbfc11b-e48f-4fcf-af3e-fcb852851540", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"High Street ATM" + }, + "details":{ + "type":"Cash withdrawal", + "description":"Cash withdrawal", + "posted":"2015-07-16T00:00:00.000Z", + "completed":"2015-07-16T00:00:00.000Z", + "new_balance":"6381.61", + "value":"-43.52" + } + },{ + "id":"74486fc8-ab0c-44ac-9f4b-2db1cb4d11c4", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"CO-OP" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-16T00:00:00.000Z", + "completed":"2015-07-16T00:00:00.000Z", + "new_balance":"6373.46", + "value":"-8.15" + } + },{ + "id":"ee38dffc-6e38-4cce-95e9-1c9d30c4fe54", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Asda" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-17T00:00:00.000Z", + "completed":"2015-07-17T00:00:00.000Z", + "new_balance":"6300.36", + "value":"-73.10" + } + },{ + "id":"716c9acc-3eda-4ce9-bea5-7dd70deb21c7", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Asda Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling station", + "posted":"2015-07-17T00:00:00.000Z", + "completed":"2015-07-17T00:00:00.000Z", + "new_balance":"6272.08", + "value":"-28.28" + } + },{ + "id":"7c175892-a805-4d86-8451-d130912be291", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Pizza hut" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-07-19T00:00:00.000Z", + "completed":"2015-07-19T00:00:00.000Z", + "new_balance":"6232.03", + "value":"-40.05" + } + },{ + "id":"ec2e04c4-3983-4bad-a97c-5fc4c204a730", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"High Street ATM" + }, + "details":{ + "type":"Cash withdrawal", + "description":"Cash withdrawal", + "posted":"2015-07-20T00:00:00.000Z", + "completed":"2015-07-20T00:00:00.000Z", + "new_balance":"6188.51", + "value":"-43.52" + } + },{ + "id":"14044493-c1cf-47ef-b11a-58011b1413de", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"CO-OP" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-23T00:00:00.000Z", + "completed":"2015-07-23T00:00:00.000Z", + "new_balance":"6175.30", + "value":"-13.21" + } + },{ + "id":"1dd9f666-7a42-4ad7-916d-e0b3246d1052", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Asda" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-26T00:00:00.000Z", + "completed":"2015-07-26T00:00:00.000Z", + "new_balance":"6125.43", + "value":"-49.87" + } + },{ + "id":"b7945ecb-060a-4e6d-8b52-a4f999a7f6c6", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Salary" + }, + "details":{ + "type":"Paid in", + "description":"Salary", + "posted":"2015-07-27T00:00:00.000Z", + "completed":"2015-07-27T00:00:00.000Z", + "new_balance":"7336.38", + "value":"1210.95" + } + },{ + "id":"6f71b200-0b9d-44a8-948c-b0c5e8bc411a", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"three" + }, + "details":{ + "type":"Direct Debit", + "description":"Mobile phone", + "posted":"2015-07-28T00:00:00.000Z", + "completed":"2015-07-28T00:00:00.000Z", + "new_balance":"7312.07", + "value":"-24.31" + } + },{ + "id":"84c3ccbd-29aa-4cce-8e76-dceeb083a0ca", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Post Office Savings Account" + }, + "details":{ + "type":"Standing Order", + "description":"Savings", + "posted":"2015-07-30T00:00:00.000Z", + "completed":"2015-07-30T00:00:00.000Z", + "new_balance":"7277.13", + "value":"-34.94" + } + },{ + "id":"f85bcbd0-923c-4511-82e0-f3740f112dc4", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Council Tax" + }, + "details":{ + "type":"Direct Debit", + "description":"Council Tax", + "posted":"2015-08-01T00:00:00.000Z", + "completed":"2015-08-01T00:00:00.000Z", + "new_balance":"7186.55", + "value":"-90.58" + } + },{ + "id":"514f998a-3802-4df5-9528-9c1146702daf", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Rent" + }, + "details":{ + "type":"Direct Debit", + "description":"Rent", + "posted":"2015-08-03T00:00:00.000Z", + "completed":"2015-08-03T00:00:00.000Z", + "new_balance":"6482.56", + "value":"-703.99" + } + },{ + "id":"31e97c98-9181-47c3-8886-a56f0e3c7f1d", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"EON" + }, + "details":{ + "type":"Direct Debit", + "description":"Gas/Elec", + "posted":"2015-08-03T00:00:00.000Z", + "completed":"2015-08-03T00:00:00.000Z", + "new_balance":"6429.54", + "value":"-53.02" + } + },{ + "id":"a4c8049b-2db3-444b-9e7f-74d538f09b4d", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Seven Trent Water" + }, + "details":{ + "type":"Direct Debit", + "description":"Water", + "posted":"2015-08-03T00:00:00.000Z", + "completed":"2015-08-03T00:00:00.000Z", + "new_balance":"6405.23", + "value":"-24.31" + } + },{ + "id":"2a55ff3d-f3c3-4ed9-9875-e3b95ba769ee", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Talk Talk" + }, + "details":{ + "type":"Direct Debit", + "description":"Telephone", + "posted":"2015-08-03T00:00:00.000Z", + "completed":"2015-08-03T00:00:00.000Z", + "new_balance":"6374.28", + "value":"-30.95" + } + },{ + "id":"e7b9ae7a-9741-4f91-bc8b-57ce088d885e", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Asda" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-08T00:00:00.000Z", + "completed":"2015-08-08T00:00:00.000Z", + "new_balance":"6328.73", + "value":"-45.55" + } + },{ + "id":"05fa5bec-cd11-4e5c-bbdd-00191e147e7f", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Asda ATM" + }, + "details":{ + "type":"Cash withdrawal", + "description":"Cash withdrawal", + "posted":"2015-08-08T00:00:00.000Z", + "completed":"2015-08-08T00:00:00.000Z", + "new_balance":"6285.21", + "value":"-43.52" + } + },{ + "id":"7bc16eb8-826a-4d09-ae94-540d8b7762fe", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"mcdonalds" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-08-13T00:00:00.000Z", + "completed":"2015-08-13T00:00:00.000Z", + "new_balance":"6259.13", + "value":"-26.08" + } + },{ + "id":"0ecbe794-8606-4e63-bf41-f198ba67ce01", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Ebay" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-14T00:00:00.000Z", + "completed":"2015-08-14T00:00:00.000Z", + "new_balance":"6218.97", + "value":"-40.16" + } + },{ + "id":"88b1b578-74cf-4653-8630-f37fa15630b1", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"The Book people" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-17T00:00:00.000Z", + "completed":"2015-08-17T00:00:00.000Z", + "new_balance":"6208.17", + "value":"-10.80" + } + },{ + "id":"4cdff543-cd92-4b0c-be93-675c0723c533", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Domino's" + }, + "details":{ + "type":"Debit Card", + "description":"Takeaway", + "posted":"2015-08-21T00:00:00.000Z", + "completed":"2015-08-21T00:00:00.000Z", + "new_balance":"6175.98", + "value":"-32.19" + } + },{ + "id":"987bfa02-b398-44dc-8aab-537d3ad12607", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Asda" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-23T00:00:00.000Z", + "completed":"2015-08-23T00:00:00.000Z", + "new_balance":"6134.57", + "value":"-41.41" + } + },{ + "id":"d1a13b55-3d72-4612-9ace-f7133dd3bca9", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Asda ATM" + }, + "details":{ + "type":"Cash withdrawal", + "description":"Cash withdrawal", + "posted":"2015-08-23T00:00:00.000Z", + "completed":"2015-08-23T00:00:00.000Z", + "new_balance":"6091.05", + "value":"-43.52" + } + },{ + "id":"f8033790-1b8c-4e93-94c0-bd1951669abe", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Argos" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-24T00:00:00.000Z", + "completed":"2015-08-24T00:00:00.000Z", + "new_balance":"6080.25", + "value":"-10.80" + } + },{ + "id":"2081deac-b982-4c8b-9212-9c44ced9574a", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"mcdonalds" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-08-27T00:00:00.000Z", + "completed":"2015-08-27T00:00:00.000Z", + "new_balance":"6062.47", + "value":"-17.78" + } + },{ + "id":"42dda6ca-a3ac-4563-826a-4215677510b0", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Asda" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-27T00:00:00.000Z", + "completed":"2015-08-27T00:00:00.000Z", + "new_balance":"6012.60", + "value":"-49.87" + } + },{ + "id":"d4b90f87-7b4d-46a9-924f-9e001c6af73f", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Salary" + }, + "details":{ + "type":"Paid in", + "description":"Salary", + "posted":"2015-08-27T00:00:00.000Z", + "completed":"2015-08-27T00:00:00.000Z", + "new_balance":"7223.55", + "value":"1210.95" + } + },{ + "id":"427607a3-18e0-42c2-9911-9a4511c6fa1a", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"three" + }, + "details":{ + "type":"Direct Debit", + "description":"Mobile phone", + "posted":"2015-08-28T00:00:00.000Z", + "completed":"2015-08-28T00:00:00.000Z", + "new_balance":"7199.24", + "value":"-24.31" + } + },{ + "id":"e674975a-e61b-474e-b039-a6eadd44b8cb", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Post Office Savings Account" + }, + "details":{ + "type":"Standing Order", + "description":"Savings", + "posted":"2015-08-30T00:00:00.000Z", + "completed":"2015-08-30T00:00:00.000Z", + "new_balance":"7164.30", + "value":"-34.94" + } + },{ + "id":"8f067a5d-d25f-4ed6-acc2-88488672bc0b", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Council Tax" + }, + "details":{ + "type":"Direct Debit", + "description":"Council Tax", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"7073.72", + "value":"-90.58" + } + },{ + "id":"3e4804f1-3518-4c10-a3c9-a13171aa8769", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Rent" + }, + "details":{ + "type":"Direct Debit", + "description":"Rent", + "posted":"2015-09-03T00:00:00.000Z", + "completed":"2015-09-03T00:00:00.000Z", + "new_balance":"6369.73", + "value":"-703.99" + } + },{ + "id":"7b3fd0bb-3ef2-4664-a10b-ed4a789170db", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"EON" + }, + "details":{ + "type":"Direct Debit", + "description":"Gas/Elec", + "posted":"2015-09-03T00:00:00.000Z", + "completed":"2015-09-03T00:00:00.000Z", + "new_balance":"6316.71", + "value":"-53.02" + } + },{ + "id":"9311ce64-2c2c-4dcd-84a8-f97c3f48fa3e", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Seven Trent Water" + }, + "details":{ + "type":"Direct Debit", + "description":"Water", + "posted":"2015-09-03T00:00:00.000Z", + "completed":"2015-09-03T00:00:00.000Z", + "new_balance":"6292.40", + "value":"-24.31" + } + },{ + "id":"9688adec-4597-4901-a30b-5e00bd69dd1f", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Talk Talk" + }, + "details":{ + "type":"Direct Debit", + "description":"Telephone", + "posted":"2015-09-03T00:00:00.000Z", + "completed":"2015-09-03T00:00:00.000Z", + "new_balance":"6261.45", + "value":"-30.95" + } + },{ + "id":"d8bb368f-b066-4119-93c9-2da47fb9b329", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Asda" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-05T00:00:00.000Z", + "completed":"2015-09-05T00:00:00.000Z", + "new_balance":"6216.90", + "value":"-44.55" + } + },{ + "id":"164921fe-e182-489a-8fe1-40ecf0e2ffbc", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"ATM Asda" + }, + "details":{ + "type":"Cash Withdrawal", + "description":"Cash withdrawal", + "posted":"2015-09-05T00:00:00.000Z", + "completed":"2015-09-05T00:00:00.000Z", + "new_balance":"6173.38", + "value":"-43.52" + } + },{ + "id":"9e26be31-11fd-4f5d-931f-f5b17e638c5a", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"CO-OP" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-07T00:00:00.000Z", + "completed":"2015-09-07T00:00:00.000Z", + "new_balance":"6151.61", + "value":"-21.77" + } + },{ + "id":"a6602093-5b3d-406c-8df0-d962920ad851", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"ATM High Street" + }, + "details":{ + "type":"Cash Withdrawal", + "description":"Cash withdrawal", + "posted":"2015-09-09T00:00:00.000Z", + "completed":"2015-09-09T00:00:00.000Z", + "new_balance":"6108.09", + "value":"-43.52" + } + },{ + "id":"bb22c6e1-ae9a-412e-bb54-afa8e794784c", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Mcdonalds" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-09-11T00:00:00.000Z", + "completed":"2015-09-11T00:00:00.000Z", + "new_balance":"6091.79", + "value":"-16.30" + } + },{ + "id":"de7b6f3b-95a9-4409-90b3-e66571b2f2ec", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Asda" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-13T00:00:00.000Z", + "completed":"2015-09-13T00:00:00.000Z", + "new_balance":"6030.24", + "value":"-61.55" + } + },{ + "id":"5ca49db0-49a9-44e1-856e-a4c68c0273c9", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Poundland" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-14T00:00:00.000Z", + "completed":"2015-09-14T00:00:00.000Z", + "new_balance":"6019.81", + "value":"-10.43" + } + },{ + "id":"a70e9f04-6a6b-48ba-8846-f61ad0c572b9", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Asda Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling station", + "posted":"2015-09-15T00:00:00.000Z", + "completed":"2015-09-15T00:00:00.000Z", + "new_balance":"5984.87", + "value":"-34.94" + } + },{ + "id":"9d2dcd29-165f-482c-806d-a8d4cda67abe", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Sport Direct" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-26T00:00:00.000Z", + "completed":"2015-09-26T00:00:00.000Z", + "new_balance":"5952.53", + "value":"-32.34" + } + },{ + "id":"9c2932ac-4e57-4f86-80b9-5a5f42563270", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Pizza Hut" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-09-26T00:00:00.000Z", + "completed":"2015-09-26T00:00:00.000Z", + "new_balance":"5908.66", + "value":"-43.87" + } + },{ + "id":"da467994-5700-4331-80c5-c295b6588b97", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"CO-OP" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-26T00:00:00.000Z", + "completed":"2015-09-26T00:00:00.000Z", + "new_balance":"5888.17", + "value":"-20.49" + } + },{ + "id":"c73f0491-c216-47fe-86b7-4997b47774f2", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Salary" + }, + "details":{ + "type":"Paid in", + "description":"Paid in", + "posted":"2015-09-27T00:00:00.000Z", + "completed":"2015-09-27T00:00:00.000Z", + "new_balance":"7099.12", + "value":"1210.95" + } + },{ + "id":"882eef84-b366-46c4-86ca-6984e4cf2389", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Asda" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-27T00:00:00.000Z", + "completed":"2015-09-27T00:00:00.000Z", + "new_balance":"7046.89", + "value":"-52.23" + } + },{ + "id":"a2322b18-3834-44f3-996b-fb089597e3c5", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Asda ATM" + }, + "details":{ + "type":"Cash Withdrawal", + "description":"Cash withdrawal", + "posted":"2015-09-27T00:00:00.000Z", + "completed":"2015-09-27T00:00:00.000Z", + "new_balance":"7003.37", + "value":"-43.52" + } + },{ + "id":"cbf37ad1-0389-40e4-a432-4ceeacfba5fb", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"three" + }, + "details":{ + "type":"Direct Debit", + "description":"Mobile phone", + "posted":"2015-09-28T00:00:00.000Z", + "completed":"2015-09-28T00:00:00.000Z", + "new_balance":"6979.06", + "value":"-24.31" + } + },{ + "id":"2ce6fc59-1165-426b-8b6f-d8695c4028c9", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Post Office Savings Account" + }, + "details":{ + "type":"Standing Order", + "description":"Savings", + "posted":"2015-09-30T00:00:00.000Z", + "completed":"2015-09-30T00:00:00.000Z", + "new_balance":"6944.12", + "value":"-34.94" + } + },{ + "id":"9d8ae590-8e5e-407f-8c45-e92d0efa054b", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Asda Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling station", + "posted":"2025-08-08T00:00:00.000Z", + "completed":"2025-08-08T00:00:00.000Z", + "new_balance":"6917.75", + "value":"-26.37" + } + },{ + "id":"d33f53ae-e4e3-4ee9-afd5-4f15c083eb60", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Rent" + }, + "details":{ + "type":"Direct Debit", + "description":"", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"4724.26", + "value":"-577.63" + } + },{ + "id":"7366d126-927e-4275-8c17-bfb4a531fd50", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Rates" + }, + "details":{ + "type":"Direct Debit", + "description":"", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"4496.22", + "value":"-228.04" + } + },{ + "id":"71a6734f-81a1-4da5-9cb8-9a945c1cc7be", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Southern Electric" + }, + "details":{ + "type":"Direct Debit", + "description":"", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"4128.21", + "value":"-368.01" + } + },{ + "id":"63b67732-0f5f-4e7d-b710-c20f9b418a45", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"BT" + }, + "details":{ + "type":"Direct Debit", + "description":"", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"4013.62", + "value":"-114.59" + } + },{ + "id":"a4f3ecef-b847-4de8-b922-45f6d3006bb4", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Salary Mr R Turner" + }, + "details":{ + "type":"Transfer", + "description":"", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"760.56", + "value":"-3253.06" + } + },{ + "id":"31ed3fde-11a4-4209-94e4-826507ab2336", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Salary Mrs F White" + }, + "details":{ + "type":"Transfer", + "description":"", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"-2551.89", + "value":"-3312.45" + } + },{ + "id":"e2a0dc41-e621-4838-882f-0d04976fd551", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"The George Hotel" + }, + "details":{ + "type":"Debit Card", + "description":"", + "posted":"2015-09-12T00:00:00.000Z", + "completed":"2015-09-12T00:00:00.000Z", + "new_balance":"-3129.52", + "value":"-577.63" + } + },{ + "id":"fb49007f-bff6-4852-a2e8-81dbddc18f70", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"The Catering Company" + }, + "details":{ + "type":"Debit Card", + "description":"", + "posted":"2015-09-12T00:00:00.000Z", + "completed":"2015-09-12T00:00:00.000Z", + "new_balance":"-3497.53", + "value":"-368.01" + } + },{ + "id":"9eae8f86-884a-4ca5-8115-f0946df723d8", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Shell Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"", + "posted":"2015-09-12T00:00:00.000Z", + "completed":"2015-09-12T00:00:00.000Z", + "new_balance":"-3549.30", + "value":"-51.77" + } + },{ + "id":"e3daab67-ae45-4bd6-a4ed-1178c5423cae", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Tesco" + }, + "details":{ + "type":"Debit Card", + "description":"", + "posted":"2015-09-12T00:00:00.000Z", + "completed":"2015-09-12T00:00:00.000Z", + "new_balance":"-3595.18", + "value":"-45.88" + } + },{ + "id":"a5c4d35d-20e5-4e16-bf82-385596b82356", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Donation Mr and Mrs Cooper" + }, + "details":{ + "type":"Paid in", + "description":"", + "posted":"2015-09-15T00:00:00.000Z", + "completed":"2015-09-15T00:00:00.000Z", + "new_balance":"-1412.09", + "value":"2183.09" + } + },{ + "id":"8e041d48-26b4-4d2e-bc1f-2a8eaa436424", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Donation The Corner Store" + }, + "details":{ + "type":"Paid in", + "description":"", + "posted":"2015-09-15T00:00:00.000Z", + "completed":"2015-09-15T00:00:00.000Z", + "new_balance":"-1137.31", + "value":"274.78" + } + },{ + "id":"d58425e2-285b-4a8b-b3e6-2d6227112e18", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Greggs" + }, + "details":{ + "type":"Debit Card", + "description":"", + "posted":"2015-09-17T00:00:00.000Z", + "completed":"2015-09-17T00:00:00.000Z", + "new_balance":"-1207.11", + "value":"-69.80" + } + },{ + "id":"c5a8e2d7-7072-4b62-a2f6-2dad68ea1ba0", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Trainline" + }, + "details":{ + "type":"Debit Card", + "description":"", + "posted":"2015-09-17T00:00:00.000Z", + "completed":"2015-09-17T00:00:00.000Z", + "new_balance":"-1284.40", + "value":"-77.29" + } + },{ + "id":"71464e7d-7c07-42c8-a00a-dcad61d24d23", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Donation" + }, + "details":{ + "type":"Paid in", + "description":"", + "posted":"2015-09-17T00:00:00.000Z", + "completed":"2015-09-17T00:00:00.000Z", + "new_balance":"-189.30", + "value":"1095.10" + } + },{ + "id":"7e48e8b3-efbf-41c9-9757-2654b2f66c4b", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Donation The Eye Company" + }, + "details":{ + "type":"Paid in", + "description":"", + "posted":"2015-09-19T00:00:00.000Z", + "completed":"2015-09-19T00:00:00.000Z", + "new_balance":"260.14", + "value":"449.44" + } + },{ + "id":"f017a08f-d5d1-4346-9b48-42664cd5c2c5", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Orange" + }, + "details":{ + "type":"Direct Debit", + "description":"", + "posted":"2015-09-21T00:00:00.000Z", + "completed":"2015-09-21T00:00:00.000Z", + "new_balance":"206.77", + "value":"-53.37" + } + },{ + "id":"70e76553-0282-4424-9b9b-e463ee0e3d26", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Donation Page Family" + }, + "details":{ + "type":"Paid in", + "description":"", + "posted":"2015-09-21T00:00:00.000Z", + "completed":"2015-09-21T00:00:00.000Z", + "new_balance":"262.20", + "value":"55.43" + } + },{ + "id":"9fd82db7-b69b-4ce8-b89b-95a4ee5300f2", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Royal Edinburgh hospital" + }, + "details":{ + "type":"Transfer", + "description":"", + "posted":"2015-09-24T00:00:00.000Z", + "completed":"2015-09-24T00:00:00.000Z", + "new_balance":"-357.39", + "value":"-619.59" + } + },{ + "id":"18841ccc-fc60-4f12-9b9b-dc244ab5fe44", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Donation Mr and Mrs Waterhouse" + }, + "details":{ + "type":"Paid in", + "description":"", + "posted":"2015-09-24T00:00:00.000Z", + "completed":"2015-09-24T00:00:00.000Z", + "new_balance":"-82.61", + "value":"274.78" + } + },{ + "id":"284e6451-3e79-41cc-97b3-e0d8bd856903", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Donation The Union" + }, + "details":{ + "type":"Paid in", + "description":"", + "posted":"2015-09-25T00:00:00.000Z", + "completed":"2015-09-25T00:00:00.000Z", + "new_balance":"394.69", + "value":"477.30" + } + },{ + "id":"10e48400-9476-4898-bdf7-9d6e956d1f31", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Rainbow hospice" + }, + "details":{ + "type":"Transfer", + "description":"", + "posted":"2015-09-25T00:00:00.000Z", + "completed":"2015-09-25T00:00:00.000Z", + "new_balance":"-554.53", + "value":"-949.22" + } + },{ + "id":"1aee29cc-1e09-413b-ba89-a875d4f01b80", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Donation Mr James" + }, + "details":{ + "type":"Paid in", + "description":"", + "posted":"2015-09-28T00:00:00.000Z", + "completed":"2015-09-28T00:00:00.000Z", + "new_balance":"1628.56", + "value":"2183.09" + } + },{ + "id":"6e2f67af-8a38-4ec9-a9b1-291286371ec7", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Donation The Keepers inn" + }, + "details":{ + "type":"Paid in", + "description":"", + "posted":"2015-09-28T00:00:00.000Z", + "completed":"2015-09-28T00:00:00.000Z", + "new_balance":"6148.90", + "value":"4520.34" + } + },{ + "id":"7f9fffda-5dd7-4810-9eff-b49ba27e79ec", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Donation Smiles" + }, + "details":{ + "type":"Paid in", + "description":"", + "posted":"2015-09-30T00:00:00.000Z", + "completed":"2015-09-30T00:00:00.000Z", + "new_balance":"6423.68", + "value":"274.78" + } + },{ + "id":"04135e2b-6489-4d56-bfb5-f7bcc8c4d3d1", + "this_account":{ + "id":"e4f001fe-0f0d-4f93-a8b2-d865077315ec", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Donation Miss Heath" + }, + "details":{ + "type":"Paid in", + "description":"", + "posted":"2015-09-30T00:00:00.000Z", + "completed":"2015-09-30T00:00:00.000Z", + "new_balance":"6662.05", + "value":"238.37" + } + },{ + "id":"48242826-7765-49a8-ab9e-69c58c3e1acb", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Salary" + }, + "details":{ + "type":"Paid in", + "description":"Salary", + "posted":"2015-07-01T00:00:00.000Z", + "completed":"2015-07-01T00:00:00.000Z", + "new_balance":"3276.25", + "value":"1192.31" + } + },{ + "id":"7ce3ff34-1c52-4401-909c-d6d71c8bfa21", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Salary" + }, + "details":{ + "type":"Paid In", + "description":"Salary", + "posted":"2015-07-01T00:00:00.000Z", + "completed":"2015-07-01T00:00:00.000Z", + "new_balance":"5547.90", + "value":"2271.65" + } + },{ + "id":"20f346a9-855f-4c3b-b961-211ab6e38b68", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Car Plan" + }, + "details":{ + "type":"Direct Debit", + "description":"Car Monthly Payment", + "posted":"2015-07-03T00:00:00.000Z", + "completed":"2015-07-03T00:00:00.000Z", + "new_balance":"5237.96", + "value":"-309.94" + } + },{ + "id":"225866a6-e6ac-4f6b-b9b2-951e7d55b1ef", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"New Look" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-04T00:00:00.000Z", + "completed":"2015-07-04T00:00:00.000Z", + "new_balance":"5197.61", + "value":"-40.35" + } + },{ + "id":"03c777d8-6d68-4c10-b5c1-7a183460cbf3", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Burger King" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-07-04T00:00:00.000Z", + "completed":"2015-07-04T00:00:00.000Z", + "new_balance":"5188.76", + "value":"-8.85" + } + },{ + "id":"5d07321d-33ff-4659-8c51-8056f27eb5f9", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Revolution Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-07-04T00:00:00.000Z", + "completed":"2015-07-04T00:00:00.000Z", + "new_balance":"5164.25", + "value":"-24.51" + } + },{ + "id":"97258c22-d49e-4246-bf5e-d54fe17ff577", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-07-05T00:00:00.000Z", + "completed":"2015-07-05T00:00:00.000Z", + "new_balance":"5154.05", + "value":"-10.20" + } + },{ + "id":"76677ba7-e0f2-4ea5-bf13-2faf88635894", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-07-07T00:00:00.000Z", + "completed":"2015-07-07T00:00:00.000Z", + "new_balance":"5148.91", + "value":"-5.14" + } + },{ + "id":"7fb7374c-7ea9-41f4-9a8d-a3dddd6fcdcc", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"The Body Shop" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-11T00:00:00.000Z", + "completed":"2015-07-11T00:00:00.000Z", + "new_balance":"5136.24", + "value":"-12.67" + } + },{ + "id":"0cd1836a-658c-4797-9772-a94f657ffb05", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Boots Plc" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-11T00:00:00.000Z", + "completed":"2015-07-11T00:00:00.000Z", + "new_balance":"5116.22", + "value":"-20.02" + } + },{ + "id":"1ad7601b-8f3f-4ee3-bdee-1c85a66d6749", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Nail Art" + }, + "details":{ + "type":"Debit Card", + "description":"Nails", + "posted":"2015-07-11T00:00:00.000Z", + "completed":"2015-07-11T00:00:00.000Z", + "new_balance":"5091.39", + "value":"-24.83" + } + },{ + "id":"323c7a7d-7c8f-45f7-a41e-db199b6bb840", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Terrance Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-07-11T00:00:00.000Z", + "completed":"2015-07-11T00:00:00.000Z", + "new_balance":"5080.57", + "value":"-10.82" + } + },{ + "id":"2db52f7f-8b87-4585-8b24-e06c88aff46d", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Zizzi" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-07-11T00:00:00.000Z", + "completed":"2015-07-11T00:00:00.000Z", + "new_balance":"5050.19", + "value":"-30.38" + } + },{ + "id":"8a586223-85f4-4be1-ba44-62f27e096644", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Texaco Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling Station", + "posted":"2015-07-13T00:00:00.000Z", + "completed":"2015-07-13T00:00:00.000Z", + "new_balance":"4989.21", + "value":"-60.98" + } + },{ + "id":"bec9defa-7cdb-4346-8097-27b889707c86", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Texaco Filling Station ATM" + }, + "details":{ + "type":"Debit Card", + "description":"Cash Withdrawals", + "posted":"2015-07-13T00:00:00.000Z", + "completed":"2015-07-13T00:00:00.000Z", + "new_balance":"4904.30", + "value":"-84.91" + } + },{ + "id":"73352def-b83c-4c53-aa4f-4ca2c2879bcd", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"The White Horse" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-07-16T00:00:00.000Z", + "completed":"2015-07-16T00:00:00.000Z", + "new_balance":"4858.69", + "value":"-45.61" + } + },{ + "id":"0cc14465-5061-4c85-9eb6-9d4c83a60d40", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-07-17T00:00:00.000Z", + "completed":"2015-07-17T00:00:00.000Z", + "new_balance":"4851.29", + "value":"-7.40" + } + },{ + "id":"8815b4b6-a874-486a-a227-aa4aaddc11e2", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Waitrose" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-17T00:00:00.000Z", + "completed":"2015-07-17T00:00:00.000Z", + "new_balance":"4841.79", + "value":"-9.50" + } + },{ + "id":"2392d4b6-3657-4cc2-910f-7797f1045405", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"The Salon" + }, + "details":{ + "type":"Debit Card", + "description":"Hair", + "posted":"2015-07-19T00:00:00.000Z", + "completed":"2015-07-19T00:00:00.000Z", + "new_balance":"4764.50", + "value":"-77.29" + } + },{ + "id":"c7610246-9e56-47c3-b9bd-c51025ea913d", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Revolution Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-07-21T00:00:00.000Z", + "completed":"2015-07-21T00:00:00.000Z", + "new_balance":"4741.02", + "value":"-23.48" + } + },{ + "id":"de343718-f13d-4642-ab2c-5489cc30868c", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"The White Horse" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-07-23T00:00:00.000Z", + "completed":"2015-07-23T00:00:00.000Z", + "new_balance":"4703.02", + "value":"-38.00" + } + },{ + "id":"41106e95-7e1a-4a88-9a35-52b551ec44ef", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Boots Plc" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-25T00:00:00.000Z", + "completed":"2015-07-25T00:00:00.000Z", + "new_balance":"4688.21", + "value":"-14.81" + } + },{ + "id":"33c6a107-156d-4520-a8c1-57fda9eb99d1", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-07-27T00:00:00.000Z", + "completed":"2015-07-27T00:00:00.000Z", + "new_balance":"4683.07", + "value":"-5.14" + } + },{ + "id":"4ce3b989-a5d4-4811-8ef4-68a0210025a2", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Orange Mobile" + }, + "details":{ + "type":"Direct Debit", + "description":"Mobile", + "posted":"2015-07-28T00:00:00.000Z", + "completed":"2015-07-28T00:00:00.000Z", + "new_balance":"4633.14", + "value":"-49.93" + } + },{ + "id":"4aa788f4-c1df-4d13-98f1-f6f55cd00da8", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Savings" + }, + "details":{ + "type":"Standing Order", + "description":"Saving", + "posted":"2015-07-28T00:00:00.000Z", + "completed":"2015-07-28T00:00:00.000Z", + "new_balance":"4405.10", + "value":"-228.04" + } + },{ + "id":"8b57174e-35d5-488c-b72e-9285ef62fe19", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Salary" + }, + "details":{ + "type":"Paid in", + "description":"Salary", + "posted":"2015-08-01T00:00:00.000Z", + "completed":"2015-08-01T00:00:00.000Z", + "new_balance":"5597.41", + "value":"1192.31" + } + },{ + "id":"2f2c19e6-ba2b-4a9d-98a0-4ffecbb3f083", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Car Plan" + }, + "details":{ + "type":"Direct Debit", + "description":"Car Monthly Payment", + "posted":"2015-08-03T00:00:00.000Z", + "completed":"2015-08-03T00:00:00.000Z", + "new_balance":"5287.47", + "value":"-309.94" + } + },{ + "id":"c28e16be-d221-45eb-a16c-920fbe09dd9f", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"High street ATM" + }, + "details":{ + "type":"Cash withdrawal", + "description":"Cash Withdrawals", + "posted":"2015-08-03T00:00:00.000Z", + "completed":"2015-08-03T00:00:00.000Z", + "new_balance":"5172.88", + "value":"-114.59" + } + },{ + "id":"faef693a-756e-4d42-930d-33601c7ee83f", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-08-04T00:00:00.000Z", + "completed":"2015-08-04T00:00:00.000Z", + "new_balance":"5158.65", + "value":"-14.23" + } + },{ + "id":"033e07d6-c895-4201-95b9-6e0c8b65a66a", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Revolution Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-08-07T00:00:00.000Z", + "completed":"2015-08-07T00:00:00.000Z", + "new_balance":"5134.39", + "value":"-24.26" + } + },{ + "id":"5ce4b4d0-042c-4d86-8685-d9fa53ff320e", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Zizzi" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-08-07T00:00:00.000Z", + "completed":"2015-08-07T00:00:00.000Z", + "new_balance":"5104.01", + "value":"-30.38" + } + },{ + "id":"50fb157b-f4f0-4ed3-a33a-43410d65c476", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Netflix" + }, + "details":{ + "type":"Debit Card", + "description":"Monthly Netflix Membership", + "posted":"2015-08-07T00:00:00.000Z", + "completed":"2015-08-07T00:00:00.000Z", + "new_balance":"5096.95", + "value":"-7.06" + } + },{ + "id":"fa626c9f-4813-40f1-aec0-1adf60940de6", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Topshop" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-08T00:00:00.000Z", + "completed":"2015-08-08T00:00:00.000Z", + "new_balance":"5041.07", + "value":"-55.88" + } + },{ + "id":"07d4635a-4358-4c29-8e41-1b37b69e3e68", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Hand Made Burger Company" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-08-08T00:00:00.000Z", + "completed":"2015-08-08T00:00:00.000Z", + "new_balance":"5003.23", + "value":"-37.84" + } + },{ + "id":"27983a4c-9485-4670-bc02-7900c258e152", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"High street ATM" + }, + "details":{ + "type":"Cash withdrawal", + "description":"Cash Withdrawals", + "posted":"2015-08-08T00:00:00.000Z", + "completed":"2015-08-08T00:00:00.000Z", + "new_balance":"4918.32", + "value":"-84.91" + } + },{ + "id":"4760d1dc-a55f-4241-9f43-55d6b8023700", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Waitrose" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-09T00:00:00.000Z", + "completed":"2015-08-09T00:00:00.000Z", + "new_balance":"4892.71", + "value":"-25.61" + } + },{ + "id":"5c917149-dd6d-42d9-bc19-d539392a6461", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-08-11T00:00:00.000Z", + "completed":"2015-08-11T00:00:00.000Z", + "new_balance":"4887.57", + "value":"-5.14" + } + },{ + "id":"316b4b70-7bdd-4ceb-aa3b-2f893f936a1f", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Terrance Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-08-14T00:00:00.000Z", + "completed":"2015-08-14T00:00:00.000Z", + "new_balance":"4853.62", + "value":"-33.95" + } + },{ + "id":"a4084c76-ce31-4ea8-b4da-47ee0242dae7", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"The White Horse" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-08-15T00:00:00.000Z", + "completed":"2015-08-15T00:00:00.000Z", + "new_balance":"4823.24", + "value":"-30.38" + } + },{ + "id":"eeea5a0a-25c2-4c5a-ad2f-4cc7b6a09dc0", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Texaco Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling Station", + "posted":"2015-08-17T00:00:00.000Z", + "completed":"2015-08-17T00:00:00.000Z", + "new_balance":"4757.68", + "value":"-65.56" + } + },{ + "id":"73e7fb56-78fb-4f8c-b243-484a0c9bf119", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-08-17T00:00:00.000Z", + "completed":"2015-08-17T00:00:00.000Z", + "new_balance":"4750.28", + "value":"-7.40" + } + },{ + "id":"15eca993-1388-445a-8829-cde8007d1f7c", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Aiport Boots Plc" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-19T00:00:00.000Z", + "completed":"2015-08-19T00:00:00.000Z", + "new_balance":"4742.54", + "value":"-7.74" + } + },{ + "id":"58550700-1f7d-4dbe-a29d-37cee2c2c49b", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Orange Mobile" + }, + "details":{ + "type":"Direct Debit", + "description":"Mobile", + "posted":"2015-08-28T00:00:00.000Z", + "completed":"2015-08-28T00:00:00.000Z", + "new_balance":"4692.61", + "value":"-49.93" + } + },{ + "id":"3bd48297-b26f-49ab-b7c1-98d26f70cd25", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Savings" + }, + "details":{ + "type":"Standing Order", + "description":"Saving", + "posted":"2015-08-28T00:00:00.000Z", + "completed":"2015-08-28T00:00:00.000Z", + "new_balance":"4464.57", + "value":"-228.04" + } + },{ + "id":"b7232c34-7cd4-4881-a453-e250baee1655", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Salary" + }, + "details":{ + "type":"Paid in", + "description":"Salary", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"5656.88", + "value":"1192.31" + } + },{ + "id":"bfc66595-4215-4298-82d3-42c0c4201d77", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Car Plan" + }, + "details":{ + "type":"Direct Debit", + "description":"Car Monthly Payment", + "posted":"2015-09-03T00:00:00.000Z", + "completed":"2015-09-03T00:00:00.000Z", + "new_balance":"5346.94", + "value":"-309.94" + } + },{ + "id":"d39f65f8-09fc-42b3-90db-fa0ce4f89317", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Zara" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-04T00:00:00.000Z", + "completed":"2015-09-04T00:00:00.000Z", + "new_balance":"5281.38", + "value":"-65.56" + } + },{ + "id":"85c0e6dc-b922-4cab-afa4-e75781954f60", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Boots Plc" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-04T00:00:00.000Z", + "completed":"2015-09-04T00:00:00.000Z", + "new_balance":"5239.89", + "value":"-41.49" + } + },{ + "id":"cb485e1c-1941-4fa7-aba5-df0913ab7990", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Revolution Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-09-04T00:00:00.000Z", + "completed":"2015-09-04T00:00:00.000Z", + "new_balance":"5216.58", + "value":"-23.31" + } + },{ + "id":"036ac198-52c4-4b6b-9d01-c6d8b6157848", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Zizzi" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-09-04T00:00:00.000Z", + "completed":"2015-09-04T00:00:00.000Z", + "new_balance":"5178.58", + "value":"-38.00" + } + },{ + "id":"04c10d10-914a-4cb1-bec7-c335d048528d", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-09-05T00:00:00.000Z", + "completed":"2015-09-05T00:00:00.000Z", + "new_balance":"5171.18", + "value":"-7.40" + } + },{ + "id":"5ab85893-d3ce-4c22-b059-a46e795bf7ed", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Ticket Master" + }, + "details":{ + "type":"Debit Card", + "description":"Tickets", + "posted":"2015-09-05T00:00:00.000Z", + "completed":"2015-09-05T00:00:00.000Z", + "new_balance":"5104.35", + "value":"-66.83" + } + },{ + "id":"206c9b33-1aee-4c7e-8ebb-9d3d94a6833f", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"The Salon" + }, + "details":{ + "type":"Debit Card", + "description":"Hair", + "posted":"2015-09-05T00:00:00.000Z", + "completed":"2015-09-05T00:00:00.000Z", + "new_balance":"5025.45", + "value":"-78.90" + } + },{ + "id":"e5f3d1a5-ef1b-497f-ab1d-d65185771b82", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Texaco Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling Station", + "posted":"2015-09-05T00:00:00.000Z", + "completed":"2015-09-05T00:00:00.000Z", + "new_balance":"4980.20", + "value":"-45.25" + } + },{ + "id":"cb4b2cc8-f64a-4b45-87df-67f649e94575", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"High Street ATM" + }, + "details":{ + "type":"Cash Withdrawal", + "description":"Cash Withdrawals", + "posted":"2015-09-05T00:00:00.000Z", + "completed":"2015-09-05T00:00:00.000Z", + "new_balance":"4895.29", + "value":"-84.91" + } + },{ + "id":"8ae09f1d-fd03-4fb9-b647-c757dfceecc9", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Showcase Cinema" + }, + "details":{ + "type":"Debit Card", + "description":"Cinema", + "posted":"2015-09-05T00:00:00.000Z", + "completed":"2015-09-05T00:00:00.000Z", + "new_balance":"4885.40", + "value":"-9.89" + } + },{ + "id":"14d985be-60e6-4eb5-bfb5-920b1bf2fd82", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Netflix" + }, + "details":{ + "type":"Direct Debit", + "description":"Monthly Netflix Membership", + "posted":"2015-09-07T00:00:00.000Z", + "completed":"2015-09-07T00:00:00.000Z", + "new_balance":"4879.94", + "value":"-5.46" + } + },{ + "id":"7b6b97b4-4520-4cbc-b309-51f572ac75e7", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-09-07T00:00:00.000Z", + "completed":"2015-09-07T00:00:00.000Z", + "new_balance":"4872.54", + "value":"-7.40" + } + },{ + "id":"cac6f521-3c79-43ca-844a-e2dbdf4d480a", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Waitrose" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-07T00:00:00.000Z", + "completed":"2015-09-07T00:00:00.000Z", + "new_balance":"4863.87", + "value":"-8.67" + } + },{ + "id":"2c41b720-c180-4dea-acbf-2378f5187101", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Netflix" + }, + "details":{ + "type":"Direct Debit", + "description":"Monthly Netflix Membership", + "posted":"2015-09-07T00:00:00.000Z", + "completed":"2015-09-07T00:00:00.000Z", + "new_balance":"4858.41", + "value":"-5.46" + } + },{ + "id":"4d718642-c5d2-4959-b76b-099ef73f0565", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Sainsbury's" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-10T00:00:00.000Z", + "completed":"2015-09-10T00:00:00.000Z", + "new_balance":"4824.33", + "value":"-34.08" + } + },{ + "id":"f0258d7a-9aa8-4f97-8119-f916c1740293", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-09-11T00:00:00.000Z", + "completed":"2015-09-11T00:00:00.000Z", + "new_balance":"4816.93", + "value":"-7.40" + } + },{ + "id":"79ffd739-82c4-4943-8e9a-d91d1f87b759", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Nando's" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-09-11T00:00:00.000Z", + "completed":"2015-09-11T00:00:00.000Z", + "new_balance":"4802.36", + "value":"-14.57" + } + },{ + "id":"9ebfcc39-6a56-46e9-9ca9-f2517c7003ef", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Hollywood Bolwing" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-09-11T00:00:00.000Z", + "completed":"2015-09-11T00:00:00.000Z", + "new_balance":"4780.11", + "value":"-22.25" + } + },{ + "id":"9f3e3bb7-e4fa-498e-9adc-e79b0d39bbb6", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Nail Art" + }, + "details":{ + "type":"Debit Card", + "description":"Nails", + "posted":"2015-09-12T00:00:00.000Z", + "completed":"2015-09-12T00:00:00.000Z", + "new_balance":"4755.28", + "value":"-24.83" + } + },{ + "id":"c7c65ffb-f8d6-4b2b-8e9d-42616bff9a10", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-09-12T00:00:00.000Z", + "completed":"2015-09-12T00:00:00.000Z", + "new_balance":"4747.88", + "value":"-7.40" + } + },{ + "id":"bc19de22-6f42-466a-b290-8427dfd16687", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"All Staints" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-12T00:00:00.000Z", + "completed":"2015-09-12T00:00:00.000Z", + "new_balance":"4597.06", + "value":"-150.82" + } + },{ + "id":"8f644f54-a21e-483b-8248-d2730b47c38e", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Topshop" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-12T00:00:00.000Z", + "completed":"2015-09-12T00:00:00.000Z", + "new_balance":"4522.81", + "value":"-74.25" + } + },{ + "id":"b76425df-902a-4ff9-ab47-7592d95c6ead", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"The White Horse" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-09-13T00:00:00.000Z", + "completed":"2015-09-13T00:00:00.000Z", + "new_balance":"4482.65", + "value":"-40.16" + } + },{ + "id":"f3b9e5da-7905-48fc-8369-3787c4d03978", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-09-14T00:00:00.000Z", + "completed":"2015-09-14T00:00:00.000Z", + "new_balance":"4475.25", + "value":"-7.40" + } + },{ + "id":"d17a509d-2043-4554-a407-be894c5c97fb", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Waitrose" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-14T00:00:00.000Z", + "completed":"2015-09-14T00:00:00.000Z", + "new_balance":"4436.48", + "value":"-38.77" + } + },{ + "id":"d5a82482-9043-465e-b3f2-eaa1e6c57a98", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Hong Wang" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant/Takeway", + "posted":"2015-09-16T00:00:00.000Z", + "completed":"2015-09-16T00:00:00.000Z", + "new_balance":"4386.20", + "value":"-50.28" + } + },{ + "id":"edbc49de-d8e4-4951-a067-65f0763f5c2b", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Amazon" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-18T00:00:00.000Z", + "completed":"2015-09-18T00:00:00.000Z", + "new_balance":"4346.09", + "value":"-40.11" + } + },{ + "id":"8f096333-d27a-4463-9523-28df9676771f", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Booking.com" + }, + "details":{ + "type":"Debit Card", + "description":"Booking.com", + "posted":"2015-09-21T00:00:00.000Z", + "completed":"2015-09-21T00:00:00.000Z", + "new_balance":"4266.30", + "value":"-79.79" + } + },{ + "id":"becd9f84-7a78-46a3-aeec-727bc2f44daa", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Easyjet" + }, + "details":{ + "type":"Debit Card", + "description":"Flights", + "posted":"2015-09-21T00:00:00.000Z", + "completed":"2015-09-21T00:00:00.000Z", + "new_balance":"4150.28", + "value":"-116.02" + } + },{ + "id":"eb58a697-44fe-4ab7-9c0d-54b96770a6d0", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Pizza Express" + }, + "details":{ + "type":"Debit Card", + "description":"Resaurant", + "posted":"2015-09-23T00:00:00.000Z", + "completed":"2015-09-23T00:00:00.000Z", + "new_balance":"4121.62", + "value":"-28.66" + } + },{ + "id":"6d5f3107-de02-4761-8b9f-2c23c8954ff1", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-09-24T00:00:00.000Z", + "completed":"2015-09-24T00:00:00.000Z", + "new_balance":"4114.22", + "value":"-7.40" + } + },{ + "id":"12fef7e4-57b7-426f-91d1-1d5dfb032057", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Terrace Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-09-26T00:00:00.000Z", + "completed":"2015-09-26T00:00:00.000Z", + "new_balance":"4090.99", + "value":"-23.23" + } + },{ + "id":"1882d7cd-430a-4805-94f0-e9d4e2b9d988", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Revolution Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-09-26T00:00:00.000Z", + "completed":"2015-09-26T00:00:00.000Z", + "new_balance":"4075.08", + "value":"-15.91" + } + },{ + "id":"b6288b17-dbcf-4fab-b529-a1f8b52ddde6", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"ABC Taxi" + }, + "details":{ + "type":"Debit Card", + "description":"Taxi", + "posted":"2015-09-26T00:00:00.000Z", + "completed":"2015-09-26T00:00:00.000Z", + "new_balance":"4060.32", + "value":"-14.76" + } + },{ + "id":"30f1f733-8785-4b96-87c7-402a473cf102", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-09-27T00:00:00.000Z", + "completed":"2015-09-27T00:00:00.000Z", + "new_balance":"4045.55", + "value":"-14.77" + } + },{ + "id":"1d7567e0-ceb2-408d-843d-e47175a53213", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Orange Mobile" + }, + "details":{ + "type":"Direct Debit", + "description":"Mobile", + "posted":"2015-09-28T00:00:00.000Z", + "completed":"2015-09-28T00:00:00.000Z", + "new_balance":"3995.62", + "value":"-49.93" + } + },{ + "id":"101e2c18-a8e8-4da0-bf6c-b30cd2c18c4b", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Savings" + }, + "details":{ + "type":"Standing Order", + "description":"Saving", + "posted":"2015-09-28T00:00:00.000Z", + "completed":"2015-09-28T00:00:00.000Z", + "new_balance":"3767.58", + "value":"-228.04" + } + },{ + "id":"36fdda56-0db8-4ba4-8fdd-a1b17b48440d", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"ING Mortgage" + }, + "details":{ + "type":"Direct Debit", + "description":"Mortgage", + "posted":"2015-07-01T00:00:00.000Z", + "completed":"2015-07-01T00:00:00.000Z", + "new_balance":"7744.26", + "value":"-627.15" + } + },{ + "id":"9cc68508-797a-4a55-9cc8-7a6479e4b00c", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Directline" + }, + "details":{ + "type":"Direct Debit", + "description":"Insurance", + "posted":"2015-07-01T00:00:00.000Z", + "completed":"2015-07-01T00:00:00.000Z", + "new_balance":"7711.34", + "value":"-32.92" + } + },{ + "id":"1b0464b6-29ed-4187-9df3-07ea5af37bd2", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Seven Trent Water" + }, + "details":{ + "type":"Direct Debit", + "description":"Water", + "posted":"2015-07-01T00:00:00.000Z", + "completed":"2015-07-01T00:00:00.000Z", + "new_balance":"7687.24", + "value":"-24.10" + } + },{ + "id":"fac683d5-96e0-426c-8b80-7858bfb79316", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"SSE" + }, + "details":{ + "type":"Direct Debit", + "description":"Gas/Elec", + "posted":"2015-07-01T00:00:00.000Z", + "completed":"2015-07-01T00:00:00.000Z", + "new_balance":"7635.47", + "value":"-51.77" + } + },{ + "id":"ed709ff1-bf16-46ed-b7c0-32404e803ecf", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Council Tax" + }, + "details":{ + "type":"Direct Debit", + "description":"Council Tax", + "posted":"2015-07-01T00:00:00.000Z", + "completed":"2015-07-01T00:00:00.000Z", + "new_balance":"7524.91", + "value":"-110.56" + } + },{ + "id":"930c78de-502b-4c2b-8df3-4916c4d92e89", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Sky" + }, + "details":{ + "type":"Direct Debit", + "description":"Monthly Sky", + "posted":"2015-07-02T00:00:00.000Z", + "completed":"2015-07-02T00:00:00.000Z", + "new_balance":"7466.31", + "value":"-58.60" + } + },{ + "id":"ac793c69-3c6e-4f51-83c2-604c093a6ff6", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Bambo Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-07-03T00:00:00.000Z", + "completed":"2015-07-03T00:00:00.000Z", + "new_balance":"7376.23", + "value":"-90.08" + } + },{ + "id":"b3181fee-9f33-47fc-b034-e57bca1fe9bb", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Takeway King" + }, + "details":{ + "type":"Debit Card", + "description":"Takeway", + "posted":"2015-07-04T00:00:00.000Z", + "completed":"2015-07-04T00:00:00.000Z", + "new_balance":"7353.90", + "value":"-22.33" + } + },{ + "id":"04ee593d-5020-406c-a1db-2e08aa268448", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Tesco" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-05T00:00:00.000Z", + "completed":"2015-07-05T00:00:00.000Z", + "new_balance":"7304.39", + "value":"-49.51" + } + },{ + "id":"6842199c-695a-41b5-b3db-e8478b23c5bb", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Tesco Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling Station", + "posted":"2015-07-05T00:00:00.000Z", + "completed":"2015-07-05T00:00:00.000Z", + "new_balance":"7251.02", + "value":"-53.37" + } + },{ + "id":"f2a79b8e-9598-4dcd-988b-b448a9517a4f", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"coffee", + "posted":"2015-07-06T00:00:00.000Z", + "completed":"2015-07-06T00:00:00.000Z", + "new_balance":"7247.04", + "value":"-3.98" + } + },{ + "id":"1d25a716-e928-4bbc-8c18-d234e8005813", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Liquid" + }, + "details":{ + "type":"Debit Card", + "description":"Club", + "posted":"2015-07-11T00:00:00.000Z", + "completed":"2015-07-11T00:00:00.000Z", + "new_balance":"7204.43", + "value":"-42.61" + } + },{ + "id":"c497d4c5-8504-4e31-9eca-f26cfeb27835", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"The choach and horses" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-07-12T00:00:00.000Z", + "completed":"2015-07-12T00:00:00.000Z", + "new_balance":"7174.55", + "value":"-29.88" + } + },{ + "id":"7d8b3c0f-7faf-451a-a7f3-ea3d51fb732b", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Amazon" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-14T00:00:00.000Z", + "completed":"2015-07-14T00:00:00.000Z", + "new_balance":"7157.89", + "value":"-16.66" + } + },{ + "id":"f711d9c2-b458-41b4-a4ae-2a50e7e66d10", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"coffee", + "posted":"2015-07-16T00:00:00.000Z", + "completed":"2015-07-16T00:00:00.000Z", + "new_balance":"7151.74", + "value":"-6.15" + } + },{ + "id":"9443ef18-9447-4a71-a7ac-2c07ec64310e", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Bambo Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-07-18T00:00:00.000Z", + "completed":"2015-07-18T00:00:00.000Z", + "new_balance":"7119.44", + "value":"-32.30" + } + },{ + "id":"e0b1e713-8b57-4f9e-abd5-32cc2571cdb5", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"The sandwich Factory" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant/Takeway", + "posted":"2015-07-20T00:00:00.000Z", + "completed":"2015-07-20T00:00:00.000Z", + "new_balance":"7113.01", + "value":"-6.43" + } + },{ + "id":"74f556c8-a5c0-46d5-907a-8d255d5e7fb5", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"High Street ATM" + }, + "details":{ + "type":"Cash Withdrawal", + "description":"Cash Withdrawal", + "posted":"2015-07-20T00:00:00.000Z", + "completed":"2015-07-20T00:00:00.000Z", + "new_balance":"6868.31", + "value":"-244.70" + } + },{ + "id":"617f3ae3-cde0-4467-b35e-b752448ec8f8", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Champagne Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-07-24T00:00:00.000Z", + "completed":"2015-07-24T00:00:00.000Z", + "new_balance":"6827.28", + "value":"-41.03" + } + },{ + "id":"51aa7fc4-e11f-4c20-94ea-96a2883157e1", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Quick Bit" + }, + "details":{ + "type":"Debit Card", + "description":"Takeway", + "posted":"2015-07-24T00:00:00.000Z", + "completed":"2015-07-24T00:00:00.000Z", + "new_balance":"6817.11", + "value":"-10.17" + } + },{ + "id":"d5ec7a5e-5cbc-488d-8338-3790f8e0d6ca", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Nando's" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-07-25T00:00:00.000Z", + "completed":"2015-07-25T00:00:00.000Z", + "new_balance":"6785.64", + "value":"-31.47" + } + },{ + "id":"335abcd1-048b-4513-b145-44a91c213e19", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Tesco" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-25T00:00:00.000Z", + "completed":"2015-07-25T00:00:00.000Z", + "new_balance":"6730.64", + "value":"-55.00" + } + },{ + "id":"7b22e014-cb52-4558-86ec-ee5da6d299e7", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Tesco Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling Station", + "posted":"2015-07-26T00:00:00.000Z", + "completed":"2015-07-26T00:00:00.000Z", + "new_balance":"6656.86", + "value":"-73.78" + } + },{ + "id":"c6fa0179-2409-4c6b-986a-54fb35071d71", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"coffee", + "posted":"2015-07-27T00:00:00.000Z", + "completed":"2015-07-27T00:00:00.000Z", + "new_balance":"6650.43", + "value":"-6.43" + } + },{ + "id":"04cc24fa-a8e7-41a7-8720-1d86d9961bd9", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Salary" + }, + "details":{ + "type":"Paid In", + "description":"Salary", + "posted":"2015-08-01T00:00:00.000Z", + "completed":"2015-08-01T00:00:00.000Z", + "new_balance":"8922.08", + "value":"2271.65" + } + },{ + "id":"97104c64-bdf6-476f-a716-88e79492a990", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"ING Mortgage" + }, + "details":{ + "type":"Direct Debit", + "description":"Mortgage", + "posted":"2015-08-01T00:00:00.000Z", + "completed":"2015-08-01T00:00:00.000Z", + "new_balance":"8294.93", + "value":"-627.15" + } + },{ + "id":"69b32565-d311-40da-9a56-6bd6cfe8f28d", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Directline" + }, + "details":{ + "type":"Direct Debit", + "description":"Insurance", + "posted":"2015-08-01T00:00:00.000Z", + "completed":"2015-08-01T00:00:00.000Z", + "new_balance":"8262.01", + "value":"-32.92" + } + },{ + "id":"bc7a71f0-9787-44fa-aa97-2d1e77744ddb", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Seven Trent Water" + }, + "details":{ + "type":"Direct Debit", + "description":"Water", + "posted":"2015-08-01T00:00:00.000Z", + "completed":"2015-08-01T00:00:00.000Z", + "new_balance":"8237.91", + "value":"-24.10" + } + },{ + "id":"3527f74e-de3c-4b9a-a1e0-531daa5a30e4", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"SSE" + }, + "details":{ + "type":"Direct Debit", + "description":"Gas/Elec", + "posted":"2015-08-01T00:00:00.000Z", + "completed":"2015-08-01T00:00:00.000Z", + "new_balance":"8186.14", + "value":"-51.77" + } + },{ + "id":"1c174f68-a811-4348-9958-e471425c7920", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Council Tax" + }, + "details":{ + "type":"Direct Debit", + "description":"Council Tax", + "posted":"2015-08-01T00:00:00.000Z", + "completed":"2015-08-01T00:00:00.000Z", + "new_balance":"8075.58", + "value":"-110.56" + } + },{ + "id":"f53d4f1d-3ce3-427e-9f94-f569baf60d88", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Sky" + }, + "details":{ + "type":"Direct Debit", + "description":"Monthly Sky", + "posted":"2015-08-02T00:00:00.000Z", + "completed":"2015-08-02T00:00:00.000Z", + "new_balance":"8016.98", + "value":"-58.60" + } + },{ + "id":"84a32d9d-24c5-424e-a7b2-d899f6c829a3", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"coffee", + "posted":"2015-08-03T00:00:00.000Z", + "completed":"2015-08-03T00:00:00.000Z", + "new_balance":"8004.04", + "value":"-12.94" + } + },{ + "id":"876a871b-a9f9-43ee-9d0b-145f2407fce5", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Quick Bit" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant/Takeway", + "posted":"2015-08-04T00:00:00.000Z", + "completed":"2015-08-04T00:00:00.000Z", + "new_balance":"7989.17", + "value":"-14.87" + } + },{ + "id":"2f71edf1-2867-462f-80da-bdf459a1a650", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Nando's" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-08-07T00:00:00.000Z", + "completed":"2015-08-07T00:00:00.000Z", + "new_balance":"7958.03", + "value":"-31.14" + } + },{ + "id":"03d8c6c7-d0cb-4f3b-8e09-a77e7ad5223e", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"The choach and horses" + }, + "details":{ + "type":"Debit Card", + "description":"resturant", + "posted":"2015-08-09T00:00:00.000Z", + "completed":"2015-08-09T00:00:00.000Z", + "new_balance":"7931.53", + "value":"-26.50" + } + },{ + "id":"20040ae8-1204-4bb1-bb3c-b358bb75b721", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Showcase Cinema" + }, + "details":{ + "type":"Debit Card", + "description":"Cinema", + "posted":"2015-08-09T00:00:00.000Z", + "completed":"2015-08-09T00:00:00.000Z", + "new_balance":"7905.92", + "value":"-25.61" + } + },{ + "id":"671d07ce-a7d5-46e0-9b52-f48d11becf97", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Shell Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling Station", + "posted":"2015-08-10T00:00:00.000Z", + "completed":"2015-08-10T00:00:00.000Z", + "new_balance":"7852.55", + "value":"-53.37" + } + },{ + "id":"7d5b2169-2040-44ad-aa2e-af0426e9f64e", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"coffee", + "posted":"2015-08-14T00:00:00.000Z", + "completed":"2015-08-14T00:00:00.000Z", + "new_balance":"7848.44", + "value":"-4.11" + } + },{ + "id":"0cd3b708-3def-4ed8-abdb-2eba23b812dc", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Pizza Express" + }, + "details":{ + "type":"Debit Card", + "description":"restaurant", + "posted":"2015-08-15T00:00:00.000Z", + "completed":"2015-08-15T00:00:00.000Z", + "new_balance":"7827.94", + "value":"-20.50" + } + },{ + "id":"3a243ebd-015e-4264-944c-b743f1230c3b", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Bambo Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-08-15T00:00:00.000Z", + "completed":"2015-08-15T00:00:00.000Z", + "new_balance":"7799.20", + "value":"-28.74" + } + },{ + "id":"94bc6f0d-e8e4-45b5-ba67-5cacd89b6a77", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Dominos" + }, + "details":{ + "type":"Debit Card", + "description":"Takeway", + "posted":"2015-08-16T00:00:00.000Z", + "completed":"2015-08-16T00:00:00.000Z", + "new_balance":"7764.42", + "value":"-34.78" + } + },{ + "id":"ae7795c1-e4fd-4082-a72a-a23d4f76d8b5", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Sainsbury's" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-17T00:00:00.000Z", + "completed":"2015-08-17T00:00:00.000Z", + "new_balance":"7719.85", + "value":"-44.57" + } + },{ + "id":"1970b872-1390-4345-808b-5120e02805da", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"coffee", + "posted":"2015-08-20T00:00:00.000Z", + "completed":"2015-08-20T00:00:00.000Z", + "new_balance":"7712.45", + "value":"-7.40" + } + },{ + "id":"bc920fcd-9b1c-4b80-99f8-fb9afd18f445", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"The sandwich Company" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant/Takeway", + "posted":"2015-08-20T00:00:00.000Z", + "completed":"2015-08-20T00:00:00.000Z", + "new_balance":"7703.78", + "value":"-8.67" + } + },{ + "id":"98aba0d2-9643-44f7-8651-020491e71bdf", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Liquid" + }, + "details":{ + "type":"Debit Card", + "description":"Club", + "posted":"2015-08-22T00:00:00.000Z", + "completed":"2015-08-22T00:00:00.000Z", + "new_balance":"7745.85", + "value":"42.07" + } + },{ + "id":"65655bdc-1734-401e-8b2c-e05be2b60242", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Tesco" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-23T00:00:00.000Z", + "completed":"2015-08-23T00:00:00.000Z", + "new_balance":"7694.40", + "value":"-51.45" + } + },{ + "id":"850fb671-b407-4c3b-b307-be359d8c1cb8", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"The choach and horses" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-08-23T00:00:00.000Z", + "completed":"2015-08-23T00:00:00.000Z", + "new_balance":"7657.10", + "value":"-37.30" + } + },{ + "id":"b72f65c0-b610-4eb1-8c2b-0b6c788869cc", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Bambo Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-08-28T00:00:00.000Z", + "completed":"2015-08-28T00:00:00.000Z", + "new_balance":"7631.66", + "value":"-25.44" + } + },{ + "id":"d2aa1cd0-3671-407f-8569-a981cfdfe1a0", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Hollywood Bowl" + }, + "details":{ + "type":"Debit Card", + "description":"Bowling", + "posted":"2015-08-28T00:00:00.000Z", + "completed":"2015-08-28T00:00:00.000Z", + "new_balance":"7605.07", + "value":"-26.59" + } + },{ + "id":"bf2dfbf0-a6c9-4ff3-a270-30d002c61b7a", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Salary" + }, + "details":{ + "type":"Paid In", + "description":"Salary", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"9876.72", + "value":"2271.65" + } + },{ + "id":"402b68a5-6249-4f84-ac31-fa16fdf47ca1", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"ING Mortgage" + }, + "details":{ + "type":"Direct Debit", + "description":"Mortgage", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"9249.57", + "value":"-627.15" + } + },{ + "id":"40bdfcdc-da8d-4f7b-a04a-1e524d7a3c5d", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Directline" + }, + "details":{ + "type":"Direct Debit", + "description":"Insurance", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"9216.65", + "value":"-32.92" + } + },{ + "id":"6cd8cecb-bfaf-419e-b25e-6e93ae06e435", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Seven Trent Water" + }, + "details":{ + "type":"Direct Debit", + "description":"Water", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"9192.55", + "value":"-24.10" + } + },{ + "id":"0d36f82b-f792-4f2b-b799-7bb12037d97f", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"SSE" + }, + "details":{ + "type":"Direct Debit", + "description":"Gas/Elec", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"9140.78", + "value":"-51.77" + } + },{ + "id":"8cc0f345-ffc4-4692-a02b-c8c10bfc8f90", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Council Tax" + }, + "details":{ + "type":"Direct Debit", + "description":"Council Tax", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"9030.22", + "value":"-110.56" + } + },{ + "id":"f13dab21-e762-4dc3-b33e-7275789f5947", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Sky" + }, + "details":{ + "type":"Direct Debit", + "description":"Monthly Sky", + "posted":"2015-09-02T00:00:00.000Z", + "completed":"2015-09-02T00:00:00.000Z", + "new_balance":"8971.62", + "value":"-58.60" + } + },{ + "id":"3e4d81ad-32b6-4a5d-b6fe-4ffff7d1ed40", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Shell Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling Station", + "posted":"2015-09-04T00:00:00.000Z", + "completed":"2015-09-04T00:00:00.000Z", + "new_balance":"8904.40", + "value":"-67.22" + } + },{ + "id":"239dda32-b668-4cca-ad87-73e63580cc7e", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"The Sandwich Factory" + }, + "details":{ + "type":"Debit Card", + "description":"Resaurant/Takeway", + "posted":"2015-09-04T00:00:00.000Z", + "completed":"2015-09-04T00:00:00.000Z", + "new_balance":"8898.72", + "value":"-5.68" + } + },{ + "id":"cb0812d4-8f83-4b1a-84fd-f307c1c5cbd8", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Bambo Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-09-04T00:00:00.000Z", + "completed":"2015-09-04T00:00:00.000Z", + "new_balance":"8860.72", + "value":"-38.00" + } + },{ + "id":"74869ba4-cf3d-40f6-b509-971abeb53816", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Quick Bit" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurtant/Takeway", + "posted":"2015-09-04T00:00:00.000Z", + "completed":"2015-09-04T00:00:00.000Z", + "new_balance":"8850.36", + "value":"-10.36" + } + },{ + "id":"159ad2de-1101-4276-a076-79a63043261b", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"coffee", + "posted":"2015-09-05T00:00:00.000Z", + "completed":"2015-09-05T00:00:00.000Z", + "new_balance":"8846.51", + "value":"-3.85" + } + },{ + "id":"02c3c1d7-8976-4b33-ae3c-8d4ffa4876bd", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Nando's" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-09-05T00:00:00.000Z", + "completed":"2015-09-05T00:00:00.000Z", + "new_balance":"8804.50", + "value":"-42.01" + } + },{ + "id":"af872dbd-6da4-4e22-8046-7d96e7c832db", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Tesco" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-06T00:00:00.000Z", + "completed":"2015-09-06T00:00:00.000Z", + "new_balance":"8746.09", + "value":"-58.41" + } + },{ + "id":"20471b1e-3f8f-47c2-a24b-53ab5fb06ec9", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Harvey Nichols" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-06T00:00:00.000Z", + "completed":"2015-09-06T00:00:00.000Z", + "new_balance":"8678.87", + "value":"-67.22" + } + },{ + "id":"112141a9-303c-4f59-a326-e888ec0256c9", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Tesco" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-08T00:00:00.000Z", + "completed":"2015-09-08T00:00:00.000Z", + "new_balance":"8620.46", + "value":"-58.41" + } + },{ + "id":"bc6b81cf-bf78-45d4-aba8-2aeddc9ade03", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Amazon" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-08T00:00:00.000Z", + "completed":"2015-09-08T00:00:00.000Z", + "new_balance":"8593.23", + "value":"-27.23" + } + },{ + "id":"c8f2fedd-b129-4f99-bbd9-9a507e5f9115", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"coffee", + "posted":"2015-09-10T00:00:00.000Z", + "completed":"2015-09-10T00:00:00.000Z", + "new_balance":"8587.91", + "value":"-5.32" + } + },{ + "id":"798b7a2a-5b17-4d03-846f-7a5eb87ac3ba", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Topman" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-10T00:00:00.000Z", + "completed":"2015-09-10T00:00:00.000Z", + "new_balance":"8507.48", + "value":"-80.43" + } + },{ + "id":"a26de2f2-c946-4cd2-97fc-affee69677c4", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Mcdonalds" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-09-11T00:00:00.000Z", + "completed":"2015-09-11T00:00:00.000Z", + "new_balance":"8500.05", + "value":"-7.43" + } + },{ + "id":"aa136a2f-4bd3-4706-b087-09b10ffb8150", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Nando's" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-09-12T00:00:00.000Z", + "completed":"2015-09-12T00:00:00.000Z", + "new_balance":"8486.43", + "value":"-13.62" + } + },{ + "id":"7bf9e1bd-c6f8-4c11-8390-9234b9cbdf78", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Hollywood Bowling" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-09-12T00:00:00.000Z", + "completed":"2015-09-12T00:00:00.000Z", + "new_balance":"8464.65", + "value":"-21.78" + } + },{ + "id":"bfdead68-9f5a-4b7e-95c8-79effc450b70", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Tesco" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-13T00:00:00.000Z", + "completed":"2015-09-13T00:00:00.000Z", + "new_balance":"8420.78", + "value":"-43.87" + } + },{ + "id":"3c6cd14d-63d3-486d-919e-4c97b340f0ae", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"coffee", + "posted":"2015-09-16T00:00:00.000Z", + "completed":"2015-09-16T00:00:00.000Z", + "new_balance":"8415.64", + "value":"-5.14" + } + },{ + "id":"39ec4a9a-6c1f-4df4-ba52-6c1502938ab3", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Champagne Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-09-17T00:00:00.000Z", + "completed":"2015-09-17T00:00:00.000Z", + "new_balance":"8320.58", + "value":"-95.06" + } + },{ + "id":"d840dfa8-e8df-4c4b-a29e-52a603be0aea", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Pizza King" + }, + "details":{ + "type":"Debit Card", + "description":"Takeway", + "posted":"2015-09-17T00:00:00.000Z", + "completed":"2015-09-17T00:00:00.000Z", + "new_balance":"8280.23", + "value":"-40.35" + } + },{ + "id":"ecd6b2de-239d-47cd-9cec-dfc0136b3ade", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"coffee", + "posted":"2015-09-18T00:00:00.000Z", + "completed":"2015-09-18T00:00:00.000Z", + "new_balance":"8275.09", + "value":"-5.14" + } + },{ + "id":"aeb1d820-9f3f-4fec-8428-2985d4a82423", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Chimichanga" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-09-19T00:00:00.000Z", + "completed":"2015-09-19T00:00:00.000Z", + "new_balance":"8229.78", + "value":"-45.31" + } + },{ + "id":"f2ed38d2-6c01-4721-8e10-216e574fc931", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Liquid" + }, + "details":{ + "type":"Debit Card", + "description":"Club", + "posted":"2015-09-19T00:00:00.000Z", + "completed":"2015-09-19T00:00:00.000Z", + "new_balance":"8170.79", + "value":"-58.99" + } + },{ + "id":"720ceb08-9e49-4bd3-a51c-bbc951bcf61b", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Liquid" + }, + "details":{ + "type":"Debit Card", + "description":"Club", + "posted":"2015-09-19T00:00:00.000Z", + "completed":"2015-09-19T00:00:00.000Z", + "new_balance":"8126.09", + "value":"-44.70" + } + },{ + "id":"5db541ed-9e1d-4438-b4c4-d190a0f75b2d", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"The coach and horses" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-09-20T00:00:00.000Z", + "completed":"2015-09-20T00:00:00.000Z", + "new_balance":"8076.15", + "value":"-49.94" + } + },{ + "id":"d3221c41-cc26-4de8-9d44-3ece2f3048d9", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Dominos" + }, + "details":{ + "type":"Debit Card", + "description":"Takeway", + "posted":"2015-09-20T00:00:00.000Z", + "completed":"2015-09-20T00:00:00.000Z", + "new_balance":"8029.14", + "value":"-47.01" + } + },{ + "id":"00f89de5-8a30-4a98-89a5-80e2eb5d5eef", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"coffee", + "posted":"2015-09-21T00:00:00.000Z", + "completed":"2015-09-21T00:00:00.000Z", + "new_balance":"8023.46", + "value":"-5.68" + } + },{ + "id":"9bdcce93-37f4-440f-9cc1-78fb263c9596", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"The Sandwich Factory" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant/Takeway", + "posted":"2015-09-21T00:00:00.000Z", + "completed":"2015-09-21T00:00:00.000Z", + "new_balance":"8019.02", + "value":"-4.44" + } + },{ + "id":"872bdae6-7abc-413d-b752-4d2796b14326", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"R J Dentist" + }, + "details":{ + "type":"Debit Card", + "description":"Dentist", + "posted":"2015-09-23T00:00:00.000Z", + "completed":"2015-09-23T00:00:00.000Z", + "new_balance":"7953.46", + "value":"-65.56" + } + },{ + "id":"88b2ab36-e0bb-4d02-a115-c83096aa9478", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Patels Parmacy" + }, + "details":{ + "type":"Debit Card", + "description":"Parmacy", + "posted":"2015-09-23T00:00:00.000Z", + "completed":"2015-09-23T00:00:00.000Z", + "new_balance":"7944.58", + "value":"-8.88" + } + },{ + "id":"c5e52fc4-f008-411d-a186-8a406992fade", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Tesco" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-24T00:00:00.000Z", + "completed":"2015-09-24T00:00:00.000Z", + "new_balance":"7907.14", + "value":"-37.44" + } + },{ + "id":"573b5c22-39b0-478f-a043-273f3e118a0e", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Bambo Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-09-25T00:00:00.000Z", + "completed":"2015-09-25T00:00:00.000Z", + "new_balance":"7876.14", + "value":"-31.00" + } + },{ + "id":"4309984d-bd47-4cc8-a028-0349a6945d8b", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Takeway King" + }, + "details":{ + "type":"Debit Card", + "description":"Takeway", + "posted":"2015-09-25T00:00:00.000Z", + "completed":"2015-09-25T00:00:00.000Z", + "new_balance":"7860.71", + "value":"-15.43" + } + },{ + "id":"ba57a215-bb5b-4a9e-b0e0-ae1970daf046", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Mr D Moda" + }, + "details":{ + "type":"Tranfer", + "description":"Transfer", + "posted":"2015-09-27T00:00:00.000Z", + "completed":"2015-09-27T00:00:00.000Z", + "new_balance":"7616.01", + "value":"-244.70" + } + },{ + "id":"116e01fa-6daf-48c9-ab6e-b5e960dd43a3", + "this_account":{ + "id":"2330135d-fca8-4268-838d-833074985209", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Bambo Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-09-29T00:00:00.000Z", + "completed":"2015-09-29T00:00:00.000Z", + "new_balance":"7588.25", + "value":"-27.76" + } + },{ + "id":"b2711b8d-d8ed-485d-82db-5256f2cbd620", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Council Tax" + }, + "details":{ + "type":"Direct Debit", + "description":"Council Tax", + "posted":"2015-07-01T00:00:00.000Z", + "completed":"2015-07-01T00:00:00.000Z", + "new_balance":"7605.12", + "value":"-66.45" + } + },{ + "id":"c4ff16ec-5664-4788-98ea-9cdcb9b51c72", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Rent" + }, + "details":{ + "type":"Direct Debit", + "description":"Rent", + "posted":"2015-07-03T00:00:00.000Z", + "completed":"2015-07-03T00:00:00.000Z", + "new_balance":"6977.97", + "value":"-627.15" + } + },{ + "id":"90232f3c-d8d1-4913-b630-84095ad451c0", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"EON" + }, + "details":{ + "type":"Direct Debit", + "description":"Gas/Elec", + "posted":"2015-07-03T00:00:00.000Z", + "completed":"2015-07-03T00:00:00.000Z", + "new_balance":"6924.60", + "value":"-53.37" + } + },{ + "id":"81fb1c80-1845-472f-a078-25aeb607cba6", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Seven Trent Water" + }, + "details":{ + "type":"Direct Debit", + "description":"Water", + "posted":"2015-07-03T00:00:00.000Z", + "completed":"2015-07-03T00:00:00.000Z", + "new_balance":"6891.68", + "value":"-32.92" + } + },{ + "id":"72493c1f-7e8f-4962-9da4-e9ee1cef411d", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Talk Talk" + }, + "details":{ + "type":"Direct Debit", + "description":"Telephone", + "posted":"2015-07-03T00:00:00.000Z", + "completed":"2015-07-03T00:00:00.000Z", + "new_balance":"6868.91", + "value":"-22.77" + } + },{ + "id":"f7d38e93-f3a3-4e40-84ec-68189962ed8e", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Asda" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-05T00:00:00.000Z", + "completed":"2015-07-05T00:00:00.000Z", + "new_balance":"6834.26", + "value":"-34.65" + } + },{ + "id":"1f617bc7-0346-4555-8aac-823de51ebe10", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Asda ATM" + }, + "details":{ + "type":"Cash withdrawal", + "description":"Cash withdrawal", + "posted":"2015-07-05T00:00:00.000Z", + "completed":"2015-07-05T00:00:00.000Z", + "new_balance":"6782.49", + "value":"-51.77" + } + },{ + "id":"f1bf0925-23f9-43fc-be2d-b8ddfefa411b", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Poundland" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-05T00:00:00.000Z", + "completed":"2015-07-05T00:00:00.000Z", + "new_balance":"6767.73", + "value":"-14.76" + } + },{ + "id":"565c0494-66cf-497a-8553-e52bc340d28f", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Specsavers" + }, + "details":{ + "type":"Debit Card", + "description":"Optician", + "posted":"2015-07-06T00:00:00.000Z", + "completed":"2015-07-06T00:00:00.000Z", + "new_balance":"6743.63", + "value":"-24.10" + } + },{ + "id":"f66a4412-6a76-4d51-a382-6af55a970d34", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"mcdonalds" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-07-06T00:00:00.000Z", + "completed":"2015-07-06T00:00:00.000Z", + "new_balance":"6708.05", + "value":"-35.58" + } + },{ + "id":"cbecb922-ed34-4a30-b95f-0f1db344d282", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"High Street ATM" + }, + "details":{ + "type":"Cash withdrawal", + "description":"Cash withdrawal", + "posted":"2015-07-10T00:00:00.000Z", + "completed":"2015-07-10T00:00:00.000Z", + "new_balance":"6656.28", + "value":"-51.77" + } + },{ + "id":"31d38ca8-36b4-41ed-9fac-5068152dbc6e", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"CO-OP" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-10T00:00:00.000Z", + "completed":"2015-07-10T00:00:00.000Z", + "new_balance":"6634.40", + "value":"-21.88" + } + },{ + "id":"e896508d-3bc0-42f3-9e92-b7d09674a0b3", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Asda" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-12T00:00:00.000Z", + "completed":"2015-07-12T00:00:00.000Z", + "new_balance":"6596.82", + "value":"-37.58" + } + },{ + "id":"afa43cde-fb9c-4063-bd7d-32a18c2aaeb3", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Clarks" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-12T00:00:00.000Z", + "completed":"2015-07-12T00:00:00.000Z", + "new_balance":"6574.05", + "value":"-22.77" + } + },{ + "id":"a45f1064-9422-42b9-aa07-4b9d9d56c5f5", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Next" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-12T00:00:00.000Z", + "completed":"2015-07-12T00:00:00.000Z", + "new_balance":"6544.21", + "value":"-29.84" + } + },{ + "id":"82b2a2ed-8907-4929-ab1f-04cda79dfacd", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"mcdonalds" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-07-15T00:00:00.000Z", + "completed":"2015-07-15T00:00:00.000Z", + "new_balance":"6524.65", + "value":"-19.56" + } + },{ + "id":"d2138860-f7e2-494c-9a92-449a2f219066", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"High Street ATM" + }, + "details":{ + "type":"Cash withdrawal", + "description":"Cash withdrawal", + "posted":"2015-07-16T00:00:00.000Z", + "completed":"2015-07-16T00:00:00.000Z", + "new_balance":"6472.88", + "value":"-51.77" + } + },{ + "id":"f2fa1731-8da3-4f95-af44-d337a32e41fa", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"CO-OP" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-16T00:00:00.000Z", + "completed":"2015-07-16T00:00:00.000Z", + "new_balance":"6465.67", + "value":"-7.21" + } + },{ + "id":"4feb5cb4-c0b8-4976-810d-8e0342f8a439", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Asda" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-17T00:00:00.000Z", + "completed":"2015-07-17T00:00:00.000Z", + "new_balance":"6407.23", + "value":"-58.44" + } + },{ + "id":"9ef1d6cb-0182-4d86-8e90-6ec4ce0497ef", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Asda Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling station", + "posted":"2015-07-17T00:00:00.000Z", + "completed":"2015-07-17T00:00:00.000Z", + "new_balance":"6377.53", + "value":"-29.70" + } + },{ + "id":"6667c0d8-4371-46ff-b63c-f029a30f041b", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Pizza hut" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-07-19T00:00:00.000Z", + "completed":"2015-07-19T00:00:00.000Z", + "new_balance":"6341.13", + "value":"-36.40" + } + },{ + "id":"72c50133-7e3c-41c3-ba8a-5f2e51c11068", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"High Street ATM" + }, + "details":{ + "type":"Cash withdrawal", + "description":"Cash withdrawal", + "posted":"2015-07-20T00:00:00.000Z", + "completed":"2015-07-20T00:00:00.000Z", + "new_balance":"6289.36", + "value":"-51.77" + } + },{ + "id":"f7e7f58b-9be9-4686-9c93-29b342d7c0b7", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"CO-OP" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-23T00:00:00.000Z", + "completed":"2015-07-23T00:00:00.000Z", + "new_balance":"6277.50", + "value":"-11.86" + } + },{ + "id":"e832358c-99b6-4f31-b263-79e3caa721ad", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Asda" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-26T00:00:00.000Z", + "completed":"2015-07-26T00:00:00.000Z", + "new_balance":"6237.16", + "value":"-40.34" + } + },{ + "id":"e777093e-3de0-4d7f-abe2-75ccbec73901", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Salary" + }, + "details":{ + "type":"Paid in", + "description":"Salary", + "posted":"2015-07-27T00:00:00.000Z", + "completed":"2015-07-27T00:00:00.000Z", + "new_balance":"7247.02", + "value":"1009.86" + } + },{ + "id":"f617e340-61a6-482d-9b53-28f8253d8f5a", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"three" + }, + "details":{ + "type":"Direct Debit", + "description":"Mobile phone", + "posted":"2015-07-28T00:00:00.000Z", + "completed":"2015-07-28T00:00:00.000Z", + "new_balance":"7214.10", + "value":"-32.92" + } + },{ + "id":"0e619ca6-d7fb-4b29-9279-ba5d8ecce707", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Post Office Savings Account" + }, + "details":{ + "type":"Standing Order", + "description":"Savings", + "posted":"2015-07-30T00:00:00.000Z", + "completed":"2015-07-30T00:00:00.000Z", + "new_balance":"7186.87", + "value":"-27.23" + } + },{ + "id":"f4547d4a-892a-4a9d-b4c4-2b50e502f264", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Council Tax" + }, + "details":{ + "type":"Direct Debit", + "description":"Council Tax", + "posted":"2015-08-01T00:00:00.000Z", + "completed":"2015-08-01T00:00:00.000Z", + "new_balance":"7120.42", + "value":"-66.45" + } + },{ + "id":"005949ad-795b-48db-b618-a991b75912b0", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Rent" + }, + "details":{ + "type":"Direct Debit", + "description":"Rent", + "posted":"2015-08-03T00:00:00.000Z", + "completed":"2015-08-03T00:00:00.000Z", + "new_balance":"6493.27", + "value":"-627.15" + } + },{ + "id":"bac696b4-2e73-4280-996f-c9eead165a96", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"EON" + }, + "details":{ + "type":"Direct Debit", + "description":"Gas/Elec", + "posted":"2015-08-03T00:00:00.000Z", + "completed":"2015-08-03T00:00:00.000Z", + "new_balance":"6439.90", + "value":"-53.37" + } + },{ + "id":"ade28734-f3fb-4404-a1cd-256ca6bd28ac", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Seven Trent Water" + }, + "details":{ + "type":"Direct Debit", + "description":"Water", + "posted":"2015-08-03T00:00:00.000Z", + "completed":"2015-08-03T00:00:00.000Z", + "new_balance":"6406.98", + "value":"-32.92" + } + },{ + "id":"10739155-7e80-4570-a232-ebadb761df46", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Talk Talk" + }, + "details":{ + "type":"Direct Debit", + "description":"Telephone", + "posted":"2015-08-03T00:00:00.000Z", + "completed":"2015-08-03T00:00:00.000Z", + "new_balance":"6384.21", + "value":"-22.77" + } + },{ + "id":"ef303daa-3999-43ac-9ec1-d65d84d25914", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Asda" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-08T00:00:00.000Z", + "completed":"2015-08-08T00:00:00.000Z", + "new_balance":"6349.56", + "value":"-34.65" + } + },{ + "id":"f62d98d2-d1fa-489f-8822-8fed019c458d", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Asda ATM" + }, + "details":{ + "type":"Cash withdrawal", + "description":"Cash withdrawal", + "posted":"2015-08-08T00:00:00.000Z", + "completed":"2015-08-08T00:00:00.000Z", + "new_balance":"6297.79", + "value":"-51.77" + } + },{ + "id":"5fc6c085-7f08-4bd4-8069-44e11671c139", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"mcdonalds" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-08-13T00:00:00.000Z", + "completed":"2015-08-13T00:00:00.000Z", + "new_balance":"6273.69", + "value":"-24.10" + } + },{ + "id":"9855f22b-ec5c-44fe-8832-d2a10803f495", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Ebay" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-14T00:00:00.000Z", + "completed":"2015-08-14T00:00:00.000Z", + "new_balance":"6244.95", + "value":"-28.74" + } + },{ + "id":"153741d5-7823-4ad6-a123-8560ca3742a1", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"The Book people" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-17T00:00:00.000Z", + "completed":"2015-08-17T00:00:00.000Z", + "new_balance":"6234.75", + "value":"-10.20" + } + },{ + "id":"3e9f1922-7ae7-4df3-8abc-924f71d719ef", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Domino's" + }, + "details":{ + "type":"Debit Card", + "description":"Takeaway", + "posted":"2015-08-21T00:00:00.000Z", + "completed":"2015-08-21T00:00:00.000Z", + "new_balance":"6196.68", + "value":"-38.07" + } + },{ + "id":"0cd42655-e648-4fa0-9bab-145277997e3f", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Asda" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-23T00:00:00.000Z", + "completed":"2015-08-23T00:00:00.000Z", + "new_balance":"6144.86", + "value":"-51.82" + } + },{ + "id":"21fd40fe-7d88-4a07-b8f2-0c669407b425", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Asda ATM" + }, + "details":{ + "type":"Cash withdrawal", + "description":"Cash withdrawal", + "posted":"2015-08-23T00:00:00.000Z", + "completed":"2015-08-23T00:00:00.000Z", + "new_balance":"6093.09", + "value":"-51.77" + } + },{ + "id":"ffe427d2-b132-46ad-b10f-d358229584ec", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Argos" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-24T00:00:00.000Z", + "completed":"2015-08-24T00:00:00.000Z", + "new_balance":"6082.89", + "value":"-10.20" + } + },{ + "id":"28e3dc89-eccf-453f-b946-2dbf673c06e6", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"mcdonalds" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-08-27T00:00:00.000Z", + "completed":"2015-08-27T00:00:00.000Z", + "new_balance":"6061.95", + "value":"-20.94" + } + },{ + "id":"48e1b751-7eb8-43bc-afeb-c5b889a15046", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Asda" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-27T00:00:00.000Z", + "completed":"2015-08-27T00:00:00.000Z", + "new_balance":"6021.61", + "value":"-40.34" + } + },{ + "id":"d0c039a0-cde2-4dbc-b6bf-f43acd2af11f", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Salary" + }, + "details":{ + "type":"Paid in", + "description":"Salary", + "posted":"2015-08-27T00:00:00.000Z", + "completed":"2015-08-27T00:00:00.000Z", + "new_balance":"7031.47", + "value":"1009.86" + } + },{ + "id":"7d8327c1-2462-4bc3-8238-59226da1550d", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"three" + }, + "details":{ + "type":"Direct Debit", + "description":"Mobile phone", + "posted":"2015-08-28T00:00:00.000Z", + "completed":"2015-08-28T00:00:00.000Z", + "new_balance":"6998.55", + "value":"-32.92" + } + },{ + "id":"1abc0d46-02c5-48c9-89ea-4be988cf195c", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Post Office Savings Account" + }, + "details":{ + "type":"Standing Order", + "description":"Savings", + "posted":"2015-08-30T00:00:00.000Z", + "completed":"2015-08-30T00:00:00.000Z", + "new_balance":"6971.32", + "value":"-27.23" + } + },{ + "id":"e298ad50-4e38-42fb-a848-0cf0c0b62908", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Council Tax" + }, + "details":{ + "type":"Direct Debit", + "description":"Council Tax", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"6904.87", + "value":"-66.45" + } + },{ + "id":"c34e91a3-47d0-412b-8a25-188751b54d16", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Rent" + }, + "details":{ + "type":"Direct Debit", + "description":"Rent", + "posted":"2015-09-03T00:00:00.000Z", + "completed":"2015-09-03T00:00:00.000Z", + "new_balance":"6277.72", + "value":"-627.15" + } + },{ + "id":"21cea876-5598-437c-955c-44771722f15b", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"EON" + }, + "details":{ + "type":"Direct Debit", + "description":"Gas/Elec", + "posted":"2015-09-03T00:00:00.000Z", + "completed":"2015-09-03T00:00:00.000Z", + "new_balance":"6224.35", + "value":"-53.37" + } + },{ + "id":"4a5744ea-fc62-4cf9-bcf0-83baf8c461ee", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Seven Trent Water" + }, + "details":{ + "type":"Direct Debit", + "description":"Water", + "posted":"2015-09-03T00:00:00.000Z", + "completed":"2015-09-03T00:00:00.000Z", + "new_balance":"6191.43", + "value":"-32.92" + } + },{ + "id":"2c79ca18-b809-429f-b257-6dfc3a9fa8e4", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Talk Talk" + }, + "details":{ + "type":"Direct Debit", + "description":"Telephone", + "posted":"2015-09-03T00:00:00.000Z", + "completed":"2015-09-03T00:00:00.000Z", + "new_balance":"6168.66", + "value":"-22.77" + } + },{ + "id":"05fd805f-1d45-4cac-8811-ab00df9353a1", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Asda" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-05T00:00:00.000Z", + "completed":"2015-09-05T00:00:00.000Z", + "new_balance":"6110.50", + "value":"-58.16" + } + },{ + "id":"316a4753-256f-4df5-91d2-8cad7feea2d5", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"ATM Asda" + }, + "details":{ + "type":"Cash Withdrawal", + "description":"Cash withdrawal", + "posted":"2015-09-05T00:00:00.000Z", + "completed":"2015-09-05T00:00:00.000Z", + "new_balance":"6058.73", + "value":"-51.77" + } + },{ + "id":"94f47bd2-f9ab-4d6b-8482-172254df5c5f", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"CO-OP" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-07T00:00:00.000Z", + "completed":"2015-09-07T00:00:00.000Z", + "new_balance":"6044.48", + "value":"-14.25" + } + },{ + "id":"470fcd75-8334-41d5-b97b-2a69a1ce8177", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"ATM High Street" + }, + "details":{ + "type":"Cash Withdrawal", + "description":"Cash withdrawal", + "posted":"2015-09-09T00:00:00.000Z", + "completed":"2015-09-09T00:00:00.000Z", + "new_balance":"5992.71", + "value":"-51.77" + } + },{ + "id":"b5e14a59-3b92-4ffe-95de-aed1e2e6a4fc", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Mcdonalds" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-09-11T00:00:00.000Z", + "completed":"2015-09-11T00:00:00.000Z", + "new_balance":"5982.37", + "value":"-10.34" + } + },{ + "id":"27a6bda1-ed22-4cef-976b-0bbdd307d1d2", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Asda" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-13T00:00:00.000Z", + "completed":"2015-09-13T00:00:00.000Z", + "new_balance":"5922.26", + "value":"-60.11" + } + },{ + "id":"56f35dfd-4d24-451c-b5ad-1e6f038c6319", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Poundland" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-14T00:00:00.000Z", + "completed":"2015-09-14T00:00:00.000Z", + "new_balance":"5910.83", + "value":"-11.43" + } + },{ + "id":"695f2508-f808-4efb-bd49-00ee65b57564", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Asda Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling station", + "posted":"2015-09-15T00:00:00.000Z", + "completed":"2015-09-15T00:00:00.000Z", + "new_balance":"5883.60", + "value":"-27.23" + } + },{ + "id":"316be52e-1c31-4dd2-84b1-92800980cc01", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Sport Direct" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-26T00:00:00.000Z", + "completed":"2015-09-26T00:00:00.000Z", + "new_balance":"5851.59", + "value":"-32.01" + } + },{ + "id":"04dc4c9e-f28f-45d6-bcda-d219f3ad89ae", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Pizza Hut" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-09-26T00:00:00.000Z", + "completed":"2015-09-26T00:00:00.000Z", + "new_balance":"5811.87", + "value":"-39.72" + } + },{ + "id":"07390e63-2339-4882-87bc-f5477922f12c", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"CO-OP" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-26T00:00:00.000Z", + "completed":"2015-09-26T00:00:00.000Z", + "new_balance":"5787.69", + "value":"-24.18" + } + },{ + "id":"c91cc425-00c3-49bc-ba9f-de0ac8bfe308", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Salary" + }, + "details":{ + "type":"Paid in", + "description":"Paid in", + "posted":"2015-09-27T00:00:00.000Z", + "completed":"2015-09-27T00:00:00.000Z", + "new_balance":"6797.55", + "value":"1009.86" + } + },{ + "id":"e93d1d73-0401-4339-870b-602541324e92", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Asda" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-27T00:00:00.000Z", + "completed":"2015-09-27T00:00:00.000Z", + "new_balance":"6741.95", + "value":"-55.60" + } + },{ + "id":"cab07ea7-ff73-4c91-b113-45af8de782ca", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Asda ATM" + }, + "details":{ + "type":"Cash Withdrawal", + "description":"Cash withdrawal", + "posted":"2015-09-27T00:00:00.000Z", + "completed":"2015-09-27T00:00:00.000Z", + "new_balance":"6690.18", + "value":"-51.77" + } + },{ + "id":"b0751086-0a7f-4874-8af3-c7419b524fb5", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"three" + }, + "details":{ + "type":"Direct Debit", + "description":"Mobile phone", + "posted":"2015-09-28T00:00:00.000Z", + "completed":"2015-09-28T00:00:00.000Z", + "new_balance":"6657.26", + "value":"-32.92" + } + },{ + "id":"96be48c8-09c7-4a80-9206-4de560171ff4", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Post Office Savings Account" + }, + "details":{ + "type":"Standing Order", + "description":"Savings", + "posted":"2015-09-30T00:00:00.000Z", + "completed":"2015-09-30T00:00:00.000Z", + "new_balance":"6630.03", + "value":"-27.23" + } + },{ + "id":"21740a94-1fd5-4cff-bb27-5629fd254a87", + "this_account":{ + "id":"f65e28a5-9abe-428f-85bb-6c3c38122adb", + "bank":"obp-bank-x-gh" + }, + "counterparty":{ + "name":"Asda Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling station", + "posted":"2025-08-08T00:00:00.000Z", + "completed":"2025-08-08T00:00:00.000Z", + "new_balance":"6599.63", + "value":"-30.40" + } + },{ + "id":"ff2534c5-881c-4102-a8e0-17e3727d509a", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Salary" + }, + "details":{ + "type":"Paid in", + "description":"Salary", + "posted":"2015-07-01T00:00:00.000Z", + "completed":"2015-07-01T00:00:00.000Z", + "new_balance":"3477.76", + "value":"1393.82" + } + },{ + "id":"4ec7a646-5bf9-4780-9ae3-6009c613d813", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Salary" + }, + "details":{ + "type":"Paid In", + "description":"Salary", + "posted":"2015-07-01T00:00:00.000Z", + "completed":"2015-07-01T00:00:00.000Z", + "new_balance":"5501.70", + "value":"2023.94" + } + },{ + "id":"afebaf96-5b9e-44b4-90bd-766b3ddf7f69", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Car Plan" + }, + "details":{ + "type":"Direct Debit", + "description":"Car Monthly Payment", + "posted":"2015-07-03T00:00:00.000Z", + "completed":"2015-07-03T00:00:00.000Z", + "new_balance":"5128.38", + "value":"-373.32" + } + },{ + "id":"0e58465e-5645-40d9-bbc5-86acc672d51d", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"New Look" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-04T00:00:00.000Z", + "completed":"2015-07-04T00:00:00.000Z", + "new_balance":"5075.48", + "value":"-52.90" + } + },{ + "id":"cb8d2abe-d22f-4d57-8d0f-31fecc2017fc", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Burger King" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-07-04T00:00:00.000Z", + "completed":"2015-07-04T00:00:00.000Z", + "new_balance":"5066.70", + "value":"-8.78" + } + },{ + "id":"1679d777-55d8-4d95-9093-b43f613520ac", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Revolution Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-07-04T00:00:00.000Z", + "completed":"2015-07-04T00:00:00.000Z", + "new_balance":"5046.77", + "value":"-19.93" + } + },{ + "id":"8d3bc7b1-6410-497d-8a69-787839395f49", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-07-05T00:00:00.000Z", + "completed":"2015-07-05T00:00:00.000Z", + "new_balance":"5035.97", + "value":"-10.80" + } + },{ + "id":"c517b676-f324-48f5-aa47-f7b66e9fae4b", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-07-07T00:00:00.000Z", + "completed":"2015-07-07T00:00:00.000Z", + "new_balance":"5030.71", + "value":"-5.26" + } + },{ + "id":"8454982d-d706-474d-8b68-a4822e87e5a3", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"The Body Shop" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-11T00:00:00.000Z", + "completed":"2015-07-11T00:00:00.000Z", + "new_balance":"5011.22", + "value":"-19.49" + } + },{ + "id":"62d80fd5-9564-48d4-bdf0-632d5fa6c480", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Boots Plc" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-11T00:00:00.000Z", + "completed":"2015-07-11T00:00:00.000Z", + "new_balance":"4993.39", + "value":"-17.83" + } + },{ + "id":"95424163-326a-45c5-bcc0-381d18d890ee", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Nail Art" + }, + "details":{ + "type":"Debit Card", + "description":"Nails", + "posted":"2015-07-11T00:00:00.000Z", + "completed":"2015-07-11T00:00:00.000Z", + "new_balance":"4971.25", + "value":"-22.14" + } + },{ + "id":"ec48f3a2-3e50-4b2d-9a62-f3f2540ab31d", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Terrance Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-07-11T00:00:00.000Z", + "completed":"2015-07-11T00:00:00.000Z", + "new_balance":"4956.52", + "value":"-14.73" + } + },{ + "id":"319cca47-b4ae-4422-becc-df59ad153f24", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Zizzi" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-07-11T00:00:00.000Z", + "completed":"2015-07-11T00:00:00.000Z", + "new_balance":"4925.53", + "value":"-30.99" + } + },{ + "id":"e1947504-3ee5-44ff-898d-5f43ac76f342", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Texaco Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling Station", + "posted":"2015-07-13T00:00:00.000Z", + "completed":"2015-07-13T00:00:00.000Z", + "new_balance":"4861.33", + "value":"-64.20" + } + },{ + "id":"175bcd89-3525-46b8-aca8-31cfb8245f74", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Texaco Filling Station ATM" + }, + "details":{ + "type":"Debit Card", + "description":"Cash Withdrawals", + "posted":"2015-07-13T00:00:00.000Z", + "completed":"2015-07-13T00:00:00.000Z", + "new_balance":"4740.74", + "value":"-120.59" + } + },{ + "id":"f74c4634-7770-4e74-a7c4-06afde828bb5", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"The White Horse" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-07-16T00:00:00.000Z", + "completed":"2015-07-16T00:00:00.000Z", + "new_balance":"4709.14", + "value":"-31.60" + } + },{ + "id":"1339f0ec-057b-4960-bfc1-7a2b908afd1a", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-07-17T00:00:00.000Z", + "completed":"2015-07-17T00:00:00.000Z", + "new_balance":"4702.16", + "value":"-6.98" + } + },{ + "id":"fc7d4d9c-1c5f-4ac5-81bb-83fc8cb1810d", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Waitrose" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-17T00:00:00.000Z", + "completed":"2015-07-17T00:00:00.000Z", + "new_balance":"4692.02", + "value":"-10.14" + } + },{ + "id":"ea5ff1a3-1805-4e73-8bee-80bffa546854", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"The Salon" + }, + "details":{ + "type":"Debit Card", + "description":"Hair", + "posted":"2015-07-19T00:00:00.000Z", + "completed":"2015-07-19T00:00:00.000Z", + "new_balance":"4604.83", + "value":"-87.19" + } + },{ + "id":"54169458-cfb0-4c8a-a129-1531482bc2f7", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Revolution Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-07-21T00:00:00.000Z", + "completed":"2015-07-21T00:00:00.000Z", + "new_balance":"4580.69", + "value":"-24.14" + } + },{ + "id":"dc128d9b-dbee-485d-a601-1c436469316a", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"The White Horse" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-07-23T00:00:00.000Z", + "completed":"2015-07-23T00:00:00.000Z", + "new_balance":"4539.53", + "value":"-41.16" + } + },{ + "id":"90fce9ca-9211-450f-9b23-5fa1fe642db5", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Boots Plc" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-25T00:00:00.000Z", + "completed":"2015-07-25T00:00:00.000Z", + "new_balance":"4522.12", + "value":"-17.41" + } + },{ + "id":"d443e754-69ca-46ab-b633-59cc6dd1fd99", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-07-27T00:00:00.000Z", + "completed":"2015-07-27T00:00:00.000Z", + "new_balance":"4516.86", + "value":"-5.26" + } + },{ + "id":"4a2cbd2c-955f-419c-8009-689e1195d560", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Orange Mobile" + }, + "details":{ + "type":"Direct Debit", + "description":"Mobile", + "posted":"2015-07-28T00:00:00.000Z", + "completed":"2015-07-28T00:00:00.000Z", + "new_balance":"4462.66", + "value":"-54.20" + } + },{ + "id":"593be880-8f14-48f7-9016-af41086812af", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Savings" + }, + "details":{ + "type":"Standing Order", + "description":"Saving", + "posted":"2015-07-28T00:00:00.000Z", + "completed":"2015-07-28T00:00:00.000Z", + "new_balance":"4225.50", + "value":"-237.16" + } + },{ + "id":"a60b50b9-4a4b-4646-a1f7-7fb032ed9c65", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Salary" + }, + "details":{ + "type":"Paid in", + "description":"Salary", + "posted":"2015-08-01T00:00:00.000Z", + "completed":"2015-08-01T00:00:00.000Z", + "new_balance":"5619.32", + "value":"1393.82" + } + },{ + "id":"4062b8d3-863b-4f29-8f5b-9e4e858868f3", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Car Plan" + }, + "details":{ + "type":"Direct Debit", + "description":"Car Monthly Payment", + "posted":"2015-08-03T00:00:00.000Z", + "completed":"2015-08-03T00:00:00.000Z", + "new_balance":"5246.00", + "value":"-373.32" + } + },{ + "id":"459c390d-4309-4f45-b4d5-b671fa43e6e8", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"High street ATM" + }, + "details":{ + "type":"Cash withdrawal", + "description":"Cash Withdrawals", + "posted":"2015-08-03T00:00:00.000Z", + "completed":"2015-08-03T00:00:00.000Z", + "new_balance":"5116.76", + "value":"-129.24" + } + },{ + "id":"a4fdba0c-11db-464d-ac96-505d183e763f", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-08-04T00:00:00.000Z", + "completed":"2015-08-04T00:00:00.000Z", + "new_balance":"5101.84", + "value":"-14.92" + } + },{ + "id":"b441baf1-5ab1-42c3-aa3b-123bde37740f", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Revolution Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-08-07T00:00:00.000Z", + "completed":"2015-08-07T00:00:00.000Z", + "new_balance":"5076.49", + "value":"-25.35" + } + },{ + "id":"d57e9d38-2e6b-4baf-87f2-960efe8de142", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Zizzi" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-08-07T00:00:00.000Z", + "completed":"2015-08-07T00:00:00.000Z", + "new_balance":"5045.50", + "value":"-30.99" + } + },{ + "id":"9983a87c-eb2c-4585-b801-74ee1a76491f", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Netflix" + }, + "details":{ + "type":"Debit Card", + "description":"Monthly Netflix Membership", + "posted":"2015-08-07T00:00:00.000Z", + "completed":"2015-08-07T00:00:00.000Z", + "new_balance":"5038.12", + "value":"-7.38" + } + },{ + "id":"ed340169-dc4b-4139-99e5-5337e7193c2b", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Topshop" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-08T00:00:00.000Z", + "completed":"2015-08-08T00:00:00.000Z", + "new_balance":"4990.29", + "value":"-47.83" + } + },{ + "id":"c76681c4-2bf9-4f46-89d3-83118a286fd1", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Hand Made Burger Company" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-08-08T00:00:00.000Z", + "completed":"2015-08-08T00:00:00.000Z", + "new_balance":"4937.48", + "value":"-52.81" + } + },{ + "id":"44354f0c-bf1f-4312-bd76-4356c40947f3", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"High street ATM" + }, + "details":{ + "type":"Cash withdrawal", + "description":"Cash Withdrawals", + "posted":"2015-08-08T00:00:00.000Z", + "completed":"2015-08-08T00:00:00.000Z", + "new_balance":"4816.89", + "value":"-120.59" + } + },{ + "id":"6b51fdc5-1c18-41f0-bc41-a750b4a3fdcf", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Waitrose" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-09T00:00:00.000Z", + "completed":"2015-08-09T00:00:00.000Z", + "new_balance":"4793.74", + "value":"-23.15" + } + },{ + "id":"77b7e795-4167-4c1b-827a-d055b3089da3", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-08-11T00:00:00.000Z", + "completed":"2015-08-11T00:00:00.000Z", + "new_balance":"4788.48", + "value":"-5.26" + } + },{ + "id":"08c5fee4-61ca-4537-bc89-bd5d73a37003", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Terrance Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-08-14T00:00:00.000Z", + "completed":"2015-08-14T00:00:00.000Z", + "new_balance":"4753.49", + "value":"-34.99" + } + },{ + "id":"7b6c843b-c88c-47be-8fe5-0788dda69b74", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"The White Horse" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-08-15T00:00:00.000Z", + "completed":"2015-08-15T00:00:00.000Z", + "new_balance":"4722.50", + "value":"-30.99" + } + },{ + "id":"f9375cf4-a38f-4a8b-9e71-bcbbf64e06c9", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Texaco Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling Station", + "posted":"2015-08-17T00:00:00.000Z", + "completed":"2015-08-17T00:00:00.000Z", + "new_balance":"4677.17", + "value":"-45.33" + } + },{ + "id":"b040daa6-885f-4b23-8153-99e49bc05603", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-08-17T00:00:00.000Z", + "completed":"2015-08-17T00:00:00.000Z", + "new_balance":"4670.19", + "value":"-6.98" + } + },{ + "id":"6e1dd2ea-d107-4c86-a4e7-ebea37e50b36", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Aiport Boots Plc" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-19T00:00:00.000Z", + "completed":"2015-08-19T00:00:00.000Z", + "new_balance":"4662.89", + "value":"-7.30" + } + },{ + "id":"d879228a-8042-44d2-b8bb-6dd3d799f366", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Orange Mobile" + }, + "details":{ + "type":"Direct Debit", + "description":"Mobile", + "posted":"2015-08-28T00:00:00.000Z", + "completed":"2015-08-28T00:00:00.000Z", + "new_balance":"4608.69", + "value":"-54.20" + } + },{ + "id":"1aac8e76-24b1-422c-8b7d-ae5e020c2417", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Savings" + }, + "details":{ + "type":"Standing Order", + "description":"Saving", + "posted":"2015-08-28T00:00:00.000Z", + "completed":"2015-08-28T00:00:00.000Z", + "new_balance":"4371.53", + "value":"-237.16" + } + },{ + "id":"bcd93119-31e3-4a90-badb-ba0e5b748499", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Salary" + }, + "details":{ + "type":"Paid in", + "description":"Salary", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"5765.35", + "value":"1393.82" + } + },{ + "id":"2edee268-bdc8-4317-909c-f819048dc719", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Car Plan" + }, + "details":{ + "type":"Direct Debit", + "description":"Car Monthly Payment", + "posted":"2015-09-03T00:00:00.000Z", + "completed":"2015-09-03T00:00:00.000Z", + "new_balance":"5392.03", + "value":"-373.32" + } + },{ + "id":"3cb12f0c-6602-4d06-b882-37e98a352039", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Zara" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-04T00:00:00.000Z", + "completed":"2015-09-04T00:00:00.000Z", + "new_balance":"5346.70", + "value":"-45.33" + } + },{ + "id":"5890d005-5225-4497-be02-2e8e2abef9d1", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Boots Plc" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-04T00:00:00.000Z", + "completed":"2015-09-04T00:00:00.000Z", + "new_balance":"5320.10", + "value":"-26.60" + } + },{ + "id":"4aed082b-5370-4723-b987-ec30e1242304", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Revolution Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-09-04T00:00:00.000Z", + "completed":"2015-09-04T00:00:00.000Z", + "new_balance":"5301.09", + "value":"-19.01" + } + },{ + "id":"98482ee9-47e0-45d7-bb04-208769edb7b9", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Zizzi" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-09-04T00:00:00.000Z", + "completed":"2015-09-04T00:00:00.000Z", + "new_balance":"5259.93", + "value":"-41.16" + } + },{ + "id":"4606bf78-575e-4325-a0b6-65f1163fc341", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-09-05T00:00:00.000Z", + "completed":"2015-09-05T00:00:00.000Z", + "new_balance":"5252.95", + "value":"-6.98" + } + },{ + "id":"247ed765-3666-455a-bd3d-1122c24968f1", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Ticket Master" + }, + "details":{ + "type":"Debit Card", + "description":"Tickets", + "posted":"2015-09-05T00:00:00.000Z", + "completed":"2015-09-05T00:00:00.000Z", + "new_balance":"5174.14", + "value":"-78.81" + } + },{ + "id":"59147a73-1440-471d-b447-b6e0fdda8d0b", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"The Salon" + }, + "details":{ + "type":"Debit Card", + "description":"Hair", + "posted":"2015-09-05T00:00:00.000Z", + "completed":"2015-09-05T00:00:00.000Z", + "new_balance":"5075.51", + "value":"-98.63" + } + },{ + "id":"663030f6-1c70-48c5-9277-d63a3aed07f1", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Texaco Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling Station", + "posted":"2015-09-05T00:00:00.000Z", + "completed":"2015-09-05T00:00:00.000Z", + "new_balance":"5015.94", + "value":"-59.57" + } + },{ + "id":"64315058-832c-463d-bf35-a666318e784f", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"High Street ATM" + }, + "details":{ + "type":"Cash Withdrawal", + "description":"Cash Withdrawals", + "posted":"2015-09-05T00:00:00.000Z", + "completed":"2015-09-05T00:00:00.000Z", + "new_balance":"4895.35", + "value":"-120.59" + } + },{ + "id":"6d347fdf-90bc-434d-933c-f58a05614d1c", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Showcase Cinema" + }, + "details":{ + "type":"Debit Card", + "description":"Cinema", + "posted":"2015-09-05T00:00:00.000Z", + "completed":"2015-09-05T00:00:00.000Z", + "new_balance":"4885.62", + "value":"-9.73" + } + },{ + "id":"97bf7571-cfee-459f-bdaa-f54f282ccd95", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Netflix" + }, + "details":{ + "type":"Direct Debit", + "description":"Monthly Netflix Membership", + "posted":"2015-09-07T00:00:00.000Z", + "completed":"2015-09-07T00:00:00.000Z", + "new_balance":"4880.20", + "value":"-5.42" + } + },{ + "id":"45663628-b9ae-4e46-a08b-730c2eeaafe7", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-09-07T00:00:00.000Z", + "completed":"2015-09-07T00:00:00.000Z", + "new_balance":"4873.22", + "value":"-6.98" + } + },{ + "id":"1ad01e3d-1e46-4045-84c4-d27b363d3be1", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Waitrose" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-07T00:00:00.000Z", + "completed":"2015-09-07T00:00:00.000Z", + "new_balance":"4866.99", + "value":"-6.23" + } + },{ + "id":"52bffd51-08dd-4ddf-920a-6b108f322292", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Netflix" + }, + "details":{ + "type":"Direct Debit", + "description":"Monthly Netflix Membership", + "posted":"2015-09-07T00:00:00.000Z", + "completed":"2015-09-07T00:00:00.000Z", + "new_balance":"4861.57", + "value":"-5.42" + } + },{ + "id":"1527c856-76cd-4849-bba6-fc152e7d512b", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Sainsbury's" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-10T00:00:00.000Z", + "completed":"2015-09-10T00:00:00.000Z", + "new_balance":"4825.82", + "value":"-35.75" + } + },{ + "id":"d97772f3-f272-41be-8728-a999c5f7ebc8", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-09-11T00:00:00.000Z", + "completed":"2015-09-11T00:00:00.000Z", + "new_balance":"4818.84", + "value":"-6.98" + } + },{ + "id":"fb8fc4aa-ca16-451b-b294-b43380c66f5a", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Nando's" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-09-11T00:00:00.000Z", + "completed":"2015-09-11T00:00:00.000Z", + "new_balance":"4807.25", + "value":"-11.59" + } + },{ + "id":"67406b53-f5a0-47f6-8e85-471132344315", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Hollywood Bolwing" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-09-11T00:00:00.000Z", + "completed":"2015-09-11T00:00:00.000Z", + "new_balance":"4776.91", + "value":"-30.34" + } + },{ + "id":"df4300d1-e7ea-4b7d-9ef3-ed1491ef6bb7", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Nail Art" + }, + "details":{ + "type":"Debit Card", + "description":"Nails", + "posted":"2015-09-12T00:00:00.000Z", + "completed":"2015-09-12T00:00:00.000Z", + "new_balance":"4754.77", + "value":"-22.14" + } + },{ + "id":"b660f09a-9479-4f2d-901e-121f759333c7", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-09-12T00:00:00.000Z", + "completed":"2015-09-12T00:00:00.000Z", + "new_balance":"4747.79", + "value":"-6.98" + } + },{ + "id":"a71b6a71-c0bd-4588-a1f7-13d92698091d", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"All Staints" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-12T00:00:00.000Z", + "completed":"2015-09-12T00:00:00.000Z", + "new_balance":"4622.78", + "value":"-125.01" + } + },{ + "id":"d762f092-844f-4818-be14-0f3fe60dec49", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Topshop" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-12T00:00:00.000Z", + "completed":"2015-09-12T00:00:00.000Z", + "new_balance":"4551.25", + "value":"-71.53" + } + },{ + "id":"247d86b3-aa8f-40d4-a1b8-cd9a6730d4c9", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"The White Horse" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant", + "posted":"2015-09-13T00:00:00.000Z", + "completed":"2015-09-13T00:00:00.000Z", + "new_balance":"4512.81", + "value":"-38.44" + } + },{ + "id":"6b93f9fa-1c38-4925-b6ad-1d2e1f546b80", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-09-14T00:00:00.000Z", + "completed":"2015-09-14T00:00:00.000Z", + "new_balance":"4505.83", + "value":"-6.98" + } + },{ + "id":"bcedb05b-b310-4791-b4b6-54a8e7627c41", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Waitrose" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-14T00:00:00.000Z", + "completed":"2015-09-14T00:00:00.000Z", + "new_balance":"4467.74", + "value":"-38.09" + } + },{ + "id":"5f91d7c4-a3dc-49c3-8dbb-812dca79ea9b", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Hong Wang" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurant/Takeway", + "posted":"2015-09-16T00:00:00.000Z", + "completed":"2015-09-16T00:00:00.000Z", + "new_balance":"4424.53", + "value":"-43.21" + } + },{ + "id":"a0b93361-1fa6-4317-b48f-d50e693aa757", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Amazon" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-18T00:00:00.000Z", + "completed":"2015-09-18T00:00:00.000Z", + "new_balance":"4392.18", + "value":"-32.35" + } + },{ + "id":"521de7b4-ffef-45e2-a74b-9033743fd135", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Booking.com" + }, + "details":{ + "type":"Debit Card", + "description":"Booking.com", + "posted":"2015-09-21T00:00:00.000Z", + "completed":"2015-09-21T00:00:00.000Z", + "new_balance":"4283.46", + "value":"-108.72" + } + },{ + "id":"77609c58-95d8-4cfb-addb-d2c6f892381e", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Easyjet" + }, + "details":{ + "type":"Debit Card", + "description":"Flights", + "posted":"2015-09-21T00:00:00.000Z", + "completed":"2015-09-21T00:00:00.000Z", + "new_balance":"4149.31", + "value":"-134.15" + } + },{ + "id":"4b7abd6b-0a7b-4de2-a6f3-28ccd7376e6e", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Pizza Express" + }, + "details":{ + "type":"Debit Card", + "description":"Resaurant", + "posted":"2015-09-23T00:00:00.000Z", + "completed":"2015-09-23T00:00:00.000Z", + "new_balance":"4115.29", + "value":"-34.02" + } + },{ + "id":"b12453ef-37d2-43a6-a51b-bb9d77bb990c", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-09-24T00:00:00.000Z", + "completed":"2015-09-24T00:00:00.000Z", + "new_balance":"4108.31", + "value":"-6.98" + } + },{ + "id":"28ddaeb6-da06-4e64-a184-11db7a298058", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Terrace Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-09-26T00:00:00.000Z", + "completed":"2015-09-26T00:00:00.000Z", + "new_balance":"4083.63", + "value":"-24.68" + } + },{ + "id":"f1c76615-5844-4ed4-a43e-2e0ab25aa983", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Revolution Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-09-26T00:00:00.000Z", + "completed":"2015-09-26T00:00:00.000Z", + "new_balance":"4067.52", + "value":"-16.11" + } + },{ + "id":"9a4e836f-a9bf-4c83-b581-92d310d567c1", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"ABC Taxi" + }, + "details":{ + "type":"Debit Card", + "description":"Taxi", + "posted":"2015-09-26T00:00:00.000Z", + "completed":"2015-09-26T00:00:00.000Z", + "new_balance":"4054.16", + "value":"-13.36" + } + },{ + "id":"4f3551df-d622-45e5-832d-19f5568d122f", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Costa Coffee" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-09-27T00:00:00.000Z", + "completed":"2015-09-27T00:00:00.000Z", + "new_balance":"4039.93", + "value":"-14.23" + } + },{ + "id":"b38708b3-3261-4a43-bb97-4c36d8e46f58", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Orange Mobile" + }, + "details":{ + "type":"Direct Debit", + "description":"Mobile", + "posted":"2015-09-28T00:00:00.000Z", + "completed":"2015-09-28T00:00:00.000Z", + "new_balance":"3985.73", + "value":"-54.20" + } + },{ + "id":"12bd7753-1edd-4b7e-9677-ca8c3c2e894e", + "this_account":{ + "id":"b8ae95c5-0b62-4d74-8a31-1a9388b128aa", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Savings" + }, + "details":{ + "type":"Standing Order", + "description":"Saving", + "posted":"2015-09-28T00:00:00.000Z", + "completed":"2015-09-28T00:00:00.000Z", + "new_balance":"3748.57", + "value":"-237.16" + } + },{ + "id":"ac381c33-e922-4128-b1f0-da8d78340050", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"British Gas " + }, + "details":{ + "type":"Direct Debit", + "description":"Gas/Elec", + "posted":"2015-07-01T00:00:00.000Z", + "completed":"2015-07-01T00:00:00.000Z", + "new_balance":"2317.59", + "value":"-120.59" + } + },{ + "id":"9c2d911b-764d-487a-93d6-1f652cfac747", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Seven Trent Water" + }, + "details":{ + "type":"Direct Debit", + "description":"Water", + "posted":"2015-07-01T00:00:00.000Z", + "completed":"2015-07-01T00:00:00.000Z", + "new_balance":"2293.28", + "value":"-24.31" + } + },{ + "id":"5afa2830-00e5-4e4a-b248-a02c856af808", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Duddingston Golf Club" + }, + "details":{ + "type":"Direct Debit", + "description":"Monthly Golf membership", + "posted":"2015-07-01T00:00:00.000Z", + "completed":"2015-07-01T00:00:00.000Z", + "new_balance":"2238.40", + "value":"-54.88" + } + },{ + "id":"4355080f-c89a-4cdd-b52d-56f3e84cd734", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Council Tax" + }, + "details":{ + "type":"Direct Debit", + "description":"Council Tax", + "posted":"2015-07-01T00:00:00.000Z", + "completed":"2015-07-01T00:00:00.000Z", + "new_balance":"2214.09", + "value":"-24.31" + } + },{ + "id":"7a132083-18f2-4505-8d59-e6aeb234c466", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Marks and Spencers" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-05T00:00:00.000Z", + "completed":"2015-07-05T00:00:00.000Z", + "new_balance":"2137.65", + "value":"-76.44" + } + },{ + "id":"628ae844-c6b2-4e6a-bbc0-e892c2cbd662", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Shell Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling Station", + "posted":"2015-07-05T00:00:00.000Z", + "completed":"2015-07-05T00:00:00.000Z", + "new_balance":"2069.12", + "value":"-68.53" + } + },{ + "id":"459eceb2-fdc7-45c7-8bdc-2207982af9ee", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-07-08T00:00:00.000Z", + "completed":"2015-07-08T00:00:00.000Z", + "new_balance":"2058.32", + "value":"-10.80" + } + },{ + "id":"c8bce33d-4310-4aa1-a3b6-66b75bd948d3", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Prezzo" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurants", + "posted":"2015-07-10T00:00:00.000Z", + "completed":"2015-07-10T00:00:00.000Z", + "new_balance":"2012.85", + "value":"-45.47" + } + },{ + "id":"1e381cf5-5f5f-4efd-ba77-b9807d75c4ac", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Sainsbury's" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-12T00:00:00.000Z", + "completed":"2015-07-12T00:00:00.000Z", + "new_balance":"1954.60", + "value":"-58.25" + } + },{ + "id":"a006842a-dfb3-487a-bec8-7c05cd27b216", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"The Florist" + }, + "details":{ + "type":"Debit Card", + "description":"Florist", + "posted":"2015-07-12T00:00:00.000Z", + "completed":"2015-07-12T00:00:00.000Z", + "new_balance":"1930.29", + "value":"-24.31" + } + },{ + "id":"9ecedccf-06e2-4d26-a4d1-9453bb6996a0", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-07-13T00:00:00.000Z", + "completed":"2015-07-13T00:00:00.000Z", + "new_balance":"1924.93", + "value":"-5.36" + } + },{ + "id":"f5d989f8-cf2c-4fff-9214-ebc2af0a6d46", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"NCP Car Park" + }, + "details":{ + "type":"Debit Card", + "description":"Parking", + "posted":"2015-07-13T00:00:00.000Z", + "completed":"2015-07-13T00:00:00.000Z", + "new_balance":"1920.40", + "value":"-4.53" + } + },{ + "id":"508348ad-c497-49fa-9bba-b953fe28d536", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"The Kitchin" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurants", + "posted":"2015-07-18T00:00:00.000Z", + "completed":"2015-07-18T00:00:00.000Z", + "new_balance":"1812.68", + "value":"-107.72" + } + },{ + "id":"df6bb658-3fe7-4925-81ab-03d45a3e758f", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Marks and Spencers" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-07-20T00:00:00.000Z", + "completed":"2015-07-20T00:00:00.000Z", + "new_balance":"1731.67", + "value":"-81.01" + } + },{ + "id":"180a50d7-5894-42b7-9e95-db53833ae882", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Shell Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling Station", + "posted":"2015-07-20T00:00:00.000Z", + "completed":"2015-07-20T00:00:00.000Z", + "new_balance":"1663.14", + "value":"-68.53" + } + },{ + "id":"94c9e3f6-d7bb-46ed-a6be-de99f55c2816", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Bar Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-07-24T00:00:00.000Z", + "completed":"2015-07-24T00:00:00.000Z", + "new_balance":"1618.93", + "value":"-44.21" + } + },{ + "id":"05148330-20ee-477e-a149-2dd919e5221a", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"British Telecom" + }, + "details":{ + "type":"Direct Debit", + "description":"BT", + "posted":"2015-07-29T00:00:00.000Z", + "completed":"2015-07-29T00:00:00.000Z", + "new_balance":"1584.82", + "value":"-34.11" + } + },{ + "id":"a7b208cd-0a70-4f5a-9f6b-756c56167519", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Paid in" + }, + "details":{ + "type":"Paid in", + "description":"Income", + "posted":"2015-07-29T00:00:00.000Z", + "completed":"2015-07-29T00:00:00.000Z", + "new_balance":"3957.30", + "value":"2372.48" + } + },{ + "id":"17ca057a-f1af-4857-8294-fcf674410193", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"RBS Savings" + }, + "details":{ + "type":"Standing Order", + "description":"Savings", + "posted":"2015-07-30T00:00:00.000Z", + "completed":"2015-07-30T00:00:00.000Z", + "new_balance":"3696.06", + "value":"-261.24" + } + },{ + "id":"5ea1d8ed-771d-4967-9f83-067a43ebf50e", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"British Gas " + }, + "details":{ + "type":"Direct Debit", + "description":"Gas/Elec", + "posted":"2015-08-01T00:00:00.000Z", + "completed":"2015-08-01T00:00:00.000Z", + "new_balance":"3575.47", + "value":"-120.59" + } + },{ + "id":"931ebaef-741b-4676-b2de-4d93316c5bb5", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Seven Trent Water" + }, + "details":{ + "type":"Direct Debit", + "description":"Water", + "posted":"2015-08-01T00:00:00.000Z", + "completed":"2015-08-01T00:00:00.000Z", + "new_balance":"3551.16", + "value":"-24.31" + } + },{ + "id":"55f06eb2-d60b-48a8-aacf-a2d00bceca4b", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Duddingston Golf Club" + }, + "details":{ + "type":"Direct Debit", + "description":"Monthly Golf membership", + "posted":"2015-08-01T00:00:00.000Z", + "completed":"2015-08-01T00:00:00.000Z", + "new_balance":"3496.28", + "value":"-54.88" + } + },{ + "id":"799b3493-a0ae-425a-9392-f4e4499efd6a", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Council Tax" + }, + "details":{ + "type":"Direct Debit", + "description":"Council Tax", + "posted":"2015-08-01T00:00:00.000Z", + "completed":"2015-08-01T00:00:00.000Z", + "new_balance":"3471.97", + "value":"-24.31" + } + },{ + "id":"9b055cd0-4341-4fdb-8f41-ce6e56979e7f", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"The News Shop" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-03T00:00:00.000Z", + "completed":"2015-08-03T00:00:00.000Z", + "new_balance":"3465.02", + "value":"-6.95" + } + },{ + "id":"a0fa82b0-75d8-4e60-9bd9-9149e4c4f659", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Shell Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling Station", + "posted":"2015-08-04T00:00:00.000Z", + "completed":"2015-08-04T00:00:00.000Z", + "new_balance":"3396.49", + "value":"-68.53" + } + },{ + "id":"03b1805b-5275-4012-a98a-5bedeaf43297", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-08-08T00:00:00.000Z", + "completed":"2015-08-08T00:00:00.000Z", + "new_balance":"3381.17", + "value":"-15.32" + } + },{ + "id":"d5113360-3b8f-47f4-9105-2513a64c57c7", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Next" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-08T00:00:00.000Z", + "completed":"2015-08-08T00:00:00.000Z", + "new_balance":"3350.17", + "value":"-31.00" + } + },{ + "id":"0b6c1036-3419-40ec-b632-7a7c16bce7ea", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"House of Fraser" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-08T00:00:00.000Z", + "completed":"2015-08-08T00:00:00.000Z", + "new_balance":"3246.61", + "value":"-103.56" + } + },{ + "id":"738ff4b0-24d5-4c5e-91c6-118879178e08", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"La Favorita Restaurant" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurants", + "posted":"2015-08-08T00:00:00.000Z", + "completed":"2015-08-08T00:00:00.000Z", + "new_balance":"3136.52", + "value":"-110.09" + } + },{ + "id":"e761f1ee-fc5e-4897-910f-f166fded9ac4", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Marks and Spencers" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-09T00:00:00.000Z", + "completed":"2015-08-09T00:00:00.000Z", + "new_balance":"3102.30", + "value":"-34.22" + } + },{ + "id":"43f8c509-d933-46ce-a807-9997070b3977", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Bar Bar" + }, + "details":{ + "type":"Debit Card", + "description":"Bar", + "posted":"2015-08-14T00:00:00.000Z", + "completed":"2015-08-14T00:00:00.000Z", + "new_balance":"3074.67", + "value":"-27.63" + } + },{ + "id":"ad25f208-8615-4b0a-9724-b8f95b9f135d", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Prezzo" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurants", + "posted":"2015-08-15T00:00:00.000Z", + "completed":"2015-08-15T00:00:00.000Z", + "new_balance":"3036.15", + "value":"-38.52" + } + },{ + "id":"f85f9901-78d0-4a25-81d8-add2d17d49f3", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-08-19T00:00:00.000Z", + "completed":"2015-08-19T00:00:00.000Z", + "new_balance":"3030.89", + "value":"-5.26" + } + },{ + "id":"6376fcdb-d302-43a6-8a5a-2a1f05509407", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Marks and Spencers" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-08-22T00:00:00.000Z", + "completed":"2015-08-22T00:00:00.000Z", + "new_balance":"2982.97", + "value":"-47.92" + } + },{ + "id":"6c563eac-1b75-4eea-b5b1-110623aa898d", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Shell Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling Station", + "posted":"2015-08-26T00:00:00.000Z", + "completed":"2015-08-26T00:00:00.000Z", + "new_balance":"2914.44", + "value":"-68.53" + } + },{ + "id":"359445d6-492d-4eea-bc4e-8aaa2003e0b7", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"British Telecom" + }, + "details":{ + "type":"Direct Debit", + "description":"BT", + "posted":"2015-08-29T00:00:00.000Z", + "completed":"2015-08-29T00:00:00.000Z", + "new_balance":"2880.33", + "value":"-34.11" + } + },{ + "id":"5191d5c4-1851-4169-b152-80a5f07af8b1", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Paid in" + }, + "details":{ + "type":"Paid in", + "description":"Income", + "posted":"2015-08-29T00:00:00.000Z", + "completed":"2015-08-29T00:00:00.000Z", + "new_balance":"5252.81", + "value":"2372.48" + } + },{ + "id":"36fbafcf-409f-4b4a-ae87-26fe1e4bdc40", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"RBS Savings" + }, + "details":{ + "type":"Standing Order", + "description":"Savings", + "posted":"2015-08-30T00:00:00.000Z", + "completed":"2015-08-30T00:00:00.000Z", + "new_balance":"4991.57", + "value":"-261.24" + } + },{ + "id":"e149772c-74b0-42c6-afe1-e9e91ea68bc3", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"British Gas" + }, + "details":{ + "type":"Direct Debit", + "description":"Gas/Elec", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"4870.98", + "value":"-120.59" + } + },{ + "id":"86124761-8e2d-46fd-b51d-b3b0f69d2bf9", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Seven Trent Water" + }, + "details":{ + "type":"Direct Debit", + "description":"Water", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"4846.67", + "value":"-24.31" + } + },{ + "id":"162a922e-6caa-48fb-8b21-f9a4de84d448", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Duddingston Golf Club" + }, + "details":{ + "type":"Direct Debit", + "description":"Monthly Golf membership", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"4791.79", + "value":"-54.88" + } + },{ + "id":"a4aecb48-27ee-4d35-b3ac-6f2e7f44f23e", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Council Tax" + }, + "details":{ + "type":"Direct Debit", + "description":"Council Tax", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"4767.48", + "value":"-24.31" + } + },{ + "id":"85897b9a-17b1-440c-a2db-767cabdc721b", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Marks and Spencers" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-09T00:00:00.000Z", + "completed":"2015-09-09T00:00:00.000Z", + "new_balance":"4699.28", + "value":"-68.20" + } + },{ + "id":"73756f16-7608-4260-a5f7-d49df7a2160d", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Shell Filling Station" + }, + "details":{ + "type":"Debit Card", + "description":"Filling Station", + "posted":"2015-09-09T00:00:00.000Z", + "completed":"2015-09-09T00:00:00.000Z", + "new_balance":"4640.17", + "value":"-59.11" + } + },{ + "id":"59293fee-1c23-4c87-ab44-daed4d62e72f", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"The Cellar Door" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurants", + "posted":"2015-09-17T00:00:00.000Z", + "completed":"2015-09-17T00:00:00.000Z", + "new_balance":"4570.32", + "value":"-69.85" + } + },{ + "id":"7bd27711-650e-43b4-ba93-013e50f6292d", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Debit Card", + "description":"Coffee", + "posted":"2015-09-18T00:00:00.000Z", + "completed":"2015-09-18T00:00:00.000Z", + "new_balance":"4565.62", + "value":"-4.70" + } + },{ + "id":"9e510750-a650-4308-bb6a-874c1a5bd77e", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Debenhams" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-18T00:00:00.000Z", + "completed":"2015-09-18T00:00:00.000Z", + "new_balance":"4466.99", + "value":"-98.63" + } + },{ + "id":"2c653345-9543-47fc-9d3a-7de07992a65a", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Next" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-18T00:00:00.000Z", + "completed":"2015-09-18T00:00:00.000Z", + "new_balance":"4415.82", + "value":"-51.17" + } + },{ + "id":"c6d7dbb2-4331-49f1-99fe-0fac337d48e0", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Pizza Express" + }, + "details":{ + "type":"Debit Card", + "description":"Restaurants", + "posted":"2015-09-18T00:00:00.000Z", + "completed":"2015-09-18T00:00:00.000Z", + "new_balance":"4380.76", + "value":"-35.06" + } + },{ + "id":"549ff0a9-a496-4497-acf3-019df417e7f4", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Marks and Spencers" + }, + "details":{ + "type":"Debit Card", + "description":"Shopping", + "posted":"2015-09-22T00:00:00.000Z", + "completed":"2015-09-22T00:00:00.000Z", + "new_balance":"4302.50", + "value":"-78.26" + } + },{ + "id":"73879ef3-a868-4d63-b7b8-e22a35058b1d", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"British Telecom" + }, + "details":{ + "type":"Direct Debit", + "description":"BT", + "posted":"2015-09-29T00:00:00.000Z", + "completed":"2015-09-29T00:00:00.000Z", + "new_balance":"4268.39", + "value":"-34.11" + } + },{ + "id":"1abcea29-198a-459f-b3b6-9d21b8d46c46", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Paid in" + }, + "details":{ + "type":"Paid in", + "description":"Income", + "posted":"2015-09-29T00:00:00.000Z", + "completed":"2015-09-29T00:00:00.000Z", + "new_balance":"6640.87", + "value":"2372.48" + } + },{ + "id":"4963fa86-faa9-4790-b23a-6dfd01be0543", + "this_account":{ + "id":"e0ec24be-5ab1-4760-9189-ad280228c134", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"RBS Savings" + }, + "details":{ + "type":"Standing Order", + "description":"Savings", + "posted":"2015-09-30T00:00:00.000Z", + "completed":"2015-09-30T00:00:00.000Z", + "new_balance":"6379.63", + "value":"-261.24" + } + },{ + "id":"d25e1b4d-f039-4a4c-9326-6be86c613001", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Bambo Bar" + }, + "details":{ + "type":"Credit Card", + "description":"Bar", + "posted":"2010-09-27T00:00:00.000Z", + "completed":"2010-09-27T00:00:00.000Z", + "new_balance":"8397.68", + "value":"-13.36" + } + },{ + "id":"bbc96ba6-e6cf-4d74-9830-8b76d1b9dad6", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Tesco" + }, + "details":{ + "type":"Credit Card", + "description":"Shopping", + "posted":"2015-09-01T00:00:00.000Z", + "completed":"2015-09-01T00:00:00.000Z", + "new_balance":"8352.35", + "value":"-45.33" + } + },{ + "id":"e2542689-b38c-44dd-9652-bda8b87ea43a", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Tesco Filling Station" + }, + "details":{ + "type":"Credit Card", + "description":"Filling Station", + "posted":"2015-09-02T00:00:00.000Z", + "completed":"2015-09-02T00:00:00.000Z", + "new_balance":"8299.33", + "value":"-53.02" + } + },{ + "id":"a555bf67-faaa-4cb2-8ebc-9c119d799446", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Bambo Bar" + }, + "details":{ + "type":"Credit Card", + "description":"Bar", + "posted":"2015-09-07T00:00:00.000Z", + "completed":"2015-09-07T00:00:00.000Z", + "new_balance":"8240.63", + "value":"-58.70" + } + },{ + "id":"40c01613-1928-4bb1-9f7a-1ccd5e3784ff", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Dominos" + }, + "details":{ + "type":"Credit Card", + "description":"Takeway", + "posted":"2015-09-11T00:00:00.000Z", + "completed":"2015-09-11T00:00:00.000Z", + "new_balance":"8231.60", + "value":"-9.03" + } + },{ + "id":"1134ae57-a35c-4bbc-a6f8-a78f13cc7394", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Credit Card", + "description":"coffee", + "posted":"2015-09-15T00:00:00.000Z", + "completed":"2015-09-15T00:00:00.000Z", + "new_balance":"8227.23", + "value":"-4.37" + } + },{ + "id":"18fa0804-f213-467e-bcf6-80e8aa5c90bb", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Prezzo" + }, + "details":{ + "type":"Credit Card", + "description":"resturant", + "posted":"2015-09-17T00:00:00.000Z", + "completed":"2015-09-17T00:00:00.000Z", + "new_balance":"8192.29", + "value":"-34.94" + } + },{ + "id":"467f9e4c-b431-4991-8950-7055ad576b40", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"H Samuals" + }, + "details":{ + "type":"Credit Card", + "description":"Shopping", + "posted":"2015-09-17T00:00:00.000Z", + "completed":"2015-09-17T00:00:00.000Z", + "new_balance":"7931.05", + "value":"-261.24" + } + },{ + "id":"c86f126e-0d51-4a4c-a3fa-b04246667141", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Zara" + }, + "details":{ + "type":"Credit Card", + "description":"Shopping", + "posted":"2015-09-17T00:00:00.000Z", + "completed":"2015-09-17T00:00:00.000Z", + "new_balance":"7801.81", + "value":"-129.24" + } + },{ + "id":"bb7a0c00-f837-4011-8bac-c98680ae6526", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Starbucks" + }, + "details":{ + "type":"Credit Card", + "description":"coffee", + "posted":"2015-09-19T00:00:00.000Z", + "completed":"2015-09-19T00:00:00.000Z", + "new_balance":"7797.44", + "value":"-4.37" + } + },{ + "id":"7d52ca8b-2171-4ea2-af34-a1a889b6febb", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Showcase Cinema" + }, + "details":{ + "type":"Credit Card", + "description":"Cinema", + "posted":"2015-09-21T00:00:00.000Z", + "completed":"2015-09-21T00:00:00.000Z", + "new_balance":"7778.77", + "value":"-18.67" + } + },{ + "id":"4776b669-cf79-409e-89d7-9bfa2da78438", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"Tesco" + }, + "details":{ + "type":"Credit Card", + "description":"Shopping", + "posted":"2015-09-23T00:00:00.000Z", + "completed":"2015-09-23T00:00:00.000Z", + "new_balance":"7733.44", + "value":"-45.33" + } + },{ + "id":"ffd0f38d-e76e-483a-969b-89a156058116", + "this_account":{ + "id":"4e1c02a1-2f9f-4587-9433-80b949c5a346", + "bank":"obp-bank-y-gh" + }, + "counterparty":{ + "name":"NCP Car Park" + }, + "details":{ + "type":"Credit Card", + "description":"Parking", + "posted":"2015-09-24T00:00:00.000Z", + "completed":"2015-09-24T00:00:00.000Z", + "new_balance":"7724.41", + "value":"-9.03" + } + }], + "branches":[{ + "id":"1234-d640", + "bank_id":"obp-bank-x-gh", + "name":"Head Office", + "address":{ + "line_1":"PO Box 2", + "line_2":"", + "line_3":"", + "city":"5 St. Jane Street", + "county":"", + "state":"", + "post_code":"BB5 1LY", + "country_code":"GB" + }, + "location":{ + "latitude":54.752158, + "longitude":-2.366534 + }, + "meta":{ + "license":{ + "id":"pddl", + "name":"Open Data Commons Public Domain Dedication and License (PDDL)" + } + }, + "lobby":{ + "hours":"Monday: 09:00 - 16:30
Tuesday: 09:00 - 16:30
Wednesday: 09:30 - 16:30
Thursday: 09:00 - 16:30
Friday: 09:00 - 16:30
Saturday: 09:00 - 12:30
Sunday: Closed" + }, + "drive_up":{ + "hours":"" + } + },{ + "id":"7658-23d2", + "bank_id":"obp-bank-x-gh", + "name":"Some Place", + "address":{ + "line_1":"20 London Road", + "line_2":"", + "line_3":"", + "city":"Alderley Edge", + "county":"", + "state":"", + "post_code":"SK9 7EF", + "country_code":"GB" + }, + "location":{ + "latitude":54.300288, + "longitude":-2.236626 + }, + "meta":{ + "license":{ + "id":"pddl", + "name":"Open Data Commons Public Domain Dedication and License (PDDL)" + } + }, + "lobby":{ + "hours":"Monday: 09:30 - 15:00
Tuesday: 09:30 - 15:00
Wednesday: 09:30 - 15:00
Thursday: 10:00 - 15:00
Friday: 09:30 - 15:00
Saturday: Closed
Sunday: Closed" + }, + "drive_up":{ + "hours":"" + } + },{ + "id":"18484-8e5b", + "bank_id":"obp-bank-y-gh", + "name":"Other Place", + "address":{ + "line_1":"Warrington Street", + "line_2":"", + "line_3":"", + "city":"Ashton Under Lyne", + "county":"", + "state":"", + "post_code":"OL6 6JL", + "country_code":"GB" + }, + "location":{ + "latitude":54.488144, + "longitude":-2.093318 + }, + "meta":{ + "license":{ + "id":"pddl", + "name":"Open Data Commons Public Domain Dedication and License (PDDL)" + } + }, + "lobby":{ + "hours":"Monday: 09:00 - 17:00
Tuesday: 09:00 - 17:00
Wednesday: 09:30 - 17:00
Thursday: 09:00 - 17:00
Friday: 09:00 - 17:00
Saturday: 09:00 - 13:00
Sunday: Closed" + }, + "drive_up":{ + "hours":"" + } + }], + "atms":[{ + "id":"24242-646b", + "bank_id":"obp-bank-x-gh", + "name":"Here place", + "address":{ + "line_1":"12 NORTH-WEST CIRCUS PLACE", + "line_2":"", + "line_3":"", + "city":"EDINBURGH", + "county":"GBR", + "state":"", + "post_code":"EH3 6SX", + "country_code":"GB" + }, + "location":{ + "latitude":55.967547, + "longitude":-3.216868 + }, + "meta":{ + "license":{ + "id":"copyrightrbs2015", + "name":"Copyright 2015 Royal Bank of Scotland" + } + } + },{ + "id":"234-6b91", + "bank_id":"obp-bank-y-gh", + "name":"Somewhere there", + "address":{ + "line_1":"MARKET PLACE", + "line_2":"", + "line_3":"", + "city":"EYEMOUTH", + "county":"GBR", + "state":"", + "post_code":"TD14 5HE", + "country_code":"GB" + }, + "location":{ + "latitude":55.882244, + "longitude":-2.099188 + }, + "meta":{ + "license":{ + "id":"copyrightrbs2015", + "name":"Copyright 2015 Royal Bank of Scotland" + } + } + },{ + "id":"78987-ae25", + "bank_id":"obp-bank-y-gh", + "name":"Somewhere here", + "address":{ + "line_1":"540A LANARK ROAD", + "line_2":"", + "line_3":"", + "city":"EDINBURGH", + "county":"GBR", + "state":"", + "post_code":"EH14 5EL", + "country_code":"GB" + }, + "location":{ + "latitude":55.912969, + "longitude":-3.38662 + }, + "meta":{ + "license":{ + "id":"copyrightrbs2015", + "name":"Copyright 2015 Royal Bank of Scotland" + } + } + }], + "products":[{ + "bank_id":"obp-bank-x-gh", + "code":"M35-3857", + "name":"OFFSET FLEXIBLE MORTGAGE", + "category":"Mortgage", + "family":"Mortgage", + "super_family":"Lending", + "more_info_url":"www.example.com", + "meta":{ + "license":{ + "id":"copyrightrbs2015", + "name":"Copyright 2015 Royal Bank of Scotland" + } + } + },{ + "bank_id":"obp-bank-x-gh", + "code":"H44-3e4e", + "name":"SUPER GOLD", + "category":"Account", + "family":"Service", + "super_family":"Service", + "more_info_url":"www.example.com", + "meta":{ + "license":{ + "id":"copyrightrbs2015", + "name":"Copyright 2015 Royal Bank of Scotland" + } + } + },{ + "bank_id":"obp-bank-x-gh", + "code":"J87-6157", + "name":"Premier Mastercard", + "category":"Credit Card", + "family":"Credit Card", + "super_family":"Lending", + "more_info_url":"www.example.com", + "meta":{ + "license":{ + "id":"copyrightrbs2015", + "name":"Copyright 2015 Royal Bank of Scotland" + } + } + },{ + "bank_id":"obp-bank-x-gh", + "code":"JF-8491", + "name":"Loan Quote", + "category":"Loan", + "family":"Loan", + "super_family":"Lending", + "more_info_url":"www.example.com", + "meta":{ + "license":{ + "id":"copyrightrbs2015", + "name":"Copyright 2015 Royal Bank of Scotland" + } + } + },{ + "bank_id":"obp-bank-x-gh", + "code":"M55-3a87", + "name":"Generic MTA/Current Account", + "category":"Current Accounts", + "family":"Service", + "super_family":"Service", + "more_info_url":"www.example.com", + "meta":{ + "license":{ + "id":"copyrightrbs2015", + "name":"Copyright 2015 Royal Bank of Scotland" + } + } + },{ + "bank_id":"obp-bank-x-gh", + "code":"KJH-8361", + "name":"Generic Savings", + "category":"Savings", + "family":"Credit", + "super_family":"Credit", + "more_info_url":"www.example.com", + "meta":{ + "license":{ + "id":"copyrightrbs2015", + "name":"Copyright 2015 Royal Bank of Scotland" + } + } + },{ + "bank_id":"obp-bank-x-gh", + "code":"FDFD-6ac4", + "name":"Generic Overdraft Product", + "category":"Overdraft", + "family":"Loan", + "super_family":"Lending", + "more_info_url":"www.example.com", + "meta":{ + "license":{ + "id":"copyrightrbs2015", + "name":"Copyright 2015 Royal Bank of Scotland" + } + } + },{ + "bank_id":"obp-bank-x-gh", + "code":"DFD-b0cf", + "name":"Generic MTA Upgrades Product", + "category":"MTA Upgrades", + "family":"MTA Upgrades", + "super_family":"MTA Upgrades", + "more_info_url":"www.example.com", + "meta":{ + "license":{ + "id":"copyrightrbs2015", + "name":"Copyright 2015 Royal Bank of Scotland" + } + } + },{ + "bank_id":"obp-bank-x-gh", + "code":"DS-0267", + "name":"Generic Credit Card Product", + "category":"Credit Card", + "family":"Credit Card", + "super_family":"Lending", + "more_info_url":"www.example.com", + "meta":{ + "license":{ + "id":"copyrightrbs2015", + "name":"Copyright 2015 Royal Bank of Scotland" + } + } + },{ + "bank_id":"obp-bank-y-gh", + "code":"FDS-85a6", + "name":"OFFSET FLEXIBLE MORTGAGE", + "category":"Mortgage", + "family":"Mortgage", + "super_family":"Lending", + "more_info_url":"www.example.com", + "meta":{ + "license":{ + "id":"copyrightrbs2015", + "name":"Copyright 2015 Royal Bank of Scotland" + } + } + },{ + "bank_id":"obp-bank-y-gh", + "code":"F45-c3dd", + "name":"RESERVE ACCOUNT", + "category":"Account", + "family":"Service", + "super_family":"Service", + "more_info_url":"www.example.com", + "meta":{ + "license":{ + "id":"copyrightrbs2015", + "name":"Copyright 2015 Royal Bank of Scotland" + } + } + },{ + "bank_id":"obp-bank-y-gh", + "code":"FDF-6827", + "name":"Red Mastercard", + "category":"Credit Card", + "family":"Credit Card", + "super_family":"Lending", + "more_info_url":"www.example.com", + "meta":{ + "license":{ + "id":"copyrightrbs2015", + "name":"Copyright 2015 Royal Bank of Scotland" + } + } + },{ + "bank_id":"obp-bank-y-gh", + "code":"AA-f370", + "name":"Loan Quote", + "category":"Loan", + "family":"Loan", + "super_family":"Lending", + "more_info_url":"www.example.com", + "meta":{ + "license":{ + "id":"copyrightrbs2015", + "name":"Copyright 2015 Royal Bank of Scotland" + } + } + },{ + "bank_id":"obp-bank-y-gh", + "code":"SD4-6c17", + "name":"Generic MTA/Current Account", + "category":"Current Accounts", + "family":"Account", + "super_family":"Service", + "more_info_url":"www.example.com", + "meta":{ + "license":{ + "id":"copyrightrbs2015", + "name":"Copyright 2015 Royal Bank of Scotland" + } + } + },{ + "bank_id":"obp-bank-y-gh", + "code":"FD2-6ee5", + "name":"Generic Savings", + "category":"Savings", + "family":"Credit", + "super_family":"Credit", + "more_info_url":"www.example.com", + "meta":{ + "license":{ + "id":"copyrightrbs2015", + "name":"Copyright 2015 Royal Bank of Scotland" + } + } + },{ + "bank_id":"obp-bank-y-gh", + "code":"DF4-a232", + "name":"Generic Overdraft Product", + "category":"Overdraft", + "family":"Loan", + "super_family":"Lending", + "more_info_url":"www.example.com", + "meta":{ + "license":{ + "id":"copyrightrbs2015", + "name":"Copyright 2015 Royal Bank of Scotland" + } + } + },{ + "bank_id":"obp-bank-y-gh", + "code":"FD5-ee8b", + "name":"Generic MTA Upgrades Product", + "category":"MTA Upgrades", + "family":"MTA Upgrades", + "super_family":"MTA Upgrades", + "more_info_url":"www.example.com", + "meta":{ + "license":{ + "id":"copyrightrbs2015", + "name":"Copyright 2015 Royal Bank of Scotland" + } + } + },{ + "bank_id":"obp-bank-y-gh", + "code":"DFD-0a61", + "name":"Generic Credit Card Product", + "category":"Credit Cards", + "family":"Credit Card", + "super_family":"Lending", + "more_info_url":"www.example.com", + "meta":{ + "license":{ + "id":"copyrightrbs2015", + "name":"Copyright 2015 Royal Bank of Scotland" + } + } + },{ + "bank_id":"obp-bank-y-gh", + "code":"DF4-7d76", + "name":"Generic MTA Upgrades Product", + "category":"MTA Upgrades", + "family":"MTA Upgrades", + "super_family":"MTA Upgrades", + "more_info_url":"www.example.com", + "meta":{ + "license":{ + "id":"copyrightrbs2015", + "name":"Copyright 2015 Royal Bank of Scotland" + } + } + },{ + "bank_id":"obp-bank-y-gh", + "code":"FD1-8a42", + "name":"Generic Credit Card Product", + "category":"Credit Cards", + "family":"Credit Card", + "super_family":"Lending", + "more_info_url":"www.example.com", + "meta":{ + "license":{ + "id":"copyrightrbs2015", + "name":"Copyright 2015 Royal Bank of Scotland" + } + } + }], + "crm_events":[{ + "id":"42f69509-3de6-402b-aa1e-c9e919828824", + "bank_id":"obp-bank-x-gh", + "customer":{ + "name":"Dennis X.0.GH", + "number":"bank-x92320234" + }, + "category":"TEST CATEGORY", + "detail":"TEST DETAIL", + "channel":"Test Channel", + "actual_date":"2015-02-14T00:00:00.000Z" + },{ + "id":"2481f7cb-d368-4cc6-9e67-0a78127bade2", + "bank_id":"obp-bank-y-gh", + "customer":{ + "name":"Dennis Y.9.GH", + "number":"bank-y121217504" + }, + "category":"TEST CATEGORY", + "detail":"TEST DETAIL", + "channel":"Test Channel", + "actual_date":"2015-02-14T00:00:00.000Z" + }] + } diff --git a/src/main/scala/code/api/util/APIUtil.scala b/src/main/scala/code/api/util/APIUtil.scala index 2799c9f8c..3943212a8 100644 --- a/src/main/scala/code/api/util/APIUtil.scala +++ b/src/main/scala/code/api/util/APIUtil.scala @@ -32,25 +32,67 @@ Berlin 13359, Germany package code.api.util +import code.api.util.APIUtil.ApiLink import code.api.v1_2.ErrorMessage import code.metrics.APIMetrics -import net.liftweb.common.{Full, Loggable} -import net.liftweb.http.{JsonResponse, S} +import code.model._ +import net.liftweb.common.{Box, Full, Loggable} import net.liftweb.http.js.JE.JsRaw import net.liftweb.http.js.JsExp +import net.liftweb.http.{JsonResponse, Req, S} import net.liftweb.json.Extraction -import net.liftweb.json.JsonAST.{JObject, JValue} - - - - +import net.liftweb.json._ +import net.liftweb.json.JsonAST.JValue import net.liftweb.util.Helpers._ - -import bootstrap.liftweb.Boot import net.liftweb.util.Props import scala.collection.JavaConversions.asScalaSet +import scala.collection.mutable.ArrayBuffer + +import net.liftweb.http.CurrentReq +import code.api.Constant._ + + + + + +object ErrorMessages { + + // Infrastructure / config messages + val HostnameNotSpecified = "OBP-00001: Hostname not specified. Could not get hostname from Props. Please edit your props file. Here are some example settings: hostname=http://127.0.0.1:8080 or hostname=https://www.example.com" + + // General messages + val InvalidJsonFormat = "OBP-10001: Incorrect json format." + val InvalidNumber = "OBP-10002: Invalid Number. Could not convert value to a number." + val InvalidInitalBalance = "OBP-10003: Invalid Number. Initial balance must be a number, e.g 1000.00" + + + + // Authentication / Authorisation messages + val UserNotLoggedIn = "OBP-20001: User not logged in. Authentication is required!" + + val DirectLoginMissingParameters = "OBP-20002: These DirectLogin parameters are missing: " + val DirectLoginInvalidToken = "OBP-20003: This DirectLogin token is invalid or expired: " + + + val InvalidLoginCredentials = "OBP-20004: Invalid login credentials. Check username/password." + + + + // Resource related messages + val BankNotFound = "OBP-30001: Bank not found. Please specify a valid value for BANK_ID." + val CustomerNotFound = "OBP-30002: Customer not found. Please specify a valid value for CUSTOMER_NUMBER." + + val AccountNotFound = "OBP-30003: Account not found. Please specify a valid value for ACCOUNT_ID." + val CounterpartyNotFound = "OBP-30004: Counterparty not found." + + val ViewNotFound = "OBP-30005: View not found for Account. Please specify a valid value for VIEW_ID" + +} + + + object APIUtil extends Loggable { @@ -64,6 +106,16 @@ object APIUtil extends Loggable { case _ => "GET" } + def isThereDirectLoginHeader : Boolean = { + S.request match { + case Full(a) => a.header("Authorization") match { + case Full(parameters) => parameters.contains("DirectLogin") + case _ => false + } + case _ => false + } + } + def isThereAnOAuthHeader : Boolean = { S.request match { case Full(a) => a.header("Authorization") match { @@ -76,6 +128,7 @@ object APIUtil extends Loggable { def logAPICall = { if(Props.getBool("write_metrics", false)) { + // TODO This should use Elastic Search or Kafka not an RDBMS APIMetrics.apiMetrics.vend.saveMetric(S.uriAndQueryString.getOrElse(""), (now: TimeSpan)) } } @@ -132,7 +185,7 @@ object APIUtil extends Loggable { import org.apache.http.protocol.HTTP.UTF_8 import scala.collection.Map - import scala.collection.immutable.{TreeMap, Map => IMap} + import scala.collection.immutable.{Map => IMap, TreeMap} import scala.collection.mutable.Set case class Consumer(key: String, secret: String) @@ -261,16 +314,243 @@ object APIUtil extends Loggable { } /* - Used to document API calls / resources. correct place for this? + Used to document API calls / resources. + + TODO Can we extract apiVersion, apiFunction, requestVerb and requestUrl from partialFunction? + */ + + // Used to tag Resource Docs + case class ResourceDocTag(tag: String) + + val apiTagPayment = ResourceDocTag("Payments") + val apiTagApiInfo = ResourceDocTag("APIInfo") + val apiTagBanks = ResourceDocTag("Banks") + val apiTagAccounts = ResourceDocTag("Accounts") + val apiTagPublicData = ResourceDocTag("PublicData") + val apiTagPrivateData = ResourceDocTag("PrivateData") + val apiTagTransactions = ResourceDocTag("Transactions") + val apiTagMetaData = ResourceDocTag("Meta Data") + val apiTagViews = ResourceDocTag("Views") + val apiTagEntitlements = ResourceDocTag("Entitlements") + val apiTagOwnerRequired = ResourceDocTag("OwnerViewRequired") + val apiTagCounterparties = ResourceDocTag("Counterparties") + val apiTagKyc = ResourceDocTag("KYC") + val apiTagCustomer = ResourceDocTag("Customer") + + + // Used to document the API calls case class ResourceDoc( - apiVersion: String, // TODO: Constrain to certain strings? - apiFunction: String, // The partial function that implements this resource. Could use it to link to the source code that implements the call - requestVerb: String, // GET, POST etc. TODO: Constrain to GET, POST etc. - requestUrl: String, // The URL (not including /obp/vX.X). Starts with / No trailing slash. TODO Constrain the string? - summary: String, // A summary of the call (originally taken from code comment) SHOULD be under 120 chars to be inline with Swagger - description: String, // Longer description (originally taken from github wiki) - exampleRequestBody: JValue, // An example of the body required (maybe empty) - successResponseBody: JValue, // A successful response body - errorResponseBodies: List[JValue]) // Possible error responses + partialFunction : PartialFunction[Req, Box[User] => Box[JsonResponse]], + apiVersion: String, // TODO: Constrain to certain strings? + apiFunction: String, // The partial function that implements this resource. Could use it to link to the source code that implements the call + requestVerb: String, // GET, POST etc. TODO: Constrain to GET, POST etc. + requestUrl: String, // The URL (not including /obp/vX.X). Starts with / No trailing slash. TODO Constrain the string? + summary: String, // A summary of the call (originally taken from code comment) SHOULD be under 120 chars to be inline with Swagger + description: String, // Longer description (originally taken from github wiki) + exampleRequestBody: JValue, // An example of the body required (maybe empty) + successResponseBody: JValue, // A successful response body + errorResponseBodies: List[JValue], // Possible error responses + isCore: Boolean, + isPSD2: Boolean, + tags: List[ResourceDocTag] + ) + + // Define relations between API end points. Used to create _links in the JSON and maybe later for API Explorer browsing + case class ApiRelation( + fromPF : PartialFunction[Req, Box[User] => Box[JsonResponse]], + toPF : PartialFunction[Req, Box[User] => Box[JsonResponse]], + rel : String + ) + + // Populated from Resource Doc and ApiRelation + case class InternalApiLink( + fromPF : PartialFunction[Req, Box[User] => Box[JsonResponse]], + toPF : PartialFunction[Req, Box[User] => Box[JsonResponse]], + rel : String, + requestUrl: String + ) + + // Used to pass context of current API call to the function that generates links for related Api calls. + case class DataContext( + user : Box[User], + bankId : Option[BankId], + accountId: Option[AccountId], + viewId: Option[ViewId], + counterpartyId: Option[CounterpartyId], + transactionId: Option[TransactionId] +) + + case class CallerContext( + caller : PartialFunction[Req, Box[User] => Box[JsonResponse]] + ) + + case class CodeContext( + resourceDocsArrayBuffer : ArrayBuffer[ResourceDoc], + relationsArrayBuffer : ArrayBuffer[ApiRelation] + ) + + + + case class ApiLink( + rel: String, + href: String + ) + + case class LinksJSON( + _links: List[ApiLink] + ) + + case class ResultAndLinksJSON( + result : JValue, + _links: List[ApiLink] + ) + + + def createResultAndLinksJSON(result : JValue, links : List[ApiLink] ) : ResultAndLinksJSON = { + new ResultAndLinksJSON( + result, + links + ) + } + + + + def authenticationRequiredMessage(authRequired: Boolean) : String = + authRequired match { + case true => "Authentication IS required" + case false => "Authentication is NOT required" + } + + + + def apiVersionWithV(apiVersion : String) : String = { + // TODO Define a list of supported versions (put in Constant) and constrain the input + // Append v and replace _ with . + s"v${apiVersion.replaceAll("_",".")}" + } + + def fullBaseUrl : String = { + val crv = CurrentReq.value + val apiPathZeroFromRequest = crv.path.partPath(0) + if (apiPathZeroFromRequest != ApiPathZero) throw new Exception("Configured ApiPathZero is not the same as the actual.") + + val path = s"$HostName/$ApiPathZero" + path + } + + +// Modify URL replacing placeholders for Ids + def contextModifiedUrl(url: String, context: DataContext) = { + + // Potentially replace BANK_ID + val url2: String = context.bankId match { + case Some(x) => url.replaceAll("BANK_ID", x.value) + case _ => url + } + + val url3: String = context.accountId match { + // Take care *not* to change OTHER_ACCOUNT_ID HERE + case Some(x) => url2.replaceAll("/ACCOUNT_ID", s"/${x.value}").replaceAll("COUNTERPARTY_ID", x.value) + case _ => url2 + } + + val url4: String = context.viewId match { + case Some(x) => url3.replaceAll("VIEW_ID", {x.value}) + case _ => url3 + } + + val url5: String = context.counterpartyId match { + // Change OTHER_ACCOUNT_ID or COUNTERPARTY_ID + case Some(x) => url4.replaceAll("OTHER_ACCOUNT_ID", x.value).replaceAll("COUNTERPARTY_ID", x.value) + case _ => url4 + } + + val url6: String = context.transactionId match { + case Some(x) => url5.replaceAll("TRANSACTION_ID", x.value) + case _ => url5 + } + + // Add host, port, prefix, version. + + // not correct because call could be in other version + val fullUrl = s"$fullBaseUrl$url6" + + fullUrl + } + + + def getApiLinkTemplates(callerContext: CallerContext, + codeContext: CodeContext + ) : List[InternalApiLink] = { + + + + // Relations of the API version where the caller is defined. + val relations = codeContext.relationsArrayBuffer.toList + + // Resource Docs + // Note: This doesn't allow linking to calls in earlier versions of the API + // TODO: Fix me + val resourceDocs = codeContext.resourceDocsArrayBuffer + + val pf = callerContext.caller + + val internalApiLinks: List[InternalApiLink] = for { + relation <- relations.filter(r => r.fromPF == pf) + toResourceDoc <- resourceDocs.find(rd => rd.partialFunction == relation.toPF) + } + yield new InternalApiLink( + pf, + toResourceDoc.partialFunction, + relation.rel, + // Add the vVersion to the documented url + s"/${apiVersionWithV(toResourceDoc.apiVersion)}${toResourceDoc.requestUrl}" + ) + internalApiLinks + } + + + + // This is not currently including "templated" attribute + def halLinkFragment (link: ApiLink) : String = { + "\"" + link.rel +"\": { \"href\": \"" +link.href + "\" }" + } + + + // Since HAL links can't be represented via a case class, (they have dynamic attributes rather than a list) we need to generate them here. + def buildHalLinks(links: List[ApiLink]): JValue = { + + val halLinksString = links match { + case head :: tail => tail.foldLeft("{"){(r: String, c: ApiLink) => ( r + " " + halLinkFragment(c) + " ," ) } + halLinkFragment(head) + "}" + case Nil => "{}" + } + parse(halLinksString) + } + + + // Returns API links (a list of them) that have placeholders (e.g. BANK_ID) replaced by values (e.g. ulster-bank) + def getApiLinks(callerContext: CallerContext, codeContext: CodeContext, dataContext: DataContext) : List[ApiLink] = { + val templates = getApiLinkTemplates(callerContext, codeContext) + // Replace place holders in the urls like BANK_ID with the current value e.g. 'ulster-bank' and return as ApiLinks for external consumption + val links = templates.map(i => ApiLink(i.rel, + contextModifiedUrl(i.requestUrl, dataContext) ) + ) + links + } + + + // Returns links formatted at objects. + def getHalLinks(callerContext: CallerContext, codeContext: CodeContext, dataContext: DataContext) : JValue = { + val links = getApiLinks(callerContext, codeContext, dataContext) + getHalLinksFromApiLinks(links) + } + + + + def getHalLinksFromApiLinks(links: List[ApiLink]) : JValue = { + val halLinksJson = buildHalLinks(links) + halLinksJson + } + } diff --git a/src/main/scala/code/api/v1_2_1/APIMethods121.scala b/src/main/scala/code/api/v1_2_1/APIMethods121.scala index d13952320..ee55bd871 100644 --- a/src/main/scala/code/api/v1_2_1/APIMethods121.scala +++ b/src/main/scala/code/api/v1_2_1/APIMethods121.scala @@ -10,7 +10,7 @@ import APIUtil._ import net.liftweb.util.Helpers._ import net.liftweb.http.rest.RestHelper import java.net.URL -import net.liftweb.util.Props +import net.liftweb.util.{True, Props} import code.bankconnectors._ import code.bankconnectors.OBPOffset import code.bankconnectors.OBPFromDate @@ -24,6 +24,11 @@ import scala.collection.mutable.ArrayBuffer // Makes JValue assignment to Nil work import net.liftweb.json.JsonDSL._ + + + + + case class MakePaymentJson( bank_id : String, account_id : String, @@ -73,8 +78,8 @@ trait APIMethods121 { val emptyObjectJson : JValue = Nil val apiVersion : String = "1_2_1" - resourceDocs += ResourceDoc( + root(apiVersion), apiVersion, "root", "GET", @@ -87,7 +92,10 @@ trait APIMethods121 { |* Git Commit""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + true, + false, + apiTagApiInfo :: Nil) def root(apiVersion : String) : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { case Nil JsonGet json => { @@ -104,11 +112,12 @@ trait APIMethods121 { resourceDocs += ResourceDoc( + allBanks, apiVersion, "allBanks", "GET", "/banks", - "Returns all banks available on this API instance", + "Get banks on this API instance", """Returns a list of banks supported on this server: | |* ID used as parameter in URLs @@ -117,7 +126,10 @@ trait APIMethods121 { |* Website""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + true, + false, + apiTagBanks :: Nil) lazy val allBanks : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //get banks @@ -137,6 +149,7 @@ trait APIMethods121 { resourceDocs += ResourceDoc( + bankById, apiVersion, "bankById", "GET", @@ -149,7 +162,10 @@ trait APIMethods121 { |* Website""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + true, + false, + apiTagBanks :: Nil) lazy val bankById : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { @@ -167,11 +183,12 @@ trait APIMethods121 { resourceDocs += ResourceDoc( + allAccountsAllBanks, apiVersion, "allAccountsAllBanks", "GET", "/accounts", - "Get all accounts a user has access to at all banks (private + public)", + "Get accounts at all banks (Authenticated + Anonymous access).", """Returns the list of accounts at that the user has access to at all banks. |For each account the API returns the account ID and the available views. | @@ -185,7 +202,10 @@ trait APIMethods121 { |""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + true, + true, + apiTagAccounts :: Nil) lazy val allAccountsAllBanks : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //get accounts for all banks (private + public) @@ -196,18 +216,22 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + privateAccountsAllBanks, apiVersion, "privateAccountsAllBanks", "GET", "/accounts/private", - "Get private accounts for all banks.", + "Get private accounts at all banks (Authenticated access).", """Returns the list of private (non-public) accounts the user has access to at all banks. |For each account the API returns the ID and the available views. | |Authentication via OAuth is required.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + true, + true, + apiTagAccounts :: Nil) lazy val privateAccountsAllBanks : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //get private accounts for all banks @@ -223,16 +247,20 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + publicAccountsAllBanks, apiVersion, "publicAccountsAllBanks", "GET", "/accounts/public", - "Get public accounts for all banks.", + "Get public accounts at all banks (Anonymous access).", """Returns the list of private (non-public) accounts the user has access to at all banks. |For each account the API returns the ID and the available views. Authentication via OAuth is required.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + apiTagAccounts :: Nil) lazy val publicAccountsAllBanks : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //get public accounts for all banks @@ -244,11 +272,12 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + allAccountsAtOneBank, apiVersion, "allAccountsAtOneBank", "GET", "/banks/BANK_ID/accounts", - "Get accounts for a single bank (private + public).", + "Get accounts at one bank (Autheneticated + Anonymous access).", """Returns the list of accounts at BANK_ID that the user has access to. |For each account the API returns the account ID and the available views. | @@ -260,7 +289,10 @@ trait APIMethods121 { """, emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + apiTagAccounts :: Nil) lazy val allAccountsAtOneBank : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //get accounts for a single bank (private + public) @@ -276,18 +308,22 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + privateAccountsAtOneBank, apiVersion, "privateAccountsAtOneBank", "GET", "/banks/BANK_ID/accounts/private", - "Get private accounts for a single bank.", + "Get private accounts at one bank (Authenticated access).", """Returns the list of private (non-public) accounts at BANK_ID that the user has access to. |For each account the API returns the ID and the available views. | |Authentication via OAuth is required.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + true, + true, + apiTagAccounts :: Nil) lazy val privateAccountsAtOneBank : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //get private accounts for a single bank @@ -304,17 +340,21 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + publicAccountsAtOneBank, apiVersion, "publicAccountsAtOneBank", "GET", "/banks/BANK_ID/accounts/public", - "Get public accounts for a single bank.", + "Get public accounts at one bank (Anonymous access).", """Returns a list of the public accounts at BANK_ID. For each account the API returns the ID and the available views. | |Authentication via OAuth is not required.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + apiTagAccounts :: apiTagPublicData :: Nil) lazy val publicAccountsAtOneBank : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //get public accounts for a single bank @@ -330,6 +370,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + accountById, apiVersion, "accountById", "GET", @@ -349,7 +390,10 @@ trait APIMethods121 { |OAuth authentication is required if the 'is_public' field in view (VIEW_ID) is not set to `true`.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + true, + apiTagAccounts :: Nil) lazy val accountById : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //get account by id @@ -369,6 +413,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + updateAccountLabel, apiVersion, "updateAccountLabel", "POST", @@ -377,7 +422,10 @@ trait APIMethods121 { "", Extraction.decompose(UpdateAccountJSON("ACCOUNT_ID of the account we want to update", "New label", "BANK_ID")), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagAccounts, apiTagMetaData)) lazy val updateAccountLabel : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //change account label @@ -396,6 +444,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + getViewsForBankAccount, apiVersion, "getViewsForBankAccount", "GET", @@ -428,7 +477,10 @@ trait APIMethods121 { |OAuth authentication is required and the user needs to have access to the owner view.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagAccounts, apiTagViews)) lazy val getViewsForBankAccount : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //get the available views on an bank account @@ -446,6 +498,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + createViewForBankAccount, apiVersion, "createViewForBankAccount", "POST", @@ -466,7 +519,10 @@ trait APIMethods121 { | The 'allowed_actions' field is a list containing the name of the actions allowed on this view, all the actions contained will be set to `true` on the view creation, the rest will be set to `false`.""", Extraction.decompose(ViewCreationJSON("Name of view to create", "Description of view (this example is public, uses the public alias, and has limited access to account data)", true, "_public_", true, List("can_see_transaction_start_date", "can_see_bank_account_label", "can_see_tags"))), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagAccounts, apiTagViews)) lazy val createViewForBankAccount : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //creates a view on an bank account @@ -485,6 +541,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + updateViewForBankAccount, apiVersion, "updateViewForBankAccount", "PUT", @@ -498,7 +555,10 @@ trait APIMethods121 { |of a view is not editable (it is only set when a view is created)""", Extraction.decompose(ViewUpdateData("New description of view", false, "_public_", true, List("can_see_transaction_start_date", "can_see_bank_account_label"))), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagAccounts, apiTagViews)) lazy val updateViewForBankAccount : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //updates a view on a bank account @@ -517,6 +577,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + deleteViewForBankAccount, apiVersion, "deleteViewForBankAccount", "DELETE", @@ -525,7 +586,10 @@ trait APIMethods121 { "", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagAccounts, apiTagViews)) lazy val deleteViewForBankAccount : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //deletes a view on an bank account @@ -540,6 +604,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + getPermissionsForBankAccount, apiVersion, "getPermissionsForBankAccount", "GET", @@ -550,7 +615,11 @@ trait APIMethods121 { |OAuth authentication is required and the user needs to have access to the owner view.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagAccounts, apiTagViews, apiTagEntitlements) + ) lazy val getPermissionsForBankAccount : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //get access @@ -568,6 +637,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + getPermissionForUserForBankAccount, apiVersion, "getPermissionForUserForBankAccount", "GET", @@ -579,7 +649,10 @@ trait APIMethods121 { |OAuth authentication is required and the user needs to have access to the owner view.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagAccounts, apiTagViews, apiTagEntitlements)) lazy val getPermissionForUserForBankAccount : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //get access for specific user @@ -597,6 +670,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + addPermissionForUserForBankAccountForMultipleViews, apiVersion, "addPermissionForUserForBankAccountForMultipleViews", "POST", @@ -609,7 +683,10 @@ trait APIMethods121 { |OAuth authentication is required and the user needs to have access to the owner view.""", Extraction.decompose(ViewIdsJson(List("owner","auditor","investor"))), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagAccounts, apiTagViews, apiTagEntitlements, apiTagOwnerRequired)) lazy val addPermissionForUserForBankAccountForMultipleViews : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //add access for specific user to a list of views @@ -628,6 +705,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + addPermissionForUserForBankAccountForOneView, apiVersion, "addPermissionForUserForBankAccountForOneView", "POST", @@ -640,7 +718,10 @@ trait APIMethods121 { |Granting access to a public view will return an error message, as the user already has access.""", emptyObjectJson, // No Json body required emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagAccounts, apiTagViews, apiTagEntitlements, apiTagOwnerRequired)) lazy val addPermissionForUserForBankAccountForOneView : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //add access for specific user to a specific view @@ -659,6 +740,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + removePermissionForUserForBankAccountForOneView, apiVersion, "removePermissionForUserForBankAccountForOneView", "DELETE", @@ -671,7 +753,10 @@ trait APIMethods121 { |OAuth authentication is required and the user needs to have access to the owner view.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagAccounts, apiTagViews, apiTagEntitlements, apiTagOwnerRequired)) lazy val removePermissionForUserForBankAccountForOneView : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //delete access for specific user to one view @@ -687,6 +772,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + removePermissionForUserForBankAccountForAllViews, apiVersion, "removePermissionForUserForBankAccountForAllViews", "DELETE", @@ -697,7 +783,10 @@ trait APIMethods121 { |OAuth authentication is required and the user needs to have access to the owner view.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagAccounts, apiTagViews, apiTagEntitlements, apiTagOwnerRequired)) lazy val removePermissionForUserForBankAccountForAllViews : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //delete access for specific user to all the views @@ -713,6 +802,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + getCounterpartiesForBankAccount, apiVersion, "getCounterpartiesForBankAccount", "GET", @@ -723,7 +813,10 @@ trait APIMethods121 { |OAuth authentication is required if the view VIEW_ID is not public.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagAccounts, apiTagCounterparties)) lazy val getCounterpartiesForBankAccount : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //get other accounts for one account @@ -741,6 +834,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + getCounterpartyByIdForBankAccount, apiVersion, "getCounterpartyByIdForBankAccount", "GET", @@ -751,7 +845,10 @@ trait APIMethods121 { |OAuth authentication is required if the view is not public.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagAccounts, apiTagCounterparties)) lazy val getCounterpartyByIdForBankAccount : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //get one other account by id @@ -769,6 +866,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + getCounterpartyMetadata, apiVersion, "getCounterpartyMetadata", "GET", @@ -779,7 +877,10 @@ trait APIMethods121 { |Authentication via OAuth is required if the view is not public.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val getCounterpartyMetadata : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //get metadata of one other account @@ -798,6 +899,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + getCounterpartyPublicAlias, apiVersion, "getCounterpartyPublicAlias", "GET", @@ -808,7 +910,10 @@ trait APIMethods121 { |OAuth authentication is required if the view is not public.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val getCounterpartyPublicAlias : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //get public alias of other bank account @@ -828,6 +933,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + addCounterpartyPublicAlias, apiVersion, "addCounterpartyPublicAlias", "POST", @@ -843,7 +949,10 @@ trait APIMethods121 { |The VIEW_ID parameter should be a view the caller is permitted to access to and that has permission to create public aliases.""", Extraction.decompose(AliasJSON("An Alias")), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val addCounterpartyPublicAlias : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //add public alias to other bank account @@ -864,6 +973,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + updateCounterpartyPublicAlias, apiVersion, "updateCounterpartyPublicAlias", "PUT", @@ -874,7 +984,10 @@ trait APIMethods121 { |OAuth authentication is required if the view is not public.""", Extraction.decompose(AliasJSON("An Alias")), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val updateCounterpartyPublicAlias : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //update public alias of other bank account @@ -895,6 +1008,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + deleteCounterpartyPublicAlias, apiVersion, "deleteCounterpartyPublicAlias", "DELETE", @@ -905,7 +1019,10 @@ trait APIMethods121 { |OAuth authentication is required if the view is not public.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val deleteCounterpartyPublicAlias : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //delete public alias of other bank account @@ -923,6 +1040,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + getCounterpartyPrivateAlias, apiVersion, "getCounterpartyPrivateAlias", "GET", @@ -933,7 +1051,10 @@ trait APIMethods121 { |OAuth authentication is required if the view is not public.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val getCounterpartyPrivateAlias : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //get private alias of other bank account @@ -953,6 +1074,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + addCounterpartyPrivateAlias, apiVersion, "addCounterpartyPrivateAlias", "POST", @@ -963,7 +1085,10 @@ trait APIMethods121 { |OAuth authentication is required if the view is not public.""", Extraction.decompose(AliasJSON("An Alias")), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val addCounterpartyPrivateAlias : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //add private alias to other bank account @@ -985,6 +1110,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + updateCounterpartyPrivateAlias, apiVersion, "updateCounterpartyPrivateAlias", "PUT", @@ -995,7 +1121,10 @@ trait APIMethods121 { |OAuth authentication is required if the view is not public.""", Extraction.decompose(AliasJSON("An Alias")), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val updateCounterpartyPrivateAlias : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //update private alias of other bank account @@ -1017,6 +1146,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + deleteCounterpartyPrivateAlias, apiVersion, "deleteCounterpartyPrivateAlias", "DELETE", @@ -1027,7 +1157,10 @@ trait APIMethods121 { |OAuth authentication is required if the view is not public.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val deleteCounterpartyPrivateAlias : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //delete private alias of other bank account @@ -1047,6 +1180,7 @@ trait APIMethods121 { //TODO: get more info of counterparty? resourceDocs += ResourceDoc( + addCounterpartyMoreInfo, apiVersion, "addCounterpartyMoreInfo", "POST", @@ -1055,7 +1189,10 @@ trait APIMethods121 { "Add a description of the counter party from the perpestive of the account e.g. My dentist.", Extraction.decompose(MoreInfoJSON("More info")), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val addCounterpartyMoreInfo : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //add more info to other bank account @@ -1077,6 +1214,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + updateCounterpartyMoreInfo, apiVersion, "updateCounterpartyMoreInfo", "PUT", @@ -1085,7 +1223,10 @@ trait APIMethods121 { "Update the description of the counter party from the perpestive of the account e.g. My dentist.", Extraction.decompose(MoreInfoJSON("More info")), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val updateCounterpartyMoreInfo : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //update more info of other bank account @@ -1107,6 +1248,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + deleteCounterpartyMoreInfo, apiVersion, "deleteCounterpartyMoreInfo", "DELETE", @@ -1115,7 +1257,10 @@ trait APIMethods121 { "", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val deleteCounterpartyMoreInfo : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //delete more info of other bank account @@ -1135,6 +1280,7 @@ trait APIMethods121 { //TODO: get url of counterparty? resourceDocs += ResourceDoc( + addCounterpartyUrl, apiVersion, "addCounterpartyUrl", "POST", @@ -1143,7 +1289,10 @@ trait APIMethods121 { "A url which represents the counterparty (home page url etc.)", Extraction.decompose(UrlJSON("www.example.com")), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val addCounterpartyUrl : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { @@ -1166,6 +1315,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + updateCounterpartyUrl, apiVersion, "updateCounterpartyUrl", "PUT", @@ -1174,7 +1324,10 @@ trait APIMethods121 { "A url which represents the counterparty (home page url etc.)", Extraction.decompose(UrlJSON("www.example.com")), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val updateCounterpartyUrl : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //update url of other bank account @@ -1196,6 +1349,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + deleteCounterpartyUrl, apiVersion, "deleteCounterpartyUrl", "DELETE", @@ -1204,7 +1358,10 @@ trait APIMethods121 { "", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val deleteCounterpartyUrl : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //delete url of other bank account @@ -1224,6 +1381,7 @@ trait APIMethods121 { //TODO: get image url of counterparty? resourceDocs += ResourceDoc( + addCounterpartyImageUrl, apiVersion, "addCounterpartyImageUrl", "POST", @@ -1232,7 +1390,10 @@ trait APIMethods121 { "Add a url that points to the logo of the counterparty", Extraction.decompose(ImageUrlJSON("www.example.com/logo.png")), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val addCounterpartyImageUrl : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //add image url to other bank account @@ -1254,6 +1415,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + updateCounterpartyImageUrl, apiVersion, "updateCounterpartyImageUrl", "PUT", @@ -1262,7 +1424,10 @@ trait APIMethods121 { "Update the url that points to the logo of the counterparty", Extraction.decompose(ImageUrlJSON("www.example.com/logo.png")), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val updateCounterpartyImageUrl : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //update image url of other bank account @@ -1284,6 +1449,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + deleteCounterpartyImageUrl, apiVersion, "deleteCounterpartyImageUrl", "DELETE", @@ -1292,7 +1458,10 @@ trait APIMethods121 { "", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val deleteCounterpartyImageUrl : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //delete image url of other bank account @@ -1312,6 +1481,7 @@ trait APIMethods121 { //TODO: get open corporates url of counterparty? resourceDocs += ResourceDoc( + addCounterpartyOpenCorporatesUrl, apiVersion, "addCounterpartyOpenCorporatesUrl", "POST", @@ -1320,7 +1490,10 @@ trait APIMethods121 { "", Extraction.decompose(OpenCorporateUrlJSON("https://opencorporates.com/companies/gb/04351490")), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val addCounterpartyOpenCorporatesUrl : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //add open corporate url to other bank account @@ -1342,6 +1515,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + updateCounterpartyOpenCorporatesUrl, apiVersion, "updateCounterpartyOpenCorporatesUrl", "PUT", @@ -1350,7 +1524,10 @@ trait APIMethods121 { "", Extraction.decompose(OpenCorporateUrlJSON("https://opencorporates.com/companies/gb/04351490")), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val updateCounterpartyOpenCorporatesUrl : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //update open corporate url of other bank account @@ -1372,6 +1549,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + deleteCounterpartyOpenCorporatesUrl, apiVersion, "deleteCounterpartyOpenCorporatesUrl", "DELETE", @@ -1380,7 +1558,10 @@ trait APIMethods121 { "", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val deleteCounterpartyOpenCorporatesUrl : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //delete open corporate url of other bank account @@ -1400,6 +1581,7 @@ trait APIMethods121 { //TODO: get corporate location of counterparty? resourceDocs += ResourceDoc( + addCounterpartyCorporateLocation, apiVersion, "addCounterpartyCorporateLocation", "POST", @@ -1408,7 +1590,10 @@ trait APIMethods121 { "Add the geolocation of the counterparty's registered address", Extraction.decompose(CorporateLocationJSON(JSONFactory.createLocationPlainJSON(52.5571573,13.3728025))), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val addCounterpartyCorporateLocation : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //add corporate location to other bank account @@ -1432,6 +1617,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + updateCounterpartyCorporateLocation, apiVersion, "updateCounterpartyCorporateLocation", "PUT", @@ -1440,7 +1626,10 @@ trait APIMethods121 { "Update the geolocation of the counterparty's registered address", Extraction.decompose(CorporateLocationJSON(JSONFactory.createLocationPlainJSON(52.5571573,13.3728025))), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val updateCounterpartyCorporateLocation : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //update corporate location of other bank account @@ -1464,6 +1653,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + deleteCounterpartyCorporateLocation, apiVersion, "deleteCounterpartyCorporateLocation", "DELETE", @@ -1472,7 +1662,10 @@ trait APIMethods121 { "Delete the geolocation of the counterparty's registered address", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val deleteCounterpartyCorporateLocation : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //delete corporate location of other bank account @@ -1497,6 +1690,7 @@ trait APIMethods121 { //TODO: get physical location of counterparty? resourceDocs += ResourceDoc( + addCounterpartyPhysicalLocation, apiVersion, "addCounterpartyPhysicalLocation", "POST", @@ -1505,7 +1699,10 @@ trait APIMethods121 { "Add geocoordinates of the counterparty's main location", Extraction.decompose(PhysicalLocationJSON(JSONFactory.createLocationPlainJSON(52.5571573,13.3728025))), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val addCounterpartyPhysicalLocation : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //add physical location to other bank account @@ -1529,6 +1726,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + updateCounterpartyPhysicalLocation, apiVersion, "updateCounterpartyPhysicalLocation", "PUT", @@ -1537,7 +1735,10 @@ trait APIMethods121 { "Update geocoordinates of the counterparty's main location", Extraction.decompose(PhysicalLocationJSON(JSONFactory.createLocationPlainJSON(52.5571573,13.3728025))), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val updateCounterpartyPhysicalLocation : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //update physical location to other bank account @@ -1561,6 +1762,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + deleteCounterpartyPhysicalLocation, apiVersion, "deleteCounterpartyPhysicalLocation", "DELETE", @@ -1569,7 +1771,10 @@ trait APIMethods121 { "", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCounterparties, apiTagMetaData)) lazy val deleteCounterpartyPhysicalLocation : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //delete physical location of other bank account @@ -1592,6 +1797,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + getTransactionsForBankAccount, apiVersion, "getTransactionsForBankAccount", "GET", @@ -1613,7 +1819,10 @@ trait APIMethods121 { |**Date format parameter**: "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" (2014-07-01T00:00:00.000Z) ==> time zone is UTC.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + true, + List(apiTagAccounts, apiTagTransactions)) lazy val getTransactionsForBankAccount : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //get transactions @@ -1633,6 +1842,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + getTransactionByIdForBankAccount, apiVersion, "getTransactionByIdForBankAccount", "GET", @@ -1643,7 +1853,10 @@ trait APIMethods121 { |Authentication via OAuth is required if the view is not public.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + true, + List(apiTagAccounts, apiTagTransactions)) lazy val getTransactionByIdForBankAccount : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //get transaction by id @@ -1661,6 +1874,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + getTransactionNarrative, apiVersion, "getTransactionNarrative", "GET", @@ -1671,7 +1885,10 @@ trait APIMethods121 { |Authentication via OAuth is required if the view is not public.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagTransactions, apiTagMetaData)) lazy val getTransactionNarrative : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //get narrative @@ -1688,6 +1905,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + addTransactionNarrative, apiVersion, "addTransactionNarrative", "POST", @@ -1698,7 +1916,10 @@ trait APIMethods121 { |Authentication via OAuth is required if the view is not public.""", Extraction.decompose(TransactionNarrativeJSON("My new (old!) piano")), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagTransactions, apiTagMetaData)) lazy val addTransactionNarrative : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //add narrative @@ -1718,6 +1939,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + updateTransactionNarrative, apiVersion, "updateTransactionNarrative", "PUT", @@ -1728,7 +1950,10 @@ trait APIMethods121 { |Authentication via OAuth is required if the view is not public.""", Extraction.decompose(TransactionNarrativeJSON("My new (old!) piano")), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagTransactions, apiTagMetaData)) lazy val updateTransactionNarrative : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //update narrative @@ -1748,6 +1973,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + deleteTransactionNarrative, apiVersion, "deleteTransactionNarrative", "DELETE", @@ -1758,7 +1984,10 @@ trait APIMethods121 { |Authentication via OAuth is required if the view is not public.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagTransactions, apiTagMetaData)) lazy val deleteTransactionNarrative : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //delete narrative @@ -1775,6 +2004,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + getCommentsForViewOnTransaction, apiVersion, "getCommentsForViewOnTransaction", "GET", @@ -1785,7 +2015,10 @@ trait APIMethods121 { |Authentication via OAuth is required if the view is not public.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagTransactions, apiTagMetaData)) lazy val getCommentsForViewOnTransaction : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //get comments @@ -1802,6 +2035,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + addCommentForViewOnTransaction, apiVersion, "addCommentForViewOnTransaction", "POST", @@ -1812,7 +2046,10 @@ trait APIMethods121 { |OAuth authentication is required since the comment is linked with the user.""", Extraction.decompose(PostTransactionCommentJSON("Why did we spend money on this again?")), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagTransactions, apiTagMetaData)) lazy val addCommentForViewOnTransaction : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //add comment @@ -1833,6 +2070,7 @@ trait APIMethods121 { // Not able to update a comment (delete and add another) resourceDocs += ResourceDoc( + deleteCommentForViewOnTransaction, apiVersion, "deleteCommentForViewOnTransaction", "DELETE", @@ -1843,7 +2081,10 @@ trait APIMethods121 { |Authentication via OAuth is required. The user must either have owner privileges for this account, or must be the user that posted the comment.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagTransactions, apiTagMetaData)) lazy val deleteCommentForViewOnTransaction : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //delete comment @@ -1860,6 +2101,7 @@ trait APIMethods121 { } resourceDocs += ResourceDoc( + getTagsForViewOnTransaction, apiVersion, "getTagsForViewOnTransaction", "GET", @@ -1870,7 +2112,10 @@ trait APIMethods121 { Authentication via OAuth is required if the view is not public.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagTransactions, apiTagMetaData)) lazy val getTagsForViewOnTransaction : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //get tags @@ -1887,6 +2132,7 @@ Authentication via OAuth is required if the view is not public.""", } resourceDocs += ResourceDoc( + addTagForViewOnTransaction, apiVersion, "addTagForViewOnTransaction", "POST", @@ -1897,7 +2143,10 @@ Authentication via OAuth is required if the view is not public.""", |OAuth authentication is required since the tag is linked with the user.""", Extraction.decompose(PostTransactionTagJSON("holiday")), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagTransactions, apiTagMetaData)) lazy val addTagForViewOnTransaction : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //add a tag @@ -1919,6 +2168,7 @@ Authentication via OAuth is required if the view is not public.""", // No update tag (delete and add another) resourceDocs += ResourceDoc( + deleteTagForViewOnTransaction, apiVersion, "deleteTagForViewOnTransaction", "DELETE", @@ -1929,7 +2179,10 @@ Authentication via OAuth is required if the view is not public.""", Authentication via OAuth is required. The user must either have owner privileges for this account, or must be the user that posted the tag.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagTransactions, apiTagMetaData)) lazy val deleteTagForViewOnTransaction : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //delete a tag @@ -1947,6 +2200,7 @@ Authentication via OAuth is required. The user must either have owner privileges } resourceDocs += ResourceDoc( + getImagesForViewOnTransaction, apiVersion, "getImagesForViewOnTransaction", "GET", @@ -1957,7 +2211,10 @@ Authentication via OAuth is required. The user must either have owner privileges Authentication via OAuth is required if the view is not public.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagTransactions, apiTagMetaData)) lazy val getImagesForViewOnTransaction : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //get images @@ -1974,6 +2231,7 @@ Authentication via OAuth is required if the view is not public.""", } resourceDocs += ResourceDoc( + addImageForViewOnTransaction, apiVersion, "addImageForViewOnTransaction", "POST", @@ -1984,7 +2242,10 @@ Authentication via OAuth is required if the view is not public.""", |OAuth authentication is required since the image is linked with the user.""", Extraction.decompose(PostTransactionImageJSON("The new printer", "www.example.com/images/printer.png")), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagTransactions, apiTagMetaData)) lazy val addImageForViewOnTransaction : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //add an image @@ -2004,17 +2265,21 @@ Authentication via OAuth is required if the view is not public.""", } resourceDocs += ResourceDoc( + deleteImageForViewOnTransaction, apiVersion, "deleteImageForViewOnTransaction", "DELETE", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transactions/TRANSACTION_ID/metadata/images/IMAGE_ID", - "delete an image", + "Delete an image", """Deletes the image IMAGE_ID about the transaction TRANSACTION_ID made on [view](#1_2_1-getViewsForBankAccount). | |Authentication via OAuth is required. The user must either have owner privileges for this account, or must be the user that posted the image.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagTransactions, apiTagMetaData)) lazy val deleteImageForViewOnTransaction : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //delete an image @@ -2031,6 +2296,7 @@ Authentication via OAuth is required if the view is not public.""", } resourceDocs += ResourceDoc( + getWhereTagForViewOnTransaction, apiVersion, "getWhereTagForViewOnTransaction", "GET", @@ -2042,7 +2308,10 @@ Authentication via OAuth is required if the view is not public.""", |Authentication via OAuth is required if the view is not public.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagTransactions, apiTagMetaData)) lazy val getWhereTagForViewOnTransaction : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //get where tag @@ -2060,6 +2329,7 @@ Authentication via OAuth is required if the view is not public.""", } resourceDocs += ResourceDoc( + addWhereTagForViewOnTransaction, apiVersion, "addWhereTagForViewOnTransaction", "POST", @@ -2070,7 +2340,10 @@ Authentication via OAuth is required if the view is not public.""", |OAuth authentication is required since the geo tag is linked with the user.""", Extraction.decompose(PostTransactionWhereJSON(JSONFactory.createLocationPlainJSON(52.5571573,13.3728025))), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagTransactions, apiTagMetaData)) lazy val addWhereTagForViewOnTransaction : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //add where tag @@ -2092,6 +2365,7 @@ Authentication via OAuth is required if the view is not public.""", } resourceDocs += ResourceDoc( + updateWhereTagForViewOnTransaction, apiVersion, "updateWhereTagForViewOnTransaction", "PUT", @@ -2102,7 +2376,10 @@ Authentication via OAuth is required if the view is not public.""", |OAuth authentication is required since the geo tag is linked with the user.""", Extraction.decompose(PostTransactionWhereJSON(JSONFactory.createLocationPlainJSON(52.5571573,13.3728025))), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagTransactions, apiTagMetaData)) lazy val updateWhereTagForViewOnTransaction : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //update where tag @@ -2124,6 +2401,7 @@ Authentication via OAuth is required if the view is not public.""", } resourceDocs += ResourceDoc( + deleteWhereTagForViewOnTransaction, apiVersion, "deleteWhereTagForViewOnTransaction", "DELETE", @@ -2134,7 +2412,10 @@ Authentication via OAuth is required if the view is not public.""", Authentication via OAuth is required. The user must either have owner privileges for this account, or must be the user that posted the geo tag.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagTransactions, apiTagMetaData)) lazy val deleteWhereTagForViewOnTransaction : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //delete where tag @@ -2155,6 +2436,7 @@ Authentication via OAuth is required. The user must either have owner privileges } resourceDocs += ResourceDoc( + getCounterpartyForTransaction, apiVersion, "getCounterpartyForTransaction", "GET", @@ -2165,7 +2447,10 @@ Authentication via OAuth is required. The user must either have owner privileges Authentication via OAuth is required if the view is not public.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagTransactions, apiTagCounterparties)) lazy val getCounterpartyForTransaction : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //get other account of a transaction @@ -2187,6 +2472,7 @@ Authentication via OAuth is required if the view is not public.""", case class TransactionIdJson(transaction_id : String) resourceDocs += ResourceDoc( + makePayment, apiVersion, "makePayment", "POST", @@ -2200,7 +2486,10 @@ Authentication via OAuth is required if the view is not public.""", |There are no checks for 'sufficient funds' at the moment, so it is possible to go into unlimited overdraft.""", Extraction.decompose(MakePaymentJson("To BANK_ID", "To ACCOUNT_ID", "12.45")), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagPayment)) lazy val makePayment : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transactions" :: Nil JsonPost json -> _ => { diff --git a/src/main/scala/code/api/v1_2_1/OBPAPI1.2.1.scala b/src/main/scala/code/api/v1_2_1/OBPAPI1.2.1.scala index 283865639..ad78adfc4 100644 --- a/src/main/scala/code/api/v1_2_1/OBPAPI1.2.1.scala +++ b/src/main/scala/code/api/v1_2_1/OBPAPI1.2.1.scala @@ -36,6 +36,8 @@ import net.liftweb.json.JsonAST._ import net.liftweb.common.Loggable import code.api.OBPRestHelper +// Added so we can add resource docs for this version of the API + object OBPAPI1_2_1 extends OBPRestHelper with APIMethods121 with Loggable { diff --git a/src/main/scala/code/api/v1_3_0/APIMethods130.scala b/src/main/scala/code/api/v1_3_0/APIMethods130.scala index 2769f93db..26f870b69 100644 --- a/src/main/scala/code/api/v1_3_0/APIMethods130.scala +++ b/src/main/scala/code/api/v1_3_0/APIMethods130.scala @@ -30,6 +30,7 @@ trait APIMethods130 { resourceDocs += ResourceDoc( + getCards, apiVersion, "getCards", "GET", @@ -38,7 +39,10 @@ trait APIMethods130 { "Returns data about all the physical cards a user has been issued. These could be debit cards, credit cards, etc.", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + true, + true, + Nil) lazy val getCards : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { case "cards" :: Nil JsonGet _ => { @@ -58,6 +62,7 @@ trait APIMethods130 { resourceDocs += ResourceDoc( + getCardsForBank, apiVersion, "getCardsForBank", "GET", @@ -66,7 +71,10 @@ trait APIMethods130 { "", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + Nil) def getCardsForBank : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { diff --git a/src/main/scala/code/api/v1_3_0/OBPAPI1_3_0.scala b/src/main/scala/code/api/v1_3_0/OBPAPI1_3_0.scala index 592f30ad4..380dbaca3 100644 --- a/src/main/scala/code/api/v1_3_0/OBPAPI1_3_0.scala +++ b/src/main/scala/code/api/v1_3_0/OBPAPI1_3_0.scala @@ -4,6 +4,9 @@ import code.api.OBPRestHelper import code.api.v1_2_1.APIMethods121 import net.liftweb.common.Loggable + +// Added so we can add resource docs for this version of the API + //has APIMethods121 as all api calls that went unchanged from 1.2.1 to 1.3.0 will use the old //implementation object OBPAPI1_3_0 extends OBPRestHelper with APIMethods130 with APIMethods121 with Loggable { diff --git a/src/main/scala/code/api/v1_4_0/APIMethods140.scala b/src/main/scala/code/api/v1_4_0/APIMethods140.scala index be2a9ebae..cad7ea00b 100644 --- a/src/main/scala/code/api/v1_4_0/APIMethods140.scala +++ b/src/main/scala/code/api/v1_4_0/APIMethods140.scala @@ -3,7 +3,9 @@ package code.api.v1_4_0 import java.text.SimpleDateFormat import java.util.Date +import code.api.v1_4_0.JSONFactory1_4_0._ import code.bankconnectors.Connector +import code.metadata.comments.MappedComment import code.transactionrequests.TransactionRequests.{TransactionRequestBody, TransactionRequestAccount} import net.liftweb.common.{Failure, Loggable, Box, Full} import net.liftweb.http.js.JE.JsRaw @@ -16,15 +18,21 @@ import net.liftweb.json.JsonDSL._ import net.liftweb.util.Props import net.liftweb.json.JsonAST.JValue + +import code.api.v1_2_1.{AmountOfMoneyJSON} + import scala.collection.immutable.Nil // JObject creation import collection.mutable.ArrayBuffer import code.api.APIFailure -import code.api.v1_2_1.APIMethods121 -import code.api.v1_3_0.APIMethods130 -import code.api.v1_4_0.JSONFactory1_4_0._ +import code.api.v1_2_1.{OBPAPI1_2_1, APIInfoJSON, HostedBy, APIMethods121} +import code.api.v1_3_0.{OBPAPI1_3_0, APIMethods130} +//import code.api.v2_0_0.{OBPAPI2_0_0, APIMethods200} + +// So we can include resource docs from future versions +//import code.api.v1_4_0.JSONFactory1_4_0._ import code.atms.Atms import code.branches.Branches import code.crm.CrmEvent @@ -32,10 +40,15 @@ import code.customer.{MockCustomerFaceImage, CustomerMessages, Customer} import code.model._ import code.products.Products import code.api.util.APIUtil._ +import code.api.util.ErrorMessages + import code.util.Helper._ import code.api.util.APIUtil.ResourceDoc import java.text.SimpleDateFormat +import code.api.util.APIUtil.authenticationRequiredMessage + + trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ //needs to be a RestHelper to get access to JsonGet, JsonPost, etc. // We add previous APIMethods so we have access to the Resource Docs @@ -52,15 +65,8 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ val exampleDate = simpleDateFormat.parse(exampleDateString) - def getResourceDocsList : Option[List[ResourceDoc]] = - { - // Get the Resource Docs for this and previous versions of the API - val cumulativeResourceDocs = resourceDocs ++ Implementations1_3_0.resourceDocs ++ Implementations1_2_1.resourceDocs - // Sort by endpoint, verb. Thus / is shown first then /accounts and /banks etc. Seems to read quite well like that. - Some(cumulativeResourceDocs.toList.sortBy(rd => (rd.requestUrl, rd.requestVerb))) - } - resourceDocs += ResourceDoc( + getCustomer, apiVersion, "getCustomer", "GET", @@ -71,14 +77,17 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ |Authentication via OAuth is required.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCustomer)) lazy val getCustomer : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { case "banks" :: BankId(bankId) :: "customer" :: Nil JsonGet _ => { user => { for { - u <- user ?~! "User must be logged in to retrieve Customer" - bank <- tryo(Bank(bankId).get) ?~! {"Unknown bank id"} + u <- user ?~! ErrorMessages.UserNotLoggedIn + bank <- tryo(Bank(bankId).get) ?~! {ErrorMessages.BankNotFound} info <- Customer.customerProvider.vend.getCustomer(bankId, u) ?~ "No customer information found for current user" } yield { val json = JSONFactory1_4_0.createCustomerJson(info) @@ -89,6 +98,7 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ } resourceDocs += ResourceDoc( + getCustomerMessages, apiVersion, "getCustomerMessages", "GET", @@ -99,14 +109,17 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ |Authentication via OAuth is required.""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCustomer)) lazy val getCustomerMessages : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { case "banks" :: BankId(bankId) :: "customer" :: "messages" :: Nil JsonGet _ => { user => { for { - u <- user ?~! "User must be logged in to retrieve customer messages" - bank <- tryo(Bank(bankId).get) ?~! {"Unknown bank id"} + u <- user ?~! ErrorMessages.UserNotLoggedIn + bank <- tryo(Bank(bankId).get) ?~! {ErrorMessages.BankNotFound} //au <- APIUser.find(By(APIUser.id, u.apiId)) //role <- au.isCustomerMessageAdmin ~> APIFailure("User does not have sufficient permissions", 401) } yield { @@ -119,6 +132,7 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ } resourceDocs += ResourceDoc( + addCustomerMessage, apiVersion, "addCustomerMessage", "POST", @@ -128,16 +142,18 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ // We use Extraction.decompose to convert to json Extraction.decompose(AddCustomerMessageJson("message to send", "from department", "from person")), emptyObjectJson, - emptyObjectJson :: Nil + emptyObjectJson :: Nil, + false, + false, + List(apiTagCustomer) ) lazy val addCustomerMessage : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { case "banks" :: BankId(bankId) :: "customer" :: customerNumber :: "messages" :: Nil JsonPost json -> _ => { - val test = Bank(bankId) user => { for { postedData <- tryo{json.extract[AddCustomerMessageJson]} ?~! "Incorrect json format" - bank <- tryo(Bank(bankId).get) ?~! {"Unknown bank id"} + bank <- tryo(Bank(bankId).get) ?~! {ErrorMessages.BankNotFound} customer <- Customer.customerProvider.vend.getUser(bankId, customerNumber) ?~! "Customer not found" messageCreated <- booleanToBox( CustomerMessages.customerMessageProvider.vend.addMessage( @@ -150,31 +166,41 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ } } + + val getBranchesIsPublic = Props.getBool("apiOptions.getBranchesIsPublic", true) + resourceDocs += ResourceDoc( + getBranches, apiVersion, "getBranches", "GET", "/banks/BANK_ID/branches", "Get branches for the bank", - """Returns information about branches for a single bank specified by BANK_ID including: + s"""Returns information about branches for a single bank specified by BANK_ID including: | |* Name |* Address |* Geo Location |* License the data under this endpoint is released under | - |Authentication via OAuth *may* be required.""", + |${authenticationRequiredMessage(!getBranchesIsPublic)}""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil + emptyObjectJson :: Nil, + true, + false, + List(apiTagBanks) ) lazy val getBranches : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { case "banks" :: BankId(bankId) :: "branches" :: Nil JsonGet _ => { user => { for { - u <- user ?~! "User must be logged in to retrieve Branches data" - bank <- tryo(Bank(bankId).get) ?~! {"Unknown bank id"} + u <- if(getBranchesIsPublic) + Box(Some(1)) + else + user ?~! "User must be logged in to retrieve Branches data" + bank <- tryo(Bank(bankId).get) ?~! {ErrorMessages.BankNotFound} // Get branches from the active provider branches <- Box(Branches.branchesProvider.vend.getBranches(bankId)) ~> APIFailure("No branches available. License may not be set.", 204) } yield { @@ -187,23 +213,28 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ } + val getAtmsIsPublic = Props.getBool("apiOptions.getAtmsIsPublic", true) resourceDocs += ResourceDoc( + getAtms, apiVersion, "getAtms", "GET", "/banks/BANK_ID/atms", "Get ATMS for the bank", - """Returns information about ATMs for a single bank specified by BANK_ID including: + s"""Returns information about ATMs for a single bank specified by BANK_ID including: | |* Address |* Geo Location |* License the data under this endpoint is released under | - |Authentication via OAuth *may* be required.""", + |${authenticationRequiredMessage(!getAtmsIsPublic)}""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil + emptyObjectJson :: Nil, + true, + false, + List(apiTagBanks) ) lazy val getAtms : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { @@ -211,8 +242,12 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ user => { for { // Get atms from the active provider - u <- user ?~! "User must be logged in to retrieve ATM data" - bank <- tryo(Bank(bankId).get) ?~! {"Unknown bank id"} + + u <- if(getAtmsIsPublic) + Box(Some(1)) + else + user ?~! "User must be logged in to retrieve ATM data" + bank <- tryo(Bank(bankId).get) ?~! {ErrorMessages.BankNotFound} atms <- Box(Atms.atmsProvider.vend.getAtms(bankId)) ~> APIFailure("No ATMs available. License may not be set.", 204) } yield { // Format the data as json @@ -225,13 +260,17 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ } + val getProductsIsPublic = Props.getBool("apiOptions.getProductsIsPublic", true) + + resourceDocs += ResourceDoc( + getProducts, apiVersion, "getProducts", "GET", "/banks/BANK_ID/products", "Get products offered by the bank", - """Returns information about financial products offered by a bank specified by BANK_ID including: + s"""Returns information about financial products offered by a bank specified by BANK_ID including: | |* Name |* Code @@ -241,10 +280,14 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ |* More info URL |* Description |* Terms and Conditions - |* License the data under this endpoint is released under""", + |* License the data under this endpoint is released under + |${authenticationRequiredMessage(!getProductsIsPublic)}""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil + emptyObjectJson :: Nil, + true, + false, + List(apiTagBanks) ) lazy val getProducts : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { @@ -252,8 +295,11 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ user => { for { // Get products from the active provider - u <- user ?~! "User must be logged in to retrieve Products data" - bank <- tryo(Bank(bankId).get) ?~! {"Unknown bank id"} + u <- if(getProductsIsPublic) + Box(Some(1)) + else + user ?~! "User must be logged in to retrieve Products data" + bank <- tryo(Bank(bankId).get) ?~! {ErrorMessages.BankNotFound} products <- Box(Products.productsProvider.vend.getProducts(bankId)) ~> APIFailure("No products available. License may not be set.", 204) } yield { // Format the data as json @@ -267,6 +313,7 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ resourceDocs += ResourceDoc( + getCrmEvents, apiVersion, "getCrmEvents", "GET", @@ -275,7 +322,10 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ "", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil + emptyObjectJson :: Nil, + false, + false, + List(apiTagCustomer) ) lazy val getCrmEvents : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { @@ -284,7 +334,7 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ for { // Get crm events from the active provider u <- user ?~! "User must be logged in to retrieve CRM Event information" - bank <- tryo(Bank(bankId).get) ?~! {"Unknown bank id"} + bank <- tryo(Bank(bankId).get) ?~! {ErrorMessages.BankNotFound} crmEvents <- Box(CrmEvent.crmEventProvider.vend.getCrmEvents(bankId)) ~> APIFailure("No CRM Events available.", 204) } yield { // Format the data as json @@ -295,40 +345,12 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ } } } - - resourceDocs += ResourceDoc( - apiVersion, - "getResourceDocs", - "GET", - "/resource-docs/obp", - "Get Resource Documentation in OBP format.", - "Returns documentation about the resources on this server including example body for POST or PUT requests.", - emptyObjectJson, - emptyObjectJson, - emptyObjectJson :: Nil - ) - - // Provides resource documents so that live docs (currently on Sofi) can display API documentation - lazy val getResourceDocs : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { - case "resource-docs" :: "obp" :: Nil JsonGet _ => { - user => { - for { - rd <- getResourceDocsList - } yield { - // Format the data as json - val json = JSONFactory1_4_0.createResourceDocsJson(rd) - // Return - successJsonResponse(Extraction.decompose(json)) - } - } - } - } - /* transaction requests (new payments since 1.4.0) */ resourceDocs += ResourceDoc( + getTransactionRequestTypes, apiVersion, "getTransactionRequestTypes", "GET", @@ -337,7 +359,10 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ "", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + true, + true, + List(apiTagPayment)) lazy val getTransactionRequestTypes: PartialFunction[Req, Box[User] => Box[JsonResponse]] = { case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transaction-request-types" :: @@ -345,8 +370,8 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ user => if (Props.getBool("transactionRequests_enabled", false)) { for { - u <- user ?~ "User not found" - fromBank <- tryo(Bank(bankId).get) ?~! {"Unknown bank id"} + u <- user ?~ ErrorMessages.UserNotLoggedIn + fromBank <- tryo(Bank(bankId).get) ?~! {ErrorMessages.BankNotFound} fromAccount <- tryo(BankAccount(bankId, accountId).get) ?~ {"Unknown bank account"} view <- tryo(fromAccount.permittedViews(user).find(_ == viewId)) ?~ {"Current user does not have access to the view " + viewId} transactionRequestTypes <- Connector.connector.vend.getTransactionRequestTypes(u, fromAccount) @@ -361,6 +386,7 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ } resourceDocs += ResourceDoc( + getTransactionRequests, apiVersion, "getTransactionRequests", "GET", @@ -369,16 +395,19 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ "", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + true, + true, + List(apiTagPayment)) lazy val getTransactionRequests: PartialFunction[Req, Box[User] => Box[JsonResponse]] = { case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transaction-requests" :: Nil JsonGet _ => { user => if (Props.getBool("transactionRequests_enabled", false)) { for { - u <- user ?~ "User not found" - fromBank <- tryo(Bank(bankId).get) ?~! {"Unknown bank id"} - fromAccount <- tryo(BankAccount(bankId, accountId).get) ?~! {"Unknown bank account"} + u <- user ?~ ErrorMessages.UserNotLoggedIn + fromBank <- tryo(Bank(bankId).get) ?~! {ErrorMessages.BankNotFound} + fromAccount <- tryo(BankAccount(bankId, accountId).get) ?~! {ErrorMessages.AccountNotFound} view <- tryo(fromAccount.permittedViews(user).find(_ == viewId)) ?~ {"Current user does not have access to the view " + viewId} transactionRequests <- Connector.connector.vend.getTransactionRequests(u, fromAccount) } @@ -397,12 +426,19 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ case class TransactionIdJson(transaction_id : String) resourceDocs += ResourceDoc( + createTransactionRequest, apiVersion, "createTransactionRequest", "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transaction-request-types/TRANSACTION_REQUEST_TYPE/transaction-requests", "Create Transaction Request.", - "", + """Initiate a Payment via a Transaction Request. + | + |This is the preferred method to create a payment and supersedes makePayment in 1.2.1. + | + |See [this python code](https://github.com/OpenBankProject/Hello-OBP-DirectLogin-Python/blob/master/hello_payments.py) for a complete example of this flow. + | + |In sandbox mode, if the amount is < 100 the transaction request will create a transaction without a challenge, else a challenge will need to be answered.""", Extraction.decompose(TransactionRequestBodyJSON ( TransactionRequestAccountJSON("BANK_ID", "ACCOUNT_ID"), AmountOfMoneyJSON("EUR", "100.53"), @@ -411,7 +447,10 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ ) ), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + true, + true, + List(apiTagPayment)) lazy val createTransactionRequest: PartialFunction[Req, Box[User] => Box[JsonResponse]] = { case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transaction-request-types" :: @@ -423,14 +462,14 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ * check if user has access using the view that is given (now it checks if user has access to owner view), will need some new permissions for transaction requests * test: functionality, error messages if user not given or invalid, if any other value is not existing */ - u <- user ?~ "User not found" - transBodyJson <- tryo{json.extract[TransactionRequestBodyJSON]} ?~ {"Invalid json format"} + u <- user ?~ ErrorMessages.UserNotLoggedIn + transBodyJson <- tryo{json.extract[TransactionRequestBodyJSON]} ?~ {ErrorMessages.InvalidJsonFormat} transBody <- tryo{getTransactionRequestBodyFromJson(transBodyJson)} - fromBank <- tryo(Bank(bankId).get) ?~! {"Unknown bank id"} - fromAccount <- tryo(BankAccount(bankId, accountId).get) ?~! {"Unknown bank account"} + fromBank <- tryo(Bank(bankId).get) ?~! {ErrorMessages.BankNotFound} + fromAccount <- tryo(BankAccount(bankId, accountId).get) ?~! {ErrorMessages.AccountNotFound} toBankId <- tryo(BankId(transBodyJson.to.bank_id)) toAccountId <- tryo(AccountId(transBodyJson.to.account_id)) - toAccount <- tryo{BankAccount(toBankId, toAccountId).get} ?~! {"Unknown counterparty account"} + toAccount <- tryo{BankAccount(toBankId, toAccountId).get} ?~! {ErrorMessages.CounterpartyNotFound} accountsCurrencyEqual <- tryo(assert(fromAccount.currency == toAccount.currency)) ?~! {"Counterparty and holder accounts have differing currencies."} transferCurrencyEqual <- tryo(assert(transBodyJson.value.currency == fromAccount.currency)) ?~! {"Request currency and holder account currency can't be different."} rawAmt <- tryo {BigDecimal(transBodyJson.value.amount)} ?~! s"Amount ${transBodyJson.value.amount} not convertible to number" @@ -448,15 +487,19 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ resourceDocs += ResourceDoc( + answerTransactionRequestChallenge, apiVersion, "answerTransactionRequestChallenge", "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transaction-request-types/TRANSACTION_REQUEST_TYPE/transaction-requests/TRANSACTION_REQUEST_ID/challenge", "Answer Transaction Request Challenge.", - "", + "In Sandbox mode, any string that can be converted to a possitive integer will be accepted as an answer.", Extraction.decompose(ChallengeAnswerJSON("89123812", "123345")), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + true, + true, + List(apiTagPayment)) lazy val answerTransactionRequestChallenge: PartialFunction[Req, Box[User] => Box[JsonResponse]] = { case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transaction-request-types" :: @@ -464,8 +507,8 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ user => if (Props.getBool("transactionRequests_enabled", false)) { for { - u <- user ?~ "User not found" - fromBank <- tryo(Bank(bankId).get) ?~! {"Unknown bank id"} + u <- user ?~ ErrorMessages.UserNotLoggedIn + fromBank <- tryo(Bank(bankId).get) ?~! {ErrorMessages.BankNotFound} fromAccount <- tryo(BankAccount(bankId, accountId).get) ?~! {"Unknown bank account"} view <- tryo(fromAccount.permittedViews(user).find(_ == viewId)) ?~ {"Current user does not have access to the view " + viewId} answerJson <- tryo{json.extract[ChallengeAnswerJSON]} ?~ {"Invalid json format"} @@ -487,20 +530,26 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ resourceDocs += ResourceDoc( + addCustomer, apiVersion, "addCustomer", "POST", "/banks/BANK_ID/customer", "Add a customer.", """Add a customer linked to the currently authenticated user. - |This call is experimental and will require additional permissions/role in the future. + |The Customer resource stores the customer number, legal name, email, phone number, their date of birth, relationship status, education attained, a url for a profile image, KYC status etc. + |This call may require additional permissions/role in the future. |For now the authenticated user can create at most one linked customer. |OAuth authentication is required. |""", Extraction.decompose(CustomerJson("687687678", "Joe David Bloggs", - "+44 07972 444 876", "person@example.com", CustomerFaceImageJson("www.example.com/person/123/image.png", exampleDate))), + "+44 07972 444 876", "person@example.com", CustomerFaceImageJson("www.example.com/person/123/image.png", exampleDate), + exampleDate, "Single", 1, List(exampleDate), "Bachelor’s Degree", "Employed", true, exampleDate)), emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + List(apiTagCustomer)) lazy val addCustomer : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { //updates a view on a bank account @@ -508,7 +557,7 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ user => for { u <- user ?~! "User must be logged in to post Customer" - bank <- tryo(Bank(bankId).get) ?~! {"Unknown bank id"} + bank <- tryo(Bank(bankId).get) ?~! {ErrorMessages.BankNotFound} customer <- booleanToBox(Customer.customerProvider.vend.getCustomer(bankId, u).isEmpty) ?~ "Customer already exists for this user." postedData <- tryo{json.extract[CustomerJson]} ?~! "Incorrect json format" customer <- Customer.customerProvider.vend.addCustomer(bankId, @@ -517,7 +566,15 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ postedData.legal_name, postedData.mobile_phone_number, postedData.email, - MockCustomerFaceImage(postedData.face_image.date, postedData.face_image.url)) ?~! "Could not create customer" + MockCustomerFaceImage(postedData.face_image.date, postedData.face_image.url), + postedData.date_of_birth, + postedData.relationship_status, + postedData.dependants, + postedData.dob_of_dependants, + postedData.highest_education_attained, + postedData.employment_status, + postedData.kyc_status, + postedData.last_ok_date) ?~! "Could not create customer" } yield { val successJson = Extraction.decompose(customer) successJsonResponse(successJson) @@ -529,10 +586,11 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ if (Props.devMode) { resourceDocs += ResourceDoc( + dummy(apiVersion), apiVersion, "testResourceDoc", "GET", - "/i-do-not-exist-i-will-404", + "/dummy", "I am only a test resource Doc", """ | @@ -562,7 +620,26 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ |_etc_...""", emptyObjectJson, emptyObjectJson, - emptyObjectJson :: Nil) + emptyObjectJson :: Nil, + false, + false, + Nil) } + + + + def dummy(apiVersion : String) : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + case "dummy" :: Nil JsonGet json => { + user => + val apiDetails: JValue = { + val hostedBy = new HostedBy("TESOBE", "contact@tesobe.com", "+49 (0)30 8145 3994") + val apiInfoJSON = new APIInfoJSON(apiVersion, gitCommit, hostedBy) + Extraction.decompose(apiInfoJSON) + } + + Full(successJsonResponse(apiDetails, 200)) + } + } + } } diff --git a/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala b/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala index cd21657df..35a2c6336 100644 --- a/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala +++ b/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala @@ -17,13 +17,25 @@ import code.transactionrequests.TransactionRequests._ import net.liftweb.json.JsonAST.{JValue, JObject} import org.pegdown.PegDownProcessor +// Use this so we don't duplicate it +import code.api.v1_2_1.{AmountOfMoneyJSON} + + object JSONFactory1_4_0 { case class CustomerJson(customer_number : String, legal_name : String, mobile_phone_number : String, email : String, - face_image : CustomerFaceImageJson) + face_image : CustomerFaceImageJson, + date_of_birth: Date, + relationship_status: String, + dependants: Int, + dob_of_dependants: List[Date], + highest_education_attained: String, + employment_status: String, + kyc_status: Boolean, + last_ok_date: Date) case class CustomerFaceImageJson(url : String, date : Date) @@ -77,7 +89,15 @@ object JSONFactory1_4_0 { mobile_phone_number = cInfo.mobileNumber, email = cInfo.email, face_image = CustomerFaceImageJson(url = cInfo.faceImage.url, - date = cInfo.faceImage.date) + date = cInfo.faceImage.date), + date_of_birth = cInfo.dateOfBirth, + relationship_status = cInfo.relationshipStatus, + dependants = cInfo.dependents, + dob_of_dependants = cInfo.dobOfDependents, + highest_education_attained = cInfo.highestEducationAttained, + employment_status = cInfo.employmentStatus, + kyc_status = cInfo.kycStatus, + last_ok_date = cInfo.lastOkDate ) } @@ -231,14 +251,18 @@ object JSONFactory1_4_0 { description: String, example_request_body: JValue, success_response_body: JValue, - implemented_by: ImplementedByJson) + implemented_by: ImplementedByJson, + is_core: Boolean, + is_psd2: Boolean, + is_obwg: Boolean, + tags: List[String]) // Creates the json resource_docs case class ResourceDocsJson (resource_docs : List[ResourceDocJson]) - def createResourceDocJson(resourceDoc: ResourceDoc) : ResourceDocJson = { + def createResourceDocJson(rd: ResourceDoc) : ResourceDocJson = { // There are multiple flavours of markdown. For instance, original markdown emphasises underscores (surrounds _ with ()) // But we don't want to have to escape underscores (\_) in our documentation @@ -250,15 +274,19 @@ object JSONFactory1_4_0 { val pegDownProcessor : PegDownProcessor = new PegDownProcessor ResourceDocJson( - operation_id = s"${resourceDoc.apiVersion.toString}-${resourceDoc.apiFunction.toString}", - request_verb = resourceDoc.requestVerb, - request_url = resourceDoc.requestUrl, - summary = resourceDoc.summary, + operation_id = s"${rd.apiVersion.toString}-${rd.apiFunction.toString}", + request_verb = rd.requestVerb, + request_url = rd.requestUrl, + summary = rd.summary, // Strip the margin character (|) and line breaks and convert from markdown to html - description = pegDownProcessor.markdownToHtml(resourceDoc.description.stripMargin).replaceAll("\n", ""), - example_request_body = resourceDoc.exampleRequestBody, - success_response_body = resourceDoc.successResponseBody, - implemented_by = ImplementedByJson(resourceDoc.apiVersion, resourceDoc.apiFunction) + description = pegDownProcessor.markdownToHtml(rd.description.stripMargin).replaceAll("\n", ""), + example_request_body = rd.exampleRequestBody, + success_response_body = rd.successResponseBody, + implemented_by = ImplementedByJson(rd.apiVersion, rd.apiFunction), + is_core = rd.isCore, + is_psd2 = rd.isPSD2, + is_obwg = rd.isCore, // For now, track isCore + tags = rd.tags.map(i => i.tag) ) } @@ -309,10 +337,6 @@ object JSONFactory1_4_0 { ) } - case class AmountOfMoneyJSON ( - currency : String, - amount : String - ) case class TransactionRequestAccountJSON ( bank_id: String, account_id : String diff --git a/src/main/scala/code/api/v1_4_0/OBPAPI1_4_0.scala b/src/main/scala/code/api/v1_4_0/OBPAPI1_4_0.scala index b852284e9..916bc2ea3 100644 --- a/src/main/scala/code/api/v1_4_0/OBPAPI1_4_0.scala +++ b/src/main/scala/code/api/v1_4_0/OBPAPI1_4_0.scala @@ -18,6 +18,7 @@ object OBPAPI1_4_0 extends OBPRestHelper with APIMethods140 with Loggable { Implementations1_2_1.privateAccountsAtOneBank, Implementations1_2_1.publicAccountsAtOneBank, Implementations1_2_1.accountById, + Implementations1_2_1.updateAccountLabel, Implementations1_2_1.getViewsForBankAccount, Implementations1_2_1.createViewForBankAccount, Implementations1_2_1.updateViewForBankAccount, @@ -77,7 +78,7 @@ object OBPAPI1_4_0 extends OBPRestHelper with APIMethods140 with Loggable { Implementations1_2_1.updateWhereTagForViewOnTransaction, Implementations1_2_1.deleteWhereTagForViewOnTransaction, Implementations1_2_1.getCounterpartyForTransaction, - //note: removed 1.2.1 makePayment + Implementations1_2_1.makePayment, // Back for a while // New in 1.3.0 Implementations1_3_0.getCards, @@ -92,7 +93,6 @@ object OBPAPI1_4_0 extends OBPRestHelper with APIMethods140 with Loggable { Implementations1_4_0.getAtms, Implementations1_4_0.getProducts, Implementations1_4_0.getCrmEvents, - Implementations1_4_0.getResourceDocs, Implementations1_4_0.createTransactionRequest, Implementations1_4_0.getTransactionRequests, Implementations1_4_0.getTransactionRequestTypes, diff --git a/src/main/scala/code/api/v2_0_0/APIMethods200.scala b/src/main/scala/code/api/v2_0_0/APIMethods200.scala new file mode 100644 index 000000000..f59bc5984 --- /dev/null +++ b/src/main/scala/code/api/v2_0_0/APIMethods200.scala @@ -0,0 +1,1014 @@ +package code.api.v2_0_0 + +import java.text.SimpleDateFormat + +import code.api.util.APIUtil +import code.api.util.ErrorMessages +import code.api.v1_2_1.OBPAPI1_2_1._ + +import code.api.v1_2_1.{JSONFactory => JSONFactory121, APIMethods121} + +import code.api.v1_2_1.{AmountOfMoneyJSON => AmountOfMoneyJSON121} + + +import code.api.v1_4_0.JSONFactory1_4_0.{AddCustomerMessageJson} + +import code.bankconnectors.Connector +import code.model.dataAccess.{BankAccountCreation} + + +import net.liftweb.http.{JsonResponse, Req} +import net.liftweb.json +import net.liftweb.json.Extraction +import net.liftweb.common._ +import code.model._ +import net.liftweb.json.JsonAST.JValue +import APIUtil._ +import net.liftweb.util.Helpers._ +import net.liftweb.http.rest.RestHelper +import net.liftweb.common.Full +import code.kycdocuments.KycDocuments +import code.kycmedias.KycMedias +import code.kycstatuses.KycStatuses +import code.kycchecks.KycChecks +import code.socialmedia.{SocialMediaHandle, SocialMedia} +import net.liftweb.util.Props + +import scala.collection.immutable.Nil +import scala.collection.mutable.ArrayBuffer +// Makes JValue assignment to Nil work +import net.liftweb.json.JsonDSL._ +import code.customer.{CustomerMessages, Customer} +import code.util.Helper._ +import net.liftweb.http.js.JE.JsRaw + +import net.liftweb.json.{ShortTypeHints, DefaultFormats, Extraction} + + +trait APIMethods200 { + //needs to be a RestHelper to get access to JsonGet, JsonPost, etc. + self: RestHelper => + + // helper methods begin here + + + val defaultBankId = Props.get("defaultBank.bank_id", "DEFAULT_BANK_ID_NOT_SET") + + + + // shows a small representation of View + private def bankAccountBasicListToJson(bankAccounts: List[BankAccount], user : Box[User]): JValue = { + Extraction.decompose(basicBankAccountList(bankAccounts, user)) + } + + // Shows accounts without view + private def coreBankAccountListToJson(callerContext: CallerContext, codeContext: CodeContext, bankAccounts: List[BankAccount], user : Box[User]): JValue = { + Extraction.decompose(coreBankAccountList(callerContext, codeContext, bankAccounts, user)) + } + + private def basicBankAccountList(bankAccounts: List[BankAccount], user : Box[User]): List[BasicAccountJSON] = { + val accJson : List[BasicAccountJSON] = bankAccounts.map(account => { + val views = account.permittedViews(user) + val viewsAvailable : List[BasicViewJSON] = + views.map( v => { + JSONFactory200.createViewBasicJSON(v) + }) + JSONFactory200.createBasicAccountJSON(account,viewsAvailable) + }) + accJson + } + + private def coreBankAccountList(callerContext: CallerContext, codeContext: CodeContext, bankAccounts: List[BankAccount], user : Box[User]): List[CoreAccountJSON] = { + val accJson : List[CoreAccountJSON] = bankAccounts.map(account => { + val views = account.permittedViews(user) + val viewsAvailable : List[BasicViewJSON] = + views.map( v => { + JSONFactory200.createViewBasicJSON(v) + }) + + val dataContext = DataContext(user, Some(account.bankId), Some(account.accountId), Empty, Empty, Empty) + + val links = code.api.util.APIUtil.getHalLinks(callerContext, codeContext, dataContext) + + JSONFactory200.createCoreAccountJSON(account, links) + }) + accJson + } + + + + // helper methods end here + + val Implementations2_0_0 = new Object(){ + + val resourceDocs = ArrayBuffer[ResourceDoc]() + val apiRelations = ArrayBuffer[ApiRelation]() + + val emptyObjectJson : JValue = Nil + val apiVersion : String = "2_0_0" + + val exampleDateString : String ="22/08/2013" + val simpleDateFormat : SimpleDateFormat = new SimpleDateFormat("dd/mm/yyyy") + val exampleDate = simpleDateFormat.parse(exampleDateString) + + val codeContext = CodeContext(resourceDocs, apiRelations) + + + + + + resourceDocs += ResourceDoc( + allAccountsAllBanks, + apiVersion, + "allAccountsAllBanks", + "GET", + "/accounts", + "Get accounts at all banks (Authenticated + Anonymous access).", + """Returns the list of accounts at that the user has access to at all banks. + |For each account the API returns the account ID and the available views. + | + |If the user is not authenticated via OAuth, the list will contain only the accounts providing public views. If + |the user is authenticated, the list will contain non-public accounts to which the user has access, in addition to + |all public accounts. + |""", + emptyObjectJson, + emptyObjectJson, + emptyObjectJson :: Nil, + false, + false, + List(apiTagAccounts, apiTagPrivateData, apiTagPublicData)) + + + lazy val allAccountsAllBanks : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + //get accounts for all banks (private + public) + case "accounts" :: Nil JsonGet json => { + user => + Full(successJsonResponse(bankAccountBasicListToJson(BankAccount.accounts(user), user))) + } + } + + resourceDocs += ResourceDoc( + privateAccountsAllBanks, + apiVersion, + "privateAccountsAllBanks", + "GET", + "/my/accounts", + "Get private accounts at all banks (Authenticated access).", + """Returns the list of accounts containing private views for the user at all banks. + |For each account the API returns the ID and the available views. + | + |Authentication via OAuth is required.""", + emptyObjectJson, + emptyObjectJson, + emptyObjectJson :: Nil, + true, + true, + List(apiTagAccounts, apiTagPrivateData)) + + + apiRelations += ApiRelation(privateAccountsAllBanks, getCoreAccountById, "detail") + apiRelations += ApiRelation(privateAccountsAllBanks, privateAccountsAllBanks, "self") + + + + lazy val privateAccountsAllBanks : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + //get private accounts for all banks + case "my" :: "accounts" :: Nil JsonGet json => { + user => + + for { + u <- user ?~ ErrorMessages.UserNotLoggedIn + } yield { + val availableAccounts = BankAccount.nonPublicAccounts(u) + val coreBankAccountListJson = coreBankAccountListToJson(CallerContext(privateAccountsAllBanks), codeContext, availableAccounts, Full(u)) + val response = successJsonResponse(coreBankAccountListJson) + response + } + } + } + + + + resourceDocs += ResourceDoc( + publicAccountsAllBanks, + apiVersion, + "publicAccountsAllBanks", + "GET", + "/accounts/public", + "Get public accounts at all banks (Anonymous access).", + """Returns the list of accounts containing public views at all banks + |For each account the API returns the ID and the available views. Authentication via OAuth is required.""", + emptyObjectJson, + emptyObjectJson, + emptyObjectJson :: Nil, + false, + false, + List(apiTagAccounts, apiTagPublicData)) + + + + + + + lazy val publicAccountsAllBanks : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + //get public accounts for all banks + case "accounts" :: "public" :: Nil JsonGet json => { + user => + val publicAccountsJson = bankAccountBasicListToJson(BankAccount.publicAccounts, Empty) + Full(successJsonResponse(publicAccountsJson)) + } + } + + + + + resourceDocs += ResourceDoc( + allAccountsAtOneBank, + apiVersion, + "allAccountsAtOneBank", + "GET", + "/banks/BANK_ID/accounts", + "Get accounts at one bank (Authenticated + Anonymous access).", + """Returns the list of accounts at BANK_ID that the user has access to. + |For each account the API returns the account ID and the available views. + | + |If the user is not authenticated via OAuth, the list will contain only the accounts providing public views. + """, + emptyObjectJson, + emptyObjectJson, + emptyObjectJson :: Nil, + false, + false, + List(apiTagAccounts, apiTagPrivateData, apiTagPublicData) + ) + + lazy val allAccountsAtOneBank : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + //get accounts for a single bank (private + public) + case "banks" :: BankId(bankId) :: "accounts" :: Nil JsonGet json => { + user => + for{ + bank <- Bank(bankId) + } yield { + val availableAccounts = bank.accounts(user) + successJsonResponse(bankAccountBasicListToJson(availableAccounts, user)) + } + } + } + + resourceDocs += ResourceDoc( + privateAccountsAtOneBank, + apiVersion, + "privateAccountsAtOneBank", + "GET", + "/my/banks/BANK_ID/accounts", + "Get private accounts at one bank (Authenticated access).", + """Returns the list of accounts containing private views for the user at BANK_ID. + |For each account the API returns the ID and the available views. + | + |Authentication via OAuth is required.""", + emptyObjectJson, + emptyObjectJson, + emptyObjectJson :: Nil, + true, + true, + List(apiTagAccounts, apiTagPrivateData)) + + + def privateAccountsAtOneBankResult (bank: Bank, u: User) = { + val availableAccounts = bank.nonPublicAccounts(u) + successJsonResponse(bankAccountBasicListToJson(availableAccounts, Full(u))) + } + + def corePrivateAccountsAtOneBankResult (callerContext: CallerContext, codeContext: CodeContext, bank: Bank, u: User) = { + val availableAccounts = bank.nonPublicAccounts(u) + successJsonResponse(coreBankAccountListToJson(callerContext, codeContext, availableAccounts, Full(u))) + } + + + + // This contains an approach to surface a resource via different end points in case of a default bank. + // The second path is experimental + lazy val privateAccountsAtOneBank : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + + + //get private accounts for a single bank + case "my" :: "banks" :: BankId(bankId) :: "accounts" :: Nil JsonGet json => { + user => + for { + u <- user ?~ ErrorMessages.UserNotLoggedIn + bank <- Bank(bankId) + + } yield { + corePrivateAccountsAtOneBankResult(CallerContext(privateAccountsAtOneBank), codeContext, bank, u) + } + } + case "bank" :: "accounts" :: Nil JsonGet json => { + println("in accounts") + user => + for { + u <- user ?~ ErrorMessages.UserNotLoggedIn + bank <- Bank(BankId(defaultBankId)) + } yield { + corePrivateAccountsAtOneBankResult(CallerContext(privateAccountsAtOneBank), codeContext, bank, u) + } + } + + } + + resourceDocs += ResourceDoc( + publicAccountsAtOneBank, + apiVersion, + "publicAccountsAtOneBank", + "GET", + "/banks/BANK_ID/accounts/public", + "Get public accounts at one bank (Anonymous access).", + """Returns a list of the public accounts at BANK_ID. For each account the API returns the ID and the available views. + | + |Authentication via OAuth is not required.""", + emptyObjectJson, + emptyObjectJson, + emptyObjectJson :: Nil, + false, + false, + List(apiTagAccounts, apiTagPublicData)) + + lazy val publicAccountsAtOneBank : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + //get public accounts for a single bank + case "banks" :: BankId(bankId) :: "accounts" :: "public" :: Nil JsonGet json => { + user => + for { + bank <- Bank(bankId) + } yield { + val publicAccountsJson = bankAccountBasicListToJson(bank.publicAccounts, Empty) + successJsonResponse(publicAccountsJson) + } + } + } + + resourceDocs += ResourceDoc( + getKycDocuments, + apiVersion, + "getKycDocuments", + "GET", + "/customers/CUSTOMER_NUMBER/kyc_documents", + "Get KYC (know your customer) documents for a customer", + """Get a list of documents that affirm the identity of the customer + |Passport, driving licence etc. + |Authentication is required.""", + emptyObjectJson, + emptyObjectJson, + emptyObjectJson :: Nil, + false, + false, + List(apiTagCustomer, apiTagKyc)) + + lazy val getKycDocuments : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + case "customers" :: customerNumber :: "kyc_documents" :: Nil JsonGet _ => { + user => { + for { + u <- user ?~! ErrorMessages.UserNotLoggedIn + cNumber <- tryo(customerNumber) ?~! {ErrorMessages.CustomerNotFound} + } yield { + val kycDocuments = KycDocuments.kycDocumentProvider.vend.getKycDocuments(cNumber) + val json = JSONFactory200.createKycDocumentsJSON(kycDocuments) + successJsonResponse(Extraction.decompose(json)) + } + } + } + } + + + resourceDocs += ResourceDoc( + getKycMedia, + apiVersion, + "getKycMedia", + "GET", + "/customers/CUSTOMER_NUMBER/kyc_media", + "Get KYC Media for a customer", + """Get KYC media (scans, pictures, videos) that affirms the identity of the customer. + | + |Authentication is required.""", + emptyObjectJson, + emptyObjectJson, + emptyObjectJson :: Nil, + false, + false, + List(apiTagCustomer, apiTagKyc)) + + lazy val getKycMedia : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + case "customers" :: customerNumber :: "kyc_media" :: Nil JsonGet _ => { + user => { + for { + u <- user ?~! ErrorMessages.UserNotLoggedIn + cNumber <- tryo(customerNumber) ?~! {ErrorMessages.CustomerNotFound} + } yield { + val kycMedias = KycMedias.kycMediaProvider.vend.getKycMedias(cNumber) + val json = JSONFactory200.createKycMediasJSON(kycMedias) + successJsonResponse(Extraction.decompose(json)) + } + } + } + } + + resourceDocs += ResourceDoc( + getKycChecks, + apiVersion, + "getKycChecks", + "GET", + "/customers/CUSTOMER_NUMBER/kyc_checks", + "Get KYC checks for the logged in customer", + """Messages sent to the currently authenticated user. + | + |Authentication via OAuth is required.""", + emptyObjectJson, + emptyObjectJson, + emptyObjectJson :: Nil, + false, + false, + List(apiTagCustomer, apiTagKyc)) + + lazy val getKycChecks : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + case "customers" :: customerNumber :: "kyc_checks" :: Nil JsonGet _ => { + user => { + for { + u <- user ?~! ErrorMessages.UserNotLoggedIn + cNumber <- tryo(customerNumber) ?~! {ErrorMessages.CustomerNotFound} + } yield { + val kycChecks = KycChecks.kycCheckProvider.vend.getKycChecks(cNumber) + val json = JSONFactory200.createKycChecksJSON(kycChecks) + successJsonResponse(Extraction.decompose(json)) + } + } + } + } + resourceDocs += ResourceDoc( + getKycStatuses, + apiVersion, + "getKycStatuses", + "GET", + "/customers/CUSTOMER_NUMBER/kyc_statuses", + "Get the KYC statuses for a customer", + """Get the KYC statuses for a customer over time + | + |Authentication is required.""", + emptyObjectJson, + emptyObjectJson, + emptyObjectJson :: Nil, + false, + false, + List(apiTagCustomer, apiTagKyc)) + + lazy val getKycStatuses : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + case "customers" :: customerNumber :: "kyc_statuses" :: Nil JsonGet _ => { + user => { + for { + u <- user ?~! ErrorMessages.UserNotLoggedIn + cNumber <- tryo(customerNumber) ?~! {ErrorMessages.CustomerNotFound} + } yield { + val kycStatuses = KycStatuses.kycStatusProvider.vend.getKycStatuses(cNumber) + val json = JSONFactory200.createKycStatusesJSON(kycStatuses) + successJsonResponse(Extraction.decompose(json)) + } + } + } + } + + resourceDocs += ResourceDoc( + getSocialMediaHandles, + apiVersion, + "getSocialMedia", + "GET", + "/customers/CUSTOMER_NUMBER/social_media_handles", + "Get social media handles for a customer", + """Get social media handles for a customer. + | + |Authentication via OAuth is required.""", + emptyObjectJson, + emptyObjectJson, + emptyObjectJson :: Nil, + false, + false, + List(apiTagCustomer, apiTagKyc)) + + lazy val getSocialMediaHandles : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + case "customers" :: customerNumber :: "social_media_handles" :: Nil JsonGet _ => { + user => { + for { + u <- user ?~! ErrorMessages.UserNotLoggedIn + cNumber <- tryo(customerNumber) ?~! {ErrorMessages.CustomerNotFound} + } yield { + val kycSocialMedias = SocialMediaHandle.socialMediaHandleProvider.vend.getSocialMedias(cNumber) + val json = JSONFactory200.createSocialMediasJSON(kycSocialMedias) + successJsonResponse(Extraction.decompose(json)) + } + } + } + } + + + + + resourceDocs += ResourceDoc( + addKycDocument, + apiVersion, + "addKycDocument", + "POST", + "/banks/BANK_ID/customers/CUSTOMER_NUMBER/kyc_documents", + "Add a KYC document for the customer specified by CUSTOMER_NUMBER", + "KYC Documents contain the document type (e.g. passport), place of issue, expiry etc. ", + Extraction.decompose(KycDocumentJSON("wuwjfuha234678", "1234", "passport", "123567", exampleDate, "London", exampleDate)), + emptyObjectJson, + emptyObjectJson :: Nil, + false, + false, + List(apiTagCustomer, apiTagKyc) + ) + + // TODO customerNumber should be in the url but not also in the postedData + + lazy val addKycDocument : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + case "banks" :: BankId(bankId) :: "customers" :: customerNumber :: "kyc_documents" :: Nil JsonPost json -> _ => { + // customerNumber is duplicated in postedData. remove from that? + user => { + for { + u <- user ?~! ErrorMessages.UserNotLoggedIn + postedData <- tryo{json.extract[KycDocumentJSON]} ?~! ErrorMessages.InvalidJsonFormat + bank <- tryo(Bank(bankId).get) ?~! {ErrorMessages.BankNotFound} + customer <- Customer.customerProvider.vend.getUser(bankId, postedData.customer_number) ?~! ErrorMessages.CustomerNotFound + kycDocumentCreated <- booleanToBox( + KycDocuments.kycDocumentProvider.vend.addKycDocuments( + postedData.id, + postedData.customer_number, + postedData.`type`, + postedData.number, + postedData.issue_date, + postedData.issue_place, + postedData.expiry_date), + "Server error: could not add KycDocument") + } yield { + successJsonResponse(JsRaw("{}"), 201) + } + } + } + } + + resourceDocs += ResourceDoc( + addKycMedia, + apiVersion, + "addKycMedia", + "POST", + "/banks/BANK_ID/customers/CUSTOMER_NUMBER/kyc_media", + "Add some KYC media for the customer specified by CUSTOMER_NUMBER", + "KYC Media resources relate to KYC Documents and KYC Checks and contain media urls for scans of passports, utility bills etc.", + Extraction.decompose(KycMediaJSON("73hyfgayt6ywerwerasd", "1239879", "image", "http://www.example.com/id-docs/123/image.png", exampleDate, "wuwjfuha234678", "98FRd987auhf87jab")), + emptyObjectJson, + emptyObjectJson :: Nil, + false, + false, + List(apiTagCustomer, apiTagKyc) + ) + + lazy val addKycMedia : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + case "banks" :: BankId(bankId) :: "customers" :: customerNumber :: "kyc_media" :: Nil JsonPost json -> _ => { + // customerNumber is in url and duplicated in postedData. remove from that? + user => { + for { + u <- user ?~! ErrorMessages.UserNotLoggedIn + postedData <- tryo{json.extract[KycMediaJSON]} ?~! ErrorMessages.InvalidJsonFormat + bank <- tryo(Bank(bankId).get) ?~! {ErrorMessages.BankNotFound} + customer <- Customer.customerProvider.vend.getUser(bankId, postedData.customer_number) ?~! ErrorMessages.CustomerNotFound + kycDocumentCreated <- booleanToBox( + KycMedias.kycMediaProvider.vend.addKycMedias( + postedData.id, + postedData.customer_number, + postedData.`type`, + postedData.url, + postedData.date, + postedData.relates_to_kyc_document_id, + postedData.relates_to_kyc_check_id), + "Server error: could not add message") + } yield { + successJsonResponse(JsRaw("{}"), 201) + } + } + } + } + + resourceDocs += ResourceDoc( + addKycCheck, + apiVersion, + "addKycCheck", + "POST", + "/banks/BANK_ID/customers/CUSTOMER_NUMBER/kyc_check", + "Add a KYC check for the customer specified by CUSTOMER_NUMBER", + "KYC Checks store details of checks on a customer made by the KYC team, their comments and a satisfied status.", + Extraction.decompose(KycCheckJSON("98FRd987auhf87jab", "1239879", exampleDate, "online_meeting", "67876", "Simon Redfern", true, "")), + emptyObjectJson, + emptyObjectJson :: Nil, + false, + false, + List(apiTagCustomer, apiTagKyc) + ) + + lazy val addKycCheck : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + case "banks" :: BankId(bankId) :: "customers" :: customerNumber :: "kyc_check" :: Nil JsonPost json -> _ => { + // customerNumber is in url and duplicated in postedData. remove from that? + user => { + for { + u <- user ?~! ErrorMessages.UserNotLoggedIn + postedData <- tryo{json.extract[KycCheckJSON]} ?~! ErrorMessages.InvalidJsonFormat + bank <- tryo(Bank(bankId).get) ?~! {ErrorMessages.BankNotFound} + customer <- Customer.customerProvider.vend.getUser(bankId, postedData.customer_number) ?~! ErrorMessages.CustomerNotFound + kycCheckCreated <- booleanToBox( + KycChecks.kycCheckProvider.vend.addKycChecks( + postedData.id, + postedData.customer_number, + postedData.date, + postedData.how, + postedData.staff_user_id, + postedData.staff_name, + postedData.satisfied, + postedData.comments), + "Server error: could not add message") + } yield { + successJsonResponse(JsRaw("{}"), 201) + } + } + } + } + + resourceDocs += ResourceDoc( + addKycStatus, + apiVersion, + "addKycStatus", + "POST", + "/banks/BANK_ID/customers/CUSTOMER_NUMBER/kyc_statuses", + "Add a kyc_status for the customer specified by CUSTOMER_NUMBER", + "KYC Status is a timeline of the KYC status of the customer", + Extraction.decompose(KycStatusJSON("8762893876", true, exampleDate)), + emptyObjectJson, + emptyObjectJson :: Nil, + false, + false, + List(apiTagCustomer, apiTagKyc) + ) + + lazy val addKycStatus : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + case "banks" :: BankId(bankId) :: "customers" :: customerNumber :: "kyc_statuses" :: Nil JsonPost json -> _ => { + // customerNumber is in url and duplicated in postedData. remove from that? + user => { + for { + u <- user ?~! ErrorMessages.UserNotLoggedIn + postedData <- tryo{json.extract[KycStatusJSON]} ?~! ErrorMessages.InvalidJsonFormat + bank <- tryo(Bank(bankId).get) ?~! {ErrorMessages.BankNotFound} + customer <- Customer.customerProvider.vend.getUser(bankId, postedData.customer_number) ?~! ErrorMessages.CustomerNotFound + kycStatusCreated <- booleanToBox( + KycStatuses.kycStatusProvider.vend.addKycStatus( + postedData.customer_number, + postedData.ok, + postedData.date), + "Server error: could not add message") + } yield { + successJsonResponse(JsRaw("{}"), 201) + } + } + } + } + + resourceDocs += ResourceDoc( + addSocialMediaHandle, + apiVersion, + "addSocialMediaHandle", + "POST", + "/banks/BANK_ID/customers/CUSTOMER_NUMBER/social_media", + "Add a social media handle for the customer specified by CUSTOMER_NUMBER", + "", + Extraction.decompose(SocialMediaJSON("8762893876", "twitter", "susan@example.com", exampleDate, exampleDate)), + emptyObjectJson, + emptyObjectJson :: Nil, + false, + false, + List(apiTagCustomer) + ) + + lazy val addSocialMediaHandle : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + case "banks" :: BankId(bankId) :: "customers" :: customerNumber :: "social_media" :: Nil JsonPost json -> _ => { + // customerNumber is in url and duplicated in postedData. remove from that? + user => { + for { + u <- user ?~! ErrorMessages.UserNotLoggedIn + postedData <- tryo{json.extract[SocialMediaJSON]} ?~! ErrorMessages.InvalidJsonFormat + bank <- tryo(Bank(bankId).get) ?~! {ErrorMessages.BankNotFound} + customer <- Customer.customerProvider.vend.getUser(bankId, postedData.customer_number) ?~! ErrorMessages.CustomerNotFound + kycSocialMediaCreated <- booleanToBox( + SocialMediaHandle.socialMediaHandleProvider.vend.addSocialMedias( + postedData.customer_number, + postedData.`type`, + postedData.handle, + postedData.date_added, + postedData.date_activated), + "Server error: could not add") + } yield { + successJsonResponse(JsRaw("{}"), 201) + } + } + } + } + + resourceDocs += ResourceDoc( + getCoreAccountById, + apiVersion, + "coreAccountById", + "GET", + "/my/banks/BANK_ID/accounts/ACCOUNT_ID/account", + "Get account by id.", + """Information returned about an account specified by ACCOUNT_ID: + | + |* Number + |* Owners + |* Type + |* Balance + |* IBAN + | + | + |OAuth authentication is required""", + emptyObjectJson, + emptyObjectJson, + emptyObjectJson :: Nil, + true, + true, + apiTagAccounts :: Nil) + + lazy val getCoreAccountById : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + //get account by id (assume owner view requested) + case "my" :: "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "account" :: Nil JsonGet json => { + + user => + // TODO return specific error if bankId == "BANK_ID" or accountID == "ACCOUNT_ID" + // Should be a generic guard we can use for all calls (also for userId etc.) + for { + account <- BankAccount(bankId, accountId) + availableviews <- Full(account.permittedViews(user)) + // Assume owner view was requested + view <- View.fromUrl( ViewId("owner"), account) + moderatedAccount <- account.moderatedBankAccount(view, user) + } yield { + val viewsAvailable = availableviews.map(JSONFactory121.createViewJSON) + val moderatedAccountJson = JSONFactory200.createCoreBankAccountJSON(moderatedAccount, viewsAvailable) + val response = successJsonResponse(Extraction.decompose(moderatedAccountJson)) + response + } + } + } + + + + resourceDocs += ResourceDoc( + getCoreTransactionsForBankAccount, + apiVersion, + "getCoreTransactionsForBankAccount", + "GET", + "/my/banks/BANK_ID/accounts/ACCOUNT_ID/transactions", + "Get transactions.", + """Returns transactions list of the account specified by ACCOUNT_ID. + | + |Authentication is required. + | + |Possible custom headers for pagination: + | + |* obp_sort_by=CRITERIA ==> default value: "completed" field + |* obp_sort_direction=ASC/DESC ==> default value: DESC + |* obp_limit=NUMBER ==> default value: 50 + |* obp_offset=NUMBER ==> default value: 0 + |* obp_from_date=DATE => default value: date of the oldest transaction registered (format below) + |* obp_to_date=DATE => default value: date of the newest transaction registered (format below) + | + |**Date format parameter**: "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" (2014-07-01T00:00:00.000Z) ==> time zone is UTC.""", + emptyObjectJson, + emptyObjectJson, + emptyObjectJson :: Nil, + true, + true, + List(apiTagAccounts, apiTagTransactions)) + + lazy val getCoreTransactionsForBankAccount : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + //get transactions + case "my" :: "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "transactions" :: Nil JsonGet json => { + user => + + for { + params <- APIMethods121.getTransactionParams(json) + bankAccount <- BankAccount(bankId, accountId) + // Assume owner view was requested + view <- View.fromUrl( ViewId("owner"), bankAccount) + transactions <- bankAccount.getModeratedTransactions(user, view, params : _*) + } yield { + val json = JSONFactory200.createCoreTransactionsJSON(transactions) + successJsonResponse(Extraction.decompose(json)) + } + } + } + + + // Copied from 1.2.1 and modified + + resourceDocs += ResourceDoc( + accountById, + apiVersion, + "accountById", + "GET", + "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/account", + "Get account by id.", + """Information returned about an account specified by ACCOUNT_ID as moderated by the view (VIEW_ID): + | + |* Number + |* Owners + |* Type + |* Balance + |* IBAN + |* Available views (sorted by short_name) + | + |More details about the data moderation by the view [here](#1_2_1-getViewsForBankAccount). + | + |OAuth authentication is required if the 'is_public' field in view (VIEW_ID) is not set to `true`.""", + emptyObjectJson, + emptyObjectJson, + emptyObjectJson :: Nil, + false, + true, + apiTagAccounts :: Nil) + + lazy val accountById : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + //get account by id + case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "account" :: Nil JsonGet json => { + user => + for { + u <- user ?~! ErrorMessages.UserNotLoggedIn // Check we have a user (rather than error or empty) + bank <- tryo(Bank(bankId).get) ?~! {ErrorMessages.BankNotFound} // Check bank exists. + account <- tryo(BankAccount(bank.bankId, accountId).get) ?~! {ErrorMessages.AccountNotFound} // Check Account exists. + availableViews <- Full(account.permittedViews(user)) + view <- tryo(View.fromUrl(viewId, account).get) ?~! {ErrorMessages.ViewNotFound} + moderatedAccount <- account.moderatedBankAccount(view, user) + } yield { + val viewsAvailable = availableViews.map(JSONFactory121.createViewJSON).sortBy(_.short_name) + val moderatedAccountJson = JSONFactory121.createBankAccountJSON(moderatedAccount, viewsAvailable) + successJsonResponse(Extraction.decompose(moderatedAccountJson)) + } + } + } + + + ///// + + + resourceDocs += ResourceDoc( + getPermissionsForBankAccount, + apiVersion, + "getPermissionsForBankAccount", + "GET", + "/banks/BANK_ID/accounts/ACCOUNT_ID/permissions", + "Get access.", + """Returns the list of the permissions at BANK_ID for account ACCOUNT_ID, with each time a pair composed of the user and the views that he has access to. + | + |OAuth authentication is required and the user needs to have access to the owner view.""", + emptyObjectJson, + emptyObjectJson, + emptyObjectJson :: Nil, + false, + false, + List(apiTagAccounts, apiTagViews, apiTagEntitlements) + ) + + lazy val getPermissionsForBankAccount : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + //get access + case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "permissions" :: Nil JsonGet json => { + user => + for { + u <- user ?~! ErrorMessages.UserNotLoggedIn // Check we have a user (rather than error or empty) + bank <- tryo(Bank(bankId).get) ?~! {ErrorMessages.BankNotFound} // Check bank exists. + account <- tryo(BankAccount(bank.bankId, accountId).get) ?~! {ErrorMessages.AccountNotFound} // Check Account exists. + permissions <- account permissions u + } yield { + val permissionsJSON = JSONFactory121.createPermissionsJSON(permissions.sortBy(_.user.emailAddress)) + successJsonResponse(Extraction.decompose(permissionsJSON)) + } + } + } + + resourceDocs += ResourceDoc( + getPermissionForUserForBankAccount, + apiVersion, + "getPermissionForUserForBankAccount", + "GET", + "/banks/BANK_ID/accounts/ACCOUNT_ID/permissions/PROVIDER_ID/USER_ID", + "Get access for specific user.", + """Returns the list of the views at BANK_ID for account ACCOUNT_ID that a USER_ID at their provider PROVIDER_ID has access to. + |All url parameters must be [%-encoded](http://en.wikipedia.org/wiki/Percent-encoding), which is often especially relevant for USER_ID and PROVIDER_ID. + | + |OAuth authentication is required and the user needs to have access to the owner view.""", + emptyObjectJson, + emptyObjectJson, + emptyObjectJson :: Nil, + false, + false, + List(apiTagAccounts, apiTagViews, apiTagEntitlements)) + + lazy val getPermissionForUserForBankAccount : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + //get access for specific user + case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "permissions" :: providerId :: userId :: Nil JsonGet json => { + user => + for { + u <- user ?~! ErrorMessages.UserNotLoggedIn // Check we have a user (rather than error or empty) + bank <- tryo(Bank(bankId).get) ?~! {ErrorMessages.BankNotFound} // Check bank exists. + account <- tryo(BankAccount(bank.bankId, accountId).get) ?~! {ErrorMessages.AccountNotFound} // Check Account exists. + permission <- account permission(u, providerId, userId) + } yield { + val views = JSONFactory121.createViewsJSON(permission.views.sortBy(_.viewId.value)) + successJsonResponse(Extraction.decompose(views)) + } + } + } + + // + + lazy val tempTemp : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + case "banks" :: BankId(bankId) :: "customer" :: customerNumber :: "messages" :: Nil JsonPost json -> _ => { + user => { + for { + postedData <- tryo{json.extract[AddCustomerMessageJson]} ?~! "Incorrect json format" + bank <- tryo(Bank(bankId).get) ?~! {ErrorMessages.BankNotFound} + customer <- Customer.customerProvider.vend.getUser(bankId, customerNumber) ?~! "Customer not found" + messageCreated <- booleanToBox( + CustomerMessages.customerMessageProvider.vend.addMessage( + customer, bankId, postedData.message, postedData.from_department, postedData.from_person), + "Server error: could not add message") + } yield { + successJsonResponse(JsRaw("{}"), 201) + } + } + } + } + + //// + + + + resourceDocs += ResourceDoc( + createAccount, + apiVersion, + "createAccount", + "PUT", + "/banks/BANK_ID/accounts/NEW_ACCOUNT_ID", + "Create an Account at bank specified by BANK_ID with Id specified by NEW_ACCOUNT_ID", + "Note: Type is currently ignored and Amount must be zero. You can update the account label with another call (see updateAccountLabel)", + Extraction.decompose(CreateAccountJSON("CURRENT", AmountOfMoneyJSON121("EUR", "0"))), + emptyObjectJson, + emptyObjectJson :: Nil, + false, + false, + List(apiTagAccounts) + ) + + apiRelations += ApiRelation(createAccount, createAccount, "self") + apiRelations += ApiRelation(createAccount, getCoreAccountById, "detail") + + // Note: This doesn't currently work (links only have access to same version resource docs). TODO fix me. + apiRelations += ApiRelation(createAccount, Implementations1_2_1.updateAccountLabel, "update_label") + + + lazy val createAccount : PartialFunction[Req, Box[User] => Box[JsonResponse]] = { + // TODO document this code (make the extract work): "JsonPut json -> _ =>" + // Create a new account + case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: Nil JsonPut json -> _ => { + user => { + + for { + u <- user ?~! ErrorMessages.UserNotLoggedIn + jsonBody <- tryo (json.extract[CreateAccountJSON]) ?~ ErrorMessages.InvalidJsonFormat + initialBalanceAsString <- tryo (jsonBody.balance.amount) ?~ s"Problem getting balance amount" + accountType <- tryo(jsonBody.`type`) ?~ s"Problem getting type" + initialBalanceAsNumber <- tryo {BigDecimal(initialBalanceAsString)} ?~! ErrorMessages.InvalidInitalBalance + isTrue <- booleanToBox(0 == initialBalanceAsNumber) ?~ s"Initial balance must be zero" + currency <- tryo (jsonBody.balance.currency) ?~ s"Problem getting balance currency" + bank <- Bank(bankId) ?~ s"Bank $bankId not found" + accountDoesNotExist <- booleanToBox(BankAccount(bankId, accountId).isEmpty, + s"Account with id $accountId already exists at bank $bankId") + bankAccount <- Connector.connector.vend.createSandboxBankAccount(bankId, accountId, currency, initialBalanceAsNumber, u.name) + } yield { + BankAccountCreation.setAsOwner(bankId, accountId, u) + + val dataContext = DataContext(user, Some(bankAccount.bankId), Some(bankAccount.accountId), Empty, Empty, Empty) + val links = code.api.util.APIUtil.getHalLinks(CallerContext(createAccount), codeContext, dataContext) + val json = JSONFactory200.createCoreAccountJSON(bankAccount, links) + + successJsonResponse(Extraction.decompose(json)) + } + } + } + } + + } +} + +object APIMethods200 { +} diff --git a/src/main/scala/code/api/v2_0_0/JSONFactory2.0.0.scala b/src/main/scala/code/api/v2_0_0/JSONFactory2.0.0.scala new file mode 100644 index 000000000..a856ccbb2 --- /dev/null +++ b/src/main/scala/code/api/v2_0_0/JSONFactory2.0.0.scala @@ -0,0 +1,438 @@ +/** +Open Bank Project - API +Copyright (C) 2011-2015, TESOBE / Music Pictures Ltd + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + +Email: contact@tesobe.com +TESOBE / Music Pictures Ltd +Osloerstrasse 16/17 +Berlin 13359, Germany + + This product includes software developed at + TESOBE (http://www.tesobe.com/) + by + Simon Redfern : simon AT tesobe DOT com + Stefan Bethge : stefan AT tesobe DOT com + Everett Sochowski : everett AT tesobe DOT com + Ayoub Benali: ayoub AT tesobe DOT com + + */ +package code.api.v2_0_0 + +import java.net.URL +import java.util.Date +import code.api.util.APIUtil.ApiLink +import code.kycdocuments.KycDocument +import code.kycmedias.KycMedia +import code.kycstatuses.KycStatus +import code.kycchecks.KycCheck +import code.socialmedia.SocialMedia +import net.liftweb.common.{Box, Full} +import code.model._ +import net.liftweb.json.JsonAST.JValue + +// Import explicitly and rename so its clear. +import code.api.v1_2_1.{AccountHolderJSON => AccountHolderJSON121, AmountOfMoneyJSON => AmountOfMoneyJSON121, UserJSON => UserJSON121, ViewJSON => ViewJSON121, ThisAccountJSON => ThisAccountJSON121, OtherAccountJSON => OtherAccountJSON121, TransactionDetailsJSON => TransactionDetailsJSON121, JSONFactory => JSONFactory121, MinimalBankJSON => MinimalBankJSON121} + + + + + +// New in 2.0.0 + +class LinkJSON( + val href: URL, + val rel: String, + val method: String +) + +class LinksJSON( + val _links: List[LinkJSON] +) + +class ResultAndLinksJSON( + val result : JValue, + val links: LinksJSON +) + + + +class BasicViewJSON( + val id: String, + val short_name: String, + val is_public: Boolean +) + +case class BasicAccountsJSON( + accounts : List[BasicAccountJSON] +) + +// Basic Account has basic View +case class BasicAccountJSON( + id : String, + label : String, + views_available : List[BasicViewJSON], + bank_id : String +) + + +// Json used in account creation +case class CreateAccountJSON( + `type` : String, + balance : AmountOfMoneyJSON121 + ) + +// No view in core +case class CoreAccountJSON( + id : String, + label : String, + bank_id : String, + _links: JValue + ) + +case class KycDocumentJSON( + id: String, + customer_number: String, + `type`: String, + number: String, + issue_date: Date, + issue_place: String, + expiry_date: Date +) +case class KycDocumentsJSON(documents: List[KycDocumentJSON]) + +case class KycMediaJSON( + id: String, + customer_number: String, + `type`: String, + url: String, + date: Date, + relates_to_kyc_document_id: String, + relates_to_kyc_check_id: String +) +case class KycMediasJSON(medias: List[KycMediaJSON]) + +case class KycCheckJSON( + id: String, + customer_number: String, + date: Date, + how: String, + staff_user_id: String, + staff_name: String, + satisfied: Boolean, + comments: String +) +case class KycChecksJSON(checks: List[KycCheckJSON]) + +case class KycStatusJSON( + customer_number: String, + ok: Boolean, + date: Date +) +case class KycStatusesJSON(statuses: List[KycStatusJSON]) + +case class SocialMediaJSON( + customer_number: String, + `type`: String, + handle: String, + date_added: Date, + date_activated: Date +) +case class SocialMediasJSON(checks: List[SocialMediaJSON]) + +object JSONFactory200{ + + + // New in 2.0.0 + + + + + + + + def createViewBasicJSON(view : View) : BasicViewJSON = { + val alias = + if(view.usePublicAliasIfOneExists) + "public" + else if(view.usePrivateAliasIfOneExists) + "private" + else + "" + + new BasicViewJSON( + id = view.viewId.value, + short_name = stringOrNull(view.name), + is_public = view.isPublic + ) + } + + + def createBasicAccountJSON(account : BankAccount, basicViewsAvailable : List[BasicViewJSON] ) : BasicAccountJSON = { + new BasicAccountJSON( + account.accountId.value, + stringOrNull(account.label), + basicViewsAvailable, + account.bankId.value + ) + } + + // Contains only minimal info (could have more if owner) plus links + def createCoreAccountJSON(account : BankAccount, links: JValue ) : CoreAccountJSON = { + val coreAccountJson = new CoreAccountJSON( + account.accountId.value, + stringOrNull(account.label), + account.bankId.value, + links + ) + coreAccountJson + } + + + + + case class ModeratedCoreAccountJSON( + id : String, + label : String, + number : String, + owners : List[UserJSON121], + `type` : String, + balance : AmountOfMoneyJSON121, + IBAN : String, + swift_bic: String, + bank_id : String + ) + + + //// + + + case class CoreTransactionsJSON( + transactions: List[CoreTransactionJSON] + ) + + case class CoreTransactionJSON( + id : String, + account : ThisAccountJSON121, + counterparty : CoreCounterpartyJSON, + details : CoreTransactionDetailsJSON + ) + + + + case class CoreAccountHolderJSON( + name : String + ) + + + case class CoreCounterpartyJSON( + id : String, + holder : CoreAccountHolderJSON, + number : String, + kind : String, + IBAN : String, + swift_bic: String, + bank : MinimalBankJSON121 + ) + + + + + def createCoreTransactionsJSON(transactions: List[ModeratedTransaction]) : CoreTransactionsJSON = { + new CoreTransactionsJSON(transactions.map(createCoreTransactionJSON)) + } + + case class CoreTransactionDetailsJSON( + `type` : String, + description : String, + posted : Date, + completed : Date, + new_balance : AmountOfMoneyJSON121, + value : AmountOfMoneyJSON121 + ) + + + + def createCoreTransactionDetailsJSON(transaction : ModeratedTransaction) : CoreTransactionDetailsJSON = { + new CoreTransactionDetailsJSON( + `type` = stringOptionOrNull(transaction.transactionType), + description = stringOptionOrNull(transaction.description), + posted = transaction.startDate.getOrElse(null), + completed = transaction.finishDate.getOrElse(null), + new_balance = JSONFactory121.createAmountOfMoneyJSON(transaction.currency, transaction.balance), + value= JSONFactory121.createAmountOfMoneyJSON(transaction.currency, transaction.amount.map(_.toString)) + ) + } + + + def createCoreTransactionJSON(transaction : ModeratedTransaction) : CoreTransactionJSON = { + new CoreTransactionJSON( + id = transaction.id.value, + account = transaction.bankAccount.map(JSONFactory121.createThisAccountJSON).getOrElse(null), + counterparty = transaction.otherBankAccount.map(createCoreCounterparty).getOrElse(null), + details = createCoreTransactionDetailsJSON(transaction) + ) + } + + + + + + case class CounterpartiesJSON( + counterparties : List[CoreCounterpartyJSON] + ) + + + def createCoreCounterparty(bankAccount : ModeratedOtherBankAccount) : CoreCounterpartyJSON = { + new CoreCounterpartyJSON( + id = bankAccount.id, + number = stringOptionOrNull(bankAccount.number), + kind = stringOptionOrNull(bankAccount.kind), + IBAN = stringOptionOrNull(bankAccount.iban), + swift_bic = stringOptionOrNull(bankAccount.swift_bic), + bank = JSONFactory121.createMinimalBankJSON(bankAccount), + holder = createAccountHolderJSON(bankAccount.label.display, bankAccount.isAlias) + ) + } + + + + def createAccountHolderJSON(owner : User, isAlias : Boolean) : CoreAccountHolderJSON = { + // Note we are not using isAlias + new CoreAccountHolderJSON( + name = owner.name + ) + } + + def createAccountHolderJSON(name : String, isAlias : Boolean) : CoreAccountHolderJSON = { + // Note we are not using isAlias + new CoreAccountHolderJSON( + name = name + ) + } + + + + def createCoreBankAccountJSON(account : ModeratedBankAccount, viewsAvailable : List[ViewJSON121]) : ModeratedCoreAccountJSON = { + val bankName = account.bankName.getOrElse("") + new ModeratedCoreAccountJSON ( + account.accountId.value, + JSONFactory121.stringOptionOrNull(account.label), + JSONFactory121.stringOptionOrNull(account.number), + JSONFactory121.createOwnersJSON(account.owners.getOrElse(Set()), bankName), + JSONFactory121.stringOptionOrNull(account.accountType), + JSONFactory121.createAmountOfMoneyJSON(account.currency.getOrElse(""), account.balance), + JSONFactory121.stringOptionOrNull(account.iban), + JSONFactory121.stringOptionOrNull(account.swift_bic), + stringOrNull(account.bankId.value) + ) + } + + + + + + + def createKycDocumentJSON(kycDocument : KycDocument) : KycDocumentJSON = { + new KycDocumentJSON( + id = kycDocument.idKycDocument, + customer_number = kycDocument.customerNumber, + `type` = kycDocument.`type`, + number = kycDocument.number, + issue_date = kycDocument.issueDate, + issue_place = kycDocument.issuePlace, + expiry_date = kycDocument.expiryDate + ) + } + + def createKycDocumentsJSON(messages : List[KycDocument]) : KycDocumentsJSON = { + KycDocumentsJSON(messages.map(createKycDocumentJSON)) + } + + def createKycMediaJSON(kycMedia : KycMedia) : KycMediaJSON = { + new KycMediaJSON( + id = kycMedia.idKycMedia, + customer_number = kycMedia.customerNumber, + `type` = kycMedia.`type`, + url = kycMedia.url, + date = kycMedia.date, + relates_to_kyc_document_id = kycMedia.relatesToKycDocumentId, + relates_to_kyc_check_id = kycMedia.relatesToKycCheckId + ) + } + def createKycMediasJSON(messages : List[KycMedia]) : KycMediasJSON = { + KycMediasJSON(messages.map(createKycMediaJSON)) + } + + def createKycCheckJSON(kycCheck : KycCheck) : KycCheckJSON = { + new KycCheckJSON( + id = kycCheck.idKycCheck, + customer_number = kycCheck.customerNumber, + date = kycCheck.date, + how = kycCheck.how, + staff_user_id = kycCheck.staffUserId, + staff_name = kycCheck.staffName, + satisfied = kycCheck.satisfied, + comments = kycCheck.comments + ) + } + def createKycChecksJSON(messages : List[KycCheck]) : KycChecksJSON = { + KycChecksJSON(messages.map(createKycCheckJSON)) + } + + def createKycStatusJSON(kycStatus : KycStatus) : KycStatusJSON = { + new KycStatusJSON( + customer_number = kycStatus.customerNumber, + ok = kycStatus.ok, + date = kycStatus.date + ) + } + def createKycStatusesJSON(messages : List[KycStatus]) : KycStatusesJSON = { + KycStatusesJSON(messages.map(createKycStatusJSON)) + } + + def createSocialMediaJSON(socialMedia : SocialMedia) : SocialMediaJSON = { + new SocialMediaJSON( + customer_number = socialMedia.customerNumber, + `type` = socialMedia.`type`, + handle = socialMedia.handle, + date_added = socialMedia.dateAdded, + date_activated = socialMedia.dateActivated + ) + } + def createSocialMediasJSON(messages : List[SocialMedia]) : SocialMediasJSON = { + SocialMediasJSON(messages.map(createSocialMediaJSON)) + } + + // Copied from 1.2.1 (import just this def instead?) + def stringOrNull(text : String) = + if(text == null || text.isEmpty) + null + else + text + + // Copied from 1.2.1 (import just this def instead?) + def stringOptionOrNull(text : Option[String]) = + text match { + case Some(t) => stringOrNull(t) + case _ => null + } + + + + + + +} \ No newline at end of file diff --git a/src/main/scala/code/api/v2_0_0/OBPAPI2_0_0.scala b/src/main/scala/code/api/v2_0_0/OBPAPI2_0_0.scala new file mode 100644 index 000000000..a06d2f963 --- /dev/null +++ b/src/main/scala/code/api/v2_0_0/OBPAPI2_0_0.scala @@ -0,0 +1,175 @@ +/** +Open Bank Project - API +Copyright (C) 2011-2015, TESOBE / Music Pictures Ltd + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + +Email: contact@tesobe.com +TESOBE / Music Pictures Ltd +Osloerstrasse 16/17 +Berlin 13359, Germany + + This product includes software developed at + TESOBE (http://www.tesobe.com/) + by + Simon Redfern : simon AT tesobe DOT com + Stefan Bethge : stefan AT tesobe DOT com + Everett Sochowski : everett AT tesobe DOT com + Ayoub Benali: ayoub AT tesobe DOT com + + */ +package code.api.v2_0_0 + +import code.api.v1_3_0.APIMethods130 +import code.api.v1_4_0.{APIMethods140} +import net.liftweb.json.Extraction +import net.liftweb.json.JsonAST._ +import net.liftweb.common.Loggable +import code.api.OBPRestHelper + +object OBPAPI2_0_0 extends OBPRestHelper with APIMethods130 with APIMethods140 with APIMethods200 with Loggable { + + + val VERSION = "2.0.0" + + + // Note: Since we pattern match on these routes, if two implementations match a given url the first will match + + val routes = List( + Implementations1_2_1.root(VERSION), + Implementations1_2_1.allBanks, + Implementations1_2_1.bankById, + // Now in 2_0_0 +// Implementations1_2_1.allAccountsAllBanks, +// Implementations1_2_1.privateAccountsAllBanks, +// Implementations1_2_1.publicAccountsAllBanks, +// Implementations1_2_1.allAccountsAtOneBank, +// Implementations1_2_1.privateAccountsAtOneBank, +// Implementations1_2_1.publicAccountsAtOneBank, +// Implementations1_2_1.accountById, + Implementations1_2_1.updateAccountLabel, + Implementations1_2_1.getViewsForBankAccount, + Implementations1_2_1.createViewForBankAccount, + Implementations1_2_1.updateViewForBankAccount, + Implementations1_2_1.deleteViewForBankAccount, +// Implementations1_2_1.getPermissionsForBankAccount, +// Implementations1_2_1.getPermissionForUserForBankAccount, + Implementations1_2_1.addPermissionForUserForBankAccountForMultipleViews, + Implementations1_2_1.addPermissionForUserForBankAccountForOneView, + Implementations1_2_1.removePermissionForUserForBankAccountForOneView, + Implementations1_2_1.removePermissionForUserForBankAccountForAllViews, + Implementations1_2_1.getCounterpartiesForBankAccount, + Implementations1_2_1.getCounterpartyByIdForBankAccount, + Implementations1_2_1.getCounterpartyMetadata, + Implementations1_2_1.getCounterpartyPublicAlias, + Implementations1_2_1.addCounterpartyPublicAlias, + Implementations1_2_1.updateCounterpartyPublicAlias, + Implementations1_2_1.deleteCounterpartyPublicAlias, + Implementations1_2_1.getCounterpartyPrivateAlias, + Implementations1_2_1.addCounterpartyPrivateAlias, + Implementations1_2_1.updateCounterpartyPrivateAlias, + Implementations1_2_1.deleteCounterpartyPrivateAlias, + Implementations1_2_1.addCounterpartyMoreInfo, + Implementations1_2_1.updateCounterpartyMoreInfo, + Implementations1_2_1.deleteCounterpartyMoreInfo, + Implementations1_2_1.addCounterpartyUrl, + Implementations1_2_1.updateCounterpartyUrl, + Implementations1_2_1.deleteCounterpartyUrl, + Implementations1_2_1.addCounterpartyImageUrl, + Implementations1_2_1.updateCounterpartyImageUrl, + Implementations1_2_1.deleteCounterpartyImageUrl, + Implementations1_2_1.addCounterpartyOpenCorporatesUrl, + Implementations1_2_1.updateCounterpartyOpenCorporatesUrl, + Implementations1_2_1.deleteCounterpartyOpenCorporatesUrl, + Implementations1_2_1.addCounterpartyCorporateLocation, + Implementations1_2_1.updateCounterpartyCorporateLocation, + Implementations1_2_1.deleteCounterpartyCorporateLocation, + Implementations1_2_1.addCounterpartyPhysicalLocation, + Implementations1_2_1.updateCounterpartyPhysicalLocation, + Implementations1_2_1.deleteCounterpartyPhysicalLocation, + Implementations1_2_1.getTransactionsForBankAccount, + Implementations1_2_1.getTransactionByIdForBankAccount, + Implementations1_2_1.getTransactionNarrative, + Implementations1_2_1.addTransactionNarrative, + Implementations1_2_1.updateTransactionNarrative, + Implementations1_2_1.deleteTransactionNarrative, + Implementations1_2_1.getCommentsForViewOnTransaction, + Implementations1_2_1.addCommentForViewOnTransaction, + Implementations1_2_1.deleteCommentForViewOnTransaction, + Implementations1_2_1.getTagsForViewOnTransaction, + Implementations1_2_1.addTagForViewOnTransaction, + Implementations1_2_1.deleteTagForViewOnTransaction, + Implementations1_2_1.getImagesForViewOnTransaction, + Implementations1_2_1.addImageForViewOnTransaction, + Implementations1_2_1.deleteImageForViewOnTransaction, + Implementations1_2_1.getWhereTagForViewOnTransaction, + Implementations1_2_1.addWhereTagForViewOnTransaction, + Implementations1_2_1.updateWhereTagForViewOnTransaction, + Implementations1_2_1.deleteWhereTagForViewOnTransaction, + Implementations1_2_1.getCounterpartyForTransaction, + Implementations1_2_1.makePayment, + + // New in 1.3.0 + Implementations1_3_0.getCards, + Implementations1_3_0.getCardsForBank, + + // New in 1.4.0 + Implementations1_4_0.getCustomer, + Implementations1_4_0.addCustomer, + Implementations1_4_0.getCustomerMessages, + Implementations1_4_0.addCustomerMessage, + Implementations1_4_0.getBranches, + Implementations1_4_0.getAtms, + Implementations1_4_0.getProducts, + Implementations1_4_0.getCrmEvents, + Implementations1_4_0.createTransactionRequest, + Implementations1_4_0.getTransactionRequests, + Implementations1_4_0.getTransactionRequestTypes, + Implementations1_4_0.answerTransactionRequestChallenge, + + // Modified in 2.0.0 (less info about the views) + Implementations2_0_0.allAccountsAllBanks, + Implementations2_0_0.privateAccountsAllBanks, + Implementations2_0_0.publicAccountsAllBanks, + Implementations2_0_0.allAccountsAtOneBank, + Implementations2_0_0.privateAccountsAtOneBank, + Implementations2_0_0.publicAccountsAtOneBank, + // Modified in 2.0.0 (added sorting and better guards / error messages) + Implementations2_0_0.accountById, + Implementations2_0_0.getPermissionsForBankAccount, + Implementations2_0_0.getPermissionForUserForBankAccount, + // New in 2.0.0 + Implementations2_0_0.getKycDocuments, + Implementations2_0_0.getKycMedia, + Implementations2_0_0.getKycStatuses, + Implementations2_0_0.getKycChecks, + Implementations2_0_0.getSocialMediaHandles, + Implementations2_0_0.addKycDocument, + Implementations2_0_0.addKycMedia, + Implementations2_0_0.addKycStatus, + Implementations2_0_0.addKycCheck, + Implementations2_0_0.addSocialMediaHandle, + Implementations2_0_0.getCoreAccountById, + Implementations2_0_0.getCoreTransactionsForBankAccount, + Implementations2_0_0.createAccount + //Implementations2_0_0.testLinks + ) + + routes.foreach(route => { + oauthServe(apiPrefix{route}) + }) + + + +} \ No newline at end of file diff --git a/src/main/scala/code/bankconnectors/Connector.scala b/src/main/scala/code/bankconnectors/Connector.scala index dfed0f295..cfdc3e897 100644 --- a/src/main/scala/code/bankconnectors/Connector.scala +++ b/src/main/scala/code/bankconnectors/Connector.scala @@ -134,7 +134,7 @@ trait Connector { //set initial status //for sandbox / testing: depending on amount, we ask for challenge or not val status = - if (transactionRequestType.value == "SANDBOX" && body.value.currency == "EUR" && BigDecimal(body.value.amount) < 100) { + if (transactionRequestType.value == TransactionRequests.CHALLENGE_SANDBOX_TAN && BigDecimal(body.value.amount) < 100) { TransactionRequests.STATUS_COMPLETED } else { TransactionRequests.STATUS_INITIATED @@ -345,4 +345,4 @@ trait Connector { def updateAccountLabel(bankId: BankId, accountId: AccountId, label: String): Boolean - } + } \ No newline at end of file diff --git a/src/main/scala/code/bankconnectors/KafkaConnector.scala b/src/main/scala/code/bankconnectors/KafkaConnector.scala deleted file mode 100644 index da8c5602f..000000000 --- a/src/main/scala/code/bankconnectors/KafkaConnector.scala +++ /dev/null @@ -1,305 +0,0 @@ -package code.bankconnectors - -/* -Open Bank Project - API -Copyright (C) 2011-2015, TESOBE / Music Pictures Ltd - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see http://www.gnu.org/licenses/. - -Email: contact@tesobe.com -TESOBE / Music Pictures Ltd -Osloerstrasse 16/17 -Berlin 13359, Germany -*/ - - -import java.text.{SimpleDateFormat, DateFormat} -import java.util.{Date, UUID, TimeZone, Locale, Properties} - -import code.model._ -import code.util.Helper -import net.liftweb.common.{Loggable, Empty, Full, Box, Failure} -import net.liftweb.mapper._ -import net.liftweb.util.Helpers._ -import net.liftweb.util.{False, Props} - -import scala.concurrent.ops._ -import scala.concurrent.duration._ - -import kafka.utils.{ZkUtils, ZKStringSerializer} -import org.I0Itec.zkclient.ZkClient -import kafka.consumer.Consumer -import kafka.consumer._ -import kafka.consumer.KafkaStream -import kafka.message._ -import kafka.producer.{KeyedMessage, Producer, ProducerConfig} - -import code.model.dataAccess.{Account, MappedBank} -import code.model.Transaction - -object KafkaConnector extends Connector with Loggable { - - - - //gets banks handled by this connector - override def getBanks: List[Bank] = { - // Generate random uuid to be used as request-respose match id - val reqId: String = UUID.randomUUID().toString - - // Create Kafka producer, using list of brokers from Zookeeper - val producer: KafkaProducer = new KafkaProducer() - // Send request to Kafka, marked with reqId - // so we can fetch the corresponding response - val argList: Map[String, String] = Map() - - producer.send(reqId, "getBanks", argList, "1") - - // Request sent, now we wait for response with the same reqId - val consumer = new KafkaConsumer() - val rList = consumer.getResponse(reqId) - // Loop through list of responses and create entry for each - val res = { for ( r <- rList ) yield { - MappedBank.create - .permalink(r.getOrElse("permalink", "")) - .fullBankName(r.getOrElse("fullBankName", "")) - .shortBankName(r.getOrElse("shortBankName", "")) - .logoURL(r.getOrElse("logoURL", "")) - .websiteURL(r.getOrElse("websiteURL", "")) - } - } - // Return list of results - res - } - - - // Gets bank identified by id - override def getBank(bankId: code.model.BankId): Box[Bank] = { - // Generate random uuid to be used as request-respose match id - val reqId: String = UUID.randomUUID().toString - - // Create Kafka producer, using list of brokers from Zookeeper - val producer: KafkaProducer = new KafkaProducer() - // Send request to Kafka, marked with reqId - // so we can fetch the corresponding response - val argList = Map( "bankId" -> bankId.toString ) - - producer.send(reqId, "getBank", argList, "1") - - // Request sent, now we wait for response with the same reqId - val consumer = new KafkaConsumer() - // Create entry only for the first item on returned list - val r = consumer.getResponse(reqId).head - val res = MappedBank.create - .permalink(r.getOrElse("permalink", "")) - .fullBankName(r.getOrElse("fullBankName", "")) - .shortBankName(r.getOrElse("shortBankName", "")) - .logoURL(r.getOrElse("logoURL", "")) - .websiteURL(r.getOrElse("websiteURL", "")) - // Return result - Full(res) - - } - - - // Gets bank account identified by bankid and accountid - override def getBankAccount(bankId : BankId, accountId : AccountId) : Box[BankAccount] = { - // Generate random uuid to be used as request-respose match id - val reqId: String = UUID.randomUUID().toString - - // Create Kafka producer, using list of brokers from Zookeeper - val producer: KafkaProducer = new KafkaProducer() - // Send request to Kafka, marked with reqId - // so we can fetch the corresponding response - val argList = Map( "bankId" -> bankId.toString, - "accountId" -> accountId.toString ) - - producer.send(reqId, "getBankAccount", argList, "1") - - // Request sent, now we wait for response with the same reqId - val consumer = new KafkaConsumer() - // Create entry only for the first item on returned list - val r = consumer.getResponse(reqId).head - val res = new BankAccount { - val accountId = AccountId(r.getOrElse("accountId", "")); - val bankId = BankId(r.getOrElse("bankId", "")); - val uuid = r.getOrElse("uuid", ""); - val accountHolder = r.getOrElse("accountHolder", ""); - val accountType = r.getOrElse("accountType", ""); - val currency = r.getOrElse("currency", ""); - val label = r.getOrElse("label", ""); - val name = r.getOrElse("name", ""); - val number = r.getOrElse("number", ""); - val balance = BigDecimal(r.getOrElse("balance", "0.0")); - val swift_bic = Some(r.getOrElse("swift_bic", "")); - val iban = Some(r.getOrElse("iban", "")); - val lastUpdate = new SimpleDateFormat("EEE MMMM d HH:mm:ss z yyyy", Locale.ENGLISH).parse(r.getOrElse("lastUpdate", "Thu Jan 1 00:00:00 UTC 1970")) - } - - // Return list of results - Full(res) - } - - // Gets transaction identified by bankid, accountid and transactionId - def getTransaction(bankId: BankId, accountId: AccountId, transactionId: TransactionId): Box[Transaction] = { - // Generate random uuid to be used as request-respose match id - val reqId: String = UUID.randomUUID().toString - - // Create Kafka producer, using list of brokers from Zookeeper - val producer: KafkaProducer = new KafkaProducer() - // Send request to Kafka, marked with reqId - // so we can fetch the corresponding response - val argList = Map( "bankId" -> bankId.toString, - "accountId" -> accountId.toString, - "transactionId" -> transactionId.toString ) - - producer.send(reqId, "getTransaction", argList, "1") - - // Request sent, now we wait for response with the same reqId - val consumer = new KafkaConsumer() - // Create entry only for the first item on returned list - val r = consumer.getResponse(reqId).head - - // helper for creating otherbankaccount - def createOtherBankAccount(alreadyFoundMetadata : Option[OtherBankAccountMetadata]) = { - new OtherBankAccount( - id = alreadyFoundMetadata.map(_.metadataId).getOrElse(""), - label = r.getOrElse("label", ""), - nationalIdentifier = r.getOrElse("nationalIdentifier ", ""), - swift_bic = Some(r.getOrElse("swift_bic", "")), //TODO: need to add this to the json/model - iban = Some(r.getOrElse("iban", "")), - number = r.getOrElse("number", ""), - bankName = r.getOrElse("bankName", ""), - kind = r.getOrElse("accountType", ""), - originalPartyBankId = new BankId(r.getOrElse("bankId", "")), - originalPartyAccountId = new AccountId(r.getOrElse("accountId", "")), - alreadyFoundMetadata = alreadyFoundMetadata - ) - } - - //creates a dummy OtherBankAccount without an OtherBankAccountMetadata, which results in one being generated (in OtherBankAccount init) - val dummyOtherBankAccount = createOtherBankAccount(None) - //and create the proper OtherBankAccount with the correct "id" attribute set to the metadataId of the OtherBankAccountMetadata object - //note: as we are passing in the OtherBankAccountMetadata we don't incur another db call to get it in OtherBankAccount init - val otherAccount = createOtherBankAccount(Some(dummyOtherBankAccount.metadata)) - - Full( - new Transaction( - TransactionId(r.getOrElse("accountId", "")).value, // uuid:String - TransactionId(r.getOrElse("accountId", "")), // id:TransactionId - getBankAccount(BankId(r.getOrElse("bankId", "")), AccountId(r.getOrElse("accountId", ""))).openOr(null), // thisAccount:BankAccount - otherAccount, // otherAccount:OtherBankAccount - r.getOrElse("transactionType", ""), // transactionType:String - BigDecimal(r.getOrElse("amount", "0.0")), // val amount:BigDecimal - r.getOrElse("currency", ""), // currency:String - Some(r.getOrElse("description", "")), // description:Option[String] - new SimpleDateFormat("EEE MMMM d HH:mm:ss z yyyy", Locale.ENGLISH).parse(r.getOrElse("startDate", "Thu Jan 1 00:00:00 UTC 1970")), // startDate:Date - new SimpleDateFormat("EEE MMMM d HH:mm:ss z yyyy", Locale.ENGLISH).parse(r.getOrElse("finishDate", "Thu Jan 1 00:00:00 UTC 1970")), // finishDate:Date - BigDecimal(r.getOrElse("balance", "0.0")) // balance:BigDecimal - )) - } - - // Gets transactions identified by bankid, accountid and filtered by queryparams - def getTransactions(bankId: BankId, accountId: AccountId, queryParams: OBPQueryParam*): Box[List[Transaction]] = { - // Generate random uuid to be used as request-respose match id - val reqId: String = UUID.randomUUID().toString - - // Create Kafka producer, using list of brokers from Zookeeper - val producer: KafkaProducer = new KafkaProducer() - // Send request to Kafka, marked with reqId - // so we can fetch the corresponding response - val argList = Map( "bankId" -> bankId.toString, - "accountId" -> accountId.toString, - "queryParams" -> queryParams.toString ) - - producer.send(reqId, "getTransactions", argList, "1") - - // Request sent, now we wait for response with the same reqId - val consumer = new KafkaConsumer() - // Create entry only for the first item on returned list - val rList = consumer.getResponse(reqId) - - // Loop through list of responses and create entry for each - val res = { for ( r <- rList ) yield { - - // helper for creating otherbankaccount - def createOtherBankAccount(alreadyFoundMetadata : Option[OtherBankAccountMetadata]) = { - // TODO Can we improve this? Remove duplication / have a more direct mapping of objects on the queue? - new OtherBankAccount( - id = alreadyFoundMetadata.map(_.metadataId).getOrElse(""), - label = r.getOrElse("label", ""), - nationalIdentifier = r.getOrElse("nationalIdentifier ", ""), - swift_bic = Some(r.getOrElse("swift_bic", "")), //TODO: need to add this to the json/model - iban = Some(r.getOrElse("iban", "")), - number = r.getOrElse("number", ""), - bankName = r.getOrElse("bankName", ""), - kind = r.getOrElse("accountType", ""), - originalPartyBankId = new BankId(r.getOrElse("bankId", "")), - originalPartyAccountId = new AccountId(r.getOrElse("accountId", "")), - alreadyFoundMetadata = alreadyFoundMetadata - ) - } - - //creates a dummy OtherBankAccount without an OtherBankAccountMetadata, which results in one being generated (in OtherBankAccount init) - val dummyOtherBankAccount = createOtherBankAccount(None) - //and create the proper OtherBankAccount with the correct "id" attribute set to the metadataId of the OtherBankAccountMetadata object - //note: as we are passing in the OtherBankAccountMetadata we don't incur another db call to get it in OtherBankAccount init - val otherAccount = createOtherBankAccount(Some(dummyOtherBankAccount.metadata)) - - new Transaction( - TransactionId(r.getOrElse("accountId", "")).value, // uuid:String - TransactionId(r.getOrElse("accountId", "")), // id:TransactionId - getBankAccount(BankId(r.getOrElse("bankId", "")), AccountId(r.getOrElse("accountId", ""))).openOr(null), // thisAccount:BankAccount - otherAccount, // otherAccount:OtherBankAccount - r.getOrElse("transactionType", ""), // transactionType:String - BigDecimal(r.getOrElse("amount", "0.0")), // val amount:BigDecimal - r.getOrElse("currency", ""), // currency:String - Some(r.getOrElse("description", "")), // description:Option[String] - new SimpleDateFormat("EEE MMMM d HH:mm:ss z yyyy", Locale.ENGLISH).parse(r.getOrElse("startDate", "Thu Jan 1 00:00:00 UTC 1970")), // startDate:Date - new SimpleDateFormat("EEE MMMM d HH:mm:ss z yyyy", Locale.ENGLISH).parse(r.getOrElse("finishDate", "Thu Jan 1 00:00:00 UTC 1970")), // finishDate:Date - BigDecimal(r.getOrElse("balance", "0.0")) // balance:BigDecimal - ) - } - } - Full(res) - } - - - def getBankAccountType(bankId: code.model.BankId, accountId: code.model.AccountId): net.liftweb.common.Box[code.bankconnectors.KafkaConnector.AccountType] = ??? - def accountExists(bankId: code.model.BankId,accountNumber: String): Boolean = ??? - def addCashTransactionAndUpdateBalance(account: code.bankconnectors.KafkaConnector.AccountType,cashTransaction: code.tesobe.CashTransaction): Unit = ??? - def createBankAndAccount(bankName: String,bankNationalIdentifier: String,accountNumber: String,accountHolderName: String): (code.model.Bank, code.model.BankAccount) = ??? - def createImportedTransaction(transaction: code.management.ImporterAPI.ImporterTransaction): net.liftweb.common.Box[code.model.Transaction] = ??? - def createSandboxBankAccount(bankId: code.model.BankId,accountId: code.model.AccountId,accountNumber: String,currency: String,initialBalance: BigDecimal,accountHolderName: String): net.liftweb.common.Box[code.model.BankAccount] = ??? - protected def createTransactionRequestImpl(transactionRequestId: code.model.TransactionRequestId,transactionRequestType: code.model.TransactionRequestType,fromAccount: code.model.BankAccount,counterparty: code.model.BankAccount,body: code.transactionrequests.TransactionRequests.TransactionRequestBody,status: String): net.liftweb.common.Box[code.transactionrequests.TransactionRequests.TransactionRequest] = ??? - def getAccountByUUID(uuid: String): net.liftweb.common.Box[code.bankconnectors.KafkaConnector.AccountType] = ??? - def getAccountHolders(bankId: code.model.BankId,accountID: code.model.AccountId): Set[code.model.User] = ??? - def getMatchingTransactionCount(bankNationalIdentifier: String,accountNumber: String,amount: String,completed: java.util.Date,otherAccountHolder: String): Int = ??? - def getOtherBankAccount(bankId: code.model.BankId,accountID: code.model.AccountId,otherAccountID: String): net.liftweb.common.Box[code.model.OtherBankAccount] = ??? - def getOtherBankAccounts(bankId: code.model.BankId,accountID: code.model.AccountId): List[code.model.OtherBankAccount] = ??? - def getPhysicalCards(user: code.model.User): Set[code.model.PhysicalCard] = ??? - def getPhysicalCardsForBank(bankId: code.model.BankId,user: code.model.User): Set[code.model.PhysicalCard] = ??? - protected def getTransactionRequestImpl(transactionRequestId: code.model.TransactionRequestId): net.liftweb.common.Box[code.transactionrequests.TransactionRequests.TransactionRequest] = ??? - protected def getTransactionRequestTypesImpl(fromAccount: code.model.BankAccount): net.liftweb.common.Box[List[code.model.TransactionRequestType]] = ??? - protected def getTransactionRequestsImpl(fromAccount: code.model.BankAccount): net.liftweb.common.Box[List[code.transactionrequests.TransactionRequests.TransactionRequest]] = ??? - protected def makePaymentImpl(fromAccount: code.bankconnectors.KafkaConnector.AccountType,toAccount: code.bankconnectors.KafkaConnector.AccountType,amt: BigDecimal,description: String): net.liftweb.common.Box[code.model.TransactionId] = ??? - def removeAccount(bankId: code.model.BankId,accountId: code.model.AccountId): Boolean = ??? - protected def saveTransactionRequestChallengeImpl(transactionRequestId: code.model.TransactionRequestId,challenge: code.transactionrequests.TransactionRequests.TransactionRequestChallenge): net.liftweb.common.Box[Boolean] = ??? - protected def saveTransactionRequestStatusImpl(transactionRequestId: code.model.TransactionRequestId,status: String): net.liftweb.common.Box[Boolean] = ??? - protected def saveTransactionRequestTransactionImpl(transactionRequestId: code.model.TransactionRequestId,transactionId: code.model.TransactionId): net.liftweb.common.Box[Boolean] = ??? - def setAccountHolder(bankAccountUID: code.model.BankAccountUID,user: code.model.User): Unit = ??? - def setBankAccountLastUpdated(bankNationalIdentifier: String,accountNumber: String,updateDate: java.util.Date): Boolean = ??? - def updateAccountBalance(bankId: code.model.BankId,accountId: code.model.AccountId,newBalance: BigDecimal): Boolean = ??? - def updateAccountLabel(bankId: code.model.BankId,accountId: code.model.AccountId,label: String): Boolean = ??? -} - diff --git a/src/main/scala/code/bankconnectors/KafkaHelper.scala b/src/main/scala/code/bankconnectors/KafkaHelper.scala index 29c9d6983..e8d8910e6 100644 --- a/src/main/scala/code/bankconnectors/KafkaHelper.scala +++ b/src/main/scala/code/bankconnectors/KafkaHelper.scala @@ -25,19 +25,13 @@ Berlin 13359, Germany import java.util.{Properties, UUID} -import scala.concurrent.ops._ -import scala.concurrent.duration._ - -import net.liftweb.util.Props - -import kafka.utils.{ZkUtils, ZKStringSerializer} -import org.I0Itec.zkclient.ZkClient -import kafka.consumer.Consumer -import kafka.consumer._ -import kafka.consumer.KafkaStream +import kafka.consumer.{Consumer, _} import kafka.message._ import kafka.producer.{KeyedMessage, Producer, ProducerConfig} - +import kafka.utils.{Json, ZKStringSerializer, ZkUtils} +import net.liftweb.json.DefaultFormats +import net.liftweb.util.Props +import org.I0Itec.zkclient.ZkClient object ZooKeeperUtils { // gets brokers tracked by zookeeper @@ -89,6 +83,7 @@ class KafkaConsumer(val zookeeper: String = Props.get("kafka.zookeeper_host")ope val config = new ConsumerConfig(props) config } + def getResponse(reqId: String): List[Map[String, String]] = { // create single stream for topic val topicCountMap = Map(topic -> 1) @@ -107,12 +102,14 @@ class KafkaConsumer(val zookeeper: String = Props.get("kafka.zookeeper_host")ope if (key == reqId) { // disconnect from kafka shutdown() - // split result if it contains multiple answers - val msgList = msg.split("\\},\\{") - // match '"":""', with possible space after colon - val p = """"([a-zA-Z0-9_-]*?)":"(.*?)"""".r - val r = (for( m <- msgList) yield (for( p(k, v) <- p.findAllIn(m) ) yield (k -> v)).toMap[String, String]).toList - return r; + // Parse JSON message + val json = Json.parseFull(msg) + val r = json.get match { + case l: List[Map[String,String]] => l.asInstanceOf[List[Map[String, String]]] + case m: Map[String,String] => List(m.asInstanceOf[Map[String, String]]) + case _ => List(Map("error" -> "incorrect JSON format")) + } + return r } } } @@ -129,7 +126,7 @@ class KafkaConsumer(val zookeeper: String = Props.get("kafka.zookeeper_host")ope } } -import ZooKeeperUtils._ +import code.bankconnectors.ZooKeeperUtils._ case class KafkaProducer( topic: String = Props.get("kafka.request_topic").openOrThrowException("no kafka.request_topic set"), @@ -170,11 +167,23 @@ case class KafkaProducer( } } + //case class Argument(name: String, value: String) + + + case class Tweet( + username: String, + tweet: String, + date: String +) + + + implicit val formats = DefaultFormats + def send(key: String, request: String, arguments: Map[String, String], partition: String = null): Unit = { - // create string from named map of arguments - val args = (for ( (k,v) <- arguments ) yield { s""""$k":"$v",""" }).mkString.replaceAll(",$", "") // create message using request and arguments strings - val message = s"$request:{$args}" + val reqArguments = arguments.map { args => Map(args._1 -> args._2) } + val reqCommand = Map(request -> reqArguments) + val message = Json.encode(reqCommand) // translate strings to utf8 before sending to kafka send(key.getBytes("UTF8"), message.getBytes("UTF8"), if (partition == null) null else partition.getBytes("UTF8")) } @@ -189,4 +198,3 @@ case class KafkaProducer( } } } - diff --git a/src/main/scala/code/bankconnectors/LocalMappedConnector.scala b/src/main/scala/code/bankconnectors/LocalMappedConnector.scala index d60b1595c..3520fb5b9 100644 --- a/src/main/scala/code/bankconnectors/LocalMappedConnector.scala +++ b/src/main/scala/code/bankconnectors/LocalMappedConnector.scala @@ -106,6 +106,7 @@ object LocalMappedConnector extends Connector with Loggable { } } + // Question: Why is this called getBankAccountType? Why not getBankAccount? override def getBankAccountType(bankId: BankId, accountId: AccountId): Box[MappedBankAccount] = { MappedBankAccount.find( By(MappedBankAccount.bank, bankId.value), diff --git a/src/main/scala/code/customer/CustomerProvider.scala b/src/main/scala/code/customer/CustomerProvider.scala index 9c722543a..785217192 100644 --- a/src/main/scala/code/customer/CustomerProvider.scala +++ b/src/main/scala/code/customer/CustomerProvider.scala @@ -19,7 +19,15 @@ trait CustomerProvider { def getUser(bankId : BankId, customerId : String) : Box[User] - def addCustomer(bankId : BankId, user : User, number : String, legalName : String, mobileNumber : String, email : String, faceImage: CustomerFaceImage) : Box[Customer] + def addCustomer(bankId: BankId, user: User, number: String, legalName: String, mobileNumber: String, email: String, faceImage: CustomerFaceImage, + dateOfBirth: Date, + relationshipStatus: String, + dependents: Int, + dobOfDependents: List[Date], + highestEducationAttained: String, + employmentStatus: String, + kycStatus: Boolean, + lastOkDate: Date): Box[Customer] } @@ -29,6 +37,14 @@ trait Customer { def mobileNumber : String def email : String def faceImage : CustomerFaceImage + def dateOfBirth: Date + def relationshipStatus: String + def dependents: Int + def dobOfDependents: List[Date] + def highestEducationAttained: String + def employmentStatus: String + def kycStatus: Boolean + def lastOkDate: Date } trait CustomerFaceImage { diff --git a/src/main/scala/code/customer/MappedCustomerProvider.scala b/src/main/scala/code/customer/MappedCustomerProvider.scala index 0134f4b9e..4ee76ebb1 100644 --- a/src/main/scala/code/customer/MappedCustomerProvider.scala +++ b/src/main/scala/code/customer/MappedCustomerProvider.scala @@ -23,12 +23,25 @@ object MappedCustomerProvider extends CustomerProvider { ).flatMap(_.mUser.obj) } - override def addCustomer(bankId: BankId, user : User, number : String, legalName : String, mobileNumber : String, email : String, faceImage: CustomerFaceImage) : Box[Customer] = { + override def addCustomer(bankId: BankId, user : User, number : String, legalName : String, mobileNumber : String, email : String, faceImage: CustomerFaceImage, + dateOfBirth: Date, + relationshipStatus: String, + dependents: Int, + dobOfDependents: List[Date], + highestEducationAttained: String, + employmentStatus: String, + kycStatus: Boolean, + lastOkDate: Date) : Box[Customer] = { val createdCustomer = MappedCustomer.create .mBank(bankId.value).mEmail(email).mFaceImageTime(faceImage.date) .mFaceImageUrl(faceImage.url).mLegalName(legalName) - .mMobileNumber(mobileNumber).mNumber(number).mUser(user.apiId.value).saveMe() + .mMobileNumber(mobileNumber).mNumber(number).mUser(user.apiId.value) + .mDateOfBirth(dateOfBirth).mRelationshipStatus(relationshipStatus) + .mDependents(dependents) + .mHighestEducationAttained(highestEducationAttained) + .mEmploymentStatus(employmentStatus).mKycStatus(kycStatus) + .mLastOkDate(lastOkDate).saveMe() Some(createdCustomer) } @@ -48,6 +61,13 @@ class MappedCustomer extends Customer with LongKeyedMapper[MappedCustomer] with object mEmail extends MappedEmail(this, 200) object mFaceImageUrl extends DefaultStringField(this) object mFaceImageTime extends MappedDateTime(this) + object mDateOfBirth extends MappedDateTime(this) + object mRelationshipStatus extends DefaultStringField(this) + object mDependents extends MappedInt(this) + object mHighestEducationAttained extends DefaultStringField(this) + object mEmploymentStatus extends DefaultStringField(this) + object mKycStatus extends MappedBoolean(this) + object mLastOkDate extends MappedDateTime(this) override def number: String = mNumber.get override def mobileNumber: String = mMobileNumber.get @@ -57,6 +77,14 @@ class MappedCustomer extends Customer with LongKeyedMapper[MappedCustomer] with override def date: Date = mFaceImageTime.get override def url: String = mFaceImageUrl.get } + override def dateOfBirth: Date = mDateOfBirth.get + override def relationshipStatus: String = mRelationshipStatus.get + override def dependents: Int = mDependents + override def dobOfDependents: List[Date] = List(createdAt.get) + override def highestEducationAttained: String = mHighestEducationAttained.get + override def employmentStatus: String = mEmploymentStatus.get + override def kycStatus: Boolean = mKycStatus + override def lastOkDate: Date = mLastOkDate.get } object MappedCustomer extends MappedCustomer with LongKeyedMetaMapper[MappedCustomer] { diff --git a/src/main/scala/code/kyccheck/KycCheck.scala b/src/main/scala/code/kyccheck/KycCheck.scala new file mode 100644 index 000000000..52490c7a9 --- /dev/null +++ b/src/main/scala/code/kyccheck/KycCheck.scala @@ -0,0 +1,32 @@ +package code.kycchecks + +import java.util.Date +import net.liftweb.util.SimpleInjector + + +object KycChecks extends SimpleInjector { + + val kycCheckProvider = new Inject(buildOne _) {} + + def buildOne: KycCheckProvider = MappedKycChecksProvider + +} + +trait KycCheckProvider { + + def getKycChecks(customerNumber: String) : List[KycCheck] + + def addKycChecks(id: String, customerNumber: String, date: Date, how: String, staffUserId: String, mStaffName: String, mSatisfied: Boolean, comments: String) : Boolean + +} + +trait KycCheck { + def idKycCheck : String + def customerNumber : String + def date : Date + def how : String + def staffUserId : String + def staffName : String + def satisfied: Boolean + def comments : String +} \ No newline at end of file diff --git a/src/main/scala/code/kyccheck/MappedKycChecksProvider.scala b/src/main/scala/code/kyccheck/MappedKycChecksProvider.scala new file mode 100644 index 000000000..b4eb8c1a2 --- /dev/null +++ b/src/main/scala/code/kyccheck/MappedKycChecksProvider.scala @@ -0,0 +1,64 @@ +package code.kycchecks + +import java.util.Date + +import code.model.{BankId, User} +import code.model.dataAccess.APIUser +import code.util.{DefaultStringField} +import net.liftweb.mapper._ + +object MappedKycChecksProvider extends KycCheckProvider { + + override def getKycChecks(customerNumber: String): List[MappedKycCheck] = { + MappedKycCheck.findAll( + By(MappedKycCheck.mCustomerNumber, customerNumber), + OrderBy(MappedKycCheck.updatedAt, Descending)) + } + + + override def addKycChecks(id: String, customerNumber: String, date: Date, how: String, staffUserId: String, mStaffName: String, mSatisfied: Boolean, comments: String): Boolean = { + MappedKycCheck.create + .mId(id) + .mCustomerNumber(customerNumber) + .mDate(date) + .mHow(how) + .mStaffUserId(staffUserId) + .mStaffName(mStaffName) + .mSatisfied(mSatisfied) + .mComments(comments) + .save() + } +} + +class MappedKycCheck extends KycCheck +with LongKeyedMapper[MappedKycCheck] with IdPK with CreatedUpdated { + + def getSingleton = MappedKycCheck + + object user extends MappedLongForeignKey(this, APIUser) + object bank extends DefaultStringField(this) + + object mId extends DefaultStringField(this) + object mCustomerNumber extends DefaultStringField(this) + object mDate extends MappedDateTime(this) + object mHow extends DefaultStringField(this) + object mStaffUserId extends DefaultStringField(this) + object mStaffName extends DefaultStringField(this) + object mSatisfied extends MappedBoolean(this) + object mComments extends DefaultStringField(this) + + + + override def idKycCheck: String = mId.get + override def customerNumber: String = mCustomerNumber.get + override def date: Date = mDate.get + override def how: String = mHow.get + override def staffUserId: String = mStaffUserId.get + override def staffName: String = mStaffName.get + override def satisfied: Boolean = mSatisfied.get + override def comments: String = mComments.get +} + +object MappedKycCheck extends MappedKycCheck with LongKeyedMetaMapper[MappedKycCheck] { + override def dbIndexes = UniqueIndex(mId) :: super.dbIndexes +} \ No newline at end of file diff --git a/src/main/scala/code/kycdocuments/KycDocuments.scala b/src/main/scala/code/kycdocuments/KycDocuments.scala new file mode 100644 index 000000000..f61a0ed53 --- /dev/null +++ b/src/main/scala/code/kycdocuments/KycDocuments.scala @@ -0,0 +1,31 @@ +package code.kycdocuments + +import java.util.Date +import net.liftweb.util.SimpleInjector + + +object KycDocuments extends SimpleInjector { + + val kycDocumentProvider = new Inject(buildOne _) {} + + def buildOne: KycDocumentProvider = MappedKycDocumentsProvider + +} + +trait KycDocumentProvider { + + def getKycDocuments(customerNumber: String) : List[KycDocument] + + def addKycDocuments(id: String, customerNumber: String, `type`: String, number: String, issueDate: Date, issuePlace: String, expiryDate: Date) : Boolean + +} + +trait KycDocument { + def idKycDocument : String + def customerNumber : String + def `type` : String + def number : String + def issueDate : Date + def issuePlace : String + def expiryDate : Date +} \ No newline at end of file diff --git a/src/main/scala/code/kycdocuments/MappedKycDocumentsProvider.scala b/src/main/scala/code/kycdocuments/MappedKycDocumentsProvider.scala new file mode 100644 index 000000000..325e58dac --- /dev/null +++ b/src/main/scala/code/kycdocuments/MappedKycDocumentsProvider.scala @@ -0,0 +1,60 @@ +package code.kycdocuments + +import java.util.Date + +import code.model.{BankId, User} +import code.model.dataAccess.APIUser +import code.util.{DefaultStringField} +import net.liftweb.mapper._ + +object MappedKycDocumentsProvider extends KycDocumentProvider { + + override def getKycDocuments(customerNumber: String): List[MappedKycDocument] = { + MappedKycDocument.findAll( + By(MappedKycDocument.mCustomerNumber, customerNumber), + OrderBy(MappedKycDocument.updatedAt, Descending)) + } + + + override def addKycDocuments(id: String, customerNumber: String, `type`: String, number: String, issueDate: Date, issuePlace: String, expiryDate: Date): Boolean = { + MappedKycDocument.create + .mId(id) + .mCustomerNumber(customerNumber) + .mType(`type`) + .mNumber(number) + .mIssueDate(issueDate) + .mIssuePlace(issuePlace) + .mExpiryDate(expiryDate) + .save() + } +} + +class MappedKycDocument extends KycDocument +with LongKeyedMapper[MappedKycDocument] with IdPK with CreatedUpdated { + + def getSingleton = MappedKycDocument + + object user extends MappedLongForeignKey(this, APIUser) + object bank extends DefaultStringField(this) + + object mId extends DefaultStringField(this) + object mCustomerNumber extends DefaultStringField(this) + object mType extends DefaultStringField(this) + object mNumber extends DefaultStringField(this) + object mIssueDate extends MappedDateTime(this) + object mIssuePlace extends DefaultStringField(this) + object mExpiryDate extends MappedDateTime(this) + + + override def idKycDocument: String = mId.get + override def customerNumber: String = mCustomerNumber.get + override def `type`: String = mType.get + override def number: String = mNumber.get + override def issueDate: Date = mIssueDate.get + override def issuePlace: String = mIssuePlace.get + override def expiryDate: Date = mExpiryDate.get +} + +object MappedKycDocument extends MappedKycDocument with LongKeyedMetaMapper[MappedKycDocument] { + override def dbIndexes = UniqueIndex(mId) :: super.dbIndexes +} \ No newline at end of file diff --git a/src/main/scala/code/kycmedia/KycMedia.scala b/src/main/scala/code/kycmedia/KycMedia.scala new file mode 100644 index 000000000..98143adfd --- /dev/null +++ b/src/main/scala/code/kycmedia/KycMedia.scala @@ -0,0 +1,33 @@ +package code.kycmedias + +import java.util.Date + +import code.model.{BankId, User} +import net.liftweb.util.SimpleInjector + + +object KycMedias extends SimpleInjector { + + val kycMediaProvider = new Inject(buildOne _) {} + + def buildOne: KycMediaProvider = MappedKycMediasProvider + +} + +trait KycMediaProvider { + + def getKycMedias(customerNumber: String) : List[KycMedia] + + def addKycMedias(id: String, customerNumber: String, `type`: String, url: String, date: Date, relatesToKycDocumentId: String, relatesToKycCheckId: String) : Boolean + +} + +trait KycMedia { + def idKycMedia : String + def customerNumber : String + def `type` : String + def url : String + def date : Date + def relatesToKycDocumentId : String + def relatesToKycCheckId : String +} \ No newline at end of file diff --git a/src/main/scala/code/kycmedia/MappedKycMediasProvider.scala b/src/main/scala/code/kycmedia/MappedKycMediasProvider.scala new file mode 100644 index 000000000..5e249ca8a --- /dev/null +++ b/src/main/scala/code/kycmedia/MappedKycMediasProvider.scala @@ -0,0 +1,60 @@ +package code.kycmedias + +import java.util.Date + +import code.model.{BankId, User} +import code.model.dataAccess.APIUser +import code.util.{DefaultStringField} +import net.liftweb.mapper._ + +object MappedKycMediasProvider extends KycMediaProvider { + + override def getKycMedias(customerNumber: String): List[MappedKycMedia] = { + MappedKycMedia.findAll( + By(MappedKycMedia.mCustomerNumber, customerNumber), + OrderBy(MappedKycMedia.updatedAt, Descending)) + } + + + override def addKycMedias(id: String, customerNumber: String, `type`: String, url: String, date: Date, relatesToKycDocumentId: String, relatesToKycCheckId: String): Boolean = { + MappedKycMedia.create + .mId(id) + .mCustomerNumber(customerNumber) + .mType(`type`) + .mUrl(url) + .mDate(date) + .mRelatesToKycDocumentId(relatesToKycDocumentId) + .mRelatesToKycCheckId(relatesToKycCheckId) + .save() + } +} + +class MappedKycMedia extends KycMedia +with LongKeyedMapper[MappedKycMedia] with IdPK with CreatedUpdated { + + def getSingleton = MappedKycMedia + + object user extends MappedLongForeignKey(this, APIUser) + object bank extends DefaultStringField(this) + + object mId extends DefaultStringField(this) + object mCustomerNumber extends DefaultStringField(this) + object mType extends DefaultStringField(this) + object mUrl extends DefaultStringField(this) + object mDate extends MappedDateTime(this) + object mRelatesToKycDocumentId extends DefaultStringField(this) + object mRelatesToKycCheckId extends DefaultStringField(this) + + + override def idKycMedia: String = mId.get + override def customerNumber: String = mCustomerNumber.get + override def `type`: String = mType.get + override def url: String = mUrl.get + override def date: Date = mDate.get + override def relatesToKycDocumentId: String = mRelatesToKycDocumentId.get + override def relatesToKycCheckId: String = mRelatesToKycCheckId.get +} + +object MappedKycMedia extends MappedKycMedia with LongKeyedMetaMapper[MappedKycMedia] { + override def dbIndexes = UniqueIndex(mId) :: super.dbIndexes +} \ No newline at end of file diff --git a/src/main/scala/code/kycstatus/KycStatus.scala b/src/main/scala/code/kycstatus/KycStatus.scala new file mode 100644 index 000000000..9c6b1c344 --- /dev/null +++ b/src/main/scala/code/kycstatus/KycStatus.scala @@ -0,0 +1,29 @@ +package code.kycstatuses + +import java.util.Date + +import code.model.{BankId, User} +import net.liftweb.util.SimpleInjector + + +object KycStatuses extends SimpleInjector { + + val kycStatusProvider = new Inject(buildOne _) {} + + def buildOne: KycStatusProvider = MappedKycStatusesProvider + +} + +trait KycStatusProvider { + + def getKycStatuses(customerNumber: String) : List[KycStatus] + + def addKycStatus(customerNumber: String, ok: Boolean, date: Date) : Boolean + +} + +trait KycStatus { + def customerNumber : String + def ok : Boolean + def date : Date +} \ No newline at end of file diff --git a/src/main/scala/code/kycstatus/MappedKycStatusesProvider.scala b/src/main/scala/code/kycstatus/MappedKycStatusesProvider.scala new file mode 100644 index 000000000..90959f8ef --- /dev/null +++ b/src/main/scala/code/kycstatus/MappedKycStatusesProvider.scala @@ -0,0 +1,50 @@ +package code.kycstatuses + +import java.util.Date + +import code.model.{BankId, User} +import code.model.dataAccess.APIUser +import code.util.{DefaultStringField} +import net.liftweb.mapper._ + +object MappedKycStatusesProvider extends KycStatusProvider { + + override def getKycStatuses(customerNumber: String): List[MappedKycStatus] = { + MappedKycStatus.findAll( + By(MappedKycStatus.mCustomerNumber, customerNumber), + OrderBy(MappedKycStatus.updatedAt, Descending)) + } + + + override def addKycStatus(customerNumber: String, ok: Boolean, date: Date): Boolean = { + MappedKycStatus.create + .mCustomerNumber(customerNumber) + .mOk(ok) + .mDate(date) + .save() + } +} + +class MappedKycStatus extends KycStatus +with LongKeyedMapper[MappedKycStatus] with IdPK with CreatedUpdated { + + def getSingleton = MappedKycStatus + + object user extends MappedLongForeignKey(this, APIUser) + object bank extends DefaultStringField(this) + + object mCustomerNumber extends DefaultStringField(this) + object mOk extends MappedBoolean(this) + object mDate extends MappedDateTime(this) + + + + override def customerNumber: String = mCustomerNumber.get + override def ok: Boolean = mOk.get + override def date: Date = mDate.get + +} + +object MappedKycStatus extends MappedKycStatus with LongKeyedMetaMapper[MappedKycStatus] { + override def dbIndexes = UniqueIndex(mCustomerNumber) :: super.dbIndexes +} \ No newline at end of file diff --git a/src/main/scala/code/management/ImporterAPI.scala b/src/main/scala/code/management/ImporterAPI.scala index 7c6489c63..5d6cf90c2 100644 --- a/src/main/scala/code/management/ImporterAPI.scala +++ b/src/main/scala/code/management/ImporterAPI.scala @@ -184,7 +184,7 @@ object ImporterAPI extends RestHelper with Loggable { savetransactions else errorJsonResponse("wrong secret", 401) - case _ => errorJsonResponse("importer_secret not set") + case _ => errorJsonResponse("importer_secret not set on the server.") } } case _ => errorJsonResponse("secret missing") diff --git a/src/main/scala/code/model/BankingData.scala b/src/main/scala/code/model/BankingData.scala index f933a1edb..9f1683993 100644 --- a/src/main/scala/code/model/BankingData.scala +++ b/src/main/scala/code/model/BankingData.scala @@ -104,6 +104,15 @@ object BankId { def unapply(id : String) = Some(BankId(id)) } +// In preparation for use in Context (api links) To replace OtherAccountId +case class CounterpartyId(val value : String) { + override def toString = value +} + +object CounterpartyId { + def unapply(id : String) = Some(CounterpartyId(id)) +} + trait Bank { def bankId: BankId def shortName : String diff --git a/src/main/scala/code/model/OAuth.scala b/src/main/scala/code/model/OAuth.scala index 814b1d5af..51422d2a8 100644 --- a/src/main/scala/code/model/OAuth.scala +++ b/src/main/scala/code/model/OAuth.scala @@ -142,15 +142,10 @@ object Consumer extends Consumer with LongKeyedMetaMapper[Consumer] with CRUDify //overridden to display extra stats above the table override def _showAllTemplate = -
-

- Total of {Consumer.count} applications from {recordsWithUniqueEmails.getOrElse("ERROR")} unique email addresses. - {recordsWithUniqueAppNames.getOrElse("ERROR")} unique app names. -

-
-
-
-
+

+ Total of {Consumer.count} applications from {recordsWithUniqueEmails.getOrElse("ERROR")} unique email addresses.
+ {recordsWithUniqueAppNames.getOrElse("ERROR")} unique app names. +

diff --git a/src/main/scala/code/model/dataAccess/Admin.scala b/src/main/scala/code/model/dataAccess/Admin.scala index 682648294..8ad222ae9 100644 --- a/src/main/scala/code/model/dataAccess/Admin.scala +++ b/src/main/scala/code/model/dataAccess/Admin.scala @@ -59,6 +59,7 @@ object Admin extends Admin with MetaMegaProtoUser[Admin]{ locale, timezone, password) // comment this line out to require email validations + // TODO Get this from Props override def skipEmailValidation = true //Keep track of the referer on login diff --git a/src/main/scala/code/model/dataAccess/OBPUser.scala b/src/main/scala/code/model/dataAccess/OBPUser.scala index 96ffa497e..3724c1a02 100755 --- a/src/main/scala/code/model/dataAccess/OBPUser.scala +++ b/src/main/scala/code/model/dataAccess/OBPUser.scala @@ -292,6 +292,74 @@ import net.liftweb.util.Helpers._ S.error("login", S.?("Invalid Username or Password")) } + def getUserId(username: String, password: String): Long = { + findUserByUserName(username) match { + case Full(user) => { + if (user.validated_? && + user.getProvider() == Props.get("hostname","") && + user.testPassword(Full(password))) + { + user.id.toLong + } + else { + getExternalUser(username, password).get.id.toLong + } + } + case _ => 0 + } + } + + def getExternalUser(username: String, password: String):Box[OBPUser] = { + getUserViaKafka(username, password) match { + case Full(SandboxUserImport(extEmail, extPassword, extDisplayName)) => { + val preLoginState = capturePreLoginState() + info("external user authenticated. login redir: " + loginRedirect.get) + val redir = loginRedirect.get match { + case Full(url) => + loginRedirect(Empty) + url + case _ => + homePage + } + + val dummyPassword = "nothingreallyjustdummypass" + val extProvider = Props.get("connector").openOrThrowException("no connector set") + + val user = findUserByUserName(username) match { + // Check if the external user is already created locally + case Full(user) if user.validated_? && + user.provider == extProvider => { + // Return existing user if found + info("external user already exists locally, using that one") + user + } + + // If not found, create new user + case _ => { + // Create OBPUser using fetched data from Kafka + // assuming that user's email is always validated + info("external user does not exist locally, creating one") + val newUser = OBPUser.create + .firstName(extDisplayName) + .email(extEmail) + // No need to store password, so store dummy string instead + .password(dummyPassword) + .provider(extProvider) + .validated(true) + // Save the user in order to be able to log in + newUser.save() + // Return created user + newUser + } + } + Full(user) + } + case _ => { + Empty + } + } + } + //overridden to allow a redirection if login fails override def login = { if (S.post_?) { @@ -326,67 +394,22 @@ import net.liftweb.util.Helpers._ case _ => { // If not found locally, try to authenticate user via Kafka, if enabled in props if (Props.get("connector").openOrThrowException("no connector set") == "kafka") { - S.param("username"). - flatMap(username => getUserViaKafka(username, S.param("password").openOr(""))) match { - case Full(SandboxUserImport(extEmail, extPassword, extDisplayName)) => { - val preLoginState = capturePreLoginState() - info("external user authenticated. login redir: " + loginRedirect.get) - val redir = loginRedirect.get match { - case Full(url) => - loginRedirect(Empty) - url - case _ => - homePage - } + val preLoginState = capturePreLoginState() + val user = getExternalUser(S.param("username").get, S.param("password").get) - val dummyPassword = "nothingreallyjustdummypass" - val extProvider = Props.get("connector").openOrThrowException("no connector set") + if (!user.isEmpty) { + logUserIn(user.get, () => { + S.notice(S.?("logged.in")) - val user = S.param("username"). - flatMap(username => findUserByUserName(username)) match { + preLoginState() - // Check if the external user is already created locally - case Full(user) if user.validated_? && - user.provider == extProvider => { - // Return existing user if found - info("external user already exists locally, using that one") - user - } - - // If not found, create new user - case _ => { - // Create OBPUser using fetched data from Kafka - // assuming that user's email is always validated - info("external user does not exist locally, creating one") - val newUser = OBPUser.create - .firstName(extDisplayName) - .email(extEmail) - // No need to store password, so store dummy string instead - .password(dummyPassword) - .provider(extProvider) - .validated(true) - // Save the user in order to be able to log in - newUser.save() - // Return created user - newUser - } - } - - logUserIn(user, () => { - S.notice(S.?("logged.in")) - - preLoginState() - - S.redirectTo(homePage) - }) - - } - case _ => { - userLoginFailed - } + S.redirectTo(homePage) + }) + } else { + userLoginFailed } } else { - userLoginFailed + userLoginFailed } } } diff --git a/src/main/scala/code/sandbox/OBPDataImport.scala b/src/main/scala/code/sandbox/OBPDataImport.scala index 5a7240584..03030c65a 100644 --- a/src/main/scala/code/sandbox/OBPDataImport.scala +++ b/src/main/scala/code/sandbox/OBPDataImport.scala @@ -487,7 +487,6 @@ trait OBPDataImport extends Loggable { * @return A full box if the import worked, or else a failure describing what went wrong */ def importData(data: SandboxDataImport) : Box[Unit] = { - logger.info(s"Hello from importData") for { banks <- createBanks(data) diff --git a/src/main/scala/code/snippet/Login.scala b/src/main/scala/code/snippet/Login.scala index b58700eff..b2b30ac27 100644 --- a/src/main/scala/code/snippet/Login.scala +++ b/src/main/scala/code/snippet/Login.scala @@ -33,9 +33,10 @@ Berlin 13359, Germany package code.snippet import code.model.dataAccess.OBPUser +import net.liftweb.common.Loggable import scala.xml.NodeSeq import net.liftweb.util.Helpers._ -import net.liftweb.util.CssSel +import net.liftweb.util.{Props, CssSel} import net.liftweb.http.S import code.model.dataAccess.Admin import net.liftweb.http.SHtml @@ -70,7 +71,7 @@ class Login { } } } - + def adminLogout : CssSel = { if(Admin.loggedIn_?) { val current = Admin.currentUser @@ -84,4 +85,19 @@ class Login { } } + + // Used to display custom message to users when they login. + // For instance we can use it to display example login on a sandbox + def customiseLogin : CssSel = { + val specialLoginInstructions = scala.xml.Unparsed(Props.get("webui_login_page_special_instructions", "")) + // In case we use Extraction.decompose + implicit val formats = net.liftweb.json.DefaultFormats + "#login_special_instructions *" #> specialLoginInstructions + } + + + + + +// End of class } \ No newline at end of file diff --git a/src/main/scala/code/snippet/OAuthAuthorisation.scala b/src/main/scala/code/snippet/OAuthAuthorisation.scala index 53322fe58..3ad1fcbb9 100644 --- a/src/main/scala/code/snippet/OAuthAuthorisation.scala +++ b/src/main/scala/code/snippet/OAuthAuthorisation.scala @@ -1,32 +1,37 @@ /** -Open Bank Project +Open Bank Project - API +Copyright (C) 2011 - 2015, TESOBE Ltd. -Copyright 2011,2012 TESOBE / Music Pictures Ltd. +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. -http://www.apache.org/licenses/LICENSE-2.0 +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. +This product includes software developed at +TESOBE (http://www.tesobe.com/) +by +Simon Redfern : simon AT tesobe DOT com +Stefan Bethge : stefan AT tesobe DOT com +Everett Sochowski : everett AT tesobe DOT com +Ayoub Benali: ayoub AT tesobe DOT com - Open Bank Project (http://www.openbankproject.com) - Copyright 2011,2012 TESOBE / Music Pictures Ltd - This product includes software developed at - TESOBE (http://www.tesobe.com/) +TESOBE Ltd. +Osloer Str. 16/17 +Berlin 13359, Germany +Email: contact@tesobe.com + +*/ - by - Simon Redfern : simon AT tesobe DOT com - Everett Sochowski: everett AT tesobe DOT com - Ayoub Benali : ayoub AT tesobe Dot com - */ package code.snippet import code.util.Helper @@ -168,7 +173,7 @@ object OAuthAuthorisation { } - //looks for expired tokens and nonces and delete them + //looks for expired tokens and nonces and deletes them def dataBaseCleaner: Unit = { import net.liftweb.util.Schedule import net.liftweb.mapper.By_< diff --git a/src/main/scala/code/snippet/WebUI.scala b/src/main/scala/code/snippet/WebUI.scala index c21fec415..cbe39be25 100644 --- a/src/main/scala/code/snippet/WebUI.scala +++ b/src/main/scala/code/snippet/WebUI.scala @@ -63,10 +63,27 @@ class WebUI extends Loggable{ ".api-explorer-link a [href]" #> scala.xml.Unparsed(Props.get("webui_api_explorer_url", "")) } + // Social Finance (Sofi) + def sofiLink: CssSel = { + ".sofi-link a [href]" #> scala.xml.Unparsed(Props.get("webui_sofi_url", "")) + } + + + // Points to the documentation. Probably a sandbox specific link is good. def apiDocumentationLink: CssSel = { ".api-documentation-link a [href]" #> scala.xml.Unparsed(Props.get("webui_api_documentation_url", "https://github.com/OpenBankProject/OBP-API/wiki")) } + // For example customers and credentials + // This relies on the page for sandbox documentation having an anchor called example-customer-logins + def exampleSandboxCredentialsLink: CssSel = { + ".example_sandbox_credentials_link a [href]" #> scala.xml.Unparsed(Props.get("webui_api_documentation_url", "") + "#example-customer-logins") + } + + // For link to OAuth Client SDKs + def sdksLink: CssSel = { + ".sdks_link a [href]" #> scala.xml.Unparsed(Props.get("webui_sdks_url", "https://github.com/OpenBankProject/OBP-API/wiki/OAuth-Client-SDKS")) + } def mainStyleSheet: CssSel = { "#main_style_sheet [href]" #> scala.xml.Unparsed(Props.get("webui_main_style_sheet", "/media/css/website.css")) diff --git a/src/main/scala/code/socialmedia/MappedSocialMediasProvider.scala b/src/main/scala/code/socialmedia/MappedSocialMediasProvider.scala new file mode 100644 index 000000000..c8f3a84dd --- /dev/null +++ b/src/main/scala/code/socialmedia/MappedSocialMediasProvider.scala @@ -0,0 +1,52 @@ +package code.socialmedia + +import java.util.Date +import code.model.dataAccess.APIUser +import code.util.{DefaultStringField} +import net.liftweb.mapper._ + +object MappedSocialMediasProvider extends SocialMediaHandleProvider { + + override def getSocialMedias(customerNumber: String): List[MappedSocialMedia] = { + MappedSocialMedia.findAll( + By(MappedSocialMedia.mCustomerNumber, customerNumber), + OrderBy(MappedSocialMedia.updatedAt, Descending)) + } + + + override def addSocialMedias(customerNumber: String, `type`: String, handle: String, dateAdded: Date, dateActivated: Date): Boolean = { + MappedSocialMedia.create + .mCustomerNumber(customerNumber) + .mType(`type`) + .mHandle(handle) + .mDateAdded(dateAdded) + .mDateActivated(dateActivated) + .save() + } +} + +class MappedSocialMedia extends SocialMedia +with LongKeyedMapper[MappedSocialMedia] with IdPK with CreatedUpdated { + + def getSingleton = MappedSocialMedia + + object user extends MappedLongForeignKey(this, APIUser) + object bank extends DefaultStringField(this) + + object mCustomerNumber extends DefaultStringField(this) + object mType extends DefaultStringField(this) + object mHandle extends DefaultStringField(this) + object mDateAdded extends MappedDateTime(this) + object mDateActivated extends MappedDateTime(this) + + + override def customerNumber: String = mCustomerNumber.get + override def `type`: String = mType.get + override def handle: String = mHandle.get + override def dateAdded: Date = mDateAdded.get + override def dateActivated: Date = mDateActivated.get +} + +object MappedSocialMedia extends MappedSocialMedia with LongKeyedMetaMapper[MappedSocialMedia] { + override def dbIndexes = UniqueIndex(mCustomerNumber) :: super.dbIndexes +} \ No newline at end of file diff --git a/src/main/scala/code/socialmedia/SocialMedia.scala b/src/main/scala/code/socialmedia/SocialMedia.scala new file mode 100644 index 000000000..290dc699d --- /dev/null +++ b/src/main/scala/code/socialmedia/SocialMedia.scala @@ -0,0 +1,34 @@ +package code.socialmedia + +import java.util.Date +import net.liftweb.util.SimpleInjector + + +// TODO Rename to SocialMediaHandle +object SocialMediaHandle extends SimpleInjector { + + val socialMediaHandleProvider = new Inject(buildOne _) {} + + def buildOne: SocialMediaHandleProvider = MappedSocialMediasProvider + +} + + +// TODO Rename to SocialMediaHandlesProvider etc. +trait SocialMediaHandleProvider { + + def getSocialMedias(customerNumber: String) : List[SocialMedia] + + def addSocialMedias(customerNumber: String, `type`: String, handle: String, dateAdded: Date, dateActivated: Date) : Boolean + +} + + +// TODO Rename to SocialMediaHandle +trait SocialMedia { + def customerNumber : String + def `type` : String + def handle : String + def dateAdded : Date + def dateActivated : Date +} \ No newline at end of file diff --git a/src/main/scala/code/views/MapperViews.scala b/src/main/scala/code/views/MapperViews.scala index 73f94fffa..f87d14ed7 100644 --- a/src/main/scala/code/views/MapperViews.scala +++ b/src/main/scala/code/views/MapperViews.scala @@ -243,6 +243,8 @@ private object MapperViews extends Views with Loggable { def getAllPublicAccounts() : List[BankAccount] = { //TODO: do this more efficiently + // An account is considered public if it contains a public view + val bankAndAccountIds : List[(BankId, AccountId)] = ViewImpl.findAll(By(ViewImpl.isPublic_, true)).map(v => (v.bankId, v.accountId) diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html index a94a51264..896af8de5 100755 --- a/src/main/webapp/index.html +++ b/src/main/webapp/index.html @@ -37,18 +37,17 @@ Berlin 13359, Germany

Get Started

    -
  1. Register / Login as a developer here
  2. -
  3. Get one or more developer keys here
  4. -
  5. Read the documentation here
  6. -
  7. Explore the API here
  8. +
  9. Explore the API here using the example customer logins here.
  10. +
  11. Get API keys and SDKs.

diff --git a/src/main/webapp/media/css/overrides/enbdg.css b/src/main/webapp/media/css/overrides/enbdg.css new file mode 100644 index 000000000..0bf7ad703 --- /dev/null +++ b/src/main/webapp/media/css/overrides/enbdg.css @@ -0,0 +1,3 @@ +#header-decoration { + background-color: #646ca1; +} diff --git a/src/main/webapp/media/css/overrides/uk.css b/src/main/webapp/media/css/overrides/uk.css new file mode 100644 index 000000000..eec137714 --- /dev/null +++ b/src/main/webapp/media/css/overrides/uk.css @@ -0,0 +1,84 @@ +h1 { + color: white; + font-family: "Nunito", "sans-serif"; + font-size: 36px; + margin-bottom: 10px; + text-align: center; + text-transform: uppercase; +} + +#main #main-about { + padding-top: 0 !important; +} + +.top { + background-color: #53C4EF; + font-family: "Nunito", "sans-serif"; + padding: 40px; +} + +.intro { + font-family: "Nunito", "sans-serif"; + margin: 0 20px; + text-align: center; +} + +.content-big { + font-family: "Nunito", "sans-serif"; + margin: 30px 50px; + text-align: center; +} + +.buttons { + font-family: "Nunito", "sans-serif"; + margin: 40px 0; + text-align: center; +} +.buttons a { + border: 2px solid white; + border-radius: 24px; + color: white; + margin-bottom: 10px; + font-family: "Nunito", "sans-serif"; + font-size: x-large; + font-weight: bold; + padding: 10px 50px; + text-decoration: none; + text-transform: uppercase; +} + +.content-small { + background-color: white; + font-family: "Nunito", "sans-serif"; + margin: 20px 20px 0 20px; + padding: 30px; + text-align: center; +} + +.newsletter { + background-color: black; + font-family: "Nunito", "sans-serif"; + margin: 0 20px; + padding: 30px; + text-align: center; +} +.newsletter input { + border: 1px solid black; + border-top-left-radius: 24px; + border-bottom-left-radius: 24px; + font-family: "Nunito", "sans-serif"; + height: auto; + padding: 10px 20px; +} +.newsletter button { + border: 1px solid black; + border-left: 0px; + border-top-right-radius: 24px; + border-bottom-right-radius: 24px; + background-color: #50b165; + color: white; + font-family: "Nunito", "sans-serif"; + font-size: 17px; + margin-left: -6px; + padding: 10px 30px 10px 10px; +} diff --git a/src/main/webapp/media/css/overrides/ulster.css b/src/main/webapp/media/css/overrides/ulster.css index e69de29bb..56f0a96f5 100644 --- a/src/main/webapp/media/css/overrides/ulster.css +++ b/src/main/webapp/media/css/overrides/ulster.css @@ -0,0 +1,29 @@ +#main-about h1 { + color: white; + font-size: 35px; + margin-bottom: 15px; + margin-left: -5px; +} + +#main-about p { + color: white; + font-weight: bold; + margin-top: 10px; + margin-bottom: 10px; +} + +#main-about ul +{ + list-style-type: initial; +} + +#main-about li +{ + color: white; + text-align: left; + margin-left: 100px; +} + +#main-about img { + margin-top: 30px; +} diff --git a/src/main/webapp/media/css/website.css b/src/main/webapp/media/css/website.css index f19bfa648..955a422f7 100644 --- a/src/main/webapp/media/css/website.css +++ b/src/main/webapp/media/css/website.css @@ -894,8 +894,21 @@ span#accountsMsg { } #admin-logout { - margin: 20px; - padding: 10px; + padding-left: 10px; border: 2px solid; border-color: #FF0000; + margin-top:-6px; + padding: 4px 0 3px 10px; } + +#admin-consumer-summary { + color: black; + margin: 20px 0; +} + +#login_special_instructions { + text-align: left; +} + + + diff --git a/src/main/webapp/templates-hidden/_login_form.html b/src/main/webapp/templates-hidden/_login_form.html index 60b9c1873..59b8c1fe6 100644 --- a/src/main/webapp/templates-hidden/_login_form.html +++ b/src/main/webapp/templates-hidden/_login_form.html @@ -1,4 +1,10 @@ +
+ + + + +
@@ -16,4 +22,6 @@
- \ No newline at end of file + + + \ No newline at end of file diff --git a/src/main/webapp/templates-hidden/default.html b/src/main/webapp/templates-hidden/default.html index d6492611f..a80c52ff3 100755 --- a/src/main/webapp/templates-hidden/default.html +++ b/src/main/webapp/templates-hidden/default.html @@ -1,6 +1,6 @@