# Checkout functions

> Type your checkout functions, and read the schemas and limits Storeware holds them to.

Source: https://storeware.ai/docs/sdk/functions

```ts
import { ... } from '@storeware/plugin-sdk/functions';
```

### defineFunctions

Types an app's `functions` export, so each handler is checked against its kind.

```ts
function defineFunctions<T extends { [handle: string]: { [K in FunctionKind]: { kind: K; run: FunctionTypes[K]; }; }[FunctionKind]; }>(functions: T): { [H in keyof T]: T[H]['run']; }
```

### FunctionKind

```ts
type FunctionKind = (typeof FUNCTION_KINDS)[number];
```

### PaymentMethod

```ts
type PaymentMethod = (typeof PAYMENT_METHODS)[number];
```

### FunctionLine

```ts
type FunctionLine = z.infer<typeof functionLine>;
```

### BasketInput

```ts
type BasketInput = z.infer<typeof basketInput>;
```

### DeliveryOption

```ts
type DeliveryOption = z.infer<typeof deliveryOption>;
```

### DeliveryRatesInput

```ts
type DeliveryRatesInput = z.infer<typeof deliveryRatesInput>;
```

### DeliveryRatesOutput

```ts
type DeliveryRatesOutput = z.infer<typeof deliveryRatesOutput>;
```

### DiscountInput

```ts
type DiscountInput = z.infer<typeof discountInput>;
```

### DiscountOutput

```ts
type DiscountOutput = z.infer<typeof discountOutput>;
```

### PaymentMethodsInput

```ts
type PaymentMethodsInput = z.infer<typeof paymentMethodsInput>;
```

### PaymentMethodsOutput

```ts
type PaymentMethodsOutput = z.infer<typeof paymentMethodsOutput>;
```

### CartValidationInput

```ts
type CartValidationInput = z.infer<typeof cartValidationInput>;
```

### CartValidationOutput

```ts
type CartValidationOutput = z.infer<typeof cartValidationOutput>;
```

### FunctionTypes

| Name | Type | Description |
| --- | --- | --- |
| `discount` | `(input: DiscountInput) => DiscountOutput \| Promise<DiscountOutput>` |  |
| `delivery_rates` | `(input: DeliveryRatesInput) => DeliveryRatesOutput \| Promise<DeliveryRatesOutput>` |  |
| `payment_methods` | `( input: PaymentMethodsInput, ) => PaymentMethodsOutput \| Promise<PaymentMethodsOutput>` |  |
| `cart_validation` | `( input: CartValidationInput, ) => CartValidationOutput \| Promise<CartValidationOutput>` |  |

### FUNCTION_KINDS

```ts
const FUNCTION_KINDS: readonly ['discount', 'delivery_rates', 'payment_methods', 'cart_validation']
```

### PAYMENT_METHODS

The payment methods a function can hide or reorder. Platform level on purpose: `card` is every card and wallet Stripe offers, `on_account` is paying later on a business's terms, `crypto` is stablecoin payment.

```ts
const PAYMENT_METHODS: readonly ['card', 'on_account', 'crypto']
```

### FUNCTION_CPU_MS

CPU time one call may use, in milliseconds.

```ts
const FUNCTION_CPU_MS: 25
```

### FUNCTION_WALL_MS

Wall time one call may take, in milliseconds, waiting included.

```ts
const FUNCTION_WALL_MS: 250
```

### MAX_FUNCTION_OUTPUT_BYTES

The largest answer a function may give, as JSON, in bytes.

```ts
const MAX_FUNCTION_OUTPUT_BYTES: number
```

### functionLine

One line of the basket, as every kind sees it.

```ts
const functionLine: z.ZodObject<{ id: z.ZodString; productId: z.ZodString; variantId: z.ZodString; sku: z.ZodNullable<z.ZodString>; title: z.ZodString; variantTitle: z.ZodNullable<z.ZodString>; quantity: z.ZodNumber; unitPrice: z.ZodNumber; grams: z.ZodNullable<z.ZodNumber>; requiresShipping: z.ZodBoolean; metafields: z.ZodRecord<z.ZodString, z.ZodUnknown>; }, z.core.$strip>
```

### basketInput

What every kind is given: the basket, who is buying, and where it goes.

