158909 :: docs: AutoPay public documentation - #169

Merged
ankitottu merged 14 commits into
devfrom
epic/158909
Aug 13, 2026
Merged

158909 :: docs: AutoPay public documentation#169
ankitottu merged 14 commits into
devfrom
epic/158909

Conversation

@ankitottu

Copy link
Copy Markdown
Contributor

Redmine epic #158909 — closes the docs half of the AutoPay launch.

Merges the epic branch into dev, which deploys to docs.ottu.dev for review. Nothing reaches docs.ottu.com until it has been signed off there — that is #158915.

What ships

#158910Developer section at /developers/payments/autopay/ + AutoPayFlowDiagram + glossary terms + payments-index entry
#158912Business section at /business/autopay/, one long page with a Pattern-A anchor sub-menu
#158913 (partial)20 customer-portal and notification screenshots from Menna
Design spec + execution plan under docs/superpowers/

Both child PRs (#167, #168) already merged here. sidebars.ts was edited once, in the scaffold commit, so the two lanes never touched the same file.

The distinction the whole thing hangs on

With auto_debit the merchant owns the billing schedule, retries and dunning. With auto_pay, AutoPay owns them.

recurring-payments.mdx is the closest existing page and readers will land on the wrong one, so both pages now cross-link with an explicit "use this when".

Facts documented that appear in no ticket

Found by reading autopay/subscriptions/ and by a live betabulk call:

  • The autopay request block is write-only — not echoed in the 201. extra.autopay.subscription_id is the merchant's only handle. Verified live: sub_hA1YYmZz1xk8speCiPbOquIP.
  • customer_email is required. Omitting it 422s inside AutoPay and surfaces as 400 {"autopay":["AutoPayClient rejected request (422)"]} — reads like an agreement-shape error and is not.
  • page-link does not rotate the token; regenerate-page-token revokes. A merchant reaching for regenerate when they meant re-send silently breaks the customer's working link, with no notification to that customer.
  • retry_window_days is dead — dropped in migrations/0028_drop_retry_window_days.py. ⚠️#158911's field table still lists it as live with a 1–30 range. That table is also the spec for the enrichment YAML, so it needs correcting there too.
  • list returns a slim summary, not full subscription objects.
  • The customer page token is signed, not encrypted — the payload is readable base64. Treat the link like a password.

Known gaps, deliberate

GapWhy
## API Reference is a placeholdercore_backend PR 121 unmerged — static/Ottu_API.yaml has zero autopay occurrences, so <ApiDocEmbed> cannot resolve. Endpoints documented as prose + cURL under ## Guide instead.
Endpoint base path unconfirmedSamples use AutoPay's own root-mounted paths. Merchants reach these through a Connect proxy whose public path is in PR 121. @yamen — needs your answer.
JWT acquisition undocumentedFive of six endpoints need a Keycloak JWT; the auth guide only covers Api-Key. Flagged in a :::warning on the page rather than papered over.
No merchant dashboard contentIt does not exist — REST-API-only this phase, confirmed by @dacian 2026-08-13.
No add-card screenshotNot captured yet; behaviour written as prose, no image referenced.
No videoRemotion is #158914, decoupled.

Verification

  • npm run typecheck ✅ · npm run build
  • Both pages adversarially verified by subagents against their tickets' own acceptance criteria: developer 14 PASS / 1 BLOCKED, business 13 PASS
  • Checked by hand in Chrome against the local production build — onBrokenLinks is "warn", so a green build proves nothing about navigation:
    • business sidebar anchors 10/10 resolve, developer 5/5, in-page links 21/21
    • every referenced image returns HTTP 200
    • flow diagram renders correctly in light and dark mode
  • Every behavioural claim traced to autopay/subscriptions/enums.py, models.py, schemas.py or tasks/_tenant_processors.py

⚠️ One thing to look at before approving

portal-01-summary-active.png is the hero image on the business page and it is a mockup that contradicts the real product. Checked against a live betabulk portal: it shows an invoice Download column (AutoPay stores no invoices), a Pay Now button on an active subscription (Pay Now only exists when past due), two cards both masked *** 1234, and a next-payment date a year adrift from its own history.

The prose no longer describes any of it, but a reader still sees it. Recutting that one image — or a straight recapture from betabulk — is the highest-value screenshot task left. @junaid@Menna

Sign-off needed (#158915)

  • @yamen — backend and API accuracy, PR 121, proxy base path
  • @ankit — AutoPay behaviour and business accuracy
  • @junaid / @Menna — visual review

Do not merge until reviewed on this branch.

…enshots
Lane-0 scaffold so #158910 and #158912 can be written in parallel without
both editing sidebars.ts.
- developerSidebar: AutoPay Pattern-A anchor category after M-Wallet
- businessSidebar: AutoPay Pattern-A anchor category after M-Wallet
- skeleton pages pinning every heading id the sidebar links to
- 22 customer-portal and notification screenshots from Menna
(real PNGs, largest 154 KB, wallet naming convention, no stubs)
Merchant-side screens are deliberately absent: AutoPay is REST-API-only
on the merchant side in this phase (confirmed by Dacian, 2026-08-13).
Refs #158909 #158910 #158912 #158913
Mirrors the M-Wallet pair the epic names as the reference model.
The plan carries a verified-facts ledger with a source citation for every
behavioural claim, plus six findings that appear in no ticket:
- the autopay request block is write-only, not echoed in the 201
- customer_email is required; omitting it surfaces as a misleading 400
- page-link does not rotate the token; regenerate-page-token revokes
- the customer page is served from the Connect host, language in the path
- the page token is signed, not encrypted — payload is readable base64
- the nonce is what makes revocation work at all
Refs #158909 #158910 #158912 #158915
New page at /developers/payments/autopay/ following the M-Wallet shape and
the section order mandated by docs/developers/CLAUDE.md.
- AutoPayFlowDiagram.tsx — inline theme-aware SVG, matching WalletFlowDiagram
and RecurringFlow; not Mermaid
- payments/index.md — AutoPay card and Choose Your Path row
- glossary-terms.ts — subscription, billing cycle, dunning
- recurring-payments.mdx — reverse half of the deconfliction cross-link, so a
reader who lands on auto_debit expecting subscriptions is redirected
## API Reference is intentionally a placeholder: core_backend PR 121 has not
merged, so the public OpenAPI schema serves no AutoPay endpoints and
<ApiDocEmbed> cannot resolve. The six endpoints are documented as prose and
cURL under ## Guide instead.
Known open item: the endpoint samples use AutoPay's own root-mounted paths
(/subscriptions/...). Merchants reach these through a Connect proxy whose
public path is in PR 121 and is not yet confirmed. Pending Yamen.
Refs #158910 #158909
New page at /business/autopay/ for merchant admins and operations staff.
One long page with a Pattern-A anchor sub-menu, matching what M-Wallet
actually shipped rather than what its spec called for.
Eleven sections, every heading carrying an explicit anchor id so renaming
the visible text cannot silently break sidebars.ts. Right-hand table of
contents kept. Zero code samples.
All 22 of Menna's screenshots wired through <StepGuide>; every referenced
image resolves to a real file.
States plainly, as the ticket requires:
- AutoPay never auto-cancels a past_due subscription — it stays past_due
indefinitely until the customer pays or the merchant cancels
- a customer cancellation always takes effect at the end of the current
period; only the merchant can cancel immediately
Merchant setup is documented as API-only. There is no merchant dashboard in
this phase (confirmed by Dacian, 2026-08-13) and no merchant screenshots.
No video — the Remotion pipeline is #158914 and is decoupled.
Refs #158912 #158909 #158913
The Expired step claimed the page "still offers a way to pick things back
up". Source says otherwise:
- subscriptions/models.py has zero @transition with source=EXPIRED — it is
a one-way terminal state
- customer_api.py reactivate() requires status==ACTIVE AND
cancel_at_period_end==true, and 422s otherwise; an expired subscription
can never satisfy that
Separately: portal-04-expired.png shows a Reactivate button that the
backend would reject, and labels the date field "Trial Ends" under an
Expired badge. Raised with the AutoPay self-service frontend owners — the
screenshot looks like a mockup rather than real Expired-state UI.
Refs #158912 #158913
158910 :: docs: AutoPay developer section
158912 :: docs: AutoPay business section
Three claims on the business page described the mockups rather than the
product. Corrected against the screen data pack
(autopay/docs/2026-08-13-autopay-screen-data-pack.md §2, §5):
- AutoPay stores no invoice and no PDF. Dropped 'downloadable invoices'
and 'invoice downloads'; a history row ends at its status.
- There is no delete-card action. Cards are kept as history by design so
an inactive card still explains an earlier cycle. Switching the active
card is the only card management there is.
- Pay Now only exists on a past-due subscription. Removed it from the
active-state description.
Also dropped the cards-01-delete-confirm.png reference — it depicts a flow
that does not exist. Per the data pack it should be recut as a
set-active-card confirm.
Root cause worth recording: the mockups themselves are wrong (portal-01
shows an invoice Download column and a Pay Now button on an active
subscription). The page described them faithfully. Backend-sourced
behaviour was all correct; only screenshot-derived UI claims were affected.
Refs #158912 #158913
… flag JWT gap
Adversarial verification of both pages found three real defects.
BLOCKER — retry_window_days is not a live field. It was removed by
subscriptions/migrations/0028_drop_retry_window_days.py (2026-06-15) and
appears nowhere outside migrations; the current SubscriptionInternalCreateIn
has no such field. Sending it today is silently dropped. Documenting it with
a 1-30 range misrepresented working behaviour. Note that ticket #158911's
own field table still lists it — the ticket is stale, not the writer.
MAJOR — the business page told readers Expired is terminal (correct: zero
@transition with source=EXPIRED, and reactivate() requires status==ACTIVE)
while displaying portal-04-expired.png, which shows a prominent Reactivate
button. Dropped the screenshot rather than caption a contradiction.
MAJOR — five of the six management endpoints need a Keycloak JWT and the
page never said how to get one; the linked authentication guide covers only
Api-Key. Added a :::warning naming it as an open gap so a reader is not left
guessing.
Known minor, not fixed: glossary terms added in this epic are not linked
from the AutoPay page. Glossary term anchors do not exist in the built
output at all (a pre-existing site-wide issue), so linking them would add
broken anchors.
Refs #158909 #158910 #158911 #158912
…duled cycle
Found by visual verification in Chrome against the local production build.
- AutoPayFlowDiagram: the 'ongoing' label's white background rect (x560-650)
painted over the tail of 'creates subscription - sync' (x450-600), which
rendered as 'creates subscriptior.'. Moved 'ongoing' below its own arrow.
Verified in both light and dark mode.
- Business page said customers 'look back through their full billing
history'. The real betabulk portal shows the NEXT cycle in the same table
with a Scheduled status, so the table is not past-only. Reworded.
Refs #158909 #158910 #158912
Editorial pass against docs/business/wallet/index.md as the house reference.
- Removed the only inline code span on the page. It was introduced by the
Scheduled-cycle edit in a76caaf and broke the zero-code audience rule that
#158912's acceptance criteria require.
- Replaced $0 with 'zero' in three places. Ottu prices in KWD/SAR and the
wallet business page uses no dollar signs at all.
- 'pending setup' -> 'Pending setup' in the lifecycle table, matching the
Title Case used for every other state name on the page.
Refs #158912
Editorial pass against docs/business/wallet/index.md, measured before and
after. Prose only — no structural, anchor, or image changes.
- StepGuide captions 20 -> 13 words average (wallet reference: 7). Captions
now point at the screenshot instead of arguing with it.
- Em-dashes 20.3 -> 16.0 per 100 lines (wallet: 14.6).
- Removed five marketing idioms with no precedent on the wallet page:
'without lifting a finger', 'in the driver's seat', 'doesn't move the
needle', 'running a billing engine by hand', 'a heads-up'.
Every factual claim is unchanged; this only affects how they are worded.
Refs #158912
…cle statuses
Accuracy and consistency pass on both AutoPay pages, verified against
autopay/subscriptions/{enums,schemas}.py.
- notification_preferences: documented the three real boolean keys —
pre_charge_enabled, failure_enabled, final_failure_enabled, each
defaulting to true (schemas.py:52-54). Previously described only as
'free-form'.
- List subscriptions returns a slim summary per item, not the full
subscription object. Callers need Retrieve for the active card and
latest-cycle detail.
- Cycle statuses: added retry_scheduled and canceled, completing the set
a merchant can observe. needs_triage is deliberately omitted — it is an
ops-only state reached by stale-cleanup, not something a merchant acts on.
- US spelling for consistency with the rest of the docs: catalogue ->
catalog, cancelling -> canceling.
Refs #158909 #158910 #158912
@jab3z

Copy link
Copy Markdown
Contributor

@claude review

@claude

claudeBot commented Aug 13, 2026

Copy link
Copy Markdown

Claude finished @jab3z's task in 3m 14s —— View job


Review complete

  • Gather context (PR body, changed files list)
  • Read diff (git diff origin/dev...HEAD)
  • Review developer AutoPay page + flow diagram
  • Review business AutoPay page
  • Review sidebars.ts, glossary, cross-links
  • Check CLAUDE.md compliance (API base URL constant, merchant_id, cross-linking, etc.)
  • Post final review

Note: node_modules isn't installed in this sandbox and installing it wasn't in scope, so I couldn't independently re-run npm run build/npm run typecheck — this review is a manual read of the diff against origin/dev and the repo's CLAUDE.md conventions.

Overall this is a strong, well-verified page pair — the sidebar anchor hygiene (sidebars.ts) all checks out against actual heading IDs on both pages, the <AutoPayFlowDiagram> follows the svg-diagram skill convention correctly (monochrome + 2 accent nodes, light/dark overrides), OTTU_CONNECT_BASE_URL is used consistently instead of hardcoded URLs, and the cross-linking between AutoPay and recurring-payments.mdx is exactly the kind of disambiguation the domain needs. A few things worth a look before merging to dev:

1. "Copy-paste accurate" claim conflicts with the PR's own "base path unconfirmed" gap

docs/developers/payments/autopay/index.mdx:437-441 (API Reference section):

"Every request and response shown above is copy-paste accurate"

But the PR description itself flags "Endpoint base path unconfirmed... Merchants reach these through a Connect proxy whose public path is in PR 121 — @yamen needs your answer." The six subscription-management cURL examples (#step-by-step) hit plain ${OTTU_CONNECT_BASE_URL}/subscriptions/... with no proxy prefix, while the one endpoint you have confirmed (checkout, step 1) is prefixed /b/checkout/v1/pymt-txn/. Since the base path for steps 2–6 is explicitly still pending confirmation, the "copy-paste accurate" claim overstates what's verified — a developer could reasonably build against /subscriptions/... directly and hit 404s once the real proxy path lands. Consider either softening that line or adding the same base-path caveat that's already implicit in the PR's "known gaps" table, so it's visible to a reader on docs.ottu.dev, not just to reviewers on this PR.
Fix this →

2. New glossary terms aren't linked from either new page

src/data/glossary-terms.ts adds Billing Cycle, Dunning, and Subscription, all introduced specifically for AutoPay — but neither docs/developers/payments/autopay/index.mdx nor docs/business/autopay/index.md links to /glossary/ on first use, despite root CLAUDE.md's "no jargon without definition — first use links to the glossary" rule and the developer CLAUDE.md cross-referencing table. (This rule is loosely followed elsewhere in the repo too, so treat this as a nice-to-have rather than a blocker — but since these terms were added for this PR, it's a natural fit to wire them up here.)

3. Two shipped screenshots are never referenced, one contradicts the page copy

git diff --stat shows 20 new business screenshots, but only 18 are referenced from docs/business/autopay/index.md:

  • static/img/business/autopay/portal-04-expired.png — the "Expired" step in the state <StepGuide> (docs/business/autopay/index.md:135-138) has no image/imageAlt, even though this exact screenshot exists.
  • static/img/business/autopay/cards-01-delete-confirm.png — unreferenced anywhere. This one is worth double-checking specifically: the page states "Saved cards are never deleted... there is no removal step to walk them through" (line 195), but a screenshot named cards-01-delete-confirm strongly implies a delete-confirmation dialog exists in the product. Given the PR already flagged portal-01-summary-active.png as a stale mockup that contradicts the real product, this looks like it could be the same class of issue — either the copy is right and this is a leftover asset that should be dropped, or card deletion does exist and the copy needs correcting. Worth a quick check with @Menna/@junaid alongside the portal-01 fix.

Not re-litigated

The factual claims (write-only autopay block, customer_email 422 behavior, page-link vs regenerate-page-token, dead retry_window_days, slim list summary, signed-not-encrypted token) all match what's described in the PR body as independently verified against autopay/subscriptions/ source and a live betabulk call — I didn't have backend access to re-verify these, so I'm relying on the PR author's stated verification method there.
· branch epic/158909

…rop mockups
Three points from the PR 169 review.
The API Reference note claimed every request and response above was
copy-paste accurate, while the PR itself flags the Connect proxy prefix for
the six management endpoints as unconfirmed. That gap was visible to
reviewers and invisible to a reader on docs.ottu.dev. Replaced the claim
with a warning under Step-by-Step naming exactly what is uncertain — the
path in front of /subscriptions/ — and stating that the headers, bodies,
responses and behaviour are not, and that Step 1's /b/checkout/v1/pymt-txn/
is the stable Checkout API path.
Billing Cycle, Dunning and Subscription were added to glossary-terms.ts for
this PR and nothing linked to them. Linked each once per page via
/glossary/#term-<id>. GlossarySearch server-renders those ids, so the
anchors resolve natively; Docusaurus still reports them broken because its
checker only reads markdown headings — the same false positive already
fires for the #term-* links on payment-states and transaction-states.
portal-04-expired.png and cards-01-delete-confirm.png were unreferenced
because both depict flows that do not exist: the first shows a Reactivate
button on an Expired subscription (terminal in the backend) plus an invoice
Download column, the second a delete-card confirmation. Dereferenced in
b5475bf and 79835e2 respectively; deleting the assets so they cannot be
wired back in. They need recapture from betabulk alongside
portal-01-summary-active.png.
npm run build passes.
@ankitottu

Copy link
Copy Markdown
ContributorAuthor

@claude re-review

@claude

claudeBot commented Aug 13, 2026

Copy link
Copy Markdown

Claude finished @ankitottu's task in 1m 38s —— View job


Re-review complete

  • Gather context (PR body, prior review, changed files)
  • Check what changed since the last review (commit 34e4484)
  • Verify the three prior findings were actually resolved
  • Fresh pass over the full diff for anything new
  • Post final review

Note: node_modules still isn't installed in this sandbox, so I couldn't independently re-run npm run build/npm run typecheck — this is a manual read of git diff origin/dev...HEAD plus the four fix commits added since the first review (b5475bf, 79835e2, 93c1567, 34e4484).

All three points from the first review are resolved cleanly in 34e4484:

1. "Copy-paste accurate" overclaim — fixed. docs/developers/payments/autopay/index.mdx:62-65 now has a dedicated :::warning The path in front of /subscriptions/ isn't final block naming exactly what's unconfirmed (the Connect proxy prefix for Steps 2–6) while explicitly stating headers/bodies/responses/behaviour are accurate, and that Step 1's /b/checkout/v1/pymt-txn/ is stable. The old blanket "copy-paste accurate" line under ## API Reference (line ~444) was replaced with a pointer to that scoped caveat instead of a second overclaim. This is exactly the right fix — it's visible to a reader on docs.ottu.dev now, not just to PR reviewers.

2. Glossary terms unlinked — fixed. Billing Cycle, Dunning, and Subscription are each now linked once per page via /glossary/#term-<id>. Verified the anchor mechanism actually resolves: src/components/GlossarySearch/index.tsx:7,30-31 builds/reads the same term-${id} hash format server-side, and the ids added in src/data/glossary-terms.ts (billing-cycle, dunning, subscription) match what's linked. The commit message's note that Docusaurus's broken-link checker will still flag these as a false positive (same pattern as the existing payment-states/transaction-states pages) checks out.

3. Unreferenced/contradictory screenshots — fixed correctly, not just papered over. Both cards-01-delete-confirm.png and portal-04-expired.png are deleted from the tree (not just dereferenced), so they can't get silently wired back in later. Confirmed no remaining references to either filename anywhere in the repo, and confirmed all 19 remaining business screenshots (20 shipped − 2 dropped − wait, 18 referenced + the dropped 2 = 20 ✓) are each referenced from docs/business/autopay/index.md. The "Expired" step in the state <StepGuide> (docs/business/autopay/index.md:135-138) now correctly has no image/imageAlt at all, matching its terminal-state description instead of showing a fabricated Reactivate button.

One thing worth noting rather than re-flagging: portal-01-summary-active.png (the hero mockup the PR body itself calls out as contradicting the real product) is still in place — that's expected, since the PR description already tracks it as the open "highest-value screenshot task left" pending recapture from betabulk, not something silently missed.

Also scanned the intervening accuracy commit (93c1567, notification keys / list-summary shape / cycle statuses) — it's a good-faith correctness pass against autopay/subscriptions/{enums,schemas}.py unrelated to the review findings, nothing to flag there.

Nothing new surfaced in this pass. The two page diffs are clean additions with no leftover inconsistencies from the fix-up commits.
· branch

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ankitottu@jab3z
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

158909 :: docs: AutoPay public documentation - #169

Merged
ankitottu merged 14 commits into
devfrom
epic/158909
Aug 13, 2026
Merged

158909 :: docs: AutoPay public documentation#169
ankitottu merged 14 commits into
devfrom
epic/158909

Conversation

@ankitottu

Copy link
Copy Markdown
Contributor

Redmine epic #158909 — closes the docs half of the AutoPay launch.

Merges the epic branch into dev, which deploys to docs.ottu.dev for review. Nothing reaches docs.ottu.com until it has been signed off there — that is #158915.

What ships

#158910Developer section at /developers/payments/autopay/ + AutoPayFlowDiagram + glossary terms + payments-index entry
#158912Business section at /business/autopay/, one long page with a Pattern-A anchor sub-menu
#158913 (partial)20 customer-portal and notification screenshots from Menna
Design spec + execution plan under docs/superpowers/

Both child PRs (#167, #168) already merged here. sidebars.ts was edited once, in the scaffold commit, so the two lanes never touched the same file.

The distinction the whole thing hangs on

With auto_debit the merchant owns the billing schedule, retries and dunning. With auto_pay, AutoPay owns them.

recurring-payments.mdx is the closest existing page and readers will land on the wrong one, so both pages now cross-link with an explicit "use this when".

Facts documented that appear in no ticket

Found by reading autopay/subscriptions/ and by a live betabulk call:

  • The autopay request block is write-only — not echoed in the 201. extra.autopay.subscription_id is the merchant's only handle. Verified live: sub_hA1YYmZz1xk8speCiPbOquIP.
  • customer_email is required. Omitting it 422s inside AutoPay and surfaces as 400 {"autopay":["AutoPayClient rejected request (422)"]} — reads like an agreement-shape error and is not.
  • page-link does not rotate the token; regenerate-page-token revokes. A merchant reaching for regenerate when they meant re-send silently breaks the customer's working link, with no notification to that customer.
  • retry_window_days is dead — dropped in migrations/0028_drop_retry_window_days.py. ⚠️#158911's field table still lists it as live with a 1–30 range. That table is also the spec for the enrichment YAML, so it needs correcting there too.
  • list returns a slim summary, not full subscription objects.
  • The customer page token is signed, not encrypted — the payload is readable base64. Treat the link like a password.

Known gaps, deliberate

GapWhy
## API Reference is a placeholdercore_backend PR 121 unmerged — static/Ottu_API.yaml has zero autopay occurrences, so <ApiDocEmbed> cannot resolve. Endpoints documented as prose + cURL under ## Guide instead.
Endpoint base path unconfirmedSamples use AutoPay's own root-mounted paths. Merchants reach these through a Connect proxy whose public path is in PR 121. @yamen — needs your answer.
JWT acquisition undocumentedFive of six endpoints need a Keycloak JWT; the auth guide only covers Api-Key. Flagged in a :::warning on the page rather than papered over.
No merchant dashboard contentIt does not exist — REST-API-only this phase, confirmed by @dacian 2026-08-13.
No add-card screenshotNot captured yet; behaviour written as prose, no image referenced.
No videoRemotion is #158914, decoupled.

Verification

  • npm run typecheck ✅ · npm run build
  • Both pages adversarially verified by subagents against their tickets' own acceptance criteria: developer 14 PASS / 1 BLOCKED, business 13 PASS
  • Checked by hand in Chrome against the local production build — onBrokenLinks is "warn", so a green build proves nothing about navigation:
    • business sidebar anchors 10/10 resolve, developer 5/5, in-page links 21/21
    • every referenced image returns HTTP 200
    • flow diagram renders correctly in light and dark mode
  • Every behavioural claim traced to autopay/subscriptions/enums.py, models.py, schemas.py or tasks/_tenant_processors.py

⚠️ One thing to look at before approving

portal-01-summary-active.png is the hero image on the business page and it is a mockup that contradicts the real product. Checked against a live betabulk portal: it shows an invoice Download column (AutoPay stores no invoices), a Pay Now button on an active subscription (Pay Now only exists when past due), two cards both masked *** 1234, and a next-payment date a year adrift from its own history.

The prose no longer describes any of it, but a reader still sees it. Recutting that one image — or a straight recapture from betabulk — is the highest-value screenshot task left. @junaid@Menna

Sign-off needed (#158915)

  • @yamen — backend and API accuracy, PR 121, proxy base path
  • @ankit — AutoPay behaviour and business accuracy
  • @junaid / @Menna — visual review

Do not merge until reviewed on this branch.

…enshots
Lane-0 scaffold so #158910 and #158912 can be written in parallel without
both editing sidebars.ts.
- developerSidebar: AutoPay Pattern-A anchor category after M-Wallet
- businessSidebar: AutoPay Pattern-A anchor category after M-Wallet
- skeleton pages pinning every heading id the sidebar links to
- 22 customer-portal and notification screenshots from Menna
(real PNGs, largest 154 KB, wallet naming convention, no stubs)
Merchant-side screens are deliberately absent: AutoPay is REST-API-only
on the merchant side in this phase (confirmed by Dacian, 2026-08-13).
Refs #158909 #158910 #158912 #158913
Mirrors the M-Wallet pair the epic names as the reference model.
The plan carries a verified-facts ledger with a source citation for every
behavioural claim, plus six findings that appear in no ticket:
- the autopay request block is write-only, not echoed in the 201
- customer_email is required; omitting it surfaces as a misleading 400
- page-link does not rotate the token; regenerate-page-token revokes
- the customer page is served from the Connect host, language in the path
- the page token is signed, not encrypted — payload is readable base64
- the nonce is what makes revocation work at all
Refs #158909 #158910 #158912 #158915
New page at /developers/payments/autopay/ following the M-Wallet shape and
the section order mandated by docs/developers/CLAUDE.md.
- AutoPayFlowDiagram.tsx — inline theme-aware SVG, matching WalletFlowDiagram
and RecurringFlow; not Mermaid
- payments/index.md — AutoPay card and Choose Your Path row
- glossary-terms.ts — subscription, billing cycle, dunning
- recurring-payments.mdx — reverse half of the deconfliction cross-link, so a
reader who lands on auto_debit expecting subscriptions is redirected
## API Reference is intentionally a placeholder: core_backend PR 121 has not
merged, so the public OpenAPI schema serves no AutoPay endpoints and
<ApiDocEmbed> cannot resolve. The six endpoints are documented as prose and
cURL under ## Guide instead.
Known open item: the endpoint samples use AutoPay's own root-mounted paths
(/subscriptions/...). Merchants reach these through a Connect proxy whose
public path is in PR 121 and is not yet confirmed. Pending Yamen.
Refs #158910 #158909
New page at /business/autopay/ for merchant admins and operations staff.
One long page with a Pattern-A anchor sub-menu, matching what M-Wallet
actually shipped rather than what its spec called for.
Eleven sections, every heading carrying an explicit anchor id so renaming
the visible text cannot silently break sidebars.ts. Right-hand table of
contents kept. Zero code samples.
All 22 of Menna's screenshots wired through <StepGuide>; every referenced
image resolves to a real file.
States plainly, as the ticket requires:
- AutoPay never auto-cancels a past_due subscription — it stays past_due
indefinitely until the customer pays or the merchant cancels
- a customer cancellation always takes effect at the end of the current
period; only the merchant can cancel immediately
Merchant setup is documented as API-only. There is no merchant dashboard in
this phase (confirmed by Dacian, 2026-08-13) and no merchant screenshots.
No video — the Remotion pipeline is #158914 and is decoupled.
Refs #158912 #158909 #158913
The Expired step claimed the page "still offers a way to pick things back
up". Source says otherwise:
- subscriptions/models.py has zero @transition with source=EXPIRED — it is
a one-way terminal state
- customer_api.py reactivate() requires status==ACTIVE AND
cancel_at_period_end==true, and 422s otherwise; an expired subscription
can never satisfy that
Separately: portal-04-expired.png shows a Reactivate button that the
backend would reject, and labels the date field "Trial Ends" under an
Expired badge. Raised with the AutoPay self-service frontend owners — the
screenshot looks like a mockup rather than real Expired-state UI.
Refs #158912 #158913
158910 :: docs: AutoPay developer section
158912 :: docs: AutoPay business section
Three claims on the business page described the mockups rather than the
product. Corrected against the screen data pack
(autopay/docs/2026-08-13-autopay-screen-data-pack.md §2, §5):
- AutoPay stores no invoice and no PDF. Dropped 'downloadable invoices'
and 'invoice downloads'; a history row ends at its status.
- There is no delete-card action. Cards are kept as history by design so
an inactive card still explains an earlier cycle. Switching the active
card is the only card management there is.
- Pay Now only exists on a past-due subscription. Removed it from the
active-state description.
Also dropped the cards-01-delete-confirm.png reference — it depicts a flow
that does not exist. Per the data pack it should be recut as a
set-active-card confirm.
Root cause worth recording: the mockups themselves are wrong (portal-01
shows an invoice Download column and a Pay Now button on an active
subscription). The page described them faithfully. Backend-sourced
behaviour was all correct; only screenshot-derived UI claims were affected.
Refs #158912 #158913
… flag JWT gap
Adversarial verification of both pages found three real defects.
BLOCKER — retry_window_days is not a live field. It was removed by
subscriptions/migrations/0028_drop_retry_window_days.py (2026-06-15) and
appears nowhere outside migrations; the current SubscriptionInternalCreateIn
has no such field. Sending it today is silently dropped. Documenting it with
a 1-30 range misrepresented working behaviour. Note that ticket #158911's
own field table still lists it — the ticket is stale, not the writer.
MAJOR — the business page told readers Expired is terminal (correct: zero
@transition with source=EXPIRED, and reactivate() requires status==ACTIVE)
while displaying portal-04-expired.png, which shows a prominent Reactivate
button. Dropped the screenshot rather than caption a contradiction.
MAJOR — five of the six management endpoints need a Keycloak JWT and the
page never said how to get one; the linked authentication guide covers only
Api-Key. Added a :::warning naming it as an open gap so a reader is not left
guessing.
Known minor, not fixed: glossary terms added in this epic are not linked
from the AutoPay page. Glossary term anchors do not exist in the built
output at all (a pre-existing site-wide issue), so linking them would add
broken anchors.
Refs #158909 #158910 #158911 #158912
…duled cycle
Found by visual verification in Chrome against the local production build.
- AutoPayFlowDiagram: the 'ongoing' label's white background rect (x560-650)
painted over the tail of 'creates subscription - sync' (x450-600), which
rendered as 'creates subscriptior.'. Moved 'ongoing' below its own arrow.
Verified in both light and dark mode.
- Business page said customers 'look back through their full billing
history'. The real betabulk portal shows the NEXT cycle in the same table
with a Scheduled status, so the table is not past-only. Reworded.
Refs #158909 #158910 #158912
Editorial pass against docs/business/wallet/index.md as the house reference.
- Removed the only inline code span on the page. It was introduced by the
Scheduled-cycle edit in a76caaf and broke the zero-code audience rule that
#158912's acceptance criteria require.
- Replaced $0 with 'zero' in three places. Ottu prices in KWD/SAR and the
wallet business page uses no dollar signs at all.
- 'pending setup' -> 'Pending setup' in the lifecycle table, matching the
Title Case used for every other state name on the page.
Refs #158912
Editorial pass against docs/business/wallet/index.md, measured before and
after. Prose only — no structural, anchor, or image changes.
- StepGuide captions 20 -> 13 words average (wallet reference: 7). Captions
now point at the screenshot instead of arguing with it.
- Em-dashes 20.3 -> 16.0 per 100 lines (wallet: 14.6).
- Removed five marketing idioms with no precedent on the wallet page:
'without lifting a finger', 'in the driver's seat', 'doesn't move the
needle', 'running a billing engine by hand', 'a heads-up'.
Every factual claim is unchanged; this only affects how they are worded.
Refs #158912
…cle statuses
Accuracy and consistency pass on both AutoPay pages, verified against
autopay/subscriptions/{enums,schemas}.py.
- notification_preferences: documented the three real boolean keys —
pre_charge_enabled, failure_enabled, final_failure_enabled, each
defaulting to true (schemas.py:52-54). Previously described only as
'free-form'.
- List subscriptions returns a slim summary per item, not the full
subscription object. Callers need Retrieve for the active card and
latest-cycle detail.
- Cycle statuses: added retry_scheduled and canceled, completing the set
a merchant can observe. needs_triage is deliberately omitted — it is an
ops-only state reached by stale-cleanup, not something a merchant acts on.
- US spelling for consistency with the rest of the docs: catalogue ->
catalog, cancelling -> canceling.
Refs #158909 #158910 #158912
@jab3z

Copy link
Copy Markdown
Contributor

@claude review

@claude

claudeBot commented Aug 13, 2026

Copy link
Copy Markdown

Claude finished @jab3z's task in 3m 14s —— View job


Review complete

  • Gather context (PR body, changed files list)
  • Read diff (git diff origin/dev...HEAD)
  • Review developer AutoPay page + flow diagram
  • Review business AutoPay page
  • Review sidebars.ts, glossary, cross-links
  • Check CLAUDE.md compliance (API base URL constant, merchant_id, cross-linking, etc.)
  • Post final review

Note: node_modules isn't installed in this sandbox and installing it wasn't in scope, so I couldn't independently re-run npm run build/npm run typecheck — this review is a manual read of the diff against origin/dev and the repo's CLAUDE.md conventions.

Overall this is a strong, well-verified page pair — the sidebar anchor hygiene (sidebars.ts) all checks out against actual heading IDs on both pages, the <AutoPayFlowDiagram> follows the svg-diagram skill convention correctly (monochrome + 2 accent nodes, light/dark overrides), OTTU_CONNECT_BASE_URL is used consistently instead of hardcoded URLs, and the cross-linking between AutoPay and recurring-payments.mdx is exactly the kind of disambiguation the domain needs. A few things worth a look before merging to dev:

1. "Copy-paste accurate" claim conflicts with the PR's own "base path unconfirmed" gap

docs/developers/payments/autopay/index.mdx:437-441 (API Reference section):

"Every request and response shown above is copy-paste accurate"

But the PR description itself flags "Endpoint base path unconfirmed... Merchants reach these through a Connect proxy whose public path is in PR 121 — @yamen needs your answer." The six subscription-management cURL examples (#step-by-step) hit plain ${OTTU_CONNECT_BASE_URL}/subscriptions/... with no proxy prefix, while the one endpoint you have confirmed (checkout, step 1) is prefixed /b/checkout/v1/pymt-txn/. Since the base path for steps 2–6 is explicitly still pending confirmation, the "copy-paste accurate" claim overstates what's verified — a developer could reasonably build against /subscriptions/... directly and hit 404s once the real proxy path lands. Consider either softening that line or adding the same base-path caveat that's already implicit in the PR's "known gaps" table, so it's visible to a reader on docs.ottu.dev, not just to reviewers on this PR.
Fix this →

2. New glossary terms aren't linked from either new page

src/data/glossary-terms.ts adds Billing Cycle, Dunning, and Subscription, all introduced specifically for AutoPay — but neither docs/developers/payments/autopay/index.mdx nor docs/business/autopay/index.md links to /glossary/ on first use, despite root CLAUDE.md's "no jargon without definition — first use links to the glossary" rule and the developer CLAUDE.md cross-referencing table. (This rule is loosely followed elsewhere in the repo too, so treat this as a nice-to-have rather than a blocker — but since these terms were added for this PR, it's a natural fit to wire them up here.)

3. Two shipped screenshots are never referenced, one contradicts the page copy

git diff --stat shows 20 new business screenshots, but only 18 are referenced from docs/business/autopay/index.md:

  • static/img/business/autopay/portal-04-expired.png — the "Expired" step in the state <StepGuide> (docs/business/autopay/index.md:135-138) has no image/imageAlt, even though this exact screenshot exists.
  • static/img/business/autopay/cards-01-delete-confirm.png — unreferenced anywhere. This one is worth double-checking specifically: the page states "Saved cards are never deleted... there is no removal step to walk them through" (line 195), but a screenshot named cards-01-delete-confirm strongly implies a delete-confirmation dialog exists in the product. Given the PR already flagged portal-01-summary-active.png as a stale mockup that contradicts the real product, this looks like it could be the same class of issue — either the copy is right and this is a leftover asset that should be dropped, or card deletion does exist and the copy needs correcting. Worth a quick check with @Menna/@junaid alongside the portal-01 fix.

Not re-litigated

The factual claims (write-only autopay block, customer_email 422 behavior, page-link vs regenerate-page-token, dead retry_window_days, slim list summary, signed-not-encrypted token) all match what's described in the PR body as independently verified against autopay/subscriptions/ source and a live betabulk call — I didn't have backend access to re-verify these, so I'm relying on the PR author's stated verification method there.
· branch epic/158909

…rop mockups
Three points from the PR 169 review.
The API Reference note claimed every request and response above was
copy-paste accurate, while the PR itself flags the Connect proxy prefix for
the six management endpoints as unconfirmed. That gap was visible to
reviewers and invisible to a reader on docs.ottu.dev. Replaced the claim
with a warning under Step-by-Step naming exactly what is uncertain — the
path in front of /subscriptions/ — and stating that the headers, bodies,
responses and behaviour are not, and that Step 1's /b/checkout/v1/pymt-txn/
is the stable Checkout API path.
Billing Cycle, Dunning and Subscription were added to glossary-terms.ts for
this PR and nothing linked to them. Linked each once per page via
/glossary/#term-<id>. GlossarySearch server-renders those ids, so the
anchors resolve natively; Docusaurus still reports them broken because its
checker only reads markdown headings — the same false positive already
fires for the #term-* links on payment-states and transaction-states.
portal-04-expired.png and cards-01-delete-confirm.png were unreferenced
because both depict flows that do not exist: the first shows a Reactivate
button on an Expired subscription (terminal in the backend) plus an invoice
Download column, the second a delete-card confirmation. Dereferenced in
b5475bf and 79835e2 respectively; deleting the assets so they cannot be
wired back in. They need recapture from betabulk alongside
portal-01-summary-active.png.
npm run build passes.
@ankitottu

Copy link
Copy Markdown
ContributorAuthor

@claude re-review

@claude

claudeBot commented Aug 13, 2026

Copy link
Copy Markdown

Claude finished @ankitottu's task in 1m 38s —— View job


Re-review complete

  • Gather context (PR body, prior review, changed files)
  • Check what changed since the last review (commit 34e4484)
  • Verify the three prior findings were actually resolved
  • Fresh pass over the full diff for anything new
  • Post final review

Note: node_modules still isn't installed in this sandbox, so I couldn't independently re-run npm run build/npm run typecheck — this is a manual read of git diff origin/dev...HEAD plus the four fix commits added since the first review (b5475bf, 79835e2, 93c1567, 34e4484).

All three points from the first review are resolved cleanly in 34e4484:

1. "Copy-paste accurate" overclaim — fixed. docs/developers/payments/autopay/index.mdx:62-65 now has a dedicated :::warning The path in front of /subscriptions/ isn't final block naming exactly what's unconfirmed (the Connect proxy prefix for Steps 2–6) while explicitly stating headers/bodies/responses/behaviour are accurate, and that Step 1's /b/checkout/v1/pymt-txn/ is stable. The old blanket "copy-paste accurate" line under ## API Reference (line ~444) was replaced with a pointer to that scoped caveat instead of a second overclaim. This is exactly the right fix — it's visible to a reader on docs.ottu.dev now, not just to PR reviewers.

2. Glossary terms unlinked — fixed. Billing Cycle, Dunning, and Subscription are each now linked once per page via /glossary/#term-<id>. Verified the anchor mechanism actually resolves: src/components/GlossarySearch/index.tsx:7,30-31 builds/reads the same term-${id} hash format server-side, and the ids added in src/data/glossary-terms.ts (billing-cycle, dunning, subscription) match what's linked. The commit message's note that Docusaurus's broken-link checker will still flag these as a false positive (same pattern as the existing payment-states/transaction-states pages) checks out.

3. Unreferenced/contradictory screenshots — fixed correctly, not just papered over. Both cards-01-delete-confirm.png and portal-04-expired.png are deleted from the tree (not just dereferenced), so they can't get silently wired back in later. Confirmed no remaining references to either filename anywhere in the repo, and confirmed all 19 remaining business screenshots (20 shipped − 2 dropped − wait, 18 referenced + the dropped 2 = 20 ✓) are each referenced from docs/business/autopay/index.md. The "Expired" step in the state <StepGuide> (docs/business/autopay/index.md:135-138) now correctly has no image/imageAlt at all, matching its terminal-state description instead of showing a fabricated Reactivate button.

One thing worth noting rather than re-flagging: portal-01-summary-active.png (the hero mockup the PR body itself calls out as contradicting the real product) is still in place — that's expected, since the PR description already tracks it as the open "highest-value screenshot task left" pending recapture from betabulk, not something silently missed.

Also scanned the intervening accuracy commit (93c1567, notification keys / list-summary shape / cycle statuses) — it's a good-faith correctness pass against autopay/subscriptions/{enums,schemas}.py unrelated to the review findings, nothing to flag there.

Nothing new surfaced in this pass. The two page diffs are clean additions with no leftover inconsistencies from the fix-up commits.
· branch

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ankitottu@jab3z
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

158909 :: docs: AutoPay public documentation - #169

Merged
ankitottu merged 14 commits into
devfrom
epic/158909
Aug 13, 2026
Merged

158909 :: docs: AutoPay public documentation#169
ankitottu merged 14 commits into
devfrom
epic/158909

Conversation

@ankitottu

Copy link
Copy Markdown
Contributor

Redmine epic #158909 — closes the docs half of the AutoPay launch.

Merges the epic branch into dev, which deploys to docs.ottu.dev for review. Nothing reaches docs.ottu.com until it has been signed off there — that is #158915.

What ships

#158910Developer section at /developers/payments/autopay/ + AutoPayFlowDiagram + glossary terms + payments-index entry
#158912Business section at /business/autopay/, one long page with a Pattern-A anchor sub-menu
#158913 (partial)20 customer-portal and notification screenshots from Menna
Design spec + execution plan under docs/superpowers/

Both child PRs (#167, #168) already merged here. sidebars.ts was edited once, in the scaffold commit, so the two lanes never touched the same file.

The distinction the whole thing hangs on

With auto_debit the merchant owns the billing schedule, retries and dunning. With auto_pay, AutoPay owns them.

recurring-payments.mdx is the closest existing page and readers will land on the wrong one, so both pages now cross-link with an explicit "use this when".

Facts documented that appear in no ticket

Found by reading autopay/subscriptions/ and by a live betabulk call:

  • The autopay request block is write-only — not echoed in the 201. extra.autopay.subscription_id is the merchant's only handle. Verified live: sub_hA1YYmZz1xk8speCiPbOquIP.
  • customer_email is required. Omitting it 422s inside AutoPay and surfaces as 400 {"autopay":["AutoPayClient rejected request (422)"]} — reads like an agreement-shape error and is not.
  • page-link does not rotate the token; regenerate-page-token revokes. A merchant reaching for regenerate when they meant re-send silently breaks the customer's working link, with no notification to that customer.
  • retry_window_days is dead — dropped in migrations/0028_drop_retry_window_days.py. ⚠️#158911's field table still lists it as live with a 1–30 range. That table is also the spec for the enrichment YAML, so it needs correcting there too.
  • list returns a slim summary, not full subscription objects.
  • The customer page token is signed, not encrypted — the payload is readable base64. Treat the link like a password.

Known gaps, deliberate

GapWhy
## API Reference is a placeholdercore_backend PR 121 unmerged — static/Ottu_API.yaml has zero autopay occurrences, so <ApiDocEmbed> cannot resolve. Endpoints documented as prose + cURL under ## Guide instead.
Endpoint base path unconfirmedSamples use AutoPay's own root-mounted paths. Merchants reach these through a Connect proxy whose public path is in PR 121. @yamen — needs your answer.
JWT acquisition undocumentedFive of six endpoints need a Keycloak JWT; the auth guide only covers Api-Key. Flagged in a :::warning on the page rather than papered over.
No merchant dashboard contentIt does not exist — REST-API-only this phase, confirmed by @dacian 2026-08-13.
No add-card screenshotNot captured yet; behaviour written as prose, no image referenced.
No videoRemotion is #158914, decoupled.

Verification

  • npm run typecheck ✅ · npm run build
  • Both pages adversarially verified by subagents against their tickets' own acceptance criteria: developer 14 PASS / 1 BLOCKED, business 13 PASS
  • Checked by hand in Chrome against the local production build — onBrokenLinks is "warn", so a green build proves nothing about navigation:
    • business sidebar anchors 10/10 resolve, developer 5/5, in-page links 21/21
    • every referenced image returns HTTP 200
    • flow diagram renders correctly in light and dark mode
  • Every behavioural claim traced to autopay/subscriptions/enums.py, models.py, schemas.py or tasks/_tenant_processors.py

⚠️ One thing to look at before approving

portal-01-summary-active.png is the hero image on the business page and it is a mockup that contradicts the real product. Checked against a live betabulk portal: it shows an invoice Download column (AutoPay stores no invoices), a Pay Now button on an active subscription (Pay Now only exists when past due), two cards both masked *** 1234, and a next-payment date a year adrift from its own history.

The prose no longer describes any of it, but a reader still sees it. Recutting that one image — or a straight recapture from betabulk — is the highest-value screenshot task left. @junaid@Menna

Sign-off needed (#158915)

  • @yamen — backend and API accuracy, PR 121, proxy base path
  • @ankit — AutoPay behaviour and business accuracy
  • @junaid / @Menna — visual review

Do not merge until reviewed on this branch.

…enshots
Lane-0 scaffold so #158910 and #158912 can be written in parallel without
both editing sidebars.ts.
- developerSidebar: AutoPay Pattern-A anchor category after M-Wallet
- businessSidebar: AutoPay Pattern-A anchor category after M-Wallet
- skeleton pages pinning every heading id the sidebar links to
- 22 customer-portal and notification screenshots from Menna
(real PNGs, largest 154 KB, wallet naming convention, no stubs)
Merchant-side screens are deliberately absent: AutoPay is REST-API-only
on the merchant side in this phase (confirmed by Dacian, 2026-08-13).
Refs #158909 #158910 #158912 #158913
Mirrors the M-Wallet pair the epic names as the reference model.
The plan carries a verified-facts ledger with a source citation for every
behavioural claim, plus six findings that appear in no ticket:
- the autopay request block is write-only, not echoed in the 201
- customer_email is required; omitting it surfaces as a misleading 400
- page-link does not rotate the token; regenerate-page-token revokes
- the customer page is served from the Connect host, language in the path
- the page token is signed, not encrypted — payload is readable base64
- the nonce is what makes revocation work at all
Refs #158909 #158910 #158912 #158915
New page at /developers/payments/autopay/ following the M-Wallet shape and
the section order mandated by docs/developers/CLAUDE.md.
- AutoPayFlowDiagram.tsx — inline theme-aware SVG, matching WalletFlowDiagram
and RecurringFlow; not Mermaid
- payments/index.md — AutoPay card and Choose Your Path row
- glossary-terms.ts — subscription, billing cycle, dunning
- recurring-payments.mdx — reverse half of the deconfliction cross-link, so a
reader who lands on auto_debit expecting subscriptions is redirected
## API Reference is intentionally a placeholder: core_backend PR 121 has not
merged, so the public OpenAPI schema serves no AutoPay endpoints and
<ApiDocEmbed> cannot resolve. The six endpoints are documented as prose and
cURL under ## Guide instead.
Known open item: the endpoint samples use AutoPay's own root-mounted paths
(/subscriptions/...). Merchants reach these through a Connect proxy whose
public path is in PR 121 and is not yet confirmed. Pending Yamen.
Refs #158910 #158909
New page at /business/autopay/ for merchant admins and operations staff.
One long page with a Pattern-A anchor sub-menu, matching what M-Wallet
actually shipped rather than what its spec called for.
Eleven sections, every heading carrying an explicit anchor id so renaming
the visible text cannot silently break sidebars.ts. Right-hand table of
contents kept. Zero code samples.
All 22 of Menna's screenshots wired through <StepGuide>; every referenced
image resolves to a real file.
States plainly, as the ticket requires:
- AutoPay never auto-cancels a past_due subscription — it stays past_due
indefinitely until the customer pays or the merchant cancels
- a customer cancellation always takes effect at the end of the current
period; only the merchant can cancel immediately
Merchant setup is documented as API-only. There is no merchant dashboard in
this phase (confirmed by Dacian, 2026-08-13) and no merchant screenshots.
No video — the Remotion pipeline is #158914 and is decoupled.
Refs #158912 #158909 #158913
The Expired step claimed the page "still offers a way to pick things back
up". Source says otherwise:
- subscriptions/models.py has zero @transition with source=EXPIRED — it is
a one-way terminal state
- customer_api.py reactivate() requires status==ACTIVE AND
cancel_at_period_end==true, and 422s otherwise; an expired subscription
can never satisfy that
Separately: portal-04-expired.png shows a Reactivate button that the
backend would reject, and labels the date field "Trial Ends" under an
Expired badge. Raised with the AutoPay self-service frontend owners — the
screenshot looks like a mockup rather than real Expired-state UI.
Refs #158912 #158913
158910 :: docs: AutoPay developer section
158912 :: docs: AutoPay business section
Three claims on the business page described the mockups rather than the
product. Corrected against the screen data pack
(autopay/docs/2026-08-13-autopay-screen-data-pack.md §2, §5):
- AutoPay stores no invoice and no PDF. Dropped 'downloadable invoices'
and 'invoice downloads'; a history row ends at its status.
- There is no delete-card action. Cards are kept as history by design so
an inactive card still explains an earlier cycle. Switching the active
card is the only card management there is.
- Pay Now only exists on a past-due subscription. Removed it from the
active-state description.
Also dropped the cards-01-delete-confirm.png reference — it depicts a flow
that does not exist. Per the data pack it should be recut as a
set-active-card confirm.
Root cause worth recording: the mockups themselves are wrong (portal-01
shows an invoice Download column and a Pay Now button on an active
subscription). The page described them faithfully. Backend-sourced
behaviour was all correct; only screenshot-derived UI claims were affected.
Refs #158912 #158913
… flag JWT gap
Adversarial verification of both pages found three real defects.
BLOCKER — retry_window_days is not a live field. It was removed by
subscriptions/migrations/0028_drop_retry_window_days.py (2026-06-15) and
appears nowhere outside migrations; the current SubscriptionInternalCreateIn
has no such field. Sending it today is silently dropped. Documenting it with
a 1-30 range misrepresented working behaviour. Note that ticket #158911's
own field table still lists it — the ticket is stale, not the writer.
MAJOR — the business page told readers Expired is terminal (correct: zero
@transition with source=EXPIRED, and reactivate() requires status==ACTIVE)
while displaying portal-04-expired.png, which shows a prominent Reactivate
button. Dropped the screenshot rather than caption a contradiction.
MAJOR — five of the six management endpoints need a Keycloak JWT and the
page never said how to get one; the linked authentication guide covers only
Api-Key. Added a :::warning naming it as an open gap so a reader is not left
guessing.
Known minor, not fixed: glossary terms added in this epic are not linked
from the AutoPay page. Glossary term anchors do not exist in the built
output at all (a pre-existing site-wide issue), so linking them would add
broken anchors.
Refs #158909 #158910 #158911 #158912
…duled cycle
Found by visual verification in Chrome against the local production build.
- AutoPayFlowDiagram: the 'ongoing' label's white background rect (x560-650)
painted over the tail of 'creates subscription - sync' (x450-600), which
rendered as 'creates subscriptior.'. Moved 'ongoing' below its own arrow.
Verified in both light and dark mode.
- Business page said customers 'look back through their full billing
history'. The real betabulk portal shows the NEXT cycle in the same table
with a Scheduled status, so the table is not past-only. Reworded.
Refs #158909 #158910 #158912
Editorial pass against docs/business/wallet/index.md as the house reference.
- Removed the only inline code span on the page. It was introduced by the
Scheduled-cycle edit in a76caaf and broke the zero-code audience rule that
#158912's acceptance criteria require.
- Replaced $0 with 'zero' in three places. Ottu prices in KWD/SAR and the
wallet business page uses no dollar signs at all.
- 'pending setup' -> 'Pending setup' in the lifecycle table, matching the
Title Case used for every other state name on the page.
Refs #158912
Editorial pass against docs/business/wallet/index.md, measured before and
after. Prose only — no structural, anchor, or image changes.
- StepGuide captions 20 -> 13 words average (wallet reference: 7). Captions
now point at the screenshot instead of arguing with it.
- Em-dashes 20.3 -> 16.0 per 100 lines (wallet: 14.6).
- Removed five marketing idioms with no precedent on the wallet page:
'without lifting a finger', 'in the driver's seat', 'doesn't move the
needle', 'running a billing engine by hand', 'a heads-up'.
Every factual claim is unchanged; this only affects how they are worded.
Refs #158912
…cle statuses
Accuracy and consistency pass on both AutoPay pages, verified against
autopay/subscriptions/{enums,schemas}.py.
- notification_preferences: documented the three real boolean keys —
pre_charge_enabled, failure_enabled, final_failure_enabled, each
defaulting to true (schemas.py:52-54). Previously described only as
'free-form'.
- List subscriptions returns a slim summary per item, not the full
subscription object. Callers need Retrieve for the active card and
latest-cycle detail.
- Cycle statuses: added retry_scheduled and canceled, completing the set
a merchant can observe. needs_triage is deliberately omitted — it is an
ops-only state reached by stale-cleanup, not something a merchant acts on.
- US spelling for consistency with the rest of the docs: catalogue ->
catalog, cancelling -> canceling.
Refs #158909 #158910 #158912
@jab3z

Copy link
Copy Markdown
Contributor

@claude review

@claude

claudeBot commented Aug 13, 2026

Copy link
Copy Markdown

Claude finished @jab3z's task in 3m 14s —— View job


Review complete

  • Gather context (PR body, changed files list)
  • Read diff (git diff origin/dev...HEAD)
  • Review developer AutoPay page + flow diagram
  • Review business AutoPay page
  • Review sidebars.ts, glossary, cross-links
  • Check CLAUDE.md compliance (API base URL constant, merchant_id, cross-linking, etc.)
  • Post final review

Note: node_modules isn't installed in this sandbox and installing it wasn't in scope, so I couldn't independently re-run npm run build/npm run typecheck — this review is a manual read of the diff against origin/dev and the repo's CLAUDE.md conventions.

Overall this is a strong, well-verified page pair — the sidebar anchor hygiene (sidebars.ts) all checks out against actual heading IDs on both pages, the <AutoPayFlowDiagram> follows the svg-diagram skill convention correctly (monochrome + 2 accent nodes, light/dark overrides), OTTU_CONNECT_BASE_URL is used consistently instead of hardcoded URLs, and the cross-linking between AutoPay and recurring-payments.mdx is exactly the kind of disambiguation the domain needs. A few things worth a look before merging to dev:

1. "Copy-paste accurate" claim conflicts with the PR's own "base path unconfirmed" gap

docs/developers/payments/autopay/index.mdx:437-441 (API Reference section):

"Every request and response shown above is copy-paste accurate"

But the PR description itself flags "Endpoint base path unconfirmed... Merchants reach these through a Connect proxy whose public path is in PR 121 — @yamen needs your answer." The six subscription-management cURL examples (#step-by-step) hit plain ${OTTU_CONNECT_BASE_URL}/subscriptions/... with no proxy prefix, while the one endpoint you have confirmed (checkout, step 1) is prefixed /b/checkout/v1/pymt-txn/. Since the base path for steps 2–6 is explicitly still pending confirmation, the "copy-paste accurate" claim overstates what's verified — a developer could reasonably build against /subscriptions/... directly and hit 404s once the real proxy path lands. Consider either softening that line or adding the same base-path caveat that's already implicit in the PR's "known gaps" table, so it's visible to a reader on docs.ottu.dev, not just to reviewers on this PR.
Fix this →

2. New glossary terms aren't linked from either new page

src/data/glossary-terms.ts adds Billing Cycle, Dunning, and Subscription, all introduced specifically for AutoPay — but neither docs/developers/payments/autopay/index.mdx nor docs/business/autopay/index.md links to /glossary/ on first use, despite root CLAUDE.md's "no jargon without definition — first use links to the glossary" rule and the developer CLAUDE.md cross-referencing table. (This rule is loosely followed elsewhere in the repo too, so treat this as a nice-to-have rather than a blocker — but since these terms were added for this PR, it's a natural fit to wire them up here.)

3. Two shipped screenshots are never referenced, one contradicts the page copy

git diff --stat shows 20 new business screenshots, but only 18 are referenced from docs/business/autopay/index.md:

  • static/img/business/autopay/portal-04-expired.png — the "Expired" step in the state <StepGuide> (docs/business/autopay/index.md:135-138) has no image/imageAlt, even though this exact screenshot exists.
  • static/img/business/autopay/cards-01-delete-confirm.png — unreferenced anywhere. This one is worth double-checking specifically: the page states "Saved cards are never deleted... there is no removal step to walk them through" (line 195), but a screenshot named cards-01-delete-confirm strongly implies a delete-confirmation dialog exists in the product. Given the PR already flagged portal-01-summary-active.png as a stale mockup that contradicts the real product, this looks like it could be the same class of issue — either the copy is right and this is a leftover asset that should be dropped, or card deletion does exist and the copy needs correcting. Worth a quick check with @Menna/@junaid alongside the portal-01 fix.

Not re-litigated

The factual claims (write-only autopay block, customer_email 422 behavior, page-link vs regenerate-page-token, dead retry_window_days, slim list summary, signed-not-encrypted token) all match what's described in the PR body as independently verified against autopay/subscriptions/ source and a live betabulk call — I didn't have backend access to re-verify these, so I'm relying on the PR author's stated verification method there.
· branch epic/158909

…rop mockups
Three points from the PR 169 review.
The API Reference note claimed every request and response above was
copy-paste accurate, while the PR itself flags the Connect proxy prefix for
the six management endpoints as unconfirmed. That gap was visible to
reviewers and invisible to a reader on docs.ottu.dev. Replaced the claim
with a warning under Step-by-Step naming exactly what is uncertain — the
path in front of /subscriptions/ — and stating that the headers, bodies,
responses and behaviour are not, and that Step 1's /b/checkout/v1/pymt-txn/
is the stable Checkout API path.
Billing Cycle, Dunning and Subscription were added to glossary-terms.ts for
this PR and nothing linked to them. Linked each once per page via
/glossary/#term-<id>. GlossarySearch server-renders those ids, so the
anchors resolve natively; Docusaurus still reports them broken because its
checker only reads markdown headings — the same false positive already
fires for the #term-* links on payment-states and transaction-states.
portal-04-expired.png and cards-01-delete-confirm.png were unreferenced
because both depict flows that do not exist: the first shows a Reactivate
button on an Expired subscription (terminal in the backend) plus an invoice
Download column, the second a delete-card confirmation. Dereferenced in
b5475bf and 79835e2 respectively; deleting the assets so they cannot be
wired back in. They need recapture from betabulk alongside
portal-01-summary-active.png.
npm run build passes.
@ankitottu

Copy link
Copy Markdown
ContributorAuthor

@claude re-review

@claude

claudeBot commented Aug 13, 2026

Copy link
Copy Markdown

Claude finished @ankitottu's task in 1m 38s —— View job


Re-review complete

  • Gather context (PR body, prior review, changed files)
  • Check what changed since the last review (commit 34e4484)
  • Verify the three prior findings were actually resolved
  • Fresh pass over the full diff for anything new
  • Post final review

Note: node_modules still isn't installed in this sandbox, so I couldn't independently re-run npm run build/npm run typecheck — this is a manual read of git diff origin/dev...HEAD plus the four fix commits added since the first review (b5475bf, 79835e2, 93c1567, 34e4484).

All three points from the first review are resolved cleanly in 34e4484:

1. "Copy-paste accurate" overclaim — fixed. docs/developers/payments/autopay/index.mdx:62-65 now has a dedicated :::warning The path in front of /subscriptions/ isn't final block naming exactly what's unconfirmed (the Connect proxy prefix for Steps 2–6) while explicitly stating headers/bodies/responses/behaviour are accurate, and that Step 1's /b/checkout/v1/pymt-txn/ is stable. The old blanket "copy-paste accurate" line under ## API Reference (line ~444) was replaced with a pointer to that scoped caveat instead of a second overclaim. This is exactly the right fix — it's visible to a reader on docs.ottu.dev now, not just to PR reviewers.

2. Glossary terms unlinked — fixed. Billing Cycle, Dunning, and Subscription are each now linked once per page via /glossary/#term-<id>. Verified the anchor mechanism actually resolves: src/components/GlossarySearch/index.tsx:7,30-31 builds/reads the same term-${id} hash format server-side, and the ids added in src/data/glossary-terms.ts (billing-cycle, dunning, subscription) match what's linked. The commit message's note that Docusaurus's broken-link checker will still flag these as a false positive (same pattern as the existing payment-states/transaction-states pages) checks out.

3. Unreferenced/contradictory screenshots — fixed correctly, not just papered over. Both cards-01-delete-confirm.png and portal-04-expired.png are deleted from the tree (not just dereferenced), so they can't get silently wired back in later. Confirmed no remaining references to either filename anywhere in the repo, and confirmed all 19 remaining business screenshots (20 shipped − 2 dropped − wait, 18 referenced + the dropped 2 = 20 ✓) are each referenced from docs/business/autopay/index.md. The "Expired" step in the state <StepGuide> (docs/business/autopay/index.md:135-138) now correctly has no image/imageAlt at all, matching its terminal-state description instead of showing a fabricated Reactivate button.

One thing worth noting rather than re-flagging: portal-01-summary-active.png (the hero mockup the PR body itself calls out as contradicting the real product) is still in place — that's expected, since the PR description already tracks it as the open "highest-value screenshot task left" pending recapture from betabulk, not something silently missed.

Also scanned the intervening accuracy commit (93c1567, notification keys / list-summary shape / cycle statuses) — it's a good-faith correctness pass against autopay/subscriptions/{enums,schemas}.py unrelated to the review findings, nothing to flag there.

Nothing new surfaced in this pass. The two page diffs are clean additions with no leftover inconsistencies from the fix-up commits.
· branch

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ankitottu@jab3z
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

158909 :: docs: AutoPay public documentation - #169

Merged
ankitottu merged 14 commits into
devfrom
epic/158909
Aug 13, 2026
Merged

158909 :: docs: AutoPay public documentation#169
ankitottu merged 14 commits into
devfrom
epic/158909

Conversation

@ankitottu

Copy link
Copy Markdown
Contributor

Redmine epic #158909 — closes the docs half of the AutoPay launch.

Merges the epic branch into dev, which deploys to docs.ottu.dev for review. Nothing reaches docs.ottu.com until it has been signed off there — that is #158915.

What ships

#158910Developer section at /developers/payments/autopay/ + AutoPayFlowDiagram + glossary terms + payments-index entry
#158912Business section at /business/autopay/, one long page with a Pattern-A anchor sub-menu
#158913 (partial)20 customer-portal and notification screenshots from Menna
Design spec + execution plan under docs/superpowers/

Both child PRs (#167, #168) already merged here. sidebars.ts was edited once, in the scaffold commit, so the two lanes never touched the same file.

The distinction the whole thing hangs on

With auto_debit the merchant owns the billing schedule, retries and dunning. With auto_pay, AutoPay owns them.

recurring-payments.mdx is the closest existing page and readers will land on the wrong one, so both pages now cross-link with an explicit "use this when".

Facts documented that appear in no ticket

Found by reading autopay/subscriptions/ and by a live betabulk call:

  • The autopay request block is write-only — not echoed in the 201. extra.autopay.subscription_id is the merchant's only handle. Verified live: sub_hA1YYmZz1xk8speCiPbOquIP.
  • customer_email is required. Omitting it 422s inside AutoPay and surfaces as 400 {"autopay":["AutoPayClient rejected request (422)"]} — reads like an agreement-shape error and is not.
  • page-link does not rotate the token; regenerate-page-token revokes. A merchant reaching for regenerate when they meant re-send silently breaks the customer's working link, with no notification to that customer.
  • retry_window_days is dead — dropped in migrations/0028_drop_retry_window_days.py. ⚠️#158911's field table still lists it as live with a 1–30 range. That table is also the spec for the enrichment YAML, so it needs correcting there too.
  • list returns a slim summary, not full subscription objects.
  • The customer page token is signed, not encrypted — the payload is readable base64. Treat the link like a password.

Known gaps, deliberate

GapWhy
## API Reference is a placeholdercore_backend PR 121 unmerged — static/Ottu_API.yaml has zero autopay occurrences, so <ApiDocEmbed> cannot resolve. Endpoints documented as prose + cURL under ## Guide instead.
Endpoint base path unconfirmedSamples use AutoPay's own root-mounted paths. Merchants reach these through a Connect proxy whose public path is in PR 121. @yamen — needs your answer.
JWT acquisition undocumentedFive of six endpoints need a Keycloak JWT; the auth guide only covers Api-Key. Flagged in a :::warning on the page rather than papered over.
No merchant dashboard contentIt does not exist — REST-API-only this phase, confirmed by @dacian 2026-08-13.
No add-card screenshotNot captured yet; behaviour written as prose, no image referenced.
No videoRemotion is #158914, decoupled.

Verification

  • npm run typecheck ✅ · npm run build
  • Both pages adversarially verified by subagents against their tickets' own acceptance criteria: developer 14 PASS / 1 BLOCKED, business 13 PASS
  • Checked by hand in Chrome against the local production build — onBrokenLinks is "warn", so a green build proves nothing about navigation:
    • business sidebar anchors 10/10 resolve, developer 5/5, in-page links 21/21
    • every referenced image returns HTTP 200
    • flow diagram renders correctly in light and dark mode
  • Every behavioural claim traced to autopay/subscriptions/enums.py, models.py, schemas.py or tasks/_tenant_processors.py

⚠️ One thing to look at before approving

portal-01-summary-active.png is the hero image on the business page and it is a mockup that contradicts the real product. Checked against a live betabulk portal: it shows an invoice Download column (AutoPay stores no invoices), a Pay Now button on an active subscription (Pay Now only exists when past due), two cards both masked *** 1234, and a next-payment date a year adrift from its own history.

The prose no longer describes any of it, but a reader still sees it. Recutting that one image — or a straight recapture from betabulk — is the highest-value screenshot task left. @junaid@Menna

Sign-off needed (#158915)

  • @yamen — backend and API accuracy, PR 121, proxy base path
  • @ankit — AutoPay behaviour and business accuracy
  • @junaid / @Menna — visual review

Do not merge until reviewed on this branch.

…enshots
Lane-0 scaffold so #158910 and #158912 can be written in parallel without
both editing sidebars.ts.
- developerSidebar: AutoPay Pattern-A anchor category after M-Wallet
- businessSidebar: AutoPay Pattern-A anchor category after M-Wallet
- skeleton pages pinning every heading id the sidebar links to
- 22 customer-portal and notification screenshots from Menna
(real PNGs, largest 154 KB, wallet naming convention, no stubs)
Merchant-side screens are deliberately absent: AutoPay is REST-API-only
on the merchant side in this phase (confirmed by Dacian, 2026-08-13).
Refs #158909 #158910 #158912 #158913
Mirrors the M-Wallet pair the epic names as the reference model.
The plan carries a verified-facts ledger with a source citation for every
behavioural claim, plus six findings that appear in no ticket:
- the autopay request block is write-only, not echoed in the 201
- customer_email is required; omitting it surfaces as a misleading 400
- page-link does not rotate the token; regenerate-page-token revokes
- the customer page is served from the Connect host, language in the path
- the page token is signed, not encrypted — payload is readable base64
- the nonce is what makes revocation work at all
Refs #158909 #158910 #158912 #158915
New page at /developers/payments/autopay/ following the M-Wallet shape and
the section order mandated by docs/developers/CLAUDE.md.
- AutoPayFlowDiagram.tsx — inline theme-aware SVG, matching WalletFlowDiagram
and RecurringFlow; not Mermaid
- payments/index.md — AutoPay card and Choose Your Path row
- glossary-terms.ts — subscription, billing cycle, dunning
- recurring-payments.mdx — reverse half of the deconfliction cross-link, so a
reader who lands on auto_debit expecting subscriptions is redirected
## API Reference is intentionally a placeholder: core_backend PR 121 has not
merged, so the public OpenAPI schema serves no AutoPay endpoints and
<ApiDocEmbed> cannot resolve. The six endpoints are documented as prose and
cURL under ## Guide instead.
Known open item: the endpoint samples use AutoPay's own root-mounted paths
(/subscriptions/...). Merchants reach these through a Connect proxy whose
public path is in PR 121 and is not yet confirmed. Pending Yamen.
Refs #158910 #158909
New page at /business/autopay/ for merchant admins and operations staff.
One long page with a Pattern-A anchor sub-menu, matching what M-Wallet
actually shipped rather than what its spec called for.
Eleven sections, every heading carrying an explicit anchor id so renaming
the visible text cannot silently break sidebars.ts. Right-hand table of
contents kept. Zero code samples.
All 22 of Menna's screenshots wired through <StepGuide>; every referenced
image resolves to a real file.
States plainly, as the ticket requires:
- AutoPay never auto-cancels a past_due subscription — it stays past_due
indefinitely until the customer pays or the merchant cancels
- a customer cancellation always takes effect at the end of the current
period; only the merchant can cancel immediately
Merchant setup is documented as API-only. There is no merchant dashboard in
this phase (confirmed by Dacian, 2026-08-13) and no merchant screenshots.
No video — the Remotion pipeline is #158914 and is decoupled.
Refs #158912 #158909 #158913
The Expired step claimed the page "still offers a way to pick things back
up". Source says otherwise:
- subscriptions/models.py has zero @transition with source=EXPIRED — it is
a one-way terminal state
- customer_api.py reactivate() requires status==ACTIVE AND
cancel_at_period_end==true, and 422s otherwise; an expired subscription
can never satisfy that
Separately: portal-04-expired.png shows a Reactivate button that the
backend would reject, and labels the date field "Trial Ends" under an
Expired badge. Raised with the AutoPay self-service frontend owners — the
screenshot looks like a mockup rather than real Expired-state UI.
Refs #158912 #158913
158910 :: docs: AutoPay developer section
158912 :: docs: AutoPay business section
Three claims on the business page described the mockups rather than the
product. Corrected against the screen data pack
(autopay/docs/2026-08-13-autopay-screen-data-pack.md §2, §5):
- AutoPay stores no invoice and no PDF. Dropped 'downloadable invoices'
and 'invoice downloads'; a history row ends at its status.
- There is no delete-card action. Cards are kept as history by design so
an inactive card still explains an earlier cycle. Switching the active
card is the only card management there is.
- Pay Now only exists on a past-due subscription. Removed it from the
active-state description.
Also dropped the cards-01-delete-confirm.png reference — it depicts a flow
that does not exist. Per the data pack it should be recut as a
set-active-card confirm.
Root cause worth recording: the mockups themselves are wrong (portal-01
shows an invoice Download column and a Pay Now button on an active
subscription). The page described them faithfully. Backend-sourced
behaviour was all correct; only screenshot-derived UI claims were affected.
Refs #158912 #158913
… flag JWT gap
Adversarial verification of both pages found three real defects.
BLOCKER — retry_window_days is not a live field. It was removed by
subscriptions/migrations/0028_drop_retry_window_days.py (2026-06-15) and
appears nowhere outside migrations; the current SubscriptionInternalCreateIn
has no such field. Sending it today is silently dropped. Documenting it with
a 1-30 range misrepresented working behaviour. Note that ticket #158911's
own field table still lists it — the ticket is stale, not the writer.
MAJOR — the business page told readers Expired is terminal (correct: zero
@transition with source=EXPIRED, and reactivate() requires status==ACTIVE)
while displaying portal-04-expired.png, which shows a prominent Reactivate
button. Dropped the screenshot rather than caption a contradiction.
MAJOR — five of the six management endpoints need a Keycloak JWT and the
page never said how to get one; the linked authentication guide covers only
Api-Key. Added a :::warning naming it as an open gap so a reader is not left
guessing.
Known minor, not fixed: glossary terms added in this epic are not linked
from the AutoPay page. Glossary term anchors do not exist in the built
output at all (a pre-existing site-wide issue), so linking them would add
broken anchors.
Refs #158909 #158910 #158911 #158912
…duled cycle
Found by visual verification in Chrome against the local production build.
- AutoPayFlowDiagram: the 'ongoing' label's white background rect (x560-650)
painted over the tail of 'creates subscription - sync' (x450-600), which
rendered as 'creates subscriptior.'. Moved 'ongoing' below its own arrow.
Verified in both light and dark mode.
- Business page said customers 'look back through their full billing
history'. The real betabulk portal shows the NEXT cycle in the same table
with a Scheduled status, so the table is not past-only. Reworded.
Refs #158909 #158910 #158912
Editorial pass against docs/business/wallet/index.md as the house reference.
- Removed the only inline code span on the page. It was introduced by the
Scheduled-cycle edit in a76caaf and broke the zero-code audience rule that
#158912's acceptance criteria require.
- Replaced $0 with 'zero' in three places. Ottu prices in KWD/SAR and the
wallet business page uses no dollar signs at all.
- 'pending setup' -> 'Pending setup' in the lifecycle table, matching the
Title Case used for every other state name on the page.
Refs #158912
Editorial pass against docs/business/wallet/index.md, measured before and
after. Prose only — no structural, anchor, or image changes.
- StepGuide captions 20 -> 13 words average (wallet reference: 7). Captions
now point at the screenshot instead of arguing with it.
- Em-dashes 20.3 -> 16.0 per 100 lines (wallet: 14.6).
- Removed five marketing idioms with no precedent on the wallet page:
'without lifting a finger', 'in the driver's seat', 'doesn't move the
needle', 'running a billing engine by hand', 'a heads-up'.
Every factual claim is unchanged; this only affects how they are worded.
Refs #158912
…cle statuses
Accuracy and consistency pass on both AutoPay pages, verified against
autopay/subscriptions/{enums,schemas}.py.
- notification_preferences: documented the three real boolean keys —
pre_charge_enabled, failure_enabled, final_failure_enabled, each
defaulting to true (schemas.py:52-54). Previously described only as
'free-form'.
- List subscriptions returns a slim summary per item, not the full
subscription object. Callers need Retrieve for the active card and
latest-cycle detail.
- Cycle statuses: added retry_scheduled and canceled, completing the set
a merchant can observe. needs_triage is deliberately omitted — it is an
ops-only state reached by stale-cleanup, not something a merchant acts on.
- US spelling for consistency with the rest of the docs: catalogue ->
catalog, cancelling -> canceling.
Refs #158909 #158910 #158912
@jab3z

Copy link
Copy Markdown
Contributor

@claude review

@claude

claudeBot commented Aug 13, 2026

Copy link
Copy Markdown

Claude finished @jab3z's task in 3m 14s —— View job


Review complete

  • Gather context (PR body, changed files list)
  • Read diff (git diff origin/dev...HEAD)
  • Review developer AutoPay page + flow diagram
  • Review business AutoPay page
  • Review sidebars.ts, glossary, cross-links
  • Check CLAUDE.md compliance (API base URL constant, merchant_id, cross-linking, etc.)
  • Post final review

Note: node_modules isn't installed in this sandbox and installing it wasn't in scope, so I couldn't independently re-run npm run build/npm run typecheck — this review is a manual read of the diff against origin/dev and the repo's CLAUDE.md conventions.

Overall this is a strong, well-verified page pair — the sidebar anchor hygiene (sidebars.ts) all checks out against actual heading IDs on both pages, the <AutoPayFlowDiagram> follows the svg-diagram skill convention correctly (monochrome + 2 accent nodes, light/dark overrides), OTTU_CONNECT_BASE_URL is used consistently instead of hardcoded URLs, and the cross-linking between AutoPay and recurring-payments.mdx is exactly the kind of disambiguation the domain needs. A few things worth a look before merging to dev:

1. "Copy-paste accurate" claim conflicts with the PR's own "base path unconfirmed" gap

docs/developers/payments/autopay/index.mdx:437-441 (API Reference section):

"Every request and response shown above is copy-paste accurate"

But the PR description itself flags "Endpoint base path unconfirmed... Merchants reach these through a Connect proxy whose public path is in PR 121 — @yamen needs your answer." The six subscription-management cURL examples (#step-by-step) hit plain ${OTTU_CONNECT_BASE_URL}/subscriptions/... with no proxy prefix, while the one endpoint you have confirmed (checkout, step 1) is prefixed /b/checkout/v1/pymt-txn/. Since the base path for steps 2–6 is explicitly still pending confirmation, the "copy-paste accurate" claim overstates what's verified — a developer could reasonably build against /subscriptions/... directly and hit 404s once the real proxy path lands. Consider either softening that line or adding the same base-path caveat that's already implicit in the PR's "known gaps" table, so it's visible to a reader on docs.ottu.dev, not just to reviewers on this PR.
Fix this →

2. New glossary terms aren't linked from either new page

src/data/glossary-terms.ts adds Billing Cycle, Dunning, and Subscription, all introduced specifically for AutoPay — but neither docs/developers/payments/autopay/index.mdx nor docs/business/autopay/index.md links to /glossary/ on first use, despite root CLAUDE.md's "no jargon without definition — first use links to the glossary" rule and the developer CLAUDE.md cross-referencing table. (This rule is loosely followed elsewhere in the repo too, so treat this as a nice-to-have rather than a blocker — but since these terms were added for this PR, it's a natural fit to wire them up here.)

3. Two shipped screenshots are never referenced, one contradicts the page copy

git diff --stat shows 20 new business screenshots, but only 18 are referenced from docs/business/autopay/index.md:

  • static/img/business/autopay/portal-04-expired.png — the "Expired" step in the state <StepGuide> (docs/business/autopay/index.md:135-138) has no image/imageAlt, even though this exact screenshot exists.
  • static/img/business/autopay/cards-01-delete-confirm.png — unreferenced anywhere. This one is worth double-checking specifically: the page states "Saved cards are never deleted... there is no removal step to walk them through" (line 195), but a screenshot named cards-01-delete-confirm strongly implies a delete-confirmation dialog exists in the product. Given the PR already flagged portal-01-summary-active.png as a stale mockup that contradicts the real product, this looks like it could be the same class of issue — either the copy is right and this is a leftover asset that should be dropped, or card deletion does exist and the copy needs correcting. Worth a quick check with @Menna/@junaid alongside the portal-01 fix.

Not re-litigated

The factual claims (write-only autopay block, customer_email 422 behavior, page-link vs regenerate-page-token, dead retry_window_days, slim list summary, signed-not-encrypted token) all match what's described in the PR body as independently verified against autopay/subscriptions/ source and a live betabulk call — I didn't have backend access to re-verify these, so I'm relying on the PR author's stated verification method there.
· branch epic/158909

…rop mockups
Three points from the PR 169 review.
The API Reference note claimed every request and response above was
copy-paste accurate, while the PR itself flags the Connect proxy prefix for
the six management endpoints as unconfirmed. That gap was visible to
reviewers and invisible to a reader on docs.ottu.dev. Replaced the claim
with a warning under Step-by-Step naming exactly what is uncertain — the
path in front of /subscriptions/ — and stating that the headers, bodies,
responses and behaviour are not, and that Step 1's /b/checkout/v1/pymt-txn/
is the stable Checkout API path.
Billing Cycle, Dunning and Subscription were added to glossary-terms.ts for
this PR and nothing linked to them. Linked each once per page via
/glossary/#term-<id>. GlossarySearch server-renders those ids, so the
anchors resolve natively; Docusaurus still reports them broken because its
checker only reads markdown headings — the same false positive already
fires for the #term-* links on payment-states and transaction-states.
portal-04-expired.png and cards-01-delete-confirm.png were unreferenced
because both depict flows that do not exist: the first shows a Reactivate
button on an Expired subscription (terminal in the backend) plus an invoice
Download column, the second a delete-card confirmation. Dereferenced in
b5475bf and 79835e2 respectively; deleting the assets so they cannot be
wired back in. They need recapture from betabulk alongside
portal-01-summary-active.png.
npm run build passes.
@ankitottu

Copy link
Copy Markdown
ContributorAuthor

@claude re-review

@claude

claudeBot commented Aug 13, 2026

Copy link
Copy Markdown

Claude finished @ankitottu's task in 1m 38s —— View job


Re-review complete

  • Gather context (PR body, prior review, changed files)
  • Check what changed since the last review (commit 34e4484)
  • Verify the three prior findings were actually resolved
  • Fresh pass over the full diff for anything new
  • Post final review

Note: node_modules still isn't installed in this sandbox, so I couldn't independently re-run npm run build/npm run typecheck — this is a manual read of git diff origin/dev...HEAD plus the four fix commits added since the first review (b5475bf, 79835e2, 93c1567, 34e4484).

All three points from the first review are resolved cleanly in 34e4484:

1. "Copy-paste accurate" overclaim — fixed. docs/developers/payments/autopay/index.mdx:62-65 now has a dedicated :::warning The path in front of /subscriptions/ isn't final block naming exactly what's unconfirmed (the Connect proxy prefix for Steps 2–6) while explicitly stating headers/bodies/responses/behaviour are accurate, and that Step 1's /b/checkout/v1/pymt-txn/ is stable. The old blanket "copy-paste accurate" line under ## API Reference (line ~444) was replaced with a pointer to that scoped caveat instead of a second overclaim. This is exactly the right fix — it's visible to a reader on docs.ottu.dev now, not just to PR reviewers.

2. Glossary terms unlinked — fixed. Billing Cycle, Dunning, and Subscription are each now linked once per page via /glossary/#term-<id>. Verified the anchor mechanism actually resolves: src/components/GlossarySearch/index.tsx:7,30-31 builds/reads the same term-${id} hash format server-side, and the ids added in src/data/glossary-terms.ts (billing-cycle, dunning, subscription) match what's linked. The commit message's note that Docusaurus's broken-link checker will still flag these as a false positive (same pattern as the existing payment-states/transaction-states pages) checks out.

3. Unreferenced/contradictory screenshots — fixed correctly, not just papered over. Both cards-01-delete-confirm.png and portal-04-expired.png are deleted from the tree (not just dereferenced), so they can't get silently wired back in later. Confirmed no remaining references to either filename anywhere in the repo, and confirmed all 19 remaining business screenshots (20 shipped − 2 dropped − wait, 18 referenced + the dropped 2 = 20 ✓) are each referenced from docs/business/autopay/index.md. The "Expired" step in the state <StepGuide> (docs/business/autopay/index.md:135-138) now correctly has no image/imageAlt at all, matching its terminal-state description instead of showing a fabricated Reactivate button.

One thing worth noting rather than re-flagging: portal-01-summary-active.png (the hero mockup the PR body itself calls out as contradicting the real product) is still in place — that's expected, since the PR description already tracks it as the open "highest-value screenshot task left" pending recapture from betabulk, not something silently missed.

Also scanned the intervening accuracy commit (93c1567, notification keys / list-summary shape / cycle statuses) — it's a good-faith correctness pass against autopay/subscriptions/{enums,schemas}.py unrelated to the review findings, nothing to flag there.

Nothing new surfaced in this pass. The two page diffs are clean additions with no leftover inconsistencies from the fix-up commits.
· branch

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ankitottu@jab3z
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

158909 :: docs: AutoPay public documentation - #169

Merged
ankitottu merged 14 commits into
devfrom
epic/158909
Aug 13, 2026
Merged

158909 :: docs: AutoPay public documentation#169
ankitottu merged 14 commits into
devfrom
epic/158909

Conversation

@ankitottu

Copy link
Copy Markdown
Contributor

Redmine epic #158909 — closes the docs half of the AutoPay launch.

Merges the epic branch into dev, which deploys to docs.ottu.dev for review. Nothing reaches docs.ottu.com until it has been signed off there — that is #158915.

What ships

#158910Developer section at /developers/payments/autopay/ + AutoPayFlowDiagram + glossary terms + payments-index entry
#158912Business section at /business/autopay/, one long page with a Pattern-A anchor sub-menu
#158913 (partial)20 customer-portal and notification screenshots from Menna
Design spec + execution plan under docs/superpowers/

Both child PRs (#167, #168) already merged here. sidebars.ts was edited once, in the scaffold commit, so the two lanes never touched the same file.

The distinction the whole thing hangs on

With auto_debit the merchant owns the billing schedule, retries and dunning. With auto_pay, AutoPay owns them.

recurring-payments.mdx is the closest existing page and readers will land on the wrong one, so both pages now cross-link with an explicit "use this when".

Facts documented that appear in no ticket

Found by reading autopay/subscriptions/ and by a live betabulk call:

  • The autopay request block is write-only — not echoed in the 201. extra.autopay.subscription_id is the merchant's only handle. Verified live: sub_hA1YYmZz1xk8speCiPbOquIP.
  • customer_email is required. Omitting it 422s inside AutoPay and surfaces as 400 {"autopay":["AutoPayClient rejected request (422)"]} — reads like an agreement-shape error and is not.
  • page-link does not rotate the token; regenerate-page-token revokes. A merchant reaching for regenerate when they meant re-send silently breaks the customer's working link, with no notification to that customer.
  • retry_window_days is dead — dropped in migrations/0028_drop_retry_window_days.py. ⚠️#158911's field table still lists it as live with a 1–30 range. That table is also the spec for the enrichment YAML, so it needs correcting there too.
  • list returns a slim summary, not full subscription objects.
  • The customer page token is signed, not encrypted — the payload is readable base64. Treat the link like a password.

Known gaps, deliberate

GapWhy
## API Reference is a placeholdercore_backend PR 121 unmerged — static/Ottu_API.yaml has zero autopay occurrences, so <ApiDocEmbed> cannot resolve. Endpoints documented as prose + cURL under ## Guide instead.
Endpoint base path unconfirmedSamples use AutoPay's own root-mounted paths. Merchants reach these through a Connect proxy whose public path is in PR 121. @yamen — needs your answer.
JWT acquisition undocumentedFive of six endpoints need a Keycloak JWT; the auth guide only covers Api-Key. Flagged in a :::warning on the page rather than papered over.
No merchant dashboard contentIt does not exist — REST-API-only this phase, confirmed by @dacian 2026-08-13.
No add-card screenshotNot captured yet; behaviour written as prose, no image referenced.
No videoRemotion is #158914, decoupled.

Verification

  • npm run typecheck ✅ · npm run build
  • Both pages adversarially verified by subagents against their tickets' own acceptance criteria: developer 14 PASS / 1 BLOCKED, business 13 PASS
  • Checked by hand in Chrome against the local production build — onBrokenLinks is "warn", so a green build proves nothing about navigation:
    • business sidebar anchors 10/10 resolve, developer 5/5, in-page links 21/21
    • every referenced image returns HTTP 200
    • flow diagram renders correctly in light and dark mode
  • Every behavioural claim traced to autopay/subscriptions/enums.py, models.py, schemas.py or tasks/_tenant_processors.py

⚠️ One thing to look at before approving

portal-01-summary-active.png is the hero image on the business page and it is a mockup that contradicts the real product. Checked against a live betabulk portal: it shows an invoice Download column (AutoPay stores no invoices), a Pay Now button on an active subscription (Pay Now only exists when past due), two cards both masked *** 1234, and a next-payment date a year adrift from its own history.

The prose no longer describes any of it, but a reader still sees it. Recutting that one image — or a straight recapture from betabulk — is the highest-value screenshot task left. @junaid@Menna

Sign-off needed (#158915)

  • @yamen — backend and API accuracy, PR 121, proxy base path
  • @ankit — AutoPay behaviour and business accuracy
  • @junaid / @Menna — visual review

Do not merge until reviewed on this branch.

…enshots
Lane-0 scaffold so #158910 and #158912 can be written in parallel without
both editing sidebars.ts.
- developerSidebar: AutoPay Pattern-A anchor category after M-Wallet
- businessSidebar: AutoPay Pattern-A anchor category after M-Wallet
- skeleton pages pinning every heading id the sidebar links to
- 22 customer-portal and notification screenshots from Menna
(real PNGs, largest 154 KB, wallet naming convention, no stubs)
Merchant-side screens are deliberately absent: AutoPay is REST-API-only
on the merchant side in this phase (confirmed by Dacian, 2026-08-13).
Refs #158909 #158910 #158912 #158913
Mirrors the M-Wallet pair the epic names as the reference model.
The plan carries a verified-facts ledger with a source citation for every
behavioural claim, plus six findings that appear in no ticket:
- the autopay request block is write-only, not echoed in the 201
- customer_email is required; omitting it surfaces as a misleading 400
- page-link does not rotate the token; regenerate-page-token revokes
- the customer page is served from the Connect host, language in the path
- the page token is signed, not encrypted — payload is readable base64
- the nonce is what makes revocation work at all
Refs #158909 #158910 #158912 #158915
New page at /developers/payments/autopay/ following the M-Wallet shape and
the section order mandated by docs/developers/CLAUDE.md.
- AutoPayFlowDiagram.tsx — inline theme-aware SVG, matching WalletFlowDiagram
and RecurringFlow; not Mermaid
- payments/index.md — AutoPay card and Choose Your Path row
- glossary-terms.ts — subscription, billing cycle, dunning
- recurring-payments.mdx — reverse half of the deconfliction cross-link, so a
reader who lands on auto_debit expecting subscriptions is redirected
## API Reference is intentionally a placeholder: core_backend PR 121 has not
merged, so the public OpenAPI schema serves no AutoPay endpoints and
<ApiDocEmbed> cannot resolve. The six endpoints are documented as prose and
cURL under ## Guide instead.
Known open item: the endpoint samples use AutoPay's own root-mounted paths
(/subscriptions/...). Merchants reach these through a Connect proxy whose
public path is in PR 121 and is not yet confirmed. Pending Yamen.
Refs #158910 #158909
New page at /business/autopay/ for merchant admins and operations staff.
One long page with a Pattern-A anchor sub-menu, matching what M-Wallet
actually shipped rather than what its spec called for.
Eleven sections, every heading carrying an explicit anchor id so renaming
the visible text cannot silently break sidebars.ts. Right-hand table of
contents kept. Zero code samples.
All 22 of Menna's screenshots wired through <StepGuide>; every referenced
image resolves to a real file.
States plainly, as the ticket requires:
- AutoPay never auto-cancels a past_due subscription — it stays past_due
indefinitely until the customer pays or the merchant cancels
- a customer cancellation always takes effect at the end of the current
period; only the merchant can cancel immediately
Merchant setup is documented as API-only. There is no merchant dashboard in
this phase (confirmed by Dacian, 2026-08-13) and no merchant screenshots.
No video — the Remotion pipeline is #158914 and is decoupled.
Refs #158912 #158909 #158913
The Expired step claimed the page "still offers a way to pick things back
up". Source says otherwise:
- subscriptions/models.py has zero @transition with source=EXPIRED — it is
a one-way terminal state
- customer_api.py reactivate() requires status==ACTIVE AND
cancel_at_period_end==true, and 422s otherwise; an expired subscription
can never satisfy that
Separately: portal-04-expired.png shows a Reactivate button that the
backend would reject, and labels the date field "Trial Ends" under an
Expired badge. Raised with the AutoPay self-service frontend owners — the
screenshot looks like a mockup rather than real Expired-state UI.
Refs #158912 #158913
158910 :: docs: AutoPay developer section
158912 :: docs: AutoPay business section
Three claims on the business page described the mockups rather than the
product. Corrected against the screen data pack
(autopay/docs/2026-08-13-autopay-screen-data-pack.md §2, §5):
- AutoPay stores no invoice and no PDF. Dropped 'downloadable invoices'
and 'invoice downloads'; a history row ends at its status.
- There is no delete-card action. Cards are kept as history by design so
an inactive card still explains an earlier cycle. Switching the active
card is the only card management there is.
- Pay Now only exists on a past-due subscription. Removed it from the
active-state description.
Also dropped the cards-01-delete-confirm.png reference — it depicts a flow
that does not exist. Per the data pack it should be recut as a
set-active-card confirm.
Root cause worth recording: the mockups themselves are wrong (portal-01
shows an invoice Download column and a Pay Now button on an active
subscription). The page described them faithfully. Backend-sourced
behaviour was all correct; only screenshot-derived UI claims were affected.
Refs #158912 #158913
… flag JWT gap
Adversarial verification of both pages found three real defects.
BLOCKER — retry_window_days is not a live field. It was removed by
subscriptions/migrations/0028_drop_retry_window_days.py (2026-06-15) and
appears nowhere outside migrations; the current SubscriptionInternalCreateIn
has no such field. Sending it today is silently dropped. Documenting it with
a 1-30 range misrepresented working behaviour. Note that ticket #158911's
own field table still lists it — the ticket is stale, not the writer.
MAJOR — the business page told readers Expired is terminal (correct: zero
@transition with source=EXPIRED, and reactivate() requires status==ACTIVE)
while displaying portal-04-expired.png, which shows a prominent Reactivate
button. Dropped the screenshot rather than caption a contradiction.
MAJOR — five of the six management endpoints need a Keycloak JWT and the
page never said how to get one; the linked authentication guide covers only
Api-Key. Added a :::warning naming it as an open gap so a reader is not left
guessing.
Known minor, not fixed: glossary terms added in this epic are not linked
from the AutoPay page. Glossary term anchors do not exist in the built
output at all (a pre-existing site-wide issue), so linking them would add
broken anchors.
Refs #158909 #158910 #158911 #158912
…duled cycle
Found by visual verification in Chrome against the local production build.
- AutoPayFlowDiagram: the 'ongoing' label's white background rect (x560-650)
painted over the tail of 'creates subscription - sync' (x450-600), which
rendered as 'creates subscriptior.'. Moved 'ongoing' below its own arrow.
Verified in both light and dark mode.
- Business page said customers 'look back through their full billing
history'. The real betabulk portal shows the NEXT cycle in the same table
with a Scheduled status, so the table is not past-only. Reworded.
Refs #158909 #158910 #158912
Editorial pass against docs/business/wallet/index.md as the house reference.
- Removed the only inline code span on the page. It was introduced by the
Scheduled-cycle edit in a76caaf and broke the zero-code audience rule that
#158912's acceptance criteria require.
- Replaced $0 with 'zero' in three places. Ottu prices in KWD/SAR and the
wallet business page uses no dollar signs at all.
- 'pending setup' -> 'Pending setup' in the lifecycle table, matching the
Title Case used for every other state name on the page.
Refs #158912
Editorial pass against docs/business/wallet/index.md, measured before and
after. Prose only — no structural, anchor, or image changes.
- StepGuide captions 20 -> 13 words average (wallet reference: 7). Captions
now point at the screenshot instead of arguing with it.
- Em-dashes 20.3 -> 16.0 per 100 lines (wallet: 14.6).
- Removed five marketing idioms with no precedent on the wallet page:
'without lifting a finger', 'in the driver's seat', 'doesn't move the
needle', 'running a billing engine by hand', 'a heads-up'.
Every factual claim is unchanged; this only affects how they are worded.
Refs #158912
…cle statuses
Accuracy and consistency pass on both AutoPay pages, verified against
autopay/subscriptions/{enums,schemas}.py.
- notification_preferences: documented the three real boolean keys —
pre_charge_enabled, failure_enabled, final_failure_enabled, each
defaulting to true (schemas.py:52-54). Previously described only as
'free-form'.
- List subscriptions returns a slim summary per item, not the full
subscription object. Callers need Retrieve for the active card and
latest-cycle detail.
- Cycle statuses: added retry_scheduled and canceled, completing the set
a merchant can observe. needs_triage is deliberately omitted — it is an
ops-only state reached by stale-cleanup, not something a merchant acts on.
- US spelling for consistency with the rest of the docs: catalogue ->
catalog, cancelling -> canceling.
Refs #158909 #158910 #158912
@jab3z

Copy link
Copy Markdown
Contributor

@claude review

@claude

claudeBot commented Aug 13, 2026

Copy link
Copy Markdown

Claude finished @jab3z's task in 3m 14s —— View job


Review complete

  • Gather context (PR body, changed files list)
  • Read diff (git diff origin/dev...HEAD)
  • Review developer AutoPay page + flow diagram
  • Review business AutoPay page
  • Review sidebars.ts, glossary, cross-links
  • Check CLAUDE.md compliance (API base URL constant, merchant_id, cross-linking, etc.)
  • Post final review

Note: node_modules isn't installed in this sandbox and installing it wasn't in scope, so I couldn't independently re-run npm run build/npm run typecheck — this review is a manual read of the diff against origin/dev and the repo's CLAUDE.md conventions.

Overall this is a strong, well-verified page pair — the sidebar anchor hygiene (sidebars.ts) all checks out against actual heading IDs on both pages, the <AutoPayFlowDiagram> follows the svg-diagram skill convention correctly (monochrome + 2 accent nodes, light/dark overrides), OTTU_CONNECT_BASE_URL is used consistently instead of hardcoded URLs, and the cross-linking between AutoPay and recurring-payments.mdx is exactly the kind of disambiguation the domain needs. A few things worth a look before merging to dev:

1. "Copy-paste accurate" claim conflicts with the PR's own "base path unconfirmed" gap

docs/developers/payments/autopay/index.mdx:437-441 (API Reference section):

"Every request and response shown above is copy-paste accurate"

But the PR description itself flags "Endpoint base path unconfirmed... Merchants reach these through a Connect proxy whose public path is in PR 121 — @yamen needs your answer." The six subscription-management cURL examples (#step-by-step) hit plain ${OTTU_CONNECT_BASE_URL}/subscriptions/... with no proxy prefix, while the one endpoint you have confirmed (checkout, step 1) is prefixed /b/checkout/v1/pymt-txn/. Since the base path for steps 2–6 is explicitly still pending confirmation, the "copy-paste accurate" claim overstates what's verified — a developer could reasonably build against /subscriptions/... directly and hit 404s once the real proxy path lands. Consider either softening that line or adding the same base-path caveat that's already implicit in the PR's "known gaps" table, so it's visible to a reader on docs.ottu.dev, not just to reviewers on this PR.
Fix this →

2. New glossary terms aren't linked from either new page

src/data/glossary-terms.ts adds Billing Cycle, Dunning, and Subscription, all introduced specifically for AutoPay — but neither docs/developers/payments/autopay/index.mdx nor docs/business/autopay/index.md links to /glossary/ on first use, despite root CLAUDE.md's "no jargon without definition — first use links to the glossary" rule and the developer CLAUDE.md cross-referencing table. (This rule is loosely followed elsewhere in the repo too, so treat this as a nice-to-have rather than a blocker — but since these terms were added for this PR, it's a natural fit to wire them up here.)

3. Two shipped screenshots are never referenced, one contradicts the page copy

git diff --stat shows 20 new business screenshots, but only 18 are referenced from docs/business/autopay/index.md:

  • static/img/business/autopay/portal-04-expired.png — the "Expired" step in the state <StepGuide> (docs/business/autopay/index.md:135-138) has no image/imageAlt, even though this exact screenshot exists.
  • static/img/business/autopay/cards-01-delete-confirm.png — unreferenced anywhere. This one is worth double-checking specifically: the page states "Saved cards are never deleted... there is no removal step to walk them through" (line 195), but a screenshot named cards-01-delete-confirm strongly implies a delete-confirmation dialog exists in the product. Given the PR already flagged portal-01-summary-active.png as a stale mockup that contradicts the real product, this looks like it could be the same class of issue — either the copy is right and this is a leftover asset that should be dropped, or card deletion does exist and the copy needs correcting. Worth a quick check with @Menna/@junaid alongside the portal-01 fix.

Not re-litigated

The factual claims (write-only autopay block, customer_email 422 behavior, page-link vs regenerate-page-token, dead retry_window_days, slim list summary, signed-not-encrypted token) all match what's described in the PR body as independently verified against autopay/subscriptions/ source and a live betabulk call — I didn't have backend access to re-verify these, so I'm relying on the PR author's stated verification method there.
· branch epic/158909

…rop mockups
Three points from the PR 169 review.
The API Reference note claimed every request and response above was
copy-paste accurate, while the PR itself flags the Connect proxy prefix for
the six management endpoints as unconfirmed. That gap was visible to
reviewers and invisible to a reader on docs.ottu.dev. Replaced the claim
with a warning under Step-by-Step naming exactly what is uncertain — the
path in front of /subscriptions/ — and stating that the headers, bodies,
responses and behaviour are not, and that Step 1's /b/checkout/v1/pymt-txn/
is the stable Checkout API path.
Billing Cycle, Dunning and Subscription were added to glossary-terms.ts for
this PR and nothing linked to them. Linked each once per page via
/glossary/#term-<id>. GlossarySearch server-renders those ids, so the
anchors resolve natively; Docusaurus still reports them broken because its
checker only reads markdown headings — the same false positive already
fires for the #term-* links on payment-states and transaction-states.
portal-04-expired.png and cards-01-delete-confirm.png were unreferenced
because both depict flows that do not exist: the first shows a Reactivate
button on an Expired subscription (terminal in the backend) plus an invoice
Download column, the second a delete-card confirmation. Dereferenced in
b5475bf and 79835e2 respectively; deleting the assets so they cannot be
wired back in. They need recapture from betabulk alongside
portal-01-summary-active.png.
npm run build passes.
@ankitottu

Copy link
Copy Markdown
ContributorAuthor

@claude re-review

@claude

claudeBot commented Aug 13, 2026

Copy link
Copy Markdown

Claude finished @ankitottu's task in 1m 38s —— View job


Re-review complete

  • Gather context (PR body, prior review, changed files)
  • Check what changed since the last review (commit 34e4484)
  • Verify the three prior findings were actually resolved
  • Fresh pass over the full diff for anything new
  • Post final review

Note: node_modules still isn't installed in this sandbox, so I couldn't independently re-run npm run build/npm run typecheck — this is a manual read of git diff origin/dev...HEAD plus the four fix commits added since the first review (b5475bf, 79835e2, 93c1567, 34e4484).

All three points from the first review are resolved cleanly in 34e4484:

1. "Copy-paste accurate" overclaim — fixed. docs/developers/payments/autopay/index.mdx:62-65 now has a dedicated :::warning The path in front of /subscriptions/ isn't final block naming exactly what's unconfirmed (the Connect proxy prefix for Steps 2–6) while explicitly stating headers/bodies/responses/behaviour are accurate, and that Step 1's /b/checkout/v1/pymt-txn/ is stable. The old blanket "copy-paste accurate" line under ## API Reference (line ~444) was replaced with a pointer to that scoped caveat instead of a second overclaim. This is exactly the right fix — it's visible to a reader on docs.ottu.dev now, not just to PR reviewers.

2. Glossary terms unlinked — fixed. Billing Cycle, Dunning, and Subscription are each now linked once per page via /glossary/#term-<id>. Verified the anchor mechanism actually resolves: src/components/GlossarySearch/index.tsx:7,30-31 builds/reads the same term-${id} hash format server-side, and the ids added in src/data/glossary-terms.ts (billing-cycle, dunning, subscription) match what's linked. The commit message's note that Docusaurus's broken-link checker will still flag these as a false positive (same pattern as the existing payment-states/transaction-states pages) checks out.

3. Unreferenced/contradictory screenshots — fixed correctly, not just papered over. Both cards-01-delete-confirm.png and portal-04-expired.png are deleted from the tree (not just dereferenced), so they can't get silently wired back in later. Confirmed no remaining references to either filename anywhere in the repo, and confirmed all 19 remaining business screenshots (20 shipped − 2 dropped − wait, 18 referenced + the dropped 2 = 20 ✓) are each referenced from docs/business/autopay/index.md. The "Expired" step in the state <StepGuide> (docs/business/autopay/index.md:135-138) now correctly has no image/imageAlt at all, matching its terminal-state description instead of showing a fabricated Reactivate button.

One thing worth noting rather than re-flagging: portal-01-summary-active.png (the hero mockup the PR body itself calls out as contradicting the real product) is still in place — that's expected, since the PR description already tracks it as the open "highest-value screenshot task left" pending recapture from betabulk, not something silently missed.

Also scanned the intervening accuracy commit (93c1567, notification keys / list-summary shape / cycle statuses) — it's a good-faith correctness pass against autopay/subscriptions/{enums,schemas}.py unrelated to the review findings, nothing to flag there.

Nothing new surfaced in this pass. The two page diffs are clean additions with no leftover inconsistencies from the fix-up commits.
· branch

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ankitottu@jab3z
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

158909 :: docs: AutoPay public documentation - #169

Merged
ankitottu merged 14 commits into
devfrom
epic/158909
Aug 13, 2026
Merged

158909 :: docs: AutoPay public documentation#169
ankitottu merged 14 commits into
devfrom
epic/158909

Conversation

@ankitottu

Copy link
Copy Markdown
Contributor

Redmine epic #158909 — closes the docs half of the AutoPay launch.

Merges the epic branch into dev, which deploys to docs.ottu.dev for review. Nothing reaches docs.ottu.com until it has been signed off there — that is #158915.

What ships

#158910Developer section at /developers/payments/autopay/ + AutoPayFlowDiagram + glossary terms + payments-index entry
#158912Business section at /business/autopay/, one long page with a Pattern-A anchor sub-menu
#158913 (partial)20 customer-portal and notification screenshots from Menna
Design spec + execution plan under docs/superpowers/

Both child PRs (#167, #168) already merged here. sidebars.ts was edited once, in the scaffold commit, so the two lanes never touched the same file.

The distinction the whole thing hangs on

With auto_debit the merchant owns the billing schedule, retries and dunning. With auto_pay, AutoPay owns them.

recurring-payments.mdx is the closest existing page and readers will land on the wrong one, so both pages now cross-link with an explicit "use this when".

Facts documented that appear in no ticket

Found by reading autopay/subscriptions/ and by a live betabulk call:

  • The autopay request block is write-only — not echoed in the 201. extra.autopay.subscription_id is the merchant's only handle. Verified live: sub_hA1YYmZz1xk8speCiPbOquIP.
  • customer_email is required. Omitting it 422s inside AutoPay and surfaces as 400 {"autopay":["AutoPayClient rejected request (422)"]} — reads like an agreement-shape error and is not.
  • page-link does not rotate the token; regenerate-page-token revokes. A merchant reaching for regenerate when they meant re-send silently breaks the customer's working link, with no notification to that customer.
  • retry_window_days is dead — dropped in migrations/0028_drop_retry_window_days.py. ⚠️#158911's field table still lists it as live with a 1–30 range. That table is also the spec for the enrichment YAML, so it needs correcting there too.
  • list returns a slim summary, not full subscription objects.
  • The customer page token is signed, not encrypted — the payload is readable base64. Treat the link like a password.

Known gaps, deliberate

GapWhy
## API Reference is a placeholdercore_backend PR 121 unmerged — static/Ottu_API.yaml has zero autopay occurrences, so <ApiDocEmbed> cannot resolve. Endpoints documented as prose + cURL under ## Guide instead.
Endpoint base path unconfirmedSamples use AutoPay's own root-mounted paths. Merchants reach these through a Connect proxy whose public path is in PR 121. @yamen — needs your answer.
JWT acquisition undocumentedFive of six endpoints need a Keycloak JWT; the auth guide only covers Api-Key. Flagged in a :::warning on the page rather than papered over.
No merchant dashboard contentIt does not exist — REST-API-only this phase, confirmed by @dacian 2026-08-13.
No add-card screenshotNot captured yet; behaviour written as prose, no image referenced.
No videoRemotion is #158914, decoupled.

Verification

  • npm run typecheck ✅ · npm run build
  • Both pages adversarially verified by subagents against their tickets' own acceptance criteria: developer 14 PASS / 1 BLOCKED, business 13 PASS
  • Checked by hand in Chrome against the local production build — onBrokenLinks is "warn", so a green build proves nothing about navigation:
    • business sidebar anchors 10/10 resolve, developer 5/5, in-page links 21/21
    • every referenced image returns HTTP 200
    • flow diagram renders correctly in light and dark mode
  • Every behavioural claim traced to autopay/subscriptions/enums.py, models.py, schemas.py or tasks/_tenant_processors.py

⚠️ One thing to look at before approving

portal-01-summary-active.png is the hero image on the business page and it is a mockup that contradicts the real product. Checked against a live betabulk portal: it shows an invoice Download column (AutoPay stores no invoices), a Pay Now button on an active subscription (Pay Now only exists when past due), two cards both masked *** 1234, and a next-payment date a year adrift from its own history.

The prose no longer describes any of it, but a reader still sees it. Recutting that one image — or a straight recapture from betabulk — is the highest-value screenshot task left. @junaid@Menna

Sign-off needed (#158915)

  • @yamen — backend and API accuracy, PR 121, proxy base path
  • @ankit — AutoPay behaviour and business accuracy
  • @junaid / @Menna — visual review

Do not merge until reviewed on this branch.

…enshots
Lane-0 scaffold so #158910 and #158912 can be written in parallel without
both editing sidebars.ts.
- developerSidebar: AutoPay Pattern-A anchor category after M-Wallet
- businessSidebar: AutoPay Pattern-A anchor category after M-Wallet
- skeleton pages pinning every heading id the sidebar links to
- 22 customer-portal and notification screenshots from Menna
(real PNGs, largest 154 KB, wallet naming convention, no stubs)
Merchant-side screens are deliberately absent: AutoPay is REST-API-only
on the merchant side in this phase (confirmed by Dacian, 2026-08-13).
Refs #158909 #158910 #158912 #158913
Mirrors the M-Wallet pair the epic names as the reference model.
The plan carries a verified-facts ledger with a source citation for every
behavioural claim, plus six findings that appear in no ticket:
- the autopay request block is write-only, not echoed in the 201
- customer_email is required; omitting it surfaces as a misleading 400
- page-link does not rotate the token; regenerate-page-token revokes
- the customer page is served from the Connect host, language in the path
- the page token is signed, not encrypted — payload is readable base64
- the nonce is what makes revocation work at all
Refs #158909 #158910 #158912 #158915
New page at /developers/payments/autopay/ following the M-Wallet shape and
the section order mandated by docs/developers/CLAUDE.md.
- AutoPayFlowDiagram.tsx — inline theme-aware SVG, matching WalletFlowDiagram
and RecurringFlow; not Mermaid
- payments/index.md — AutoPay card and Choose Your Path row
- glossary-terms.ts — subscription, billing cycle, dunning
- recurring-payments.mdx — reverse half of the deconfliction cross-link, so a
reader who lands on auto_debit expecting subscriptions is redirected
## API Reference is intentionally a placeholder: core_backend PR 121 has not
merged, so the public OpenAPI schema serves no AutoPay endpoints and
<ApiDocEmbed> cannot resolve. The six endpoints are documented as prose and
cURL under ## Guide instead.
Known open item: the endpoint samples use AutoPay's own root-mounted paths
(/subscriptions/...). Merchants reach these through a Connect proxy whose
public path is in PR 121 and is not yet confirmed. Pending Yamen.
Refs #158910 #158909
New page at /business/autopay/ for merchant admins and operations staff.
One long page with a Pattern-A anchor sub-menu, matching what M-Wallet
actually shipped rather than what its spec called for.
Eleven sections, every heading carrying an explicit anchor id so renaming
the visible text cannot silently break sidebars.ts. Right-hand table of
contents kept. Zero code samples.
All 22 of Menna's screenshots wired through <StepGuide>; every referenced
image resolves to a real file.
States plainly, as the ticket requires:
- AutoPay never auto-cancels a past_due subscription — it stays past_due
indefinitely until the customer pays or the merchant cancels
- a customer cancellation always takes effect at the end of the current
period; only the merchant can cancel immediately
Merchant setup is documented as API-only. There is no merchant dashboard in
this phase (confirmed by Dacian, 2026-08-13) and no merchant screenshots.
No video — the Remotion pipeline is #158914 and is decoupled.
Refs #158912 #158909 #158913
The Expired step claimed the page "still offers a way to pick things back
up". Source says otherwise:
- subscriptions/models.py has zero @transition with source=EXPIRED — it is
a one-way terminal state
- customer_api.py reactivate() requires status==ACTIVE AND
cancel_at_period_end==true, and 422s otherwise; an expired subscription
can never satisfy that
Separately: portal-04-expired.png shows a Reactivate button that the
backend would reject, and labels the date field "Trial Ends" under an
Expired badge. Raised with the AutoPay self-service frontend owners — the
screenshot looks like a mockup rather than real Expired-state UI.
Refs #158912 #158913
158910 :: docs: AutoPay developer section
158912 :: docs: AutoPay business section
Three claims on the business page described the mockups rather than the
product. Corrected against the screen data pack
(autopay/docs/2026-08-13-autopay-screen-data-pack.md §2, §5):
- AutoPay stores no invoice and no PDF. Dropped 'downloadable invoices'
and 'invoice downloads'; a history row ends at its status.
- There is no delete-card action. Cards are kept as history by design so
an inactive card still explains an earlier cycle. Switching the active
card is the only card management there is.
- Pay Now only exists on a past-due subscription. Removed it from the
active-state description.
Also dropped the cards-01-delete-confirm.png reference — it depicts a flow
that does not exist. Per the data pack it should be recut as a
set-active-card confirm.
Root cause worth recording: the mockups themselves are wrong (portal-01
shows an invoice Download column and a Pay Now button on an active
subscription). The page described them faithfully. Backend-sourced
behaviour was all correct; only screenshot-derived UI claims were affected.
Refs #158912 #158913
… flag JWT gap
Adversarial verification of both pages found three real defects.
BLOCKER — retry_window_days is not a live field. It was removed by
subscriptions/migrations/0028_drop_retry_window_days.py (2026-06-15) and
appears nowhere outside migrations; the current SubscriptionInternalCreateIn
has no such field. Sending it today is silently dropped. Documenting it with
a 1-30 range misrepresented working behaviour. Note that ticket #158911's
own field table still lists it — the ticket is stale, not the writer.
MAJOR — the business page told readers Expired is terminal (correct: zero
@transition with source=EXPIRED, and reactivate() requires status==ACTIVE)
while displaying portal-04-expired.png, which shows a prominent Reactivate
button. Dropped the screenshot rather than caption a contradiction.
MAJOR — five of the six management endpoints need a Keycloak JWT and the
page never said how to get one; the linked authentication guide covers only
Api-Key. Added a :::warning naming it as an open gap so a reader is not left
guessing.
Known minor, not fixed: glossary terms added in this epic are not linked
from the AutoPay page. Glossary term anchors do not exist in the built
output at all (a pre-existing site-wide issue), so linking them would add
broken anchors.
Refs #158909 #158910 #158911 #158912
…duled cycle
Found by visual verification in Chrome against the local production build.
- AutoPayFlowDiagram: the 'ongoing' label's white background rect (x560-650)
painted over the tail of 'creates subscription - sync' (x450-600), which
rendered as 'creates subscriptior.'. Moved 'ongoing' below its own arrow.
Verified in both light and dark mode.
- Business page said customers 'look back through their full billing
history'. The real betabulk portal shows the NEXT cycle in the same table
with a Scheduled status, so the table is not past-only. Reworded.
Refs #158909 #158910 #158912
Editorial pass against docs/business/wallet/index.md as the house reference.
- Removed the only inline code span on the page. It was introduced by the
Scheduled-cycle edit in a76caaf and broke the zero-code audience rule that
#158912's acceptance criteria require.
- Replaced $0 with 'zero' in three places. Ottu prices in KWD/SAR and the
wallet business page uses no dollar signs at all.
- 'pending setup' -> 'Pending setup' in the lifecycle table, matching the
Title Case used for every other state name on the page.
Refs #158912
Editorial pass against docs/business/wallet/index.md, measured before and
after. Prose only — no structural, anchor, or image changes.
- StepGuide captions 20 -> 13 words average (wallet reference: 7). Captions
now point at the screenshot instead of arguing with it.
- Em-dashes 20.3 -> 16.0 per 100 lines (wallet: 14.6).
- Removed five marketing idioms with no precedent on the wallet page:
'without lifting a finger', 'in the driver's seat', 'doesn't move the
needle', 'running a billing engine by hand', 'a heads-up'.
Every factual claim is unchanged; this only affects how they are worded.
Refs #158912
…cle statuses
Accuracy and consistency pass on both AutoPay pages, verified against
autopay/subscriptions/{enums,schemas}.py.
- notification_preferences: documented the three real boolean keys —
pre_charge_enabled, failure_enabled, final_failure_enabled, each
defaulting to true (schemas.py:52-54). Previously described only as
'free-form'.
- List subscriptions returns a slim summary per item, not the full
subscription object. Callers need Retrieve for the active card and
latest-cycle detail.
- Cycle statuses: added retry_scheduled and canceled, completing the set
a merchant can observe. needs_triage is deliberately omitted — it is an
ops-only state reached by stale-cleanup, not something a merchant acts on.
- US spelling for consistency with the rest of the docs: catalogue ->
catalog, cancelling -> canceling.
Refs #158909 #158910 #158912
@jab3z

Copy link
Copy Markdown
Contributor

@claude review

@claude

claudeBot commented Aug 13, 2026

Copy link
Copy Markdown

Claude finished @jab3z's task in 3m 14s —— View job


Review complete

  • Gather context (PR body, changed files list)
  • Read diff (git diff origin/dev...HEAD)
  • Review developer AutoPay page + flow diagram
  • Review business AutoPay page
  • Review sidebars.ts, glossary, cross-links
  • Check CLAUDE.md compliance (API base URL constant, merchant_id, cross-linking, etc.)
  • Post final review

Note: node_modules isn't installed in this sandbox and installing it wasn't in scope, so I couldn't independently re-run npm run build/npm run typecheck — this review is a manual read of the diff against origin/dev and the repo's CLAUDE.md conventions.

Overall this is a strong, well-verified page pair — the sidebar anchor hygiene (sidebars.ts) all checks out against actual heading IDs on both pages, the <AutoPayFlowDiagram> follows the svg-diagram skill convention correctly (monochrome + 2 accent nodes, light/dark overrides), OTTU_CONNECT_BASE_URL is used consistently instead of hardcoded URLs, and the cross-linking between AutoPay and recurring-payments.mdx is exactly the kind of disambiguation the domain needs. A few things worth a look before merging to dev:

1. "Copy-paste accurate" claim conflicts with the PR's own "base path unconfirmed" gap

docs/developers/payments/autopay/index.mdx:437-441 (API Reference section):

"Every request and response shown above is copy-paste accurate"

But the PR description itself flags "Endpoint base path unconfirmed... Merchants reach these through a Connect proxy whose public path is in PR 121 — @yamen needs your answer." The six subscription-management cURL examples (#step-by-step) hit plain ${OTTU_CONNECT_BASE_URL}/subscriptions/... with no proxy prefix, while the one endpoint you have confirmed (checkout, step 1) is prefixed /b/checkout/v1/pymt-txn/. Since the base path for steps 2–6 is explicitly still pending confirmation, the "copy-paste accurate" claim overstates what's verified — a developer could reasonably build against /subscriptions/... directly and hit 404s once the real proxy path lands. Consider either softening that line or adding the same base-path caveat that's already implicit in the PR's "known gaps" table, so it's visible to a reader on docs.ottu.dev, not just to reviewers on this PR.
Fix this →

2. New glossary terms aren't linked from either new page

src/data/glossary-terms.ts adds Billing Cycle, Dunning, and Subscription, all introduced specifically for AutoPay — but neither docs/developers/payments/autopay/index.mdx nor docs/business/autopay/index.md links to /glossary/ on first use, despite root CLAUDE.md's "no jargon without definition — first use links to the glossary" rule and the developer CLAUDE.md cross-referencing table. (This rule is loosely followed elsewhere in the repo too, so treat this as a nice-to-have rather than a blocker — but since these terms were added for this PR, it's a natural fit to wire them up here.)

3. Two shipped screenshots are never referenced, one contradicts the page copy

git diff --stat shows 20 new business screenshots, but only 18 are referenced from docs/business/autopay/index.md:

  • static/img/business/autopay/portal-04-expired.png — the "Expired" step in the state <StepGuide> (docs/business/autopay/index.md:135-138) has no image/imageAlt, even though this exact screenshot exists.
  • static/img/business/autopay/cards-01-delete-confirm.png — unreferenced anywhere. This one is worth double-checking specifically: the page states "Saved cards are never deleted... there is no removal step to walk them through" (line 195), but a screenshot named cards-01-delete-confirm strongly implies a delete-confirmation dialog exists in the product. Given the PR already flagged portal-01-summary-active.png as a stale mockup that contradicts the real product, this looks like it could be the same class of issue — either the copy is right and this is a leftover asset that should be dropped, or card deletion does exist and the copy needs correcting. Worth a quick check with @Menna/@junaid alongside the portal-01 fix.

Not re-litigated

The factual claims (write-only autopay block, customer_email 422 behavior, page-link vs regenerate-page-token, dead retry_window_days, slim list summary, signed-not-encrypted token) all match what's described in the PR body as independently verified against autopay/subscriptions/ source and a live betabulk call — I didn't have backend access to re-verify these, so I'm relying on the PR author's stated verification method there.
· branch epic/158909

…rop mockups
Three points from the PR 169 review.
The API Reference note claimed every request and response above was
copy-paste accurate, while the PR itself flags the Connect proxy prefix for
the six management endpoints as unconfirmed. That gap was visible to
reviewers and invisible to a reader on docs.ottu.dev. Replaced the claim
with a warning under Step-by-Step naming exactly what is uncertain — the
path in front of /subscriptions/ — and stating that the headers, bodies,
responses and behaviour are not, and that Step 1's /b/checkout/v1/pymt-txn/
is the stable Checkout API path.
Billing Cycle, Dunning and Subscription were added to glossary-terms.ts for
this PR and nothing linked to them. Linked each once per page via
/glossary/#term-<id>. GlossarySearch server-renders those ids, so the
anchors resolve natively; Docusaurus still reports them broken because its
checker only reads markdown headings — the same false positive already
fires for the #term-* links on payment-states and transaction-states.
portal-04-expired.png and cards-01-delete-confirm.png were unreferenced
because both depict flows that do not exist: the first shows a Reactivate
button on an Expired subscription (terminal in the backend) plus an invoice
Download column, the second a delete-card confirmation. Dereferenced in
b5475bf and 79835e2 respectively; deleting the assets so they cannot be
wired back in. They need recapture from betabulk alongside
portal-01-summary-active.png.
npm run build passes.
@ankitottu

Copy link
Copy Markdown
ContributorAuthor

@claude re-review

@claude

claudeBot commented Aug 13, 2026

Copy link
Copy Markdown

Claude finished @ankitottu's task in 1m 38s —— View job


Re-review complete

  • Gather context (PR body, prior review, changed files)
  • Check what changed since the last review (commit 34e4484)
  • Verify the three prior findings were actually resolved
  • Fresh pass over the full diff for anything new
  • Post final review

Note: node_modules still isn't installed in this sandbox, so I couldn't independently re-run npm run build/npm run typecheck — this is a manual read of git diff origin/dev...HEAD plus the four fix commits added since the first review (b5475bf, 79835e2, 93c1567, 34e4484).

All three points from the first review are resolved cleanly in 34e4484:

1. "Copy-paste accurate" overclaim — fixed. docs/developers/payments/autopay/index.mdx:62-65 now has a dedicated :::warning The path in front of /subscriptions/ isn't final block naming exactly what's unconfirmed (the Connect proxy prefix for Steps 2–6) while explicitly stating headers/bodies/responses/behaviour are accurate, and that Step 1's /b/checkout/v1/pymt-txn/ is stable. The old blanket "copy-paste accurate" line under ## API Reference (line ~444) was replaced with a pointer to that scoped caveat instead of a second overclaim. This is exactly the right fix — it's visible to a reader on docs.ottu.dev now, not just to PR reviewers.

2. Glossary terms unlinked — fixed. Billing Cycle, Dunning, and Subscription are each now linked once per page via /glossary/#term-<id>. Verified the anchor mechanism actually resolves: src/components/GlossarySearch/index.tsx:7,30-31 builds/reads the same term-${id} hash format server-side, and the ids added in src/data/glossary-terms.ts (billing-cycle, dunning, subscription) match what's linked. The commit message's note that Docusaurus's broken-link checker will still flag these as a false positive (same pattern as the existing payment-states/transaction-states pages) checks out.

3. Unreferenced/contradictory screenshots — fixed correctly, not just papered over. Both cards-01-delete-confirm.png and portal-04-expired.png are deleted from the tree (not just dereferenced), so they can't get silently wired back in later. Confirmed no remaining references to either filename anywhere in the repo, and confirmed all 19 remaining business screenshots (20 shipped − 2 dropped − wait, 18 referenced + the dropped 2 = 20 ✓) are each referenced from docs/business/autopay/index.md. The "Expired" step in the state <StepGuide> (docs/business/autopay/index.md:135-138) now correctly has no image/imageAlt at all, matching its terminal-state description instead of showing a fabricated Reactivate button.

One thing worth noting rather than re-flagging: portal-01-summary-active.png (the hero mockup the PR body itself calls out as contradicting the real product) is still in place — that's expected, since the PR description already tracks it as the open "highest-value screenshot task left" pending recapture from betabulk, not something silently missed.

Also scanned the intervening accuracy commit (93c1567, notification keys / list-summary shape / cycle statuses) — it's a good-faith correctness pass against autopay/subscriptions/{enums,schemas}.py unrelated to the review findings, nothing to flag there.

Nothing new surfaced in this pass. The two page diffs are clean additions with no leftover inconsistencies from the fix-up commits.
· branch

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ankitottu@jab3z
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

158909 :: docs: AutoPay public documentation - #169

Merged
ankitottu merged 14 commits into
devfrom
epic/158909
Aug 13, 2026
Merged

158909 :: docs: AutoPay public documentation#169
ankitottu merged 14 commits into
devfrom
epic/158909

Conversation

@ankitottu

Copy link
Copy Markdown
Contributor

Redmine epic #158909 — closes the docs half of the AutoPay launch.

Merges the epic branch into dev, which deploys to docs.ottu.dev for review. Nothing reaches docs.ottu.com until it has been signed off there — that is #158915.

What ships

#158910Developer section at /developers/payments/autopay/ + AutoPayFlowDiagram + glossary terms + payments-index entry
#158912Business section at /business/autopay/, one long page with a Pattern-A anchor sub-menu
#158913 (partial)20 customer-portal and notification screenshots from Menna
Design spec + execution plan under docs/superpowers/

Both child PRs (#167, #168) already merged here. sidebars.ts was edited once, in the scaffold commit, so the two lanes never touched the same file.

The distinction the whole thing hangs on

With auto_debit the merchant owns the billing schedule, retries and dunning. With auto_pay, AutoPay owns them.

recurring-payments.mdx is the closest existing page and readers will land on the wrong one, so both pages now cross-link with an explicit "use this when".

Facts documented that appear in no ticket

Found by reading autopay/subscriptions/ and by a live betabulk call:

  • The autopay request block is write-only — not echoed in the 201. extra.autopay.subscription_id is the merchant's only handle. Verified live: sub_hA1YYmZz1xk8speCiPbOquIP.
  • customer_email is required. Omitting it 422s inside AutoPay and surfaces as 400 {"autopay":["AutoPayClient rejected request (422)"]} — reads like an agreement-shape error and is not.
  • page-link does not rotate the token; regenerate-page-token revokes. A merchant reaching for regenerate when they meant re-send silently breaks the customer's working link, with no notification to that customer.
  • retry_window_days is dead — dropped in migrations/0028_drop_retry_window_days.py. ⚠️#158911's field table still lists it as live with a 1–30 range. That table is also the spec for the enrichment YAML, so it needs correcting there too.
  • list returns a slim summary, not full subscription objects.
  • The customer page token is signed, not encrypted — the payload is readable base64. Treat the link like a password.

Known gaps, deliberate

GapWhy
## API Reference is a placeholdercore_backend PR 121 unmerged — static/Ottu_API.yaml has zero autopay occurrences, so <ApiDocEmbed> cannot resolve. Endpoints documented as prose + cURL under ## Guide instead.
Endpoint base path unconfirmedSamples use AutoPay's own root-mounted paths. Merchants reach these through a Connect proxy whose public path is in PR 121. @yamen — needs your answer.
JWT acquisition undocumentedFive of six endpoints need a Keycloak JWT; the auth guide only covers Api-Key. Flagged in a :::warning on the page rather than papered over.
No merchant dashboard contentIt does not exist — REST-API-only this phase, confirmed by @dacian 2026-08-13.
No add-card screenshotNot captured yet; behaviour written as prose, no image referenced.
No videoRemotion is #158914, decoupled.

Verification

  • npm run typecheck ✅ · npm run build
  • Both pages adversarially verified by subagents against their tickets' own acceptance criteria: developer 14 PASS / 1 BLOCKED, business 13 PASS
  • Checked by hand in Chrome against the local production build — onBrokenLinks is "warn", so a green build proves nothing about navigation:
    • business sidebar anchors 10/10 resolve, developer 5/5, in-page links 21/21
    • every referenced image returns HTTP 200
    • flow diagram renders correctly in light and dark mode
  • Every behavioural claim traced to autopay/subscriptions/enums.py, models.py, schemas.py or tasks/_tenant_processors.py

⚠️ One thing to look at before approving

portal-01-summary-active.png is the hero image on the business page and it is a mockup that contradicts the real product. Checked against a live betabulk portal: it shows an invoice Download column (AutoPay stores no invoices), a Pay Now button on an active subscription (Pay Now only exists when past due), two cards both masked *** 1234, and a next-payment date a year adrift from its own history.

The prose no longer describes any of it, but a reader still sees it. Recutting that one image — or a straight recapture from betabulk — is the highest-value screenshot task left. @junaid@Menna

Sign-off needed (#158915)

  • @yamen — backend and API accuracy, PR 121, proxy base path
  • @ankit — AutoPay behaviour and business accuracy
  • @junaid / @Menna — visual review

Do not merge until reviewed on this branch.

…enshots
Lane-0 scaffold so #158910 and #158912 can be written in parallel without
both editing sidebars.ts.
- developerSidebar: AutoPay Pattern-A anchor category after M-Wallet
- businessSidebar: AutoPay Pattern-A anchor category after M-Wallet
- skeleton pages pinning every heading id the sidebar links to
- 22 customer-portal and notification screenshots from Menna
(real PNGs, largest 154 KB, wallet naming convention, no stubs)
Merchant-side screens are deliberately absent: AutoPay is REST-API-only
on the merchant side in this phase (confirmed by Dacian, 2026-08-13).
Refs #158909 #158910 #158912 #158913
Mirrors the M-Wallet pair the epic names as the reference model.
The plan carries a verified-facts ledger with a source citation for every
behavioural claim, plus six findings that appear in no ticket:
- the autopay request block is write-only, not echoed in the 201
- customer_email is required; omitting it surfaces as a misleading 400
- page-link does not rotate the token; regenerate-page-token revokes
- the customer page is served from the Connect host, language in the path
- the page token is signed, not encrypted — payload is readable base64
- the nonce is what makes revocation work at all
Refs #158909 #158910 #158912 #158915
New page at /developers/payments/autopay/ following the M-Wallet shape and
the section order mandated by docs/developers/CLAUDE.md.
- AutoPayFlowDiagram.tsx — inline theme-aware SVG, matching WalletFlowDiagram
and RecurringFlow; not Mermaid
- payments/index.md — AutoPay card and Choose Your Path row
- glossary-terms.ts — subscription, billing cycle, dunning
- recurring-payments.mdx — reverse half of the deconfliction cross-link, so a
reader who lands on auto_debit expecting subscriptions is redirected
## API Reference is intentionally a placeholder: core_backend PR 121 has not
merged, so the public OpenAPI schema serves no AutoPay endpoints and
<ApiDocEmbed> cannot resolve. The six endpoints are documented as prose and
cURL under ## Guide instead.
Known open item: the endpoint samples use AutoPay's own root-mounted paths
(/subscriptions/...). Merchants reach these through a Connect proxy whose
public path is in PR 121 and is not yet confirmed. Pending Yamen.
Refs #158910 #158909
New page at /business/autopay/ for merchant admins and operations staff.
One long page with a Pattern-A anchor sub-menu, matching what M-Wallet
actually shipped rather than what its spec called for.
Eleven sections, every heading carrying an explicit anchor id so renaming
the visible text cannot silently break sidebars.ts. Right-hand table of
contents kept. Zero code samples.
All 22 of Menna's screenshots wired through <StepGuide>; every referenced
image resolves to a real file.
States plainly, as the ticket requires:
- AutoPay never auto-cancels a past_due subscription — it stays past_due
indefinitely until the customer pays or the merchant cancels
- a customer cancellation always takes effect at the end of the current
period; only the merchant can cancel immediately
Merchant setup is documented as API-only. There is no merchant dashboard in
this phase (confirmed by Dacian, 2026-08-13) and no merchant screenshots.
No video — the Remotion pipeline is #158914 and is decoupled.
Refs #158912 #158909 #158913
The Expired step claimed the page "still offers a way to pick things back
up". Source says otherwise:
- subscriptions/models.py has zero @transition with source=EXPIRED — it is
a one-way terminal state
- customer_api.py reactivate() requires status==ACTIVE AND
cancel_at_period_end==true, and 422s otherwise; an expired subscription
can never satisfy that
Separately: portal-04-expired.png shows a Reactivate button that the
backend would reject, and labels the date field "Trial Ends" under an
Expired badge. Raised with the AutoPay self-service frontend owners — the
screenshot looks like a mockup rather than real Expired-state UI.
Refs #158912 #158913
158910 :: docs: AutoPay developer section
158912 :: docs: AutoPay business section
Three claims on the business page described the mockups rather than the
product. Corrected against the screen data pack
(autopay/docs/2026-08-13-autopay-screen-data-pack.md §2, §5):
- AutoPay stores no invoice and no PDF. Dropped 'downloadable invoices'
and 'invoice downloads'; a history row ends at its status.
- There is no delete-card action. Cards are kept as history by design so
an inactive card still explains an earlier cycle. Switching the active
card is the only card management there is.
- Pay Now only exists on a past-due subscription. Removed it from the
active-state description.
Also dropped the cards-01-delete-confirm.png reference — it depicts a flow
that does not exist. Per the data pack it should be recut as a
set-active-card confirm.
Root cause worth recording: the mockups themselves are wrong (portal-01
shows an invoice Download column and a Pay Now button on an active
subscription). The page described them faithfully. Backend-sourced
behaviour was all correct; only screenshot-derived UI claims were affected.
Refs #158912 #158913
… flag JWT gap
Adversarial verification of both pages found three real defects.
BLOCKER — retry_window_days is not a live field. It was removed by
subscriptions/migrations/0028_drop_retry_window_days.py (2026-06-15) and
appears nowhere outside migrations; the current SubscriptionInternalCreateIn
has no such field. Sending it today is silently dropped. Documenting it with
a 1-30 range misrepresented working behaviour. Note that ticket #158911's
own field table still lists it — the ticket is stale, not the writer.
MAJOR — the business page told readers Expired is terminal (correct: zero
@transition with source=EXPIRED, and reactivate() requires status==ACTIVE)
while displaying portal-04-expired.png, which shows a prominent Reactivate
button. Dropped the screenshot rather than caption a contradiction.
MAJOR — five of the six management endpoints need a Keycloak JWT and the
page never said how to get one; the linked authentication guide covers only
Api-Key. Added a :::warning naming it as an open gap so a reader is not left
guessing.
Known minor, not fixed: glossary terms added in this epic are not linked
from the AutoPay page. Glossary term anchors do not exist in the built
output at all (a pre-existing site-wide issue), so linking them would add
broken anchors.
Refs #158909 #158910 #158911 #158912
…duled cycle
Found by visual verification in Chrome against the local production build.
- AutoPayFlowDiagram: the 'ongoing' label's white background rect (x560-650)
painted over the tail of 'creates subscription - sync' (x450-600), which
rendered as 'creates subscriptior.'. Moved 'ongoing' below its own arrow.
Verified in both light and dark mode.
- Business page said customers 'look back through their full billing
history'. The real betabulk portal shows the NEXT cycle in the same table
with a Scheduled status, so the table is not past-only. Reworded.
Refs #158909 #158910 #158912
Editorial pass against docs/business/wallet/index.md as the house reference.
- Removed the only inline code span on the page. It was introduced by the
Scheduled-cycle edit in a76caaf and broke the zero-code audience rule that
#158912's acceptance criteria require.
- Replaced $0 with 'zero' in three places. Ottu prices in KWD/SAR and the
wallet business page uses no dollar signs at all.
- 'pending setup' -> 'Pending setup' in the lifecycle table, matching the
Title Case used for every other state name on the page.
Refs #158912
Editorial pass against docs/business/wallet/index.md, measured before and
after. Prose only — no structural, anchor, or image changes.
- StepGuide captions 20 -> 13 words average (wallet reference: 7). Captions
now point at the screenshot instead of arguing with it.
- Em-dashes 20.3 -> 16.0 per 100 lines (wallet: 14.6).
- Removed five marketing idioms with no precedent on the wallet page:
'without lifting a finger', 'in the driver's seat', 'doesn't move the
needle', 'running a billing engine by hand', 'a heads-up'.
Every factual claim is unchanged; this only affects how they are worded.
Refs #158912
…cle statuses
Accuracy and consistency pass on both AutoPay pages, verified against
autopay/subscriptions/{enums,schemas}.py.
- notification_preferences: documented the three real boolean keys —
pre_charge_enabled, failure_enabled, final_failure_enabled, each
defaulting to true (schemas.py:52-54). Previously described only as
'free-form'.
- List subscriptions returns a slim summary per item, not the full
subscription object. Callers need Retrieve for the active card and
latest-cycle detail.
- Cycle statuses: added retry_scheduled and canceled, completing the set
a merchant can observe. needs_triage is deliberately omitted — it is an
ops-only state reached by stale-cleanup, not something a merchant acts on.
- US spelling for consistency with the rest of the docs: catalogue ->
catalog, cancelling -> canceling.
Refs #158909 #158910 #158912
@jab3z

Copy link
Copy Markdown
Contributor

@claude review

@claude

claudeBot commented Aug 13, 2026

Copy link
Copy Markdown

Claude finished @jab3z's task in 3m 14s —— View job


Review complete

  • Gather context (PR body, changed files list)
  • Read diff (git diff origin/dev...HEAD)
  • Review developer AutoPay page + flow diagram
  • Review business AutoPay page
  • Review sidebars.ts, glossary, cross-links
  • Check CLAUDE.md compliance (API base URL constant, merchant_id, cross-linking, etc.)
  • Post final review

Note: node_modules isn't installed in this sandbox and installing it wasn't in scope, so I couldn't independently re-run npm run build/npm run typecheck — this review is a manual read of the diff against origin/dev and the repo's CLAUDE.md conventions.

Overall this is a strong, well-verified page pair — the sidebar anchor hygiene (sidebars.ts) all checks out against actual heading IDs on both pages, the <AutoPayFlowDiagram> follows the svg-diagram skill convention correctly (monochrome + 2 accent nodes, light/dark overrides), OTTU_CONNECT_BASE_URL is used consistently instead of hardcoded URLs, and the cross-linking between AutoPay and recurring-payments.mdx is exactly the kind of disambiguation the domain needs. A few things worth a look before merging to dev:

1. "Copy-paste accurate" claim conflicts with the PR's own "base path unconfirmed" gap

docs/developers/payments/autopay/index.mdx:437-441 (API Reference section):

"Every request and response shown above is copy-paste accurate"

But the PR description itself flags "Endpoint base path unconfirmed... Merchants reach these through a Connect proxy whose public path is in PR 121 — @yamen needs your answer." The six subscription-management cURL examples (#step-by-step) hit plain ${OTTU_CONNECT_BASE_URL}/subscriptions/... with no proxy prefix, while the one endpoint you have confirmed (checkout, step 1) is prefixed /b/checkout/v1/pymt-txn/. Since the base path for steps 2–6 is explicitly still pending confirmation, the "copy-paste accurate" claim overstates what's verified — a developer could reasonably build against /subscriptions/... directly and hit 404s once the real proxy path lands. Consider either softening that line or adding the same base-path caveat that's already implicit in the PR's "known gaps" table, so it's visible to a reader on docs.ottu.dev, not just to reviewers on this PR.
Fix this →

2. New glossary terms aren't linked from either new page

src/data/glossary-terms.ts adds Billing Cycle, Dunning, and Subscription, all introduced specifically for AutoPay — but neither docs/developers/payments/autopay/index.mdx nor docs/business/autopay/index.md links to /glossary/ on first use, despite root CLAUDE.md's "no jargon without definition — first use links to the glossary" rule and the developer CLAUDE.md cross-referencing table. (This rule is loosely followed elsewhere in the repo too, so treat this as a nice-to-have rather than a blocker — but since these terms were added for this PR, it's a natural fit to wire them up here.)

3. Two shipped screenshots are never referenced, one contradicts the page copy

git diff --stat shows 20 new business screenshots, but only 18 are referenced from docs/business/autopay/index.md:

  • static/img/business/autopay/portal-04-expired.png — the "Expired" step in the state <StepGuide> (docs/business/autopay/index.md:135-138) has no image/imageAlt, even though this exact screenshot exists.
  • static/img/business/autopay/cards-01-delete-confirm.png — unreferenced anywhere. This one is worth double-checking specifically: the page states "Saved cards are never deleted... there is no removal step to walk them through" (line 195), but a screenshot named cards-01-delete-confirm strongly implies a delete-confirmation dialog exists in the product. Given the PR already flagged portal-01-summary-active.png as a stale mockup that contradicts the real product, this looks like it could be the same class of issue — either the copy is right and this is a leftover asset that should be dropped, or card deletion does exist and the copy needs correcting. Worth a quick check with @Menna/@junaid alongside the portal-01 fix.

Not re-litigated

The factual claims (write-only autopay block, customer_email 422 behavior, page-link vs regenerate-page-token, dead retry_window_days, slim list summary, signed-not-encrypted token) all match what's described in the PR body as independently verified against autopay/subscriptions/ source and a live betabulk call — I didn't have backend access to re-verify these, so I'm relying on the PR author's stated verification method there.
· branch epic/158909

…rop mockups
Three points from the PR 169 review.
The API Reference note claimed every request and response above was
copy-paste accurate, while the PR itself flags the Connect proxy prefix for
the six management endpoints as unconfirmed. That gap was visible to
reviewers and invisible to a reader on docs.ottu.dev. Replaced the claim
with a warning under Step-by-Step naming exactly what is uncertain — the
path in front of /subscriptions/ — and stating that the headers, bodies,
responses and behaviour are not, and that Step 1's /b/checkout/v1/pymt-txn/
is the stable Checkout API path.
Billing Cycle, Dunning and Subscription were added to glossary-terms.ts for
this PR and nothing linked to them. Linked each once per page via
/glossary/#term-<id>. GlossarySearch server-renders those ids, so the
anchors resolve natively; Docusaurus still reports them broken because its
checker only reads markdown headings — the same false positive already
fires for the #term-* links on payment-states and transaction-states.
portal-04-expired.png and cards-01-delete-confirm.png were unreferenced
because both depict flows that do not exist: the first shows a Reactivate
button on an Expired subscription (terminal in the backend) plus an invoice
Download column, the second a delete-card confirmation. Dereferenced in
b5475bf and 79835e2 respectively; deleting the assets so they cannot be
wired back in. They need recapture from betabulk alongside
portal-01-summary-active.png.
npm run build passes.
@ankitottu

Copy link
Copy Markdown
ContributorAuthor

@claude re-review

@claude

claudeBot commented Aug 13, 2026

Copy link
Copy Markdown

Claude finished @ankitottu's task in 1m 38s —— View job


Re-review complete

  • Gather context (PR body, prior review, changed files)
  • Check what changed since the last review (commit 34e4484)
  • Verify the three prior findings were actually resolved
  • Fresh pass over the full diff for anything new
  • Post final review

Note: node_modules still isn't installed in this sandbox, so I couldn't independently re-run npm run build/npm run typecheck — this is a manual read of git diff origin/dev...HEAD plus the four fix commits added since the first review (b5475bf, 79835e2, 93c1567, 34e4484).

All three points from the first review are resolved cleanly in 34e4484:

1. "Copy-paste accurate" overclaim — fixed. docs/developers/payments/autopay/index.mdx:62-65 now has a dedicated :::warning The path in front of /subscriptions/ isn't final block naming exactly what's unconfirmed (the Connect proxy prefix for Steps 2–6) while explicitly stating headers/bodies/responses/behaviour are accurate, and that Step 1's /b/checkout/v1/pymt-txn/ is stable. The old blanket "copy-paste accurate" line under ## API Reference (line ~444) was replaced with a pointer to that scoped caveat instead of a second overclaim. This is exactly the right fix — it's visible to a reader on docs.ottu.dev now, not just to PR reviewers.

2. Glossary terms unlinked — fixed. Billing Cycle, Dunning, and Subscription are each now linked once per page via /glossary/#term-<id>. Verified the anchor mechanism actually resolves: src/components/GlossarySearch/index.tsx:7,30-31 builds/reads the same term-${id} hash format server-side, and the ids added in src/data/glossary-terms.ts (billing-cycle, dunning, subscription) match what's linked. The commit message's note that Docusaurus's broken-link checker will still flag these as a false positive (same pattern as the existing payment-states/transaction-states pages) checks out.

3. Unreferenced/contradictory screenshots — fixed correctly, not just papered over. Both cards-01-delete-confirm.png and portal-04-expired.png are deleted from the tree (not just dereferenced), so they can't get silently wired back in later. Confirmed no remaining references to either filename anywhere in the repo, and confirmed all 19 remaining business screenshots (20 shipped − 2 dropped − wait, 18 referenced + the dropped 2 = 20 ✓) are each referenced from docs/business/autopay/index.md. The "Expired" step in the state <StepGuide> (docs/business/autopay/index.md:135-138) now correctly has no image/imageAlt at all, matching its terminal-state description instead of showing a fabricated Reactivate button.

One thing worth noting rather than re-flagging: portal-01-summary-active.png (the hero mockup the PR body itself calls out as contradicting the real product) is still in place — that's expected, since the PR description already tracks it as the open "highest-value screenshot task left" pending recapture from betabulk, not something silently missed.

Also scanned the intervening accuracy commit (93c1567, notification keys / list-summary shape / cycle statuses) — it's a good-faith correctness pass against autopay/subscriptions/{enums,schemas}.py unrelated to the review findings, nothing to flag there.

Nothing new surfaced in this pass. The two page diffs are clean additions with no leftover inconsistencies from the fix-up commits.
· branch

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ankitottu@jab3z
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

158909 :: docs: AutoPay public documentation - #169

Merged
ankitottu merged 14 commits into
devfrom
epic/158909
Aug 13, 2026
Merged

158909 :: docs: AutoPay public documentation#169
ankitottu merged 14 commits into
devfrom
epic/158909

Conversation

@ankitottu

Copy link
Copy Markdown
Contributor

Redmine epic #158909 — closes the docs half of the AutoPay launch.

Merges the epic branch into dev, which deploys to docs.ottu.dev for review. Nothing reaches docs.ottu.com until it has been signed off there — that is #158915.

What ships

#158910Developer section at /developers/payments/autopay/ + AutoPayFlowDiagram + glossary terms + payments-index entry
#158912Business section at /business/autopay/, one long page with a Pattern-A anchor sub-menu
#158913 (partial)20 customer-portal and notification screenshots from Menna
Design spec + execution plan under docs/superpowers/

Both child PRs (#167, #168) already merged here. sidebars.ts was edited once, in the scaffold commit, so the two lanes never touched the same file.

The distinction the whole thing hangs on

With auto_debit the merchant owns the billing schedule, retries and dunning. With auto_pay, AutoPay owns them.

recurring-payments.mdx is the closest existing page and readers will land on the wrong one, so both pages now cross-link with an explicit "use this when".

Facts documented that appear in no ticket

Found by reading autopay/subscriptions/ and by a live betabulk call:

  • The autopay request block is write-only — not echoed in the 201. extra.autopay.subscription_id is the merchant's only handle. Verified live: sub_hA1YYmZz1xk8speCiPbOquIP.
  • customer_email is required. Omitting it 422s inside AutoPay and surfaces as 400 {"autopay":["AutoPayClient rejected request (422)"]} — reads like an agreement-shape error and is not.
  • page-link does not rotate the token; regenerate-page-token revokes. A merchant reaching for regenerate when they meant re-send silently breaks the customer's working link, with no notification to that customer.
  • retry_window_days is dead — dropped in migrations/0028_drop_retry_window_days.py. ⚠️#158911's field table still lists it as live with a 1–30 range. That table is also the spec for the enrichment YAML, so it needs correcting there too.
  • list returns a slim summary, not full subscription objects.
  • The customer page token is signed, not encrypted — the payload is readable base64. Treat the link like a password.

Known gaps, deliberate

GapWhy
## API Reference is a placeholdercore_backend PR 121 unmerged — static/Ottu_API.yaml has zero autopay occurrences, so <ApiDocEmbed> cannot resolve. Endpoints documented as prose + cURL under ## Guide instead.
Endpoint base path unconfirmedSamples use AutoPay's own root-mounted paths. Merchants reach these through a Connect proxy whose public path is in PR 121. @yamen — needs your answer.
JWT acquisition undocumentedFive of six endpoints need a Keycloak JWT; the auth guide only covers Api-Key. Flagged in a :::warning on the page rather than papered over.
No merchant dashboard contentIt does not exist — REST-API-only this phase, confirmed by @dacian 2026-08-13.
No add-card screenshotNot captured yet; behaviour written as prose, no image referenced.
No videoRemotion is #158914, decoupled.

Verification

  • npm run typecheck ✅ · npm run build
  • Both pages adversarially verified by subagents against their tickets' own acceptance criteria: developer 14 PASS / 1 BLOCKED, business 13 PASS
  • Checked by hand in Chrome against the local production build — onBrokenLinks is "warn", so a green build proves nothing about navigation:
    • business sidebar anchors 10/10 resolve, developer 5/5, in-page links 21/21
    • every referenced image returns HTTP 200
    • flow diagram renders correctly in light and dark mode
  • Every behavioural claim traced to autopay/subscriptions/enums.py, models.py, schemas.py or tasks/_tenant_processors.py

⚠️ One thing to look at before approving

portal-01-summary-active.png is the hero image on the business page and it is a mockup that contradicts the real product. Checked against a live betabulk portal: it shows an invoice Download column (AutoPay stores no invoices), a Pay Now button on an active subscription (Pay Now only exists when past due), two cards both masked *** 1234, and a next-payment date a year adrift from its own history.

The prose no longer describes any of it, but a reader still sees it. Recutting that one image — or a straight recapture from betabulk — is the highest-value screenshot task left. @junaid@Menna

Sign-off needed (#158915)

  • @yamen — backend and API accuracy, PR 121, proxy base path
  • @ankit — AutoPay behaviour and business accuracy
  • @junaid / @Menna — visual review

Do not merge until reviewed on this branch.

…enshots
Lane-0 scaffold so #158910 and #158912 can be written in parallel without
both editing sidebars.ts.
- developerSidebar: AutoPay Pattern-A anchor category after M-Wallet
- businessSidebar: AutoPay Pattern-A anchor category after M-Wallet
- skeleton pages pinning every heading id the sidebar links to
- 22 customer-portal and notification screenshots from Menna
(real PNGs, largest 154 KB, wallet naming convention, no stubs)
Merchant-side screens are deliberately absent: AutoPay is REST-API-only
on the merchant side in this phase (confirmed by Dacian, 2026-08-13).
Refs #158909 #158910 #158912 #158913
Mirrors the M-Wallet pair the epic names as the reference model.
The plan carries a verified-facts ledger with a source citation for every
behavioural claim, plus six findings that appear in no ticket:
- the autopay request block is write-only, not echoed in the 201
- customer_email is required; omitting it surfaces as a misleading 400
- page-link does not rotate the token; regenerate-page-token revokes
- the customer page is served from the Connect host, language in the path
- the page token is signed, not encrypted — payload is readable base64
- the nonce is what makes revocation work at all
Refs #158909 #158910 #158912 #158915
New page at /developers/payments/autopay/ following the M-Wallet shape and
the section order mandated by docs/developers/CLAUDE.md.
- AutoPayFlowDiagram.tsx — inline theme-aware SVG, matching WalletFlowDiagram
and RecurringFlow; not Mermaid
- payments/index.md — AutoPay card and Choose Your Path row
- glossary-terms.ts — subscription, billing cycle, dunning
- recurring-payments.mdx — reverse half of the deconfliction cross-link, so a
reader who lands on auto_debit expecting subscriptions is redirected
## API Reference is intentionally a placeholder: core_backend PR 121 has not
merged, so the public OpenAPI schema serves no AutoPay endpoints and
<ApiDocEmbed> cannot resolve. The six endpoints are documented as prose and
cURL under ## Guide instead.
Known open item: the endpoint samples use AutoPay's own root-mounted paths
(/subscriptions/...). Merchants reach these through a Connect proxy whose
public path is in PR 121 and is not yet confirmed. Pending Yamen.
Refs #158910 #158909
New page at /business/autopay/ for merchant admins and operations staff.
One long page with a Pattern-A anchor sub-menu, matching what M-Wallet
actually shipped rather than what its spec called for.
Eleven sections, every heading carrying an explicit anchor id so renaming
the visible text cannot silently break sidebars.ts. Right-hand table of
contents kept. Zero code samples.
All 22 of Menna's screenshots wired through <StepGuide>; every referenced
image resolves to a real file.
States plainly, as the ticket requires:
- AutoPay never auto-cancels a past_due subscription — it stays past_due
indefinitely until the customer pays or the merchant cancels
- a customer cancellation always takes effect at the end of the current
period; only the merchant can cancel immediately
Merchant setup is documented as API-only. There is no merchant dashboard in
this phase (confirmed by Dacian, 2026-08-13) and no merchant screenshots.
No video — the Remotion pipeline is #158914 and is decoupled.
Refs #158912 #158909 #158913
The Expired step claimed the page "still offers a way to pick things back
up". Source says otherwise:
- subscriptions/models.py has zero @transition with source=EXPIRED — it is
a one-way terminal state
- customer_api.py reactivate() requires status==ACTIVE AND
cancel_at_period_end==true, and 422s otherwise; an expired subscription
can never satisfy that
Separately: portal-04-expired.png shows a Reactivate button that the
backend would reject, and labels the date field "Trial Ends" under an
Expired badge. Raised with the AutoPay self-service frontend owners — the
screenshot looks like a mockup rather than real Expired-state UI.
Refs #158912 #158913
158910 :: docs: AutoPay developer section
158912 :: docs: AutoPay business section
Three claims on the business page described the mockups rather than the
product. Corrected against the screen data pack
(autopay/docs/2026-08-13-autopay-screen-data-pack.md §2, §5):
- AutoPay stores no invoice and no PDF. Dropped 'downloadable invoices'
and 'invoice downloads'; a history row ends at its status.
- There is no delete-card action. Cards are kept as history by design so
an inactive card still explains an earlier cycle. Switching the active
card is the only card management there is.
- Pay Now only exists on a past-due subscription. Removed it from the
active-state description.
Also dropped the cards-01-delete-confirm.png reference — it depicts a flow
that does not exist. Per the data pack it should be recut as a
set-active-card confirm.
Root cause worth recording: the mockups themselves are wrong (portal-01
shows an invoice Download column and a Pay Now button on an active
subscription). The page described them faithfully. Backend-sourced
behaviour was all correct; only screenshot-derived UI claims were affected.
Refs #158912 #158913
… flag JWT gap
Adversarial verification of both pages found three real defects.
BLOCKER — retry_window_days is not a live field. It was removed by
subscriptions/migrations/0028_drop_retry_window_days.py (2026-06-15) and
appears nowhere outside migrations; the current SubscriptionInternalCreateIn
has no such field. Sending it today is silently dropped. Documenting it with
a 1-30 range misrepresented working behaviour. Note that ticket #158911's
own field table still lists it — the ticket is stale, not the writer.
MAJOR — the business page told readers Expired is terminal (correct: zero
@transition with source=EXPIRED, and reactivate() requires status==ACTIVE)
while displaying portal-04-expired.png, which shows a prominent Reactivate
button. Dropped the screenshot rather than caption a contradiction.
MAJOR — five of the six management endpoints need a Keycloak JWT and the
page never said how to get one; the linked authentication guide covers only
Api-Key. Added a :::warning naming it as an open gap so a reader is not left
guessing.
Known minor, not fixed: glossary terms added in this epic are not linked
from the AutoPay page. Glossary term anchors do not exist in the built
output at all (a pre-existing site-wide issue), so linking them would add
broken anchors.
Refs #158909 #158910 #158911 #158912
…duled cycle
Found by visual verification in Chrome against the local production build.
- AutoPayFlowDiagram: the 'ongoing' label's white background rect (x560-650)
painted over the tail of 'creates subscription - sync' (x450-600), which
rendered as 'creates subscriptior.'. Moved 'ongoing' below its own arrow.
Verified in both light and dark mode.
- Business page said customers 'look back through their full billing
history'. The real betabulk portal shows the NEXT cycle in the same table
with a Scheduled status, so the table is not past-only. Reworded.
Refs #158909 #158910 #158912
Editorial pass against docs/business/wallet/index.md as the house reference.
- Removed the only inline code span on the page. It was introduced by the
Scheduled-cycle edit in a76caaf and broke the zero-code audience rule that
#158912's acceptance criteria require.
- Replaced $0 with 'zero' in three places. Ottu prices in KWD/SAR and the
wallet business page uses no dollar signs at all.
- 'pending setup' -> 'Pending setup' in the lifecycle table, matching the
Title Case used for every other state name on the page.
Refs #158912
Editorial pass against docs/business/wallet/index.md, measured before and
after. Prose only — no structural, anchor, or image changes.
- StepGuide captions 20 -> 13 words average (wallet reference: 7). Captions
now point at the screenshot instead of arguing with it.
- Em-dashes 20.3 -> 16.0 per 100 lines (wallet: 14.6).
- Removed five marketing idioms with no precedent on the wallet page:
'without lifting a finger', 'in the driver's seat', 'doesn't move the
needle', 'running a billing engine by hand', 'a heads-up'.
Every factual claim is unchanged; this only affects how they are worded.
Refs #158912
…cle statuses
Accuracy and consistency pass on both AutoPay pages, verified against
autopay/subscriptions/{enums,schemas}.py.
- notification_preferences: documented the three real boolean keys —
pre_charge_enabled, failure_enabled, final_failure_enabled, each
defaulting to true (schemas.py:52-54). Previously described only as
'free-form'.
- List subscriptions returns a slim summary per item, not the full
subscription object. Callers need Retrieve for the active card and
latest-cycle detail.
- Cycle statuses: added retry_scheduled and canceled, completing the set
a merchant can observe. needs_triage is deliberately omitted — it is an
ops-only state reached by stale-cleanup, not something a merchant acts on.
- US spelling for consistency with the rest of the docs: catalogue ->
catalog, cancelling -> canceling.
Refs #158909 #158910 #158912
@jab3z

Copy link
Copy Markdown
Contributor

@claude review

@claude

claudeBot commented Aug 13, 2026

Copy link
Copy Markdown

Claude finished @jab3z's task in 3m 14s —— View job


Review complete

  • Gather context (PR body, changed files list)
  • Read diff (git diff origin/dev...HEAD)
  • Review developer AutoPay page + flow diagram
  • Review business AutoPay page
  • Review sidebars.ts, glossary, cross-links
  • Check CLAUDE.md compliance (API base URL constant, merchant_id, cross-linking, etc.)
  • Post final review

Note: node_modules isn't installed in this sandbox and installing it wasn't in scope, so I couldn't independently re-run npm run build/npm run typecheck — this review is a manual read of the diff against origin/dev and the repo's CLAUDE.md conventions.

Overall this is a strong, well-verified page pair — the sidebar anchor hygiene (sidebars.ts) all checks out against actual heading IDs on both pages, the <AutoPayFlowDiagram> follows the svg-diagram skill convention correctly (monochrome + 2 accent nodes, light/dark overrides), OTTU_CONNECT_BASE_URL is used consistently instead of hardcoded URLs, and the cross-linking between AutoPay and recurring-payments.mdx is exactly the kind of disambiguation the domain needs. A few things worth a look before merging to dev:

1. "Copy-paste accurate" claim conflicts with the PR's own "base path unconfirmed" gap

docs/developers/payments/autopay/index.mdx:437-441 (API Reference section):

"Every request and response shown above is copy-paste accurate"

But the PR description itself flags "Endpoint base path unconfirmed... Merchants reach these through a Connect proxy whose public path is in PR 121 — @yamen needs your answer." The six subscription-management cURL examples (#step-by-step) hit plain ${OTTU_CONNECT_BASE_URL}/subscriptions/... with no proxy prefix, while the one endpoint you have confirmed (checkout, step 1) is prefixed /b/checkout/v1/pymt-txn/. Since the base path for steps 2–6 is explicitly still pending confirmation, the "copy-paste accurate" claim overstates what's verified — a developer could reasonably build against /subscriptions/... directly and hit 404s once the real proxy path lands. Consider either softening that line or adding the same base-path caveat that's already implicit in the PR's "known gaps" table, so it's visible to a reader on docs.ottu.dev, not just to reviewers on this PR.
Fix this →

2. New glossary terms aren't linked from either new page

src/data/glossary-terms.ts adds Billing Cycle, Dunning, and Subscription, all introduced specifically for AutoPay — but neither docs/developers/payments/autopay/index.mdx nor docs/business/autopay/index.md links to /glossary/ on first use, despite root CLAUDE.md's "no jargon without definition — first use links to the glossary" rule and the developer CLAUDE.md cross-referencing table. (This rule is loosely followed elsewhere in the repo too, so treat this as a nice-to-have rather than a blocker — but since these terms were added for this PR, it's a natural fit to wire them up here.)

3. Two shipped screenshots are never referenced, one contradicts the page copy

git diff --stat shows 20 new business screenshots, but only 18 are referenced from docs/business/autopay/index.md:

  • static/img/business/autopay/portal-04-expired.png — the "Expired" step in the state <StepGuide> (docs/business/autopay/index.md:135-138) has no image/imageAlt, even though this exact screenshot exists.
  • static/img/business/autopay/cards-01-delete-confirm.png — unreferenced anywhere. This one is worth double-checking specifically: the page states "Saved cards are never deleted... there is no removal step to walk them through" (line 195), but a screenshot named cards-01-delete-confirm strongly implies a delete-confirmation dialog exists in the product. Given the PR already flagged portal-01-summary-active.png as a stale mockup that contradicts the real product, this looks like it could be the same class of issue — either the copy is right and this is a leftover asset that should be dropped, or card deletion does exist and the copy needs correcting. Worth a quick check with @Menna/@junaid alongside the portal-01 fix.

Not re-litigated

The factual claims (write-only autopay block, customer_email 422 behavior, page-link vs regenerate-page-token, dead retry_window_days, slim list summary, signed-not-encrypted token) all match what's described in the PR body as independently verified against autopay/subscriptions/ source and a live betabulk call — I didn't have backend access to re-verify these, so I'm relying on the PR author's stated verification method there.
· branch epic/158909

…rop mockups
Three points from the PR 169 review.
The API Reference note claimed every request and response above was
copy-paste accurate, while the PR itself flags the Connect proxy prefix for
the six management endpoints as unconfirmed. That gap was visible to
reviewers and invisible to a reader on docs.ottu.dev. Replaced the claim
with a warning under Step-by-Step naming exactly what is uncertain — the
path in front of /subscriptions/ — and stating that the headers, bodies,
responses and behaviour are not, and that Step 1's /b/checkout/v1/pymt-txn/
is the stable Checkout API path.
Billing Cycle, Dunning and Subscription were added to glossary-terms.ts for
this PR and nothing linked to them. Linked each once per page via
/glossary/#term-<id>. GlossarySearch server-renders those ids, so the
anchors resolve natively; Docusaurus still reports them broken because its
checker only reads markdown headings — the same false positive already
fires for the #term-* links on payment-states and transaction-states.
portal-04-expired.png and cards-01-delete-confirm.png were unreferenced
because both depict flows that do not exist: the first shows a Reactivate
button on an Expired subscription (terminal in the backend) plus an invoice
Download column, the second a delete-card confirmation. Dereferenced in
b5475bf and 79835e2 respectively; deleting the assets so they cannot be
wired back in. They need recapture from betabulk alongside
portal-01-summary-active.png.
npm run build passes.
@ankitottu

Copy link
Copy Markdown
ContributorAuthor

@claude re-review

@claude

claudeBot commented Aug 13, 2026

Copy link
Copy Markdown

Claude finished @ankitottu's task in 1m 38s —— View job


Re-review complete

  • Gather context (PR body, prior review, changed files)
  • Check what changed since the last review (commit 34e4484)
  • Verify the three prior findings were actually resolved
  • Fresh pass over the full diff for anything new
  • Post final review

Note: node_modules still isn't installed in this sandbox, so I couldn't independently re-run npm run build/npm run typecheck — this is a manual read of git diff origin/dev...HEAD plus the four fix commits added since the first review (b5475bf, 79835e2, 93c1567, 34e4484).

All three points from the first review are resolved cleanly in 34e4484:

1. "Copy-paste accurate" overclaim — fixed. docs/developers/payments/autopay/index.mdx:62-65 now has a dedicated :::warning The path in front of /subscriptions/ isn't final block naming exactly what's unconfirmed (the Connect proxy prefix for Steps 2–6) while explicitly stating headers/bodies/responses/behaviour are accurate, and that Step 1's /b/checkout/v1/pymt-txn/ is stable. The old blanket "copy-paste accurate" line under ## API Reference (line ~444) was replaced with a pointer to that scoped caveat instead of a second overclaim. This is exactly the right fix — it's visible to a reader on docs.ottu.dev now, not just to PR reviewers.

2. Glossary terms unlinked — fixed. Billing Cycle, Dunning, and Subscription are each now linked once per page via /glossary/#term-<id>. Verified the anchor mechanism actually resolves: src/components/GlossarySearch/index.tsx:7,30-31 builds/reads the same term-${id} hash format server-side, and the ids added in src/data/glossary-terms.ts (billing-cycle, dunning, subscription) match what's linked. The commit message's note that Docusaurus's broken-link checker will still flag these as a false positive (same pattern as the existing payment-states/transaction-states pages) checks out.

3. Unreferenced/contradictory screenshots — fixed correctly, not just papered over. Both cards-01-delete-confirm.png and portal-04-expired.png are deleted from the tree (not just dereferenced), so they can't get silently wired back in later. Confirmed no remaining references to either filename anywhere in the repo, and confirmed all 19 remaining business screenshots (20 shipped − 2 dropped − wait, 18 referenced + the dropped 2 = 20 ✓) are each referenced from docs/business/autopay/index.md. The "Expired" step in the state <StepGuide> (docs/business/autopay/index.md:135-138) now correctly has no image/imageAlt at all, matching its terminal-state description instead of showing a fabricated Reactivate button.

One thing worth noting rather than re-flagging: portal-01-summary-active.png (the hero mockup the PR body itself calls out as contradicting the real product) is still in place — that's expected, since the PR description already tracks it as the open "highest-value screenshot task left" pending recapture from betabulk, not something silently missed.

Also scanned the intervening accuracy commit (93c1567, notification keys / list-summary shape / cycle statuses) — it's a good-faith correctness pass against autopay/subscriptions/{enums,schemas}.py unrelated to the review findings, nothing to flag there.

Nothing new surfaced in this pass. The two page diffs are clean additions with no leftover inconsistencies from the fix-up commits.
· branch

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ankitottu@jab3z