Repository: stripe/stripe-cli
Pull request #934 (2022-08-08 15:41:16)

Move Subscription fixtures to Price and PaymentMethod API
 ### Reviewers
r? @etsai-stripe 
cc @stripe/developer-products

 ### Summary
Move Subscription fixtures to Price and PaymentMethod API



Patch:
diff --git a/.gitignore b/.gitignore
index da0257c..ae8ba00 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,3 @@
-
 dist/
 bin/
 coverage.txt
diff --git a/api/openapi-spec/spec3.sdk.json b/api/openapi-spec/spec3.sdk.json
index dc8d6b4..8a8ed6f 100644
--- a/api/openapi-spec/spec3.sdk.json
+++ b/api/openapi-spec/spec3.sdk.json
@@ -2177,6 +2177,12 @@
             "description": "If true, indicates that this secret has been deleted",
             "type": "boolean"
           },
+          "expires_at": {
+            "description": "The Unix timestamp for the expiry time of the secret, after which the secret deletes.",
+            "format": "unix-time",
+            "nullable": true,
+            "type": "integer"
+          },
           "id": {
             "description": "Unique identifier for the object.",
             "maxLength": 5000,
@@ -2210,6 +2216,7 @@
         },
         "required": [
           "created",
+          "expires_at",
           "id",
           "livemode",
           "name",
@@ -43530,6 +43537,24 @@
           "in_class": "terminal_reader_reader_resource_cart"
         }
       },
+      "terminal_reader_reader_resource_process_config": {
+        "description": "Represents a per-transaction override of a reader configuration",
+        "properties": {
+          "skip_tipping": {
+            "description": "Override showing a tipping selection screen on this transaction.",
+            "type": "boolean"
+          }
+        },
+        "title": "TerminalReaderReaderResourceProcessConfig",
+        "type": "object",
+        "x-expandableFields": [
+
+        ],
+        "x-stripeResource": {
+          "class_name": "ProcessConfig",
+          "in_class": "terminal_reader_reader_resource_process_payment_intent_action"
+        }
+      },
       "terminal_reader_reader_resource_process_payment_intent_action": {
         "description": "Represents a reader action to process a payment intent",
         "properties": {
@@ -43551,6 +43576,9 @@
                 }
               ]
             }
+          },
+          "process_config": {
+            "$ref": "#/components/schemas/terminal_reader_reader_resource_process_config"
           }
         },
         "required": [
@@ -43559,11 +43587,15 @@
         "title": "TerminalReaderReaderResourceProcessPaymentIntentAction",
         "type": "object",
         "x-expandableFields": [
-          "payment_intent"
+          "payment_intent",
+          "process_config"
         ],
         "x-stripeResource": {
           "class_name": "ProcessPaymentIntentAction",
-          "in_class": "terminal_reader_reader_resource_reader_action"
+          "in_class": "terminal_reader_reader_resource_reader_action",
+          "inner_classes": [
+            "terminal_reader_reader_resource_process_config"
+          ]
         }
       },
       "terminal_reader_reader_resource_process_setup_intent_action": {
@@ -55472,6 +55504,11 @@
                     },
                     "type": "array"
                   },
+                  "expires_at": {
+                    "description": "The Unix timestamp for the expiry time of the secret, after which the secret deletes.",
+                    "format": "unix-time",
+                    "type": "integer"
+                  },
                   "name": {
                     "description": "A name for the secret that's unique within the scope.",
                     "maxLength": 5000,
diff --git a/pkg/cmd/resources_cmds.go b/pkg/cmd/resources_cmds.go
index 3235bc6..c29d0a5 100644
--- a/pkg/cmd/resources_cmds.go
+++ b/pkg/cmd/resources_cmds.go
@@ -1501,8 +1501,9 @@ func addAllResourcesCmds(rootCmd *cobra.Command) {
 		"url":         "string",
 	}, &Config)
 	resource.NewOperationCmd(rAppsSecretsCmd.Cmd, "create", "/v1/apps/secrets", http.MethodPost, map[string]string{
-		"name":    "string",
-		"payload": "string",
+		"expires_at": "integer",
+		"name":       "string",
+		"payload":    "string",
 	}, &Config)
 	resource.NewOperationCmd(rAppsSecretsCmd.Cmd, "delete_where", "/v1/apps/secrets/delete", http.MethodPost, map[string]string{
 		"name": "string",
diff --git a/pkg/config/config.go b/pkg/config/config.go
index ac5c81c..b99e1ad 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -168,7 +168,7 @@ func (c *Config) InitConfig() {
 	})
 
 	// redact livemode values for existing configs
-	c.Profile.redactAllLivemodeValues()
+	// c.Profile.redactAllLivemodeValues()
 }
 
 // EditConfig opens the configuration file in the default editor.
