Skip to content

Commit

Permalink
feat: add generate subscription invoice (#94)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
keyding and brankoconjic authored Jun 10, 2024
1 parent 597fb05 commit bea7295
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changeset/khaki-ways-train.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@lemonsqueezy/lemonsqueezy.js": patch
---

Improve tests
5 changes: 5 additions & 0 deletions .changeset/silver-rings-love.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@lemonsqueezy/lemonsqueezy.js": minor
---

Add `generateOrderInvoice` and `generateSubscriptionInvoice`
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)

[![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)
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,15 @@ export {
export type {
SubscriptionInvoice,
ListSubscriptionInvoices,
GenerateSubscriptionInvoice,
GetSubscriptionInvoiceParams,
ListSubscriptionInvoicesParams,
GenerateSubscriptionInvoiceParams,
} from "./subscriptionInvoices/types";
export {
getSubscriptionInvoice,
listSubscriptionInvoices,
generateSubscriptionInvoice,
} from "./subscriptionInvoices";

// Subscription Items
Expand Down
34 changes: 34 additions & 0 deletions src/subscriptionInvoices/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ import {
convertIncludeToQueryString,
convertListParamsToQueryString,
requiredCheck,
convertKeys,
} from "../internal";
import type {
GetSubscriptionInvoiceParams,
ListSubscriptionInvoices,
ListSubscriptionInvoicesParams,
SubscriptionInvoice,
GenerateSubscriptionInvoiceParams,
GenerateSubscriptionInvoice,
} from "./types";

/**
Expand Down Expand Up @@ -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<string, any>
).toString();
const query = queryString ? `?${queryString}` : "";

return $fetch<GenerateSubscriptionInvoice>({
path: `/v1/subscription-invoices/${subscriptionInvoiceId}/generate-invoice${query}`,
method: "POST",
});
}
34 changes: 34 additions & 0 deletions src/subscriptionInvoices/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<SubscriptionInvoiceData, unknown, Pick<Links, "self">>,
"meta"
Expand All @@ -206,3 +236,7 @@ export type ListSubscriptionInvoices = LemonSqueezyResponse<
Pick<Meta, "page">,
Pick<Links, "first" | "last">
>;
export type GenerateSubscriptionInvoice = Pick<
LemonSqueezyResponse<unknown, Pick<Meta, "urls">, unknown>,
"meta" | "jsonapi"
>;
7 changes: 4 additions & 3 deletions src/types/response/meta.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import type { IntervalUnit } from "../index";

type MetaUrls = {
download_invoice: string;
};

type MetaPage = {
currentPage: number;
from: number;
Expand All @@ -8,9 +12,6 @@ type MetaPage = {
to: number;
total: number;
};
type MetaUrls = {
download_invoice: string;
};

export type Meta = {
test_mode: boolean;
Expand Down
1 change: 1 addition & 0 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ describe("Export", () => {
// Subscriptions Invoices
"getSubscriptionInvoice",
"listSubscriptionInvoices",
"generateSubscriptionInvoice",

// Subscriptions Items
"getSubscriptionItem",
Expand Down
76 changes: 72 additions & 4 deletions test/subscriptionInvoices/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
getSubscriptionInvoice,
lemonSqueezySetup,
listSubscriptionInvoices,
generateSubscriptionInvoice,
} from "../../src";
import { API_BASE_URL } from "../../src/internal";

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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."
);
}
});
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
});
});

0 comments on commit bea7295

Please sign in to comment.