hive-intelligence 1.16.1 → 1.17.0

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.
package/build/server.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  getWalletStatus,
9
9
  refundCredits,
10
10
  refundSettled
11
- } from "./chunk-6M6O3DZM.js";
11
+ } from "./chunk-DAYJPXIW.js";
12
12
  import {
13
13
  CategoryEndpoints,
14
14
  HERO_TOOL_NAMES,
@@ -97,7 +97,7 @@ import {
97
97
  validateEndpointArgs,
98
98
  verifyApiKey,
99
99
  waitForRedisAvailability
100
- } from "./chunk-757ZNB6W.js";
100
+ } from "./chunk-ZLLD7AXP.js";
101
101
  import {
102
102
  HIVE_AGENT_SKILLS
103
103
  } from "./chunk-QYK3EWTE.js";
@@ -754,7 +754,7 @@ function resolveTrustedProxies(raw = getEnvVar("TRUSTED_PROXY_ADDRESSES")) {
754
754
  }
755
755
 
756
756
  // src/server.ts
757
- import { randomUUID as randomUUID4, timingSafeEqual as timingSafeEqual6 } from "crypto";
757
+ import { randomUUID as randomUUID4, timingSafeEqual as timingSafeEqual5 } from "crypto";
758
758
  import { resolve } from "path";
759
759
  import { fileURLToPath } from "url";
760
760
  import express2 from "express";
@@ -1774,7 +1774,7 @@ function buildCatalogIntegrityDimension(history) {
1774
1774
  }
1775
1775
  };
1776
1776
  }
