Skip to content
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
36 changes: 31 additions & 5 deletions frontend/app/[locale]/shop/cart/capabilities.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,40 @@
import { resolveStandardStorefrontProviderCapabilities } from '@/lib/shop/commercial-policy.server';
import { isMonobankEnabled } from '@/lib/env/monobank';
import { readServerEnv } from '@/lib/env/server-env';
import { isPaymentsEnabled as isStripePaymentsEnabled } from '@/lib/env/stripe';

function isFlagEnabled(value: string | undefined): boolean {
const normalized = (value ?? '').trim().toLowerCase();
return (
normalized === 'true' ||
normalized === '1' ||
normalized === 'yes' ||
normalized === 'on'
);
}

export function resolveStripeCheckoutEnabled(): boolean {
return resolveStandardStorefrontProviderCapabilities().stripeCheckoutEnabled;
try {
return isStripePaymentsEnabled({
requirePublishableKey: true,
});
} catch {
return false;
}
}

export function resolveMonobankCheckoutEnabled(): boolean {
return resolveStandardStorefrontProviderCapabilities().monobankCheckoutEnabled;
const paymentsEnabled = isFlagEnabled(readServerEnv('PAYMENTS_ENABLED'));
if (!paymentsEnabled) return false;

try {
return isMonobankEnabled();
} catch {
return false;
}
}

export function resolveMonobankGooglePayEnabled(): boolean {
return resolveStandardStorefrontProviderCapabilities()
.monobankGooglePayEnabled;
if (!resolveMonobankCheckoutEnabled()) return false;

return isFlagEnabled(readServerEnv('SHOP_MONOBANK_GPAY_ENABLED'));
}
39 changes: 20 additions & 19 deletions frontend/lib/env/monobank.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import 'server-only';

import { getRuntimeEnv } from '@/lib/env';
import {
assertProductionLikeProviderString,
assertProductionLikeProviderUrl,
} from '@/lib/env/provider-runtime';
import { readServerEnv } from '@/lib/env/server-env';

