Skip to content

Commit 4fe0427

Browse files
committed
[server] Make Stripe usage-based product price IDs configurable
1 parent fe6e39e commit 4fe0427

File tree

10 files changed

+84
-15
lines changed

10 files changed

+84
-15
lines changed

.werft/jobs/build/helm/values.payment.yaml

+8-2
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,22 @@ components:
66
- name: chargebee-config
77
mountPath: "/chargebee"
88
readOnly: true
9+
- name: stripe-secret
10+
mountPath: "/stripe-secret"
11+
readOnly: true
912
- name: stripe-config
10-
mountPath: "/stripe"
13+
mountPath: "/stripe-config"
1114
readOnly: true
1215
volumes:
1316
- name: chargebee-config
1417
secret:
1518
secretName: chargebee-config
16-
- name: stripe-config
19+
- name: stripe-secret
1720
secret:
1821
secretName: stripe-api-keys
22+
- name: stripe-config
23+
configMap:
24+
name: stripe-config
1925

2026
paymentEndpoint:
2127
disabled: false

.werft/jobs/build/installer/installer.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ export class Installer {
8383
exec(`yq w -i ${this.options.installerConfigPath} experimental.webapp.server.chargebeeSecret chargebee-config`, { slice: slice });
8484

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

8990
} catch (err) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: stripe-config
5+
data:
6+
config: >
7+
{
8+
"usageProductPriceIds": {
9+
"EUR": "price_1LAE0AGadRXm50o3xjegX0Kd",
10+
"USD": "price_1LAE0AGadRXm50o3rKoktPiJ"
11+
}
12+
}

components/server/ee/src/user/stripe-service.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -116,16 +116,15 @@ export class StripeService {
116116
}
117117

118118
async createSubscriptionForCustomer(customerId: string, currency: Currency): Promise<void> {
119-
// FIXME(janx): Use configmap.
120-
const prices = {
121-
EUR: "price_1LAE0AGadRXm50o3xjegX0Kd",
122-
USD: "price_1LAE0AGadRXm50o3rKoktPiJ",
123-
};
119+
const priceId = this.config?.stripeConfig?.usageProductPriceIds[currency];
120+
if (!priceId) {
121+
throw new Error(`No Stripe Price ID configured for currency '${currency}'`);
122+
}
124123
const startOfNextMonth = new Date(new Date().toISOString().slice(0, 7) + "-01"); // First day of this month (YYYY-MM-01)
125124
startOfNextMonth.setMonth(startOfNextMonth.getMonth() + 1); // Add one month
126125
await this.getStripe().subscriptions.create({
127126
customer: customerId,
128-
items: [{ price: prices[currency] }],
127+
items: [{ price: priceId }],
129128
billing_cycle_anchor: Math.round(startOfNextMonth.getTime() / 1000),
130129
});
131130
}

components/server/src/config.ts