```ts
const basketInput: z.ZodObject<{ storeId: z.ZodString; currency: z.ZodString; lines: z.ZodArray<z.ZodObject<{ id: z.ZodString; productId: z.ZodString; variantId: z.ZodString; sku: z.ZodNullable<z.ZodString>; title: z.ZodString; variantTitle: z.ZodNullable<z.ZodString>; quantity: z.ZodNumber; unitPrice: z.ZodNumber; grams: z.ZodNullable<z.ZodNumber>; requiresShipping: z.ZodBoolean; metafields: z.ZodRecord<z.ZodString, z.ZodUnknown>; }, z.core.$strip>>; subtotal: z.ZodNumber; buyer: z.ZodObject<{ customerId: z.ZodNullable<z.ZodString>; companyId: z.ZodNullable<z.ZodString>; email: z.ZodNullable<z.ZodString>; metafields: z.ZodRecord<z.ZodString, z.ZodUnknown>; }, z.core.$strip>; destination: z.ZodNullable<z.ZodObject<{ country: z.ZodString; region: z.ZodNullable<z.ZodString>; postalCode: z.ZodNullable<z.ZodString>; city: z.ZodNullable<z.ZodString>; }, z.core.$strip>>; discountCode: z.ZodNullable<z.ZodString>; delivery: z.ZodNullable<z.ZodObject<{ id: z.ZodString; type: z.ZodString; title: z.ZodString; amount: z.ZodNumber; }, z.core.$strip>>; channel: z.ZodEnum<{ web: 'web'; agent: 'agent'; }>; }, z.core.$strip>
```

### deliveryOption

```ts
const deliveryOption: z.ZodObject<{ id: z.ZodString; type: z.ZodString; title: z.ZodString; description: z.ZodOptional<z.ZodString>; carrier: z.ZodOptional<z.ZodString>; amount: z.ZodNumber; }, z.core.$strip>
```

### deliveryRatesInput

```ts
const deliveryRatesInput: z.ZodObject<{ storeId: z.ZodString; currency: z.ZodString; lines: z.ZodArray<z.ZodObject<{ id: z.ZodString; productId: z.ZodString; variantId: z.ZodString; sku: z.ZodNullable<z.ZodString>; title: z.ZodString; variantTitle: z.ZodNullable<z.ZodString>; quantity: z.ZodNumber; unitPrice: z.ZodNumber; grams: z.ZodNullable<z.ZodNumber>; requiresShipping: z.ZodBoolean; metafields: z.ZodRecord<z.ZodString, z.ZodUnknown>; }, z.core.$strip>>; subtotal: z.ZodNumber; buyer: z.ZodObject<{ customerId: z.ZodNullable<z.ZodString>; companyId: z.ZodNullable<z.ZodString>; email: z.ZodNullable<z.ZodString>; metafields: z.ZodRecord<z.ZodString, z.ZodUnknown>; }, z.core.$strip>; destination: z.ZodNullable<z.ZodObject<{ country: z.ZodString; region: z.ZodNullable<z.ZodString>; postalCode: z.ZodNullable<z.ZodString>; city: z.ZodNullable<z.ZodString>; }, z.core.$strip>>; discountCode: z.ZodNullable<z.ZodString>; delivery: z.ZodNullable<z.ZodObject<{ id: z.ZodString; type: z.ZodString; title: z.ZodString; amount: z.ZodNumber; }, z.core.$strip>>; channel: z.ZodEnum<{ web: 'web'; agent: 'agent'; }>; options: z.ZodArray<z.ZodObject<{ id: z.ZodString; type: z.ZodString; title: z.ZodString; description: z.ZodOptional<z.ZodString>; carrier: z.ZodOptional<z.ZodString>; amount: z.ZodNumber; }, z.core.$strip>>; }, z.core.$strip>
```

### deliveryRatesOutput

The options to offer, in order. An option left out is hidden. Only options the input named may appear, each once, so an id never changes and nothing is invented; offering new rates is `[checkout.delivery_rates]`'s job.

```ts
const deliveryRatesOutput: z.ZodObject<{ options: z.ZodArray<z.ZodObject<{ id: z.ZodString; title: z.ZodOptional<z.ZodString>; amount: z.ZodOptional<z.ZodNumber>; }, z.core.$strict>>; }, z.core.$strip>
```

### discountInput

