pay_hash 1.0.2 → 1.0.3
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.ts +90 -0
- package/dist/index.js +255 -2
- package/package.json +9 -3
- package/pay_hash-1.0.0.tgz +0 -0
- package/src/viem.config.ts +7 -7
- package/dist/client.js +0 -163
- package/dist/constants.js +0 -1
- package/dist/contracts.js +0 -2
- package/dist/mailer.js +0 -13
- package/dist/pdf.js +0 -19
- package/dist/render.js +0 -24
- package/dist/types.js +0 -1
- package/dist/utils.js +0 -1
- package/dist/viem.config.js +0 -12
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { WalletClient, Transport, Chain, Account, WalletActions, PublicActions, Client, RpcSchema, Address, Hex } from 'viem';
|
|
2
|
+
import { TempoActions, Actions } from 'viem/tempo';
|
|
3
|
+
|
|
4
|
+
type PayHashViemClient = WalletClient<Transport, Chain, Account> & WalletActions<Chain, Account> & PublicActions<Transport, Chain> & TempoActions<Chain, Account> & Omit<Client<Transport, Chain, Account, RpcSchema>, "cacheTime">;
|
|
5
|
+
interface PayHashSDKConfig {
|
|
6
|
+
client: PayHashViemClient;
|
|
7
|
+
}
|
|
8
|
+
interface PayParams {
|
|
9
|
+
token: Address;
|
|
10
|
+
amount: string;
|
|
11
|
+
memo: Hex;
|
|
12
|
+
orgAddress: Address;
|
|
13
|
+
orgName: string;
|
|
14
|
+
orgMail: string;
|
|
15
|
+
clientMail: string;
|
|
16
|
+
additionalInfo?: Hex;
|
|
17
|
+
}
|
|
18
|
+
interface SMTPConfig {
|
|
19
|
+
host: string;
|
|
20
|
+
port: number;
|
|
21
|
+
secure?: boolean;
|
|
22
|
+
user: string;
|
|
23
|
+
pass: string;
|
|
24
|
+
}
|
|
25
|
+
type EmailTemplate = {
|
|
26
|
+
pdfFileName?: string;
|
|
27
|
+
pdfTemplatePath?: string;
|
|
28
|
+
pdfTemplateName?: string;
|
|
29
|
+
emailTemplatePath?: string;
|
|
30
|
+
emailTemplateName?: string;
|
|
31
|
+
subject?: string;
|
|
32
|
+
brandName: string;
|
|
33
|
+
logoUrl?: string;
|
|
34
|
+
primaryColor?: string;
|
|
35
|
+
secondaryColor?: string;
|
|
36
|
+
backgroundColor?: string;
|
|
37
|
+
textColor?: string;
|
|
38
|
+
fontFamily?: "inter" | "roboto" | "poppins";
|
|
39
|
+
titleFontSize?: number;
|
|
40
|
+
bodyFontSize?: number;
|
|
41
|
+
footerFontSize?: number;
|
|
42
|
+
layout?: "compact" | "standard" | "spacious";
|
|
43
|
+
align?: "left" | "center";
|
|
44
|
+
receiptTitle?: string;
|
|
45
|
+
footerText?: string;
|
|
46
|
+
showTxHash?: boolean;
|
|
47
|
+
showTimestamp?: boolean;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
declare class PayHashClient {
|
|
51
|
+
private client;
|
|
52
|
+
private transporter;
|
|
53
|
+
private emailTemplate;
|
|
54
|
+
constructor(client: PayHashViemClient, smtp: SMTPConfig, emailTemplate: EmailTemplate);
|
|
55
|
+
verifyTransporter(): Promise<true>;
|
|
56
|
+
pay(params: PayParams): Promise<{
|
|
57
|
+
success: boolean;
|
|
58
|
+
receipt: Actions.token.transferSync.ReturnValue;
|
|
59
|
+
account: `0x${string}`;
|
|
60
|
+
error?: undefined;
|
|
61
|
+
} | {
|
|
62
|
+
success: boolean;
|
|
63
|
+
error: any;
|
|
64
|
+
receipt?: undefined;
|
|
65
|
+
account?: undefined;
|
|
66
|
+
}>;
|
|
67
|
+
batchPay(params: PayParams[]): Promise<{
|
|
68
|
+
success: boolean;
|
|
69
|
+
message: string;
|
|
70
|
+
}>;
|
|
71
|
+
batchPayAsync(params: PayParams[]): Promise<{
|
|
72
|
+
success: boolean;
|
|
73
|
+
results: ({
|
|
74
|
+
success: boolean;
|
|
75
|
+
index: number;
|
|
76
|
+
orgAddress: `0x${string}`;
|
|
77
|
+
txHash: `0x${string}`;
|
|
78
|
+
error?: undefined;
|
|
79
|
+
} | {
|
|
80
|
+
success: boolean;
|
|
81
|
+
index: number;
|
|
82
|
+
orgAddress: `0x${string}`;
|
|
83
|
+
error: any;
|
|
84
|
+
txHash?: undefined;
|
|
85
|
+
})[];
|
|
86
|
+
}>;
|
|
87
|
+
private sendReceiptAndEmail;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export { type EmailTemplate, PayHashClient, type PayHashSDKConfig, type PayHashViemClient, type PayParams, type SMTPConfig };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,255 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// src/client.ts
|
|
2
|
+
import { parseUnits, stringToHex } from "viem";
|
|
3
|
+
|
|
4
|
+
// src/pdf.ts
|
|
5
|
+
import puppeteer from "puppeteer";
|
|
6
|
+
import Handlebars from "handlebars";
|
|
7
|
+
import fs from "fs";
|
|
8
|
+
async function generateReceiptPDF(templatePath, data) {
|
|
9
|
+
const html = fs.readFileSync(templatePath, "utf8");
|
|
10
|
+
const compiled = Handlebars.compile(html);
|
|
11
|
+
const content = compiled(data);
|
|
12
|
+
const browser = await puppeteer.launch({
|
|
13
|
+
args: ["--no-sandbox"]
|
|
14
|
+
});
|
|
15
|
+
const page = await browser.newPage();
|
|
16
|
+
await page.setContent(content, { waitUntil: "networkidle0" });
|
|
17
|
+
const pdf = await page.pdf({
|
|
18
|
+
format: "A4",
|
|
19
|
+
printBackground: true
|
|
20
|
+
});
|
|
21
|
+
await browser.close();
|
|
22
|
+
return pdf;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/mailer.ts
|
|
26
|
+
import nodemailer from "nodemailer";
|
|
27
|
+
function createTransporter(smtp) {
|
|
28
|
+
return nodemailer.createTransport({
|
|
29
|
+
host: smtp.host,
|
|
30
|
+
port: smtp.port,
|
|
31
|
+
secure: smtp.secure ?? smtp.port === 465,
|
|
32
|
+
auth: {
|
|
33
|
+
user: smtp.user,
|
|
34
|
+
pass: smtp.pass
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// src/client.ts
|
|
40
|
+
import path2 from "path";
|
|
41
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
42
|
+
|
|
43
|
+
// src/constants.ts
|
|
44
|
+
var TEMPO_TESTNET_CHAIN_ID = 42429;
|
|
45
|
+
|
|
46
|
+
// src/render.ts
|
|
47
|
+
import Handlebars2 from "handlebars";
|
|
48
|
+
import fs2 from "fs";
|
|
49
|
+
import path from "path";
|
|
50
|
+
import { fileURLToPath } from "url";
|
|
51
|
+
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
52
|
+
function renderReceiptHtml({
|
|
53
|
+
receipt,
|
|
54
|
+
template,
|
|
55
|
+
params,
|
|
56
|
+
tokenName
|
|
57
|
+
}) {
|
|
58
|
+
const templateSource = fs2.readFileSync(
|
|
59
|
+
path.join(
|
|
60
|
+
template.emailTemplatePath ?? __dirname,
|
|
61
|
+
template.emailTemplateName ?? "mailTemplate.hbs"
|
|
62
|
+
),
|
|
63
|
+
"utf8"
|
|
64
|
+
);
|
|
65
|
+
const templates = Handlebars2.compile(templateSource);
|
|
66
|
+
return templates({
|
|
67
|
+
brandName: template.brandName || params.orgName,
|
|
68
|
+
logoUrl: template.logoUrl || "",
|
|
69
|
+
primaryColor: template.primaryColor || "#b5a8b0ff",
|
|
70
|
+
titleFontSize: template.titleFontSize || 20,
|
|
71
|
+
bodyFontSize: template.bodyFontSize || 14,
|
|
72
|
+
receiptTitle: template.subject || "Payment Receipt",
|
|
73
|
+
footerText: template.footerText || "Powered by PayHash",
|
|
74
|
+
txHash: receipt.transactionHash,
|
|
75
|
+
payer: receipt.from,
|
|
76
|
+
timestamp: new Date(receipt.timestamp).toUTCString(),
|
|
77
|
+
year: (/* @__PURE__ */ new Date()).getFullYear(),
|
|
78
|
+
amount: params.amount,
|
|
79
|
+
token: tokenName
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/client.ts
|
|
84
|
+
import { Actions } from "viem/tempo";
|
|
85
|
+
var __dirname2 = path2.dirname(fileURLToPath2(import.meta.url));
|
|
86
|
+
var pdfPath = path2.join(__dirname2, "./receipt.hbs");
|
|
87
|
+
var PayHashClient = class {
|
|
88
|
+
client;
|
|
89
|
+
transporter;
|
|
90
|
+
emailTemplate;
|
|
91
|
+
constructor(client, smtp, emailTemplate) {
|
|
92
|
+
this.client = client;
|
|
93
|
+
this.transporter = createTransporter(smtp);
|
|
94
|
+
this.emailTemplate = emailTemplate;
|
|
95
|
+
}
|
|
96
|
+
async verifyTransporter() {
|
|
97
|
+
return await this.transporter.verify();
|
|
98
|
+
}
|
|
99
|
+
// private loadEmailTemplate(filePath: string, fileName: string) {
|
|
100
|
+
// const templatePath = path.join(filePath, fileName);
|
|
101
|
+
// const html = fs.readFileSync(templatePath, "utf-8");
|
|
102
|
+
// return html;
|
|
103
|
+
// }
|
|
104
|
+
async pay(params) {
|
|
105
|
+
try {
|
|
106
|
+
const [account] = await this.client?.getAddresses();
|
|
107
|
+
const metadata = await this.client.token.getMetadata({
|
|
108
|
+
token: params.token
|
|
109
|
+
});
|
|
110
|
+
const tokenName = metadata.symbol;
|
|
111
|
+
const decimals = metadata.decimals;
|
|
112
|
+
const chainId = this.client.chain.id;
|
|
113
|
+
let receipt = null;
|
|
114
|
+
if (chainId == TEMPO_TESTNET_CHAIN_ID) {
|
|
115
|
+
receipt = await this.client.token.transferSync({
|
|
116
|
+
amount: parseUnits(params.amount, decimals),
|
|
117
|
+
memo: stringToHex(params.memo || "0x"),
|
|
118
|
+
token: params.token,
|
|
119
|
+
to: params.orgAddress
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
if (receipt == null) {
|
|
123
|
+
throw new Error("Receipt not found");
|
|
124
|
+
}
|
|
125
|
+
await this.sendReceiptAndEmail({
|
|
126
|
+
params,
|
|
127
|
+
receipt,
|
|
128
|
+
txHash: receipt.receipt.transactionHash,
|
|
129
|
+
tokenName: metadata.symbol
|
|
130
|
+
});
|
|
131
|
+
return {
|
|
132
|
+
success: true,
|
|
133
|
+
receipt,
|
|
134
|
+
account
|
|
135
|
+
};
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error("[PayHashClient Error]", error);
|
|
138
|
+
return {
|
|
139
|
+
success: false,
|
|
140
|
+
error: error?.message || "Unknown error occurred"
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// bathch Pay sync to multiple addresses at once
|
|
145
|
+
async batchPay(params) {
|
|
146
|
+
for (const param of params) {
|
|
147
|
+
await this.pay(param);
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
success: true,
|
|
151
|
+
message: "Batch payment successful"
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
// batch Pay async to multiple addresses at once
|
|
155
|
+
async batchPayAsync(params) {
|
|
156
|
+
const [account] = await this.client.getAddresses();
|
|
157
|
+
const nonceKeys = params.map((_, i) => BigInt(i + 1));
|
|
158
|
+
const nonces = await Promise.all(
|
|
159
|
+
nonceKeys.map(
|
|
160
|
+
(nonceKey) => Actions.nonce.getNonce(this.client, { account, nonceKey })
|
|
161
|
+
)
|
|
162
|
+
);
|
|
163
|
+
const results = await Promise.all(
|
|
164
|
+
params.map(async (param, i) => {
|
|
165
|
+
try {
|
|
166
|
+
const metadata = await this.client.token.getMetadata({
|
|
167
|
+
token: param.token
|
|
168
|
+
});
|
|
169
|
+
const decimals = metadata.decimals;
|
|
170
|
+
const receipt = await Actions.token.transferSync(this.client, {
|
|
171
|
+
amount: parseUnits(param.amount, decimals),
|
|
172
|
+
to: param.orgAddress,
|
|
173
|
+
token: param.token,
|
|
174
|
+
// memo: stringToHex(param.memo || "0x"),
|
|
175
|
+
nonceKey: nonceKeys[i],
|
|
176
|
+
nonce: Number(nonces[i])
|
|
177
|
+
});
|
|
178
|
+
await this.sendReceiptAndEmail({
|
|
179
|
+
params: param,
|
|
180
|
+
receipt,
|
|
181
|
+
txHash: receipt.receipt.transactionHash,
|
|
182
|
+
tokenName: metadata.symbol
|
|
183
|
+
});
|
|
184
|
+
return {
|
|
185
|
+
success: true,
|
|
186
|
+
index: i,
|
|
187
|
+
orgAddress: param.orgAddress,
|
|
188
|
+
txHash: receipt.receipt.transactionHash
|
|
189
|
+
};
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.error(
|
|
192
|
+
`[BatchPay Error] index=${i}, org=${param.orgAddress}`,
|
|
193
|
+
error
|
|
194
|
+
);
|
|
195
|
+
return {
|
|
196
|
+
success: false,
|
|
197
|
+
index: i,
|
|
198
|
+
orgAddress: param.orgAddress,
|
|
199
|
+
error: error?.message ?? "Unknown error"
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
);
|
|
204
|
+
return {
|
|
205
|
+
success: true,
|
|
206
|
+
results
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
async sendReceiptAndEmail({
|
|
210
|
+
params,
|
|
211
|
+
receipt,
|
|
212
|
+
txHash,
|
|
213
|
+
tokenName
|
|
214
|
+
}) {
|
|
215
|
+
const pdf = await generateReceiptPDF(
|
|
216
|
+
path2.join(
|
|
217
|
+
this.emailTemplate?.pdfTemplatePath ?? __dirname2,
|
|
218
|
+
this.emailTemplate?.pdfTemplateName ?? "receipt.hbs"
|
|
219
|
+
),
|
|
220
|
+
{
|
|
221
|
+
brandName: this.emailTemplate?.brandName ?? params.orgName,
|
|
222
|
+
primaryColor: this.emailTemplate?.primaryColor ?? "#000",
|
|
223
|
+
receiptTitle: this.emailTemplate?.receiptTitle ?? "Payment Receipt",
|
|
224
|
+
amount: params.amount,
|
|
225
|
+
token: tokenName,
|
|
226
|
+
txHash,
|
|
227
|
+
date: (/* @__PURE__ */ new Date()).toUTCString(),
|
|
228
|
+
footerText: this.emailTemplate?.footerText ?? "Powered by PayHash",
|
|
229
|
+
fontFamily: this.emailTemplate?.fontFamily ?? "Inter",
|
|
230
|
+
titleFontSize: this.emailTemplate?.titleFontSize ?? 24
|
|
231
|
+
}
|
|
232
|
+
);
|
|
233
|
+
const html = renderReceiptHtml({
|
|
234
|
+
receipt,
|
|
235
|
+
template: this.emailTemplate,
|
|
236
|
+
params,
|
|
237
|
+
tokenName
|
|
238
|
+
});
|
|
239
|
+
await this.transporter.sendMail({
|
|
240
|
+
from: `${params.orgName} <${params.orgMail}>`,
|
|
241
|
+
to: `${params.clientMail}, ${params.orgMail}`,
|
|
242
|
+
subject: this.emailTemplate?.subject ?? "Payment Receipt",
|
|
243
|
+
html,
|
|
244
|
+
attachments: [
|
|
245
|
+
{
|
|
246
|
+
filename: this.emailTemplate?.pdfFileName ?? "receipt.pdf",
|
|
247
|
+
content: pdf
|
|
248
|
+
}
|
|
249
|
+
]
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
export {
|
|
254
|
+
PayHashClient
|
|
255
|
+
};
|
package/package.json
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pay_hash",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "PayHash SDK is a professional TypeScript library designed to streamline blockchain payments, receipt generation, and automated email notifications",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
7
13
|
"repository": {
|
|
8
14
|
"type": "git",
|
|
9
15
|
"url": "https://github.com/the-first-elder/pay_hash"
|
|
@@ -17,9 +23,9 @@
|
|
|
17
23
|
"author": "the-first-elder",
|
|
18
24
|
"twitter": "https://x.com/The_First_Elder",
|
|
19
25
|
"scripts": {
|
|
20
|
-
"build": "
|
|
26
|
+
"build": "tsup src/index.ts --format esm --dts && npm run copy-templates",
|
|
21
27
|
"copy-templates": "cpy 'src/**/*.hbs' 'dist'",
|
|
22
|
-
"dev": "
|
|
28
|
+
"dev": "tsup src/index.ts --format esm --dts --watch"
|
|
23
29
|
},
|
|
24
30
|
"license": "ISC",
|
|
25
31
|
"type": "module",
|
|
Binary file
|
package/src/viem.config.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { createClient, http, publicActions, walletActions } from
|
|
2
|
-
import { privateKeyToAccount } from
|
|
3
|
-
import { tempoTestnet } from
|
|
4
|
-
import { tempoActions } from
|
|
5
|
-
|
|
1
|
+
import { createClient, http, publicActions, walletActions } from "viem";
|
|
2
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
3
|
+
import { tempoTestnet } from "viem/chains";
|
|
4
|
+
import { tempoActions } from "viem/tempo";
|
|
5
|
+
|
|
6
6
|
export const client = createClient({
|
|
7
|
-
account: privateKeyToAccount(
|
|
7
|
+
account: privateKeyToAccount("0x..."),
|
|
8
8
|
chain: tempoTestnet,
|
|
9
9
|
transport: http(),
|
|
10
10
|
})
|
|
11
11
|
.extend(publicActions)
|
|
12
12
|
.extend(walletActions)
|
|
13
|
-
.extend(tempoActions())
|
|
13
|
+
.extend(tempoActions());
|
package/dist/client.js
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import { parseUnits, stringToHex } from "viem";
|
|
2
|
-
import { generateReceiptPDF } from "./pdf";
|
|
3
|
-
import { createTransporter } from "./mailer";
|
|
4
|
-
import path from "path";
|
|
5
|
-
import { fileURLToPath } from "url";
|
|
6
|
-
import { TEMPO_TESTNET_CHAIN_ID } from "./constants";
|
|
7
|
-
import { renderReceiptHtml } from "./render";
|
|
8
|
-
import { Actions } from "viem/tempo";
|
|
9
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
-
const pdfPath = path.join(__dirname, "./receipt.hbs");
|
|
11
|
-
export class PayHashClient {
|
|
12
|
-
client;
|
|
13
|
-
transporter;
|
|
14
|
-
emailTemplate;
|
|
15
|
-
constructor(client, smtp, emailTemplate) {
|
|
16
|
-
this.client = client;
|
|
17
|
-
this.transporter = createTransporter(smtp);
|
|
18
|
-
this.emailTemplate = emailTemplate;
|
|
19
|
-
}
|
|
20
|
-
async verifyTransporter() {
|
|
21
|
-
return await this.transporter.verify();
|
|
22
|
-
}
|
|
23
|
-
// private loadEmailTemplate(filePath: string, fileName: string) {
|
|
24
|
-
// const templatePath = path.join(filePath, fileName);
|
|
25
|
-
// const html = fs.readFileSync(templatePath, "utf-8");
|
|
26
|
-
// return html;
|
|
27
|
-
// }
|
|
28
|
-
async pay(params) {
|
|
29
|
-
try {
|
|
30
|
-
const [account] = await this.client?.getAddresses();
|
|
31
|
-
const metadata = await this.client.token.getMetadata({
|
|
32
|
-
token: params.token,
|
|
33
|
-
});
|
|
34
|
-
const tokenName = metadata.symbol;
|
|
35
|
-
const decimals = metadata.decimals;
|
|
36
|
-
const chainId = this.client.chain.id;
|
|
37
|
-
let receipt = null;
|
|
38
|
-
if (chainId == TEMPO_TESTNET_CHAIN_ID) {
|
|
39
|
-
receipt = await this.client.token.transferSync({
|
|
40
|
-
amount: parseUnits(params.amount, decimals),
|
|
41
|
-
memo: stringToHex(params.memo || "0x"),
|
|
42
|
-
token: params.token,
|
|
43
|
-
to: params.orgAddress,
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
if (receipt == null) {
|
|
47
|
-
throw new Error("Receipt not found");
|
|
48
|
-
}
|
|
49
|
-
await this.sendReceiptAndEmail({
|
|
50
|
-
params: params,
|
|
51
|
-
receipt: receipt,
|
|
52
|
-
txHash: receipt.receipt.transactionHash,
|
|
53
|
-
tokenName: metadata.symbol,
|
|
54
|
-
});
|
|
55
|
-
return {
|
|
56
|
-
success: true,
|
|
57
|
-
receipt,
|
|
58
|
-
account,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
catch (error) {
|
|
62
|
-
console.error("[PayHashClient Error]", error);
|
|
63
|
-
return {
|
|
64
|
-
success: false,
|
|
65
|
-
error: error?.message || "Unknown error occurred",
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
// bathch Pay sync to multiple addresses at once
|
|
70
|
-
async batchPay(params) {
|
|
71
|
-
for (const param of params) {
|
|
72
|
-
await this.pay(param);
|
|
73
|
-
}
|
|
74
|
-
return {
|
|
75
|
-
success: true,
|
|
76
|
-
message: "Batch payment successful",
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
// batch Pay async to multiple addresses at once
|
|
80
|
-
async batchPayAsync(params) {
|
|
81
|
-
const [account] = await this.client.getAddresses();
|
|
82
|
-
// 1️⃣ unique nonce key per tx
|
|
83
|
-
const nonceKeys = params.map((_, i) => BigInt(i + 1));
|
|
84
|
-
// 2️⃣ fetch all nonces in parallel
|
|
85
|
-
const nonces = await Promise.all(nonceKeys.map((nonceKey) => Actions.nonce.getNonce(this.client, { account, nonceKey })));
|
|
86
|
-
// 3️⃣ send all txs in parallel with per-item error handling
|
|
87
|
-
const results = await Promise.all(params.map(async (param, i) => {
|
|
88
|
-
try {
|
|
89
|
-
const metadata = await this.client.token.getMetadata({
|
|
90
|
-
token: param.token,
|
|
91
|
-
});
|
|
92
|
-
const decimals = metadata.decimals;
|
|
93
|
-
const receipt = await Actions.token.transferSync(this.client, {
|
|
94
|
-
amount: parseUnits(param.amount, decimals),
|
|
95
|
-
to: param.orgAddress,
|
|
96
|
-
token: param.token,
|
|
97
|
-
// memo: stringToHex(param.memo || "0x"),
|
|
98
|
-
nonceKey: nonceKeys[i],
|
|
99
|
-
nonce: Number(nonces[i]),
|
|
100
|
-
});
|
|
101
|
-
await this.sendReceiptAndEmail({
|
|
102
|
-
params: param,
|
|
103
|
-
receipt: receipt,
|
|
104
|
-
txHash: receipt.receipt.transactionHash,
|
|
105
|
-
tokenName: metadata.symbol,
|
|
106
|
-
});
|
|
107
|
-
return {
|
|
108
|
-
success: true,
|
|
109
|
-
index: i,
|
|
110
|
-
orgAddress: param.orgAddress,
|
|
111
|
-
txHash: receipt.receipt.transactionHash,
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
catch (error) {
|
|
115
|
-
console.error(`[BatchPay Error] index=${i}, org=${param.orgAddress}`, error);
|
|
116
|
-
return {
|
|
117
|
-
success: false,
|
|
118
|
-
index: i,
|
|
119
|
-
orgAddress: param.orgAddress,
|
|
120
|
-
error: error?.message ?? "Unknown error",
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
}));
|
|
124
|
-
return {
|
|
125
|
-
success: true,
|
|
126
|
-
results,
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
async sendReceiptAndEmail({ params, receipt, txHash, tokenName, }) {
|
|
130
|
-
/* Generate PDF */
|
|
131
|
-
const pdf = await generateReceiptPDF(path.join(this.emailTemplate?.pdfTemplatePath ?? __dirname, this.emailTemplate?.pdfTemplateName ?? "receipt.hbs"), {
|
|
132
|
-
brandName: this.emailTemplate?.brandName ?? params.orgName,
|
|
133
|
-
primaryColor: this.emailTemplate?.primaryColor ?? "#000",
|
|
134
|
-
receiptTitle: this.emailTemplate?.receiptTitle ?? "Payment Receipt",
|
|
135
|
-
amount: params.amount,
|
|
136
|
-
token: tokenName,
|
|
137
|
-
txHash,
|
|
138
|
-
date: new Date().toUTCString(),
|
|
139
|
-
footerText: this.emailTemplate?.footerText ?? "Powered by PayHash",
|
|
140
|
-
fontFamily: this.emailTemplate?.fontFamily ?? "Inter",
|
|
141
|
-
titleFontSize: this.emailTemplate?.titleFontSize ?? 24,
|
|
142
|
-
});
|
|
143
|
-
const html = renderReceiptHtml({
|
|
144
|
-
receipt,
|
|
145
|
-
template: this.emailTemplate,
|
|
146
|
-
params,
|
|
147
|
-
tokenName,
|
|
148
|
-
});
|
|
149
|
-
/* Send email */
|
|
150
|
-
await this.transporter.sendMail({
|
|
151
|
-
from: `${params.orgName} <${params.orgMail}>`,
|
|
152
|
-
to: `${params.clientMail}, ${params.orgMail}`,
|
|
153
|
-
subject: this.emailTemplate?.subject ?? "Payment Receipt",
|
|
154
|
-
html,
|
|
155
|
-
attachments: [
|
|
156
|
-
{
|
|
157
|
-
filename: this.emailTemplate?.pdfFileName ?? "receipt.pdf",
|
|
158
|
-
content: pdf,
|
|
159
|
-
},
|
|
160
|
-
],
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
}
|
package/dist/constants.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const TEMPO_TESTNET_CHAIN_ID = 42429;
|
package/dist/contracts.js
DELETED
package/dist/mailer.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
// mailer.ts
|
|
2
|
-
import nodemailer from "nodemailer";
|
|
3
|
-
export function createTransporter(smtp) {
|
|
4
|
-
return nodemailer.createTransport({
|
|
5
|
-
host: smtp.host,
|
|
6
|
-
port: smtp.port,
|
|
7
|
-
secure: smtp.secure ?? smtp.port === 465,
|
|
8
|
-
auth: {
|
|
9
|
-
user: smtp.user,
|
|
10
|
-
pass: smtp.pass,
|
|
11
|
-
},
|
|
12
|
-
});
|
|
13
|
-
}
|
package/dist/pdf.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import puppeteer from "puppeteer";
|
|
2
|
-
import Handlebars from "handlebars";
|
|
3
|
-
import fs from "fs";
|
|
4
|
-
export async function generateReceiptPDF(templatePath, data) {
|
|
5
|
-
const html = fs.readFileSync(templatePath, "utf8");
|
|
6
|
-
const compiled = Handlebars.compile(html);
|
|
7
|
-
const content = compiled(data);
|
|
8
|
-
const browser = await puppeteer.launch({
|
|
9
|
-
args: ["--no-sandbox"],
|
|
10
|
-
});
|
|
11
|
-
const page = await browser.newPage();
|
|
12
|
-
await page.setContent(content, { waitUntil: "networkidle0" });
|
|
13
|
-
const pdf = await page.pdf({
|
|
14
|
-
format: "A4",
|
|
15
|
-
printBackground: true,
|
|
16
|
-
});
|
|
17
|
-
await browser.close();
|
|
18
|
-
return pdf;
|
|
19
|
-
}
|
package/dist/render.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import Handlebars from "handlebars";
|
|
2
|
-
import fs from "fs";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import { fileURLToPath } from "url";
|
|
5
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
-
export function renderReceiptHtml({ receipt, template, params, tokenName, }) {
|
|
7
|
-
const templateSource = fs.readFileSync(path.join(template.emailTemplatePath ?? __dirname, template.emailTemplateName ?? "mailTemplate.hbs"), "utf8");
|
|
8
|
-
const templates = Handlebars.compile(templateSource);
|
|
9
|
-
return templates({
|
|
10
|
-
brandName: template.brandName || params.orgName,
|
|
11
|
-
logoUrl: template.logoUrl || "",
|
|
12
|
-
primaryColor: template.primaryColor || "#b5a8b0ff",
|
|
13
|
-
titleFontSize: template.titleFontSize || 20,
|
|
14
|
-
bodyFontSize: template.bodyFontSize || 14,
|
|
15
|
-
receiptTitle: template.subject || "Payment Receipt",
|
|
16
|
-
footerText: template.footerText || "Powered by PayHash",
|
|
17
|
-
txHash: receipt.transactionHash,
|
|
18
|
-
payer: receipt.from,
|
|
19
|
-
timestamp: new Date(receipt.timestamp).toUTCString(),
|
|
20
|
-
year: new Date().getFullYear(),
|
|
21
|
-
amount: params.amount,
|
|
22
|
-
token: tokenName,
|
|
23
|
-
});
|
|
24
|
-
}
|
package/dist/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/utils.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";
|
package/dist/viem.config.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { createClient, http, publicActions, walletActions } from 'viem';
|
|
2
|
-
import { privateKeyToAccount } from 'viem/accounts';
|
|
3
|
-
import { tempoTestnet } from 'viem/chains';
|
|
4
|
-
import { tempoActions } from 'viem/tempo';
|
|
5
|
-
export const client = createClient({
|
|
6
|
-
account: privateKeyToAccount('0x...'),
|
|
7
|
-
chain: tempoTestnet,
|
|
8
|
-
transport: http(),
|
|
9
|
-
})
|
|
10
|
-
.extend(publicActions)
|
|
11
|
-
.extend(walletActions)
|
|
12
|
-
.extend(tempoActions());
|