@develit-services/bank 0.0.44 → 0.1.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.
Files changed (41) hide show
  1. package/dist/database/schema.cjs +1 -1
  2. package/dist/database/schema.d.cts +1 -1
  3. package/dist/database/schema.d.mts +1 -1
  4. package/dist/database/schema.d.ts +1 -1
  5. package/dist/database/schema.mjs +1 -1
  6. package/dist/export/worker.cjs +113 -568
  7. package/dist/export/worker.d.cts +81 -14
  8. package/dist/export/worker.d.mts +82 -13
  9. package/dist/export/worker.d.ts +81 -14
  10. package/dist/export/worker.mjs +105 -557
  11. package/dist/export/workflows.cjs +21 -0
  12. package/dist/export/workflows.d.cts +2 -0
  13. package/dist/export/workflows.d.mts +2 -0
  14. package/dist/export/workflows.d.ts +2 -0
  15. package/dist/export/workflows.mjs +15 -0
  16. package/dist/export/wrangler.cjs +7 -0
  17. package/dist/export/wrangler.d.cts +5 -0
  18. package/dist/export/wrangler.d.mts +5 -0
  19. package/dist/export/wrangler.d.ts +5 -0
  20. package/dist/export/wrangler.mjs +7 -0
  21. package/dist/shared/{bank.BVzOzXdX.cjs → bank.3YYIj-n6.cjs} +11 -9
  22. package/dist/shared/{bank.6WoCPIFy.mjs → bank.B72e0ibs.mjs} +1 -1
  23. package/dist/shared/{bank.BPcrbUBa.cjs → bank.BgpXGC_1.cjs} +1 -1
  24. package/dist/shared/{bank.CCilUVCi.d.ts → bank.BriEYREq.d.cts} +4 -4
  25. package/dist/shared/{bank.CCilUVCi.d.cts → bank.BriEYREq.d.mts} +4 -4
  26. package/dist/shared/{bank.CCilUVCi.d.mts → bank.BriEYREq.d.ts} +4 -4
  27. package/dist/shared/bank.CH22Zrdv.d.cts +20 -0
  28. package/dist/shared/bank.CH22Zrdv.d.mts +20 -0
  29. package/dist/shared/bank.CH22Zrdv.d.ts +20 -0
  30. package/dist/shared/{bank.BWcFhTu1.mjs → bank.CcKNlFRd.mjs} +11 -9
  31. package/dist/shared/{bank.DmzkJQVU.d.cts → bank.DSZbtb1J.d.mts} +33 -29
  32. package/dist/shared/{bank.0KTS9nMz.d.ts → bank.Ddhhr_rz.d.ts} +33 -29
  33. package/dist/shared/{bank.NTyp2XAv.d.mts → bank.DqRaP8LS.d.cts} +33 -29
  34. package/dist/shared/bank.GMK4QNvo.cjs +276 -0
  35. package/dist/shared/bank.jyyw3_3-.mjs +267 -0
  36. package/dist/types.cjs +2 -2
  37. package/dist/types.d.cts +16 -9
  38. package/dist/types.d.mts +16 -9
  39. package/dist/types.d.ts +16 -9
  40. package/dist/types.mjs +2 -2
  41. package/package.json +6 -1
@@ -1,11 +1,24 @@
1
1
  import { Environment } from '@develit-io/backend-sdk';
2
- import { CURRENCY_CODES, BANK_CODES, CODES } from '@develit-io/general-codes';
3
- import { a as PaymentInsertType, t as tables } from './bank.CCilUVCi.mjs';
4
2
  import { DrizzleD1Database } from 'drizzle-orm/d1';
3
+ import { a as PaymentInsertType, t as tables } from './bank.BriEYREq.cjs';
4
+ import { CURRENCY_CODES, BANK_CODES, CODES } from '@develit-io/general-codes';
5
5
  import { z } from 'zod';
6
6
  import * as drizzle_zod from 'drizzle-zod';
7
7
  import * as drizzle_orm_sqlite_core from 'drizzle-orm/sqlite-core';
8
8
 
9
+ interface BankAccountWithLastSync extends ConnectedAccount {
10
+ lastSyncedAt: Date;
11
+ }
12
+ declare const CONNECTOR_KEYS: readonly ["ERSTE", "FINBRICKS", "MOCK", "CREDITAS", "MOCK_COBS", "FIO", "MONETA"];
13
+ type ConnectorKey = (typeof CONNECTOR_KEYS)[number];
14
+ declare const CREDENTIALS_TYPES: readonly ["AUTH_TOKEN", "REFRESH_TOKEN", "CLIENT_ID", "API_KEY"];
15
+ type CredentialsType = (typeof CREDENTIALS_TYPES)[number];
16
+ declare const TOKEN_TYPES: readonly ["ACCOUNT_AUTHORIZATION"];
17
+ type TokenType = (typeof TOKEN_TYPES)[number];
18
+ interface ConfigEnvironmentBank {
19
+ allowedProviders: ConnectorKey[];
20
+ }
21
+
9
22
  declare const PAYMENT_TYPES: readonly ["SEPA", "SWIFT", "IFSC", "DOMESTIC"];
