Skip to content

Commit 4397850

Browse files
committed
migrated desktop and android checkout to paddle billing
1 parent 2a1cc0c commit 4397850

18 files changed

+230
-290
lines changed

assets/js/androidkey.js

+42-40
Original file line numberDiff line numberDiff line change
@@ -8,31 +8,57 @@ class AndroidLicense {
88
this._form = form;
99
this._checkoutData = checkoutData;
1010
this._paddle = $.ajax({
11-
url: 'https://cdn.paddle.com/paddle/paddle.js',
11+
url: 'https://cdn.paddle.com/paddle/v2/paddle.js',
1212
cache: true,
1313
dataType: 'script'
1414
}).then(() => {
1515
if (PADDLE_ENABLE_SANDBOX) {
1616
window.Paddle.Environment.set('sandbox');
1717
}
18-
window.Paddle.Setup({ vendor: PADDLE_VENDOR_ID });
18+
window.Paddle.Initialize({
19+
token: PADDLE_TOKEN,
20+
eventCallback: data => {
21+
if (data.name == "checkout.completed") {
22+
this.onCheckoutSucceeded();
23+
if (this._checkoutData.acceptNewsletter) {
24+
subscribeToNewsletter(data.customer.email, 6);
25+
}
26+
} else if (data.name == "checkout.closed") {
27+
this._checkoutData.inProgress = false;
28+
}
29+
}
30+
});
1931
return window.Paddle;
2032
});
2133
}
2234

