spaps 0.3.6 → 0.3.8

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/local-server.js +114 -59
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spaps",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "Sweet Potato Authentication & Payment Service CLI - Zero-config local development and project scaffolding",
5
5
  "main": "bin/spaps.js",
6
6
  "bin": {
@@ -249,27 +249,33 @@ class LocalServer {
249
249
  limit: req.query.limit ? parseInt(req.query.limit) : 10
250
250
  });
251
251
 
252
- // Get prices for each product
252
+ // Get prices for each product and filter out local-only products
253
253
  const productsWithPrices = await Promise.all(
254
- products.data.map(async (product) => {
255
- const prices = await stripe.prices.list({
256
- product: product.id,
257
- active: true,
258
- limit: 1
259
- });
260
-
261
- const defaultPrice = prices.data[0];
262
- return {
263
- id: product.id,
264
- name: product.name,
265
- description: product.description,
266
- price: defaultPrice ? defaultPrice.unit_amount : 0,
267
- currency: defaultPrice ? defaultPrice.currency : 'usd',
268
- price_id: defaultPrice ? defaultPrice.id : null,
269
- active: product.active,
270
- metadata: product.metadata
271
- };
272
- })
254
+ products.data
255
+ .filter(product => {
256
+ // Only show products that were synced from SPAPS (have spaps_managed metadata)
257
+ // or don't start with prod_local_ (which are local-only placeholders)
258
+ return product.metadata?.spaps_managed === 'true' || !product.id.startsWith('prod_local_');
259
+ })
260
+ .map(async (product) => {
261
+ const prices = await stripe.prices.list({
262
+ product: product.id,
263
+ active: true,
264
+ limit: 1
265
+ });
266
+
267
+ const defaultPrice = prices.data[0];
268
+ return {
269
+ id: product.id,
270
+ name: product.name,
271
+ description: product.description,
272
+ price: defaultPrice ? defaultPrice.unit_amount : 0,
273
+ currency: defaultPrice ? defaultPrice.currency : 'usd',
274
+ price_id: defaultPrice ? defaultPrice.id : null,
275
+ active: product.active,
276
+ metadata: product.metadata
277
+ };
278
+ })
273
279
  );
274
280
 
275
281
  res.json({
@@ -349,6 +355,54 @@ class LocalServer {
349
355
  });
350
356
  });
351
357
 
358
+ // Admin whitelist check endpoint
359
+ this.app.post('/api/v1/admin/whitelist/check', (req, res) => {
360
+ const { email } = req.body;
361
+
362
+ if (!email) {
363
+ return res.status(400).json({
364
+ success: false,
365
+ error: { message: 'Email is required' }
366
+ });
367
+ }
368
+
369
+ // Mock whitelist check - some emails are "whitelisted"
370
+ const whitelistedEmails = ['vip@example.com', 'admin@example.com', 'premium@example.com'];
371
+ const isWhitelisted = whitelistedEmails.includes(email.toLowerCase());
372
+
373
+ res.json({
374
+ success: true,
375
+ data: {
376
+ email,
377
+ whitelisted: isWhitelisted,
378
+ reason: isWhitelisted ? 'VIP access granted' : 'Standard access only'
379
+ }
380
+ });
381
+ });
382
+
383
+ // Admin pricing update endpoint
384
+ this.app.post('/api/v1/admin/pricing/update', (req, res) => {
385
+ const { product_id, new_price, currency = 'usd' } = req.body;
386
+
387
+ if (!product_id || !new_price) {
388
+ return res.status(400).json({
389
+ success: false,
390
+ error: { message: 'Product ID and new price are required' }
391
+ });
392
+ }
393
+
394
+ res.json({
395
+ success: true,
396
+ data: {
397
+ product_id,
398
+ old_price: 999,
399
+ new_price,
400
+ currency,
401
+ updated_at: new Date().toISOString()
402
+ }
403
+ });
404
+ });
405
+
352
406
  // Admin product sync endpoint - REAL or MOCK based on config
353
407
  this.app.post('/api/v1/admin/products/sync', async (req, res) => {
354
408
  try {
@@ -359,51 +413,52 @@ class LocalServer {
359
413
 
360
414
  for (const product of localProducts) {
361
415
  try {
362
- // Check if product already exists in Stripe
416
+ // Check if product already exists in Stripe by searching metadata
363
417
  let stripeProduct;
364
- try {
365
- stripeProduct = await stripe.products.retrieve(product.id);
366
- } catch (error) {
367
- if (error.code === 'resource_missing') {
368
- // Create new product in Stripe
369
- stripeProduct = await stripe.products.create({
370
- id: product.id,
371
- name: product.name,
372
- description: product.description,
373
- metadata: {
374
- spaps_managed: 'true',
375
- created_by: 'spaps_admin'
376
- }
377
- });
378
-
379
- // Create corresponding price
380
- await stripe.prices.create({
381
- id: product.price_id,
382
- product: stripeProduct.id,
383
- unit_amount: product.price,
384
- currency: product.currency,
385
- metadata: {
386
- spaps_managed: 'true'
387
- }
388
- });
389
-
390
- syncResults.push({
391
- id: product.id,
392
- name: product.name,
393
- action: 'created',
394
- stripe_id: stripeProduct.id
395
- });
396
- } else {
397
- throw error;
398
- }
399
- }
418
+ const existingProducts = await stripe.products.list({
419
+ limit: 100,
420
+ expand: ['data.default_price']
421
+ });
422
+
423
+ stripeProduct = existingProducts.data.find(p =>
424
+ p.metadata && p.metadata.spaps_id === product.id
425
+ );
400
426
 
401
- if (stripeProduct && !syncResults.find(r => r.id === product.id)) {
427
+ if (!stripeProduct) {
428
+ // Create new product in Stripe
429
+ stripeProduct = await stripe.products.create({
430
+ name: product.name,
431
+ description: product.description,
432
+ metadata: {
433
+ spaps_managed: 'true',
434
+ created_by: 'spaps_admin',
435
+ spaps_id: product.id
436
+ }
437
+ });
438
+
439
+ // Create corresponding price
440
+ await stripe.prices.create({
441
+ product: stripeProduct.id,
442
+ unit_amount: product.price,
443
+ currency: product.currency,
444
+ metadata: {
445
+ spaps_managed: 'true',
446
+ spaps_price_id: product.price_id
447
+ }
448
+ });
449
+
450
+ syncResults.push({
451
+ id: product.id,
452
+ name: product.name,
453
+ action: 'created',
454
+ stripe_id: stripeProduct.id
455
+ });
456
+ } else {
402
457
  // Update existing product
403
458
  await stripe.products.update(stripeProduct.id, {
404
459
  name: product.name,
405
460
  description: product.description,
406
- active: product.active
461
+ active: product.active !== false
407
462
  });
408
463
 
409
464
  syncResults.push({