glyphpay 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,184 +1,184 @@
1
- # glyphpay
2
-
3
- Drop-in connector that lets a website take payments through [GlyphPay](https://glyphpay.net) by Design Glyph. Payments go to the site owner's own Stripe connected account.
4
-
5
- | Import | Runs on | What it gives you |
6
- | --- | --- | --- |
7
- | `glyphpay/server` | Site's server | `glyphPay()`: `checkout` and `webhook` route handlers, plus `createPayment`, `getPayment`, `refund`, `reconcile` |
8
- | `glyphpay/react` | Browser | `<GlyphPayCheckout />` |
9
- | `glyphpay/browser` | Browser, any site | `mountCheckout(el, options)` for sites without React |
10
-
11
- The CLI covers setup, diagnostics, and credential lifecycle:
12
-
13
- | Command | What it does |
14
- | --- | --- |
15
- | `glyphpay connect` | Pair a site through the browser and write the server credential. |
16
- | `glyphpay doctor` | Check env, token, Stripe readiness, webhook URL, and an optional signed test event. |
17
- | `glyphpay rotate` | Re-pair the site and replace the credential. |
18
- | `glyphpay disconnect` | Revoke the credential and remove the local keys. |
19
- | `glyphpay deliveries` | List recent webhook deliveries and retry one with `--retry <id>`. |
20
- | `glyphpay init` | Scaffold routes for Next.js, Remix, Astro, SvelteKit, Hono, or Express. |
21
-
22
- ## Connect a site from the CLI
23
-
24
- ```sh
25
- npx glyphpay connect --url https://client-site.com
26
- ```
27
-
28
- The command opens Glyph Pay in your browser. Sign in as the client's billing email (or a Design Glyph admin), pick the client account, confirm the webhook URL and approve the pairing code. The terminal receives a test service credential and, when a webhook URL was registered, its signing secret. It writes both to `.env.local`:
29
-
30
- ```sh
31
- GLYPHPAY_TOKEN=dgsvc_test_...
32
- GLYPHPAY_CALLBACK_SECRET=dgcb_...
33
- ```
34
-
35
- - `--url <site>` registers `<site>/api/glyphpay/webhook`. Pass a full URL to use a different route.
36
- - `--env <path>` targets another env file; `--print` prints the values instead of writing.
37
- - `--no-open`, `--base-url` and `--timeout` help scripts and local testing.
38
- - Codes expire after ten minutes. Approving again rotates the token: the previous token stops working immediately.
39
- - Pairing is disabled while the platform is in live mode.
40
-
41
- The browser never receives the credential. Keep the token in server environment variables and never prefix it with `NEXT_PUBLIC_`, `VITE_` or `PUBLIC_`.
42
-
43
- ## Check, rotate, and disconnect
44
-
45
- ```sh
46
- npx glyphpay doctor
47
- npx glyphpay doctor --json
48
- npx glyphpay rotate
49
- npx glyphpay disconnect
50
- ```
51
-
52
- `doctor` verifies the local env file, checks for credentials leaked into public-prefixed variables, validates the token with Glyph Pay, checks Stripe charge readiness and the registered webhook, and optionally sends a signed test event:
53
-
54
- ```sh
55
- npx glyphpay doctor --send-test-event
56
- ```
57
-
58
- The test event has type `webhook.test` and a zero-dollar `data` payload. Ignore event types you do not handle in `onEvent`; do not treat the test event as a real payment.
59
-
60
- `rotate` re-runs browser approval and reuses the registered callback URL when the current token is still valid. `disconnect` revokes the credential immediately and removes `GLYPHPAY_TOKEN` and `GLYPHPAY_CALLBACK_SECRET` from the env file; pass `--yes` in scripts or `--keep-env` to leave the local values in place.
61
-
62
- ## Inspect and retry webhook deliveries
63
-
64
- ```sh
65
- npx glyphpay deliveries
66
- npx glyphpay deliveries --retry evt_...
67
- ```
68
-
69
- `deliveries` lists the most recent callback outbox records for the credential's client and environment: event type, delivery state, attempt count, timestamp, and the last error. A retry sends the exact stored signed event again and persists the result. Retry is one attempt; fix the endpoint first. The admin **Connections** tab exposes the same operations.
70
-
71
- ## Scaffold a site
72
-
73
- ```sh
74
- npx glyphpay init
75
- ```
76
-
77
- The command detects the project framework from `package.json`. It creates a server connector plus the framework-native checkout and webhook entry points, and appends placeholders to `.env.example` without changing existing values. Supported targets:
78
-
79
- - **Next.js App Router** — `app/api/glyphpay/*` or `src/app/api/glyphpay/*` route handlers.
80
- - **SvelteKit** — `src/routes/api/glyphpay/*/+server.ts` with `src/lib/server/glyphpay.ts`.
81
- - **Astro** — `src/pages/api/glyphpay/*.ts`; use server/SSR output and an adapter.
82
- - **Remix** — `app/routes/api.glyphpay.*.ts` with `app/lib/glyphpay.server.ts`.
83
- - **Hono** — `src/glyphpay-routes.ts`; mount it with `app.route("/api/glyphpay", glyphpayRoutes)`.
84
- - **Express** — `src/glyphpay-routes.ts`; mount it with `app.use("/api/glyphpay", glyphpayRoutes)`. The webhook route uses `express.raw` so signatures verify against the original bytes.
85
-
86
- It refuses to overwrite existing files unless you pass `--force`; use `--dry-run` to preview. Then run `glyphpay connect` and wire `priceOrder()` and `onEvent()` to your order database.
87
-
88
- ## Manual setup: connect the client in Design Glyph admin
89
-
90
- 1. **Admin → Clients → (client)**: make sure Stripe onboarding is complete (charges enabled).
91
- 2. **Issue a service credential** (test first). Copy the `dgsvc_test_…` token. It is shown only once.
92
- 3. **Set the callback URL** to `https://<client-site>/api/glyphpay/webhook`. Copy the signing secret. It is also shown only once.
93
-
94
- ## Add to the client site
95
-
96
- ```sh
97
- pnpm add glyphpay
98
- ```
99
-
100
- `.env` (server only, never `NEXT_PUBLIC_`):
101
-
102
- ```sh
103
- GLYPHPAY_TOKEN=dgsvc_test_...
104
- GLYPHPAY_CALLBACK_SECRET=...
105
- ```
106
-
107
- `lib/glyphpay.ts`
108
-
109
- ```ts
110
- import { glyphPay, CheckoutError } from "glyphpay/server";
111
-
112
- export const glyphpay = glyphPay({
113
- token: process.env.GLYPHPAY_TOKEN,
114
- callbackSecret: process.env.GLYPHPAY_CALLBACK_SECRET,
115
-
116
- // Price the order from YOUR data. Never trust an amount sent by the browser.
117
- async priceOrder(body) {
118
- const cart = await db.carts.get((body as { cartId: string }).cartId);
119
- if (!cart?.items.length) throw new CheckoutError("Your cart is empty.");
120
- // orderId must change whenever the total changes, or GlyphPay returns 409.
121
- return { orderId: `${cart.id}-v${cart.version}`, amountCents: cart.totalCents, description: `Order ${cart.id}` };
122
- },
123
-
124
- // Signed and verified. At-least-once delivery: dedupe on event.id. Throw to have it retried.
125
- async onEvent(event) {
126
- if (event.type === "payment.succeeded") await db.orders.markPaid(event.data.orderId, event.id);
127
- },
128
- });
129
- ```
130
-
131
- `app/api/glyphpay/checkout/route.ts`
132
-
133
- ```ts
134
- import { glyphpay } from "@/lib/glyphpay";
135
- export const POST = glyphpay.checkout;
136
- ```
137
-
138
- `app/api/glyphpay/webhook/route.ts`
139
-
140
- ```ts
141
- import { glyphpay } from "@/lib/glyphpay";
142
- export const POST = glyphpay.webhook;
143
- ```
144
-
145
- Checkout page:
146
-
147
- ```tsx
148
- import { GlyphPayCheckout } from "glyphpay/react";
149
-
150
- <GlyphPayCheckout order={{ cartId }} returnUrl={`/thank-you?cart=${cartId}`} appearance={{ theme: "stripe" }} />
151
- ```
152
-
153
- Style the button with `.glyphpay button { … }`. Errors appear in `.glyphpay [role=alert]`.
154
-
155
- Without React:
156
-
157
- ```html
158
- <div id="pay"></div>
159
- <script type="module">
160
- import { mountCheckout } from "glyphpay/browser";
161
- mountCheckout(document.getElementById("pay"), { order: { cartId: "42" }, returnUrl: "/thank-you" });
162
- </script>
163
- ```
164
-
165
- The route handlers take a standard `Request` and return a `Response`, so the same two lines work in Remix, Astro, Hono, SvelteKit, and Cloudflare Workers. In Express, `glyphpay init` generates a small adapter that preserves the raw webhook body.
166
-
167
- ## How a payment flows
168
-
169
- 1. The component POSTs `order` to `/api/glyphpay/checkout`. `priceOrder` sets the amount, and the site's server calls GlyphPay with the token.
170
- 2. The browser gets back only `clientSecret`, `publishableKey`, and `stripeAccount`, then mounts Stripe's Payment Element.
171
- 3. The shopper pays, and Stripe redirects to `returnUrl`. **The redirect doesn't prove payment.**
172
- 4. GlyphPay POSTs `payment.succeeded` to `/api/glyphpay/webhook`, and `onEvent` marks the order paid. The thank-you page reads the order status from your database, or calls `glyphpay.getPayment(paymentId)`.
173
-
174
- Refunds run server-side:
175
-
176
- ```ts
177
- await glyphpay.refund(paymentId, { idempotencyKey: `${orderId}-refund-1` });
178
- ```
179
-
180
- ## Develop
181
-
182
- ```sh
183
- cd packages/glyphpay && npm install && npm test
184
- ```
1
+ # glyphpay
2
+
3
+ Drop-in connector that lets a website take payments through [GlyphPay](https://glyphpay.net) by Design Glyph. Payments go to the site owner's own Stripe connected account.
4
+
5
+ | Import | Runs on | What it gives you |
6
+ | --- | --- | --- |
7
+ | `glyphpay/server` | Site's server | `glyphPay()`: `checkout` and `webhook` route handlers, plus `createPayment`, `getPayment`, `refund`, `reconcile` |
8
+ | `glyphpay/react` | Browser | `<GlyphPayCheckout />` |
9
+ | `glyphpay/browser` | Browser, any site | `mountCheckout(el, options)` for sites without React |
10
+
11
+ The CLI covers setup, diagnostics, and credential lifecycle:
12
+
13
+ | Command | What it does |
14
+ | --- | --- |
15
+ | `glyphpay connect` | Pair a site through the browser and write the server credential. |
16
+ | `glyphpay doctor` | Check env, token, Stripe readiness, webhook URL, and an optional signed test event. |
17
+ | `glyphpay rotate` | Re-pair the site and replace the credential. |
18
+ | `glyphpay disconnect` | Revoke the credential and remove the local keys. |
19
+ | `glyphpay deliveries` | Page/filter webhook deliveries and retry one with `--retry <id>`. |
20
+ | `glyphpay init` | Scaffold routes for Next.js, Remix, Astro, SvelteKit, Hono, or Express. |
21
+
22
+ ## Connect a site from the CLI
23
+
24
+ ```sh
25
+ npx glyphpay connect --url https://client-site.com
26
+ ```
27
+
28
+ The command opens Glyph Pay in your browser. Sign in as the client's billing email (or a Design Glyph admin), pick the client account, confirm the webhook URL and approve the pairing code. The terminal receives a test service credential and, when a webhook URL was registered, its signing secret. It writes both to `.env.local`:
29
+
30
+ ```sh
31
+ GLYPHPAY_TOKEN=dgsvc_test_...
32
+ GLYPHPAY_CALLBACK_SECRET=dgcb_...
33
+ ```
34
+
35
+ - `--url <site>` registers `<site>/api/glyphpay/webhook`. Pass a full URL to use a different route.
36
+ - `--env <path>` targets another env file; `--print` prints the values instead of writing.
37
+ - `--no-open`, `--base-url` and `--timeout` help scripts and local testing.
38
+ - Codes expire after ten minutes. Approving again rotates the token: the previous token stops working immediately.
39
+ - Pairing is disabled while the platform is in live mode.
40
+
41
+ The browser never receives the credential. Keep the token in server environment variables and never prefix it with `NEXT_PUBLIC_`, `VITE_` or `PUBLIC_`.
42
+
43
+ ## Check, rotate, and disconnect
44
+
45
+ ```sh
46
+ npx glyphpay doctor
47
+ npx glyphpay doctor --json
48
+ npx glyphpay rotate
49
+ npx glyphpay disconnect
50
+ ```
51
+
52
+ `doctor` verifies the local env file, checks for credentials leaked into public-prefixed variables, validates the token with Glyph Pay, checks Stripe charge readiness and the registered webhook, and optionally sends a signed test event:
53
+
54
+ ```sh
55
+ npx glyphpay doctor --send-test-event
56
+ ```
57
+
58
+ The test event has type `webhook.test` and a zero-dollar `data` payload. Ignore event types you do not handle in `onEvent`; do not treat the test event as a real payment.
59
+
60
+ `rotate` re-runs browser approval and reuses the registered callback URL when the current token is still valid. `disconnect` revokes the credential immediately and removes `GLYPHPAY_TOKEN` and `GLYPHPAY_CALLBACK_SECRET` from the env file; pass `--yes` in scripts or `--keep-env` to leave the local values in place.
61
+
62
+ ## Inspect and retry webhook deliveries
63
+
64
+ ```sh
65
+ npx glyphpay deliveries
66
+ npx glyphpay deliveries --retry evt_...
67
+ ```
68
+
69
+ `deliveries` pages through callback outbox records for the credential's client and environment: event type, delivery state, attempt count, timestamp, and the last error. Filter with `--state pending|delivered|failed`, control the page with `--limit 1..100`, and pass the returned `--cursor` for the next page. A retry sends the exact stored signed event again and persists the result. Retry is one attempt; fix the endpoint first. The admin **Connections** tab exposes the same filtering, pagination, and retry operations.
70
+
71
+ ## Scaffold a site
72
+
73
+ ```sh
74
+ npx glyphpay init
75
+ ```
76
+
77
+ The command detects the project framework from `package.json`. It creates a server connector plus the framework-native checkout and webhook entry points, and appends placeholders to `.env.example` without changing existing values. Supported targets:
78
+
79
+ - **Next.js App Router** — `app/api/glyphpay/*` or `src/app/api/glyphpay/*` route handlers.
80
+ - **SvelteKit** — `src/routes/api/glyphpay/*/+server.ts` with `src/lib/server/glyphpay.ts`.
81
+ - **Astro** — `src/pages/api/glyphpay/*.ts`; use server/SSR output and an adapter.
82
+ - **Remix** — `app/routes/api.glyphpay.*.ts` with `app/lib/glyphpay.server.ts`.
83
+ - **Hono** — `src/glyphpay-routes.ts`; mount it with `app.route("/api/glyphpay", glyphpayRoutes)`.
84
+ - **Express** — `src/glyphpay-routes.ts`; mount it with `app.use("/api/glyphpay", glyphpayRoutes)`. The webhook route uses `express.raw` so signatures verify against the original bytes.
85
+
86
+ It refuses to overwrite existing files unless you pass `--force`; use `--dry-run` to preview. Then run `glyphpay connect` and wire `priceOrder()` and `onEvent()` to your order database.
87
+
88
+ ## Manual setup: connect the client in Design Glyph admin
89
+
90
+ 1. **Admin → Clients → (client)**: make sure Stripe onboarding is complete (charges enabled).
91
+ 2. **Issue a service credential** (test first). Copy the `dgsvc_test_…` token. It is shown only once.
92
+ 3. **Set the callback URL** to `https://<client-site>/api/glyphpay/webhook`. Copy the signing secret. It is also shown only once.
93
+
94
+ ## Add to the client site
95
+
96
+ ```sh
97
+ pnpm add glyphpay
98
+ ```
99
+
100
+ `.env` (server only, never `NEXT_PUBLIC_`):
101
+
102
+ ```sh
103
+ GLYPHPAY_TOKEN=dgsvc_test_...
104
+ GLYPHPAY_CALLBACK_SECRET=...
105
+ ```
106
+
107
+ `lib/glyphpay.ts`
108
+
109
+ ```ts
110
+ import { glyphPay, CheckoutError } from "glyphpay/server";
111
+
112
+ export const glyphpay = glyphPay({
113
+ token: process.env.GLYPHPAY_TOKEN,
114
+ callbackSecret: process.env.GLYPHPAY_CALLBACK_SECRET,
115
+
116
+ // Price the order from YOUR data. Never trust an amount sent by the browser.
117
+ async priceOrder(body) {
118
+ const cart = await db.carts.get((body as { cartId: string }).cartId);
119
+ if (!cart?.items.length) throw new CheckoutError("Your cart is empty.");
120
+ // orderId must change whenever the total changes, or GlyphPay returns 409.
121
+ return { orderId: `${cart.id}-v${cart.version}`, amountCents: cart.totalCents, description: `Order ${cart.id}` };
122
+ },
123
+
124
+ // Signed and verified. At-least-once delivery: dedupe on event.id. Throw to have it retried.
125
+ async onEvent(event) {
126
+ if (event.type === "payment.succeeded") await db.orders.markPaid(event.data.orderId, event.id);
127
+ },
128
+ });
129
+ ```
130
+
131
+ `app/api/glyphpay/checkout/route.ts`
132
+
133
+ ```ts
134
+ import { glyphpay } from "@/lib/glyphpay";
135
+ export const POST = glyphpay.checkout;
136
+ ```
137
+
138
+ `app/api/glyphpay/webhook/route.ts`
139
+
140
+ ```ts
141
+ import { glyphpay } from "@/lib/glyphpay";
142
+ export const POST = glyphpay.webhook;
143
+ ```
144
+
145
+ Checkout page:
146
+
147
+ ```tsx
148
+ import { GlyphPayCheckout } from "glyphpay/react";
149
+
150
+ <GlyphPayCheckout order={{ cartId }} returnUrl={`/thank-you?cart=${cartId}`} appearance={{ theme: "stripe" }} />
151
+ ```
152
+
153
+ Style the button with `.glyphpay button { … }`. Errors appear in `.glyphpay [role=alert]`.
154
+
155
+ Without React:
156
+
157
+ ```html
158
+ <div id="pay"></div>
159
+ <script type="module">
160
+ import { mountCheckout } from "glyphpay/browser";
161
+ mountCheckout(document.getElementById("pay"), { order: { cartId: "42" }, returnUrl: "/thank-you" });
162
+ </script>
163
+ ```
164
+
165
+ The route handlers take a standard `Request` and return a `Response`, so the same two lines work in Remix, Astro, Hono, SvelteKit, and Cloudflare Workers. In Express, `glyphpay init` generates a small adapter that preserves the raw webhook body.
166
+
167
+ ## How a payment flows
168
+
169
+ 1. The component POSTs `order` to `/api/glyphpay/checkout`. `priceOrder` sets the amount, and the site's server calls GlyphPay with the token.
170
+ 2. The browser gets back only `clientSecret`, `publishableKey`, and `stripeAccount`, then mounts Stripe's Payment Element.
171
+ 3. The shopper pays, and Stripe redirects to `returnUrl`. **The redirect doesn't prove payment.**
172
+ 4. GlyphPay POSTs `payment.succeeded` to `/api/glyphpay/webhook`, and `onEvent` marks the order paid. The thank-you page reads the order status from your database, or calls `glyphpay.getPayment(paymentId)`.
173
+
174
+ Refunds run server-side:
175
+
176
+ ```ts
177
+ await glyphpay.refund(paymentId, { idempotencyKey: `${orderId}-refund-1` });
178
+ ```
179
+
180
+ ## Develop
181
+
182
+ ```sh
183
+ cd packages/glyphpay && npm install && npm test
184
+ ```
package/dist/cli.d.ts CHANGED
@@ -51,6 +51,12 @@ export interface CliOptions {
51
51
  dir?: string;
52
52
  /** Deliveries: replay one failed or delivered callback by id. */
53
53
  retry?: string;
54
+ /** Deliveries: filter by delivery state. */
55
+ state?: "pending" | "delivered" | "failed";
56
+ /** Deliveries: opaque next-page cursor returned by the API. */
57
+ cursor?: string;
58
+ /** Deliveries: page size, 1-100. */
59
+ limit?: number;
54
60
  }
55
61
  export interface ConnectResult {
56
62
  businessName: string;
@@ -79,6 +85,7 @@ export interface CallbackDeliveriesResponse {
79
85
  businessName: string;
80
86
  environment: "test" | "live";
81
87
  deliveries: CallbackDeliveryView[];
88
+ nextCursor: string | null;
82
89
  }
83
90
  export interface CallbackReplayResponse {
84
91
  outcome: "delivered" | "retry" | "failed";
package/dist/cli.js CHANGED
@@ -20,7 +20,7 @@ import { access, chmod, mkdir, readFile, stat, writeFile } from "node:fs/promise
20
20
  import { dirname, join, relative, resolve } from "node:path";
21
21
  import { createInterface } from "node:readline/promises";
22
22
  import { pathToFileURL } from "node:url";
23
- const VERSION = "0.4.0";
23
+ const VERSION = "0.5.0";
24
24
  const DEFAULT_BASE_URL = "https://designglyph.web.app";
25
25
  const DEFAULT_ENV_FILE = ".env.local";
26
26
  const DEFAULT_TIMEOUT_SECONDS = 600;
@@ -180,6 +180,40 @@ export function parseArgs(argv) {
180
180
  }
181
181
  continue;
182
182
  }
183
+ if (flag === "--state") {
184
+ const raw = inlineValue ?? takeValue(argv, index, flag)[0];
185
+ if (inlineValue === undefined)
186
+ index += 1;
187
+ if (raw !== "pending" && raw !== "delivered" && raw !== "failed") {
188
+ throw new CliUsageError("--state must be pending, delivered, or failed.");
189
+ }
190
+ options.state = raw;
191
+ continue;
192
+ }
193
+ if (flag === "--cursor") {
194
+ if (inlineValue !== undefined) {
195
+ if (!inlineValue)
196
+ throw new CliUsageError("--cursor needs a value.");
197
+ options.cursor = inlineValue;
198
+ }
199
+ else {
200
+ const [value, next] = takeValue(argv, index, flag);
201
+ options.cursor = value;
202
+ index = next;
203
+ }
204
+ continue;
205
+ }
206
+ if (flag === "--limit") {
207
+ const raw = inlineValue ?? takeValue(argv, index, flag)[0];
208
+ if (inlineValue === undefined)
209
+ index += 1;
210
+ const parsed = Number(raw);
211
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > 100) {
212
+ throw new CliUsageError("--limit must be an integer between 1 and 100.");
213
+ }
214
+ options.limit = parsed;
215
+ continue;
216
+ }
183
217
  if (flag === "--dir") {
184
218
  if (inlineValue !== undefined) {
185
219
  if (!inlineValue)
@@ -211,44 +245,47 @@ export function parseArgs(argv) {
211
245
  return { command, options };
212
246
  }
213
247
  export function helpText() {
214
- return `glyphpay - take payments on your site through Glyph Pay
215
-
216
- Usage
217
- glyphpay connect [options] Connect a site (alias: login)
218
- glyphpay rotate [options] Re-pair and rotate the current credential
219
- glyphpay disconnect [options] Revoke the credential and remove local keys
220
- glyphpay doctor [options] Check env, token, Stripe and webhook setup
221
- glyphpay deliveries [options] List recent webhook deliveries
222
- glyphpay init [options] Scaffold an integration for your framework
223
- glyphpay --version
224
-
225
- Connect / rotate
226
- --url <site-url> Site to connect. Adds ${WEBHOOK_PATH} when the URL
227
- has no path. Or pass the exact callback URL.
228
- --env <path> Env file to update (default: ${DEFAULT_ENV_FILE})
229
- --print Print the credentials instead of writing a file
230
- --no-open Do not open a browser automatically
231
- --base-url <url> Glyph Pay API origin (default: ${DEFAULT_BASE_URL})
232
- --timeout <seconds> Wait for approval (default: ${DEFAULT_TIMEOUT_SECONDS})
233
-
234
- Doctor
235
- --json Print a machine-readable report
236
- --send-test-event POST a signed webhook.test event to the callback URL
237
-
238
- Disconnect
239
- --yes, -y Do not ask for confirmation
240
- --keep-env Keep the local env values after revocation
241
-
242
- Deliveries
243
- --retry <id> Send one stored webhook again (alias: webhooks)
244
- --json Print a machine-readable response
245
-
246
- Init
247
- --dir <path> Project directory (default: current directory)
248
- --dry-run Show planned files without writing them
249
- --force Overwrite generated files
250
-
251
- The browser step signs in to Glyph Pay, lets you pick the client account, and
248
+ return `glyphpay - take payments on your site through Glyph Pay
249
+
250
+ Usage
251
+ glyphpay connect [options] Connect a site (alias: login)
252
+ glyphpay rotate [options] Re-pair and rotate the current credential
253
+ glyphpay disconnect [options] Revoke the credential and remove local keys
254
+ glyphpay doctor [options] Check env, token, Stripe and webhook setup
255
+ glyphpay deliveries [options] List recent webhook deliveries
256
+ glyphpay init [options] Scaffold an integration for your framework
257
+ glyphpay --version
258
+
259
+ Connect / rotate
260
+ --url <site-url> Site to connect. Adds ${WEBHOOK_PATH} when the URL
261
+ has no path. Or pass the exact callback URL.
262
+ --env <path> Env file to update (default: ${DEFAULT_ENV_FILE})
263
+ --print Print the credentials instead of writing a file
264
+ --no-open Do not open a browser automatically
265
+ --base-url <url> Glyph Pay API origin (default: ${DEFAULT_BASE_URL})
266
+ --timeout <seconds> Wait for approval (default: ${DEFAULT_TIMEOUT_SECONDS})
267
+
268
+ Doctor
269
+ --json Print a machine-readable report
270
+ --send-test-event POST a signed webhook.test event to the callback URL
271
+
272
+ Disconnect
273
+ --yes, -y Do not ask for confirmation
274
+ --keep-env Keep the local env values after revocation
275
+
276
+ Deliveries
277
+ --state <state> pending, delivered, or failed
278
+ --limit <n> Page size, 1-100 (default: 50)
279
+ --cursor <cursor> Next-page cursor from the previous response
280
+ --retry <id> Send one stored webhook again (alias: webhooks)
281
+ --json Print a machine-readable response
282
+
283
+ Init
284
+ --dir <path> Project directory (default: current directory)
285
+ --dry-run Show planned files without writing them
286
+ --force Overwrite generated files
287
+
288
+ The browser step signs in to Glyph Pay, lets you pick the client account, and
252
289
  approves this machine. The service credential goes only to this terminal.`;
253
290
  }
254
291
  /* ------------------------------------------------------------------ */
@@ -890,7 +927,15 @@ export async function runDeliveries(options = {}) {
890
927
  }
891
928
  return result;
892
929
  }
893
- const result = await api(baseUrl, "/credential/deliveries", undefined, token);
930
+ const params = new URLSearchParams();
931
+ if (options.state)
932
+ params.set("state", options.state);
933
+ if (options.limit)
934
+ params.set("limit", String(options.limit));
935
+ if (options.cursor)
936
+ params.set("cursor", options.cursor);
937
+ const query = params.toString();
938
+ const result = await api(baseUrl, `/credential/deliveries${query ? `?${query}` : ""}`, undefined, token);
894
939
  if (options.json) {
895
940
  say(JSON.stringify(result, null, 2));
896
941
  return result;
@@ -899,13 +944,22 @@ export async function runDeliveries(options = {}) {
899
944
  say(style(1, `Webhook deliveries - ${result.businessName} (${result.environment} mode)`));
900
945
  say();
901
946
  if (result.deliveries.length === 0) {
902
- say("No deliveries recorded yet.");
947
+ say(options.state ? `No ${options.state} deliveries recorded.` : "No deliveries recorded yet.");
903
948
  }
904
949
  else {
905
950
  for (const delivery of result.deliveries)
906
951
  printDelivery(delivery);
907
952
  say();
908
953
  say("Retry one with `glyphpay deliveries --retry <id>`.");
954
+ if (result.nextCursor) {
955
+ const next = ["glyphpay deliveries"];
956
+ if (options.state)
957
+ next.push("--state", options.state);
958
+ if (options.limit)
959
+ next.push("--limit", String(options.limit));
960
+ next.push("--cursor", result.nextCursor);
961
+ say(`Next page: ${next.join(" ")}`);
962
+ }
909
963
  }
910
964
  return result;
911
965
  }
@@ -913,28 +967,28 @@ function libTemplate(ts) {
913
967
  const cartId = ts
914
968
  ? `const cartId = (body as { cartId?: string } | null)?.cartId;`
915
969
  : `const cartId = body?.cartId;`;
916
- return `import { glyphPay, CheckoutError } from "glyphpay/server";
917
-
918
- export const glyphpay = glyphPay({
919
- token: process.env.GLYPHPAY_TOKEN,
920
- callbackSecret: process.env.GLYPHPAY_CALLBACK_SECRET,
921
-
922
- async priceOrder(body) {
923
- // Load the cart/order from YOUR database and return its server-side price.
924
- // Never trust an amount sent by the browser.
925
- ${cartId}
926
- if (!cartId) throw new CheckoutError("Your cart is empty.");
927
- throw new CheckoutError("Connect priceOrder() to your order database before accepting payments.");
928
- },
929
-
930
- async onEvent(event) {
931
- // At-least-once delivery: dedupe on event.id before changing an order.
932
- if (event.type === "payment.succeeded") {
933
- // TODO: mark the order paid and store event.id.
934
- console.log(\`GlyphPay payment succeeded for \${event.data.orderId}\`);
935
- }
936
- },
937
- });
970
+ return `import { glyphPay, CheckoutError } from "glyphpay/server";
971
+
972
+ export const glyphpay = glyphPay({
973
+ token: process.env.GLYPHPAY_TOKEN,
974
+ callbackSecret: process.env.GLYPHPAY_CALLBACK_SECRET,
975
+
976
+ async priceOrder(body) {
977
+ // Load the cart/order from YOUR database and return its server-side price.
978
+ // Never trust an amount sent by the browser.
979
+ ${cartId}
980
+ if (!cartId) throw new CheckoutError("Your cart is empty.");
981
+ throw new CheckoutError("Connect priceOrder() to your order database before accepting payments.");
982
+ },
983
+
984
+ async onEvent(event) {
985
+ // At-least-once delivery: dedupe on event.id before changing an order.
986
+ if (event.type === "payment.succeeded") {
987
+ // TODO: mark the order paid and store event.id.
988
+ console.log(\`GlyphPay payment succeeded for \${event.data.orderId}\`);
989
+ }
990
+ },
991
+ });
938
992
  `;
939
993
  }
940
994
  function routeTemplate(libImport) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glyphpay",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Drop-in GlyphPay checkout for websites, with a CLI for setup, diagnostics, and credential lifecycle.",
5
5
  "homepage": "https://glyphpay.net",
6
6
  "keywords": [