@freemius/sdk 0.0.6 → 0.1.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 +153 -63
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +153 -63
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +212 -119
- package/dist/index.mjs +210 -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.1.0";
|
|
92
151
|
|
|
93
152
|
//#endregion
|
|
94
153
|
//#region src/api/client.ts
|
|
@@ -436,6 +495,30 @@ var Payment = class extends ApiBase {
|
|
|
436
495
|
}
|
|
437
496
|
};
|
|
438
497
|
|
|
498
|
+
//#endregion
|
|
499
|
+
//#region src/api/WebhookEvent.ts
|
|
500
|
+
var WebhookEvent = class extends ApiBase {
|
|
501
|
+
async retrieve(eventId) {
|
|
502
|
+
const result = await this.client.GET(`/products/{product_id}/events/{event_id}.json`, { params: { path: {
|
|
503
|
+
product_id: this.productId,
|
|
504
|
+
event_id: this.getIdForPath(eventId)
|
|
505
|
+
} } });
|
|
506
|
+
if (!this.isGoodResponse(result.response) || !result.data || !result.data.id) return null;
|
|
507
|
+
return result.data;
|
|
508
|
+
}
|
|
509
|
+
async retrieveMany(filter, pagination) {
|
|
510
|
+
const result = await this.client.GET(`/products/{product_id}/events.json`, { params: {
|
|
511
|
+
path: { product_id: this.productId },
|
|
512
|
+
query: {
|
|
513
|
+
...this.getPagingParams(pagination),
|
|
514
|
+
...filter ?? {}
|
|
515
|
+
}
|
|
516
|
+
} });
|
|
517
|
+
if (!this.isGoodResponse(result.response) || !result.data || !Array.isArray(result.data.events)) return [];
|
|
518
|
+
return result.data.events;
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
|
|
439
522
|
//#endregion
|
|
440
523
|
//#region src/utils/ops.ts
|
|
441
524
|
function splitName(name) {
|
|
@@ -464,6 +547,7 @@ var ApiService = class {
|
|
|
464
547
|
product;
|
|
465
548
|
subscription;
|
|
466
549
|
payment;
|
|
550
|
+
event;
|
|
467
551
|
baseUrl;
|
|
468
552
|
constructor(productId, apiKey, secretKey, publicKey) {
|
|
469
553
|
this.secretKey = secretKey;
|
|
@@ -476,6 +560,7 @@ var ApiService = class {
|
|
|
476
560
|
this.product = new Product(this.productId, this.client);
|
|
477
561
|
this.subscription = new Subscription(this.productId, this.client);
|
|
478
562
|
this.payment = new Payment(this.productId, this.client);
|
|
563
|
+
this.event = new WebhookEvent(this.productId, this.client);
|
|
479
564
|
}
|
|
480
565
|
/**
|
|
481
566
|
* Low level API client for direct access to the Freemius API.
|
|
@@ -556,15 +641,7 @@ var ApiService = class {
|
|
|
556
641
|
* Every method returns the existing instance of the builder for chainability,
|
|
557
642
|
* The final `getOptions()` method returns the constructed `CheckoutOptions` object.
|
|
558
643
|
*/
|
|
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
|
-
}
|
|
644
|
+
var Checkout = class {
|
|
568
645
|
options;
|
|
569
646
|
constructor(productId, publicKey, secretKey) {
|
|
570
647
|
this.productId = productId;
|
|
@@ -575,12 +652,11 @@ var Checkout = class Checkout {
|
|
|
575
652
|
/**
|
|
576
653
|
* Enables sandbox mode for testing purposes.
|
|
577
654
|
*
|
|
578
|
-
* @returns A new builder instance with sandbox configuration
|
|
579
655
|
*/
|
|
580
|
-
setSandbox() {
|
|
656
|
+
setSandbox(sandbox) {
|
|
581
657
|
this.options = {
|
|
582
658
|
...this.options,
|
|
583
|
-
sandbox
|
|
659
|
+
sandbox
|
|
584
660
|
};
|
|
585
661
|
return this;
|
|
586
662
|
}
|
|
@@ -590,7 +666,6 @@ var Checkout = class Checkout {
|
|
|
590
666
|
* @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
667
|
* @param readonly If true, the user information will be read-only in the checkout session.
|
|
592
668
|
*
|
|
593
|
-
* @returns A new builder instance with user configuration
|
|
594
669
|
*/
|
|
595
670
|
setUser(user, readonly = true) {
|
|
596
671
|
if (!user) return this;
|
|
@@ -614,7 +689,6 @@ var Checkout = class Checkout {
|
|
|
614
689
|
* Applies recommended UI settings for better user experience.
|
|
615
690
|
* This includes fullscreen mode, upsells, refund badge, and reviews display.
|
|
616
691
|
*
|
|
617
|
-
* @returns A new builder instance with recommended UI settings
|
|
618
692
|
*/
|
|
619
693
|
setRecommendations() {
|
|
620
694
|
this.options = {
|
|
@@ -631,7 +705,6 @@ var Checkout = class Checkout {
|
|
|
631
705
|
* Sets the plan ID for the checkout.
|
|
632
706
|
*
|
|
633
707
|
* @param planId The plan ID to purchase
|
|
634
|
-
* @returns A new builder instance with plan ID set
|
|
635
708
|
*/
|
|
636
709
|
setPlan(planId) {
|
|
637
710
|
this.options = {
|
|
@@ -644,7 +717,6 @@ var Checkout = class Checkout {
|
|
|
644
717
|
* Sets the number of licenses to purchase.
|
|
645
718
|
*
|
|
646
719
|
* @param count Number of licenses
|
|
647
|
-
* @returns A new builder instance with license count set
|
|
648
720
|
*/
|
|
649
721
|
setQuota(count) {
|
|
650
722
|
this.options = {
|
|
@@ -672,7 +744,6 @@ var Checkout = class Checkout {
|
|
|
672
744
|
*
|
|
673
745
|
* @param coupon The coupon code to apply
|
|
674
746
|
* @param hideUI Whether to hide the coupon input field from users
|
|
675
|
-
* @returns A new builder instance with coupon configuration
|
|
676
747
|
*/
|
|
677
748
|
setCoupon(options) {
|
|
678
749
|
const { code: coupon, hideUI = false } = options;
|
|
@@ -687,7 +758,6 @@ var Checkout = class Checkout {
|
|
|
687
758
|
* Enables trial mode for the checkout.
|
|
688
759
|
*
|
|
689
760
|
* @param mode Trial type - true/false for plan default, or specific 'free'/'paid' mode
|
|
690
|
-
* @returns A new builder instance with trial configuration
|
|
691
761
|
*/
|
|
692
762
|
setTrial(mode = true) {
|
|
693
763
|
this.options = {
|
|
@@ -700,7 +770,6 @@ var Checkout = class Checkout {
|
|
|
700
770
|
* Configures the visual layout and appearance of the checkout.
|
|
701
771
|
*
|
|
702
772
|
* @param options Appearance configuration options
|
|
703
|
-
* @returns A new builder instance with appearance configuration
|
|
704
773
|
*/
|
|
705
774
|
setAppearance(options) {
|
|
706
775
|
this.options = { ...this.options };
|
|
@@ -715,7 +784,6 @@ var Checkout = class Checkout {
|
|
|
715
784
|
* Configures discount display settings.
|
|
716
785
|
*
|
|
717
786
|
* @param options Discount configuration options
|
|
718
|
-
* @returns A new builder instance with discount configuration
|
|
719
787
|
*/
|
|
720
788
|
setDiscounts(options) {
|
|
721
789
|
this.options = { ...this.options };
|
|
@@ -730,7 +798,6 @@ var Checkout = class Checkout {
|
|
|
730
798
|
*
|
|
731
799
|
* @param selector Type of billing cycle selector to show
|
|
732
800
|
* @param defaultCycle Default billing cycle to select
|
|
733
|
-
* @returns A new builder instance with billing cycle configuration
|
|
734
801
|
*/
|
|
735
802
|
setBillingCycle(defaultCycle, selector) {
|
|
736
803
|
this.options = { ...this.options };
|
|
@@ -742,7 +809,6 @@ var Checkout = class Checkout {
|
|
|
742
809
|
* Sets the language/locale for the checkout.
|
|
743
810
|
*
|
|
744
811
|
* @param locale Language setting - 'auto', 'auto-beta', or specific locale like 'en_US'
|
|
745
|
-
* @returns A new builder instance with locale configuration
|
|
746
812
|
*/
|
|
747
813
|
setLanguage(locale = "auto") {
|
|
748
814
|
this.options = {
|
|
@@ -755,7 +821,6 @@ var Checkout = class Checkout {
|
|
|
755
821
|
* Configures review and badge display settings.
|
|
756
822
|
*
|
|
757
823
|
* @param options Review and badge configuration
|
|
758
|
-
* @returns A new builder instance with reviews and badges configuration
|
|
759
824
|
*/
|
|
760
825
|
setSocialProofing(options) {
|
|
761
826
|
this.options = { ...this.options };
|
|
@@ -771,7 +836,6 @@ var Checkout = class Checkout {
|
|
|
771
836
|
* @param currency Primary currency or 'auto' for automatic detection
|
|
772
837
|
* @param defaultCurrency Default currency when using 'auto'
|
|
773
838
|
* @param showInlineSelector Whether to show inline currency selector
|
|
774
|
-
* @returns A new builder instance with currency configuration
|
|
775
839
|
*/
|
|
776
840
|
setCurrency(currency, defaultCurrency = "usd", showInlineSelector = true) {
|
|
777
841
|
this.options = {
|
|
@@ -787,7 +851,6 @@ var Checkout = class Checkout {
|
|
|
787
851
|
*
|
|
788
852
|
* @param cancelUrl URL for back button when in page mode
|
|
789
853
|
* @param cancelIcon Custom cancel icon URL
|
|
790
|
-
* @returns A new builder instance with navigation configuration
|
|
791
854
|
*/
|
|
792
855
|
setCancelButton(cancelUrl, cancelIcon) {
|
|
793
856
|
this.options = { ...this.options };
|
|
@@ -799,7 +862,6 @@ var Checkout = class Checkout {
|
|
|
799
862
|
* Associates purchases with an affiliate account.
|
|
800
863
|
*
|
|
801
864
|
* @param userId Affiliate user ID
|
|
802
|
-
* @returns A new builder instance with affiliate configuration
|
|
803
865
|
*/
|
|
804
866
|
setAffiliate(userId) {
|
|
805
867
|
this.options = {
|
|
@@ -812,7 +874,6 @@ var Checkout = class Checkout {
|
|
|
812
874
|
* Sets a custom image/icon for the checkout.
|
|
813
875
|
*
|
|
814
876
|
* @param imageUrl Secure HTTPS URL to the image
|
|
815
|
-
* @returns A new builder instance with custom image
|
|
816
877
|
*/
|
|
817
878
|
setImage(imageUrl) {
|
|
818
879
|
this.options = {
|
|
@@ -822,12 +883,13 @@ var Checkout = class Checkout {
|
|
|
822
883
|
return this;
|
|
823
884
|
}
|
|
824
885
|
/**
|
|
825
|
-
* Configures the checkout for license renewal.
|
|
886
|
+
* Configures the checkout for license renewal or upgrade by the license key.
|
|
887
|
+
*
|
|
888
|
+
* @note - This is less secure since it exposes the license key to the client. Use only in authenticated contexts.
|
|
826
889
|
*
|
|
827
890
|
* @param licenseKey The license key to renew
|
|
828
|
-
* @returns A new builder instance configured for renewal
|
|
829
891
|
*/
|
|
830
|
-
|
|
892
|
+
setLicenseUpgradeByKey(licenseKey) {
|
|
831
893
|
this.options = {
|
|
832
894
|
...this.options,
|
|
833
895
|
license_key: licenseKey
|
|
@@ -835,9 +897,20 @@ var Checkout = class Checkout {
|
|
|
835
897
|
return this;
|
|
836
898
|
}
|
|
837
899
|
/**
|
|
838
|
-
*
|
|
900
|
+
* Configures the checkout for license upgrade using an authorization token.
|
|
839
901
|
*
|
|
840
|
-
* @
|
|
902
|
+
* @param params The license upgrade authorization parameters
|
|
903
|
+
*/
|
|
904
|
+
setLicenseUpgradeByAuth(params) {
|
|
905
|
+
this.options = {
|
|
906
|
+
...this.options,
|
|
907
|
+
license_id: params.licenseId,
|
|
908
|
+
authorization: params.authorization
|
|
909
|
+
};
|
|
910
|
+
return this;
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* Builds and returns the final checkout options to be used with the `@freemius/checkout` package.
|
|
841
914
|
*
|
|
842
915
|
* @returns The constructed CheckoutOptions object
|
|
843
916
|
*/
|
|
@@ -846,8 +919,6 @@ var Checkout = class Checkout {
|
|
|
846
919
|
}
|
|
847
920
|
/**
|
|
848
921
|
* 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
922
|
*/
|
|
852
923
|
getLink() {
|
|
853
924
|
const checkoutOptions = convertCheckoutOptionsToQueryParams(this.options);
|
|
@@ -868,39 +939,6 @@ var Checkout = class Checkout {
|
|
|
868
939
|
}
|
|
869
940
|
};
|
|
870
941
|
|
|
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
942
|
//#endregion
|
|
905
943
|
//#region src/checkout/PricingRetriever.ts
|
|
906
944
|
var PricingRetriever = class {
|
|
@@ -933,7 +971,10 @@ var PurchaseProcessor = class {
|
|
|
933
971
|
return request.method === "POST" && action === "process_purchase";
|
|
934
972
|
}
|
|
935
973
|
async processAction(request) {
|
|
936
|
-
const purchaseSchema = zod.object({
|
|
974
|
+
const purchaseSchema = zod.object({
|
|
975
|
+
purchase: zod.object({ license_id: zod.string() }).optional(),
|
|
976
|
+
trial: zod.object({ license_id: zod.string() }).optional()
|
|
977
|
+
});
|
|
937
978
|
const contentType = request.headers.get("content-type");
|
|
938
979
|
if (!contentType || !contentType.includes("application/json")) throw ActionError.badRequest("Invalid content type. Expected application/json");
|
|
939
980
|
let requestBody;
|
|
@@ -944,7 +985,8 @@ var PurchaseProcessor = class {
|
|
|
944
985
|
}
|
|
945
986
|
const parseResult = purchaseSchema.safeParse(requestBody);
|
|
946
987
|
if (!parseResult.success) throw ActionError.validationFailed("Invalid request body format", parseResult.error.issues);
|
|
947
|
-
const
|
|
988
|
+
const licenseId = parseResult.data.purchase?.license_id ?? parseResult.data.trial?.license_id;
|
|
989
|
+
if (!licenseId) throw ActionError.badRequest("License ID is required in the request body, either from purchase or from trial.");
|
|
948
990
|
const purchase = await this.purchase.retrievePurchase(licenseId);
|
|
949
991
|
if (!purchase) throw ActionError.notFound("No purchase data found for the provided license ID");
|
|
950
992
|
if (this.callback) {
|
|
@@ -962,11 +1004,13 @@ var CheckoutRedirectInfo = class {
|
|
|
962
1004
|
plan_id;
|
|
963
1005
|
email;
|
|
964
1006
|
pricing_id;
|
|
965
|
-
|
|
1007
|
+
action;
|
|
966
1008
|
license_id;
|
|
967
1009
|
expiration;
|
|
968
1010
|
quota;
|
|
969
|
-
|
|
1011
|
+
trial;
|
|
1012
|
+
trial_ends_at;
|
|
1013
|
+
currency;
|
|
970
1014
|
amount;
|
|
971
1015
|
tax;
|
|
972
1016
|
type;
|
|
@@ -978,17 +1022,19 @@ var CheckoutRedirectInfo = class {
|
|
|
978
1022
|
this.plan_id = idToString(data.plan_id);
|
|
979
1023
|
this.email = data.email;
|
|
980
1024
|
this.pricing_id = idToString(data.pricing_id);
|
|
981
|
-
this.
|
|
1025
|
+
this.action = data.action ? data.action : "purchase";
|
|
982
1026
|
this.license_id = idToString(data.license_id);
|
|
983
1027
|
this.expiration = data.expiration ? parseDateTime(data.expiration) : null;
|
|
984
1028
|
this.quota = data.quota ? parseNumber(data.quota) : null;
|
|
985
|
-
this.
|
|
1029
|
+
this.trial = data.trial ? data.trial : null;
|
|
1030
|
+
this.trial_ends_at = data.trial_ends_at ? parseDateTime(data.trial_ends_at) : null;
|
|
1031
|
+
this.currency = data.currency ? parseCurrency(data.currency) : CURRENCY.USD;
|
|
986
1032
|
this.amount = parseNumber(data.amount);
|
|
987
1033
|
this.tax = parseNumber(data.tax);
|
|
988
1034
|
this.subscription_id = data.subscription_id ? idToString(data.subscription_id) : null;
|
|
989
1035
|
this.billing_cycle = data.billing_cycle ? parseBillingCycle(data.billing_cycle) : null;
|
|
990
1036
|
this.payment_id = data.payment_id ? idToString(data.payment_id) : null;
|
|
991
|
-
this.type = this.subscription_id ? "subscription" : "one-off";
|
|
1037
|
+
this.type = this.subscription_id ? "subscription" : this.payment_id ? "one-off" : null;
|
|
992
1038
|
}
|
|
993
1039
|
isSubscription() {
|
|
994
1040
|
return this.type === "subscription";
|
|
@@ -999,17 +1045,19 @@ var CheckoutRedirectInfo = class {
|
|
|
999
1045
|
plan_id: this.plan_id,
|
|
1000
1046
|
email: this.email,
|
|
1001
1047
|
pricing_id: this.pricing_id,
|
|
1002
|
-
currency: this.currency,
|
|
1003
1048
|
license_id: this.license_id,
|
|
1004
1049
|
expiration: this.expiration,
|
|
1005
1050
|
quota: this.quota,
|
|
1051
|
+
trial: this.trial,
|
|
1052
|
+
trial_ends_at: this.trial_ends_at,
|
|
1053
|
+
currency: this.currency,
|
|
1006
1054
|
action: this.action,
|
|
1007
1055
|
amount: this.amount,
|
|
1008
1056
|
tax: this.tax,
|
|
1009
|
-
type: this.type,
|
|
1010
1057
|
subscription_id: this.subscription_id,
|
|
1011
1058
|
billing_cycle: this.billing_cycle,
|
|
1012
|
-
payment_id: this.payment_id
|
|
1059
|
+
payment_id: this.payment_id,
|
|
1060
|
+
type: this.type
|
|
1013
1061
|
};
|
|
1014
1062
|
}
|
|
1015
1063
|
};
|
|
@@ -1053,11 +1101,16 @@ var RedirectProcessor = class {
|
|
|
1053
1101
|
}
|
|
1054
1102
|
const cleanUrl = this.getCleanUrl(url.href);
|
|
1055
1103
|
const calculatedSignature = createHmac("sha256", this.secretKey).update(cleanUrl).digest("hex");
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1104
|
+
try {
|
|
1105
|
+
const result = timingSafeEqual(Buffer.from(calculatedSignature), Buffer.from(signature));
|
|
1106
|
+
if (!result) return null;
|
|
1107
|
+
const params = Object.fromEntries(url.searchParams.entries());
|
|
1108
|
+
if (!params.user_id || !params.plan_id || !params.pricing_id || !params.email) return null;
|
|
1109
|
+
return new CheckoutRedirectInfo(params);
|
|
1110
|
+
} catch (e) {
|
|
1111
|
+
console.error("Error getting redirection information:", e);
|
|
1112
|
+
return null;
|
|
1113
|
+
}
|
|
1061
1114
|
}
|
|
1062
1115
|
getCleanUrl(url) {
|
|
1063
1116
|
const signatureParam = "&signature=";
|
|
@@ -1081,9 +1134,6 @@ var CheckoutRequestProcessor = class {
|
|
|
1081
1134
|
return (request) => this.process(config, request);
|
|
1082
1135
|
}
|
|
1083
1136
|
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
1137
|
const actionHandlers = [
|
|
1088
1138
|
this.getPricingRetriever(),
|
|
1089
1139
|
this.getRedirectProcessor({
|
|
@@ -1145,7 +1195,8 @@ var CheckoutRequestProcessor = class {
|
|
|
1145
1195
|
//#region src/services/CheckoutService.ts
|
|
1146
1196
|
var CheckoutService = class {
|
|
1147
1197
|
request;
|
|
1148
|
-
constructor(productId, publicKey, secretKey, purchase, pricing) {
|
|
1198
|
+
constructor(api, productId, publicKey, secretKey, purchase, pricing) {
|
|
1199
|
+
this.api = api;
|
|
1149
1200
|
this.productId = productId;
|
|
1150
1201
|
this.publicKey = publicKey;
|
|
1151
1202
|
this.secretKey = secretKey;
|
|
@@ -1186,16 +1237,20 @@ var CheckoutService = class {
|
|
|
1186
1237
|
* @example
|
|
1187
1238
|
*/
|
|
1188
1239
|
async create(options = {}) {
|
|
1189
|
-
const { user, isSandbox = false, withRecommendation = true, title, image, planId, quota, trial } = options;
|
|
1240
|
+
const { user, isSandbox = false, withRecommendation = true, title, image, planId, quota, trial, licenseId } = options;
|
|
1190
1241
|
const builder = new Checkout(idToString(this.productId), this.publicKey, this.secretKey);
|
|
1191
1242
|
if (user) builder.setUser(user, true);
|
|
1192
1243
|
if (withRecommendation) builder.setRecommendations();
|
|
1193
|
-
if (isSandbox) builder.setSandbox();
|
|
1244
|
+
if (isSandbox) builder.setSandbox(await this.getSandboxParams());
|
|
1194
1245
|
if (title) builder.setTitle(title);
|
|
1195
1246
|
if (image) builder.setImage(image);
|
|
1196
1247
|
if (planId) builder.setPlan(planId);
|
|
1197
1248
|
if (quota) builder.setQuota(quota);
|
|
1198
1249
|
if (trial) builder.setTrial(trial);
|
|
1250
|
+
if (licenseId) {
|
|
1251
|
+
const authorization = await this.getLicenseUpgradeAuth(licenseId);
|
|
1252
|
+
builder.setLicenseUpgradeByAuth(authorization);
|
|
1253
|
+
}
|
|
1199
1254
|
return builder;
|
|
1200
1255
|
}
|
|
1201
1256
|
/**
|
|
@@ -1204,12 +1259,28 @@ var CheckoutService = class {
|
|
|
1204
1259
|
* This shouldn't be used in production, but is useful for testing purposes.
|
|
1205
1260
|
*
|
|
1206
1261
|
* @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
1262
|
*/
|
|
1211
1263
|
async getSandboxParams() {
|
|
1212
|
-
|
|
1264
|
+
const productId = idToString(this.productId);
|
|
1265
|
+
const timestamp = Math.floor(Date.now() / 1e3).toString();
|
|
1266
|
+
const token = `${timestamp}${productId}${this.secretKey}${this.publicKey}checkout`;
|
|
1267
|
+
return {
|
|
1268
|
+
ctx: timestamp,
|
|
1269
|
+
token: createHash("md5").update(token).digest("hex")
|
|
1270
|
+
};
|
|
1271
|
+
}
|
|
1272
|
+
/**
|
|
1273
|
+
* Retrieves the license upgrade authorization for a given license ID.
|
|
1274
|
+
*
|
|
1275
|
+
* This is used to authorize a license upgrade during the checkout process. Useful when creating upgrade links for existing users.
|
|
1276
|
+
*/
|
|
1277
|
+
async getLicenseUpgradeAuth(licenseId) {
|
|
1278
|
+
const auth = await this.api.license.retrieveCheckoutUpgradeAuthorization(licenseId);
|
|
1279
|
+
if (!auth) throw new Error("Failed to retrieve license upgrade authorization");
|
|
1280
|
+
return {
|
|
1281
|
+
licenseId,
|
|
1282
|
+
authorization: auth
|
|
1283
|
+
};
|
|
1213
1284
|
}
|
|
1214
1285
|
/**
|
|
1215
1286
|
* Processes a redirect URL and returns the checkout redirect information if valid.
|
|
@@ -1841,11 +1912,16 @@ var CustomerPortalService = class {
|
|
|
1841
1912
|
//#endregion
|
|
1842
1913
|
//#region src/webhook/WebhookListener.ts
|
|
1843
1914
|
const SIGNATURE_HEADER = "x-signature";
|
|
1915
|
+
const DEFAULT_ERROR_HANDLER = async (error) => {
|
|
1916
|
+
console.error("Webhook processing error:", error);
|
|
1917
|
+
};
|
|
1844
1918
|
var WebhookListener = class {
|
|
1845
1919
|
eventHandlers = /* @__PURE__ */ new Map();
|
|
1846
|
-
constructor(secretKey, onError =
|
|
1920
|
+
constructor(api, secretKey, onError = DEFAULT_ERROR_HANDLER, authenticationMethod = WebhookAuthenticationMethod.SignatureHeader) {
|
|
1921
|
+
this.api = api;
|
|
1847
1922
|
this.secretKey = secretKey;
|
|
1848
1923
|
this.onError = onError;
|
|
1924
|
+
this.authenticationMethod = authenticationMethod;
|
|
1849
1925
|
}
|
|
1850
1926
|
on(typeOrTypes, handler) {
|
|
1851
1927
|
const types = Array.isArray(typeOrTypes) ? typeOrTypes : [typeOrTypes];
|
|
@@ -1913,40 +1989,52 @@ var WebhookListener = class {
|
|
|
1913
1989
|
* Returns an object you can map to your framework's response easily.
|
|
1914
1990
|
*/
|
|
1915
1991
|
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
1992
|
try {
|
|
1924
|
-
const
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
error: "Invalid payload"
|
|
1929
|
-
};
|
|
1930
|
-
evt = parsed;
|
|
1931
|
-
} catch {
|
|
1993
|
+
const event = this.authenticationMethod === WebhookAuthenticationMethod.SignatureHeader ? await this.authenticateAndGetEventFromInput(input) : await this.authenticateAndGetEventFromApi(input);
|
|
1994
|
+
return this.processEvent(event);
|
|
1995
|
+
} catch (error) {
|
|
1996
|
+
if (error instanceof WebhookError) return error.toResponse();
|
|
1932
1997
|
return {
|
|
1933
|
-
status:
|
|
1998
|
+
status: 500,
|
|
1934
1999
|
success: false,
|
|
1935
|
-
error: "
|
|
2000
|
+
error: "Internal Server Error"
|
|
1936
2001
|
};
|
|
1937
2002
|
}
|
|
1938
|
-
|
|
2003
|
+
}
|
|
2004
|
+
async authenticateAndGetEventFromInput(input) {
|
|
2005
|
+
const sig = this.getHeader(SIGNATURE_HEADER, input.headers);
|
|
2006
|
+
if (!this.verifySignature(input.rawBody, sig)) throw new WebhookError("Invalid signature", 401);
|
|
2007
|
+
return this.parseEventFromInput(input);
|
|
2008
|
+
}
|
|
2009
|
+
async authenticateAndGetEventFromApi(input) {
|
|
2010
|
+
const jsonPayload = this.parseEventFromInput(input);
|
|
2011
|
+
if (!jsonPayload.id) throw new WebhookError("Invalid payload");
|
|
2012
|
+
const event = await this.api.event.retrieve(jsonPayload.id);
|
|
2013
|
+
if (!event) throw new WebhookError("Event not found", 404);
|
|
2014
|
+
return event;
|
|
2015
|
+
}
|
|
2016
|
+
parseEventFromInput(input) {
|
|
2017
|
+
try {
|
|
2018
|
+
const parsed = JSON.parse(typeof input.rawBody === "string" ? input.rawBody : input.rawBody.toString("utf8"));
|
|
2019
|
+
if (!parsed || typeof parsed.type !== "string") throw new WebhookError("Invalid payload");
|
|
2020
|
+
return parsed;
|
|
2021
|
+
} catch {
|
|
2022
|
+
throw new WebhookError("Malformed JSON");
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
async processEvent(event) {
|
|
2026
|
+
const eventType = event.type;
|
|
1939
2027
|
const eventHandlers = this.eventHandlers.get(eventType);
|
|
1940
2028
|
if (!eventHandlers || eventHandlers.size === 0) console.warn(`No handlers registered for event type: ${eventType}`);
|
|
1941
2029
|
try {
|
|
1942
2030
|
const promises = Array.from(eventHandlers || []).map((handler) => {
|
|
1943
2031
|
const typedHandler = handler;
|
|
1944
|
-
const typedEvent =
|
|
2032
|
+
const typedEvent = event;
|
|
1945
2033
|
return typedHandler(typedEvent);
|
|
1946
2034
|
});
|
|
1947
2035
|
await Promise.all(promises);
|
|
1948
2036
|
} catch (error) {
|
|
1949
|
-
this.onError?.(error);
|
|
2037
|
+
await this.onError?.(error);
|
|
1950
2038
|
return {
|
|
1951
2039
|
status: 500,
|
|
1952
2040
|
success: false,
|
|
@@ -1970,11 +2058,13 @@ var WebhookListener = class {
|
|
|
1970
2058
|
//#endregion
|
|
1971
2059
|
//#region src/services/WebhookService.ts
|
|
1972
2060
|
var WebhookService = class {
|
|
1973
|
-
constructor(secretKey) {
|
|
2061
|
+
constructor(api, secretKey) {
|
|
2062
|
+
this.api = api;
|
|
1974
2063
|
this.secretKey = secretKey;
|
|
1975
2064
|
}
|
|
1976
|
-
createListener(
|
|
1977
|
-
|
|
2065
|
+
createListener(config = {}) {
|
|
2066
|
+
const { onError, authenticationMethod } = config;
|
|
2067
|
+
return new WebhookListener(this.api, this.secretKey, onError, authenticationMethod);
|
|
1978
2068
|
}
|
|
1979
2069
|
createRequestProcessor(listener) {
|
|
1980
2070
|
return (request) => this.processFetch(listener, request);
|
|
@@ -2476,12 +2566,12 @@ var Freemius = class {
|
|
|
2476
2566
|
this.auth = new AuthService(productId, secretKey);
|
|
2477
2567
|
this.pricing = new PricingService(this.api);
|
|
2478
2568
|
this.purchase = new PurchaseService(this.api);
|
|
2479
|
-
this.checkout = new CheckoutService(productId, publicKey, secretKey, this.purchase, this.pricing);
|
|
2569
|
+
this.checkout = new CheckoutService(this.api, productId, publicKey, secretKey, this.purchase, this.pricing);
|
|
2480
2570
|
this.customerPortal = new CustomerPortalService(this.api, this.checkout, this.auth, this.purchase);
|
|
2481
|
-
this.webhook = new WebhookService(secretKey);
|
|
2571
|
+
this.webhook = new WebhookService(this.api, secretKey);
|
|
2482
2572
|
}
|
|
2483
2573
|
};
|
|
2484
2574
|
|
|
2485
2575
|
//#endregion
|
|
2486
|
-
export { BILLING_CYCLE, CURRENCY, Freemius, idToNumber, idToString, isIdsEqual, parseBillingCycle, parseCurrency, parseDate, parseDateTime, parseNumber, parsePaymentMethod };
|
|
2576
|
+
export { ActionError, BILLING_CYCLE, CURRENCY, Freemius, WebhookAuthenticationMethod, WebhookError, idToNumber, idToString, isIdsEqual, parseBillingCycle, parseCurrency, parseDate, parseDateTime, parseNumber, parsePaymentMethod };
|
|
2487
2577
|
//# sourceMappingURL=index.mjs.map
|