@kash-88/alerts 1.2.1 → 1.3.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/LICENSE +21 -0
- package/dist/src/Function/createCustomAlerts.d.ts +16 -0
- package/dist/src/Function/createCustomAlerts.js +51 -0
- package/dist/src/Function/createMerchandise.d.ts +22 -0
- package/dist/src/Function/createMerchandise.js +82 -0
- package/dist/src/Function/generateSignature.d.ts +13 -0
- package/dist/src/Function/generateSignature.js +18 -0
- package/dist/src/Function/getAuthorizeLink.d.ts +13 -0
- package/dist/src/Function/getAuthorizeLink.js +27 -0
- package/dist/src/Function/getDonationsAlerts.d.ts +11 -0
- package/dist/src/Function/getDonationsAlerts.js +30 -0
- package/dist/src/Function/getExternal.d.ts +6 -0
- package/dist/src/Function/getExternal.js +27 -0
- package/dist/src/Function/getOauthToken.d.ts +13 -0
- package/dist/src/Function/getOauthToken.js +44 -0
- package/dist/src/Function/getPrivateToken.d.ts +10 -0
- package/dist/src/Function/getPrivateToken.js +40 -0
- package/dist/src/Function/getUser.d.ts +10 -0
- package/dist/src/Function/getUser.js +26 -0
- package/dist/src/Function/getUserChannel.d.ts +8 -0
- package/dist/src/Function/getUserChannel.js +13 -0
- package/dist/src/Function/getUserDataFromPromocode.d.ts +11 -0
- package/dist/src/Function/getUserDataFromPromocode.js +39 -0
- package/dist/src/Function/sendSaleAlert.d.ts +20 -0
- package/dist/src/Function/sendSaleAlert.js +81 -0
- package/dist/src/Function/updateMerchandise.d.ts +21 -0
- package/dist/src/Function/updateMerchandise.js +76 -0
- package/dist/src/Function/updateOauthToken.d.ts +13 -0
- package/dist/src/Function/updateOauthToken.js +44 -0
- package/dist/src/Function/updateOrCreateMerchandise.d.ts +22 -0
- package/dist/src/Function/updateOrCreateMerchandise.js +80 -0
- package/dist/src/Types/CustomAlerts.d.ts +12 -0
- package/dist/src/Types/DonationsAlerts.d.ts +31 -0
- package/dist/src/Types/DonationsAlerts.js +1 -0
- package/dist/src/Types/Merchandise.d.ts +18 -0
- package/dist/src/Types/Merchandise.js +1 -0
- package/dist/src/Types/MerchandiseSale.d.ts +14 -0
- package/dist/src/Types/MerchandiseSale.js +1 -0
- package/dist/src/Types/OAuthScope.d.ts +9 -0
- package/dist/src/Types/OAuthScope.js +10 -0
- package/dist/src/Types/OAuthToken.d.ts +7 -0
- package/dist/src/Types/OAuthToken.js +1 -0
- package/dist/src/Types/User.d.ts +9 -0
- package/dist/src/Types/User.js +1 -0
- package/dist/src/Types/index.d.ts +8 -0
- package/dist/src/Types/index.js +2 -0
- package/dist/src/WebSocket/CentrifugeClient.d.ts +71 -0
- package/dist/src/WebSocket/CentrifugeClient.js +216 -0
- package/dist/src/index.d.ts +18 -9
- package/dist/src/index.js +18 -9
- package/dist/src/utils.d.ts +5 -13
- package/dist/src/utils.js +16 -32
- package/package.json +22 -18
- package/readme.md +502 -81
- package/src/Function/createCustomAlerts.ts +65 -0
- package/src/Function/createMerchandise.ts +105 -0
- package/src/Function/generateSignature.ts +23 -0
- package/src/Function/getAuthorizeLink.ts +37 -0
- package/src/Function/getDonationsAlerts.ts +37 -0
- package/src/Function/getExternal.ts +31 -0
- package/src/Function/getOauthToken.ts +54 -0
- package/src/Function/getPrivateToken.ts +50 -0
- package/src/Function/getUser.ts +32 -0
- package/src/Function/getUserChannel.ts +17 -0
- package/src/Function/getUserDataFromPromocode.ts +51 -0
- package/src/Function/sendSaleAlert.ts +103 -0
- package/src/Function/updateMerchandise.ts +98 -0
- package/src/Function/updateOauthToken.ts +54 -0
- package/src/Function/updateOrCreateMerchandise.ts +103 -0
- package/src/Types/CustomAlerts.ts +13 -0
- package/src/Types/DonationsAlerts.ts +32 -0
- package/src/Types/Merchandise.ts +19 -0
- package/src/Types/MerchandiseSale.ts +15 -0
- package/src/Types/OAuthScope.ts +10 -0
- package/src/Types/OAuthToken.ts +8 -0
- package/src/Types/User.ts +10 -0
- package/src/Types/index.ts +17 -0
- package/src/WebSocket/CentrifugeClient.ts +271 -0
- package/src/index.ts +25 -6
- package/src/utils.ts +22 -44
- package/dist/src/func/getAuthorizeLink.d.ts +0 -18
- package/dist/src/func/getAuthorizeLink.js +0 -29
- package/dist/src/func/getOauthToken.d.ts +0 -27
- package/dist/src/func/getOauthToken.js +0 -42
- package/dist/src/func/getPrivateToken.d.ts +0 -29
- package/dist/src/func/getPrivateToken.js +0 -46
- package/dist/src/func/getUser.d.ts +0 -23
- package/dist/src/func/getUser.js +0 -38
- package/dist/src/func/getUserChannel.d.ts +0 -19
- package/dist/src/func/getUserChannel.js +0 -32
- package/dist/src/func/updateAccessToken.d.ts +0 -24
- package/dist/src/func/updateAccessToken.js +0 -39
- package/dist/src/types.d.ts +0 -33
- package/dist/src/ws/CentrifugeClient.d.ts +0 -31
- package/dist/src/ws/CentrifugeClient.js +0 -71
- package/src/example/getAuthorizeLink.ts +0 -11
- package/src/example/getOauthToken.ts +0 -16
- package/src/example/getUser.ts +0 -12
- package/src/example/getUserChannel.ts +0 -10
- package/src/example/updateAccessToken.ts +0 -17
- package/src/example/wsExample.ts +0 -50
- package/src/func/getAuthorizeLink.ts +0 -33
- package/src/func/getOauthToken.ts +0 -46
- package/src/func/getPrivateToken.ts +0 -53
- package/src/func/getUser.ts +0 -42
- package/src/func/getUserChannel.ts +0 -34
- package/src/func/updateAccessToken.ts +0 -43
- package/src/types.ts +0 -38
- package/src/ws/CentrifugeClient.ts +0 -108
- /package/dist/src/{types.js → Types/CustomAlerts.js} +0 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { CustomAlerts } from "@type";
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
import { formatAxiosError } from "@utils";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Send a custom alert to the authorized user.
|
|
7
|
+
*
|
|
8
|
+
* @param {string} access_token - User access token
|
|
9
|
+
* @param {string} external_id - Up to 32 characters long unique alert ID
|
|
10
|
+
* @param {string} header - Up to 255 characters long header text
|
|
11
|
+
* @param {string} message - Up to 300 characters long message text
|
|
12
|
+
* @param {number} is_shown - 0 or 1, determines whether the alert should be displayed
|
|
13
|
+
* @param {string | null} [image_url] - URL to the image file displayed with the alert
|
|
14
|
+
* @param {string | null} [sound_url] - URL to the sound file played when displaying the alert
|
|
15
|
+
*
|
|
16
|
+
* @returns {Promise<CustomAlerts>} Created custom alert data
|
|
17
|
+
* @see {@link https://www.donationalerts.com/apidoc#api_v1__custom_alerts__send_custom_alerts}
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export default async function createCustomAlerts(
|
|
21
|
+
access_token: string,
|
|
22
|
+
external_id: string,
|
|
23
|
+
header: string,
|
|
24
|
+
message: string,
|
|
25
|
+
is_shown: 0 | 1,
|
|
26
|
+
image_url?: string | null,
|
|
27
|
+
sound_url?: string | null
|
|
28
|
+
): Promise<CustomAlerts> {
|
|
29
|
+
if (!access_token || typeof access_token !== "string") {
|
|
30
|
+
throw new Error("access_token must be a non-empty string");
|
|
31
|
+
}
|
|
32
|
+
if (!external_id || typeof external_id !== "string") {
|
|
33
|
+
throw new Error("external_id must be a non-empty string");
|
|
34
|
+
}
|
|
35
|
+
if (typeof header !== "string" || typeof message !== "string") {
|
|
36
|
+
throw new Error("header and message must be strings");
|
|
37
|
+
}
|
|
38
|
+
if (is_shown !== 0 && is_shown !== 1) {
|
|
39
|
+
throw new Error("is_shown must be 0 or 1");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const formData = new URLSearchParams();
|
|
44
|
+
formData.append("external_id", external_id);
|
|
45
|
+
formData.append("header", header);
|
|
46
|
+
formData.append("message", message);
|
|
47
|
+
formData.append("is_shown", String(is_shown));
|
|
48
|
+
if (image_url) formData.append("image_url", image_url);
|
|
49
|
+
if (sound_url) formData.append("sound_url", sound_url);
|
|
50
|
+
|
|
51
|
+
const response = await axios.post<{ data: CustomAlerts }>("https://www.donationalerts.com/api/v1/custom_alert",
|
|
52
|
+
formData,
|
|
53
|
+
{
|
|
54
|
+
headers: {
|
|
55
|
+
Authorization: `Bearer ${access_token}`,
|
|
56
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
return response.data.data;
|
|
62
|
+
} catch (error: any) {
|
|
63
|
+
throw new Error(formatAxiosError(error));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { Merchandise } from "@type";
|
|
3
|
+
import generateSignature from "@function/generateSignature.js";
|
|
4
|
+
import { formatAxiosError } from "@utils";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Create a new merchandise.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} access_token - User access token
|
|
10
|
+
* @param {string} client_secret - API client secret key for signature
|
|
11
|
+
* @param {string} merchant_identifier - Merchant's ID on DonationAlerts
|
|
12
|
+
* @param {string} merchandise_identifier - Up to 16 characters long unique merchandise ID
|
|
13
|
+
* @param {Record<string, string>} title - Object with merchandise titles in different locales (en_US required)
|
|
14
|
+
* @param {string} currency - Currency code (EUR, USD, RUB, BRL, TRY)
|
|
15
|
+
* @param {number} price_user - Revenue added to streamer for each sale
|
|
16
|
+
* @param {number} price_service - Revenue added to DonationAlerts for each sale
|
|
17
|
+
* @param {number} [is_active=0] - 0 or 1, whether merchandise is available for purchase
|
|
18
|
+
* @param {number} [is_percentage=0] - 0 or 1, whether prices are percentages or absolute amounts
|
|
19
|
+
* @param {string} [url] - URL to merchandise's web page
|
|
20
|
+
* @param {string} [img_url] - URL to merchandise's image
|
|
21
|
+
* @param {number} [end_at_ts] - Unix timestamp when merchandise becomes inactive
|
|
22
|
+
*
|
|
23
|
+
* @returns {Promise<Merchandise>} Created merchandise data
|
|
24
|
+
* @see {@link https://www.donationalerts.com/apidoc#api_v1__merchandises__create_merchandise}
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
export default async function createMerchandise(
|
|
28
|
+
access_token: string,
|
|
29
|
+
client_secret: string,
|
|
30
|
+
merchant_identifier: string,
|
|
31
|
+
merchandise_identifier: string,
|
|
32
|
+
title: Record<string, string>,
|
|
33
|
+
currency: string,
|
|
34
|
+
price_user: number,
|
|
35
|
+
price_service: number,
|
|
36
|
+
is_active: number = 0,
|
|
37
|
+
is_percentage: number = 0,
|
|
38
|
+
url?: string,
|
|
39
|
+
img_url?: string,
|
|
40
|
+
end_at_ts?: number
|
|
41
|
+
): Promise<Merchandise> {
|
|
42
|
+
if (!access_token || typeof access_token !== "string") {
|
|
43
|
+
throw new Error("access_token must be a non-empty string");
|
|
44
|
+
}
|
|
45
|
+
if (!client_secret || typeof client_secret !== "string") {
|
|
46
|
+
throw new Error("client_secret must be a non-empty string");
|
|
47
|
+
}
|
|
48
|
+
if (!merchant_identifier || typeof merchant_identifier !== "string") {
|
|
49
|
+
throw new Error("merchant_identifier must be a non-empty string");
|
|
50
|
+
}
|
|
51
|
+
if (!merchandise_identifier || typeof merchandise_identifier !== "string") {
|
|
52
|
+
throw new Error("merchandise_identifier must be a non-empty string");
|
|
53
|
+
}
|
|
54
|
+
if (!title || Object.keys(title).length === 0) {
|
|
55
|
+
throw new Error("title must be a non-empty object");
|
|
56
|
+
}
|
|
57
|
+
if (!currency || typeof currency !== "string") {
|
|
58
|
+
throw new Error("currency must be a non-empty string");
|
|
59
|
+
}
|
|
60
|
+
if (typeof price_user !== "number" || typeof price_service !== "number") {
|
|
61
|
+
throw new Error("price_user and price_service must be numbers");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
const params: Record<string, string | number> = {
|
|
66
|
+
merchant_identifier,
|
|
67
|
+
merchandise_identifier,
|
|
68
|
+
currency,
|
|
69
|
+
price_user,
|
|
70
|
+
price_service,
|
|
71
|
+
is_active,
|
|
72
|
+
is_percentage
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
for (const [locale, value] of Object.entries(title)) {
|
|
76
|
+
params[`title[${locale}]`] = value;
|
|
77
|
+
}
|
|
78
|
+
if (url) params.url = url;
|
|
79
|
+
if (img_url) params.img_url = img_url;
|
|
80
|
+
if (end_at_ts !== undefined) params.end_at_ts = end_at_ts;
|
|
81
|
+
|
|
82
|
+
const signature = generateSignature(params, client_secret);
|
|
83
|
+
params.signature = signature;
|
|
84
|
+
|
|
85
|
+
const formData = new URLSearchParams();
|
|
86
|
+
for (const [key, value] of Object.entries(params)) {
|
|
87
|
+
formData.append(key, String(value));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const response = await axios.post<{ data: Merchandise }>(
|
|
91
|
+
"https://www.donationalerts.com/api/v1/merchandise",
|
|
92
|
+
formData,
|
|
93
|
+
{
|
|
94
|
+
headers: {
|
|
95
|
+
Authorization: `Bearer ${access_token}`,
|
|
96
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
return response.data.data;
|
|
102
|
+
} catch (error: any) {
|
|
103
|
+
throw new Error(formatAxiosError(error));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createHash } from "crypto";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generate a SHA256 request signature for Merchandise API.
|
|
5
|
+
*
|
|
6
|
+
* The signature is formed from alphabetically sorted parameter values
|
|
7
|
+
* (interpreted as strings) with the API client secret appended to the end.
|
|
8
|
+
*
|
|
9
|
+
* @param {Record<string, string | number>} params - Request parameters
|
|
10
|
+
* @param {string} client_secret - API client secret key
|
|
11
|
+
*
|
|
12
|
+
* @returns {string} SHA256 hex digest signature
|
|
13
|
+
* @see {@link https://www.donationalerts.com/apidoc#introduction__http_api_requests__request_signatures}
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export default function generateSignature(
|
|
17
|
+
params: Record<string, string | number>,
|
|
18
|
+
client_secret: string
|
|
19
|
+
): string {
|
|
20
|
+
const sortedValues = Object.values(params).map(String).sort();
|
|
21
|
+
const valuesString = sortedValues.join("");
|
|
22
|
+
return createHash("sha256").update(valuesString + client_secret).digest("hex");
|
|
23
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { OAuthScope } from "@type";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generate OAuth authorization link for DonationAlerts.
|
|
5
|
+
*
|
|
6
|
+
* @param {string} client_id - Your client (application) ID
|
|
7
|
+
* @param {string} redirect_uri - The URL where users will be sent after authorization
|
|
8
|
+
* @param {OAuthScope[]} scopes - Array of access scopes
|
|
9
|
+
* @param {'code' | 'token'} type - Type response oauth token
|
|
10
|
+
*
|
|
11
|
+
* @returns {string} The authorization URL.
|
|
12
|
+
* @see {@link https://www.donationalerts.com/apidoc#authorization__authorization_code__authorization_steps}
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export default function getAuthorizeLink(
|
|
16
|
+
client_id: string,
|
|
17
|
+
redirect_uri: string,
|
|
18
|
+
scopes: OAuthScope[],
|
|
19
|
+
type: 'code' | 'token'
|
|
20
|
+
): string {
|
|
21
|
+
if (!client_id || typeof client_id !== "string") {
|
|
22
|
+
throw new Error("client_id must be a non-empty string");
|
|
23
|
+
}
|
|
24
|
+
if (!redirect_uri || typeof redirect_uri !== "string") {
|
|
25
|
+
throw new Error("redirect_uri must be a non-empty string");
|
|
26
|
+
}
|
|
27
|
+
if (!Array.isArray(scopes) || scopes.length === 0) {
|
|
28
|
+
throw new Error("scopes must be a non-empty array");
|
|
29
|
+
}
|
|
30
|
+
if (type !== "code" && type !== "token") {
|
|
31
|
+
throw new Error("type must be 'code' or 'token'");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const uniqueScopes = Array.from(new Set(scopes));
|
|
35
|
+
|
|
36
|
+
return `https://www.donationalerts.com/oauth/authorize?client_id=${encodeURIComponent(client_id)}&redirect_uri=${encodeURIComponent(redirect_uri)}&response_type=${type}&scope=${uniqueScopes.join("%20")}`;
|
|
37
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { DonationsAlerts } from "@type";
|
|
3
|
+
import { formatAxiosError } from "@utils";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Fetch user donation alerts list.
|
|
7
|
+
*
|
|
8
|
+
* @param {string} access_token - User access token
|
|
9
|
+
* @param {number | string} [page=1] - Page number for pagination
|
|
10
|
+
*
|
|
11
|
+
* @returns {Promise<DonationsAlerts>} Donation alerts data
|
|
12
|
+
* @see {@link https://www.donationalerts.com/apidoc#api_v1__donations__donation_alerts_list}
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export default async function getDonationsAlerts(
|
|
16
|
+
access_token: string,
|
|
17
|
+
page: number | string = 1
|
|
18
|
+
): Promise<DonationsAlerts> {
|
|
19
|
+
if (!access_token || typeof access_token !== "string") {
|
|
20
|
+
throw new Error("access_token must be a non-empty string");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const response = await axios.get<DonationsAlerts>("https://www.donationalerts.com/api/v1/alerts/donations", {
|
|
25
|
+
params: {
|
|
26
|
+
page
|
|
27
|
+
},
|
|
28
|
+
headers: {
|
|
29
|
+
Authorization: `Bearer ${access_token}`
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return response.data;
|
|
34
|
+
} catch (error: any) {
|
|
35
|
+
throw new Error(formatAxiosError(error));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { randomBytes } from "crypto";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generate a random 32-character external ID.
|
|
5
|
+
*
|
|
6
|
+
* @returns {string} 32 random alphanumeric characters
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export default function getExternal(): string {
|
|
10
|
+
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
11
|
+
const randomValues = randomBytes(32);
|
|
12
|
+
|
|
13
|
+
// Use rejection sampling to avoid bias
|
|
14
|
+
const charsetLength = charset.length;
|
|
15
|
+
const maxValidValue = 256 - (256 % charsetLength);
|
|
16
|
+
let result = "";
|
|
17
|
+
let index = 0;
|
|
18
|
+
|
|
19
|
+
while (index < 32) {
|
|
20
|
+
const randomValue = randomValues[index];
|
|
21
|
+
if (randomValue < maxValidValue) {
|
|
22
|
+
result += charset[randomValue % charsetLength];
|
|
23
|
+
index++;
|
|
24
|
+
} else {
|
|
25
|
+
// Rejection: generate a new random value for this position
|
|
26
|
+
randomValues[index] = randomBytes(1)[0];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { OAuthToken } from "@type";
|
|
3
|
+
import { formatAxiosError } from "@utils";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Exchange Authorization code for OAuth token and Refresh token.
|
|
7
|
+
*
|
|
8
|
+
* @param {string} client_id - Your client (application) ID
|
|
9
|
+
* @param {string} client_secret - Your client (application) secret
|
|
10
|
+
* @param {string} redirect_uri - The URL where users will be sent after authorization
|
|
11
|
+
* @param {string} code - User authorization code
|
|
12
|
+
*
|
|
13
|
+
* @returns {Promise<OAuthToken>} OAuth user token
|
|
14
|
+
* @see {@link https://github.com/kash-ts/alerts-SDK?tab=readme-ov-file#getOauthToken}
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export default async function getOauthToken(
|
|
18
|
+
client_id: string,
|
|
19
|
+
client_secret: string,
|
|
20
|
+
redirect_uri: string,
|
|
21
|
+
code: string
|
|
22
|
+
): Promise<OAuthToken> {
|
|
23
|
+
if (!client_id || typeof client_id !== "string") {
|
|
24
|
+
throw new Error("client_id must be a non-empty string");
|
|
25
|
+
}
|
|
26
|
+
if (!client_secret || typeof client_secret !== "string") {
|
|
27
|
+
throw new Error("client_secret must be a non-empty string");
|
|
28
|
+
}
|
|
29
|
+
if (!redirect_uri || typeof redirect_uri !== "string") {
|
|
30
|
+
throw new Error("redirect_uri must be a non-empty string");
|
|
31
|
+
}
|
|
32
|
+
if (!code || typeof code !== "string") {
|
|
33
|
+
throw new Error("code must be a non-empty string");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const formData = new URLSearchParams();
|
|
38
|
+
formData.append("grant_type", "authorization_code");
|
|
39
|
+
formData.append("client_id", client_id);
|
|
40
|
+
formData.append("client_secret", client_secret);
|
|
41
|
+
formData.append("redirect_uri", redirect_uri);
|
|
42
|
+
formData.append("code", code);
|
|
43
|
+
|
|
44
|
+
const response = await axios.post<OAuthToken>("https://www.donationalerts.com/oauth/token", formData, {
|
|
45
|
+
headers: {
|
|
46
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return response.data;
|
|
51
|
+
} catch (error: any) {
|
|
52
|
+
throw new Error(formatAxiosError(error));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { formatAxiosError } from "@utils";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get a Private token for subscribing to a DonationAlerts channel via Centrifuge.
|
|
6
|
+
*
|
|
7
|
+
* @param {string} channel - User channel
|
|
8
|
+
* @param {string} client - Centrifugo UUIDv4 client ID
|
|
9
|
+
* @param {string} access_token - User OAuth token
|
|
10
|
+
*
|
|
11
|
+
* @returns {Promise<string>} - Token for channel subscription.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export default async function getPrivateToken(
|
|
15
|
+
channel: string,
|
|
16
|
+
client: string,
|
|
17
|
+
access_token: string
|
|
18
|
+
): Promise<string> {
|
|
19
|
+
if (!channel || typeof channel !== "string") {
|
|
20
|
+
throw new Error("channel must be a non-empty string");
|
|
21
|
+
}
|
|
22
|
+
if (!client || typeof client !== "string") {
|
|
23
|
+
throw new Error("client must be a non-empty string");
|
|
24
|
+
}
|
|
25
|
+
if (!access_token || typeof access_token !== "string") {
|
|
26
|
+
throw new Error("access_token must be a non-empty string");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const response = await axios.post("https://www.donationalerts.com/api/v1/centrifuge/subscribe",
|
|
31
|
+
{
|
|
32
|
+
channels: [channel],
|
|
33
|
+
client: client
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
headers: {
|
|
37
|
+
Authorization: `Bearer ${access_token}`,
|
|
38
|
+
"Content-Type": "application/json"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
if (!response.data?.channels?.[0]?.token) {
|
|
44
|
+
throw new Error("Failed to get private token: no channels in response");
|
|
45
|
+
}
|
|
46
|
+
return response.data.channels[0].token;
|
|
47
|
+
} catch (error: any) {
|
|
48
|
+
throw new Error(formatAxiosError(error));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { User } from "@type";
|
|
3
|
+
import { formatAxiosError } from "@utils";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Fetch user profile information by OAuth token.
|
|
7
|
+
*
|
|
8
|
+
* @param {string} access_token - User access token
|
|
9
|
+
*
|
|
10
|
+
* @returns {Promise<User>} User data
|
|
11
|
+
* @see {@link https://www.donationalerts.com/apidoc#api_v1__users__user_profile_information}
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export default async function getUser(
|
|
15
|
+
access_token: string
|
|
16
|
+
): Promise<User> {
|
|
17
|
+
if (!access_token || typeof access_token !== "string") {
|
|
18
|
+
throw new Error("access_token must be a non-empty string");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const response = await axios.get<{ data: User }>("https://www.donationalerts.com/api/v1/user/oauth", {
|
|
23
|
+
headers: {
|
|
24
|
+
Authorization: `Bearer ${access_token}`
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
return response.data.data;
|
|
29
|
+
} catch (error: any) {
|
|
30
|
+
throw new Error(formatAxiosError(error));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get user channel by user id for WebSocket.
|
|
3
|
+
*
|
|
4
|
+
* @param {string | number} id - User ID
|
|
5
|
+
*
|
|
6
|
+
* @returns {string} The donation alert channel identifier in the format "$alerts:donation_{id}".
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export default function getUserChannel(
|
|
10
|
+
id: string | number
|
|
11
|
+
): string {
|
|
12
|
+
if (id === null || id === undefined || id === "") {
|
|
13
|
+
throw new Error("id must be a non-empty string or number");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return `$alerts:donation_${id}`;
|
|
17
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import generateSignature from "@function/generateSignature.js";
|
|
3
|
+
import { formatAxiosError } from "@utils";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Get user ID from an advertising promocode.
|
|
7
|
+
*
|
|
8
|
+
* @param {string} access_token - User access token
|
|
9
|
+
* @param {string} client_secret - API client secret key for signature
|
|
10
|
+
* @param {string} promocode - User promocode
|
|
11
|
+
*
|
|
12
|
+
* @returns {Promise<number>} DonationAlerts user ID
|
|
13
|
+
* @see {@link https://www.donationalerts.com/apidoc#api_v1__merchandises__update_or_create_merchandise__get_user_data_from_promocode}
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export default async function getUserDataFromPromocode(
|
|
17
|
+
access_token: string,
|
|
18
|
+
client_secret: string,
|
|
19
|
+
promocode: string
|
|
20
|
+
): Promise<number> {
|
|
21
|
+
if (!access_token || typeof access_token !== "string") {
|
|
22
|
+
throw new Error("access_token must be a non-empty string");
|
|
23
|
+
}
|
|
24
|
+
if (!client_secret || typeof client_secret !== "string") {
|
|
25
|
+
throw new Error("client_secret must be a non-empty string");
|
|
26
|
+
}
|
|
27
|
+
if (!promocode || typeof promocode !== "string") {
|
|
28
|
+
throw new Error("promocode must be a non-empty string");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const params: Record<string, string | number> = { promocode };
|
|
33
|
+
|
|
34
|
+
const signature = generateSignature(params, client_secret);
|
|
35
|
+
params.signature = signature;
|
|
36
|
+
|
|
37
|
+
const response = await axios.get<{ data: { user_id: number } }>(
|
|
38
|
+
"https://www.donationalerts.com/api/v1/merchandise/user",
|
|
39
|
+
{
|
|
40
|
+
params,
|
|
41
|
+
headers: {
|
|
42
|
+
Authorization: `Bearer ${access_token}`
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return response.data.data.user_id;
|
|
48
|
+
} catch (error: any) {
|
|
49
|
+
throw new Error(formatAxiosError(error));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { MerchandiseSale } from "@type";
|
|
3
|
+
import generateSignature from "@function/generateSignature.js";
|
|
4
|
+
import { formatAxiosError } from "@utils";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Send a merchandise sale alert.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} access_token - User access token
|
|
10
|
+
* @param {string} client_secret - API client secret key for signature
|
|
11
|
+
* @param {number} user_id - DonationAlerts user ID to which the sale is referenced
|
|
12
|
+
* @param {string} external_id - Up to 32 characters long unique sale ID
|
|
13
|
+
* @param {string} merchant_identifier - Merchant's ID on DonationAlerts
|
|
14
|
+
* @param {string} merchandise_identifier - Merchant's merchandise ID that was bought
|
|
15
|
+
* @param {number} amount - Grand total of the sale
|
|
16
|
+
* @param {string} currency - Currency code (EUR, USD, RUB, BRL, TRY)
|
|
17
|
+
* @param {number} [bought_amount=1] - Total number of bought items
|
|
18
|
+
* @param {string} [username] - Name of the customer
|
|
19
|
+
* @param {string} [message] - Message sent by the customer
|
|
20
|
+
*
|
|
21
|
+
* @returns {Promise<MerchandiseSale>} Created merchandise sale alert data
|
|
22
|
+
* @see {@link https://www.donationalerts.com/apidoc#api_v1__merchandise_sale_notifications__send_sale_alerts}
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
export default async function sendSaleAlert(
|
|
26
|
+
access_token: string,
|
|
27
|
+
client_secret: string,
|
|
28
|
+
user_id: number,
|
|
29
|
+
external_id: string,
|
|
30
|
+
merchant_identifier: string,
|
|
31
|
+
merchandise_identifier: string,
|
|
32
|
+
amount: number,
|
|
33
|
+
currency: string,
|
|
34
|
+
bought_amount: number = 1,
|
|
35
|
+
username?: string,
|
|
36
|
+
message?: string
|
|
37
|
+
): Promise<MerchandiseSale> {
|
|
38
|
+
if (!access_token || typeof access_token !== "string") {
|
|
39
|
+
throw new Error("access_token must be a non-empty string");
|
|
40
|
+
}
|
|
41
|
+
if (!client_secret || typeof client_secret !== "string") {
|
|
42
|
+
throw new Error("client_secret must be a non-empty string");
|
|
43
|
+
}
|
|
44
|
+
if (typeof user_id !== "number" || user_id <= 0) {
|
|
45
|
+
throw new Error("user_id must be a positive number");
|
|
46
|
+
}
|
|
47
|
+
if (!external_id || typeof external_id !== "string") {
|
|
48
|
+
throw new Error("external_id must be a non-empty string");
|
|
49
|
+
}
|
|
50
|
+
if (!merchant_identifier || typeof merchant_identifier !== "string") {
|
|
51
|
+
throw new Error("merchant_identifier must be a non-empty string");
|
|
52
|
+
}
|
|
53
|
+
if (!merchandise_identifier || typeof merchandise_identifier !== "string") {
|
|
54
|
+
throw new Error("merchandise_identifier must be a non-empty string");
|
|
55
|
+
}
|
|
56
|
+
if (typeof amount !== "number" || amount <= 0) {
|
|
57
|
+
throw new Error("amount must be a positive number");
|
|
58
|
+
}
|
|
59
|
+
if (!currency || typeof currency !== "string") {
|
|
60
|
+
throw new Error("currency must be a non-empty string");
|
|
61
|
+
}
|
|
62
|
+
if (typeof bought_amount !== "number" || bought_amount <= 0) {
|
|
63
|
+
throw new Error("bought_amount must be a positive number");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const params: Record<string, string | number> = {
|
|
68
|
+
user_id,
|
|
69
|
+
external_id,
|
|
70
|
+
merchant_identifier,
|
|
71
|
+
merchandise_identifier,
|
|
72
|
+
amount,
|
|
73
|
+
currency,
|
|
74
|
+
bought_amount
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
if (username) params.username = username;
|
|
78
|
+
if (message) params.message = message;
|
|
79
|
+
|
|
80
|
+
const signature = generateSignature(params, client_secret);
|
|
81
|
+
params.signature = signature;
|
|
82
|
+
|
|
83
|
+
const formData = new URLSearchParams();
|
|
84
|
+
for (const [key, value] of Object.entries(params)) {
|
|
85
|
+
formData.append(key, String(value));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const response = await axios.post<{ data: MerchandiseSale }>(
|
|
89
|
+
"https://www.donationalerts.com/api/v1/merchandise_sale",
|
|
90
|
+
formData,
|
|
91
|
+
{
|
|
92
|
+
headers: {
|
|
93
|
+
Authorization: `Bearer ${access_token}`,
|
|
94
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
return response.data.data;
|
|
100
|
+
} catch (error: any) {
|
|
101
|
+
throw new Error(formatAxiosError(error));
|
|
102
|
+
}
|
|
103
|
+
}
|