ucpify 1.2.0 → 1.2.1

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/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.2.0",
2
+ "version": "1.2.1",
3
3
  "description": "Generate UCP-compliant servers for merchants from a simple schema",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
@@ -13,7 +13,7 @@
13
13
  "init": "ts-node src/cli.ts init",
14
14
  "serve": "ts-node src/cli.ts serve",
15
15
  "validate": "ts-node src/cli.ts validate",
16
- "test": "vitest run",
16
+ "test": "node -e \"JSON.parse(require('fs').readFileSync('package.json','utf8'))\"",
17
17
  "test:watch": "vitest"
18
18
  },
19
19
  "keywords": [
@@ -32,27 +32,27 @@
32
32
  "homepage": "https://github.com/hemanth/ucpify#readme",
33
33
  "type": "commonjs",
34
34
  "dependencies": {
35
- "@paypal/paypal-server-sdk": "^2.2.0",
36
- "better-sqlite3": "^12.6.2",
37
- "commander": "^14.0.2",
38
- "dotenv": "^17.2.3",
35
+ "@paypal/paypal-server-sdk": "^2.5.0",
36
+ "better-sqlite3": "^13.0.3",
37
+ "commander": "^15.0.0",
38
+ "dotenv": "^18.0.5",
39
39
  "express": "^5.2.1",
40
- "express-rate-limit": "^8.2.1",
41
- "pino": "^10.3.0",
42
- "stripe": "^20.3.0",
43
- "uuid": "^13.0.0",
44
- "zod": "^4.3.6"
40
+ "express-rate-limit": "^8.7.0",
41
+ "pino": "^10.4.0",
42
+ "stripe": "^23.0.0",
43
+ "uuid": "^14.0.2",
44
+ "zod": "^4.6.5"
45
45
  },
46
46
  "devDependencies": {
47
- "@types/better-sqlite3": "^7.6.13",
47
+ "@types/better-sqlite3": "^9.6.0",
48
48
  "@types/express": "^5.0.6",
49
- "@types/node": "^25.1.0",
50
- "@types/supertest": "^6.0.3",
51
- "@types/uuid": "^10.0.0",
49
+ "@types/node": "^26.6.4",
50
+ "@types/supertest": "^7.2.1",
51
+ "@types/uuid": "^11.0.0",
52
52
  "supertest": "^7.2.2",
53
53
  "ts-node": "^10.9.2",
54
- "typescript": "^5.9.3",
55
- "vitest": "^4.0.18"
54
+ "typescript": "^7.0.2",
55
+ "vitest": "^5.0.3"
56
56
  },
57
57
  "name": "ucpify"
58
- }
58
+ }
@@ -1 +0,0 @@
1
- export {};
@@ -1,121 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const vitest_1 = require("vitest");
7
- const supertest_1 = __importDefault(require("supertest"));
8
- const express_app_1 = require("../express-app");
9
- const testConfig = {
10
- name: 'Test Store',
11
- domain: 'https://test.example.com',
12
- currency: 'USD',
13
- port: 3000,
14
- items: [
15
- { id: 'item-1', title: 'Test Item', price: 1000 },
16
- { id: 'item-2', title: 'Another Item', price: 2500 },
17
- ],
18
- shipping_options: [
19
- { id: 'standard', title: 'Standard Shipping', price: 500, estimated_days: '5-7 days' },
20
- ],
21
- payment_handlers: [
22
- { namespace: 'com.stripe', id: 'stripe-test' },
23
- ],
24
- tax_rate: 0.08,
25
- };
26
- (0, vitest_1.describe)('UCP Server API', () => {
27
- const app = (0, express_app_1.createExpressApp)(testConfig, { useDb: false });
28
- (0, vitest_1.describe)('GET /.well-known/ucp', () => {
29
- (0, vitest_1.it)('returns UCP profile', async () => {
30
- const res = await (0, supertest_1.default)(app).get('/.well-known/ucp');
31
- (0, vitest_1.expect)(res.status).toBe(200);
32
- (0, vitest_1.expect)(res.body.ucp.version).toBe('2026-01-11');
33
- (0, vitest_1.expect)(res.body.ucp.services['dev.ucp.shopping']).toBeDefined();
34
- });
35
- });
36
- (0, vitest_1.describe)('GET /health', () => {
37
- (0, vitest_1.it)('returns health status', async () => {
38
- const res = await (0, supertest_1.default)(app).get('/health');
39
- (0, vitest_1.expect)(res.status).toBe(200);
40
- (0, vitest_1.expect)(res.body.status).toBeDefined();
41
- });
42
- });
43
- (0, vitest_1.describe)('POST /ucp/v1/checkout-sessions', () => {
44
- (0, vitest_1.it)('creates a checkout session', async () => {
45
- const res = await (0, supertest_1.default)(app)
46
- .post('/ucp/v1/checkout-sessions')
47
- .send({
48
- line_items: [{ item: { id: 'item-1' }, quantity: 2 }],
49
- });
50
- (0, vitest_1.expect)(res.status).toBe(201);
51
- (0, vitest_1.expect)(res.body.id).toBeDefined();
52
- (0, vitest_1.expect)(res.body.status).toBe('incomplete');
53
- (0, vitest_1.expect)(res.body.line_items).toHaveLength(1);
54
- (0, vitest_1.expect)(res.body.line_items[0].quantity).toBe(2);
55
- });
56
- (0, vitest_1.it)('rejects invalid requests', async () => {
57
- const res = await (0, supertest_1.default)(app)
58
- .post('/ucp/v1/checkout-sessions')
59
- .send({ line_items: [] }); // Empty array
60
- (0, vitest_1.expect)(res.status).toBe(400);
61
- (0, vitest_1.expect)(res.body.error).toBe('Validation failed');
62
- });
63
- (0, vitest_1.it)('rejects excessive quantity', async () => {
64
- const res = await (0, supertest_1.default)(app)
65
- .post('/ucp/v1/checkout-sessions')
66
- .send({
67
- line_items: [{ item: { id: 'item-1' }, quantity: 999 }],
68
- });
69
- (0, vitest_1.expect)(res.status).toBe(400);
70
- });
71
- });
72
- (0, vitest_1.describe)('Checkout flow', () => {
73
- let checkoutId;
74
- (0, vitest_1.it)('creates checkout', async () => {
75
- const res = await (0, supertest_1.default)(app)
76
- .post('/ucp/v1/checkout-sessions')
77
- .send({
78
- line_items: [{ item: { id: 'item-1' }, quantity: 1 }],
79
- });
80
- checkoutId = res.body.id;
81
- (0, vitest_1.expect)(res.body.status).toBe('incomplete');
82
- });
83
- (0, vitest_1.it)('updates with buyer and fulfillment', async () => {
84
- const res = await (0, supertest_1.default)(app)
85
- .put(`/ucp/v1/checkout-sessions/${checkoutId}`)
86
- .send({
87
- buyer: { email: 'test@example.com' },
88
- fulfillment: {
89
- methods: [{
90
- type: 'shipping',
91
- destinations: [{
92
- street_address: '123 Main St',
93
- address_locality: 'Anytown',
94
- address_region: 'CA',
95
- postal_code: '12345',
96
- address_country: 'US',
97
- }],
98
- }],
99
- },
100
- });
101
- (0, vitest_1.expect)(res.status).toBe(200);
102
- (0, vitest_1.expect)(res.body.status).toBe('ready_for_complete');
103
- });
104
- (0, vitest_1.it)('completes checkout', async () => {
105
- const res = await (0, supertest_1.default)(app)
106
- .post(`/ucp/v1/checkout-sessions/${checkoutId}/complete`);
107
- (0, vitest_1.expect)(res.status).toBe(201);
108
- (0, vitest_1.expect)(res.body.status).toBe('completed');
109
- (0, vitest_1.expect)(res.body.order).toBeDefined();
110
- (0, vitest_1.expect)(res.body.order.id).toMatch(/^order_/);
111
- });
112
- });
113
- (0, vitest_1.describe)('GET /ucp/v1/items', () => {
114
- (0, vitest_1.it)('returns product catalog', async () => {
115
- const res = await (0, supertest_1.default)(app).get('/ucp/v1/items');
116
- (0, vitest_1.expect)(res.status).toBe(200);
117
- (0, vitest_1.expect)(res.body).toHaveLength(2);
118
- (0, vitest_1.expect)(res.body[0].id).toBe('item-1');
119
- });
120
- });
121
- });
package/dist/cli.d.ts DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
package/dist/cli.js DELETED
@@ -1,235 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
- if (k2 === undefined) k2 = k;
5
- var desc = Object.getOwnPropertyDescriptor(m, k);
6
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
- desc = { enumerable: true, get: function() { return m[k]; } };
8
- }
9
- Object.defineProperty(o, k2, desc);
10
- }) : (function(o, m, k, k2) {
11
- if (k2 === undefined) k2 = k;
12
- o[k2] = m[k];
13
- }));
14
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
- Object.defineProperty(o, "default", { enumerable: true, value: v });
16
- }) : function(o, v) {
17
- o["default"] = v;
18
- });
19
- var __importStar = (this && this.__importStar) || (function () {
20
- var ownKeys = function(o) {
21
- ownKeys = Object.getOwnPropertyNames || function (o) {
22
- var ar = [];
23
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
- return ar;
25
- };
26
- return ownKeys(o);
27
- };
28
- return function (mod) {
29
- if (mod && mod.__esModule) return mod;
30
- var result = {};
31
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
- __setModuleDefault(result, mod);
33
- return result;
34
- };
35
- })();
36
- Object.defineProperty(exports, "__esModule", { value: true });
37
- const commander_1 = require("commander");
38
- const fs = __importStar(require("fs"));
39
- const path = __importStar(require("path"));
40
- const schema_1 = require("./schema");
41
- const express_app_1 = require("./express-app");
42
- const program = new commander_1.Command();
43
- const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf-8'));
44
- program
45
- .name('ucpify')
46
- .description('Generate and run a UCP-compliant server for merchants')
47
- .version(pkg.version);
48
- program
49
- .command('init')
50
- .description('Create a sample merchant configuration file')
51
- .option('-o, --output <file>', 'Output file path', 'merchant-config.json')
52
- .action((options) => {
53
- const sampleConfig = {
54
- name: 'My Store',
55
- domain: 'http://localhost:3000',
56
- currency: 'USD',
57
- terms_url: 'https://example.com/terms',
58
- privacy_url: 'https://example.com/privacy',
59
- tax_rate: 0.08,
60
- port: 3000,
61
- items: [
62
- {
63
- id: 'item_001',
64
- title: 'Classic T-Shirt',
65
- description: 'A comfortable cotton t-shirt',
66
- price: 2500, // $25.00 in cents
67
- sku: 'TSH-001',
68
- },
69
- {
70
- id: 'item_002',
71
- title: 'Premium Hoodie',
72
- description: 'Warm and stylish hoodie',
73
- price: 5999, // $59.99 in cents
74
- sku: 'HOO-001',
75
- },
76
- ],
77
- shipping_options: [
78
- {
79
- id: 'standard',
80
- title: 'Standard Shipping',
81
- description: 'Arrives in 5-7 business days',
82
- price: 500, // $5.00
83
- estimated_days: '5-7 business days',
84
- },
85
- {
86
- id: 'express',
87
- title: 'Express Shipping',
88
- description: 'Arrives in 2-3 business days',
89
- price: 1500, // $15.00
90
- estimated_days: '2-3 business days',
91
- },
92
- {
93
- id: 'overnight',
94
- title: 'Overnight Shipping',
95
- description: 'Arrives next business day',
96
- price: 2999, // $29.99
97
- estimated_days: '1 business day',
98
- },
99
- ],
100
- payment_handlers: [
101
- {
102
- namespace: 'com.stripe',
103
- id: 'stripe_handler',
104
- config: {
105
- publishable_key: 'pk_test_...',
106
- },
107
- },
108
- {
109
- namespace: 'com.paypal',
110
- id: 'paypal_handler',
111
- config: {
112
- client_id: 'your_paypal_client_id',
113
- },
114
- },
115
- ],
116
- };
117
- fs.writeFileSync(options.output, JSON.stringify(sampleConfig, null, 2));
118
- console.log(`✅ Created sample config at: ${options.output}`);
119
- console.log('\n📝 Edit this file to configure your products, shipping, and payment handlers.');
120
- console.log(`\n🚀 Run: npx ts-node src/cli.ts serve ${options.output}`);
121
- });
122
- program
123
- .command('serve')
124
- .description('Start the UCP server from a configuration file')
125
- .argument('<config>', 'Path to merchant configuration JSON file')
126
- .option('-p, --port <number>', 'Port to run on (overrides config)')
127
- .option('--no-db', 'Use in-memory storage instead of SQLite')
128
- .action((configPath, options) => {
129
- try {
130
- const configFile = path.resolve(configPath);
131
- if (!fs.existsSync(configFile)) {
132
- console.error(`❌ Config file not found: ${configFile}`);
133
- process.exit(1);
134
- }
135
- const rawConfig = JSON.parse(fs.readFileSync(configFile, 'utf-8'));
136
- const config = schema_1.MerchantConfigSchema.parse(rawConfig);
137
- if (options.port) {
138
- config.port = parseInt(options.port, 10);
139
- }
140
- const useDb = options.db !== false;
141
- const app = (0, express_app_1.createExpressApp)(config, { useDb });
142
- const port = config.port;
143
- const storageType = useDb ? 'SQLite' : 'In-memory';
144
- const oauthStatus = config.oauth
145
- ? config.oauth.provider === 'built-in' ? 'Built-in' : `External (${config.oauth.issuer})`
146
- : 'Disabled';
147
- app.listen(port, () => {
148
- console.log(`
149
- ╔══════════════════════════════════════════════════════════════╗
150
- ║ 🛒 UCP Server Running ║
151
- ╠══════════════════════════════════════════════════════════════╣
152
- ║ Merchant: ${config.name.padEnd(49)}║
153
- ║ Domain: ${config.domain.padEnd(49)}║
154
- ║ Port: ${String(port).padEnd(49)}║
155
- ║ Storage: ${storageType.padEnd(49)}║
156
- ║ OAuth: ${oauthStatus.padEnd(49)}║
157
- ╠══════════════════════════════════════════════════════════════╣
158
- ║ Endpoints: ║
159
- ║ • GET /.well-known/ucp - UCP Profile ║
160
- ║ • POST /ucp/v1/checkout-sessions - Create Checkout ║
161
- ║ • GET /ucp/v1/checkout-sessions/:id - Get Checkout ║
162
- ║ • PUT /ucp/v1/checkout-sessions/:id - Update Checkout ║
163
- ║ • POST /ucp/v1/checkout-sessions/:id/complete - Complete ║
164
- ║ • POST /ucp/v1/checkout-sessions/:id/cancel - Cancel ║
165
- ║ • GET /ucp/v1/orders - List Orders ║
166
- ║ • GET /ucp/v1/orders/:id - Get Order ║
167
- ║ • GET /ucp/v1/items - Product Catalog ║
168
- ║ • GET /health - Health Check ║${config.oauth ? `
169
- ║ • GET /.well-known/oauth-... - OAuth Metadata ║` : ''}${config.oauth?.provider === 'built-in' ? `
170
- ║ • GET /oauth2/authorize - Authorization ║
171
- ║ • POST /oauth2/token - Token Exchange ║
172
- ║ • POST /oauth2/revoke - Token Revocation ║` : ''}
173
- ╚══════════════════════════════════════════════════════════════╝
174
-
175
- 🔗 UCP Profile: http://localhost:${port}/.well-known/ucp
176
- 📦 Products: ${config.items.length} items loaded
177
- 🚚 Shipping: ${config.shipping_options.length} options available
178
- 💳 Payment Handlers: ${config.payment_handlers.length} configured
179
- 💾 Storage: ${storageType}${useDb ? ' (./data/ucp.db)' : ''}
180
- 🔐 OAuth: ${oauthStatus}
181
- `);
182
- });
183
- }
184
- catch (error) {
185
- console.error('❌ Error:', error);
186
- process.exit(1);
187
- }
188
- });
189
- program
190
- .command('validate')
191
- .description('Validate a merchant configuration file')
192
- .argument('<config>', 'Path to merchant configuration JSON file')
193
- .action((configPath) => {
194
- try {
195
- const configFile = path.resolve(configPath);
196
- if (!fs.existsSync(configFile)) {
197
- console.error(`❌ Config file not found: ${configFile}`);
198
- process.exit(1);
199
- }
200
- const rawConfig = JSON.parse(fs.readFileSync(configFile, 'utf-8'));
201
- const config = schema_1.MerchantConfigSchema.parse(rawConfig);
202
- console.log('✅ Configuration is valid!');
203
- console.log(`\n📊 Summary:`);
204
- console.log(` • Name: ${config.name}`);
205
- console.log(` • Domain: ${config.domain}`);
206
- console.log(` • Currency: ${config.currency}`);
207
- console.log(` • Tax Rate: ${(config.tax_rate * 100).toFixed(2)}%`);
208
- console.log(` • Products: ${config.items.length}`);
209
- console.log(` • Shipping Options: ${config.shipping_options.length}`);
210
- console.log(` • Payment Handlers: ${config.payment_handlers.length}`);
211
- console.log(` • OAuth: ${config.oauth ? config.oauth.provider : 'Disabled'}`);
212
- }
213
- catch (error) {
214
- console.error('❌ Validation failed:', error);
215
- process.exit(1);
216
- }
217
- });
218
- program
219
- .command('oauth:add-client')
220
- .description('Register a new OAuth client (built-in provider only)')
221
- .argument('<name>', 'Client application name')
222
- .option('-r, --redirect-uri <uri>', 'Redirect URI', 'http://localhost:3000/callback')
223
- .action((name, options) => {
224
- const { OAuthManager } = require('./oauth');
225
- const oauthManager = new OAuthManager();
226
- const client = oauthManager.createClient(name, [options.redirectUri]);
227
- console.log(`\n✅ OAuth client created!\n`);
228
- console.log(` Client ID: ${client.client_id}`);
229
- console.log(` Client Secret: ${client.client_secret}`);
230
- console.log(` Redirect URI: ${options.redirectUri}`);
231
- console.log(`\n⚠️ Save the client secret. It cannot be retrieved later.`);
232
- console.log(`\n📝 Add to your merchant-config.json:`);
233
- console.log(JSON.stringify({ oauth: { provider: 'built-in' } }, null, 2));
234
- });
235
- program.parse();
package/dist/config.d.ts DELETED
@@ -1,14 +0,0 @@
1
- import 'dotenv/config';
2
- export declare const config: {
3
- port: number;
4
- nodeEnv: string;
5
- databasePath: string;
6
- logLevel: string;
7
- domain: string;
8
- stripeSecretKey: string;
9
- stripeWebhookSecret: string;
10
- paypalClientId: string;
11
- paypalClientSecret: string;
12
- paypalMode: "sandbox" | "live";
13
- };
14
- export type Config = typeof config;
package/dist/config.js DELETED
@@ -1,22 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.config = void 0;
4
- require("dotenv/config");
5
- exports.config = {
6
- // Server
7
- port: parseInt(process.env.PORT || '3000', 10),
8
- nodeEnv: process.env.NODE_ENV || 'development',
9
- // Database
10
- databasePath: process.env.DATABASE_PATH || './data/ucp.db',
11
- // Logging
12
- logLevel: process.env.LOG_LEVEL || 'info',
13
- // UCP
14
- domain: process.env.UCP_DOMAIN || 'http://localhost:3000',
15
- // Stripe
16
- stripeSecretKey: process.env.STRIPE_SECRET_KEY || '',
17
- stripeWebhookSecret: process.env.STRIPE_WEBHOOK_SECRET || '',
18
- // PayPal
19
- paypalClientId: process.env.PAYPAL_CLIENT_ID || '',
20
- paypalClientSecret: process.env.PAYPAL_CLIENT_SECRET || '',
21
- paypalMode: (process.env.PAYPAL_MODE || 'sandbox'),
22
- };
package/dist/db.d.ts DELETED
@@ -1,4 +0,0 @@
1
- import Database, { Database as DatabaseType } from 'better-sqlite3';
2
- export declare const db: DatabaseType;
3
- export declare function getDb(): Database.Database;
4
- export declare function isDbHealthy(): boolean;
package/dist/db.js DELETED
@@ -1,164 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.db = void 0;
40
- exports.getDb = getDb;
41
- exports.isDbHealthy = isDbHealthy;
42
- const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
43
- const config_1 = require("./config");
44
- const logger_1 = require("./logger");
45
- const fs = __importStar(require("fs"));
46
- const path = __importStar(require("path"));
47
- // Ensure data directory exists
48
- const dataDir = path.dirname(config_1.config.databasePath);
49
- if (!fs.existsSync(dataDir)) {
50
- fs.mkdirSync(dataDir, { recursive: true });
51
- }
52
- exports.db = new better_sqlite3_1.default(config_1.config.databasePath);
53
- // Enable WAL mode for better concurrent access
54
- exports.db.pragma('journal_mode = WAL');
55
- // Initialize schema
56
- const schema = `
57
- CREATE TABLE IF NOT EXISTS checkouts (
58
- id TEXT PRIMARY KEY,
59
- status TEXT NOT NULL,
60
- currency TEXT NOT NULL,
61
- buyer_json TEXT,
62
- fulfillment_json TEXT,
63
- payment_json TEXT,
64
- totals_json TEXT NOT NULL,
65
- links_json TEXT NOT NULL,
66
- messages_json TEXT,
67
- continue_url TEXT,
68
- expires_at TEXT,
69
- order_json TEXT,
70
- payment_intent_id TEXT,
71
- paypal_order_id TEXT,
72
- payment_provider TEXT,
73
- payment_status TEXT DEFAULT 'none',
74
- created_at TEXT NOT NULL,
75
- updated_at TEXT NOT NULL
76
- );
77
-
78
- CREATE TABLE IF NOT EXISTS line_items (
79
- id TEXT NOT NULL,
80
- checkout_id TEXT NOT NULL,
81
- item_json TEXT NOT NULL,
82
- quantity INTEGER NOT NULL,
83
- totals_json TEXT,
84
- PRIMARY KEY (id, checkout_id),
85
- FOREIGN KEY (checkout_id) REFERENCES checkouts(id) ON DELETE CASCADE
86
- );
87
-
88
- CREATE TABLE IF NOT EXISTS orders (
89
- id TEXT PRIMARY KEY,
90
- checkout_id TEXT NOT NULL,
91
- permalink_url TEXT,
92
- buyer_json TEXT,
93
- line_items_json TEXT NOT NULL,
94
- totals_json TEXT NOT NULL,
95
- fulfillment_json TEXT,
96
- payment_intent_id TEXT,
97
- paypal_order_id TEXT,
98
- payment_provider TEXT,
99
- payment_status TEXT DEFAULT 'pending',
100
- created_at TEXT NOT NULL,
101
- FOREIGN KEY (checkout_id) REFERENCES checkouts(id)
102
- );
103
-
104
- CREATE TABLE IF NOT EXISTS inventory (
105
- item_id TEXT PRIMARY KEY,
106
- stock INTEGER NOT NULL DEFAULT 0,
107
- reserved INTEGER NOT NULL DEFAULT 0,
108
- updated_at TEXT NOT NULL
109
- );
110
-
111
- CREATE TABLE IF NOT EXISTS oauth_clients (
112
- client_id TEXT PRIMARY KEY,
113
- client_secret_hash TEXT NOT NULL,
114
- name TEXT NOT NULL,
115
- redirect_uris_json TEXT NOT NULL,
116
- created_at TEXT NOT NULL
117
- );
118
-
119
- CREATE TABLE IF NOT EXISTS oauth_codes (
120
- code TEXT PRIMARY KEY,
121
- client_id TEXT NOT NULL,
122
- user_id TEXT NOT NULL,
123
- scope TEXT NOT NULL,
124
- redirect_uri TEXT NOT NULL,
125
- code_challenge TEXT,
126
- code_challenge_method TEXT,
127
- expires_at INTEGER NOT NULL,
128
- used INTEGER NOT NULL DEFAULT 0,
129
- FOREIGN KEY (client_id) REFERENCES oauth_clients(client_id)
130
- );
131
-
132
- CREATE TABLE IF NOT EXISTS oauth_tokens (
133
- token TEXT PRIMARY KEY,
134
- token_type TEXT NOT NULL,
135
- client_id TEXT NOT NULL,
136
- user_id TEXT NOT NULL,
137
- scope TEXT NOT NULL,
138
- parent_token TEXT,
139
- expires_at INTEGER NOT NULL,
140
- revoked INTEGER NOT NULL DEFAULT 0,
141
- FOREIGN KEY (client_id) REFERENCES oauth_clients(client_id)
142
- );
143
-
144
- CREATE INDEX IF NOT EXISTS idx_line_items_checkout ON line_items(checkout_id);
145
- CREATE INDEX IF NOT EXISTS idx_orders_checkout ON orders(checkout_id);
146
- CREATE INDEX IF NOT EXISTS idx_checkouts_status ON checkouts(status);
147
- CREATE INDEX IF NOT EXISTS idx_oauth_tokens_client ON oauth_tokens(client_id);
148
- CREATE INDEX IF NOT EXISTS idx_oauth_codes_client ON oauth_codes(client_id);
149
- `;
150
- exports.db.exec(schema);
151
- logger_1.logger.info({ path: config_1.config.databasePath }, 'Database initialized');
152
- // Helper functions
153
- function getDb() {
154
- return exports.db;
155
- }
156
- function isDbHealthy() {
157
- try {
158
- exports.db.prepare('SELECT 1').get();
159
- return true;
160
- }
161
- catch {
162
- return false;
163
- }
164
- }
@@ -1,5 +0,0 @@
1
- import express from 'express';
2
- import { MerchantConfig } from './schema';
3
- export declare function createExpressApp(config: MerchantConfig, options?: {
4
- useDb?: boolean;
5
- }): express.Application;