priceos 1.0.21 → 1.0.22

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/dist/next.cjs CHANGED
@@ -58,19 +58,16 @@ var getRetryDelayMs = (retryCount) => {
58
58
  return Math.min(RETRY_DELAY_CAP_MS, backoff + jitter);
59
59
  };
60
60
  var resolveIdempotencyKey = (input) => {
61
- const providedEventKey = typeof input.eventKey === "string" ? input.eventKey.trim() : "";
62
61
  const providedIdempotencyKey = typeof input.idempotencyKey === "string" ? String(input.idempotencyKey).trim() : "";
63
- return providedIdempotencyKey || providedEventKey;
62
+ return providedIdempotencyKey;
64
63
  };
65
64
  var withIdempotencyKey = (input) => {
66
65
  const idempotencyKey = resolveIdempotencyKey(input);
67
66
  if (!idempotencyKey) return input;
68
- const next = {
67
+ return {
69
68
  ...input,
70
69
  idempotencyKey
71
70
  };
72
- delete next.eventKey;
73
- return next;
74
71
  };
75
72
  var createLogger = (logLevel) => {
76
73
  const shouldLog = (level) => logLevel !== "none" && LOG_LEVELS[level] <= LOG_LEVELS[logLevel];
@@ -145,6 +142,22 @@ var throwRequestError = (log, error, response, context) => {
145
142
  log.error("Request failed", { context, status: response?.status, error });
146
143
  throw new PriceOSError(getErrorMessage(error), { status: response?.status, details: error });
147
144
  };
145
+ var resolveUrlResponse = (data, response) => {
146
+ if (data && typeof data === "object" && !Array.isArray(data)) {
147
+ const value = data.url;
148
+ if (typeof value === "string" && value.trim().length > 0) {
149
+ return { url: value };
150
+ }
151
+ }
152
+ const location = response?.headers.get("location");
153
+ if (location && location.trim().length > 0) {
154
+ return { url: location };
155
+ }
156
+ if (response?.redirected && typeof response.url === "string" && response.url.trim().length > 0) {
157
+ return { url: response.url };
158
+ }
159
+ return null;
160
+ };
148
161
  var createRetryingFetch = (baseFetch, log) => {
149
162
  return async (input, init) => {
150
163
  const { method, url } = getRequestDetails(input, init);
@@ -308,7 +321,43 @@ var PriceOS = class {
308
321
  if (error) {
309
322
  throwRequestError(this.log, error, response, "POST /v1/customers/{customerId}/customer_portal");
310
323
  }
311
- return data;
324
+ if (response && !response.ok) {
325
+ throw new PriceOSError(response.statusText || "Request failed", { status: response.status });
326
+ }
327
+ const result = resolveUrlResponse(data, response);
328
+ if (result) {
329
+ return result;
330
+ }
331
+ throw new PriceOSError("Invalid customer portal response", {
332
+ status: response?.status,
333
+ details: {
334
+ redirected: response?.redirected ?? false,
335
+ responseUrl: response?.url ?? null
336
+ }
337
+ });
338
+ },
339
+ createCheckout: async (customerId, input) => {
340
+ const { data, error, response } = await this.client.POST("/v1/customers/{customerId}/checkout", {
341
+ params: { path: { customerId }, header: this.header },
342
+ body: input
343
+ });
344
+ if (error) {
345
+ throwRequestError(this.log, error, response, "POST /v1/customers/{customerId}/checkout");
346
+ }
347
+ if (response && !response.ok) {
348
+ throw new PriceOSError(response.statusText || "Request failed", { status: response.status });
349
+ }
350
+ const result = resolveUrlResponse(data, response);
351
+ if (result) {
352
+ return result;
353
+ }
354
+ throw new PriceOSError("Invalid checkout response", {
355
+ status: response?.status,
356
+ details: {
357
+ redirected: response?.redirected ?? false,
358
+ responseUrl: response?.url ?? null
359
+ }
360
+ });
312
361
  }
313
362
  };
314
363
  const getFeatureAccessForCustomer = async (customerId) => {
@@ -346,7 +395,20 @@ var PriceOS = class {
346
395
  body: input
347
396
  });
348
397
  if (error) throwRequestError(this.log, error, response, "POST /v1/checkout");
349
- return data;
398
+ if (response && !response.ok) {
399
+ throw new PriceOSError(response.statusText || "Request failed", { status: response.status });
400
+ }
401
+ const result = resolveUrlResponse(data, response);
402
+ if (result) {
403
+ return result;
404
+ }
405
+ throw new PriceOSError("Invalid checkout response", {
406
+ status: response?.status,
407
+ details: {
408
+ redirected: response?.redirected ?? false,
409
+ responseUrl: response?.url ?? null
410
+ }
411
+ });
350
412
  }
351
413
  };
352
414
  this.bonuses = {
@@ -408,24 +470,6 @@ var PriceOS = class {
408
470
  if (error) throwRequestError(this.log, error, response, "GET /v1/usage/{id}");
409
471
  return data;
410
472
  },
411
- getEventByIdempotencyKey: async (idempotencyKey) => {
412
- const { data, error, response } = await this.client.GET("/v1/usage/idempotency-key/{idempotencyKey}", {
413
- params: { header: this.header, path: { idempotencyKey } }
414
- });
415
- if (error) {
416
- throwRequestError(this.log, error, response, "GET /v1/usage/idempotency-key/{idempotencyKey}");
417
- }
418
- return data;
419
- },
420
- getEventByKey: async (eventKey) => {
421
- const { data, error, response } = await this.client.GET("/v1/usage/idempotency-key/{idempotencyKey}", {
422
- params: { header: this.header, path: { idempotencyKey: eventKey } }
423
- });
424
- if (error) {
425
- throwRequestError(this.log, error, response, "GET /v1/usage/idempotency-key/{idempotencyKey}");
426
- }
427
- return data;
428
- },
429
473
  updateEvent: async (input) => {
430
474
  const { id, ...body } = input;
431
475
  const { data, error, response } = await this.client.PUT("/v1/usage/{id}", {
@@ -490,6 +534,7 @@ var FEATURE_ACCESS_PATH = "v1/feature-access";
490
534
  var TRACK_USAGE_PATH = "v1/usage";
491
535
  var PRICING_TABLE_PATH = "v1/pricing-table";
492
536
  var CHECKOUT_PATH = "v1/checkout";
537
+ var CUSTOMER_PORTAL_PATH = "v1/customer-portal";
493
538
  var normalizePath = (value) => value.replace(/^\/+/, "");
494
539
  var logTrackUsageError = (message, details) => {
495
540
  if (details === void 0) {
@@ -530,7 +575,7 @@ var parseTrackUsageBody = async (request) => {
530
575
  if (!Number.isFinite(amount) || amount <= 0) {
531
576
  return { error: "amount must be a positive number" };
532
577
  }
533
- const idempotencyKeyRaw = body.idempotencyKey ?? body.eventKey;
578
+ const idempotencyKeyRaw = body.idempotencyKey;
534
579
  const idempotencyKey = idempotencyKeyRaw === void 0 ? void 0 : typeof idempotencyKeyRaw === "string" && idempotencyKeyRaw.trim().length > 0 ? idempotencyKeyRaw.trim() : null;
535
580
  if (idempotencyKey === null) {
536
581
  return { error: "idempotencyKey must be a non-empty string" };
@@ -595,13 +640,20 @@ var parseCheckoutBody = async (request) => {
595
640
  return { error: "Invalid JSON body" };
596
641
  }
597
642
  const body = rawBody;
598
- const stripePriceId = typeof body.stripePriceId === "string" && body.stripePriceId.trim().length > 0 ? body.stripePriceId.trim() : "";
599
- if (!stripePriceId) {
600
- return { error: "stripePriceId is required" };
643
+ const stripeProductKey = typeof body.stripeProductKey === "string" && body.stripeProductKey.trim().length > 0 ? body.stripeProductKey.trim() : void 0;
644
+ const stripePriceId = typeof body.stripePriceId === "string" && body.stripePriceId.trim().length > 0 ? body.stripePriceId.trim() : void 0;
645
+ if (!stripeProductKey && !stripePriceId) {
646
+ return { error: "stripeProductKey or stripePriceId is required" };
647
+ }
648
+ const successUrlRaw = body.successUrl;
649
+ if (successUrlRaw !== void 0 && typeof successUrlRaw !== "string") {
650
+ return { error: "successUrl must be a non-empty string" };
601
651
  }
602
- const successUrl = typeof body.successUrl === "string" && body.successUrl.trim().length > 0 ? body.successUrl.trim() : "";
603
- if (!successUrl) {
604
- return { error: "successUrl is required" };
652
+ const successUrl = normalizeCustomerId(
653
+ typeof successUrlRaw === "string" ? successUrlRaw : void 0
654
+ );
655
+ if (successUrlRaw !== void 0 && !successUrl) {
656
+ return { error: "successUrl must be a non-empty string" };
605
657
  }
606
658
  const cancelUrl = typeof body.cancelUrl === "string" && body.cancelUrl.trim().length > 0 ? body.cancelUrl.trim() : void 0;
607
659
  const customerId = typeof body.customerId === "string" && body.customerId.trim().length > 0 ? body.customerId.trim() : void 0;
@@ -616,16 +668,71 @@ var parseCheckoutBody = async (request) => {
616
668
  }
617
669
  }
618
670
  }
671
+ const checkoutParamsRaw = body.checkoutParams;
672
+ if (checkoutParamsRaw !== void 0 && (!checkoutParamsRaw || typeof checkoutParamsRaw !== "object" || Array.isArray(checkoutParamsRaw))) {
673
+ return { error: "checkoutParams must be an object" };
674
+ }
675
+ const customerInfoRaw = body.customerInfo;
676
+ if (customerInfoRaw !== void 0 && (!customerInfoRaw || typeof customerInfoRaw !== "object" || Array.isArray(customerInfoRaw))) {
677
+ return { error: "customerInfo must be an object" };
678
+ }
679
+ const customerInfoRecord = customerInfoRaw;
680
+ const customerName = normalizeCustomerId(
681
+ typeof customerInfoRecord?.name === "string" ? customerInfoRecord.name : void 0
682
+ );
683
+ const customerEmail = normalizeCustomerId(
684
+ typeof customerInfoRecord?.email === "string" ? customerInfoRecord.email : void 0
685
+ );
619
686
  return {
620
687
  data: {
621
- stripePriceId,
622
- successUrl,
688
+ ...stripeProductKey ? { stripeProductKey } : {},
689
+ ...stripePriceId ? { stripePriceId } : {},
690
+ ...successUrl ? { successUrl } : {},
623
691
  ...cancelUrl ? { cancelUrl } : {},
624
692
  ...customerId ? { customerId } : {},
625
- ...metadataRaw ? { metadata: metadataRaw } : {}
693
+ ...metadataRaw ? { metadata: metadataRaw } : {},
694
+ ...checkoutParamsRaw ? { checkoutParams: checkoutParamsRaw } : {},
695
+ ...customerInfoRaw ? {
696
+ customerInfo: {
697
+ ...customerName ? { name: customerName } : {},
698
+ ...customerEmail ? { email: customerEmail } : {}
699
+ }
700
+ } : {}
626
701
  }
627
702
  };
628
703
  };
704
+ var parseCustomerPortalBody = async (request) => {
705
+ let rawText = "";
706
+ try {
707
+ rawText = await request.text();
708
+ } catch {
709
+ return { error: "Invalid JSON body" };
710
+ }
711
+ if (!rawText.trim()) return { data: {} };
712
+ let rawBody;
713
+ try {
714
+ rawBody = JSON.parse(rawText);
715
+ } catch {
716
+ return { error: "Invalid JSON body" };
717
+ }
718
+ if (!rawBody || typeof rawBody !== "object" || Array.isArray(rawBody)) {
719
+ return { error: "Invalid JSON body" };
720
+ }
721
+ const body = rawBody;
722
+ const customerIdRaw = body.customerId;
723
+ if (customerIdRaw !== void 0 && typeof customerIdRaw !== "string") {
724
+ return { error: "customerId must be a non-empty string" };
725
+ }
726
+ const customerId = normalizeCustomerId(
727
+ typeof customerIdRaw === "string" ? customerIdRaw : void 0
728
+ );
729
+ if (customerIdRaw !== void 0 && !customerId) {
730
+ return { error: "customerId must be a non-empty string" };
731
+ }
732
+ return {
733
+ data: customerId ? { customerId } : {}
734
+ };
735
+ };
629
736
  var handleGetCustomer = async (request, url, client, getCustomerId) => {
630
737
  if (request.method.toUpperCase() !== "GET") {
631
738
  return new Response("Method not allowed.", { status: 405 });
@@ -769,12 +876,79 @@ var handleCreateCheckout = async (request, client, getCustomerId) => {
769
876
  request
770
877
  );
771
878
  if (errorResponse) return errorResponse;
879
+ if (parsed.data.customerId && resolvedCustomerId && parsed.data.customerId !== resolvedCustomerId) {
880
+ return jsonResponse(403, { error: "customerId must match authenticated customer" });
881
+ }
882
+ const customerId = parsed.data.customerId ?? resolvedCustomerId;
883
+ if (!customerId) {
884
+ return jsonResponse(401, { error: "Customer not identified" });
885
+ }
886
+ if (parsed.data.stripePriceId) {
887
+ const requestOrigin = new URL(request.url).origin;
888
+ const successUrl = parsed.data.successUrl ?? `${requestOrigin}/settings/billing?checkout=success`;
889
+ const cancelUrl = parsed.data.cancelUrl ?? successUrl;
890
+ try {
891
+ const data = await client.checkout.create({
892
+ stripePriceId: parsed.data.stripePriceId,
893
+ successUrl,
894
+ cancelUrl,
895
+ ...parsed.data.metadata ? { metadata: parsed.data.metadata } : {},
896
+ customerId
897
+ });
898
+ return jsonResponse(200, data);
899
+ } catch (error) {
900
+ if (error instanceof PriceOSError) {
901
+ return jsonResponse(error.status ?? 500, { error: error.message });
902
+ }
903
+ return jsonResponse(500, { error: "Request failed" });
904
+ }
905
+ }
906
+ if (!parsed.data.stripeProductKey) {
907
+ return jsonResponse(400, { error: "stripeProductKey is required" });
908
+ }
909
+ const checkoutParams = {
910
+ ...parsed.data.checkoutParams ?? {},
911
+ ...parsed.data.cancelUrl ? { cancel_url: parsed.data.cancelUrl } : {},
912
+ ...parsed.data.metadata ? { metadata: parsed.data.metadata } : {}
913
+ };
772
914
  const body = {
773
- ...parsed.data,
774
- ...parsed.data.customerId ?? resolvedCustomerId ? { customerId: parsed.data.customerId ?? resolvedCustomerId ?? void 0 } : {}
915
+ stripeProductKey: parsed.data.stripeProductKey,
916
+ ...parsed.data.successUrl ? { successUrl: parsed.data.successUrl } : {},
917
+ ...parsed.data.customerInfo ? { customerInfo: parsed.data.customerInfo } : {},
918
+ ...Object.keys(checkoutParams).length ? { checkoutParams } : {}
775
919
  };
776
920
  try {
777
- const data = await client.checkout.create(body);
921
+ const data = await client.customers.createCheckout(customerId, body);
922
+ return jsonResponse(200, data);
923
+ } catch (error) {
924
+ if (error instanceof PriceOSError) {
925
+ return jsonResponse(error.status ?? 500, { error: error.message });
926
+ }
927
+ return jsonResponse(500, { error: "Request failed" });
928
+ }
929
+ };
930
+ var handleCreateCustomerPortal = async (request, client, getCustomerId) => {
931
+ if (request.method.toUpperCase() !== "POST") {
932
+ return new Response("Method not allowed.", { status: 405 });
933
+ }
934
+ const parsed = await parseCustomerPortalBody(request);
935
+ if (!parsed.data) {
936
+ return jsonResponse(400, { error: parsed.error ?? "Invalid request body" });
937
+ }
938
+ const { customerId: resolvedCustomerId, errorResponse } = await resolveOptionalCustomerId(
939
+ getCustomerId,
940
+ request
941
+ );
942
+ if (errorResponse) return errorResponse;
943
+ if (parsed.data.customerId && resolvedCustomerId && parsed.data.customerId !== resolvedCustomerId) {
944
+ return jsonResponse(403, { error: "customerId must match authenticated customer" });
945
+ }
946
+ const customerId = parsed.data.customerId ?? resolvedCustomerId;
947
+ if (!customerId) {
948
+ return jsonResponse(401, { error: "Customer not identified" });
949
+ }
950
+ try {
951
+ const data = await client.customers.createPortal(customerId);
778
952
  return jsonResponse(200, data);
779
953
  } catch (error) {
780
954
  if (error instanceof PriceOSError) {
@@ -823,6 +997,9 @@ function priceosHandler(options = {}) {
823
997
  if (path === CHECKOUT_PATH) {
824
998
  return handleCreateCheckout(request, client, getCustomerId);
825
999
  }
1000
+ if (path === CUSTOMER_PORTAL_PATH) {
1001
+ return handleCreateCustomerPortal(request, client, getCustomerId);
1002
+ }
826
1003
  return new Response("Not found.", { status: 404 });
827
1004
  };
828
1005
  return Object.assign(handler, {