@stacksjs/defaults 0.75.79 → 0.75.81

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.
@@ -21,6 +21,7 @@ Full Stripe integration via the Payment facade. Uses Stripe API version `2026-01
21
21
  - Adyen: Checkout API v72, `X-API-Key`; shopper reference `user-<id>` (no PII); full refunds go to `/reversals`; webhooks verified per item with HMAC-SHA256 over 8 colon-joined fields under the hex key, acknowledged with 202.
22
22
  - `PaymentEvent` carries `provider` and `id` (together, the retry key), `reference` (the payment; for a refund, the payment refunded), `amount` (for a refund, that one refund - Stripe's cumulative `amount_refunded` is turned into a delta from `previous_attributes`) and `reason`. Adyen's id is `eventCode:pspReference:success`, since Adyen may resend a pair with the outcome changed.
23
23
  - The Stripe driver delegates to the existing billable modules, so idempotency keys and `stripe_id` handling are unchanged.
24
+ - Webhook route: the `payments` default route bundle (mounted with the `commerce` feature) serves `POST /webhooks/payments` through `orders.receivePaymentWebhook(request)`: 401 `missing-config` while the driver's secret is unset (`WebhookNotConfiguredError`), 400 `invalid-signature` (`WebhookSignatureError`), otherwise applies the events and answers with `acknowledgeWebhook()`.
24
25
  - Commerce: `orders.handleCommercePaymentEvent(event)` from `@stacksjs/commerce` applies `payment.succeeded` / `payment.failed` (reason into `payments.failure_reason`) / `refund.succeeded` (added to `refund_amount`; `refunded` + order REFUNDED only when it covers the payment, else `partiallyRefunded`) to the order, deduplicated in `payment_webhook_events` (the `PaymentWebhookEvent` model) in the same transaction.
25
26
  - Everything below this section is Stripe's own API and returns Stripe objects.
26
27
 
@@ -546,7 +547,7 @@ The instance methods are exactly the keys of `createBillableMethods` in
546
547
 
547
548
  ## Gotchas
548
549
  - Stripe API keys MUST be in `.env` as `STRIPE_SECRET_KEY` and `STRIPE_PUBLISHABLE_KEY` -- never hardcode them in config files
549
- - The Stripe SDK is initialized eagerly -- if `STRIPE_SECRET_KEY` is missing, the module throws on import
550
+ - The Stripe client is created lazily, on first use: importing `@stacksjs/payments` without `STRIPE_SECRET_KEY` (or without the opt-in `stripe` package) is fine, and the first Stripe call throws naming what is missing
550
551
  - All amounts are in cents -- use `toCents()` and `toDollars()` for conversion
551
552
  - `charge()` creates AND confirms the PaymentIntent in one step
552
553
  - `subscribe()` resolves the price via `lookup_key`, not a direct Stripe price ID
@@ -2,7 +2,7 @@ import { Action } from '@stacksjs/actions/runtime'
2
2
  import { isBillable } from '@stacksjs/orm'
3
3
  import { BILLING_NOT_ENABLED } from '@stacksjs/payments'
4
4
  import { response } from '@stacksjs/router'
5
- import { forBrowser, paymentFailure } from './payment-response'
5
+ import { paymentFailure } from './payment-response'
6
6
 
7
7
  export default new Action({
8
8
  name: 'CreateSetupIntentAction',
@@ -2,7 +2,7 @@ import { Action } from '@stacksjs/actions/runtime'
2
2
  import { isBillable } from '@stacksjs/orm'
3
3
  import { BILLING_NOT_ENABLED } from '@stacksjs/payments'
4
4
  import { response } from '@stacksjs/router'
5
- import { forBrowser, paymentFailure } from './payment-response'
5
+ import { paymentFailure } from './payment-response'
6
6
 
7
7
  export default new Action({
8
8
  name: 'DeleteDefaultPaymentAction',
@@ -2,7 +2,7 @@ import { Action } from '@stacksjs/actions/runtime'
2
2
  import { isBillable } from '@stacksjs/orm'
3
3
  import { BILLING_NOT_ENABLED } from '@stacksjs/payments'
4
4
  import { response } from '@stacksjs/router'
5
- import { forBrowser, paymentFailure } from './payment-response'
5
+ import { paymentFailure } from './payment-response'
6
6
 
7
7
  export default new Action({
8
8
  name: 'FetchPaymentCustomerAction',
package/bootstrap.ts CHANGED
@@ -153,6 +153,13 @@ if (mounts('delivery')) {
153
153
  await route.register(frameworkPath('defaults/routes/delivery.ts'))
154
154
  }
155
155
 
156
+ // The payment provider's webhook, which applies payments and refunds to
157
+ // orders. Gated with the commerce surface; it answers 401 until the driver's
158
+ // webhook secret is configured.
159
+ if (mounts('payments')) {
160
+ await route.register(frameworkPath('defaults/routes/payments.ts'))
161
+ }
162
+
156
163
  // Social sign-in: `/auth/{provider}` + `/auth/{provider}/callback`
157
164
  // (stacksjs/stacks#2276). An opt-in bundle, NOT part of the implicit default
158
165
  // set or `all` — OAuth callback URLs in an app that configured no provider
@@ -2,7 +2,7 @@
2
2
  "publisher": "Stacks",
3
3
  "name": "vscode-stacks",
4
4
  "displayName": "Stacks",
5
- "version": "0.75.79",
5
+ "version": "0.75.81",
6
6
  "description": "Stacks, built into VS Code: stx templates, pickier linting and formatting, encrypted env files, the dev server preview and every buddy command. No other extensions required.",
7
7
  "license": "MIT",
8
8
  "funding": "https://github.com/sponsors/chrisbbreuer",
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/defaults",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.75.79",
5
+ "version": "0.75.81",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/stacksjs/stacks.git",
@@ -55,7 +55,7 @@
55
55
  "dependencies": {
56
56
  "@iconify-json/f7": "^1.2.2",
57
57
  "@iconify-json/hugeicons": "^1.2.35",
58
- "@stacksjs/mobile": "^0.75.79",
58
+ "@stacksjs/mobile": "^0.75.81",
59
59
  "@stacksjs/sanitizer": "^0.2.378",
60
60
  "@xterm/addon-fit": "^0.11.0",
61
61
  "@xterm/xterm": "^6.0.0",
@@ -0,0 +1,17 @@
1
+ import { orders } from '@stacksjs/commerce'
2
+ import { route } from '@stacksjs/router'
3
+
4
+ /**
5
+ * The payment provider's webhook (stacksjs/stacks#665, #1879 Co-17).
6
+ *
7
+ * Point the provider at `POST /webhooks/payments`: Stripe's endpoint with its
8
+ * signing secret in `STRIPE_WEBHOOK_SECRET`, or Adyen's standard webhook with
9
+ * its HMAC key in `ADYEN_HMAC_KEY`, whichever `config.payment.driver` names.
10
+ * Payments and refunds then reach their orders without app code; orders used
11
+ * to sit at PENDING whenever the webhook was the only news of a payment.
12
+ *
13
+ * Unconfigured, it answers 401 and applies nothing. CSRF-exempt, since the
14
+ * provider calls it; the signature is what authenticates it. An app route at
15
+ * the same path registers first and wins.
16
+ */
17
+ route.post('/webhooks/payments', request => orders.receivePaymentWebhook(request)).skipCsrf()