Skip to content

[server] Make Stripe usage-based product price IDs configurable #10660

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .werft/jobs/build/installer/installer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export class Installer {

// let installer know that there is a stripe config
exec(`yq w -i ${this.options.installerConfigPath} experimental.webapp.server.stripeSecret stripe-api-keys`, { slice: slice });
exec(`yq w -i ${this.options.installerConfigPath} experimental.webapp.server.stripeConfig stripe-config`, { slice: slice });
}

} catch (err) {
Expand Down
13 changes: 13 additions & 0 deletions .werft/jobs/build/payment/stripe-configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: stripe-config
namespace: ${NAMESPACE}
data:
config: >
{
"usageProductPriceIds": {
"EUR": "price_1LAyjSGadRXm50o3MO7CNyVU",
"USD": "price_1LAyjSGadRXm50o3tkwZe1N2"
}
}
11 changes: 5 additions & 6 deletions components/server/ee/src/user/stripe-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,16 +116,15 @@ export class StripeService {
}

async createSubscriptionForCustomer(customerId: string, currency: Currency): Promise<void> {
// FIXME(janx): Use configmap.
const prices = {
EUR: "price_1LAE0AGadRXm50o3xjegX0Kd",
USD: "price_1LAE0AGadRXm50o3rKoktPiJ",
};
const priceId = this.config?.stripeConfig?.usageProductPriceIds[currency];
if (!priceId) {
throw new Error(`No Stripe Price ID configured for currency '${currency}'`);
}
const startOfNextMonth = new Date(new Date().toISOString().slice(0, 7) + "-01"); // First day of this month (YYYY-MM-01)
startOfNextMonth.setMonth(startOfNextMonth.getMonth() + 1); // Add one month
await this.getStripe().subscriptions.create({
customer: customerId,
items: [{ price: prices[currency] }],
items: [{ price: priceId }],
billing_cycle_anchor: Math.round(startOfNextMonth.getTime() / 1000),
});
}
Expand Down
20 changes: 18 additions & 2 deletions components/server/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,18 @@ import { filePathTelepresenceAware } from "@gitpod/gitpod-protocol/lib/env";
export const Config = Symbol("Config");
export type Config = Omit<
ConfigSerialized,
"blockedRepositories" | "hostUrl" | "chargebeeProviderOptionsFile" | "stripeSecretsFile" | "licenseFile"
| "blockedRepositories"
| "hostUrl"
| "chargebeeProviderOptionsFile"
| "stripeSecretsFile"
| "stripeConfigFile"
| "licenseFile"
> & {
hostUrl: GitpodHostUrl;
workspaceDefaults: WorkspaceDefaults;
chargebeeProviderOptions?: ChargebeeProviderOptions;
stripeSecrets?: { publishableKey: string; secretKey: string };
stripeConfig?: { usageProductPriceIds: { EUR: string; USD: string } };
builtinAuthProvidersConfigured: boolean;
blockedRepositories: { urlRegExp: RegExp; blockUser: boolean }[];
inactivityPeriodForRepos?: number;
Expand Down Expand Up @@ -152,6 +158,7 @@ export interface ConfigSerialized {
*/
chargebeeProviderOptionsFile?: string;
stripeSecretsFile?: string;
stripeConfigFile?: string;
enablePayment?: boolean;

/**
Expand Down Expand Up @@ -222,7 +229,15 @@ export namespace ConfigFile {
fs.readFileSync(filePathTelepresenceAware(config.stripeSecretsFile), "utf-8"),
);
} catch (error) {
console.error("Could not load Stripe secrets", error);
log.error("Could not load Stripe secrets", error);
}
}
let stripeConfig: { usageProductPriceIds: { EUR: string; USD: string } } | undefined;
if (config.enablePayment && config.stripeConfigFile) {
try {
stripeConfig = JSON.parse(fs.readFileSync(filePathTelepresenceAware(config.stripeConfigFile), "utf-8"));
} catch (error) {
log.error("Could not load Stripe config", error);
}
}
let license = config.license;
Expand Down Expand Up @@ -252,6 +267,7 @@ export namespace ConfigFile {
builtinAuthProvidersConfigured,
chargebeeProviderOptions,
stripeSecrets,
stripeConfig,
license,
workspaceGarbageCollection: {
...config.workspaceGarbageCollection,
Expand Down
13 changes: 11 additions & 2 deletions install/installer/pkg/components/server/configmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,14 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) {
return nil
})

stripeConfig := ""
_ = ctx.WithExperimental(func(cfg *experimental.Config) error {
if cfg.WebApp != nil && cfg.WebApp.Server != nil {
stripeConfig = cfg.WebApp.Server.StripeConfig
}
return nil
})

disableWsGarbageCollection := false
_ = ctx.WithExperimental(func(cfg *experimental.Config) error {
if cfg.WebApp != nil && cfg.WebApp.Server != nil {
Expand Down Expand Up @@ -216,9 +224,10 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) {
ImageBuilderAddr: "image-builder-mk3:8080",
CodeSync: CodeSync{},
VSXRegistryUrl: fmt.Sprintf("https://open-vsx.%s", ctx.Config.Domain), // todo(sje): or "https://{{ .Values.vsxRegistry.host | default "open-vsx.org" }}" if not using OpenVSX proxy
EnablePayment: chargebeeSecret != "" || stripeSecret != "",
EnablePayment: chargebeeSecret != "" || stripeSecret != "" || stripeConfig != "",
ChargebeeProviderOptionsFile: fmt.Sprintf("%s/providerOptions", chargebeeMountPath),
StripeSecretsFile: fmt.Sprintf("%s/apikeys", stripeMountPath),
StripeSecretsFile: fmt.Sprintf("%s/apikeys", stripeSecretMountPath),
StripeConfigFile: fmt.Sprintf("%s/config", stripeConfigMountPath),
InsecureNoDomain: false,
PrebuildLimiter: map[string]int{
// default limit for all cloneURLs
Expand Down
3 changes: 2 additions & 1 deletion install/installer/pkg/components/server/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ const (
authProviderFilePath = "/gitpod/auth-providers"
licenseFilePath = "/gitpod/license"
chargebeeMountPath = "/chargebee"
stripeMountPath = "/stripe"
stripeSecretMountPath = "/stripe-secret"
stripeConfigMountPath = "/stripe-config"
githubAppCertSecret = "github-app-cert-secret"
PrometheusPort = 9500
PrometheusPortName = "metrics"
Expand Down
25 changes: 24 additions & 1 deletion install/installer/pkg/components/server/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,30 @@ func deployment(ctx *common.RenderContext) ([]runtime.Object, error) {

volumeMounts = append(volumeMounts, corev1.VolumeMount{
Name: "stripe-secret",
MountPath: stripeMountPath,
MountPath: stripeSecretMountPath,
ReadOnly: true,
})
}
return nil
})

_ = ctx.WithExperimental(func(cfg *experimental.Config) error {
if cfg.WebApp != nil && cfg.WebApp.Server != nil && cfg.WebApp.Server.StripeConfig != "" {
stripeConfig := cfg.WebApp.Server.StripeConfig

volumes = append(volumes,
corev1.Volume{
Name: "stripe-config",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{Name: stripeConfig},
},
},
})

volumeMounts = append(volumeMounts, corev1.VolumeMount{
Name: "stripe-config",
MountPath: stripeConfigMountPath,
ReadOnly: true,
})
}
Expand Down
1 change: 1 addition & 0 deletions install/installer/pkg/components/server/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type ConfigSerialized struct {
VSXRegistryUrl string `json:"vsxRegistryUrl"`
ChargebeeProviderOptionsFile string `json:"chargebeeProviderOptionsFile"`
StripeSecretsFile string `json:"stripeSecretsFile"`
StripeConfigFile string `json:"stripeConfigFile"`
EnablePayment bool `json:"enablePayment"`

WorkspaceHeartbeat WorkspaceHeartbeat `json:"workspaceHeartbeat"`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ type ServerConfig struct {
GithubApp *GithubApp `json:"githubApp"`
ChargebeeSecret string `json:"chargebeeSecret"`
StripeSecret string `json:"stripeSecret"`
StripeConfig string `json:"stripeConfig"`
DisableDynamicAuthProviderLogin bool `json:"disableDynamicAuthProviderLogin"`
EnableLocalApp *bool `json:"enableLocalApp"`
RunDbDeleter *bool `json:"runDbDeleter"`
Expand Down