1777
- var RATE_LIMITER_SCOPE = "This instance's shared rate-limit store, upstream response cache, and idempotency keys.";
1777
+ var RATE_LIMITER_SCOPE = "This instance's shared rate-limit store and idempotency keys.";
1778
1778
  function buildRateLimiterDimension(signal) {
1779
1779
  const observedAt = (/* @__PURE__ */ new Date()).toISOString();
1780
1780
  if (!signal.configured) {
@@ -3745,181 +3745,17 @@ import {
3745
3745
  } from "express";
3746
3746
  import { z as z2 } from "zod";
3747
3747
 
3748
- // src/services/stripe-service.ts
3748
+ // src/services/llamapay-service.ts
3749
3749
  import { createHmac, timingSafeEqual as timingSafeEqual3 } from "crypto";
3750
- var STRIPE_API_BASE = "https://api.stripe.com";
3751
- var STRIPE_SIGNATURE_TOLERANCE_SECONDS = 300;
3750
+ var LLAMAPAY_API_BASE = "https://api.llamapay.io";
3752
3751
  var PAID_PLAN_ID = "analyst";
3753
3752
  var FREE_PLAN_ID = "demo";
3754
- function getStripeSecretKey() {
3755
- return getEnvVar("STRIPE_SECRET_KEY");
3756
- }
3757
- function getStripeWebhookSecret() {
3758
- return getEnvVar("STRIPE_WEBHOOK_SECRET");
3759
- }
3760
- function isStripeCheckoutConfigured() {
3761
- return Boolean(getStripeSecretKey());
3762
- }
3763
- var STRIPE_PRICE_ENV = {
3764
- starter: {
3765
- monthly: "STRIPE_PRICE_STARTER_MONTHLY",
3766
- yearly: "STRIPE_PRICE_STARTER_YEARLY"
3767
- },
3768
- analyst: {
3769
- monthly: "STRIPE_PRICE_PRO_MONTHLY",
3770
- yearly: "STRIPE_PRICE_PRO_YEARLY"
3771
- }
3772
- };
3773
- function priceIdForPlanCycle(planId, cycle) {
3774
- const row = STRIPE_PRICE_ENV[planId];
3775
- if (!row) return void 0;
3776
- return getEnvVar(row[cycle]);
3777
- }
3778
- function planIdForPrice(priceId) {
3779
- if (!priceId) return null;
3780
- for (const [planId, cycles] of Object.entries(STRIPE_PRICE_ENV)) {
3781
- for (const envName of Object.values(cycles)) {
3782
- if (priceId === getEnvVar(envName)) return planId;
3783
- }
3784
- }
3785
- return null;
3786
- }
3787
- var STRIPE_SUBSCRIPTION_STATUSES = [
3788
- "incomplete",
3789
- "incomplete_expired",
3790
- "trialing",
3791
- "active",
3792
- "past_due",
3793
- "canceled",
3794
- "unpaid",
3795
- "paused"
3796
- ];
3797
- function registrySubscriptionStatus(status) {
3798
- if (!status) return null;
3799
- return STRIPE_SUBSCRIPTION_STATUSES.includes(status) ? status : null;
3800
- }
3801
3753
  function getBillingSuccessUrl() {
3802
3754
  return getEnvVar("BILLING_SUCCESS_URL") ?? "https://www.hiveintelligence.xyz/dashboard?checkout=success";
3803
3755
  }
3804
3756
  function getBillingCancelUrl() {
3805
3757
  return getEnvVar("BILLING_CANCEL_URL") ?? "https://www.hiveintelligence.xyz/dashboard?checkout=cancelled";
3806
3758
  }
3807
- function verifyStripeSignature(args) {
3808
- const {
3809
- payload,
3810
- header,
3811
- secret,
3812
- toleranceSeconds = STRIPE_SIGNATURE_TOLERANCE_SECONDS,
3813
- nowSeconds = Math.floor(Date.now() / 1e3)
3814
- } = args;
3815
- if (typeof header !== "string" || header.length === 0) {
3816
- return { valid: false, reason: "missing_header" };
3817
- }
3818
- let timestamp = null;
3819
- const candidates = [];
3820
- for (const part of header.split(",")) {
3821
- const eq = part.indexOf("=");
3822
- if (eq <= 0) continue;
3823
- const key = part.slice(0, eq).trim();
3824
- const value = part.slice(eq + 1).trim();
3825
- if (key === "t") {
3826
- const parsed = Number.parseInt(value, 10);
3827
- if (Number.isFinite(parsed)) timestamp = parsed;
3828
- } else if (key === "v1" && /^[0-9a-f]+$/i.test(value)) {
3829
- candidates.push(value);
3830
- }
3831
- }
3832
- if (timestamp === null || candidates.length === 0) {
3833
- return { valid: false, reason: "malformed_header" };
3834
- }
3835
- if (Math.abs(nowSeconds - timestamp) > toleranceSeconds) {
3836
- return { valid: false, reason: "timestamp_out_of_tolerance" };
3837
- }
3838
- const expected = createHmac("sha256", secret).update(`${timestamp}.`).update(payload).digest();
3839
- for (const candidate of candidates) {
3840
- const provided = Buffer.from(candidate, "hex");
3841
- if (provided.length === expected.length && timingSafeEqual3(provided, expected)) {
3842
- return { valid: true };
3843
- }
3844
- }
3845
- return { valid: false, reason: "signature_mismatch" };
3846
- }
3847
- var StripeApiError = class extends Error {
3848
- constructor(status, message) {
3849
- super(message);
3850
- this.status = status;
3851
- this.name = "StripeApiError";
3852
- }
3853
- status;
3854
- };
3855
- async function stripePost(path, params) {
3856
- const key = getStripeSecretKey();
3857
- if (!key) {
3858
- throw new StripeApiError(503, "STRIPE_SECRET_KEY is not configured");
3859
- }
3860
- const response = await fetch(`${STRIPE_API_BASE}${path}`, {
3861
- method: "POST",
3862
- headers: {
3863
- authorization: `Bearer ${key}`,
3864
- "content-type": "application/x-www-form-urlencoded"
3865
- },
3866
- body: new URLSearchParams(params).toString()
3867
- });
3868
- let json = {};
3869
- try {
3870
- json = await response.json();
3871
- } catch {
3872
- json = {};
3873
- }
3874
- if (!response.ok) {
3875
- const error = json.error;
3876
- throw new StripeApiError(
3877
- response.status,
3878
- error?.message ?? `Stripe API request to ${path} failed`
3879
- );
3880
- }
3881
- return json;
3882
- }
3883
- async function createStripeCustomer(args) {
3884
- const params = {
3885
- "metadata[user_id]": args.userId
3886
- };
3887
- if (args.email) params.email = args.email;
3888
- const customer = await stripePost("/v1/customers", params);
3889
- const id = customer.id;
3890
- if (typeof id !== "string" || !id) {
3891
- throw new StripeApiError(502, "Stripe customer creation returned no id");
3892
- }
3893
- return id;
3894
- }
3895
- async function createStripeCheckoutSession(args) {
3896
- const session = await stripePost("/v1/checkout/sessions", {
3897
- mode: "subscription",
3898
- customer: args.customerId,
3899
- "line_items[0][price]": args.priceId,
3900
- "line_items[0][quantity]": "1",
3901
- success_url: getBillingSuccessUrl(),
3902
- cancel_url: getBillingCancelUrl(),
3903
- client_reference_id: args.userId,
3904
- "metadata[user_id]": args.userId,
3905
- "metadata[plan_id]": args.planId,
3906
- "subscription_data[metadata][user_id]": args.userId,
3907
- "subscription_data[metadata][plan_id]": args.planId
3908
- });
3909
- const id = session.id;
3910
- const url = session.url;
3911
- if (typeof id !== "string" || typeof url !== "string" || !url) {
3912
- throw new StripeApiError(
3913
- 502,
3914
- "Stripe checkout session creation returned no URL"
3915
- );
3916
- }
3917
- return { id, url };
3918
- }
3919
-
3920
- // src/services/llamapay-service.ts
3921
- import { createHmac as createHmac2, timingSafeEqual as timingSafeEqual4 } from "crypto";
3922
- var LLAMAPAY_API_BASE = "https://api.llamapay.io";
3923
3759
  var LLAMAPAY_SIGNATURE_HEADER = "x-cc-webhook-signature";
3924
3760
  function getLlamaPayApiKey() {
3925
3761
  const key = process.env.LLAMAPAY_API_KEY?.trim();
@@ -4069,13 +3905,13 @@ function verifyLlamaPaySignature(args) {
4069
3905
  if (!/^[0-9a-f]+$/.test(received)) {
4070
3906
  return { valid: false, reason: "malformed_header" };
4071
3907
  }
4072
- const expected = createHmac2("sha256", args.secret).update(args.payload).digest("hex");
3908
+ const expected = createHmac("sha256", args.secret).update(args.payload).digest("hex");
4073
3909
  const receivedBuf = Buffer.from(received, "utf8");
4074
3910
  const expectedBuf = Buffer.from(expected, "utf8");
4075
3911
  if (receivedBuf.length !== expectedBuf.length) {
4076
3912
  return { valid: false, reason: "no_match" };
4077
3913
  }
4078
- if (!timingSafeEqual4(receivedBuf, expectedBuf)) {
3914
+ if (!timingSafeEqual3(receivedBuf, expectedBuf)) {
4079
3915
  return { valid: false, reason: "no_match" };
4080
3916
  }
4081
3917
  return { valid: true };
@@ -4103,7 +3939,7 @@ function requireBillingPrincipal(req, res, next) {
4103
3939
  verifyApiKey(apiKeyHeader, [
4104
3940
  "service=hive",
4105
3941
  "transport=rest",
4106
- "route=/api/v1/billing/checkout-session"
3942
+ "route=/api/v1/billing"
4107
3943
  ]).then((result) => {
4108
3944
  if (!result.valid || !result.userId) {
4109
3945
  res.status(401).json(
@@ -4135,13 +3971,6 @@ function requireBillingPrincipal(req, res, next) {
4135
3971
  next();
4136
3972
  });
4137
3973
  }
4138
- var CheckoutSchema = z2.object({
4139
- // Was absent, and the route hardcoded PAID_PLAN_ID. The crypto lane sold
4140
- // Starter and this one did not, so the two processors sold different
4141
- // catalogues -- with Starter the emphasised middle card on /pricing.
4142
- plan_id: z2.string().min(1).default(PAID_PLAN_ID),
4143
- billing_cycle: z2.enum(["monthly", "yearly"]).default("monthly")
4144
- });
4145
3974
  var OCCUPYING_BILLING_STATUSES = /* @__PURE__ */ new Set(["active", "past_due", "unpaid"]);
4146
3975
  var CHECKOUT_LOCK_TTL_MS = 6e4;
4147
3976
  var CHECKOUT_LOCK_PREFIX = "billing:checkout:";
@@ -4230,24 +4059,6 @@ async function findOpenCharge(userId) {
4230
4059
  const row = Array.isArray(data) ? data[0] : void 0;
4231
4060
  return typeof row?.charge_id === "string" ? row.charge_id : null;
4232
4061
  }
4233
- async function settleChargeRecord(chargeId, status, eventType) {
4234
- if (!chargeId || !isSupabaseConfigured()) return;
4235
- const now = (/* @__PURE__ */ new Date()).toISOString();
4236
- try {
4237
- const { error } = await getSupabaseAdmin().from("billing_charges").update({
4238
- status,
4239
- settled_at: now,
4240
- last_event_at: now,
4241
- last_event_type: eventType
4242
- }).eq("charge_id", chargeId);
4243
- if (error) throw new Error(error.message);
4244
- } catch (err) {
4245
- secureLogger.error("Failed to settle billing charge record", err, {
4246
- chargeId,
4247
- eventType
4248
- });
4249
- }
4250
- }
4251
4062
  async function recordChargeCreated(charge) {
4252
4063
  const { error } = await getSupabaseAdmin().from("billing_charges").insert({
4253
4064
  charge_id: charge.chargeId,
@@ -4264,452 +4075,15 @@ async function recordChargeCreated(charge) {
4264
4075
  }
4265
4076
  }
4266
4077
  var billingRouter = Router6();
4267
- billingRouter.post(
4268
- "/checkout-session",
4269
- requireBillingPrincipal,
4270
- withCheckoutLock,
4271
- asyncHandler(async (req, res) => {
4272
- const userId = req.billingUserId;
4273
- if (!isStripeCheckoutConfigured()) {
4274
- return res.status(503).json(BILLING_UNAVAILABLE);
4275
- }
4276
- if (!isSupabaseConfigured()) {
4277
- return res.status(503).json(BILLING_UNAVAILABLE);
4278
- }
4279
- const parsed = CheckoutSchema.safeParse(req.body ?? {});
4280
- if (!parsed.success) {
4281
- return res.status(400).json(
4282
- apiError(
4283
- "invalid_request_error",
4284
- "VALIDATION_ERROR",
4285
- 'billing_cycle must be "monthly" or "yearly".'
4286
- )
4287
- );
4288
- }
4289
- const { plan_id: planId, billing_cycle: cycle } = parsed.data;
4290
- if (!isSellablePlan(planId)) {
4291
- return res.status(400).json(
4292
- apiError(
4293
- "invalid_request_error",
4294
- "PLAN_NOT_SELLABLE",
4295
- `Plan "${planId}" is not sold self-serve. Next action: choose starter or ${PAID_PLAN_ID}, or contact support for Enterprise.`
4296
- )
4297
- );
4298
- }
4299
- const priceId = priceIdForPlanCycle(planId, cycle);
4300
- if (!priceId) {
4301
- return res.status(503).json(BILLING_UNAVAILABLE);
4302
- }
4303
- const { data: profile } = await getSupabaseAdmin().from("user_profiles").select(
4304
- "plan_id, billing_customer_id, billing_subscription_id, billing_status"
4305
- ).eq("id", userId).maybeSingle();
4306
- if (profile?.billing_subscription_id && typeof profile.billing_status === "string" && OCCUPYING_BILLING_STATUSES.has(profile.billing_status)) {
4307
- return res.status(409).json(
4308
- apiError(
4309
- "invalid_request_error",
4310
- "SUBSCRIPTION_EXISTS",
4311
- "This account already has an active subscription. Next action: manage it from the dashboard billing tab instead of creating a new checkout."
4312
- )
4313
- );
4314
- }
4315
- const openChargeId = await findOpenCharge(userId);
4316
- if (openChargeId) {
4317
- return res.status(409).json(checkoutAlreadyOpen(openChargeId));
4318
- }
4319
- try {
4320
- let customerId = profile?.billing_customer_id ?? null;
4321
- if (!customerId) {
4322
- customerId = await createStripeCustomer({
4323
- userId,
4324
- email: req.billingUserEmail
4325
- });
4326
- const { error: bindError } = await getSupabaseAdmin().from("user_profiles").update({ billing_customer_id: customerId }).eq("id", userId);
4327
- if (bindError) {
4328
- throw new Error(
4329
- `failed to store billing_customer_id: ${bindError.message}`
4330
- );
4331
- }
4332
- }
4333
- const session = await createStripeCheckoutSession({
4334
- customerId,
4335
- priceId,
4336
- userId,
4337
- planId
4338
- });
4339
- await recordChargeCreated({
4340
- chargeId: session.id,
4341
- processor: "stripe",
4342
- userId,
4343
- planId,
4344
- cycle,
4345
- amountUsd: priceForPlanCycle(planId, cycle) ?? 0,
4346
- planIdBefore: asString(profile?.plan_id)
4347
- });
4348
- return res.json({
4349
- ok: true,
4350
- data: {
4351
- url: session.url,
4352
- session_id: session.id,
4353
- plan_id: planId,
4354
- billing_cycle: cycle
4355
- }
4356
- });
4357
- } catch (err) {
4358
- if (err instanceof StripeApiError) {
4359
- secureLogger.error("Stripe checkout-session creation failed", err, {
4360
- userId,
4361
- status: err.status
4362
- });
4363
- return res.status(502).json(
4364
- apiError(
4365
- "provider_error",
4366
- "BILLING_PROVIDER_ERROR",
4367
- "Stripe rejected the checkout request. Next action: retry; if it persists, contact support@hiveintelligence.xyz."
4368
- )
4369
- );
4370
- }
4371
- throw err;
4372
- }
4373
- })
4374
- );
4375
4078
  function asString(value) {
4376
4079
  return typeof value === "string" && value.length > 0 ? value : null;
4377
4080
  }
4378
- function refId(value) {
4379
- if (typeof value === "string") return value || null;
4380
- if (value && typeof value === "object") {
4381
- return asString(value.id);
4382
- }
4383
- return null;
4384
- }
4385
4081
  function metadataOf(value) {
4386
4082
  if (value && typeof value === "object" && !Array.isArray(value)) {
4387
4083
  return value;
4388
4084
  }
4389
4085
  return {};
4390
4086
  }
4391
- function isoFromUnixSeconds(value) {
4392
- if (typeof value !== "number" || !Number.isFinite(value)) return null;
4393
- return new Date(value * 1e3).toISOString();
4394
- }
4395
- async function resolveUserIdByCustomer(customerId) {
4396
- if (!customerId || !isSupabaseConfigured()) return null;
4397
- const { data, error } = await getSupabaseAdmin().from("user_profiles").select("id").eq("billing_customer_id", customerId).limit(1);
4398
- if (error || !data || data.length === 0) return null;
4399
- return asString(data[0]?.id);
4400
- }
4401
- function mapSubscriptionStatusToBilling(status) {
4402
- switch (status) {
4403
- case "active":
4404
- case "trialing":
4405
- return "active";
4406
- case "past_due":
4407
- return "past_due";
4408
- case "unpaid":
4409
- return "unpaid";
4410
- case "canceled":
4411
- case "incomplete_expired":
4412
- return "canceled";
4413
- default:
4414
- return null;
4415
- }
4416
- }
4417
- function invoiceSubscriptionContext(invoice) {
4418
- const parent = metadataOf(invoice.parent);
4419
- const parentDetails = metadataOf(parent.subscription_details);
4420
- const details = metadataOf(invoice.subscription_details);
4421
- return {
4422
- subscriptionId: refId(invoice.subscription) ?? refId(parentDetails.subscription),
4423
- metadata: {
4424
- ...metadataOf(parentDetails.metadata),
4425
- ...metadataOf(details.metadata)
4426
- }
4427
- };
4428
- }
4429
- function invoicePeriod(invoice) {
4430
- const lines = metadataOf(invoice.lines);
4431
- const data = Array.isArray(lines.data) ? lines.data : [];
4432
- let best = null;
4433
- let bestEnd = -Infinity;
4434
- for (const raw of data) {
4435
- const line = metadataOf(raw);
4436
- const period2 = metadataOf(line.period);
4437
- const end = typeof period2.end === "number" ? period2.end : null;
4438
- if (end !== null && end > bestEnd) {
4439
- bestEnd = end;
4440
- best = line;
4441
- }
4442
- }
4443
- if (!best) return { cycleStart: null, cycleEnd: null, priceId: null };
4444
- const period = metadataOf(best.period);
4445
- const price = metadataOf(best.price);
4446
- const pricing = metadataOf(best.pricing);
4447
- const priceDetails = metadataOf(pricing.price_details);
4448
- return {
4449
- cycleStart: isoFromUnixSeconds(period.start),
4450
- cycleEnd: isoFromUnixSeconds(period.end),
4451
- priceId: asString(price.id) ?? asString(priceDetails.price)
4452
- };
4453
- }
4454
- function cappedCreditCycleEnd(cycleStart, cycleEnd) {
4455
- if (!cycleStart) return cycleEnd;
4456
- const start = new Date(cycleStart);
4457
- if (Number.isNaN(start.getTime())) return cycleEnd;
4458
- const monthly = creditCycleEndFrom(start);
4459
- if (!cycleEnd) return monthly.toISOString();
4460
- const end = new Date(cycleEnd);
4461
- if (Number.isNaN(end.getTime())) return monthly.toISOString();
4462
- return (end < monthly ? end : monthly).toISOString();
4463
- }
4464
- async function decideWebhookAction(event) {
4465
- const object = metadataOf(event.data?.object);
4466
- switch (event.type) {
4467
- case "checkout.session.completed": {
4468
- const mode = asString(object.mode);
4469
- if (mode !== null && mode !== "subscription") {
4470
- return { kind: "ignore", note: "non-subscription checkout" };
4471
- }
4472
- const metadata = metadataOf(object.metadata);
4473
- const userId = asString(metadata.user_id) ?? asString(object.client_reference_id);
4474
- if (!userId) {
4475
- return { kind: "ignore", note: "checkout session without user_id" };
4476
- }
4477
- return {
4478
- kind: "apply",
4479
- args: {
4480
- eventId: event.id,
4481
- eventType: event.type,
4482
- userId,
4483
- newPlanId: asString(metadata.plan_id) ?? PAID_PLAN_ID,
4484
- grantCredits: true,
4485
- billingStatus: "active",
4486
- customerId: refId(object.customer),
4487
- subscriptionId: refId(object.subscription),
4488
- subscriptionStatus: "active",
4489
- ledgerReason: "plan_change"
4490
- }
4491
- };
4492
- }
4493
- case "customer.subscription.updated":
4494
- case "customer.subscription.deleted": {
4495
- const metadata = metadataOf(object.metadata);
4496
- const customerId = refId(object.customer);
4497
- const userId = asString(metadata.user_id) ?? await resolveUserIdByCustomer(customerId);
4498
- if (!userId) {
4499
- return {
4500
- kind: "unresolved_user",
4501
- note: `no user binding for customer ${customerId ?? "<none>"}`
4502
- };
4503
- }
4504
- if (event.type === "customer.subscription.deleted") {
4505
- return {
4506
- kind: "apply",
4507
- args: {
4508
- eventId: event.id,
4509
- eventType: event.type,
4510
- userId,
4511
- newPlanId: FREE_PLAN_ID,
4512
- grantCredits: true,
4513
- billingStatus: "canceled",
4514
- customerId,
4515
- subscriptionId: refId(object.id),
4516
- subscriptionStatus: "canceled",
4517
- ledgerReason: "plan_change"
4518
- }
4519
- };
4520
- }
4521
- const status = asString(object.status);
4522
- const items = metadataOf(object.items);
4523
- const firstItem = metadataOf(
4524
- Array.isArray(items.data) ? items.data[0] : void 0
4525
- );
4526
- const priceId = asString(metadataOf(firstItem.price).id);
4527
- return {
4528
- kind: "apply",
4529
- args: {
4530
- eventId: event.id,
4531
- eventType: event.type,
4532
- userId,
4533
- // Tier/status sync only — credits ride the money event
4534
- // (invoice.paid), which is the one wallet clock.
4535
- newPlanId: planIdForPrice(priceId) ?? asString(metadata.plan_id) ?? null,
4536
- grantCredits: false,
4537
- billingStatus: mapSubscriptionStatusToBilling(status),
4538
- customerId,
4539
- subscriptionId: refId(object.id),
4540
- // Whitelisted, never forwarded raw. stripe_subscriptions.status has
4541
- // a CHECK over eight literals; a status Stripe adds later raises
4542
- // 23514 *inside* apply_plan_change, which rolls back the dedupe
4543
- // claim and the wallet grant with it, 500s, and leaves Stripe
4544
- // retrying forever. Null here means "skip the registry upsert",
4545
- // which still applies the tier and still acks 200.
4546
- subscriptionStatus: registrySubscriptionStatus(status),
4547
- priceId,
4548
- ledgerReason: "plan_change"
4549
- }
4550
- };
4551
- }
4552
- case "invoice.paid":
4553
- case "invoice.payment_failed": {
4554
- const { subscriptionId, metadata } = invoiceSubscriptionContext(object);
4555
- if (!subscriptionId) {
4556
- return { kind: "ignore", note: "invoice without a subscription" };
4557
- }
4558
- const customerId = refId(object.customer);
4559
- const userId = asString(metadata.user_id) ?? await resolveUserIdByCustomer(customerId);
4560
- if (!userId) {
4561
- return {
4562
- kind: "unresolved_user",
4563
- note: `no user binding for customer ${customerId ?? "<none>"}`
4564
- };
4565
- }
4566
- if (event.type === "invoice.payment_failed") {
4567
- return {
4568
- kind: "apply",
4569
- args: {
4570
- eventId: event.id,
4571
- eventType: event.type,
4572
- userId,
4573
- billingStatus: "past_due",
4574
- customerId,
4575
- subscriptionId,
4576
- ledgerReason: "plan_change"
4577
- }
4578
- };
4579
- }
4580
- const period = invoicePeriod(object);
4581
- const creditCycleEnd = cappedCreditCycleEnd(
4582
- period.cycleStart,
4583
- period.cycleEnd
4584
- );
4585
- return {
4586
- kind: "apply",
4587
- args: {
4588
- eventId: event.id,
4589
- eventType: event.type,
4590
- userId,
4591
- newPlanId: planIdForPrice(period.priceId) ?? asString(metadata.plan_id) ?? null,
4592
- grantCredits: true,
4593
- cycleStart: period.cycleStart,
4594
- cycleEnd: creditCycleEnd,
4595
- termEnd: period.cycleEnd,
4596
- billingStatus: "active",
4597
- customerId,
4598
- subscriptionId,
4599
- priceId: period.priceId,
4600
- ledgerReason: "refill"
4601
- }
4602
- };
4603
- }
4604
- default:
4605
- return { kind: "ignore", note: `unhandled event type ${event.type}` };
4606
- }
4607
- }
4608
- var stripeWebhookHandler = asyncHandler(
4609
- async (req, res) => {
4610
- const secret = getStripeWebhookSecret();
4611
- if (!secret) {
4612
- return res.status(503).json(BILLING_UNAVAILABLE);
4613
- }
4614
- const raw = req.body;
4615
- if (!Buffer.isBuffer(raw)) {
4616
- secureLogger.error(
4617
- "Stripe webhook received a non-raw body \u2014 express.raw must mount before the JSON parser",
4618
- void 0,
4619
- { path: req.path }
4620
- );
4621
- return res.status(500).json(
4622
- apiError(
4623
- "internal_error",
4624
- "WEBHOOK_BODY_NOT_RAW",
4625
- "Webhook raw body unavailable; server middleware misconfigured."
4626
- )
4627
- );
4628
- }
4629
- const verification = verifyStripeSignature({
4630
- payload: raw,
4631
- header: req.headers["stripe-signature"],
4632
- secret
4633
- });
4634
- if (!verification.valid) {
4635
- secureLogger.warn("Stripe webhook signature rejected", {
4636
- reason: verification.reason
4637
- });
4638
- return res.status(400).json(
4639
- apiError(
4640
- "authentication_error",
4641
- "INVALID_WEBHOOK_SIGNATURE",
4642
- `Stripe signature verification failed (${verification.reason}).`
4643
- )
4644
- );
4645
- }
4646
- let event;
4647
- try {
4648
- event = JSON.parse(raw.toString("utf8"));
4649
- } catch {
4650
- return res.status(400).json(
4651
- apiError(
4652
- "invalid_request_error",
4653
- "INVALID_JSON",
4654
- "Webhook payload is not valid JSON."
4655
- )
4656
- );
4657
- }
4658
- if (!asString(event?.id) || !asString(event?.type)) {
4659
- return res.status(400).json(
4660
- apiError(
4661
- "invalid_request_error",
4662
- "INVALID_EVENT",
4663
- "Webhook payload is missing event id or type."
4664
- )
4665
- );
4666
- }
4667
- try {
4668
- const decision = await decideWebhookAction(event);
4669
- if (decision.kind === "ignore") {
4670
- return res.json({ received: true, handled: false });
4671
- }
4672
- if (decision.kind === "unresolved_user") {
4673
- secureLogger.warn("Stripe webhook user unresolved \u2014 asking for retry", {
4674
- eventType: event.type,
4675
- note: decision.note
4676
- });
4677
- return res.status(500).json(
4678
- apiError(
4679
- "internal_error",
4680
- "WEBHOOK_USER_UNRESOLVED",
4681
- "Could not resolve the Hive user for this event yet; Stripe should retry."
4682
- )
4683
- );
4684
- }
4685
- const result = await applyPlanChange(decision.args);
4686
- if (event.type === "checkout.session.completed") {
4687
- await settleChargeRecord(
4688
- asString(metadataOf(event.data?.object).id),
4689
- "confirmed",
4690
- event.type
4691
- );
4692
- }
4693
- return res.json({
4694
- received: true,
4695
- handled: true,
4696
- outcome: result.outcome,
4697
- replayed: result.replayed
4698
- });
4699
- } catch (err) {
4700
- secureLogger.error("Stripe webhook processing failed", err, {
4701
- eventType: event.type
4702
- });
4703
- return res.status(500).json(
4704
- apiError(
4705
- "internal_error",
4706
- "WEBHOOK_PROCESSING_FAILED",
4707
- "Webhook processing failed; Stripe should retry. Processing is idempotent by event id."
4708
- )
4709
- );
4710
- }
4711
- }
4712
- );
4713
4087
  var LLAMAPAY_UNAVAILABLE = apiError(
4714
4088
  "internal_error",
4715
4089
  "BILLING_UNAVAILABLE",
@@ -5879,7 +5253,7 @@ function buildServerCard() {
5879
5253
  }
5880
5254
 
5881
5255
  // src/auth/oauth/router.ts
5882
- import { timingSafeEqual as timingSafeEqual5 } from "crypto";
5256
+ import { timingSafeEqual as timingSafeEqual4 } from "crypto";
5883
5257
  import express from "express";
5884
5258
  import rateLimit2 from "express-rate-limit";
5885
5259
  import { z as z4 } from "zod";
@@ -5932,7 +5306,7 @@ function monitorSecretAuthorized(req) {
5932
5306
  if (!expected || typeof supplied !== "string") return false;
5933
5307
  const left = Buffer.from(supplied);
5934
5308
  const right = Buffer.from(expected);
5935
- return left.length === right.length && timingSafeEqual5(left, right);
5309
+ return left.length === right.length && timingSafeEqual4(left, right);
5936
5310
  }
5937
5311
  function deploymentCanaryUserId() {
5938
5312
  const userId = getEnvVar("HIVE_CANARY_USER_ID");
@@ -6774,11 +6148,6 @@ app.use(
6774
6148
  type: "application/json"
6775
6149
  })
6776
6150
  );
6777
- app.post(
6778
- "/api/v1/billing/webhook",
6779
- express2.raw({ type: "application/json", limit: "1mb" }),
6780
- stripeWebhookHandler
6781
- );
6782
6151
  app.post(
6783
6152
  "/api/v1/billing/webhook/llamapay",
6784
6153
  express2.raw({ type: "application/json", limit: "1mb" }),
@@ -7334,7 +6703,7 @@ function isMonitorSecretRequest(req) {
7334
6703
  if (!secret || typeof provided !== "string") return false;
7335
6704
  const a = Buffer.from(provided);
7336
6705
  const b = Buffer.from(secret);
7337
- return a.length === b.length && timingSafeEqual6(a, b);
6706
+ return a.length === b.length && timingSafeEqual5(a, b);
7338
6707
  }
7339
6708
  function getValidatedMcpHiveToolName(scope, body) {
7340
6709
  if (body?.method !== "tools/call") return void 0;
@@ -8058,7 +7427,7 @@ app.post(
8058
7427
  if (monitorSecret && typeof authToken === "string") {
8059
7428
  const a = Buffer.from(authToken);
8060
7429
  const b = Buffer.from(monitorSecret);
8061
- isAuthorized = a.length === b.length && timingSafeEqual6(a, b);
7430
+ isAuthorized = a.length === b.length && timingSafeEqual5(a, b);
8062
7431
  }
8063
7432
  secureLogger.authEvent("monitor-tests-access", isAuthorized, {
8064
7433
  hasToken: !!authToken,