10
23
  type PaymentType = (typeof PAYMENT_TYPES)[number];
11
24
  declare const CHARGE_BEARERS: readonly ["SHA", "OUR", "BEN"];
@@ -37,19 +50,6 @@ type BankCode = (typeof BANK_CODES)[number];
37
50
  declare const COUNTRY_CODES: ["AF" | "AL" | "DZ" | "AS" | "AD" | "AO" | "AG" | "AR" | "AM" | "AW" | "AU" | "AT" | "AZ" | "BS" | "BH" | "BD" | "BB" | "BY" | "BE" | "BZ" | "BJ" | "BO" | "BQ" | "BA" | "BW" | "BR" | "IO" | "VG" | "BG" | "BF" | "BI" | "KH" | "CM" | "CA" | "CV" | "CF" | "TD" | "CL" | "CN" | "CO" | "KM" | "CK" | "CR" | "HR" | "CW" | "CY" | "CZ" | "CD" | "DK" | "DJ" | "DM" | "DO" | "TL" | "EC" | "EG" | "SV" | "GQ" | "ER" | "EE" | "ET" | "FO" | "FJ" | "FI" | "FR" | "GF" | "PF" | "GA" | "GM" | "GE" | "DE" | "GH" | "GR" | "GD" | "GP" | "GU" | "GT" | "GN" | "GW" | "GY" | "HT" | "HN" | "HU" | "IS" | "IN" | "ID" | "IR" | "IQ" | "IE" | "IM" | "IL" | "IT" | "CI" | "JM" | "JP" | "JE" | "JO" | "KZ" | "KE" | "KI" | "XK" | "KW" | "KG" | "LA" | "LV" | "LB" | "LS" | "LR" | "LI" | "LT" | "LU" | "MK" | "MG" | "MW" | "MY" | "MV" | "ML" | "MT" | "MQ" | "MR" | "MU" | "MX" | "FM" | "MD" | "MC" | "MN" | "ME" | "MS" | "MA" | "MZ" | "NA" | "NP" | "NL" | "NZ" | "NI" | "NE" | "NG" | "NU" | "NF" | "KP" | "NO" | "OM" | "PK" | "PS" | "PA" | "PG" | "PY" | "PE" | "PH" | "PL" | "PT" | "PR" | "QA" | "CG" | "RE" | "RO" | "RU" | "RW" | "BL" | "KN" | "LC" | "MF" | "VC" | "WS" | "SM" | "SA" | "SN" | "RS" | "SC" | "SL" | "SG" | "SX" | "SK" | "SI" | "SO" | "ZA" | "KR" | "SS" | "ES" | "LK" | "SD" | "SR" | "SJ" | "SZ" | "SE" | "CH" | "TW" | "TJ" | "TZ" | "TH" | "TG" | "TO" | "TT" | "TN" | "TR" | "TM" | "UM" | "VI" | "UG" | "UA" | "AE" | "GB" | "UZ" | "VU" | "VE" | "VN" | "WF" | "EH" | "YE" | "ZM" | "ZW", ...("AF" | "AL" | "DZ" | "AS" | "AD" | "AO" | "AG" | "AR" | "AM" | "AW" | "AU" | "AT" | "AZ" | "BS" | "BH" | "BD" | "BB" | "BY" | "BE" | "BZ" | "BJ" | "BO" | "BQ" | "BA" | "BW" | "BR" | "IO" | "VG" | "BG" | "BF" | "BI" | "KH" | "CM" | "CA" | "CV" | "CF" | "TD" | "CL" | "CN" | "CO" | "KM" | "CK" | "CR" | "HR" | "CW" | "CY" | "CZ" | "CD" | "DK" | "DJ" | "DM" | "DO" | "TL" | "EC" | "EG" | "SV" | "GQ" | "ER" | "EE" | "ET" | "FO" | "FJ" | "FI" | "FR" | "GF" | "PF" | "GA" | "GM" | "GE" | "DE" | "GH" | "GR" | "GD" | "GP" | "GU" | "GT" | "GN" | "GW" | "GY" | "HT" | "HN" | "HU" | "IS" | "IN" | "ID" | "IR" | "IQ" | "IE" | "IM" | "IL" | "IT" | "CI" | "JM" | "JP" | "JE" | "JO" | "KZ" | "KE" | "KI" | "XK" | "KW" | "KG" | "LA" | "LV" | "LB" | "LS" | "LR" | "LI" | "LT" | "LU" | "MK" | "MG" | "MW" | "MY" | "MV" | "ML" | "MT" | "MQ" | "MR" | "MU" | "MX" | "FM" | "MD" | "MC" | "MN" | "ME" | "MS" | "MA" | "MZ" | "NA" | "NP" | "NL" | "NZ" | "NI" | "NE" | "NG" | "NU" | "NF" | "KP" | "NO" | "OM" | "PK" | "PS" | "PA" | "PG" | "PY" | "PE" | "PH" | "PL" | "PT" | "PR" | "QA" | "CG" | "RE" | "RO" | "RU" | "RW" | "BL" | "KN" | "LC" | "MF" | "VC" | "WS" | "SM" | "SA" | "SN" | "RS" | "SC" | "SL" | "SG" | "SX" | "SK" | "SI" | "SO" | "ZA" | "KR" | "SS" | "ES" | "LK" | "SD" | "SR" | "SJ" | "SZ" | "SE" | "CH" | "TW" | "TJ" | "TZ" | "TH" | "TG" | "TO" | "TT" | "TN" | "TR" | "TM" | "UM" | "VI" | "UG" | "UA" | "AE" | "GB" | "UZ" | "VU" | "VE" | "VN" | "WF" | "EH" | "YE" | "ZM" | "ZW")[]];
