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