Stripe synchronizer extension (#15515)
Challenge 7 from "Call for projects" list
This commit is contained in:
+942
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
||||
yarnPath: .yarn/releases/yarn-4.9.2.cjs
|
||||
|
||||
nodeLinker: node-modules
|
||||
@@ -0,0 +1,28 @@
|
||||
# Stripe synchronizer
|
||||
|
||||
Synchronizes customers from Stripe to Twenty
|
||||
|
||||
## Requirements
|
||||
- twenty-cli `npm install -g twenty-cli`
|
||||
- an `apiKey`. Go to `https://twenty.com/settings/api-webhooks` to generate one
|
||||
-
|
||||
|
||||
## Setup
|
||||
1. Synchronize app
|
||||
```bash
|
||||
twenty auth login
|
||||
cd stripe-synchronizer
|
||||
twenty app sync
|
||||
```
|
||||
2. Go to Settings > Integrations > Stripe synchronizer > Settings and add values
|
||||
|
||||
## Flow
|
||||
1. Retrieve webhook
|
||||
2. Check if it's either subscription created or updated
|
||||
3. Read customer ID, sub status and quantity
|
||||
4. Read customer data
|
||||
5. Check if customer company exists in Twenty, if not, create it
|
||||
6. Check if related person exists in Twenty, if not, create it and link to company
|
||||
|
||||
## Todo
|
||||
- add validation of signature key from Stripe to ensure that incoming request is valid
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"version": "0.0.1",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^24.5.0",
|
||||
"npm": "please-use-yarn",
|
||||
"yarn": ">=4.0.2"
|
||||
},
|
||||
"packageManager": "yarn@4.9.2",
|
||||
"$schema": "https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-cli/schemas/appManifest.schema.json",
|
||||
"universalIdentifier": "0ed2bcb8-64ab-4ca1-b875-eeabf41b5f95",
|
||||
"name": "stripe-synchronizer",
|
||||
"description": "Plugin synchronizing data from Stripe to Twenty",
|
||||
"env": {
|
||||
"TWENTY_API_KEY": {
|
||||
"isSecret": true,
|
||||
"value": "",
|
||||
"description": "Required to send requests to Twenty"
|
||||
},
|
||||
"TWENTY_API_URL": {
|
||||
"isSecret": false,
|
||||
"value": "",
|
||||
"description": "Optional, defaults to cloud API URL"
|
||||
},
|
||||
"STRIPE_API_KEY": {
|
||||
"isSecret": true,
|
||||
"value": "",
|
||||
"description": "Required to send request to Stripe"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.13.1"
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-cli/schemas/serverlessFunction.schema.json",
|
||||
"universalIdentifier": "cd15a738-18a5-406e-8b83-959dc52ebe14",
|
||||
"name": "stripe",
|
||||
"triggers": [
|
||||
{
|
||||
"universalIdentifier": "55f58e19-d832-43c4-9f8b-3f29fc05c162",
|
||||
"type": "route",
|
||||
"path": "/stripe",
|
||||
"httpMethod": "POST",
|
||||
"isAuthRequired": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
import axios from 'axios';
|
||||
|
||||
const TWENTY_API_KEY: string = process.env.TWENTY_API_KEY ?? '';
|
||||
const TWENTY_API_URL: string =
|
||||
process.env.TWENTY_API_URL !== '' && process.env.TWENTY_API_URL !== undefined
|
||||
? `${process.env.TWENTY_API_URL}/rest`
|
||||
: 'https://api.twenty.com/rest';
|
||||
const STRIPE_API_KEY: string = process.env.STRIPE_API_KEY ?? '';
|
||||
const STRIPE_API_URL: string = 'https://api.stripe.com/v1/customers';
|
||||
|
||||
enum stripeStatus {
|
||||
Incomplete = 'INCOMPLETE',
|
||||
IncompleteExpired = 'INCOMPLETE_EXPIRED',
|
||||
Trialing = 'TRIALING',
|
||||
Active = 'ACTIVE',
|
||||
PastDue = 'PAST_DUE',
|
||||
Canceled = 'CANCELED',
|
||||
Unpaid = 'UNPAID',
|
||||
Paused = 'PAUSED',
|
||||
}
|
||||
|
||||
type stripeData = {
|
||||
quantity: number;
|
||||
}
|
||||
|
||||
type stripeItems = {
|
||||
data: stripeData[];
|
||||
};
|
||||
|
||||
type stripeResponse = {
|
||||
customer: string,
|
||||
items: stripeItems;
|
||||
status: stripeStatus;
|
||||
type: string;
|
||||
};
|
||||
|
||||
type stripeCustomer = {
|
||||
businessName: string;
|
||||
}
|
||||
|
||||
type twentyObject = {
|
||||
id: string;
|
||||
nameSingular: string;
|
||||
fields: Record<string, any>[];
|
||||
}
|
||||
|
||||
const getCompaniesObject = async (): Promise<twentyObject> => {
|
||||
const options = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${TWENTY_API_KEY}`,
|
||||
},
|
||||
url: `${TWENTY_API_URL}/metadata/objects`,
|
||||
};
|
||||
try {
|
||||
const response = await axios.request(options);
|
||||
if (response.status === 200) {
|
||||
const companyObject = response.data.data.objects.find(
|
||||
(object: twentyObject) => object.nameSingular === 'company',
|
||||
);
|
||||
return companyObject as twentyObject ?? {} as twentyObject;
|
||||
}
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error)) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const createFields = async (objectId: string, fieldName: string) => {
|
||||
const data =
|
||||
fieldName === 'seats'
|
||||
? {
|
||||
type: 'NUMBER',
|
||||
objectMetadataId: objectId,
|
||||
name: 'seats',
|
||||
label: 'Seats',
|
||||
}
|
||||
: {
|
||||
type: 'SELECT',
|
||||
objectMetadataId: objectId,
|
||||
name: 'subStatus',
|
||||
label: 'Sub Status',
|
||||
options: [
|
||||
{
|
||||
color: 'iris',
|
||||
label: 'Incomplete',
|
||||
value: stripeStatus.Incomplete,
|
||||
position: 1,
|
||||
},
|
||||
{
|
||||
color: 'sky',
|
||||
label: 'Incomplete (expired)',
|
||||
value: stripeStatus.IncompleteExpired,
|
||||
position: 2,
|
||||
},
|
||||
{
|
||||
color: 'amber',
|
||||
label: 'Trialing',
|
||||
value: stripeStatus.Trialing,
|
||||
position: 3,
|
||||
},
|
||||
{
|
||||
color: 'green',
|
||||
label: 'Active',
|
||||
value: stripeStatus.Active,
|
||||
position: 4,
|
||||
},
|
||||
{
|
||||
color: 'orange',
|
||||
label: 'Past due',
|
||||
value: stripeStatus.PastDue,
|
||||
position: 5,
|
||||
},
|
||||
{
|
||||
color: 'brown',
|
||||
label: 'Canceled',
|
||||
value: stripeStatus.Canceled,
|
||||
position: 6,
|
||||
},
|
||||
{
|
||||
color: 'red',
|
||||
label: 'Unpaid',
|
||||
value: stripeStatus.Unpaid,
|
||||
position: 7,
|
||||
},
|
||||
{
|
||||
color: 'gray',
|
||||
label: 'Paused',
|
||||
value: stripeStatus.Paused,
|
||||
position: 8,
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${TWENTY_API_KEY}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
url: `${TWENTY_API_URL}/metadata/fields`,
|
||||
data: data,
|
||||
};
|
||||
try {
|
||||
const response = await axios(options);
|
||||
return response.status === 201;
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error)) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getStripeCustomerData = async (customerID: string): Promise<stripeCustomer> => {
|
||||
const options = {
|
||||
method: 'GET',
|
||||
url: `${STRIPE_API_URL}/${customerID}`,
|
||||
auth: {
|
||||
username: STRIPE_API_KEY,
|
||||
password: '',
|
||||
},
|
||||
};
|
||||
try {
|
||||
const response = await axios(options);
|
||||
return response.status === 200 ? response.data as stripeCustomer : {} as stripeCustomer ;
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error)) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const checkIfCompanyExistsInTwenty = async (name: string) => {
|
||||
const options = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${TWENTY_API_KEY}`,
|
||||
},
|
||||
url: `${TWENTY_API_URL}/companies?filter=name%5Beq%5D%3A%22${name}%22`,
|
||||
};
|
||||
try {
|
||||
const response = await axios(options);
|
||||
return response.status === 200 && response.data.data.companies.length > 0
|
||||
? response.data.data.companies[0]
|
||||
: {};
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error)) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const updateTwentyCompany = async (
|
||||
companyId: string,
|
||||
seats: number,
|
||||
subStatus: stripeStatus,
|
||||
): Promise<boolean> => {
|
||||
const options = {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
Authorization: `Bearer ${TWENTY_API_KEY}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
url: `${TWENTY_API_URL}/companies/${companyId}`,
|
||||
data: {
|
||||
seats: seats,
|
||||
subStatus: subStatus,
|
||||
},
|
||||
};
|
||||
try {
|
||||
const response = await axios(options);
|
||||
return response.status === 200;
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error)) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const createTwentyCustomer = async (
|
||||
customerName: string,
|
||||
seats: number,
|
||||
subStatus: string,
|
||||
) => {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${TWENTY_API_KEY}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
url: `${TWENTY_API_URL}/companies`,
|
||||
data: {
|
||||
name: customerName,
|
||||
seats: seats,
|
||||
subStatus: subStatus,
|
||||
},
|
||||
};
|
||||
try {
|
||||
const response = await axios(options);
|
||||
return response.status === 201 ? response.data : {};
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error)) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const main = async (params: {
|
||||
properties: unknown;
|
||||
}): Promise<object> => {
|
||||
if (TWENTY_API_KEY === '' || STRIPE_API_KEY === '') {
|
||||
console.warn('Missing variables');
|
||||
return {};
|
||||
}
|
||||
|
||||
try { // TODO: add validation of signature key from Stripe
|
||||
const { properties } = params;
|
||||
const stripe = properties as stripeResponse;
|
||||
const allowed_types = [
|
||||
'customer.subscription.created',
|
||||
'customer.subscription.updated',
|
||||
];
|
||||
if (!allowed_types.includes(stripe.type)) {
|
||||
console.error('Wrong webhook');
|
||||
return {};
|
||||
}
|
||||
|
||||
const companyObject = await getCompaniesObject();
|
||||
if (
|
||||
companyObject.fields.find((field) => field.name === 'seats') === undefined
|
||||
) {
|
||||
const t: boolean = await createFields(companyObject.id, 'seats');
|
||||
if (t == false) {
|
||||
console.error('Seats field creation failed');
|
||||
return {};
|
||||
}
|
||||
}
|
||||
if (
|
||||
companyObject.fields.find((field) => field.name === 'subStatus') ===
|
||||
undefined
|
||||
) {
|
||||
const t: boolean = await createFields(companyObject.id, 'subStatus');
|
||||
if (t == false) {
|
||||
console.error('Sub status field creation failed');
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const stripeCustomer: stripeCustomer = await getStripeCustomerData(
|
||||
stripe.customer,
|
||||
);
|
||||
if (stripeCustomer.businessName) {
|
||||
console.warn('Set customer business name in Stripe');
|
||||
return {};
|
||||
}
|
||||
const twentyCustomer = await checkIfCompanyExistsInTwenty(
|
||||
stripeCustomer.businessName,
|
||||
);
|
||||
if (Object.keys(twentyCustomer).length === 0) {
|
||||
const a = await createTwentyCustomer(
|
||||
stripeCustomer.businessName,
|
||||
stripe.items.data[0].quantity,
|
||||
stripe.status.toUpperCase(),
|
||||
);
|
||||
if (Object.keys(a).length === 0) {
|
||||
console.error('Creation of Stripe customer in Twenty failed');
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
const a = await updateTwentyCompany(
|
||||
twentyCustomer.id,
|
||||
stripe.items.data[0].quantity,
|
||||
stripe.status.toUpperCase() as stripeStatus,
|
||||
);
|
||||
if (!a) {
|
||||
console.error('Update of Stripe customer in Twenty failed');
|
||||
return {};
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error)) {
|
||||
console.error(error.message);
|
||||
}
|
||||
console.error(error);
|
||||
return {};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,248 @@
|
||||
# This file is generated by running "yarn install" inside your project.
|
||||
# Manual changes might be lost - proceed with caution!
|
||||
|
||||
__metadata:
|
||||
version: 8
|
||||
cacheKey: 10c0
|
||||
|
||||
"async-function@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "async-function@npm:1.0.0"
|
||||
checksum: 10c0/669a32c2cb7e45091330c680e92eaeb791bc1d4132d827591e499cd1f776ff5a873e77e5f92d0ce795a8d60f10761dec9ddfe7225a5de680f5d357f67b1aac73
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"async-generator-function@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "async-generator-function@npm:1.0.0"
|
||||
checksum: 10c0/2c50ef856c543ad500d8d8777d347e3c1ba623b93e99c9263ecc5f965c1b12d2a140e2ab6e43c3d0b85366110696f28114649411cbcd10b452a92a2318394186
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"asynckit@npm:^0.4.0":
|
||||
version: 0.4.0
|
||||
resolution: "asynckit@npm:0.4.0"
|
||||
checksum: 10c0/d73e2ddf20c4eb9337e1b3df1a0f6159481050a5de457c55b14ea2e5cb6d90bb69e004c9af54737a5ee0917fcf2c9e25de67777bbe58261847846066ba75bc9d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"axios@npm:^1.13.1":
|
||||
version: 1.13.1
|
||||
resolution: "axios@npm:1.13.1"
|
||||
dependencies:
|
||||
follow-redirects: "npm:^1.15.6"
|
||||
form-data: "npm:^4.0.4"
|
||||
proxy-from-env: "npm:^1.1.0"
|
||||
checksum: 10c0/de9c3c6de43d3ee1146d3afe78645f19450cac6a5d7235bef8b8e8eeb705c2e47e2d231dea99cecaec4dae1897c521118ca9413b9d474063c719c4d94c5b9adc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"call-bind-apply-helpers@npm:^1.0.1, call-bind-apply-helpers@npm:^1.0.2":
|
||||
version: 1.0.2
|
||||
resolution: "call-bind-apply-helpers@npm:1.0.2"
|
||||
dependencies:
|
||||
es-errors: "npm:^1.3.0"
|
||||
function-bind: "npm:^1.1.2"
|
||||
checksum: 10c0/47bd9901d57b857590431243fea704ff18078b16890a6b3e021e12d279bbf211d039155e27d7566b374d49ee1f8189344bac9833dec7a20cdec370506361c938
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"combined-stream@npm:^1.0.8":
|
||||
version: 1.0.8
|
||||
resolution: "combined-stream@npm:1.0.8"
|
||||
dependencies:
|
||||
delayed-stream: "npm:~1.0.0"
|
||||
checksum: 10c0/0dbb829577e1b1e839fa82b40c07ffaf7de8a09b935cadd355a73652ae70a88b4320db322f6634a4ad93424292fa80973ac6480986247f1734a1137debf271d5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"delayed-stream@npm:~1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "delayed-stream@npm:1.0.0"
|
||||
checksum: 10c0/d758899da03392e6712f042bec80aa293bbe9e9ff1b2634baae6a360113e708b91326594c8a486d475c69d6259afb7efacdc3537bfcda1c6c648e390ce601b19
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dunder-proto@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "dunder-proto@npm:1.0.1"
|
||||
dependencies:
|
||||
call-bind-apply-helpers: "npm:^1.0.1"
|
||||
es-errors: "npm:^1.3.0"
|
||||
gopd: "npm:^1.2.0"
|
||||
checksum: 10c0/199f2a0c1c16593ca0a145dbf76a962f8033ce3129f01284d48c45ed4e14fea9bbacd7b3610b6cdc33486cef20385ac054948fefc6272fcce645c09468f93031
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"es-define-property@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "es-define-property@npm:1.0.1"
|
||||
checksum: 10c0/3f54eb49c16c18707949ff25a1456728c883e81259f045003499efba399c08bad00deebf65cccde8c0e07908c1a225c9d472b7107e558f2a48e28d530e34527c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"es-errors@npm:^1.3.0":
|
||||
version: 1.3.0
|
||||
resolution: "es-errors@npm:1.3.0"
|
||||
checksum: 10c0/0a61325670072f98d8ae3b914edab3559b6caa980f08054a3b872052640d91da01d38df55df797fcc916389d77fc92b8d5906cf028f4db46d7e3003abecbca85
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"es-object-atoms@npm:^1.0.0, es-object-atoms@npm:^1.1.1":
|
||||
version: 1.1.1
|
||||
resolution: "es-object-atoms@npm:1.1.1"
|
||||
dependencies:
|
||||
es-errors: "npm:^1.3.0"
|
||||
checksum: 10c0/65364812ca4daf48eb76e2a3b7a89b3f6a2e62a1c420766ce9f692665a29d94fe41fe88b65f24106f449859549711e4b40d9fb8002d862dfd7eb1c512d10be0c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"es-set-tostringtag@npm:^2.1.0":
|
||||
version: 2.1.0
|
||||
resolution: "es-set-tostringtag@npm:2.1.0"
|
||||
dependencies:
|
||||
es-errors: "npm:^1.3.0"
|
||||
get-intrinsic: "npm:^1.2.6"
|
||||
has-tostringtag: "npm:^1.0.2"
|
||||
hasown: "npm:^2.0.2"
|
||||
checksum: 10c0/ef2ca9ce49afe3931cb32e35da4dcb6d86ab02592cfc2ce3e49ced199d9d0bb5085fc7e73e06312213765f5efa47cc1df553a6a5154584b21448e9fb8355b1af
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"follow-redirects@npm:^1.15.6":
|
||||
version: 1.15.11
|
||||
resolution: "follow-redirects@npm:1.15.11"
|
||||
peerDependenciesMeta:
|
||||
debug:
|
||||
optional: true
|
||||
checksum: 10c0/d301f430542520a54058d4aeeb453233c564aaccac835d29d15e050beb33f339ad67d9bddbce01739c5dc46a6716dbe3d9d0d5134b1ca203effa11a7ef092343
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"form-data@npm:^4.0.4":
|
||||
version: 4.0.4
|
||||
resolution: "form-data@npm:4.0.4"
|
||||
dependencies:
|
||||
asynckit: "npm:^0.4.0"
|
||||
combined-stream: "npm:^1.0.8"
|
||||
es-set-tostringtag: "npm:^2.1.0"
|
||||
hasown: "npm:^2.0.2"
|
||||
mime-types: "npm:^2.1.12"
|
||||
checksum: 10c0/373525a9a034b9d57073e55eab79e501a714ffac02e7a9b01be1c820780652b16e4101819785e1e18f8d98f0aee866cc654d660a435c378e16a72f2e7cac9695
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"function-bind@npm:^1.1.2":
|
||||
version: 1.1.2
|
||||
resolution: "function-bind@npm:1.1.2"
|
||||
checksum: 10c0/d8680ee1e5fcd4c197e4ac33b2b4dce03c71f4d91717292785703db200f5c21f977c568d28061226f9b5900cbcd2c84463646134fd5337e7925e0942bc3f46d5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"generator-function@npm:^2.0.0":
|
||||
version: 2.0.1
|
||||
resolution: "generator-function@npm:2.0.1"
|
||||
checksum: 10c0/8a9f59df0f01cfefafdb3b451b80555e5cf6d76487095db91ac461a0e682e4ff7a9dbce15f4ecec191e53586d59eece01949e05a4b4492879600bbbe8e28d6b8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-intrinsic@npm:^1.2.6":
|
||||
version: 1.3.1
|
||||
resolution: "get-intrinsic@npm:1.3.1"
|
||||
dependencies:
|
||||
async-function: "npm:^1.0.0"
|
||||
async-generator-function: "npm:^1.0.0"
|
||||
call-bind-apply-helpers: "npm:^1.0.2"
|
||||
es-define-property: "npm:^1.0.1"
|
||||
es-errors: "npm:^1.3.0"
|
||||
es-object-atoms: "npm:^1.1.1"
|
||||
function-bind: "npm:^1.1.2"
|
||||
generator-function: "npm:^2.0.0"
|
||||
get-proto: "npm:^1.0.1"
|
||||
gopd: "npm:^1.2.0"
|
||||
has-symbols: "npm:^1.1.0"
|
||||
hasown: "npm:^2.0.2"
|
||||
math-intrinsics: "npm:^1.1.0"
|
||||
checksum: 10c0/9f4ab0cf7efe0fd2c8185f52e6f637e708f3a112610c88869f8f041bb9ecc2ce44bf285dfdbdc6f4f7c277a5b88d8e94a432374d97cca22f3de7fc63795deb5d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-proto@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "get-proto@npm:1.0.1"
|
||||
dependencies:
|
||||
dunder-proto: "npm:^1.0.1"
|
||||
es-object-atoms: "npm:^1.0.0"
|
||||
checksum: 10c0/9224acb44603c5526955e83510b9da41baf6ae73f7398875fba50edc5e944223a89c4a72b070fcd78beb5f7bdda58ecb6294adc28f7acfc0da05f76a2399643c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"gopd@npm:^1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "gopd@npm:1.2.0"
|
||||
checksum: 10c0/50fff1e04ba2b7737c097358534eacadad1e68d24cccee3272e04e007bed008e68d2614f3987788428fd192a5ae3889d08fb2331417e4fc4a9ab366b2043cead
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"has-symbols@npm:^1.0.3, has-symbols@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "has-symbols@npm:1.1.0"
|
||||
checksum: 10c0/dde0a734b17ae51e84b10986e651c664379018d10b91b6b0e9b293eddb32f0f069688c841fb40f19e9611546130153e0a2a48fd7f512891fb000ddfa36f5a20e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"has-tostringtag@npm:^1.0.2":
|
||||
version: 1.0.2
|
||||
resolution: "has-tostringtag@npm:1.0.2"
|
||||
dependencies:
|
||||
has-symbols: "npm:^1.0.3"
|
||||
checksum: 10c0/a8b166462192bafe3d9b6e420a1d581d93dd867adb61be223a17a8d6dad147aa77a8be32c961bb2f27b3ef893cae8d36f564ab651f5e9b7938ae86f74027c48c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"hasown@npm:^2.0.2":
|
||||
version: 2.0.2
|
||||
resolution: "hasown@npm:2.0.2"
|
||||
dependencies:
|
||||
function-bind: "npm:^1.1.2"
|
||||
checksum: 10c0/3769d434703b8ac66b209a4cca0737519925bbdb61dd887f93a16372b14694c63ff4e797686d87c90f08168e81082248b9b028bad60d4da9e0d1148766f56eb9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"math-intrinsics@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "math-intrinsics@npm:1.1.0"
|
||||
checksum: 10c0/7579ff94e899e2f76ab64491d76cf606274c874d8f2af4a442c016bd85688927fcfca157ba6bf74b08e9439dc010b248ce05b96cc7c126a354c3bae7fcb48b7f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mime-db@npm:1.52.0":
|
||||
version: 1.52.0
|
||||
resolution: "mime-db@npm:1.52.0"
|
||||
checksum: 10c0/0557a01deebf45ac5f5777fe7740b2a5c309c6d62d40ceab4e23da9f821899ce7a900b7ac8157d4548ddbb7beffe9abc621250e6d182b0397ec7f10c7b91a5aa
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mime-types@npm:^2.1.12":
|
||||
version: 2.1.35
|
||||
resolution: "mime-types@npm:2.1.35"
|
||||
dependencies:
|
||||
mime-db: "npm:1.52.0"
|
||||
checksum: 10c0/82fb07ec56d8ff1fc999a84f2f217aa46cb6ed1033fefaabd5785b9a974ed225c90dc72fff460259e66b95b73648596dbcc50d51ed69cdf464af2d237d3149b2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"proxy-from-env@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "proxy-from-env@npm:1.1.0"
|
||||
checksum: 10c0/fe7dd8b1bdbbbea18d1459107729c3e4a2243ca870d26d34c2c1bcd3e4425b7bcc5112362df2d93cc7fb9746f6142b5e272fd1cc5c86ddf8580175186f6ad42b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"stripe-synchronizer@workspace:.":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "stripe-synchronizer@workspace:."
|
||||
dependencies:
|
||||
axios: "npm:^1.13.1"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
Reference in New Issue
Block a user