38
51
  type CountryCode = (typeof COUNTRY_CODES)[number];
39
52
 
40
- interface BankAccountWithLastSync extends ConnectedAccount {
41
- lastSyncedAt: Date;
42
- }
43
- declare const CONNECTOR_KEYS: readonly ["ERSTE", "FINBRICKS", "MOCK", "CREDITAS", "MOCK_COBS", "FIO", "MONETA"];
44
- type ConnectorKey = (typeof CONNECTOR_KEYS)[number];
45
- declare const CREDENTIALS_TYPES: readonly ["AUTH_TOKEN", "REFRESH_TOKEN", "CLIENT_ID", "API_KEY"];
46
- type CredentialsType = (typeof CREDENTIALS_TYPES)[number];
47
- declare const TOKEN_TYPES: readonly ["ACCOUNT_AUTHORIZATION"];
48
- type TokenType = (typeof TOKEN_TYPES)[number];
49
- interface ConfigEnvironmentBank {
50
- allowedProviders: ConnectorKey[];
51
- }
52
-
53
53
  type WithStatus<T extends {
54
54
  status: PaymentStatus;
55
55
  }, S extends PaymentStatus> = Omit<T, 'status'> & {
@@ -106,10 +106,14 @@ declare abstract class IBankConnector {
106
106
  }): Promise<InitiatedBatch>;
107
107
  abstract initiateSEPAPayment(payment: IncomingPaymentMessage): Promise<InitiatedPayment>;
108
108
  abstract initiateSinglePayment(payment: PaymentPreparedInsertType): Promise<InitiatedPayment>;
