Skip to content

Commit 5176f1b

Browse files
authored
[JB][OPS-13234] payments can be auth and captured (#26)
1 parent bb015df commit 5176f1b

28 files changed

+394
-127
lines changed

app/uk/gov/hmrc/cardpaymentfrontend/connectors/CardPaymentConnector.scala

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616

1717
package uk.gov.hmrc.cardpaymentfrontend.connectors
1818

19+
import play.api.libs.json.{JsBoolean, Json}
1920
import uk.gov.hmrc.cardpaymentfrontend.config.AppConfig
20-
import uk.gov.hmrc.cardpaymentfrontend.models.cardpayment.CardPaymentInitiatePaymentResponse
21+
import uk.gov.hmrc.cardpaymentfrontend.models.cardpayment.{CardPaymentInitiatePaymentRequest, CardPaymentInitiatePaymentResponse}
2122
import uk.gov.hmrc.http.{HeaderCarrier, HttpResponse, StringContextOps}
2223
import uk.gov.hmrc.http.client.HttpClientV2
2324
import uk.gov.hmrc.http.HttpReads.Implicits._
@@ -31,15 +32,22 @@ class CardPaymentConnector @Inject() (appConfig: AppConfig, httpClientV2: HttpCl
3132

3233
private val cardPaymentBaseUrl: URL = url"""${appConfig.cardPaymentBaseUrl}"""
3334
private val initiatePaymentUrl: URL = url"$cardPaymentBaseUrl/card-payment/initiate-payment"
34-
private val authAndSettleUrl: URL = url"$cardPaymentBaseUrl/card-payment/auth-and-settle"
35+
private val checkPaymentStatusUrl: String => URL = (transactionNumber: String) => url"$cardPaymentBaseUrl/card-payment/payment-status/$transactionNumber"
36+
private val authAndSettleUrl: String => URL = (transactionNumber: String) => url"$cardPaymentBaseUrl/card-payment/auth-and-settle/$transactionNumber"
3537

36-
def initiatePayment()(implicit headerCarrier: HeaderCarrier): Future[CardPaymentInitiatePaymentResponse] =
38+
def initiatePayment(cardPaymentInitiatePaymentRequest: CardPaymentInitiatePaymentRequest)(implicit headerCarrier: HeaderCarrier): Future[CardPaymentInitiatePaymentResponse] =
3739
httpClientV2
3840
.post(initiatePaymentUrl)
41+
.withBody(Json.toJson(cardPaymentInitiatePaymentRequest))
3942
.execute[CardPaymentInitiatePaymentResponse]
4043

41-
def authAndSettle()(implicit headerCarrier: HeaderCarrier): Future[HttpResponse] =
44+
def checkPaymentStatus(transactionNumber: String)(implicit headerCarrier: HeaderCarrier): Future[JsBoolean] =
4245
httpClientV2
43-
.post(authAndSettleUrl)
46+
.get(checkPaymentStatusUrl(transactionNumber))
47+
.execute[JsBoolean]
48+
49+
def authAndSettle(transactionNumber: String)(implicit headerCarrier: HeaderCarrier): Future[HttpResponse] =
50+
httpClientV2
51+
.post(authAndSettleUrl(transactionNumber))
4452
.execute[HttpResponse]
4553
}

app/uk/gov/hmrc/cardpaymentfrontend/connectors/PayApiConnector.scala

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ package uk.gov.hmrc.cardpaymentfrontend.connectors
1818

1919
import com.google.inject.Inject
2020
import payapi.cardpaymentjourney.model.journey.{Journey, JourneySpecificData}
21+
import play.api.libs.json.Json
2122
import uk.gov.hmrc.cardpaymentfrontend.config.AppConfig
23+
import uk.gov.hmrc.cardpaymentfrontend.models.payapi.{BeginWebPaymentRequest, FailWebPaymentRequest, SucceedWebPaymentRequest}
2224
import uk.gov.hmrc.http.HeaderCarrier
2325
import uk.gov.hmrc.http.StringContextOps
2426
import uk.gov.hmrc.http.client.HttpClientV2
@@ -33,11 +35,37 @@ class PayApiConnector @Inject() (appConfig: AppConfig, httpClientV2: HttpClientV
3335

3436
private val findBySessionIdUrl: URL = url"""${appConfig.payApiBaseUrl}/pay-api/journey/find-latest-by-session-id"""
3537

36-
def findLatestJourneyBySessionId()(implicit hc: HeaderCarrier): Future[Option[Journey[JourneySpecificData]]] = {
38+
def findLatestJourneyBySessionId()(implicit headerCarrier: HeaderCarrier): Future[Option[Journey[JourneySpecificData]]] = {
3739
for {
38-
_ <- Future(require(hc.sessionId.isDefined, "Missing required 'SessionId'"))
40+
_ <- Future(require(headerCarrier.sessionId.isDefined, "Missing required 'SessionId'"))
3941
maybeJourneyResult <- httpClientV2.get(findBySessionIdUrl).execute[Option[Journey[JourneySpecificData]]]
4042
} yield maybeJourneyResult
4143
}
4244

45+
object JourneyUpdates {
46+
47+
def updateBeginWebPayment(journeyId: String, beginWebPaymentRequest: BeginWebPaymentRequest)(implicit headerCarrier: HeaderCarrier): Future[Unit] =
48+
httpClientV2
49+
.put(url"""${appConfig.payApiBaseUrl}/pay-api/journey/$journeyId/update/begin-web-payment""")
50+
.withBody(Json.toJson(beginWebPaymentRequest))
51+
.execute[Unit]
52+
53+
def updateSucceedWebPayment(journeyId: String, succeedWebPaymentRequest: SucceedWebPaymentRequest)(implicit headerCarrier: HeaderCarrier): Future[Unit] =
54+
httpClientV2
55+
.put(url"""${appConfig.payApiBaseUrl}/pay-api/journey/$journeyId/update/succeed-web-payment""")
56+
.withBody(Json.toJson(succeedWebPaymentRequest))
57+
.execute[Unit]
58+
59+
def updateCancelWebPayment(journeyId: String)(implicit headerCarrier: HeaderCarrier): Future[Unit] =
60+
httpClientV2
61+
.put(url"""${appConfig.payApiBaseUrl}/pay-api/journey/$journeyId/update/cancel-web-payment""")
62+
.execute[Unit]
63+
64+
def updateFailWebPayment(journeyId: String, failWebPaymentRequest: FailWebPaymentRequest)(implicit headerCarrier: HeaderCarrier): Future[Unit] =
65+
httpClientV2
66+
.put(url"""${appConfig.payApiBaseUrl}/pay-api/journey/$journeyId/update/fail-web-payment""")
67+
.withBody(Json.toJson(failWebPaymentRequest))
68+
.execute[Unit]
69+
}
70+
4371
}

app/uk/gov/hmrc/cardpaymentfrontend/controllers/CheckYourAnswersController.scala

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@ package uk.gov.hmrc.cardpaymentfrontend.controllers
1818

1919
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents}
2020
import uk.gov.hmrc.cardpaymentfrontend.actions.{Actions, JourneyRequest}
21-
import uk.gov.hmrc.cardpaymentfrontend.connectors.CardPaymentConnector
22-
import uk.gov.hmrc.cardpaymentfrontend.models.CheckYourAnswersRow
21+
import uk.gov.hmrc.cardpaymentfrontend.models.{Address, CheckYourAnswersRow, EmailAddress}
2322
import uk.gov.hmrc.cardpaymentfrontend.models.CheckYourAnswersRow.summarise
2423
import uk.gov.hmrc.cardpaymentfrontend.models.extendedorigins.ExtendedOrigin
2524
import uk.gov.hmrc.cardpaymentfrontend.models.extendedorigins.ExtendedOrigin.OriginExtended
2625
import uk.gov.hmrc.cardpaymentfrontend.requests.RequestSupport
26+
import uk.gov.hmrc.cardpaymentfrontend.services.CardPaymentService
27+
import uk.gov.hmrc.cardpaymentfrontend.session.JourneySessionSupport._
2728
import uk.gov.hmrc.cardpaymentfrontend.views.html.CheckYourAnswersPage
2829
import uk.gov.hmrc.govukfrontend.views.Aliases.SummaryList
2930
import uk.gov.hmrc.govukfrontend.views.viewmodels.summarylist.SummaryListRow
@@ -36,7 +37,7 @@ import scala.concurrent.ExecutionContext
3637
@Singleton()
3738
class CheckYourAnswersController @Inject() (
3839
actions: Actions,
39-
cardPaymentConnector: CardPaymentConnector, //todo introduce a service layer maybe
40+
cardPaymentService: CardPaymentService,
4041
checkYourAnswersPage: CheckYourAnswersPage,
4142
mcc: MessagesControllerComponents,
4243
requestSupport: RequestSupport
@@ -65,9 +66,13 @@ class CheckYourAnswersController @Inject() (
6566
Ok(checkYourAnswersPage(SummaryList(summaryListRows)))
6667
}
6768

68-
def submit: Action[AnyContent] = actions.journeyAction.async { implicit request: JourneyRequest[AnyContent] =>
69-
cardPaymentConnector
70-
.initiatePayment()(requestSupport.hc)
69+
def submit: Action[AnyContent] = actions.journeyAction.async { implicit journeyRequest: JourneyRequest[AnyContent] =>
70+
cardPaymentService
71+
.initiatePayment(
72+
journey = journeyRequest.journey,
73+
addressFromSession = journeyRequest.readFromSession[Address](journeyRequest.journeyId, Keys.address).getOrElse(throw new RuntimeException("We can't process a card payment without the billing address.")),
74+
maybeEmailFromSession = journeyRequest.readFromSession[EmailAddress](journeyRequest.journeyId, Keys.email)
75+
)(requestSupport.hc, journeyRequest.request)
7176
.map { cardPaymentInitiatePaymentResponse =>
7277
Redirect(routes.PaymentStatusController.showIframe(RedirectUrl(cardPaymentInitiatePaymentResponse.redirectUrl)))
7378
}

app/uk/gov/hmrc/cardpaymentfrontend/controllers/PaymentStatusController.scala

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,24 +20,27 @@ import play.api.mvc.{Action, AnyContent, MessagesControllerComponents, Result}
2020
import uk.gov.hmrc.cardpaymentfrontend.actions.Actions
2121
import uk.gov.hmrc.cardpaymentfrontend.config.AppConfig
2222
import uk.gov.hmrc.cardpaymentfrontend.requests.RequestSupport
23+
import uk.gov.hmrc.cardpaymentfrontend.services.CardPaymentService
2324
import uk.gov.hmrc.cardpaymentfrontend.views.html.iframe.{IframeContainerPage, RedirectToParentPage}
25+
import uk.gov.hmrc.http.{HeaderCarrier, HttpResponse}
2426
import uk.gov.hmrc.play.bootstrap.binders.RedirectUrl.idFunctor
2527
import uk.gov.hmrc.play.bootstrap.binders.RedirectUrlPolicy.Id
2628
import uk.gov.hmrc.play.bootstrap.binders.{AbsoluteWithHostnameFromAllowlist, RedirectUrl, RedirectUrlPolicy}
2729
import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendController
2830

2931
import javax.inject.{Inject, Singleton}
30-
import scala.concurrent.Future
32+
import scala.concurrent.{ExecutionContext, Future}
3133

3234
@Singleton()
3335
class PaymentStatusController @Inject() (
34-
actions: Actions,
35-
appConfig: AppConfig,
36-
mcc: MessagesControllerComponents,
37-
requestSupport: RequestSupport,
38-
iframeContainer: IframeContainerPage,
39-
redirectToParent: RedirectToParentPage
40-
) extends FrontendController(mcc) {
36+
actions: Actions,
37+
appConfig: AppConfig,
38+
cardPaymentService: CardPaymentService,
39+
mcc: MessagesControllerComponents,
40+
requestSupport: RequestSupport,
41+
iframeContainer: IframeContainerPage,
42+
redirectToParent: RedirectToParentPage
43+
)(implicit executionContext: ExecutionContext) extends FrontendController(mcc) {
4144

4245
import requestSupport._
4346

@@ -48,7 +51,7 @@ class PaymentStatusController @Inject() (
4851
iframeUrl
4952
.getEither[Id](redirectUrlPolicy)
5053
.fold[Result](
51-
_ => BadRequest("Bad url"),
54+
_ => BadRequest("Bad url provided that doesn't match the redirect policy. Check allow list if this is not expected."),
5255
safeUrl => Ok(iframeContainer(safeUrl.url))
5356
)
5457
}
@@ -58,8 +61,28 @@ class PaymentStatusController @Inject() (
5861
}
5962

6063
//todo append something to the return url so we can extract/work out the session/journey - are we allowed to do this or do we use session?
61-
def paymentStatus(transactionReference: String): Action[AnyContent] = actions.default.async { _ =>
62-
Future.successful(Ok(s"We're back from the iframe!\nNow we need to just do the check payment status bit and auth and settle. ${transactionReference}"))
64+
def paymentStatus(traceId: String): Action[AnyContent] = actions.journeyAction.async { implicit journeyRequest =>
65+
implicit val hc: HeaderCarrier = requestSupport.hc
66+
val transactionRefFromJourney: Option[String] = journeyRequest.journey.order.map(_.transactionReference.value)
67+
for {
68+
paymentStatus <- cardPaymentService.checkPaymentStatus(transactionRefFromJourney.getOrElse(throw new RuntimeException("Could not find transaction ref, pay-api probably didn't update.")))
69+
authAndCaptureResult <- maybeAuthAndSettleResult(transactionRefFromJourney, paymentStatus.value)
70+
} yield Ok(
71+
s"""We're back from the iframe!\n""" +
72+
s"""traceId: $traceId\n""" +
73+
s"""we now need to update the journey in pay-api with the completed payment info\n""" +
74+
s"""CheckPaymentStatus response:\n${paymentStatus.value.toString}\n\n""" +
75+
s"""authAndSettle response:\n${authAndCaptureResult.body}\n\n"""
76+
)
77+
}
78+
79+
//todo in future, lets check the result json and redirect accordingly. (i.e. if it's failed etc)
80+
private def maybeAuthAndSettleResult(transactionRefFromJourney: Option[String], shouldAuthAndSettle: Boolean)(implicit headerCarrier: HeaderCarrier): Future[HttpResponse] = {
81+
if (shouldAuthAndSettle) {
82+
cardPaymentService.authAndSettle(transactionRefFromJourney.getOrElse(throw new RuntimeException("Could not find transaction ref, therefore we can't auth and settle.")))
83+
} else {
84+
Future.successful(HttpResponse.apply(500, "Cannot auth and capture when status is not acceptable. We should redirect accordingly."))
85+
}
6386
}
6487

6588
}

app/uk/gov/hmrc/cardpaymentfrontend/forms/AddressForm.scala

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ object AddressForm {
4646
"county" -> optional(text.transform[String](_.trim, identity)
4747
.verifying(maxLength(60))
4848
.verifying(emojiConstraint("county", "address.field-name.error.invalid.char"))),
49-
"postcode" -> of(postcodeFormatter),
50-
"country" -> text.verifying(countryConstraint)
49+
"postCode" -> of(postCodeFormatter),
50+
"countryCode" -> text.verifying(countryConstraint)
5151
)(Address.apply)(Address.unapply)
5252
)
5353

@@ -66,20 +66,20 @@ object AddressForm {
6666

6767
}
6868

69-
def countryConstraint: Constraint[String] = Constraint[String]("constraint.country") { o =>
70-
if (o.isBlank) Invalid(ValidationError("address.field-name.error.required.country")) else if (o.trim.isEmpty) Invalid(ValidationError("address.field-name.error.required.country")) else Valid
69+
def countryConstraint: Constraint[String] = Constraint[String]("constraint.countryCode") { o =>
70+
if (o.isBlank) Invalid(ValidationError("address.field-name.error.required.countryCode")) else if (o.trim.isEmpty) Invalid(ValidationError("address.field-name.error.required.countryCode")) else Valid
7171
}
7272

73-
val postcodeFormatter: Formatter[String] = new Formatter[String] {
73+
val postCodeFormatter: Formatter[String] = new Formatter[String] {
7474

7575
override def bind(key: String, data: Map[String, String]): Either[Seq[FormError], String] = {
7676
// for accessibility, we need to allow users to enter spaces anywhere in postcode, we strip them to assert the postcode matches the regex, then use what the user entered.
77-
val postCode: String = data("postcode").filterNot(_.isWhitespace)
78-
val selectedCountryIsGBR: Boolean = data("country").matches("GBR")
77+
val postCode: String = data("postCode").filterNot(_.isWhitespace)
78+
val selectedCountryIsGBR: Boolean = data("countryCode").matches("GBR")
7979
if (selectedCountryIsGBR && postCode.isEmpty)
80-
Left(Seq(FormError("postcode", "address.field-name.error.empty.postcode")))
80+
Left(Seq(FormError("postCode", "address.field-name.error.empty.postCode")))
8181
else if (selectedCountryIsGBR && !postCode.matches(ukPostcodeRegex.regex))
82-
Left(Seq(FormError("postcode", "address.field-name.error.invalid.postcode")))
82+
Left(Seq(FormError("postCode", "address.field-name.error.invalid.postCode")))
8383
else
8484
Right(postCode)
8585
}

app/uk/gov/hmrc/cardpaymentfrontend/models/Address.scala

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ package uk.gov.hmrc.cardpaymentfrontend.models
1919
import play.api.libs.json.{Json, OFormat}
2020

2121
final case class Address(
22-
line1: String,
23-
line2: Option[String] = None,
24-
city: Option[String] = None,
25-
county: Option[String] = None,
26-
postcode: String,
27-
country: String
22+
line1: String,
23+
line2: Option[String] = None,
24+
city: Option[String] = None,
25+
county: Option[String] = None,
26+
postCode: String,
27+
countryCode: String
2828
) {
2929
def hasSelect(maybeString: Option[String]): Boolean = {
3030
maybeString match {
@@ -37,7 +37,7 @@ final case class Address(
3737

3838
// For UK (GBR) addresses only, replace counties containing variations on Select with None.
3939
def sanitiseCounty(): Address =
40-
if (country.matches("GBR") && hasSelect(this.county)) this.copy (county = None)
40+
if (countryCode.matches("GBR") && hasSelect(this.county)) this.copy (county = None)
4141
else this
4242
}
4343

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2025 HM Revenue & Customs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package uk.gov.hmrc.cardpaymentfrontend.models.cardpayment
18+
19+
import payapi.corcommon.model.AmountInPence
20+
import play.api.libs.json.{Format, Json}
21+
import uk.gov.hmrc.cardpaymentfrontend.models.{Address, EmailAddress}
22+
23+
final case class CardPaymentInitiatePaymentRequest(
24+
redirectUrl: String,
25+
clientId: String, //or merchant id, i.e. SAEE etc
26+
purchaseDescription: String, // i.e. tax reference
27+
purchaseAmount: AmountInPence, //in pennies
28+
billingAddress: Address,
29+
emailAddress: Option[EmailAddress]
30+
)
31+
32+
object CardPaymentInitiatePaymentRequest {
33+
@SuppressWarnings(Array("org.wartremover.warts.Any"))
34+
implicit val format: Format[CardPaymentInitiatePaymentRequest] = Json.format[CardPaymentInitiatePaymentRequest]
35+
}

app/uk/gov/hmrc/cardpaymentfrontend/models/extendedorigins/ExtendedBtaSa.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ object ExtendedBtaSa extends ExtendedOrigin {
8484
CheckYourAnswersRow(
8585
"btasa.address.title",
8686
maybeAddress match {
87-
case Some(addr) => Seq(addr.line1, addr.line2.getOrElse(""), addr.city.getOrElse(""), addr.county.getOrElse(""), addr.postcode, addr.country).filter(_.nonEmpty)
87+
case Some(addr) => Seq(addr.line1, addr.line2.getOrElse(""), addr.city.getOrElse(""), addr.county.getOrElse(""), addr.postCode, addr.countryCode).filter(_.nonEmpty)
8888
case None => Seq.empty
8989
},
9090
Some(Link(

app/uk/gov/hmrc/cardpaymentfrontend/models/extendedorigins/ExtendedItSa.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ object ExtendedItSa extends ExtendedOrigin {
8585
val addressRow = CheckYourAnswersRow(
8686
"itsa.address.title",
8787
maybeAddress match {
88-
case Some(addr) => Seq(addr.line1, addr.line2.getOrElse(""), addr.city.getOrElse(""), addr.county.getOrElse(""), addr.postcode, addr.country).filter(_.nonEmpty)
88+
case Some(addr) => Seq(addr.line1, addr.line2.getOrElse(""), addr.city.getOrElse(""), addr.county.getOrElse(""), addr.postCode, addr.countryCode).filter(_.nonEmpty)
8989
case None => Seq.empty
9090
},
9191
Some(Link(

app/uk/gov/hmrc/cardpaymentfrontend/models/extendedorigins/ExtendedOrigin.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ trait ExtendedOrigin {
105105
addressFromSession.line2.getOrElse(""),
106106
addressFromSession.city.getOrElse(""),
107107
addressFromSession.county.getOrElse(""),
108-
addressFromSession.postcode
108+
addressFromSession.postCode
109109
).filter(_.nonEmpty)
110110

111111
Some(CheckYourAnswersRow(

app/uk/gov/hmrc/cardpaymentfrontend/models/extendedorigins/ExtendedPfSa.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ object ExtendedPfSa extends ExtendedOrigin {
6262
val addressRow = CheckYourAnswersRow(
6363
titleMessageKey = "PfSa.address.title",
6464
value = maybeAddress match {
65-
case Some(addr) => Seq(addr.line1, addr.line2.getOrElse(""), addr.city.getOrElse(""), addr.county.getOrElse(""), addr.postcode, addr.country).filter(_.nonEmpty)
65+
//todo, we're doing the same thing over and over, lets write this once and reuse.
66+
case Some(addr) => Seq(addr.line1, addr.line2.getOrElse(""), addr.city.getOrElse(""), addr.county.getOrElse(""), addr.postCode, addr.countryCode).filter(_.nonEmpty)
6667
case None => Seq.empty
6768
},
6869
changeLink = Some(Link(

app/uk/gov/hmrc/cardpaymentfrontend/models/extendedorigins/ExtendedPtaSa.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ object ExtendedPtaSa extends ExtendedOrigin {
6868
val addressRow = CheckYourAnswersRow(
6969
"ptasa.address.title",
7070
maybeAddress match {
71-
case Some(addr) => Seq(addr.line1, addr.line2.getOrElse(""), addr.city.getOrElse(""), addr.county.getOrElse(""), addr.postcode, addr.country).filter(_.nonEmpty)
71+
case Some(addr) => Seq(addr.line1, addr.line2.getOrElse(""), addr.city.getOrElse(""), addr.county.getOrElse(""), addr.postCode, addr.countryCode).filter(_.nonEmpty)
7272
case None => Seq.empty
7373
},
7474
Some(Link(
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright 2025 HM Revenue & Customs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package uk.gov.hmrc.cardpaymentfrontend.models.payapi
18+
19+
import play.api.libs.json.{Json, OFormat}
20+
21+
//todo should we use types from cor? I'd rather not to stop being tied to it.
22+
final case class BeginWebPaymentRequest(
23+
transactionReference: String,
24+
iFrameUrl: String
25+
)
26+
27+
object BeginWebPaymentRequest {
28+
@SuppressWarnings(Array("org.wartremover.warts.Any"))
29+
implicit val formats: OFormat[BeginWebPaymentRequest] = Json.format
30+
}

0 commit comments

Comments
 (0)