@freemius/sdk 0.0.6 → 0.2.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/dist/index.d.mts +262 -231
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +262 -231
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +228 -119
- package/dist/index.mjs +226 -120
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -21,6 +21,14 @@ let CURRENCY = /* @__PURE__ */ function(CURRENCY$1) {
|
|
|
21
21
|
return CURRENCY$1;
|
|
22
22
|
}({});
|
|
23
23
|
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/contracts/webhook.ts
|
|
26
|
+
let WebhookAuthenticationMethod = /* @__PURE__ */ function(WebhookAuthenticationMethod$1) {
|
|
27
|
+
WebhookAuthenticationMethod$1["SignatureHeader"] = "SignatureHeader";
|
|
28
|
+
WebhookAuthenticationMethod$1["Api"] = "Api";
|
|
29
|
+
return WebhookAuthenticationMethod$1;
|
|
30
|
+
}({});
|
|
31
|
+
|
|
24
32
|
//#endregion
|
|
25
33
|
//#region src/api/parser.ts
|
|
26
34
|
function idToNumber(id) {
|
|
@@ -86,9 +94,60 @@ function parsePaymentMethod(gateway) {
|
|
|
86
94
|
return gateway?.startsWith("stripe") ? "card" : gateway?.startsWith("paypal") ? "paypal" : null;
|
|
87
95
|
}
|
|
88
96
|
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region src/errors/ActionError.ts
|
|
99
|
+
var ActionError = class ActionError extends Error {
|
|
100
|
+
statusCode;
|
|
101
|
+
validationIssues;
|
|
102
|
+
constructor(message, statusCode = 400, validationIssues) {
|
|
103
|
+
super(message);
|
|
104
|
+
this.name = "ActionError";
|
|
105
|
+
this.statusCode = statusCode;
|
|
106
|
+
this.validationIssues = validationIssues;
|
|
107
|
+
}
|
|
108
|
+
toResponse() {
|
|
109
|
+
const errorResponse = { message: this.message };
|
|
110
|
+
if (this.validationIssues) errorResponse.issues = this.validationIssues;
|
|
111
|
+
return Response.json(errorResponse, { status: this.statusCode });
|
|
112
|
+
}
|
|
113
|
+
static badRequest(message) {
|
|
114
|
+
return new ActionError(message, 400);
|
|
115
|
+
}
|
|
116
|
+
static unauthorized(message = "Unauthorized") {
|
|
117
|
+
return new ActionError(message, 401);
|
|
118
|
+
}
|
|
119
|
+
static notFound(message = "Not found") {
|
|
120
|
+
return new ActionError(message, 404);
|
|
121
|
+
}
|
|
122
|
+
static validationFailed(message, validationIssues) {
|
|
123
|
+
return new ActionError(message, 400, validationIssues);
|
|
124
|
+
}
|
|
125
|
+
static internalError(message = "Internal server error") {
|
|
126
|
+
return new ActionError(message, 500);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
//#endregion
|
|
131
|
+
//#region src/errors/WebhookError.ts
|
|
132
|
+
var WebhookError = class extends Error {
|
|
133
|
+
statusCode;
|
|
134
|
+
constructor(message, statusCode = 400) {
|
|
135
|
+
super(message);
|
|
136
|
+
this.name = "WebhookError";
|
|
137
|
+
this.statusCode = statusCode;
|
|
138
|
+
}
|
|
139
|
+
toResponse() {
|
|
140
|
+
return {
|
|
141
|
+
status: this.statusCode,
|
|
142
|
+
success: false,
|
|
143
|
+
error: this.message
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
89
148
|
//#endregion
|
|
90
149
|
//#region package.json
|
|
91
|
-
var version = "0.0
|
|
150
|
+
var version = "0.2.0";
|
|
92
151
|
|
|
93
152
|
//#endregion
|
|
94
153
|
//#region src/api/client.ts
|
|
@@ -399,6 +458,22 @@ var User = class extends ApiBase {
|
|
|
399
458
|
if (!this.isGoodResponse(response.response) || !response.data || !response.data) return null;
|
|
400
459
|
return response.data;
|
|
401
460
|
}
|
|
461
|
+
async retrieveHostedCustomerPortal(userId) {
|
|
462
|
+
const response = await this.client.POST(`/products/{product_id}/portal/login.json`, {
|
|
463
|
+
params: { path: { product_id: this.productId } },
|
|
464
|
+
body: { id: idToString(userId) }
|
|
465
|
+
});
|
|
466
|
+
if (!this.isGoodResponse(response.response) || !response.data) return null;
|
|
467
|
+
return response.data;
|
|
468
|
+
}
|
|
469
|
+
async retrieveHostedCustomerPortalByEmail(email) {
|
|
470
|
+
const response = await this.client.POST(`/products/{product_id}/portal/login.json`, {
|
|
471
|
+
params: { path: { product_id: this.productId } },
|
|
472
|
+
body: { email }
|
|
473
|
+
});
|
|
474
|
+
if (!this.isGoodResponse(response.response) || !response.data) return null;
|
|
475
|
+
return response.data;
|
|
476
|
+
}
|
|
402
477
|
};
|
|
403
478
|
|
|
404
479
|
//#endregion
|
|
@@ -436,6 +511,30 @@ var Payment = class extends ApiBase {
|
|
|
436
511
|
}
|
|
437
512
|
};
|
|
438
513
|
|
|
514
|
+
//#endregion
|
|
515
|
+
//#region src/api/WebhookEvent.ts
|
|
516
|
+
var WebhookEvent = class extends ApiBase {
|
|
517
|
+
async retrieve(eventId) {
|
|
518
|
+
const result = await this.client.GET(`/products/{product_id}/events/{event_id}.json`, { params: { path: {
|
|
519
|
+
product_id: this.productId,
|
|
520
|
+
event_id: this.getIdForPath(eventId)
|
|
521
|
+
} } });
|
|
522
|
+
if (!this.isGoodResponse(result.response) || !result.data || !result.data.id) return null;
|
|
523
|
+
return result.data;
|
|
524
|
+
}
|
|
525
|
+
async retrieveMany(filter, pagination) {
|
|
526
|
+
const result = await this.client.GET(`/products/{product_id}/events.json`, { params: {
|
|
527
|
+
path: { product_id: this.productId },
|
|
528
|
+
query: {
|
|
529
|
+
...this.getPagingParams(pagination),
|
|
530
|
+
...filter ?? {}
|
|
531
|
+
}
|
|
532
|
+
} });
|
|
533
|
+
if (!this.isGoodResponse(result.response) || !result.data || !Array.isArray(result.data.events)) return [];
|
|
534
|
+
return result.data.events;
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
|
|
439
538
|
//#endregion
|
|
440
539
|
//#region src/utils/ops.ts
|
|
441
540
|
function splitName(name) {
|
|
@@ -464,6 +563,7 @@ var ApiService = class {
|
|
|
464
563
|
product;
|
|
465
564
|
subscription;
|
|
466
565
|
payment;
|
|
566
|
+
event;
|
|
467
567
|
baseUrl;
|
|
468
568
|
constructor(productId, apiKey, secretKey, publicKey) {
|
|
469
569
|
this.secretKey = secretKey;
|
|
@@ -476,6 +576,7 @@ var ApiService = class {
|
|
|
476
576
|
this.product = new Product(this.productId, this.client);
|
|
477
577
|
this.subscription = new Subscription(this.productId, this.client);
|
|
478
578
|
this.payment = new Payment(this.productId, this.client);
|
|
579
|
+
this.event = new WebhookEvent(this.productId, this.client);
|
|
479
580
|
}
|
|
480
581
|
/**
|
|
481
582
|
* Low level API client for direct access to the Freemius API.
|
|
@@ -556,15 +657,7 @@ var ApiService = class {
|
|
|
556
657
|
* Every method returns the existing instance of the builder for chainability,
|
|
557
658
|
* The final `getOptions()` method returns the constructed `CheckoutOptions` object.
|
|
558
659
|
*/
|
|
559
|
-
var Checkout = class
|
|
560
|
-
static createSandboxToken(productId, secretKey, publicKey) {
|
|
561
|
-
const timestamp = Math.floor(Date.now() / 1e3).toString();
|
|
562
|
-
const token = `${timestamp}${productId}${secretKey}${publicKey}checkout`;
|
|
563
|
-
return {
|
|
564
|
-
ctx: timestamp,
|
|
565
|
-
token: createHash("md5").update(token).digest("hex")
|
|
566
|
-
};
|
|
567
|
-
}
|
|
660
|
+
var Checkout = class {
|
|
568
661
|
options;
|
|
569
662
|
constructor(productId, publicKey, secretKey) {
|
|
570
663
|
this.productId = productId;
|
|
@@ -575,12 +668,11 @@ var Checkout = class Checkout {
|
|
|
575
668
|
/**
|
|
576
669
|
* Enables sandbox mode for testing purposes.
|
|
577
670
|
*
|
|
578
|
-
* @returns A new builder instance with sandbox configuration
|
|
579
671
|
*/
|
|
580
|
-
setSandbox() {
|
|
672
|
+
setSandbox(sandbox) {
|
|
581
673
|
this.options = {
|
|
582
674
|
...this.options,
|
|
583
|
-
sandbox
|
|
675
|
+
sandbox
|
|
584
676
|
};
|
|
585
677
|
return this;
|
|
586
678
|
}
|
|
@@ -590,7 +682,6 @@ var Checkout = class Checkout {
|
|
|
590
682
|
* @param user User object with email and optional name fields. The shape matches the session from `better-auth` or next-auth packages. Also handles `null` or `undefined` gracefully.
|
|
591
683
|
* @param readonly If true, the user information will be read-only in the checkout session.
|
|
592
684
|
*
|
|
593
|
-
* @returns A new builder instance with user configuration
|
|
594
685
|
*/
|
|
595
686
|
setUser(user, readonly = true) {
|
|
596
687
|
if (!user) return this;
|
|
@@ -614,7 +705,6 @@ var Checkout = class Checkout {
|
|
|
614
705
|
* Applies recommended UI settings for better user experience.
|
|
615
706
|
* This includes fullscreen mode, upsells, refund badge, and reviews display.
|
|
616
707
|
*
|
|
617
|
-
* @returns A new builder instance with recommended UI settings
|
|
618
708
|
*/
|
|
619
709
|
setRecommendations() {
|
|
620
710
|
this.options = {
|
|
@@ -631,7 +721,6 @@ var Checkout = class Checkout {
|
|
|
631
721
|
* Sets the plan ID for the checkout.
|
|
632
722
|
*
|
|
633
723
|
* @param planId The plan ID to purchase
|
|
634
|
-
* @returns A new builder instance with plan ID set
|
|
635
724
|
*/
|
|
636
725
|
setPlan(planId) {
|
|
637
726
|
this.options = {
|
|
@@ -644,7 +733,6 @@ var Checkout = class Checkout {
|
|
|
644
733
|
* Sets the number of licenses to purchase.
|
|
645
734
|
*
|
|
646
735
|
* @param count Number of licenses
|
|
647
|
-
* @returns A new builder instance with license count set
|
|
648
736
|
*/
|
|
649
737
|
setQuota(count) {
|
|
650
738
|
this.options = {
|
|
@@ -672,7 +760,6 @@ var Checkout = class Checkout {
|
|
|
672
760
|
*
|
|
673
761
|
* @param coupon The coupon code to apply
|
|
674
762
|
* @param hideUI Whether to hide the coupon input field from users
|
|
675
|
-
* @returns A new builder instance with coupon configuration
|
|
676
763
|
*/
|
|
677
764
|
setCoupon(options) {
|
|
678
765
|
const { code: coupon, hideUI = false } = options;
|
|
@@ -687,7 +774,6 @@ var Checkout = class Checkout {
|
|
|
687
774
|
* Enables trial mode for the checkout.
|
|
688
775
|
*
|
|
689
776
|
* @param mode Trial type - true/false for plan default, or specific 'free'/'paid' mode
|
|
690
|
-
* @returns A new builder instance with trial configuration
|
|
691
777
|
*/
|
|
692
778
|
setTrial(mode = true) {
|
|
693
779
|
this.options = {
|
|
@@ -700,7 +786,6 @@ var Checkout = class Checkout {
|
|
|
700
786
|
* Configures the visual layout and appearance of the checkout.
|
|
701
787
|
*
|
|
702
788
|
* @param options Appearance configuration options
|
|
703
|
-
* @returns A new builder instance with appearance configuration
|
|
704
789
|
*/
|
|
705
790
|
setAppearance(options) {
|
|
706
791
|
this.options = { ...this.options };
|
|
@@ -715,7 +800,6 @@ var Checkout = class Checkout {
|
|
|
715
800
|
* Configures discount display settings.
|
|
716
801
|
*
|
|
717
802
|
* @param options Discount configuration options
|
|
718
|
-
* @returns A new builder instance with discount configuration
|
|
719
803
|
*/
|
|
720
804
|
setDiscounts(options) {
|
|
721
805
|
this.options = { ...this.options };
|
|
@@ -730,7 +814,6 @@ var Checkout = class Checkout {
|
|
|
730
814
|
*
|
|
731
815
|
* @param selector Type of billing cycle selector to show
|
|
732
816
|
* @param defaultCycle Default billing cycle to select
|
|
733
|
-
* @returns A new builder instance with billing cycle configuration
|
|
734
817
|
*/
|
|
735
818
|
setBillingCycle(defaultCycle, selector) {
|
|
736
819
|
this.options = { ...this.options };
|
|
@@ -742,7 +825,6 @@ var Checkout = class Checkout {
|
|
|
742
825
|
* Sets the language/locale for the checkout.
|
|
743
826
|
*
|
|
744
827
|
* @param locale Language setting - 'auto', 'auto-beta', or specific locale like 'en_US'
|
|
745
|
-
* @returns A new builder instance with locale configuration
|
|
746
828
|
*/
|
|
747
829
|
setLanguage(locale = "auto") {
|
|
748
830
|
this.options = {
|
|
@@ -755,7 +837,6 @@ var Checkout = class Checkout {
|
|
|
755
837
|
* Configures review and badge display settings.
|
|
756
838
|
*
|
|
757
839
|
* @param options Review and badge configuration
|
|
758
|
-
* @returns A new builder instance with reviews and badges configuration
|
|
759
840
|
*/
|
|
760
841
|
setSocialProofing(options) {
|
|
761
842
|
this.options = { ...this.options };
|
|
@@ -771,7 +852,6 @@ var Checkout = class Checkout {
|
|
|
771
852
|
* @param currency Primary currency or 'auto' for automatic detection
|
|
772
853
|
* @param defaultCurrency Default currency when using 'auto'
|
|
773
854
|
* @param showInlineSelector Whether to show inline currency selector
|
|
774
|
-
* @returns A new builder instance with currency configuration
|
|
775
855
|
*/
|
|
776
856
|
setCurrency(currency, defaultCurrency = "usd", showInlineSelector = true) {
|
|
777
857
|
this.options = {
|
|
@@ -787,7 +867,6 @@ var Checkout = class Checkout {
|
|
|
787
867
|
*
|
|
788
868
|
* @param cancelUrl URL for back button when in page mode
|
|
789
869
|
* @param cancelIcon Custom cancel icon URL
|
|
790
|
-
* @returns A new builder instance with navigation configuration
|
|
791
870
|
*/
|
|
792
871
|
setCancelButton(cancelUrl, cancelIcon) {
|
|
793
872
|
this.options = { ...this.options };
|
|
@@ -799,7 +878,6 @@ var Checkout = class Checkout {
|
|
|
799
878
|
* Associates purchases with an affiliate account.
|
|
800
879
|
*
|
|
801
880
|
* @param userId Affiliate user ID
|
|
802
|
-
* @returns A new builder instance with affiliate configuration
|
|
803
881
|
*/
|
|
804
882
|
setAffiliate(userId) {
|
|
805
883
|
this.options = {
|
|
@@ -812,7 +890,6 @@ var Checkout = class Checkout {
|
|
|
812
890
|
* Sets a custom image/icon for the checkout.
|
|
813
891
|
*
|
|
814
892
|
* @param imageUrl Secure HTTPS URL to the image
|
|
815
|
-
* @returns A new builder instance with custom image
|
|
816
893
|
*/
|
|
817
894
|
setImage(imageUrl) {
|
|
818
895
|
this.options = {
|
|
@@ -822,12 +899,13 @@ var Checkout = class Checkout {
|
|
|
822
899
|
return this;
|
|
823
900
|
}
|
|
824
901
|
/**
|
|
825
|
-
* Configures the checkout for license renewal.
|
|
902
|
+
* Configures the checkout for license renewal or upgrade by the license key.
|
|
903
|
+
*
|
|
904
|
+
* @note - This is less secure since it exposes the license key to the client. Use only in authenticated contexts.
|
|
826
905
|
*
|
|
827
906
|
* @param licenseKey The license key to renew
|
|
828
|
-
* @returns A new builder instance configured for renewal
|
|
829
907
|
*/
|
|
830
|
-
|
|
908
|
+
setLicenseUpgradeByKey(licenseKey) {
|
|
831
909
|
this.options = {
|
|
832
910
|
...this.options,
|
|
833
911
|
license_key: licenseKey
|
|
@@ -835,9 +913,20 @@ var Checkout = class Checkout {
|
|
|
835
913
|
return this;
|
|
836
914
|
}
|
|
837
915
|
/**
|
|
838
|
-
*
|
|
916
|
+
* Configures the checkout for license upgrade using an authorization token.
|
|
839
917
|
*
|
|
840
|
-
* @
|
|
918
|
+
* @param params The license upgrade authorization parameters
|
|
919
|
+
*/
|
|
920
|
+
setLicenseUpgradeByAuth(params) {
|
|
921
|
+
this.options = {
|
|
922
|
+
...this.options,
|
|
923
|
+
license_id: params.licenseId,
|
|
924
|
+
authorization: params.authorization
|
|
925
|
+
};
|
|
926
|
+
return this;
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* Builds and returns the final checkout options to be used with the `@freemius/checkout` package.
|
|
841
930
|
*
|
|
842
931
|
* @returns The constructed CheckoutOptions object
|
|
843
932
|
*/
|
|
@@ -846,8 +935,6 @@ var Checkout = class Checkout {
|
|
|
846
935
|
}
|
|
847
936
|
/**
|
|
848
937
|
* Generates a checkout link based on the current builder state.
|
|
849
|
-
*
|
|
850
|
-
* @note - This is async by purpose so that we can allow for future enhancements that might require async operations.
|
|
851
938
|
*/
|
|
852
939
|
getLink() {
|
|
853
940
|
const checkoutOptions = convertCheckoutOptionsToQueryParams(this.options);
|
|
@@ -868,39 +955,6 @@ var Checkout = class Checkout {
|
|
|
868
955
|
}
|
|
869
956
|
};
|
|
870
957
|
|
|
871
|
-
//#endregion
|
|
872
|
-
//#region src/errors/ActionError.ts
|
|
873
|
-
var ActionError = class ActionError extends Error {
|
|
874
|
-
statusCode;
|
|
875
|
-
validationIssues;
|
|
876
|
-
constructor(message, statusCode = 400, validationIssues) {
|
|
877
|
-
super(message);
|
|
878
|
-
this.name = "ActionError";
|
|
879
|
-
this.statusCode = statusCode;
|
|
880
|
-
this.validationIssues = validationIssues;
|
|
881
|
-
}
|
|
882
|
-
toResponse() {
|
|
883
|
-
const errorResponse = { message: this.message };
|
|
884
|
-
if (this.validationIssues) errorResponse.issues = this.validationIssues;
|
|
885
|
-
return Response.json(errorResponse, { status: this.statusCode });
|
|
886
|
-
}
|
|
887
|
-
static badRequest(message) {
|
|
888
|
-
return new ActionError(message, 400);
|
|
889
|
-
}
|
|
890
|
-
static unauthorized(message = "Unauthorized") {
|
|
891
|
-
return new ActionError(message, 401);
|
|
892
|
-
}
|
|
893
|
-
static notFound(message = "Not found") {
|
|
894
|
-
return new ActionError(message, 404);
|
|
895
|
-
}
|
|
896
|
-
static validationFailed(message, validationIssues) {
|
|
897
|
-
return new ActionError(message, 400, validationIssues);
|
|
898
|
-
}
|
|
899
|
-
static internalError(message = "Internal server error") {
|
|
900
|
-
return new ActionError(message, 500);
|
|
901
|
-
}
|
|
902
|
-
};
|
|
903
|
-
|
|
904
958
|
//#endregion
|
|
905
959
|
//#region src/checkout/PricingRetriever.ts
|
|
906
960
|
var PricingRetriever = class {
|
|
@@ -933,7 +987,10 @@ var PurchaseProcessor = class {
|
|
|
933
987
|
return request.method === "POST" && action === "process_purchase";
|
|
934
988
|
}
|
|
935
989
|
async processAction(request) {
|
|
936
|
-
const purchaseSchema = zod.object({
|
|
990
|
+
const purchaseSchema = zod.object({
|
|
991
|
+
purchase: zod.object({ license_id: zod.string() }).optional(),
|
|
992
|
+
trial: zod.object({ license_id: zod.string() }).optional()
|
|
993
|
+
});
|
|
937
994
|
const contentType = request.headers.get("content-type");
|
|
938
995
|
if (!contentType || !contentType.includes("application/json")) throw ActionError.badRequest("Invalid content type. Expected application/json");
|
|
939
996
|
let requestBody;
|
|
@@ -944,7 +1001,8 @@ var PurchaseProcessor = class {
|
|
|
944
1001
|
}
|
|
945
1002
|
const parseResult = purchaseSchema.safeParse(requestBody);
|
|
946
1003
|
if (!parseResult.success) throw ActionError.validationFailed("Invalid request body format", parseResult.error.issues);
|
|
947
|
-
const
|
|
1004
|
+
const licenseId = parseResult.data.purchase?.license_id ?? parseResult.data.trial?.license_id;
|
|
1005
|
+
if (!licenseId) throw ActionError.badRequest("License ID is required in the request body, either from purchase or from trial.");
|
|
948
1006
|
const purchase = await this.purchase.retrievePurchase(licenseId);
|
|
949
1007
|
if (!purchase) throw ActionError.notFound("No purchase data found for the provided license ID");
|
|
950
1008
|
if (this.callback) {
|
|
@@ -962,11 +1020,13 @@ var CheckoutRedirectInfo = class {
|
|
|
962
1020
|
plan_id;
|
|
963
1021
|
email;
|
|
964
1022
|
pricing_id;
|
|
965
|
-
|
|
1023
|
+
action;
|
|
966
1024
|
license_id;
|
|
967
1025
|
expiration;
|
|
968
1026
|
quota;
|
|
969
|
-
|
|
1027
|
+
trial;
|
|
1028
|
+
trial_ends_at;
|
|
1029
|
+
currency;
|
|
970
1030
|
amount;
|
|
971
1031
|
tax;
|
|
972
1032
|
type;
|
|
@@ -978,17 +1038,19 @@ var CheckoutRedirectInfo = class {
|
|
|
978
1038
|
this.plan_id = idToString(data.plan_id);
|
|
979
1039
|
this.email = data.email;
|
|
980
1040
|
this.pricing_id = idToString(data.pricing_id);
|
|
981
|
-
this.
|
|
1041
|
+
this.action = data.action ? data.action : "purchase";
|
|
982
1042
|
this.license_id = idToString(data.license_id);
|
|
983
1043
|
this.expiration = data.expiration ? parseDateTime(data.expiration) : null;
|
|
984
1044
|
this.quota = data.quota ? parseNumber(data.quota) : null;
|
|
985
|
-
this.
|
|
1045
|
+
this.trial = data.trial ? data.trial : null;
|
|
1046
|
+
this.trial_ends_at = data.trial_ends_at ? parseDateTime(data.trial_ends_at) : null;
|
|
1047
|
+
this.currency = data.currency ? parseCurrency(data.currency) : CURRENCY.USD;
|
|
986
1048
|
this.amount = parseNumber(data.amount);
|
|
987
1049
|
this.tax = parseNumber(data.tax);
|
|
988
1050
|
this.subscription_id = data.subscription_id ? idToString(data.subscription_id) : null;
|
|
989
1051
|
this.billing_cycle = data.billing_cycle ? parseBillingCycle(data.billing_cycle) : null;
|
|
990
1052
|
this.payment_id = data.payment_id ? idToString(data.payment_id) : null;
|
|
991
|
-
this.type = this.subscription_id ? "subscription" : "one-off";
|
|
1053
|
+
this.type = this.subscription_id ? "subscription" : this.payment_id ? "one-off" : null;
|
|
992
1054
|
}
|
|
993
1055
|
isSubscription() {
|
|
994
1056
|
return this.type === "subscription";
|
|
@@ -999,17 +1061,19 @@ var CheckoutRedirectInfo = class {
|
|
|
999
1061
|
plan_id: this.plan_id,
|
|
1000
1062
|
email: this.email,
|
|
1001
1063
|
pricing_id: this.pricing_id,
|
|
1002
|
-
currency: this.currency,
|
|
1003
1064
|
license_id: this.license_id,
|
|
1004
1065
|
expiration: this.expiration,
|
|
1005
1066
|
quota: this.quota,
|
|
1067
|
+
trial: this.trial,
|
|
1068
|
+
trial_ends_at: this.trial_ends_at,
|
|
1069
|
+
currency: this.currency,
|
|
1006
1070
|
action: this.action,
|
|
1007
1071
|
amount: this.amount,
|
|
1008
1072
|
tax: this.tax,
|
|
1009
|
-
type: this.type,
|
|
1010
1073
|
subscription_id: this.subscription_id,
|
|
1011
1074
|
billing_cycle: this.billing_cycle,
|
|
1012
|
-
payment_id: this.payment_id
|
|
1075
|
+
payment_id: this.payment_id,
|
|
1076
|
+
type: this.type
|
|
1013
1077
|
};
|
|
1014
1078
|
}
|
|
1015
1079
|
};
|
|
@@ -1053,11 +1117,16 @@ var RedirectProcessor = class {
|
|
|
1053
1117
|
}
|
|
1054
1118
|
const cleanUrl = this.getCleanUrl(url.href);
|
|
1055
1119
|
const calculatedSignature = createHmac("sha256", this.secretKey).update(cleanUrl).digest("hex");
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1120
|
+
try {
|
|
1121
|
+
const result = timingSafeEqual(Buffer.from(calculatedSignature), Buffer.from(signature));
|
|
1122
|
+
if (!result) return null;
|
|
1123
|
+
const params = Object.fromEntries(url.searchParams.entries());
|
|
1124
|
+
if (!params.user_id || !params.plan_id || !params.pricing_id || !params.email) return null;
|
|
1125
|
+
return new CheckoutRedirectInfo(params);
|
|
1126
|
+
} catch (e) {
|
|
1127
|
+
console.error("Error getting redirection information:", e);
|
|
1128
|
+
return null;
|
|
1129
|
+
}
|
|
1061
1130
|
}
|
|
1062
1131
|
getCleanUrl(url) {
|
|
1063
1132
|
const signatureParam = "&signature=";
|
|
@@ -1081,9 +1150,6 @@ var CheckoutRequestProcessor = class {
|
|
|
1081
1150
|
return (request) => this.process(config, request);
|
|
1082
1151
|
}
|
|
1083
1152
|
async process(config, request) {
|
|
1084
|
-
const url = new URL(request.url);
|
|
1085
|
-
const action = url.searchParams.get("action");
|
|
1086
|
-
if (!action) return Response.json({ error: "Action parameter is required" }, { status: 400 });
|
|
1087
1153
|
const actionHandlers = [
|
|
1088
1154
|
this.getPricingRetriever(),
|
|
1089
1155
|
this.getRedirectProcessor({
|
|
@@ -1145,7 +1211,8 @@ var CheckoutRequestProcessor = class {
|
|
|
1145
1211
|
//#region src/services/CheckoutService.ts
|
|
1146
1212
|
var CheckoutService = class {
|
|
1147
1213
|
request;
|
|
1148
|
-
constructor(productId, publicKey, secretKey, purchase, pricing) {
|
|
1214
|
+
constructor(api, productId, publicKey, secretKey, purchase, pricing) {
|
|
1215
|
+
this.api = api;
|
|
1149
1216
|
this.productId = productId;
|
|
1150
1217
|
this.publicKey = publicKey;
|
|
1151
1218
|
this.secretKey = secretKey;
|
|
@@ -1186,16 +1253,20 @@ var CheckoutService = class {
|
|
|
1186
1253
|
* @example
|
|
1187
1254
|
*/
|
|
1188
1255
|
async create(options = {}) {
|
|
1189
|
-
const { user, isSandbox = false, withRecommendation = true, title, image, planId, quota, trial } = options;
|
|
1256
|
+
const { user, isSandbox = false, withRecommendation = true, title, image, planId, quota, trial, licenseId } = options;
|
|
1190
1257
|
const builder = new Checkout(idToString(this.productId), this.publicKey, this.secretKey);
|
|
1191
1258
|
if (user) builder.setUser(user, true);
|
|
1192
1259
|
if (withRecommendation) builder.setRecommendations();
|
|
1193
|
-
if (isSandbox) builder.setSandbox();
|
|
1260
|
+
if (isSandbox) builder.setSandbox(await this.getSandboxParams());
|
|
1194
1261
|
if (title) builder.setTitle(title);
|
|
1195
1262
|
if (image) builder.setImage(image);
|
|
1196
1263
|
if (planId) builder.setPlan(planId);
|
|
1197
1264
|
if (quota) builder.setQuota(quota);
|
|
1198
1265
|
if (trial) builder.setTrial(trial);
|
|
1266
|
+
if (licenseId) {
|
|
1267
|
+
const authorization = await this.getLicenseUpgradeAuth(licenseId);
|
|
1268
|
+
builder.setLicenseUpgradeByAuth(authorization);
|
|
1269
|
+
}
|
|
1199
1270
|
return builder;
|
|
1200
1271
|
}
|
|
1201
1272
|
/**
|
|
@@ -1204,12 +1275,28 @@ var CheckoutService = class {
|
|
|
1204
1275
|
* This shouldn't be used in production, but is useful for testing purposes.
|
|
1205
1276
|
*
|
|
1206
1277
|
* @note This is intentionally set as `async` because we would use the API in the future to generate more fine grained sandbox params (for example for a specific email address only).
|
|
1207
|
-
*
|
|
1208
|
-
* @todo - This has a duplication with the `inSandbox` method in the builder. Consider refactoring to avoid this duplication.
|
|
1209
|
-
* Also think about whether we should make the builder's `inSandbox` method async as well.
|
|
1210
1278
|
*/
|
|
1211
1279
|
async getSandboxParams() {
|
|
1212
|
-
|
|
1280
|
+
const productId = idToString(this.productId);
|
|
1281
|
+
const timestamp = Math.floor(Date.now() / 1e3).toString();
|
|
1282
|
+
const token = `${timestamp}${productId}${this.secretKey}${this.publicKey}checkout`;
|
|
1283
|
+
return {
|
|
1284
|
+
ctx: timestamp,
|
|
1285
|
+
token: createHash("md5").update(token).digest("hex")
|
|
1286
|
+
};
|
|
1287
|
+
}
|
|
1288
|
+
/**
|
|
1289
|
+
* Retrieves the license upgrade authorization for a given license ID.
|
|
1290
|
+
*
|
|
1291
|
+
* This is used to authorize a license upgrade during the checkout process. Useful when creating upgrade links for existing users.
|
|
1292
|
+
*/
|
|
1293
|
+
async getLicenseUpgradeAuth(licenseId) {
|
|
1294
|
+
const auth = await this.api.license.retrieveCheckoutUpgradeAuthorization(licenseId);
|
|
1295
|
+
if (!auth) throw new Error("Failed to retrieve license upgrade authorization");
|
|
1296
|
+
return {
|
|
1297
|
+
licenseId,
|
|
1298
|
+
authorization: auth
|
|
1299
|
+
};
|
|
1213
1300
|
}
|
|
1214
1301
|
/**
|
|
1215
1302
|
* Processes a redirect URL and returns the checkout redirect information if valid.
|
|
@@ -1841,11 +1928,16 @@ var CustomerPortalService = class {
|
|
|
1841
1928
|
//#endregion
|
|
1842
1929
|
//#region src/webhook/WebhookListener.ts
|
|
1843
1930
|
const SIGNATURE_HEADER = "x-signature";
|
|
1931
|
+
const DEFAULT_ERROR_HANDLER = async (error) => {
|
|
1932
|
+
console.error("Webhook processing error:", error);
|
|
1933
|
+
};
|
|
1844
1934
|
var WebhookListener = class {
|
|
1845
1935
|
eventHandlers = /* @__PURE__ */ new Map();
|
|
1846
|
-
constructor(secretKey, onError =
|
|
1936
|
+
constructor(api, secretKey, onError = DEFAULT_ERROR_HANDLER, authenticationMethod = WebhookAuthenticationMethod.SignatureHeader) {
|
|
1937
|
+
this.api = api;
|
|
1847
1938
|
this.secretKey = secretKey;
|
|
1848
1939
|
this.onError = onError;
|
|
1940
|
+
this.authenticationMethod = authenticationMethod;
|
|
1849
1941
|
}
|
|
1850
1942
|
on(typeOrTypes, handler) {
|
|
1851
1943
|
const types = Array.isArray(typeOrTypes) ? typeOrTypes : [typeOrTypes];
|
|
@@ -1913,40 +2005,52 @@ var WebhookListener = class {
|
|
|
1913
2005
|
* Returns an object you can map to your framework's response easily.
|
|
1914
2006
|
*/
|
|
1915
2007
|
async process(input) {
|
|
1916
|
-
const sig = this.getHeader(SIGNATURE_HEADER, input.headers);
|
|
1917
|
-
if (!this.verifySignature(input.rawBody, sig)) return {
|
|
1918
|
-
status: 401,
|
|
1919
|
-
success: false,
|
|
1920
|
-
error: "Invalid signature"
|
|
1921
|
-
};
|
|
1922
|
-
let evt;
|
|
1923
2008
|
try {
|
|
1924
|
-
const
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
error: "Invalid payload"
|
|
1929
|
-
};
|
|
1930
|
-
evt = parsed;
|
|
1931
|
-
} catch {
|
|
2009
|
+
const event = this.authenticationMethod === WebhookAuthenticationMethod.SignatureHeader ? await this.authenticateAndGetEventFromInput(input) : await this.authenticateAndGetEventFromApi(input);
|
|
2010
|
+
return this.processEvent(event);
|
|
2011
|
+
} catch (error) {
|
|
2012
|
+
if (error instanceof WebhookError) return error.toResponse();
|
|
1932
2013
|
return {
|
|
1933
|
-
status:
|
|
2014
|
+
status: 500,
|
|
1934
2015
|
success: false,
|
|
1935
|
-
error: "
|
|
2016
|
+
error: "Internal Server Error"
|
|
1936
2017
|
};
|
|
1937
2018
|
}
|
|
1938
|
-
|
|
2019
|
+
}
|
|
2020
|
+
async authenticateAndGetEventFromInput(input) {
|
|
2021
|
+
const sig = this.getHeader(SIGNATURE_HEADER, input.headers);
|
|
2022
|
+
if (!this.verifySignature(input.rawBody, sig)) throw new WebhookError("Invalid signature", 401);
|
|
2023
|
+
return this.parseEventFromInput(input);
|
|
2024
|
+
}
|
|
2025
|
+
async authenticateAndGetEventFromApi(input) {
|
|
2026
|
+
const jsonPayload = this.parseEventFromInput(input);
|
|
2027
|
+
if (!jsonPayload.id) throw new WebhookError("Invalid payload");
|
|
2028
|
+
const event = await this.api.event.retrieve(jsonPayload.id);
|
|
2029
|
+
if (!event) throw new WebhookError("Event not found", 404);
|
|
2030
|
+
return event;
|
|
2031
|
+
}
|
|
2032
|
+
parseEventFromInput(input) {
|
|
2033
|
+
try {
|
|
2034
|
+
const parsed = JSON.parse(typeof input.rawBody === "string" ? input.rawBody : input.rawBody.toString("utf8"));
|
|
2035
|
+
if (!parsed || typeof parsed.type !== "string") throw new WebhookError("Invalid payload");
|
|
2036
|
+
return parsed;
|
|
2037
|
+
} catch {
|
|
2038
|
+
throw new WebhookError("Malformed JSON");
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
async processEvent(event) {
|
|
2042
|
+
const eventType = event.type;
|
|
1939
2043
|
const eventHandlers = this.eventHandlers.get(eventType);
|
|
1940
2044
|
if (!eventHandlers || eventHandlers.size === 0) console.warn(`No handlers registered for event type: ${eventType}`);
|
|
1941
2045
|
try {
|
|
1942
2046
|
const promises = Array.from(eventHandlers || []).map((handler) => {
|
|
1943
2047
|
const typedHandler = handler;
|
|
1944
|
-
const typedEvent =
|
|
2048
|
+
const typedEvent = event;
|
|
1945
2049
|
return typedHandler(typedEvent);
|
|
1946
2050
|
});
|
|
1947
2051
|
await Promise.all(promises);
|
|
1948
2052
|
} catch (error) {
|
|
1949
|
-
this.onError?.(error);
|
|
2053
|
+
await this.onError?.(error);
|
|
1950
2054
|
return {
|
|
1951
2055
|
status: 500,
|
|
1952
2056
|
success: false,
|
|
@@ -1970,11 +2074,13 @@ var WebhookListener = class {
|
|
|
1970
2074
|
//#endregion
|
|
1971
2075
|
//#region src/services/WebhookService.ts
|
|
1972
2076
|
var WebhookService = class {
|
|
1973
|
-
constructor(secretKey) {
|
|
2077
|
+
constructor(api, secretKey) {
|
|
2078
|
+
this.api = api;
|
|
1974
2079
|
this.secretKey = secretKey;
|
|
1975
2080
|
}
|
|
1976
|
-
createListener(
|
|
1977
|
-
|
|
2081
|
+
createListener(config = {}) {
|
|
2082
|
+
const { onError, authenticationMethod } = config;
|
|
2083
|
+
return new WebhookListener(this.api, this.secretKey, onError, authenticationMethod);
|
|
1978
2084
|
}
|
|
1979
2085
|
createRequestProcessor(listener) {
|
|
1980
2086
|
return (request) => this.processFetch(listener, request);
|
|
@@ -2476,12 +2582,12 @@ var Freemius = class {
|
|
|
2476
2582
|
this.auth = new AuthService(productId, secretKey);
|
|
2477
2583
|
this.pricing = new PricingService(this.api);
|
|
2478
2584
|
this.purchase = new PurchaseService(this.api);
|
|
2479
|
-
this.checkout = new CheckoutService(productId, publicKey, secretKey, this.purchase, this.pricing);
|
|
2585
|
+
this.checkout = new CheckoutService(this.api, productId, publicKey, secretKey, this.purchase, this.pricing);
|
|
2480
2586
|
this.customerPortal = new CustomerPortalService(this.api, this.checkout, this.auth, this.purchase);
|
|
2481
|
-
this.webhook = new WebhookService(secretKey);
|
|
2587
|
+
this.webhook = new WebhookService(this.api, secretKey);
|
|
2482
2588
|
}
|
|
2483
2589
|
};
|
|
2484
2590
|
|
|
2485
2591
|
//#endregion
|
|
2486
|
-
export { BILLING_CYCLE, CURRENCY, Freemius, idToNumber, idToString, isIdsEqual, parseBillingCycle, parseCurrency, parseDate, parseDateTime, parseNumber, parsePaymentMethod };
|
|
2592
|
+
export { ActionError, BILLING_CYCLE, CURRENCY, Freemius, WebhookAuthenticationMethod, WebhookError, idToNumber, idToString, isIdsEqual, parseBillingCycle, parseCurrency, parseDate, parseDateTime, parseNumber, parsePaymentMethod };
|
|
2487
2593
|
//# sourceMappingURL=index.mjs.map
|