@develit-services/bank 0.2.2 → 0.2.4
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/database/schema.cjs +2 -3
- package/dist/database/schema.d.cts +6 -3
- package/dist/database/schema.d.mts +6 -3
- package/dist/database/schema.d.ts +6 -3
- package/dist/database/schema.mjs +2 -3
- package/dist/export/worker.cjs +39 -46
- package/dist/export/worker.d.cts +25 -15
- package/dist/export/worker.d.mts +25 -15
- package/dist/export/worker.d.ts +25 -15
- package/dist/export/worker.mjs +35 -42
- package/dist/export/workflows.cjs +92 -94
- package/dist/export/workflows.mjs +92 -94
- package/dist/export/wrangler.cjs +1 -0
- package/dist/export/wrangler.d.cts +1 -0
- package/dist/export/wrangler.d.mts +1 -0
- package/dist/export/wrangler.d.ts +1 -0
- package/dist/export/wrangler.mjs +1 -0
- package/dist/shared/{bank.CXUMEJH4.mjs → bank.BvUtf1S3.mjs} +41 -20
- package/dist/shared/{bank.CEraaQIT.cjs → bank.CHS79KTx.cjs} +42 -21
- package/dist/shared/{bank.2BgwPCw_.d.ts → bank.Cfz44GPA.d.cts} +1598 -12
- package/dist/shared/{bank.BOe_Qmuw.d.mts → bank.Cfz44GPA.d.mts} +1598 -12
- package/dist/shared/{bank.RD4xwHFf.d.cts → bank.Cfz44GPA.d.ts} +1598 -12
- package/dist/shared/{bank.B8QzFnJl.cjs → bank.CyinOg3r.cjs} +1 -1
- package/dist/shared/{bank.CjZRgRoJ.mjs → bank.DKtu6cfY.mjs} +1 -1
- package/dist/shared/{bank.B0uSWeRe.mjs → bank.DXn9jD0Q.mjs} +43 -60
- package/dist/shared/{bank.CWAToo_g.cjs → bank.NdqOkgpd.cjs} +41 -59
- package/dist/types.cjs +2 -4
- package/dist/types.d.cts +3 -5
- package/dist/types.d.mts +3 -5
- package/dist/types.d.ts +3 -5
- package/dist/types.mjs +2 -3
- package/package.json +2 -1
- package/dist/shared/bank.BGZYksmV.d.cts +0 -1598
- package/dist/shared/bank.BGZYksmV.d.mts +0 -1598
- package/dist/shared/bank.BGZYksmV.d.ts +0 -1598
|
@@ -3,25 +3,34 @@
|
|
|
3
3
|
const cloudflare_workers = require('cloudflare:workers');
|
|
4
4
|
const cloudflare_workflows = require('cloudflare:workflows');
|
|
5
5
|
const d1 = require('drizzle-orm/d1');
|
|
6
|
-
const database_schema = require('../shared/bank.
|
|
6
|
+
const database_schema = require('../shared/bank.NdqOkgpd.cjs');
|
|
7
7
|
const backendSdk = require('@develit-io/backend-sdk');
|
|
8
|
-
const
|
|
8
|
+
const encryption = require('../shared/bank.CHS79KTx.cjs');
|
|
9
9
|
const drizzleOrm = require('drizzle-orm');
|
|
10
10
|
require('drizzle-orm/sqlite-core');
|
|
11
11
|
require('date-fns');
|
|
12
|
-
require('@develit-io/general-codes');
|
|
13
|
-
require('zod');
|
|
14
12
|
require('jose');
|
|
13
|
+
require('@develit-io/general-codes');
|
|
15
14
|
require('drizzle-zod');
|
|
16
15
|
require('drizzle-orm/relations');
|
|
17
|
-
require('../shared/bank.
|
|
16
|
+
require('../shared/bank.CyinOg3r.cjs');
|
|
18
17
|
|
|
19
|
-
const
|
|
20
|
-
|
|
18
|
+
const updateAccountLastSyncCommand = (db, {
|
|
19
|
+
lastSyncAt,
|
|
20
|
+
accountId,
|
|
21
|
+
lastSyncMetadata
|
|
22
|
+
}) => {
|
|
23
|
+
const command = db.update(database_schema.tables.account).set({
|
|
24
|
+
lastSyncAt,
|
|
25
|
+
lastSyncMetadata
|
|
26
|
+
}).where(drizzleOrm.eq(database_schema.tables.account.id, accountId)).returning();
|
|
27
|
+
return {
|
|
28
|
+
command
|
|
29
|
+
};
|
|
21
30
|
};
|
|
22
31
|
|
|
23
|
-
const
|
|
24
|
-
return await db.select().from(database_schema.tables.
|
|
32
|
+
const getAccountByIdQuery = async (db, { accountId }) => {
|
|
33
|
+
return await db.select().from(database_schema.tables.account).where(drizzleOrm.eq(database_schema.tables.account.id, accountId)).get();
|
|
25
34
|
};
|
|
26
35
|
|
|
27
36
|
function pushToQueue(queue, message) {
|
|
@@ -38,6 +47,7 @@ class SyncAccountPaymentsWorkflow extends cloudflare_workers.WorkflowEntrypoint
|
|
|
38
47
|
const { accountId } = event.payload;
|
|
39
48
|
const db = d1.drizzle(this.env.BANK_D1, { schema: database_schema.tables });
|
|
40
49
|
while (true) {
|
|
50
|
+
const now = /* @__PURE__ */ new Date();
|
|
41
51
|
const account = await step.do("load account", async () => {
|
|
42
52
|
const account2 = await getAccountByIdQuery(db, { accountId });
|
|
43
53
|
if (!account2) {
|
|
@@ -45,21 +55,10 @@ class SyncAccountPaymentsWorkflow extends cloudflare_workers.WorkflowEntrypoint
|
|
|
45
55
|
}
|
|
46
56
|
return account2;
|
|
47
57
|
});
|
|
48
|
-
if (!account.
|
|
58
|
+
if (!account.lastSyncAt) {
|
|
49
59
|
throw new Error(`lastSyncedAt is not set for account: ${accountId}`);
|
|
50
60
|
}
|
|
51
|
-
|
|
52
|
-
"load account credentials",
|
|
53
|
-
async () => {
|
|
54
|
-
const credentials2 = await bank.getCredentialsByAccountId(db, { accountId });
|
|
55
|
-
if (!credentials2) {
|
|
56
|
-
throw new cloudflare_workflows.NonRetryableError(
|
|
57
|
-
`No credentials found for account: ${accountId}`
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
return credentials2;
|
|
61
|
-
}
|
|
62
|
-
);
|
|
61
|
+
//! ### This step is currently inside the fetch payments step with retries due to cf workflows output logs (until they allow hiding sensitive data) <-----
|
|
63
62
|
const payments = await step.do(
|
|
64
63
|
"fetch bank payments",
|
|
65
64
|
{
|
|
@@ -67,7 +66,23 @@ class SyncAccountPaymentsWorkflow extends cloudflare_workers.WorkflowEntrypoint
|
|
|
67
66
|
timeout: "30 seconds"
|
|
68
67
|
},
|
|
69
68
|
async () => {
|
|
70
|
-
const
|
|
69
|
+
const encryptionKey = await encryption.importAesKey(this.env.ENCRYPTION_KEY);
|
|
70
|
+
const credentials = await encryption.getCredentialsByAccountId(
|
|
71
|
+
db,
|
|
72
|
+
encryptionKey,
|
|
73
|
+
{ accountId }
|
|
74
|
+
);
|
|
75
|
+
if (!credentials) {
|
|
76
|
+
throw new cloudflare_workflows.NonRetryableError(
|
|
77
|
+
`No credentials found for account: ${accountId}`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
if (credentials.expiresAt < /* @__PURE__ */ new Date()) {
|
|
81
|
+
throw new cloudflare_workflows.NonRetryableError(
|
|
82
|
+
`Credentials have expired for account: ${accountId}`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
const connector = encryption.initiateConnector({
|
|
71
86
|
env: this.env,
|
|
72
87
|
bank: account.connectorKey,
|
|
73
88
|
connectedAccounts: [
|
|
@@ -82,84 +97,67 @@ class SyncAccountPaymentsWorkflow extends cloudflare_workers.WorkflowEntrypoint
|
|
|
82
97
|
environment: this.env.ENVIRONMENT,
|
|
83
98
|
db,
|
|
84
99
|
account,
|
|
85
|
-
filter: { dateFrom: account.
|
|
100
|
+
filter: { dateFrom: account.lastSyncAt }
|
|
86
101
|
});
|
|
87
102
|
}
|
|
88
103
|
);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const paymentsToUpdate = payments.filter(
|
|
97
|
-
(p) => existing.some((e) => e.bankRefId === p.parsed.bankRefId)
|
|
98
|
-
);
|
|
99
|
-
const paymentsToCreate = payments.filter(
|
|
100
|
-
(p) => !existing.some((e) => e.bankRefId === p.parsed.bankRefId)
|
|
104
|
+
const paymentsToProcess = payments.filter(
|
|
105
|
+
(p) => p.parsed.status === "COMPLETED" || p.parsed.status === "FAILED"
|
|
106
|
+
);
|
|
107
|
+
if (paymentsToProcess.length) {
|
|
108
|
+
const lastSyncBankRefIds = account.lastSyncMetadata?.lastSyncBankRefIds || [];
|
|
109
|
+
const paymentsToInsert = paymentsToProcess.filter(
|
|
110
|
+
(p) => !lastSyncBankRefIds.includes(p.parsed.bankRefId)
|
|
101
111
|
);
|
|
102
|
-
await step.do(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
...paymentsToUpdate.map((p) => ({
|
|
109
|
-
eventType: "BANK_PAYMENT",
|
|
110
|
-
eventSignal: "paymentUpdated",
|
|
111
|
-
bankPayment: p.parsed,
|
|
112
|
-
metadata: {
|
|
113
|
-
correlationId: p.parsed.correlationId,
|
|
114
|
-
entityId: p.parsed.id,
|
|
115
|
-
idempotencySuffix: p.parsed.status,
|
|
116
|
-
timestamp: (/* @__PURE__ */ new Date()).toDateString()
|
|
117
|
-
}
|
|
118
|
-
}))
|
|
119
|
-
);
|
|
120
|
-
const createCommands = paymentsToCreate.map(
|
|
121
|
-
(p) => bank.createPaymentCommand(db, { payment: p.parsed }).command
|
|
122
|
-
);
|
|
123
|
-
eventsToEmit.push(
|
|
124
|
-
...paymentsToCreate.map((p) => ({
|
|
125
|
-
eventType: "BANK_PAYMENT",
|
|
126
|
-
eventSignal: "paymentCreated",
|
|
127
|
-
bankPayment: p.parsed,
|
|
128
|
-
metadata: {
|
|
129
|
-
correlationId: p.parsed.correlationId,
|
|
130
|
-
entityId: p.parsed.id,
|
|
131
|
-
timestamp: (/* @__PURE__ */ new Date()).toDateString()
|
|
132
|
-
}
|
|
133
|
-
}))
|
|
134
|
-
);
|
|
135
|
-
const latest = payments.reduce((current, p) => {
|
|
136
|
-
return new Date(p.parsed.processedAt).getTime() > new Date(current.parsed.processedAt).getTime() ? p : current;
|
|
137
|
-
}, payments[0]);
|
|
138
|
-
const updateLastSyncCommand = bank.updateAccountLastSyncCommand(db, {
|
|
139
|
-
accountId: account.id,
|
|
140
|
-
lastSyncedAt: latest.parsed.processedAt
|
|
141
|
-
}).command;
|
|
142
|
-
if (eventsToEmit.length) {
|
|
143
|
-
await db.batch(
|
|
144
|
-
backendSdk.asNonEmpty([
|
|
145
|
-
updateLastSyncCommand,
|
|
146
|
-
...updateCommands,
|
|
147
|
-
...createCommands
|
|
148
|
-
])
|
|
112
|
+
await step.do(
|
|
113
|
+
"process new payments and update lastSyncAt",
|
|
114
|
+
async () => {
|
|
115
|
+
const eventsToEmit = [];
|
|
116
|
+
const createCommands = paymentsToInsert.map(
|
|
117
|
+
(p) => encryption.createPaymentCommand(db, { payment: p.parsed }).command
|
|
149
118
|
);
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
119
|
+
eventsToEmit.push(
|
|
120
|
+
...paymentsToInsert.map((p) => ({
|
|
121
|
+
eventType: "BANK_PAYMENT",
|
|
122
|
+
eventSignal: "paymentCreated",
|
|
123
|
+
bankPayment: p.parsed,
|
|
124
|
+
metadata: {
|
|
125
|
+
correlationId: p.parsed.correlationId,
|
|
126
|
+
entityId: p.parsed.id,
|
|
127
|
+
timestamp: (/* @__PURE__ */ new Date()).toDateString()
|
|
128
|
+
}
|
|
129
|
+
}))
|
|
153
130
|
);
|
|
131
|
+
const lastSyncMetadata = {
|
|
132
|
+
payments: payments.length,
|
|
133
|
+
paymentsToProcess: paymentsToProcess.length,
|
|
134
|
+
paymentsInserted: paymentsToInsert.length,
|
|
135
|
+
lastSyncBankRefIds: paymentsToProcess.map(
|
|
136
|
+
(p) => p.parsed.bankRefId
|
|
137
|
+
),
|
|
138
|
+
lastSyncPayments: lastSyncBankRefIds.length,
|
|
139
|
+
eventsEmitted: eventsToEmit.length
|
|
140
|
+
};
|
|
141
|
+
const updateLastSyncCommand = updateAccountLastSyncCommand(db, {
|
|
142
|
+
accountId: account.id,
|
|
143
|
+
lastSyncAt: now,
|
|
144
|
+
lastSyncMetadata
|
|
145
|
+
}).command;
|
|
146
|
+
if (eventsToEmit.length) {
|
|
147
|
+
await db.batch(
|
|
148
|
+
backendSdk.asNonEmpty([updateLastSyncCommand, ...createCommands])
|
|
149
|
+
);
|
|
150
|
+
await pushToQueue(
|
|
151
|
+
this.env.QUEUE_BUS_QUEUE,
|
|
152
|
+
eventsToEmit
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
...lastSyncMetadata,
|
|
157
|
+
newLastSyncAt: now
|
|
158
|
+
};
|
|
154
159
|
}
|
|
155
|
-
|
|
156
|
-
payments: payments.length,
|
|
157
|
-
updated: paymentsToUpdate.length,
|
|
158
|
-
created: paymentsToCreate.length,
|
|
159
|
-
eventsEmitted: eventsToEmit.length,
|
|
160
|
-
lastSyncedAt: latest?.parsed?.createdAt
|
|
161
|
-
};
|
|
162
|
-
});
|
|
160
|
+
);
|
|
163
161
|
}
|
|
164
162
|
await step.sleep(
|
|
165
163
|
"Sleep for next sync",
|
|
@@ -1,25 +1,34 @@
|
|
|
1
1
|
import { WorkflowEntrypoint } from 'cloudflare:workers';
|
|
2
2
|
import { NonRetryableError } from 'cloudflare:workflows';
|
|
3
3
|
import { drizzle } from 'drizzle-orm/d1';
|
|
4
|
-
import { t as tables } from '../shared/bank.
|
|
4
|
+
import { t as tables } from '../shared/bank.DXn9jD0Q.mjs';
|
|
5
5
|
import { asNonEmpty } from '@develit-io/backend-sdk';
|
|
6
|
-
import {
|
|
7
|
-
import { eq
|
|
6
|
+
import { i as importAesKey, g as getCredentialsByAccountId, a as initiateConnector, c as createPaymentCommand } from '../shared/bank.BvUtf1S3.mjs';
|
|
7
|
+
import { eq } from 'drizzle-orm';
|
|
8
8
|
import 'drizzle-orm/sqlite-core';
|
|
9
9
|
import 'date-fns';
|
|
10
|
-
import '@develit-io/general-codes';
|
|
11
|
-
import 'zod';
|
|
12
10
|
import 'jose';
|
|
11
|
+
import '@develit-io/general-codes';
|
|
13
12
|
import 'drizzle-zod';
|
|
14
13
|
import 'drizzle-orm/relations';
|
|
15
|
-
import '../shared/bank.
|
|
14
|
+
import '../shared/bank.DKtu6cfY.mjs';
|
|
16
15
|
|
|
17
|
-
const
|
|
18
|
-
|
|
16
|
+
const updateAccountLastSyncCommand = (db, {
|
|
17
|
+
lastSyncAt,
|
|
18
|
+
accountId,
|
|
19
|
+
lastSyncMetadata
|
|
20
|
+
}) => {
|
|
21
|
+
const command = db.update(tables.account).set({
|
|
22
|
+
lastSyncAt,
|
|
23
|
+
lastSyncMetadata
|
|
24
|
+
}).where(eq(tables.account.id, accountId)).returning();
|
|
25
|
+
return {
|
|
26
|
+
command
|
|
27
|
+
};
|
|
19
28
|
};
|
|
20
29
|
|
|
21
|
-
const
|
|
22
|
-
return await db.select().from(tables.
|
|
30
|
+
const getAccountByIdQuery = async (db, { accountId }) => {
|
|
31
|
+
return await db.select().from(tables.account).where(eq(tables.account.id, accountId)).get();
|
|
23
32
|
};
|
|
24
33
|
|
|
25
34
|
function pushToQueue(queue, message) {
|
|
@@ -36,6 +45,7 @@ class SyncAccountPaymentsWorkflow extends WorkflowEntrypoint {
|
|
|
36
45
|
const { accountId } = event.payload;
|
|
37
46
|
const db = drizzle(this.env.BANK_D1, { schema: tables });
|
|
38
47
|
while (true) {
|
|
48
|
+
const now = /* @__PURE__ */ new Date();
|
|
39
49
|
const account = await step.do("load account", async () => {
|
|
40
50
|
const account2 = await getAccountByIdQuery(db, { accountId });
|
|
41
51
|
if (!account2) {
|
|
@@ -43,21 +53,10 @@ class SyncAccountPaymentsWorkflow extends WorkflowEntrypoint {
|
|
|
43
53
|
}
|
|
44
54
|
return account2;
|
|
45
55
|
});
|
|
46
|
-
if (!account.
|
|
56
|
+
if (!account.lastSyncAt) {
|
|
47
57
|
throw new Error(`lastSyncedAt is not set for account: ${accountId}`);
|
|
48
58
|
}
|
|
49
|
-
|
|
50
|
-
"load account credentials",
|
|
51
|
-
async () => {
|
|
52
|
-
const credentials2 = await getCredentialsByAccountId(db, { accountId });
|
|
53
|
-
if (!credentials2) {
|
|
54
|
-
throw new NonRetryableError(
|
|
55
|
-
`No credentials found for account: ${accountId}`
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
return credentials2;
|
|
59
|
-
}
|
|
60
|
-
);
|
|
59
|
+
//! ### This step is currently inside the fetch payments step with retries due to cf workflows output logs (until they allow hiding sensitive data) <-----
|
|
61
60
|
const payments = await step.do(
|
|
62
61
|
"fetch bank payments",
|
|
63
62
|
{
|
|
@@ -65,6 +64,22 @@ class SyncAccountPaymentsWorkflow extends WorkflowEntrypoint {
|
|
|
65
64
|
timeout: "30 seconds"
|
|
66
65
|
},
|
|
67
66
|
async () => {
|
|
67
|
+
const encryptionKey = await importAesKey(this.env.ENCRYPTION_KEY);
|
|
68
|
+
const credentials = await getCredentialsByAccountId(
|
|
69
|
+
db,
|
|
70
|
+
encryptionKey,
|
|
71
|
+
{ accountId }
|
|
72
|
+
);
|
|
73
|
+
if (!credentials) {
|
|
74
|
+
throw new NonRetryableError(
|
|
75
|
+
`No credentials found for account: ${accountId}`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
if (credentials.expiresAt < /* @__PURE__ */ new Date()) {
|
|
79
|
+
throw new NonRetryableError(
|
|
80
|
+
`Credentials have expired for account: ${accountId}`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
68
83
|
const connector = initiateConnector({
|
|
69
84
|
env: this.env,
|
|
70
85
|
bank: account.connectorKey,
|
|
@@ -80,84 +95,67 @@ class SyncAccountPaymentsWorkflow extends WorkflowEntrypoint {
|
|
|
80
95
|
environment: this.env.ENVIRONMENT,
|
|
81
96
|
db,
|
|
82
97
|
account,
|
|
83
|
-
filter: { dateFrom: account.
|
|
98
|
+
filter: { dateFrom: account.lastSyncAt }
|
|
84
99
|
});
|
|
85
100
|
}
|
|
86
101
|
);
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const paymentsToUpdate = payments.filter(
|
|
95
|
-
(p) => existing.some((e) => e.bankRefId === p.parsed.bankRefId)
|
|
96
|
-
);
|
|
97
|
-
const paymentsToCreate = payments.filter(
|
|
98
|
-
(p) => !existing.some((e) => e.bankRefId === p.parsed.bankRefId)
|
|
102
|
+
const paymentsToProcess = payments.filter(
|
|
103
|
+
(p) => p.parsed.status === "COMPLETED" || p.parsed.status === "FAILED"
|
|
104
|
+
);
|
|
105
|
+
if (paymentsToProcess.length) {
|
|
106
|
+
const lastSyncBankRefIds = account.lastSyncMetadata?.lastSyncBankRefIds || [];
|
|
107
|
+
const paymentsToInsert = paymentsToProcess.filter(
|
|
108
|
+
(p) => !lastSyncBankRefIds.includes(p.parsed.bankRefId)
|
|
99
109
|
);
|
|
100
|
-
await step.do(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
...paymentsToUpdate.map((p) => ({
|
|
107
|
-
eventType: "BANK_PAYMENT",
|
|
108
|
-
eventSignal: "paymentUpdated",
|
|
109
|
-
bankPayment: p.parsed,
|
|
110
|
-
metadata: {
|
|
111
|
-
correlationId: p.parsed.correlationId,
|
|
112
|
-
entityId: p.parsed.id,
|
|
113
|
-
idempotencySuffix: p.parsed.status,
|
|
114
|
-
timestamp: (/* @__PURE__ */ new Date()).toDateString()
|
|
115
|
-
}
|
|
116
|
-
}))
|
|
117
|
-
);
|
|
118
|
-
const createCommands = paymentsToCreate.map(
|
|
119
|
-
(p) => createPaymentCommand(db, { payment: p.parsed }).command
|
|
120
|
-
);
|
|
121
|
-
eventsToEmit.push(
|
|
122
|
-
...paymentsToCreate.map((p) => ({
|
|
123
|
-
eventType: "BANK_PAYMENT",
|
|
124
|
-
eventSignal: "paymentCreated",
|
|
125
|
-
bankPayment: p.parsed,
|
|
126
|
-
metadata: {
|
|
127
|
-
correlationId: p.parsed.correlationId,
|
|
128
|
-
entityId: p.parsed.id,
|
|
129
|
-
timestamp: (/* @__PURE__ */ new Date()).toDateString()
|
|
130
|
-
}
|
|
131
|
-
}))
|
|
132
|
-
);
|
|
133
|
-
const latest = payments.reduce((current, p) => {
|
|
134
|
-
return new Date(p.parsed.processedAt).getTime() > new Date(current.parsed.processedAt).getTime() ? p : current;
|
|
135
|
-
}, payments[0]);
|
|
136
|
-
const updateLastSyncCommand = updateAccountLastSyncCommand(db, {
|
|
137
|
-
accountId: account.id,
|
|
138
|
-
lastSyncedAt: latest.parsed.processedAt
|
|
139
|
-
}).command;
|
|
140
|
-
if (eventsToEmit.length) {
|
|
141
|
-
await db.batch(
|
|
142
|
-
asNonEmpty([
|
|
143
|
-
updateLastSyncCommand,
|
|
144
|
-
...updateCommands,
|
|
145
|
-
...createCommands
|
|
146
|
-
])
|
|
110
|
+
await step.do(
|
|
111
|
+
"process new payments and update lastSyncAt",
|
|
112
|
+
async () => {
|
|
113
|
+
const eventsToEmit = [];
|
|
114
|
+
const createCommands = paymentsToInsert.map(
|
|
115
|
+
(p) => createPaymentCommand(db, { payment: p.parsed }).command
|
|
147
116
|
);
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
117
|
+
eventsToEmit.push(
|
|
118
|
+
...paymentsToInsert.map((p) => ({
|
|
119
|
+
eventType: "BANK_PAYMENT",
|
|
120
|
+
eventSignal: "paymentCreated",
|
|
121
|
+
bankPayment: p.parsed,
|
|
122
|
+
metadata: {
|
|
123
|
+
correlationId: p.parsed.correlationId,
|
|
124
|
+
entityId: p.parsed.id,
|
|
125
|
+
timestamp: (/* @__PURE__ */ new Date()).toDateString()
|
|
126
|
+
}
|
|
127
|
+
}))
|
|
151
128
|
);
|
|
129
|
+
const lastSyncMetadata = {
|
|
130
|
+
payments: payments.length,
|
|
131
|
+
paymentsToProcess: paymentsToProcess.length,
|
|
132
|
+
paymentsInserted: paymentsToInsert.length,
|
|
133
|
+
lastSyncBankRefIds: paymentsToProcess.map(
|
|
134
|
+
(p) => p.parsed.bankRefId
|
|
135
|
+
),
|
|
136
|
+
lastSyncPayments: lastSyncBankRefIds.length,
|
|
137
|
+
eventsEmitted: eventsToEmit.length
|
|
138
|
+
};
|
|
139
|
+
const updateLastSyncCommand = updateAccountLastSyncCommand(db, {
|
|
140
|
+
accountId: account.id,
|
|
141
|
+
lastSyncAt: now,
|
|
142
|
+
lastSyncMetadata
|
|
143
|
+
}).command;
|
|
144
|
+
if (eventsToEmit.length) {
|
|
145
|
+
await db.batch(
|
|
146
|
+
asNonEmpty([updateLastSyncCommand, ...createCommands])
|
|
147
|
+
);
|
|
148
|
+
await pushToQueue(
|
|
149
|
+
this.env.QUEUE_BUS_QUEUE,
|
|
150
|
+
eventsToEmit
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
...lastSyncMetadata,
|
|
155
|
+
newLastSyncAt: now
|
|
156
|
+
};
|
|
152
157
|
}
|
|
153
|
-
|
|
154
|
-
payments: payments.length,
|
|
155
|
-
updated: paymentsToUpdate.length,
|
|
156
|
-
created: paymentsToCreate.length,
|
|
157
|
-
eventsEmitted: eventsToEmit.length,
|
|
158
|
-
lastSyncedAt: latest?.parsed?.createdAt
|
|
159
|
-
};
|
|
160
|
-
});
|
|
158
|
+
);
|
|
161
159
|
}
|
|
162
160
|
await step.sleep(
|
|
163
161
|
"Sleep for next sync",
|
package/dist/export/wrangler.cjs
CHANGED
|
@@ -13,6 +13,7 @@ declare function defineBankServiceWrangler(config: BankServiceWranglerConfig): {
|
|
|
13
13
|
CRON_PAYMENTS_PROCESSING: string;
|
|
14
14
|
BANK_AUTH_RECIPIENT: string;
|
|
15
15
|
REDIRECT_URI: string;
|
|
16
|
+
ENCRYPTION_KEY: string;
|
|
16
17
|
FINBRICKS_PRIVATE_KEY_PEM: string;
|
|
17
18
|
ERSTE_API_KEY: string;
|
|
18
19
|
ERSTE_CLIENT_SECRET: string;
|
|
@@ -13,6 +13,7 @@ declare function defineBankServiceWrangler(config: BankServiceWranglerConfig): {
|
|
|
13
13
|
CRON_PAYMENTS_PROCESSING: string;
|
|
14
14
|
BANK_AUTH_RECIPIENT: string;
|
|
15
15
|
REDIRECT_URI: string;
|
|
16
|
+
ENCRYPTION_KEY: string;
|
|
16
17
|
FINBRICKS_PRIVATE_KEY_PEM: string;
|
|
17
18
|
ERSTE_API_KEY: string;
|
|
18
19
|
ERSTE_CLIENT_SECRET: string;
|
|
@@ -13,6 +13,7 @@ declare function defineBankServiceWrangler(config: BankServiceWranglerConfig): {
|
|
|
13
13
|
CRON_PAYMENTS_PROCESSING: string;
|
|
14
14
|
BANK_AUTH_RECIPIENT: string;
|
|
15
15
|
REDIRECT_URI: string;
|
|
16
|
+
ENCRYPTION_KEY: string;
|
|
16
17
|
FINBRICKS_PRIVATE_KEY_PEM: string;
|
|
17
18
|
ERSTE_API_KEY: string;
|
|
18
19
|
ERSTE_CLIENT_SECRET: string;
|
package/dist/export/wrangler.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { t as tables, F as FinbricksConnector, M as MockConnector, E as ErsteConnector } from './bank.
|
|
1
|
+
import { t as tables, F as FinbricksConnector, M as MockConnector, E as ErsteConnector } from './bank.DXn9jD0Q.mjs';
|
|
2
|
+
import '@develit-io/backend-sdk';
|
|
2
3
|
import { eq } from 'drizzle-orm';
|
|
3
|
-
import { M as MockCobsConnector } from './bank.CjZRgRoJ.mjs';
|
|
4
4
|
import 'jose';
|
|
5
5
|
import '@develit-io/general-codes';
|
|
6
|
+
import { M as MockCobsConnector } from './bank.DKtu6cfY.mjs';
|
|
6
7
|
|
|
7
8
|
const createPaymentCommand = (db, { payment }) => {
|
|
8
9
|
return {
|
|
@@ -14,24 +15,12 @@ const createPaymentCommand = (db, { payment }) => {
|
|
|
14
15
|
};
|
|
15
16
|
};
|
|
16
17
|
|
|
17
|
-
const
|
|
18
|
-
return {
|
|
19
|
-
command: db.update(tables.payment).set(payment).where(eq(tables.payment.id, payment.id)).returning()
|
|
20
|
-
};
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const updateAccountLastSyncCommand = (db, { lastSyncedAt, accountId }) => {
|
|
24
|
-
const command = db.update(tables.account).set({
|
|
25
|
-
lastSyncedAt
|
|
26
|
-
}).where(eq(tables.account.id, accountId)).returning();
|
|
27
|
-
return {
|
|
28
|
-
command
|
|
29
|
-
};
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const getCredentialsByAccountId = async (db, { accountId }) => {
|
|
18
|
+
const getCredentialsByAccountId = async (db, encryptionKey, { accountId }) => {
|
|
33
19
|
const cred = await db.select().from(tables.accountCredentials).where(eq(tables.accountCredentials.accountId, accountId)).get();
|
|
34
|
-
return cred
|
|
20
|
+
return cred ? {
|
|
21
|
+
...cred,
|
|
22
|
+
value: await decrypt(cred.value, encryptionKey)
|
|
23
|
+
} : void 0;
|
|
35
24
|
};
|
|
36
25
|
|
|
37
26
|
class CreditasConnector extends FinbricksConnector {
|
|
@@ -106,4 +95,36 @@ const initiateConnector = ({
|
|
|
106
95
|
}
|
|
107
96
|
};
|
|
108
97
|
|
|
109
|
-
|
|
98
|
+
async function importAesKey(base64Key) {
|
|
99
|
+
const raw = Uint8Array.from(atob(base64Key), (c) => c.charCodeAt(0));
|
|
100
|
+
return await crypto.subtle.importKey("raw", raw, { name: "AES-GCM" }, false, [
|
|
101
|
+
"encrypt",
|
|
102
|
+
"decrypt"
|
|
103
|
+
]);
|
|
104
|
+
}
|
|
105
|
+
async function encrypt(value, key) {
|
|
106
|
+
const encoder = new TextEncoder();
|
|
107
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
108
|
+
const ciphertext = await crypto.subtle.encrypt(
|
|
109
|
+
{ name: "AES-GCM", iv },
|
|
110
|
+
key,
|
|
111
|
+
encoder.encode(value)
|
|
112
|
+
);
|
|
113
|
+
const combined = new Uint8Array(iv.length + ciphertext.byteLength);
|
|
114
|
+
combined.set(iv, 0);
|
|
115
|
+
combined.set(new Uint8Array(ciphertext), iv.length);
|
|
116
|
+
return btoa(String.fromCharCode(...combined));
|
|
117
|
+
}
|
|
118
|
+
async function decrypt(base64, key) {
|
|
119
|
+
const raw = Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
|
|
120
|
+
const iv = raw.slice(0, 12);
|
|
121
|
+
const ciphertext = raw.slice(12);
|
|
122
|
+
const decrypted = await crypto.subtle.decrypt(
|
|
123
|
+
{ name: "AES-GCM", iv },
|
|
124
|
+
key,
|
|
125
|
+
ciphertext
|
|
126
|
+
);
|
|
127
|
+
return new TextDecoder().decode(decrypted);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export { initiateConnector as a, createPaymentCommand as c, encrypt as e, getCredentialsByAccountId as g, importAesKey as i };
|