export type MonobankEnv = {
token: string | null;
Expand All @@ -23,17 +23,17 @@ function parseWebhookMode(raw: string | undefined): MonobankWebhookMode {
}

export function getMonobankConfig(): MonobankConfig {
const rawMode = process.env.MONO_WEBHOOK_MODE;
const rawMode = readServerEnv('MONO_WEBHOOK_MODE');

return {
webhookMode: parseWebhookMode(rawMode),
refundEnabled: process.env.MONO_REFUND_ENABLED === 'true',
refundEnabled: readServerEnv('MONO_REFUND_ENABLED') === 'true',
invoiceValiditySeconds: parsePositiveInt(
process.env.MONO_INVOICE_VALIDITY_SECONDS,
readServerEnv('MONO_INVOICE_VALIDITY_SECONDS'),
86400
),
timeSkewToleranceSec: parsePositiveInt(
process.env.MONO_TIME_SKEW_TOLERANCE_SEC,
readServerEnv('MONO_TIME_SKEW_TOLERANCE_SEC'),
300
),
baseUrlSource: resolveBaseUrlSource(),
Expand Down Expand Up @@ -71,7 +71,7 @@ function parsePositiveInt(raw: string | undefined, fallback: number): number {
}

function resolveMonobankToken(): string | null {
return nonEmpty(process.env.MONO_MERCHANT_TOKEN);
return nonEmpty(readServerEnv('MONO_MERCHANT_TOKEN'));
}

function assertMonobankRuntimeConfig(args: {
Expand Down Expand Up @@ -102,9 +102,10 @@ function assertMonobankRuntimeConfig(args: {
}

function resolveBaseUrlSource(): MonobankConfig['baseUrlSource'] {
if (nonEmpty(process.env.SHOP_BASE_URL)) return 'shop_base_url';
if (nonEmpty(process.env.APP_ORIGIN)) return 'app_origin';
if (nonEmpty(process.env.NEXT_PUBLIC_SITE_URL)) return 'next_public_site_url';
if (nonEmpty(readServerEnv('SHOP_BASE_URL'))) return 'shop_base_url';
if (nonEmpty(readServerEnv('APP_ORIGIN'))) return 'app_origin';
if (nonEmpty(readServerEnv('NEXT_PUBLIC_SITE_URL')))
return 'next_public_site_url';
return 'unknown';
}

Expand All @@ -116,28 +117,28 @@ export function requireMonobankToken(): string {
assertMonobankRuntimeConfig({
token,
apiBaseUrl:
nonEmpty(process.env.MONO_API_BASE) ?? 'https://api.monobank.ua',
publicKey: nonEmpty(process.env.MONO_PUBLIC_KEY),
nonEmpty(readServerEnv('MONO_API_BASE')) ?? 'https://api.monobank.ua',
publicKey: nonEmpty(readServerEnv('MONO_PUBLIC_KEY')),
});
return token;
}

export function getMonobankEnv(): MonobankEnv {
const runtimeEnv = getRuntimeEnv();
const nodeEnv = readServerEnv('NODE_ENV') ?? process.env.NODE_ENV;

const token = resolveMonobankToken();
const publicKey = nonEmpty(process.env.MONO_PUBLIC_KEY);
const publicKey = nonEmpty(readServerEnv('MONO_PUBLIC_KEY'));

const apiBaseUrl =
nonEmpty(process.env.MONO_API_BASE) ?? 'https://api.monobank.ua';
nonEmpty(readServerEnv('MONO_API_BASE')) ?? 'https://api.monobank.ua';

const paymentsFlag = process.env.PAYMENTS_ENABLED ?? 'false';
const paymentsFlag = readServerEnv('PAYMENTS_ENABLED') ?? 'false';
const configured = !!token;
const paymentsEnabled = String(paymentsFlag).trim() === 'true' && configured;

const invoiceTimeoutMs = parseTimeoutMs(
process.env.MONO_INVOICE_TIMEOUT_MS,
runtimeEnv.NODE_ENV === 'production' ? 8000 : 12000
readServerEnv('MONO_INVOICE_TIMEOUT_MS'),
String(nodeEnv).trim().toLowerCase() === 'production' ? 8000 : 12000
);

if (!paymentsEnabled) {
Expand Down Expand Up @@ -172,8 +173,8 @@ export function isMonobankEnabled(): boolean {
assertMonobankRuntimeConfig({
token,
apiBaseUrl:
nonEmpty(process.env.MONO_API_BASE) ?? 'https://api.monobank.ua',
publicKey: nonEmpty(process.env.MONO_PUBLIC_KEY),
nonEmpty(readServerEnv('MONO_API_BASE')) ?? 'https://api.monobank.ua',
publicKey: nonEmpty(readServerEnv('MONO_PUBLIC_KEY')),
});

return true;
Expand Down
6 changes: 4 additions & 2 deletions frontend/lib/env/provider-runtime.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'server-only';

import { readServerEnv } from './server-env';

const PLACEHOLDER_SEGMENTS = new Set([
'test',
'testing',
Expand Down Expand Up @@ -59,10 +61,10 @@ export class ShopProviderConfigError extends Error {
}

export function isProductionLikeRuntime(): boolean {
const appEnv = String(process.env.APP_ENV ?? '')
const appEnv = String(readServerEnv('APP_ENV') ?? '')
.trim()
.toLowerCase();
const nodeEnv = String(process.env.NODE_ENV ?? '')
const nodeEnv = String(readServerEnv('NODE_ENV') ?? process.env.NODE_ENV ?? '')
.trim()
.toLowerCase();
return appEnv === 'production' || nodeEnv === 'production';
Expand Down
26 changes: 14 additions & 12 deletions frontend/lib/env/stripe.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { getClientEnv, getRuntimeEnv } from '@/lib/env';
import {
assertProductionLikeProviderString,
isProductionLikeRuntime,
ShopProviderConfigError,
} from '@/lib/env/provider-runtime';
import { readServerEnv } from '@/lib/env/server-env';

export type StripeEnv = {
secretKey: string | null;
Expand All @@ -25,19 +25,21 @@ function nonEmpty(v: string | undefined): string | null {
}

export function getStripeEnv(): StripeEnv {
const runtimeEnv = getRuntimeEnv();
const clientEnv = getClientEnv();

const paymentsFlag = process.env.PAYMENTS_ENABLED ?? 'false';
const secretKey = nonEmpty(process.env.STRIPE_SECRET_KEY);
const webhookSecret = nonEmpty(process.env.STRIPE_WEBHOOK_SECRET);
const nodeEnv = readServerEnv('NODE_ENV') ?? process.env.NODE_ENV;
const paymentsFlag = readServerEnv('PAYMENTS_ENABLED') ?? 'false';
const secretKey = nonEmpty(readServerEnv('STRIPE_SECRET_KEY'));
const webhookSecret = nonEmpty(readServerEnv('STRIPE_WEBHOOK_SECRET'));
const publishableKey = nonEmpty(
clientEnv.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY ?? undefined
readServerEnv('NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY')
);
const rawMode = nonEmpty(readServerEnv('STRIPE_MODE'))?.toLowerCase();

const mode =
(nonEmpty(process.env.STRIPE_MODE) as 'test' | 'live' | null) ??
(runtimeEnv.NODE_ENV === 'production' ? 'live' : 'test');
rawMode === 'test' || rawMode === 'live'
? rawMode
: String(nodeEnv).trim().toLowerCase() === 'production'
? 'live'
: 'test';

const paymentsEnabled =
paymentsFlag === 'true' && !!secretKey && !!webhookSecret;
Expand Down Expand Up @@ -100,10 +102,10 @@ function isFlagEnabled(value: string | undefined): boolean {
}

function isStripeRailEnabledByFlags(): boolean {
const paymentsEnabled = isFlagEnabled(process.env.PAYMENTS_ENABLED);
const paymentsEnabled = isFlagEnabled(readServerEnv('PAYMENTS_ENABLED'));
if (!paymentsEnabled) return false;

const stripeFlag = (process.env.STRIPE_PAYMENTS_ENABLED ?? '').trim();
const stripeFlag = (readServerEnv('STRIPE_PAYMENTS_ENABLED') ?? '').trim();
return stripeFlag.length > 0 ? stripeFlag === 'true' : true;
}

Expand Down
Loading
Loading