Skip to content
This repository has been archived by the owner on Nov 4, 2024. It is now read-only.

Fix create checkout logic #6

Merged
merged 8 commits into from
Mar 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 30 additions & 8 deletions src/modules/checkout/checkout.action.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { requestLemonSqueeze } from "~/shared";
import { LemonsqueezyDataType, requestLemonSqueeze } from "~/shared";

import type {
CreateCheckoutBody,
CreateCheckoutOptions,
CreateCheckoutResult,
ListAllCheckoutsOptions,
Expand All @@ -20,24 +21,45 @@ import type { SharedModuleOptions } from "~/shared";
* @returns A checkout object
*/
export async function createCheckout(
options: CreateCheckoutOptions & SharedModuleOptions
options: CreateCheckoutOptions & Pick<SharedModuleOptions, "apiKey">
): Promise<CreateCheckoutResult> {
const {
checkout_data,
checkout_options,
custom_price,
expires_at,
product_options,
store,
variant,
...rest
} = options;

return requestLemonSqueeze<CreateCheckoutResult>({
return requestLemonSqueeze<CreateCheckoutResult, CreateCheckoutBody>({
data: {
...(checkout_data ? { checkout_data } : {}),
...(checkout_options ? { checkout_options } : {}),
...(custom_price ? { custom_price } : {}),
...(expires_at ? { expires_at } : {}),
...(product_options ? { product_options } : {}),
data: {
attributes: {
checkout_data,
checkout_options,
custom_price,
expires_at,
product_options,
},
relationships: {
store: {
data: {
id: store,
type: LemonsqueezyDataType.stores,
},
},
variant: {
data: {
id: variant,
type: LemonsqueezyDataType.variants,
},
},
},
type: LemonsqueezyDataType.checkouts,
},
},
path: "/checkouts",
method: "POST",
Expand Down
77 changes: 70 additions & 7 deletions src/modules/checkout/checkout.test.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { describe, it, expect, beforeAll } from "vitest";
import { email, firstName, name, zipCode } from "minifaker";
import "minifaker/locales/en";

import { listAllCheckouts } from ".";
import { createCheckout, listAllCheckouts, retrieveCheckout } from ".";
import { listAllStores, listAllVariants } from "..";

describe.concurrent("Checkout", () => {
import type { CreateCheckoutResult } from ".";

describe("Checkout", () => {
const apiKey = process.env.LEMON_SQUEEZY_API_KEY as string;
let newCheckout: CreateCheckoutResult;

beforeAll(() => {
if (!apiKey) throw "No LEMON_SQUEEZY_API_KEY environment variable found";
});

it("Retrieve checkout", async () => {
expect(true).toEqual(true);
});

it("List all checkouts", async () => {
it.concurrent("List all checkouts", async () => {
const checkouts = await listAllCheckouts({
apiKey,
});
Expand All @@ -22,4 +24,65 @@ describe.concurrent("Checkout", () => {
expect(Array.isArray(checkouts.data)).toBe(true);
expect(checkouts.errors).toBeUndefined();
});

it("Create a checkout", async () => {
if (newCheckout)
throw new Error(
"`newCheckout` is already initialised before a new checkout has been created."
);

const [stores, variants] = await Promise.all([
listAllStores({
apiKey,
}),
listAllVariants({
apiKey,
}),
]);

newCheckout = await createCheckout({
apiKey,
checkout_data: {
billing_address: {
country: "US",
zip: zipCode(),
},
email: email(),
name: name(),
},
custom_price: 100000,
product_options: {
description: "Hello World",
name: firstName(),
receipt_button_text: "Buy now",
receipt_link_url: "https://lemonsqueezy.com",
receipt_thank_you_note: "Thank you for your purchase",
redirect_url: "https://lemonsqueezy.com",
},
store: stores.data.at(0)!.id,
variant: variants.data.at(0)!.id,
});

expect(newCheckout).toBeDefined();
expect(newCheckout.data).toBeDefined();
expect(newCheckout.data).not.toBeNull();
expect(newCheckout.errors).toBeUndefined();
});

it("Retrieve checkout", async () => {
if (!newCheckout)
throw new Error(
"`newCheckout` is not defined. Check the `Create a checkout` test has run first."
);

const checkout = await retrieveCheckout({
apiKey,
id: newCheckout.data.id,
});

expect(checkout).toBeDefined();
expect(checkout.data).toBeDefined();
expect(checkout.data).not.toBeNull();
expect(checkout.errors).toBeUndefined();
});
});
128 changes: 104 additions & 24 deletions src/modules/checkout/checkout.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ export interface LemonsqueezyCheckoutData {
/**
* An object containing any custom data to be passed to the checkout
*/
custom: Array<any>;
custom?: Array<any>;
/**
* A pre-filled discount code
*/
discount_code: string;
discount_code?: string;
/**
* A pre-filled email address
*/
Expand All @@ -39,40 +39,57 @@ export interface LemonsqueezyCheckoutData {
/**
* A pre-filled tax number
*/
tax_number: string;
tax_number?: string;
}

export interface LemonsqueezyCheckoutOptions {
/**
* A custom hex color to use for the checkout button
*/
button_color: `#${string}`;
button_color?: `#${string}`;
/**
* If `true`, use the dark theme
*/
dark: boolean;
dark?: boolean;
/**
* If `false`, hide the product description
*/
desc: boolean;
desc?: boolean;
/**
* If `false`, hide the discount code field
*/
discount: boolean;
discount?: boolean;
/**
* If `true`, show the checkout overlay
*
* @docs https://docs.lemonsqueezy.com/help/checkout/checkout-overlay
*/
embed: boolean;
embed?: boolean;
/**
* If `false`, hide the store logo
*/
logo: boolean;
logo?: boolean;
/**
* If `false`, hide the product media
*/
media: boolean;
media?: boolean;
}

export interface LemonsqueezyCheckoutPreview {
currency_rate: number;
currency: string;
discount_total_formatted: string;
discount_total_usd: number;
discount_total: number;
subtotal_formatted: string;
subtotal_usd: number;
subtotal: number;
tax_formatted: string;
tax_usd: number;
tax: number;
total_formatted: string;
total_usd: number;
total: number;
}

export interface LemonsqueezyProductOptions {
Expand All @@ -83,11 +100,11 @@ export interface LemonsqueezyProductOptions {
/**
* An array of variant IDs to enable for this checkout. If this is empty, all variants will be enabled
*/
enabled_variants: Array<string>;
enabled_variants?: Array<string>;
/**
* An array of image URLs to use as the product's media
*/
media: Array<string>;
media?: Array<string>;
/**
* A custom name for the product
*/
Expand Down Expand Up @@ -144,6 +161,7 @@ export interface LemonsqueezyCheckout {
* @see https://en.wikipedia.org/wiki/ISO_8601
*/
expires_at: Date | null;
preview: LemonsqueezyCheckoutPreview;
/**
* An object containing any overridden product options for this checkout
*/
Expand Down Expand Up @@ -179,18 +197,80 @@ export interface LemonsqueezyCheckout {
id: string;
}

export interface CreateCheckoutOptions
extends SharedLemonsqueezyOptions,
Partial<
Pick<
LemonsqueezyCheckout["attributes"],
| "custom_price"
| "product_options"
| "checkout_options"
| "checkout_data"
| "expires_at"
>
> {}
export interface CreateCheckoutOptions extends SharedLemonsqueezyOptions {
/**
* An object containing any prefill or custom data to be used in the checkout
*
* @docs https://docs.lemonsqueezy.com/help/checkout/prefilling-checkout-fields
* @docs https://docs.lemonsqueezy.com/help/checkout/passing-custom-data
*/
checkout_data: LemonsqueezyCheckoutData;
/**
* An object containing checkout options for this checkout
*/
checkout_options?: LemonsqueezyCheckoutOptions;
/**
* A positive integer in cents representing the custom price of the variant.
*
* Note: If the product purchased is a subscription, this custom price is used
* for all renewal payments. If the subscription's variant changes in the
* future (i.e. the customer is moved to a different subscription "tier") the
* new variant's price will be used from that moment forward.
*/
custom_price: number;
/**
* An ISO-8601 formatted date-time string indicating when the checkout expires
*
* Can be `null` if the checkout is perpetual
*
* @see https://en.wikipedia.org/wiki/ISO_8601
*/
expires_at?: Date | null;
/**
* A boolean indicating whether to return a preview of the checkout.
*
* If `true`, the checkout will include a `preview` object with the checkout preview data.
*/
preview?: boolean;
/**
* An object containing any overridden product options for this checkout.
*/
product_options: LemonsqueezyProductOptions;
/**
* The ID of the store this checkout belongs to.
*/
store: string;
/**
* The ID of the variant associated with this checkout.
*
* Note: by default, all variants of the related product will be shown in the checkout, with
* your selected variant highlighted. If you want hide to other variants, you can utilise
* the `product_options.enabled_variants` option to determine which variant(s) are
* displayed in the checkout.
*/
variant: string;
}

export interface CreateCheckoutBody {
data: {
type: LemonsqueezyDataType.checkouts;
attributes: Omit<CreateCheckoutOptions, "store" | "variant">;
relationships: {
store: {
data: {
id: string;
type: LemonsqueezyDataType.stores;
};
};
variant: {
data: {
id: string;
type: LemonsqueezyDataType.variants;
};
};
};
};
}

export type CreateCheckoutResult =
BaseLemonsqueezyResponse<LemonsqueezyCheckout>;
Expand Down
1 change: 1 addition & 0 deletions src/modules/checkout/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export type {
LemonsqueezyCheckout,
LemonsqueezyCheckoutData,
LemonsqueezyCheckoutOptions,
LemonsqueezyCheckoutPreview,
LemonsqueezyProductOptions,
ListAllCheckoutsOptions,
ListAllCheckoutsResult,
Expand Down
2 changes: 1 addition & 1 deletion src/modules/discount/discount.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export interface LemonsqueezyDiscount {
updated_at: Date;
};
type: LemonsqueezyDataType.discounts;
id: string | number;
id: string;
}

export interface ListAllDiscountsOptions extends SharedLemonsqueezyOptions {
Expand Down
2 changes: 1 addition & 1 deletion src/modules/file/file.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export interface LemonsqueezyFile {
version: string;
};
type: LemonsqueezyDataType.files;
id: string | number;
id: string;
}

export interface ListAllFilesOptions extends SharedLemonsqueezyOptions {
Expand Down
2 changes: 1 addition & 1 deletion src/modules/licenseKey/licenseKey.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export interface LemonsqueezyLicenseKey {
user_name: string;
};
type: LemonsqueezyDataType.license_keys;
id: string | number;
id: string;
}

export interface ListAllLicenseKeysOptions extends SharedLemonsqueezyOptions {
Expand Down
2 changes: 1 addition & 1 deletion src/modules/licenseKeyInstance/licenseKeyInstance.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export interface LemonsqueezyLicenseKeyInstance {
updated_at: Date;
};
type: LemonsqueezyDataType.license_key_instances;
id: string | number;
id: string;
}

export interface ListAllLicenseKeyInstancesOptions
Expand Down
Loading