2335
loadPrice() {
24-
$.ajax({
25-
url: PADDLE_PRICES_URL,
26-
dataType: 'jsonp',
27-
data: {
28-
product_ids: PADDLE_ANDROID_PRODUCT_ID
29-
},
30-
}).done(data => {
31-
this._checkoutData.price = {
32-
amount: data.response.products[0].price.gross,
33-
listAmount: data.response.products[0].list_price.gross,
34-
currency: data.response.products[0].currency
36+
this._paddle.then(paddle => {
37+
let request = {
38+
items: [{ priceId: PADDLE_ANDROID_PRICE_ID, quantity: 1}]
3539
};
40+
paddle.PricePreview(request).then(result => {
41+
this._checkoutData.price = {
42+
amount: result.data.details.lineItems[0].totals.total,
43+
formattedAmount: result.data.details.lineItems[0].formattedTotals.total
44+
};
45+
}).catch(() => {
46+
this._checkoutData.errorMessage = 'Retrieving price failed. Please try again later.';
47+
});
48+
// let discountedRequest = {
49+
// items: [{ priceId: PADDLE_ANDROID_PRICE_ID, quantity: 1}],
50+
// discountId: 'dsc_',
51+
// };
52+
// paddle.PricePreview(discountedRequest).then(discountedResult => {
53+
// if (Number(discountedResult.data.details.lineItems[0].totals.discount) > 0) {
54+
// this._checkoutData.discountedPrice = {
55+
// amount: discountedResult.data.details.lineItems[0].totals.total,
56+
// formattedAmount: discountedResult.data.details.lineItems[0].formattedTotals.total
57+
// };
58+
// }
59+
// }).catch(error => {
60+
// this._checkoutData.errorMessage = 'Retrieving discounted price failed. Please try again later.';
61+
// });
3662
});
3763
}
3864

@@ -48,33 +74,9 @@ class AndroidLicense {
4874
this._checkoutData.success = false;
4975
this._paddle.then(paddle => {
5076
paddle.Checkout.open({
51-
product: PADDLE_ANDROID_PRODUCT_ID,
52-
email: this._checkoutData.email,
53-
allowQuantity: false,
54-
locale: locale,
55-
successCallback: data => {
56-
this.onCheckoutSucceeded();
57-
this.getPaddleOrderDetails(data.checkout.id);
58-
if (this._checkoutData.acceptNewsletter) {
59-
subscribeToNewsletter(data.user.email, 6);
60-
}
61-
},
62-
closeCallback: () => {
63-
this._checkoutData.inProgress = false;
64-
}
65-
});
66-
});
67-
}
68-
69-
getPaddleOrderDetails(checkoutId) {
70-
this._paddle.then(paddle => {
71-
paddle.Order.details(checkoutId, data => {
72-
let licenseKey = data.lockers?.[0]?.license_code;
73-
if (licenseKey) {
74-
this._checkoutData.licenseKey = licenseKey;
75-
} else {
76-
this._checkoutData.errorMessage = 'Retrieving license key failed. Please check your emails instead.';
77-
}
77+
settings: { locale: locale },
78+
items: [{ priceId: PADDLE_ANDROID_PRICE_ID, quantity: 1 }],
79+
customer: { email: this._checkoutData.email }
7880
});
7981
});
8082
}

assets/js/const.dev.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
const BASE_API_URL = 'http://localhost';
44
const PADDLE_ENABLE_SANDBOX = true;
5+
const PADDLE_TOKEN = 'test_693d7082cf217f0c0d3cc21f888';
6+
const PADDLE_DESKTOP_PRICE_IDS = ['pri_01jb24dee06jaxrsz6xhfakkvy', 'pri_01jb24dvt2cj55r7p8agebef65', 'pri_01jb24e3z1zkzsq5yrhm5252bd'];
7+
const PADDLE_ANDROID_PRICE_ID = 'pri_01jb24ewyc6fbhnzt26jdj9qp1';
58
const PADDLE_VENDOR_ID = 1385;
6-
const PADDLE_DESKTOP_PRODUCT_IDS = [54582, 54583, 54584];
7-
const PADDLE_ANDROID_PRODUCT_ID = 9642;
89
const PADDLE_HUB_SELF_HOSTED_SUBSCRIPTION_PLAN_ID = 23141;
910
const PADDLE_HUB_MANAGED_SUBSCRIPTION_PLAN_ID = 42235;
1011
const PADDLE_PRICES_URL = 'https://sandbox-checkout.paddle.com/api/2.0/prices';

assets/js/const.prod.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
const BASE_API_URL = 'https://api.cryptomator.org';
44
const PADDLE_ENABLE_SANDBOX = false;
5+
const PADDLE_TOKEN = 'live_0af4126c5788dcda805e8389b96';
6+
const PADDLE_DESKTOP_PRICE_IDS = ['pri_01jb23tj1jxp88jt6yphast6th', 'pri_01jb23w0s2c2j3jebwtr5htwzj', 'pri_01jb23wh9vjqdskxyz96zhc4yk'];
7+
const PADDLE_ANDROID_PRICE_ID = 'pri_01jb2432chrz583xtxb029g40m';
58
const PADDLE_VENDOR_ID = 39223;
6-
const PADDLE_DESKTOP_PRODUCT_IDS = [840163, 840164, 840165];
7-
const PADDLE_ANDROID_PRODUCT_ID = 578277;
89
const PADDLE_HUB_SELF_HOSTED_SUBSCRIPTION_PLAN_ID = 770132;
910
const PADDLE_HUB_MANAGED_SUBSCRIPTION_PLAN_ID = 807339;
1011
const PADDLE_PRICES_URL = 'https://checkout.paddle.com/api/2.0/prices';

assets/js/desktopkey.js

+46-44
Original file line numberDiff line numberDiff line change
@@ -8,38 +8,64 @@ class DesktopLicense {
88
this._form = form;
99
this._checkoutData = checkoutData;
1010
this._paddle = $.ajax({
11-
url: 'https://cdn.paddle.com/paddle/paddle.js',
11+
url: 'https://cdn.paddle.com/paddle/v2/paddle.js',
1212
cache: true,
1313
dataType: 'script'
1414
}).then(() => {
1515
if (PADDLE_ENABLE_SANDBOX) {
1616
window.Paddle.Environment.set('sandbox');
1717
}
18-
window.Paddle.Setup({ vendor: PADDLE_VENDOR_ID });
18+
window.Paddle.Initialize({
19+
token: PADDLE_TOKEN,
20+
eventCallback: data => {
21+
if (data.name == "checkout.completed") {
22+
this.onCheckoutSucceeded();
23+
if (this._checkoutData.acceptNewsletter) {
24+
subscribeToNewsletter(data.customer.email, 6);
25+
}
26+
} else if (data.name == "checkout.closed") {
27+
this._checkoutData.inProgress = false;
28+
}
29+
}
30+
});
1931
return window.Paddle;
2032
});
2133
}
2234

2335
loadPrice() {
24-
$.ajax({
25-
url: PADDLE_PRICES_URL,
26-
dataType: 'jsonp',
27-
data: {
28-
product_ids: PADDLE_DESKTOP_PRODUCT_IDS.join(',')
29-
},
30-
}).done(data => {
31-
this._checkoutData.prices = data.response.products.map(product => {
32-
return {
33-
productId: product.product_id,
34-
amount: product.price.gross,
35-
listAmount: product.list_price.gross,
36-
currency: product.currency
37-
}
36+
this._paddle.then(paddle => {
37+
let request = {
38+
items: PADDLE_DESKTOP_PRICE_IDS.map(priceId => ({ priceId: priceId, quantity: 1 }))
39+
};
40+
paddle.PricePreview(request).then(result => {
41+
this._checkoutData.prices = result.data.details.lineItems.map(item => {
42+
return {
43+
priceId: item.price.id,
44+
amount: item.totals.total,
45+
formattedAmount: item.formattedTotals.total
46+
}
47+
});
48+
}).catch(() => {
49+
this._checkoutData.errorMessage = 'Retrieving prices failed. Please try again later.';
3850
});
51+
// let discountedRequest = {
52+
// items: [{ priceId: PADDLE_DESKTOP_PRICE_IDS[0], quantity: 1 }],
53+
// discountId: 'dsc_',
54+
// };
55+
// paddle.PricePreview(discountedRequest).then(discountedResult => {
56+
// if (Number(discountedResult.data.details.lineItems[0].totals.discount) > 0) {
57+
// this._checkoutData.discountedPrice = {
58+
// amount: discountedResult.data.details.lineItems[0].totals.total,
59+
// formattedAmount: discountedResult.data.details.lineItems[0].formattedTotals.total
60+
// };
61+
// }
62+
// }).catch(() => {
63+
// this._checkoutData.errorMessage = 'Retrieving discounted price failed. Please try again later.';
64+
// });
3965
});
4066
}
4167

42-
checkout(productId, locale) {
68+
checkout(priceId, locale) {
4369
if (!$(this._form)[0].checkValidity()) {
4470
$(this._form).find(':input').addClass('show-invalid');
4571
this._checkoutData.errorMessage = 'Please fill in all required fields.';
@@ -51,33 +77,9 @@ class DesktopLicense {
5177
this._checkoutData.success = false;
5278
this._paddle.then(paddle => {
5379
paddle.Checkout.open({
54-
product: productId,
55-
email: this._checkoutData.email,
56-
quantity: this._checkoutData.quantity,
57-
locale: locale,
58-
successCallback: data => {
59-
this.onCheckoutSucceeded();
60-
this.getPaddleOrderDetails(data.checkout.id);
61-
if (this._checkoutData.acceptNewsletter) {
62-
subscribeToNewsletter(data.user.email, 6);
63-
}
64-
},
65-
closeCallback: () => {
66-
this._checkoutData.inProgress = false;
67-
}
68-
});
69-
});
70-
}
71-
72-
getPaddleOrderDetails(checkoutId) {
73-
this._paddle.then(paddle => {
74-
paddle.Order.details(checkoutId, data => {
75-
let licenseKey = data.lockers?.[0]?.license_code;
76-
if (licenseKey) {
77-
this._checkoutData.licenseKey = licenseKey;
78-
} else {
79-
this._checkoutData.errorMessage = 'Retrieving supporter certificate failed. Please check your emails instead.';
80-
}
80+
settings: { locale: locale },
81+
items: [{ priceId: priceId, quantity: this._checkoutData.quantity }],
82+
customer: { email: this._checkoutData.email }
8183
});
8284
});
8385
}

assets/js/keyrecovery.js

-54
This file was deleted.

assets/js/paddlecheckout.js

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
"use strict";
2+
3+
// requires store.js
4+
5+
class PaddleCheckout {
6+
7+
constructor(checkoutData) {
8+
this._checkoutData = checkoutData;
9+
this._checkoutData.transactionId = new URLSearchParams(window.location.search).get('_ptxn');
10+
this._paddle = $.ajax({
11+
url: 'https://cdn.paddle.com/paddle/v2/paddle.js',
12+
cache: true,
13+
dataType: 'script'
14+
}).then(() => {
15+
if (PADDLE_ENABLE_SANDBOX) {
16+
window.Paddle.Environment.set('sandbox');
17+
}
18+
window.Paddle.Initialize({
19+
token: PADDLE_TOKEN,
20+
eventCallback: data => {
21+
if (data.name == "checkout.completed") {
22+
this.onCheckoutSucceeded();
23+
} else if (data.name == "checkout.closed") {
24+
this._checkoutData.inProgress = false;
25+
}
26+
}
27+
});
28+
return window.Paddle;
29+
});
30+
}
31+
32+
checkout(locale) {
33+
if (this._checkoutData.transactionId == null) {
34+
this._checkoutData.errorMessage = 'Transaction ID is missing.';
35+
return;
36+
}
37+
38+
this._checkoutData.inProgress = true;
39+
this._checkoutData.errorMessage = '';
40+
this._checkoutData.success = false;
41+
this._paddle.then(paddle => {
42+
paddle.Checkout.open({
43+
settings: { locale: locale},
44+
transactionId: this._checkoutData.transactionId
45+
});
46+
});
47+
}
48+
49+
onCheckoutSucceeded() {
50+
this._checkoutData.success = true;
51+
this._checkoutData.errorMessage = '';
52+
this._checkoutData.inProgress = false;
53+
}
54+
55+
}

content/paddle-checkout.de.html

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
title: "Paddle Checkout"
3+
type: paddle-checkout
4+
---

content/paddle-checkout.en.html

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
title: "Paddle Checkout"
3+
type: paddle-checkout
4+
---

data/de/pricing_individuals_faq.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,5 @@
1414
Answer: "Dies ist nur für Android möglich, da du die Cryptomator-App als APK-Datei oder über F-Droid herunterladen und die Lizenz über unsere Website kaufen kannst. Das Supporter-Zertifikat kann nur über unsere Website erworben werden."
1515
- Question: "Kann ich Cryptomator auch anonym erwerben?"
1616
Answer: "Ja, du kannst einen Lizenzschlüssel für Cryptomator für Android oder ein Supporter-Zertifikat über [ProxyStore](https://digitalgoods.proxysto.re/de) kaufen. Sie akzeptieren Monero, Bitcoin und Bargeld. Mehr über unsere Zusammenarbeit erfährst du [hier](/de/coop/proxystore/)."
17+
- Question: "Was soll ich tun, wenn ich meinen Lizenzschlüssel oder mein Supporter-Zertifikat verloren habe?"
18+
Answer: "Wenn du deinen Lizenzschlüssel oder dein Supporter-Zertifikat verloren hast, kontaktiere bitte unser Support-Team unter [support@cryptomator.org](mailto:support@cryptomator.org). Wir helfen dir gerne dabei, deine Informationen wiederherzustellen."

data/en/pricing_individuals_faq.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,5 @@
1414
Answer: "This is possible on Android only, as you can download the Cryptomator app as an APK file or through F-Droid and buy the license using our website. The supporter certificate can only be purchased using our website."
1515
- Question: "Can I purchase Cryptomator anonymously?"
1616
Answer: "Yes, you can buy a Cryptomator for Android license key or a supporter certificate through [ProxyStore](https://digitalgoods.proxysto.re/en). They accept Monero, Bitcoin, and cash. You can read more about our cooperation [here](/coop/proxystore/)."
17+
- Question: "What should I do if I lose my license key or supporter certificate?"
18+
Answer: "If you've lost your license key or supporter certificate, please contact our support team at [support@cryptomator.org](mailto:support@cryptomator.org). We'll assist you in recovering your information."

hugo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,6 @@ block = true
124124
[[server.headers]]
125125
for = '/**'
126126
[server.headers.values]
127-
Content-Security-Policy = "default-src 'none'; script-src 'self' 'unsafe-eval' https://umami.skymatic.de/ https://community.cryptomator.org/ https://js.stripe.com/ https://*.paddle.com/; style-src 'self' 'unsafe-inline' https://*.paddle.com/; img-src 'self' data: https://static.cryptomator.org/ https://*.paddle.com/ https://paddle.s3.amazonaws.com/; connect-src 'self' https://api.cryptomator.org/ https://store.cryptomator.org/ https://umami.skymatic.de/ http://localhost/ http://localhost:8787/; font-src 'self'; media-src https://static.cryptomator.org/; frame-src https://community.cryptomator.org/ https://js.stripe.com/ https://*.paddle.com/; base-uri 'self'; form-action 'self' https://www.paypal.com/ https://www.coinpayments.net/; frame-ancestors 'none';"
127+
Content-Security-Policy = "default-src 'none'; script-src 'self' 'unsafe-eval' https://umami.skymatic.de/ https://community.cryptomator.org/ https://js.stripe.com/ https://*.paddle.com/; style-src 'self' 'unsafe-inline' https://*.paddle.com/; img-src 'self' data: https://static.cryptomator.org/ https://*.paddle.com/ https://paddle.s3.amazonaws.com/; connect-src 'self' https://api.cryptomator.org/ https://store.cryptomator.org/ https://umami.skymatic.de/ https://*.paddle.com/ http://localhost:8787/; font-src 'self'; media-src https://static.cryptomator.org/; frame-src https://community.cryptomator.org/ https://js.stripe.com/ https://*.paddle.com/; base-uri 'self'; form-action 'self' https://www.paypal.com/ https://www.coinpayments.net/; frame-ancestors 'none'"
128128
Strict-Transport-Security = "max-age=31536000; includeSubDomains"
129129
X-Content-Type-Options = "nosniff"

0 commit comments

Comments
 (0)