Skip to content

Commit bb015df

Browse files
authored
[JB][OPS-13233] initial beginning of Barclaycard rest implementation (#25)
1 parent d212eae commit bb015df

22 files changed

+306
-43
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
document.getElementById('returnControlLink').click();
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#barclaycard-iframe {
2+
height: 130em;
3+
width: 100%;
4+
border: none;
5+
margin-top: 8px;
6+
}

app/uk/gov/hmrc/cardpaymentfrontend/actions/JourneyRequest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023 HM Revenue & Customs
2+
* Copyright 2024 HM Revenue & Customs
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

app/uk/gov/hmrc/cardpaymentfrontend/config/AppConfig.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class AppConfig @Inject() (config: Configuration, servicesConfig: ServicesConfig
2525

2626
val payAnotherWayLink: String = config.get[String]("urls.govuk.pay-another-way")
2727

28+
val cardPaymentBaseUrl: String = servicesConfig.baseUrl("card-payment")
2829
val payApiBaseUrl: String = servicesConfig.baseUrl("pay-api")
2930
val openBankingBaseUrl: String = servicesConfig.baseUrl("open-banking")
3031
val paymentsSurveyBaseUrl: String = servicesConfig.baseUrl("payments-survey")
@@ -38,4 +39,6 @@ class AppConfig @Inject() (config: Configuration, servicesConfig: ServicesConfig
3839
val vatOssUrl: String = s"${config.get[String]("urls.vatOssBaseUrl")}/pay-vat-on-goods-sold-to-eu/northern-ireland-returns-payments/your-account"
3940
val vatIossUrl: String = s"${config.get[String]("urls.vatIossBaseUrl")}/pay-vat-on-goods-sold-to-eu/import-one-stop-shop-returns-payments/your-account"
4041

42+
val iframeHostNameAllowList: Set[String] = config.get[Seq[String]]("iframeHostNameAllowList").toSet
43+
4144
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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.connectors
18+
19+
import uk.gov.hmrc.cardpaymentfrontend.config.AppConfig
20+
import uk.gov.hmrc.cardpaymentfrontend.models.cardpayment.CardPaymentInitiatePaymentResponse
21+
import uk.gov.hmrc.http.{HeaderCarrier, HttpResponse, StringContextOps}
22+
import uk.gov.hmrc.http.client.HttpClientV2
23+
import uk.gov.hmrc.http.HttpReads.Implicits._
24+
25+
import java.net.URL
26+
import javax.inject.{Inject, Singleton}
27+
import scala.concurrent.{ExecutionContext, Future}
28+
29+
@Singleton
30+
class CardPaymentConnector @Inject() (appConfig: AppConfig, httpClientV2: HttpClientV2)(implicit executionContext: ExecutionContext) {
31+
32+
private val cardPaymentBaseUrl: URL = url"""${appConfig.cardPaymentBaseUrl}"""
33+
private val initiatePaymentUrl: URL = url"$cardPaymentBaseUrl/card-payment/initiate-payment"
34+
private val authAndSettleUrl: URL = url"$cardPaymentBaseUrl/card-payment/auth-and-settle"
35+
36+
def initiatePayment()(implicit headerCarrier: HeaderCarrier): Future[CardPaymentInitiatePaymentResponse] =
37+
httpClientV2
38+
.post(initiatePaymentUrl)
39+
.execute[CardPaymentInitiatePaymentResponse]
40+
41+
def authAndSettle()(implicit headerCarrier: HeaderCarrier): Future[HttpResponse] =
42+
httpClientV2
43+
.post(authAndSettleUrl)
44+
.execute[HttpResponse]
45+
}

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

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

1919
import play.api.data.Form
2020
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents}
21+
import uk.gov.hmrc.cardpaymentfrontend.actions.{Actions, JourneyRequest}
2122
import uk.gov.hmrc.cardpaymentfrontend.forms.AddressForm
2223
import uk.gov.hmrc.cardpaymentfrontend.models.Address
24+
import uk.gov.hmrc.cardpaymentfrontend.requests.RequestSupport
2325
import uk.gov.hmrc.cardpaymentfrontend.services.CountriesService
26+
import uk.gov.hmrc.cardpaymentfrontend.session.JourneySessionSupport._
2427
import uk.gov.hmrc.cardpaymentfrontend.views.html.AddressPage
2528
import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendController
2629

2730
import javax.inject.{Inject, Singleton}
2831

2932
@Singleton
3033
class AddressController @Inject() (
31-
mcc: MessagesControllerComponents,
34+
actions: Actions,
3235
addressPage: AddressPage,
33-
countriesService: CountriesService
36+
countriesService: CountriesService,
37+
mcc: MessagesControllerComponents,
38+
requestSupport: RequestSupport
3439
) extends FrontendController(mcc) {
3540

36-
val renderPage: Action[AnyContent] = Action { implicit request =>
41+
import requestSupport._
42+
43+
val renderPage: Action[AnyContent] = actions.journeyAction { implicit request: JourneyRequest[AnyContent] =>
3744
Ok(addressPage(AddressForm.form(), countriesService.getCountries))
3845
}
3946

40-
val submit: Action[AnyContent] = Action { implicit request =>
47+
val submit: Action[AnyContent] = actions.journeyAction { implicit journeyRequest: JourneyRequest[AnyContent] =>
4148
AddressForm.form()
4249
.bindFromRequest()
4350
.fold(
4451
(formWithErrors: Form[Address]) => BadRequest(addressPage(form = formWithErrors, countriesService.getCountries)),
45-
{ _ =>
46-
Ok("Happy with the address entered")
52+
{ address =>
53+
Redirect(routes.CheckYourAnswersController.renderPage)
54+
.placeInSession(journeyRequest.journeyId, Keys.address -> address)
4755
}
4856
)
4957
}

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ 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
2122
import uk.gov.hmrc.cardpaymentfrontend.models.CheckYourAnswersRow
2223
import uk.gov.hmrc.cardpaymentfrontend.models.CheckYourAnswersRow.summarise
2324
import uk.gov.hmrc.cardpaymentfrontend.models.extendedorigins.ExtendedOrigin
@@ -26,17 +27,20 @@ import uk.gov.hmrc.cardpaymentfrontend.requests.RequestSupport
2627
import uk.gov.hmrc.cardpaymentfrontend.views.html.CheckYourAnswersPage
2728
import uk.gov.hmrc.govukfrontend.views.Aliases.SummaryList
2829
import uk.gov.hmrc.govukfrontend.views.viewmodels.summarylist.SummaryListRow
30+
import uk.gov.hmrc.play.bootstrap.binders.RedirectUrl
2931
import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendController
3032

3133
import javax.inject.{Inject, Singleton}
34+
import scala.concurrent.ExecutionContext
3235

3336
@Singleton()
3437
class CheckYourAnswersController @Inject() (
3538
actions: Actions,
36-
mcc: MessagesControllerComponents,
39+
cardPaymentConnector: CardPaymentConnector, //todo introduce a service layer maybe
3740
checkYourAnswersPage: CheckYourAnswersPage,
41+
mcc: MessagesControllerComponents,
3842
requestSupport: RequestSupport
39-
) extends FrontendController(mcc) {
43+
)(implicit executionContext: ExecutionContext) extends FrontendController(mcc) {
4044
import requestSupport._
4145

4246
def renderPage: Action[AnyContent] = actions.journeyAction { implicit journeyRequest: JourneyRequest[AnyContent] =>
@@ -61,8 +65,12 @@ class CheckYourAnswersController @Inject() (
6165
Ok(checkYourAnswersPage(SummaryList(summaryListRows)))
6266
}
6367

64-
def submit: Action[AnyContent] = actions.journeyAction { _ =>
65-
Ok("nice, you submitted the check your details page, this is where the iframe needs to go")
68+
def submit: Action[AnyContent] = actions.journeyAction.async { implicit request: JourneyRequest[AnyContent] =>
69+
cardPaymentConnector
70+
.initiatePayment()(requestSupport.hc)
71+
.map { cardPaymentInitiatePaymentResponse =>
72+
Redirect(routes.PaymentStatusController.showIframe(RedirectUrl(cardPaymentInitiatePaymentResponse.redirectUrl)))
73+
}
6674
}
6775

6876
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,14 @@ class EmailAddressController @Inject() (
4343
}
4444

4545
val submit: Action[AnyContent] = actions.journeyAction { implicit journeyRequest: JourneyRequest[AnyContent] =>
46-
4746
EmailAddressForm.form()
4847
.bindFromRequest()
4948
.fold(
5049
(formWithErrors: Form[EmailAddress]) => BadRequest(emailAddressPage(form = formWithErrors)),
5150
{ email =>
52-
Ok("Happy with the email entered").placeInSession(journeyRequest.journeyId, "email" -> email)
51+
Redirect(routes.AddressController.renderPage)
52+
.placeInSession(journeyRequest.journeyId, Keys.email -> email)
53+
5354
}
5455
)
5556
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2024 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.controllers
18+
19+
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents, Result}
20+
import uk.gov.hmrc.cardpaymentfrontend.actions.Actions
21+
import uk.gov.hmrc.cardpaymentfrontend.config.AppConfig
22+
import uk.gov.hmrc.cardpaymentfrontend.requests.RequestSupport
23+
import uk.gov.hmrc.cardpaymentfrontend.views.html.iframe.{IframeContainerPage, RedirectToParentPage}
24+
import uk.gov.hmrc.play.bootstrap.binders.RedirectUrl.idFunctor
25+
import uk.gov.hmrc.play.bootstrap.binders.RedirectUrlPolicy.Id
26+
import uk.gov.hmrc.play.bootstrap.binders.{AbsoluteWithHostnameFromAllowlist, RedirectUrl, RedirectUrlPolicy}
27+
import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendController
28+
29+
import javax.inject.{Inject, Singleton}
30+
import scala.concurrent.Future
31+
32+
@Singleton()
33+
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) {
41+
42+
import requestSupport._
43+
44+
private val redirectUrlPolicy: RedirectUrlPolicy[Id] = AbsoluteWithHostnameFromAllowlist(appConfig.iframeHostNameAllowList)
45+
46+
//todo need to write a test for this, where we override the allow list or something to trigger bad request.
47+
def showIframe(iframeUrl: RedirectUrl): Action[AnyContent] = Action { implicit req =>
48+
iframeUrl
49+
.getEither[Id](redirectUrlPolicy)
50+
.fold[Result](
51+
_ => BadRequest("Bad url"),
52+
safeUrl => Ok(iframeContainer(safeUrl.url))
53+
)
54+
}
55+
56+
def returnToHmrc(transactionReference: String): Action[AnyContent] = actions.default { implicit request =>
57+
Ok(redirectToParent(transactionReference))
58+
}
59+
60+
//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}"))
63+
}
64+
65+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ object AddressForm {
3434
def form(): Form[Address] = Form(
3535
mapping(
3636

37-
"line1" -> text.transform[String](_.trim, identity).verifying("address.field-name.error.invalid.line1", s => s.length > 0)
37+
"line1" -> text.transform[String](_.trim, identity).verifying("address.field-name.error.invalid.line1", s => s.nonEmpty)
3838
.verifying(maxLength(50))
3939
.verifying(emojiConstraint("line1", "address.field-name.error.invalid.char")),
4040
"line2" -> optional(text.transform[String](_.trim, identity)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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 play.api.libs.json.{Format, Json}
20+
21+
final case class CardPaymentInitiatePaymentResponse(redirectUrl: String, transactionReference: String)
22+
23+
object CardPaymentInitiatePaymentResponse {
24+
@SuppressWarnings(Array("org.wartremover.warts.Any"))
25+
implicit val format: Format[CardPaymentInitiatePaymentResponse] = Json.format[CardPaymentInitiatePaymentResponse]
26+
}

app/uk/gov/hmrc/cardpaymentfrontend/session/JourneySessionSupport.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ trait JourneySessionSupport {
3333
object Keys {
3434
val address = "address"
3535
val email = "email"
36+
val transactionReference = "transactionReference"
3637
}
3738

3839
implicit class ResultOps(r: Result)(implicit request: RequestHeader) {

app/uk/gov/hmrc/cardpaymentfrontend/views/AddressPage.scala.html

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,21 @@
2626
@import uk.gov.hmrc.cardpaymentfrontend.forms.AddressForm
2727

2828
@this(
29-
errorSummary: GovukErrorSummary,
30-
govukInput: GovukInput,
31-
govukButton: GovukButton,
32-
govukSelect: GovukSelect,
33-
layout: Layout,
34-
formWithCSRF: FormWithCSRF
29+
errorSummary: GovukErrorSummary,
30+
govukInput: GovukInput,
31+
govukButton: GovukButton,
32+
govukSelect: GovukSelect,
33+
layout: Layout,
34+
formWithCSRF: FormWithCSRF
3535
)
3636

3737
@(
38-
form: Form[Address],
39-
countries: Seq[Country]
38+
form: Form[Address],
39+
countries: Seq[Country]
4040
)(implicit request: RequestHeader, messages: Messages)
4141

4242
@heading= @{messages("address.heading")}
4343
@key = @{AddressForm.addressKey}
44-
@hint1: String = @{messages("address.line1.hint")}
45-
@hint2: String = @{messages("address.line2.hint")}
4644

4745
@layout(pageTitle = Some(heading)) {
4846

@@ -56,7 +54,7 @@
5654
<legend class="govuk-fieldset__legend govuk-fieldset__legend--l">
5755
<h1 class="govuk-fieldset__heading">@heading</h1>
5856
</legend>
59-
<p class="govuk-hint govuk-!-margin-bottom-2" id="fieldset-hint" > @hint1 <span> @hint2 </span></p>
57+
<p class="govuk-hint govuk-!-margin-bottom-2" id="fieldset-hint"> @{messages("address.line1.hint")} <span> @{messages("address.line2.hint")} </span></p>
6058
</fieldset>
6159

6260
@formWithCSRF(action = uk.gov.hmrc.cardpaymentfrontend.controllers.routes.AddressController.submit) {
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
@*
2+
* Copyright 2024 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+
@import uk.gov.hmrc.cardpaymentfrontend.views.html.Layout
18+
19+
20+
@this(layout: Layout)
21+
22+
@(iframeUrl: String)(implicit request: RequestHeader, messages: Messages)
23+
@* TODO: Jake, update the title *@
24+
@layout(pageTitle = Some("IFrame holder page")) {
25+
@Html(s"""<link rel="stylesheet" media="all" type="text/css" href="${controllers.routes.Assets.versioned("stylesheets/barclaycard-iframe.css").toString}">""")
26+
<h1 class="govuk-heading-xl">IFrame holder page</h1>
27+
28+
<iframe title="I am the Barclaycard iframe"
29+
src="@{iframeUrl}"
30+
id="barclaycard-iframe">
31+
</iframe>
32+
}

0 commit comments

Comments
 (0)