Skip to content

Commit a53f9bf

Browse files
authored
Merge pull request #119 from gadget-inc/usage-sub-ts
Usage sub TS migration
2 parents 9c269ff + a443f95 commit a53f9bf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+897
-1273
lines changed

shopify/usage-subscription-template/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,8 @@ sketch
206206
# .pnp.*
207207

208208
# Shopify
209-
shopify.app.toml
209+
extensions/**/dist
210+
extensions/**/node_modules
210211

211212
# Miscellaneous
212213
.gadget/

shopify/usage-subscription-template/.ignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Shopify
2-
shopify.app.toml
2+
extensions/**/dist
3+
extensions/**/node_modules
34

45
# Miscellaneous
56
**/.DS_*

shopify/usage-subscription-template/README-DEV.md

Lines changed: 0 additions & 58 deletions
This file was deleted.

shopify/usage-subscription-template/api/actions/billingPeriodTracking.js renamed to shopify/usage-subscription-template/api/actions/billingPeriodTracking.ts

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,21 @@
1-
import {
2-
ActionOptions,
3-
BillingPeriodTrackingGlobalActionContext,
4-
} from "gadget-server";
1+
import { ActionOptions } from "gadget-server";
52
import { DateTime } from "luxon";
63

74
/**
8-
* @param { BillingPeriodTrackingGlobalActionContext } context
9-
*
105
* Features of this global action:
116
* - Runs on a cron trigger (schedule) every 5 minutes
127
* - Paginating to fetch all shops where the billingPeriodEnd date has passed
138
* - Creates a usage charge for previous billing period overages
149
* - Sets the shop billing period information to track the next period
1510
*/
16-
export async function run({ params, logger, api, connections }) {
11+
export const run: ActionRun = async ({ params, logger, api, connections }) => {
1712
let shops = await api.shopifyShop.findMany({
1813
filter: {
1914
billingPeriodEnd: {
2015
lessThanOrEqual: new Date(),
2116
},
2217
state: {
23-
inState: "installed",
18+
inState: "created.installed",
2419
},
2520
planId: {
2621
isSet: true,
@@ -31,7 +26,6 @@ export async function run({ params, logger, api, connections }) {
3126
name: true,
3227
billingPeriodEnd: true,
3328
usagePlanId: true,
34-
currency: true,
3529
overage: true,
3630
activeSubscriptionId: true,
3731
plan: {
@@ -46,21 +40,23 @@ export async function run({ params, logger, api, connections }) {
4640

4741
while (shops.hasNextPage) {
4842
shops = await shops.nextPage();
49-
allShops = allShops.concat(shops);
43+
allShops.push(...shops);
5044
}
5145

5246
for (const shop of allShops) {
47+
if (!shop.name) continue;
48+
5349
await api.enqueue(
5450
api.chargeShop,
5551
{
5652
shop: {
5753
id: shop.id,
58-
currency: shop.currency,
5954
overage: shop.overage,
6055
activeSubscriptionId: shop.activeSubscriptionId,
6156
usagePlanId: shop.usagePlanId,
6257
plan: {
63-
price: shop.plan.pricePerOrder,
58+
price: shop?.plan?.pricePerOrder ?? 0,
59+
currency: shop?.plan?.currency ?? "CAD",
6460
},
6561
},
6662
},
@@ -75,18 +71,21 @@ export async function run({ params, logger, api, connections }) {
7571

7672
// Updating billing period information
7773
await api.internal.shopifyShop.update(shop.id, {
78-
billingPeriodStart: DateTime.fromJSDate(new Date(shop.billingPeriodEnd))
74+
billingPeriodStart: DateTime.fromJSDate(
75+
new Date(shop.billingPeriodEnd ?? new Date())
76+
)
7977
.plus({ milliseconds: 1 })
8078
.toJSDate(),
81-
billingPeriodEnd: DateTime.fromJSDate(new Date(shop.billingPeriodEnd))
79+
billingPeriodEnd: DateTime.fromJSDate(
80+
new Date(shop.billingPeriodEnd ?? new Date())
81+
)
8282
.plus({ days: 30 })
8383
.toJSDate(),
8484
});
8585
}
86-
}
86+
};
8787

88-
/** @type { ActionOptions } */
89-
export const options = {
88+
export const options: ActionOptions = {
9089
timeoutMS: 900000,
9190
triggers: {
9291
api: true,

shopify/usage-subscription-template/api/actions/chargeShop.js renamed to shopify/usage-subscription-template/api/actions/chargeShop.ts

Lines changed: 54 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,45 @@
1-
import { ActionOptions, ChargeShopGlobalActionContext } from "gadget-server";
1+
import { ActionOptions } from "gadget-server";
22
import { getCappedAmount } from "../../utilities";
3-
4-
/**
5-
* @param { ChargeShopGlobalActionContext } context
6-
*/
7-
export async function run({ params, logger, api, connections }) {
8-
const { shop, order } = params;
3+
import type { SubscriptionLineItems } from "../models/shopifyShop/actions/subscribe";
4+
5+
export const run: ActionRun = async ({ params, logger, api, connections }) => {
6+
const {
7+
shop,
8+
order,
9+
}: {
10+
shop?: {
11+
id?: string;
12+
overage?: number;
13+
activeSubscriptionId?: string;
14+
usagePlanId?: string;
15+
plan?: {
16+
price?: number;
17+
currency?: string;
18+
};
19+
};
20+
order?: {
21+
email?: string;
22+
};
23+
} = params;
24+
25+
if (!shop || !shop.id || !shop.activeSubscriptionId)
26+
throw new Error("Missing required shop data");
927

1028
// Creating an instance of the Shopify Admin API
11-
const shopify = await connections.shopify.forShopId(shop.id);
29+
const shopify = await connections.shopify.forShopId(shop?.id);
1230

1331
// Returning early if the shop is uninstalled and no Shopify instance is created
1432
if (!shopify)
1533
return logger.warn({
1634
message: "BILLING - Shop uninstalled",
17-
shopId: shop.id,
35+
shopId: shop?.id,
1836
});
1937

2038
// Fetching the amount used in the period
21-
const { amountUsedInPeriod } = await api.shopifyShop.findOne(shop.id, {
22-
amountUsedInPeriod: true,
39+
const { amountUsedInPeriod } = await api.shopifyShop.findOne(shop?.id, {
40+
select: {
41+
amountUsedInPeriod: true,
42+
},
2343
});
2444

2545
let remainder = 0;
@@ -37,31 +57,33 @@ export async function run({ params, logger, api, connections }) {
3757
if (!subscription)
3858
return logger.warn({
3959
message: "BILLING - No subscription found for the shop",
40-
shopId: shop.id,
60+
shopId: shop?.id,
4161
});
4262

4363
// Pulling out the capped amount from the active subscription
44-
const cappedAmount = getCappedAmount(subscription);
64+
const cappedAmount = getCappedAmount(
65+
subscription.lineItems as SubscriptionLineItems
66+
);
4567

4668
if (!cappedAmount)
4769
return logger.warn({
4870
message: "BILLING - No capped amount found for the shop",
49-
shopId: shop.id,
71+
shopId: shop?.id,
5072
});
5173

5274
// Initially setting the price to the plan price and adding the overage amount if they exist
53-
let price = shop.plan?.price + (shop.overage || 0) || 0;
75+
let price = shop?.plan?.price ?? 0 + (shop?.overage ?? 0);
5476

5577
// Returning early if the amount used in the period is greater than or equal to the capped amount
56-
if (amountUsedInPeriod >= cappedAmount) {
78+
if (amountUsedInPeriod && (amountUsedInPeriod as number) >= cappedAmount) {
5779
// Modifying the overage amount of the current shop
58-
return await api.internal.shopifyShop.update(shop.id, {
80+
return await api.internal.shopifyShop.update(shop?.id, {
5981
overage: price,
6082
});
6183
}
6284

6385
// Calculating the available amount
64-
const availableAmount = cappedAmount - amountUsedInPeriod;
86+
const availableAmount = cappedAmount - ((amountUsedInPeriod as number) ?? 0);
6587

6688
// Setting a remainder if the price is greater than the available amount
6789
if (price >= availableAmount) {
@@ -77,22 +99,21 @@ export async function run({ params, logger, api, connections }) {
7799
id
78100
}
79101
userErrors {
80-
field
81102
message
82103
}
83104
}
84105
}`,
85106
{
86-
description: shop.overage
87-
? `Charge of ${price} ${shop.currency} ${
88-
order.email ? `for order placed by ${order.email}` : ""
107+
description: shop?.overage
108+
? `Charge of ${price} ${shop?.plan?.currency} ${
109+
order?.email ? `for order placed by ${order?.email}` : ""
89110
}, with overages from the previous billing period`
90-
: `Charge of ${price} ${shop.currency} for order placed by ${order.email}`,
111+
: `Charge of ${price} ${shop?.plan?.currency} for order placed by ${order?.email}`,
91112
price: {
92113
amount: price,
93-
currencyCode: shop.currency,
114+
currencyCode: shop?.plan?.currency,
94115
},
95-
subscriptionLineItemId: shop.usagePlanId,
116+
subscriptionLineItemId: shop?.usagePlanId,
96117
}
97118
);
98119

@@ -104,21 +125,20 @@ export async function run({ params, logger, api, connections }) {
104125
await api.internal.usageRecord.create({
105126
id: result.appUsageRecordCreate.appUsageRecord.id.split("/")[4],
106127
price,
107-
currency: shop.currency,
128+
currency: shop?.plan?.currency,
108129
shop: {
109-
_link: shop.id,
130+
_link: shop?.id,
110131
},
111132
});
112133

113134
// Updating the overage amount if there is a remainder
114135
if (remainder)
115-
await api.internal.shopifyShop.update(shop.id, {
136+
await api.internal.shopifyShop.update(shop?.id, {
116137
overage: remainder,
117138
});
118-
}
139+
};
119140

120-
/** @type { ActionOptions } */
121-
export const options = {};
141+
export const options: ActionOptions = {};
122142

123143
export const params = {
124144
shop: {
@@ -133,9 +153,6 @@ export const params = {
133153
usagePlanId: {
134154
type: "string",
135155
},
136-
currency: {
137-
type: "string",
138-
},
139156
overage: {
140157
type: "number",
141158
},
@@ -145,6 +162,9 @@ export const params = {
145162
price: {
146163
type: "number",
147164
},
165+
currency: {
166+
type: "string",
167+
},
148168
},
149169
},
150170
},

0 commit comments

Comments
 (0)