Spike: Stripe integration architecture (Payment Links, webhooks, k8s secrets) #128

Open
opened 2026-06-06 21:09:11 +00:00 by ldraney · 1 comment
Owner

Type

Spike

Lineage

Informs #125 (Stripe integration) and #124 (admin request management). Child of spike #121. Phase 3. Resolves Open Question #5 from docs/user-stories-auth.md (webhook vs polling for payment status).

Repo

ldraney/landscaping-assistant

Question

What is the right Stripe integration pattern for admin-generated payment links with webhook-driven status updates, and how do we wire the existing ~/secrets/stripe/ credentials into the k8s deployment?

  • Payment Links API vs Checkout Sessions — the flow design is settled (admin-generated per-request links). Confirm the API-level product choice: do Payment Links support metadata (to map back to ServiceRequest ID)?
  • Webhook architecture — endpoint routing (/webhooks/stripe), signature verification via Stripe::Webhook.construct_event, idempotency keys. Interaction with Rails CSRF protection (skip_forgery_protection)?
  • Secrets wiring — keys exist at ~/secrets/stripe/ (test-secret-key, test-webhook-secret, plus live equivalents). Path from files to k8s secret to Rails env vars. New pal-e-services entry or manual sealed secret?
  • Test vs live modesk_test_* for dev, sk_live_* for prod. Environment-based switching. Separate Stripe test webhook endpoint registration?
  • stripe gem — confirm stripe Ruby gem compatibility with Rails 8.1 and that it's worth the dependency for Payment Links + one webhook.
  • Payment link lifecycle — expiration, regeneration, duplicate payment handling, declined/expired link UX.
  • Webhook endpoint registration — manual via Stripe dashboard or API-driven on deploy? Tailscale Funnel provides the public URL.

Deliverables

Required outputs:

  • docs/stripe-integration.md created — architecture decisions covering all questions above, with rationale and tradeoffs. Merged via docs-only PR.
  • #125 acceptance criteria updated with any new requirements discovered. If scope changes significantly, create additional tickets.

Time-box

1 session. If any question requires Stripe API experimentation, note it as a follow-up task rather than blocking the spike. If time-box expires, document findings in the docs file and escalate remaining questions to Lucas for direction.

  • landscaping-assistant — project slug
  • #121 — parent spike (client request flow design)
  • #125 — Stripe integration (implementation ticket this spike informs)
  • #122 — ServiceRequest model (data layer dependency)
  • #124 — Admin request management (UI that triggers payment links)
  • ~/secrets/stripe/ — existing Stripe credentials (test + live)
### Type Spike ### Lineage Informs #125 (Stripe integration) and #124 (admin request management). Child of spike #121. Phase 3. Resolves Open Question #5 from `docs/user-stories-auth.md` (webhook vs polling for payment status). ### Repo `ldraney/landscaping-assistant` ### Question What is the right Stripe integration pattern for admin-generated payment links with webhook-driven status updates, and how do we wire the existing `~/secrets/stripe/` credentials into the k8s deployment? - **Payment Links API vs Checkout Sessions** — the flow design is settled (admin-generated per-request links). Confirm the API-level product choice: do Payment Links support metadata (to map back to ServiceRequest ID)? - **Webhook architecture** — endpoint routing (`/webhooks/stripe`), signature verification via `Stripe::Webhook.construct_event`, idempotency keys. Interaction with Rails CSRF protection (`skip_forgery_protection`)? - **Secrets wiring** — keys exist at `~/secrets/stripe/` (test-secret-key, test-webhook-secret, plus live equivalents). Path from files to k8s secret to Rails env vars. New pal-e-services entry or manual sealed secret? - **Test vs live mode** — `sk_test_*` for dev, `sk_live_*` for prod. Environment-based switching. Separate Stripe test webhook endpoint registration? - **stripe gem** — confirm `stripe` Ruby gem compatibility with Rails 8.1 and that it's worth the dependency for Payment Links + one webhook. - **Payment link lifecycle** — expiration, regeneration, duplicate payment handling, declined/expired link UX. - **Webhook endpoint registration** — manual via Stripe dashboard or API-driven on deploy? Tailscale Funnel provides the public URL. ### Deliverables Required outputs: - [ ] `docs/stripe-integration.md` created — architecture decisions covering all questions above, with rationale and tradeoffs. Merged via docs-only PR. - [ ] #125 acceptance criteria updated with any new requirements discovered. If scope changes significantly, create additional tickets. ### Time-box 1 session. If any question requires Stripe API experimentation, note it as a follow-up task rather than blocking the spike. If time-box expires, document findings in the docs file and escalate remaining questions to Lucas for direction. ### Related - `landscaping-assistant` — project slug - #121 — parent spike (client request flow design) - #125 — Stripe integration (implementation ticket this spike informs) - #122 — ServiceRequest model (data layer dependency) - #124 — Admin request management (UI that triggers payment links) - `~/secrets/stripe/` — existing Stripe credentials (test + live)
Author
Owner

