PaySaxas XS2A
PSD2 access to account — Berlin Group NextGenPSD2 v1.3.15

← All documentation

Strong customer authentication (redirect)

PaySaxas offers the redirect approach only. The embedded and decoupled approaches are not implemented and will not be — the customer authenticates on our surface, and you never see, relay or store their credentials.

The flow

sequenceDiagram
    participant T as Your service
    participant P as PaySaxas XS2A
    participant C as Customer (PSU)
    participant A as PaySaxas auth surface

    T->>P: POST /v1/consents (or /v1/payments/...)
    P-->>T: 201 + consentId/paymentId + _links.scaRedirect
    T->>C: send them to scaRedirect
    C->>A: authenticates (strong customer authentication)
    A->>C: shows exactly what is being granted
    C->>A: approves or declines
    A->>C: redirect to your TPP-Redirect-URI?state=...
    C->>T: returns with state
    T->>P: GET .../status
    P-->>T: valid | rejected | RCVD → ACSP ...

What the customer sees

For a consent: your name, the data types requested, the accounts (only those they hold), and the validity period. For a bank-offered consent the accounts appear as a list the customer ticks, with nothing ticked for them.

For a confirmation-of-funds consent: your name and the account, with the plain statement that you only ever learn yes or no, and that no balance or transactions are shared.

If the customer is signed in to a PaySaxas account that is not the one your request is for, they see “not found” and are told to sign in with the right account. Nothing about the request is shown to them.

A customer who completed full strong customer authentication recently is not asked to authenticate again for a consent (RTS Art. 10(b)). They approve, and return to you straight away.

For a payment: the amount and the payee, bound into the authentication itself — see dynamic linking below. If you sent no debtorAccount, the customer first chooses the account to pay from (nothing is chosen for them), and then sees the payee check and approves as for any payment.

Dynamic linking (payments)

A payment ceremony is cryptographically bound to this amount and this payee. The binding is computed from the persisted payment, so an authorisation cannot be replayed against different money. If the payment changes after the ceremony was created, the authorisation fails — by design.

The state parameter

Unusually, we generate state, not you. Most OAuth-shaped flows have the client mint it; here the authorisation resource is ours, so the value is created when the authorisation is created and handed to you twice — appended to the scaRedirect link, and appended again to your redirect URI when the customer returns.

   
Source Generated by PaySaxas when the authorisation is created
Format UUID v4 — 36 characters, lowercase hex with hyphens, [0-9a-f-]
Lifetime The authorisation’s own: 15 minutes, or until it is finalised, declined or expires
Reuse Never. A new authorisation always carries a new value
Where you get it The scaRedirect href, as ?state=…
Where it comes back Your TPP-Redirect-URI, as ?state=…

What to do with it. Store it against the pending authorisation on your side — keyed by consentId or paymentId — and when the customer returns, compare the value on the incoming request with the one you stored. If they differ, or you hold no pending authorisation for it, discard the callback.

That comparison is the whole point: without it, anyone who can reach your redirect URI can claim a customer just approved something. It is not proof that the approval succeeded — always confirm with GET .../status — but it ties the return to a flow you actually started.

Treat it as opaque. The format above is what we emit today; do not parse it, derive anything from it, or assume it stays a UUID.

Redirect URIs

Timing

An authorisation lives for 15 minutes from creation. After that the customer sees an expiry message, scaStatus reads failed (a consent reads rejected), and you must start again — the link is not renewable, since a stale approval prompt is exactly what a social-engineering attack wants.

Polling scaStatus

GET /v1/consents/{consentId}/authorisations/{authorisationId}
GET /v1/payments/{product}/{paymentId}/authorisations/{authorisationId}

Values: received, started, psuAuthenticated, finalised, failed.

Where authorisationId comes from

Take it from the creation response. POST /v1/consents and POST /v1/payments/{product} both return authorisationId as a top-level field, next to consentId / paymentId:

{
  "consentId": "…",
  "consentStatus": "received",
  "authorisationId": "AUTH-…",
  "_links": { "scaRedirect": { "href": "…" }, "scaStatus": { "href": "…" } }
}

If you no longer hold it, list the sub-resources:

GET /v1/consents/{consentId}/authorisations
GET /v1/payments/{product}/{paymentId}/authorisations
→ { "authorisationIds": ["AUTH-…"] }

Do not parse the id out of the scaStatus href. It appears there, but URL shape is not part of this contract and may change without a breaking-change notice; the field and the list above are the supported routes. Either follow _links.scaStatus as an opaque href, or use the id — not a substring of one.

Only the implicit authorisation start is offered — POST /v1/consents already returns scaRedirect, so there is no separate authorisation resource to create. The templated sub-resource exists for polling only.

When SCA is not repeated

Under RTS Art. 10a (as amended by (EU) 2022/2360), account data stays readable for up to 180 days under one consent, without a fresh ceremony (see consents for how the date is set). The four-per-day unattended cap still applies throughout.

Payments always require a ceremony. We do not offer TRA, low-value or trusted-beneficiary exemptions on this channel.