Skip to content

Commit

Permalink
Add draft code for p24 card balance
Browse files Browse the repository at this point in the history
  • Loading branch information
UnknownNPC committed Aug 3, 2019
1 parent 4c9699b commit 446e20b
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 76 deletions.
Expand Up @@ -5,6 +5,7 @@ import java.util.Date

import com.github.unknownnpc.psw.api.APIException
import com.github.unknownnpc.psw.p24.action.RetrieveTransferHistoryAction
import com.github.unknownnpc.psw.p24.model.P24Model
import com.github.unknownnpc.psw.p24.model.P24Model.Merchant
import com.github.unknownnpc.psw.p24.model.P24Model.WalletHistory._
import com.github.unknownnpc.psw.p24.serializer.P24Serializer.walletHistoryReqResSerializer
Expand All @@ -25,17 +26,17 @@ private[p24] class P24API(merchId: Long, merchPass: String, httpClient: Closeabl
*/
def retrieveTransferHistory(cardNum: String, from: Date, to: Date, waitVal: Long = 15): Either[APIException, WalletHistoryResponse] = {

val request = WalletHistoryRequest(
val request = P24Model.Request(
merchPass,
Merchant(merchId, None),
WalletHistoryRequestData(
P24Model.RequestData(
waitField = waitVal,
payment =
WalletHistoryRequestDataPayment(
P24Model.RequestDataPayment(
props = List(
WalletHistoryRequestDataProp(WalletRequestHistoryFromDate, p24ReqDateFormatter.format(from)),
WalletHistoryRequestDataProp(WalletRequestHistoryToDate, p24ReqDateFormatter.format(to)),
WalletHistoryRequestDataProp(WalletRequestHistoryCardName, cardNum)
P24Model.RequestDataProp(WalletRequestHistoryFromDate, p24ReqDateFormatter.format(from)),
P24Model.RequestDataProp(WalletRequestHistoryToDate, p24ReqDateFormatter.format(to)),
P24Model.RequestDataProp(WalletRequestHistoryCardName, cardNum)
)
)
)
Expand All @@ -44,6 +45,8 @@ private[p24] class P24API(merchId: Long, merchPass: String, httpClient: Closeabl
RetrieveTransferHistoryAction(httpClient).run(request)
}

def retrieveCardBalance(cardNum: String, waitVal: Long = 15)

}

object P24API {
Expand Down
@@ -0,0 +1,19 @@
package com.github.unknownnpc.psw.p24.action

import com.github.unknownnpc.psw.api.action.ActionContext
import com.github.unknownnpc.psw.api.executor.RestHttpExecutor
import com.github.unknownnpc.psw.p24.model.P24Model
import com.github.unknownnpc.psw.p24.model.P24Model.WalletHistory.WalletHistoryResponse
import org.apache.http.client.methods.HttpPost
import org.apache.http.impl.client.CloseableHttpClient

private[action] trait RetrieveCardBalanceAction extends
ActionContext[P24Model.Request, WalletHistoryResponse, HttpPost, String] with RestHttpExecutor[HttpPost]

object RetrieveCardBalanceAction {

def apply(httpClientParam: CloseableHttpClient): RetrieveCardBalanceAction = new RetrieveCardBalanceAction() {
override val httpClient: CloseableHttpClient = httpClientParam
}

}
Expand Up @@ -2,12 +2,13 @@ package com.github.unknownnpc.psw.p24.action

import com.github.unknownnpc.psw.api.action.ActionContext
import com.github.unknownnpc.psw.api.executor.RestHttpExecutor
import com.github.unknownnpc.psw.p24.model.P24Model.WalletHistory.{WalletHistoryRequest, WalletHistoryResponse}
import com.github.unknownnpc.psw.p24.model.P24Model
import com.github.unknownnpc.psw.p24.model.P24Model.WalletHistory.WalletHistoryResponse
import org.apache.http.client.methods.HttpPost
import org.apache.http.impl.client.CloseableHttpClient

private[action] trait RetrieveTransferHistoryAction extends
ActionContext[WalletHistoryRequest, WalletHistoryResponse, HttpPost, String] with RestHttpExecutor[HttpPost]
ActionContext[P24Model.Request, WalletHistoryResponse, HttpPost, String] with RestHttpExecutor[HttpPost]

object RetrieveTransferHistoryAction {

Expand Down
Expand Up @@ -6,18 +6,18 @@ private[p24] object P24Model {

case class Merchant(id: Long, signature: Option[String])

case class Request(merchantPassword: String, merchant: Merchant, data: RequestData)
case class RequestData(oper: String = "cmt", waitField: Long = 0, test: Long = 0, payment: RequestDataPayment)
case class RequestDataPayment(idAttr: String = "", props: List[RequestDataProp])
case class RequestDataProp(name: String, value: String)

object WalletHistory {

case class WalletHistoryResponse(merchant: Merchant, data: WalletHistoryResponseData)
case class WalletHistoryResponseData(oper: String, info: WalletHistoryResponseInfo)
case class WalletHistoryResponseInfo(status: String, credit: String, debet: String, statements: List[WalletHistoryResponseStatementEntity])
case class WalletHistoryResponseStatementEntity(card: String, appcode: String, trandate: Date, amount: String, cardamount: String, rest: String, terminal: String, description: String)

case class WalletHistoryRequest(merchantPassword: String, merchant: Merchant, data: WalletHistoryRequestData)
case class WalletHistoryRequestData(oper: String = "cmt", waitField: Long = 0, test: Long = 0, payment: WalletHistoryRequestDataPayment)
case class WalletHistoryRequestDataPayment(idAttr: String = "", props: List[WalletHistoryRequestDataProp])
case class WalletHistoryRequestDataProp(name: String, value: String)

}


Expand Down
@@ -1,11 +1,12 @@
package com.github.unknownnpc.psw.p24.serializer

import com.github.unknownnpc.psw.api.Serializer
import com.github.unknownnpc.psw.p24.model.P24Model.WalletHistory.{WalletHistoryRequest, WalletHistoryResponse}
import com.github.unknownnpc.psw.p24.model.P24Model
import com.github.unknownnpc.psw.p24.model.P24Model.WalletHistory.WalletHistoryResponse
import org.apache.http.client.methods.HttpPost

private[p24] object P24Serializer {

implicit val walletHistoryReqResSerializer: Serializer[WalletHistoryRequest, WalletHistoryResponse, HttpPost, String] = new WalletHistoryReqResSerializer
implicit val walletHistoryReqResSerializer: Serializer[P24Model.Request, WalletHistoryResponse, HttpPost, String] = new WalletHistoryReqResSerializer

}
@@ -0,0 +1,68 @@
package com.github.unknownnpc.psw.p24.serializer

import java.security.MessageDigest
import java.text.SimpleDateFormat

import com.github.unknownnpc.psw.p24.model.P24Model
import com.github.unknownnpc.psw.p24.model.P24Model.Merchant
import org.apache.commons.codec.binary.Hex

import scala.xml.{Elem, Node}

trait P24SerializerLike {

val p24ResponseDateFormatter = new SimpleDateFormat("yyyy-MM-dd")

def unPrettyOut(string: String): String = {
string.replaceAll(">\\s+<", "><")
}

def formRequestXmlStr(req: P24Model.Request): String = {
val dataXml = requestDataToXml(req.data)
val merchantXml = merchantToXml(req.merchant, dataXml.child, req.merchantPassword)

unPrettyOut(
formRequestXml(merchantXml, dataXml).toString()
)
}

private def encodeMessage(message: String, hashType: String): String = {
val messageDigest = MessageDigest.getInstance(hashType)
messageDigest.update(message.getBytes())
new String(Hex.encodeHex(messageDigest.digest()))
}

private def formRequestXml(merchantXml: Elem, dataXml: Elem): Elem = {
<request version="1.0">
<xml version="1.0" encoding="UTF-8">
{merchantXml}{dataXml}
</xml>
</request>
}

private def merchantToXml(merchant: Merchant, dataProps: Seq[Node], password: String): Elem = {

val dataPropsStr: String = dataProps.map(d => unPrettyOut(d.toString()))
.filter(d => !d.trim.isEmpty).mkString("")

<merchant>
<id>{merchant.id}</id>
<signature>{encodeMessage(encodeMessage(dataPropsStr + password, "MD5"), "SHA1")}</signature>
</merchant>
}

private def requestDataToXml(walletHistoryRequestData: P24Model.RequestData): Elem = {

def walletHistoryRequestDataPropToXml(requestDataProp: P24Model.RequestDataProp): Elem = {
<prop name={requestDataProp.name} value={requestDataProp.value}></prop>
}

<data>
<oper>{walletHistoryRequestData.oper}</oper>
<wait>{walletHistoryRequestData.waitField}</wait>
<test>{walletHistoryRequestData.test}</test>
<payment id={walletHistoryRequestData.payment.idAttr}>{walletHistoryRequestData.payment.props.map(walletHistoryRequestDataPropToXml)}</payment>
</data>
}

}
Expand Up @@ -4,88 +4,41 @@ import java.security.MessageDigest
import java.text.SimpleDateFormat

import com.github.unknownnpc.psw.api.Serializer
import com.github.unknownnpc.psw.p24.model.P24Model
import com.github.unknownnpc.psw.p24.model.P24Model.Merchant
import com.github.unknownnpc.psw.p24.model.P24Model.WalletHistory._
import org.apache.commons.codec.binary.Hex
import org.apache.http.client.methods.HttpPost
import org.apache.http.entity.{ContentType, StringEntity}

import scala.xml.{Elem, Node, XML}
import scala.xml.XML

/**
* New lines/spaces sensitive. Do not auto-format.
*/
private[serializer] class WalletHistoryReqResSerializer extends Serializer[WalletHistoryRequest, WalletHistoryResponse, HttpPost, String] {
private[serializer] class WalletHistoryReqResSerializer extends Serializer[P24Model.Request, WalletHistoryResponse, HttpPost, String] with P24SerializerLike {

private val urlTarget: String = "https://api.privatbank.ua/p24api/rest_fiz"

private def encodeMessage(message: String, hashType: String): String = {
val messageDigest = MessageDigest.getInstance(hashType)
messageDigest.update(message.getBytes())
new String(Hex.encodeHex(messageDigest.digest()))
}

private def formRequestXml(merchantXml: Elem, dataXml: Elem): Elem = {
<request version="1.0">
<xml version="1.0" encoding="UTF-8">
{merchantXml}{dataXml}
</xml>
</request>
}

private def merchantToXml(merchant: Merchant, dataProps: Seq[Node], password: String): Elem = {

val dataPropsStr: String = dataProps.map(d => unPrettyOut(d.toString()))
.filter(d => !d.trim.isEmpty).mkString("")

<merchant>
<id>{merchant.id}</id>
<signature>{encodeMessage(encodeMessage(dataPropsStr + password, "MD5"), "SHA1")}</signature>
</merchant>
}

private def walletHistoryRequestDataToXml(walletHistoryRequestData: WalletHistoryRequestData): Elem = {

def walletHistoryRequestDataPropToXml(walletHistoryRequestDataProp: WalletHistoryRequestDataProp): Elem = {
<prop name={walletHistoryRequestDataProp.name} value={walletHistoryRequestDataProp.value}></prop>
}

<data>
<oper>{walletHistoryRequestData.oper}</oper>
<wait>{walletHistoryRequestData.waitField}</wait>
<test>{walletHistoryRequestData.test}</test>
<payment id={walletHistoryRequestData.payment.idAttr}>{walletHistoryRequestData.payment.props.map(walletHistoryRequestDataPropToXml)}</payment>
</data>
}

/**
* Form request related to next p24 doc:
* https://api.privatbank.ua/#p24/orders
*
* @param walletHistoryReq the request entity.
* @return the p24 request on the xml format.
*/
override def toReq(walletHistoryReq: WalletHistoryRequest): HttpPost = {
override def toReq(req: P24Model.Request): HttpPost = {

def formHttpPostReq(payload: String): HttpPost = {
val httpPost = new HttpPost(urlTarget)
httpPost.setEntity(new StringEntity(payload, ContentType.APPLICATION_XML))
httpPost
}

val dataXml = walletHistoryRequestDataToXml(walletHistoryReq.data)
val merchantXml = merchantToXml(walletHistoryReq.merchant, dataXml.child, walletHistoryReq.merchantPassword)

val reqString = formRequestXml(merchantXml, dataXml).toString()
formHttpPostReq(unPrettyOut(reqString))
}
val reqString = formRequestXmlStr(req)

private def unPrettyOut(string: String): String = {
string.replaceAll(">\\s+<", "><")
formHttpPostReq(reqString)
}

val p24StatementDateFormatter = new SimpleDateFormat("yyyy-MM-dd")

override def fromRes(out: String): WalletHistoryResponse = {
val responseXml = XML.loadString(unPrettyOut(out))

Expand All @@ -104,7 +57,7 @@ private[serializer] class WalletHistoryReqResSerializer extends Serializer[Walle
WalletHistoryResponseStatementEntity(
(statementXml \ "@card").text,
(statementXml \ "@appcode").text,
p24StatementDateFormatter.parse((statementXml \ "@trandate").text),
p24ResponseDateFormatter.parse((statementXml \ "@trandate").text),
(statementXml \ "@amount").text,
(statementXml \ "@cardamount").text,
(statementXml \ "@rest").text,
Expand Down
@@ -1,7 +1,7 @@
package com.github.unknownnpc.psw.p24.serializer

import com.github.unknownnpc.psw.p24.model.P24Model
import com.github.unknownnpc.psw.p24.model.P24Model.Merchant
import com.github.unknownnpc.psw.p24.model.P24Model.WalletHistory._
import org.apache.http.util.EntityUtils
import org.scalatest.{FunSpec, Matchers}

Expand All @@ -20,19 +20,19 @@ class WalletHistoryReqResSerializerTest extends FunSpec with Matchers {
val fromPropVal = "01.12.1992"
val toPropName = "sd"
val toPropVal = "01.12.1992"
val request = WalletHistoryRequest(
val request = P24Model.Request(
"password",
Merchant(merchantIdVal, Some("signature")),
WalletHistoryRequestData(
P24Model.RequestData(
operVal,
waitField,
testFieldVal,
WalletHistoryRequestDataPayment(
P24Model.RequestDataPayment(
paymentId,
List(
WalletHistoryRequestDataProp(cardPropName, cardPropVal),
WalletHistoryRequestDataProp(fromPropName, fromPropVal),
WalletHistoryRequestDataProp(toPropName, toPropVal)
P24Model.RequestDataProp(cardPropName, cardPropVal),
P24Model.RequestDataProp(fromPropName, fromPropVal),
P24Model.RequestDataProp(toPropName, toPropVal)
)
)
)
Expand Down

0 comments on commit 446e20b

Please sign in to comment.