From bea7295eb51a31bc0a6b62c1d34dd920f19d7fdd Mon Sep 17 00:00:00 2001 From: Caven Date: Tue, 11 Jun 2024 06:07:48 +0800 Subject: [PATCH] feat: add generate subscription invoice (#94) * feat(subscriptionInvoices): add generate subscription invoice * refactor: merge main into pr/94 * refactor: format * fix: remove private info * refactor: do not skip tests * refactor: remove comments from the JSON file * refactor: fix formatting * refactor: delete duplicate MetaUrls * docs: add changeset --------- Co-authored-by: Branko Conjic --- .changeset/khaki-ways-train.md | 5 ++ .changeset/silver-rings-love.md | 5 ++ README.md | 5 +- src/index.ts | 3 + src/subscriptionInvoices/index.ts | 34 +++++++++++ src/subscriptionInvoices/types.ts | 34 +++++++++++ src/types/response/meta.ts | 7 ++- test/index.test.ts | 1 + test/subscriptionInvoices/index.test.ts | 76 +++++++++++++++++++++++-- 9 files changed, 160 insertions(+), 10 deletions(-) create mode 100644 .changeset/khaki-ways-train.md create mode 100644 .changeset/silver-rings-love.md diff --git a/.changeset/khaki-ways-train.md b/.changeset/khaki-ways-train.md new file mode 100644 index 0000000..244259a --- /dev/null +++ b/.changeset/khaki-ways-train.md @@ -0,0 +1,5 @@ +--- +"@lemonsqueezy/lemonsqueezy.js": patch +--- + +Improve tests diff --git a/.changeset/silver-rings-love.md b/.changeset/silver-rings-love.md new file mode 100644 index 0000000..02ee984 --- /dev/null +++ b/.changeset/silver-rings-love.md @@ -0,0 +1,5 @@ +--- +"@lemonsqueezy/lemonsqueezy.js": minor +--- + +Add `generateOrderInvoice` and `generateSubscriptionInvoice` diff --git a/README.md b/README.md index a5f13fb..7fa5aaa 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,6 @@ This is the official JavaScript SDK for [Lemon Squeezy](https://lemonsqueezy.com), helping make it easy to incorporate billing into your JavaScript application. - Read [API Reference](https://docs.lemonsqueezy.com/api) to understand how the Lemon Squeezy API works. - - Visit [Wiki page](https://github.com/lmsqueezy/lemonsqueezy.js/wiki) for function usage. ## Features @@ -139,6 +138,6 @@ Do not use this package directly in the browser, as this will expose your API ke See the [Contributing Guide](https://github.com/lmsqueezy/lemonsqueezy.js/blob/main/CONTRIBUTING.md). - ## License -[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Flmsqueezy%2Flemonsqueezy.js.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Flmsqueezy%2Flemonsqueezy.js?ref=badge_large) \ No newline at end of file + +[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Flmsqueezy%2Flemonsqueezy.js.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Flmsqueezy%2Flemonsqueezy.js?ref=badge_large) diff --git a/src/index.ts b/src/index.ts index 0947301..ce4be32 100644 --- a/src/index.ts +++ b/src/index.ts @@ -107,12 +107,15 @@ export { export type { SubscriptionInvoice, ListSubscriptionInvoices, + GenerateSubscriptionInvoice, GetSubscriptionInvoiceParams, ListSubscriptionInvoicesParams, + GenerateSubscriptionInvoiceParams, } from "./subscriptionInvoices/types"; export { getSubscriptionInvoice, listSubscriptionInvoices, + generateSubscriptionInvoice, } from "./subscriptionInvoices"; // Subscription Items diff --git a/src/subscriptionInvoices/index.ts b/src/subscriptionInvoices/index.ts index e4dcb55..ae6933c 100644 --- a/src/subscriptionInvoices/index.ts +++ b/src/subscriptionInvoices/index.ts @@ -3,12 +3,15 @@ import { convertIncludeToQueryString, convertListParamsToQueryString, requiredCheck, + convertKeys, } from "../internal"; import type { GetSubscriptionInvoiceParams, ListSubscriptionInvoices, ListSubscriptionInvoicesParams, SubscriptionInvoice, + GenerateSubscriptionInvoiceParams, + GenerateSubscriptionInvoice, } from "./types"; /** @@ -51,3 +54,34 @@ export function listSubscriptionInvoices( path: `/v1/subscription-invoices${convertListParamsToQueryString(params)}`, }); } + +/** + * Generate subscription invoice. + * + * @param subscriptionInvoiceId The given subscription invoice id. + * @param [params] (Optional) Then given parameters. + * @param [params.name] (Optional) The full name of the customer. + * @param [params.address] (Optional) The street address of the customer. + * @param [params.city] (Optional) The city of the customer. + * @param [params.state] (Optional) The state of the customer. + * @param [params.zipCode] (Optional) The ZIP code of the customer. + * @param [params.country] (Optional) The country of the customer. + * @param [params.notes] (Optional) Any additional notes to include on the invoice. + * @returns A link to download the generated invoice. + */ +export function generateSubscriptionInvoice( + subscriptionInvoiceId: number | string, + params: GenerateSubscriptionInvoiceParams = {} +) { + requiredCheck({ subscriptionInvoiceId }); + const searchParams = convertKeys(params); + const queryString = new URLSearchParams( + searchParams as Record + ).toString(); + const query = queryString ? `?${queryString}` : ""; + + return $fetch({ + path: `/v1/subscription-invoices/${subscriptionInvoiceId}/generate-invoice${query}`, + method: "POST", + }); +} diff --git a/src/subscriptionInvoices/types.ts b/src/subscriptionInvoices/types.ts index eb4ad46..9744912 100644 --- a/src/subscriptionInvoices/types.ts +++ b/src/subscriptionInvoices/types.ts @@ -197,6 +197,36 @@ export type ListSubscriptionInvoicesParams = Params< subscriptionId?: string | number; } >; +export type GenerateSubscriptionInvoiceParams = { + /** + * Optional. The full name of the customer. + */ + name?: string; + /** + * Optional. The street address of the customer. + */ + address?: string; + /** + * Optional. The city of the customer. + */ + city?: string; + /** + * Optional. The state of the customer. + */ + state?: string; + /** + * Optional. The ZIP code of the customer. + */ + zipCode?: number; + /** + * Optional. The country of the customer. + */ + country?: string; + /** + * Optional. Any additional notes to include on the invoice. + */ + notes?: string; +}; export type SubscriptionInvoice = Omit< LemonSqueezyResponse>, "meta" @@ -206,3 +236,7 @@ export type ListSubscriptionInvoices = LemonSqueezyResponse< Pick, Pick >; +export type GenerateSubscriptionInvoice = Pick< + LemonSqueezyResponse, unknown>, + "meta" | "jsonapi" +>; diff --git a/src/types/response/meta.ts b/src/types/response/meta.ts index 5c7b25a..702efc1 100644 --- a/src/types/response/meta.ts +++ b/src/types/response/meta.ts @@ -1,5 +1,9 @@ import type { IntervalUnit } from "../index"; +type MetaUrls = { + download_invoice: string; +}; + type MetaPage = { currentPage: number; from: number; @@ -8,9 +12,6 @@ type MetaPage = { to: number; total: number; }; -type MetaUrls = { - download_invoice: string; -}; export type Meta = { test_mode: boolean; diff --git a/test/index.test.ts b/test/index.test.ts index afca9ac..110f986 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -55,6 +55,7 @@ describe("Export", () => { // Subscriptions Invoices "getSubscriptionInvoice", "listSubscriptionInvoices", + "generateSubscriptionInvoice", // Subscriptions Items "getSubscriptionItem", diff --git a/test/subscriptionInvoices/index.test.ts b/test/subscriptionInvoices/index.test.ts index a089f73..87dd5eb 100644 --- a/test/subscriptionInvoices/index.test.ts +++ b/test/subscriptionInvoices/index.test.ts @@ -3,6 +3,7 @@ import { getSubscriptionInvoice, lemonSqueezySetup, listSubscriptionInvoices, + generateSubscriptionInvoice, } from "../../src"; import { API_BASE_URL } from "../../src/internal"; @@ -143,7 +144,7 @@ describe("List all subscription invoices", () => { ).toEqual(data.length); }); - it("Should return a paginated list of subscription invoices with page_number = 1 and page_size = 5", async () => { + it("Should return a paginated list of subscription invoices with page_number = 1 and page_size = 5", async () => { const { error, data: _data, @@ -174,12 +175,13 @@ describe("List all subscription invoices", () => { }); describe("Retrieve a subscription invoice", () => { - it("Throw an error about a parameter that must be provided", async () => { + it("Should throw an error when `subscriptionInvoiceId` parameter is not provided", async () => { try { await getSubscriptionInvoice(""); } catch (error) { + expect(error).toBeInstanceOf(Error); expect((error as Error).message).toMatch( - "Please provide the required parameter:" + "Please provide the required parameter: subscriptionInvoiceId." ); } }); @@ -304,7 +306,7 @@ describe("Retrieve a subscription invoice", () => { ); expect(included).toBeArray(); expect( - !!included?.filter((item) => item.type === "subscriptions") + Boolean(included?.filter((item) => item.type === "subscriptions")) ).toBeTrue(); const { type, id, attributes, relationships } = data; @@ -392,3 +394,69 @@ describe("Retrieve a subscription invoice", () => { for (const item of relationshipItems) expect(item.links).toBeDefined(); }); }); + +describe("Generate subscription invoice", () => { + it("Throw an error about a parameter that must be provided", async () => { + try { + await generateSubscriptionInvoice(""); + } catch (error) { + expect((error as Error).message).toMatch( + "Please provide the required parameter:" + ); + } + }); + + it("Should returns a link with the given subscription invoice id", async () => { + const { + statusCode, + error, + data: _data, + } = await generateSubscriptionInvoice(subscriptionInvoiceId); + expect(statusCode).toEqual(200); + expect(error).toBeNull(); + expect(_data).toBeDefined(); + + const { meta } = _data!; + expect(meta).toBeDefined(); + expect(meta.urls).toBeDefined(); + expect(meta.urls.download_invoice).toStartWith( + "https://app.lemonsqueezy.com/my-orders/" + ); + }); + + it("Should returns a link with the given subscription invoice id and params", async () => { + const params = { + name: "John Doe", + address: "123 Main St", + city: "Anytown", + state: "CA", + country: "US", + zipCode: 12345, + notes: "Thank you for your business!", + }; + const { + statusCode, + error, + data: _data, + } = await generateSubscriptionInvoice(subscriptionInvoiceId, params); + expect(statusCode).toEqual(200); + expect(error).toBeNull(); + expect(_data).toBeDefined(); + + const { meta } = _data!; + expect(meta).toBeDefined(); + expect(meta.urls).toBeDefined(); + expect(meta.urls.download_invoice); + + const invoiceUrl = new URL(meta.urls.download_invoice); + const searchParams = invoiceUrl.searchParams; + + expect(searchParams.get("name")).toEqual(params.name); + expect(searchParams.get("address")).toEqual(params.address); + expect(searchParams.get("city")).toEqual(params.city); + expect(searchParams.get("state")).toEqual(params.state); + expect(searchParams.get("country")).toEqual(params.country); + expect(searchParams.get("zip_code")).toEqual(params.zipCode.toString()); + expect(searchParams.get("notes")).toEqual(params.notes); + }); +});