ucpify 1.1.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/src/schema.ts CHANGED
@@ -30,6 +30,23 @@ export const PaymentHandlerSchema = z.object({
30
30
  config: z.record(z.string(), z.unknown()).optional(),
31
31
  });
32
32
 
33
+ // OAuth Configuration Schema
34
+ export const OAuthConfigSchema = z.discriminatedUnion('provider', [
35
+ z.object({
36
+ provider: z.literal('built-in'),
37
+ }),
38
+ z.object({
39
+ provider: z.literal('external'),
40
+ issuer: z.string().url(),
41
+ authorization_endpoint: z.string().url(),
42
+ token_endpoint: z.string().url(),
43
+ revocation_endpoint: z.string().url().optional(),
44
+ jwks_uri: z.string().url().optional(),
45
+ }),
46
+ ]);
47
+
48
+ export type OAuthConfig = z.infer<typeof OAuthConfigSchema>;
49
+
33
50
  // Merchant Configuration Schema
34
51
  export const MerchantConfigSchema = z.object({
35
52
  name: z.string(),
@@ -42,6 +59,7 @@ export const MerchantConfigSchema = z.object({
42
59
  payment_handlers: z.array(PaymentHandlerSchema).default([]),
43
60
  tax_rate: z.number().min(0).max(1).default(0), // e.g., 0.08 for 8%
44
61
  port: z.number().default(3000),
62
+ oauth: OAuthConfigSchema.optional(),
45
63
  });
46
64
 
47
65
  export type Item = z.infer<typeof ItemSchema>;
@@ -57,6 +57,11 @@ export class UCPServerDB {
57
57
  'dev.ucp.shopping.order': [
58
58
  { version: UCP_VERSION, spec: 'https://ucp.dev/specification/order', schema: 'https://ucp.dev/schemas/shopping/order.json' },
59
59
  ],
60
+ ...(this.config.oauth ? {
61
+ 'dev.ucp.identity_linking': [
62
+ { version: UCP_VERSION, spec: 'https://ucp.dev/specification/identity-linking' },
63
+ ],
64
+ } : {}),
60
65
  },
61
66
  payment_handlers: paymentHandlers,
62
67
  },
@@ -105,9 +110,9 @@ export class UCPServerDB {
105
110
 
106
111
  private sessionFromRow(row: any): CheckoutSession | undefined {
107
112
  if (!row) return undefined;
108
-
113
+
109
114
  const lineItems = db.prepare('SELECT * FROM line_items WHERE checkout_id = ?').all(row.id) as any[];
110
-
115
+
111
116
  return {
112
117
  ucp: {
113
118
  version: UCP_VERSION,
@@ -169,7 +174,7 @@ export class UCPServerDB {
169
174
  `);
170
175
 
171
176
  db.transaction(() => {
172
- insertCheckout.run(id, status, this.config.currency, JSON.stringify(totals), JSON.stringify(links),
177
+ insertCheckout.run(id, status, this.config.currency, JSON.stringify(totals), JSON.stringify(links),
173
178
  messages.length > 0 ? JSON.stringify(messages) : null, continueUrl, expiresAt, now, now);
174
179
  for (const li of lineItems) {
175
180
  insertLineItem.run(li.id, id, JSON.stringify(li.item), li.quantity, JSON.stringify(li.totals));
@@ -284,13 +289,13 @@ export class UCPServerDB {
284
289
 
285
290
  // Get total amount for payment
286
291
  const totalAmount = session.totals.find(t => t.type === 'total')?.amount || 0;
287
-
292
+
288
293
  // Payment processing - try Stripe first, then PayPal
289
294
  let paymentIntentId: string | null = null;
290
295
  let paypalOrderId: string | null = null;
291
296
  let paymentProvider: string | null = null;
292
297
  let paymentStatus = 'none';
293
-
298
+
294
299
  if (totalAmount > 0) {
295
300
  // Try Stripe first
296
301
  if (isStripeConfigured()) {
package/src/ucp-server.ts CHANGED
@@ -76,6 +76,14 @@ export class UCPServer {
76
76
  schema: 'https://ucp.dev/schemas/shopping/order.json',
77
77
  },
78
78
  ],
79
+ ...(this.config.oauth ? {
80
+ 'dev.ucp.identity_linking': [
81
+ {
82
+ version: UCP_VERSION,
83
+ spec: 'https://ucp.dev/specification/identity-linking',
84
+ },
85
+ ],
86
+ } : {}),
79
87
  },
80
88
  payment_handlers: paymentHandlers,
81
89
  },
@@ -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,208 +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
- app.listen(port, () => {
145
- console.log(`
146
- ╔══════════════════════════════════════════════════════════════╗
147
- ║ 🛒 UCP Server Running ║
148
- ╠══════════════════════════════════════════════════════════════╣
149
- ║ Merchant: ${config.name.padEnd(49)}║
150
- ║ Domain: ${config.domain.padEnd(49)}║
151
- ║ Port: ${String(port).padEnd(49)}║
152
- ║ Storage: ${storageType.padEnd(49)}║
153
- ╠══════════════════════════════════════════════════════════════╣
154
- ║ Endpoints: ║
155
- ║ • GET /.well-known/ucp - UCP Profile ║
156
- ║ • POST /ucp/v1/checkout-sessions - Create Checkout ║
157
- ║ • GET /ucp/v1/checkout-sessions/:id - Get Checkout ║
158
- ║ • PUT /ucp/v1/checkout-sessions/:id - Update Checkout ║
159
- ║ • POST /ucp/v1/checkout-sessions/:id/complete - Complete ║
160
- ║ • POST /ucp/v1/checkout-sessions/:id/cancel - Cancel ║
161
- ║ • GET /ucp/v1/orders - List Orders ║
162
- ║ • GET /ucp/v1/orders/:id - Get Order ║
163
- ║ • GET /ucp/v1/items - Product Catalog ║
164
- ║ • GET /health - Health Check ║
165
- ╚══════════════════════════════════════════════════════════════╝
166
-
167
- 🔗 UCP Profile: http://localhost:${port}/.well-known/ucp
168
- 📦 Products: ${config.items.length} items loaded
169
- 🚚 Shipping: ${config.shipping_options.length} options available
170
- 💳 Payment Handlers: ${config.payment_handlers.length} configured
171
- 💾 Storage: ${storageType}${useDb ? ' (./data/ucp.db)' : ''}
172
- `);
173
- });
174
- }
175
- catch (error) {
176
- console.error('❌ Error:', error);
177
- process.exit(1);
178
- }
179
- });
180
- program
181
- .command('validate')
182
- .description('Validate a merchant configuration file')
183
- .argument('<config>', 'Path to merchant configuration JSON file')
184
- .action((configPath) => {
185
- try {
186
- const configFile = path.resolve(configPath);
187
- if (!fs.existsSync(configFile)) {
188
- console.error(`❌ Config file not found: ${configFile}`);
189
- process.exit(1);
190
- }
191
- const rawConfig = JSON.parse(fs.readFileSync(configFile, 'utf-8'));
192
- const config = schema_1.MerchantConfigSchema.parse(rawConfig);
193
- console.log('✅ Configuration is valid!');
194
- console.log(`\n📊 Summary:`);
195
- console.log(` • Name: ${config.name}`);
196
- console.log(` • Domain: ${config.domain}`);
197
- console.log(` • Currency: ${config.currency}`);
198
- console.log(` • Tax Rate: ${(config.tax_rate * 100).toFixed(2)}%`);
199
- console.log(` • Products: ${config.items.length}`);
200
- console.log(` • Shipping Options: ${config.shipping_options.length}`);
201
- console.log(` • Payment Handlers: ${config.payment_handlers.length}`);
202
- }
203
- catch (error) {
204
- console.error('❌ Validation failed:', error);
205
- process.exit(1);
206
- }
207
- });
208
- 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,129 +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 INDEX IF NOT EXISTS idx_line_items_checkout ON line_items(checkout_id);
112
- CREATE INDEX IF NOT EXISTS idx_orders_checkout ON orders(checkout_id);
113
- CREATE INDEX IF NOT EXISTS idx_checkouts_status ON checkouts(status);
114
- `;
115
- exports.db.exec(schema);
116
- logger_1.logger.info({ path: config_1.config.databasePath }, 'Database initialized');
117
- // Helper functions
118
- function getDb() {
119
- return exports.db;
120
- }
121
- function isDbHealthy() {
122
- try {
123
- exports.db.prepare('SELECT 1').get();
124
- return true;
125
- }
126
- catch {
127
- return false;
128
- }
129
- }
@@ -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;