@payloops/processor-core 0.0.1 → 0.0.7
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 +70 -11
- package/dist/index.js +0 -21
- package/dist/lib/observability/index.d.ts +1 -0
- package/dist/lib/observability/index.js +2 -0
- package/package.json +17 -30
- package/.env.example +0 -4
- package/Dockerfile +0 -41
- package/dist/activities/index.d.ts +0 -2
- package/dist/activities/index.js +0 -19
- package/dist/chunk-AV4OEJXY.js +0 -132
- package/dist/chunk-CZTZBCNV.js +0 -133
- package/dist/chunk-MLKGABMK.js +0 -9
- package/dist/chunk-X2Y2ZUQA.js +0 -297
- package/dist/index-CXmmtGo_.d.ts +0 -25
- package/dist/index-CsnpS83V.d.ts +0 -72
- package/dist/workflows/index.d.ts +0 -27
- package/dist/workflows/index.js +0 -13
- package/src/activities/index.ts +0 -224
- package/src/index.ts +0 -22
- package/src/lib/crypto.ts +0 -24
- package/src/lib/db.ts +0 -91
- package/src/lib/observability/index.ts +0 -2
- package/src/lib/observability/logger.ts +0 -44
- package/src/lib/observability/otel.ts +0 -53
- package/src/lib/registry.ts +0 -28
- package/src/types/index.ts +0 -95
- package/src/worker.ts +0 -105
- package/src/workflows/index.ts +0 -5
- package/src/workflows/payment.ts +0 -111
- package/src/workflows/webhook.ts +0 -64
- package/tsconfig.json +0 -20
package/dist/chunk-X2Y2ZUQA.js
DELETED
|
@@ -1,297 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
__export
|
|
3
|
-
} from "./chunk-MLKGABMK.js";
|
|
4
|
-
|
|
5
|
-
// src/activities/index.ts
|
|
6
|
-
var activities_exports = {};
|
|
7
|
-
__export(activities_exports, {
|
|
8
|
-
capturePayment: () => capturePayment,
|
|
9
|
-
deliverWebhook: () => deliverWebhook,
|
|
10
|
-
getMerchantWebhookUrl: () => getMerchantWebhookUrl,
|
|
11
|
-
getProcessorConfig: () => getProcessorConfig,
|
|
12
|
-
processPayment: () => processPayment,
|
|
13
|
-
refundPayment: () => refundPayment,
|
|
14
|
-
updateOrderStatus: () => updateOrderStatus
|
|
15
|
-
});
|
|
16
|
-
import { eq, and } from "drizzle-orm";
|
|
17
|
-
|
|
18
|
-
// src/lib/db.ts
|
|
19
|
-
import { drizzle } from "drizzle-orm/node-postgres";
|
|
20
|
-
import { Pool } from "pg";
|
|
21
|
-
import {
|
|
22
|
-
pgTable,
|
|
23
|
-
text,
|
|
24
|
-
timestamp,
|
|
25
|
-
integer,
|
|
26
|
-
boolean,
|
|
27
|
-
jsonb,
|
|
28
|
-
varchar,
|
|
29
|
-
uuid
|
|
30
|
-
} from "drizzle-orm/pg-core";
|
|
31
|
-
var orders = pgTable("orders", {
|
|
32
|
-
id: uuid("id").primaryKey().defaultRandom(),
|
|
33
|
-
merchantId: uuid("merchant_id").notNull(),
|
|
34
|
-
externalId: varchar("external_id", { length: 255 }).notNull(),
|
|
35
|
-
amount: integer("amount").notNull(),
|
|
36
|
-
currency: varchar("currency", { length: 3 }).notNull().default("USD"),
|
|
37
|
-
status: varchar("status", { length: 50 }).notNull().default("pending"),
|
|
38
|
-
processor: varchar("processor", { length: 50 }),
|
|
39
|
-
processorOrderId: varchar("processor_order_id", { length: 255 }),
|
|
40
|
-
metadata: jsonb("metadata").default({}),
|
|
41
|
-
workflowId: varchar("workflow_id", { length: 255 }),
|
|
42
|
-
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
43
|
-
updatedAt: timestamp("updated_at").defaultNow().notNull()
|
|
44
|
-
});
|
|
45
|
-
var transactions = pgTable("transactions", {
|
|
46
|
-
id: uuid("id").primaryKey().defaultRandom(),
|
|
47
|
-
orderId: uuid("order_id").notNull(),
|
|
48
|
-
type: varchar("type", { length: 50 }).notNull(),
|
|
49
|
-
amount: integer("amount").notNull(),
|
|
50
|
-
status: varchar("status", { length: 50 }).notNull().default("pending"),
|
|
51
|
-
processorTransactionId: varchar("processor_transaction_id", { length: 255 }),
|
|
52
|
-
processorResponse: jsonb("processor_response"),
|
|
53
|
-
errorCode: varchar("error_code", { length: 100 }),
|
|
54
|
-
errorMessage: text("error_message"),
|
|
55
|
-
createdAt: timestamp("created_at").defaultNow().notNull()
|
|
56
|
-
});
|
|
57
|
-
var processorConfigs = pgTable("processor_configs", {
|
|
58
|
-
id: uuid("id").primaryKey().defaultRandom(),
|
|
59
|
-
merchantId: uuid("merchant_id").notNull(),
|
|
60
|
-
processor: varchar("processor", { length: 50 }).notNull(),
|
|
61
|
-
credentialsEncrypted: text("credentials_encrypted").notNull(),
|
|
62
|
-
priority: integer("priority").notNull().default(1),
|
|
63
|
-
enabled: boolean("enabled").notNull().default(true),
|
|
64
|
-
testMode: boolean("test_mode").notNull().default(true),
|
|
65
|
-
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
66
|
-
updatedAt: timestamp("updated_at").defaultNow().notNull()
|
|
67
|
-
});
|
|
68
|
-
var webhookEvents = pgTable("webhook_events", {
|
|
69
|
-
id: uuid("id").primaryKey().defaultRandom(),
|
|
70
|
-
merchantId: uuid("merchant_id").notNull(),
|
|
71
|
-
orderId: uuid("order_id"),
|
|
72
|
-
eventType: varchar("event_type", { length: 100 }).notNull(),
|
|
73
|
-
payload: jsonb("payload").notNull(),
|
|
74
|
-
status: varchar("status", { length: 50 }).notNull().default("pending"),
|
|
75
|
-
attempts: integer("attempts").notNull().default(0),
|
|
76
|
-
lastAttemptAt: timestamp("last_attempt_at"),
|
|
77
|
-
nextRetryAt: timestamp("next_retry_at"),
|
|
78
|
-
deliveredAt: timestamp("delivered_at"),
|
|
79
|
-
workflowId: varchar("workflow_id", { length: 255 }),
|
|
80
|
-
createdAt: timestamp("created_at").defaultNow().notNull()
|
|
81
|
-
});
|
|
82
|
-
var merchants = pgTable("merchants", {
|
|
83
|
-
id: uuid("id").primaryKey().defaultRandom(),
|
|
84
|
-
name: varchar("name", { length: 255 }).notNull(),
|
|
85
|
-
email: varchar("email", { length: 255 }).notNull().unique(),
|
|
86
|
-
webhookUrl: text("webhook_url"),
|
|
87
|
-
webhookSecret: text("webhook_secret")
|
|
88
|
-
});
|
|
89
|
-
var dbInstance = null;
|
|
90
|
-
function getDb() {
|
|
91
|
-
if (!dbInstance) {
|
|
92
|
-
const pool = new Pool({
|
|
93
|
-
connectionString: process.env.DATABASE_URL
|
|
94
|
-
});
|
|
95
|
-
dbInstance = drizzle(pool);
|
|
96
|
-
}
|
|
97
|
-
return dbInstance;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// src/lib/crypto.ts
|
|
101
|
-
import { createDecipheriv, createHash } from "crypto";
|
|
102
|
-
var ALGORITHM = "aes-256-gcm";
|
|
103
|
-
function getKey() {
|
|
104
|
-
const key = process.env.ENCRYPTION_KEY;
|
|
105
|
-
if (!key) throw new Error("ENCRYPTION_KEY not set");
|
|
106
|
-
return createHash("sha256").update(key).digest();
|
|
107
|
-
}
|
|
108
|
-
function decrypt(encryptedText) {
|
|
109
|
-
const [ivHex, authTagHex, encrypted] = encryptedText.split(":");
|
|
110
|
-
const iv = Buffer.from(ivHex, "hex");
|
|
111
|
-
const authTag = Buffer.from(authTagHex, "hex");
|
|
112
|
-
const decipher = createDecipheriv(ALGORITHM, getKey(), iv);
|
|
113
|
-
decipher.setAuthTag(authTag);
|
|
114
|
-
let decrypted = decipher.update(encrypted, "hex", "utf8");
|
|
115
|
-
decrypted += decipher.final("utf8");
|
|
116
|
-
return decrypted;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// src/lib/registry.ts
|
|
120
|
-
var processors = /* @__PURE__ */ new Map();
|
|
121
|
-
function registerProcessor(processor) {
|
|
122
|
-
if (processors.has(processor.name)) {
|
|
123
|
-
throw new Error(`Processor ${processor.name} is already registered`);
|
|
124
|
-
}
|
|
125
|
-
processors.set(processor.name, processor);
|
|
126
|
-
console.log(`Registered processor: ${processor.name}`);
|
|
127
|
-
}
|
|
128
|
-
function getProcessor(name) {
|
|
129
|
-
const processor = processors.get(name);
|
|
130
|
-
if (!processor) {
|
|
131
|
-
throw new Error(`Processor ${name} not found. Available: ${Array.from(processors.keys()).join(", ")}`);
|
|
132
|
-
}
|
|
133
|
-
return processor;
|
|
134
|
-
}
|
|
135
|
-
function getRegisteredProcessors() {
|
|
136
|
-
return Array.from(processors.keys());
|
|
137
|
-
}
|
|
138
|
-
function hasProcessor(name) {
|
|
139
|
-
return processors.has(name);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// src/activities/index.ts
|
|
143
|
-
import crypto from "crypto";
|
|
144
|
-
async function getProcessorConfig(merchantId, processor) {
|
|
145
|
-
const db = getDb();
|
|
146
|
-
const config = await db.select().from(processorConfigs).where(and(eq(processorConfigs.merchantId, merchantId), eq(processorConfigs.processor, processor))).limit(1);
|
|
147
|
-
if (config.length === 0) return null;
|
|
148
|
-
const credentials = JSON.parse(decrypt(config[0].credentialsEncrypted));
|
|
149
|
-
return {
|
|
150
|
-
merchantId,
|
|
151
|
-
processor,
|
|
152
|
-
testMode: config[0].testMode,
|
|
153
|
-
credentials
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
async function updateOrderStatus(orderId, status, processorOrderId, processorTransactionId) {
|
|
157
|
-
const db = getDb();
|
|
158
|
-
await db.update(orders).set({
|
|
159
|
-
status,
|
|
160
|
-
processorOrderId: processorOrderId || void 0,
|
|
161
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
162
|
-
}).where(eq(orders.id, orderId));
|
|
163
|
-
if (processorTransactionId) {
|
|
164
|
-
const order = await db.select().from(orders).where(eq(orders.id, orderId)).limit(1);
|
|
165
|
-
if (order.length > 0) {
|
|
166
|
-
await db.insert(transactions).values({
|
|
167
|
-
orderId,
|
|
168
|
-
type: status === "captured" ? "capture" : status === "authorized" ? "authorization" : "authorization",
|
|
169
|
-
amount: order[0].amount,
|
|
170
|
-
status: status === "failed" ? "failed" : "success",
|
|
171
|
-
processorTransactionId
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
async function processPayment(input) {
|
|
177
|
-
const config = await getProcessorConfig(input.merchantId, input.processor);
|
|
178
|
-
if (!config) {
|
|
179
|
-
return {
|
|
180
|
-
success: false,
|
|
181
|
-
status: "failed",
|
|
182
|
-
errorCode: "no_processor_config",
|
|
183
|
-
errorMessage: `Processor ${input.processor} not configured for merchant`
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
const processor = getProcessor(input.processor);
|
|
187
|
-
return processor.createPayment(input, config);
|
|
188
|
-
}
|
|
189
|
-
async function capturePayment(processorOrderId, amount, merchantId, processor) {
|
|
190
|
-
const config = await getProcessorConfig(merchantId, processor);
|
|
191
|
-
if (!config) {
|
|
192
|
-
return {
|
|
193
|
-
success: false,
|
|
194
|
-
status: "failed",
|
|
195
|
-
errorCode: "no_processor_config",
|
|
196
|
-
errorMessage: `Processor ${processor} not configured`
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
const processorInstance = getProcessor(processor);
|
|
200
|
-
return processorInstance.capturePayment(processorOrderId, amount, config);
|
|
201
|
-
}
|
|
202
|
-
async function refundPayment(processorTransactionId, amount, merchantId, processor) {
|
|
203
|
-
const config = await getProcessorConfig(merchantId, processor);
|
|
204
|
-
if (!config) {
|
|
205
|
-
return {
|
|
206
|
-
success: false,
|
|
207
|
-
status: "failed",
|
|
208
|
-
errorCode: "no_processor_config",
|
|
209
|
-
errorMessage: `Processor ${processor} not configured`
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
const processorInstance = getProcessor(processor);
|
|
213
|
-
return processorInstance.refundPayment(processorTransactionId, amount, config);
|
|
214
|
-
}
|
|
215
|
-
async function deliverWebhook(webhookEventId, webhookUrl, webhookSecret, payload) {
|
|
216
|
-
const db = getDb();
|
|
217
|
-
const event = await db.select().from(webhookEvents).where(eq(webhookEvents.id, webhookEventId)).limit(1);
|
|
218
|
-
const attempts = (event[0]?.attempts || 0) + 1;
|
|
219
|
-
try {
|
|
220
|
-
const body = JSON.stringify(payload);
|
|
221
|
-
const headers = {
|
|
222
|
-
"Content-Type": "application/json",
|
|
223
|
-
"X-Loop-Event-Id": webhookEventId,
|
|
224
|
-
"X-Loop-Timestamp": String(Date.now())
|
|
225
|
-
};
|
|
226
|
-
if (webhookSecret) {
|
|
227
|
-
const timestamp2 = headers["X-Loop-Timestamp"];
|
|
228
|
-
const signaturePayload = `${timestamp2}.${body}`;
|
|
229
|
-
const signature = crypto.createHmac("sha256", webhookSecret).update(signaturePayload).digest("hex");
|
|
230
|
-
headers["X-Loop-Signature"] = `v1=${signature}`;
|
|
231
|
-
}
|
|
232
|
-
const response = await fetch(webhookUrl, {
|
|
233
|
-
method: "POST",
|
|
234
|
-
headers,
|
|
235
|
-
body,
|
|
236
|
-
signal: AbortSignal.timeout(3e4)
|
|
237
|
-
// 30 second timeout
|
|
238
|
-
});
|
|
239
|
-
const success = response.ok;
|
|
240
|
-
await db.update(webhookEvents).set({
|
|
241
|
-
status: success ? "delivered" : "pending",
|
|
242
|
-
attempts,
|
|
243
|
-
lastAttemptAt: /* @__PURE__ */ new Date(),
|
|
244
|
-
deliveredAt: success ? /* @__PURE__ */ new Date() : void 0,
|
|
245
|
-
nextRetryAt: success ? void 0 : new Date(Date.now() + getRetryDelay(attempts))
|
|
246
|
-
}).where(eq(webhookEvents.id, webhookEventId));
|
|
247
|
-
return {
|
|
248
|
-
success,
|
|
249
|
-
statusCode: response.status,
|
|
250
|
-
attempts,
|
|
251
|
-
deliveredAt: success ? /* @__PURE__ */ new Date() : void 0,
|
|
252
|
-
errorMessage: success ? void 0 : `HTTP ${response.status}`
|
|
253
|
-
};
|
|
254
|
-
} catch (error) {
|
|
255
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
256
|
-
await db.update(webhookEvents).set({
|
|
257
|
-
status: attempts >= 5 ? "failed" : "pending",
|
|
258
|
-
attempts,
|
|
259
|
-
lastAttemptAt: /* @__PURE__ */ new Date(),
|
|
260
|
-
nextRetryAt: attempts >= 5 ? void 0 : new Date(Date.now() + getRetryDelay(attempts))
|
|
261
|
-
}).where(eq(webhookEvents.id, webhookEventId));
|
|
262
|
-
return {
|
|
263
|
-
success: false,
|
|
264
|
-
attempts,
|
|
265
|
-
errorMessage
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
async function getMerchantWebhookUrl(merchantId) {
|
|
270
|
-
const db = getDb();
|
|
271
|
-
const merchant = await db.select().from(merchants).where(eq(merchants.id, merchantId)).limit(1);
|
|
272
|
-
return {
|
|
273
|
-
url: merchant[0]?.webhookUrl || null,
|
|
274
|
-
secret: merchant[0]?.webhookSecret || null
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
function getRetryDelay(attempt) {
|
|
278
|
-
const baseDelay = 60 * 1e3;
|
|
279
|
-
const maxDelay = 24 * 60 * 60 * 1e3;
|
|
280
|
-
const delay = baseDelay * Math.pow(2, attempt - 1);
|
|
281
|
-
return Math.min(delay, maxDelay);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
export {
|
|
285
|
-
registerProcessor,
|
|
286
|
-
getProcessor,
|
|
287
|
-
getRegisteredProcessors,
|
|
288
|
-
hasProcessor,
|
|
289
|
-
getProcessorConfig,
|
|
290
|
-
updateOrderStatus,
|
|
291
|
-
processPayment,
|
|
292
|
-
capturePayment,
|
|
293
|
-
refundPayment,
|
|
294
|
-
deliverWebhook,
|
|
295
|
-
getMerchantWebhookUrl,
|
|
296
|
-
activities_exports
|
|
297
|
-
};
|
package/dist/index-CXmmtGo_.d.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { a as PaymentConfig, b as PaymentInput, c as PaymentResult, d as RefundResult, e as WebhookDeliveryResult } from './index-CsnpS83V.js';
|
|
2
|
-
|
|
3
|
-
declare function getProcessorConfig(merchantId: string, processor: string): Promise<PaymentConfig | null>;
|
|
4
|
-
declare function updateOrderStatus(orderId: string, status: string, processorOrderId?: string, processorTransactionId?: string): Promise<void>;
|
|
5
|
-
declare function processPayment(input: PaymentInput): Promise<PaymentResult>;
|
|
6
|
-
declare function capturePayment(processorOrderId: string, amount: number, merchantId: string, processor: string): Promise<PaymentResult>;
|
|
7
|
-
declare function refundPayment(processorTransactionId: string, amount: number, merchantId: string, processor: string): Promise<RefundResult>;
|
|
8
|
-
declare function deliverWebhook(webhookEventId: string, webhookUrl: string, webhookSecret: string | undefined, payload: Record<string, unknown>): Promise<WebhookDeliveryResult>;
|
|
9
|
-
declare function getMerchantWebhookUrl(merchantId: string): Promise<{
|
|
10
|
-
url: string | null;
|
|
11
|
-
secret: string | null;
|
|
12
|
-
}>;
|
|
13
|
-
|
|
14
|
-
declare const index_capturePayment: typeof capturePayment;
|
|
15
|
-
declare const index_deliverWebhook: typeof deliverWebhook;
|
|
16
|
-
declare const index_getMerchantWebhookUrl: typeof getMerchantWebhookUrl;
|
|
17
|
-
declare const index_getProcessorConfig: typeof getProcessorConfig;
|
|
18
|
-
declare const index_processPayment: typeof processPayment;
|
|
19
|
-
declare const index_refundPayment: typeof refundPayment;
|
|
20
|
-
declare const index_updateOrderStatus: typeof updateOrderStatus;
|
|
21
|
-
declare namespace index {
|
|
22
|
-
export { index_capturePayment as capturePayment, index_deliverWebhook as deliverWebhook, index_getMerchantWebhookUrl as getMerchantWebhookUrl, index_getProcessorConfig as getProcessorConfig, index_processPayment as processPayment, index_refundPayment as refundPayment, index_updateOrderStatus as updateOrderStatus };
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export { getMerchantWebhookUrl as a, capturePayment as c, deliverWebhook as d, getProcessorConfig as g, index as i, processPayment as p, refundPayment as r, updateOrderStatus as u };
|
package/dist/index-CsnpS83V.d.ts
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
interface PaymentConfig {
|
|
2
|
-
merchantId: string;
|
|
3
|
-
processor: string;
|
|
4
|
-
testMode: boolean;
|
|
5
|
-
credentials: Record<string, string>;
|
|
6
|
-
}
|
|
7
|
-
interface PaymentInput {
|
|
8
|
-
orderId: string;
|
|
9
|
-
merchantId: string;
|
|
10
|
-
amount: number;
|
|
11
|
-
currency: string;
|
|
12
|
-
processor: string;
|
|
13
|
-
returnUrl?: string;
|
|
14
|
-
cancelUrl?: string;
|
|
15
|
-
metadata?: Record<string, unknown>;
|
|
16
|
-
customer?: {
|
|
17
|
-
id?: string;
|
|
18
|
-
email?: string;
|
|
19
|
-
name?: string;
|
|
20
|
-
};
|
|
21
|
-
paymentMethod?: {
|
|
22
|
-
type: 'card' | 'upi' | 'netbanking' | 'wallet';
|
|
23
|
-
token?: string;
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
interface PaymentResult {
|
|
27
|
-
success: boolean;
|
|
28
|
-
status: 'authorized' | 'captured' | 'failed' | 'pending' | 'requires_action';
|
|
29
|
-
processorOrderId?: string;
|
|
30
|
-
processorTransactionId?: string;
|
|
31
|
-
redirectUrl?: string;
|
|
32
|
-
errorCode?: string;
|
|
33
|
-
errorMessage?: string;
|
|
34
|
-
metadata?: Record<string, unknown>;
|
|
35
|
-
}
|
|
36
|
-
interface RefundInput {
|
|
37
|
-
orderId: string;
|
|
38
|
-
transactionId: string;
|
|
39
|
-
amount: number;
|
|
40
|
-
reason?: string;
|
|
41
|
-
}
|
|
42
|
-
interface RefundResult {
|
|
43
|
-
success: boolean;
|
|
44
|
-
refundId?: string;
|
|
45
|
-
status: 'pending' | 'success' | 'failed';
|
|
46
|
-
errorCode?: string;
|
|
47
|
-
errorMessage?: string;
|
|
48
|
-
}
|
|
49
|
-
interface WebhookDeliveryInput {
|
|
50
|
-
webhookEventId: string;
|
|
51
|
-
merchantId: string;
|
|
52
|
-
webhookUrl: string;
|
|
53
|
-
webhookSecret?: string;
|
|
54
|
-
payload: Record<string, unknown>;
|
|
55
|
-
}
|
|
56
|
-
interface WebhookDeliveryResult {
|
|
57
|
-
success: boolean;
|
|
58
|
-
statusCode?: number;
|
|
59
|
-
attempts: number;
|
|
60
|
-
deliveredAt?: Date;
|
|
61
|
-
errorMessage?: string;
|
|
62
|
-
}
|
|
63
|
-
interface PaymentProcessor {
|
|
64
|
-
name: string;
|
|
65
|
-
createPayment(input: PaymentInput, config: PaymentConfig): Promise<PaymentResult>;
|
|
66
|
-
capturePayment(processorOrderId: string, amount: number, config: PaymentConfig): Promise<PaymentResult>;
|
|
67
|
-
refundPayment(processorTransactionId: string, amount: number, config: PaymentConfig): Promise<RefundResult>;
|
|
68
|
-
getPaymentStatus(processorOrderId: string, config: PaymentConfig): Promise<PaymentResult>;
|
|
69
|
-
}
|
|
70
|
-
type ProcessorRegistry = Map<string, PaymentProcessor>;
|
|
71
|
-
|
|
72
|
-
export type { PaymentProcessor as P, RefundInput as R, WebhookDeliveryInput as W, PaymentConfig as a, PaymentInput as b, PaymentResult as c, RefundResult as d, WebhookDeliveryResult as e, ProcessorRegistry as f };
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import * as _temporalio_common from '@temporalio/common';
|
|
2
|
-
import { c as PaymentResult, e as WebhookDeliveryResult } from '../index-CsnpS83V.js';
|
|
3
|
-
|
|
4
|
-
interface PaymentWorkflowInput {
|
|
5
|
-
orderId: string;
|
|
6
|
-
merchantId: string;
|
|
7
|
-
amount: number;
|
|
8
|
-
currency: string;
|
|
9
|
-
processor: string;
|
|
10
|
-
returnUrl?: string;
|
|
11
|
-
}
|
|
12
|
-
declare const completePaymentSignal: _temporalio_common.SignalDefinition<[{
|
|
13
|
-
success: boolean;
|
|
14
|
-
processorTransactionId?: string;
|
|
15
|
-
}], string>;
|
|
16
|
-
declare const cancelPaymentSignal: _temporalio_common.SignalDefinition<[], "cancelPayment">;
|
|
17
|
-
declare function PaymentWorkflow(input: PaymentWorkflowInput): Promise<PaymentResult>;
|
|
18
|
-
|
|
19
|
-
interface WebhookDeliveryWorkflowInput {
|
|
20
|
-
webhookEventId: string;
|
|
21
|
-
merchantId: string;
|
|
22
|
-
webhookUrl: string;
|
|
23
|
-
payload: Record<string, unknown>;
|
|
24
|
-
}
|
|
25
|
-
declare function WebhookDeliveryWorkflow(input: WebhookDeliveryWorkflowInput): Promise<WebhookDeliveryResult>;
|
|
26
|
-
|
|
27
|
-
export { PaymentWorkflow, type PaymentWorkflowInput, WebhookDeliveryWorkflow, type WebhookDeliveryWorkflowInput, cancelPaymentSignal, completePaymentSignal };
|
package/dist/workflows/index.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
PaymentWorkflow,
|
|
3
|
-
WebhookDeliveryWorkflow,
|
|
4
|
-
cancelPaymentSignal,
|
|
5
|
-
completePaymentSignal
|
|
6
|
-
} from "../chunk-CZTZBCNV.js";
|
|
7
|
-
import "../chunk-MLKGABMK.js";
|
|
8
|
-
export {
|
|
9
|
-
PaymentWorkflow,
|
|
10
|
-
WebhookDeliveryWorkflow,
|
|
11
|
-
cancelPaymentSignal,
|
|
12
|
-
completePaymentSignal
|
|
13
|
-
};
|
package/src/activities/index.ts
DELETED
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
import { eq, and } from 'drizzle-orm';
|
|
2
|
-
import { getDb, orders, transactions, processorConfigs, webhookEvents, merchants } from '../lib/db';
|
|
3
|
-
import { decrypt } from '../lib/crypto';
|
|
4
|
-
import { getProcessor } from '../lib/registry';
|
|
5
|
-
import type { PaymentInput, PaymentResult, PaymentConfig, RefundResult, WebhookDeliveryResult } from '../types';
|
|
6
|
-
import crypto from 'crypto';
|
|
7
|
-
|
|
8
|
-
// Get processor config for a merchant
|
|
9
|
-
export async function getProcessorConfig(
|
|
10
|
-
merchantId: string,
|
|
11
|
-
processor: string
|
|
12
|
-
): Promise<PaymentConfig | null> {
|
|
13
|
-
const db = getDb();
|
|
14
|
-
|
|
15
|
-
const config = await db
|
|
16
|
-
.select()
|
|
17
|
-
.from(processorConfigs)
|
|
18
|
-
.where(and(eq(processorConfigs.merchantId, merchantId), eq(processorConfigs.processor, processor)))
|
|
19
|
-
.limit(1);
|
|
20
|
-
|
|
21
|
-
if (config.length === 0) return null;
|
|
22
|
-
|
|
23
|
-
const credentials = JSON.parse(decrypt(config[0].credentialsEncrypted));
|
|
24
|
-
|
|
25
|
-
return {
|
|
26
|
-
merchantId,
|
|
27
|
-
processor,
|
|
28
|
-
testMode: config[0].testMode,
|
|
29
|
-
credentials
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Update order status
|
|
34
|
-
export async function updateOrderStatus(
|
|
35
|
-
orderId: string,
|
|
36
|
-
status: string,
|
|
37
|
-
processorOrderId?: string,
|
|
38
|
-
processorTransactionId?: string
|
|
39
|
-
): Promise<void> {
|
|
40
|
-
const db = getDb();
|
|
41
|
-
|
|
42
|
-
await db
|
|
43
|
-
.update(orders)
|
|
44
|
-
.set({
|
|
45
|
-
status,
|
|
46
|
-
processorOrderId: processorOrderId || undefined,
|
|
47
|
-
updatedAt: new Date()
|
|
48
|
-
})
|
|
49
|
-
.where(eq(orders.id, orderId));
|
|
50
|
-
|
|
51
|
-
// Create transaction record if we have a transaction id
|
|
52
|
-
if (processorTransactionId) {
|
|
53
|
-
const order = await db.select().from(orders).where(eq(orders.id, orderId)).limit(1);
|
|
54
|
-
|
|
55
|
-
if (order.length > 0) {
|
|
56
|
-
await db.insert(transactions).values({
|
|
57
|
-
orderId,
|
|
58
|
-
type: status === 'captured' ? 'capture' : status === 'authorized' ? 'authorization' : 'authorization',
|
|
59
|
-
amount: order[0].amount,
|
|
60
|
-
status: status === 'failed' ? 'failed' : 'success',
|
|
61
|
-
processorTransactionId
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Process payment using the appropriate processor
|
|
68
|
-
export async function processPayment(input: PaymentInput): Promise<PaymentResult> {
|
|
69
|
-
const config = await getProcessorConfig(input.merchantId, input.processor);
|
|
70
|
-
|
|
71
|
-
if (!config) {
|
|
72
|
-
return {
|
|
73
|
-
success: false,
|
|
74
|
-
status: 'failed',
|
|
75
|
-
errorCode: 'no_processor_config',
|
|
76
|
-
errorMessage: `Processor ${input.processor} not configured for merchant`
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const processor = getProcessor(input.processor);
|
|
81
|
-
return processor.createPayment(input, config);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Capture a payment
|
|
85
|
-
export async function capturePayment(
|
|
86
|
-
processorOrderId: string,
|
|
87
|
-
amount: number,
|
|
88
|
-
merchantId: string,
|
|
89
|
-
processor: string
|
|
90
|
-
): Promise<PaymentResult> {
|
|
91
|
-
const config = await getProcessorConfig(merchantId, processor);
|
|
92
|
-
|
|
93
|
-
if (!config) {
|
|
94
|
-
return {
|
|
95
|
-
success: false,
|
|
96
|
-
status: 'failed',
|
|
97
|
-
errorCode: 'no_processor_config',
|
|
98
|
-
errorMessage: `Processor ${processor} not configured`
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const processorInstance = getProcessor(processor);
|
|
103
|
-
return processorInstance.capturePayment(processorOrderId, amount, config);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Refund a payment
|
|
107
|
-
export async function refundPayment(
|
|
108
|
-
processorTransactionId: string,
|
|
109
|
-
amount: number,
|
|
110
|
-
merchantId: string,
|
|
111
|
-
processor: string
|
|
112
|
-
): Promise<RefundResult> {
|
|
113
|
-
const config = await getProcessorConfig(merchantId, processor);
|
|
114
|
-
|
|
115
|
-
if (!config) {
|
|
116
|
-
return {
|
|
117
|
-
success: false,
|
|
118
|
-
status: 'failed',
|
|
119
|
-
errorCode: 'no_processor_config',
|
|
120
|
-
errorMessage: `Processor ${processor} not configured`
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const processorInstance = getProcessor(processor);
|
|
125
|
-
return processorInstance.refundPayment(processorTransactionId, amount, config);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Deliver webhook to merchant
|
|
129
|
-
export async function deliverWebhook(
|
|
130
|
-
webhookEventId: string,
|
|
131
|
-
webhookUrl: string,
|
|
132
|
-
webhookSecret: string | undefined,
|
|
133
|
-
payload: Record<string, unknown>
|
|
134
|
-
): Promise<WebhookDeliveryResult> {
|
|
135
|
-
const db = getDb();
|
|
136
|
-
|
|
137
|
-
// Get current attempt count
|
|
138
|
-
const event = await db.select().from(webhookEvents).where(eq(webhookEvents.id, webhookEventId)).limit(1);
|
|
139
|
-
|
|
140
|
-
const attempts = (event[0]?.attempts || 0) + 1;
|
|
141
|
-
|
|
142
|
-
try {
|
|
143
|
-
const body = JSON.stringify(payload);
|
|
144
|
-
const headers: Record<string, string> = {
|
|
145
|
-
'Content-Type': 'application/json',
|
|
146
|
-
'X-Loop-Event-Id': webhookEventId,
|
|
147
|
-
'X-Loop-Timestamp': String(Date.now())
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
// Sign the webhook if secret is provided
|
|
151
|
-
if (webhookSecret) {
|
|
152
|
-
const timestamp = headers['X-Loop-Timestamp'];
|
|
153
|
-
const signaturePayload = `${timestamp}.${body}`;
|
|
154
|
-
const signature = crypto.createHmac('sha256', webhookSecret).update(signaturePayload).digest('hex');
|
|
155
|
-
headers['X-Loop-Signature'] = `v1=${signature}`;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const response = await fetch(webhookUrl, {
|
|
159
|
-
method: 'POST',
|
|
160
|
-
headers,
|
|
161
|
-
body,
|
|
162
|
-
signal: AbortSignal.timeout(30000) // 30 second timeout
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
const success = response.ok;
|
|
166
|
-
|
|
167
|
-
await db
|
|
168
|
-
.update(webhookEvents)
|
|
169
|
-
.set({
|
|
170
|
-
status: success ? 'delivered' : 'pending',
|
|
171
|
-
attempts,
|
|
172
|
-
lastAttemptAt: new Date(),
|
|
173
|
-
deliveredAt: success ? new Date() : undefined,
|
|
174
|
-
nextRetryAt: success ? undefined : new Date(Date.now() + getRetryDelay(attempts))
|
|
175
|
-
})
|
|
176
|
-
.where(eq(webhookEvents.id, webhookEventId));
|
|
177
|
-
|
|
178
|
-
return {
|
|
179
|
-
success,
|
|
180
|
-
statusCode: response.status,
|
|
181
|
-
attempts,
|
|
182
|
-
deliveredAt: success ? new Date() : undefined,
|
|
183
|
-
errorMessage: success ? undefined : `HTTP ${response.status}`
|
|
184
|
-
};
|
|
185
|
-
} catch (error) {
|
|
186
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
187
|
-
|
|
188
|
-
await db
|
|
189
|
-
.update(webhookEvents)
|
|
190
|
-
.set({
|
|
191
|
-
status: attempts >= 5 ? 'failed' : 'pending',
|
|
192
|
-
attempts,
|
|
193
|
-
lastAttemptAt: new Date(),
|
|
194
|
-
nextRetryAt: attempts >= 5 ? undefined : new Date(Date.now() + getRetryDelay(attempts))
|
|
195
|
-
})
|
|
196
|
-
.where(eq(webhookEvents.id, webhookEventId));
|
|
197
|
-
|
|
198
|
-
return {
|
|
199
|
-
success: false,
|
|
200
|
-
attempts,
|
|
201
|
-
errorMessage
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Get merchant webhook URL
|
|
207
|
-
export async function getMerchantWebhookUrl(merchantId: string): Promise<{ url: string | null; secret: string | null }> {
|
|
208
|
-
const db = getDb();
|
|
209
|
-
|
|
210
|
-
const merchant = await db.select().from(merchants).where(eq(merchants.id, merchantId)).limit(1);
|
|
211
|
-
|
|
212
|
-
return {
|
|
213
|
-
url: merchant[0]?.webhookUrl || null,
|
|
214
|
-
secret: merchant[0]?.webhookSecret || null
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Exponential backoff for retries
|
|
219
|
-
function getRetryDelay(attempt: number): number {
|
|
220
|
-
const baseDelay = 60 * 1000; // 1 minute
|
|
221
|
-
const maxDelay = 24 * 60 * 60 * 1000; // 24 hours
|
|
222
|
-
const delay = baseDelay * Math.pow(2, attempt - 1);
|
|
223
|
-
return Math.min(delay, maxDelay);
|
|
224
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
// Core types
|
|
2
|
-
export type {
|
|
3
|
-
PaymentConfig,
|
|
4
|
-
PaymentInput,
|
|
5
|
-
PaymentResult,
|
|
6
|
-
RefundInput,
|
|
7
|
-
RefundResult,
|
|
8
|
-
WebhookDeliveryInput,
|
|
9
|
-
WebhookDeliveryResult,
|
|
10
|
-
PaymentProcessor,
|
|
11
|
-
ProcessorRegistry
|
|
12
|
-
} from './types';
|
|
13
|
-
|
|
14
|
-
// Processor registry
|
|
15
|
-
export { registerProcessor, getProcessor, getRegisteredProcessors, hasProcessor } from './lib/registry';
|
|
16
|
-
|
|
17
|
-
// Activities (for worker registration)
|
|
18
|
-
export * as activities from './activities';
|
|
19
|
-
|
|
20
|
-
// Workflows
|
|
21
|
-
export { PaymentWorkflow, WebhookDeliveryWorkflow } from './workflows';
|
|
22
|
-
export type { PaymentWorkflowInput, WebhookDeliveryWorkflowInput } from './workflows';
|