Launch
This commit is contained in:
@@ -1,156 +1,186 @@
|
||||
import { CareInfo, IdentificationResult, Language, PlantHealthCheck } from '../../types';
|
||||
|
||||
export type PlanId = 'free' | 'pro';
|
||||
export type BillingProvider = 'mock' | 'revenuecat' | 'stripe';
|
||||
export type PurchaseProductId = 'monthly_pro' | 'yearly_pro' | 'topup_small' | 'topup_medium' | 'topup_large';
|
||||
export type SimulatedWebhookEvent =
|
||||
| 'entitlement_granted'
|
||||
| 'entitlement_revoked'
|
||||
| 'topup_granted'
|
||||
| 'credits_depleted';
|
||||
|
||||
export interface BackendDatabaseEntry extends IdentificationResult {
|
||||
imageUri: string;
|
||||
imageStatus?: 'ok' | 'missing' | 'invalid';
|
||||
categories: string[];
|
||||
}
|
||||
|
||||
export interface CreditState {
|
||||
monthlyAllowance: number;
|
||||
usedThisCycle: number;
|
||||
topupBalance: number;
|
||||
available: number;
|
||||
cycleStartedAt: string;
|
||||
cycleEndsAt: string;
|
||||
}
|
||||
|
||||
export interface EntitlementState {
|
||||
plan: PlanId;
|
||||
provider: BillingProvider;
|
||||
status: 'active' | 'inactive';
|
||||
renewsAt: string | null;
|
||||
}
|
||||
|
||||
import { CareInfo, IdentificationResult, Language, PlantHealthCheck } from '../../types';
|
||||
|
||||
export type PlanId = 'free' | 'pro';
|
||||
export type BillingProvider = 'mock' | 'revenuecat' | 'stripe';
|
||||
export type PurchaseProductId = 'monthly_pro' | 'yearly_pro' | 'topup_small' | 'topup_medium' | 'topup_large';
|
||||
export type SimulatedWebhookEvent =
|
||||
| 'entitlement_granted'
|
||||
| 'entitlement_revoked'
|
||||
| 'topup_granted'
|
||||
| 'credits_depleted';
|
||||
|
||||
export interface BackendDatabaseEntry extends IdentificationResult {
|
||||
imageUri: string;
|
||||
imageStatus?: 'ok' | 'missing' | 'invalid';
|
||||
categories: string[];
|
||||
}
|
||||
|
||||
export interface CreditState {
|
||||
monthlyAllowance: number;
|
||||
usedThisCycle: number;
|
||||
topupBalance: number;
|
||||
available: number;
|
||||
cycleStartedAt: string;
|
||||
cycleEndsAt: string;
|
||||
}
|
||||
|
||||
export interface EntitlementState {
|
||||
plan: PlanId;
|
||||
provider: BillingProvider;
|
||||
status: 'active' | 'inactive';
|
||||
renewsAt: string | null;
|
||||
}
|
||||
|
||||
export interface BillingSummary {
|
||||
entitlement: EntitlementState;
|
||||
credits: CreditState;
|
||||
availableProducts: PurchaseProductId[];
|
||||
}
|
||||
|
||||
export interface ScanPlantRequest {
|
||||
userId: string;
|
||||
idempotencyKey: string;
|
||||
imageUri: string;
|
||||
language: Language;
|
||||
export interface RevenueCatEntitlementInfo {
|
||||
productIdentifier?: string;
|
||||
expirationDate?: string | null;
|
||||
expiresDate?: string | null;
|
||||
}
|
||||
|
||||
export interface ScanPlantResponse {
|
||||
result: IdentificationResult;
|
||||
creditsCharged: number;
|
||||
modelPath: string[];
|
||||
modelUsed?: string | null;
|
||||
modelFallbackCount?: number;
|
||||
billing: BillingSummary;
|
||||
export interface RevenueCatNonSubscriptionTransaction {
|
||||
productIdentifier?: string;
|
||||
transactionIdentifier?: string;
|
||||
transactionId?: string;
|
||||
purchaseDate?: string | null;
|
||||
}
|
||||
|
||||
export interface SemanticSearchRequest {
|
||||
userId: string;
|
||||
idempotencyKey: string;
|
||||
query: string;
|
||||
language: Language;
|
||||
}
|
||||
|
||||
export interface SemanticSearchResponse {
|
||||
status: 'success' | 'no_results';
|
||||
results: BackendDatabaseEntry[];
|
||||
creditsCharged: number;
|
||||
billing: BillingSummary;
|
||||
}
|
||||
|
||||
export interface HealthCheckRequest {
|
||||
userId: string;
|
||||
idempotencyKey: string;
|
||||
imageUri: string;
|
||||
language: Language;
|
||||
plantContext?: {
|
||||
name: string;
|
||||
botanicalName: string;
|
||||
careInfo: CareInfo;
|
||||
description?: string;
|
||||
export interface RevenueCatCustomerInfo {
|
||||
appUserId?: string | null;
|
||||
originalAppUserId?: string | null;
|
||||
entitlements: {
|
||||
active: Record<string, RevenueCatEntitlementInfo>;
|
||||
};
|
||||
nonSubscriptions?: Record<string, RevenueCatNonSubscriptionTransaction[]>;
|
||||
allPurchasedProductIdentifiers?: string[];
|
||||
latestExpirationDate?: string | null;
|
||||
}
|
||||
|
||||
export interface HealthCheckResponse {
|
||||
healthCheck: PlantHealthCheck;
|
||||
creditsCharged: number;
|
||||
modelUsed?: string | null;
|
||||
modelFallbackCount?: number;
|
||||
billing: BillingSummary;
|
||||
}
|
||||
|
||||
export interface ServiceHealthResponse {
|
||||
ok: boolean;
|
||||
uptimeSec: number;
|
||||
timestamp: string;
|
||||
openAiConfigured: boolean;
|
||||
dbReady?: boolean;
|
||||
dbPath?: string;
|
||||
stripeConfigured?: boolean;
|
||||
scanModel?: string;
|
||||
healthModel?: string;
|
||||
}
|
||||
|
||||
export interface SimulatePurchaseRequest {
|
||||
userId: string;
|
||||
idempotencyKey: string;
|
||||
productId: PurchaseProductId;
|
||||
}
|
||||
|
||||
export interface SimulatePurchaseResponse {
|
||||
appliedProduct: PurchaseProductId;
|
||||
billing: BillingSummary;
|
||||
}
|
||||
|
||||
export interface SimulateWebhookRequest {
|
||||
userId: string;
|
||||
idempotencyKey: string;
|
||||
event: SimulatedWebhookEvent;
|
||||
payload?: {
|
||||
credits?: number;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export interface ScanPlantRequest {
|
||||
userId: string;
|
||||
idempotencyKey: string;
|
||||
imageUri: string;
|
||||
language: Language;
|
||||
}
|
||||
|
||||
export interface ScanPlantResponse {
|
||||
result: IdentificationResult;
|
||||
creditsCharged: number;
|
||||
modelPath: string[];
|
||||
modelUsed?: string | null;
|
||||
modelFallbackCount?: number;
|
||||
billing: BillingSummary;
|
||||
}
|
||||
|
||||
export interface SemanticSearchRequest {
|
||||
userId: string;
|
||||
idempotencyKey: string;
|
||||
query: string;
|
||||
language: Language;
|
||||
}
|
||||
|
||||
export interface SemanticSearchResponse {
|
||||
status: 'success' | 'no_results';
|
||||
results: BackendDatabaseEntry[];
|
||||
creditsCharged: number;
|
||||
billing: BillingSummary;
|
||||
}
|
||||
|
||||
export interface HealthCheckRequest {
|
||||
userId: string;
|
||||
idempotencyKey: string;
|
||||
imageUri: string;
|
||||
language: Language;
|
||||
plantContext?: {
|
||||
name: string;
|
||||
botanicalName: string;
|
||||
careInfo: CareInfo;
|
||||
description?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface HealthCheckResponse {
|
||||
healthCheck: PlantHealthCheck;
|
||||
creditsCharged: number;
|
||||
modelUsed?: string | null;
|
||||
modelFallbackCount?: number;
|
||||
billing: BillingSummary;
|
||||
}
|
||||
|
||||
export interface ServiceHealthResponse {
|
||||
ok: boolean;
|
||||
uptimeSec: number;
|
||||
timestamp: string;
|
||||
openAiConfigured: boolean;
|
||||
dbReady?: boolean;
|
||||
dbPath?: string;
|
||||
stripeConfigured?: boolean;
|
||||
scanModel?: string;
|
||||
healthModel?: string;
|
||||
}
|
||||
|
||||
export interface SimulatePurchaseRequest {
|
||||
userId: string;
|
||||
idempotencyKey: string;
|
||||
productId: PurchaseProductId;
|
||||
}
|
||||
|
||||
export interface SimulatePurchaseResponse {
|
||||
appliedProduct: PurchaseProductId;
|
||||
billing: BillingSummary;
|
||||
}
|
||||
|
||||
export interface SimulateWebhookRequest {
|
||||
userId: string;
|
||||
idempotencyKey: string;
|
||||
event: SimulatedWebhookEvent;
|
||||
payload?: {
|
||||
credits?: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface SimulateWebhookResponse {
|
||||
event: SimulatedWebhookEvent;
|
||||
billing: BillingSummary;
|
||||
}
|
||||
|
||||
export type BackendErrorCode =
|
||||
| 'INSUFFICIENT_CREDITS'
|
||||
| 'UNAUTHORIZED'
|
||||
| 'TIMEOUT'
|
||||
| 'PROVIDER_ERROR'
|
||||
| 'BAD_REQUEST';
|
||||
|
||||
export class BackendApiError extends Error {
|
||||
public readonly code: BackendErrorCode;
|
||||
public readonly status: number;
|
||||
public readonly metadata?: Record<string, unknown>;
|
||||
|
||||
constructor(
|
||||
code: BackendErrorCode,
|
||||
message: string,
|
||||
status = 500,
|
||||
metadata?: Record<string, unknown>,
|
||||
) {
|
||||
super(message);
|
||||
this.name = 'BackendApiError';
|
||||
this.code = code;
|
||||
this.status = status;
|
||||
this.metadata = metadata;
|
||||
}
|
||||
export interface SyncRevenueCatStateResponse {
|
||||
billing: BillingSummary;
|
||||
syncedAt: string;
|
||||
}
|
||||
|
||||
export const isBackendApiError = (error: unknown): error is BackendApiError => {
|
||||
return error instanceof BackendApiError;
|
||||
};
|
||||
|
||||
export type BackendErrorCode =
|
||||
| 'INSUFFICIENT_CREDITS'
|
||||
| 'UNAUTHORIZED'
|
||||
| 'TIMEOUT'
|
||||
| 'NETWORK_ERROR'
|
||||
| 'PROVIDER_ERROR'
|
||||
| 'BAD_REQUEST';
|
||||
|
||||
export class BackendApiError extends Error {
|
||||
public readonly code: BackendErrorCode;
|
||||
public readonly status: number;
|
||||
public readonly metadata?: Record<string, unknown>;
|
||||
|
||||
constructor(
|
||||
code: BackendErrorCode,
|
||||
message: string,
|
||||
status = 500,
|
||||
metadata?: Record<string, unknown>,
|
||||
) {
|
||||
super(message);
|
||||
this.name = 'BackendApiError';
|
||||
this.code = code;
|
||||
this.status = status;
|
||||
this.metadata = metadata;
|
||||
}
|
||||
}
|
||||
|
||||
export const isBackendApiError = (error: unknown): error is BackendApiError => {
|
||||
return error instanceof BackendApiError;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user