spaps 0.3.10 → 0.4.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/local-server.js +235 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spaps",
3
- "version": "0.3.10",
3
+ "version": "0.4.1",
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": {
@@ -539,6 +539,241 @@ class LocalServer {
539
539
  }
540
540
 
541
541
  setupStripeRoutes() {
542
+ // Enhanced Stripe Product Management - Full CRUD
543
+
544
+ // GET /api/stripe/products - List all products with filtering
545
+ this.app.get('/api/stripe/products', async (req, res) => {
546
+ try {
547
+ if (USE_REAL_STRIPE) {
548
+ const { active, category, limit = '100' } = req.query;
549
+
550
+ const products = await stripe.products.list({
551
+ active: active === 'true' ? true : active === 'false' ? false : undefined,
552
+ limit: parseInt(limit),
553
+ expand: ['data.default_price']
554
+ });
555
+
556
+ // Filter out non-SPAPS managed products if needed
557
+ let filteredProducts = products.data;
558
+ if (category === 'spaps') {
559
+ filteredProducts = products.data.filter(p =>
560
+ p.metadata && p.metadata.spaps_managed === 'true'
561
+ );
562
+ }
563
+
564
+ res.json({
565
+ success: true,
566
+ data: {
567
+ products: filteredProducts.map(product => ({
568
+ id: product.id,
569
+ name: product.name,
570
+ description: product.description,
571
+ images: product.images,
572
+ active: product.active,
573
+ metadata: product.metadata,
574
+ default_price: product.default_price ? {
575
+ id: product.default_price.id,
576
+ unit_amount: product.default_price.unit_amount,
577
+ currency: product.default_price.currency,
578
+ recurring: product.default_price.recurring
579
+ } : null,
580
+ created: product.created,
581
+ updated: product.updated
582
+ })),
583
+ has_more: products.has_more,
584
+ total_count: filteredProducts.length
585
+ }
586
+ });
587
+ } else {
588
+ // Mock response
589
+ const localProducts = this.adminManager.listProducts();
590
+ res.json({
591
+ success: true,
592
+ data: {
593
+ products: localProducts,
594
+ has_more: false,
595
+ total_count: localProducts.length
596
+ }
597
+ });
598
+ }
599
+ } catch (error) {
600
+ console.error('List products error:', error);
601
+ res.status(500).json({
602
+ success: false,
603
+ error: {
604
+ code: 'LIST_PRODUCTS_ERROR',
605
+ message: error.message || 'Failed to list products'
606
+ }
607
+ });
608
+ }
609
+ });
610
+
611
+ // POST /api/stripe/products - Create new product
612
+ this.app.post('/api/stripe/products', async (req, res) => {
613
+ try {
614
+ const { name, description, images, metadata = {}, active = true } = req.body;
615
+
616
+ if (!name) {
617
+ return res.status(400).json({
618
+ success: false,
619
+ error: { message: 'Product name is required' }
620
+ });
621
+ }
622
+
623
+ if (USE_REAL_STRIPE) {
624
+ // Create product in Stripe
625
+ const stripeProduct = await stripe.products.create({
626
+ name,
627
+ description,
628
+ images,
629
+ active,
630
+ metadata: {
631
+ ...metadata,
632
+ spaps_managed: 'true',
633
+ created_by: 'spaps_cli'
634
+ }
635
+ });
636
+
637
+ res.json({
638
+ success: true,
639
+ data: {
640
+ product: {
641
+ id: stripeProduct.id,
642
+ name: stripeProduct.name,
643
+ description: stripeProduct.description,
644
+ images: stripeProduct.images,
645
+ active: stripeProduct.active,
646
+ metadata: stripeProduct.metadata,
647
+ created: stripeProduct.created
648
+ }
649
+ }
650
+ });
651
+ } else {
652
+ // Create locally
653
+ const product = this.adminManager.createProduct({
654
+ name,
655
+ description,
656
+ price: 0, // Will need to set price separately
657
+ currency: 'usd'
658
+ });
659
+
660
+ res.json({
661
+ success: true,
662
+ data: { product }
663
+ });
664
+ }
665
+ } catch (error) {
666
+ console.error('Create product error:', error);
667
+ res.status(500).json({
668
+ success: false,
669
+ error: {
670
+ code: 'CREATE_PRODUCT_ERROR',
671
+ message: error.message || 'Failed to create product'
672
+ }
673
+ });
674
+ }
675
+ });
676
+
677
+ // PUT /api/stripe/products/:productId - Update product
678
+ this.app.put('/api/stripe/products/:productId', async (req, res) => {
679
+ try {
680
+ const { productId } = req.params;
681
+ const { name, description, images, metadata, active } = req.body;
682
+
683
+ if (USE_REAL_STRIPE) {
684
+ // Update in Stripe
685
+ const updateData = {};
686
+ if (name !== undefined) updateData.name = name;
687
+ if (description !== undefined) updateData.description = description;
688
+ if (images !== undefined) updateData.images = images;
689
+ if (metadata !== undefined) updateData.metadata = metadata;
690
+ if (active !== undefined) updateData.active = active;
691
+
692
+ const stripeProduct = await stripe.products.update(productId, updateData);
693
+
694
+ res.json({
695
+ success: true,
696
+ data: {
697
+ product: {
698
+ id: stripeProduct.id,
699
+ name: stripeProduct.name,
700
+ description: stripeProduct.description,
701
+ images: stripeProduct.images,
702
+ active: stripeProduct.active,
703
+ metadata: stripeProduct.metadata,
704
+ updated: stripeProduct.updated
705
+ }
706
+ }
707
+ });
708
+ } else {
709
+ // Update locally
710
+ const product = this.adminManager.updateProduct(productId, {
711
+ name,
712
+ description,
713
+ active
714
+ });
715
+
716
+ res.json({
717
+ success: true,
718
+ data: { product }
719
+ });
720
+ }
721
+ } catch (error) {
722
+ console.error('Update product error:', error);
723
+ res.status(500).json({
724
+ success: false,
725
+ error: {
726
+ code: 'UPDATE_PRODUCT_ERROR',
727
+ message: error.message || 'Failed to update product'
728
+ }
729
+ });
730
+ }
731
+ });
732
+
733
+ // DELETE /api/stripe/products/:productId - Archive product
734
+ this.app.delete('/api/stripe/products/:productId', async (req, res) => {
735
+ try {
736
+ const { productId } = req.params;
737
+
738
+ if (USE_REAL_STRIPE) {
739
+ // Archive in Stripe (can't truly delete)
740
+ const stripeProduct = await stripe.products.update(productId, {
741
+ active: false
742
+ });
743
+
744
+ res.json({
745
+ success: true,
746
+ message: 'Product archived successfully',
747
+ data: {
748
+ product: {
749
+ id: stripeProduct.id,
750
+ active: stripeProduct.active,
751
+ updated: stripeProduct.updated
752
+ }
753
+ }
754
+ });
755
+ } else {
756
+ // Soft delete locally
757
+ const result = this.adminManager.deleteProduct(productId);
758
+
759
+ res.json({
760
+ success: true,
761
+ message: 'Product deleted successfully',
762
+ data: result
763
+ });
764
+ }
765
+ } catch (error) {
766
+ console.error('Delete product error:', error);
767
+ res.status(500).json({
768
+ success: false,
769
+ error: {
770
+ code: 'DELETE_PRODUCT_ERROR',
771
+ message: error.message || 'Failed to delete product'
772
+ }
773
+ });
774
+ }
775
+ });
776
+
542
777
  // Mock Stripe checkout session
543
778
  this.app.post('/api/stripe/create-checkout-session', async (req, res) => {
544
779
  const { price_id, success_url, cancel_url } = req.body;