nodebb-plugin-onekite-calendar 2.0.96 → 2.0.97
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/lib/helloassoWebhook.js +12 -7
- package/lib/scheduler.js +48 -1
- package/package.json +1 -1
package/lib/helloassoWebhook.js
CHANGED
|
@@ -336,17 +336,22 @@ async function handler(req, res, next) {
|
|
|
336
336
|
// Extract paymentId.
|
|
337
337
|
// Format A: data.id is the payment ID.
|
|
338
338
|
// Format B: order.payments[0].id is the payment ID (data.id is the checkout intent ID).
|
|
339
|
+
// For Format B, HelloAsso sometimes omits order.payments — fall back to checkoutIntentId
|
|
340
|
+
// as the idempotency key (prefixed to avoid collision with numeric payment IDs).
|
|
339
341
|
const _payments = (_d && _d.order && Array.isArray(_d.order.payments)) ? _d.order.payments : [];
|
|
340
342
|
const paymentId = payload.data
|
|
341
343
|
? (_d.id || _d.paymentId)
|
|
342
344
|
: (_payments.length ? _payments[0].id : null);
|
|
343
|
-
|
|
345
|
+
const idempotencyKey = paymentId
|
|
346
|
+
? String(paymentId)
|
|
347
|
+
: (!payload.data && checkoutIntentId ? `ci_${checkoutIntentId}` : null);
|
|
348
|
+
if (!idempotencyKey) {
|
|
344
349
|
// eslint-disable-next-line no-console
|
|
345
|
-
console.warn('[calendar-onekite] HelloAsso webhook: missing payment id', { eventType, dataKeys: _d ? Object.keys(_d) : [] });
|
|
350
|
+
console.warn('[calendar-onekite] HelloAsso webhook: missing payment id and checkout intent id', { eventType, dataKeys: _d ? Object.keys(_d) : [] });
|
|
346
351
|
return res.json({ ok: true, ignored: true, missingPaymentId: true });
|
|
347
352
|
}
|
|
348
353
|
|
|
349
|
-
if (await alreadyProcessed(
|
|
354
|
+
if (await alreadyProcessed(idempotencyKey)) {
|
|
350
355
|
return res.json({ ok: true, duplicate: true });
|
|
351
356
|
}
|
|
352
357
|
|
|
@@ -369,13 +374,13 @@ async function handler(req, res, next) {
|
|
|
369
374
|
if (!r) {
|
|
370
375
|
// eslint-disable-next-line no-console
|
|
371
376
|
console.warn('[calendar-onekite] HelloAsso webhook: reservation not found (expired/deleted?)', { rid: resolvedRid, paymentId });
|
|
372
|
-
await markProcessed(
|
|
377
|
+
await markProcessed(idempotencyKey);
|
|
373
378
|
return res.json({ ok: true, processed: true, reservationNotFound: true });
|
|
374
379
|
}
|
|
375
380
|
|
|
376
381
|
// Idempotency: already paid → just mark as processed and return.
|
|
377
382
|
if (r.status === 'paid') {
|
|
378
|
-
await markProcessed(
|
|
383
|
+
await markProcessed(idempotencyKey);
|
|
379
384
|
return res.json({ ok: true, processed: true, alreadyPaid: true });
|
|
380
385
|
}
|
|
381
386
|
|
|
@@ -384,7 +389,7 @@ async function handler(req, res, next) {
|
|
|
384
389
|
|
|
385
390
|
r.status = 'paid';
|
|
386
391
|
r.paidAt = Date.now();
|
|
387
|
-
r.paymentId = String(paymentId);
|
|
392
|
+
if (paymentId) r.paymentId = String(paymentId);
|
|
388
393
|
// paymentReceiptUrl: Format A = data.paymentReceiptUrl, Format B = order.payments[0].paymentReceiptUrl
|
|
389
394
|
const _receiptUrl = (payload.data && payload.data.paymentReceiptUrl)
|
|
390
395
|
? String(payload.data.paymentReceiptUrl)
|
|
@@ -444,7 +449,7 @@ async function handler(req, res, next) {
|
|
|
444
449
|
});
|
|
445
450
|
} catch (e) {}
|
|
446
451
|
|
|
447
|
-
await markProcessed(
|
|
452
|
+
await markProcessed(idempotencyKey);
|
|
448
453
|
return res.json({ ok: true, processed: true });
|
|
449
454
|
} catch (err) {
|
|
450
455
|
return next(err);
|
package/lib/scheduler.js
CHANGED
|
@@ -1,8 +1,54 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const nconf = require.main.require('nconf');
|
|
4
|
+
const meta = require.main.require('./src/meta');
|
|
5
|
+
|
|
6
|
+
const dbLayer = require('./db');
|
|
7
|
+
const { syncPayment } = require('./syncPayment');
|
|
8
|
+
|
|
9
|
+
const SETTINGS_KEY = 'calendar-onekite';
|
|
10
|
+
const SYNC_INTERVAL_MS = 60 * 1000;
|
|
11
|
+
// Minimum time between HelloAsso API checks for the same reservation (avoid spamming)
|
|
12
|
+
const MIN_CHECK_INTERVAL_MS = 2 * 60 * 1000;
|
|
4
13
|
|
|
5
14
|
let timer = null;
|
|
15
|
+
const lastChecked = new Map(); // rid → timestamp of last HelloAsso check
|
|
16
|
+
|
|
17
|
+
async function runPaymentSync() {
|
|
18
|
+
try {
|
|
19
|
+
const settings = await meta.settings.get(SETTINGS_KEY);
|
|
20
|
+
if (!settings || !settings.helloassoClientId || !settings.helloassoClientSecret) return;
|
|
21
|
+
|
|
22
|
+
const ids = await dbLayer.listAllReservationIds(5000);
|
|
23
|
+
if (!ids || !ids.length) return;
|
|
24
|
+
|
|
25
|
+
const reservations = await dbLayer.getReservations(ids);
|
|
26
|
+
const now = Date.now();
|
|
27
|
+
|
|
28
|
+
const toSync = (reservations || []).filter((r) => {
|
|
29
|
+
if (!r || r.status !== 'awaiting_payment' || !r.checkoutIntentId) return false;
|
|
30
|
+
const last = lastChecked.get(String(r.rid));
|
|
31
|
+
return !last || (now - last) >= MIN_CHECK_INTERVAL_MS;
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
for (const r of toSync) {
|
|
35
|
+
lastChecked.set(String(r.rid), now);
|
|
36
|
+
try {
|
|
37
|
+
const result = await syncPayment({ r, settings, actorUid: 0 });
|
|
38
|
+
if (result.synced) {
|
|
39
|
+
// eslint-disable-next-line no-console
|
|
40
|
+
console.info('[calendar-onekite] Scheduler: auto-synced payment from HelloAsso', { rid: r.rid });
|
|
41
|
+
}
|
|
42
|
+
} catch (e) {
|
|
43
|
+
// eslint-disable-next-line no-console
|
|
44
|
+
console.warn('[calendar-onekite] Scheduler: syncPayment error', { rid: r.rid, err: e && e.message });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
} catch (e) {
|
|
48
|
+
// eslint-disable-next-line no-console
|
|
49
|
+
console.warn('[calendar-onekite] Scheduler: payment sync error', { err: e && e.message });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
6
52
|
|
|
7
53
|
function start() {
|
|
8
54
|
const runJobs = nconf.get('runJobs');
|
|
@@ -11,8 +57,9 @@ function start() {
|
|
|
11
57
|
console.info('[calendar-onekite] Scheduler disabled (runJobs=false)');
|
|
12
58
|
return;
|
|
13
59
|
}
|
|
60
|
+
timer = setInterval(runPaymentSync, SYNC_INTERVAL_MS);
|
|
14
61
|
// eslint-disable-next-line no-console
|
|
15
|
-
console.info('[calendar-onekite] Scheduler started
|
|
62
|
+
console.info('[calendar-onekite] Scheduler started');
|
|
16
63
|
}
|
|
17
64
|
|
|
18
65
|
function stop() {
|
package/package.json
CHANGED