sanity-plugin-stripe 0.1.0 → 0.2.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/dist/server.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
- * `sanity-plugin-stripe/server`: the site's checkout route and Stripe webhook. Web `Request` and
3
- * `Response` only, so the handlers fit Next.js, Remix, Astro, SvelteKit or Hono. It imports
4
- * neither Sanity nor React.
2
+ * `sanity-plugin-stripe/server`: the site's checkout route, Stripe webhook and customer portal
3
+ * route. Web `Request` and `Response` only, so the handlers fit Next.js, Remix, Astro, SvelteKit
4
+ * or Hono. It imports neither Sanity nor React.
5
5
  */
6
- import { a as CartVariant, f as readCartProducts, n as CartLine, o as CheckoutProblem, r as CartProduct, t as Availability } from "./cart-D2KxuwQz.js";
7
- import { a as regionOptions, i as readShopSettings, n as ShippingRegion, o as formatMoney, r as ShopSettings, s as formatPrice, t as RegionOption } from "./regions-DbCIM0I1.js";
6
+ import { C as readSubscription, S as readCartProducts, a as SubscriptionDelivery, b as describeInterval, c as shippingQuote, d as CartProduct, h as Interval, i as ShopSettings, l as Availability, m as CheckoutProblem, n as ShippingQuote, o as readShopSettings, p as CartVariant, r as ShippingRegion, s as regionOptions, t as RegionOption, u as CartLine } from "./regions-Bfhg4QDs.js";
8
7
  import { t as EU_COUNTRIES } from "./countries-BSdN9FCR.js";
8
+ import { n as formatPrice, t as formatMoney } from "./money-XMQaHsbV.js";
9
9
  import Stripe$1, { Stripe } from "stripe";
