mollie-api-typescript 1.4.1 → 1.5.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/README.md +57 -0
- package/dist/commonjs/funcs/mandatesList.js +1 -0
- package/dist/commonjs/funcs/mandatesList.js.map +1 -1
- package/dist/commonjs/index.d.ts +1 -0
- package/dist/commonjs/index.d.ts.map +1 -1
- package/dist/commonjs/index.js +1 -0
- package/dist/commonjs/index.js.map +1 -1
- package/dist/commonjs/lib/config.d.ts +2 -2
- package/dist/commonjs/lib/config.js +2 -2
- package/dist/commonjs/models/index.d.ts +1 -0
- package/dist/commonjs/models/index.d.ts.map +1 -1
- package/dist/commonjs/models/index.js +1 -0
- package/dist/commonjs/models/index.js.map +1 -1
- package/dist/commonjs/models/listmandateresponse.d.ts +26 -0
- package/dist/commonjs/models/listmandateresponse.d.ts.map +1 -1
- package/dist/commonjs/models/listmandateresponse.js +15 -1
- package/dist/commonjs/models/listmandateresponse.js.map +1 -1
- package/dist/commonjs/models/listpaymentresponse.d.ts +3 -3
- package/dist/commonjs/models/listsettlementpaymentresponse.d.ts +3 -3
- package/dist/commonjs/models/mandateresponse.d.ts +26 -0
- package/dist/commonjs/models/mandateresponse.d.ts.map +1 -1
- package/dist/commonjs/models/mandateresponse.js +14 -1
- package/dist/commonjs/models/mandateresponse.js.map +1 -1
- package/dist/commonjs/models/mandatescopes.d.ts +22 -0
- package/dist/commonjs/models/mandatescopes.d.ts.map +1 -0
- package/dist/commonjs/models/mandatescopes.js +54 -0
- package/dist/commonjs/models/mandatescopes.js.map +1 -0
- package/dist/commonjs/models/operations/listmandates.d.ts +5 -0
- package/dist/commonjs/models/operations/listmandates.d.ts.map +1 -1
- package/dist/commonjs/models/operations/listmandates.js +1 -0
- package/dist/commonjs/models/operations/listmandates.js.map +1 -1
- package/dist/commonjs/models/paymentrequest.d.ts +11 -3
- package/dist/commonjs/models/paymentrequest.d.ts.map +1 -1
- package/dist/commonjs/models/paymentrequest.js +1 -0
- package/dist/commonjs/models/paymentrequest.js.map +1 -1
- package/dist/commonjs/models/paymentresponse.d.ts +3 -3
- package/dist/commonjs/utils/webhooks/index.d.ts +2 -0
- package/dist/commonjs/utils/webhooks/index.d.ts.map +1 -0
- package/dist/commonjs/utils/webhooks/index.js +7 -0
- package/dist/commonjs/utils/webhooks/index.js.map +1 -0
- package/dist/commonjs/utils/webhooks/signature_validator.d.ts +17 -0
- package/dist/commonjs/utils/webhooks/signature_validator.d.ts.map +1 -0
- package/dist/commonjs/utils/webhooks/signature_validator.js +88 -0
- package/dist/commonjs/utils/webhooks/signature_validator.js.map +1 -0
- package/dist/esm/funcs/mandatesList.js +1 -0
- package/dist/esm/funcs/mandatesList.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/config.d.ts +2 -2
- package/dist/esm/lib/config.js +2 -2
- package/dist/esm/models/index.d.ts +1 -0
- package/dist/esm/models/index.d.ts.map +1 -1
- package/dist/esm/models/index.js +1 -0
- package/dist/esm/models/index.js.map +1 -1
- package/dist/esm/models/listmandateresponse.d.ts +26 -0
- package/dist/esm/models/listmandateresponse.d.ts.map +1 -1
- package/dist/esm/models/listmandateresponse.js +14 -0
- package/dist/esm/models/listmandateresponse.js.map +1 -1
- package/dist/esm/models/listpaymentresponse.d.ts +3 -3
- package/dist/esm/models/listsettlementpaymentresponse.d.ts +3 -3
- package/dist/esm/models/mandateresponse.d.ts +26 -0
- package/dist/esm/models/mandateresponse.d.ts.map +1 -1
- package/dist/esm/models/mandateresponse.js +13 -0
- package/dist/esm/models/mandateresponse.js.map +1 -1
- package/dist/esm/models/mandatescopes.d.ts +22 -0
- package/dist/esm/models/mandatescopes.d.ts.map +1 -0
- package/dist/esm/models/mandatescopes.js +18 -0
- package/dist/esm/models/mandatescopes.js.map +1 -0
- package/dist/esm/models/operations/listmandates.d.ts +5 -0
- package/dist/esm/models/operations/listmandates.d.ts.map +1 -1
- package/dist/esm/models/operations/listmandates.js +1 -0
- package/dist/esm/models/operations/listmandates.js.map +1 -1
- package/dist/esm/models/paymentrequest.d.ts +11 -3
- package/dist/esm/models/paymentrequest.d.ts.map +1 -1
- package/dist/esm/models/paymentrequest.js +1 -0
- package/dist/esm/models/paymentrequest.js.map +1 -1
- package/dist/esm/models/paymentresponse.d.ts +3 -3
- package/dist/esm/utils/webhooks/index.d.ts +2 -0
- package/dist/esm/utils/webhooks/index.d.ts.map +1 -0
- package/dist/esm/utils/webhooks/index.js +2 -0
- package/dist/esm/utils/webhooks/index.js.map +1 -0
- package/dist/esm/utils/webhooks/signature_validator.d.ts +17 -0
- package/dist/esm/utils/webhooks/signature_validator.d.ts.map +1 -0
- package/dist/esm/utils/webhooks/signature_validator.js +83 -0
- package/dist/esm/utils/webhooks/signature_validator.js.map +1 -0
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/funcs/mandatesList.ts +1 -0
- package/src/index.ts +1 -0
- package/src/lib/config.ts +2 -2
- package/src/models/index.ts +1 -0
- package/src/models/listmandateresponse.ts +36 -0
- package/src/models/listpaymentresponse.ts +3 -3
- package/src/models/listsettlementpaymentresponse.ts +3 -3
- package/src/models/mandateresponse.ts +33 -0
- package/src/models/mandatescopes.ts +30 -0
- package/src/models/operations/listmandates.ts +6 -0
- package/src/models/paymentrequest.ts +12 -3
- package/src/models/paymentresponse.ts +3 -3
- package/src/utils/webhooks/index.ts +4 -0
- package/src/utils/webhooks/signature_validator.ts +136 -0
|
@@ -1027,12 +1027,12 @@ export type ListSettlementPaymentResponse = {
|
|
|
1027
1027
|
*/
|
|
1028
1028
|
subscriptionId?: string | null | undefined;
|
|
1029
1029
|
/**
|
|
1030
|
-
* **Only relevant for recurring payments.**
|
|
1030
|
+
* **Only relevant for recurring payments and stored cards.**
|
|
1031
1031
|
*
|
|
1032
1032
|
* @remarks
|
|
1033
1033
|
*
|
|
1034
|
-
* When creating recurring payments, the ID of a specific [mandate](get-mandate) can be supplied to indicate which of
|
|
1035
|
-
* the customer's accounts should be
|
|
1034
|
+
* When creating recurring or stored cards payments, the ID of a specific [mandate](get-mandate) can be supplied to indicate which of
|
|
1035
|
+
* the customer's accounts should be debited.
|
|
1036
1036
|
*/
|
|
1037
1037
|
mandateId?: string | null | undefined;
|
|
1038
1038
|
customerId?: string | undefined;
|
|
@@ -59,6 +59,24 @@ export type MandateResponseDetails = {
|
|
|
59
59
|
cardFingerprint?: string | null | undefined;
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* An array defining the eligible use cases for the mandate. For creditcard mandates, this field will always be
|
|
64
|
+
*
|
|
65
|
+
* @remarks
|
|
66
|
+
* present and can contain one or both of the following values:
|
|
67
|
+
*/
|
|
68
|
+
export const MandateResponseScope = {
|
|
69
|
+
CustomerPresent: "customer-present",
|
|
70
|
+
CustomerNotPresent: "customer-not-present",
|
|
71
|
+
} as const;
|
|
72
|
+
/**
|
|
73
|
+
* An array defining the eligible use cases for the mandate. For creditcard mandates, this field will always be
|
|
74
|
+
*
|
|
75
|
+
* @remarks
|
|
76
|
+
* present and can contain one or both of the following values:
|
|
77
|
+
*/
|
|
78
|
+
export type MandateResponseScope = OpenEnum<typeof MandateResponseScope>;
|
|
79
|
+
|
|
62
80
|
/**
|
|
63
81
|
* The status of the mandate. A status can be `pending` for mandates when the first payment is not yet finalized, or
|
|
64
82
|
*
|
|
@@ -129,6 +147,13 @@ export type MandateResponse = {
|
|
|
129
147
|
* decline Direct Debit payments if the mandate reference is not unique.
|
|
130
148
|
*/
|
|
131
149
|
mandateReference: string | null;
|
|
150
|
+
/**
|
|
151
|
+
* An array defining the eligible use cases for the mandate. This field will always be
|
|
152
|
+
*
|
|
153
|
+
* @remarks
|
|
154
|
+
* present and can contain one or both of the following values:
|
|
155
|
+
*/
|
|
156
|
+
scopes?: Array<MandateResponseScope> | null | undefined;
|
|
132
157
|
status: MandateResponseStatus;
|
|
133
158
|
/**
|
|
134
159
|
* The identifier referring to the [customer](get-customer) this mandate was linked to.
|
|
@@ -171,6 +196,13 @@ export function mandateResponseDetailsFromJSON(
|
|
|
171
196
|
);
|
|
172
197
|
}
|
|
173
198
|
|
|
199
|
+
/** @internal */
|
|
200
|
+
export const MandateResponseScope$inboundSchema: z.ZodType<
|
|
201
|
+
MandateResponseScope,
|
|
202
|
+
z.ZodTypeDef,
|
|
203
|
+
unknown
|
|
204
|
+
> = openEnums.inboundSchema(MandateResponseScope);
|
|
205
|
+
|
|
174
206
|
/** @internal */
|
|
175
207
|
export const MandateResponseStatus$inboundSchema: z.ZodType<
|
|
176
208
|
MandateResponseStatus,
|
|
@@ -212,6 +244,7 @@ export const MandateResponse$inboundSchema: z.ZodType<
|
|
|
212
244
|
details: z.lazy(() => MandateResponseDetails$inboundSchema),
|
|
213
245
|
signatureDate: z.nullable(z.string()),
|
|
214
246
|
mandateReference: z.nullable(z.string()),
|
|
247
|
+
scopes: z.nullable(z.array(MandateResponseScope$inboundSchema)).optional(),
|
|
215
248
|
status: MandateResponseStatus$inboundSchema,
|
|
216
249
|
customerId: z.string(),
|
|
217
250
|
createdAt: z.string(),
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
|
3
|
+
* @generated-id: 578dd7beb9f7
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as z from "zod/v3";
|
|
7
|
+
import { ClosedEnum } from "../types/enums.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* An array defining the eligible use cases for the mandate. For creditcard mandates, this field will always be
|
|
11
|
+
*
|
|
12
|
+
* @remarks
|
|
13
|
+
* present and can contain one or both of the following values:
|
|
14
|
+
*/
|
|
15
|
+
export const MandateScopes = {
|
|
16
|
+
CustomerPresent: "customer-present",
|
|
17
|
+
CustomerNotPresent: "customer-not-present",
|
|
18
|
+
} as const;
|
|
19
|
+
/**
|
|
20
|
+
* An array defining the eligible use cases for the mandate. For creditcard mandates, this field will always be
|
|
21
|
+
*
|
|
22
|
+
* @remarks
|
|
23
|
+
* present and can contain one or both of the following values:
|
|
24
|
+
*/
|
|
25
|
+
export type MandateScopes = ClosedEnum<typeof MandateScopes>;
|
|
26
|
+
|
|
27
|
+
/** @internal */
|
|
28
|
+
export const MandateScopes$outboundSchema: z.ZodNativeEnum<
|
|
29
|
+
typeof MandateScopes
|
|
30
|
+
> = z.nativeEnum(MandateScopes);
|
|
@@ -46,6 +46,10 @@ export type ListMandatesRequest = {
|
|
|
46
46
|
* newest to oldest.
|
|
47
47
|
*/
|
|
48
48
|
sort?: models.Sorting | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Returns only mandates that include the specified scopes.
|
|
51
|
+
*/
|
|
52
|
+
scopes?: Array<models.MandateScopes> | undefined;
|
|
49
53
|
/**
|
|
50
54
|
* Most API credentials are specifically created for either live mode or test mode. In those cases the `testmode` query
|
|
51
55
|
*
|
|
@@ -100,6 +104,7 @@ export type ListMandatesRequest$Outbound = {
|
|
|
100
104
|
from?: string | undefined;
|
|
101
105
|
limit?: number | null | undefined;
|
|
102
106
|
sort?: string | undefined;
|
|
107
|
+
scopes?: Array<string> | undefined;
|
|
103
108
|
testmode?: boolean | undefined;
|
|
104
109
|
"idempotency-key"?: string | undefined;
|
|
105
110
|
};
|
|
@@ -114,6 +119,7 @@ export const ListMandatesRequest$outboundSchema: z.ZodType<
|
|
|
114
119
|
from: z.string().optional(),
|
|
115
120
|
limit: z.nullable(z.number().int()).optional(),
|
|
116
121
|
sort: models.Sorting$outboundSchema.optional(),
|
|
122
|
+
scopes: z.array(models.MandateScopes$outboundSchema).optional(),
|
|
117
123
|
testmode: z.boolean().optional(),
|
|
118
124
|
idempotencyKey: z.string().optional(),
|
|
119
125
|
}).transform((v) => {
|
|
@@ -445,12 +445,12 @@ export type PaymentRequest = {
|
|
|
445
445
|
routing?: Array<EntityPaymentRoute> | null | undefined;
|
|
446
446
|
sequenceType?: SequenceType | undefined;
|
|
447
447
|
/**
|
|
448
|
-
* **Only relevant for recurring payments.**
|
|
448
|
+
* **Only relevant for recurring payments and stored cards.**
|
|
449
449
|
*
|
|
450
450
|
* @remarks
|
|
451
451
|
*
|
|
452
|
-
* When creating recurring payments, the ID of a specific [mandate](get-mandate) can be supplied to indicate which of
|
|
453
|
-
* the customer's accounts should be
|
|
452
|
+
* When creating recurring or stored cards payments, the ID of a specific [mandate](get-mandate) can be supplied to indicate which of
|
|
453
|
+
* the customer's accounts should be debited.
|
|
454
454
|
*/
|
|
455
455
|
mandateId?: string | null | undefined;
|
|
456
456
|
customerId?: string | undefined;
|
|
@@ -468,6 +468,13 @@ export type PaymentRequest = {
|
|
|
468
468
|
* The date by which the payment should be completed in `YYYY-MM-DD` format
|
|
469
469
|
*/
|
|
470
470
|
dueDate?: string | undefined;
|
|
471
|
+
/**
|
|
472
|
+
* Whether the card details should be stored for the customer after a successful payment. This will create a mandate for the customer,
|
|
473
|
+
*
|
|
474
|
+
* @remarks
|
|
475
|
+
* allowing for future customer present saved-card CIT payments. Requires customerId, cardToken, and the creditcard method to be specified.
|
|
476
|
+
*/
|
|
477
|
+
storeCredentials?: boolean | undefined;
|
|
471
478
|
/**
|
|
472
479
|
* Whether to create the entity in test mode or live mode.
|
|
473
480
|
*
|
|
@@ -740,6 +747,7 @@ export type PaymentRequest$Outbound = {
|
|
|
740
747
|
customerId?: string | undefined;
|
|
741
748
|
profileId?: string | undefined;
|
|
742
749
|
dueDate?: string | undefined;
|
|
750
|
+
storeCredentials?: boolean | undefined;
|
|
743
751
|
testmode?: boolean | null | undefined;
|
|
744
752
|
applePayPaymentToken?: string | undefined;
|
|
745
753
|
company?: Company$Outbound | undefined;
|
|
@@ -791,6 +799,7 @@ export const PaymentRequest$outboundSchema: z.ZodType<
|
|
|
791
799
|
customerId: z.string().optional(),
|
|
792
800
|
profileId: z.string().optional(),
|
|
793
801
|
dueDate: z.string().optional(),
|
|
802
|
+
storeCredentials: z.boolean().optional(),
|
|
794
803
|
testmode: z.nullable(z.boolean()).optional(),
|
|
795
804
|
applePayPaymentToken: z.string().optional(),
|
|
796
805
|
company: z.lazy(() => Company$outboundSchema).optional(),
|
|
@@ -1044,12 +1044,12 @@ export type PaymentResponse = {
|
|
|
1044
1044
|
*/
|
|
1045
1045
|
subscriptionId?: string | null | undefined;
|
|
1046
1046
|
/**
|
|
1047
|
-
* **Only relevant for recurring payments.**
|
|
1047
|
+
* **Only relevant for recurring payments and stored cards.**
|
|
1048
1048
|
*
|
|
1049
1049
|
* @remarks
|
|
1050
1050
|
*
|
|
1051
|
-
* When creating recurring payments, the ID of a specific [mandate](get-mandate) can be supplied to indicate which of
|
|
1052
|
-
* the customer's accounts should be
|
|
1051
|
+
* When creating recurring or stored cards payments, the ID of a specific [mandate](get-mandate) can be supplied to indicate which of
|
|
1052
|
+
* the customer's accounts should be debited.
|
|
1053
1053
|
*/
|
|
1054
1054
|
mandateId?: string | null | undefined;
|
|
1055
1055
|
customerId?: string | undefined;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
export class InvalidSignatureException extends Error {
|
|
2
|
+
constructor(message: string) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = "InvalidSignatureException";
|
|
5
|
+
|
|
6
|
+
Object.setPrototypeOf(this, InvalidSignatureException.prototype);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class SignatureValidator {
|
|
11
|
+
static readonly SIGNATURE_HEADER = "X-Mollie-Signature";
|
|
12
|
+
private static readonly SIGNATURE_PREFIX = "sha256=";
|
|
13
|
+
|
|
14
|
+
private readonly signingSecrets: string[];
|
|
15
|
+
|
|
16
|
+
constructor(signingSecrets: string | string[]) {
|
|
17
|
+
this.signingSecrets = Array.isArray(signingSecrets)
|
|
18
|
+
? [...signingSecrets]
|
|
19
|
+
: [signingSecrets];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static async validate(
|
|
23
|
+
payload: string,
|
|
24
|
+
signingSecrets: string | string[],
|
|
25
|
+
signatures?: string | string[] | null,
|
|
26
|
+
): Promise<boolean> {
|
|
27
|
+
return new SignatureValidator(signingSecrets).validatePayload(
|
|
28
|
+
payload,
|
|
29
|
+
signatures,
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async validatePayload(
|
|
34
|
+
payload: string,
|
|
35
|
+
signatures?: string | string[] | null,
|
|
36
|
+
): Promise<boolean> {
|
|
37
|
+
const signatureList = this.normalizeSignatures(signatures);
|
|
38
|
+
|
|
39
|
+
if (signatureList.length === 0) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return this.validateSignatures(payload, signatureList);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private normalizeSignatures(
|
|
47
|
+
signatures?: string | string[] | null,
|
|
48
|
+
): string[] {
|
|
49
|
+
if (typeof signatures === "string") {
|
|
50
|
+
return signatures ? [signatures] : [];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!signatures) {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return signatures.filter((signature): signature is string => !!signature);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private async validateSignatures(
|
|
61
|
+
payload: string,
|
|
62
|
+
signatures: string[],
|
|
63
|
+
): Promise<boolean> {
|
|
64
|
+
for (const signature of signatures) {
|
|
65
|
+
const extractedSignature = this.extractSignature(signature);
|
|
66
|
+
|
|
67
|
+
if (await this.isValidSignature(extractedSignature, payload)) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
throw new InvalidSignatureException("Invalid webhook signature");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private extractSignature(signatureHeader: string): string {
|
|
76
|
+
if (signatureHeader.startsWith(SignatureValidator.SIGNATURE_PREFIX)) {
|
|
77
|
+
return signatureHeader.slice(SignatureValidator.SIGNATURE_PREFIX.length);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return signatureHeader;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private async isValidSignature(
|
|
84
|
+
providedSignature: string,
|
|
85
|
+
payload: string,
|
|
86
|
+
): Promise<boolean> {
|
|
87
|
+
for (const secret of this.signingSecrets) {
|
|
88
|
+
const expectedSignature = await SignatureValidator.createSignature(
|
|
89
|
+
payload,
|
|
90
|
+
secret,
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
if (constantTimeEquals(expectedSignature, providedSignature)) {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
static async createSignature(payload: string, secret: string): Promise<string> {
|
|
102
|
+
const subtle = globalThis.crypto?.subtle;
|
|
103
|
+
if (!subtle) {
|
|
104
|
+
throw new Error("Web Crypto API is not available in this runtime");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const encoder = new TextEncoder();
|
|
108
|
+
const key = await subtle.importKey(
|
|
109
|
+
"raw",
|
|
110
|
+
encoder.encode(secret),
|
|
111
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
112
|
+
false,
|
|
113
|
+
["sign"],
|
|
114
|
+
);
|
|
115
|
+
const signature = await subtle.sign("HMAC", key, encoder.encode(payload));
|
|
116
|
+
|
|
117
|
+
return toHex(signature);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function constantTimeEquals(left: string, right: string): boolean {
|
|
122
|
+
const maxLength = Math.max(left.length, right.length);
|
|
123
|
+
let mismatch = left.length ^ right.length;
|
|
124
|
+
|
|
125
|
+
for (let index = 0; index < maxLength; index++) {
|
|
126
|
+
mismatch |= (left.charCodeAt(index) || 0) ^ (right.charCodeAt(index) || 0);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return mismatch === 0;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function toHex(buffer: ArrayBuffer): string {
|
|
133
|
+
return Array.from(new Uint8Array(buffer))
|
|
134
|
+
.map((value) => value.toString(16).padStart(2, "0"))
|
|
135
|
+
.join("");
|
|
136
|
+
}
|