```ts
const discountInput: z.ZodObject<{ storeId: z.ZodString; currency: z.ZodString; lines: z.ZodArray<z.ZodObject<{ id: z.ZodString; productId: z.ZodString; variantId: z.ZodString; sku: z.ZodNullable<z.ZodString>; title: z.ZodString; variantTitle: z.ZodNullable<z.ZodString>; quantity: z.ZodNumber; unitPrice: z.ZodNumber; grams: z.ZodNullable<z.ZodNumber>; requiresShipping: z.ZodBoolean; metafields: z.ZodRecord<z.ZodString, z.ZodUnknown>; }, z.core.$strip>>; subtotal: z.ZodNumber; buyer: z.ZodObject<{ customerId: z.ZodNullable<z.ZodString>; companyId: z.ZodNullable<z.ZodString>; email: z.ZodNullable<z.ZodString>; metafields: z.ZodRecord<z.ZodString, z.ZodUnknown>; }, z.core.$strip>; destination: z.ZodNullable<z.ZodObject<{ country: z.ZodString; region: z.ZodNullable<z.ZodString>; postalCode: z.ZodNullable<z.ZodString>; city: z.ZodNullable<z.ZodString>; }, z.core.$strip>>; discountCode: z.ZodNullable<z.ZodString>; delivery: z.ZodNullable<z.ZodObject<{ id: z.ZodString; type: z.ZodString; title: z.ZodString; amount: z.ZodNumber; }, z.core.$strip>>; channel: z.ZodEnum<{ web: 'web'; agent: 'agent'; }>; }, z.core.$strip>
```

### discountOutput

```ts
const discountOutput: z.ZodObject<{ discounts: z.ZodArray<z.ZodObject<{ title: z.ZodString; target: z.ZodUnion<readonly [z.ZodLiteral<'order'>, z.ZodLiteral<'delivery'>, z.ZodObject<{ lines: z.ZodArray<z.ZodString>; }, z.core.$strict>]>; value: z.ZodUnion<readonly [z.ZodObject<{ percent: z.ZodNumber; }, z.core.$strict>, z.ZodObject<{ amount: z.ZodNumber; }, z.core.$strict>]>; }, z.core.$strict>>; }, z.core.$strip>
```

### paymentMethodsInput

```ts
const paymentMethodsInput: z.ZodObject<{ storeId: z.ZodString; currency: z.ZodString; lines: z.ZodArray<z.ZodObject<{ id: z.ZodString; productId: z.ZodString; variantId: z.ZodString; sku: z.ZodNullable<z.ZodString>; title: z.ZodString; variantTitle: z.ZodNullable<z.ZodString>; quantity: z.ZodNumber; unitPrice: z.ZodNumber; grams: z.ZodNullable<z.ZodNumber>; requiresShipping: z.ZodBoolean; metafields: z.ZodRecord<z.ZodString, z.ZodUnknown>; }, z.core.$strip>>; subtotal: z.ZodNumber; buyer: z.ZodObject<{ customerId: z.ZodNullable<z.ZodString>; companyId: z.ZodNullable<z.ZodString>; email: z.ZodNullable<z.ZodString>; metafields: z.ZodRecord<z.ZodString, z.ZodUnknown>; }, z.core.$strip>; destination: z.ZodNullable<z.ZodObject<{ country: z.ZodString; region: z.ZodNullable<z.ZodString>; postalCode: z.ZodNullable<z.ZodString>; city: z.ZodNullable<z.ZodString>; }, z.core.$strip>>; discountCode: z.ZodNullable<z.ZodString>; delivery: z.ZodNullable<z.ZodObject<{ id: z.ZodString; type: z.ZodString; title: z.ZodString; amount: z.ZodNumber; }, z.core.$strip>>; channel: z.ZodEnum<{ web: 'web'; agent: 'agent'; }>; methods: z.ZodArray<z.ZodEnum<{ card: 'card'; on_account: 'on_account'; crypto: 'crypto'; }>>; }, z.core.$strip>
```

### paymentMethodsOutput

The methods to offer, in order. A method left out is not offered, and refused if tried.

```ts
const paymentMethodsOutput: z.ZodObject<{ methods: z.ZodArray<z.ZodEnum<{ card: 'card'; on_account: 'on_account'; crypto: 'crypto'; }>>; }, z.core.$strip>
```

### cartValidationInput

```ts
const cartValidationInput: z.ZodObject<{ storeId: z.ZodString; currency: z.ZodString; lines: z.ZodArray<z.ZodObject<{ id: z.ZodString; productId: z.ZodString; variantId: z.ZodString; sku: z.ZodNullable<z.ZodString>; title: z.ZodString; variantTitle: z.ZodNullable<z.ZodString>; quantity: z.ZodNumber; unitPrice: z.ZodNumber; grams: z.ZodNullable<z.ZodNumber>; requiresShipping: z.ZodBoolean; metafields: z.ZodRecord<z.ZodString, z.ZodUnknown>; }, z.core.$strip>>; subtotal: z.ZodNumber; buyer: z.ZodObject<{ customerId: z.ZodNullable<z.ZodString>; companyId: z.ZodNullable<z.ZodString>; email: z.ZodNullable<z.ZodString>; metafields: z.ZodRecord<z.ZodString, z.ZodUnknown>; }, z.core.$strip>; destination: z.ZodNullable<z.ZodObject<{ country: z.ZodString; region: z.ZodNullable<z.ZodString>; postalCode: z.ZodNullable<z.ZodString>; city: z.ZodNullable<z.ZodString>; }, z.core.$strip>>; discountCode: z.ZodNullable<z.ZodString>; delivery: z.ZodNullable<z.ZodObject<{ id: z.ZodString; type: z.ZodString; title: z.ZodString; amount: z.ZodNumber; }, z.core.$strip>>; channel: z.ZodEnum<{ web: 'web'; agent: 'agent'; }>; }, z.core.$strip>
```

