@stacksjs/payments 0.70.88 → 0.70.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/billable/charge.d.ts +9 -0
- package/dist/billable/charge.js +64 -0
- package/dist/billable/checkout.d.ts +6 -0
- package/dist/billable/checkout.js +43 -0
- package/dist/billable/customer.d.ts +15 -0
- package/dist/billable/customer.js +117 -0
- package/dist/billable/index.d.ts +13 -0
- package/dist/billable/index.js +12 -0
- package/dist/billable/intent.d.ts +6 -0
- package/dist/billable/intent.js +18 -0
- package/dist/billable/invoice.d.ts +6 -0
- package/dist/billable/invoice.js +14 -0
- package/dist/billable/payment-method.d.ts +18 -0
- package/dist/billable/payment-method.js +119 -0
- package/dist/billable/price.d.ts +6 -0
- package/dist/billable/price.js +19 -0
- package/dist/billable/product.d.ts +37 -0
- package/dist/billable/product.js +101 -0
- package/dist/billable/setup-products.d.ts +2 -0
- package/dist/billable/setup-products.js +34 -0
- package/dist/billable/subscription.d.ts +12 -0
- package/dist/billable/subscription.js +138 -0
- package/dist/billable/transaction.d.ts +14 -0
- package/dist/billable/transaction.js +25 -0
- package/dist/billable/webhook.d.ts +155 -0
- package/dist/billable/webhook.js +153 -0
- package/dist/drivers/stripe.d.ts +8 -0
- package/dist/drivers/stripe.js +25 -0
- package/dist/idempotency.d.ts +26 -0
- package/dist/idempotency.js +13 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +5 -0
- package/dist/payment.d.ts +182 -0
- package/dist/payment.js +199 -0
- package/dist/stripe.backup.d.ts +1 -0
- package/dist/stripe.backup.js +0 -0
- package/package.json +3 -3
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { UserModel } from '@stacksjs/orm';
|
|
2
|
+
import type Stripe from 'stripe';
|
|
3
|
+
export declare const manageCharge: ManageCharge;
|
|
4
|
+
export declare interface ManageCharge {
|
|
5
|
+
createPayment: (user: UserModel, amount: number, options: Stripe.PaymentIntentCreateParams) => Promise<Stripe.Response<Stripe.PaymentIntent>>
|
|
6
|
+
findPayment: (id: string) => Promise<Stripe.PaymentIntent | null>
|
|
7
|
+
refund: (paymentIntentId: string, options?: Stripe.RefundCreateParams) => Promise<Stripe.Response<Stripe.Refund>>
|
|
8
|
+
charge: (user: UserModel, amount: number, paymentMethod: string, options: Stripe.PaymentIntentCreateParams) => Promise<Stripe.Response<Stripe.PaymentIntent>>
|
|
9
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { config } from "@stacksjs/config";
|
|
2
|
+
import { log } from "@stacksjs/logging";
|
|
3
|
+
import { stripe } from "..";
|
|
4
|
+
import { freshIdempotencyKey } from "../idempotency";
|
|
5
|
+
function defaultCurrency() {
|
|
6
|
+
const cfg = config || {};
|
|
7
|
+
return (cfg.payment?.currency || cfg.billing?.currency || process.env.STRIPE_CURRENCY || "usd").toLowerCase();
|
|
8
|
+
}
|
|
9
|
+
export const manageCharge = (() => {
|
|
10
|
+
async function createPayment(user, amount, options) {
|
|
11
|
+
const defaultOptions = {
|
|
12
|
+
currency: defaultCurrency(),
|
|
13
|
+
amount
|
|
14
|
+
};
|
|
15
|
+
if (user.hasStripeId())
|
|
16
|
+
defaultOptions.customer = user.stripe_id ?? void 0;
|
|
17
|
+
const mergedOptions = { ...defaultOptions, ...options };
|
|
18
|
+
return await stripe.paymentIntents.create(mergedOptions, {
|
|
19
|
+
idempotencyKey: freshIdempotencyKey("payment_intent.create", user.id, amount)
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
async function findPayment(id) {
|
|
23
|
+
try {
|
|
24
|
+
return await stripe.paymentIntents.retrieve(id);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
log.error(error);
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function refund(paymentIntentId, options = {}) {
|
|
31
|
+
if (!paymentIntentId || typeof paymentIntentId !== "string")
|
|
32
|
+
throw Error("[payments/refund] paymentIntentId is required");
|
|
33
|
+
if (options.amount != null) {
|
|
34
|
+
if (typeof options.amount !== "number" || !Number.isFinite(options.amount) || options.amount <= 0)
|
|
35
|
+
throw Error("[payments/refund] amount must be a positive finite number");
|
|
36
|
+
try {
|
|
37
|
+
const original = await stripe.paymentIntents.retrieve(paymentIntentId), captured = original.amount_received ?? original.amount;
|
|
38
|
+
if (captured && options.amount > captured)
|
|
39
|
+
throw Error(`[payments/refund] amount ${options.amount} exceeds captured amount ${captured}`);
|
|
40
|
+
} catch (err) {
|
|
41
|
+
if (err instanceof Error && err.message.startsWith("[payments/refund]"))
|
|
42
|
+
throw err;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const { idempotencyKey, ...rest } = options, refundParams = {
|
|
46
|
+
payment_intent: paymentIntentId,
|
|
47
|
+
...rest
|
|
48
|
+
}, requestOptions = {
|
|
49
|
+
idempotencyKey: idempotencyKey || `refund:${paymentIntentId}:${rest.amount ?? "full"}`
|
|
50
|
+
};
|
|
51
|
+
return await stripe.refunds.create(refundParams, requestOptions);
|
|
52
|
+
}
|
|
53
|
+
async function charge(user, amount, paymentMethod, options) {
|
|
54
|
+
const mergedOptions = { ...{
|
|
55
|
+
confirmation_method: "automatic",
|
|
56
|
+
confirm: !0,
|
|
57
|
+
payment_method: paymentMethod,
|
|
58
|
+
currency: defaultCurrency(),
|
|
59
|
+
amount
|
|
60
|
+
}, ...options };
|
|
61
|
+
return await createPayment(user, amount, mergedOptions);
|
|
62
|
+
}
|
|
63
|
+
return { createPayment, charge, findPayment, refund };
|
|
64
|
+
})();
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { UserModel } from '@stacksjs/orm';
|
|
2
|
+
import type Stripe from 'stripe';
|
|
3
|
+
export declare const manageCheckout: Checkout;
|
|
4
|
+
export declare interface Checkout {
|
|
5
|
+
create: (user: UserModel, params: Stripe.Checkout.SessionCreateParams) => Promise<Stripe.Response<Stripe.Checkout.Session>>
|
|
6
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { config } from "@stacksjs/config";
|
|
2
|
+
import { stripe } from "..";
|
|
3
|
+
import { freshIdempotencyKey } from "../idempotency";
|
|
4
|
+
function assertSameOriginUrl(url, label) {
|
|
5
|
+
if (!url)
|
|
6
|
+
return;
|
|
7
|
+
let parsed;
|
|
8
|
+
try {
|
|
9
|
+
parsed = new URL(url);
|
|
10
|
+
} catch {
|
|
11
|
+
throw Error(`[payments/checkout] ${label} is not a valid URL: ${url}`);
|
|
12
|
+
}
|
|
13
|
+
const appUrl = config?.app?.url;
|
|
14
|
+
if (!appUrl)
|
|
15
|
+
return;
|
|
16
|
+
let appOrigin;
|
|
17
|
+
try {
|
|
18
|
+
appOrigin = new URL(appUrl.startsWith("http") ? appUrl : `https://${appUrl}`).origin;
|
|
19
|
+
} catch {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (parsed.origin !== appOrigin)
|
|
23
|
+
throw Error(`[payments/checkout] ${label} origin (${parsed.origin}) does not match app origin (${appOrigin})`);
|
|
24
|
+
}
|
|
25
|
+
export const manageCheckout = (() => {
|
|
26
|
+
async function create(user, params) {
|
|
27
|
+
const customerId = params.customer || user.stripe_id;
|
|
28
|
+
if (!customerId)
|
|
29
|
+
throw Error("User has no Stripe customer");
|
|
30
|
+
assertSameOriginUrl(params.success_url, "success_url");
|
|
31
|
+
assertSameOriginUrl(params.cancel_url, "cancel_url");
|
|
32
|
+
const mergedParams = { ...{
|
|
33
|
+
customer: customerId,
|
|
34
|
+
mode: "payment",
|
|
35
|
+
success_url: params.success_url,
|
|
36
|
+
cancel_url: params.cancel_url
|
|
37
|
+
}, ...params };
|
|
38
|
+
return await stripe.checkout.sessions.create(mergedParams, {
|
|
39
|
+
idempotencyKey: freshIdempotencyKey("checkout.session.create", user.id)
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return { create };
|
|
43
|
+
})();
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { StripeCustomerOptions } from '@stacksjs/types';
|
|
2
|
+
import type { UserModel } from '@stacksjs/orm';
|
|
3
|
+
import type Stripe from 'stripe';
|
|
4
|
+
export declare const manageCustomer: ManageCustomer;
|
|
5
|
+
export declare interface ManageCustomer {
|
|
6
|
+
stripeId: (user: UserModel) => string
|
|
7
|
+
hasStripeId: (user: UserModel) => boolean
|
|
8
|
+
createStripeCustomer: (user: UserModel, options: Stripe.CustomerCreateParams) => Promise<Stripe.Response<Stripe.Customer>>
|
|
9
|
+
updateStripeCustomer: (user: UserModel, options: Stripe.CustomerUpdateParams) => Promise<Stripe.Response<Stripe.Customer>>
|
|
10
|
+
createOrGetStripeUser: (user: UserModel, options: Stripe.CustomerCreateParams) => Promise<Stripe.Response<Stripe.Customer>>
|
|
11
|
+
retrieveStripeUser: (user: UserModel) => Promise<Stripe.Response<Stripe.Customer> | undefined>
|
|
12
|
+
createOrUpdateStripeUser: (user: UserModel, options: Stripe.CustomerCreateParams) => Promise<Stripe.Response<Stripe.Customer>>
|
|
13
|
+
deleteStripeUser: (user: UserModel) => Promise<Stripe.Response<Stripe.DeletedCustomer>>
|
|
14
|
+
syncStripeCustomerDetails: (user: UserModel, options: StripeCustomerOptions) => Promise<Stripe.Response<Stripe.Customer>>
|
|
15
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { stripe } from "..";
|
|
2
|
+
import { stacksIdempotencyKey } from "../idempotency";
|
|
3
|
+
export const manageCustomer = (() => {
|
|
4
|
+
function stripeId(user) {
|
|
5
|
+
return user.stripe_id || "";
|
|
6
|
+
}
|
|
7
|
+
function stripeAddress(options) {
|
|
8
|
+
return {
|
|
9
|
+
line1: options.address?.line1,
|
|
10
|
+
city: options.address?.city,
|
|
11
|
+
state: options.address?.state,
|
|
12
|
+
postal_code: options.address?.postal_code,
|
|
13
|
+
country: options.address?.country
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function stripePreferredLocales() {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
function stripeMetadata(metadata) {
|
|
20
|
+
return metadata;
|
|
21
|
+
}
|
|
22
|
+
function hasStripeId(user) {
|
|
23
|
+
return Boolean(user.stripe_id);
|
|
24
|
+
}
|
|
25
|
+
async function createStripeCustomer(user, options = {}) {
|
|
26
|
+
if (hasStripeId(user))
|
|
27
|
+
throw Error("Customer already created");
|
|
28
|
+
if (!options.name && stripeName(user))
|
|
29
|
+
options.name = stripeName(user);
|
|
30
|
+
if (!options.email && stripeEmail(user))
|
|
31
|
+
options.email = stripeEmail(user);
|
|
32
|
+
const customer = await stripe.customers.create(options, {
|
|
33
|
+
idempotencyKey: stacksIdempotencyKey("customer.create", user.id)
|
|
34
|
+
});
|
|
35
|
+
await user.update({ stripe_id: customer.id });
|
|
36
|
+
return customer;
|
|
37
|
+
}
|
|
38
|
+
async function updateStripeCustomer(user, options = {}) {
|
|
39
|
+
if (!user.stripe_id)
|
|
40
|
+
throw Error("User does not have a Stripe customer ID. Create a customer first.");
|
|
41
|
+
return await stripe.customers.update(user.stripe_id, options, {
|
|
42
|
+
idempotencyKey: stacksIdempotencyKey("customer.update", user.id, user.stripe_id)
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async function deleteStripeUser(user) {
|
|
46
|
+
if (!hasStripeId(user))
|
|
47
|
+
throw Error("User does not have a Stripe ID");
|
|
48
|
+
try {
|
|
49
|
+
if (!user.stripe_id)
|
|
50
|
+
throw Error("User has no Stripe ID");
|
|
51
|
+
const deletedCustomer = await stripe.customers.del(user.stripe_id);
|
|
52
|
+
await user.update({ stripe_id: "" });
|
|
53
|
+
return deletedCustomer;
|
|
54
|
+
} catch (error) {
|
|
55
|
+
if (error.statusCode === 404)
|
|
56
|
+
throw Error("Customer not found in Stripe");
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async function createOrGetStripeUser(user, options = {}) {
|
|
61
|
+
if (!hasStripeId(user))
|
|
62
|
+
return await createStripeCustomer(user, options);
|
|
63
|
+
try {
|
|
64
|
+
const customer = await stripe.customers.retrieve(user.stripe_id);
|
|
65
|
+
if (customer.deleted)
|
|
66
|
+
throw Error("Customer was deleted");
|
|
67
|
+
return customer;
|
|
68
|
+
} catch (error) {
|
|
69
|
+
if (error.statusCode === 404)
|
|
70
|
+
return await createStripeCustomer(user, options);
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async function retrieveStripeUser(user) {
|
|
75
|
+
if (!hasStripeId(user))
|
|
76
|
+
return;
|
|
77
|
+
try {
|
|
78
|
+
const customer = await stripe.customers.retrieve(user.stripe_id);
|
|
79
|
+
if (customer.deleted)
|
|
80
|
+
throw Error("Customer was deleted in Stripe");
|
|
81
|
+
return customer;
|
|
82
|
+
} catch (error) {
|
|
83
|
+
if (error.statusCode === 404)
|
|
84
|
+
throw Error("Customer not found in Stripe");
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async function createOrUpdateStripeUser(user, options) {
|
|
89
|
+
if (!hasStripeId(user))
|
|
90
|
+
return await createStripeCustomer(user, options);
|
|
91
|
+
try {
|
|
92
|
+
if ((await stripe.customers.retrieve(user.stripe_id)).deleted)
|
|
93
|
+
return await createStripeCustomer(user, options);
|
|
94
|
+
return await updateStripeCustomer(user, options);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
if (error.statusCode === 404)
|
|
97
|
+
return await createStripeCustomer(user, options);
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function syncStripeCustomerDetails(user, options) {
|
|
102
|
+
return await updateStripeCustomer(user, {
|
|
103
|
+
name: stripeName(user),
|
|
104
|
+
email: stripeEmail(user),
|
|
105
|
+
address: stripeAddress(options),
|
|
106
|
+
preferred_locales: stripePreferredLocales(),
|
|
107
|
+
metadata: options.metadata ? stripeMetadata(options.metadata) : {}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function stripeName(user) {
|
|
111
|
+
return user.name || "";
|
|
112
|
+
}
|
|
113
|
+
function stripeEmail(user) {
|
|
114
|
+
return user.email || "";
|
|
115
|
+
}
|
|
116
|
+
return { stripeId, hasStripeId, createStripeCustomer, updateStripeCustomer, createOrGetStripeUser, createOrUpdateStripeUser, deleteStripeUser, retrieveStripeUser, syncStripeCustomerDetails };
|
|
117
|
+
})();
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Core Billable Modules
|
|
2
|
+
export * from './charge';
|
|
3
|
+
export * from './checkout';
|
|
4
|
+
export * from './customer';
|
|
5
|
+
export * from './intent';
|
|
6
|
+
export * from './invoice';
|
|
7
|
+
export * from './payment-method';
|
|
8
|
+
export * from './price';
|
|
9
|
+
export * from './product';
|
|
10
|
+
export * from './setup-products';
|
|
11
|
+
export * from './subscription';
|
|
12
|
+
export * from './transaction';
|
|
13
|
+
export * from './webhook';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from "./charge";
|
|
2
|
+
export * from "./checkout";
|
|
3
|
+
export * from "./customer";
|
|
4
|
+
export * from "./intent";
|
|
5
|
+
export * from "./invoice";
|
|
6
|
+
export * from "./payment-method";
|
|
7
|
+
export * from "./price";
|
|
8
|
+
export * from "./product";
|
|
9
|
+
export * from "./setup-products";
|
|
10
|
+
export * from "./subscription";
|
|
11
|
+
export * from "./transaction";
|
|
12
|
+
export * from "./webhook";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { UserModel } from '@stacksjs/orm';
|
|
2
|
+
import type Stripe from 'stripe';
|
|
3
|
+
export declare const manageSetupIntent: SetupIntent;
|
|
4
|
+
export declare interface SetupIntent {
|
|
5
|
+
create: (user: UserModel, params: Stripe.SetupIntentCreateParams) => Promise<Stripe.Response<Stripe.SetupIntent>>
|
|
6
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { manageCustomer, stripe } from "..";
|
|
2
|
+
import { freshIdempotencyKey } from "../idempotency";
|
|
3
|
+
export const manageSetupIntent = (() => {
|
|
4
|
+
async function create(user, params) {
|
|
5
|
+
const mergedParams = { ...{
|
|
6
|
+
customer: await manageCustomer.createOrGetStripeUser(user, {}).then((customer) => {
|
|
7
|
+
if (!customer || !customer.id)
|
|
8
|
+
throw Error("Customer does not exist in Stripe");
|
|
9
|
+
return customer.id;
|
|
10
|
+
}),
|
|
11
|
+
payment_method_types: ["card"]
|
|
12
|
+
}, ...params };
|
|
13
|
+
return await stripe.setupIntents.create(mergedParams, {
|
|
14
|
+
idempotencyKey: freshIdempotencyKey("setup_intent.create", user.id)
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return { create };
|
|
18
|
+
})();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { stripe } from "..";
|
|
2
|
+
export const manageInvoice = (() => {
|
|
3
|
+
async function list(user) {
|
|
4
|
+
if (!user.hasStripeId())
|
|
5
|
+
throw Error("Customer does not exist in Stripe");
|
|
6
|
+
if (!user.stripe_id)
|
|
7
|
+
throw Error("User has no Stripe ID");
|
|
8
|
+
return await stripe.invoices.list({
|
|
9
|
+
customer: user.stripe_id,
|
|
10
|
+
expand: ["data.payment_intent.payment_method"]
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
return { list };
|
|
14
|
+
})();
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { PaymentMethod } from '@stacksjs/orm';
|
|
2
|
+
import type { ModelRow, UserModel } from '@stacksjs/orm';
|
|
3
|
+
import type { Selectable } from '@stacksjs/database';
|
|
4
|
+
import type Stripe from 'stripe';
|
|
5
|
+
export declare const managePaymentMethod: ManagePaymentMethod;
|
|
6
|
+
export declare interface ManagePaymentMethod {
|
|
7
|
+
addPaymentMethod: (user: UserModel, paymentMethod: string | Stripe.PaymentMethod) => Promise<Stripe.Response<Stripe.PaymentMethod>>
|
|
8
|
+
updatePaymentMethod: (user: UserModel, paymentMethodId: string, updateParams?: Stripe.PaymentMethodUpdateParams) => Promise<Stripe.Response<Stripe.PaymentMethod>>
|
|
9
|
+
setUserDefaultPayment: (user: UserModel, paymentMethodId: string) => Promise<Stripe.Response<Stripe.Customer>>
|
|
10
|
+
setDefaultPaymentMethod: (user: UserModel, paymentMethodId: number) => Promise<Stripe.Response<Stripe.Customer>>
|
|
11
|
+
storePaymentMethod: (user: UserModel, paymentMethodId: Stripe.PaymentMethod) => Promise<PaymentMethodInstance>
|
|
12
|
+
deletePaymentMethod: (user: UserModel, paymentMethodId: string | number) => Promise<Stripe.Response<Stripe.PaymentMethod>>
|
|
13
|
+
retrievePaymentMethod: (user: UserModel, paymentMethodId: number) => Promise<PaymentMethodInstance | undefined>
|
|
14
|
+
retrieveDefaultPaymentMethod: (user: UserModel) => Promise<PaymentMethodInstance | undefined>
|
|
15
|
+
listPaymentMethods: (user: UserModel, cardType?: string) => Promise<Selectable<PaymentMethodsTable>[]>
|
|
16
|
+
}
|
|
17
|
+
declare type PaymentMethodInstance = NonNullable<Awaited<ReturnType<typeof PaymentMethod.find>>>;
|
|
18
|
+
declare type PaymentMethodsTable = ModelRow<typeof PaymentMethod>;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { db } from "@stacksjs/database";
|
|
2
|
+
import { PaymentMethod } from "@stacksjs/orm";
|
|
3
|
+
import { stripe } from "..";
|
|
4
|
+
export const managePaymentMethod = (() => {
|
|
5
|
+
async function addPaymentMethod(user, paymentMethod) {
|
|
6
|
+
if (!user.hasStripeId())
|
|
7
|
+
throw Error("Customer does not exist in Stripe");
|
|
8
|
+
let stripePaymentMethod;
|
|
9
|
+
if (typeof paymentMethod === "string")
|
|
10
|
+
stripePaymentMethod = await stripe.paymentMethods.retrieve(paymentMethod);
|
|
11
|
+
else
|
|
12
|
+
stripePaymentMethod = paymentMethod;
|
|
13
|
+
if (stripePaymentMethod.customer !== user.stripe_id)
|
|
14
|
+
stripePaymentMethod = await stripe.paymentMethods.attach(stripePaymentMethod.id, {
|
|
15
|
+
customer: user.stripe_id
|
|
16
|
+
});
|
|
17
|
+
await storePaymentMethod(user, stripePaymentMethod);
|
|
18
|
+
return stripePaymentMethod;
|
|
19
|
+
}
|
|
20
|
+
async function setUserDefaultPayment(user, paymentMethodId) {
|
|
21
|
+
if (!user.hasStripeId())
|
|
22
|
+
throw Error("Customer does not exist in Stripe");
|
|
23
|
+
const paymentMethod = await stripe.paymentMethods.retrieve(paymentMethodId), paymentMethodModel = await db.selectFrom("payment_methods").where("provider_id", "=", paymentMethodId).selectAll().executeTakeFirst();
|
|
24
|
+
if (!user.stripe_id)
|
|
25
|
+
throw Error("User has no Stripe ID");
|
|
26
|
+
if (paymentMethod.customer !== user.stripe_id)
|
|
27
|
+
await stripe.paymentMethods.attach(paymentMethod.id, {
|
|
28
|
+
customer: user.stripe_id
|
|
29
|
+
});
|
|
30
|
+
const updatedCustomer = await stripe.customers.update(user.stripe_id, {
|
|
31
|
+
invoice_settings: {
|
|
32
|
+
default_payment_method: paymentMethodId
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
await db.updateTable("payment_methods").set({ is_default: !1 }).where("user_id", "=", user.id).executeTakeFirst();
|
|
36
|
+
if (paymentMethodModel)
|
|
37
|
+
await updateDefault(paymentMethodModel);
|
|
38
|
+
return updatedCustomer;
|
|
39
|
+
}
|
|
40
|
+
async function setDefaultPaymentMethod(user, paymentId) {
|
|
41
|
+
if (!user.hasStripeId())
|
|
42
|
+
throw Error("Customer does not exist in Stripe");
|
|
43
|
+
const pm = await db.selectFrom("payment_methods").where("id", "=", paymentId).selectAll().executeTakeFirst();
|
|
44
|
+
if (!pm?.provider_id)
|
|
45
|
+
throw Error(`Payment method with id ${paymentId} not found`);
|
|
46
|
+
const paymentMethod = await stripe.paymentMethods.retrieve(String(pm.provider_id));
|
|
47
|
+
if (paymentMethod.customer !== user.stripe_id)
|
|
48
|
+
await stripe.paymentMethods.attach(paymentMethod.id, {
|
|
49
|
+
customer: user.stripe_id
|
|
50
|
+
});
|
|
51
|
+
if (!user.stripe_id)
|
|
52
|
+
throw Error("User has no Stripe ID");
|
|
53
|
+
const updatedCustomer = await stripe.customers.update(user.stripe_id, {
|
|
54
|
+
invoice_settings: {
|
|
55
|
+
default_payment_method: String(pm.provider_id)
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
await db.updateTable("payment_methods").set({ is_default: !1 }).where("user_id", "=", user.id).executeTakeFirst();
|
|
59
|
+
await db.updateTable("payment_methods").set({ is_default: !0 }).where("id", "=", paymentId).executeTakeFirst();
|
|
60
|
+
return updatedCustomer;
|
|
61
|
+
}
|
|
62
|
+
async function storePaymentMethod(user, paymentMethod) {
|
|
63
|
+
if (!user.hasStripeId())
|
|
64
|
+
throw Error("Customer does not exist in Stripe");
|
|
65
|
+
if (!paymentMethod.card?.brand || !paymentMethod.card?.exp_year || !paymentMethod.card?.exp_month)
|
|
66
|
+
throw Error("Invalid payment method: missing required card details");
|
|
67
|
+
const method = {
|
|
68
|
+
type: "card",
|
|
69
|
+
last_four: String(paymentMethod.card.last4),
|
|
70
|
+
brand: paymentMethod.card.brand,
|
|
71
|
+
exp_year: paymentMethod.card.exp_year,
|
|
72
|
+
exp_month: paymentMethod.card.exp_month,
|
|
73
|
+
user_id: user.id,
|
|
74
|
+
provider_id: paymentMethod.id
|
|
75
|
+
};
|
|
76
|
+
if (paymentMethod.customer !== user.stripe_id)
|
|
77
|
+
await stripe.paymentMethods.attach(paymentMethod.id, {
|
|
78
|
+
customer: user.stripe_id
|
|
79
|
+
});
|
|
80
|
+
return await PaymentMethod.create(method);
|
|
81
|
+
}
|
|
82
|
+
async function updateDefault(paymentMethodModel) {
|
|
83
|
+
return await paymentMethodModel.update({ is_default: !0 });
|
|
84
|
+
}
|
|
85
|
+
async function deletePaymentMethod(user, paymentMethodId) {
|
|
86
|
+
if (!user.hasStripeId())
|
|
87
|
+
throw Error("Customer does not exist in Stripe");
|
|
88
|
+
const pm = typeof paymentMethodId === "string" ? await db.selectFrom("payment_methods").where("provider_id", "=", paymentMethodId).selectAll().executeTakeFirst() : await PaymentMethod.find(paymentMethodId);
|
|
89
|
+
if (!pm?.provider_id)
|
|
90
|
+
throw Error(`Payment method with id ${paymentMethodId} not found`);
|
|
91
|
+
if ((await stripe.paymentMethods.retrieve(String(pm.provider_id))).customer !== user.stripe_id)
|
|
92
|
+
throw Error("Payment method does not belong to this customer");
|
|
93
|
+
await pm.delete();
|
|
94
|
+
return await stripe.paymentMethods.detach(String(pm.provider_id));
|
|
95
|
+
}
|
|
96
|
+
async function updatePaymentMethod(user, paymentMethodId, updateParams) {
|
|
97
|
+
if (!user.hasStripeId())
|
|
98
|
+
throw Error("Customer does not exist in Stripe");
|
|
99
|
+
if ((await stripe.paymentMethods.retrieve(paymentMethodId)).customer !== user.stripe_id)
|
|
100
|
+
throw Error("Payment method does not belong to this customer");
|
|
101
|
+
return await stripe.paymentMethods.update(paymentMethodId, updateParams);
|
|
102
|
+
}
|
|
103
|
+
async function listPaymentMethods(user) {
|
|
104
|
+
if (!user.hasStripeId())
|
|
105
|
+
throw Error("Customer does not exist in Stripe");
|
|
106
|
+
return await db.selectFrom("payment_methods").selectAll().where("user_id", "=", user.id).execute();
|
|
107
|
+
}
|
|
108
|
+
async function retrievePaymentMethod(user, paymentMethodId) {
|
|
109
|
+
if (!user.hasStripeId())
|
|
110
|
+
throw Error("Customer does not exist in Stripe");
|
|
111
|
+
return await db.selectFrom("payment_methods").where("id", "=", paymentMethodId).selectAll().executeTakeFirst();
|
|
112
|
+
}
|
|
113
|
+
async function retrieveDefaultPaymentMethod(user) {
|
|
114
|
+
if (!user.hasStripeId())
|
|
115
|
+
throw Error("Customer does not exist in Stripe");
|
|
116
|
+
return await PaymentMethod.where("user_id", user.id).where("is_default", !0).first();
|
|
117
|
+
}
|
|
118
|
+
return { addPaymentMethod, deletePaymentMethod, retrieveDefaultPaymentMethod, updatePaymentMethod, listPaymentMethods, setDefaultPaymentMethod, storePaymentMethod, retrievePaymentMethod, setUserDefaultPayment };
|
|
119
|
+
})();
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type Stripe from 'stripe';
|
|
2
|
+
export declare const managePrice: PriceManager;
|
|
3
|
+
export declare interface PriceManager {
|
|
4
|
+
retrieveByLookupKey: (lookupKey: string) => Promise<Stripe.Price | undefined>
|
|
5
|
+
createOrGet: (lookupKey: string, params: Stripe.PriceCreateParams) => Promise<Stripe.Price>
|
|
6
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { stripe } from "..";
|
|
2
|
+
export const managePrice = (() => {
|
|
3
|
+
async function retrieveByLookupKey(lookupKey) {
|
|
4
|
+
const prices = await stripe.prices.list({ lookup_keys: [lookupKey] });
|
|
5
|
+
if (!prices.data.length)
|
|
6
|
+
return;
|
|
7
|
+
return prices.data[0];
|
|
8
|
+
}
|
|
9
|
+
async function createOrGet(lookupKey, params) {
|
|
10
|
+
const existingPrice = await retrieveByLookupKey(lookupKey);
|
|
11
|
+
if (existingPrice)
|
|
12
|
+
return existingPrice;
|
|
13
|
+
return await stripe.prices.create({
|
|
14
|
+
...params,
|
|
15
|
+
lookup_key: lookupKey
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
return { retrieveByLookupKey, createOrGet };
|
|
19
|
+
})();
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type Stripe from 'stripe';
|
|
2
|
+
export declare const manageProduct: ProductManager;
|
|
3
|
+
export declare const managePriceExtended: ExtendedPriceManager;
|
|
4
|
+
export declare const manageCoupon: CouponManager;
|
|
5
|
+
export declare interface ProductManager {
|
|
6
|
+
create: (params: Stripe.ProductCreateParams) => Promise<Stripe.Response<Stripe.Product>>
|
|
7
|
+
retrieve: (productId: string) => Promise<Stripe.Response<Stripe.Product>>
|
|
8
|
+
update: (productId: string, params: Stripe.ProductUpdateParams) => Promise<Stripe.Response<Stripe.Product>>
|
|
9
|
+
list: (params?: Stripe.ProductListParams) => Promise<Stripe.Response<Stripe.ApiList<Stripe.Product>>>
|
|
10
|
+
archive: (productId: string) => Promise<Stripe.Response<Stripe.Product>>
|
|
11
|
+
createWithPrice: (product: Stripe.ProductCreateParams, price: Omit<Stripe.PriceCreateParams, 'product'>) => Promise<{ product: Stripe.Product, price: Stripe.Price }>
|
|
12
|
+
search: (query: string, params?: Stripe.ProductSearchParams) => Promise<Stripe.Response<Stripe.ApiSearchResult<Stripe.Product>>>
|
|
13
|
+
}
|
|
14
|
+
// =============================================================================
|
|
15
|
+
// Extended Price Management
|
|
16
|
+
// =============================================================================
|
|
17
|
+
export declare interface ExtendedPriceManager {
|
|
18
|
+
create: (params: Stripe.PriceCreateParams) => Promise<Stripe.Response<Stripe.Price>>
|
|
19
|
+
retrieve: (priceId: string) => Promise<Stripe.Response<Stripe.Price>>
|
|
20
|
+
update: (priceId: string, params: Stripe.PriceUpdateParams) => Promise<Stripe.Response<Stripe.Price>>
|
|
21
|
+
list: (params?: Stripe.PriceListParams) => Promise<Stripe.Response<Stripe.ApiList<Stripe.Price>>>
|
|
22
|
+
listByProduct: (productId: string, params?: Stripe.PriceListParams) => Promise<Stripe.Response<Stripe.ApiList<Stripe.Price>>>
|
|
23
|
+
search: (query: string, params?: Stripe.PriceSearchParams) => Promise<Stripe.Response<Stripe.ApiSearchResult<Stripe.Price>>>
|
|
24
|
+
archive: (priceId: string) => Promise<Stripe.Response<Stripe.Price>>
|
|
25
|
+
}
|
|
26
|
+
// =============================================================================
|
|
27
|
+
// Coupon and Promotion Code Management
|
|
28
|
+
// =============================================================================
|
|
29
|
+
export declare interface CouponManager {
|
|
30
|
+
create: (params: Stripe.CouponCreateParams) => Promise<Stripe.Response<Stripe.Coupon>>
|
|
31
|
+
retrieve: (couponId: string) => Promise<Stripe.Response<Stripe.Coupon>>
|
|
32
|
+
update: (couponId: string, params: Stripe.CouponUpdateParams) => Promise<Stripe.Response<Stripe.Coupon>>
|
|
33
|
+
delete: (couponId: string) => Promise<Stripe.Response<Stripe.DeletedCoupon>>
|
|
34
|
+
list: (params?: Stripe.CouponListParams) => Promise<Stripe.Response<Stripe.ApiList<Stripe.Coupon>>>
|
|
35
|
+
createPromotionCode: (params: Stripe.PromotionCodeCreateParams) => Promise<Stripe.Response<Stripe.PromotionCode>>
|
|
36
|
+
retrievePromotionCode: (code: string) => Promise<Stripe.PromotionCode | null>
|
|
37
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { stripe } from "..";
|
|
2
|
+
export const manageProduct = (() => {
|
|
3
|
+
async function create(params) {
|
|
4
|
+
return await stripe.products.create(params);
|
|
5
|
+
}
|
|
6
|
+
async function retrieve(productId) {
|
|
7
|
+
return await stripe.products.retrieve(productId);
|
|
8
|
+
}
|
|
9
|
+
async function update(productId, params) {
|
|
10
|
+
return await stripe.products.update(productId, params);
|
|
11
|
+
}
|
|
12
|
+
async function list(params = {}) {
|
|
13
|
+
return await stripe.products.list({
|
|
14
|
+
active: !0,
|
|
15
|
+
...params
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
async function archive(productId) {
|
|
19
|
+
return await stripe.products.update(productId, { active: !1 });
|
|
20
|
+
}
|
|
21
|
+
async function createWithPrice(productParams, priceParams) {
|
|
22
|
+
const product = await stripe.products.create(productParams), price = await stripe.prices.create({
|
|
23
|
+
...priceParams,
|
|
24
|
+
product: product.id
|
|
25
|
+
});
|
|
26
|
+
return { product, price };
|
|
27
|
+
}
|
|
28
|
+
async function search(query, params = { query: "" }) {
|
|
29
|
+
return await stripe.products.search({
|
|
30
|
+
...params,
|
|
31
|
+
query
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return { create, retrieve, update, list, archive, createWithPrice, search };
|
|
35
|
+
})(), managePriceExtended = (() => {
|
|
36
|
+
async function create(params) {
|
|
37
|
+
return await stripe.prices.create(params);
|
|
38
|
+
}
|
|
39
|
+
async function retrieve(priceId) {
|
|
40
|
+
return await stripe.prices.retrieve(priceId);
|
|
41
|
+
}
|
|
42
|
+
async function update(priceId, params) {
|
|
43
|
+
return await stripe.prices.update(priceId, params);
|
|
44
|
+
}
|
|
45
|
+
async function list(params = {}) {
|
|
46
|
+
return await stripe.prices.list({
|
|
47
|
+
active: !0,
|
|
48
|
+
...params
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
async function listByProduct(productId, params = {}) {
|
|
52
|
+
return await stripe.prices.list({
|
|
53
|
+
product: productId,
|
|
54
|
+
active: !0,
|
|
55
|
+
...params
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
async function search(query, params = { query: "" }) {
|
|
59
|
+
return await stripe.prices.search({
|
|
60
|
+
...params,
|
|
61
|
+
query
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async function archive(priceId) {
|
|
65
|
+
return await stripe.prices.update(priceId, { active: !1 });
|
|
66
|
+
}
|
|
67
|
+
return { create, retrieve, update, list, listByProduct, search, archive };
|
|
68
|
+
})(), manageCoupon = (() => {
|
|
69
|
+
async function create(params) {
|
|
70
|
+
return await stripe.coupons.create(params);
|
|
71
|
+
}
|
|
72
|
+
async function retrieve(couponId) {
|
|
73
|
+
return await stripe.coupons.retrieve(couponId);
|
|
74
|
+
}
|
|
75
|
+
async function update(couponId, params) {
|
|
76
|
+
return await stripe.coupons.update(couponId, params);
|
|
77
|
+
}
|
|
78
|
+
async function deleteCoupon(couponId) {
|
|
79
|
+
return await stripe.coupons.del(couponId);
|
|
80
|
+
}
|
|
81
|
+
async function list(params = {}) {
|
|
82
|
+
return await stripe.coupons.list(params);
|
|
83
|
+
}
|
|
84
|
+
async function createPromotionCode(params) {
|
|
85
|
+
return await stripe.promotionCodes.create(params);
|
|
86
|
+
}
|
|
87
|
+
async function retrievePromotionCode(code) {
|
|
88
|
+
try {
|
|
89
|
+
const promotionCodes = await stripe.promotionCodes.list({
|
|
90
|
+
code,
|
|
91
|
+
active: !0
|
|
92
|
+
});
|
|
93
|
+
if (promotionCodes.data.length > 0)
|
|
94
|
+
return promotionCodes.data[0] ?? null;
|
|
95
|
+
return null;
|
|
96
|
+
} catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return { create, retrieve, update, delete: deleteCoupon, list, createPromotionCode, retrievePromotionCode };
|
|
101
|
+
})();
|