import {z} from 'zod' export type IntegrationMode = 'LINK'; export type PaymentResourceType = 'BUY_NOW'; export type Reusable = 'MULTIPLE'; export type CurrencyCode = unknown; export type Money = {currency_code: CurrencyCode, value: string}; export type Percentage = unknown; export type Tax = {name: string, percent: Percentage, amount?: Money | undefined, tax_note?: string | undefined}; export type TaxList = Array; export type ShippingName = {full_name?: string | undefined}; export type ShippingPhoneNumber = {country_code: string, national_number: string}; export type CountryCode = unknown; export type ShippingAddress = {address_line_1?: string | undefined, address_line_2?: string | undefined, admin_area_2?: string | undefined, admin_area_1?: string | undefined, postal_code?: string | undefined, country_code: CountryCode}; export type Shipping = {name?: ShippingName | undefined, phone_number?: ShippingPhoneNumber | undefined, type?: ('SHIPPING' | 'PICKUP_IN_PERSON' | 'PICKUP_IN_STORE' | 'PICKUP_FROM_PERSON') | undefined, address?: ShippingAddress | undefined}; export type ShippingList = Array; export type CustomerNote = {required?: boolean | undefined, label?: string | undefined}; export type CustomerNoteList = Array; export type DimensionOption = {label: string, unit_amount?: Money | undefined}; export type DimensionOptionList = Array; export type Dimension = {name: string, primary: boolean, options: DimensionOptionList}; export type DimensionList = Array; export type Variants = {dimensions: DimensionList}; export type AdjustableQuantity = {maximum: number}; export type LineItem = {name?: string | undefined, product_id?: string | undefined, description?: string | undefined, unit_amount?: Money | undefined, taxes?: TaxList | undefined, shipping?: ShippingList | undefined, collect_shipping_address?: boolean | undefined, customer_notes?: CustomerNoteList | undefined, variants?: Variants | undefined, adjustable_quantity?: AdjustableQuantity | undefined}; export type LineItemList = Array; export type PaymentResourceRequest = {integration_mode: IntegrationMode, type: PaymentResourceType, reusable?: Reusable | undefined, return_url?: string | undefined, line_items?: LineItemList | undefined}; export type CreateApiV1CheckoutPaymentResourcesArgs = {'PayPal-Request-Id'?: string | undefined, 'Content-Type'?: string | undefined, 'PayPal-Auth-Assertion'?: string | undefined, body: PaymentResourceRequest}; export type CreateApiV1CheckoutPaymentResourcesOptions = {baseUrl?: string | undefined, Oauth2?: string | undefined, fetch?: typeof fetch | undefined}; export const paymentResourceId = z.string().min(1).max(127); export const linkDescription = z.object({href: z.string(), rel: z.string(), method: z.enum(["GET", "POST", "PUT", "DELETE", "HEAD", "CONNECT", "OPTIONS", "PATCH"]).optional()}); export const paymentResourceBaseResponseDefinitionsLinkDescriptionList = z.array(linkDescription); export const dateTime = z.string().min(20).max(64); export const status = z.enum(["ACTIVE", "INACTIVE"]).min(1).max(20); export const integrationMode = z.literal("LINK").min(1).max(20); export const paymentResourceType = z.literal("BUY_NOW").min(1).max(20); export const reusable = z.literal("MULTIPLE").min(1).max(20); export const currencyCode = z.string().min(3).max(3); export const money = z.object({currency_code: currencyCode, value: z.string().max(32)}); export const percentage = z.string(); export const tax = z.object({name: z.string().max(100), percent: percentage, amount: money.optional(), tax_note: z.string().max(40).optional()}); export const taxList = z.array(tax); export const shippingName = z.object({full_name: z.string().max(300).optional()}); export const shippingPhoneNumber = z.object({country_code: z.string().min(1).max(3), national_number: z.string().min(1).max(14)}); export const countryCode = z.string().min(2).max(2); export const shippingAddress = z.object({address_line_1: z.string().max(300).optional(), address_line_2: z.string().max(300).optional(), admin_area_2: z.string().max(120).optional(), admin_area_1: z.string().max(300).optional(), postal_code: z.string().max(60).optional(), country_code: countryCode}); export const shipping = z.object({name: shippingName.optional(), phone_number: shippingPhoneNumber.optional(), type: z.enum(["SHIPPING", "PICKUP_IN_PERSON", "PICKUP_IN_STORE", "PICKUP_FROM_PERSON"]).min(1).max(255).optional(), address: shippingAddress.optional()}); export const shippingList = z.array(shipping); export const customerNote = z.object({required: z.boolean().optional(), label: z.string().min(1).max(127).optional()}); export const customerNoteList = z.array(customerNote); export const dimensionOption = z.object({label: z.string().min(1).max(200), unit_amount: money.optional()}); export const dimensionOptionList = z.array(dimensionOption); export const dimension = z.object({name: z.string().min(1).max(64), primary: z.boolean(), options: dimensionOptionList}); export const dimensionList = z.array(dimension); export const variants = z.object({dimensions: dimensionList}); export const adjustableQuantity = z.object({maximum: z.number().int().gte(1).lte(100)}); export const lineItem = z.object({name: z.string().min(1).max(127).optional(), product_id: z.string().min(1).max(50).optional(), description: z.string().min(1).max(2048).optional(), unit_amount: money.optional(), taxes: taxList.optional(), shipping: shippingList.optional(), collect_shipping_address: z.boolean().optional(), customer_notes: customerNoteList.optional(), variants: variants.optional(), adjustable_quantity: adjustableQuantity.optional()}); export const lineItemList = z.array(lineItem); export const single = z.object({body: z.string().min(1).max(9999).optional()}); export const codeSnippetStacked = z.object({framework: z.enum(["HTML", "REACT"]).min(1).max(32), head: z.string().min(1).max(9999).optional(), body: z.string().min(1).max(9999).optional(), button_placement: z.string().min(1).max(9999).optional()}); export const codeSnippetStackedList = z.array(codeSnippetStacked); export const codeSnippet = z.object({single: single.optional(), stacked: codeSnippetStackedList.optional()}); export const paymentResourceDetailedResponse = z.object({id: paymentResourceId.optional(), payment_link: z.string().min(1).max(200).optional(), links: paymentResourceBaseResponseDefinitionsLinkDescriptionList.optional(), create_time: dateTime.optional(), update_time: dateTime.optional(), status: status.optional(), integration_mode: integrationMode, type: paymentResourceType, reusable: reusable.optional(), return_url: z.string().min(1).max(1024).optional(), line_items: lineItemList.optional(), code_snippets: codeSnippet.optional()}); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * Creates a payment resource for a single-item purchase * * POST /v1/checkout/payment-resources * * Also accepts auth: Oauth2 */ export const createApiV1CheckoutPaymentResources = async (args: CreateApiV1CheckoutPaymentResourcesArgs, options?: CreateApiV1CheckoutPaymentResourcesOptions) => { const {baseUrl = "https://api-m.paypal.com", fetch: fetchFn = fetch, Oauth2} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/json") headers.set('accept', "application/json") if (args['PayPal-Request-Id'] != null) headers.set("PayPal-Request-Id", String(args['PayPal-Request-Id'])) if (args['Content-Type'] != null) headers.set("Content-Type", String(args['Content-Type'])) if (args['PayPal-Auth-Assertion'] != null) headers.set("PayPal-Auth-Assertion", String(args['PayPal-Auth-Assertion'])) if (Oauth2 !== undefined) headers.set('authorization', `Bearer ${Oauth2}`) const url = `${baseUrl.replace(/\/+$/, '')}/v1/checkout/payment-resources${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "POST", headers, body: JSON.stringify(args.body)}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(paymentResourceDetailedResponse, await res.json()) };