From e1cbc99991749fad1bd6d28947e98ebaa632e65f Mon Sep 17 00:00:00 2001 From: Ben Stokes Date: Tue, 14 Jul 2026 14:33:19 -0700 Subject: [PATCH] feat: payment link definitions --- .../src/lib/CheckoutSessionSchema.ts | 26 +- .../src/lib/PaymentLinkSchema.ts | 359 ++++++++++++++++++ libs/shared-schemas/src/lib/index.ts | 1 + libs/shared-types/src/lib/CheckoutSession.ts | 56 ++- libs/shared-types/src/lib/PaymentLink.ts | 304 +++++++++++++++ libs/shared-types/src/lib/index.ts | 1 + 6 files changed, 726 insertions(+), 21 deletions(-) create mode 100644 libs/shared-schemas/src/lib/PaymentLinkSchema.ts create mode 100644 libs/shared-types/src/lib/PaymentLink.ts diff --git a/libs/shared-schemas/src/lib/CheckoutSessionSchema.ts b/libs/shared-schemas/src/lib/CheckoutSessionSchema.ts index 0938953..43e18c0 100644 --- a/libs/shared-schemas/src/lib/CheckoutSessionSchema.ts +++ b/libs/shared-schemas/src/lib/CheckoutSessionSchema.ts @@ -16,7 +16,7 @@ const CheckoutSessionAfterExpirationSchema = z.object({ }), }); -const CheckoutSessionAutomaticTaxSchema = z.object({ +export const CheckoutSessionAutomaticTaxSchema = z.object({ enabled: z.boolean(), liability: z .object({ @@ -42,7 +42,7 @@ const CheckoutSessionBrandingSettingsSchema = z.object({ logo: CheckoutSessionBrandingImageSchema.optional(), }); -const CheckoutSessionConsentCollectionSchema = z.object({ +export const CheckoutSessionConsentCollectionSchema = z.object({ payment_method_reuse_agreement: z .object({ position: z.enum(['auto', 'hidden']), @@ -52,7 +52,7 @@ const CheckoutSessionConsentCollectionSchema = z.object({ terms_of_service: z.enum(['none', 'required']).optional(), }); -const CheckoutSessionCustomFieldSchema = z +export const CheckoutSessionCustomFieldSchema = z .object({ key: z .string() @@ -101,7 +101,7 @@ const CheckoutSessionCustomTextEntrySchema = z.object({ message: z.string().min(1).max(1200), }); -const CheckoutSessionCustomTextSchema = z.object({ +export const CheckoutSessionCustomTextSchema = z.object({ after_submit: CheckoutSessionCustomTextEntrySchema.optional(), shipping_address: CheckoutSessionCustomTextEntrySchema.optional(), submit: CheckoutSessionCustomTextEntrySchema.optional(), @@ -123,7 +123,7 @@ const CheckoutSessionDiscountSchema = z message: 'Only one of `coupon` or `promotion_code` may be specified', }); -const CheckoutSessionInvoiceCreationSchema = z.object({ +export const CheckoutSessionInvoiceCreationSchema = z.object({ enabled: z.boolean(), invoice_data: z .object({ @@ -172,7 +172,7 @@ const CheckoutSessionProductDataSchema = z.object({ unit_label: z.string().max(12).optional(), }); -const CheckoutSessionPriceDataSchema = z +export const CheckoutSessionPriceDataSchema = z .object({ currency: z.string().min(1).max(4), product: z.string().optional(), @@ -197,7 +197,7 @@ const CheckoutSessionPriceDataSchema = z { message: 'Either `unit_amount` or `unit_amount_decimal` is required' } ); -const CheckoutSessionAdjustableQuantitySchema = z.object({ +export const CheckoutSessionAdjustableQuantitySchema = z.object({ enabled: z.boolean(), maximum: z.number().int().min(1).max(999999).optional(), minimum: z.number().int().min(0).optional(), @@ -217,7 +217,7 @@ const CheckoutSessionLineItemSchema = z message: 'Either `price` or `price_data` is required for each line item', }); -const CheckoutSessionOptionalItemSchema = z.object({ +export const CheckoutSessionOptionalItemSchema = z.object({ price: z.string().min(1), quantity: z.number().int().nonnegative(), adjustable_quantity: CheckoutSessionAdjustableQuantitySchema.optional(), @@ -228,7 +228,7 @@ const CheckoutSessionNameCollectionEntrySchema = z.object({ optional: z.boolean().optional(), }); -const CheckoutSessionNameCollectionSchema = z.object({ +export const CheckoutSessionNameCollectionSchema = z.object({ business: CheckoutSessionNameCollectionEntrySchema.optional(), individual: CheckoutSessionNameCollectionEntrySchema.optional(), }); @@ -248,7 +248,7 @@ const CheckoutSessionShippingSchema = z.object({ tracking_number: z.string().optional(), }); -const CheckoutSessionTransferDataSchema = z.object({ +export const CheckoutSessionTransferDataSchema = z.object({ destination: z.string().min(1), amount: z.number().int().nonnegative().optional(), }); @@ -277,7 +277,7 @@ const CheckoutSessionPermissionsSchema = z.object({ update_shipping_details: z.enum(['client_only', 'server_only']).optional(), }); -const CheckoutSessionPhoneNumberCollectionSchema = z.object({ +export const CheckoutSessionPhoneNumberCollectionSchema = z.object({ enabled: z.boolean(), }); @@ -295,7 +295,7 @@ const CheckoutSessionSetupIntentDataSchema = z.object({ on_behalf_of: z.string().optional(), }); -const CheckoutSessionShippingAddressCollectionSchema = z.object({ +export const CheckoutSessionShippingAddressCollectionSchema = z.object({ /** * @remarks Stripe defines ~240 supported country codes here; kept as a plain string array * for maintainability, matching the simplification already used for CheckoutSession's @@ -349,7 +349,7 @@ const CheckoutSessionShippingOptionSchema = z message: 'Either `shipping_rate` or `shipping_rate_data` is required', }); -const CheckoutSessionTaxIdCollectionSchema = z.object({ +export const CheckoutSessionTaxIdCollectionSchema = z.object({ enabled: z.boolean(), required: z.enum(['if_supported', 'never']).optional(), }); diff --git a/libs/shared-schemas/src/lib/PaymentLinkSchema.ts b/libs/shared-schemas/src/lib/PaymentLinkSchema.ts new file mode 100644 index 0000000..bdba9a7 --- /dev/null +++ b/libs/shared-schemas/src/lib/PaymentLinkSchema.ts @@ -0,0 +1,359 @@ +import { z } from 'zod'; +import { ExpandableSchema } from './ExpandableSchema'; +import { + CheckoutSessionAdjustableQuantitySchema, + CheckoutSessionAutomaticTaxSchema, + CheckoutSessionConsentCollectionSchema, + CheckoutSessionCustomFieldSchema, + CheckoutSessionCustomTextSchema, + CheckoutSessionInvoiceCreationSchema, + CheckoutSessionNameCollectionSchema, + CheckoutSessionOptionalItemSchema, + CheckoutSessionPhoneNumberCollectionSchema, + CheckoutSessionPriceDataSchema, + CheckoutSessionShippingAddressCollectionSchema, + CheckoutSessionTaxIdCollectionSchema, + CheckoutSessionTransferDataSchema, +} from './CheckoutSessionSchema'; + +// ───────────────────────────────────────────────────────────────────────────── +// Reusable nested object schemas +// ───────────────────────────────────────────────────────────────────────────── + +const PaymentLinkAfterCompletionSchema = z + .object({ + type: z.enum(['hosted_confirmation', 'redirect']), + hosted_confirmation: z + .object({ + custom_message: z.string().max(500).optional(), + }) + .optional(), + redirect: z + .object({ + url: z.string().url(), + }) + .optional(), + }) + .refine( + (afterCompletion) => + afterCompletion.type !== 'redirect' || !!afterCompletion.redirect, + { + message: '`redirect` is required when `type` is `redirect`', + path: ['redirect'], + } + ); + +const PaymentLinkLineItemSchema = z + .object({ + quantity: z.number().int().nonnegative(), + adjustable_quantity: CheckoutSessionAdjustableQuantitySchema.optional(), + price: z.string().optional(), + price_data: CheckoutSessionPriceDataSchema.optional(), + }) + .refine((lineItem) => !!lineItem.price || !!lineItem.price_data, { + message: 'Either `price` or `price_data` is required for each line item', + }); + +/** + * A subset of parameters passed to PaymentIntent creation for Checkout Sessions in + * `payment` mode created by this payment link. + */ +const PaymentLinkPaymentIntentDataSchema = z.object({ + capture_method: z.enum(['automatic', 'automatic_async', 'manual']).optional(), + description: z.string().optional(), + metadata: z.record(z.string(), z.string()).optional(), + setup_future_usage: z.enum(['off_session', 'on_session']).optional(), + statement_descriptor: z.string().max(22).optional(), + statement_descriptor_suffix: z.string().optional(), + transfer_group: z.string().optional(), +}); + +/** + * Payment-method-specific configuration. + * @remarks Stripe's documented Payment Link options here are card brand restrictions. + * Zoneless only accepts USDC wallet payments; the `card` bag is retained for API parity, + * and `crypto` is exposed to match Checkout Session payment method options. + */ +const PaymentLinkPaymentMethodOptionsSchema = z.object({ + card: z + .object({ + restrictions: z + .object({ + brands_blocked: z + .array( + z.enum([ + 'american_express', + 'discover_global_network', + 'mastercard', + 'visa', + ]) + ) + .optional(), + }) + .optional(), + }) + .optional(), + crypto: z + .object({ + setup_future_usage: z.enum(['none']).optional(), + }) + .optional(), +}); + +const PaymentLinkRestrictionsSchema = z.object({ + completed_sessions: z.object({ + limit: z.number().int().positive(), + }), +}); + +const PaymentLinkShippingOptionSchema = z.object({ + shipping_rate: z.string().optional(), +}); + +/** + * Configuration data used when creating a subscription from this payment link. + * There must be at least one line item with a recurring price to use subscription_data. + */ +const PaymentLinkSubscriptionDataSchema = z.object({ + description: z.string().max(500).optional(), + invoice_settings: z + .object({ + issuer: z + .object({ + type: z.enum(['account', 'self']), + account: z.string().optional(), + }) + .optional(), + }) + .optional(), + metadata: z.record(z.string(), z.string()).optional(), + trial_period_days: z.number().int().positive().optional(), + trial_settings: z + .object({ + end_behavior: z.object({ + missing_payment_method: z.enum(['cancel', 'create_invoice', 'pause']), + }), + }) + .optional(), +}); + +// ───────────────────────────────────────────────────────────────────────────── +// Retrieve Payment Link Schema +// ───────────────────────────────────────────────────────────────────────────── + +export const RetrievePaymentLinkSchema = ExpandableSchema; +export type RetrievePaymentLinkInput = z.infer< + typeof RetrievePaymentLinkSchema +>; + +// ───────────────────────────────────────────────────────────────────────────── +// Create Payment Link Schema +// ───────────────────────────────────────────────────────────────────────────── + +/** + * Schema for creating a payment link. + * @see https://docs.stripe.com/api/payment_links/payment_links/create + */ +export const CreatePaymentLinkSchema = z + .object({ + line_items: z.array(PaymentLinkLineItemSchema).min(1).max(20), + + after_completion: PaymentLinkAfterCompletionSchema.optional(), + allow_promotion_codes: z.boolean().optional(), + application_fee_amount: z.number().int().nonnegative().optional(), + application_fee_percent: z.number().min(0).max(100).optional(), + automatic_tax: CheckoutSessionAutomaticTaxSchema.optional(), + billing_address_collection: z.enum(['auto', 'required']).optional(), + consent_collection: CheckoutSessionConsentCollectionSchema.optional(), + currency: z.string().min(1).max(4).optional(), + custom_fields: z.array(CheckoutSessionCustomFieldSchema).max(3).optional(), + custom_text: CheckoutSessionCustomTextSchema.optional(), + customer_creation: z.enum(['always', 'if_required']).optional(), + inactive_message: z.string().max(500).optional(), + invoice_creation: CheckoutSessionInvoiceCreationSchema.optional(), + managed_payments: z + .object({ + enabled: z.boolean().optional(), + }) + .optional(), + metadata: z.record(z.string(), z.string()).optional(), + name_collection: CheckoutSessionNameCollectionSchema.optional(), + on_behalf_of: z.string().optional(), + optional_items: z + .array(CheckoutSessionOptionalItemSchema) + .max(10) + .optional(), + payment_intent_data: PaymentLinkPaymentIntentDataSchema.optional(), + payment_method_collection: z.enum(['always', 'if_required']).optional(), + payment_method_options: PaymentLinkPaymentMethodOptionsSchema.optional(), + payment_method_types: z.array(z.enum(['crypto'])).optional(), + phone_number_collection: + CheckoutSessionPhoneNumberCollectionSchema.optional(), + restrictions: PaymentLinkRestrictionsSchema.optional(), + shipping_address_collection: + CheckoutSessionShippingAddressCollectionSchema.optional(), + shipping_options: z.array(PaymentLinkShippingOptionSchema).optional(), + submit_type: z + .enum(['auto', 'book', 'donate', 'pay', 'subscribe']) + .optional(), + subscription_data: PaymentLinkSubscriptionDataSchema.optional(), + tax_id_collection: CheckoutSessionTaxIdCollectionSchema.optional(), + transfer_data: CheckoutSessionTransferDataSchema.optional(), + }) + .merge(ExpandableSchema) + .refine( + (paymentLink) => + (paymentLink.line_items.length ?? 0) + + (paymentLink.optional_items?.length ?? 0) <= + 20, + { + message: + 'Combined `line_items` and `optional_items` must not exceed 20 items', + path: ['optional_items'], + } + ); + +export type CreatePaymentLinkInput = z.infer; + +// ───────────────────────────────────────────────────────────────────────────── +// Update Payment Link Schema +// ───────────────────────────────────────────────────────────────────────────── + +/** + * To update an existing line item, specify its `id` along with the fields to change. + * New line items cannot be added via update — use create with `price` / `price_data`. + */ +const UpdatePaymentLinkLineItemSchema = z.object({ + id: z.string().min(1), + adjustable_quantity: CheckoutSessionAdjustableQuantitySchema.optional(), + quantity: z.number().int().nonnegative().optional(), +}); + +/** + * Updatable PaymentIntent parameters for Checkout Sessions created by this payment link. + * Narrower than create — capture_method and setup_future_usage are not updatable. + */ +const UpdatePaymentLinkPaymentIntentDataSchema = z.object({ + description: z.string().optional(), + metadata: z.record(z.string(), z.string()).optional(), + statement_descriptor: z.string().max(22).optional(), + statement_descriptor_suffix: z.string().optional(), + transfer_group: z.string().optional(), +}); + +/** + * Updatable subscription configuration for this payment link. + * Narrower than create — description is not updatable. + */ +const UpdatePaymentLinkSubscriptionDataSchema = z.object({ + invoice_settings: z + .object({ + issuer: z + .object({ + type: z.enum(['account', 'self']), + account: z.string().optional(), + }) + .optional(), + }) + .optional(), + metadata: z.record(z.string(), z.string()).optional(), + trial_period_days: z.number().int().positive().optional(), + trial_settings: z + .object({ + end_behavior: z.object({ + missing_payment_method: z.enum(['cancel', 'create_invoice', 'pause']), + }), + }) + .optional(), +}); + +/** + * Schema for updating a payment link. + * @see https://docs.stripe.com/api/payment_links/payment_links/update + */ +export const UpdatePaymentLinkSchema = z + .object({ + active: z.boolean().optional(), + after_completion: PaymentLinkAfterCompletionSchema.optional(), + allow_promotion_codes: z.boolean().optional(), + automatic_tax: CheckoutSessionAutomaticTaxSchema.optional(), + billing_address_collection: z.enum(['auto', 'required']).optional(), + custom_fields: z.array(CheckoutSessionCustomFieldSchema).max(3).optional(), + custom_text: CheckoutSessionCustomTextSchema.optional(), + customer_creation: z.enum(['always', 'if_required']).optional(), + inactive_message: z.string().max(500).optional(), + invoice_creation: CheckoutSessionInvoiceCreationSchema.optional(), + line_items: z.array(UpdatePaymentLinkLineItemSchema).max(20).optional(), + metadata: z.record(z.string(), z.string()).optional(), + name_collection: CheckoutSessionNameCollectionSchema.optional(), + optional_items: z + .array(CheckoutSessionOptionalItemSchema) + .max(10) + .optional(), + payment_intent_data: UpdatePaymentLinkPaymentIntentDataSchema.optional(), + payment_method_collection: z.enum(['always', 'if_required']).optional(), + payment_method_options: PaymentLinkPaymentMethodOptionsSchema.optional(), + /** + * Pass an empty array to enable dynamic payment methods from payment method settings. + */ + payment_method_types: z.array(z.enum(['crypto'])).optional(), + phone_number_collection: + CheckoutSessionPhoneNumberCollectionSchema.optional(), + restrictions: PaymentLinkRestrictionsSchema.optional(), + shipping_address_collection: + CheckoutSessionShippingAddressCollectionSchema.optional(), + submit_type: z + .enum(['auto', 'book', 'donate', 'pay', 'subscribe']) + .optional(), + subscription_data: UpdatePaymentLinkSubscriptionDataSchema.optional(), + tax_id_collection: CheckoutSessionTaxIdCollectionSchema.optional(), + }) + .merge(ExpandableSchema); + +export type UpdatePaymentLinkInput = z.infer; + +// ───────────────────────────────────────────────────────────────────────────── +// List Payment Links Schema +// ───────────────────────────────────────────────────────────────────────────── + +/** + * Schema for listing payment links. + * @see https://docs.stripe.com/api/payment_links/payment_links/list + */ +export const ListPaymentLinksSchema = z + .object({ + active: z.boolean().optional(), + ending_before: z.string().optional(), + limit: z.number().int().min(1).max(100).optional(), + starting_after: z.string().optional(), + }) + .merge(ExpandableSchema); + +export type ListPaymentLinksInput = z.infer; + +export const ListPaymentLinksFiltersSchema = z.object({ + active: z.boolean().optional(), +}); +export type ListPaymentLinksFiltersInput = z.infer< + typeof ListPaymentLinksFiltersSchema +>; + +// ───────────────────────────────────────────────────────────────────────────── +// List Payment Link Line Items Schema +// ───────────────────────────────────────────────────────────────────────────── + +/** + * Schema for listing a payment link's line items. + * @see https://docs.stripe.com/api/payment_links/payment_links/line_items + */ +export const ListPaymentLinkLineItemsSchema = z + .object({ + ending_before: z.string().optional(), + limit: z.number().int().min(1).max(100).optional(), + starting_after: z.string().optional(), + }) + .merge(ExpandableSchema); + +export type ListPaymentLinkLineItemsInput = z.infer< + typeof ListPaymentLinkLineItemsSchema +>; diff --git a/libs/shared-schemas/src/lib/index.ts b/libs/shared-schemas/src/lib/index.ts index 609b4ea..5bd8dec 100644 --- a/libs/shared-schemas/src/lib/index.ts +++ b/libs/shared-schemas/src/lib/index.ts @@ -9,6 +9,7 @@ export * from './EventSchema'; export * from './ExpandableSchema'; export * from './ExternalWalletSchema'; export * from './PaymentIntentSchema'; +export * from './PaymentLinkSchema'; export * from './PayoutSchema'; export * from './PersonSchema'; export * from './PriceSchema'; diff --git a/libs/shared-types/src/lib/CheckoutSession.ts b/libs/shared-types/src/lib/CheckoutSession.ts index 446b1fc..dc9e2e0 100644 --- a/libs/shared-types/src/lib/CheckoutSession.ts +++ b/libs/shared-types/src/lib/CheckoutSession.ts @@ -497,8 +497,11 @@ export interface CheckoutSession { // Custom Fields // ───────────────────────────────────────────────────────────────────────────── -/** A custom field collected from the customer during Checkout. Up to 3 are supported per session. */ -export interface CheckoutSessionCustomField { +/** + * Custom field configuration shared by Checkout Sessions and Payment Links. + * Does not include collected customer values — those exist only on completed sessions. + */ +export interface CheckoutCustomField { /** Configuration for type=dropdown fields. */ dropdown: { /** The value that pre-fills on the payment page. */ @@ -510,8 +513,6 @@ export interface CheckoutSessionCustomField { /** The value for this option, not displayed to the customer. Must be unique, alphanumeric, and up to 100 characters. */ value: string; }[]; - /** The option selected by the customer. This will be the value for the option. */ - value: string | null; } | null; /** String of your choice that your integration can use to reconcile this field. Must be unique, alphanumeric, and up to 200 characters. */ key: string; @@ -530,8 +531,6 @@ export interface CheckoutSessionCustomField { maximum_length: number | null; /** The minimum character length requirement for the customer's input. */ minimum_length: number | null; - /** The value entered by the customer, containing only digits. */ - value: string | null; } | null; /** Whether the customer is required to complete the field before completing the Checkout Session. Defaults to false. */ optional: boolean; @@ -543,13 +542,54 @@ export interface CheckoutSessionCustomField { maximum_length: number | null; /** The minimum character length requirement for the customer's input. */ minimum_length: number | null; - /** The value entered by the customer. */ - value: string | null; } | null; /** The type of the field. */ type: 'dropdown' | 'numeric' | 'text'; } +/** + * A custom field collected from the customer during Checkout. Up to 3 are supported per session. + * Extends the shared config shape with the values entered by the customer. + */ +export interface CheckoutSessionCustomField extends CheckoutCustomField { + /** Configuration for type=dropdown fields, including the option selected by the customer. */ + dropdown: { + /** The value that pre-fills on the payment page. */ + default_value: string | null; + /** The options available for the customer to select. Up to 200 options allowed. */ + options: { + /** The label for the option, displayed to the customer. Up to 100 characters. */ + label: string; + /** The value for this option, not displayed to the customer. Must be unique, alphanumeric, and up to 100 characters. */ + value: string; + }[]; + /** The option selected by the customer. This will be the value for the option. */ + value: string | null; + } | null; + /** Configuration for type=numeric fields, including the value entered by the customer. */ + numeric: { + /** The value that pre-fills the field on the payment page. */ + default_value: string | null; + /** The maximum character length constraint for the customer's input. */ + maximum_length: number | null; + /** The minimum character length requirement for the customer's input. */ + minimum_length: number | null; + /** The value entered by the customer, containing only digits. */ + value: string | null; + } | null; + /** Configuration for type=text fields, including the value entered by the customer. */ + text: { + /** The value that pre-fills the field on the payment page. */ + default_value: string | null; + /** The maximum character length constraint for the customer's input. */ + maximum_length: number | null; + /** The minimum character length requirement for the customer's input. */ + minimum_length: number | null; + /** The value entered by the customer. */ + value: string | null; + } | null; +} + // ───────────────────────────────────────────────────────────────────────────── // Line Items // ───────────────────────────────────────────────────────────────────────────── diff --git a/libs/shared-types/src/lib/PaymentLink.ts b/libs/shared-types/src/lib/PaymentLink.ts new file mode 100644 index 0000000..1f3779e --- /dev/null +++ b/libs/shared-types/src/lib/PaymentLink.ts @@ -0,0 +1,304 @@ +import { + CheckoutCustomField, + CheckoutSessionLineItemList, +} from './CheckoutSession'; + +/** + * Stripe-compatible Payment Link object for Zoneless. + * A shareable URL that customers can use to pay, creating a Checkout Session when visited. + * + * @see https://docs.stripe.com/api/payment_links/object + */ +export interface PaymentLink { + /** Unique identifier for the object. */ + id: string; + /** String representing the object's type. Objects of the same type share the same value. */ + object: 'payment_link'; + /** Whether the payment link's url is active. If false, customers visiting the URL will be shown a page saying that the link has been deactivated. */ + active: boolean; + /** Behavior after the purchase is complete. */ + after_completion: PaymentLinkAfterCompletion; + /** Whether user redeemable promotion codes are enabled. */ + allow_promotion_codes: boolean; + /** The ID of the Connect application that created the Payment Link. */ + application: string | null; + /** The amount of the application fee (if any) that will be requested to be applied to the payment and transferred to the application owner's Stripe account. */ + application_fee_amount: number | null; + /** This represents the percentage of the subscription invoice total that will be transferred to the application owner's Stripe account. */ + application_fee_percent: number | null; + /** Configuration details for automatic tax collection. */ + automatic_tax: { + /** If true, tax will be calculated automatically using the customer's location. */ + enabled: boolean; + /** The account that's liable for tax. If set, the business address and tax registrations required to perform the tax calculation are loaded from this account. */ + liability: { + /** The connected account being referenced when type is account. */ + account: string | null; + /** Type of the account referenced. */ + type: 'account' | 'self'; + } | null; + }; + /** Configuration for collecting the customer's billing address. Defaults to auto. */ + billing_address_collection: 'auto' | 'required'; + /** When set, provides configuration to gather active consent from customers. */ + consent_collection: { + /** Settings related to the payment method reuse text shown in the Checkout UI. */ + payment_method_reuse_agreement: { + /** Determines the position and visibility of the payment method reuse agreement in the UI. */ + position: 'auto' | 'hidden'; + } | null; + /** If set to auto, enables the collection of customer consent for promotional communications. */ + promotions: 'auto' | 'none' | null; + /** If set to required, it requires customers to accept the terms of service before being able to pay. */ + terms_of_service: 'none' | 'required' | null; + } | null; + /** Three-letter ISO currency code, in lowercase. Must be a supported currency. */ + currency: string; + /** + * Collect additional information from your customer using custom fields. Up to 3 fields are supported. + * Reuses the Checkout Session custom field shape; collected `value` fields are null on Payment Links. + */ + custom_fields: CheckoutCustomField[]; + /** Display additional text for your customers using custom text. */ + custom_text: { + /** Custom text that should be displayed after the payment confirmation button. */ + after_submit: { message: string } | null; + /** Custom text that should be displayed alongside shipping address collection. */ + shipping_address: { message: string } | null; + /** Custom text that should be displayed alongside the payment confirmation button. */ + submit: { message: string } | null; + /** Custom text that should be displayed in place of the default terms of service agreement text. */ + terms_of_service_acceptance: { message: string } | null; + }; + /** Configuration for Customer creation during checkout. */ + customer_creation: 'always' | 'if_required'; + /** The custom message to be displayed to a customer when a payment link is no longer active. */ + inactive_message: string | null; + /** Configuration for creating invoice for payment mode payment links. */ + invoice_creation: { + /** Enable creating an invoice on successful payment. */ + enabled: boolean; + /** Configuration for the invoice. Default invoice values will be used if unspecified. */ + invoice_data: { + /** The account tax IDs associated with the invoice. */ + account_tax_ids: string[] | null; + /** A list of up to 4 custom fields to be displayed on the invoice. */ + custom_fields: { name: string; value: string }[] | null; + /** An arbitrary string attached to the object. Often useful for displaying to users. */ + description: string | null; + /** Footer to be displayed on the invoice. */ + footer: string | null; + /** The connected account that issues the invoice. */ + issuer: { + /** The connected account being referenced when type is account. */ + account: string | null; + /** Type of the account referenced. */ + type: 'account' | 'self'; + } | null; + /** Set of key-value pairs that you can attach to an object. */ + metadata: Record | null; + /** Options for invoice PDF rendering. */ + rendering_options: { + /** How line-item prices and amounts will be displayed with respect to tax on invoice PDFs. */ + amount_tax_display: string | null; + /** ID of the invoice rendering template to be used for the generated invoice. */ + template: string | null; + } | null; + } | null; + } | null; + /** The line items representing what is being sold. */ + line_items: CheckoutSessionLineItemList | null; + /** If the object exists in live mode, the value is true. If the object exists in test mode, the value is false. */ + livemode: boolean; + /** Settings for Managed Payments for this Payment Link and resulting Checkout Sessions, Payment Intents, Invoices, and Subscriptions. */ + managed_payments: { + /** Indicates whether Managed Payments is enabled for this transaction. */ + enabled: boolean; + } | null; + /** Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format. */ + metadata: Record; + /** Details on the state of name collection for the payment link. */ + name_collection: { + /** The settings applied for collecting a business's name. */ + business: { + /** Indicates whether business name collection is enabled for the payment link. */ + enabled: boolean; + /** Whether the customer is required to complete the field before checking out. Defaults to false. */ + optional: boolean; + } | null; + /** The settings applied for collecting an individual's name. */ + individual: { + /** Indicates whether individual name collection is enabled for the payment link. */ + enabled: boolean; + /** Whether the customer is required to complete the field before checking out. Defaults to false. */ + optional: boolean; + } | null; + } | null; + /** The account on behalf of which to charge. */ + on_behalf_of: string | null; + /** + * The optional items presented to the customer at checkout. + * @remarks Stripe does not publicly document the nested shape of this field. + */ + optional_items: object[] | null; + /** Indicates the parameters to be passed to PaymentIntent creation during checkout. */ + payment_intent_data: PaymentLinkPaymentIntentData | null; + /** Configuration for collecting a payment method during checkout. Defaults to always. */ + payment_method_collection: 'always' | 'if_required'; + /** + * Payment-method-specific configuration. + * @remarks Stripe defines many per-payment-method-type option bags here that are specific to + * fiat payment rails. Zoneless only accepts USDC wallet payments; the documented `card` + * restrictions shape is retained for API parity. + */ + payment_method_options: { + /** Configuration for card payment methods. */ + card: { + /** Restrictions to apply to the card payment method. */ + restrictions: { + /** The card brands to block. */ + brands_blocked: ( + | 'american_express' + | 'discover_global_network' + | 'mastercard' + | 'visa' + )[]; + } | null; + } | null; + } | null; + /** + * The list of payment method types that customers can use. When null, relevant payment + * methods enabled in payment method settings are shown dynamically. + */ + payment_method_types: string[] | null; + /** Controls phone number collection settings during checkout. */ + phone_number_collection: { + /** If true, a phone number will be collected during checkout. */ + enabled: boolean; + }; + /** Settings that restrict the usage of a payment link. */ + restrictions: { + /** Configuration for the completed_sessions restriction type. */ + completed_sessions: { + /** The current number of checkout sessions that have been completed on the payment link which count towards the completed_sessions restriction to be met. */ + count: number; + /** The maximum number of checkout sessions that can be completed for the completed_sessions restriction to be met. */ + limit: number; + }; + } | null; + /** Configuration for collecting the customer's shipping address. */ + shipping_address_collection: { + /** + * An array of two-letter ISO country codes representing which countries Checkout should + * provide as options for shipping locations. + * @remarks Stripe defines ~240 supported country codes here; kept as a plain string array + * for maintainability. + */ + allowed_countries: string[]; + } | null; + /** The shipping rate options applied to the session. */ + shipping_options: { + /** A non-negative integer in cents representing how much to charge. */ + shipping_amount: number; + /** The ID of the Shipping Rate to use for this shipping option. */ + shipping_rate: string; + }[]; + /** Indicates the type of transaction being performed which customizes relevant text on the page, such as the submit button. */ + submit_type: 'auto' | 'book' | 'donate' | 'pay' | 'subscribe'; + /** When creating a subscription, the specified configuration data will be used. There must be at least one line item with a recurring price to use subscription_data. */ + subscription_data: PaymentLinkSubscriptionData | null; + /** Details on the state of tax ID collection for the payment link. */ + tax_id_collection: { + /** Indicates whether tax ID collection is enabled for the session. */ + enabled: boolean; + }; + /** The account (if any) the payments will be attributed to for tax reporting, and where funds from each payment will be transferred to. */ + transfer_data: { + /** The amount in the smallest currency unit that will be transferred to the destination account. By default, the entire amount is transferred to the destination. */ + amount: number | null; + /** The connected account receiving the transfer. */ + destination: string; + } | null; + /** The public URL that can be shared with customers. */ + url: string; + + /** + * The platform account that owns this resource. + * @zoneless_extension + */ + platform_account: string; +} + +// ───────────────────────────────────────────────────────────────────────────── +// After Completion +// ───────────────────────────────────────────────────────────────────────────── + +/** Behavior after the purchase is complete. */ +export interface PaymentLinkAfterCompletion { + /** Configuration when type=hosted_confirmation. */ + hosted_confirmation: { + /** The custom message that is displayed to the customer after the purchase is complete. */ + custom_message: string | null; + } | null; + /** Configuration when type=redirect. */ + redirect: { + /** The URL the customer will be redirected to after the purchase is complete. */ + url: string; + } | null; + /** The specified behavior after the purchase is complete. */ + type: 'hosted_confirmation' | 'redirect'; +} + +// ───────────────────────────────────────────────────────────────────────────── +// Payment Intent Data +// ───────────────────────────────────────────────────────────────────────────── + +/** Parameters passed to PaymentIntent creation during checkout. */ +export interface PaymentLinkPaymentIntentData { + /** Indicates when the funds will be captured from the customer's account. */ + capture_method: 'automatic' | 'automatic_async' | 'manual' | null; + /** An arbitrary string attached to the object. Often useful for displaying to users. */ + description: string | null; + /** Set of key-value pairs that will set metadata on Payment Intents generated from this payment link. */ + metadata: Record; + /** Indicates that you intend to make future payments with the payment method collected during checkout. */ + setup_future_usage: 'off_session' | 'on_session' | null; + /** For a non-card payment, information about the charge that appears on the customer's statement when this payment succeeds in creating a charge. */ + statement_descriptor: string | null; + /** For a card payment, information about the charge that appears on the customer's statement when this payment succeeds in creating a charge. */ + statement_descriptor_suffix: string | null; + /** A string that identifies the resulting payment as part of a group. */ + transfer_group: string | null; +} + +// ───────────────────────────────────────────────────────────────────────────── +// Subscription Data +// ───────────────────────────────────────────────────────────────────────────── + +/** Configuration data used when creating a subscription from this payment link. */ +export interface PaymentLinkSubscriptionData { + /** The subscription's description, meant to be displayable to the customer. */ + description: string | null; + /** All invoices will be billed using the specified settings. */ + invoice_settings: { + /** The connected account that issues the invoice. */ + issuer: { + /** The connected account being referenced when type is account. */ + account: string | null; + /** Type of the account referenced. */ + type: 'account' | 'self'; + }; + }; + /** Set of key-value pairs that will set metadata on Subscriptions generated from this payment link. */ + metadata: Record; + /** Integer representing the number of trial period days before the customer is charged for the first time. */ + trial_period_days: number | null; + /** Settings related to subscription trials. */ + trial_settings: { + /** Defines how the subscription should behave when the user's free trial ends. */ + end_behavior: { + /** Indicates how the subscription should change when the trial ends if the user did not provide a payment method. */ + missing_payment_method: 'cancel' | 'create_invoice' | 'pause'; + }; + } | null; +} diff --git a/libs/shared-types/src/lib/index.ts b/libs/shared-types/src/lib/index.ts index 3495e99..5ef7157 100644 --- a/libs/shared-types/src/lib/index.ts +++ b/libs/shared-types/src/lib/index.ts @@ -16,6 +16,7 @@ export * from './Event'; export * from './ExternalWallet'; export * from './IdempotencyKey'; export * from './PaymentIntent'; +export * from './PaymentLink'; export * from './Payout'; export * from './Person'; export * from './Price';