Issue #128 Spike Review

TEMPLATE CONFORMANCE

Checked against template-issue-spike from pal-e-docs.

Required Section Present Well-formed Notes
Type Yes Yes Correctly says "Spike"
Lineage Yes Yes References #125 and #124, states Phase 3
Repo Yes Yes ldraney/landscaping-assistant
Question Yes Yes Clear framing question with 7 sub-questions
Deliverables Yes Partial See finding below
Time-box Yes Yes "1 session" with escalation plan
Related Yes Yes References 4 issues + filesystem path

Deliverables format issue: The template requires checkboxes (- [ ]) for both deliverables. The issue body has two deliverables but they are rendered as plain bullet points without checkboxes. Should be:

- [ ] `docs/stripe-integration.md` created ...
- [ ] #125 acceptance criteria updated ...

This is a minor formatting nit, but checkboxes are functional in Forgejo (trackable completion).

Deliverables content issue: The template specifies two required outputs for every spike: (1) a docs file, and (2) "Follow-up tickets created or existing tickets updated." The issue's second deliverable says "#125 acceptance criteria updated" which is close but slightly narrows the scope. The template allows for creating new tickets if scope changes significantly. The issue does mention this ("If scope changes significantly, create additional tickets") so this is acceptable.


CONTENT QUALITY

Framing question: GOOD. The top-level question is clear and specific: "What is the right Stripe integration pattern for admin-generated payment links with webhook-driven status updates, and how do we wire the existing ~/secrets/stripe/ credentials into the k8s deployment?" This is answerable and scoped.

Sub-questions: COMPREHENSIVE but has overlap with already-answered material. This is the main finding.

Spike #121 (PR #127) already documented several decisions in docs/user-stories-auth.md:

  1. "Payment Links API vs Checkout Sessions" -- The existing doc already states the flow: "Admin sends Stripe payment link / Client gets notification" and "Admin sets price" then client pays. The decision that admin generates payment links is already made. What remains unknown is the API-level choice (Payment Links API vs Checkout Sessions API). The sub-question is valid but should acknowledge that the flow design is settled and frame it as: "Confirm Payment Links API is the right Stripe product for the already-designed admin-generates-link flow."

  2. "stripe gem vs raw HTTP" -- This is a reasonable question, but the answer is almost certainly "use the gem." This sub-question risks spending time on a question with an obvious answer. Consider whether this actually needs spike investigation or can just be stated as a decision.

  3. All other sub-questions (webhook architecture, secrets wiring, test vs live, payment link lifecycle, webhook endpoint registration) -- These are genuinely unknown and well-scoped. Good spike material.

Time-box: GOOD. "1 session" is realistic for a docs-only investigation. The escalation clause ("note it as a follow-up task rather than blocking the spike") is exactly right for preventing rabbit holes.

Related links: VERIFIED. All referenced issues exist:

  • #125 (open) -- Stripe integration implementation ticket
  • #122 (open) -- ServiceRequest model
  • #124 (open) -- Admin request management
  • #121 (open) -- Parent spike

The ~/secrets/stripe/ reference is a filesystem path, not an issue -- appropriate for context.


OVERLAP WITH EXISTING DOCS

