@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.
- package/ai/skills/stacks-payments/SKILL.md +2 -1
- package/app/Actions/Payment/CreateSetupIntentAction.ts +1 -1
- package/app/Actions/Payment/DeleteDefaultPaymentAction.ts +1 -1
- package/app/Actions/Payment/FetchPaymentCustomerAction.ts +1 -1
- package/bootstrap.ts +7 -0
- package/ide/vscode/package.json +1 -1
- package/package.json +2 -2
- package/routes/payments.ts +17 -0
|
@@ -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
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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
|
package/ide/vscode/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"publisher": "Stacks",
|
|
3
3
|
"name": "vscode-stacks",
|
|
4
4
|
"displayName": "Stacks",
|
|
5
|
-
"version": "0.75.
|
|
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.
|
|
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.
|
|
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()
|