### cartValidationOutput

```ts
const cartValidationOutput: z.ZodObject<{ errors: z.ZodArray<z.ZodObject<{ message: z.ZodString; lineId: z.ZodOptional<z.ZodString>; }, z.core.$strict>>; }, z.core.$strip>
```

### FUNCTION_SCHEMAS

Each kind's input and output schema, by kind.

```ts
const FUNCTION_SCHEMAS: { readonly discount: { readonly input: z.ZodObject<{ storeId: z.ZodString; currency: z.ZodString; lines: z.ZodArray<z.ZodObject<{ id: z.ZodString; productId: z.ZodString; variantId: z.ZodString; sku: z.ZodNullable<z.ZodString>; title: z.ZodString; variantTitle: z.ZodNullable<z.ZodString>; quantity: z.ZodNumber; unitPrice: z.ZodNumber; grams: z.ZodNullable<z.ZodNumber>; requiresShipping: z.ZodBoolean; metafields: z.ZodRecord<z.ZodString, z.ZodUnknown>; }, z.core.$strip>>; subtotal: z.ZodNumber; buyer: z.ZodObject<{ customerId: z.ZodNullable<z.ZodString>; companyId: z.ZodNullable<z.ZodString>; email: z.ZodNullable<z.ZodString>; metafields: z.ZodRecord<z.ZodString, z.ZodUnknown>; }, z.core.$strip>; destination: z.ZodNullable<z.ZodObject<{ country: z.ZodString; region: z.ZodNullable<z.ZodString>; postalCode: z.ZodNullable<z.ZodString>; city: z.ZodNullable<z.ZodString>; }, z.core.$strip>>; discountCode: z.ZodNullable<z.ZodString>; delivery: z.ZodNullable<z.ZodObject<{ id: z.ZodString; type: z.ZodString; title: z.ZodString; amount: z.ZodNumber; }, z.core.$strip>>; channel: z.ZodEnum<{ web: 'web'; agent: 'agent'; }>; }, z.core.$strip>; readonly output: z.ZodObject<{ discounts: z.ZodArray<z.ZodObject<{ title: z.ZodString; target: z.ZodUnion<readonly [z.ZodLiteral<'order'>, z.ZodLiteral<'delivery'>, z.ZodObject<{ lines: z.ZodArray<z.ZodString>; }, z.core.$strict>]>; value: z.ZodUnion<readonly [z.ZodObject<{ percent: z.ZodNumber; }, z.core.$strict>, z.ZodObject<{ amount: z.ZodNumber; }, z.core.$strict>]>; }, z.core.$strict>>; }, z.core.$strip>; }; readonly delivery_rates: { readonly input: z.ZodObject<{ storeId: z.ZodString; currency: z.ZodString; lines: z.ZodArray<z.ZodObject<{ id: z.ZodString; productId: z.ZodString; variantId: z.ZodString; sku: z.ZodNullable<z.ZodString>; title: z.ZodString; variantTitle: z.ZodNullable<z.ZodString>; quantity: z.ZodNumber; unitPrice: z.ZodNumber; grams: z.ZodNullable<z.ZodNumber>; requiresShipping: z.ZodBoolean; metafields: z.ZodRecord<z.ZodString, z.ZodUnknown>; }, z.core.$strip>>; subtotal: z.ZodNumber; buyer: z.ZodObject<{ customerId: z.ZodNullable<z.ZodString>; companyId: z.ZodNullable<z.ZodString>; email: z.ZodNullable<z.ZodString>; metafields: z.ZodRecord<z.ZodString, z.ZodUnknown>; }, z.core.$strip>; destination: z.ZodNullable<z.ZodObject<{ country: z.ZodString; region: z.ZodNullable<z.ZodString>; postalCode: z.ZodNullable<z.ZodString>; city: z.ZodNullable<z.ZodString>; }, z.core.$strip>>; discountCode: z.ZodNullable<z.ZodString>; delivery: z.ZodNullable<z.ZodObject<{ id: z.ZodString; type: z.ZodString; title: z.ZodString; amount: z.ZodNumber; }, z.core.$strip>>; channel: z.ZodEnum<{ web: 'web'; agent: 'agent'; }>; options: z.ZodArray<z.ZodObject<{ id: z.ZodString; type: z.ZodString; title: z.ZodString; description: z.ZodOptional<z.ZodString>; carrier: z.ZodOptional<z.ZodString>; amount: z.ZodNumber; }, z.core.$strip>>; }, z.core.$strip>; readonly output: z.ZodObject<{ options: z.ZodArray<z.ZodObject<{ id: z.ZodString; title: z.ZodOptional<z.ZodString>; amount: z.ZodOptional<z.ZodNumber>; }, z.core.$strict>>; }, z.core.$strip>; }; readonly payment_methods: { readonly input: z.ZodObject<{ storeId: z.ZodString; currency: z.ZodString; lines: z.ZodArray<z.ZodObject<{ id: z.ZodString; productId: z.ZodString; variantId: z.ZodString; sku: z.ZodNullable<z.ZodString>; title: z.ZodString; variantTitle: z.ZodNullable<z.ZodString>; quantity: z.ZodNumber; unitPrice: z.ZodNumber; grams: z.ZodNullable<z.ZodNumber>; requiresShipping: z.ZodBoolean; metafields: z.ZodRecord<z.ZodString, z.ZodUnknown>; }, z.core.$strip>>; subtotal: z.ZodNumber; buyer: z.ZodObject<{ customerId: z.ZodNullable<z.ZodString>; companyId: z.ZodNullable<z.ZodString>; email: z.ZodNullable<z.ZodString>; metafields: z.ZodRecord<z.ZodString, z.ZodUnknown>; }, z.core.$strip>; destination: z.ZodNullable<z.ZodObject<{ country: z.ZodString; region: z.ZodNullable<z.ZodString>; postalCode: z.ZodNullable<z.ZodString>; city: z.ZodNullable<z.ZodString>; }, z.core.$strip>>; discountCode: z.ZodNullable<z.ZodString>; delivery: z.ZodNullable<z.ZodObject<{ id: z.ZodString; type: z.ZodString; title: z.ZodString; amount: z.ZodNumber; }, z.core.$strip>>; channel: z.ZodEnum<{ web: 'web'; agent: 'agent'; }>; methods: z.ZodArray<z.ZodEnum<{ card: 'card'; on_account: 'on_account'; crypto: 'crypto'; }>>; }, z.core.$strip>; readonly output: z.ZodObject<{ methods: z.ZodArray<z.ZodEnum<{ card: 'card'; on_account: 'on_account'; crypto: 'crypto'; }>>; }, z.core.$strip>; }; readonly cart_validation: { readonly input: z.ZodObject<{ storeId: z.ZodString; currency: z.ZodString; lines: z.ZodArray<z.ZodObject<{ id: z.ZodString; productId: z.ZodString; variantId: z.ZodString; sku: z.ZodNullable<z.ZodString>; title: z.ZodString; variantTitle: z.ZodNullable<z.ZodString>; quantity: z.ZodNumber; unitPrice: z.ZodNumber; grams: z.ZodNullable<z.ZodNumber>; requiresShipping: z.ZodBoolean; metafields: z.ZodRecord<z.ZodString, z.ZodUnknown>; }, z.core.$strip>>; subtotal: z.ZodNumber; buyer: z.ZodObject<{ customerId: z.ZodNullable<z.ZodString>; companyId: z.ZodNullable<z.ZodString>; email: z.ZodNullable<z.ZodString>; metafields: z.ZodRecord<z.ZodString, z.ZodUnknown>; }, z.core.$strip>; destination: z.ZodNullable<z.ZodObject<{ country: z.ZodString; region: z.ZodNullable<z.ZodString>; postalCode: z.ZodNullable<z.ZodString>; city: z.ZodNullable<z.ZodString>; }, z.core.$strip>>; discountCode: z.ZodNullable<z.ZodString>; delivery: z.ZodNullable<z.ZodObject<{ id: z.ZodString; type: z.ZodString; title: z.ZodString; amount: z.ZodNumber; }, z.core.$strip>>; channel: z.ZodEnum<{ web: 'web'; agent: 'agent'; }>; }, z.core.$strip>; readonly output: z.ZodObject<{ errors: z.ZodArray<z.ZodObject<{ message: z.ZodString; lineId: z.ZodOptional<z.ZodString>; }, z.core.$strict>>; }, z.core.$strip>; }; }
```
