diff --git a/pom.xml b/pom.xml
index 758b7c56b..dd57c046f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -89,7 +89,7 @@
org.postgresql
postgresql
- 9.4-1206-jdbc4
+ 9.4.1211
com.h2database
diff --git a/src/main/scala/code/api/util/APIUtil.scala b/src/main/scala/code/api/util/APIUtil.scala
index e30653427..fa729f8d9 100644
--- a/src/main/scala/code/api/util/APIUtil.scala
+++ b/src/main/scala/code/api/util/APIUtil.scala
@@ -93,6 +93,7 @@ object ErrorMessages {
val CustomerNumberAlreadyExists = "OBP-30006: Customer Number already exists. Please specify a different value for BANK_ID or CUSTOMER_NUMBER."
val CustomerAlreadyExistsForUser = "OBP-30007: The User is already linked to a Customer at the bank specified by BANK_ID"
val CustomerDoNotExistsForUser = "OBP-30008: User is not linked to a Customer at the bank specified by BANK_ID"
+ val CounterpartyNotFoundByIban = "OBP-30009: Counterparty not found. The IBan specified does not exist on this server."
val MeetingsNotSupported = "OBP-30101: Meetings are not supported on this server."
val MeetingApiKeyNotConfigured = "OBP-30102: Meeting provider API Key is not configured."
diff --git a/src/main/scala/code/api/v2_1_0/APIMethods210.scala b/src/main/scala/code/api/v2_1_0/APIMethods210.scala
index 3d85908ed..831d5d84a 100644
--- a/src/main/scala/code/api/v2_1_0/APIMethods210.scala
+++ b/src/main/scala/code/api/v2_1_0/APIMethods210.scala
@@ -14,16 +14,19 @@ import code.api.v2_1_0.JSONFactory210._
import code.bankconnectors.Connector
import code.entitlement.Entitlement
import code.fx.fx
+import code.metadata.counterparties.MappedCounterpartyMetadata
import code.model.dataAccess.OBPUser
import code.model.{BankId, _}
import net.liftweb.http.{CurrentReq, Req}
import net.liftweb.json.Extraction
import net.liftweb.json.JsonAST.JValue
+import net.liftweb.json.Serialization._
import net.liftweb.mapper.By
import net.liftweb.util.Props
import scala.collection.immutable.Nil
import scala.collection.mutable.ArrayBuffer
+
// Makes JValue assignment to Nil work
import code.util.Helper._
import net.liftweb.json.JsonDSL._
@@ -44,7 +47,6 @@ import net.liftweb.json.Serialization.{read, write}
trait APIMethods210 {
//needs to be a RestHelper to get access to JsonGet, JsonPost, etc.
self: RestHelper =>
-
// helper methods begin here
// helper methods end here
@@ -214,8 +216,8 @@ trait APIMethods210 {
|
|""",
Extraction.decompose(TransactionRequestBodyJSON (
- TransactionRequestAccountJSON("BANK_ID", "ACCOUNT_ID"),
- AmountOfMoneyJSON("EUR", "100.53"),
+ TransactionRequestAccountJSON("bank_id", "account_id"),
+ AmountOfMoneyJSON("eur", "100.53"),
"A description for the transaction to be created"
)
),
@@ -277,16 +279,7 @@ trait APIMethods210 {
// Prevent default value for transaction request type (at least).
transferCurrencyEqual <- tryo(assert(transDetailsJson.value.currency == fromAccount.currency)) ?~! {s"${ErrorMessages.InvalidTransactionRequestCurrency} From Account Currency is ${fromAccount.currency} Requested Transaction Currency is: ${transDetailsJson.value.currency}"}
- transDetailsSerialized <- transactionRequestType.value match {
- case "FREE_FORM" => tryo{
- implicit val formats = Serialization.formats(NoTypeHints)
- write(json)
- }
- case _ => tryo{
- implicit val formats = Serialization.formats(NoTypeHints)
- write(transDetailsJson)
- }
- }
+ amountOfMoneyJSON <- Full(AmountOfMoneyJSON(transDetails.value.currency, transDetails.value.amount))
// Note: These store in the table TransactionRequestv210
createdTransactionRequest <- transactionRequestType.value match {
@@ -295,16 +288,52 @@ trait APIMethods210 {
toBankId <- Full(BankId(transDetailsJson.asInstanceOf[TransactionRequestDetailsSandBoxTanJSON].to.bank_id))
toAccountId <- Full(AccountId(transDetailsJson.asInstanceOf[TransactionRequestDetailsSandBoxTanJSON].to.account_id))
toAccount <- BankAccount(toBankId, toAccountId) ?~! {ErrorMessages.CounterpartyNotFound}
-
+ transDetailsSerialized <- tryo {
+ implicit val formats = Serialization.formats(NoTypeHints)
+ write(json)
+ }
createdTransactionRequest <- Connector.connector.vend.createTransactionRequestv210(u, fromAccount, Full(toAccount), transactionRequestType, transDetails, transDetailsSerialized)
} yield createdTransactionRequest
}
case "SEPA" => {
- Connector.connector.vend.createTransactionRequestv210(u, fromAccount, Empty, transactionRequestType, transDetails, transDetailsSerialized)
+ for {
+ //for SEPA, the user do not send the Bank_ID and Acound_ID,so this will search for the bank firstly.
+ toIban<- Full(transDetailsJson.asInstanceOf[TransactionRequestDetailsSEPAJSON].iban)
+ mappedCounterpartyMetadata <- MappedCounterpartyMetadata.find(By(MappedCounterpartyMetadata.accountNumber, toIban)) ?~! {ErrorMessages.CounterpartyNotFoundByIban}
+ toBankId <- Full(BankId(mappedCounterpartyMetadata.thisAccountBankId))
+ toAccountId <- Full(AccountId(mappedCounterpartyMetadata.thisAccountId))
+ toAccount <- BankAccount(toBankId, toAccountId) ?~! {ErrorMessages.CounterpartyNotFound}
+
+ // Following four lines: just transfer the details body ,add Bank_Id and Account_Id in the Detail part.
+ transactionRequestAccountJSON = TransactionRequestAccountJSON(toBankId.value, toAccountId.value)
+ detailDescription = transDetailsJson.asInstanceOf[TransactionRequestDetailsSEPAJSON].description
+ transactionRequestDetailsAddedTobankSEPAJSON = TransactionRequestDetailsSEPAResponseJSON(toIban.toString,transactionRequestAccountJSON, amountOfMoneyJSON, detailDescription.toString)
+ transResponseDetails = getTransactionRequestDetailsAddedTobankSEPAJSONFromJson(transactionRequestDetailsAddedTobankSEPAJSON)
+
+ //Serialize the new format SEPA data.
+ transDetailsResponseSerialized <-tryo{
+ implicit val formats = Serialization.formats(NoTypeHints)
+ write(transResponseDetails)
+ }
+ createdTransactionRequest <- Connector.connector.vend.createTransactionRequestv210(u, fromAccount, Full(toAccount), transactionRequestType, transResponseDetails, transDetailsResponseSerialized)
+ } yield createdTransactionRequest
}
case "FREE_FORM" => {
- Connector.connector.vend.createTransactionRequestv210(u, fromAccount, Empty, transactionRequestType, transDetails, transDetailsSerialized)
+ for {
+ // Following three lines: just transfer the details body ,add Bank_Id and Account_Id in the Detail part.
+ transactionRequestAccountJSON <- Full(TransactionRequestAccountJSON(fromAccount.bankId.value, fromAccount.accountId.value))
+ // the Free form the discription is empty, so make it "" in the following code
+ transactionRequestDetailsFreeFormResponseJSON = TransactionRequestDetailsFreeFormResponseJSON(transactionRequestAccountJSON,amountOfMoneyJSON,"")
+ transResponseDetails <- Full(getTransactionRequestDetailsFreeFormAddedTobankJson(transactionRequestDetailsFreeFormResponseJSON))
+
+ transDetailsResponseSerialized<-tryo{
+ implicit val formats = Serialization.formats(NoTypeHints)
+ write(transResponseDetails)
+ }
+ createdTransactionRequest <- Connector.connector.vend.createTransactionRequestv210(u, fromAccount, Full(fromAccount), transactionRequestType, transResponseDetails, transDetailsResponseSerialized)
+ } yield
+ createdTransactionRequest
}
}
} yield {
@@ -609,7 +638,7 @@ trait APIMethods210 {
u <- user ?~! ErrorMessages.UserNotLoggedIn
putData <- tryo{json.extract[PutEnabledJSON]} ?~! ErrorMessages.InvalidJsonFormat
hasEntitlement <- putData.enabled match {
- case true => booleanToBox(hasEntitlement("", u.userId, ApiRole.CanEnableConsumers), s"$CanEnableConsumers entitlement required")
+ case true => booleanToBox(hasEntitlement("", u.userId, ApiRole.CanEnableConsumers), s"$CanEnableConsumers entitlement required")
case false => booleanToBox(hasEntitlement("", u.userId, ApiRole.CanDisableConsumers), s"$CanDisableConsumers entitlement required")
}
consumer <- Consumer.find(By(Consumer.id, consumerId.toLong))
@@ -624,7 +653,6 @@ trait APIMethods210 {
}
-
resourceDocs += ResourceDoc(
addCardsForBank,
apiVersion,
@@ -758,7 +786,7 @@ trait APIMethods210 {
| * charge : The charge to the customer for each one of these
|
|${authenticationRequiredMessage(getTransactionTypesIsPublic)}""",
- Extraction.decompose(TransactionTypeJSON(TransactionTypeId("wuwjfuha234678"), "1", "2", "3", "4", AmountOfMoneyJSON("EUR", "123"))),
+ Extraction.decompose(TransactionTypeJSON(TransactionTypeId("wuwjfuha234678"), "1", "2", "3", "4", AmountOfMoneyJSON("eur", "123"))),
emptyObjectJson,
emptyObjectJson :: Nil,
false,
@@ -768,7 +796,6 @@ trait APIMethods210 {
)
-
lazy val createTransactionType: PartialFunction[Req, Box[User] => Box[JsonResponse]] = {
case "banks" :: BankId(bankId) :: "transaction-types" :: Nil JsonPut json -> _ => {
user => {
diff --git a/src/main/scala/code/api/v2_1_0/JSONFactory2.1.0.scala b/src/main/scala/code/api/v2_1_0/JSONFactory2.1.0.scala
index d1fae985a..8b82e4f5e 100644
--- a/src/main/scala/code/api/v2_1_0/JSONFactory2.1.0.scala
+++ b/src/main/scala/code/api/v2_1_0/JSONFactory2.1.0.scala
@@ -37,7 +37,7 @@ import code.api.util.ApiRole
import code.api.v1_2_1.AmountOfMoneyJSON
import code.api.v1_4_0.JSONFactory1_4_0.{ChallengeJSON, TransactionRequestAccountJSON}
import code.api.v2_0_0.TransactionRequestChargeJSON
-import code.model.{AmountOfMoney, Consumer}
+import code.model.{AmountOfMoney, Consumer, CounterpartyMetadataIban, Iban}
import code.transactionrequests.TransactionRequests._
import net.liftweb.json.JValue
@@ -58,15 +58,30 @@ case class TransactionRequestDetailsSandBoxTanJSON(
) extends TransactionRequestDetailsJSON
case class TransactionRequestDetailsSEPAJSON(
- value : AmountOfMoneyJSON,
- IBAN: String,
- description : String
- ) extends TransactionRequestDetailsJSON
+ value: AmountOfMoneyJSON,
+ iban: String,
+ description: String
+ ) extends TransactionRequestDetailsJSON
+
+case class TransactionRequestDetailsSEPAResponseJSON(
+ iban: String,
+ to: TransactionRequestAccountJSON,
+ value: AmountOfMoneyJSON,
+ description: String
+ ) extends TransactionRequestDetailsJSON
case class TransactionRequestDetailsFreeFormJSON(
value : AmountOfMoneyJSON
) extends TransactionRequestDetailsJSON
+case class TransactionRequestDetailsFreeFormResponseJSON(
+ to: TransactionRequestAccountJSON,
+ value: AmountOfMoneyJSON,
+ description: String
+ ) extends TransactionRequestDetailsJSON
+
+
+
case class TransactionRequestWithChargeJSON210(
id: String,
`type`: String,
@@ -135,12 +150,35 @@ object JSONFactory210{
}
def getTransactionRequestDetailsSEPAFromJson(details: TransactionRequestDetailsSEPAJSON) : TransactionRequestDetailsSEPA = {
+ val toAccIban = Iban (
+ iban = details.iban
+ )
val amount = AmountOfMoney (
currency = details.value.currency,
amount = details.value.amount
)
-
TransactionRequestDetailsSEPA (
+ iban = details.iban,
+ value = amount,
+ description = details.description
+ )
+ }
+
+ def getTransactionRequestDetailsAddedTobankSEPAJSONFromJson(details: TransactionRequestDetailsSEPAResponseJSON) : TransactionRequestDetailsSEPAResponse = {
+ val toAcc = TransactionRequestAccount (
+ bank_id = details.to.bank_id,
+ account_id = details.to.account_id
+ )
+ val toAccIban = Iban (
+ iban = details.iban
+ )
+ val amount = AmountOfMoney (
+ currency = details.value.currency,
+ amount = details.value.amount
+ )
+ TransactionRequestDetailsSEPAResponse (
+ iban = details.iban,
+ to=toAcc,
value = amount,
description = details.description
)
@@ -157,6 +195,23 @@ object JSONFactory210{
)
}
+ def getTransactionRequestDetailsFreeFormAddedTobankJson(details: TransactionRequestDetailsFreeFormResponseJSON) : TransactionRequestDetailsFreeFormResponse = {
+ val toAcc = TransactionRequestAccount (
+ bank_id = details.to.bank_id,
+ account_id = details.to.account_id
+ )
+ val amount = AmountOfMoney (
+ currency = details.value.currency,
+ amount = details.value.amount
+ )
+ TransactionRequestDetailsFreeFormResponse (
+ to=toAcc,
+ value = amount,
+ description = details.description
+ )
+ }
+
+
/** Creates v2.1.0 representation of a TransactionType
*
* @param tr An internal TransactionRequest instance
diff --git a/src/main/scala/code/bankconnectors/Connector.scala b/src/main/scala/code/bankconnectors/Connector.scala
index 0f8b40acd..b29b82144 100644
--- a/src/main/scala/code/bankconnectors/Connector.scala
+++ b/src/main/scala/code/bankconnectors/Connector.scala
@@ -5,7 +5,7 @@ import java.util.Date
import code.api.util.APIUtil._
import code.api.util.ApiRole._
import code.api.util.ErrorMessages
-import code.api.v2_1_0.{TransactionRequestDetailsFreeFormJSON, TransactionRequestDetailsSEPAJSON, TransactionRequestDetailsSandBoxTanJSON}
+import code.api.v2_1_0.{TransactionRequestDetailsFreeFormJSON, TransactionRequestDetailsSEPAResponseJSON, TransactionRequestDetailsSandBoxTanJSON}
import code.fx.fx
import code.management.ImporterAPI.ImporterTransaction
import code.model.{Transaction, User, _}
@@ -266,7 +266,6 @@ trait Connector {
//set challenge to null
result = Full(result.get.copy(challenge = null))
-
//save transaction_id if we have one
createdTransactionId match {
case Full(ti) => {
@@ -395,7 +394,9 @@ trait Connector {
case "SANDBOX_TAN" => Connector.connector.vend.makePaymentv200(initiator, BankAccountUID(fromAccount.bankId, fromAccount.accountId),
BankAccountUID(toAccount.get.bankId, toAccount.get.accountId), BigDecimal(details.value.amount), details.asInstanceOf[TransactionRequestDetailsSandBoxTan].description)
case "SEPA" => Connector.connector.vend.makePaymentv200(initiator, BankAccountUID(fromAccount.bankId, fromAccount.accountId),
- BankAccountUID(toAccount.get.bankId, toAccount.get.accountId), BigDecimal(details.value.amount), details.asInstanceOf[TransactionRequestDetailsSandBoxTan].description)
+ BankAccountUID(toAccount.get.bankId, toAccount.get.accountId), BigDecimal(details.value.amount), details.asInstanceOf[TransactionRequestDetailsSEPAResponse].description)
+ case "FREE_FORM" => Connector.connector.vend.makePaymentv200(initiator, BankAccountUID(fromAccount.bankId, fromAccount.accountId),
+ BankAccountUID(toAccount.get.bankId, toAccount.get.accountId), BigDecimal(details.value.amount), "")
}
//set challenge to null
diff --git a/src/main/scala/code/model/BankingData.scala b/src/main/scala/code/model/BankingData.scala
index 85de07a13..72e48a0fa 100644
--- a/src/main/scala/code/model/BankingData.scala
+++ b/src/main/scala/code/model/BankingData.scala
@@ -112,6 +112,12 @@ object BankId {
def unapply(id : String) = Some(BankId(id))
}
+case class CounterpartyMetadataIban(val value : String) {
+override def toString = value
+}
+object CounterpartyMetadataIban {
+ def unapply(id : String) = Some(CounterpartyMetadataIban(id))
+}
case class CustomerId(val value : String) {
override def toString = value
@@ -658,4 +664,8 @@ class Transaction(
case class AmountOfMoney (
val currency: String,
val amount: String
+)
+
+case class Iban(
+ val iban: String
)
\ No newline at end of file
diff --git a/src/main/scala/code/transactionrequests/TransactionRequests.scala b/src/main/scala/code/transactionrequests/TransactionRequests.scala
index 0ebf3b573..5edc5726b 100644
--- a/src/main/scala/code/transactionrequests/TransactionRequests.scala
+++ b/src/main/scala/code/transactionrequests/TransactionRequests.scala
@@ -67,14 +67,28 @@ object TransactionRequests extends SimpleInjector {
val description : String
) extends TransactionRequestDetails
- case class TransactionRequestDetailsSEPA (
- val value : AmountOfMoney,
- val description : String
- ) extends TransactionRequestDetails
+ case class TransactionRequestDetailsSEPA(
+ val iban: String,
+ val value: AmountOfMoney,
+ val description: String
+ ) extends TransactionRequestDetails
+ case class TransactionRequestDetailsSEPAResponse(
+ val iban: String,
+ val to: TransactionRequestAccount,
+ val value: AmountOfMoney,
+ val description: String
+ ) extends TransactionRequestDetails
+
+ case class TransactionRequestDetailsFreeForm(
+ val value: AmountOfMoney
+ ) extends TransactionRequestDetails
+
+ case class TransactionRequestDetailsFreeFormResponse(
+ val to: TransactionRequestAccount,
+ val value: AmountOfMoney,
+ val description: String
+ ) extends TransactionRequestDetails
- case class TransactionRequestDetailsFreeForm (
- val value : AmountOfMoney
- ) extends TransactionRequestDetails
val transactionRequestProvider = new Inject(buildOne _) {}
diff --git a/src/test/scala/code/api/LocalMappedConnectorTestSetup.scala b/src/test/scala/code/api/LocalMappedConnectorTestSetup.scala
index ac4a3ef5b..9424edfa7 100644
--- a/src/test/scala/code/api/LocalMappedConnectorTestSetup.scala
+++ b/src/test/scala/code/api/LocalMappedConnectorTestSetup.scala
@@ -9,6 +9,7 @@ import net.liftweb.common.Box
import net.liftweb.mapper.MetaMapper
import net.liftweb.util.Helpers._
import code.entitlement.{Entitlement, MappedEntitlement}
+import code.metadata.counterparties.MappedCounterpartyMetadata
import code.transaction.MappedTransaction
import scala.util.Random
@@ -26,6 +27,14 @@ trait LocalMappedConnectorTestSetup extends TestConnectorSetupWithStandardPermis
.national_identifier(randomString(5)).saveMe
}
+ override protected def createCounterpartyMetadata(BankId:String,AccountId:String,iBan:String):CounterpartyMetadata = {
+ MappedCounterpartyMetadata.create.
+ thisAccountBankId(BankId).
+ thisAccountId(AccountId).
+ accountNumber(iBan).
+ saveMe
+ }
+
// TODO: Should return an option or box so can test if the insert succeeded
// or if it failed due to unique exception etc. However, we'll need to modify / lift callers so they can handle an Option
// override protected def createBank(id : String) : Option[Bank] = {
diff --git a/src/test/scala/code/api/TestConnectorSetup.scala b/src/test/scala/code/api/TestConnectorSetup.scala
index 9dca04e43..8bed1cde8 100644
--- a/src/test/scala/code/api/TestConnectorSetup.scala
+++ b/src/test/scala/code/api/TestConnectorSetup.scala
@@ -2,7 +2,8 @@ package code.api
import java.util.{Calendar, Date}
-import code.bankconnectors.{OBPLimit, OBPOffset, Connector}
+import code.bankconnectors.{Connector, OBPLimit, OBPOffset}
+import code.metadata.counterparties.MappedCounterpartyMetadata
import code.model._
import net.liftweb.util.Helpers._
@@ -13,6 +14,8 @@ trait TestConnectorSetup {
protected def createAccount(bankId: BankId, accountId : AccountId, currency : String) : BankAccount
protected def createTransaction(account : BankAccount, startDate : Date, finishDate : Date)
+ //TODO: Here I use the createCounterpartyMetadata.accountNumber to replace the Iban. I will fix it when new version is comming.
+ protected def createCounterpartyMetadata(BankId:String,AccountId:String,iBan:String):CounterpartyMetadata
final protected def createAccountAndOwnerView(accountOwner: Option[User], bankId: BankId, accountId : AccountId, currency : String) : BankAccount = {
val account = createAccount(bankId, accountId, currency)
diff --git a/src/test/scala/code/api/v2_1_0/TransactionRequestsFreeformTest.scala b/src/test/scala/code/api/v2_1_0/TransactionRequestsFreeformTest.scala
new file mode 100644
index 000000000..f8a6e2f59
--- /dev/null
+++ b/src/test/scala/code/api/v2_1_0/TransactionRequestsFreeformTest.scala
@@ -0,0 +1,1284 @@
+package code.api.v2_1_0
+
+import code.api.util.APIUtil.OAuth._
+import code.api.util.ApiRole._
+import code.api.util.ErrorMessages
+import code.api.v1_2_1.AmountOfMoneyJSON
+import code.api.v1_4_0.JSONFactory1_4_0.{ChallengeAnswerJSON, TransactionRequestAccountJSON}
+import code.api.v2_0_0.TransactionRequestBodyJSON
+import code.api.{DefaultUsers, ServerSetupWithTestData}
+import code.bankconnectors.Connector
+import code.fx.fx
+import code.model.{AccountId, BankAccount, TransactionRequestId}
+import net.liftweb.json.JsonAST.JString
+import net.liftweb.json.Serialization.write
+import net.liftweb.util.Props
+import org.scalatest.Tag
+
+class TransactionRequestsFreeformTest extends ServerSetupWithTestData with DefaultUsers with V210ServerSetup {
+
+ object TransactionRequest extends Tag("transactionRequests")
+
+ val transactionRequestType: String = "FREE_FORM"
+
+ feature("we can make transaction requests") {
+ val view = "owner"
+
+ def transactionCount(accounts: BankAccount*): Int = {
+ accounts.foldLeft(0)((accumulator, account) => {
+ //TODO: might be nice to avoid direct use of the connector, but if we use an api call we need to do
+ //it with the correct account owners, and be sure that we don't even run into pagination problems
+ accumulator + Connector.connector.vend.getTransactions(account.bankId, account.accountId).get.size
+ })
+ }
+
+ // No challenge, No FX (same currencies)
+ if ( Props.getBool("transactionRequests_enabled", false) == false) {
+ ignore("we create a transaction request with a user who doesn't have access to owner view but has CanCreateAnyTransactionRequest at BANK_ID", TransactionRequest) {}
+ } else {
+ scenario("we create a transaction request with a user who doesn't have access to owner view but has CanCreateAnyTransactionRequest at BANK_ID", TransactionRequest) {
+ val testBank = createBank("transactions-test-bank")
+ val bankId = testBank.bankId
+ val accountId1 = AccountId("__acc1")
+ val accountId2 = AccountId("__acc1")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, "EUR")
+
+ addEntitlement(bankId.value, obpuser3.userId, CanCreateAnyTransactionRequest.toString)
+ Then("We add entitlement to user3")
+ val hasEntitlement = code.api.util.APIUtil.hasEntitlement(bankId.value, obpuser3.userId, CanCreateAnyTransactionRequest)
+ hasEntitlement should equal(true)
+
+ def getFromAccount: BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ def getToAccount: BankAccount = {
+ BankAccount(bankId, accountId2).getOrElse(fail("couldn't get to account"))
+ }
+
+ val fromAccount = getFromAccount
+ val toAccount = getToAccount
+
+ val totalTransactionsBefore = transactionCount(fromAccount, toAccount)
+
+ val beforeFromBalance = fromAccount.balance
+ val beforeToBalance = toAccount.balance
+
+ //Create a transaction (request)
+ //1. get possible challenge types for from account
+ //2. create transaction request to to-account with one of the possible challenges
+ //3. answer challenge
+ //4. have a new transaction
+
+ val transactionRequestId = TransactionRequestId("__trans1")
+ val toAccountJson = TransactionRequestAccountJSON(toAccount.bankId.value, toAccount.accountId.value)
+
+ val amt = BigDecimal("12.50")
+ val bodyValue = AmountOfMoneyJSON("EUR", amt.toString())
+ val transactionRequestBody = TransactionRequestDetailsFreeFormJSON(bodyValue)
+
+ //call createTransactionRequest
+ var request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-request-types" / transactionRequestType / "transaction-requests").POST <@ (user3)
+ var response = makePostRequest(request, write(transactionRequestBody))
+ Then("we should get a 201 created code")
+ response.code should equal(201)
+
+ println(response.body)
+
+ //created a transaction request, check some return values. As type is SANDBOX_TAN and value is < 1000, we expect no challenge
+ val transRequestId: String = (response.body \ "id") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ Then("We should have some new transaction id")
+ transRequestId should not equal ("")
+
+ val responseBody = response.body
+
+
+ val status: String = (response.body \ "status") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ status should equal(code.transactionrequests.TransactionRequests.STATUS_COMPLETED)
+
+ // Challenge should be null (none required)
+ var challenge = (response.body \ "challenge").children
+ challenge.size should equal(0)
+
+ var transaction_ids = (response.body \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ //If user does not have access to owner or other view - they won’t be able to view transaction. Hence they can’t see the transaction_id
+ transaction_ids should not equal ("")
+
+ //call getTransactionRequests, check that we really created a transaction request
+ request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-requests").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+ val transactionRequests = response.body.children
+ transactionRequests.size should not equal (0)
+
+
+ val tr2Body = response.body
+
+ //check transaction_ids again
+ transaction_ids = (response.body \ "transaction_requests_with_charges" \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_ids should not equal ("")
+
+ //make sure that we also get no challenges back from this url (after getting from db)
+ challenge = (response.body \ "challenge").children
+ challenge.size should equal(0)
+
+ //check that we created a new transaction (since no challenge)
+ request = (v1_4Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transactions").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+ val transactions = response.body.children
+
+ transactions.size should equal(1)
+
+ //check that the description has been set
+ println(response.body)
+ /*val description = (((response.body \ "transactions")(0) \ "details") \ "description") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ description should not equal ("")*/
+
+ //check that the balances have been properly decreased/increased (since we handle that logic for sandbox accounts at least)
+ //(do it here even though the payments test does test makePayment already)
+ val rate = fx.exchangeRate(fromAccount.currency, toAccount.currency)
+ val convertedAmount = fx.convert(amt, rate)
+ val fromAccountBalance = getFromAccount.balance
+ And("the from account should have a balance bigger by the amount specified to pay")
+ fromAccountBalance should equal((beforeFromBalance + convertedAmount))
+
+ /*
+ And("the newest transaction for the account receiving the payment should have the proper amount")
+ newestToAccountTransaction.details.value.amount should equal(amt.toString)
+ */
+
+ And("the account receiving the payment should have a new balance plus the amount paid")
+ val toAccountBalance = getToAccount.balance
+ toAccountBalance should equal(beforeToBalance + convertedAmount)
+
+ And("there should now be 4 new transactions in the database (one for the sender, one for the receiver * 2")
+ transactionCount(fromAccount, toAccount) should equal(totalTransactionsBefore + 4)
+ }
+ }
+
+
+ // No challenge, No FX (same currencies)
+ if ( Props.getBool("transactionRequests_enabled", false) == false) {
+ ignore("we create a transaction request without challenge, no FX (same currencies)", TransactionRequest) {}
+ } else {
+ scenario("we create a transaction request without challenge, no FX (same currencies)", TransactionRequest) {
+ val testBank = createBank("transactions-test-bank")
+ val bankId = testBank.bankId
+ val accountId1 = AccountId("__acc1")
+ val accountId2 = AccountId("__acc1")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, "EUR")
+
+ def getFromAccount: BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ def getToAccount: BankAccount = {
+ BankAccount(bankId, accountId2).getOrElse(fail("couldn't get to account"))
+ }
+
+ val fromAccount = getFromAccount
+ val toAccount = getToAccount
+
+ val totalTransactionsBefore = transactionCount(fromAccount, toAccount)
+
+ val beforeFromBalance = fromAccount.balance
+ val beforeToBalance = toAccount.balance
+
+ //Create a transaction (request)
+ //1. get possible challenge types for from account
+ //2. create transaction request to to-account with one of the possible challenges
+ //3. answer challenge
+ //4. have a new transaction
+
+ val transactionRequestId = TransactionRequestId("__trans1")
+ val toAccountJson = TransactionRequestAccountJSON(toAccount.bankId.value, toAccount.accountId.value)
+
+ val amt = BigDecimal("12.50")
+ val bodyValue = AmountOfMoneyJSON("EUR", amt.toString())
+ val transactionRequestBody = TransactionRequestDetailsFreeFormJSON(bodyValue)
+
+ //call createTransactionRequest
+ var request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-request-types" / transactionRequestType / "transaction-requests").POST <@ (user1)
+ var response = makePostRequest(request, write(transactionRequestBody))
+ Then("we should get a 201 created code")
+ response.code should equal(201)
+
+ //created a transaction request, check some return values. As type is SANDBOX_TAN and value is < 1000, we expect no challenge
+ val transRequestId: String = (response.body \ "id") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ Then("We should have some new transaction id")
+ transRequestId should not equal ("")
+
+ val responseBody = response.body
+
+
+ val status: String = (response.body \ "status") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ status should equal(code.transactionrequests.TransactionRequests.STATUS_COMPLETED)
+
+ // Challenge should be null (none required)
+ var challenge = (response.body \ "challenge").children
+ challenge.size should equal(0)
+
+ var transaction_ids = (response.body \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_ids should not equal ("")
+
+ //call getTransactionRequests, check that we really created a transaction request
+ request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-requests").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+ val transactionRequests = response.body.children
+ transactionRequests.size should not equal (0)
+
+
+ val tr2Body = response.body
+
+ //check transaction_ids again
+ transaction_ids = (response.body \ "transaction_requests_with_charges" \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_ids should not equal ("")
+
+ //make sure that we also get no challenges back from this url (after getting from db)
+ challenge = (response.body \ "challenge").children
+ challenge.size should equal(0)
+
+ //check that we created a new transaction (since no challenge)
+ request = (v1_4Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transactions").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+ val transactions = response.body.children
+
+ transactions.size should equal(1)
+
+ //check that the balances have been properly decreased/increased (since we handle that logic for sandbox accounts at least)
+ //(do it here even though the payments test does test makePayment already)
+ val rate = fx.exchangeRate(fromAccount.currency, toAccount.currency)
+ val convertedAmount = fx.convert(amt, rate)
+ val fromAccountBalance = getFromAccount.balance
+ And("the from account should have a balance bigger by the amount specified to pay")
+ fromAccountBalance should equal((beforeFromBalance + convertedAmount))
+
+ /*
+ And("the newest transaction for the account receiving the payment should have the proper amount")
+ newestToAccountTransaction.details.value.amount should equal(amt.toString)
+ */
+
+ And("the account receiving the payment should have a new balance plus the amount paid")
+ val toAccountBalance = getToAccount.balance
+ toAccountBalance should equal(beforeToBalance + convertedAmount)
+
+ And("there should now be 2 new transactions in the database (one for the sender, one for the receiver * 2")
+ transactionCount(fromAccount, toAccount) should equal(totalTransactionsBefore + 2 * 2)
+ }
+ }
+
+ if ( Props.getBool("transactionRequests_enabled", false) == false) {
+ ignore("we create a transaction request with a user without owner view access", TransactionRequest) {}
+ } else {
+ scenario("we create a transaction request with a user without owner view access", TransactionRequest) {
+ val testBank = createBank("transactions-test-bank")
+ val bankId = testBank.bankId
+ val accountId1 = AccountId("__acc1")
+ val accountId2 = AccountId("__acc1")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, "EUR")
+
+ def getFromAccount: BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ def getToAccount: BankAccount = {
+ BankAccount(bankId, accountId2).getOrElse(fail("couldn't get to account"))
+ }
+
+ val fromAccount = getFromAccount
+ val toAccount = getToAccount
+
+ val toAccountJson = TransactionRequestAccountJSON(toAccount.bankId.value, toAccount.accountId.value)
+
+ val amt = BigDecimal("12.50")
+ val bodyValue = AmountOfMoneyJSON("EUR", amt.toString())
+ val transactionRequestBody = TransactionRequestDetailsFreeFormJSON(bodyValue)
+
+ //call createTransactionRequest with a user without owner view access
+ val request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-request-types" / transactionRequestType / "transaction-requests").POST <@ (user2)
+ val response = makePostRequest(request, write(transactionRequestBody))
+ Then("we should get a 400 created code")
+ response.code should equal(400)
+
+ //created a transaction request, check some return values. As type is SANDBOX_TAN and value is < 1000, we expect no challenge
+ val error: String = (response.body \ "error") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ Then("We should have the error: " + ErrorMessages.InsufficientAuthorisationToCreateTransactionRequest)
+ error should equal(ErrorMessages.InsufficientAuthorisationToCreateTransactionRequest)
+
+ }
+
+ }
+
+ if ( Props.getBool("transactionRequests_enabled", false) == false) {
+ ignore("we create a transaction request with a user who doesn't have access to owner view but has CanCreateAnyTransactionRequest at a different BANK_ID", TransactionRequest) {}
+ } else {
+ scenario("we create a transaction request with a user who doesn't have access to owner view but has CanCreateAnyTransactionRequest at a different BANK_ID", TransactionRequest) {
+ val testBank = createBank("transactions-test-bank")
+ val testBank2 = createBank("transactions-test-bank2")
+ val bankId = testBank.bankId
+ val bankId2 = testBank2.bankId
+ val accountId1 = AccountId("__acc1")
+ val accountId2 = AccountId("__acc2")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, "EUR")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId2, "EUR")
+ addEntitlement(bankId2.value, obpuser3.userId, CanCreateAnyTransactionRequest.toString)
+
+ Then("We add entitlement to user3")
+ val hasEntitlement = code.api.util.APIUtil.hasEntitlement(bankId2.value, obpuser3.userId, CanCreateAnyTransactionRequest)
+ hasEntitlement should equal(true)
+
+ def getFromAccount: BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ def getToAccount: BankAccount = {
+ BankAccount(bankId, accountId2).getOrElse(fail("couldn't get to account"))
+ }
+
+ val fromAccount = getFromAccount
+ val toAccount = getToAccount
+
+ val totalTransactionsBefore = transactionCount(fromAccount, toAccount)
+
+ val beforeFromBalance = fromAccount.balance
+ val beforeToBalance = toAccount.balance
+
+ val transactionRequestId = TransactionRequestId("__trans2")
+ val toAccountJson = TransactionRequestAccountJSON(toAccount.bankId.value, toAccount.accountId.value)
+
+ val amt = BigDecimal("12.50")
+ val bodyValue = AmountOfMoneyJSON("EUR", amt.toString())
+ val transactionRequestBody = TransactionRequestDetailsFreeFormJSON(bodyValue)
+
+ //call createTransactionRequest
+ val request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-request-types" / transactionRequestType / "transaction-requests").POST <@ (user3)
+ val response = makePostRequest(request, write(transactionRequestBody))
+ Then("we should get a 400 created code")
+ response.code should equal(400)
+
+ //created a transaction request, check some return values. As type is SANDBOX_TAN and value is < 1000, we expect no challenge
+ val error: String = (response.body \ "error") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ Then("We should have the error: " + ErrorMessages.InsufficientAuthorisationToCreateTransactionRequest)
+ error should equal(ErrorMessages.InsufficientAuthorisationToCreateTransactionRequest)
+
+
+ }
+ }
+
+ // No challenge, with FX
+ if (Props.getBool("transactionRequests_enabled", false) == false) {
+ ignore("we create an FX transaction request without challenge, with FX (different currencies)", TransactionRequest) {}
+ } else {
+ scenario("we create an FX transaction request without challenge, with FX (different currencies)", TransactionRequest) {
+ val testBank = createBank("transactions-test-bank")
+ val bankId = testBank.bankId
+ val accountId1 = AccountId("__acc1fx")
+ val accountId2 = AccountId("__acc1fx")
+
+ val fromCurrency = "AED"
+ val toCurrency = "AED"
+
+ val amt = BigDecimal("10.00") // This is money going out. We want to transfer this away from the From account.
+
+
+ val expectedAmtTo = amt * fx.exchangeRate(fromCurrency, toCurrency).get
+
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, fromCurrency)
+
+ def getFromAccount: BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ def getToAccount: BankAccount = {
+ BankAccount(bankId, accountId2).getOrElse(fail("couldn't get to account"))
+ }
+
+ val fromAccount = getFromAccount
+ val toAccount = getToAccount
+
+ val totalTransactionsBefore = transactionCount(fromAccount, toAccount)
+
+ val beforeFromBalance = fromAccount.balance
+ val beforeFromCurrency = fromAccount.currency
+
+
+ val beforeToBalance = toAccount.balance
+ val beforeToCurrency = toAccount.currency
+
+ // We debit the From
+ val expectedFromNewBalance = beforeFromBalance + amt
+
+ // We credit the To
+ val expectedToNewBalance = beforeToBalance + expectedAmtTo
+
+
+ //Create a transaction (request)
+ //1. get possible challenge types for from account
+ //2. create transaction request to to-account with one of the possible challenges
+ //3. answer challenge
+ //4. have a new transaction
+
+ val transactionRequestId = TransactionRequestId("__trans1")
+ val toAccountJson = TransactionRequestAccountJSON(toAccount.bankId.value, toAccount.accountId.value)
+
+
+ val bodyValue = AmountOfMoneyJSON(fromCurrency, amt.toString())
+ val transactionRequestBody = TransactionRequestDetailsFreeFormJSON(bodyValue)
+
+ //call createTransactionRequest
+ var request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-request-types" / transactionRequestType / "transaction-requests").POST <@ (user1)
+ var response = makePostRequest(request, write(transactionRequestBody))
+ Then("we should get a 201 created code")
+ response.code should equal(201)
+
+
+ val responseBody = response.body
+
+ //created a transaction request, check some return values. As type is SANDBOX_TAN, we expect no challenge
+ val transRequestId: String = (response.body \ "id") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ Then("We should have some new transaction request id")
+ transRequestId should not equal ("")
+
+ val status: String = (response.body \ "status") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ status should equal(code.transactionrequests.TransactionRequests.STATUS_COMPLETED)
+
+
+ Then("we should not have a challenge object")
+ var challenge = (response.body \ "challenge").children
+ challenge.size should equal(0)
+
+ var transaction_id = (response.body \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_id should not equal ("")
+
+ //call getTransactionRequests, check that we really created a transaction request
+ request = (v1_4Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-requests").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+ val transactionRequests = response.body.children
+ transactionRequests.size should not equal (0)
+
+ //check transaction_ids again
+ transaction_id = (response.body \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_id should not equal ("")
+
+ //make sure that we also get no challenges back from this url (after getting from db)
+ challenge = (response.body \ "challenge").children
+ challenge.size should equal(0)
+
+ //check that we created a new transaction (since no challenge)
+ request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transactions").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+
+ val fromTransactions = response.body.children
+
+ fromTransactions.size should equal(1)
+
+
+ // Transaction Value
+ val actualFromAmount = (((response.body \ "transactions") (0) \ "details") \ "value" \ "amount") match {
+ case JString(i) => i
+ case _ => ""
+ }
+
+ // We are debiting the amount
+ amt should equal(BigDecimal(actualFromAmount))
+
+ // New Balance
+ val actualFromBalance = (((response.body \ "transactions") (0) \ "details") \ "new_balance" \ "amount") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ expectedFromNewBalance should equal(BigDecimal(actualFromBalance))
+
+ //check that we created a new transaction (since no challenge)
+ request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / toAccount.accountId.value /
+ "owner" / "transactions").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+
+ val toTransactions = response.body.children
+
+ toTransactions.size should equal(1)
+
+ // Transaction Value
+ val actualToAmount = (((response.body \ "transactions") (0) \ "details") \ "value" \ "amount") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ expectedAmtTo should equal(BigDecimal(actualToAmount))
+
+ // New Balance
+ val actualToBalance = (((response.body \ "transactions") (0) \ "details") \ "new_balance" \ "amount") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ expectedToNewBalance should equal (BigDecimal(actualToBalance))
+ //
+
+ val rate = fx.exchangeRate(fromAccount.currency, toAccount.currency)
+ val convertedAmount = fx.convert(amt, rate)
+ val fromAccountBalance = getFromAccount.balance
+ And("the from account should have a balance bigger by the original amount specified to pay")
+ fromAccountBalance should equal(beforeFromBalance + amt)
+
+
+ //val fromAccountBalance = getFromAccount.balance
+ //And("the from account should have a balance bigger by the amount specified to pay")
+ //fromAccountBalance should equal((beforeFromBalance - amt))
+
+ /*
+ And("the newest transaction for the account receiving the payment should have the proper amount")
+ newestToAccountTransaction.details.value.amount should equal(amt.toString)
+ */
+
+ And("the account receiving the payment should have a new balance plus the amount paid")
+ val toAccountBalance = getToAccount.balance
+ toAccountBalance should equal(beforeToBalance + convertedAmount)
+
+ And("there should now be 2 new transactions in the database (one for the sender, one for the receiver)*2")
+ transactionCount(fromAccount, toAccount) should equal(totalTransactionsBefore + 2 * 2)
+ }
+ }
+
+
+ // With challenge, No FX (Same currencies)
+ if ( Props.getBool("transactionRequests_enabled", false) == false) {
+ ignore("we create a transaction request with a challenge, same currencies", TransactionRequest) {}
+ } else {
+ scenario("we create a transaction request with a challenge", TransactionRequest) {
+ //setup accounts
+ val testBank = createBank("transactions-test-bank")
+ val bankId = testBank.bankId
+ val accountId1 = AccountId("__acc1")
+ val accountId2 = AccountId("__acc1")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, "EUR")
+
+ def getFromAccount: BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ def getToAccount: BankAccount = {
+ BankAccount(bankId, accountId2).getOrElse(fail("couldn't get to account"))
+ }
+
+ val fromAccount = getFromAccount
+ val toAccount = getToAccount
+
+ val totalTransactionsBefore = transactionCount(fromAccount, toAccount)
+
+ val beforeFromBalance = fromAccount.balance
+ val beforeToBalance = toAccount.balance
+
+ val transactionRequestId = TransactionRequestId("__trans1")
+ val toAccountJson = TransactionRequestAccountJSON(toAccount.bankId.value, toAccount.accountId.value)
+
+ //1. TODO: get possible challenge types from account
+
+ //2. create transaction request to to-account with one of the possible challenges
+
+ //amount over 1000 €, so should trigger challenge request
+ val amt = BigDecimal("1250.00")
+ val bodyValue = AmountOfMoneyJSON("EUR", amt.toString())
+ val transactionRequestBody = TransactionRequestBodyJSON(
+ toAccountJson,
+ bodyValue,
+ "Test Transaction Request description")
+
+ //call createTransactionRequest API method
+ var request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-request-types" / transactionRequestType / "transaction-requests").POST <@ (user1)
+ var response = makePostRequest(request, write(transactionRequestBody))
+ Then("we should get a 201 created code")
+ response.code should equal(201)
+
+ //ok, created a transaction request, check some return values. As type is SANDBOX_TAN but over 100€, we expect a challenge
+ val transRequestId: String = (response.body \ "id") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transRequestId should not equal ("")
+
+ var status: String = (response.body \ "status") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ status should equal(code.transactionrequests.TransactionRequests.STATUS_INITIATED)
+
+ var transaction_id = (response.body \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_id should equal("")
+
+ var challenge = (response.body \ "challenge").children
+ challenge.size should not equal (0)
+
+ val challenge_id = (response.body \ "challenge" \ "id") match {
+ case JString(s) => s
+ case _ => ""
+ }
+ challenge_id should not equal ("")
+
+ //call getTransactionRequests, check that we really created a transaction request
+ request = (v1_4Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-requests").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+ var transactionRequests = response.body.children
+
+ transactionRequests.size should equal(1)
+ transaction_id = (response.body \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_id should equal("")
+
+ challenge = (response.body \ "challenge").children
+ challenge.size should not equal (0)
+
+ //3. answer challenge and check if transaction is being created
+ //call answerTransactionRequestChallenge, give a false answer
+ var answerJson = ChallengeAnswerJSON(id = challenge_id, answer = "hello") //wrong answer, not a number
+ request = (v1_4Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-request-types" / transactionRequestType / "transaction-requests" / transRequestId / "challenge").POST <@ (user1)
+ response = makePostRequest(request, write(answerJson))
+ Then("we should get a 400 bad request code")
+ response.code should equal(400)
+
+ //TODO: check if allowed_attempts is decreased
+
+ //call answerTransactionRequestChallenge again, give a good answer
+ answerJson = ChallengeAnswerJSON(id = challenge_id, answer = "12345") //good answer, not a number
+ //TODO: why V4_1 does not work ??
+ request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-request-types" / transactionRequestType / "transaction-requests" / transRequestId / "challenge").POST <@ (user1)
+ response = makePostRequest(request, write(answerJson))
+ Then("we should get a 202 accepted code")
+ response.code should equal(202)
+
+ //check if returned data includes new transaction's id
+ status = (response.body \ "status") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ status should equal(code.transactionrequests.TransactionRequests.STATUS_COMPLETED)
+
+ transaction_id = (response.body \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_id should not equal ("")
+
+ //call getTransactionRequests, check that we really created a transaction
+ request = (v1_4Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-requests").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+ transactionRequests = response.body.children
+
+ transactionRequests.size should equal(1)
+ transaction_id = (response.body \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_id should not equal ("")
+
+ challenge = (response.body \ "challenge").children
+ challenge.size should not equal (0)
+
+ //check that the balances have been properly decreased/increased (since we handle that logic for sandbox accounts at least)
+ //(do it here even though the payments test does test makePayment already)
+
+ val fromAccountBalance = getFromAccount.balance
+ And("the from account should have a balance bigger by the amount specified to pay")
+ fromAccountBalance should equal((beforeFromBalance + amt))
+
+ /*
+ And("the newest transaction for the account receiving the payment should have the proper amount")
+ newestToAccountTransaction.details.value.amount should equal(amt.toString)
+ */
+
+ And("the account receiving the payment should have a new balance plus the amount paid")
+ val toAccountBalance = getToAccount.balance
+ toAccountBalance should equal(beforeToBalance + amt)
+
+ And("there should now be 2 new transactions in the database (one for the sender, one for the receiver)*2")
+ transactionCount(fromAccount, toAccount) should equal(totalTransactionsBefore + 2 * 2)
+ }
+ }
+
+
+ // With Challenge, with FX
+ if ( Props.getBool("transactionRequests_enabled", false) == false) {
+ ignore("we create an FX transaction request with challenge", TransactionRequest) {}
+ } else {
+ scenario("we create an FX transaction request with challenge", TransactionRequest) {
+ val testBank = createBank("transactions-test-bank")
+ val bankId = testBank.bankId
+ val accountId1 = AccountId("__acc1fx")
+ val accountId2 = AccountId("__acc1fx")
+
+ val fromCurrency = "AED"
+ val toCurrency = "AED"
+
+ // This value is over the "challenge threshold" i.e. a security challenge will need to be answered.
+ // the limited AED is 4140 = 1000 eruo
+ val amt = BigDecimal("5000.00") // This is money going out. We want to transfer this away from the From account.
+
+
+ val expectedAmtTo = amt * fx.exchangeRate(fromCurrency, toCurrency).get
+
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, fromCurrency)
+
+ def getFromAccount: BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ def getToAccount: BankAccount = {
+ BankAccount(bankId, accountId2).getOrElse(fail("couldn't get to account"))
+ }
+
+ val fromAccount = getFromAccount
+ val toAccount = getToAccount
+
+ val totalTransactionsBefore = transactionCount(fromAccount, toAccount)
+
+ val beforeFromBalance = fromAccount.balance
+ val beforeFromCurrency = fromAccount.currency
+
+
+ val beforeToBalance = toAccount.balance
+ val beforeToCurrency = toAccount.currency
+
+ // We debit the From
+ val expectedFromNewBalance = beforeFromBalance + amt
+
+ // We credit the To
+ val expectedToNewBalance = beforeToBalance + expectedAmtTo
+
+
+ //Create a transaction (request)
+ //1. get possible challenge types for from account
+ //2. create transaction request to to-account with one of the possible challenges
+ //3. answer challenge
+ //4. have a new transaction
+
+ val transactionRequestId = TransactionRequestId("__trans1")
+ val toAccountJson = TransactionRequestAccountJSON(toAccount.bankId.value, toAccount.accountId.value)
+
+
+ val bodyValue = AmountOfMoneyJSON(fromCurrency, amt.toString())
+ val transactionRequestBody = TransactionRequestDetailsFreeFormJSON(bodyValue)
+
+ //call createTransactionRequest
+ var request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-request-types" / transactionRequestType / "transaction-requests").POST <@ (user1)
+ var response = makePostRequest(request, write(transactionRequestBody))
+ Then("we should get a 201 created code")
+ response.code should equal(201)
+
+ //created a transaction request, check some return values. As type is SANDBOX_TAN, we expect no challenge
+ val transRequestId: String = (response.body \ "id") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ Then("We should have some new transaction id")
+ transRequestId should not equal ("")
+
+ var status: String = (response.body \ "status") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ status should equal(code.transactionrequests.TransactionRequests.STATUS_INITIATED)
+
+ var transaction_ids = (response.body \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_ids should equal("")
+
+ var challenge = (response.body \ "challenge").children
+ challenge.size should not equal (0)
+
+ val challenge_id = (response.body \ "challenge" \ "id") match {
+ case JString(s) => s
+ case _ => ""
+ }
+ challenge_id should not equal ("")
+
+ //call getTransactionRequests, check that we really created a transaction request
+ request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-requests").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+ var transactionRequests = response.body.children
+
+ transactionRequests.size should equal(1)
+ transaction_ids = (response.body \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_ids should equal("")
+
+ //Then("we should have a challenge object")
+ //challenge = (response.body \ "challenge").children
+ // TODO fix this path challenge.size should not equal(0)
+
+ //3. answer challenge and check if transaction is being created
+ //call answerTransactionRequestChallenge, give a false answer
+ var answerJson = ChallengeAnswerJSON(id = challenge_id, answer = "hello") //wrong answer, not a number
+ request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-request-types" / transactionRequestType / "transaction-requests" / transRequestId / "challenge").POST <@ (user1)
+ response = makePostRequest(request, write(answerJson))
+ Then("we should get a 400 bad request code")
+ response.code should equal(400)
+
+ //TODO: check if allowed_attempts is decreased
+
+ //call answerTransactionRequestChallenge again, give a good answer
+ answerJson = ChallengeAnswerJSON(id = challenge_id, answer = "12345") //good answer, not a number
+ request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-request-types" / transactionRequestType / "transaction-requests" / transRequestId / "challenge").POST <@ (user1)
+ response = makePostRequest(request, write(answerJson))
+ Then("we should get a 202 accepted code")
+ response.code should equal(202)
+
+ //check if returned data includes new transaction's id
+ status = (response.body \ "status") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ status should equal(code.transactionrequests.TransactionRequests.STATUS_COMPLETED)
+
+ transaction_ids = (response.body \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_ids should not equal ("")
+
+ //call getTransactionRequests, check that we really created a transaction request
+ request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-requests").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+ transactionRequests = response.body.children
+
+ transactionRequests.size should not equal (0)
+
+ //check transaction_ids again
+ transaction_ids = (response.body \ "transaction_requests_with_charges" \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_ids should not equal ("")
+
+ //make sure that we also get no challenges back from this url (after getting from db)
+ // challenge = (response.body \ "challenge").children
+ // TODO challenge.size should not equal(0)
+
+ //check that we created a new transaction (since no challenge)
+ request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transactions").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+
+ val fromTransactions = response.body.children
+
+ fromTransactions.size should equal(1)
+
+
+ // Transaction Value
+ val actualFromAmount = (((response.body \ "transactions") (0) \ "details") \ "value" \ "amount") match {
+ case JString(i) => i
+ case _ => ""
+ }
+
+ // We are debiting the amount
+ amt should equal(BigDecimal(actualFromAmount))
+
+ // New Balance
+ val actualFromBalance = (((response.body \ "transactions") (0) \ "details") \ "new_balance" \ "amount") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ expectedFromNewBalance should equal(BigDecimal(actualFromBalance))
+
+ //check that we created a new transaction
+ request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / toAccount.accountId.value /
+ "owner" / "transactions").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+
+ val toTransactions = response.body.children
+
+ toTransactions.size should equal(1)
+
+ // Transaction Value
+ val actualToAmount = (((response.body \ "transactions") (0) \ "details") \ "value" \ "amount") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ expectedAmtTo should equal (BigDecimal(actualToAmount))
+
+ // New Balance
+ val actualToBalance = (((response.body \ "transactions") (0) \ "details") \ "new_balance" \ "amount") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ expectedToNewBalance should equal (BigDecimal(actualToBalance))
+
+
+ val rate = fx.exchangeRate(fromAccount.currency, toAccount.currency)
+ val convertedAmount = fx.convert(amt, rate)
+ val fromAccountBalance = getFromAccount.balance
+ And("the from account should have a balance bigger by the original amount specified to pay")
+ fromAccountBalance should equal(beforeFromBalance + amt)
+
+
+ //val fromAccountBalance = getFromAccount.balance
+ //And("the from account should have a balance bigger by the amount specified to pay")
+ //fromAccountBalance should equal((beforeFromBalance - amt))
+
+ /*
+ And("the newest transaction for the account receiving the payment should have the proper amount")
+ newestToAccountTransaction.details.value.amount should equal(amt.toString)
+ */
+
+ And("the account receiving the payment should have a new balance plus the amount paid")
+ val toAccountBalance = getToAccount.balance
+ toAccountBalance should equal(beforeToBalance + convertedAmount)
+
+ And("there should now be 2 new transactions in the database (one for the sender, one for the receiver)*2")
+ transactionCount(fromAccount, toAccount) should equal(totalTransactionsBefore + 2 * 2)
+ }
+ }
+
+ /*
+ scenario("we can't make a payment without access to the owner view", Payments) {
+ val testBank = createPaymentTestBank()
+ val bankId = testBank.bankId
+
+ val accountId1 = AccountId("__acc1")
+ val accountId2 = AccountId("__acc2")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, "EUR")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId2, "EUR")
+
+ def getFromAccount : BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ def getToAccount : BankAccount = {
+ BankAccount(bankId, accountId2).getOrElse(fail("couldn't get to account"))
+ }
+
+ val fromAccount = getFromAccount
+ val toAccount = getToAccount
+
+ val totalTransactionsBefore = transactionCount(fromAccount, toAccount)
+
+ val beforeFromBalance = fromAccount.balance
+ val beforeToBalance = toAccount.balance
+
+ val amt = BigDecimal("12.33")
+
+ val payJson = MakePaymentJson(toAccount.bankId.value, toAccount.accountId.value, amt.toString)
+ val postResult = postTransaction(fromAccount.bankId.value, fromAccount.accountId.value, view, payJson, user2)
+
+ Then("we should get a 400")
+ postResult.code should equal(400)
+
+ And("the number of transactions for each account should remain unchanged")
+ totalTransactionsBefore should equal(transactionCount(fromAccount, toAccount))
+
+ And("the balances of each account should remain unchanged")
+ beforeFromBalance should equal(getFromAccount.balance)
+ beforeToBalance should equal(getToAccount.balance)
+ }
+
+ scenario("we can't make a payment without an oauth user", Payments) {
+ val testBank = createPaymentTestBank()
+ val bankId = testBank.bankId
+ val accountId1 = AccountId("__acc1")
+ val accountId2 = AccountId("__acc2")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, "EUR")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId2, "EUR")
+
+ def getFromAccount : BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ def getToAccount : BankAccount = {
+ BankAccount(bankId, accountId2).getOrElse(fail("couldn't get to account"))
+ }
+
+ val fromAccount = getFromAccount
+ val toAccount = getToAccount
+
+ val totalTransactionsBefore = transactionCount(fromAccount, toAccount)
+
+ val beforeFromBalance = fromAccount.balance
+ val beforeToBalance = toAccount.balance
+
+ val amt = BigDecimal("12.33")
+
+ val payJson = MakePaymentJson(toAccount.bankId.value, toAccount.accountId.value, amt.toString)
+ val postResult = postTransaction(fromAccount.bankId.value, fromAccount.accountId.value, view, payJson, None)
+
+ Then("we should get a 400")
+ postResult.code should equal(400)
+
+ And("the number of transactions for each account should remain unchanged")
+ totalTransactionsBefore should equal(transactionCount(fromAccount, toAccount))
+
+ And("the balances of each account should remain unchanged")
+ beforeFromBalance should equal(getFromAccount.balance)
+ beforeToBalance should equal(getToAccount.balance)
+ }
+
+ scenario("we can't make a payment of zero units of currency", Payments) {
+ When("we try to make a payment with amount = 0")
+
+ val testBank = createPaymentTestBank()
+ val bankId = testBank.bankId
+ val accountId1 = AccountId("__acc1")
+ val accountId2 = AccountId("__acc2")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, "EUR")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId2, "EUR")
+
+ def getFromAccount : BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ def getToAccount : BankAccount = {
+ BankAccount(bankId, accountId2).getOrElse(fail("couldn't get to account"))
+ }
+
+ val fromAccount = getFromAccount
+ val toAccount = getToAccount
+
+ val totalTransactionsBefore = transactionCount(fromAccount, toAccount)
+
+ val beforeFromBalance = fromAccount.balance
+ val beforeToBalance = toAccount.balance
+
+ val amt = BigDecimal("0")
+
+ val payJson = MakePaymentJson(toAccount.bankId.value, toAccount.accountId.value, amt.toString)
+ val postResult = postTransaction(fromAccount.bankId.value, fromAccount.accountId.value, view, payJson, user1)
+
+ Then("we should get a 400")
+ postResult.code should equal(400)
+
+ And("the number of transactions for each account should remain unchanged")
+ totalTransactionsBefore should equal(transactionCount(fromAccount, toAccount))
+
+ And("the balances of each account should remain unchanged")
+ beforeFromBalance should equal(getFromAccount.balance)
+ beforeToBalance should equal(getToAccount.balance)
+ }
+
+ scenario("we can't make a payment with a negative amount of money", Payments) {
+
+ val testBank = createPaymentTestBank()
+ val bankId = testBank.bankId
+ val accountId1 = AccountId("__acc1")
+ val accountId2 = AccountId("__acc2")
+ val acc1 = createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, "EUR")
+ val acc2 = createAccountAndOwnerView(Some(obpuser1), bankId, accountId2, "EUR")
+
+ When("we try to make a payment with amount < 0")
+
+ def getFromAccount : BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ def getToAccount : BankAccount = {
+ BankAccount(bankId, accountId2).getOrElse(fail("couldn't get to account"))
+ }
+
+ val fromAccount = getFromAccount
+ val toAccount = getToAccount
+
+ val totalTransactionsBefore = transactionCount(fromAccount, toAccount)
+
+ val beforeFromBalance = fromAccount.balance
+ val beforeToBalance = toAccount.balance
+
+ val amt = BigDecimal("-20.30")
+
+ val payJson = MakePaymentJson(toAccount.bankId.value, toAccount.accountId.value, amt.toString)
+ val postResult = postTransaction(fromAccount.bankId.value, fromAccount.accountId.value, view, payJson, user1)
+
+ Then("we should get a 400")
+ postResult.code should equal(400)
+
+ And("the number of transactions for each account should remain unchanged")
+ totalTransactionsBefore should equal(transactionCount(fromAccount, toAccount))
+
+ And("the balances of each account should remain unchanged")
+ beforeFromBalance should equal(getFromAccount.balance)
+ beforeToBalance should equal(getToAccount.balance)
+ }
+
+ scenario("we can't make a payment to an account that doesn't exist", Payments) {
+
+ val testBank = createPaymentTestBank()
+ val bankId = testBank.bankId
+ val accountId1 = AccountId("__acc1")
+ val acc1 = createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, "EUR")
+
+ When("we try to make a payment to an account that doesn't exist")
+
+ def getFromAccount : BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ val fromAccount = getFromAccount
+
+ val totalTransactionsBefore = transactionCount(fromAccount)
+
+ val beforeFromBalance = fromAccount.balance
+
+ val amt = BigDecimal("17.30")
+
+ val payJson = MakePaymentJson(bankId.value, "ACCOUNTTHATDOESNOTEXIST232321321", amt.toString)
+ val postResult = postTransaction(fromAccount.bankId.value, fromAccount.accountId.value, view, payJson, user1)
+
+ Then("we should get a 400")
+ postResult.code should equal(400)
+
+ And("the number of transactions for the sender's account should remain unchanged")
+ totalTransactionsBefore should equal(transactionCount(fromAccount))
+
+ And("the balance of the sender's account should remain unchanged")
+ beforeFromBalance should equal(getFromAccount.balance)
+ }
+
+ scenario("we can't make a payment between accounts with different currencies", Payments) {
+ When("we try to make a payment to an account that has a different currency")
+ val testBank = createPaymentTestBank()
+ val bankId = testBank.bankId
+ val accountId1 = AccountId("__acc1")
+ val accountId2 = AccountId("__acc2")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, "EUR")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId2, "GBP")
+
+ def getFromAccount : BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ def getToAccount : BankAccount = {
+ BankAccount(bankId, accountId2).getOrElse(fail("couldn't get to account"))
+ }
+
+ val fromAccount = getFromAccount
+ val toAccount = getToAccount
+
+ val totalTransactionsBefore = transactionCount(fromAccount, toAccount)
+
+ val beforeFromBalance = fromAccount.balance
+ val beforeToBalance = toAccount.balance
+
+ val amt = BigDecimal("4.95")
+
+ val payJson = MakePaymentJson(toAccount.bankId.value, toAccount.accountId.value, amt.toString)
+ val postResult = postTransaction(fromAccount.bankId.value, fromAccount.accountId.value, view, payJson, user1)
+
+ Then("we should get a 400")
+ postResult.code should equal(400)
+
+ And("the number of transactions for each account should remain unchanged")
+ totalTransactionsBefore should equal(transactionCount(fromAccount, toAccount))
+
+ And("the balances of each account should remain unchanged")
+ beforeFromBalance should equal(getFromAccount.balance)
+ beforeToBalance should equal(getToAccount.balance)
+ } */
+ }
+}
diff --git a/src/test/scala/code/api/v2_1_0/TransactionRequestsSepaTest.scala b/src/test/scala/code/api/v2_1_0/TransactionRequestsSepaTest.scala
new file mode 100644
index 000000000..635633d00
--- /dev/null
+++ b/src/test/scala/code/api/v2_1_0/TransactionRequestsSepaTest.scala
@@ -0,0 +1,1383 @@
+package code.api.v2_0_1
+
+import code.api.util.ErrorMessages
+import code.api.{DefaultUsers, ErrorMessage, ServerSetupWithTestData}
+import code.api.util.APIUtil.OAuth._
+import code.api.v1_2_1.AmountOfMoneyJSON
+import code.api.v1_4_0.JSONFactory1_4_0.{ChallengeAnswerJSON, TransactionRequestAccountJSON}
+import code.bankconnectors.Connector
+import code.fx.fx
+import code.model.{AccountId, BankAccount, CounterpartyMetadataIban, TransactionRequestId}
+import net.liftweb.json.JsonAST.JString
+import net.liftweb.json.Serialization.write
+import net.liftweb.util.Props
+import org.scalatest.Tag
+import code.api.util.ApiRole._
+import code.api.v2_0_0.TransactionRequestBodyJSON
+import code.api.v2_1_0.{TransactionRequestDetailsFreeFormJSON, TransactionRequestDetailsSEPAJSON, TransactionRequestDetailsSandBoxTanJSON, V210ServerSetup}
+import code.metadata.counterparties.MappedCounterpartyMetadata
+import net.liftweb.mapper.By
+import net.liftweb.util.Helpers._
+
+class TransactionRequestsSepaTest extends ServerSetupWithTestData with DefaultUsers with V210ServerSetup {
+
+ object TransactionRequest extends Tag("transactionRequests")
+
+ val transactionRequestType: String = "SEPA" // SANDBOX_TAN SEPA FREE_FORM
+
+ feature("we can make transaction requests") {
+ val view = "owner"
+
+ def transactionCount(accounts: BankAccount*): Int = {
+ accounts.foldLeft(0)((accumulator, account) => {
+ //TODO: might be nice to avoid direct use of the connector, but if we use an api call we need to do
+ //it with the correct account owners, and be sure that we don't even run into pagination problems
+ accumulator + Connector.connector.vend.getTransactions(account.bankId, account.accountId).get.size
+ })
+ }
+
+ // No challenge, No FX (same currencies)
+ if (Props.getBool("transactionRequests_enabled", false) == false) {
+ ignore("we create a transaction request with a user who doesn't have access to owner view but has CanCreateAnyTransactionRequest at BANK_ID", TransactionRequest) {}
+ } else {
+ scenario("we create a transaction request with a user who doesn't have access to owner view but has CanCreateAnyTransactionRequest at BANK_ID", TransactionRequest) {
+
+ val testBank = createBank("transactions-test-bank")
+ val bankId = testBank.bankId
+ val accountId1 = AccountId("__acc1")
+ val accountId2 = AccountId("__acc2")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, "EUR")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId2, "EUR")
+
+ val counterpartyMetadataIban1 = CounterpartyMetadataIban("iBan1");
+ val counterpartyMetadataIban2 = CounterpartyMetadataIban("iBan2");
+ val counterpartyMetadata1 = createCounterpartyMetadata(bankId.value, accountId1.value, counterpartyMetadataIban1.value);
+ val counterpartyMetadata2 = createCounterpartyMetadata(bankId.value, accountId2.value, counterpartyMetadataIban2.value);
+
+ addEntitlement(bankId.value, obpuser3.userId, CanCreateAnyTransactionRequest.toString)
+ Then("We add entitlement to user3")
+ val hasEntitlement = code.api.util.APIUtil.hasEntitlement(bankId.value, obpuser3.userId, CanCreateAnyTransactionRequest)
+ hasEntitlement should equal(true)
+
+ def getFromAccount: BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ def getToAccount: BankAccount = {
+ BankAccount(bankId, accountId2).getOrElse(fail("couldn't get to account"))
+ }
+
+ val fromAccount = getFromAccount
+ val toAccount = getToAccount
+
+ val totalTransactionsBefore = transactionCount(fromAccount, toAccount)
+
+ val beforeFromBalance = fromAccount.balance
+ val beforeToBalance = toAccount.balance
+
+ //Create a transaction (request)
+ //1. get possible challenge types for from account
+ //2. create transaction request to to-account with one of the possible challenges
+ //3. answer challenge
+ //4. have a new transaction
+
+ val transactionRequestId = TransactionRequestId("__trans1")
+ val toAccountJson = TransactionRequestAccountJSON(toAccount.bankId.value, toAccount.accountId.value)
+
+ val amt = BigDecimal("12.50")
+ val bodyValue = AmountOfMoneyJSON("EUR", amt.toString())
+ // val transactionRequestBody = TransactionRequestBodyJSON(toAccountJson, bodyValue, "Test Transaction Request description")
+ val transactionRequestBody = TransactionRequestDetailsSEPAJSON(AmountOfMoneyJSON("EUR", amt.toString()), counterpartyMetadata2.getAccountNumber, "Test Transaction Request description")
+
+ //call createTransactionRequest
+ var request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-request-types" / transactionRequestType / "transaction-requests").POST <@ (user3)
+ var response = makePostRequest(request, write(transactionRequestBody))
+ Then("we should get a 201 created code")
+ response.code should equal(201)
+
+ println(response.body)
+
+ //created a transaction request, check some return values. As type is SANDBOX_TAN and value is < 1000, we expect no challenge
+ val transRequestId: String = (response.body \ "id") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ Then("We should have some new transaction id")
+ transRequestId should not equal ("")
+
+ val responseBody = response.body
+
+
+ val status: String = (response.body \ "status") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ status should equal(code.transactionrequests.TransactionRequests.STATUS_COMPLETED)
+
+ // Challenge should be null (none required)
+ var challenge = (response.body \ "challenge").children
+ challenge.size should equal(0)
+
+ var transaction_ids = (response.body \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ //If user does not have access to owner or other view - they won’t be able to view transaction. Hence they can’t see the transaction_id
+ transaction_ids should not equal ("")
+
+ //call getTransactionRequests, check that we really created a transaction request
+ request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-requests").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+ val transactionRequests = response.body.children
+ transactionRequests.size should not equal (0)
+
+
+ val tr2Body = response.body
+
+ //check transaction_ids again
+ transaction_ids = (response.body \ "transaction_requests_with_charges" \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_ids should not equal ("")
+
+ //make sure that we also get no challenges back from this url (after getting from db)
+ challenge = (response.body \ "challenge").children
+ challenge.size should equal(0)
+
+ //check that we created a new transaction (since no challenge)
+ request = (v1_4Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transactions").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+ val transactions = response.body.children
+
+ transactions.size should equal(1)
+
+ //check that the description has been set
+ println(response.body)
+ /*val description = (((response.body \ "transactions")(0) \ "details") \ "description") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ description should not equal ("")*/
+
+ //check that the balances have been properly decreased/increased (since we handle that logic for sandbox accounts at least)
+ //(do it here even though the payments test does test makePayment already)
+ val rate = fx.exchangeRate(fromAccount.currency, toAccount.currency)
+ val convertedAmount = fx.convert(amt, rate)
+ val fromAccountBalance = getFromAccount.balance
+ And("the from account should have a balance smaller by the amount specified to pay")
+ fromAccountBalance should equal((beforeFromBalance - convertedAmount))
+
+ /*
+ And("the newest transaction for the account receiving the payment should have the proper amount")
+ newestToAccountTransaction.details.value.amount should equal(amt.toString)
+ */
+
+ And("the account receiving the payment should have a new balance plus the amount paid")
+ val toAccountBalance = getToAccount.balance
+ toAccountBalance should equal(beforeToBalance + convertedAmount)
+
+ And("there should now be 2 new transactions in the database (one for the sender, one for the receiver")
+ transactionCount(fromAccount, toAccount) should equal(totalTransactionsBefore + 2)
+ }
+ }
+
+
+ // No challenge, No FX (same currencies)
+ if (Props.getBool("transactionRequests_enabled", false) == false) {
+ ignore("we create a transaction request without challenge, no FX (same currencies)", TransactionRequest) {}
+ } else {
+ scenario("we create a transaction request without challenge, no FX (same currencies)", TransactionRequest) {
+ val testBank = createBank("transactions-test-bank")
+ val bankId = testBank.bankId
+ val accountId1 = AccountId("__acc1")
+ val accountId2 = AccountId("__acc2")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, "EUR")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId2, "EUR")
+
+ val counterpartyMetadataIban1 = CounterpartyMetadataIban("iBan1");
+ val counterpartyMetadataIban2 = CounterpartyMetadataIban("iBan2");
+ val counterpartyMetadata1 = createCounterpartyMetadata(bankId.value, accountId1.value, counterpartyMetadataIban1.value);
+ val counterpartyMetadata2 = createCounterpartyMetadata(bankId.value, accountId2.value, counterpartyMetadataIban2.value);
+
+
+ def getFromAccount: BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ def getToAccount: BankAccount = {
+ BankAccount(bankId, accountId2).getOrElse(fail("couldn't get to account"))
+ }
+
+ val fromAccount = getFromAccount
+ val toAccount = getToAccount
+
+ val totalTransactionsBefore = transactionCount(fromAccount, toAccount)
+
+ val beforeFromBalance = fromAccount.balance
+ val beforeToBalance = toAccount.balance
+
+ //Create a transaction (request)
+ //1. get possible challenge types for from account
+ //2. create transaction request to to-account with one of the possible challenges
+ //3. answer challenge
+ //4. have a new transaction
+
+ val transactionRequestId = TransactionRequestId("__trans1")
+ val toAccountJson = TransactionRequestAccountJSON(toAccount.bankId.value, toAccount.accountId.value)
+
+ val amt = BigDecimal("12.50")
+ val bodyValue = AmountOfMoneyJSON("EUR", amt.toString())
+ // val transactionRequestBody = TransactionRequestBodyJSON(toAccountJson, bodyValue, "Test Transaction Request description")
+ val transactionRequestBody = TransactionRequestDetailsSEPAJSON(AmountOfMoneyJSON("EUR", amt.toString()), counterpartyMetadata2.getAccountNumber, "Test Transaction Request description")
+
+ //call createTransactionRequest
+ var request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-request-types" / transactionRequestType / "transaction-requests").POST <@ (user1)
+ var response = makePostRequest(request, write(transactionRequestBody))
+ Then("we should get a 201 created code")
+ response.code should equal(201)
+
+ //created a transaction request, check some return values. As type is SANDBOX_TAN and value is < 1000, we expect no challenge
+ val transRequestId: String = (response.body \ "id") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ Then("We should have some new transaction id")
+ transRequestId should not equal ("")
+
+ val responseBody = response.body
+
+
+ val status: String = (response.body \ "status") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ status should equal(code.transactionrequests.TransactionRequests.STATUS_COMPLETED)
+
+ // Challenge should be null (none required)
+ var challenge = (response.body \ "challenge").children
+ challenge.size should equal(0)
+
+ var transaction_ids = (response.body \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_ids should not equal ("")
+
+ //call getTransactionRequests, check that we really created a transaction request
+ request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-requests").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+ val transactionRequests = response.body.children
+ transactionRequests.size should not equal (0)
+
+
+ val tr2Body = response.body
+
+ //check transaction_ids again
+ transaction_ids = (response.body \ "transaction_requests_with_charges" \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_ids should not equal ("")
+
+ //make sure that we also get no challenges back from this url (after getting from db)
+ challenge = (response.body \ "challenge").children
+ challenge.size should equal(0)
+
+ //check that we created a new transaction (since no challenge)
+ request = (v1_4Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transactions").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+ val transactions = response.body.children
+
+ transactions.size should equal(1)
+
+ //check that the description has been set
+ val description = (((response.body \ "transactions") (0) \ "details") \ "description") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ description should not equal ("")
+
+ //check that the balances have been properly decreased/increased (since we handle that logic for sandbox accounts at least)
+ //(do it here even though the payments test does test makePayment already)
+ val rate = fx.exchangeRate(fromAccount.currency, toAccount.currency)
+ val convertedAmount = fx.convert(amt, rate)
+ val fromAccountBalance = getFromAccount.balance
+ And("the from account should have a balance smaller by the amount specified to pay")
+ fromAccountBalance should equal((beforeFromBalance - convertedAmount))
+
+ /*
+ And("the newest transaction for the account receiving the payment should have the proper amount")
+ newestToAccountTransaction.details.value.amount should equal(amt.toString)
+ */
+
+ And("the account receiving the payment should have a new balance plus the amount paid")
+ val toAccountBalance = getToAccount.balance
+ toAccountBalance should equal(beforeToBalance + convertedAmount)
+
+ And("there should now be 2 new transactions in the database (one for the sender, one for the receiver")
+ transactionCount(fromAccount, toAccount) should equal(totalTransactionsBefore + 2)
+ }
+ }
+
+ if (Props.getBool("transactionRequests_enabled", false) == false) {
+ ignore("we create a transaction request with a user without owner view access", TransactionRequest) {}
+ } else {
+ scenario("we create a transaction request with a user without owner view access", TransactionRequest) {
+
+
+ val testBank = createBank("transactions-test-bank")
+ val bankId = testBank.bankId
+ val accountId1 = AccountId("__acc1")
+ val accountId2 = AccountId("__acc2")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, "EUR")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId2, "EUR")
+
+ def getFromAccount: BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ def getToAccount: BankAccount = {
+ BankAccount(bankId, accountId2).getOrElse(fail("couldn't get to account"))
+ }
+
+ val fromAccount = getFromAccount
+ val toAccount = getToAccount
+
+ val toAccountJson = TransactionRequestAccountJSON(toAccount.bankId.value, toAccount.accountId.value)
+
+ val amt = BigDecimal("12.50")
+ val bodyValue = AmountOfMoneyJSON("EUR", amt.toString())
+ // val transactionRequestBody = TransactionRequestBodyJSON(toAccountJson, bodyValue, "Test Transaction Request description")
+
+ val counterpartyMetadataIban1 = CounterpartyMetadataIban("iBan1");
+ val counterpartyMetadataIban2 = CounterpartyMetadataIban("iBan2");
+ val counterpartyMetadata1 = createCounterpartyMetadata(bankId.value, accountId1.value, counterpartyMetadataIban1.value);
+ val counterpartyMetadata2 = createCounterpartyMetadata(bankId.value, accountId2.value, counterpartyMetadataIban2.value);
+
+ val transactionRequestBody = TransactionRequestDetailsSEPAJSON(AmountOfMoneyJSON("EUR", amt.toString()), counterpartyMetadata2.getAccountNumber, "Test Transaction Request description")
+
+
+ //call createTransactionRequest with a user without owner view access
+ val request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-request-types" / transactionRequestType / "transaction-requests").POST <@ (user2)
+ val response = makePostRequest(request, write(transactionRequestBody))
+ Then("we should get a 400 created code")
+ response.code should equal(400)
+
+ //created a transaction request, check some return values. As type is SANDBOX_TAN and value is < 1000, we expect no challenge
+ val error: String = (response.body \ "error") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ Then("We should have the error: " + ErrorMessages.InsufficientAuthorisationToCreateTransactionRequest)
+ error should equal(ErrorMessages.InsufficientAuthorisationToCreateTransactionRequest)
+
+ }
+
+ }
+
+ if (Props.getBool("transactionRequests_enabled", false) == false) {
+ ignore("we create a transaction request with a user who doesn't have access to owner view but has CanCreateAnyTransactionRequest at a different BANK_ID", TransactionRequest) {}
+ } else {
+ scenario("we create a transaction request with a user who doesn't have access to owner view but has CanCreateAnyTransactionRequest at a different BANK_ID", TransactionRequest) {
+ val testBank = createBank("transactions-test-bank")
+ val testBank2 = createBank("transactions-test-bank2")
+ val bankId = testBank.bankId
+ val bankId2 = testBank2.bankId
+ val accountId1 = AccountId("__acc1")
+ val accountId2 = AccountId("__acc2")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, "EUR")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId2, "EUR")
+ addEntitlement(bankId2.value, obpuser3.userId, CanCreateAnyTransactionRequest.toString)
+
+ Then("We add entitlement to user3")
+ val hasEntitlement = code.api.util.APIUtil.hasEntitlement(bankId2.value, obpuser3.userId, CanCreateAnyTransactionRequest)
+ hasEntitlement should equal(true)
+
+ def getFromAccount: BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ def getToAccount: BankAccount = {
+ BankAccount(bankId, accountId2).getOrElse(fail("couldn't get to account"))
+ }
+
+ val fromAccount = getFromAccount
+ val toAccount = getToAccount
+
+ val totalTransactionsBefore = transactionCount(fromAccount, toAccount)
+
+ val beforeFromBalance = fromAccount.balance
+ val beforeToBalance = toAccount.balance
+
+ val transactionRequestId = TransactionRequestId("__trans2")
+ val toAccountJson = TransactionRequestAccountJSON(toAccount.bankId.value, toAccount.accountId.value)
+
+ val amt = BigDecimal("12.50")
+ val bodyValue = AmountOfMoneyJSON("EUR", amt.toString())
+ // val transactionRequestBody = TransactionRequestBodyJSON(toAccountJson, bodyValue, "Test Transaction Request description")
+
+ val counterpartyMetadataIban1 = CounterpartyMetadataIban("iBan1");
+ val counterpartyMetadataIban2 = CounterpartyMetadataIban("iBan2");
+ val counterpartyMetadata1 = createCounterpartyMetadata(bankId.value, accountId1.value, counterpartyMetadataIban1.value);
+ val counterpartyMetadata2 = createCounterpartyMetadata(bankId.value, accountId2.value, counterpartyMetadataIban2.value);
+
+ val transactionRequestBody = TransactionRequestDetailsSEPAJSON(AmountOfMoneyJSON("EUR", amt.toString()), counterpartyMetadata2.getAccountNumber, "Test Transaction Request description")
+
+
+ //call createTransactionRequest
+ val request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-request-types" / transactionRequestType / "transaction-requests").POST <@ (user3)
+ val response = makePostRequest(request, write(transactionRequestBody))
+ Then("we should get a 400 created code")
+ response.code should equal(400)
+
+ //created a transaction request, check some return values. As type is SANDBOX_TAN and value is < 1000, we expect no challenge
+ val error: String = (response.body \ "error") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ Then("We should have the error: " + ErrorMessages.InsufficientAuthorisationToCreateTransactionRequest)
+ error should equal(ErrorMessages.InsufficientAuthorisationToCreateTransactionRequest)
+
+
+ }
+ }
+
+ // No challenge, with FX
+ if (Props.getBool("transactionRequests_enabled", false) == false) {
+ ignore("we create an FX transaction request without challenge, with FX (different currencies)", TransactionRequest) {}
+ } else {
+ scenario("we create an FX transaction request without challenge, with FX (different currencies)", TransactionRequest) {
+ val testBank = createBank("transactions-test-bank")
+ val bankId = testBank.bankId
+ val accountId1 = AccountId("__acc1fx")
+ val accountId2 = AccountId("__acc2fx")
+
+ val fromCurrency = "AED"
+ val toCurrency = "INR"
+
+ val amt = BigDecimal("10.00") // This is money going out. We want to transfer this away from the From account.
+
+
+ val expectedAmtTo = amt * fx.exchangeRate(fromCurrency, toCurrency).get
+
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, fromCurrency)
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId2, toCurrency)
+
+ val counterpartyMetadataIban1 = CounterpartyMetadataIban("iBan1");
+ val counterpartyMetadataIban2 = CounterpartyMetadataIban("iBan2");
+ val counterpartyMetadata1 = createCounterpartyMetadata(bankId.value, accountId1.value, counterpartyMetadataIban1.value);
+ val counterpartyMetadata2 = createCounterpartyMetadata(bankId.value, accountId2.value, counterpartyMetadataIban2.value);
+
+ def getFromAccount: BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ def getToAccount: BankAccount = {
+ BankAccount(bankId, accountId2).getOrElse(fail("couldn't get to account"))
+ }
+
+ val fromAccount = getFromAccount
+ val toAccount = getToAccount
+
+ val totalTransactionsBefore = transactionCount(fromAccount, toAccount)
+
+ val beforeFromBalance = fromAccount.balance
+ val beforeFromCurrency = fromAccount.currency
+
+
+ val beforeToBalance = toAccount.balance
+ val beforeToCurrency = toAccount.currency
+
+ // We debit the From
+ val expectedFromNewBalance = beforeFromBalance - amt
+
+ // We credit the To
+ val expectedToNewBalance = beforeToBalance + expectedAmtTo
+
+
+ //Create a transaction (request)
+ //1. get possible challenge types for from account
+ //2. create transaction request to to-account with one of the possible challenges
+ //3. answer challenge
+ //4. have a new transaction
+
+ val transactionRequestId = TransactionRequestId("__trans1")
+ val toAccountJson = TransactionRequestAccountJSON(toAccount.bankId.value, toAccount.accountId.value)
+
+
+ val bodyValue = AmountOfMoneyJSON(fromCurrency, amt.toString())
+ // val transactionRequestBody = TransactionRequestBodyJSON(toAccountJson, bodyValue, "Test Transaction Request description")
+
+ val transactionRequestBody = TransactionRequestDetailsSEPAJSON(AmountOfMoneyJSON("AED", amt.toString()), counterpartyMetadata2.getAccountNumber, "Test Transaction Request description")
+
+
+ //call createTransactionRequest
+ var request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-request-types" / transactionRequestType / "transaction-requests").POST <@ (user1)
+ var response = makePostRequest(request, write(transactionRequestBody))
+ Then("we should get a 201 created code")
+ response.code should equal(201)
+
+
+ val responseBody = response.body
+
+ //created a transaction request, check some return values. As type is SANDBOX_TAN, we expect no challenge
+ val transRequestId: String = (response.body \ "id") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ Then("We should have some new transaction request id")
+ transRequestId should not equal ("")
+
+ val status: String = (response.body \ "status") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ status should equal(code.transactionrequests.TransactionRequests.STATUS_COMPLETED)
+
+
+ Then("we should not have a challenge object")
+ var challenge = (response.body \ "challenge").children
+ challenge.size should equal(0)
+
+ var transaction_id = (response.body \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_id should not equal ("")
+
+ //call getTransactionRequests, check that we really created a transaction request
+ request = (v1_4Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-requests").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+ val transactionRequests = response.body.children
+ transactionRequests.size should not equal (0)
+
+ //check transaction_ids again
+ transaction_id = (response.body \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_id should not equal ("")
+
+ //make sure that we also get no challenges back from this url (after getting from db)
+ challenge = (response.body \ "challenge").children
+ challenge.size should equal(0)
+
+ //check that we created a new transaction (since no challenge)
+ request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transactions").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+
+ val fromTransactions = response.body.children
+
+ fromTransactions.size should equal(1)
+
+ //check that the description has been set
+ val description = (((response.body \ "transactions") (0) \ "details") \ "description") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ description should not equal ("")
+
+ // Transaction Value
+ val actualFromAmount = (((response.body \ "transactions") (0) \ "details") \ "value" \ "amount") match {
+ case JString(i) => i
+ case _ => ""
+ }
+
+ // We are debiting the amount
+ amt should equal(-1 * BigDecimal(actualFromAmount))
+
+ // New Balance
+ val actualFromBalance = (((response.body \ "transactions") (0) \ "details") \ "new_balance" \ "amount") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ expectedFromNewBalance should equal(BigDecimal(actualFromBalance))
+
+ //check that we created a new transaction (since no challenge)
+ request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / toAccount.accountId.value /
+ "owner" / "transactions").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+
+ val toTransactions = response.body.children
+
+ toTransactions.size should equal(1)
+
+ //check that the description has been set
+ val toDescription = (((response.body \ "transactions") (0) \ "details") \ "description") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ description should not equal ("")
+
+ // Transaction Value
+ val actualToAmount = (((response.body \ "transactions") (0) \ "details") \ "value" \ "amount") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ expectedAmtTo should equal(BigDecimal(actualToAmount))
+
+ // New Balance
+ val actualToBalance = (((response.body \ "transactions") (0) \ "details") \ "new_balance" \ "amount") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ expectedToNewBalance should equal(BigDecimal(actualToBalance))
+
+
+ val rate = fx.exchangeRate(fromAccount.currency, toAccount.currency)
+ val convertedAmount = fx.convert(amt, rate)
+ val fromAccountBalance = getFromAccount.balance
+ And("the from account should have a balance smaller by the original amount specified to pay")
+ fromAccountBalance should equal(beforeFromBalance - amt)
+
+
+ //val fromAccountBalance = getFromAccount.balance
+ //And("the from account should have a balance smaller by the amount specified to pay")
+ //fromAccountBalance should equal((beforeFromBalance - amt))
+
+ /*
+ And("the newest transaction for the account receiving the payment should have the proper amount")
+ newestToAccountTransaction.details.value.amount should equal(amt.toString)
+ */
+
+ And("the account receiving the payment should have a new balance plus the amount paid")
+ val toAccountBalance = getToAccount.balance
+ toAccountBalance should equal(beforeToBalance + convertedAmount)
+
+ And("there should now be 2 new transactions in the database (one for the sender, one for the receiver)")
+ transactionCount(fromAccount, toAccount) should equal(totalTransactionsBefore + 2)
+ }
+ }
+
+
+ // With challenge, No FX (Same currencies)
+ if (Props.getBool("transactionRequests_enabled", false) == false) {
+ ignore("we create a transaction request with a challenge, same currencies", TransactionRequest) {}
+ } else {
+ scenario("we create a transaction request with a challenge", TransactionRequest) {
+ //setup accounts
+ val testBank = createBank("transactions-test-bank")
+ val bankId = testBank.bankId
+ val accountId1 = AccountId("__acc1")
+ val accountId2 = AccountId("__acc2")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, "EUR")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId2, "EUR")
+
+ def getFromAccount: BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ def getToAccount: BankAccount = {
+ BankAccount(bankId, accountId2).getOrElse(fail("couldn't get to account"))
+ }
+
+ val fromAccount = getFromAccount
+ val toAccount = getToAccount
+
+ val totalTransactionsBefore = transactionCount(fromAccount, toAccount)
+
+ val beforeFromBalance = fromAccount.balance
+ val beforeToBalance = toAccount.balance
+
+ val transactionRequestId = TransactionRequestId("__trans1")
+ val toAccountJson = TransactionRequestAccountJSON(toAccount.bankId.value, toAccount.accountId.value)
+
+ //1. TODO: get possible challenge types from account
+
+ //2. create transaction request to to-account with one of the possible challenges
+
+ //amount over 1000 €, so should trigger challenge request
+ val amt = BigDecimal("1250.00")
+ val bodyValue = AmountOfMoneyJSON("EUR", amt.toString())
+ // val transactionRequestBody = TransactionRequestBodyJSON(
+ // toAccountJson,
+ // bodyValue,
+ // "Test Transaction Request description")
+
+
+ val counterpartyMetadataIban1 = CounterpartyMetadataIban("iBan1");
+ val counterpartyMetadataIban2 = CounterpartyMetadataIban("iBan2");
+ val counterpartyMetadata1 = createCounterpartyMetadata(bankId.value, accountId1.value, counterpartyMetadataIban1.value);
+ val counterpartyMetadata2 = createCounterpartyMetadata(bankId.value, accountId2.value, counterpartyMetadataIban2.value);
+
+ val transactionRequestBody = TransactionRequestDetailsSEPAJSON(AmountOfMoneyJSON("EUR", amt.toString()), counterpartyMetadata2.getAccountNumber, "Test Transaction Request description")
+
+
+ //call createTransactionRequest API method
+ var request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-request-types" / transactionRequestType / "transaction-requests").POST <@ (user1)
+ var response = makePostRequest(request, write(transactionRequestBody))
+ Then("we should get a 201 created code")
+ response.code should equal(201)
+
+ //ok, created a transaction request, check some return values. As type is SANDBOX_TAN but over 100€, we expect a challenge
+ val transRequestId: String = (response.body \ "id") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transRequestId should not equal ("")
+
+ var status: String = (response.body \ "status") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ status should equal(code.transactionrequests.TransactionRequests.STATUS_INITIATED)
+
+ var transaction_id = (response.body \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_id should equal("")
+
+ var challenge = (response.body \ "challenge").children
+ challenge.size should not equal (0)
+
+ val challenge_id = (response.body \ "challenge" \ "id") match {
+ case JString(s) => s
+ case _ => ""
+ }
+ challenge_id should not equal ("")
+
+ //call getTransactionRequests, check that we really created a transaction request
+ request = (v1_4Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-requests").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+ var transactionRequests = response.body.children
+
+ transactionRequests.size should equal(1)
+ transaction_id = (response.body \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_id should equal("")
+
+ challenge = (response.body \ "challenge").children
+ challenge.size should not equal (0)
+
+ //3. answer challenge and check if transaction is being created
+ //call answerTransactionRequestChallenge, give a false answer
+ var answerJson = ChallengeAnswerJSON(id = challenge_id, answer = "hello") //wrong answer, not a number
+ request = (v1_4Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-request-types" / transactionRequestType / "transaction-requests" / transRequestId / "challenge").POST <@ (user1)
+ response = makePostRequest(request, write(answerJson))
+ Then("we should get a 400 bad request code")
+ response.code should equal(400)
+
+ //TODO: check if allowed_attempts is decreased
+
+ //call answerTransactionRequestChallenge again, give a good answer
+ answerJson = ChallengeAnswerJSON(id = challenge_id, answer = "12345") //good answer, not a number
+ //TODO: why V4_1 does not work ??
+ request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-request-types" / transactionRequestType / "transaction-requests" / transRequestId / "challenge").POST <@ (user1)
+ response = makePostRequest(request, write(answerJson))
+ Then("we should get a 202 accepted code")
+ response.code should equal(202)
+
+ //check if returned data includes new transaction's id
+ status = (response.body \ "status") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ status should equal(code.transactionrequests.TransactionRequests.STATUS_COMPLETED)
+
+ transaction_id = (response.body \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_id should not equal ("")
+
+ //call getTransactionRequests, check that we really created a transaction
+ request = (v1_4Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-requests").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+ transactionRequests = response.body.children
+
+ transactionRequests.size should equal(1)
+ transaction_id = (response.body \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_id should not equal ("")
+
+ challenge = (response.body \ "challenge").children
+ challenge.size should not equal (0)
+
+ //check that the balances have been properly decreased/increased (since we handle that logic for sandbox accounts at least)
+ //(do it here even though the payments test does test makePayment already)
+
+ val fromAccountBalance = getFromAccount.balance
+ And("the from account should have a balance smaller by the amount specified to pay")
+ fromAccountBalance should equal((beforeFromBalance - amt))
+
+ /*
+ And("the newest transaction for the account receiving the payment should have the proper amount")
+ newestToAccountTransaction.details.value.amount should equal(amt.toString)
+ */
+
+ And("the account receiving the payment should have a new balance plus the amount paid")
+ val toAccountBalance = getToAccount.balance
+ toAccountBalance should equal(beforeToBalance + amt)
+
+ And("there should now be 2 new transactions in the database (one for the sender, one for the receiver")
+ transactionCount(fromAccount, toAccount) should equal(totalTransactionsBefore + 2)
+ }
+ }
+
+
+ // With Challenge, with FX
+ if (Props.getBool("transactionRequests_enabled", false) == false) {
+ ignore("we create an FX transaction request with challenge", TransactionRequest) {}
+ } else {
+ scenario("we create an FX transaction request with challenge", TransactionRequest) {
+ val testBank = createBank("transactions-test-bank")
+ val bankId = testBank.bankId
+ val accountId1 = AccountId("__acc1fx")
+ val accountId2 = AccountId("__acc2fx")
+
+ val fromCurrency = "AED"
+ val toCurrency = "INR"
+
+ // This value is over the "challenge threshold" i.e. a security challenge will need to be answered.
+ // the limited AED is 4140 = 1000 eruo
+ val amt = BigDecimal("5000.00") // This is money going out. We want to transfer this away from the From account.
+
+
+ val expectedAmtTo = amt * fx.exchangeRate(fromCurrency, toCurrency).get
+
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, fromCurrency)
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId2, toCurrency)
+
+ def getFromAccount: BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ def getToAccount: BankAccount = {
+ BankAccount(bankId, accountId2).getOrElse(fail("couldn't get to account"))
+ }
+
+ val fromAccount = getFromAccount
+ val toAccount = getToAccount
+
+ val totalTransactionsBefore = transactionCount(fromAccount, toAccount)
+
+ val beforeFromBalance = fromAccount.balance
+ val beforeFromCurrency = fromAccount.currency
+
+
+ val beforeToBalance = toAccount.balance
+ val beforeToCurrency = toAccount.currency
+
+ // We debit the From
+ val expectedFromNewBalance = beforeFromBalance - amt
+
+ // We credit the To
+ val expectedToNewBalance = beforeToBalance + expectedAmtTo
+
+
+ //Create a transaction (request)
+ //1. get possible challenge types for from account
+ //2. create transaction request to to-account with one of the possible challenges
+ //3. answer challenge
+ //4. have a new transaction
+
+ val transactionRequestId = TransactionRequestId("__trans1")
+ val toAccountJson = TransactionRequestAccountJSON(toAccount.bankId.value, toAccount.accountId.value)
+
+
+ val bodyValue = AmountOfMoneyJSON(fromCurrency, amt.toString())
+ // val transactionRequestBody = TransactionRequestBodyJSON(toAccountJson, bodyValue, "Test Transaction Request description")
+ val counterpartyMetadataIban1 = CounterpartyMetadataIban("iBan1");
+ val counterpartyMetadataIban2 = CounterpartyMetadataIban("iBan2");
+ val counterpartyMetadata1 = createCounterpartyMetadata(bankId.value, accountId1.value, counterpartyMetadataIban1.value);
+ val counterpartyMetadata2 = createCounterpartyMetadata(bankId.value, accountId2.value, counterpartyMetadataIban2.value);
+
+ val transactionRequestBody = TransactionRequestDetailsSEPAJSON(AmountOfMoneyJSON("AED", amt.toString()), counterpartyMetadata2.getAccountNumber, "Test Transaction Request description")
+
+
+ //call createTransactionRequest
+ var request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-request-types" / transactionRequestType / "transaction-requests").POST <@ (user1)
+ var response = makePostRequest(request, write(transactionRequestBody))
+ Then("we should get a 201 created code")
+ response.code should equal(201)
+
+ //created a transaction request, check some return values. As type is SANDBOX_TAN, we expect no challenge
+ val transRequestId: String = (response.body \ "id") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ Then("We should have some new transaction id")
+ transRequestId should not equal ("")
+
+ var status: String = (response.body \ "status") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ status should equal(code.transactionrequests.TransactionRequests.STATUS_INITIATED)
+
+ var transaction_ids = (response.body \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_ids should equal("")
+
+ var challenge = (response.body \ "challenge").children
+ challenge.size should not equal (0)
+
+ val challenge_id = (response.body \ "challenge" \ "id") match {
+ case JString(s) => s
+ case _ => ""
+ }
+ challenge_id should not equal ("")
+
+ //call getTransactionRequests, check that we really created a transaction request
+ request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-requests").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+ var transactionRequests = response.body.children
+
+ transactionRequests.size should equal(1)
+ transaction_ids = (response.body \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_ids should equal("")
+
+ //Then("we should have a challenge object")
+ //challenge = (response.body \ "challenge").children
+ // TODO fix this path challenge.size should not equal(0)
+
+ //3. answer challenge and check if transaction is being created
+ //call answerTransactionRequestChallenge, give a false answer
+ var answerJson = ChallengeAnswerJSON(id = challenge_id, answer = "hello") //wrong answer, not a number
+ request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-request-types" / transactionRequestType / "transaction-requests" / transRequestId / "challenge").POST <@ (user1)
+ response = makePostRequest(request, write(answerJson))
+ Then("we should get a 400 bad request code")
+ response.code should equal(400)
+
+ //TODO: check if allowed_attempts is decreased
+
+ //call answerTransactionRequestChallenge again, give a good answer
+ answerJson = ChallengeAnswerJSON(id = challenge_id, answer = "12345") //good answer, not a number
+ request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-request-types" / transactionRequestType / "transaction-requests" / transRequestId / "challenge").POST <@ (user1)
+ response = makePostRequest(request, write(answerJson))
+ Then("we should get a 202 accepted code")
+ response.code should equal(202)
+
+ //check if returned data includes new transaction's id
+ status = (response.body \ "status") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ status should equal(code.transactionrequests.TransactionRequests.STATUS_COMPLETED)
+
+ transaction_ids = (response.body \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_ids should not equal ("")
+
+ //call getTransactionRequests, check that we really created a transaction request
+ request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transaction-requests").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+ transactionRequests = response.body.children
+
+ transactionRequests.size should not equal (0)
+
+ //check transaction_ids again
+ transaction_ids = (response.body \ "transaction_requests_with_charges" \ "transaction_ids") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ transaction_ids should not equal ("")
+
+ //make sure that we also get no challenges back from this url (after getting from db)
+ // challenge = (response.body \ "challenge").children
+ // TODO challenge.size should not equal(0)
+
+ //check that we created a new transaction (since no challenge)
+ request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / fromAccount.accountId.value /
+ "owner" / "transactions").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+
+ val fromTransactions = response.body.children
+
+ fromTransactions.size should equal(1)
+
+ //check that the description has been set
+ val description = (((response.body \ "transactions") (0) \ "details") \ "description") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ description should not equal ("")
+
+ // Transaction Value
+ val actualFromAmount = (((response.body \ "transactions") (0) \ "details") \ "value" \ "amount") match {
+ case JString(i) => i
+ case _ => ""
+ }
+
+ // We are debiting the amount
+ amt should equal(-1 * BigDecimal(actualFromAmount))
+
+ // New Balance
+ val actualFromBalance = (((response.body \ "transactions") (0) \ "details") \ "new_balance" \ "amount") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ expectedFromNewBalance should equal(BigDecimal(actualFromBalance))
+
+ //check that we created a new transaction
+ request = (v2_1Request / "banks" / testBank.bankId.value / "accounts" / toAccount.accountId.value /
+ "owner" / "transactions").GET <@ (user1)
+ response = makeGetRequest(request)
+
+ Then("we should get a 200 ok code")
+ response.code should equal(200)
+
+ val toTransactions = response.body.children
+
+ toTransactions.size should equal(1)
+
+ //check that the description has been set
+ val toDescription = (((response.body \ "transactions") (0) \ "details") \ "description") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ description should not equal ("")
+
+ // Transaction Value
+ val actualToAmount = (((response.body \ "transactions") (0) \ "details") \ "value" \ "amount") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ expectedAmtTo should equal(BigDecimal(actualToAmount))
+
+ // New Balance
+ val actualToBalance = (((response.body \ "transactions") (0) \ "details") \ "new_balance" \ "amount") match {
+ case JString(i) => i
+ case _ => ""
+ }
+ expectedToNewBalance should equal(BigDecimal(actualToBalance))
+
+
+ val rate = fx.exchangeRate(fromAccount.currency, toAccount.currency)
+ val convertedAmount = fx.convert(amt, rate)
+ val fromAccountBalance = getFromAccount.balance
+ And("the from account should have a balance smaller by the original amount specified to pay")
+ fromAccountBalance should equal(beforeFromBalance - amt)
+
+
+ //val fromAccountBalance = getFromAccount.balance
+ //And("the from account should have a balance smaller by the amount specified to pay")
+ //fromAccountBalance should equal((beforeFromBalance - amt))
+
+ /*
+ And("the newest transaction for the account receiving the payment should have the proper amount")
+ newestToAccountTransaction.details.value.amount should equal(amt.toString)
+ */
+
+ And("the account receiving the payment should have a new balance plus the amount paid")
+ val toAccountBalance = getToAccount.balance
+ toAccountBalance should equal(beforeToBalance + convertedAmount)
+
+ And("there should now be 2 new transactions in the database (one for the sender, one for the receiver)")
+ transactionCount(fromAccount, toAccount) should equal(totalTransactionsBefore + 2)
+ }
+ }
+
+ /*
+ scenario("we can't make a payment without access to the owner view", Payments) {
+ val testBank = createPaymentTestBank()
+ val bankId = testBank.bankId
+
+ val accountId1 = AccountId("__acc1")
+ val accountId2 = AccountId("__acc2")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, "EUR")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId2, "EUR")
+
+ def getFromAccount : BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ def getToAccount : BankAccount = {
+ BankAccount(bankId, accountId2).getOrElse(fail("couldn't get to account"))
+ }
+
+ val fromAccount = getFromAccount
+ val toAccount = getToAccount
+
+ val totalTransactionsBefore = transactionCount(fromAccount, toAccount)
+
+ val beforeFromBalance = fromAccount.balance
+ val beforeToBalance = toAccount.balance
+
+ val amt = BigDecimal("12.33")
+
+ val payJson = MakePaymentJson(toAccount.bankId.value, toAccount.accountId.value, amt.toString)
+ val postResult = postTransaction(fromAccount.bankId.value, fromAccount.accountId.value, view, payJson, user2)
+
+ Then("we should get a 400")
+ postResult.code should equal(400)
+
+ And("the number of transactions for each account should remain unchanged")
+ totalTransactionsBefore should equal(transactionCount(fromAccount, toAccount))
+
+ And("the balances of each account should remain unchanged")
+ beforeFromBalance should equal(getFromAccount.balance)
+ beforeToBalance should equal(getToAccount.balance)
+ }
+
+ scenario("we can't make a payment without an oauth user", Payments) {
+ val testBank = createPaymentTestBank()
+ val bankId = testBank.bankId
+ val accountId1 = AccountId("__acc1")
+ val accountId2 = AccountId("__acc2")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, "EUR")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId2, "EUR")
+
+ def getFromAccount : BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ def getToAccount : BankAccount = {
+ BankAccount(bankId, accountId2).getOrElse(fail("couldn't get to account"))
+ }
+
+ val fromAccount = getFromAccount
+ val toAccount = getToAccount
+
+ val totalTransactionsBefore = transactionCount(fromAccount, toAccount)
+
+ val beforeFromBalance = fromAccount.balance
+ val beforeToBalance = toAccount.balance
+
+ val amt = BigDecimal("12.33")
+
+ val payJson = MakePaymentJson(toAccount.bankId.value, toAccount.accountId.value, amt.toString)
+ val postResult = postTransaction(fromAccount.bankId.value, fromAccount.accountId.value, view, payJson, None)
+
+ Then("we should get a 400")
+ postResult.code should equal(400)
+
+ And("the number of transactions for each account should remain unchanged")
+ totalTransactionsBefore should equal(transactionCount(fromAccount, toAccount))
+
+ And("the balances of each account should remain unchanged")
+ beforeFromBalance should equal(getFromAccount.balance)
+ beforeToBalance should equal(getToAccount.balance)
+ }
+
+ scenario("we can't make a payment of zero units of currency", Payments) {
+ When("we try to make a payment with amount = 0")
+
+ val testBank = createPaymentTestBank()
+ val bankId = testBank.bankId
+ val accountId1 = AccountId("__acc1")
+ val accountId2 = AccountId("__acc2")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, "EUR")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId2, "EUR")
+
+ def getFromAccount : BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ def getToAccount : BankAccount = {
+ BankAccount(bankId, accountId2).getOrElse(fail("couldn't get to account"))
+ }
+
+ val fromAccount = getFromAccount
+ val toAccount = getToAccount
+
+ val totalTransactionsBefore = transactionCount(fromAccount, toAccount)
+
+ val beforeFromBalance = fromAccount.balance
+ val beforeToBalance = toAccount.balance
+
+ val amt = BigDecimal("0")
+
+ val payJson = MakePaymentJson(toAccount.bankId.value, toAccount.accountId.value, amt.toString)
+ val postResult = postTransaction(fromAccount.bankId.value, fromAccount.accountId.value, view, payJson, user1)
+
+ Then("we should get a 400")
+ postResult.code should equal(400)
+
+ And("the number of transactions for each account should remain unchanged")
+ totalTransactionsBefore should equal(transactionCount(fromAccount, toAccount))
+
+ And("the balances of each account should remain unchanged")
+ beforeFromBalance should equal(getFromAccount.balance)
+ beforeToBalance should equal(getToAccount.balance)
+ }
+
+ scenario("we can't make a payment with a negative amount of money", Payments) {
+
+ val testBank = createPaymentTestBank()
+ val bankId = testBank.bankId
+ val accountId1 = AccountId("__acc1")
+ val accountId2 = AccountId("__acc2")
+ val acc1 = createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, "EUR")
+ val acc2 = createAccountAndOwnerView(Some(obpuser1), bankId, accountId2, "EUR")
+
+ When("we try to make a payment with amount < 0")
+
+ def getFromAccount : BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ def getToAccount : BankAccount = {
+ BankAccount(bankId, accountId2).getOrElse(fail("couldn't get to account"))
+ }
+
+ val fromAccount = getFromAccount
+ val toAccount = getToAccount
+
+ val totalTransactionsBefore = transactionCount(fromAccount, toAccount)
+
+ val beforeFromBalance = fromAccount.balance
+ val beforeToBalance = toAccount.balance
+
+ val amt = BigDecimal("-20.30")
+
+ val payJson = MakePaymentJson(toAccount.bankId.value, toAccount.accountId.value, amt.toString)
+ val postResult = postTransaction(fromAccount.bankId.value, fromAccount.accountId.value, view, payJson, user1)
+
+ Then("we should get a 400")
+ postResult.code should equal(400)
+
+ And("the number of transactions for each account should remain unchanged")
+ totalTransactionsBefore should equal(transactionCount(fromAccount, toAccount))
+
+ And("the balances of each account should remain unchanged")
+ beforeFromBalance should equal(getFromAccount.balance)
+ beforeToBalance should equal(getToAccount.balance)
+ }
+
+ scenario("we can't make a payment to an account that doesn't exist", Payments) {
+
+ val testBank = createPaymentTestBank()
+ val bankId = testBank.bankId
+ val accountId1 = AccountId("__acc1")
+ val acc1 = createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, "EUR")
+
+ When("we try to make a payment to an account that doesn't exist")
+
+ def getFromAccount : BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ val fromAccount = getFromAccount
+
+ val totalTransactionsBefore = transactionCount(fromAccount)
+
+ val beforeFromBalance = fromAccount.balance
+
+ val amt = BigDecimal("17.30")
+
+ val payJson = MakePaymentJson(bankId.value, "ACCOUNTTHATDOESNOTEXIST232321321", amt.toString)
+ val postResult = postTransaction(fromAccount.bankId.value, fromAccount.accountId.value, view, payJson, user1)
+
+ Then("we should get a 400")
+ postResult.code should equal(400)
+
+ And("the number of transactions for the sender's account should remain unchanged")
+ totalTransactionsBefore should equal(transactionCount(fromAccount))
+
+ And("the balance of the sender's account should remain unchanged")
+ beforeFromBalance should equal(getFromAccount.balance)
+ }
+
+ scenario("we can't make a payment between accounts with different currencies", Payments) {
+ When("we try to make a payment to an account that has a different currency")
+ val testBank = createPaymentTestBank()
+ val bankId = testBank.bankId
+ val accountId1 = AccountId("__acc1")
+ val accountId2 = AccountId("__acc2")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId1, "EUR")
+ createAccountAndOwnerView(Some(obpuser1), bankId, accountId2, "GBP")
+
+ def getFromAccount : BankAccount = {
+ BankAccount(bankId, accountId1).getOrElse(fail("couldn't get from account"))
+ }
+
+ def getToAccount : BankAccount = {
+ BankAccount(bankId, accountId2).getOrElse(fail("couldn't get to account"))
+ }
+
+ val fromAccount = getFromAccount
+ val toAccount = getToAccount
+
+ val totalTransactionsBefore = transactionCount(fromAccount, toAccount)
+
+ val beforeFromBalance = fromAccount.balance
+ val beforeToBalance = toAccount.balance
+
+ val amt = BigDecimal("4.95")
+
+ val payJson = MakePaymentJson(toAccount.bankId.value, toAccount.accountId.value, amt.toString)
+ val postResult = postTransaction(fromAccount.bankId.value, fromAccount.accountId.value, view, payJson, user1)
+
+ Then("we should get a 400")
+ postResult.code should equal(400)
+
+ And("the number of transactions for each account should remain unchanged")
+ totalTransactionsBefore should equal(transactionCount(fromAccount, toAccount))
+
+ And("the balances of each account should remain unchanged")
+ beforeFromBalance should equal(getFromAccount.balance)
+ beforeToBalance should equal(getToAccount.balance)
+ } */
+ }
+}