+18-2
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,18 @@ import { filePathTelepresenceAware } from "@gitpod/gitpod-protocol/lib/env";
2020
export const Config = Symbol("Config");
2121
export type Config = Omit<
2222
ConfigSerialized,
23-
"blockedRepositories" | "hostUrl" | "chargebeeProviderOptionsFile" | "stripeSecretsFile" | "licenseFile"
23+
| "blockedRepositories"
24+
| "hostUrl"
25+
| "chargebeeProviderOptionsFile"
26+
| "stripeSecretsFile"
27+
| "stripeConfigFile"
28+
| "licenseFile"
2429
> & {
2530
hostUrl: GitpodHostUrl;
2631
workspaceDefaults: WorkspaceDefaults;
2732
chargebeeProviderOptions?: ChargebeeProviderOptions;
2833
stripeSecrets?: { publishableKey: string; secretKey: string };
34+
stripeConfig?: { usageProductPriceIds: { EUR: string; USD: string } };
2935
builtinAuthProvidersConfigured: boolean;
3036
blockedRepositories: { urlRegExp: RegExp; blockUser: boolean }[];
3137
inactivityPeriodForRepos?: number;
@@ -152,6 +158,7 @@ export interface ConfigSerialized {
152158
*/
153159
chargebeeProviderOptionsFile?: string;
154160
stripeSecretsFile?: string;
161+
stripeConfigFile?: string;
155162
enablePayment?: boolean;
156163

157164
/**
@@ -222,7 +229,15 @@ export namespace ConfigFile {
222229
fs.readFileSync(filePathTelepresenceAware(config.stripeSecretsFile), "utf-8"),
223230
);
224231
} catch (error) {
225-
console.error("Could not load Stripe secrets", error);
232+
log.error("Could not load Stripe secrets", error);
233+
}
234+
}
235+
let stripeConfig: { usageProductPriceIds: { EUR: string; USD: string } } | undefined;
236+
if (config.enablePayment && config.stripeConfigFile) {
237+
try {
238+
stripeConfig = JSON.parse(fs.readFileSync(filePathTelepresenceAware(config.stripeConfigFile), "utf-8"));
239+
} catch (error) {
240+
log.error("Could not load Stripe config", error);
226241
}
227242
}
228243
let license = config.license;
@@ -252,6 +267,7 @@ export namespace ConfigFile {
252267
builtinAuthProvidersConfigured,
253268
chargebeeProviderOptions,
254269
stripeSecrets,
270+
stripeConfig,
255271
license,
256272
workspaceGarbageCollection: {
257273
...config.workspaceGarbageCollection,

install/installer/pkg/components/server/configmap.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,14 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) {
9999
return nil
100100
})
101101

102+
stripeConfig := ""
103+
_ = ctx.WithExperimental(func(cfg *experimental.Config) error {
104+
if cfg.WebApp != nil && cfg.WebApp.Server != nil {
105+
stripeConfig = cfg.WebApp.Server.StripeConfig
106+
}
107+
return nil
108+
})
109+
102110
disableWsGarbageCollection := false
103111
_ = ctx.WithExperimental(func(cfg *experimental.Config) error {
104112
if cfg.WebApp != nil && cfg.WebApp.Server != nil {
@@ -216,9 +224,10 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) {
216224
ImageBuilderAddr: "image-builder-mk3:8080",
217225
CodeSync: CodeSync{},
218226
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
219-
EnablePayment: chargebeeSecret != "" || stripeSecret != "",
227+
EnablePayment: chargebeeSecret != "" || stripeSecret != "" || stripeConfig != "",
220228
ChargebeeProviderOptionsFile: fmt.Sprintf("%s/providerOptions", chargebeeMountPath),
221-
StripeSecretsFile: fmt.Sprintf("%s/apikeys", stripeMountPath),
229+
StripeSecretsFile: fmt.Sprintf("%s/apikeys", stripeSecretMountPath),
230+
StripeConfigFile: fmt.Sprintf("%s/config", stripeConfigMountPath),
222231
InsecureNoDomain: false,
223232
PrebuildLimiter: map[string]int{
224233
// default limit for all cloneURLs

install/installer/pkg/components/server/constants.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ const (
1515
authProviderFilePath = "/gitpod/auth-providers"
1616
licenseFilePath = "/gitpod/license"
1717
chargebeeMountPath = "/chargebee"
18-
stripeMountPath = "/stripe"
18+
stripeSecretMountPath = "/stripe-secret"
19+
stripeConfigMountPath = "/stripe-config"
1920
githubAppCertSecret = "github-app-cert-secret"
2021
PrometheusPort = 9500
2122
PrometheusPortName = "metrics"

install/installer/pkg/components/server/deployment.go

+24-1
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,30 @@ func deployment(ctx *common.RenderContext) ([]runtime.Object, error) {
204204

205205
volumeMounts = append(volumeMounts, corev1.VolumeMount{
206206
Name: "stripe-secret",
207-
MountPath: stripeMountPath,
207+
MountPath: stripeSecretMountPath,
208+
ReadOnly: true,
209+
})
210+
}
211+
return nil
212+
})
213+
214+
_ = ctx.WithExperimental(func(cfg *experimental.Config) error {
215+
if cfg.WebApp != nil && cfg.WebApp.Server != nil && cfg.WebApp.Server.StripeConfig != "" {
216+
stripeConfig := cfg.WebApp.Server.StripeConfig
217+
218+
volumes = append(volumes,
219+
corev1.Volume{
220+
Name: "stripe-config",
221+
VolumeSource: corev1.VolumeSource{
222+
ConfigMap: &corev1.ConfigMapVolumeSource{
223+
LocalObjectReference: corev1.LocalObjectReference{Name: stripeConfig},
224+
},
225+
},
226+
})
227+
228+
volumeMounts = append(volumeMounts, corev1.VolumeMount{
229+
Name: "stripe-config",
230+
MountPath: stripeConfigMountPath,
208231
ReadOnly: true,
209232
})
210233
}

install/installer/pkg/components/server/types.go

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type ConfigSerialized struct {
3333
VSXRegistryUrl string `json:"vsxRegistryUrl"`
3434
ChargebeeProviderOptionsFile string `json:"chargebeeProviderOptionsFile"`
3535
StripeSecretsFile string `json:"stripeSecretsFile"`
36+
StripeConfigFile string `json:"stripeConfigFile"`
3637
EnablePayment bool `json:"enablePayment"`
3738

3839
WorkspaceHeartbeat WorkspaceHeartbeat `json:"workspaceHeartbeat"`

install/installer/pkg/config/v1/experimental/experimental.go

+1
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ type ServerConfig struct {
140140
GithubApp *GithubApp `json:"githubApp"`
141141
ChargebeeSecret string `json:"chargebeeSecret"`
142142
StripeSecret string `json:"stripeSecret"`
143+
StripeConfig string `json:"stripeConfig"`
143144
DisableDynamicAuthProviderLogin bool `json:"disableDynamicAuthProviderLogin"`
144145
EnableLocalApp *bool `json:"enableLocalApp"`
145146
RunDbDeleter *bool `json:"runDbDeleter"`

0 commit comments

Comments
 (0)