10
10
  interface OrderItem {
11
11
  /** Sanity document id of the product. */
@@ -15,11 +15,25 @@ interface OrderItem {
15
15
  quantity: number;
16
16
  /** For the whole line, in the currency's minor unit (cents). */
17
17
  amountTotal: number;
18
+ /** True on a line that is a subscription: it is delivered and charged again. */
19
+ subscription?: boolean;
18
20
  }
19
- /** A paid Checkout Session, read into what a shop needs to send it. It is never saved in Sanity. */
21
+ /**
22
+ * A paid order, read into what a shop needs to send it: a paid Checkout Session, or a renewal of
23
+ * a subscription that one started. It is never saved in Sanity.
24
+ */
20
25
  interface Order {
26
+ /** The Checkout Session that took the order. For a renewal, the one that started the subscription. */
21
27
  sessionId: string;
22
28
  paymentIntentId: string | null;
29
+ /** Set on a renewal: the invoice Stripe charged for it. */
30
+ invoiceId?: string;
31
+ /** Set when the order started a subscription, and on each of its renewals. */
32
+ subscription?: {
33
+ id: string;
34
+ /** Null when Stripe holds an interval the plugin doesn't offer, such as days. */
35
+ interval: Interval | null;
36
+ };
23
37
  /** False for a payment made in a sandbox or in test mode. */
24
38
  livemode: boolean;
25
39
  currency: string;
@@ -63,8 +77,8 @@ interface ProductQueryOptions {
63
77
  export declare function cartProductProjection(options?: ProductQueryOptions): string;
64
78
  /** Every product that has the commerce field filled in, for the cart page. */
65
79
  export declare function cartProductsQuery(options?: ProductQueryOptions): string;
66
- /** The shop settings: shipping regions and whether stock sells out by itself. */
67
- export declare const SHOP_SETTINGS_QUERY = "*[_type == \"stripeShopSettings\" && _id == \"stripeShopSettings\"][0]{shippingRegions[]{_key, title, everywhereElse, countries, rate}, autoSoldOut}";
80
+ /** The shop settings: shipping regions, selling out at 0, and delivery for subscriptions. */
81
+ export declare const SHOP_SETTINGS_QUERY = "*[_type == \"stripeShopSettings\" && _id == \"stripeShopSettings\"][0]{shippingRegions[]{_key, title, everywhereElse, countries, rate, freeFrom}, autoSoldOut, subscriptionDelivery}";
68
82
  /** A product's stock, as its private stock document holds it. */
69
83
  interface ProductStock {
70
84
  /** For a product without variants. Null when it isn't counted. */
@@ -127,8 +141,8 @@ interface StockChange {
127
141
  /** Below 0 when more was sold than was in stock. */
128
142
  to: number;
129
143
  }
130
- /** Where the payment is in the Stripe Dashboard. */
131
- export declare function dashboardUrl(order: Pick<Order, 'livemode' | 'paymentIntentId' | 'sessionId'>): string;
144
+ /** Where the payment is in the Stripe Dashboard: a renewal's invoice, a subscription, a payment. */
145
+ export declare function dashboardUrl(order: Pick<Order, 'livemode' | 'paymentIntentId' | 'sessionId' | 'invoiceId' | 'subscription'>): string;
132
146
  interface OrderEmailOptions {
133
147
  /** Starts the subject line. */
134
148
  shopName: string;
@@ -144,10 +158,16 @@ interface OrderEmail {
144
158
  text: string;
145
159
  html: string;
146
160
  }
147
- /** A plain order email for the shop: what to pack, where to send it and who paid. */
161
+ /**
162
+ * A plain order email for the shop: what to pack, where to send it and who paid. It serves a
163
+ * subscription's renewal too, from `onRenewal`.
164
+ */
148
165
  export declare function orderEmail(order: Order, options: OrderEmailOptions): OrderEmail;
149
- /** The events a webhook endpoint for the shop listens to. */
150
- export declare const WEBHOOK_EVENTS: readonly ["checkout.session.completed", "checkout.session.async_payment_succeeded", "checkout.session.async_payment_failed"];
166
+ /**
167
+ * The events a webhook endpoint for the shop listens to. The last three are for subscriptions; a
168
+ * shop without any can leave them out.
169
+ */
170
+ export declare const WEBHOOK_EVENTS: readonly ["checkout.session.completed", "checkout.session.async_payment_succeeded", "checkout.session.async_payment_failed", "invoice.paid", "invoice.payment_failed", "customer.subscription.deleted"];
151
171
  type Handler = (request: Request) => Promise<Response>;
152
172
  type Log = (level: 'info' | 'warn' | 'error', message: string) => void;
153
173
  interface CheckoutMessages {
@@ -199,7 +219,12 @@ interface CheckoutRouteOptions {
199
219
  hideRemaining?: boolean;
200
220
  /** Minutes until an unpaid session expires, about 30 at least. Stripe's default is 24 hours. */
201
221
  expiresInMinutes?: number;
202
- /** Changes the session before it is created: promotion codes, phone numbers, consent. */
222
+ /**
223
+ * Shows a field for a promotion code on Stripe's page. Codes are made in the Stripe Dashboard,
224
+ * one per checkout. On a subscription, the code's coupon says whether renewals are discounted.
225
+ */
226
+ promotionCodes?: boolean;
227
+ /** Changes the session before it is created: phone numbers, consent, a customer. */
203
228
  extend?: (params: Stripe$1.Checkout.SessionCreateParams, context: {
204
229
  lines: CartLine[];
205
230
  region: ShippingRegion;
@@ -213,7 +238,9 @@ interface CheckoutRouteOptions {
213
238
  }
214
239
  /**
215
240
  * The site's checkout route. The browser posts the cart's product references and the chosen
216
- * shipping region; prices, stock and shipping are read fresh from Sanity here.
241
+ * shipping region; prices, stock and shipping are read fresh from Sanity here. A cart with a
242
+ * subscription in it becomes a subscription checkout; anything bought once in the same cart is
243
+ * charged with the first payment.
217
244
  *
218
245
  * ```ts
219
246
  * // app/api/checkout/route.ts
@@ -233,6 +260,16 @@ interface OrderContext {
233
260
  * Stripe is asked to deliver the event again, and only the stock is tried again.
234
261
  */
235
262
  stockError?: string;
263
+ /** The Checkout Session that took the order. */
264
+ session: Stripe$1.Checkout.Session;
265
+ stripe: Stripe$1;
266
+ }
267
+ interface RenewalContext extends OrderContext {
268
+ /** The paid invoice. `session` is the Checkout Session that started the subscription. */
269
+ invoice: Stripe$1.Invoice;
270
+ }
271
+ interface SubscriptionContext {
272
+ /** The Checkout Session that started the subscription. */
236
273
  session: Stripe$1.Checkout.Session;
237
274
  stripe: Stripe$1;
238
275
  }
@@ -259,13 +296,33 @@ interface WebhookRouteOptions {
259
296
  onOrder?: (order: Order, context: OrderContext) => void | Promise<void>;
260
297
  /** Called when a delayed payment (a bank transfer, for one) fails. Nothing was fulfilled. */
261
298
  onPaymentFailed?: (session: Stripe$1.Checkout.Session) => void | Promise<void>;
299
+ /**
300
+ * Called once for each paid renewal of a subscription, after stock is lowered: there is a
301
+ * parcel to send again. The order's address is the customer's as it is now. The first payment
302
+ * of a subscription is not a renewal: it reaches `onOrder`.
303
+ */
304
+ onRenewal?: (order: Order, context: RenewalContext) => void | Promise<void>;
305
+ /**
306
+ * Called when a renewal's payment fails. Stripe tries again and writes to the customer by
307
+ * itself, so there is nothing that has to be done here. It can be called more than once.
308
+ */
309
+ onRenewalFailed?: (invoice: Stripe$1.Invoice, context: SubscriptionContext) => void | Promise<void>;
310
+ /** Called when a subscription ends: cancelled, or given up on after failed payments. */
311
+ onSubscriptionEnded?: (subscription: Stripe$1.Subscription, context: SubscriptionContext) => void | Promise<void>;
312
+ /**
313
+ * Called for every other event the endpoint receives, once its signature is checked: a
314
+ * dispute, a refund. These are not sorted by shop: they are the Stripe account's.
315
+ */
316
+ onEvent?: (event: Stripe$1.Event, context: {
317
+ stripe: Stripe$1;
318
+ }) => void | Promise<void>;
262
319
  log?: Log;
263
320
  }
264
321
  /**
265
322
  * The site's Stripe webhook. A paid order of this shop lowers stock and calls `onOrder`, each
266
- * once per payment however often Stripe delivers the event. Payments made any other way on the
267
- * same Stripe account (a Payment Link, another site) are left alone. Nothing is fulfilled on the
268
- * thank-you page.
323
+ * once per payment however often Stripe delivers the event. A paid renewal of a subscription
324
+ * does the same and calls `onRenewal`. Payments made any other way on the same Stripe account (a
325
+ * Payment Link, another site) are left alone. Nothing is fulfilled on the thank-you page.
269
326
  *
270
327
  * ```ts
271
328
  * // app/api/stripe/webhook/route.ts
@@ -275,6 +332,27 @@ interface WebhookRouteOptions {
275
332
  * Point a Stripe webhook endpoint at it with the events in `WEBHOOK_EVENTS`.
276
333
  */
277
334
  export declare function webhookRoute(options: WebhookRouteOptions): Handler;
335
+ interface PortalRouteOptions {
336
+ /** From `createStripe`. */
337
+ stripe: Stripe$1 | null | undefined;
338
+ /** What a visitor reads when the portal can't be opened. */
339
+ message?: string;
340
+ log?: Log;
341
+ }
342
+ /**
343
+ * Sends a visitor to Stripe's customer portal, where a subscriber changes their card or address,
344
+ * pauses or cancels, and finds their invoices. There are no accounts to build: the visitor types
345
+ * their email on Stripe's page and Stripe emails them a link in.
346
+ *
347
+ * ```ts
348
+ * // app/account/route.ts
349
+ * export const GET = portalRoute({stripe})
350
+ * ```
351
+ *
352
+ * Switch the portal's login link on once, in the Stripe Dashboard under Settings, Billing,
353
+ * Customer portal, and choose there what subscribers may change.
354
+ */
355
+ export declare function portalRoute(options: PortalRouteOptions): () => Promise<Response>;
278
356
  /** The Stripe API version the plugin is written and tested against (stripe-node 23). */
279
357
  export declare const STRIPE_API_VERSION = "2026-10-28.endive";
280
358
  /**
@@ -289,4 +367,4 @@ export declare const STRIPE_API_VERSION = "2026-10-28.endive";
289
367
  * stripe-node's options, such as `maxNetworkRetries` or `httpClient`.
290
368
  */
291
369
  export declare function createStripe(secretKey: string, config?: Omit<Stripe.StripeConfig, 'apiVersion' | 'appInfo'>): Stripe;
292
- export { type Availability, type CartLine, type CartProduct, type CartVariant, type CheckoutMessages, type CheckoutProblem, type CheckoutRouteOptions, EU_COUNTRIES, type Order, type OrderContext, type OrderEmail, type OrderEmailOptions, type OrderItem, type ProductQueryOptions, type ProductStock, type RegionOption, type SanityReader, type SanityTransaction, type SanityWriter, type ShippingRegion, type ShopSettings, type StockChange, type WebhookRouteOptions, formatMoney, formatPrice, readCartProducts, readShopSettings, regionOptions };
370
+ export { type Availability, type CartLine, type CartProduct, type CartVariant, type CheckoutMessages, type CheckoutProblem, type CheckoutRouteOptions, EU_COUNTRIES, type Interval, type Order, type OrderContext, type OrderEmail, type OrderEmailOptions, type OrderItem, type PortalRouteOptions, type ProductQueryOptions, type ProductStock, type RegionOption, type RenewalContext, type SanityReader, type SanityTransaction, type SanityWriter, type ShippingQuote, type ShippingRegion, type ShopSettings, type StockChange, type SubscriptionContext, type SubscriptionDelivery, type WebhookRouteOptions, describeInterval, formatMoney, formatPrice, readCartProducts, readShopSettings, readSubscription, regionOptions, shippingQuote };