Skip to content

Commit 50b0296

Browse files
committed
Ship v0.3 installable widget flow
1 parent 9b6471b commit 50b0296

18 files changed

Lines changed: 1122 additions & 34 deletions

README.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Live app: <https://p4nd4907.github.io/panda-notes/>
1010

1111
Widget demo: <https://p4nd4907.github.io/panda-notes/widget-demo.html>
1212

13+
Install widget: <https://p4nd4907.github.io/panda-notes/install.html>
14+
1315
Paid services: <https://p4nd4907.github.io/panda-notes/services.html>
1416

1517
Private intake: <https://p4nd4907.github.io/panda-notes/private-intake.html>
@@ -22,7 +24,7 @@ Need it installed or cleaned up for you? Start with the [Panda Notes services pa
2224

2325
- Testers capture the broken, confusing, missing, cramped, or slow part of a product while the target context is fresh.
2426
- Developers review a grouped repair queue with note evidence, role/tag filters, code hints, JSON exports, and issue drafts.
25-
- Teams keep the public tool free and local-first, then pay for setup sprints, feedback cleanup, or private integration when they want help.
27+
- Teams can self-host the MIT code, then pay for setup sprints, feedback cleanup, or private integration when they want it handled cleanly.
2628

2729
## Focus
2830

@@ -40,7 +42,7 @@ Need it installed or cleaned up for you? Start with the [Panda Notes services pa
4042
- GitHub-ready issue draft copy/export with tester evidence, labels, target code, and test plan.
4143
- Repair prompt copy/export for handing a selected issue to Codex, ChatGPT, or another code assistant without adding secrets to the widget.
4244
- Developer packet copy/export and JSON import/export for moving notes between browsers or projects.
43-
- Alpha embeddable right-click widget at `/panda-notes-widget.js`.
45+
- v0.3 installable right-click widget at `/panda-notes-widget.js`, with optional launcher, hotkey, private-intake link, copied issue drafts, and target code hints.
4446

4547
## Can I Use It In My Project?
4648

@@ -52,6 +54,7 @@ Current integration:
5254
- Import/export Panda Notes JSON between projects or browsers.
5355
- Copy developer packets and GitHub-ready issue drafts.
5456
- Add the alpha right-click widget to a project with one script tag.
57+
- Generate a paid-install snippet at <https://p4nd4907.github.io/panda-notes/install.html>.
5558

5659
Alpha widget install:
5760

@@ -62,7 +65,11 @@ Alpha widget install:
6265
project: "my-app",
6366
role: "beta",
6467
mode: "local",
65-
contextMenu: true
68+
contextMenu: true,
69+
launcher: true,
70+
hotkey: "ctrl+shift+p",
71+
privateIntakeUrl: "https://p4nd4907.github.io/panda-notes/private-intake.html",
72+
installPlan: "setup-sprint"
6673
});
6774
</script>
6875
```

api/_lib/stripeWebhook.js

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ const acceptedEventTypes = new Set([
66
'payment_intent.succeeded'
77
]);
88

9+
const offerLabels = {
10+
'setup-sprint': 'Setup Sprint',
11+
'developer-handoff': 'Developer Handoff Pack',
12+
'private-integration': 'Private Integration',
13+
'scope-needed': 'Scope Needed'
14+
};
15+
916
export function verifyStripeSignature(rawBody, signatureHeader, secret, toleranceSeconds = 300) {
1017
if (!signatureHeader || !secret) return false;
1118
const parts = Object.fromEntries(
@@ -34,29 +41,39 @@ export function shouldRecordStripeEvent(event) {
3441
return acceptedEventTypes.has(event?.type);
3542
}
3643

37-
export function buildStripeIssue(event) {
44+
export function buildStripeIssue(event, options = {}) {
3845
const object = event?.data?.object || {};
3946
const payment = extractPaymentSummary(event);
47+
const offerLabel = offerLabels[payment.offerKey] || 'Panda Notes Service';
48+
const intakeLink = buildPaidIntakeUrl({
49+
offerKey: payment.offerKey,
50+
eventId: event.id,
51+
email: payment.email,
52+
paymentIntent: payment.paymentIntent
53+
}, options.baseUrl || process.env.PANDA_NOTES_BASE_URL || 'https://p4nd4907.github.io/panda-notes/');
4054
const marker = `stripe-event-${event.id}`;
41-
const title = `[Stripe Paid] ${payment.amountLabel} - ${payment.email || payment.customer || event.id}`;
55+
const title = `[Stripe Paid] ${offerLabel} - ${payment.amountLabel} - ${payment.email || payment.customer || event.id}`;
4256
const body = [
4357
`<!-- ${marker} -->`,
4458
'# Panda Notes Stripe Confirmation',
4559
'',
4660
`Event ID: ${event.id}`,
4761
`Event type: ${event.type}`,
62+
`Offer: ${offerLabel}`,
4863
`Created: ${event.created ? new Date(event.created * 1000).toISOString() : new Date().toISOString()}`,
4964
`Amount: ${payment.amountLabel}`,
5065
`Customer email: ${payment.email || 'Not provided'}`,
5166
`Customer ID: ${payment.customer || 'Not provided'}`,
5267
`Payment link: ${payment.paymentLink || 'Not provided'}`,
5368
`Checkout session: ${object.id || 'Not provided'}`,
5469
`Payment intent: ${payment.paymentIntent || 'Not provided'}`,
70+
`Private intake link: ${intakeLink}`,
5571
'',
5672
'## Metadata',
5773
codeBlock(JSON.stringify(object.metadata || {}, null, 2)),
5874
'',
5975
'## Owner Checklist',
76+
'- Send the private intake link to the customer if they did not already submit scope.',
6077
'- Match this payment to a private intake packet or public-safe GitHub request.',
6178
'- Add `payment-confirmed` to the matching work item.',
6279
'- Confirm scope and delivery date before work starts.'
@@ -66,7 +83,7 @@ export function buildStripeIssue(event) {
6683
marker,
6784
title,
6885
body,
69-
labels: ['paid-service', 'payment-confirmed', 'stripe-deposit']
86+
labels: ['paid-service', 'payment-confirmed', 'stripe-deposit', payment.offerKey].filter(Boolean)
7087
};
7188
}
7289

@@ -75,17 +92,33 @@ export function extractPaymentSummary(event) {
7592
const amount = object.amount_total ?? object.amount_received ?? object.amount ?? 0;
7693
const currency = String(object.currency || 'usd').toUpperCase();
7794
const email = object.customer_details?.email || object.receipt_email || object.billing_details?.email || '';
95+
const offerKey = normalizeOfferKey(object.metadata?.panda_notes_offer || object.metadata?.service || '');
7896
return {
7997
amount,
8098
amountLabel: formatAmount(amount, currency),
8199
currency,
82100
customer: typeof object.customer === 'string' ? object.customer : '',
83101
email,
102+
offerKey,
84103
paymentIntent: typeof object.payment_intent === 'string' ? object.payment_intent : object.id || '',
85104
paymentLink: typeof object.payment_link === 'string' ? object.payment_link : ''
86105
};
87106
}
88107

108+
export function buildPaidIntakeUrl({
109+
offerKey = 'scope-needed',
110+
eventId = '',
111+
email = '',
112+
paymentIntent = ''
113+
} = {}, baseUrl = 'https://p4nd4907.github.io/panda-notes/') {
114+
const url = new URL('private-intake.html', withTrailingSlash(baseUrl));
115+
url.searchParams.set('service', offerLabels[normalizeOfferKey(offerKey)] || 'Not sure yet');
116+
if (eventId) url.searchParams.set('paymentReference', eventId);
117+
if (email) url.searchParams.set('replyEmail', email);
118+
if (paymentIntent) url.searchParams.set('stripePayment', paymentIntent);
119+
return url.toString();
120+
}
121+
89122
function formatAmount(amount, currency) {
90123
const major = Number(amount || 0) / 100;
91124
return `${major.toLocaleString('en-US', { style: 'currency', currency })} ${currency}`;
@@ -95,6 +128,15 @@ function codeBlock(value) {
95128
return ['```json', value, '```'].join('\n');
96129
}
97130

131+
function normalizeOfferKey(value) {
132+
const key = String(value || '').trim().toLowerCase();
133+
return offerLabels[key] ? key : 'scope-needed';
134+
}
135+
136+
function withTrailingSlash(value) {
137+
return String(value || '').endsWith('/') ? String(value) : `${value}/`;
138+
}
139+
98140
function secureCompareHex(a, b) {
99141
try {
100142
const left = Buffer.from(a, 'hex');

docs/EMBEDDING.md

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22

33
Panda Notes is MIT licensed, so teams may link to it, copy it, fork it, and adapt it for their own projects as long as the license notice stays with the software.
44

5+
Paid setup is available when a team wants Panda Notes installed, configured, and handed off without doing the integration work themselves.
6+
57
## Current Integration
68

7-
Today, Panda Notes includes a standalone local-first console and an alpha right-click widget:
9+
Today, Panda Notes includes a standalone local-first console and a v0.3 right-click widget:
810

911
- Send testers to the hosted app: <https://p4nd4907.github.io/panda-notes/>
12+
- Generate a paid-install snippet: <https://p4nd4907.github.io/panda-notes/install.html>
1013
- Try the widget demo: <https://p4nd4907.github.io/panda-notes/widget-demo.html>
1114
- Export notes as JSON from one browser and import them into another.
1215
- Copy a developer packet or GitHub-ready issue draft from the selected target issue.
@@ -25,7 +28,11 @@ Projects can load the widget during alpha or beta testing:
2528
project: "my-app",
2629
role: "beta",
2730
mode: "local",
28-
contextMenu: true
31+
contextMenu: true,
32+
launcher: true,
33+
hotkey: "ctrl+shift+p",
34+
privateIntakeUrl: "https://p4nd4907.github.io/panda-notes/private-intake.html",
35+
installPlan: "setup-sprint"
2936
});
3037
</script>
3138
```
@@ -35,8 +42,8 @@ Expected tester flow:
3542
1. Tester right-clicks a target UI element.
3643
2. Panda Notes opens a small themed note popup.
3744
3. Tester picks a tag and writes what happened.
38-
4. The widget captures page, route, viewport, exact right-click point, element label, CSS path, and optional `data-component`.
39-
5. Notes remain local until the tester exports JSON or copies a handoff packet.
45+
4. The widget captures page, route, viewport, exact right-click point, element label, CSS selector/path, selected text, optional `data-component`, and optional code hints.
46+
5. Notes remain local until the tester exports JSON, copies a handoff packet, or copies a GitHub-ready issue draft.
4047