docs/user-stories-auth.md (lines 267-274) already defines the ServiceRequest model with stripe_payment_link field and the status lifecycle requested -> quoted -> paid -> scheduled -> completed | declined. Open Question #5 in that doc explicitly asks: "Do we listen for Stripe payment webhooks to auto-update ServiceRequest status, or does admin manually mark as paid? Webhooks are cleaner but need a public endpoint."

This spike is well-positioned to answer that open question. The spike should reference Open Question #5 from docs/user-stories-auth.md explicitly, to make it clear this spike resolves that question rather than independently re-discovering it.

Recommendation: Add to the Question section or Lineage: "Resolves Open Question #5 from docs/user-stories-auth.md (Stripe webhook vs polling)."


NITS

  1. Lineage says "Informs #125 and #124" but does not mention #121 as the parent spike. #121 is listed in Related but the Lineage should acknowledge the chain: #121 produced the design, #128 digs into the Stripe-specific technical questions that #121 left open.

  2. Sub-question on skip_forgery_protection in the webhook section is a good catch -- this is a real Rails footgun. Well done including it.

  3. Missing sub-question: The existing data model has ServiceRequest.stripe_payment_link as a single string field. The spike should ask whether one link per ServiceRequest is sufficient, or whether re-generation (e.g., expired link) means the model needs a stripe_payment_links association or at minimum a link history. This connects to the "payment link lifecycle" sub-question but should be explicit about the data model impact.


SOP COMPLIANCE

  • Type is "Spike" -- correct template used
  • All required sections present
  • Deliverables missing checkboxes (minor)
  • Time-box defined with escalation plan
  • Related references exist and are valid
  • No secrets or credentials in the issue body (filesystem path ~/secrets/stripe/ is a reference, not a credential)
  • Scope is appropriate -- not duplicating #121, focuses on technical Stripe questions

VERDICT: APPROVED

The spike is well-formed, correctly scoped, and asks the right questions. The three recommendations below would improve it but are not blockers:

  1. Add checkboxes to Deliverables (- [ ] format)
  2. Add explicit reference to resolving Open Question #5 from docs/user-stories-auth.md
  3. Acknowledge in Lineage that this spike is a child of #121 (not just related)

The sub-question about "stripe gem vs raw HTTP" is low-value (answer is near-certain), but including it in a 1-session time-box is harmless -- it can be answered in one sentence during the spike.