diff --git a/pkg/config/profile.go b/pkg/config/profile.go
index 2094f58..82210be 100644
--- a/pkg/config/profile.go
+++ b/pkg/config/profile.go
@@ -139,10 +139,18 @@ func (p *Profile) GetAPIKey(livemode bool) (string, error) {
 			key = viper.GetString(p.GetConfigField(TestModeAPIKeyName))
 		}
 	} else {
-		p.redactAllLivemodeValues()
-		key, err = p.retrieveLivemodeValue(LiveModeAPIKeyName)
-		if err != nil {
-			return "", err
+		// p.redactAllLivemodeValues()
+		// key, err = p.retrieveLivemodeValue(LiveModeAPIKeyName)
+		// if err != nil {
+		// 	return "", err
+		// }
+
+		if err := viper.ReadInConfig(); err == nil {
+			key = viper.GetString(p.GetConfigField(LiveModeAPIKeyName))
+		}
+
+		if isRedactedAPIKey(key) {
+			return "", validators.ErrAPIKeyNotConfigured
 		}
 	}
 
@@ -160,13 +168,14 @@ func (p *Profile) GetAPIKey(livemode bool) (string, error) {
 // GetExpiresAt returns the API key expirary date
 func (p *Profile) GetExpiresAt(livemode bool) (time.Time, error) {
 	var timeString string
-	var err error
+	// var err error
 
 	if livemode {
-		timeString, err = p.retrieveLivemodeValue(LiveModeKeyExpiresAtName)
-		if err != nil {
-			return time.Time{}, err
-		}
+		// timeString, err = p.retrieveLivemodeValue(LiveModeKeyExpiresAtName)
+		// if err != nil {
+		// 	return time.Time{}, err
+		// }
+		timeString = viper.GetString(p.GetConfigField(LiveModeKeyExpiresAtName))
 	} else {
 		timeString = viper.GetString(p.GetConfigField(TestModeKeyExpiresAtName))
 	}
@@ -258,9 +267,9 @@ func (p *Profile) DeleteConfigField(field string) error {
 	}
 
 	// delete livemode redacted values from config and full values from keyring
-	if field == LiveModeAPIKeyName || field == LiveModePubKeyName || field == LiveModeKeyExpiresAtName {
-		p.deleteLivemodeValue(field)
-	}
+	// if field == LiveModeAPIKeyName || field == LiveModePubKeyName || field == LiveModeKeyExpiresAtName {
+	// 	p.deleteLivemodeValue(field)
+	// }
 
 	return p.writeProfile(v)
 }
@@ -278,15 +287,19 @@ func (p *Profile) writeProfile(runtimeViper *viper.Viper) error {
 	}
 
 	if p.LiveModeAPIKey != "" {
-		expiresAt := getKeyExpiresAt()
+		// comment out livemode storage until bugs are ironed out
+		// expiresAt := getKeyExpiresAt()
+
+		// // store redacted key in config
+		// runtimeViper.Set(p.GetConfigField(LiveModeAPIKeyName), RedactAPIKey(strings.TrimSpace(p.LiveModeAPIKey)))
+		// runtimeViper.Set(p.GetConfigField(LiveModeKeyExpiresAtName), expiresAt)
 
-		// store redacted key in config
-		runtimeViper.Set(p.GetConfigField(LiveModeAPIKeyName), RedactAPIKey(strings.TrimSpace(p.LiveModeAPIKey)))
-		runtimeViper.Set(p.GetConfigField(LiveModeKeyExpiresAtName), expiresAt)
+		// // store actual key in secure keyring
+		// p.saveLivemodeValue(LiveModeAPIKeyName, strings.TrimSpace(p.LiveModeAPIKey), "Live mode API key")
+		// p.saveLivemodeValue(LiveModeKeyExpiresAtName, expiresAt, "Live mode API key expirary")
 
-		// store actual key in secure keyring
-		p.saveLivemodeValue(LiveModeAPIKeyName, strings.TrimSpace(p.LiveModeAPIKey), "Live mode API key")
-		p.saveLivemodeValue(LiveModeKeyExpiresAtName, expiresAt, "Live mode API key expirary")
+		runtimeViper.Set(p.GetConfigField(LiveModeAPIKeyName), strings.TrimSpace(p.LiveModeAPIKey))
+		runtimeViper.Set(p.GetConfigField(LiveModeKeyExpiresAtName), getKeyExpiresAt())
 	}
 
 	if p.LiveModePublishableKey != "" {
diff --git a/pkg/config/profile_livemode.go b/pkg/config/profile_livemode.go
index 5cf34de..57003ca 100644
--- a/pkg/config/profile_livemode.go
+++ b/pkg/config/profile_livemode.go
@@ -1,15 +1,9 @@
 package config
 
 import (
-	"fmt"
-	"os"
 	"strings"
 
 	"github.com/99designs/keyring"
-	"github.com/spf13/viper"
-
-	"github.com/stripe/stripe-cli/pkg/ansi"
-	"github.com/stripe/stripe-cli/pkg/validators"
 )
 
 // DateStringFormat ...
@@ -22,68 +16,68 @@ const KeyValidInDays = 90
 var KeyRing keyring.Keyring
 
 // saveLivemodeValue saves livemode value of given key in keyring
-func (p *Profile) saveLivemodeValue(field, value, description string) {
-	fieldID := p.GetConfigField(field)
-	_ = KeyRing.Set(keyring.Item{
-		Key:         fieldID,
-		Data:        []byte(value),
-		Description: description,
-		Label:       fieldID,
-	})
-}
+// func (p *Profile) saveLivemodeValue(field, value, description string) {
+// 	fieldID := p.GetConfigField(field)
+// 	_ = KeyRing.Set(keyring.Item{
+// 		Key:         fieldID,
+// 		Data:        []byte(value),
+// 		Description: description,
+// 		Label:       fieldID,
+// 	})
+// }
 
 // retrieveLivemodeValue retrieves livemode value of given key in keyring
-func (p *Profile) retrieveLivemodeValue(key string) (string, error) {
-	fieldID := p.GetConfigField(key)
-	existingKeys, err := KeyRing.Keys()
-	if err != nil {
-		return "", err
-	}
-
-	for _, item := range existingKeys {
-		if item == fieldID {
-			value, _ := KeyRing.Get(fieldID)
-			return string(value.Data), nil
-		}
-	}
-
-	return "", validators.ErrAPIKeyNotConfigured
-}
+// func (p *Profile) retrieveLivemodeValue(key string) (string, error) {
+// 	fieldID := p.GetConfigField(key)
+// 	existingKeys, err := KeyRing.Keys()
+// 	if err != nil {
+// 		return "", err
+// 	}
+
+// 	for _, item := range existingKeys {
+// 		if item == fieldID {
+// 			value, _ := KeyRing.Get(fieldID)
+// 			return string(value.Data), nil
+// 		}
+// 	}
+
+// 	return "", validators.ErrAPIKeyNotConfigured
+// }
 
 // deleteLivemodeValue deletes livemode value of given key in keyring
-func (p *Profile) deleteLivemodeValue(key string) error {
-	fieldID := p.GetConfigField(key)
-	existingKeys, err := KeyRing.Keys()
-	if err != nil {
-		return err
-	}
-	for _, item := range existingKeys {
-		if item == fieldID {
-			KeyRing.Remove(fieldID)
-			return nil
-		}
-	}
-	return nil
-}
+// func (p *Profile) deleteLivemodeValue(key string) error {
+// 	fieldID := p.GetConfigField(key)
+// 	existingKeys, err := KeyRing.Keys()
+// 	if err != nil {
+// 		return err
+// 	}
+// 	for _, item := range existingKeys {
+// 		if item == fieldID {
+// 			KeyRing.Remove(fieldID)
+// 			return nil
+// 		}
+// 	}
+// 	return nil
+// }
 
 // redactAllLivemodeValues redacts all livemode values in the local config file
-func (p *Profile) redactAllLivemodeValues() {
-	color := ansi.Color(os.Stdout)
-
-	if err := viper.ReadInConfig(); err == nil {
-		// if the config file has expires at date, then it is using the new livemode key storage
-		if viper.IsSet(p.GetConfigField(LiveModeAPIKeyName)) {
-			key := viper.GetString(p.GetConfigField(LiveModeAPIKeyName))
-			if !isRedactedAPIKey(key) {
-				fmt.Println(color.Yellow(`
-(!) Livemode value found for the field '` + LiveModeAPIKeyName + `' in your config file.
-Livemode values from the config file will be redacted and will not be used.`))
-
-				p.WriteConfigField(LiveModeAPIKeyName, RedactAPIKey(key))
-			}
-		}
-	}
-}
+// func (p *Profile) redactAllLivemodeValues() {
+// 	color := ansi.Color(os.Stdout)
+
+// 	if err := viper.ReadInConfig(); err == nil {
+// 		// if the config file has expires at date, then it is using the new livemode key storage
+// 		if viper.IsSet(p.GetConfigField(LiveModeAPIKeyName)) {
+// 			key := viper.GetString(p.GetConfigField(LiveModeAPIKeyName))
+// 			if !isRedactedAPIKey(key) {
+// 				fmt.Println(color.Yellow(`
+// (!) Livemode value found for the field '` + LiveModeAPIKeyName + `' in your config file.
+// Livemode values from the config file will be redacted and will not be used.`))
+
+// 				p.WriteConfigField(LiveModeAPIKeyName, RedactAPIKey(key))
+// 			}
+// 		}
+// 	}
+// }
 
 // RedactAPIKey returns a redacted version of API keys. The first 8 and last 4
 // characters are not redacted, everything else is replaced by "*" characters.
diff --git a/pkg/fixtures/triggers/customer.subscription.created.json b/pkg/fixtures/triggers/customer.subscription.created.json
index 5839555..2071d72 100644
--- a/pkg/fixtures/triggers/customer.subscription.created.json
+++ b/pkg/fixtures/triggers/customer.subscription.created.json
@@ -9,20 +9,30 @@
         "method": "post",
         "params": {
           "description": "(created by Stripe CLI)",
-          "source": "tok_visa"
+          "payment_method": "pm_card_visa",
+          "invoice_settings": {
+            "default_payment_method": "pm_card_visa"
+          }
         }
       },
       {
-        "name": "plan",
-        "path": "/v1/plans",
+        "name": "product",
+        "path": "/v1/products",
         "method": "post",
         "params": {
+            "name": "myproduct",
+            "description": "(created by Stripe CLI)"
+        }
+      },
+      {
+        "name": "price",
+        "path": "/v1/prices",
+        "method": "post",
+        "params": {
+          "product": "${product:id}",
+          "unit_amount": "1500",
           "currency": "usd",
-          "interval": "month",
-          "amount": 2000,
-          "product": {
-            "name": "myproduct"
-          }
+          "recurring[interval]": "month"
         }
       },
       {
@@ -33,7 +43,7 @@
           "customer": "${customer:id}",
           "items": [
             {
-              "plan": "${plan:id}"
+              "price": "${price:id}"
             }
           ]
         }
diff --git a/pkg/fixtures/triggers/customer.subscription.deleted.json b/pkg/fixtures/triggers/customer.subscription.deleted.json
index 3e894ae..96c3207 100644
--- a/pkg/fixtures/triggers/customer.subscription.deleted.json
+++ b/pkg/fixtures/triggers/customer.subscription.deleted.json
@@ -9,20 +9,30 @@
       "method": "post",
       "params": {
         "description": "(created by Stripe CLI)",
-        "source": "tok_visa"
+        "payment_method": "pm_card_visa",
+        "invoice_settings": {
+          "default_payment_method": "pm_card_visa"
+        }
       }
     },
     {
-      "name": "plan",
-      "path": "/v1/plans",
+      "name": "product",
+      "path": "/v1/products",
       "method": "post",
       "params": {
+          "name": "myproduct",
+          "description": "(created by Stripe CLI)"
+      }
+    },
+    {
+      "name": "price",
+      "path": "/v1/prices",
+      "method": "post",
+      "params": {
+        "product": "${product:id}",
+        "unit_amount": "1500",
         "currency": "usd",
-        "interval": "month",
-        "amount": 2000,
-        "product": {
-          "name": "myproduct"
-        }
+        "recurring[interval]": "month"
       }
     },
     {
@@ -33,7 +43,7 @@
         "customer": "${customer:id}",
         "items": [
           {
-            "plan": "${plan:id}"
+            "price": "${price:id}"
           }
         ]
       }
diff --git a/pkg/fixtures/triggers/customer.subscription.updated.json b/pkg/fixtures/triggers/customer.subscription.updated.json
index ddb3e4c..51cd8c5 100644
--- a/pkg/fixtures/triggers/customer.subscription.updated.json
+++ b/pkg/fixtures/triggers/customer.subscription.updated.json
@@ -9,20 +9,30 @@
       "method": "post",
       "params": {
         "description": "(created by Stripe CLI)",
-        "source": "tok_visa"
+        "payment_method": "pm_card_visa",
+        "invoice_settings": {
+          "default_payment_method": "pm_card_visa"
+        }
       }
     },
     {
-      "name": "plan",
-      "path": "/v1/plans",
+      "name": "product",
+      "path": "/v1/products",
       "method": "post",
       "params": {
+          "name": "myproduct",
+          "description": "(created by Stripe CLI)"
+      }
+    },
+    {
+      "name": "price",
+      "path": "/v1/prices",
+      "method": "post",
+      "params": {
+        "product": "${product:id}",
+        "unit_amount": "1500",
         "currency": "usd",
-        "interval": "month",
-        "amount": 2000,
-        "product": {
-          "name": "myproduct"
-        }
+        "recurring[interval]": "month"
       }
     },
     {
@@ -33,7 +43,7 @@
         "customer": "${customer:id}",
         "items": [
           {
-            "plan": "${plan:id}"
+            "price": "${price:id}"
           }
         ]
       }


