ucpify 1.0.0

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/cli.js ADDED
@@ -0,0 +1,207 @@
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
+ program
44
+ .name('ucp-server')
45
+ .description('Generate and run a UCP-compliant server for merchants')
46
+ .version('1.0.0');
47
+ program
48
+ .command('init')
49
+ .description('Create a sample merchant configuration file')
50
+ .option('-o, --output <file>', 'Output file path', 'merchant-config.json')
51
+ .action((options) => {
52
+ const sampleConfig = {
53
+ name: 'My Store',
54
+ domain: 'http://localhost:3000',
55
+ currency: 'USD',
56
+ terms_url: 'https://example.com/terms',
57
+ privacy_url: 'https://example.com/privacy',
58
+ tax_rate: 0.08,
59
+ port: 3000,
60
+ items: [
61
+ {
62
+ id: 'item_001',
63
+ title: 'Classic T-Shirt',
64
+ description: 'A comfortable cotton t-shirt',
65
+ price: 2500, // $25.00 in cents
66
+ sku: 'TSH-001',
67
+ },
68
+ {
69
+ id: 'item_002',
70
+ title: 'Premium Hoodie',
71
+ description: 'Warm and stylish hoodie',
72
+ price: 5999, // $59.99 in cents
73
+ sku: 'HOO-001',
74
+ },
75
+ ],
76
+ shipping_options: [
77
+ {
78
+ id: 'standard',
79
+ title: 'Standard Shipping',
80
+ description: 'Arrives in 5-7 business days',
81
+ price: 500, // $5.00
82
+ estimated_days: '5-7 business days',
83
+ },
84
+ {
85
+ id: 'express',
86
+ title: 'Express Shipping',
87
+ description: 'Arrives in 2-3 business days',
88
+ price: 1500, // $15.00
89
+ estimated_days: '2-3 business days',
90
+ },
91
+ {
92
+ id: 'overnight',
93
+ title: 'Overnight Shipping',
94
+ description: 'Arrives next business day',
95
+ price: 2999, // $29.99
96
+ estimated_days: '1 business day',
97
+ },
98
+ ],
99
+ payment_handlers: [
100
+ {
101
+ namespace: 'com.stripe',
102
+ id: 'stripe_handler',
103
+ config: {
104
+ publishable_key: 'pk_test_...',
105
+ },
106
+ },
107
+ {
108
+ namespace: 'com.paypal',
109
+ id: 'paypal_handler',
110
+ config: {
111
+ client_id: 'your_paypal_client_id',
112
+ },
113
+ },
114
+ ],
115
+ };
116
+ fs.writeFileSync(options.output, JSON.stringify(sampleConfig, null, 2));
117
+ console.log(`✅ Created sample config at: ${options.output}`);
118
+ console.log('\n📝 Edit this file to configure your products, shipping, and payment handlers.');
119
+ console.log(`\n🚀 Run: npx ts-node src/cli.ts serve ${options.output}`);
120
+ });
121
+ program
122
+ .command('serve')
123
+ .description('Start the UCP server from a configuration file')
124
+ .argument('<config>', 'Path to merchant configuration JSON file')
125
+ .option('-p, --port <number>', 'Port to run on (overrides config)')
126
+ .option('--no-db', 'Use in-memory storage instead of SQLite')
127
+ .action((configPath, options) => {
128
+ try {
129
+ const configFile = path.resolve(configPath);
130
+ if (!fs.existsSync(configFile)) {
131
+ console.error(`❌ Config file not found: ${configFile}`);
132
+ process.exit(1);
133
+ }
134
+ const rawConfig = JSON.parse(fs.readFileSync(configFile, 'utf-8'));
135
+ const config = schema_1.MerchantConfigSchema.parse(rawConfig);
136
+ if (options.port) {
137
+ config.port = parseInt(options.port, 10);
138
+ }
139
+ const useDb = options.db !== false;
140
+ const app = (0, express_app_1.createExpressApp)(config, { useDb });
141
+ const port = config.port;
142
+ const storageType = useDb ? 'SQLite' : 'In-memory';
143
+ app.listen(port, () => {
144
+ console.log(`
145
+ ╔══════════════════════════════════════════════════════════════╗
146
+ ║ 🛒 UCP Server Running ║
147
+ ╠══════════════════════════════════════════════════════════════╣
148
+ ║ Merchant: ${config.name.padEnd(49)}║
149
+ ║ Domain: ${config.domain.padEnd(49)}║
150
+ ║ Port: ${String(port).padEnd(49)}║
151
+ ║ Storage: ${storageType.padEnd(49)}║
152
+ ╠══════════════════════════════════════════════════════════════╣
153
+ ║ Endpoints: ║
154
+ ║ • GET /.well-known/ucp - UCP Profile ║
155
+ ║ • POST /ucp/v1/checkout-sessions - Create Checkout ║
156
+ ║ • GET /ucp/v1/checkout-sessions/:id - Get Checkout ║
157
+ ║ • PUT /ucp/v1/checkout-sessions/:id - Update Checkout ║
158
+ ║ • POST /ucp/v1/checkout-sessions/:id/complete - Complete ║
159
+ ║ • POST /ucp/v1/checkout-sessions/:id/cancel - Cancel ║
160
+ ║ • GET /ucp/v1/orders - List Orders ║
161
+ ║ • GET /ucp/v1/orders/:id - Get Order ║
162
+ ║ • GET /ucp/v1/items - Product Catalog ║
163
+ ║ • GET /health - Health Check ║
164
+ ╚══════════════════════════════════════════════════════════════╝
165
+
166
+ 🔗 UCP Profile: http://localhost:${port}/.well-known/ucp
167
+ 📦 Products: ${config.items.length} items loaded
168
+ 🚚 Shipping: ${config.shipping_options.length} options available
169
+ 💳 Payment Handlers: ${config.payment_handlers.length} configured
170
+ 💾 Storage: ${storageType}${useDb ? ' (./data/ucp.db)' : ''}
171
+ `);
172
+ });
173
+ }
174
+ catch (error) {
175
+ console.error('❌ Error:', error);
176
+ process.exit(1);
177
+ }
178
+ });
179
+ program
180
+ .command('validate')
181
+ .description('Validate a merchant configuration file')
182
+ .argument('<config>', 'Path to merchant configuration JSON file')
183
+ .action((configPath) => {
184
+ try {
185
+ const configFile = path.resolve(configPath);
186
+ if (!fs.existsSync(configFile)) {
187
+ console.error(`❌ Config file not found: ${configFile}`);
188
+ process.exit(1);
189
+ }
190
+ const rawConfig = JSON.parse(fs.readFileSync(configFile, 'utf-8'));
191
+ const config = schema_1.MerchantConfigSchema.parse(rawConfig);
192
+ console.log('✅ Configuration is valid!');
193
+ console.log(`\n📊 Summary:`);
194
+ console.log(` • Name: ${config.name}`);
195
+ console.log(` • Domain: ${config.domain}`);
196
+ console.log(` • Currency: ${config.currency}`);
197
+ console.log(` • Tax Rate: ${(config.tax_rate * 100).toFixed(2)}%`);
198
+ console.log(` • Products: ${config.items.length}`);
199
+ console.log(` • Shipping Options: ${config.shipping_options.length}`);
200
+ console.log(` • Payment Handlers: ${config.payment_handlers.length}`);
201
+ }
202
+ catch (error) {
203
+ console.error('❌ Validation failed:', error);
204
+ process.exit(1);
205
+ }
206
+ });
207
+ program.parse();
@@ -0,0 +1,14 @@
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 ADDED
@@ -0,0 +1,22 @@
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 ADDED
@@ -0,0 +1,4 @@
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 ADDED
@@ -0,0 +1,129 @@
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
+ }
@@ -0,0 +1,5 @@
1
+ import express from 'express';
2
+ import { MerchantConfig } from './schema';
3
+ export declare function createExpressApp(config: MerchantConfig, options?: {
4
+ useDb?: boolean;
5
+ }): express.Application;