4148
## Suggested Markup
4249

@@ -46,6 +53,8 @@ Apps that want better developer handoff should add stable hints to important con
4653
<button
4754
data-component="CheckoutSubmitButton"
4855
data-panda-target="Checkout submit"
56+
data-panda-file="src/checkout/CheckoutSubmitButton.jsx"
57+
data-panda-symbol="handleCheckoutSubmit"
4958
>
5059
Place order
5160
</button>
@@ -60,20 +69,28 @@ PandaNotes.init({
6069
project: "my-app",
6170
role: "alpha", // alpha, beta, or developer
6271
storageKey: "panda-notes-widget:my-app",
63-
contextMenu: true
72+
contextMenu: true,
73+
launcher: true,
74+
hotkey: "ctrl+shift+p",
75+
privateIntakeUrl: "https://p4nd4907.github.io/panda-notes/private-intake.html",
76+
installPlan: "setup-sprint"
6477
});
6578

79+
PandaNotes.openForTarget(document.querySelector('[data-panda-target]'), { x: 320, y: 240 });
6680
PandaNotes.getNotes();
6781
PandaNotes.exportNotes();
6882
PandaNotes.destroy();
6983
```
7084

7185
The exported JSON is compatible with the standalone console import flow.
7286

87+
## Paid Install Checklist
88+
89+
For a setup sprint, send the app URL, the target flows, tester roles, preferred delivery window, and whether the widget should use only right-click, the launcher button, the hotkey, or all three. Keep credentials and private files out of public GitHub issues; use the private intake page for scope and move secrets only through the agreed private channel.
90+
7391
## Still To Strengthen
7492

7593
- Optional screenshot or DOM snapshot hook for projects that opt in.
76-
- Optional GitHub issue draft handoff from captured notes.
7794
- Import helper that sends widget JSON directly into the standalone console.
7895
- More browser coverage around cross-origin app shells and shadow DOM targets.
7996
- No hidden telemetry remains the default.

docs/STRIPE.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ Create three one-time deposit links:
1212

1313
The public Services page reads `public/stripe-links.json`. If a Stripe URL is present for an offer, its button goes to Stripe checkout. If a Stripe URL is blank, the button falls back to the matching GitHub intake form.
1414

15+
Generated Payment Links redirect buyers back to Panda Notes after checkout. The current automation routes buyers to the private intake path so the paid customer can submit service, payment reference, reply email, scope, deadline, project URL, and delivery context without posting private details to public GitHub issues.
16+
1517
## Dashboard Setup
1618

1719
1. Open Stripe Dashboard Payment Links.
@@ -70,7 +72,7 @@ Never commit `sk_test_...` or `sk_live_...` keys. `.env` and `.env.local` are ig
7072

7173
## Webhook Confirmation
7274

73-
Panda Notes includes a serverless webhook at `/api/stripe-webhook`. It verifies Stripe's `Stripe-Signature` header with `STRIPE_WEBHOOK_SECRET`, ignores unrelated event types, and creates a private `payment-confirmed` issue in the private intake repo for successful checkout events.
75+
Panda Notes includes a serverless webhook at `/api/stripe-webhook`. It verifies Stripe's `Stripe-Signature` header with `STRIPE_WEBHOOK_SECRET`, ignores unrelated event types, and creates a private `payment-confirmed` issue in the private intake repo for successful checkout events. The private issue includes the matched offer, amount, customer email when Stripe provides it, and a ready-to-send private intake link.
7476

7577
Do not expose the webhook signing secret in GitHub Pages, public issues, or committed files.
7678

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "panda-notes",
3-
"version": "0.2.8",
3+
"version": "0.3.0",
44
"private": false,
55
"description": "Standalone developer and alpha/beta tester feedback console.",
66
"author": "P4ND4907",

0 commit comments

Comments
 (0)