## Issue #128 Spike Review ### TEMPLATE CONFORMANCE Checked against `template-issue-spike` from pal-e-docs. | Required Section | Present | Well-formed | Notes | |-----------------|---------|-------------|-------| | Type | Yes | Yes | Correctly says "Spike" | | Lineage | Yes | Yes | References #125 and #124, states Phase 3 | | Repo | Yes | Yes | `ldraney/landscaping-assistant` | | Question | Yes | Yes | Clear framing question with 7 sub-questions | | Deliverables | Yes | Partial | See finding below | | Time-box | Yes | Yes | "1 session" with escalation plan | | Related | Yes | Yes | References 4 issues + filesystem path | **Deliverables format issue:** The template requires checkboxes (`- [ ]`) for both deliverables. The issue body has two deliverables but they are rendered as plain bullet points without checkboxes. Should be: ``` - [ ] `docs/stripe-integration.md` created ... - [ ] #125 acceptance criteria updated ... ``` This is a minor formatting nit, but checkboxes are functional in Forgejo (trackable completion). **Deliverables content issue:** The template specifies two required outputs for every spike: (1) a docs file, and (2) "Follow-up tickets created or existing tickets updated." The issue's second deliverable says "#125 acceptance criteria updated" which is close but slightly narrows the scope. The template allows for creating *new* tickets if scope changes significantly. The issue does mention this ("If scope changes significantly, create additional tickets") so this is acceptable. --- ### CONTENT QUALITY **Framing question: GOOD.** The top-level question is clear and specific: "What is the right Stripe integration pattern for admin-generated payment links with webhook-driven status updates, and how do we wire the existing `~/secrets/stripe/` credentials into the k8s deployment?" This is answerable and scoped. **Sub-questions: COMPREHENSIVE but has overlap with already-answered material.** This is the main finding. Spike #121 (PR #127) already documented several decisions in `docs/user-stories-auth.md`: 1. **"Payment Links API vs Checkout Sessions"** -- The existing doc already states the flow: "Admin sends Stripe payment link / Client gets notification" and "Admin sets price" then client pays. The *decision* that admin generates payment links is already made. What remains unknown is the *API-level choice* (Payment Links API vs Checkout Sessions API). The sub-question is valid but should acknowledge that the flow design is settled and frame it as: "Confirm Payment Links API is the right Stripe product for the already-designed admin-generates-link flow." 2. **"stripe gem vs raw HTTP"** -- This is a reasonable question, but the answer is almost certainly "use the gem." This sub-question risks spending time on a question with an obvious answer. Consider whether this actually needs spike investigation or can just be stated as a decision. 3. **All other sub-questions (webhook architecture, secrets wiring, test vs live, payment link lifecycle, webhook endpoint registration)** -- These are genuinely unknown and well-scoped. Good spike material. **Time-box: GOOD.** "1 session" is realistic for a docs-only investigation. The escalation clause ("note it as a follow-up task rather than blocking the spike") is exactly right for preventing rabbit holes. **Related links: VERIFIED.** All referenced issues exist: - #125 (open) -- Stripe integration implementation ticket - #122 (open) -- ServiceRequest model - #124 (open) -- Admin request management - #121 (open) -- Parent spike The `~/secrets/stripe/` reference is a filesystem path, not an issue -- appropriate for context. --- ### OVERLAP WITH EXISTING DOCS `docs/user-stories-auth.md` (lines 267-274) already defines the `ServiceRequest` model with `stripe_payment_link` field and the status lifecycle `requested -> quoted -> paid -> scheduled -> completed | declined`. Open Question #5 in that doc explicitly asks: "Do we listen for Stripe payment webhooks to auto-update ServiceRequest status, or does admin manually mark as paid? Webhooks are cleaner but need a public endpoint." This spike is well-positioned to *answer* that open question. The spike should reference Open Question #5 from `docs/user-stories-auth.md` explicitly, to make it clear this spike resolves that question rather than independently re-discovering it. **Recommendation:** Add to the Question section or Lineage: "Resolves Open Question #5 from `docs/user-stories-auth.md` (Stripe webhook vs polling)." --- ### NITS 1. **Lineage says "Informs #125 and #124"** but does not mention #121 as the parent spike. #121 is listed in Related but the Lineage should acknowledge the chain: #121 produced the design, #128 digs into the Stripe-specific technical questions that #121 left open. 2. **Sub-question on `skip_forgery_protection`** in the webhook section is a good catch -- this is a real Rails footgun. Well done including it. 3. **Missing sub-question:** The existing data model has `ServiceRequest.stripe_payment_link` as a single string field. The spike should ask whether one link per ServiceRequest is sufficient, or whether re-generation (e.g., expired link) means the model needs a `stripe_payment_links` association or at minimum a link history. This connects to the "payment link lifecycle" sub-question but should be explicit about the data model impact. --- ### SOP COMPLIANCE - [x] Type is "Spike" -- correct template used - [x] All required sections present - [ ] Deliverables missing checkboxes (minor) - [x] Time-box defined with escalation plan - [x] Related references exist and are valid - [x] No secrets or credentials in the issue body (filesystem path `~/secrets/stripe/` is a reference, not a credential) - [x] Scope is appropriate -- not duplicating #121, focuses on technical Stripe questions --- ### VERDICT: APPROVED The spike is well-formed, correctly scoped, and asks the right questions. The three recommendations below would improve it but are not blockers: 1. Add checkboxes to Deliverables (`- [ ]` format) 2. Add explicit reference to resolving Open Question #5 from `docs/user-stories-auth.md` 3. Acknowledge in Lineage that this spike is a child of #121 (not just related) The sub-question about "stripe gem vs raw HTTP" is low-value (answer is near-certain), but including it in a 1-session time-box is harmless -- it can be answered in one sentence during the spike.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ldraney/landscaping-assistant#128
No description provided.