diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..64d3bf0 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +[*.py] +indent_size = 4 + +[Makefile] +indent_style = tab diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..ddaf92c --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @hellopaywaz diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index fc5af00..0000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: CodeQL - -on: - workflow_dispatch: - push: - branches: [main] - pull_request: - schedule: - - cron: "0 6 * * 1" - -jobs: - analyze: - runs-on: ubuntu-latest - permissions: - security-events: write - contents: read - - steps: - - uses: actions/checkout@v4 - - uses: github/codeql-action/init@v3 - with: - languages: javascript-typescript - build-mode: none - - uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/samples-ci.yml b/.github/workflows/samples-ci.yml index fac68dc..cf3934d 100644 --- a/.github/workflows/samples-ci.yml +++ b/.github/workflows/samples-ci.yml @@ -19,6 +19,18 @@ jobs: with: node-version: 20 + - name: Install root tooling + run: npm ci + + - name: Lint (golden command) + run: npm run lint + + - name: Test (golden command) + run: npm test + + - name: Build (golden command) + run: npm run build + # ----------------------- # Payments sample checks # ----------------------- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8d43456 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +.DS_Store +node_modules/ +*.log +*.tmp +.env +.env.local +.npmrc +coverage/ +dist/ +build/ +/.cache/ +/.nyc_output/ +__pycache__/ +*.pyc +*.pyo diff --git a/HEALTH.md b/HEALTH.md new file mode 100644 index 0000000..ba07fb3 --- /dev/null +++ b/HEALTH.md @@ -0,0 +1,43 @@ +# Repository Health + +**Overall score: 65/100** — Samples now have baseline tooling, ownership, and a minimal webhook unit test, but dependency pinning and release/ops practices remain light. + +## Category scores +- CI/Build Reproducibility (25): **18/25** — GitHub Actions runs lint/test/build and a webhook smoke flow, but JavaScript sample dependencies are not lockfile-pinned and Python has no automation. +- Tests/Quality Gates (20): **12/20** — Node webhook signature tests exist; other samples rely on manual runs and have no linting beyond syntax checks. +- Security Baseline (20): **14/20** — SECURITY.md and CodeQL are present and CODEOWNERS added; no secret scanning or dependency audit in CI. +- Release/Packaging (15): **8/15** — No versioning, changelog, or publish flow; samples are not packaged for distribution. +- Documentation/Onboarding (10): **8/10** — README documents golden commands and setup; sample-specific docs are brief and Python onboarding is minimal. +- Ops/Runbooks/Observability (10): **5/10** — Webhook samples log minimally; no health checks or runbooks, which is acceptable for samples but still a gap for production readiness. + +## P0 blockers (must-fix) +None detected after this pass. + +## P1 risks (should fix soon) +- Lock JavaScript sample dependencies (`javascript/payments`, `javascript/webhooks-node`) with package-locks or pnpm/yarn locks to improve reproducibility. +- Add automated checks for the Python sample (e.g., `python -m compileall python` in CI) so it participates in quality gates. +- Add dependency vulnerability scanning/secret scanning to CI (e.g., `npm audit --production` for JavaScript, GitHub Advanced Security features if available). +- Flesh out release notes/versioning expectations if any sample is intended for reuse beyond ad-hoc demos. + +## P2 hygiene (nice-to-have) +- Add lightweight linting (ESLint/Prettier) to the JavaScript samples for style consistency. +- Provide small README sections per sample with explicit env vars and example outputs. +- Add a minimal runbook/operational note for the webhook server sample (expected ports, health endpoint guidance). + +## CI status +- `samples-ci.yml`: Node 20 job that runs repo-wide golden commands (`npm run lint`, `npm test`, `npm run build`), then syntax checks and exercises the Node webhook server with a signed webhook smoke test. +- `codeql.yml`: Weekly/PR CodeQL scan for JavaScript/TypeScript with read-only permissions and security-events write. + +## Security baseline +- SECURITY.md present with disclosure instructions. License documented in `LICENSE`. `.env.example` keeps secrets placeholder-only. +- No automated secret scanning or dependency audit currently configured. CI permissions are least-privilege for CodeQL; other workflows rely on defaults. + +## Release readiness +- No versioning, changelog, or tagged releases. Samples are not published; no artifact pipeline exists. + +## Ops readiness +- Webhook samples log to stdout and support secret rotation/tolerance settings via env vars; no health endpoints or runbooks. No monitoring guidance. + +## Repo hygiene +- Baseline community files present: README, CONTRIBUTING, CODE_OF_CONDUCT, SECURITY, CODEOWNERS, LICENSE, .editorconfig, .gitignore. +- Samples lack lockfiles and detailed per-sample docs; Python sample lacks dependency metadata. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fda0c1a --- /dev/null +++ b/LICENSE @@ -0,0 +1,109 @@ +Paywaz Proprietary License Agreement +Version 1.0 — © 2025 Paywaz.com LLC + +IMPORTANT: This software, documentation, or file set (the “Software”) is the +intellectual property of Paywaz.com LLC (“Paywaz”). By accessing, downloading, +copying, or using the Software, you agree to the terms of this License. + +If you do not agree, you must immediately cease all use of the Software. + +--- + +1. GRANT OF LIMITED RIGHT + +Paywaz grants you a limited, revocable, non-exclusive, non-transferable, +non-sublicensable license to: + +(a) View the Software for informational, evaluation, or integration-planning +purposes; +(b) Use the Software solely for non-commercial internal evaluation; +(c) Reference the Software to assess compatibility with your own systems. + +No other rights are granted. + +--- + +2. RESTRICTIONS + +Except where explicitly permitted in writing by Paywaz, you MAY NOT: + +(a) Copy, modify, distribute, publish, or create derivative works of the Software; +(b) Use the Software for any commercial activity, paid service, production +environment, or merchant processing; +(c) Reverse engineer, decompile, disassemble, or attempt to derive source logic, +data models, API behavior, or internal system architecture; +(d) Remove, alter, or obscure any Paywaz ownership notices; +(e) Redistribute the Software on GitHub or any other platform; +(f) Use the Software to build or improve a competing product or service. + +Any unauthorized use terminates this License immediately. + +--- + +3. OWNERSHIP + +All rights, title, interest, trademarks, trade secrets, and intellectual property +associated with the Software remain the exclusive property of Paywaz.com LLC. + +Nothing in this License transfers or assigns ownership to you. + +--- + +4. NO WARRANTY + +The Software is provided “AS IS” with no warranties, express or implied, +including but not limited to warranties of: + +– Merchantability +– Fitness for a particular purpose +– Accuracy +– Availability +– Non-infringement + +Paywaz disclaims all liability arising from use of the Software. + +--- + +5. LIMITATION OF LIABILITY + +To the maximum extent permitted by law, Paywaz shall not be liable for any: + +– Direct, indirect, incidental, special, punitive, or consequential damages +– Loss of revenue, profits, data, business opportunity, goodwill +– Security breaches, interruptions, or operational impacts + +Use of the Software is entirely at your own risk. + +--- + +6. TERMINATION + +Paywaz may revoke this License at any time without notice. +Upon termination, you must immediately cease all use and destroy any copies of +the Software. + +--- + +7. GOVERNING LAW + +This License shall be governed by and construed in accordance with the laws of +the State of Delaware, without regard to conflict-of-law principles. + +--- + +8. CONTACT + +For commercial licensing, permissions, or partnership inquiries: + +Paywaz.com LLC +Email: legal@paywaz.com +Website: https://paywaz.com + +--- + +9. ACCEPTANCE + +By accessing or using the Software, you acknowledge that you have read, +understood, and agree to be bound by this License. + +© 2025 Paywaz.com LLC — All Rights Reserved. diff --git a/README.md b/README.md index ad8c34f..1cbf3fe 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,23 @@ npm install npm run start ``` +### Golden commands +Run these from the repo root to sanity-check the samples: + +```bash +# Parse-check JavaScript samples +npm run lint + +# Webhook signature unit tests (node:test) +npm test + +# Alias for lint (no compilation required yet) +npm run build + +# Run the payments sample (requires PAYWAZ_* env vars) +npm start +``` + --- ## Requirements diff --git a/javascript/webhooks-node/package.json b/javascript/webhooks-node/package.json index d637600..a6593eb 100644 --- a/javascript/webhooks-node/package.json +++ b/javascript/webhooks-node/package.json @@ -8,6 +8,7 @@ }, "scripts": { "start": "node server.mjs", + "test": "node --test", "test:send": "node send-test-webhook.mjs" }, "dependencies": { diff --git a/javascript/webhooks-node/test/verify-signature.test.mjs b/javascript/webhooks-node/test/verify-signature.test.mjs new file mode 100644 index 0000000..4cc2e02 --- /dev/null +++ b/javascript/webhooks-node/test/verify-signature.test.mjs @@ -0,0 +1,90 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; +import { signPaywazWebhook } from "../sign-webhook.mjs"; +import { verifyPaywazWebhook } from "../verify-signature.mjs"; + +test("sign + verify succeeds with rotation support", () => { + const rawBody = JSON.stringify({ hello: "world" }); + const timestamp = 1_700_000_000; + const secrets = ["whsec_current", "whsec_previous"]; + + const { signature } = signPaywazWebhook({ + rawBody, + secret: secrets[0], + timestamp + }); + + const originalNow = Date.now; + Date.now = () => timestamp * 1000; + + try { + const result = verifyPaywazWebhook({ + rawBody, + timestampHeader: String(timestamp), + signatureHeader: signature, + secrets, + toleranceSeconds: 300 + }); + + assert.equal(result.secretIndex, 0); + assert.equal(result.timestamp, timestamp); + } finally { + Date.now = originalNow; + } +}); + +test("verifies signatures created with previous secrets", () => { + const rawBody = JSON.stringify({ id: "evt_123" }); + const timestamp = 1_700_000_123; + const secrets = ["whsec_rotated", "whsec_old"]; + + const { signature } = signPaywazWebhook({ + rawBody, + secret: secrets[1], + timestamp + }); + + const originalNow = Date.now; + Date.now = () => timestamp * 1000; + + try { + const result = verifyPaywazWebhook({ + rawBody, + timestampHeader: String(timestamp), + signatureHeader: signature, + secrets, + toleranceSeconds: 300 + }); + + assert.equal(result.secretIndex, 1); + } finally { + Date.now = originalNow; + } +}); + +test("rejects stale timestamps", () => { + const rawBody = "{}"; + const timestamp = 1_700_000_000; + const { signature } = signPaywazWebhook({ + rawBody, + secret: "whsec_expired", + timestamp + }); + + const originalNow = Date.now; + Date.now = () => (timestamp + 1000) * 1000; + + try { + assert.throws(() => + verifyPaywazWebhook({ + rawBody, + timestampHeader: String(timestamp), + signatureHeader: signature, + secrets: ["whsec_expired"], + toleranceSeconds: 300 + }) + ); + } finally { + Date.now = originalNow; + } +}); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..f5cee24 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,15 @@ +{ + "name": "paywaz-samples", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "paywaz-samples", + "version": "0.1.0", + "engines": { + "node": ">=20" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..4381a9d --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "paywaz-samples", + "version": "0.1.0", + "private": true, + "description": "Health-checked Paywaz sample integrations", + "engines": { + "node": ">=20" + }, + "scripts": { + "lint": "npm run lint:payments && npm run lint:webhooks-express && npm run lint:webhooks-node", + "lint:payments": "node --check javascript/payments/create-payment.mjs && node --check javascript/payments/get-payment.mjs", + "lint:webhooks-express": "node --check javascript/webhooks-express/server.mjs && node --check javascript/webhooks-express/verify-signature.mjs", + "lint:webhooks-node": "node --check javascript/webhooks-node/server.mjs && node --check javascript/webhooks-node/verify-signature.mjs && node --check javascript/webhooks-node/sign-webhook.mjs && node --check javascript/webhooks-node/send-test-webhook.mjs", + "test": "npm run test:webhooks-node", + "test:webhooks-node": "npm --prefix javascript/webhooks-node test", + "build": "npm run lint", + "start": "node javascript/payments/create-payment.mjs" + } +}