109
- abstract getAllAccountPayments({ account, db, env, }: {
109
+ abstract getAllAccountPayments({ account, db, environment, filter, }: {
110
110
  account: AccountSelectType;
111
111
  db: DrizzleD1Database<typeof tables>;
112
- env: Environment;
112
+ environment: Environment;
113
+ filter: {
114
+ dateFrom: Date;
115
+ dateTo?: Date;
116
+ };
113
117
  }): Promise<PaymentInsertType[]>;
114
118
  abstract getPaymentStatus({ paymentId, }: {
115
119
  paymentId: string;
@@ -217,15 +221,15 @@ declare const accountInsertSchema: drizzle_zod.BuildSchema<"insert", {
217
221
  identity: undefined;
218
222
  generated: undefined;
219
223
  }, {}, {}>;
220
- syncPeriod: drizzle_orm_sqlite_core.SQLiteColumn<{
221
- name: "sync_period";
224
+ syncIntervalS: drizzle_orm_sqlite_core.SQLiteColumn<{
225
+ name: "sync_interval_s";
222
226
  tableName: "account";
223
227
  dataType: "number";
224
228
  columnType: "SQLiteInteger";
225
229
  data: number;
226
230
  driverParam: number;
227
- notNull: false;
228
- hasDefault: false;
231
+ notNull: true;
232
+ hasDefault: true;
229
233
  isPrimaryKey: false;
230
234
  isAutoincrement: false;
231
235
  hasRuntimeDefault: false;
@@ -682,15 +686,15 @@ declare const accountUpdateSchema: drizzle_zod.BuildSchema<"update", {
682
686
  identity: undefined;
683
687
  generated: undefined;
684
688
  }, {}, {}>;
685
- syncPeriod: drizzle_orm_sqlite_core.SQLiteColumn<{
686
- name: "sync_period";
689
+ syncIntervalS: drizzle_orm_sqlite_core.SQLiteColumn<{
690
+ name: "sync_interval_s";
687
691
  tableName: "account";
688
692
  dataType: "number";
689
693
  columnType: "SQLiteInteger";
690
694
  data: number;
691
695
  driverParam: number;
692
- notNull: false;
693
- hasDefault: false;
696
+ notNull: true;
697
+ hasDefault: true;
694
698
  isPrimaryKey: false;
695
699
  isAutoincrement: false;
696
700
  hasRuntimeDefault: false;
@@ -1147,15 +1151,15 @@ declare const accountSelectSchema: drizzle_zod.BuildSchema<"select", {
1147
1151
  identity: undefined;
1148
1152
  generated: undefined;
1149
1153
  }, {}, {}>;
1150
- syncPeriod: drizzle_orm_sqlite_core.SQLiteColumn<{
1151
- name: "sync_period";
1154
+ syncIntervalS: drizzle_orm_sqlite_core.SQLiteColumn<{
1155
+ name: "sync_interval_s";
1152
1156
  tableName: "account";
1153
1157
  dataType: "number";
1154
1158
  columnType: "SQLiteInteger";
1155
1159
  data: number;
1156
1160
  driverParam: number;
1157
- notNull: false;
1158
- hasDefault: false;
1161
+ notNull: true;
1162
+ hasDefault: true;
1159
1163
  isPrimaryKey: false;
1160
1164
  isAutoincrement: false;
1161
1165
  hasRuntimeDefault: false;
@@ -0,0 +1,276 @@
1
+ 'use strict';
2
+
3
+ const cloudflare_workers = require('cloudflare:workers');
4
+ const cloudflare_workflows = require('cloudflare:workflows');
5
+ const d1 = require('drizzle-orm/d1');
6
+ const database_schema = require('./bank.3YYIj-n6.cjs');
7
+ require('@develit-io/backend-sdk');
8
+ const drizzleOrm = require('drizzle-orm');
9
+ const mockCobs_connector = require('./bank.BgpXGC_1.cjs');
10
+ require('jose');
11
+ require('@develit-io/general-codes');
12
+
13
+ const createPaymentCommand = (db, { payment }) => {
14
+ return {
15
+ command: db.insert(database_schema.tables.payment).values(payment).returning()
16
+ };
17
+ };
18
+
19
+ const updatePaymentCommand = (db, { payment }) => {
20
+ return {
21
+ command: db.update(database_schema.tables.payment).set(payment).where(drizzleOrm.eq(database_schema.tables.payment.id, payment.id)).returning()
22
+ };
23
+ };
24
+
25
+ const updateAccountLastSyncCommand = (db, { lastSyncedAt, accountId }) => {
26
+ const command = db.update(database_schema.tables.account).set({
27
+ lastSyncedAt
28
+ }).where(drizzleOrm.eq(database_schema.tables.account.id, accountId)).returning();
29
+ return {
30
+ command
31
+ };
32
+ };
33
+
34
+ const getAccountByIdQuery = async (db, { accountId }) => {
35
+ return await db.select().from(database_schema.tables.account).where(drizzleOrm.eq(database_schema.tables.account.id, accountId)).get();
36
+ };
37
+
38
+ const getCredentialsByAccountId = async (db, { accountId }) => {
39
+ const cred = await db.select().from(database_schema.tables.accountCredentials).where(drizzleOrm.eq(database_schema.tables.accountCredentials.accountId, accountId)).get();
40
+ return cred;
41
+ };
42
+
43
+ const getPaymentsByBankRefIdsQuery = async (db, { ids }) => {
44
+ return await db.select().from(database_schema.tables.payment).where(drizzleOrm.inArray(database_schema.tables.payment.bankRefId, ids));
45
+ };
46
+
47
+ class CreditasConnector extends database_schema.FinbricksConnector {
48
+ constructor(config) {
49
+ super("CREDITAS", config);
50
+ }
51
+ }
52
+
53
+ class FioConnector extends database_schema.FinbricksConnector {
54
+ constructor(config) {
55
+ super("FIO", config);
56
+ }
57
+ }
58
+
59
+ class MonetaConnector extends database_schema.FinbricksConnector {
60
+ constructor(config) {
61
+ super("MONETA", config);
62
+ }
63
+ }
64
+
65
+ const initiateConnector = ({
66
+ bank,
67
+ env,
68
+ connectedAccounts
69
+ }) => {
70
+ switch (bank) {
71
+ case "ERSTE":
72
+ return new database_schema.ErsteConnector({
73
+ API_KEY: env.ERSTE_API_KEY,
74
+ CLIENT_ID: env.ERSTE_CLIENT_ID,
75
+ CLIENT_SECRET: env.ERSTE_CLIENT_SECRET,
76
+ REDIRECT_URI: env.REDIRECT_URI,
77
+ AUTH_URI: env.ERSTE_AUTH_URI,
78
+ PAYMENTS_URI: env.ERSTE_PAYMENTS_URI,
79
+ ACCOUNTS_URI: env.ERSTE_ACCOUNTS_URI,
80
+ connectedAccounts
81
+ });
82
+ case "CREDITAS":
83
+ return new CreditasConnector({
84
+ BASE_URI: env.FINBRICKS_BASE_URI,
85
+ MERCHANT_ID: env.FINBRICKS_MERCHANT_ID,
86
+ PRIVATE_KEY_PEM: env.FINBRICKS_PRIVATE_KEY_PEM,
87
+ REDIRECT_URI: env.REDIRECT_URI,
88
+ connectedAccounts
89
+ });
90
+ case "MOCK_COBS":
91
+ return new mockCobs_connector.MockCobsConnector({
92
+ BASE_URI: env.FINBRICKS_BASE_URI,
93
+ MERCHANT_ID: env.FINBRICKS_MERCHANT_ID,
94
+ PRIVATE_KEY_PEM: env.FINBRICKS_PRIVATE_KEY_PEM,
95
+ REDIRECT_URI: env.REDIRECT_URI,
96
+ connectedAccounts
97
+ });
98
+ case "FIO":
99
+ return new FioConnector({
100
+ BASE_URI: env.FINBRICKS_BASE_URI,
101
+ MERCHANT_ID: env.FINBRICKS_MERCHANT_ID,
102
+ PRIVATE_KEY_PEM: env.FINBRICKS_PRIVATE_KEY_PEM,
103
+ REDIRECT_URI: env.REDIRECT_URI,
104
+ connectedAccounts
105
+ });
106
+ case "MONETA":
107
+ return new MonetaConnector({
108
+ BASE_URI: env.FINBRICKS_BASE_URI,
109
+ MERCHANT_ID: env.FINBRICKS_MERCHANT_ID,
110
+ PRIVATE_KEY_PEM: env.FINBRICKS_PRIVATE_KEY_PEM,
111
+ REDIRECT_URI: env.REDIRECT_URI,
112
+ connectedAccounts
113
+ });
114
+ default:
115
+ return new database_schema.MockConnector();
116
+ }
117
+ };
118
+
119
+ function pushToQueue(queue, message) {
120
+ if (!Array.isArray(message)) return queue.send(message, { contentType: "v8" });
121
+ return queue.sendBatch(
122
+ message.map((m) => ({
123
+ body: m,
124
+ contentType: "v8"
125
+ }))
126
+ );
127
+ }
128
+ class SyncAccountPaymentsWorkflow extends cloudflare_workers.WorkflowEntrypoint {
129
+ async run(event, step) {
130
+ const { accountId } = event.payload;
131
+ const db = d1.drizzle(this.env.BANK_D1, { schema: database_schema.tables });
132
+ while (true) {
133
+ const account = await step.do("load account", async () => {
134
+ const account2 = await getAccountByIdQuery(db, { accountId });
135
+ if (!account2) {
136
+ throw new cloudflare_workflows.NonRetryableError(`Bank account not found: ${accountId}`);
137
+ }
138
+ return account2;
139
+ });
140
+ if (!account.lastSyncedAt) {
141
+ return { status: "skipped_no_last_sync" };
142
+ }
143
+ const credentials = await step.do(
144
+ "load account credentials",
145
+ async () => {
146
+ const credentials2 = await getCredentialsByAccountId(db, { accountId });
147
+ if (!credentials2) {
148
+ throw new cloudflare_workflows.NonRetryableError(
149
+ `No credentials found for account: ${accountId}`
150
+ );
151
+ }
152
+ return credentials2;
153
+ }
154
+ );
155
+ const connector = await step.do(
156
+ `init ${account.connectorKey} connector`,
157
+ async () => {
158
+ return initiateConnector({
159
+ env: this.env,
160
+ bank: account.connectorKey,
161
+ connectedAccounts: [
162
+ {
163
+ ...account,
164
+ iban: account.iban,
165
+ token: credentials.value
166
+ }
167
+ ]
168
+ });
169
+ }
170
+ );
171
+ const payments = await step.do(
172
+ "fetch bank payments",
173
+ {
174
+ retries: { limit: 5, delay: 2e3, backoff: "constant" },
175
+ timeout: "30 seconds"
176
+ },
177
+ async () => {
178
+ const fetched = await connector.getAllAccountPayments({
179
+ environment: this.env.ENVIRONMENT,
180
+ db,
181
+ account,
182
+ filter: { dateFrom: account.lastSyncedAt }
183
+ });
184
+ return fetched.map((p) => ({
185
+ ...p,
186
+ direction: database_schema.getPaymentDirection(p, account.iban)
187
+ }));
188
+ }
189
+ );
190
+ if (payments.length === 0) {
191
+ return { status: "no_new_payments" };
192
+ }
193
+ const bankRefIds = payments.map((payment) => payment.bankRefId).filter(Boolean);
194
+ const existing = await step.do("load existing payments", async () => {
195
+ return await getPaymentsByBankRefIdsQuery(db, {
196
+ ids: bankRefIds
197
+ });
198
+ });
199
+ const paymentsToUpdate = payments.filter(
200
+ (p) => existing.some((e) => e.bankRefId === p.bankRefId)
201
+ );
202
+ if (paymentsToUpdate.length)
203
+ await step.do("update existing payments", async () => {
204
+ const commands = paymentsToUpdate.map(
205
+ (p) => updatePaymentCommand(db, { payment: p }).command
206
+ );
207
+ await db.batch([commands[0], ...commands.slice(1)]);
208
+ await pushToQueue(
209
+ this.env.QUEUE_BUS_QUEUE,
210
+ paymentsToUpdate.map((p) => ({
211
+ eventType: "BANK_PAYMENT",
212
+ eventSignal: "paymentUpdated",
213
+ bankPayment: p,
214
+ metadata: {
215
+ correlationId: p.correlationId,
216
+ entityId: p.id,
217
+ idempotencySuffix: p.status,
218
+ timestamp: (/* @__PURE__ */ new Date()).toDateString()
219
+ }
220
+ }))
221
+ );
222
+ });
223
+ const paymentsToCreate = payments.filter(
224
+ (p) => !existing.some((e) => e.bankRefId === p.bankRefId)
225
+ );
226
+ if (paymentsToCreate.length)
227
+ await step.do("create new payments", async () => {
228
+ const commands = paymentsToCreate.map(
229
+ (p) => createPaymentCommand(db, { payment: p }).command
230
+ );
231
+ await db.batch([commands[0], ...commands.slice(1)]);
232
+ await pushToQueue(
233
+ this.env.QUEUE_BUS_QUEUE,
234
+ paymentsToCreate.map((p) => ({
235
+ eventType: "BANK_PAYMENT",
236
+ eventSignal: "paymentCreated",
237
+ bankPayment: p,
238
+ metadata: {
239
+ correlationId: p.correlationId,
240
+ entityId: p.id,
241
+ timestamp: (/* @__PURE__ */ new Date()).toDateString()
242
+ }
243
+ }))
244
+ );
245
+ });
246
+ await step.do("determine new lastSyncedAt", async () => {
247
+ const latest = payments.reduce((current, p) => {
248
+ return new Date(p.createdAt).getTime() > new Date(current.createdAt).getTime() ? p : current;
249
+ }, payments[0]);
250
+ await updateAccountLastSyncCommand(db, {
251
+ accountId: account.id,
252
+ lastSyncedAt: latest.createdAt
253
+ }).command.execute();
254
+ return latest.createdAt;
255
+ });
256
+ await step.sleep(
257
+ "Sleep for next sync",
258
+ `${account.syncIntervalS} seconds`
259
+ );
260
+ }
261
+ }
262
+ }
263
+
264
+ const workflows = {
265
+ __proto__: null,
266
+ SyncAccountPaymentsWorkflow: SyncAccountPaymentsWorkflow
267
+ };
268
+
269
+ exports.SyncAccountPaymentsWorkflow = SyncAccountPaymentsWorkflow;
270
+ exports.createPaymentCommand = createPaymentCommand;
271
+ exports.getCredentialsByAccountId = getCredentialsByAccountId;
272
+ exports.getPaymentsByBankRefIdsQuery = getPaymentsByBankRefIdsQuery;
273
+ exports.initiateConnector = initiateConnector;
274
+ exports.updateAccountLastSyncCommand = updateAccountLastSyncCommand;
275
+ exports.updatePaymentCommand = updatePaymentCommand;
276
+ exports.workflows = workflows;
@@ -0,0 +1,267 @@
1
+ import { WorkflowEntrypoint } from 'cloudflare:workers';
2
+ import { NonRetryableError } from 'cloudflare:workflows';
3
+ import { drizzle } from 'drizzle-orm/d1';
4
+ import { t as tables, F as FinbricksConnector, M as MockConnector, E as ErsteConnector, v as getPaymentDirection } from './bank.CcKNlFRd.mjs';
5
+ import '@develit-io/backend-sdk';
6
+ import { eq, inArray } from 'drizzle-orm';
7
+ import { M as MockCobsConnector } from './bank.B72e0ibs.mjs';
8
+ import 'jose';
9
+ import '@develit-io/general-codes';
10
+
11
+ const createPaymentCommand = (db, { payment }) => {
12
+ return {
13
+ command: db.insert(tables.payment).values(payment).returning()
14
+ };
15
+ };
16
+
17
+ const updatePaymentCommand = (db, { payment }) => {
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 getAccountByIdQuery = async (db, { accountId }) => {
33
+ return await db.select().from(tables.account).where(eq(tables.account.id, accountId)).get();
34
+ };
35
+
36
+ const getCredentialsByAccountId = async (db, { accountId }) => {
37
+ const cred = await db.select().from(tables.accountCredentials).where(eq(tables.accountCredentials.accountId, accountId)).get();
38
+ return cred;
39
+ };
40
+
41
+ const getPaymentsByBankRefIdsQuery = async (db, { ids }) => {
42
+ return await db.select().from(tables.payment).where(inArray(tables.payment.bankRefId, ids));
43
+ };
44
+
45
+ class CreditasConnector extends FinbricksConnector {
46
+ constructor(config) {
47
+ super("CREDITAS", config);
48
+ }
49
+ }
50
+
51
+ class FioConnector extends FinbricksConnector {
52
+ constructor(config) {
53
+ super("FIO", config);
54
+ }
55
+ }
56
+
57
+ class MonetaConnector extends FinbricksConnector {
58
+ constructor(config) {
59
+ super("MONETA", config);
60
+ }
61
+ }
62
+
63
+ const initiateConnector = ({
64
+ bank,
65
+ env,
66
+ connectedAccounts
67
+ }) => {
68
+ switch (bank) {
69
+ case "ERSTE":
70
+ return new ErsteConnector({
71
+ API_KEY: env.ERSTE_API_KEY,
72
+ CLIENT_ID: env.ERSTE_CLIENT_ID,
73
+ CLIENT_SECRET: env.ERSTE_CLIENT_SECRET,
74
+ REDIRECT_URI: env.REDIRECT_URI,
75
+ AUTH_URI: env.ERSTE_AUTH_URI,
76
+ PAYMENTS_URI: env.ERSTE_PAYMENTS_URI,
77
+ ACCOUNTS_URI: env.ERSTE_ACCOUNTS_URI,
78
+ connectedAccounts
79
+ });
80
+ case "CREDITAS":
81
+ return new CreditasConnector({
82
+ BASE_URI: env.FINBRICKS_BASE_URI,
83
+ MERCHANT_ID: env.FINBRICKS_MERCHANT_ID,
84
+ PRIVATE_KEY_PEM: env.FINBRICKS_PRIVATE_KEY_PEM,
85
+ REDIRECT_URI: env.REDIRECT_URI,
86
+ connectedAccounts
87
+ });
88
+ case "MOCK_COBS":
89
+ return new MockCobsConnector({
90
+ BASE_URI: env.FINBRICKS_BASE_URI,
91
+ MERCHANT_ID: env.FINBRICKS_MERCHANT_ID,
92
+ PRIVATE_KEY_PEM: env.FINBRICKS_PRIVATE_KEY_PEM,
93
+ REDIRECT_URI: env.REDIRECT_URI,
94
+ connectedAccounts
95
+ });
96
+ case "FIO":
97
+ return new FioConnector({
98
+ BASE_URI: env.FINBRICKS_BASE_URI,
99
+ MERCHANT_ID: env.FINBRICKS_MERCHANT_ID,
100
+ PRIVATE_KEY_PEM: env.FINBRICKS_PRIVATE_KEY_PEM,
101
+ REDIRECT_URI: env.REDIRECT_URI,
102
+ connectedAccounts
103
+ });
104
+ case "MONETA":
105
+ return new MonetaConnector({
106
+ BASE_URI: env.FINBRICKS_BASE_URI,
107
+ MERCHANT_ID: env.FINBRICKS_MERCHANT_ID,
108
+ PRIVATE_KEY_PEM: env.FINBRICKS_PRIVATE_KEY_PEM,
109
+ REDIRECT_URI: env.REDIRECT_URI,
110
+ connectedAccounts
111
+ });
112
+ default:
113
+ return new MockConnector();
114
+ }
115
+ };
116
+
117
+ function pushToQueue(queue, message) {
118
+ if (!Array.isArray(message)) return queue.send(message, { contentType: "v8" });
119
+ return queue.sendBatch(
120
+ message.map((m) => ({
121
+ body: m,
122
+ contentType: "v8"
123
+ }))
124
+ );
125
+ }
126
+ class SyncAccountPaymentsWorkflow extends WorkflowEntrypoint {
127
+ async run(event, step) {
128
+ const { accountId } = event.payload;
129
+ const db = drizzle(this.env.BANK_D1, { schema: tables });
130
+ while (true) {
131
+ const account = await step.do("load account", async () => {
132
+ const account2 = await getAccountByIdQuery(db, { accountId });
133
+ if (!account2) {
134
+ throw new NonRetryableError(`Bank account not found: ${accountId}`);
135
+ }
136
+ return account2;
137
+ });
138
+ if (!account.lastSyncedAt) {
139
+ return { status: "skipped_no_last_sync" };
140
+ }
141
+ const credentials = await step.do(
142
+ "load account credentials",
143
+ async () => {
144
+ const credentials2 = await getCredentialsByAccountId(db, { accountId });
145
+ if (!credentials2) {
146
+ throw new NonRetryableError(
147
+ `No credentials found for account: ${accountId}`
148
+ );
149
+ }
150
+ return credentials2;
151
+ }
152
+ );
153
+ const connector = await step.do(
154
+ `init ${account.connectorKey} connector`,
155
+ async () => {
156
+ return initiateConnector({
157
+ env: this.env,
158
+ bank: account.connectorKey,
159
+ connectedAccounts: [
160
+ {
161
+ ...account,
162
+ iban: account.iban,
163
+ token: credentials.value
164
+ }
165
+ ]
166
+ });
167
+ }
168
+ );
169
+ const payments = await step.do(
170
+ "fetch bank payments",
171
+ {
172
+ retries: { limit: 5, delay: 2e3, backoff: "constant" },
173
+ timeout: "30 seconds"
174
+ },
175
+ async () => {
176
+ const fetched = await connector.getAllAccountPayments({
177
+ environment: this.env.ENVIRONMENT,
178
+ db,
179
+ account,
180
+ filter: { dateFrom: account.lastSyncedAt }
181
+ });
182
+ return fetched.map((p) => ({
183
+ ...p,
184
+ direction: getPaymentDirection(p, account.iban)
185
+ }));
186
+ }
187
+ );
188
+ if (payments.length === 0) {
189
+ return { status: "no_new_payments" };
190
+ }
191
+ const bankRefIds = payments.map((payment) => payment.bankRefId).filter(Boolean);
192
+ const existing = await step.do("load existing payments", async () => {
193
+ return await getPaymentsByBankRefIdsQuery(db, {
194
+ ids: bankRefIds
195
+ });
196
+ });
197
+ const paymentsToUpdate = payments.filter(
198
+ (p) => existing.some((e) => e.bankRefId === p.bankRefId)
199
+ );
200
+ if (paymentsToUpdate.length)
201
+ await step.do("update existing payments", async () => {
202
+ const commands = paymentsToUpdate.map(
203
+ (p) => updatePaymentCommand(db, { payment: p }).command
204
+ );
205
+ await db.batch([commands[0], ...commands.slice(1)]);
206
+ await pushToQueue(
207
+ this.env.QUEUE_BUS_QUEUE,
208
+ paymentsToUpdate.map((p) => ({
209
+ eventType: "BANK_PAYMENT",
210
+ eventSignal: "paymentUpdated",
211
+ bankPayment: p,
212
+ metadata: {
213
+ correlationId: p.correlationId,
214
+ entityId: p.id,
215
+ idempotencySuffix: p.status,
216
+ timestamp: (/* @__PURE__ */ new Date()).toDateString()
217
+ }
218
+ }))
219
+ );
220
+ });
221
+ const paymentsToCreate = payments.filter(
222
+ (p) => !existing.some((e) => e.bankRefId === p.bankRefId)
223
+ );
224
+ if (paymentsToCreate.length)
225
+ await step.do("create new payments", async () => {
226
+ const commands = paymentsToCreate.map(
227
+ (p) => createPaymentCommand(db, { payment: p }).command
228
+ );
229
+ await db.batch([commands[0], ...commands.slice(1)]);
230
+ await pushToQueue(
231
+ this.env.QUEUE_BUS_QUEUE,
232
+ paymentsToCreate.map((p) => ({
233
+ eventType: "BANK_PAYMENT",
234
+ eventSignal: "paymentCreated",
235
+ bankPayment: p,
236
+ metadata: {
237
+ correlationId: p.correlationId,
238
+ entityId: p.id,
239
+ timestamp: (/* @__PURE__ */ new Date()).toDateString()
240
+ }
241
+ }))
242
+ );
243
+ });
244
+ await step.do("determine new lastSyncedAt", async () => {
245
+ const latest = payments.reduce((current, p) => {
246
+ return new Date(p.createdAt).getTime() > new Date(current.createdAt).getTime() ? p : current;
247
+ }, payments[0]);
248
+ await updateAccountLastSyncCommand(db, {
249
+ accountId: account.id,
250
+ lastSyncedAt: latest.createdAt
251
+ }).command.execute();
252
+ return latest.createdAt;
253
+ });
254
+ await step.sleep(
255
+ "Sleep for next sync",
256
+ `${account.syncIntervalS} seconds`
257
+ );
258
+ }
259
+ }
260
+ }
261
+
262
+ const workflows = {
263
+ __proto__: null,
264
+ SyncAccountPaymentsWorkflow: SyncAccountPaymentsWorkflow
265
+ };
266
+
267
+ export { SyncAccountPaymentsWorkflow as S, updateAccountLastSyncCommand as a, getCredentialsByAccountId as b, createPaymentCommand as c, getPaymentsByBankRefIdsQuery as g, initiateConnector as i, updatePaymentCommand as u, workflows as w };
package/dist/types.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const database_schema = require('./shared/bank.BVzOzXdX.cjs');
4
- const mockCobs_connector = require('./shared/bank.BPcrbUBa.cjs');
3
+ const database_schema = require('./shared/bank.3YYIj-n6.cjs');
4
+ const mockCobs_connector = require('./shared/bank.BgpXGC_1.cjs');
5
5
  const generalCodes = require('@develit-io/general-codes');
6
6
  require('@develit-io/backend-sdk');
7
7
  require('drizzle-orm/sqlite-core');