@bonginkan/maria 4.3.31 → 4.3.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5158,22 +5158,22 @@ var init_from = __esm({
5158
5158
  init_file();
5159
5159
  init_fetch_blob();
5160
5160
  ({ stat } = fs.promises);
5161
- blobFromSync = (path3, type) => fromBlob(fs.statSync(path3), path3, type);
5162
- blobFrom = (path3, type) => stat(path3).then((stat2) => fromBlob(stat2, path3, type));
5163
- fileFrom = (path3, type) => stat(path3).then((stat2) => fromFile(stat2, path3, type));
5164
- fileFromSync = (path3, type) => fromFile(fs.statSync(path3), path3, type);
5165
- fromBlob = (stat2, path3, type = "") => new fetch_blob_default([new BlobDataItem({
5166
- path: path3,
5161
+ blobFromSync = (path4, type) => fromBlob(fs.statSync(path4), path4, type);
5162
+ blobFrom = (path4, type) => stat(path4).then((stat2) => fromBlob(stat2, path4, type));
5163
+ fileFrom = (path4, type) => stat(path4).then((stat2) => fromFile(stat2, path4, type));
5164
+ fileFromSync = (path4, type) => fromFile(fs.statSync(path4), path4, type);
5165
+ fromBlob = (stat2, path4, type = "") => new fetch_blob_default([new BlobDataItem({
5166
+ path: path4,
5167
5167
  size: stat2.size,
5168
5168
  lastModified: stat2.mtimeMs,
5169
5169
  start: 0
5170
5170
  })], { type });
5171
- fromFile = (stat2, path3, type = "") => new file_default([new BlobDataItem({
5172
- path: path3,
5171
+ fromFile = (stat2, path4, type = "") => new file_default([new BlobDataItem({
5172
+ path: path4,
5173
5173
  size: stat2.size,
5174
5174
  lastModified: stat2.mtimeMs,
5175
5175
  start: 0
5176
- })], path.basename(path3), { type, lastModified: stat2.mtimeMs });
5176
+ })], path.basename(path4), { type, lastModified: stat2.mtimeMs });
5177
5177
  BlobDataItem = class _BlobDataItem {
5178
5178
  #path;
5179
5179
  #start;
@@ -6844,11 +6844,11 @@ function getRateLimitConfig(endpoint, plan) {
6844
6844
  const key = `${endpoint}:${plan.toUpperCase()}`;
6845
6845
  return RATE_LIMITS[key] || RATE_LIMITS.default;
6846
6846
  }
6847
- function getEndpointCategory(path3) {
6848
- if (path3.includes("/image")) return "/image";
6849
- if (path3.includes("/video")) return "/video";
6850
- if (path3.includes("/code")) return "/code";
6851
- if (path3.includes("/chat")) return "/chat";
6847
+ function getEndpointCategory(path4) {
6848
+ if (path4.includes("/image")) return "/image";
6849
+ if (path4.includes("/video")) return "/video";
6850
+ if (path4.includes("/code")) return "/code";
6851
+ if (path4.includes("/chat")) return "/chat";
6852
6852
  return "default";
6853
6853
  }
6854
6854
  async function rateLimitMiddleware(req, res, next) {
@@ -8580,11 +8580,921 @@ I can still help summarize, clarify, or suggest next steps. Try again in a momen
8580
8580
  }
8581
8581
  };
8582
8582
  var IMSFacade_default = IMSFacade;
8583
+ var DEFAULT_STATE = {
8584
+ events: {},
8585
+ customers: {},
8586
+ subscriptions: {},
8587
+ invoices: {},
8588
+ entitlements: {},
8589
+ usage: {}
8590
+ };
8591
+ var SubscriptionStore = class {
8592
+ filePath;
8593
+ state = null;
8594
+ loadPromise = null;
8595
+ queue = Promise.resolve();
8596
+ constructor(filePath) {
8597
+ this.filePath = filePath ?? path__namespace.default.resolve(process.cwd(), "data", "subscription-state.json");
8598
+ }
8599
+ async ensureLoaded() {
8600
+ if (this.state) {
8601
+ return;
8602
+ }
8603
+ if (!this.loadPromise) {
8604
+ this.loadPromise = this.loadStateFromDisk();
8605
+ }
8606
+ await this.loadPromise;
8607
+ }
8608
+ async loadStateFromDisk() {
8609
+ try {
8610
+ const raw = await fsp__namespace.default.readFile(this.filePath, "utf8");
8611
+ const parsed = JSON.parse(raw);
8612
+ this.state = {
8613
+ events: parsed.events ?? {},
8614
+ customers: parsed.customers ?? {},
8615
+ subscriptions: parsed.subscriptions ?? {},
8616
+ invoices: parsed.invoices ?? {},
8617
+ entitlements: parsed.entitlements ?? {},
8618
+ usage: parsed.usage ?? {}
8619
+ };
8620
+ } catch (error) {
8621
+ if (error?.code === "ENOENT") {
8622
+ this.state = { ...DEFAULT_STATE };
8623
+ await this.persist();
8624
+ return;
8625
+ }
8626
+ throw error;
8627
+ }
8628
+ }
8629
+ async persist() {
8630
+ if (!this.state) {
8631
+ return;
8632
+ }
8633
+ const directory = path__namespace.default.dirname(this.filePath);
8634
+ await fsp__namespace.default.mkdir(directory, { recursive: true });
8635
+ const payload = JSON.stringify(this.state, null, 2);
8636
+ await fsp__namespace.default.writeFile(this.filePath, payload, "utf8");
8637
+ }
8638
+ runExclusive(task) {
8639
+ const result = this.queue.then(task);
8640
+ this.queue = result.then(() => void 0).catch(() => void 0);
8641
+ return result;
8642
+ }
8643
+ async markEventReceived(event) {
8644
+ return this.runExclusive(async () => {
8645
+ await this.ensureLoaded();
8646
+ const now = (/* @__PURE__ */ new Date()).toISOString();
8647
+ const state = this.state;
8648
+ const existing = state.events[event.id];
8649
+ if (!existing) {
8650
+ state.events[event.id] = {
8651
+ id: event.id,
8652
+ type: event.type,
8653
+ status: "received",
8654
+ receivedAt: now,
8655
+ stripeCreatedAt: event.created ? new Date(event.created * 1e3).toISOString() : void 0
8656
+ };
8657
+ await this.persist();
8658
+ return "new";
8659
+ }
8660
+ if (existing.status === "processed") {
8661
+ return "duplicate";
8662
+ }
8663
+ state.events[event.id] = {
8664
+ ...existing,
8665
+ type: event.type,
8666
+ status: existing.status === "failed" ? "received" : existing.status,
8667
+ receivedAt: existing.receivedAt ?? now
8668
+ };
8669
+ await this.persist();
8670
+ return "pending";
8671
+ });
8672
+ }
8673
+ async markEventProcessed(eventId) {
8674
+ await this.runExclusive(async () => {
8675
+ await this.ensureLoaded();
8676
+ const state = this.state;
8677
+ const existing = state.events[eventId];
8678
+ if (!existing) {
8679
+ return;
8680
+ }
8681
+ state.events[eventId] = {
8682
+ ...existing,
8683
+ status: "processed",
8684
+ processedAt: (/* @__PURE__ */ new Date()).toISOString(),
8685
+ error: void 0
8686
+ };
8687
+ await this.persist();
8688
+ });
8689
+ }
8690
+ async markEventFailed(eventId, error) {
8691
+ await this.runExclusive(async () => {
8692
+ await this.ensureLoaded();
8693
+ const state = this.state;
8694
+ const existing = state.events[eventId];
8695
+ if (!existing) {
8696
+ return;
8697
+ }
8698
+ state.events[eventId] = {
8699
+ ...existing,
8700
+ status: "failed",
8701
+ error: error instanceof Error ? error.message : String(error)
8702
+ };
8703
+ await this.persist();
8704
+ });
8705
+ }
8706
+ async upsertCustomer(record) {
8707
+ return this.runExclusive(async () => {
8708
+ await this.ensureLoaded();
8709
+ const now = (/* @__PURE__ */ new Date()).toISOString();
8710
+ const state = this.state;
8711
+ const existing = state.customers[record.id];
8712
+ const createdAt = existing?.createdAt ?? record.createdAt ?? now;
8713
+ const nextRecord = {
8714
+ id: record.id,
8715
+ uid: record.uid ?? existing?.uid,
8716
+ email: record.email ?? existing?.email,
8717
+ metadata: {
8718
+ ...existing?.metadata ?? {},
8719
+ ...record.metadata ?? {}
8720
+ },
8721
+ createdAt,
8722
+ updatedAt: record.updatedAt ?? now
8723
+ };
8724
+ state.customers[record.id] = nextRecord;
8725
+ await this.persist();
8726
+ return nextRecord;
8727
+ });
8728
+ }
8729
+ async getCustomer(customerId) {
8730
+ await this.ensureLoaded();
8731
+ const state = this.state;
8732
+ const record = state.customers[customerId];
8733
+ return record ? { ...record, metadata: record.metadata ? { ...record.metadata } : void 0 } : void 0;
8734
+ }
8735
+ async upsertSubscription(record) {
8736
+ return this.runExclusive(async () => {
8737
+ await this.ensureLoaded();
8738
+ const now = (/* @__PURE__ */ new Date()).toISOString();
8739
+ const state = this.state;
8740
+ const existing = state.subscriptions[record.id];
8741
+ const createdAt = existing?.createdAt ?? record.createdAt ?? now;
8742
+ const nextRecord = {
8743
+ id: record.id,
8744
+ customerId: record.customerId,
8745
+ uid: record.uid ?? existing?.uid,
8746
+ status: record.status,
8747
+ planId: record.planId,
8748
+ pendingPlanId: record.pendingPlanId !== void 0 ? record.pendingPlanId : existing?.pendingPlanId ?? null,
8749
+ pendingPlanEffectiveAt: record.pendingPlanEffectiveAt !== void 0 ? record.pendingPlanEffectiveAt : existing?.pendingPlanEffectiveAt ?? null,
8750
+ cancelAt: record.cancelAt ?? null,
8751
+ cancelAtPeriodEnd: record.cancelAtPeriodEnd ?? false,
8752
+ canceledAt: record.canceledAt ?? null,
8753
+ currentPeriodStart: record.currentPeriodStart,
8754
+ currentPeriodEnd: record.currentPeriodEnd,
8755
+ trialEnd: record.trialEnd ?? null,
8756
+ latestInvoiceId: record.latestInvoiceId ?? existing?.latestInvoiceId,
8757
+ latestInvoiceStatus: record.latestInvoiceStatus ?? existing?.latestInvoiceStatus,
8758
+ latestInvoiceAmount: record.latestInvoiceAmount ?? existing?.latestInvoiceAmount,
8759
+ metadata: {
8760
+ ...existing?.metadata ?? {},
8761
+ ...record.metadata ?? {}
8762
+ },
8763
+ createdAt,
8764
+ updatedAt: record.updatedAt ?? now
8765
+ };
8766
+ state.subscriptions[record.id] = nextRecord;
8767
+ await this.persist();
8768
+ return nextRecord;
8769
+ });
8770
+ }
8771
+ async getSubscription(subscriptionId) {
8772
+ await this.ensureLoaded();
8773
+ const state = this.state;
8774
+ const record = state.subscriptions[subscriptionId];
8775
+ if (!record) {
8776
+ return void 0;
8777
+ }
8778
+ return {
8779
+ ...record,
8780
+ metadata: record.metadata ? { ...record.metadata } : void 0
8781
+ };
8782
+ }
8783
+ async findSubscriptionByCustomer(customerId) {
8784
+ await this.ensureLoaded();
8785
+ const state = this.state;
8786
+ return Object.values(state.subscriptions).find((sub) => sub.customerId === customerId);
8787
+ }
8788
+ async recordInvoice(record) {
8789
+ return this.runExclusive(async () => {
8790
+ await this.ensureLoaded();
8791
+ const state = this.state;
8792
+ state.invoices[record.id] = { ...record };
8793
+ await this.persist();
8794
+ return state.invoices[record.id];
8795
+ });
8796
+ }
8797
+ async setEntitlements(uid, planId, features) {
8798
+ return this.runExclusive(async () => {
8799
+ await this.ensureLoaded();
8800
+ const now = (/* @__PURE__ */ new Date()).toISOString();
8801
+ const state = this.state;
8802
+ const record = {
8803
+ uid,
8804
+ planId,
8805
+ features: [...features],
8806
+ updatedAt: now
8807
+ };
8808
+ state.entitlements[uid] = record;
8809
+ await this.persist();
8810
+ return record;
8811
+ });
8812
+ }
8813
+ async setUsage(params) {
8814
+ return this.runExclusive(async () => {
8815
+ await this.ensureLoaded();
8816
+ const now = (/* @__PURE__ */ new Date()).toISOString();
8817
+ const state = this.state;
8818
+ const key = `${params.uid}:${params.periodId}`;
8819
+ const existing = state.usage[key];
8820
+ const used = existing?.used ?? {
8821
+ requests: 0,
8822
+ tokens: 0,
8823
+ code: 0,
8824
+ attachment: 0
8825
+ };
8826
+ const remaining = {
8827
+ requests: Math.max(0, params.limits.requests - used.requests),
8828
+ tokens: Math.max(0, params.limits.tokens - used.tokens),
8829
+ code: Math.max(0, params.limits.code - used.code),
8830
+ attachment: Math.max(0, params.limits.attachment - used.attachment)
8831
+ };
8832
+ const record = {
8833
+ uid: params.uid,
8834
+ planId: params.planId,
8835
+ periodId: params.periodId,
8836
+ limits: { ...params.limits },
8837
+ used,
8838
+ remaining,
8839
+ createdAt: existing?.createdAt ?? now,
8840
+ updatedAt: now
8841
+ };
8842
+ state.usage[key] = record;
8843
+ await this.persist();
8844
+ return record;
8845
+ });
8846
+ }
8847
+ async getUsage(uid, periodId) {
8848
+ await this.ensureLoaded();
8849
+ const key = `${uid}:${periodId}`;
8850
+ const record = this.state.usage[key];
8851
+ if (!record) {
8852
+ return void 0;
8853
+ }
8854
+ return {
8855
+ ...record,
8856
+ limits: { ...record.limits },
8857
+ used: { ...record.used },
8858
+ remaining: { ...record.remaining }
8859
+ };
8860
+ }
8861
+ async getEntitlements(uid) {
8862
+ await this.ensureLoaded();
8863
+ const record = this.state.entitlements[uid];
8864
+ if (!record) {
8865
+ return void 0;
8866
+ }
8867
+ return {
8868
+ ...record,
8869
+ features: [...record.features]
8870
+ };
8871
+ }
8872
+ async snapshot() {
8873
+ await this.ensureLoaded();
8874
+ const state = this.state;
8875
+ return JSON.parse(JSON.stringify(state));
8876
+ }
8877
+ };
8878
+ var subscription_store_default = SubscriptionStore;
8879
+ var PLAN_PRIORITY = {
8880
+ free: 0,
8881
+ starter: 1,
8882
+ pro: 2,
8883
+ ultra: 3
8884
+ };
8885
+ var PLAN_CONFIG = {
8886
+ free: {
8887
+ entitlements: [
8888
+ "core.cli.basic",
8889
+ "ai.chat.basic",
8890
+ "usage.metrics.basic"
8891
+ ],
8892
+ quota: {
8893
+ requests: 100,
8894
+ tokens: 5e4,
8895
+ code: 30,
8896
+ attachment: 10
8897
+ }
8898
+ },
8899
+ starter: {
8900
+ entitlements: [
8901
+ "core.cli.basic",
8902
+ "ai.chat.basic",
8903
+ "ai.chat.priority",
8904
+ "usage.metrics.expanded",
8905
+ "workspace.multi-device"
8906
+ ],
8907
+ quota: {
8908
+ requests: 500,
8909
+ tokens: 25e4,
8910
+ code: 200,
8911
+ attachment: 50
8912
+ }
8913
+ },
8914
+ pro: {
8915
+ entitlements: [
8916
+ "core.cli.basic",
8917
+ "ai.chat.priority",
8918
+ "ai.code.advanced",
8919
+ "orchestrator.code",
8920
+ "usage.metrics.expanded",
8921
+ "workspace.multi-device",
8922
+ "api.webhooks"
8923
+ ],
8924
+ quota: {
8925
+ requests: 2e3,
8926
+ tokens: 1e6,
8927
+ code: 1e3,
8928
+ attachment: 200
8929
+ }
8930
+ },
8931
+ ultra: {
8932
+ entitlements: [
8933
+ "core.cli.basic",
8934
+ "ai.chat.priority",
8935
+ "ai.code.advanced",
8936
+ "orchestrator.code",
8937
+ "orchestrator.media",
8938
+ "monitoring.analytics",
8939
+ "api.webhooks",
8940
+ "support.priority"
8941
+ ],
8942
+ quota: {
8943
+ requests: 1e4,
8944
+ tokens: 5e6,
8945
+ code: 5e3,
8946
+ attachment: 1e3
8947
+ }
8948
+ }
8949
+ };
8950
+ var PLAN_ALIAS_MAP = {
8951
+ starterannual: "starter",
8952
+ "starter-annual": "starter",
8953
+ "starter_yearly": "starter",
8954
+ "starter-yearly": "starter",
8955
+ starteryearly: "starter",
8956
+ proannual: "pro",
8957
+ "pro-annual": "pro",
8958
+ "pro_yearly": "pro",
8959
+ "pro-yearly": "pro",
8960
+ proyearly: "pro",
8961
+ ultraannual: "ultra",
8962
+ "ultra-annual": "ultra",
8963
+ "ultra_yearly": "ultra",
8964
+ "ultra-yearly": "ultra",
8965
+ ultrayearly: "ultra"
8966
+ };
8967
+ function sanitizeKey(value) {
8968
+ return value.trim().toLowerCase().replace(/[^a-z0-9-]/g, "");
8969
+ }
8970
+ function toIso(timestamp) {
8971
+ if (!timestamp) {
8972
+ return null;
8973
+ }
8974
+ return new Date(timestamp * 1e3).toISOString();
8975
+ }
8976
+ function formatPeriodId(date) {
8977
+ const instance = typeof date === "number" ? new Date(date) : date;
8978
+ const year = instance.getUTCFullYear();
8979
+ const month = String(instance.getUTCMonth() + 1).padStart(2, "0");
8980
+ return `${year}${month}`;
8981
+ }
8982
+ function extractId(value) {
8983
+ if (!value) {
8984
+ return void 0;
8985
+ }
8986
+ if (typeof value === "string") {
8987
+ return value;
8988
+ }
8989
+ return value.id ?? void 0;
8990
+ }
8991
+ function extractMetadataPlan(metadata) {
8992
+ if (!metadata) {
8993
+ return void 0;
8994
+ }
8995
+ const keys = ["planId", "plan_id", "plan", "plan-id", "plan_name"];
8996
+ for (const key of keys) {
8997
+ const value = metadata[key];
8998
+ if (value && value.trim()) {
8999
+ return value.trim();
9000
+ }
9001
+ }
9002
+ return void 0;
9003
+ }
9004
+ function extractUidFromMetadata(metadata) {
9005
+ if (!metadata) {
9006
+ return void 0;
9007
+ }
9008
+ const keys = ["uid", "userId", "user_id", "customer_uid"];
9009
+ for (const key of keys) {
9010
+ const value = metadata[key];
9011
+ if (value && value.trim()) {
9012
+ return value.trim();
9013
+ }
9014
+ }
9015
+ return void 0;
9016
+ }
9017
+ var StripeWebhookService = class {
9018
+ secret;
9019
+ toleranceSeconds;
9020
+ store;
9021
+ pricePlanMap = /* @__PURE__ */ new Map();
9022
+ productPlanMap = /* @__PURE__ */ new Map();
9023
+ constructor(options) {
9024
+ if (!options.secret) {
9025
+ throw new Error("Stripe webhook secret must be provided");
9026
+ }
9027
+ this.secret = options.secret;
9028
+ this.toleranceSeconds = options.toleranceSeconds ?? 300;
9029
+ this.store = options.store ?? new subscription_store_default();
9030
+ this.registerEnvPlanMappings();
9031
+ if (options.priceIdMap) {
9032
+ for (const [id, plan] of Object.entries(options.priceIdMap)) {
9033
+ this.pricePlanMap.set(sanitizeKey(id), plan);
9034
+ }
9035
+ }
9036
+ if (options.productIdMap) {
9037
+ for (const [id, plan] of Object.entries(options.productIdMap)) {
9038
+ this.productPlanMap.set(sanitizeKey(id), plan);
9039
+ }
9040
+ }
9041
+ }
9042
+ verifySignature(rawBody, signatureHeader) {
9043
+ if (!signatureHeader) {
9044
+ throw new Error("Stripe signature header is missing");
9045
+ }
9046
+ const header = Array.isArray(signatureHeader) ? signatureHeader.join(",") : signatureHeader;
9047
+ const { timestamp, signatures } = this.parseSignatureHeader(header);
9048
+ if (!timestamp || signatures.length === 0) {
9049
+ throw new Error("Stripe signature header is invalid");
9050
+ }
9051
+ const timestampSeconds = Number(timestamp);
9052
+ if (!Number.isFinite(timestampSeconds)) {
9053
+ throw new Error("Stripe signature timestamp is invalid");
9054
+ }
9055
+ const nowSeconds = Math.floor(Date.now() / 1e3);
9056
+ if (Math.abs(nowSeconds - timestampSeconds) > this.toleranceSeconds) {
9057
+ throw new Error("Stripe signature timestamp outside of tolerance window");
9058
+ }
9059
+ const signedPayload = `${timestamp}.${rawBody.toString("utf8")}`;
9060
+ const expected = crypto.createHmac("sha256", this.secret).update(signedPayload).digest("hex");
9061
+ const expectedBuffer = Buffer.from(expected, "hex");
9062
+ const isValid = signatures.some((signature) => {
9063
+ const signatureBuffer = Buffer.from(signature, "hex");
9064
+ if (signatureBuffer.length !== expectedBuffer.length) {
9065
+ return false;
9066
+ }
9067
+ return crypto.timingSafeEqual(signatureBuffer, expectedBuffer);
9068
+ });
9069
+ if (!isValid) {
9070
+ throw new Error("Stripe signature verification failed");
9071
+ }
9072
+ }
9073
+ parseEvent(rawBody) {
9074
+ try {
9075
+ const parsed = JSON.parse(rawBody.toString("utf8"));
9076
+ if (!parsed || typeof parsed !== "object" || !parsed.id || !parsed.type) {
9077
+ throw new Error("Invalid event payload");
9078
+ }
9079
+ return parsed;
9080
+ } catch (error) {
9081
+ const message = error instanceof Error ? error.message : "Unknown error";
9082
+ throw new Error(`Failed to parse Stripe event payload: ${message}`);
9083
+ }
9084
+ }
9085
+ async processEvent(event) {
9086
+ const receipt = await this.store.markEventReceived({
9087
+ id: event.id,
9088
+ type: event.type,
9089
+ created: event.created
9090
+ });
9091
+ if (receipt === "duplicate") {
9092
+ return;
9093
+ }
9094
+ try {
9095
+ switch (event.type) {
9096
+ case "checkout.session.completed":
9097
+ await this.handleCheckoutSession(event);
9098
+ break;
9099
+ case "customer.subscription.created":
9100
+ await this.handleSubscriptionCreated(event);
9101
+ break;
9102
+ case "customer.subscription.updated":
9103
+ await this.handleSubscriptionUpdated(event);
9104
+ break;
9105
+ case "customer.subscription.deleted":
9106
+ await this.handleSubscriptionDeleted(event);
9107
+ break;
9108
+ case "invoice.payment_succeeded":
9109
+ await this.handleInvoicePayment(event, true);
9110
+ break;
9111
+ case "invoice.payment_failed":
9112
+ await this.handleInvoicePayment(event, false);
9113
+ break;
9114
+ default:
9115
+ console.info(`Unhandled Stripe event type: ${event.type}`);
9116
+ }
9117
+ await this.store.markEventProcessed(event.id);
9118
+ } catch (error) {
9119
+ await this.store.markEventFailed(event.id, error);
9120
+ throw error;
9121
+ }
9122
+ }
9123
+ parseSignatureHeader(signatureHeader) {
9124
+ const parts = signatureHeader.split(",");
9125
+ let timestamp;
9126
+ const signatures = [];
9127
+ for (const part of parts) {
9128
+ const [key, value] = part.trim().split("=");
9129
+ if (key === "t") {
9130
+ timestamp = value;
9131
+ } else if (key.startsWith("v")) {
9132
+ signatures.push(value);
9133
+ }
9134
+ }
9135
+ return { timestamp, signatures };
9136
+ }
9137
+ registerEnvPlanMappings() {
9138
+ const mapping = [
9139
+ [process.env.STRIPE_PRICE_STARTER, "starter"],
9140
+ [process.env.STRIPE_PRICE_STARTER_ANNUAL, "starter"],
9141
+ [process.env.STRIPE_PRICE_PRO, "pro"],
9142
+ [process.env.STRIPE_PRICE_PRO_ANNUAL, "pro"],
9143
+ [process.env.STRIPE_PRICE_ULTRA, "ultra"],
9144
+ [process.env.STRIPE_PRICE_ULTRA_ANNUAL, "ultra"]
9145
+ ];
9146
+ mapping.forEach(([id, plan]) => {
9147
+ if (id && id.trim()) {
9148
+ this.pricePlanMap.set(sanitizeKey(id), plan);
9149
+ }
9150
+ });
9151
+ const productMapping = [
9152
+ [process.env.STRIPE_PRODUCT_STARTER, "starter"],
9153
+ [process.env.STRIPE_PRODUCT_PRO, "pro"],
9154
+ [process.env.STRIPE_PRODUCT_ULTRA, "ultra"]
9155
+ ];
9156
+ productMapping.forEach(([id, plan]) => {
9157
+ if (id && id.trim()) {
9158
+ this.productPlanMap.set(sanitizeKey(id), plan);
9159
+ }
9160
+ });
9161
+ }
9162
+ normalizePlanId(planId) {
9163
+ if (!planId) {
9164
+ return "free";
9165
+ }
9166
+ const key = sanitizeKey(planId);
9167
+ if (PLAN_CONFIG[key]) {
9168
+ return key;
9169
+ }
9170
+ const alias = PLAN_ALIAS_MAP[key];
9171
+ if (alias) {
9172
+ return alias;
9173
+ }
9174
+ return "free";
9175
+ }
9176
+ resolvePlanFromSubscription(subscription) {
9177
+ const metadataPlan = extractMetadataPlan(subscription.metadata);
9178
+ if (metadataPlan) {
9179
+ return this.normalizePlanId(metadataPlan);
9180
+ }
9181
+ const items = subscription.items?.data ?? [];
9182
+ for (const item of items) {
9183
+ const itemMetadataPlan = extractMetadataPlan(item.metadata);
9184
+ if (itemMetadataPlan) {
9185
+ return this.normalizePlanId(itemMetadataPlan);
9186
+ }
9187
+ const priceMetadataPlan = extractMetadataPlan(item.price?.metadata);
9188
+ if (priceMetadataPlan) {
9189
+ return this.normalizePlanId(priceMetadataPlan);
9190
+ }
9191
+ const planMetadataPlan = extractMetadataPlan(item.plan?.metadata);
9192
+ if (planMetadataPlan) {
9193
+ return this.normalizePlanId(planMetadataPlan);
9194
+ }
9195
+ const priceId = item.price?.id;
9196
+ if (priceId) {
9197
+ const mapped = this.pricePlanMap.get(sanitizeKey(priceId));
9198
+ if (mapped) {
9199
+ return mapped;
9200
+ }
9201
+ }
9202
+ const productId = extractId(item.price?.product ?? item.plan?.product ?? null);
9203
+ if (productId) {
9204
+ const mapped = this.productPlanMap.get(sanitizeKey(productId));
9205
+ if (mapped) {
9206
+ return mapped;
9207
+ }
9208
+ }
9209
+ const nickname = item.price?.nickname;
9210
+ if (nickname) {
9211
+ const candidate = this.normalizePlanId(nickname);
9212
+ if (candidate !== "free") {
9213
+ return candidate;
9214
+ }
9215
+ }
9216
+ }
9217
+ return "free";
9218
+ }
9219
+ getPlanConfig(planId) {
9220
+ return PLAN_CONFIG[planId] ?? PLAN_CONFIG.free;
9221
+ }
9222
+ async applyPlanEntitlements(uid, planId, subscription, overridePeriod) {
9223
+ const config = this.getPlanConfig(planId);
9224
+ await this.store.setEntitlements(uid, planId, config.entitlements);
9225
+ const periodStart = overridePeriod?.start ?? subscription?.current_period_start;
9226
+ overridePeriod?.end ?? subscription?.current_period_end;
9227
+ const periodId = periodStart ? formatPeriodId(new Date(periodStart * 1e3)) : formatPeriodId(Date.now());
9228
+ await this.store.setUsage({
9229
+ uid,
9230
+ planId,
9231
+ periodId,
9232
+ limits: config.quota
9233
+ });
9234
+ }
9235
+ comparePlanPriority(a, b) {
9236
+ return PLAN_PRIORITY[a] - PLAN_PRIORITY[b];
9237
+ }
9238
+ async resolveUid(customerId, ...metadataSources) {
9239
+ for (const metadata of metadataSources) {
9240
+ const uidFromMetadata = extractUidFromMetadata(metadata);
9241
+ if (uidFromMetadata) {
9242
+ return uidFromMetadata;
9243
+ }
9244
+ }
9245
+ if (customerId) {
9246
+ const customer = await this.store.getCustomer(customerId);
9247
+ return customer?.uid;
9248
+ }
9249
+ return void 0;
9250
+ }
9251
+ async handleCheckoutSession(event) {
9252
+ const session = event.data.object;
9253
+ const customerId = extractId(session.customer ?? null);
9254
+ const uid = await this.resolveUid(customerId, session.metadata);
9255
+ if (!customerId) {
9256
+ console.warn("checkout.session.completed received without customer id", { sessionId: session.id });
9257
+ return;
9258
+ }
9259
+ const metadata = {};
9260
+ if (session.metadata) {
9261
+ for (const [key, value] of Object.entries(session.metadata)) {
9262
+ if (typeof value === "string") {
9263
+ metadata[key] = value;
9264
+ }
9265
+ }
9266
+ }
9267
+ await this.store.upsertCustomer({
9268
+ id: customerId,
9269
+ uid,
9270
+ email: session.customer_email ?? void 0,
9271
+ metadata: {
9272
+ ...metadata,
9273
+ lastCheckoutSessionId: session.id,
9274
+ lastCheckoutMode: session.mode ?? void 0
9275
+ }
9276
+ });
9277
+ }
9278
+ async handleSubscriptionCreated(event) {
9279
+ const subscription = event.data.object;
9280
+ const customerId = extractId(subscription.customer);
9281
+ if (!customerId) {
9282
+ throw new Error(`Subscription ${subscription.id} missing customer id`);
9283
+ }
9284
+ const planId = this.resolvePlanFromSubscription(subscription);
9285
+ const uid = await this.resolveUid(customerId, subscription.metadata);
9286
+ const customerRecord = {
9287
+ id: customerId,
9288
+ uid,
9289
+ email: typeof subscription.customer === "object" ? subscription.customer.email ?? void 0 : void 0,
9290
+ metadata: subscription.metadata && Object.keys(subscription.metadata).length > 0 ? { ...subscription.metadata } : void 0,
9291
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
9292
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
9293
+ };
9294
+ await this.store.upsertCustomer(customerRecord);
9295
+ await this.store.upsertSubscription({
9296
+ id: subscription.id,
9297
+ customerId,
9298
+ uid,
9299
+ status: subscription.status,
9300
+ planId,
9301
+ pendingPlanId: null,
9302
+ pendingPlanEffectiveAt: null,
9303
+ cancelAt: toIso(subscription.cancel_at),
9304
+ cancelAtPeriodEnd: subscription.cancel_at_period_end ?? false,
9305
+ canceledAt: toIso(subscription.canceled_at),
9306
+ currentPeriodStart: toIso(subscription.current_period_start) ?? (/* @__PURE__ */ new Date()).toISOString(),
9307
+ currentPeriodEnd: toIso(subscription.current_period_end) ?? (/* @__PURE__ */ new Date()).toISOString(),
9308
+ trialEnd: toIso(subscription.trial_end),
9309
+ metadata: subscription.metadata && Object.keys(subscription.metadata).length > 0 ? { ...subscription.metadata } : void 0
9310
+ });
9311
+ if (uid) {
9312
+ await this.applyPlanEntitlements(uid, planId, subscription);
9313
+ }
9314
+ }
9315
+ async handleSubscriptionUpdated(event) {
9316
+ const subscription = event.data.object;
9317
+ const customerId = extractId(subscription.customer);
9318
+ if (!customerId) {
9319
+ throw new Error(`Subscription ${subscription.id} missing customer id`);
9320
+ }
9321
+ const existing = await this.store.getSubscription(subscription.id);
9322
+ const resolvedPlan = this.resolvePlanFromSubscription(subscription);
9323
+ const uid = await this.resolveUid(customerId, subscription.metadata, existing?.metadata);
9324
+ const planChange = existing ? this.comparePlanPriority(resolvedPlan, existing.planId) : 0;
9325
+ let effectivePlan = resolvedPlan;
9326
+ let pendingPlan = existing?.pendingPlanId ?? null;
9327
+ let pendingEffectiveAt = existing?.pendingPlanEffectiveAt ?? null;
9328
+ if (existing) {
9329
+ if (planChange < 0) {
9330
+ effectivePlan = existing.planId;
9331
+ pendingPlan = resolvedPlan;
9332
+ pendingEffectiveAt = toIso(subscription.current_period_end) ?? toIso(subscription.cancel_at) ?? existing.currentPeriodEnd;
9333
+ } else {
9334
+ pendingPlan = null;
9335
+ pendingEffectiveAt = null;
9336
+ if (planChange > 0 && uid) {
9337
+ await this.applyPlanEntitlements(uid, resolvedPlan, subscription);
9338
+ }
9339
+ }
9340
+ } else if (uid) {
9341
+ await this.applyPlanEntitlements(uid, resolvedPlan, subscription);
9342
+ }
9343
+ await this.store.upsertSubscription({
9344
+ id: subscription.id,
9345
+ customerId,
9346
+ uid,
9347
+ status: subscription.status,
9348
+ planId: effectivePlan,
9349
+ pendingPlanId: pendingPlan,
9350
+ pendingPlanEffectiveAt: pendingEffectiveAt,
9351
+ cancelAt: toIso(subscription.cancel_at),
9352
+ cancelAtPeriodEnd: subscription.cancel_at_period_end ?? false,
9353
+ canceledAt: toIso(subscription.canceled_at),
9354
+ currentPeriodStart: toIso(subscription.current_period_start) ?? existing?.currentPeriodStart ?? (/* @__PURE__ */ new Date()).toISOString(),
9355
+ currentPeriodEnd: toIso(subscription.current_period_end) ?? existing?.currentPeriodEnd ?? (/* @__PURE__ */ new Date()).toISOString(),
9356
+ trialEnd: toIso(subscription.trial_end),
9357
+ metadata: subscription.metadata && Object.keys(subscription.metadata).length > 0 ? { ...subscription.metadata } : void 0
9358
+ });
9359
+ if (uid) {
9360
+ await this.store.upsertCustomer({
9361
+ id: customerId,
9362
+ uid,
9363
+ metadata: subscription.metadata && Object.keys(subscription.metadata).length > 0 ? { ...subscription.metadata } : void 0
9364
+ });
9365
+ }
9366
+ }
9367
+ async handleSubscriptionDeleted(event) {
9368
+ const subscription = event.data.object;
9369
+ const customerId = extractId(subscription.customer);
9370
+ const existing = await this.store.getSubscription(subscription.id);
9371
+ const uid = await this.resolveUid(customerId, subscription.metadata, existing?.metadata);
9372
+ await this.store.upsertSubscription({
9373
+ id: subscription.id,
9374
+ customerId: customerId ?? existing?.customerId ?? "unknown",
9375
+ uid: uid ?? existing?.uid,
9376
+ status: "canceled",
9377
+ planId: "free",
9378
+ pendingPlanId: null,
9379
+ pendingPlanEffectiveAt: null,
9380
+ cancelAt: toIso(subscription.cancel_at),
9381
+ cancelAtPeriodEnd: subscription.cancel_at_period_end ?? false,
9382
+ canceledAt: toIso(subscription.canceled_at) ?? (/* @__PURE__ */ new Date()).toISOString(),
9383
+ currentPeriodStart: toIso(subscription.current_period_start) ?? existing?.currentPeriodStart ?? (/* @__PURE__ */ new Date()).toISOString(),
9384
+ currentPeriodEnd: toIso(subscription.current_period_end) ?? existing?.currentPeriodEnd ?? (/* @__PURE__ */ new Date()).toISOString(),
9385
+ trialEnd: toIso(subscription.trial_end),
9386
+ metadata: subscription.metadata && Object.keys(subscription.metadata).length > 0 ? { ...subscription.metadata } : void 0
9387
+ });
9388
+ if (uid) {
9389
+ await this.applyPlanEntitlements(uid, "free", subscription);
9390
+ }
9391
+ }
9392
+ async handleInvoicePayment(event, succeeded) {
9393
+ const invoice = event.data.object;
9394
+ const subscriptionId = extractId(invoice.subscription ?? null);
9395
+ if (!subscriptionId) {
9396
+ console.warn("Invoice received without subscription id", { invoiceId: invoice.id });
9397
+ return;
9398
+ }
9399
+ const subscription = await this.store.getSubscription(subscriptionId);
9400
+ if (!subscription) {
9401
+ console.warn("Invoice received for unknown subscription", { invoiceId: invoice.id, subscriptionId });
9402
+ return;
9403
+ }
9404
+ const customerId = extractId(invoice.customer ?? subscription.customerId);
9405
+ await this.store.recordInvoice({
9406
+ id: invoice.id,
9407
+ subscriptionId,
9408
+ customerId,
9409
+ amountPaid: (invoice.amount_paid ?? 0) / 100,
9410
+ currency: invoice.currency ?? "usd",
9411
+ status: succeeded ? "succeeded" : "failed",
9412
+ paidAt: invoice.created ? new Date(invoice.created * 1e3).toISOString() : (/* @__PURE__ */ new Date()).toISOString(),
9413
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
9414
+ periodStart: toIso(invoice.lines?.data?.[0]?.period?.start ?? invoice.period_start ?? null),
9415
+ periodEnd: toIso(invoice.lines?.data?.[0]?.period?.end ?? invoice.period_end ?? null),
9416
+ hostedInvoiceUrl: invoice.hosted_invoice_url ?? void 0
9417
+ });
9418
+ if (!succeeded) {
9419
+ await this.store.upsertSubscription({
9420
+ id: subscription.id,
9421
+ customerId: subscription.customerId,
9422
+ uid: subscription.uid,
9423
+ status: subscription.status,
9424
+ planId: subscription.planId,
9425
+ pendingPlanId: subscription.pendingPlanId ?? null,
9426
+ pendingPlanEffectiveAt: subscription.pendingPlanEffectiveAt ?? null,
9427
+ cancelAt: subscription.cancelAt ?? null,
9428
+ cancelAtPeriodEnd: subscription.cancelAtPeriodEnd ?? false,
9429
+ canceledAt: subscription.canceledAt ?? null,
9430
+ currentPeriodStart: subscription.currentPeriodStart,
9431
+ currentPeriodEnd: subscription.currentPeriodEnd,
9432
+ trialEnd: subscription.trialEnd ?? null,
9433
+ latestInvoiceId: invoice.id,
9434
+ latestInvoiceStatus: "failed",
9435
+ latestInvoiceAmount: (invoice.amount_paid ?? 0) / 100
9436
+ });
9437
+ return;
9438
+ }
9439
+ const fallbackPeriodStart = subscription.currentPeriodStart ? Date.parse(subscription.currentPeriodStart) / 1e3 : void 0;
9440
+ const fallbackPeriodEnd = subscription.currentPeriodEnd ? Date.parse(subscription.currentPeriodEnd) / 1e3 : void 0;
9441
+ const periodStart = invoice.lines?.data?.[0]?.period?.start ?? invoice.period_start ?? fallbackPeriodStart;
9442
+ const periodEnd = invoice.lines?.data?.[0]?.period?.end ?? invoice.period_end ?? fallbackPeriodEnd;
9443
+ let nextPlanId = subscription.planId;
9444
+ let pendingPlanId = subscription.pendingPlanId ?? null;
9445
+ let pendingEffectiveAt = subscription.pendingPlanEffectiveAt ?? null;
9446
+ if (subscription.pendingPlanId) {
9447
+ nextPlanId = subscription.pendingPlanId;
9448
+ pendingPlanId = null;
9449
+ pendingEffectiveAt = null;
9450
+ if (subscription.uid) {
9451
+ await this.applyPlanEntitlements(subscription.uid, nextPlanId, null, { start: periodStart, end: periodEnd });
9452
+ }
9453
+ } else if (subscription.uid) {
9454
+ await this.applyPlanEntitlements(subscription.uid, subscription.planId, null, { start: periodStart, end: periodEnd });
9455
+ }
9456
+ await this.store.upsertSubscription({
9457
+ id: subscription.id,
9458
+ customerId: subscription.customerId,
9459
+ uid: subscription.uid,
9460
+ status: subscription.status,
9461
+ planId: nextPlanId,
9462
+ pendingPlanId,
9463
+ pendingPlanEffectiveAt: pendingEffectiveAt,
9464
+ cancelAt: subscription.cancelAt ?? null,
9465
+ cancelAtPeriodEnd: subscription.cancelAtPeriodEnd ?? false,
9466
+ canceledAt: subscription.canceledAt ?? null,
9467
+ currentPeriodStart: periodStart ? toIso(periodStart) ?? subscription.currentPeriodStart : subscription.currentPeriodStart,
9468
+ currentPeriodEnd: periodEnd ? toIso(periodEnd) ?? subscription.currentPeriodEnd : subscription.currentPeriodEnd,
9469
+ trialEnd: subscription.trialEnd ?? null,
9470
+ latestInvoiceId: invoice.id,
9471
+ latestInvoiceStatus: "succeeded",
9472
+ latestInvoiceAmount: (invoice.amount_paid ?? 0) / 100
9473
+ });
9474
+ }
9475
+ };
9476
+ var stripe_webhook_service_default = StripeWebhookService;
8583
9477
 
8584
9478
  // src/server/express-server.ts
8585
9479
  var jobIndex = /* @__PURE__ */ new Map();
8586
9480
  var app = express__default.default();
8587
9481
  var port = process.env.PORT || 8080;
9482
+ var subscriptionStore = new subscription_store_default();
9483
+ var stripeWebhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
9484
+ var stripeWebhookService = null;
9485
+ if (stripeWebhookSecret) {
9486
+ try {
9487
+ stripeWebhookService = new stripe_webhook_service_default({
9488
+ secret: stripeWebhookSecret,
9489
+ store: subscriptionStore
9490
+ });
9491
+ } catch (error) {
9492
+ stripeWebhookService = null;
9493
+ console.error("Failed to initialize Stripe webhook service:", error);
9494
+ }
9495
+ } else {
9496
+ console.warn("STRIPE_WEBHOOK_SECRET is not configured; Stripe webhook endpoint will be disabled.");
9497
+ }
8588
9498
  app.use(helmet__default.default());
8589
9499
  app.use(cors__default.default());
8590
9500
  app.use(compression__default.default({
@@ -8593,6 +9503,7 @@ app.use(compression__default.default({
8593
9503
  return compression.filter(req, res);
8594
9504
  }
8595
9505
  }));
9506
+ app.use("/api/stripe/webhook", express__default.default.raw({ type: "application/json" }));
8596
9507
  app.use(express__default.default.json({ limit: "10mb" }));
8597
9508
  app.use(express__default.default.urlencoded({ extended: true }));
8598
9509
  try {
@@ -8621,7 +9532,7 @@ app.get("/api/status", (req, res) => {
8621
9532
  app.get("/", (req, res) => {
8622
9533
  res.json({
8623
9534
  name: "MARIA CODE API",
8624
- version: "4.3.31",
9535
+ version: "4.3.33",
8625
9536
  status: "running",
8626
9537
  environment: process.env.NODE_ENV || "development",
8627
9538
  endpoints: {
@@ -8636,6 +9547,30 @@ app.get("/", (req, res) => {
8636
9547
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
8637
9548
  });
8638
9549
  });
9550
+ app.post("/api/stripe/webhook", async (req, res) => {
9551
+ if (!stripeWebhookService) {
9552
+ return res.status(503).json({ error: "Stripe webhook processing is not configured" });
9553
+ }
9554
+ const signature = req.headers["stripe-signature"];
9555
+ const rawBody = Buffer.isBuffer(req.body) ? req.body : Buffer.from(typeof req.body === "string" ? req.body : JSON.stringify(req.body ?? {}), "utf8");
9556
+ try {
9557
+ stripeWebhookService.verifySignature(rawBody, signature);
9558
+ } catch (error) {
9559
+ console.error("Stripe webhook signature verification failed:", error);
9560
+ return res.status(400).json({ error: "Invalid Stripe signature" });
9561
+ }
9562
+ let event;
9563
+ try {
9564
+ event = stripeWebhookService.parseEvent(rawBody);
9565
+ } catch (error) {
9566
+ console.error("Stripe webhook payload parsing failed:", error);
9567
+ return res.status(400).json({ error: "Invalid Stripe payload" });
9568
+ }
9569
+ stripeWebhookService.processEvent(event).catch((error) => {
9570
+ console.error("Stripe webhook processing error:", error);
9571
+ });
9572
+ return res.sendStatus(200);
9573
+ });
8639
9574
  function classifyMediaError(err) {
8640
9575
  const raw = err;
8641
9576
  const msg = String(raw?.message || raw || "unknown error");
@@ -9221,7 +10156,7 @@ app.post("/v1/ai-proxy", rateLimitMiddleware, async (req, res) => {
9221
10156
  } catch {
9222
10157
  }
9223
10158
  }
9224
- const sanitizeKey = (v) => {
10159
+ const sanitizeKey2 = (v) => {
9225
10160
  if (!v) return void 0;
9226
10161
  let k = String(v).trim();
9227
10162
  if (!k) return void 0;
@@ -9246,7 +10181,7 @@ app.post("/v1/ai-proxy", rateLimitMiddleware, async (req, res) => {
9246
10181
  return {};
9247
10182
  }
9248
10183
  })();
9249
- const gemKey = sanitizeKey(keys?.googleApiKey || process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY);
10184
+ const gemKey = sanitizeKey2(keys?.googleApiKey || process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY);
9250
10185
  if (gemKey) {
9251
10186
  try {
9252
10187
  const { GoogleGenerativeAI: GoogleGenerativeAI2 } = await import('@google/generative-ai');
@@ -9272,7 +10207,7 @@ app.post("/v1/ai-proxy", rateLimitMiddleware, async (req, res) => {
9272
10207
  }
9273
10208
  }
9274
10209
  }
9275
- const openaiKey = sanitizeKey(keys?.openaiApiKey || process.env.OPENAI_API_KEY);
10210
+ const openaiKey = sanitizeKey2(keys?.openaiApiKey || process.env.OPENAI_API_KEY);
9276
10211
  if (!openaiKey) {
9277
10212
  if (process.env.MARIA_TELEMETRY === "1") {
9278
10213
  try {