Hosted payment pages
A payment created in the default mode (authorisation_mode: web) comes back with a next_url. Redirect the user's browser
there, exactly as you would with GOV.UK Pay. The mock hosts the pages the user sees, so a full citizen journey works end to
end: your service, the payment pages, and back to your return_url.
The journey
- Your service creates the payment with
POST /v1/payments, including areturn_url(https). - Redirect the user to
_links.next_url.href. Alternatively submit a formPOSTto_links.next_url_post.hrefwith thechargeTokenIdfield, so the token is not in a URL. - The user sees Enter payment details: a payment summary (description and total amount) and the scenario picker.
prefilled_cardholder_detailsandemailare carried through. - If they choose Payment succeeds and select Continue, they see Confirm your payment with the mock card details. Selecting Confirm payment completes the payment.
- The user is redirected (
303) to yourreturn_url. - Your service finds the outcome with
GET /v1/payments/{paymentId}, or receives a webhook. Thereturn_urldoes not carry the result, as with the real service.
Scenarios
| User selects | User sees | Payment ends as | Events recorded |
|---|---|---|---|
| Payment succeeds, then Confirm payment | Confirm your payment, then your return_url | success, or capturable if delayed_capture is true | created, started, submitted, success (or capturable) |
| Card is declined | Your payment has been declined | failed, code P0010 | created, started, failed |
| Payment provider error | We're experiencing technical problems | error, code P0050 | created, started, error |
| Cancel payment (on either page) | Your payment has been cancelled | failed, code P0030 (cancelled by the user) | created (, started), failed |
Each error page has a Continue button that sends the user to your return_url. Cancelling through the API (POST .../cancel) is different: it gives status cancelled, code P0040.
Behaviour to know about
- The pages are public. They need no bearer token, because the user's browser is redirected to them.
- Once a payment has finished (or is
capturable), opening its page again redirects to thereturn_url. - Submitting the first page without choosing shows an error message and changes nothing.
- An unknown or mistyped link shows a "Page not found" page (
404). Payments created withmoto_apihave no hosted page; they complete throughPOST /v1/auth. - Payment data expires after 7 days, so old links stop working.
- Not modelled: real card fields, Apple Pay and Google Pay, 3D Secure, billing address entry, the 90 minute expiry, and confirmation emails.
Trying it without a browser
Create a payment, then follow the redirects by hand:
curl -s -X POST https://publicapi.payments.platform-engineering.com/v1/payments \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"amount":2500,"reference":"CAZ-1","description":"Clean air zone charge","return_url":"https://example.com/done"}'
# take _links.next_url.href from the response, then:
curl -s https://publicapi.payments.platform-engineering.com/secure/<token> # the Enter payment details page (HTML)
curl -si -X POST https://publicapi.payments.platform-engineering.com/secure/<token> -d scenario=success # 303 to .../confirm
curl -s https://publicapi.payments.platform-engineering.com/secure/<token>/confirm # the Confirm your payment page
curl -si -X POST https://publicapi.payments.platform-engineering.com/secure/<token>/confirm # 303 to your return_url
curl -s https://publicapi.payments.platform-engineering.com/v1/payments/<payment_id> -H "Authorization: Bearer $TOKEN" # state: success
Use scenario=declined, scenario=error or scenario=cancel for the other outcomes.
Routes
| Route | Purpose |
|---|---|
GET /secure/{token} | Enter payment details (the next_url) |
POST /secure/{token} | Submit the scenario (scenario = success, declined, error or cancel) |
GET /secure/{token}/confirm | Confirm your payment |
POST /secure/{token}/confirm | Confirm payment, then redirect to the return_url |
POST /secure | Start from the next_url_post form (chargeTokenId field) |