Test patch:
diff --git a/pkg/rpcservice/trigger_test.go b/pkg/rpcservice/trigger_test.go
index a99f229..e9d37a6 100644
--- a/pkg/rpcservice/trigger_test.go
+++ b/pkg/rpcservice/trigger_test.go
@@ -20,7 +20,8 @@ import (
 const customerPath = "/v1/customers"
 const customerWithIDPath = "/v1/customers/cust_12345"
 const customerPayload = `{"id": "cust_12345", "foo": "bar"}`
-const planPath = "/v1/plans"
+const productPath = "/v1/products"
+const pricePath = "/v1/prices"
 const subscriptionPath = "/v1/subscriptions"
 
 func TestTriggerSucceedsWithSupportedEvent(t *testing.T) {
@@ -152,8 +153,10 @@ func TestTriggerSucceedsWithFixtureFlags(t *testing.T) {
 		case customerPath:
 			require.True(t, strings.Contains(string(body), "name=testUser"))
 			require.True(t, strings.Contains(string(body), "email=testEmail"))
-		case planPath:
-			require.True(t, strings.Contains(string(body), "amount=500"))
+		case productPath:
+			require.True(t, strings.Contains(string(body), "name=myproduct"))
+		case pricePath:
+			require.True(t, strings.Contains(string(body), "unit_amount=500"))
 		case subscriptionPath:
 			require.False(t, strings.Contains(string(body), "description"))
 		default:
@@ -169,13 +172,13 @@ func TestTriggerSucceedsWithFixtureFlags(t *testing.T) {
 		Event:         "customer.subscription.created",
 		StripeAccount: "acct_123",
 		Skip:          []string{},
-		Override:      []string{"customer:name=testUser", "plan:amount=500"},
+		Override:      []string{"customer:name=testUser", "price:unit_amount=500"},
 		Add:           []string{"customer:email=testEmail"},
 		Remove:        []string{"customer:description"},
 	})
 
 	expected := rpc.TriggerResponse{
-		Requests: []string{"customer", "plan", "subscription"},
+		Requests: []string{"customer", "product", "price", "subscription"},
 	}
 
 	assert.Nil(t, err)