payment-kit 1.13.15
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/.eslintrc.js +15 -0
- package/README.md +3 -0
- package/api/dev.ts +6 -0
- package/api/hooks/pre-start.js +12 -0
- package/api/src/hooks/pre-start.ts +21 -0
- package/api/src/index.ts +92 -0
- package/api/src/jobs/event.ts +72 -0
- package/api/src/jobs/invoice.ts +148 -0
- package/api/src/jobs/payment.ts +208 -0
- package/api/src/jobs/subscription.ts +301 -0
- package/api/src/jobs/webhook.ts +113 -0
- package/api/src/libs/audit.ts +73 -0
- package/api/src/libs/auth.ts +40 -0
- package/api/src/libs/chain/arcblock.ts +13 -0
- package/api/src/libs/dayjs.ts +17 -0
- package/api/src/libs/env.ts +5 -0
- package/api/src/libs/hooks.ts +42 -0
- package/api/src/libs/logger.ts +27 -0
- package/api/src/libs/middleware.ts +12 -0
- package/api/src/libs/payment.ts +53 -0
- package/api/src/libs/queue/index.ts +263 -0
- package/api/src/libs/queue/store.ts +47 -0
- package/api/src/libs/security.ts +95 -0
- package/api/src/libs/session.ts +164 -0
- package/api/src/libs/util.ts +93 -0
- package/api/src/locales/en.ts +3 -0
- package/api/src/locales/index.ts +37 -0
- package/api/src/locales/zh.ts +3 -0
- package/api/src/routes/checkout-sessions.ts +536 -0
- package/api/src/routes/connect/collect.ts +109 -0
- package/api/src/routes/connect/pay.ts +116 -0
- package/api/src/routes/connect/setup.ts +121 -0
- package/api/src/routes/connect/shared.ts +410 -0
- package/api/src/routes/connect/subscribe.ts +128 -0
- package/api/src/routes/customers.ts +70 -0
- package/api/src/routes/events.ts +76 -0
- package/api/src/routes/index.ts +59 -0
- package/api/src/routes/invoices.ts +126 -0
- package/api/src/routes/payment-currencies.ts +38 -0
- package/api/src/routes/payment-intents.ts +122 -0
- package/api/src/routes/payment-links.ts +221 -0
- package/api/src/routes/payment-methods.ts +39 -0
- package/api/src/routes/prices.ts +134 -0
- package/api/src/routes/products.ts +191 -0
- package/api/src/routes/settings.ts +33 -0
- package/api/src/routes/subscription-items.ts +148 -0
- package/api/src/routes/subscriptions.ts +254 -0
- package/api/src/routes/usage-records.ts +120 -0
- package/api/src/routes/webhook-attempts.ts +57 -0
- package/api/src/routes/webhook-endpoints.ts +105 -0
- package/api/src/store/migrate.ts +16 -0
- package/api/src/store/migrations/20230905-genesis.ts +52 -0
- package/api/src/store/migrations/20230911-seeding.ts +145 -0
- package/api/src/store/models/checkout-session.ts +395 -0
- package/api/src/store/models/coupon.ts +137 -0
- package/api/src/store/models/customer.ts +199 -0
- package/api/src/store/models/discount.ts +116 -0
- package/api/src/store/models/event.ts +111 -0
- package/api/src/store/models/index.ts +165 -0
- package/api/src/store/models/invoice-item.ts +185 -0
- package/api/src/store/models/invoice.ts +492 -0
- package/api/src/store/models/job.ts +75 -0
- package/api/src/store/models/payment-currency.ts +139 -0
- package/api/src/store/models/payment-intent.ts +282 -0
- package/api/src/store/models/payment-link.ts +219 -0
- package/api/src/store/models/payment-method.ts +169 -0
- package/api/src/store/models/price.ts +266 -0
- package/api/src/store/models/product.ts +162 -0
- package/api/src/store/models/promotion-code.ts +112 -0
- package/api/src/store/models/setup-intent.ts +206 -0
- package/api/src/store/models/subscription-item.ts +103 -0
- package/api/src/store/models/subscription-schedule.ts +157 -0
- package/api/src/store/models/subscription.ts +307 -0
- package/api/src/store/models/types.ts +406 -0
- package/api/src/store/models/usage-record.ts +132 -0
- package/api/src/store/models/webhook-attempt.ts +96 -0
- package/api/src/store/models/webhook-endpoint.ts +96 -0
- package/api/src/store/sequelize.ts +15 -0
- package/api/third.d.ts +28 -0
- package/blocklet.md +3 -0
- package/blocklet.yml +89 -0
- package/index.html +14 -0
- package/logo.png +0 -0
- package/package.json +133 -0
- package/public/.gitkeep +0 -0
- package/screenshots/.gitkeep +0 -0
- package/screenshots/1-subscription.png +0 -0
- package/screenshots/2-customer-1.png +0 -0
- package/screenshots/3-customer-2.png +0 -0
- package/screenshots/4-admin-3.png +0 -0
- package/screenshots/5-admin-4.png +0 -0
- package/scripts/build-clean.js +6 -0
- package/scripts/bump-version.mjs +35 -0
- package/src/app.tsx +68 -0
- package/src/components/actions.tsx +85 -0
- package/src/components/blockchain/tx.tsx +29 -0
- package/src/components/checkout/amount.tsx +24 -0
- package/src/components/checkout/error.tsx +30 -0
- package/src/components/checkout/footer.tsx +12 -0
- package/src/components/checkout/form/address.tsx +38 -0
- package/src/components/checkout/form/index.tsx +295 -0
- package/src/components/checkout/header.tsx +23 -0
- package/src/components/checkout/pay.tsx +222 -0
- package/src/components/checkout/product-card.tsx +56 -0
- package/src/components/checkout/product-item.tsx +37 -0
- package/src/components/checkout/skeleton/overview.tsx +21 -0
- package/src/components/checkout/skeleton/payment.tsx +35 -0
- package/src/components/checkout/success.tsx +183 -0
- package/src/components/checkout/summary.tsx +34 -0
- package/src/components/collapse.tsx +50 -0
- package/src/components/confirm.tsx +55 -0
- package/src/components/copyable.tsx +38 -0
- package/src/components/currency.tsx +15 -0
- package/src/components/customer/actions.tsx +73 -0
- package/src/components/data.tsx +20 -0
- package/src/components/drawer-form.tsx +77 -0
- package/src/components/error-fallback.tsx +7 -0
- package/src/components/error.tsx +39 -0
- package/src/components/event/list.tsx +217 -0
- package/src/components/info-card.tsx +40 -0
- package/src/components/info-metric.tsx +35 -0
- package/src/components/info-row.tsx +28 -0
- package/src/components/input.tsx +40 -0
- package/src/components/invoice/action.tsx +94 -0
- package/src/components/invoice/list.tsx +225 -0
- package/src/components/invoice/table.tsx +110 -0
- package/src/components/layout.tsx +70 -0
- package/src/components/livemode.tsx +23 -0
- package/src/components/metadata/editor.tsx +57 -0
- package/src/components/metadata/form.tsx +45 -0
- package/src/components/payment-intent/actions.tsx +81 -0
- package/src/components/payment-intent/list.tsx +204 -0
- package/src/components/payment-link/actions.tsx +114 -0
- package/src/components/payment-link/after-pay.tsx +87 -0
- package/src/components/payment-link/before-pay.tsx +175 -0
- package/src/components/payment-link/item.tsx +135 -0
- package/src/components/payment-link/product-select.tsx +66 -0
- package/src/components/payment-link/rename.tsx +64 -0
- package/src/components/portal/invoice/list.tsx +110 -0
- package/src/components/portal/subscription/cancel.tsx +83 -0
- package/src/components/portal/subscription/list.tsx +232 -0
- package/src/components/price/actions.tsx +21 -0
- package/src/components/price/form.tsx +292 -0
- package/src/components/product/actions.tsx +125 -0
- package/src/components/product/add-price.tsx +59 -0
- package/src/components/product/create.tsx +97 -0
- package/src/components/product/edit-price.tsx +75 -0
- package/src/components/product/edit.tsx +67 -0
- package/src/components/product/features.tsx +32 -0
- package/src/components/product/form.tsx +76 -0
- package/src/components/relative-time.tsx +41 -0
- package/src/components/section/header.tsx +29 -0
- package/src/components/status.tsx +12 -0
- package/src/components/subscription/actions/cancel.tsx +66 -0
- package/src/components/subscription/actions/index.tsx +172 -0
- package/src/components/subscription/actions/pause.tsx +83 -0
- package/src/components/subscription/items/actions.tsx +31 -0
- package/src/components/subscription/items/index.tsx +107 -0
- package/src/components/subscription/list.tsx +200 -0
- package/src/components/switch.tsx +48 -0
- package/src/components/table.tsx +66 -0
- package/src/components/uploader.tsx +81 -0
- package/src/components/webhook/attempts.tsx +149 -0
- package/src/contexts/products.tsx +42 -0
- package/src/contexts/session.ts +10 -0
- package/src/contexts/settings.tsx +54 -0
- package/src/env.d.ts +17 -0
- package/src/global.css +97 -0
- package/src/hooks/mobile.ts +15 -0
- package/src/index.tsx +6 -0
- package/src/libs/api.ts +19 -0
- package/src/libs/dayjs.ts +17 -0
- package/src/libs/util.ts +474 -0
- package/src/locales/en.tsx +395 -0
- package/src/locales/index.tsx +8 -0
- package/src/locales/zh.tsx +389 -0
- package/src/pages/admin/billing/index.tsx +56 -0
- package/src/pages/admin/billing/invoices/detail.tsx +215 -0
- package/src/pages/admin/billing/invoices/index.tsx +5 -0
- package/src/pages/admin/billing/subscriptions/detail.tsx +237 -0
- package/src/pages/admin/billing/subscriptions/index.tsx +5 -0
- package/src/pages/admin/customers/customers/detail.tsx +209 -0
- package/src/pages/admin/customers/customers/index.tsx +109 -0
- package/src/pages/admin/customers/index.tsx +47 -0
- package/src/pages/admin/developers/events/detail.tsx +77 -0
- package/src/pages/admin/developers/events/index.tsx +5 -0
- package/src/pages/admin/developers/index.tsx +60 -0
- package/src/pages/admin/developers/logs.tsx +3 -0
- package/src/pages/admin/developers/overview.tsx +3 -0
- package/src/pages/admin/developers/webhooks/detail.tsx +109 -0
- package/src/pages/admin/developers/webhooks/index.tsx +102 -0
- package/src/pages/admin/index.tsx +120 -0
- package/src/pages/admin/overview.tsx +3 -0
- package/src/pages/admin/payments/index.tsx +65 -0
- package/src/pages/admin/payments/intents/detail.tsx +205 -0
- package/src/pages/admin/payments/intents/index.tsx +5 -0
- package/src/pages/admin/payments/links/create.tsx +141 -0
- package/src/pages/admin/payments/links/detail.tsx +318 -0
- package/src/pages/admin/payments/links/index.tsx +167 -0
- package/src/pages/admin/products/coupons/index.tsx +3 -0
- package/src/pages/admin/products/index.tsx +81 -0
- package/src/pages/admin/products/prices/actions.tsx +151 -0
- package/src/pages/admin/products/prices/detail.tsx +203 -0
- package/src/pages/admin/products/prices/list.tsx +95 -0
- package/src/pages/admin/products/pricing-tables.tsx +3 -0
- package/src/pages/admin/products/products/create.tsx +105 -0
- package/src/pages/admin/products/products/detail.tsx +246 -0
- package/src/pages/admin/products/products/index.tsx +154 -0
- package/src/pages/admin/settings/branding.tsx +3 -0
- package/src/pages/admin/settings/business.tsx +3 -0
- package/src/pages/admin/settings/index.tsx +47 -0
- package/src/pages/admin/settings/payment-methods.tsx +80 -0
- package/src/pages/checkout/index.tsx +38 -0
- package/src/pages/checkout/pay.tsx +89 -0
- package/src/pages/customer/index.tsx +93 -0
- package/src/pages/customer/invoice.tsx +147 -0
- package/src/pages/home.tsx +9 -0
- package/tsconfig.api.json +9 -0
- package/tsconfig.eslint.json +7 -0
- package/tsconfig.json +99 -0
- package/tsconfig.types.json +11 -0
- package/vite.config.ts +19 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/lines-between-class-members */
|
|
2
|
+
import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes, Model } from 'sequelize';
|
|
3
|
+
import type { LiteralUnion } from 'type-fest';
|
|
4
|
+
|
|
5
|
+
import { createIdGenerator } from '../../libs/util';
|
|
6
|
+
import { sequelize } from '../sequelize';
|
|
7
|
+
|
|
8
|
+
const nextId = createIdGenerator('pm', 24);
|
|
9
|
+
|
|
10
|
+
type StripeConfig = {
|
|
11
|
+
publishable_key: string;
|
|
12
|
+
secret_key: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type ArcblockConfig = {
|
|
16
|
+
chain_id: string;
|
|
17
|
+
api_host: string;
|
|
18
|
+
explorer_host: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type EthereumConfig = {
|
|
22
|
+
chain_id: number;
|
|
23
|
+
api_host: string;
|
|
24
|
+
explorer_host: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
type BitcoinConfig = {
|
|
28
|
+
chain_id: number;
|
|
29
|
+
api_host: string;
|
|
30
|
+
explorer_host: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// eslint-disable-next-line prettier/prettier
|
|
34
|
+
export class PaymentMethod extends Model<InferAttributes<PaymentMethod>, InferCreationAttributes<PaymentMethod>> {
|
|
35
|
+
// Unique identifier for the object.
|
|
36
|
+
declare id: CreationOptional<string>;
|
|
37
|
+
|
|
38
|
+
declare active: boolean;
|
|
39
|
+
declare livemode: boolean;
|
|
40
|
+
declare locked: boolean;
|
|
41
|
+
|
|
42
|
+
declare type: LiteralUnion<'stripe' | 'arcblock' | 'ethereum' | 'bitcoin', string>;
|
|
43
|
+
|
|
44
|
+
declare name: string;
|
|
45
|
+
|
|
46
|
+
declare description: string;
|
|
47
|
+
|
|
48
|
+
declare logo: string;
|
|
49
|
+
|
|
50
|
+
declare default_currency_id?: string;
|
|
51
|
+
|
|
52
|
+
// How do we confirm transactions
|
|
53
|
+
declare confirmation: {
|
|
54
|
+
type: LiteralUnion<'immediate' | 'callback' | 'block', string>;
|
|
55
|
+
block?: number;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// Extra settings to make the payment method work
|
|
59
|
+
declare settings: {
|
|
60
|
+
stripe?: StripeConfig;
|
|
61
|
+
arcblock?: ArcblockConfig;
|
|
62
|
+
ethereum?: EthereumConfig;
|
|
63
|
+
bitcoin?: BitcoinConfig;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// What features are supported
|
|
67
|
+
declare features: {
|
|
68
|
+
recurring: boolean;
|
|
69
|
+
refund: boolean;
|
|
70
|
+
dispute: boolean;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
declare metadata: Record<string, any>;
|
|
74
|
+
|
|
75
|
+
declare created_at: CreationOptional<Date>;
|
|
76
|
+
|
|
77
|
+
declare updated_at: CreationOptional<Date>;
|
|
78
|
+
|
|
79
|
+
public static readonly GENESIS_ATTRIBUTES = {
|
|
80
|
+
id: {
|
|
81
|
+
type: DataTypes.STRING(30),
|
|
82
|
+
primaryKey: true,
|
|
83
|
+
allowNull: false,
|
|
84
|
+
defaultValue: nextId,
|
|
85
|
+
},
|
|
86
|
+
active: {
|
|
87
|
+
type: DataTypes.BOOLEAN,
|
|
88
|
+
allowNull: false,
|
|
89
|
+
},
|
|
90
|
+
livemode: {
|
|
91
|
+
type: DataTypes.BOOLEAN,
|
|
92
|
+
allowNull: false,
|
|
93
|
+
},
|
|
94
|
+
locked: {
|
|
95
|
+
type: DataTypes.BOOLEAN,
|
|
96
|
+
defaultValue: false,
|
|
97
|
+
},
|
|
98
|
+
type: {
|
|
99
|
+
type: DataTypes.ENUM('stripe', 'arcblock', 'ethereum', 'bitcoin'),
|
|
100
|
+
},
|
|
101
|
+
name: {
|
|
102
|
+
type: DataTypes.STRING(64),
|
|
103
|
+
},
|
|
104
|
+
description: {
|
|
105
|
+
type: DataTypes.STRING(512),
|
|
106
|
+
},
|
|
107
|
+
logo: {
|
|
108
|
+
type: DataTypes.STRING(512),
|
|
109
|
+
},
|
|
110
|
+
default_currency_id: {
|
|
111
|
+
type: DataTypes.STRING(15),
|
|
112
|
+
allowNull: true,
|
|
113
|
+
},
|
|
114
|
+
confirmation: {
|
|
115
|
+
type: DataTypes.JSON,
|
|
116
|
+
allowNull: false,
|
|
117
|
+
},
|
|
118
|
+
settings: {
|
|
119
|
+
type: DataTypes.JSON,
|
|
120
|
+
allowNull: false,
|
|
121
|
+
},
|
|
122
|
+
features: {
|
|
123
|
+
type: DataTypes.JSON,
|
|
124
|
+
allowNull: false,
|
|
125
|
+
},
|
|
126
|
+
metadata: {
|
|
127
|
+
type: DataTypes.JSON,
|
|
128
|
+
allowNull: true,
|
|
129
|
+
},
|
|
130
|
+
created_at: {
|
|
131
|
+
type: DataTypes.DATE,
|
|
132
|
+
defaultValue: DataTypes.NOW,
|
|
133
|
+
allowNull: false,
|
|
134
|
+
},
|
|
135
|
+
updated_at: {
|
|
136
|
+
type: DataTypes.DATE,
|
|
137
|
+
defaultValue: DataTypes.NOW,
|
|
138
|
+
allowNull: false,
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
143
|
+
public static initialize(sequelize: any) {
|
|
144
|
+
this.init(PaymentMethod.GENESIS_ATTRIBUTES, {
|
|
145
|
+
sequelize,
|
|
146
|
+
modelName: 'PaymentMethod',
|
|
147
|
+
tableName: 'payment_methods',
|
|
148
|
+
createdAt: 'created_at',
|
|
149
|
+
updatedAt: 'updated_at',
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
public static associate(models: any) {
|
|
154
|
+
this.hasMany(models.PaymentCurrency, {
|
|
155
|
+
foreignKey: 'payment_method_id',
|
|
156
|
+
as: 'payment_currencies',
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
public static expand(livemode?: boolean) {
|
|
161
|
+
return PaymentMethod.findAll({
|
|
162
|
+
where: { livemode: !!livemode },
|
|
163
|
+
order: [['created_at', 'DESC']],
|
|
164
|
+
include: [{ model: sequelize.models.PaymentCurrency, as: 'payment_currencies' }],
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export type TPaymentMethod = InferAttributes<PaymentMethod>;
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import isEmpty from 'lodash/isEmpty';
|
|
2
|
+
import {
|
|
3
|
+
CreationOptional,
|
|
4
|
+
DataTypes,
|
|
5
|
+
FindOptions,
|
|
6
|
+
InferAttributes,
|
|
7
|
+
InferCreationAttributes,
|
|
8
|
+
Model,
|
|
9
|
+
Op,
|
|
10
|
+
} from 'sequelize';
|
|
11
|
+
import type { LiteralUnion } from 'type-fest';
|
|
12
|
+
|
|
13
|
+
import { createEvent } from '../../libs/audit';
|
|
14
|
+
import { createIdGenerator, formatMetadata } from '../../libs/util';
|
|
15
|
+
import { sequelize } from '../sequelize';
|
|
16
|
+
import type { CustomUnitAmount, LineItem, PriceRecurring, PriceTier, TransformQuantity } from './types';
|
|
17
|
+
|
|
18
|
+
const nextId = createIdGenerator('price', 24);
|
|
19
|
+
|
|
20
|
+
// @link https://stripe.com/docs/api/prices
|
|
21
|
+
export class Price extends Model<InferAttributes<Price>, InferCreationAttributes<Price>> {
|
|
22
|
+
// Unique identifier for the object.
|
|
23
|
+
declare id: CreationOptional<string>;
|
|
24
|
+
|
|
25
|
+
// the product id
|
|
26
|
+
declare product_id: string;
|
|
27
|
+
|
|
28
|
+
// A brief description of the price, hidden from customers.
|
|
29
|
+
declare nickname?: string;
|
|
30
|
+
|
|
31
|
+
// Whether the price can be used for new purchases.
|
|
32
|
+
declare active: boolean;
|
|
33
|
+
declare livemode: boolean;
|
|
34
|
+
declare locked: CreationOptional<boolean>;
|
|
35
|
+
|
|
36
|
+
// One of one_time or recurring depending on whether the price is for a one-time purchase or a recurring (subscription) purchase.
|
|
37
|
+
declare type: LiteralUnion<'one_time' | 'recurring', string>;
|
|
38
|
+
|
|
39
|
+
// Describes how to compute the price per period.
|
|
40
|
+
declare billing_scheme: LiteralUnion<'per_unit' | 'tiered', string>;
|
|
41
|
+
|
|
42
|
+
// The unit amount in big number to be charged, represented as a whole integer if possible
|
|
43
|
+
declare unit_amount: string;
|
|
44
|
+
|
|
45
|
+
// The recurring components of a price such as interval and usage_type.
|
|
46
|
+
declare recurring?: PriceRecurring;
|
|
47
|
+
|
|
48
|
+
// Defines if the tiering price should be graduated or volume based.
|
|
49
|
+
declare tiers_mode?: LiteralUnion<'graduated' | 'volume', string>;
|
|
50
|
+
|
|
51
|
+
// Each element represents a pricing tier. This parameter requires billing_scheme to be set to tiered
|
|
52
|
+
declare tiers?: PriceTier[];
|
|
53
|
+
|
|
54
|
+
// When set, provides configuration for the amount to be adjusted by the customer during Checkout Sessions and Payment Links.
|
|
55
|
+
declare custom_unit_amount?: CustomUnitAmount;
|
|
56
|
+
|
|
57
|
+
// A lookup key used to retrieve prices dynamically from a static string
|
|
58
|
+
declare lookup_key?: string;
|
|
59
|
+
|
|
60
|
+
// Set of key-value pairs that you can attach to an object.
|
|
61
|
+
declare metadata: Record<string, any>;
|
|
62
|
+
|
|
63
|
+
declare transform_quantity?: TransformQuantity;
|
|
64
|
+
|
|
65
|
+
// Payment currency id
|
|
66
|
+
declare currency_id: string;
|
|
67
|
+
|
|
68
|
+
declare upsell?: {
|
|
69
|
+
upsells_to_id: string;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
declare created_at: CreationOptional<Date>;
|
|
73
|
+
declare created_via: LiteralUnion<'api' | 'dashboard' | 'portal', string>;
|
|
74
|
+
declare updated_at: CreationOptional<Date>;
|
|
75
|
+
|
|
76
|
+
public static readonly GENESIS_ATTRIBUTES = {
|
|
77
|
+
id: {
|
|
78
|
+
type: DataTypes.STRING(32),
|
|
79
|
+
primaryKey: true,
|
|
80
|
+
allowNull: false,
|
|
81
|
+
defaultValue: nextId,
|
|
82
|
+
},
|
|
83
|
+
product_id: {
|
|
84
|
+
type: DataTypes.STRING(32),
|
|
85
|
+
allowNull: false,
|
|
86
|
+
},
|
|
87
|
+
nickname: {
|
|
88
|
+
type: DataTypes.STRING(512),
|
|
89
|
+
},
|
|
90
|
+
active: {
|
|
91
|
+
type: DataTypes.BOOLEAN,
|
|
92
|
+
allowNull: false,
|
|
93
|
+
},
|
|
94
|
+
livemode: {
|
|
95
|
+
type: DataTypes.BOOLEAN,
|
|
96
|
+
allowNull: false,
|
|
97
|
+
},
|
|
98
|
+
locked: {
|
|
99
|
+
type: DataTypes.BOOLEAN,
|
|
100
|
+
defaultValue: false,
|
|
101
|
+
},
|
|
102
|
+
type: {
|
|
103
|
+
type: DataTypes.ENUM('one_time', 'recurring'),
|
|
104
|
+
},
|
|
105
|
+
billing_scheme: {
|
|
106
|
+
type: DataTypes.ENUM('per_unit', 'tiered'),
|
|
107
|
+
},
|
|
108
|
+
unit_amount: {
|
|
109
|
+
type: DataTypes.STRING(32),
|
|
110
|
+
allowNull: false,
|
|
111
|
+
},
|
|
112
|
+
recurring: {
|
|
113
|
+
type: DataTypes.JSON,
|
|
114
|
+
allowNull: true,
|
|
115
|
+
},
|
|
116
|
+
tiers_mode: {
|
|
117
|
+
type: DataTypes.ENUM('graduated', 'volume'),
|
|
118
|
+
allowNull: true,
|
|
119
|
+
},
|
|
120
|
+
tiers: {
|
|
121
|
+
type: DataTypes.JSON,
|
|
122
|
+
allowNull: true,
|
|
123
|
+
},
|
|
124
|
+
custom_unit_amount: {
|
|
125
|
+
type: DataTypes.JSON,
|
|
126
|
+
allowNull: true,
|
|
127
|
+
},
|
|
128
|
+
lookup_key: {
|
|
129
|
+
type: DataTypes.STRING(128),
|
|
130
|
+
allowNull: true,
|
|
131
|
+
},
|
|
132
|
+
metadata: {
|
|
133
|
+
type: DataTypes.JSON,
|
|
134
|
+
allowNull: true,
|
|
135
|
+
},
|
|
136
|
+
transform_quantity: {
|
|
137
|
+
type: DataTypes.JSON,
|
|
138
|
+
allowNull: true,
|
|
139
|
+
},
|
|
140
|
+
currency_id: {
|
|
141
|
+
type: DataTypes.STRING(16),
|
|
142
|
+
},
|
|
143
|
+
created_at: {
|
|
144
|
+
type: DataTypes.DATE,
|
|
145
|
+
defaultValue: DataTypes.NOW,
|
|
146
|
+
allowNull: false,
|
|
147
|
+
},
|
|
148
|
+
created_via: {
|
|
149
|
+
type: DataTypes.ENUM('api', 'dashboard', 'portal'),
|
|
150
|
+
},
|
|
151
|
+
updated_at: {
|
|
152
|
+
type: DataTypes.DATE,
|
|
153
|
+
defaultValue: DataTypes.NOW,
|
|
154
|
+
allowNull: false,
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
159
|
+
public static initialize(sequelize: any) {
|
|
160
|
+
this.init(Price.GENESIS_ATTRIBUTES, {
|
|
161
|
+
sequelize,
|
|
162
|
+
modelName: 'Price',
|
|
163
|
+
tableName: 'prices',
|
|
164
|
+
createdAt: 'created_at',
|
|
165
|
+
updatedAt: 'updated_at',
|
|
166
|
+
hooks: {
|
|
167
|
+
afterCreate: (model: Price, options) =>
|
|
168
|
+
createEvent('Price', 'price.created', model, options).catch(console.error),
|
|
169
|
+
afterUpdate: (model: Price, options) =>
|
|
170
|
+
createEvent('Price', 'price.updated', model, options).catch(console.error),
|
|
171
|
+
afterDestroy: (model: Price, options) =>
|
|
172
|
+
createEvent('Price', 'price.deleted', model, options).catch(console.error),
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
public static associate(models: any) {
|
|
178
|
+
this.hasOne(models.Product, {
|
|
179
|
+
sourceKey: 'product_id',
|
|
180
|
+
foreignKey: 'id',
|
|
181
|
+
as: 'product',
|
|
182
|
+
});
|
|
183
|
+
this.hasOne(models.PaymentCurrency, {
|
|
184
|
+
sourceKey: 'currency_id',
|
|
185
|
+
foreignKey: 'id',
|
|
186
|
+
as: 'currency',
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
public static getModel(price: TPrice) {
|
|
191
|
+
if (price.billing_scheme === 'tiered') {
|
|
192
|
+
return price.tiers_mode;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (price.transform_quantity) {
|
|
196
|
+
return 'package';
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return 'standard';
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
public static format(price: Partial<TPrice & { model: string }>) {
|
|
203
|
+
if (price.type === 'recurring') {
|
|
204
|
+
if (price.recurring) {
|
|
205
|
+
price.recurring.usage_type = price.recurring?.metered ? 'metered' : 'licensed';
|
|
206
|
+
} else {
|
|
207
|
+
throw new Error('recurring config is required for recurring prices');
|
|
208
|
+
}
|
|
209
|
+
} else {
|
|
210
|
+
price.recurring = undefined;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (price.model && ['graduated', 'volume'].includes(price.model)) {
|
|
214
|
+
price.billing_scheme = 'tiered';
|
|
215
|
+
price.tiers_mode = price.model;
|
|
216
|
+
if (isEmpty(price.tiers)) {
|
|
217
|
+
throw new Error('tiers is required for graduated and volume prices');
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
price.billing_scheme = 'per_unit';
|
|
221
|
+
delete price.tiers;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (price.model !== 'package') {
|
|
225
|
+
delete price.transform_quantity;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
price.metadata = formatMetadata(price.metadata);
|
|
229
|
+
|
|
230
|
+
return price;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
public static async expand(items: LineItem[], deep: boolean = true): Promise<(LineItem & { price: TPrice })[]> {
|
|
234
|
+
const priceIds: string[] = items.map((i) => i.price_id);
|
|
235
|
+
const prices = await Price.findAll({
|
|
236
|
+
where: { id: priceIds },
|
|
237
|
+
include: deep ? [{ model: sequelize.models.Product, as: 'product' }] : [],
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
return items.map((x) => ({
|
|
241
|
+
...x,
|
|
242
|
+
price: prices.find((p) => p.id === x.price_id),
|
|
243
|
+
})) as (LineItem & { price: TPrice })[];
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
public static async insert(price: TPrice & { model: string }) {
|
|
247
|
+
if (price.lookup_key) {
|
|
248
|
+
const exist = await this.count({ where: { lookup_key: price.lookup_key } });
|
|
249
|
+
if (exist) {
|
|
250
|
+
throw new Error('lookup_key already exists');
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// @ts-ignore
|
|
255
|
+
return this.create(this.format(price));
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
public static findByPkOrLookupKey(id: string, options: FindOptions<Price> = {}) {
|
|
259
|
+
return this.findOne({
|
|
260
|
+
where: { [Op.or]: [{ id }, { lookup_key: id }] },
|
|
261
|
+
...options,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export type TPrice = InferAttributes<Price>;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/lines-between-class-members */
|
|
2
|
+
import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes, Model } from 'sequelize';
|
|
3
|
+
import type { LiteralUnion } from 'type-fest';
|
|
4
|
+
|
|
5
|
+
import { createEvent } from '../../libs/audit';
|
|
6
|
+
import { createIdGenerator } from '../../libs/util';
|
|
7
|
+
|
|
8
|
+
const nextId = createIdGenerator('prod', 14);
|
|
9
|
+
|
|
10
|
+
export type ProductFeature = { name: string };
|
|
11
|
+
|
|
12
|
+
// FIXME: default_price_id is not implemented
|
|
13
|
+
// @link https://stripe.com/docs/api/products
|
|
14
|
+
export class Product extends Model<InferAttributes<Product>, InferCreationAttributes<Product>> {
|
|
15
|
+
// Unique identifier for the object.
|
|
16
|
+
declare id: CreationOptional<string>;
|
|
17
|
+
|
|
18
|
+
// Whether the product is currently available for purchase.
|
|
19
|
+
declare active: boolean;
|
|
20
|
+
declare livemode: boolean;
|
|
21
|
+
declare locked: CreationOptional<boolean>;
|
|
22
|
+
|
|
23
|
+
declare type: LiteralUnion<'service' | 'good', string>;
|
|
24
|
+
|
|
25
|
+
// The product’s name, meant to be displayable to the customer.
|
|
26
|
+
declare name: string;
|
|
27
|
+
|
|
28
|
+
// The product’s description, meant to be displayable to the customer.
|
|
29
|
+
declare description: string;
|
|
30
|
+
|
|
31
|
+
// A list of up to 8 URLs of images for this product, meant to be displayable to the customer.
|
|
32
|
+
declare images: any[];
|
|
33
|
+
|
|
34
|
+
// A list of up to 15 features for this product. These are displayed in pricing tables.
|
|
35
|
+
declare features: ProductFeature[];
|
|
36
|
+
|
|
37
|
+
// A label that represents units of this product.
|
|
38
|
+
declare unit_label?: string;
|
|
39
|
+
|
|
40
|
+
// The ID of the Price object that is the default price for this product.
|
|
41
|
+
declare default_price_id?: string;
|
|
42
|
+
|
|
43
|
+
declare metadata?: Record<string, any>;
|
|
44
|
+
|
|
45
|
+
declare statement_descriptor?: string;
|
|
46
|
+
|
|
47
|
+
// If set, we will mint an nft to consumer on purchase
|
|
48
|
+
declare nft_factory?: string;
|
|
49
|
+
|
|
50
|
+
// TODO: goods related props are not supported
|
|
51
|
+
// declare package_dimensions?: any;
|
|
52
|
+
// declare shippable?: boolean;
|
|
53
|
+
|
|
54
|
+
declare created_at: CreationOptional<Date>;
|
|
55
|
+
|
|
56
|
+
declare created_via: LiteralUnion<'api' | 'dashboard' | 'portal', string>;
|
|
57
|
+
|
|
58
|
+
declare updated_at: CreationOptional<Date>;
|
|
59
|
+
|
|
60
|
+
public static readonly GENESIS_ATTRIBUTES = {
|
|
61
|
+
id: {
|
|
62
|
+
type: DataTypes.STRING(18),
|
|
63
|
+
primaryKey: true,
|
|
64
|
+
allowNull: false,
|
|
65
|
+
defaultValue: nextId,
|
|
66
|
+
},
|
|
67
|
+
active: {
|
|
68
|
+
type: DataTypes.BOOLEAN,
|
|
69
|
+
allowNull: false,
|
|
70
|
+
},
|
|
71
|
+
livemode: {
|
|
72
|
+
type: DataTypes.BOOLEAN,
|
|
73
|
+
allowNull: false,
|
|
74
|
+
},
|
|
75
|
+
locked: {
|
|
76
|
+
type: DataTypes.BOOLEAN,
|
|
77
|
+
defaultValue: false,
|
|
78
|
+
},
|
|
79
|
+
type: {
|
|
80
|
+
type: DataTypes.ENUM('service', 'good'),
|
|
81
|
+
},
|
|
82
|
+
name: {
|
|
83
|
+
type: DataTypes.STRING(512),
|
|
84
|
+
},
|
|
85
|
+
description: {
|
|
86
|
+
type: DataTypes.STRING(2048),
|
|
87
|
+
},
|
|
88
|
+
images: {
|
|
89
|
+
type: DataTypes.JSON,
|
|
90
|
+
defaultValue: [],
|
|
91
|
+
},
|
|
92
|
+
features: {
|
|
93
|
+
type: DataTypes.JSON,
|
|
94
|
+
defaultValue: [],
|
|
95
|
+
},
|
|
96
|
+
unit_label: {
|
|
97
|
+
type: DataTypes.STRING(32),
|
|
98
|
+
allowNull: true,
|
|
99
|
+
},
|
|
100
|
+
default_price_id: {
|
|
101
|
+
type: DataTypes.STRING(32),
|
|
102
|
+
allowNull: true,
|
|
103
|
+
},
|
|
104
|
+
metadata: {
|
|
105
|
+
type: DataTypes.JSON,
|
|
106
|
+
allowNull: true,
|
|
107
|
+
},
|
|
108
|
+
statement_descriptor: {
|
|
109
|
+
type: DataTypes.STRING(32),
|
|
110
|
+
allowNull: true,
|
|
111
|
+
},
|
|
112
|
+
nft_factory: {
|
|
113
|
+
type: DataTypes.STRING(40),
|
|
114
|
+
allowNull: true,
|
|
115
|
+
},
|
|
116
|
+
created_at: {
|
|
117
|
+
type: DataTypes.DATE,
|
|
118
|
+
defaultValue: DataTypes.NOW,
|
|
119
|
+
allowNull: false,
|
|
120
|
+
},
|
|
121
|
+
created_via: {
|
|
122
|
+
type: DataTypes.ENUM('api', 'dashboard', 'portal'),
|
|
123
|
+
},
|
|
124
|
+
updated_at: {
|
|
125
|
+
type: DataTypes.DATE,
|
|
126
|
+
defaultValue: DataTypes.NOW,
|
|
127
|
+
allowNull: false,
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
public static initialize(sequelize: any) {
|
|
132
|
+
this.init(Product.GENESIS_ATTRIBUTES, {
|
|
133
|
+
sequelize,
|
|
134
|
+
modelName: 'Product',
|
|
135
|
+
tableName: 'products',
|
|
136
|
+
createdAt: 'created_at',
|
|
137
|
+
updatedAt: 'updated_at',
|
|
138
|
+
hooks: {
|
|
139
|
+
afterCreate: (model: Product, options) =>
|
|
140
|
+
createEvent('Product', 'product.created', model, options).catch(console.error),
|
|
141
|
+
afterUpdate: (model: Product, options) =>
|
|
142
|
+
createEvent('Product', 'product.updated', model, options).catch(console.error),
|
|
143
|
+
afterDestroy: (model: Product, options) =>
|
|
144
|
+
createEvent('Product', 'product.deleted', model, options).catch(console.error),
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
public static associate(models: any) {
|
|
150
|
+
this.hasMany(models.Price, {
|
|
151
|
+
foreignKey: 'product_id',
|
|
152
|
+
as: 'prices',
|
|
153
|
+
});
|
|
154
|
+
this.hasOne(models.Price, {
|
|
155
|
+
sourceKey: 'default_price_id',
|
|
156
|
+
foreignKey: 'id',
|
|
157
|
+
as: 'default_price',
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export type TProduct = InferAttributes<Product>;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/lines-between-class-members */
|
|
2
|
+
import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes, Model } from 'sequelize';
|
|
3
|
+
|
|
4
|
+
import { createIdGenerator } from '../../libs/util';
|
|
5
|
+
|
|
6
|
+
const nextId = createIdGenerator('promo', 24);
|
|
7
|
+
|
|
8
|
+
// @link https://stripe.com/docs/api/promotion_codes
|
|
9
|
+
// eslint-disable-next-line prettier/prettier
|
|
10
|
+
export class PromotionCode extends Model<InferAttributes<PromotionCode>, InferCreationAttributes<PromotionCode>> {
|
|
11
|
+
declare id: CreationOptional<string>;
|
|
12
|
+
declare livemode: boolean;
|
|
13
|
+
declare active: boolean;
|
|
14
|
+
|
|
15
|
+
declare code: string;
|
|
16
|
+
|
|
17
|
+
declare coupon_id: string;
|
|
18
|
+
|
|
19
|
+
// redeem conditions
|
|
20
|
+
declare max_redemptions?: number;
|
|
21
|
+
declare restrictions?: {
|
|
22
|
+
currency_options?: Record<string, number>;
|
|
23
|
+
first_time_transaction?: boolean;
|
|
24
|
+
minimum_amount?: number;
|
|
25
|
+
minimum_amount_currency?: string;
|
|
26
|
+
};
|
|
27
|
+
declare customer_id?: string;
|
|
28
|
+
|
|
29
|
+
declare times_redeemed?: number;
|
|
30
|
+
|
|
31
|
+
declare expires_at: CreationOptional<Date>;
|
|
32
|
+
declare created_at: CreationOptional<Date>;
|
|
33
|
+
declare updated_at: CreationOptional<Date>;
|
|
34
|
+
|
|
35
|
+
public static readonly GENESIS_ATTRIBUTES = {
|
|
36
|
+
id: {
|
|
37
|
+
type: DataTypes.STRING(30),
|
|
38
|
+
primaryKey: true,
|
|
39
|
+
allowNull: false,
|
|
40
|
+
defaultValue: nextId,
|
|
41
|
+
},
|
|
42
|
+
livemode: {
|
|
43
|
+
type: DataTypes.BOOLEAN,
|
|
44
|
+
allowNull: false,
|
|
45
|
+
},
|
|
46
|
+
active: {
|
|
47
|
+
type: DataTypes.BOOLEAN,
|
|
48
|
+
defaultValue: true,
|
|
49
|
+
},
|
|
50
|
+
code: {
|
|
51
|
+
type: DataTypes.STRING(16),
|
|
52
|
+
allowNull: false,
|
|
53
|
+
},
|
|
54
|
+
coupon_id: {
|
|
55
|
+
type: DataTypes.STRING(30),
|
|
56
|
+
allowNull: true,
|
|
57
|
+
},
|
|
58
|
+
max_redemptions: {
|
|
59
|
+
type: DataTypes.INTEGER,
|
|
60
|
+
allowNull: true,
|
|
61
|
+
},
|
|
62
|
+
restrictions: {
|
|
63
|
+
type: DataTypes.JSON,
|
|
64
|
+
defaultValue: {},
|
|
65
|
+
},
|
|
66
|
+
customer_id: {
|
|
67
|
+
type: DataTypes.STRING(30),
|
|
68
|
+
allowNull: true,
|
|
69
|
+
},
|
|
70
|
+
times_redeemed: {
|
|
71
|
+
type: DataTypes.INTEGER,
|
|
72
|
+
defaultValue: 0,
|
|
73
|
+
},
|
|
74
|
+
metadata: {
|
|
75
|
+
type: DataTypes.JSON,
|
|
76
|
+
allowNull: true,
|
|
77
|
+
},
|
|
78
|
+
expires_at: {
|
|
79
|
+
type: DataTypes.DATE,
|
|
80
|
+
allowNull: true,
|
|
81
|
+
},
|
|
82
|
+
created_at: {
|
|
83
|
+
type: DataTypes.DATE,
|
|
84
|
+
defaultValue: DataTypes.NOW,
|
|
85
|
+
allowNull: false,
|
|
86
|
+
},
|
|
87
|
+
updated_at: {
|
|
88
|
+
type: DataTypes.DATE,
|
|
89
|
+
defaultValue: DataTypes.NOW,
|
|
90
|
+
allowNull: false,
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
public static initialize(sequelize: any) {
|
|
95
|
+
this.init(PromotionCode.GENESIS_ATTRIBUTES, {
|
|
96
|
+
sequelize,
|
|
97
|
+
modelName: 'PromotionCode',
|
|
98
|
+
tableName: 'promotion_codes',
|
|
99
|
+
createdAt: 'created_at',
|
|
100
|
+
updatedAt: 'updated_at',
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public static associate(models: any) {
|
|
105
|
+
this.belongsTo(models.Coupon, {
|
|
106
|
+
foreignKey: 'coupon_id',
|
|
107
|
+
as: 'coupon',
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export type TPromotionCode = InferAttributes<PromotionCode>;
|