@shware/purchase 5.2.2 → 6.0.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/stripe/index.cjs +1 -0
- package/dist/stripe/index.d.cts +2 -2
- package/dist/stripe/index.d.mts +2 -2
- package/dist/stripe/index.mjs +2 -2
- package/dist/stripe/schema.cjs +43 -3
- package/dist/stripe/schema.cjs.map +1 -1
- package/dist/stripe/schema.d.cts +14 -1
- package/dist/stripe/schema.d.cts.map +1 -1
- package/dist/stripe/schema.d.mts +14 -1
- package/dist/stripe/schema.d.mts.map +1 -1
- package/dist/stripe/schema.mjs +44 -5
- package/dist/stripe/schema.mjs.map +1 -1
- package/package.json +1 -1
package/dist/stripe/index.cjs
CHANGED
|
@@ -26,3 +26,4 @@ exports.mapTime = require_stripe_mapper.mapTime;
|
|
|
26
26
|
exports.minorUnits = require_stripe_mapper.minorUnits;
|
|
27
27
|
exports.price = require_stripe_mapper.price;
|
|
28
28
|
exports.removePendingCheckout = require_stripe_pending_checkout.removePendingCheckout;
|
|
29
|
+
exports.returnUrlSchema = require_stripe_schema.returnUrlSchema;
|
package/dist/stripe/index.d.cts
CHANGED
|
@@ -2,8 +2,8 @@ import { CancellationDetails } from "../subscription/index.cjs";
|
|
|
2
2
|
import { PriceId, StripeConfig, StripeMode } from "./config.cjs";
|
|
3
3
|
import { StripeEventHandler } from "./handler.cjs";
|
|
4
4
|
import { BeginCheckoutProperties, CheckoutSession, LineItem, PaymentStatus, Price, ProductPrice, PurchaseProperties, getBeginCheckoutProperties, getPurchaseProperties, mapCancellationDetails, mapCharge, mapCheckoutSession, mapInvoice, mapLineItem, mapPaymentIntent, mapPrice, mapSubscriptionStatus, mapTime, minorUnits, price } from "./mapper.cjs";
|
|
5
|
-
import { CreateCheckoutSessionDTO, cancellationDetailsSchema, createCheckoutSessionSchema } from "./schema.cjs";
|
|
5
|
+
import { AllowedOrigins, CreateCheckoutSessionDTO, CreateCheckoutSessionSchemaOptions, cancellationDetailsSchema, createCheckoutSessionSchema, returnUrlSchema } from "./schema.cjs";
|
|
6
6
|
import { METADATA_KEYS } from "./metadata.cjs";
|
|
7
7
|
import { addPendingCheckout, getPendingCheckouts, removePendingCheckout } from "./pending-checkout.cjs";
|
|
8
8
|
import { Stripe } from "stripe";
|
|
9
|
-
export { type BeginCheckoutProperties, type CancellationDetails, type CheckoutSession, type CreateCheckoutSessionDTO, type LineItem, METADATA_KEYS, type PaymentStatus, type Price, type PriceId, type ProductPrice, type PurchaseProperties, type Stripe, StripeConfig, StripeEventHandler, type StripeMode, addPendingCheckout, cancellationDetailsSchema, createCheckoutSessionSchema, getBeginCheckoutProperties, getPendingCheckouts, getPurchaseProperties, mapCancellationDetails, mapCharge, mapCheckoutSession, mapInvoice, mapLineItem, mapPaymentIntent, mapPrice, mapSubscriptionStatus, mapTime, minorUnits, price, removePendingCheckout };
|
|
9
|
+
export { type AllowedOrigins, type BeginCheckoutProperties, type CancellationDetails, type CheckoutSession, type CreateCheckoutSessionDTO, type CreateCheckoutSessionSchemaOptions, type LineItem, METADATA_KEYS, type PaymentStatus, type Price, type PriceId, type ProductPrice, type PurchaseProperties, type Stripe, StripeConfig, StripeEventHandler, type StripeMode, addPendingCheckout, cancellationDetailsSchema, createCheckoutSessionSchema, getBeginCheckoutProperties, getPendingCheckouts, getPurchaseProperties, mapCancellationDetails, mapCharge, mapCheckoutSession, mapInvoice, mapLineItem, mapPaymentIntent, mapPrice, mapSubscriptionStatus, mapTime, minorUnits, price, removePendingCheckout, returnUrlSchema };
|
package/dist/stripe/index.d.mts
CHANGED
|
@@ -2,8 +2,8 @@ import { CancellationDetails } from "../subscription/index.mjs";
|
|
|
2
2
|
import { PriceId, StripeConfig, StripeMode } from "./config.mjs";
|
|
3
3
|
import { StripeEventHandler } from "./handler.mjs";
|
|
4
4
|
import { BeginCheckoutProperties, CheckoutSession, LineItem, PaymentStatus, Price, ProductPrice, PurchaseProperties, getBeginCheckoutProperties, getPurchaseProperties, mapCancellationDetails, mapCharge, mapCheckoutSession, mapInvoice, mapLineItem, mapPaymentIntent, mapPrice, mapSubscriptionStatus, mapTime, minorUnits, price } from "./mapper.mjs";
|
|
5
|
-
import { CreateCheckoutSessionDTO, cancellationDetailsSchema, createCheckoutSessionSchema } from "./schema.mjs";
|
|
5
|
+
import { AllowedOrigins, CreateCheckoutSessionDTO, CreateCheckoutSessionSchemaOptions, cancellationDetailsSchema, createCheckoutSessionSchema, returnUrlSchema } from "./schema.mjs";
|
|
6
6
|
import { METADATA_KEYS } from "./metadata.mjs";
|
|
7
7
|
import { addPendingCheckout, getPendingCheckouts, removePendingCheckout } from "./pending-checkout.mjs";
|
|
8
8
|
import { Stripe } from "stripe";
|
|
9
|
-
export { type BeginCheckoutProperties, type CancellationDetails, type CheckoutSession, type CreateCheckoutSessionDTO, type LineItem, METADATA_KEYS, type PaymentStatus, type Price, type PriceId, type ProductPrice, type PurchaseProperties, type Stripe, StripeConfig, StripeEventHandler, type StripeMode, addPendingCheckout, cancellationDetailsSchema, createCheckoutSessionSchema, getBeginCheckoutProperties, getPendingCheckouts, getPurchaseProperties, mapCancellationDetails, mapCharge, mapCheckoutSession, mapInvoice, mapLineItem, mapPaymentIntent, mapPrice, mapSubscriptionStatus, mapTime, minorUnits, price, removePendingCheckout };
|
|
9
|
+
export { type AllowedOrigins, type BeginCheckoutProperties, type CancellationDetails, type CheckoutSession, type CreateCheckoutSessionDTO, type CreateCheckoutSessionSchemaOptions, type LineItem, METADATA_KEYS, type PaymentStatus, type Price, type PriceId, type ProductPrice, type PurchaseProperties, type Stripe, StripeConfig, StripeEventHandler, type StripeMode, addPendingCheckout, cancellationDetailsSchema, createCheckoutSessionSchema, getBeginCheckoutProperties, getPendingCheckouts, getPurchaseProperties, mapCancellationDetails, mapCharge, mapCheckoutSession, mapInvoice, mapLineItem, mapPaymentIntent, mapPrice, mapSubscriptionStatus, mapTime, minorUnits, price, removePendingCheckout, returnUrlSchema };
|
package/dist/stripe/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { StripeConfig } from "./config.mjs";
|
|
2
2
|
import { StripeEventHandler } from "./handler.mjs";
|
|
3
3
|
import { getBeginCheckoutProperties, getPurchaseProperties, mapCancellationDetails, mapCharge, mapCheckoutSession, mapInvoice, mapLineItem, mapPaymentIntent, mapPrice, mapSubscriptionStatus, mapTime, minorUnits, price } from "./mapper.mjs";
|
|
4
|
-
import { cancellationDetailsSchema, createCheckoutSessionSchema } from "./schema.mjs";
|
|
4
|
+
import { cancellationDetailsSchema, createCheckoutSessionSchema, returnUrlSchema } from "./schema.mjs";
|
|
5
5
|
import { METADATA_KEYS } from "./metadata.mjs";
|
|
6
6
|
import { addPendingCheckout, getPendingCheckouts, removePendingCheckout } from "./pending-checkout.mjs";
|
|
7
|
-
export { METADATA_KEYS, StripeConfig, StripeEventHandler, addPendingCheckout, cancellationDetailsSchema, createCheckoutSessionSchema, getBeginCheckoutProperties, getPendingCheckouts, getPurchaseProperties, mapCancellationDetails, mapCharge, mapCheckoutSession, mapInvoice, mapLineItem, mapPaymentIntent, mapPrice, mapSubscriptionStatus, mapTime, minorUnits, price, removePendingCheckout };
|
|
7
|
+
export { METADATA_KEYS, StripeConfig, StripeEventHandler, addPendingCheckout, cancellationDetailsSchema, createCheckoutSessionSchema, getBeginCheckoutProperties, getPendingCheckouts, getPurchaseProperties, mapCancellationDetails, mapCharge, mapCheckoutSession, mapInvoice, mapLineItem, mapPaymentIntent, mapPrice, mapSubscriptionStatus, mapTime, minorUnits, price, removePendingCheckout, returnUrlSchema };
|
package/dist/stripe/schema.cjs
CHANGED
|
@@ -8,16 +8,56 @@ const cancellationDetailsSchema = (0, zod_mini.object)({
|
|
|
8
8
|
feedback: (0, zod_mini.optional)((0, zod_mini.nullable)((0, zod_mini.enum)(require_subscription_index.CANCELLATION_FEEDBACKS))),
|
|
9
9
|
resubscribeIntent: (0, zod_mini.optional)((0, zod_mini.nullable)((0, zod_mini.enum)(require_subscription_index.RESUBSCRIBE_INTENTS)))
|
|
10
10
|
});
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Normalizes each allowed origin with `new URL()`, so `https://example.com/` and
|
|
13
|
+
* `https://example.com` are the same entry. Throws on anything that is not an http(s) origin: a
|
|
14
|
+
* misconfigured list must fail at startup, not let every return URL through or refuse them all.
|
|
15
|
+
*/
|
|
16
|
+
function normalizeOrigins(origins) {
|
|
17
|
+
return origins.map((origin) => {
|
|
18
|
+
const parsed = new URL(origin);
|
|
19
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw new Error(`returnUrl: ${origin} is not an http(s) origin`);
|
|
20
|
+
return parsed.origin;
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Whether `value` may be handed to Stripe as a URL its hosted pages (Checkout, the billing portal)
|
|
25
|
+
* send the browser back to. Origins are compared as parsed by `new URL()`, never by string prefix,
|
|
26
|
+
* so a lookalike host (`https://example.com.evil.com`), userinfo (`https://example.com@evil.com`),
|
|
27
|
+
* protocol-relative (`//evil.com`) and `javascript:` values are all refused. The scheme is part of
|
|
28
|
+
* the origin, so an https-only list refuses `http:` too.
|
|
29
|
+
*/
|
|
30
|
+
function isAllowedReturnUrl(value, allowedOrigins) {
|
|
31
|
+
let parsed;
|
|
32
|
+
try {
|
|
33
|
+
parsed = new URL(value);
|
|
34
|
+
} catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
if (parsed.username !== "" || parsed.password !== "") return false;
|
|
38
|
+
return allowedOrigins.includes(parsed.origin);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A client-supplied return / success / cancel URL. It goes straight to Stripe, so without an
|
|
42
|
+
* origin check anyone could create a session with their own account and hand out a
|
|
43
|
+
* `checkout.stripe.com` / `billing.stripe.com` link that ends on a page of their choosing: an open
|
|
44
|
+
* redirect behind a trusted domain. `allowedOrigins` is usually just the web app's origin.
|
|
45
|
+
*/
|
|
46
|
+
function returnUrlSchema(allowedOrigins) {
|
|
47
|
+
const origins = normalizeOrigins(allowedOrigins);
|
|
48
|
+
return (0, zod_mini.url)().check((0, zod_mini.refine)((value) => isAllowedReturnUrl(value, origins), { message: `URL must be on ${origins.join(", ")}` }));
|
|
49
|
+
}
|
|
50
|
+
function createCheckoutSessionSchema(productIds, { allowedOrigins }) {
|
|
12
51
|
return (0, zod_mini.object)({
|
|
13
52
|
quantity: (0, zod_mini._default)((0, zod_mini.int)().check((0, zod_mini.gte)(1)), 1),
|
|
14
53
|
productId: (0, zod_mini.enum)(productIds),
|
|
15
|
-
cancelUrl: (0, zod_mini.optional)((
|
|
16
|
-
successUrl: (0, zod_mini.optional)((
|
|
54
|
+
cancelUrl: (0, zod_mini.optional)(returnUrlSchema(allowedOrigins)),
|
|
55
|
+
successUrl: (0, zod_mini.optional)(returnUrlSchema(allowedOrigins))
|
|
17
56
|
});
|
|
18
57
|
}
|
|
19
58
|
//#endregion
|
|
20
59
|
exports.cancellationDetailsSchema = cancellationDetailsSchema;
|
|
21
60
|
exports.createCheckoutSessionSchema = createCheckoutSessionSchema;
|
|
61
|
+
exports.returnUrlSchema = returnUrlSchema;
|
|
22
62
|
|
|
23
63
|
//# sourceMappingURL=schema.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.cjs","names":["object","optional","nullable","string","maxLength","_enum","CANCELLATION_FEEDBACKS","RESUBSCRIBE_INTENTS","_default","int","gte"
|
|
1
|
+
{"version":3,"file":"schema.cjs","names":["object","optional","nullable","string","maxLength","_enum","CANCELLATION_FEEDBACKS","RESUBSCRIBE_INTENTS","url","refine","_default","int","gte"],"sources":["../../src/stripe/schema.ts"],"sourcesContent":["import {\n _default,\n enum as _enum,\n gte,\n int,\n maxLength,\n nullable,\n object,\n optional,\n refine,\n string,\n url,\n} from 'zod/mini';\nimport { CANCELLATION_FEEDBACKS, RESUBSCRIBE_INTENTS } from '../subscription/index';\n\n/** What our own cancel dialog may send along with a cancellation (see `CancellationDetails`). */\nexport const cancellationDetailsSchema = object({\n comment: optional(nullable(string().check(maxLength(1024)))),\n feedback: optional(nullable(_enum(CANCELLATION_FEEDBACKS))),\n resubscribeIntent: optional(nullable(_enum(RESUBSCRIBE_INTENTS))),\n});\n\n/** At least one origin: an empty list would refuse every return URL. */\nexport type AllowedOrigins = readonly [string, ...string[]];\n\n/**\n * Normalizes each allowed origin with `new URL()`, so `https://example.com/` and\n * `https://example.com` are the same entry. Throws on anything that is not an http(s) origin: a\n * misconfigured list must fail at startup, not let every return URL through or refuse them all.\n */\nfunction normalizeOrigins(origins: AllowedOrigins): string[] {\n return origins.map((origin) => {\n const parsed = new URL(origin);\n if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {\n throw new Error(`returnUrl: ${origin} is not an http(s) origin`);\n }\n return parsed.origin;\n });\n}\n\n/**\n * Whether `value` may be handed to Stripe as a URL its hosted pages (Checkout, the billing portal)\n * send the browser back to. Origins are compared as parsed by `new URL()`, never by string prefix,\n * so a lookalike host (`https://example.com.evil.com`), userinfo (`https://example.com@evil.com`),\n * protocol-relative (`//evil.com`) and `javascript:` values are all refused. The scheme is part of\n * the origin, so an https-only list refuses `http:` too.\n */\nfunction isAllowedReturnUrl(value: string, allowedOrigins: readonly string[]): boolean {\n let parsed: URL;\n try {\n parsed = new URL(value);\n } catch {\n return false;\n }\n if (parsed.username !== '' || parsed.password !== '') return false;\n return allowedOrigins.includes(parsed.origin);\n}\n\n/**\n * A client-supplied return / success / cancel URL. It goes straight to Stripe, so without an\n * origin check anyone could create a session with their own account and hand out a\n * `checkout.stripe.com` / `billing.stripe.com` link that ends on a page of their choosing: an open\n * redirect behind a trusted domain. `allowedOrigins` is usually just the web app's origin.\n */\nexport function returnUrlSchema(allowedOrigins: AllowedOrigins) {\n const origins = normalizeOrigins(allowedOrigins);\n return url().check(\n refine((value) => isAllowedReturnUrl(value, origins), {\n message: `URL must be on ${origins.join(', ')}`,\n })\n );\n}\n\nexport interface CreateCheckoutSessionSchemaOptions {\n /** Origins (`https://example.com`) `cancelUrl` and `successUrl` may point at; see `returnUrlSchema`. */\n allowedOrigins: AllowedOrigins;\n}\n\nexport function createCheckoutSessionSchema(\n productIds: string[],\n { allowedOrigins }: CreateCheckoutSessionSchemaOptions\n) {\n return object({\n quantity: _default(int().check(gte(1)), 1),\n productId: _enum(productIds),\n cancelUrl: optional(returnUrlSchema(allowedOrigins)),\n successUrl: optional(returnUrlSchema(allowedOrigins)),\n });\n}\n\n// Kept under the stripe entry for callers that imported it from here; the type is platform\n// neutral and lives with the subscription statuses.\nexport type { CancellationDetails } from '../subscription/index';\n\nexport interface CreateCheckoutSessionDTO {\n productId: string;\n cancelUrl?: string;\n successUrl?: `${string}session_id={CHECKOUT_SESSION_ID}${string}`;\n}\n"],"mappings":";;;;;AAgBA,MAAa,6BAAA,GAA4BA,SAAAA,OAAAA,CAAO;CAC9C,UAAA,GAASC,SAAAA,SAAAA,EAAAA,GAASC,SAAAA,SAAAA,EAAAA,GAASC,SAAAA,OAAAA,CAAO,CAAC,CAAC,OAAA,GAAMC,SAAAA,UAAAA,CAAU,IAAI,CAAC,CAAC,CAAC;CAC3D,WAAA,GAAUH,SAAAA,SAAAA,EAAAA,GAASC,SAAAA,SAAAA,EAAAA,GAASG,SAAAA,KAAAA,CAAMC,2BAAAA,sBAAsB,CAAC,CAAC;CAC1D,oBAAA,GAAmBL,SAAAA,SAAAA,EAAAA,GAASC,SAAAA,SAAAA,EAAAA,GAASG,SAAAA,KAAAA,CAAME,2BAAAA,mBAAmB,CAAC,CAAC;AAClE,CAAC;;;;;;AAUD,SAAS,iBAAiB,SAAmC;CAC3D,OAAO,QAAQ,KAAK,WAAW;EAC7B,MAAM,SAAS,IAAI,IAAI,MAAM;EAC7B,IAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SACtD,MAAM,IAAI,MAAM,cAAc,OAAO,0BAA0B;EAEjE,OAAO,OAAO;CAChB,CAAC;AACH;;;;;;;;AASA,SAAS,mBAAmB,OAAe,gBAA4C;CACrF,IAAI;CACJ,IAAI;EACF,SAAS,IAAI,IAAI,KAAK;CACxB,QAAQ;EACN,OAAO;CACT;CACA,IAAI,OAAO,aAAa,MAAM,OAAO,aAAa,IAAI,OAAO;CAC7D,OAAO,eAAe,SAAS,OAAO,MAAM;AAC9C;;;;;;;AAQA,SAAgB,gBAAgB,gBAAgC;CAC9D,MAAM,UAAU,iBAAiB,cAAc;CAC/C,QAAA,GAAOC,SAAAA,IAAAA,CAAI,CAAC,CAAC,OAAA,GACXC,SAAAA,OAAAA,EAAQ,UAAU,mBAAmB,OAAO,OAAO,GAAG,EACpD,SAAS,kBAAkB,QAAQ,KAAK,IAAI,IAC9C,CAAC,CACH;AACF;AAOA,SAAgB,4BACd,YACA,EAAE,kBACF;CACA,QAAA,GAAOT,SAAAA,OAAAA,CAAO;EACZ,WAAA,GAAUU,SAAAA,SAAAA,EAAAA,GAASC,SAAAA,IAAAA,CAAI,CAAC,CAAC,OAAA,GAAMC,SAAAA,IAAAA,CAAI,CAAC,CAAC,GAAG,CAAC;EACzC,YAAA,GAAWP,SAAAA,KAAAA,CAAM,UAAU;EAC3B,YAAA,GAAWJ,SAAAA,SAAAA,CAAS,gBAAgB,cAAc,CAAC;EACnD,aAAA,GAAYA,SAAAA,SAAAA,CAAS,gBAAgB,cAAc,CAAC;CACtD,CAAC;AACH"}
|
package/dist/stripe/schema.d.cts
CHANGED
|
@@ -19,7 +19,20 @@ export declare const cancellationDetailsSchema: import("zod/mini").ZodMiniObject
|
|
|
19
19
|
yes: "yes";
|
|
20
20
|
}>>>;
|
|
21
21
|
}, import("zod/v4/core").$strip>;
|
|
22
|
-
|
|
22
|
+
/** At least one origin: an empty list would refuse every return URL. */
|
|
23
|
+
export type AllowedOrigins = readonly [string, ...string[]];
|
|
24
|
+
/**
|
|
25
|
+
* A client-supplied return / success / cancel URL. It goes straight to Stripe, so without an
|
|
26
|
+
* origin check anyone could create a session with their own account and hand out a
|
|
27
|
+
* `checkout.stripe.com` / `billing.stripe.com` link that ends on a page of their choosing: an open
|
|
28
|
+
* redirect behind a trusted domain. `allowedOrigins` is usually just the web app's origin.
|
|
29
|
+
*/
|
|
30
|
+
export declare function returnUrlSchema(allowedOrigins: AllowedOrigins): import("zod/mini").ZodMiniURL;
|
|
31
|
+
export interface CreateCheckoutSessionSchemaOptions {
|
|
32
|
+
/** Origins (`https://example.com`) `cancelUrl` and `successUrl` may point at; see `returnUrlSchema`. */
|
|
33
|
+
allowedOrigins: AllowedOrigins;
|
|
34
|
+
}
|
|
35
|
+
export declare function createCheckoutSessionSchema(productIds: string[], { allowedOrigins }: CreateCheckoutSessionSchemaOptions): import("zod/mini").ZodMiniObject<{
|
|
23
36
|
quantity: import("zod/mini").ZodMiniDefault<import("zod/mini").ZodMiniInt>;
|
|
24
37
|
productId: import("zod/mini").ZodMiniEnum<{
|
|
25
38
|
[x: string]: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.cts","names":[],"sources":["../../src/stripe/schema.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"schema.d.cts","names":[],"sources":["../../src/stripe/schema.ts"],"mappings":";;;qBAgBa,8CAAyB;;;;;;;;;;;;;;;;;yBAIpC;;YAGU;;;;;;;wBAyCI,gBAAgB,gBAAgB,oCAAc;iBAS7C;;EAEf,gBAAgB;;wBAGF,4BACd,wBACE,kBAAkB,wDAAkC;;;;;;;yBAQvD;iBAMgB;EACf;EACA;EACA"}
|
package/dist/stripe/schema.d.mts
CHANGED
|
@@ -19,7 +19,20 @@ export declare const cancellationDetailsSchema: import("zod/mini").ZodMiniObject
|
|
|
19
19
|
yes: "yes";
|
|
20
20
|
}>>>;
|
|
21
21
|
}, import("zod/v4/core").$strip>;
|
|
22
|
-
|
|
22
|
+
/** At least one origin: an empty list would refuse every return URL. */
|
|
23
|
+
export type AllowedOrigins = readonly [string, ...string[]];
|
|
24
|
+
/**
|
|
25
|
+
* A client-supplied return / success / cancel URL. It goes straight to Stripe, so without an
|
|
26
|
+
* origin check anyone could create a session with their own account and hand out a
|
|
27
|
+
* `checkout.stripe.com` / `billing.stripe.com` link that ends on a page of their choosing: an open
|
|
28
|
+
* redirect behind a trusted domain. `allowedOrigins` is usually just the web app's origin.
|
|
29
|
+
*/
|
|
30
|
+
export declare function returnUrlSchema(allowedOrigins: AllowedOrigins): import("zod/mini").ZodMiniURL;
|
|
31
|
+
export interface CreateCheckoutSessionSchemaOptions {
|
|
32
|
+
/** Origins (`https://example.com`) `cancelUrl` and `successUrl` may point at; see `returnUrlSchema`. */
|
|
33
|
+
allowedOrigins: AllowedOrigins;
|
|
34
|
+
}
|
|
35
|
+
export declare function createCheckoutSessionSchema(productIds: string[], { allowedOrigins }: CreateCheckoutSessionSchemaOptions): import("zod/mini").ZodMiniObject<{
|
|
23
36
|
quantity: import("zod/mini").ZodMiniDefault<import("zod/mini").ZodMiniInt>;
|
|
24
37
|
productId: import("zod/mini").ZodMiniEnum<{
|
|
25
38
|
[x: string]: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.mts","names":[],"sources":["../../src/stripe/schema.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"schema.d.mts","names":[],"sources":["../../src/stripe/schema.ts"],"mappings":";;;qBAgBa,8CAAyB;;;;;;;;;;;;;;;;;yBAIpC;;YAGU;;;;;;;wBAyCI,gBAAgB,gBAAgB,oCAAc;iBAS7C;;EAEf,gBAAgB;;wBAGF,4BACd,wBACE,kBAAkB,wDAAkC;;;;;;;yBAQvD;iBAMgB;EACf;EACA;EACA"}
|
package/dist/stripe/schema.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CANCELLATION_FEEDBACKS, RESUBSCRIBE_INTENTS } from "../subscription/index.mjs";
|
|
2
|
-
import { _default, enum as enum$1, gte, int, maxLength, nullable, object, optional, string, url } from "zod/mini";
|
|
2
|
+
import { _default, enum as enum$1, gte, int, maxLength, nullable, object, optional, refine, string, url } from "zod/mini";
|
|
3
3
|
//#region src/stripe/schema.ts
|
|
4
4
|
/** What our own cancel dialog may send along with a cancellation (see `CancellationDetails`). */
|
|
5
5
|
const cancellationDetailsSchema = object({
|
|
@@ -7,15 +7,54 @@ const cancellationDetailsSchema = object({
|
|
|
7
7
|
feedback: optional(nullable(enum$1(CANCELLATION_FEEDBACKS))),
|
|
8
8
|
resubscribeIntent: optional(nullable(enum$1(RESUBSCRIBE_INTENTS)))
|
|
9
9
|
});
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Normalizes each allowed origin with `new URL()`, so `https://example.com/` and
|
|
12
|
+
* `https://example.com` are the same entry. Throws on anything that is not an http(s) origin: a
|
|
13
|
+
* misconfigured list must fail at startup, not let every return URL through or refuse them all.
|
|
14
|
+
*/
|
|
15
|
+
function normalizeOrigins(origins) {
|
|
16
|
+
return origins.map((origin) => {
|
|
17
|
+
const parsed = new URL(origin);
|
|
18
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw new Error(`returnUrl: ${origin} is not an http(s) origin`);
|
|
19
|
+
return parsed.origin;
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Whether `value` may be handed to Stripe as a URL its hosted pages (Checkout, the billing portal)
|
|
24
|
+
* send the browser back to. Origins are compared as parsed by `new URL()`, never by string prefix,
|
|
25
|
+
* so a lookalike host (`https://example.com.evil.com`), userinfo (`https://example.com@evil.com`),
|
|
26
|
+
* protocol-relative (`//evil.com`) and `javascript:` values are all refused. The scheme is part of
|
|
27
|
+
* the origin, so an https-only list refuses `http:` too.
|
|
28
|
+
*/
|
|
29
|
+
function isAllowedReturnUrl(value, allowedOrigins) {
|
|
30
|
+
let parsed;
|
|
31
|
+
try {
|
|
32
|
+
parsed = new URL(value);
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
if (parsed.username !== "" || parsed.password !== "") return false;
|
|
37
|
+
return allowedOrigins.includes(parsed.origin);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A client-supplied return / success / cancel URL. It goes straight to Stripe, so without an
|
|
41
|
+
* origin check anyone could create a session with their own account and hand out a
|
|
42
|
+
* `checkout.stripe.com` / `billing.stripe.com` link that ends on a page of their choosing: an open
|
|
43
|
+
* redirect behind a trusted domain. `allowedOrigins` is usually just the web app's origin.
|
|
44
|
+
*/
|
|
45
|
+
function returnUrlSchema(allowedOrigins) {
|
|
46
|
+
const origins = normalizeOrigins(allowedOrigins);
|
|
47
|
+
return url().check(refine((value) => isAllowedReturnUrl(value, origins), { message: `URL must be on ${origins.join(", ")}` }));
|
|
48
|
+
}
|
|
49
|
+
function createCheckoutSessionSchema(productIds, { allowedOrigins }) {
|
|
11
50
|
return object({
|
|
12
51
|
quantity: _default(int().check(gte(1)), 1),
|
|
13
52
|
productId: enum$1(productIds),
|
|
14
|
-
cancelUrl: optional(
|
|
15
|
-
successUrl: optional(
|
|
53
|
+
cancelUrl: optional(returnUrlSchema(allowedOrigins)),
|
|
54
|
+
successUrl: optional(returnUrlSchema(allowedOrigins))
|
|
16
55
|
});
|
|
17
56
|
}
|
|
18
57
|
//#endregion
|
|
19
|
-
export { cancellationDetailsSchema, createCheckoutSessionSchema };
|
|
58
|
+
export { cancellationDetailsSchema, createCheckoutSessionSchema, returnUrlSchema };
|
|
20
59
|
|
|
21
60
|
//# sourceMappingURL=schema.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.mjs","names":["_enum"],"sources":["../../src/stripe/schema.ts"],"sourcesContent":["import {\n _default,\n enum as _enum,\n gte,\n int,\n maxLength,\n nullable,\n object,\n optional,\n string,\n url,\n} from 'zod/mini';\nimport { CANCELLATION_FEEDBACKS, RESUBSCRIBE_INTENTS } from '../subscription/index';\n\n/** What our own cancel dialog may send along with a cancellation (see `CancellationDetails`). */\nexport const cancellationDetailsSchema = object({\n comment: optional(nullable(string().check(maxLength(1024)))),\n feedback: optional(nullable(_enum(CANCELLATION_FEEDBACKS))),\n resubscribeIntent: optional(nullable(_enum(RESUBSCRIBE_INTENTS))),\n});\n\nexport function createCheckoutSessionSchema(productIds: string[]) {\n return object({\n quantity: _default(int().check(gte(1)), 1),\n productId: _enum(productIds),\n cancelUrl: optional(
|
|
1
|
+
{"version":3,"file":"schema.mjs","names":["_enum"],"sources":["../../src/stripe/schema.ts"],"sourcesContent":["import {\n _default,\n enum as _enum,\n gte,\n int,\n maxLength,\n nullable,\n object,\n optional,\n refine,\n string,\n url,\n} from 'zod/mini';\nimport { CANCELLATION_FEEDBACKS, RESUBSCRIBE_INTENTS } from '../subscription/index';\n\n/** What our own cancel dialog may send along with a cancellation (see `CancellationDetails`). */\nexport const cancellationDetailsSchema = object({\n comment: optional(nullable(string().check(maxLength(1024)))),\n feedback: optional(nullable(_enum(CANCELLATION_FEEDBACKS))),\n resubscribeIntent: optional(nullable(_enum(RESUBSCRIBE_INTENTS))),\n});\n\n/** At least one origin: an empty list would refuse every return URL. */\nexport type AllowedOrigins = readonly [string, ...string[]];\n\n/**\n * Normalizes each allowed origin with `new URL()`, so `https://example.com/` and\n * `https://example.com` are the same entry. Throws on anything that is not an http(s) origin: a\n * misconfigured list must fail at startup, not let every return URL through or refuse them all.\n */\nfunction normalizeOrigins(origins: AllowedOrigins): string[] {\n return origins.map((origin) => {\n const parsed = new URL(origin);\n if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {\n throw new Error(`returnUrl: ${origin} is not an http(s) origin`);\n }\n return parsed.origin;\n });\n}\n\n/**\n * Whether `value` may be handed to Stripe as a URL its hosted pages (Checkout, the billing portal)\n * send the browser back to. Origins are compared as parsed by `new URL()`, never by string prefix,\n * so a lookalike host (`https://example.com.evil.com`), userinfo (`https://example.com@evil.com`),\n * protocol-relative (`//evil.com`) and `javascript:` values are all refused. The scheme is part of\n * the origin, so an https-only list refuses `http:` too.\n */\nfunction isAllowedReturnUrl(value: string, allowedOrigins: readonly string[]): boolean {\n let parsed: URL;\n try {\n parsed = new URL(value);\n } catch {\n return false;\n }\n if (parsed.username !== '' || parsed.password !== '') return false;\n return allowedOrigins.includes(parsed.origin);\n}\n\n/**\n * A client-supplied return / success / cancel URL. It goes straight to Stripe, so without an\n * origin check anyone could create a session with their own account and hand out a\n * `checkout.stripe.com` / `billing.stripe.com` link that ends on a page of their choosing: an open\n * redirect behind a trusted domain. `allowedOrigins` is usually just the web app's origin.\n */\nexport function returnUrlSchema(allowedOrigins: AllowedOrigins) {\n const origins = normalizeOrigins(allowedOrigins);\n return url().check(\n refine((value) => isAllowedReturnUrl(value, origins), {\n message: `URL must be on ${origins.join(', ')}`,\n })\n );\n}\n\nexport interface CreateCheckoutSessionSchemaOptions {\n /** Origins (`https://example.com`) `cancelUrl` and `successUrl` may point at; see `returnUrlSchema`. */\n allowedOrigins: AllowedOrigins;\n}\n\nexport function createCheckoutSessionSchema(\n productIds: string[],\n { allowedOrigins }: CreateCheckoutSessionSchemaOptions\n) {\n return object({\n quantity: _default(int().check(gte(1)), 1),\n productId: _enum(productIds),\n cancelUrl: optional(returnUrlSchema(allowedOrigins)),\n successUrl: optional(returnUrlSchema(allowedOrigins)),\n });\n}\n\n// Kept under the stripe entry for callers that imported it from here; the type is platform\n// neutral and lives with the subscription statuses.\nexport type { CancellationDetails } from '../subscription/index';\n\nexport interface CreateCheckoutSessionDTO {\n productId: string;\n cancelUrl?: string;\n successUrl?: `${string}session_id={CHECKOUT_SESSION_ID}${string}`;\n}\n"],"mappings":";;;;AAgBA,MAAa,4BAA4B,OAAO;CAC9C,SAAS,SAAS,SAAS,OAAO,CAAC,CAAC,MAAM,UAAU,IAAI,CAAC,CAAC,CAAC;CAC3D,UAAU,SAAS,SAASA,OAAM,sBAAsB,CAAC,CAAC;CAC1D,mBAAmB,SAAS,SAASA,OAAM,mBAAmB,CAAC,CAAC;AAClE,CAAC;;;;;;AAUD,SAAS,iBAAiB,SAAmC;CAC3D,OAAO,QAAQ,KAAK,WAAW;EAC7B,MAAM,SAAS,IAAI,IAAI,MAAM;EAC7B,IAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SACtD,MAAM,IAAI,MAAM,cAAc,OAAO,0BAA0B;EAEjE,OAAO,OAAO;CAChB,CAAC;AACH;;;;;;;;AASA,SAAS,mBAAmB,OAAe,gBAA4C;CACrF,IAAI;CACJ,IAAI;EACF,SAAS,IAAI,IAAI,KAAK;CACxB,QAAQ;EACN,OAAO;CACT;CACA,IAAI,OAAO,aAAa,MAAM,OAAO,aAAa,IAAI,OAAO;CAC7D,OAAO,eAAe,SAAS,OAAO,MAAM;AAC9C;;;;;;;AAQA,SAAgB,gBAAgB,gBAAgC;CAC9D,MAAM,UAAU,iBAAiB,cAAc;CAC/C,OAAO,IAAI,CAAC,CAAC,MACX,QAAQ,UAAU,mBAAmB,OAAO,OAAO,GAAG,EACpD,SAAS,kBAAkB,QAAQ,KAAK,IAAI,IAC9C,CAAC,CACH;AACF;AAOA,SAAgB,4BACd,YACA,EAAE,kBACF;CACA,OAAO,OAAO;EACZ,UAAU,SAAS,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC;EACzC,WAAWA,OAAM,UAAU;EAC3B,WAAW,SAAS,gBAAgB,cAAc,CAAC;EACnD,YAAY,SAAS,gBAAgB,cAAc,CAAC;CACtD,CAAC;AACH"}
|