@develit-services/bank 0.0.14 → 0.0.16
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.d.cts +1 -1
- package/dist/database/schema.d.mts +1 -1
- package/dist/database/schema.d.ts +1 -1
- package/dist/export/worker.cjs +42 -24
- package/dist/export/worker.d.cts +4 -3
- package/dist/export/worker.d.mts +4 -3
- package/dist/export/worker.d.ts +4 -3
- package/dist/export/worker.mjs +43 -25
- package/dist/shared/{bank.CBNQZ5Pd.d.cts → bank.BT_Yf43j.d.cts} +2 -0
- package/dist/shared/{bank.CBNQZ5Pd.d.mts → bank.BT_Yf43j.d.mts} +2 -0
- package/dist/shared/{bank.CBNQZ5Pd.d.ts → bank.BT_Yf43j.d.ts} +2 -0
- package/dist/shared/{bank.CKtJPLds.d.mts → bank.BoVUuQ1D.d.mts} +16 -4
- package/dist/shared/{bank.CXskd9OZ.d.cts → bank.BwleO_ON.d.cts} +16 -4
- package/dist/shared/{bank.DD0U00q7.cjs → bank.Ce58djRL.cjs} +399 -220
- package/dist/shared/{bank.D_KtNZfn.mjs → bank.DZS4d3MJ.mjs} +397 -222
- package/dist/shared/{bank.BZqjlltW.d.ts → bank.DlquvmPs.d.ts} +16 -4
- package/dist/types.cjs +6 -2
- package/dist/types.d.cts +135 -20
- package/dist/types.d.mts +135 -20
- package/dist/types.d.ts +135 -20
- package/dist/types.mjs +2 -2
- package/package.json +1 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { useResult, createInternalError
|
|
1
|
+
import { uuidv4, useResult, createInternalError } from '@develit-io/backend-sdk';
|
|
2
2
|
import { s as schema } from './bank.BeH-ZCJJ.mjs';
|
|
3
3
|
import { and, eq } from 'drizzle-orm';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { COUNTRY_CODES_2, CURRENCY_CODES } from '@develit-io/general-codes';
|
|
6
|
+
import { importPKCS8, SignJWT } from 'jose';
|
|
6
7
|
import 'superjson';
|
|
7
8
|
import { format, parseISO } from 'date-fns';
|
|
8
|
-
import { importPKCS8, SignJWT } from 'jose';
|
|
9
9
|
|
|
10
10
|
const tables = schema;
|
|
11
11
|
|
|
@@ -33,6 +33,104 @@ const COUNTRY_CODES = COUNTRY_CODES_2;
|
|
|
33
33
|
class IBankConnector {
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
async function signFinbricksJws({
|
|
37
|
+
privateKeyPem,
|
|
38
|
+
merchantId,
|
|
39
|
+
jwsData
|
|
40
|
+
}) {
|
|
41
|
+
const privateKey = await importPKCS8(privateKeyPem, "RS256");
|
|
42
|
+
const payload = JSON.stringify(jwsData);
|
|
43
|
+
const jws = await new SignJWT(JSON.parse(payload)).setProtectedHeader({
|
|
44
|
+
alg: "RS256",
|
|
45
|
+
kid: merchantId,
|
|
46
|
+
typ: "JWT"
|
|
47
|
+
}).sign(privateKey);
|
|
48
|
+
const [header, , signature] = jws.split(".");
|
|
49
|
+
return `${header}..${signature}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const useFinbricksFetch = async (config, init) => {
|
|
53
|
+
const {
|
|
54
|
+
baseUrl,
|
|
55
|
+
endpoint,
|
|
56
|
+
method = "GET",
|
|
57
|
+
merchantId,
|
|
58
|
+
privateKeyPem,
|
|
59
|
+
query,
|
|
60
|
+
body,
|
|
61
|
+
correlationId,
|
|
62
|
+
psuIp = "88.205.47.1",
|
|
63
|
+
psuAgent = "Develit/BankService"
|
|
64
|
+
} = config;
|
|
65
|
+
const url = new URL(`${baseUrl}${endpoint}`);
|
|
66
|
+
if (query) {
|
|
67
|
+
for (const [k, v] of Object.entries(query)) {
|
|
68
|
+
if (v !== void 0 && v !== null)
|
|
69
|
+
url.searchParams.append(k, v.toString());
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const uriWithQuery = `${endpoint}${url.search ? `?${url.searchParams.toString()}` : ""}`;
|
|
73
|
+
const bodyString = body ? JSON.stringify(body) : "";
|
|
74
|
+
const jwsSignature = await signFinbricksJws({
|
|
75
|
+
privateKeyPem,
|
|
76
|
+
merchantId,
|
|
77
|
+
jwsData: {
|
|
78
|
+
uri: uriWithQuery,
|
|
79
|
+
body: bodyString
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
const headers = {
|
|
83
|
+
"Content-Type": "application/json",
|
|
84
|
+
"JWS-Signature": jwsSignature,
|
|
85
|
+
"PSU-IP-Address": psuIp,
|
|
86
|
+
"PSU-User-Agent": psuAgent,
|
|
87
|
+
...init?.headers
|
|
88
|
+
};
|
|
89
|
+
if (correlationId) {
|
|
90
|
+
headers["Correlation-ID"] = correlationId;
|
|
91
|
+
}
|
|
92
|
+
const res = await fetch(url.toString(), {
|
|
93
|
+
method,
|
|
94
|
+
headers,
|
|
95
|
+
body: method !== "GET" ? bodyString : void 0
|
|
96
|
+
});
|
|
97
|
+
if (!res.ok) {
|
|
98
|
+
const text = await res.text().catch(() => "unknown error");
|
|
99
|
+
throw new Error(
|
|
100
|
+
`Finbricks API error: ${res.status} ${res.statusText} \u2013 ${text}`
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
return await res.json();
|
|
104
|
+
};
|
|
105
|
+
class FinbricksClient {
|
|
106
|
+
constructor(baseUrl, merchantId, privateKeyPem) {
|
|
107
|
+
this.BASE_URL = baseUrl;
|
|
108
|
+
this.MERCHANT_ID = merchantId;
|
|
109
|
+
this.PRIVATE_KEY_PEM = privateKeyPem;
|
|
110
|
+
}
|
|
111
|
+
async request(params) {
|
|
112
|
+
return useFinbricksFetch({
|
|
113
|
+
baseUrl: this.BASE_URL,
|
|
114
|
+
merchantId: this.MERCHANT_ID,
|
|
115
|
+
privateKeyPem: this.PRIVATE_KEY_PEM,
|
|
116
|
+
...params
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const FINBRICKS_ENDPOINTS = {
|
|
122
|
+
AUTHENTICATE_V2: "/v2/auth/authenticate",
|
|
123
|
+
AUTH_TOKEN_STATUS: "/auth/token",
|
|
124
|
+
AUTH_TOKEN_REVOKE: "/auth/revoke",
|
|
125
|
+
ACCOUNT_LIST: "/account/list",
|
|
126
|
+
ACCOUNT_BALANCE: "/account/balance",
|
|
127
|
+
ACCOUNT_TRANSACTIONS: "/account/transactions",
|
|
128
|
+
TRANSACTION_INIT: "/transaction/platform/init",
|
|
129
|
+
TRANSACTION_STATUS: "/transaction/platform/status",
|
|
130
|
+
TRANSACTION_BATCH_INIT: "/transaction/platform/batchPayment/init",
|
|
131
|
+
BATCH_STATUS: "/transaction/platform/batchPayment/status"
|
|
132
|
+
};
|
|
133
|
+
|
|
36
134
|
const mapReferencesToPayment = (reference) => {
|
|
37
135
|
const symbols = {
|
|
38
136
|
vs: void 0,
|
|
@@ -76,81 +174,186 @@ const mapFinbricksStatus = (status) => {
|
|
|
76
174
|
return "PENDING";
|
|
77
175
|
}
|
|
78
176
|
};
|
|
177
|
+
const mapFinbricksTransactionStatus = (status) => {
|
|
178
|
+
switch (status) {
|
|
179
|
+
case "OPENED":
|
|
180
|
+
return "CREATED";
|
|
181
|
+
case "AUTHORIZED":
|
|
182
|
+
return "PENDING";
|
|
183
|
+
case "COMPLETED":
|
|
184
|
+
return "COMPLETED";
|
|
185
|
+
case "BOOKED":
|
|
186
|
+
return "PENDING";
|
|
187
|
+
case "SETTLED":
|
|
188
|
+
return "COMPLETED";
|
|
189
|
+
case "REJECTED":
|
|
190
|
+
return "FAILED";
|
|
191
|
+
case "CLOSED":
|
|
192
|
+
return "FAILED";
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
const mapFinbricksBatchStatus = (status) => {
|
|
196
|
+
return status === "REJECTED" ? "FAILED" : status === "AUTHORIZED" ? "PROCESSING" : "READY_TO_SIGN";
|
|
197
|
+
};
|
|
198
|
+
const mapFinbricksTransactionToPayment = (tx, account) => {
|
|
199
|
+
const isIncoming = tx.creditDebitIndicator === "CRDT";
|
|
200
|
+
const related = tx.entryDetails.transactionDetails.relatedParties;
|
|
201
|
+
const base = {
|
|
202
|
+
id: uuidv4(),
|
|
203
|
+
bankRefId: tx.entryReference || tx.fbxReference,
|
|
204
|
+
amount: tx.amount?.value || 0,
|
|
205
|
+
currency: tx.amount?.currency || "CZK",
|
|
206
|
+
paymentType: "DOMESTIC",
|
|
207
|
+
status: mapFinbricksStatus(tx.status),
|
|
208
|
+
message: tx.entryDetails.transactionDetails.remittanceInformation?.unstructured || tx.entryDetails.transactionDetails.additionalRemittanceInformation || null,
|
|
209
|
+
processedAt: new Date(tx.bookingDate.date),
|
|
210
|
+
...mapReferencesToPayment(
|
|
211
|
+
tx.entryDetails.transactionDetails.remittanceInformation?.structured?.creditorReferenceInformation?.reference || tx.entryDetails.transactionDetails.references?.endToEndIdentification
|
|
212
|
+
),
|
|
213
|
+
debtorHolderName: isIncoming ? related.debtor?.name || "Unknown" : "Unknown",
|
|
214
|
+
creditorHolderName: isIncoming ? "Unknown" : related.creditor?.name || "Unknown",
|
|
215
|
+
debtorIban: isIncoming ? related.debtorAccount?.identification?.iban || account.identification.iban : account.identification.iban,
|
|
216
|
+
creditorIban: isIncoming ? account.identification.iban : related.creditorAccount?.identification?.iban || account.identification.iban,
|
|
217
|
+
direction: "INCOMING"
|
|
218
|
+
// přepíšeme správným směrem níže
|
|
219
|
+
};
|
|
220
|
+
base.direction = getPaymentDirection(base, account.identification.iban);
|
|
221
|
+
return base;
|
|
222
|
+
};
|
|
79
223
|
|
|
80
224
|
class FinbricksConnector extends IBankConnector {
|
|
81
|
-
constructor(
|
|
225
|
+
constructor(provider, { BASE_URI, MERCHANT_ID, PRIVATE_KEY_PEM }) {
|
|
82
226
|
super();
|
|
83
227
|
this.connectorKey = "FINBRICKS";
|
|
84
|
-
this.
|
|
85
|
-
|
|
86
|
-
["batch-payment-domestic", "/transaction/platform/batchPayment/init"],
|
|
87
|
-
["get-accounts", "/account/list"],
|
|
88
|
-
["get-account-transactions", "/account/transactions"],
|
|
89
|
-
["authenticate-client", "/v2/auth/authenticate"],
|
|
90
|
-
["expiration", "/auth/token"]
|
|
91
|
-
]);
|
|
92
|
-
this.PROVIDER = PROVIDER;
|
|
93
|
-
this.BASE_URI = BASE_URI;
|
|
94
|
-
this.MERCHANT_ID = MERCHANT_ID;
|
|
95
|
-
this.PRIVATE_KEY_PEM = PRIVATE_KEY_PEM;
|
|
228
|
+
this.PROVIDER = provider;
|
|
229
|
+
this.finbricks = new FinbricksClient(BASE_URI, MERCHANT_ID, PRIVATE_KEY_PEM);
|
|
96
230
|
}
|
|
97
231
|
static {
|
|
98
232
|
this.FETCH_INTERVAL = 60 * 60 * 5;
|
|
99
233
|
}
|
|
100
|
-
async
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
234
|
+
async connectUserAccount() {
|
|
235
|
+
const clientId = uuidv4();
|
|
236
|
+
const body = {
|
|
237
|
+
merchantId: this.finbricks.MERCHANT_ID,
|
|
238
|
+
clientId,
|
|
239
|
+
paymentProvider: this.PROVIDER,
|
|
240
|
+
scope: "AISP_PISP",
|
|
241
|
+
callbackUrl: "https://example.com/callback"
|
|
242
|
+
};
|
|
243
|
+
const [response, error] = await useResult(
|
|
244
|
+
this.finbricks.request({
|
|
245
|
+
endpoint: FINBRICKS_ENDPOINTS.AUTHENTICATE_V2,
|
|
246
|
+
method: "POST",
|
|
247
|
+
body
|
|
248
|
+
})
|
|
249
|
+
);
|
|
250
|
+
if (error || !response) {
|
|
251
|
+
throw createInternalError(error, {
|
|
252
|
+
message: "Finbricks: failed to connect user"
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
return {
|
|
256
|
+
redirectUrl: response.redirectUrl,
|
|
118
257
|
clientId
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
async authenticate({ token: clientId }) {
|
|
261
|
+
if (!!clientId) {
|
|
262
|
+
const isExpired = await this.isClientIdExpired(clientId);
|
|
263
|
+
if (!isExpired) {
|
|
264
|
+
this.lastValidClientId = clientId;
|
|
265
|
+
return clientId;
|
|
126
266
|
}
|
|
127
|
-
}
|
|
128
|
-
const
|
|
129
|
-
|
|
267
|
+
}
|
|
268
|
+
const newClientId = uuidv4();
|
|
269
|
+
const body = {
|
|
270
|
+
merchantId: this.finbricks.MERCHANT_ID,
|
|
271
|
+
paymentProvider: this.PROVIDER,
|
|
272
|
+
clientId: newClientId,
|
|
273
|
+
scope: "AISP_PISP",
|
|
274
|
+
callbackUrl: `www.example.com/callback`
|
|
275
|
+
};
|
|
276
|
+
const [res, error] = await useResult(
|
|
277
|
+
this.finbricks.request({
|
|
278
|
+
endpoint: "/v2/auth/authenticate",
|
|
130
279
|
method: "POST",
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
280
|
+
body
|
|
281
|
+
})
|
|
282
|
+
);
|
|
283
|
+
if (error || !res) {
|
|
284
|
+
throw createInternalError(error, {
|
|
285
|
+
message: `Finbricks: authentication request failed (${this.PROVIDER})`
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
this.lastValidClientId = newClientId;
|
|
289
|
+
return res.redirectUrl;
|
|
290
|
+
}
|
|
291
|
+
async revokeAuthentication(clientId) {
|
|
292
|
+
const [_, error] = await useResult(
|
|
293
|
+
this.finbricks.request({
|
|
294
|
+
endpoint: FINBRICKS_ENDPOINTS.AUTH_TOKEN_REVOKE,
|
|
295
|
+
method: "DELETE",
|
|
296
|
+
query: {
|
|
297
|
+
merchantId: this.finbricks.MERCHANT_ID,
|
|
298
|
+
clientId,
|
|
299
|
+
provider: this.PROVIDER
|
|
300
|
+
}
|
|
136
301
|
})
|
|
137
302
|
);
|
|
138
303
|
if (error) {
|
|
139
|
-
const body = await data?.json();
|
|
140
304
|
throw createInternalError(error, {
|
|
141
|
-
message:
|
|
305
|
+
message: `Finbricks: failed to revoke authentication for clientId ${clientId}`
|
|
142
306
|
});
|
|
143
307
|
}
|
|
144
|
-
|
|
145
|
-
|
|
308
|
+
this.lastValidClientId = "";
|
|
309
|
+
}
|
|
310
|
+
async isClientIdExpired(clientId) {
|
|
311
|
+
const [response, error] = await useResult(
|
|
312
|
+
this.finbricks.request({
|
|
313
|
+
endpoint: FINBRICKS_ENDPOINTS.AUTH_TOKEN_STATUS,
|
|
314
|
+
method: "GET",
|
|
315
|
+
query: {
|
|
316
|
+
merchantId: this.finbricks.MERCHANT_ID,
|
|
317
|
+
clientId,
|
|
318
|
+
provider: this.PROVIDER
|
|
319
|
+
}
|
|
320
|
+
})
|
|
321
|
+
);
|
|
322
|
+
if (error) {
|
|
323
|
+
throw createInternalError(error, {
|
|
324
|
+
message: `Finbricks: failed to fetch token info for clientId: ${clientId}`
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
if (!response || response.length === 0) return true;
|
|
328
|
+
const record = response.find((token) => token.clientId === clientId);
|
|
329
|
+
if (!record) return true;
|
|
330
|
+
const validTo = new Date(record.validTo);
|
|
331
|
+
return validTo.getTime() < Date.now();
|
|
332
|
+
}
|
|
333
|
+
async listAccounts() {
|
|
334
|
+
if (!this.lastValidClientId) {
|
|
146
335
|
throw createInternalError(null, {
|
|
147
|
-
message:
|
|
148
|
-
code: "
|
|
149
|
-
|
|
336
|
+
message: "Finbricks: missing valid clientId",
|
|
337
|
+
code: "FINBRICKS_CLIENT_ID_MISSING"
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
const [response, error] = await useResult(
|
|
341
|
+
this.finbricks.request({
|
|
342
|
+
endpoint: FINBRICKS_ENDPOINTS.ACCOUNT_LIST,
|
|
343
|
+
method: "GET",
|
|
344
|
+
query: {
|
|
345
|
+
merchantId: this.finbricks.MERCHANT_ID,
|
|
346
|
+
paymentProvider: this.PROVIDER,
|
|
347
|
+
clientId: this.lastValidClientId
|
|
348
|
+
}
|
|
349
|
+
})
|
|
350
|
+
);
|
|
351
|
+
if (error || !response) {
|
|
352
|
+
throw createInternalError(error, {
|
|
353
|
+
message: "Finbricks: failed to list accounts"
|
|
150
354
|
});
|
|
151
355
|
}
|
|
152
|
-
|
|
153
|
-
return !!tokens.find((token) => token.clientId === clientId);
|
|
356
|
+
return response.accounts;
|
|
154
357
|
}
|
|
155
358
|
async preparePayment(payment) {
|
|
156
359
|
const bankRefId = uuidv4();
|
|
@@ -167,111 +370,64 @@ class FinbricksConnector extends IBankConnector {
|
|
|
167
370
|
async initiateBatchFromPayments({
|
|
168
371
|
payments
|
|
169
372
|
}) {
|
|
170
|
-
const uri = this.apiDictionary.get("batch-payment-domestic");
|
|
171
373
|
const batchId = uuidv4();
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
debtorAccountIban: payments[0].debtorIban,
|
|
176
|
-
clientId: this.lastValidClientId,
|
|
177
|
-
callbackUrl: "https://example.com/callback",
|
|
178
|
-
merchantBatchId: batchId
|
|
179
|
-
},
|
|
180
|
-
payments: payments.map((payment) => ({
|
|
181
|
-
merchantTransactionId: payment.bankRefId,
|
|
182
|
-
creditorAccountIban: payment.creditorIban,
|
|
183
|
-
amount: payment.amount
|
|
184
|
-
}))
|
|
185
|
-
};
|
|
186
|
-
const payload = JSON.stringify(batchBody);
|
|
187
|
-
const jwsData = {
|
|
188
|
-
uri,
|
|
189
|
-
body: payload
|
|
190
|
-
};
|
|
191
|
-
const signature = await this.signFinbricksJws({
|
|
192
|
-
jwsData
|
|
193
|
-
});
|
|
194
|
-
const [data, error] = await useResult(
|
|
195
|
-
fetch(`${this.BASE_URI}${uri}`, {
|
|
374
|
+
const [response, error] = await useResult(
|
|
375
|
+
this.finbricks.request({
|
|
376
|
+
endpoint: FINBRICKS_ENDPOINTS.TRANSACTION_BATCH_INIT,
|
|
196
377
|
method: "POST",
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
378
|
+
body: {
|
|
379
|
+
batchPaymentIdentification: {
|
|
380
|
+
merchantId: this.finbricks.MERCHANT_ID,
|
|
381
|
+
debtorAccountIban: payments[0].debtorIban,
|
|
382
|
+
clientId: this.lastValidClientId,
|
|
383
|
+
callbackUrl: "https://example.com/callback",
|
|
384
|
+
merchantBatchId: batchId
|
|
385
|
+
},
|
|
386
|
+
payments: payments.map((p) => ({
|
|
387
|
+
merchantTransactionId: p.bankRefId,
|
|
388
|
+
creditorAccountIban: p.creditorIban,
|
|
389
|
+
amount: p.amount
|
|
390
|
+
}))
|
|
391
|
+
}
|
|
202
392
|
})
|
|
203
393
|
);
|
|
204
|
-
|
|
205
|
-
if (error) {
|
|
394
|
+
if (error || !response) {
|
|
206
395
|
throw createInternalError(error, {
|
|
207
|
-
message:
|
|
396
|
+
message: "Finbricks: failed to initiate batch payment"
|
|
208
397
|
});
|
|
209
398
|
}
|
|
210
|
-
if (!data?.ok) {
|
|
211
|
-
console.log("FINBRICKSBODY", body);
|
|
212
|
-
throw createInternalError(null, {
|
|
213
|
-
message: body?.message || "Finbricks API error",
|
|
214
|
-
code: "FINBRICKS_API_ERROR",
|
|
215
|
-
status: data?.status || 500
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
const { redirectUrl } = body;
|
|
219
399
|
return {
|
|
220
400
|
id: batchId,
|
|
221
|
-
authorizationUrls: [redirectUrl],
|
|
222
|
-
payments: payments.map((
|
|
223
|
-
...payment,
|
|
224
|
-
status: "INITIALIZED"
|
|
225
|
-
}))
|
|
401
|
+
authorizationUrls: [response.redirectUrl],
|
|
402
|
+
payments: payments.map((p) => ({ ...p, status: "INITIALIZED" }))
|
|
226
403
|
};
|
|
227
404
|
}
|
|
228
405
|
async initiateSinglePayment(payment) {
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
merchantTransactionId: payment.bankRefId,
|
|
233
|
-
totalPrice: payment.amount,
|
|
234
|
-
creditorAccountIban: payment.creditorIban,
|
|
235
|
-
debtorAccountIban: payment.debtorIban,
|
|
236
|
-
creditorName: payment.creditorHolderName,
|
|
237
|
-
description: payment.message || "",
|
|
238
|
-
variableSymbol: payment.vs || "",
|
|
239
|
-
callbackUrl: "example.com",
|
|
240
|
-
paymentProvider: this.PROVIDER
|
|
241
|
-
};
|
|
242
|
-
const signature = await this.signFinbricksJws({
|
|
243
|
-
jwsData: {
|
|
244
|
-
body: JSON.stringify(paymentBody),
|
|
245
|
-
uri
|
|
246
|
-
}
|
|
247
|
-
});
|
|
248
|
-
const [data, error] = await useResult(
|
|
249
|
-
fetch(`${this.BASE_URI}${uri}`, {
|
|
406
|
+
const [response, error] = await useResult(
|
|
407
|
+
this.finbricks.request({
|
|
408
|
+
endpoint: FINBRICKS_ENDPOINTS.TRANSACTION_INIT,
|
|
250
409
|
method: "POST",
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
410
|
+
body: {
|
|
411
|
+
merchantId: this.finbricks.MERCHANT_ID,
|
|
412
|
+
merchantTransactionId: payment.bankRefId,
|
|
413
|
+
totalPrice: payment.amount,
|
|
414
|
+
debtorAccountIban: payment.debtorIban,
|
|
415
|
+
creditorAccountIban: payment.creditorIban,
|
|
416
|
+
creditorName: payment.creditorHolderName,
|
|
417
|
+
description: payment.message || "",
|
|
418
|
+
variableSymbol: payment.vs || "",
|
|
419
|
+
callbackUrl: "https://example.com/callback",
|
|
420
|
+
paymentProvider: this.PROVIDER
|
|
421
|
+
}
|
|
256
422
|
})
|
|
257
423
|
);
|
|
258
|
-
|
|
259
|
-
if (error) {
|
|
424
|
+
if (error || !response) {
|
|
260
425
|
throw createInternalError(error, {
|
|
261
|
-
message:
|
|
426
|
+
message: "Finbricks: failed to initiate payment"
|
|
262
427
|
});
|
|
263
428
|
}
|
|
264
|
-
if (!data?.ok) {
|
|
265
|
-
console.log("FINBRICKSBODY", body);
|
|
266
|
-
throw createInternalError(null, {
|
|
267
|
-
message: body?.message || "Finbricks API error",
|
|
268
|
-
code: "FINBRICKS_API_ERROR",
|
|
269
|
-
status: data?.status || 500
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
const redirectUrl = (await data.json()).redirectUrl;
|
|
273
429
|
return {
|
|
274
|
-
authorizationUrl: redirectUrl,
|
|
430
|
+
authorizationUrl: response.redirectUrl,
|
|
275
431
|
payment: { ...payment, status: "INITIALIZED" }
|
|
276
432
|
};
|
|
277
433
|
}
|
|
@@ -279,95 +435,85 @@ class FinbricksConnector extends IBankConnector {
|
|
|
279
435
|
account,
|
|
280
436
|
lastSync
|
|
281
437
|
}) {
|
|
282
|
-
const uri = this.apiDictionary.get("get-account-transactions");
|
|
283
438
|
const dateFormat = "yyyy-MM-dd";
|
|
284
|
-
let cursor = null;
|
|
285
|
-
let allPayments = [];
|
|
286
439
|
const dateFrom = format(lastSync.lastSyncedAt, dateFormat);
|
|
287
440
|
const dateTo = format(/* @__PURE__ */ new Date(), dateFormat);
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
bankAccountId: account.identification.number,
|
|
294
|
-
dateFrom,
|
|
295
|
-
dateTo,
|
|
296
|
-
currency: account.currency,
|
|
297
|
-
size: "20"
|
|
298
|
-
});
|
|
299
|
-
console.log("query", query);
|
|
300
|
-
if (cursor) query.set("cursor", cursor);
|
|
301
|
-
const fullUri = `${uri}?${query.toString()}`;
|
|
302
|
-
const jwsData = {
|
|
303
|
-
uri: fullUri,
|
|
304
|
-
body: ""
|
|
305
|
-
};
|
|
306
|
-
const signature = await this.signFinbricksJws({ jwsData });
|
|
307
|
-
console.log("signature ready", signature);
|
|
308
|
-
const [data, error] = await useResult(
|
|
309
|
-
fetch(`${this.BASE_URI}${fullUri}`, {
|
|
441
|
+
let cursor = null;
|
|
442
|
+
const allPayments = [];
|
|
443
|
+
const fetchTransactions = async (cursor2) => {
|
|
444
|
+
return useResult(
|
|
445
|
+
this.finbricks.request({
|
|
310
446
|
method: "GET",
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
447
|
+
endpoint: FINBRICKS_ENDPOINTS.ACCOUNT_TRANSACTIONS,
|
|
448
|
+
query: {
|
|
449
|
+
merchantId: this.finbricks.MERCHANT_ID,
|
|
450
|
+
clientId: this.lastValidClientId,
|
|
451
|
+
paymentProvider: this.PROVIDER,
|
|
452
|
+
bankAccountId: account.identification.number,
|
|
453
|
+
dateFrom,
|
|
454
|
+
dateTo,
|
|
455
|
+
currency: account.currency,
|
|
456
|
+
size: "20",
|
|
457
|
+
cursor: cursor2 ?? void 0
|
|
314
458
|
}
|
|
315
459
|
})
|
|
316
460
|
);
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
461
|
+
};
|
|
462
|
+
do {
|
|
463
|
+
{
|
|
464
|
+
const [response, error] = await fetchTransactions(cursor);
|
|
465
|
+
if (error || !response) {
|
|
466
|
+
throw createInternalError(error, {
|
|
467
|
+
message: "Finbricks: failed to fetch account transactions"
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
const { transactions, links } = response;
|
|
471
|
+
cursor = links?.[0]?.value || null;
|
|
472
|
+
for (const tx of transactions) {
|
|
473
|
+
allPayments.push(mapFinbricksTransactionToPayment(tx, account));
|
|
474
|
+
}
|
|
330
475
|
}
|
|
331
|
-
const { transactions, links } = await data.json();
|
|
332
|
-
cursor = links?.[0]?.value || null;
|
|
333
|
-
allPayments = [
|
|
334
|
-
...allPayments,
|
|
335
|
-
...transactions.map((tx) => {
|
|
336
|
-
const isIncoming = tx.creditDebitIndicator === "CRDT";
|
|
337
|
-
const relatedParties = tx.entryDetails?.transactionDetails?.relatedParties;
|
|
338
|
-
const paymentInsert = {
|
|
339
|
-
id: uuidv4(),
|
|
340
|
-
bankRefId: tx.entryReference || tx.fbxReference,
|
|
341
|
-
amount: tx.amount?.value || 0,
|
|
342
|
-
currency: tx.amount?.currency || "CZK",
|
|
343
|
-
debtorHolderName: isIncoming ? relatedParties?.debtor?.name || "Unknown" : "Unknown",
|
|
344
|
-
debtorIban: isIncoming ? relatedParties?.debtorAccount?.identification?.iban || account.identification.iban : account.identification.iban,
|
|
345
|
-
debtorAccountNumberWithBankCode: isIncoming ? relatedParties?.debtorAccount?.identification?.other?.identification || `${account.identification.number}/${account.identification.bankCode}` : `${account.identification.number}/${account.identification.bankCode}`,
|
|
346
|
-
creditorHolderName: isIncoming ? "Unknown" : relatedParties?.creditor?.name || "Unknown",
|
|
347
|
-
creditorIban: isIncoming ? account.identification.iban : relatedParties?.creditorAccount?.identification?.iban || account.identification.iban,
|
|
348
|
-
creditorAccountNumberWithBankCode: isIncoming ? `${account.identification.number}/${account.identification.bankCode}` : relatedParties?.creditorAccount?.identification?.other?.identification || `${account.identification.number}/${account.identification.bankCode}`,
|
|
349
|
-
paymentType: "DOMESTIC",
|
|
350
|
-
direction: isIncoming ? "INCOMING" : "OUTGOING",
|
|
351
|
-
message: tx.entryDetails?.transactionDetails?.remittanceInformation?.unstructured || tx.entryDetails?.transactionDetails?.additionalRemittanceInformation || null,
|
|
352
|
-
...mapReferencesToPayment(
|
|
353
|
-
tx.entryDetails?.transactionDetails?.remittanceInformation?.structured?.creditorReferenceInformation?.reference || tx.entryDetails?.transactionDetails?.references?.endToEndIdentification
|
|
354
|
-
),
|
|
355
|
-
processedAt: new Date(tx.bookingDate.date),
|
|
356
|
-
status: "COMPLETED"
|
|
357
|
-
};
|
|
358
|
-
return {
|
|
359
|
-
...paymentInsert,
|
|
360
|
-
direction: getPaymentDirection(
|
|
361
|
-
paymentInsert,
|
|
362
|
-
account.identification.iban
|
|
363
|
-
),
|
|
364
|
-
status: mapFinbricksStatus(tx.status)
|
|
365
|
-
};
|
|
366
|
-
})
|
|
367
|
-
];
|
|
368
476
|
} while (cursor != null);
|
|
369
477
|
return allPayments;
|
|
370
478
|
}
|
|
479
|
+
async getPaymentStatus({
|
|
480
|
+
paymentId
|
|
481
|
+
}) {
|
|
482
|
+
const [response, error] = await useResult(
|
|
483
|
+
this.finbricks.request({
|
|
484
|
+
method: "GET",
|
|
485
|
+
endpoint: FINBRICKS_ENDPOINTS.TRANSACTION_STATUS,
|
|
486
|
+
query: {
|
|
487
|
+
merchantId: this.finbricks.MERCHANT_ID,
|
|
488
|
+
merchantTransactionId: paymentId
|
|
489
|
+
}
|
|
490
|
+
})
|
|
491
|
+
);
|
|
492
|
+
if (error || !response) {
|
|
493
|
+
throw createInternalError(error, {
|
|
494
|
+
message: "Finbricks: failed to fetch payment status"
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
return mapFinbricksTransactionStatus(response.resultCode);
|
|
498
|
+
}
|
|
499
|
+
async getBatchStatus({ batchId }) {
|
|
500
|
+
const [response, error] = await useResult(
|
|
501
|
+
this.finbricks.request({
|
|
502
|
+
method: "POST",
|
|
503
|
+
endpoint: FINBRICKS_ENDPOINTS.BATCH_STATUS,
|
|
504
|
+
body: {
|
|
505
|
+
merchantId: this.finbricks.MERCHANT_ID,
|
|
506
|
+
merchantBatchId: batchId
|
|
507
|
+
}
|
|
508
|
+
})
|
|
509
|
+
);
|
|
510
|
+
if (error || !response) {
|
|
511
|
+
throw createInternalError(error, {
|
|
512
|
+
message: "Finbricks: failed to fetch batch status"
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
return mapFinbricksBatchStatus(response.batchResultCode);
|
|
516
|
+
}
|
|
371
517
|
}
|
|
372
518
|
|
|
373
519
|
const isDeposit = (payment, creditorIban) => {
|
|
@@ -391,6 +537,9 @@ class ErsteConnector extends IBankConnector {
|
|
|
391
537
|
this.PAYMENTS_URI = config.PAYMENTS_URI;
|
|
392
538
|
this.ACCOUNTS_URI = config.ACCOUNTS_URI;
|
|
393
539
|
}
|
|
540
|
+
connectUserAccount() {
|
|
541
|
+
throw new Error("Method not implemented.");
|
|
542
|
+
}
|
|
394
543
|
static {
|
|
395
544
|
this.FETCH_INTERVAL = 60 * 60 * 5;
|
|
396
545
|
}
|
|
@@ -435,6 +584,9 @@ class ErsteConnector extends IBankConnector {
|
|
|
435
584
|
this.accessToken = json.access_token;
|
|
436
585
|
return json.refresh_token || "";
|
|
437
586
|
}
|
|
587
|
+
listAccounts() {
|
|
588
|
+
throw new Error("Method not implemented.");
|
|
589
|
+
}
|
|
438
590
|
async preparePayment(payment) {
|
|
439
591
|
if (!this.accessToken) {
|
|
440
592
|
throw createInternalError(null, {
|
|
@@ -621,6 +773,12 @@ class ErsteConnector extends IBankConnector {
|
|
|
621
773
|
});
|
|
622
774
|
return payments;
|
|
623
775
|
}
|
|
776
|
+
getPaymentStatus(_) {
|
|
777
|
+
throw new Error("Method not implemented.");
|
|
778
|
+
}
|
|
779
|
+
getBatchStatus(_) {
|
|
780
|
+
throw new Error("Method not implemented.");
|
|
781
|
+
}
|
|
624
782
|
}
|
|
625
783
|
|
|
626
784
|
class MockConnector extends IBankConnector {
|
|
@@ -634,6 +792,15 @@ class MockConnector extends IBankConnector {
|
|
|
634
792
|
async authenticate() {
|
|
635
793
|
return "";
|
|
636
794
|
}
|
|
795
|
+
async listAccounts() {
|
|
796
|
+
return [];
|
|
797
|
+
}
|
|
798
|
+
async connectUserAccount() {
|
|
799
|
+
return {
|
|
800
|
+
clientId: uuidv4(),
|
|
801
|
+
redirectUrl: ""
|
|
802
|
+
};
|
|
803
|
+
}
|
|
637
804
|
async preparePayment(payment) {
|
|
638
805
|
const bankRefId = uuidv4();
|
|
639
806
|
return {
|
|
@@ -681,6 +848,12 @@ class MockConnector extends IBankConnector {
|
|
|
681
848
|
status: "COMPLETED"
|
|
682
849
|
}));
|
|
683
850
|
}
|
|
851
|
+
getPaymentStatus(_) {
|
|
852
|
+
throw new Error("Method not implemented.");
|
|
853
|
+
}
|
|
854
|
+
getBatchStatus(_) {
|
|
855
|
+
throw new Error("Method not implemented.");
|
|
856
|
+
}
|
|
684
857
|
}
|
|
685
858
|
|
|
686
859
|
class MockCobsConnector extends FinbricksConnector {
|
|
@@ -701,6 +874,8 @@ const paymentInsertTypeZod = z.object({
|
|
|
701
874
|
bankPaymentInitiatedAt: z.coerce.date(),
|
|
702
875
|
bankPaymentProcessedAt: z.coerce.date(),
|
|
703
876
|
bankingVs: z.string().max(10),
|
|
877
|
+
bankingSs: z.string().max(10),
|
|
878
|
+
bankingKs: z.string().max(10),
|
|
704
879
|
message: z.string(),
|
|
705
880
|
creditorIban: z.string().max(34),
|
|
706
881
|
creditorHolderName: z.string(),
|
|
@@ -708,4 +883,4 @@ const paymentInsertTypeZod = z.object({
|
|
|
708
883
|
debtorHolderName: z.string().nullable()
|
|
709
884
|
});
|
|
710
885
|
|
|
711
|
-
export { BATCH_STATUSES as B, COUNTRY_CODES as C, ErsteConnector as E, FinbricksConnector as F, IBankConnector as I, MockConnector as M, PAYMENT_TYPES as P, MockCobsConnector as a,
|
|
886
|
+
export { BATCH_STATUSES as B, COUNTRY_CODES as C, ErsteConnector as E, FinbricksConnector as F, IBankConnector as I, MockConnector as M, PAYMENT_TYPES as P, MockCobsConnector as a, FinbricksClient as b, FINBRICKS_ENDPOINTS as c, PAYMENT_STATUSES as d, PAYMENT_DIRECTIONS as e, getPaymentDirection as g, paymentInsertTypeZod as p, signFinbricksJws as s, tables as t, useFinbricksFetch as u };
|