@payloops/processor-core 0.0.1

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.
@@ -0,0 +1,111 @@
1
+ import { proxyActivities, sleep, continueAsNew, defineSignal, setHandler } from '@temporalio/workflow';
2
+ import type * as activities from '../activities';
3
+ import type { PaymentInput, PaymentResult } from '../types';
4
+
5
+ const { processPayment, updateOrderStatus, capturePayment } = proxyActivities<typeof activities>({
6
+ startToCloseTimeout: '2 minutes',
7
+ retry: {
8
+ initialInterval: '1s',
9
+ maximumInterval: '1m',
10
+ backoffCoefficient: 2,
11
+ maximumAttempts: 5
12
+ }
13
+ });
14
+
15
+ export interface PaymentWorkflowInput {
16
+ orderId: string;
17
+ merchantId: string;
18
+ amount: number;
19
+ currency: string;
20
+ processor: string;
21
+ returnUrl?: string;
22
+ }
23
+
24
+ // Signals for external events
25
+ export const completePaymentSignal = defineSignal<[{ success: boolean; processorTransactionId?: string }]>(
26
+ 'completePayment'
27
+ );
28
+
29
+ export const cancelPaymentSignal = defineSignal('cancelPayment');
30
+
31
+ export async function PaymentWorkflow(input: PaymentWorkflowInput): Promise<PaymentResult> {
32
+ let paymentCompleted = false;
33
+ let paymentResult: PaymentResult | null = null;
34
+ let cancelled = false;
35
+
36
+ // Handle external payment completion (3DS, redirect flows)
37
+ setHandler(completePaymentSignal, (result) => {
38
+ paymentCompleted = true;
39
+ paymentResult = {
40
+ success: result.success,
41
+ status: result.success ? 'captured' : 'failed',
42
+ processorTransactionId: result.processorTransactionId
43
+ };
44
+ });
45
+
46
+ // Handle cancellation
47
+ setHandler(cancelPaymentSignal, () => {
48
+ cancelled = true;
49
+ });
50
+
51
+ try {
52
+ // Step 1: Process payment
53
+ const result = await processPayment({
54
+ orderId: input.orderId,
55
+ merchantId: input.merchantId,
56
+ amount: input.amount,
57
+ currency: input.currency,
58
+ processor: input.processor,
59
+ returnUrl: input.returnUrl
60
+ });
61
+
62
+ // If payment requires redirect (3DS, UPI, etc.)
63
+ if (result.status === 'requires_action') {
64
+ await updateOrderStatus(input.orderId, 'requires_action', result.processorOrderId);
65
+
66
+ // Wait for completion signal (with timeout)
67
+ const timeout = 15 * 60 * 1000; // 15 minutes
68
+ const startTime = Date.now();
69
+
70
+ while (!paymentCompleted && !cancelled && Date.now() - startTime < timeout) {
71
+ await sleep('10 seconds');
72
+ }
73
+
74
+ if (cancelled) {
75
+ await updateOrderStatus(input.orderId, 'cancelled');
76
+ return { success: false, status: 'failed', errorCode: 'cancelled', errorMessage: 'Payment cancelled' };
77
+ }
78
+
79
+ if (!paymentCompleted) {
80
+ await updateOrderStatus(input.orderId, 'failed');
81
+ return { success: false, status: 'failed', errorCode: 'timeout', errorMessage: 'Payment timeout' };
82
+ }
83
+
84
+ if (paymentResult !== null) {
85
+ const finalResult = paymentResult as PaymentResult;
86
+ await updateOrderStatus(
87
+ input.orderId,
88
+ finalResult.status,
89
+ result.processorOrderId,
90
+ finalResult.processorTransactionId
91
+ );
92
+ return finalResult;
93
+ }
94
+ }
95
+
96
+ // Update order with result
97
+ await updateOrderStatus(input.orderId, result.status, result.processorOrderId, result.processorTransactionId);
98
+
99
+ return result;
100
+ } catch (error) {
101
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
102
+ await updateOrderStatus(input.orderId, 'failed');
103
+
104
+ return {
105
+ success: false,
106
+ status: 'failed',
107
+ errorCode: 'workflow_error',
108
+ errorMessage
109
+ };
110
+ }
111
+ }
@@ -0,0 +1,64 @@
1
+ import { proxyActivities, sleep } from '@temporalio/workflow';
2
+ import type * as activities from '../activities';
3
+ import type { WebhookDeliveryResult } from '../types';
4
+
5
+ const { deliverWebhook, getMerchantWebhookUrl } = proxyActivities<typeof activities>({
6
+ startToCloseTimeout: '1 minute',
7
+ retry: {
8
+ initialInterval: '1s',
9
+ maximumInterval: '30s',
10
+ backoffCoefficient: 2,
11
+ maximumAttempts: 3
12
+ }
13
+ });
14
+
15
+ export interface WebhookDeliveryWorkflowInput {
16
+ webhookEventId: string;
17
+ merchantId: string;
18
+ webhookUrl: string;
19
+ payload: Record<string, unknown>;
20
+ }
21
+
22
+ const MAX_ATTEMPTS = 5;
23
+ const RETRY_DELAYS = [
24
+ 60 * 1000, // 1 minute
25
+ 5 * 60 * 1000, // 5 minutes
26
+ 30 * 60 * 1000, // 30 minutes
27
+ 2 * 60 * 60 * 1000, // 2 hours
28
+ 24 * 60 * 60 * 1000 // 24 hours
29
+ ];
30
+
31
+ export async function WebhookDeliveryWorkflow(input: WebhookDeliveryWorkflowInput): Promise<WebhookDeliveryResult> {
32
+ // Get merchant's webhook secret
33
+ const { secret } = await getMerchantWebhookUrl(input.merchantId);
34
+
35
+ let attempt = 0;
36
+ let lastResult: WebhookDeliveryResult | null = null;
37
+
38
+ while (attempt < MAX_ATTEMPTS) {
39
+ attempt++;
40
+
41
+ const result = await deliverWebhook(input.webhookEventId, input.webhookUrl, secret || undefined, input.payload);
42
+
43
+ lastResult = result;
44
+
45
+ if (result.success) {
46
+ return result;
47
+ }
48
+
49
+ // If we've exhausted all attempts, return failure
50
+ if (attempt >= MAX_ATTEMPTS) {
51
+ return {
52
+ success: false,
53
+ attempts: attempt,
54
+ errorMessage: result.errorMessage || 'Max attempts reached'
55
+ };
56
+ }
57
+
58
+ // Wait before next retry
59
+ const delay = RETRY_DELAYS[attempt - 1] || RETRY_DELAYS[RETRY_DELAYS.length - 1];
60
+ await sleep(delay);
61
+ }
62
+
63
+ return lastResult || { success: false, attempts: attempt, errorMessage: 'Unknown error' };
64
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "esModuleInterop": true,
7
+ "strict": true,
8
+ "skipLibCheck": true,
9
+ "outDir": "dist",
10
+ "rootDir": "src",
11
+ "declaration": true,
12
+ "resolveJsonModule": true,
13
+ "baseUrl": ".",
14
+ "paths": {
15
+ "@/*": ["src/*"]
16
+ }
17
+ },
18
+ "include": ["src/**/*"],
19
+ "exclude": ["node_modules", "dist"]
20
+ }