feat: Open Payments aggregator mock for acctest
Tiny in-memory stand-in for the Open Payments PSD2/BerlinGroup
aggregator, extracted from acctest where it lived inline (which
couldn't docker-build on the self-hosted CI runner — /.docker/buildx
was read-only). A separate repo:
- lets the mock build + publish its image via the standard shiny CI
flow, pulled like any other service
- keeps acctest focused on test infra, not service source
- gives the mock its own versioned release lifecycle
**Endpoints** (see README for the full list)
- OAuth: POST /token → canned bearer token.
- AIS: aspsps catalog, consent lifecycle, /authorize self-redirect SCA
stub, payment + card accounts + transactions.
- PIS: sepa-credit-transfers initiate + status (RCVD → PDNG → ACSC;
creditor name 'REJECT ME' → RJCT), signing baskets (RCVD → ACCP →
ACSC; basket ACSC advances all linked payments).
- Admin: /admin/* endpoints let acctest force deterministic
transitions, seed transactions, reset state, override consent
expiry.
**CI**
Standard shiny ci.yaml: check (go build + vet), vulnerabilities
(govulncheck), build (buildtools publishes oci.unbound.se/shiny/
openpayments-mock:${COMMIT}). No deploy job — image is consumed by
acctest only.
**k8s/deploy.yaml**
Deployment + Service on port 8080 with /healthz readiness/liveness.
acctest's infra manifest will reference the published tag.
This commit is contained in:
@@ -0,0 +1,69 @@
|
|||||||
|
name: openpayments-mock
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
if: gitea.event_name == 'pull_request'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/setup-go@v6
|
||||||
|
with:
|
||||||
|
go-version: 'stable'
|
||||||
|
- name: Build
|
||||||
|
run: go build ./...
|
||||||
|
- name: Vet
|
||||||
|
run: go vet ./...
|
||||||
|
|
||||||
|
vulnerabilities:
|
||||||
|
if: gitea.event_name == 'pull_request'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/setup-go@v6
|
||||||
|
with:
|
||||||
|
go-version: 'stable'
|
||||||
|
- name: Check vulnerabilities
|
||||||
|
run: |
|
||||||
|
go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||||
|
govulncheck ./...
|
||||||
|
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
BUILDTOOLS_CONTENT: ${{ secrets.BUILDTOOLS_CONTENT }}
|
||||||
|
GITEA_REPOSITORY: ${{ gitea.repository }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: buildtool/setup-buildtools-action@v1
|
||||||
|
- name: Build and push
|
||||||
|
run: unset GITEA_TOKEN && build && push
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: deployment-artifacts
|
||||||
|
path: release/
|
||||||
|
retention-days: 30
|
||||||
|
- name: Trigger acceptance tests
|
||||||
|
if: gitea.event_name == 'pull_request'
|
||||||
|
env:
|
||||||
|
GITEA_TOKEN: ${{ secrets.ACCTEST_TOKEN }}
|
||||||
|
GITEA_URL: ${{ gitea.server_url }}
|
||||||
|
run: |
|
||||||
|
curl --fail-with-body -X POST \
|
||||||
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"ref": "main",
|
||||||
|
"inputs": {
|
||||||
|
"trigger_repo": "openpayments-mock",
|
||||||
|
"trigger_commit": "${{ gitea.sha }}",
|
||||||
|
"trigger_run": "${{ gitea.run_id }}"
|
||||||
|
}
|
||||||
|
}' \
|
||||||
|
"${GITEA_URL}/api/v1/repos/shiny/acctest/actions/workflows/ci.yaml/dispatches"
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
openpayments-mock
|
||||||
|
service
|
||||||
|
coverage.txt
|
||||||
|
coverage.out
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
repos:
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v6.0.0
|
||||||
|
hooks:
|
||||||
|
- id: trailing-whitespace
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
- id: check-yaml
|
||||||
|
args:
|
||||||
|
- --allow-multiple-documents
|
||||||
|
- id: check-added-large-files
|
||||||
|
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
|
||||||
|
rev: v9.24.0
|
||||||
|
hooks:
|
||||||
|
- id: commitlint
|
||||||
|
stages: [commit-msg]
|
||||||
|
additional_dependencies: ['@commitlint/config-conventional']
|
||||||
|
- repo: https://github.com/dnephin/pre-commit-golang
|
||||||
|
rev: v0.5.1
|
||||||
|
hooks:
|
||||||
|
- id: go-mod-tidy
|
||||||
|
- id: go-imports
|
||||||
|
args:
|
||||||
|
- -local
|
||||||
|
- gitea.unbound.se/shiny/openpayments-mock
|
||||||
|
- repo: https://github.com/lietu/go-pre-commit
|
||||||
|
rev: v1.0.0
|
||||||
|
hooks:
|
||||||
|
- id: gofumpt
|
||||||
|
- id: golangci-lint-full
|
||||||
|
- repo: https://github.com/gitleaks/gitleaks
|
||||||
|
rev: v8.28.0
|
||||||
|
hooks:
|
||||||
|
- id: gitleaks
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
## openpayments-mock
|
||||||
|
|
||||||
|
Tiny Go HTTP server that mimics the Open Payments (PSD2/BerlinGroup) aggregator for Shiny's acceptance tests. Consumed by `acctest`'s `banking-suite` via the image published from this repo's CI.
|
||||||
|
|
||||||
|
## Shared Documentation
|
||||||
|
|
||||||
|
@../docs/claude/architecture.md
|
||||||
|
@../docs/claude/go-services.md
|
||||||
|
@../docs/claude/conventions.md
|
||||||
|
@../docs/claude/cicd.md
|
||||||
|
|
||||||
|
## Service-Specific Information
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
|
||||||
|
Stand-in for the real Open Payments aggregator. Not safe for anything but tests — state is entirely in-memory, there are no authentication checks, and admin endpoints let the acctest suite force deterministic transitions (payment status, basket status, transaction seeding, consent expiry).
|
||||||
|
|
||||||
|
### Port
|
||||||
|
|
||||||
|
8080 (matching production aggregator convention; acctest reaches it via cluster DNS `openpayments-mock:8080`).
|
||||||
|
|
||||||
|
### Not deployed to staging/prod
|
||||||
|
|
||||||
|
CI builds + publishes the image but does not run `deploy` — the mock is test-only infrastructure. acctest's `k8s/infra/base/openpayments-mock.yaml` references the published image.
|
||||||
|
|
||||||
|
### Endpoints
|
||||||
|
|
||||||
|
See `README.md` for the full endpoint list.
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
FROM amd64/golang:1.26.2@sha256:3e677b9776e5fcb030321772b4fe13c58b22b8abe772c647be8f746159d1a2dc as modules
|
||||||
|
WORKDIR /build
|
||||||
|
ENV GOPRIVATE=gitea.unbound.se/shiny,gitea.unbound.se/unboundsoftware
|
||||||
|
ADD go.* /build
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
FROM modules as build
|
||||||
|
ARG CI_COMMIT
|
||||||
|
WORKDIR /build
|
||||||
|
ENV CGO_ENABLED=0
|
||||||
|
ADD . /build
|
||||||
|
RUN GOOS=linux GOARCH=amd64 go build \
|
||||||
|
-tags prod \
|
||||||
|
-a -installsuffix cgo \
|
||||||
|
-mod=readonly \
|
||||||
|
-o /release/service \
|
||||||
|
-ldflags "-w -s -X main.buildVersion=${CI_COMMIT}" \
|
||||||
|
./cmd/service/service.go
|
||||||
|
|
||||||
|
FROM scratch
|
||||||
|
ENV TZ Europe/Stockholm
|
||||||
|
COPY --from=build /usr/share/zoneinfo /usr/share/zoneinfo
|
||||||
|
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||||
|
COPY --from=build /release/service /
|
||||||
|
CMD ["/service"]
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# openpayments-mock
|
||||||
|
|
||||||
|
Tiny in-memory stand-in for the Open Payments PSD2/BerlinGroup aggregator,
|
||||||
|
used by the `banking-suite` acceptance tests. Accepts any bearer token
|
||||||
|
and returns canned responses.
|
||||||
|
|
||||||
|
Not safe for anything but tests — state is entirely in-memory and there
|
||||||
|
are no authentication checks.
|
||||||
|
|
||||||
|
## Endpoints
|
||||||
|
|
||||||
|
### OAuth
|
||||||
|
- `POST /token` — returns static `{access_token: "acctest", ...}`
|
||||||
|
|
||||||
|
### AIS (BerlinGroup shape)
|
||||||
|
- `GET /psd2/aspspinformation/v1/aspsps` — catalog
|
||||||
|
- `POST /psd2/consent/v1/consents`
|
||||||
|
- `GET /psd2/consent/v1/consents/{id}/status`
|
||||||
|
- `DELETE /psd2/consent/v1/consents/{id}`
|
||||||
|
- `GET /authorize` — mock SCA stub that immediately redirects back to the
|
||||||
|
`TPP-Redirect-URI` with `?code=mock-auth-code&state=...`
|
||||||
|
- `GET /psd2/accountinformation/v1/accounts`
|
||||||
|
- `GET /psd2/accountinformation/v1/accounts/{id}/balances`
|
||||||
|
- `GET /psd2/accountinformation/v1/accounts/{id}/transactions`
|
||||||
|
- `GET /psd2/cardaccountinformation/v1/card-accounts`
|
||||||
|
|
||||||
|
### PIS
|
||||||
|
- `POST /psd2/paymentinitiation/v1/payments/sepa-credit-transfers`
|
||||||
|
- `GET /psd2/paymentinitiation/v1/payments/sepa-credit-transfers/{id}/status`
|
||||||
|
— transitions RCVD → PDNG → ACSC deterministically on successive polls.
|
||||||
|
Creditor name `"REJECT ME"` transitions to RJCT.
|
||||||
|
- `POST /psd2/v1/signing-baskets`
|
||||||
|
- `GET /psd2/v1/signing-baskets/{id}/status` — RCVD → ACCP → ACSC;
|
||||||
|
reaching ACSC advances all linked payments to ACSC.
|
||||||
|
|
||||||
|
### Admin (acctest only, not part of PSD2)
|
||||||
|
- `POST /admin/reset`
|
||||||
|
- `GET /admin/consents`
|
||||||
|
- `POST /admin/transactions` — body `{ accountId, bookingStatus, entries: [...] }`
|
||||||
|
- `POST /admin/payments/{id}/status` — body `{ status }`
|
||||||
|
- `POST /admin/baskets/{id}/status` — body `{ status }`
|
||||||
|
- `POST /admin/consents/{id}/expires-at` — body `{ expiresAt }`
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t openpayments-mock:acctest .
|
||||||
|
```
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
module gitea.unbound.se/shiny/openpayments-mock
|
||||||
|
|
||||||
|
go 1.24
|
||||||
|
|
||||||
|
require github.com/google/uuid v1.6.0
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: openpayments-mock
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: openpayments-mock
|
||||||
|
annotations:
|
||||||
|
kubernetes.io/change-cause: "${TIMESTAMP} Deployed commit id: ${COMMIT}"
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: openpayments-mock
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: openpayments-mock
|
||||||
|
app.kubernetes.io/instance: shiny
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: openpayments-mock
|
||||||
|
image: oci.unbound.se/shiny/openpayments-mock:${COMMIT}
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: 8080
|
||||||
|
env:
|
||||||
|
- name: PUBLIC_BASE_URL
|
||||||
|
value: "https://openpayments-mock"
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "10m"
|
||||||
|
memory: "20Mi"
|
||||||
|
limits:
|
||||||
|
memory: "64Mi"
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 8080
|
||||||
|
periodSeconds: 2
|
||||||
|
failureThreshold: 10
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 8080
|
||||||
|
periodSeconds: 10
|
||||||
|
failureThreshold: 3
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: openpayments-mock
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: openpayments-mock
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/name: openpayments-mock
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 8080
|
||||||
|
targetPort: 8080
|
||||||
Reference in New Issue
Block a user