From bd99931255c0a5848683e50f2e71b371b646a6d6 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 29 May 2026 23:17:30 +0100 Subject: [PATCH 1/4] feat: update TypeScript configuration to use Node16 module and resolution --- tsconfig.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 5c26e8fc..f0e41aff 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "ES2022", - "module": "commonjs", + "module": "Node16", "lib": ["ES2022", "DOM"], "outDir": "./dist", "rootDir": ".", @@ -15,7 +15,7 @@ "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, - "moduleResolution": "node", + "moduleResolution": "Node16", "types": ["node"], "typeRoots": ["./node_modules/@types"] }, From 606a4e3b64285e9f53edae32a7b24351f84944ee Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 29 May 2026 23:17:57 +0100 Subject: [PATCH 2/4] Refactor code structure for improved readability and maintainability --- ...edis-pub-sub-for-real-time-notificat.patch | 171 +++++++ ...e-pr-add-PR-bundle-patch-description.patch | 103 ++++ ...e-table-partitioning-for-audit-logs-.patch | 176 +++++++ ...itioning-migration-and-audit-middlew.patch | 484 ++++++++++++++++++ .pull_request/pr.md | 3 + .pull_request/pr_bundle.zip | Bin 0 -> 90634 bytes sdk/.gradle/9.2.0/fileHashes/fileHashes.lock | Bin 17 -> 17 bytes src/queue/batchPayoutWorker.ts | 2 +- src/services/mobilemoney/providers/mtn.ts | 82 ++- 9 files changed, 1009 insertions(+), 12 deletions(-) create mode 100644 .pull_request/0001-feat-implement-Redis-pub-sub-for-real-time-notificat.patch create mode 100644 .pull_request/0002-chore-pr-add-PR-bundle-patch-description.patch create mode 100644 .pull_request/0003-feat-add-database-table-partitioning-for-audit-logs-.patch create mode 100644 .pull_request/0004-revert-undo-partitioning-migration-and-audit-middlew.patch create mode 100644 .pull_request/pr.md create mode 100644 .pull_request/pr_bundle.zip diff --git a/.pull_request/0001-feat-implement-Redis-pub-sub-for-real-time-notificat.patch b/.pull_request/0001-feat-implement-Redis-pub-sub-for-real-time-notificat.patch new file mode 100644 index 00000000..99e899c8 --- /dev/null +++ b/.pull_request/0001-feat-implement-Redis-pub-sub-for-real-time-notificat.patch @@ -0,0 +1,171 @@ +From c2324c74ceb8da6ae9cde5355d981cf872650bee Mon Sep 17 00:00:00 2001 +From: Martin +Date: Fri, 29 May 2026 16:09:30 +0100 +Subject: [PATCH 1/4] feat: implement Redis pub/sub for real-time notifications + +--- + src/jobs/scheduler.ts | 8 +++ + src/models/transaction.ts | 2 +- + src/workers/notificationWorker.ts | 113 ++++++++++++++++++++++++++++++ + 3 files changed, 122 insertions(+), 1 deletion(-) + create mode 100644 src/workers/notificationWorker.ts + +diff --git a/src/jobs/scheduler.ts b/src/jobs/scheduler.ts +index 3fff24b..6186211 100644 +--- a/src/jobs/scheduler.ts ++++ b/src/jobs/scheduler.ts +@@ -17,6 +17,7 @@ import { runLiquidityRebalanceJob } from "./liquidityRebalanceJob"; + import { runCrossChainMonitorJob } from "./crossChainMonitorJob"; + import { runDailyProviderReconciliation } from "./providerReconciliationJob"; + import { runReconciliationJob } from "./reconciliationJob"; ++import { startNotificationWorker } from "../workers/notificationWorker"; + + + interface JobConfig { +@@ -128,4 +129,11 @@ export function startJobs(): void { + cron.schedule(job.schedule, () => runJob(job)); + console.log(`[scheduler] "${job.name}" scheduled - ${job.schedule}`); + } ++ ++ // Start the notification worker which listens for Redis pub/sub events ++ // and drives user-facing notifications in real-time. This replaces any ++ // DB-polling notification mechanisms. ++ startNotificationWorker().catch((err) => { ++ console.warn("Failed to start NotificationWorker:", err); ++ }); + } +diff --git a/src/models/transaction.ts b/src/models/transaction.ts +index bfbd273..00d5e31 100644 +--- a/src/models/transaction.ts ++++ b/src/models/transaction.ts +@@ -195,7 +195,7 @@ export class TransactionModel { + const res = await queryWrite(q, params); + if (!res.rowCount) return; + +- const row = result.rows[0]; ++ const row = res.rows[0]; + + // ── Invalidate caches on transaction status update ──────────────────── + if (row.user_id) { +diff --git a/src/workers/notificationWorker.ts b/src/workers/notificationWorker.ts +new file mode 100644 +index 0000000..59050b4 +--- /dev/null ++++ b/src/workers/notificationWorker.ts +@@ -0,0 +1,113 @@ ++import IORedis from "ioredis"; ++import { SubscriptionChannels } from "../graphql/subscriptions"; ++import { notificationRouter } from "../services/notificationRouter"; ++import { TransactionModel } from "../models/transaction"; ++ ++const REDIS_URL = process.env.REDIS_URL || "redis://localhost:6379"; ++ ++const redisOptions: any = { ++ retryStrategy: (times: number) => Math.min(100 + times * 200, 3000), ++ enableOfflineQueue: false, ++ maxRetriesPerRequest: 1, ++ lazyConnect: false, ++}; ++ ++let subscriber: IORedis | null = null; ++ ++/** ++ * Notification worker — subscribes to transaction update channels in Redis ++ * and routes user-facing notifications (email/sms/push/etc.) via ++ * `NotificationRouter`. This replaces DB polling for notification triggers. ++ */ ++export async function startNotificationWorker(): Promise { ++ if (!process.env.REDIS_URL) { ++ console.warn( ++ "NotificationWorker: REDIS_URL not set — running without Redis subscription", ++ ); ++ return; ++ } ++ ++ subscriber = new IORedis(REDIS_URL, redisOptions); ++ ++ subscriber.on("connect", () => console.log("NotificationWorker: Redis connected")); ++ subscriber.on("error", (err) => ++ console.error("NotificationWorker: Redis error:", err), ++ ); ++ ++ await subscriber.connect(); ++ ++ // Subscribe to broadcast updates and per-transaction channels (pattern) ++ await subscriber.subscribe(SubscriptionChannels.TRANSACTION_UPDATED); ++ await subscriber.psubscribe("TRANSACTION_UPDATED:*"); ++ ++ subscriber.on("message", async (_channel: string, rawMessage: string) => { ++ try { ++ const payload = JSON.parse(rawMessage) as { ++ id?: string; ++ status?: string; ++ [key: string]: any; ++ }; ++ ++ const txId = payload.id; ++ const status = payload.status; ++ if (!txId || !status) return; ++ ++ const txModel = new TransactionModel(); ++ const tx = await txModel.findById(txId); ++ if (!tx) return; ++ ++ if (status === "completed") { ++ await notificationRouter.routeTransactionNotification(tx, "completed"); ++ } else if (status === "failed") { ++ await notificationRouter.routeTransactionNotification(tx, "failed", payload.error); ++ } ++ } catch (err) { ++ console.error("NotificationWorker: failed to handle message:", err); ++ } ++ }); ++ ++ // pmessage handles pattern subscriptions (TRANSACTION_UPDATED:) ++ subscriber.on( ++ "pmessage", ++ async (_pattern: string, _channel: string, rawMessage: string) => { ++ try { ++ const payload = JSON.parse(rawMessage) as { ++ id?: string; ++ status?: string; ++ [key: string]: any; ++ }; ++ ++ const txId = payload.id; ++ const status = payload.status; ++ if (!txId || !status) return; ++ ++ const txModel = new TransactionModel(); ++ const tx = await txModel.findById(txId); ++ if (!tx) return; ++ ++ if (status === "completed") { ++ await notificationRouter.routeTransactionNotification(tx, "completed"); ++ } else if (status === "failed") { ++ await notificationRouter.routeTransactionNotification(tx, "failed", payload.error); ++ } ++ } catch (err) { ++ console.error("NotificationWorker: failed to handle pmessage:", err); ++ } ++ }, ++ ); ++ ++ console.log("NotificationWorker: subscribed to transaction update channels"); ++} ++ ++export async function stopNotificationWorker(): Promise { ++ try { ++ if (!subscriber) return; ++ await subscriber.unsubscribe(SubscriptionChannels.TRANSACTION_UPDATED); ++ await subscriber.punsubscribe("TRANSACTION_UPDATED:*"); ++ await subscriber.quit(); ++ subscriber = null; ++ console.log("NotificationWorker: stopped"); ++ } catch (err) { ++ console.warn("NotificationWorker: stop error:", err); ++ } ++} +-- +2.45.1.windows.1 + diff --git a/.pull_request/0002-chore-pr-add-PR-bundle-patch-description.patch b/.pull_request/0002-chore-pr-add-PR-bundle-patch-description.patch new file mode 100644 index 00000000..5520dd0f --- /dev/null +++ b/.pull_request/0002-chore-pr-add-PR-bundle-patch-description.patch @@ -0,0 +1,103 @@ +From 44ee883a769411489145c35d82c0da042411f2bf Mon Sep 17 00:00:00 2001 +From: Martin +Date: Fri, 29 May 2026 16:15:22 +0100 +Subject: [PATCH 2/4] chore(pr): add PR bundle (patch + description) + +--- + .pull_request/PR.md | 21 +++++++++++++++++++++ + .pull_request/changes.patch | Bin 0 -> 11444 bytes + 2 files changed, 21 insertions(+) + create mode 100644 .pull_request/PR.md + create mode 100644 .pull_request/changes.patch + +diff --git a/.pull_request/PR.md b/.pull_request/PR.md +new file mode 100644 +index 0000000..bc020e1 +--- /dev/null ++++ b/.pull_request/PR.md +@@ -0,0 +1,21 @@ ++# Implement Redis Pub/Sub real-time notification worker ++ ++Adds a Redis-backed notification worker that subscribes to transaction update channels and routes user notifications via the existing NotificationRouter. Wires the worker into the job scheduler and fixes a bug in TransactionModel.updateStatus. ++ ++## Files changed ++- src/workers/notificationWorker.ts (new) ++- src/jobs/scheduler.ts (start worker) ++- src/models/transaction.ts (bugfix) ++ ++## Testing notes ++1. Ensure `REDIS_URL` points to a running Redis instance. ++2. Start the app: `npm run dev` ++3. Publish test messages to Redis, e.g.: ++ `redis-cli PUBLISH transaction.updated '{"id":"","status":"completed"}'` ++ ++## Acceptance criteria ++- Worker subscribes to transaction channels and routes notifications via `NotificationRouter`. ++- Replaces DB polling for notifications, lowers DB load, and enables sub-second notifications. ++ ++## Patch apply (git) ++Apply the patch locally: `git apply .pull_request/changes.patch` +diff --git a/.pull_request/changes.patch b/.pull_request/changes.patch +new file mode 100644 +index 0000000000000000000000000000000000000000..49e17b48233105c6672a61ca043d2781196774ea +GIT binary patch +literal 11444 +zcmd6t+j1Mn5r(JAcd5!dtXR2#B?_cSTZ$Dsu|p+Qa#@x`Dy0jTD*{3A;E*5;P?Q;) +zPvM918~HNH|4m~!GrP0Ef{q;57O|(9K27)E-Lw49zYpArJJeCR`#O$vccJTn+i){C +zbwi!McIR$icRU%oV>faK?zKDBdFuKaJ=6I8mB#^OV598Wy&Q`zDR +zPxDgz@rbFn&Cp9U)H8G+$Yv-0N!dP=6)2c?%jIK*+N4-yq3P0jf%iS>R9~Z+G#yBr +z3-`J7Me9TT4HFBz)bpwPr?*te8u-hbWDJoz?m#O@Rn7f}Xu*rdl7dw^lnnT3HTRDl +z(Hwi}j`Zw1KP$$_mg09^(P~k$lb7Vz3@GYHDA9F2Uxm?$N4erf< +zk#s{x<@RoDS?LM{uf)IXT~+2g+#P%WJJuW`k#!k)%|R(VvGVZ>u8|2QT1m?WoGXpJ +z^yA4BhQCoRkPl4{MCHVfgTiZ##NY1EJq{1V!`dWf4r5rmk*s>EnZym&?d$0auK}ZY +zKhb;hW1b+vRYR^P?$4rucbZd57nLy>@iKe +z)S~U4G_b>PDthA^$ugBRJ$ct(bq$J=w^{#l{YHtZPB`;ebcmE7hed<&zfQc+JaBnU +zaV9Ci`cp}=A(^J$b~F7(3@FtW-RI$Jctxl&_T^3cy4zCr-j;p)zM?pA?@NkpAyc;+ +zV>SMzT4OZ|5l=smr0bI1@HF0)&~zxf4rEz8{6*2W&u;8rLQz9D>Odj}Dj(@@AS}R> +zcup;YFI>5o`sLY3zV?mxk%>mJo8PxvbN48*-Nr`^~2T$ITsR7w}#%i(*Wdwe(` +zFDnWr<{ocSLcydoiK$O7w8lnzCQRNG2B%&Nl@K*$8s*s`I|exnVSjk%C-7dzI>w*taZGn}bx?xlk8B$!KFNp`Z^1f=FP5o{PZ}M(s!=urq +zugCDv(|L{nC-RQu=jjqqV^i_CDa!OIR();jj<;xBuRfo;=UFCCdk|`GBFnOTs1)lI +z=y&$y33Q9pVkh$3P`B^Q^CNZ!_1h}fBh4FV-m%c+RF;~x(3>eEg?MgO(QUQDu16WG +zXWJyO>Vs~CYIjlfoKjmP&2kKG_IyXtbxO{JYlnGg%!Z +zw{FR_qf()syVSWf{(JYmW~?Qv|5%dIk)C@*JJm{^%Ssoj%^xb0K9ct@k|}!8^Ta!5 +zzOuJ!%w#n5unNPn0oi6xeDDue@JRRc13m9!RC`8cERQ*p4}wOqu7Bod9ZSnItt^$1 +z?I{o)R7F-V;+J>zx8-A+if7wjZHm%*Qqf_tfp~xv&xIL>x`OJ+{Y~6Q{twycnOMD1 +zHv3FZzxA<(HYa*JQxA;1yZW`3}hwK6e +zfo|g@1;GVIjTaS&HPwB^Uc|)xoH8tE!t7v=I+$U&8AbmZ#f{3Cn +z%F&@$&=<69L-#0Kl)1#SFB@3bYIzLqAe?od={`-!b72+3{>v&W7V4SM2?TCDUsKFR +zsqu)#@)AMd$@E56dlE8G)Vt(^aW)-$cb|(IL<;p@J@u2QQIsd`>&iOjYr!hy832vK +zPE%U_ky`?e?|bVEWLf$kvRmHQfcOEICz{9pPaF0((Y_~4penG+ZY&8`(XXj>uX7#S +zSBJgpKGB-(>32tWM9SyB;`&0>*C%>H6&7uVwU*m_Qt&r?qZ_x!%E)+dbDf&H@)ajU +zomEln!;lj!>Qc<_Nw3`cU>~bVCIQV11<>!AD6j`%wCdwRT;v#!ZJ>3fbBg^u=Lk`c +z_pstu%Dd+}v$F+SeX3Q!`hEYFoW{Nc6=Pi<_@h~w54`Bz|Kw|{RKH-+AW=9o1`j0j +zcc1m8-^aH)Hs$hFQV*9n)@!>U1?SX87pgO#-dHEMSl??{qMlMn^)egRsiY%H)MDO_ +z(pXn26kXO}+RSB#@mBnz}l7Bs!nn9aHNRSV($ +z2dcxayuIm3%7C1mE$Xopvv}EWLgf~G3H#W$ +zK~R}q*eJB=9wH}HO_AR!bw{d4$nW;`~O%IpYlD((NV6Li~LU6(V@oF-}LG0PccdA=_3Fv@9u%Q>%}v*D84r+eN`=cem` +zl()^@&`kCFHRf+*d|NsE|EGVN|17hun#av9d7cQiS|y(|Ct%6j&gu0mR^MkRVM?0W +zO}}Nk5p``=-`8no=Jqx2NVx>4zbScf}t1q^!@Av^FXvsuSC +z?G^W0?Q2UtlKvMl!Pk_Dj?fdPJXYx<>axV$^X~hWWI&_||4+oO=^`` +Date: Fri, 29 May 2026 21:22:31 +0100 +Subject: [PATCH 3/4] feat: add database table partitioning for audit logs and + update related middleware + +--- + .pull_request/PR.md | 21 ------- + .vscode/settings.json | 2 + + .../migrations/20260424_create_audit_logs.sql | 55 +++++++++++++++---- + src/middleware/auditInterceptor.ts | 9 +-- + tsconfig.json | 3 +- + tsconfig.test.json | 7 +++ + 6 files changed, 60 insertions(+), 37 deletions(-) + create mode 100644 tsconfig.test.json + +diff --git a/.pull_request/PR.md b/.pull_request/PR.md +index bc020e1..e69de29 100644 +--- a/.pull_request/PR.md ++++ b/.pull_request/PR.md +@@ -1,21 +0,0 @@ +-# Implement Redis Pub/Sub real-time notification worker +- +-Adds a Redis-backed notification worker that subscribes to transaction update channels and routes user notifications via the existing NotificationRouter. Wires the worker into the job scheduler and fixes a bug in TransactionModel.updateStatus. +- +-## Files changed +-- src/workers/notificationWorker.ts (new) +-- src/jobs/scheduler.ts (start worker) +-- src/models/transaction.ts (bugfix) +- +-## Testing notes +-1. Ensure `REDIS_URL` points to a running Redis instance. +-2. Start the app: `npm run dev` +-3. Publish test messages to Redis, e.g.: +- `redis-cli PUBLISH transaction.updated '{"id":"","status":"completed"}'` +- +-## Acceptance criteria +-- Worker subscribes to transaction channels and routes notifications via `NotificationRouter`. +-- Replaces DB polling for notifications, lowers DB load, and enables sub-second notifications. +- +-## Patch apply (git) +-Apply the patch locally: `git apply .pull_request/changes.patch` +diff --git a/.vscode/settings.json b/.vscode/settings.json +index 4b852bb..6eaf0c5 100644 +--- a/.vscode/settings.json ++++ b/.vscode/settings.json +@@ -1,4 +1,6 @@ + { + "kiroAgent.configureMCP": "Disabled", + "codium.codeCompletion.enable": false ++ ,"typescript.tsdk": "./node_modules/typescript/lib", ++ "typescript.enablePromptUseWorkspaceTsdk": true + } +diff --git a/database/migrations/20260424_create_audit_logs.sql b/database/migrations/20260424_create_audit_logs.sql +index 9bac8db..e553f42 100644 +--- a/database/migrations/20260424_create_audit_logs.sql ++++ b/database/migrations/20260424_create_audit_logs.sql +@@ -1,14 +1,47 @@ ++-- Parent partitioned table: partitioned by month on created_at + CREATE TABLE audit_logs ( +- id UUID PRIMARY KEY DEFAULT gen_random_uuid(), +- admin_id UUID NOT NULL REFERENCES users(id), +- action VARCHAR(255) NOT NULL, +- resource VARCHAR(255) NOT NULL, +- resource_id VARCHAR(255), +- diff JSONB NOT NULL, +- ip_address VARCHAR(45), +- user_agent TEXT, +- created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP +-); ++ id UUID PRIMARY KEY DEFAULT gen_random_uuid(), ++ admin_id UUID NOT NULL REFERENCES users(id), ++ action VARCHAR(255) NOT NULL, ++ resource VARCHAR(255) NOT NULL, ++ resource_id VARCHAR(255), ++ diff JSONB NOT NULL, ++ ip_address VARCHAR(45), ++ user_agent TEXT, ++ created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP ++) PARTITION BY RANGE (created_at); + ++-- Create indexes on the partitioned parent (propagates to partitions) + CREATE INDEX idx_audit_logs_admin_id ON audit_logs(admin_id); +-CREATE INDEX idx_audit_logs_resource ON audit_logs(resource, resource_id); +\ No newline at end of file ++CREATE INDEX idx_audit_logs_resource ON audit_logs(resource, resource_id); ++ ++-- Helper function: create monthly partition if it does not exist ++CREATE OR REPLACE FUNCTION audit_logs_create_monthly_partition() ++RETURNS TRIGGER AS $$ ++DECLARE ++ partition_name TEXT; ++ start_month TIMESTAMP WITH TIME ZONE; ++ end_month TIMESTAMP WITH TIME ZONE; ++ sql TEXT; ++BEGIN ++ start_month := date_trunc('month', NEW.created_at); ++ end_month := (start_month + INTERVAL '1 month'); ++ partition_name := format('audit_logs_%s', to_char(start_month, 'YYYYMM')); ++ ++ sql := format( ++ 'CREATE TABLE IF NOT EXISTS %I PARTITION OF audit_logs FOR VALUES FROM (''%s'') TO (''%s'')', ++ partition_name, ++ start_month, ++ end_month ++ ); ++ ++ EXECUTE sql; ++ RETURN NEW; ++END; ++$$ LANGUAGE plpgsql; ++ ++-- Trigger: ensures appropriate monthly partition exists before insert ++DROP TRIGGER IF EXISTS trg_audit_logs_create_partition ON audit_logs; ++CREATE TRIGGER trg_audit_logs_create_partition ++BEFORE INSERT ON audit_logs ++FOR EACH ROW EXECUTE FUNCTION audit_logs_create_monthly_partition(); +\ No newline at end of file +diff --git a/src/middleware/auditInterceptor.ts b/src/middleware/auditInterceptor.ts +index 20cf701..b2d9ccc 100644 +--- a/src/middleware/auditInterceptor.ts ++++ b/src/middleware/auditInterceptor.ts +@@ -34,10 +34,10 @@ export const auditInterceptor = (db: Pool) => { + }; + + const query = ` +- INSERT INTO audit_logs (admin_id, action, resource, resource_id, diff, ip_address, user_agent) +- VALUES ($1, $2, $3, $4, $5, $6, $7) ++ INSERT INTO audit_logs (admin_id, action, resource, resource_id, diff, ip_address, user_agent, created_at) ++ VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + `; +- ++ + await db.query(query, [ + adminId, + action, +@@ -45,7 +45,8 @@ export const auditInterceptor = (db: Pool) => { + resourceId, + JSON.stringify(diff), + req.ip, +- req.get('user-agent') || null ++ req.get('user-agent') || null, ++ new Date().toISOString() + ]); + } catch (error) { + console.error('[Audit Log] Failed to save admin audit log event:', error); +diff --git a/tsconfig.json b/tsconfig.json +index f531b27..5c26e8f 100644 +--- a/tsconfig.json ++++ b/tsconfig.json +@@ -16,7 +16,8 @@ + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "moduleResolution": "node", +- "types": ["node", "jest"] ++ "types": ["node"], ++ "typeRoots": ["./node_modules/@types"] + }, + "include": ["src/**/*", "scripts/**/*"], + "exclude": ["node_modules", "dist", "src/utils/currency/examples/react-usage.tsx", "**/*.test.ts", "**/*.spec.ts", "**/__tests__/**", "tests/**"] +diff --git a/tsconfig.test.json b/tsconfig.test.json +new file mode 100644 +index 0000000..79750a1 +--- /dev/null ++++ b/tsconfig.test.json +@@ -0,0 +1,7 @@ ++{ ++ "extends": "./tsconfig.json", ++ "compilerOptions": { ++ "types": ["node", "jest"] ++ }, ++ "include": ["src/**/*.test.ts", "src/**/*.spec.ts", "tests/**/*.ts", "src/**/__tests__/**"] ++} +-- +2.45.1.windows.1 + diff --git a/.pull_request/0004-revert-undo-partitioning-migration-and-audit-middlew.patch b/.pull_request/0004-revert-undo-partitioning-migration-and-audit-middlew.patch new file mode 100644 index 00000000..e3f945f9 --- /dev/null +++ b/.pull_request/0004-revert-undo-partitioning-migration-and-audit-middlew.patch @@ -0,0 +1,484 @@ +From b4241eb768752edb1891a8a68ef48d28d164da07 Mon Sep 17 00:00:00 2001 +From: Martin +Date: Fri, 29 May 2026 21:56:29 +0100 +Subject: [PATCH 4/4] revert: undo partitioning migration and audit middleware + edit; remove PR bundle artifacts + +--- + .pull_request/changes.patch | Bin 11444 -> 0 bytes + .../migrations/20260424_create_audit_logs.sql | 55 ++++-------------- + sdk/.gradle/9.2.0/checksums/checksums.lock | Bin 0 -> 17 bytes + sdk/.gradle/9.2.0/checksums/md5-checksums.bin | Bin 0 -> 21747 bytes + .../9.2.0/checksums/sha1-checksums.bin | Bin 0 -> 26543 bytes + sdk/.gradle/9.2.0/fileChanges/last-build.bin | Bin 0 -> 1 bytes + sdk/.gradle/9.2.0/fileHashes/fileHashes.bin | Bin 0 -> 18547 bytes + sdk/.gradle/9.2.0/fileHashes/fileHashes.lock | Bin 0 -> 17 bytes + .../PR.md => sdk/.gradle/9.2.0/gc.properties | 0 + .../buildOutputCleanup.lock | Bin 17 -> 17 bytes + .../buildOutputCleanup/cache.properties | 4 +- + .../reports/problems/problems-report.html | 11 +--- + src/middleware/auditInterceptor.ts | 9 ++- + 13 files changed, 19 insertions(+), 60 deletions(-) + delete mode 100644 .pull_request/changes.patch + create mode 100644 sdk/.gradle/9.2.0/checksums/checksums.lock + create mode 100644 sdk/.gradle/9.2.0/checksums/md5-checksums.bin + create mode 100644 sdk/.gradle/9.2.0/checksums/sha1-checksums.bin + create mode 100644 sdk/.gradle/9.2.0/fileChanges/last-build.bin + create mode 100644 sdk/.gradle/9.2.0/fileHashes/fileHashes.bin + create mode 100644 sdk/.gradle/9.2.0/fileHashes/fileHashes.lock + rename .pull_request/PR.md => sdk/.gradle/9.2.0/gc.properties (100%) + +diff --git a/.pull_request/changes.patch b/.pull_request/changes.patch +deleted file mode 100644 +index 49e17b48233105c6672a61ca043d2781196774ea..0000000000000000000000000000000000000000 +GIT binary patch +literal 0 +HcmV?d00001 + +literal 11444 +zcmd6t+j1Mn5r(JAcd5!dtXR2#B?_cSTZ$Dsu|p+Qa#@x`Dy0jTD*{3A;E*5;P?Q;) +zPvM918~HNH|4m~!GrP0Ef{q;57O|(9K27)E-Lw49zYpArJJeCR`#O$vccJTn+i){C +zbwi!McIR$icRU%oV>faK?zKDBdFuKaJ=6I8mB#^OV598Wy&Q`zDR +zPxDgz@rbFn&Cp9U)H8G+$Yv-0N!dP=6)2c?%jIK*+N4-yq3P0jf%iS>R9~Z+G#yBr +z3-`J7Me9TT4HFBz)bpwPr?*te8u-hbWDJoz?m#O@Rn7f}Xu*rdl7dw^lnnT3HTRDl +z(Hwi}j`Zw1KP$$_mg09^(P~k$lb7Vz3@GYHDA9F2Uxm?$N4erf< +zk#s{x<@RoDS?LM{uf)IXT~+2g+#P%WJJuW`k#!k)%|R(VvGVZ>u8|2QT1m?WoGXpJ +z^yA4BhQCoRkPl4{MCHVfgTiZ##NY1EJq{1V!`dWf4r5rmk*s>EnZym&?d$0auK}ZY +zKhb;hW1b+vRYR^P?$4rucbZd57nLy>@iKe +z)S~U4G_b>PDthA^$ugBRJ$ct(bq$J=w^{#l{YHtZPB`;ebcmE7hed<&zfQc+JaBnU +zaV9Ci`cp}=A(^J$b~F7(3@FtW-RI$Jctxl&_T^3cy4zCr-j;p)zM?pA?@NkpAyc;+ +zV>SMzT4OZ|5l=smr0bI1@HF0)&~zxf4rEz8{6*2W&u;8rLQz9D>Odj}Dj(@@AS}R> +zcup;YFI>5o`sLY3zV?mxk%>mJo8PxvbN48*-Nr`^~2T$ITsR7w}#%i(*Wdwe(` +zFDnWr<{ocSLcydoiK$O7w8lnzCQRNG2B%&Nl@K*$8s*s`I|exnVSjk%C-7dzI>w*taZGn}bx?xlk8B$!KFNp`Z^1f=FP5o{PZ}M(s!=urq +zugCDv(|L{nC-RQu=jjqqV^i_CDa!OIR();jj<;xBuRfo;=UFCCdk|`GBFnOTs1)lI +z=y&$y33Q9pVkh$3P`B^Q^CNZ!_1h}fBh4FV-m%c+RF;~x(3>eEg?MgO(QUQDu16WG +zXWJyO>Vs~CYIjlfoKjmP&2kKG_IyXtbxO{JYlnGg%!Z +zw{FR_qf()syVSWf{(JYmW~?Qv|5%dIk)C@*JJm{^%Ssoj%^xb0K9ct@k|}!8^Ta!5 +zzOuJ!%w#n5unNPn0oi6xeDDue@JRRc13m9!RC`8cERQ*p4}wOqu7Bod9ZSnItt^$1 +z?I{o)R7F-V;+J>zx8-A+if7wjZHm%*Qqf_tfp~xv&xIL>x`OJ+{Y~6Q{twycnOMD1 +zHv3FZzxA<(HYa*JQxA;1yZW`3}hwK6e +zfo|g@1;GVIjTaS&HPwB^Uc|)xoH8tE!t7v=I+$U&8AbmZ#f{3Cn +z%F&@$&=<69L-#0Kl)1#SFB@3bYIzLqAe?od={`-!b72+3{>v&W7V4SM2?TCDUsKFR +zsqu)#@)AMd$@E56dlE8G)Vt(^aW)-$cb|(IL<;p@J@u2QQIsd`>&iOjYr!hy832vK +zPE%U_ky`?e?|bVEWLf$kvRmHQfcOEICz{9pPaF0((Y_~4penG+ZY&8`(XXj>uX7#S +zSBJgpKGB-(>32tWM9SyB;`&0>*C%>H6&7uVwU*m_Qt&r?qZ_x!%E)+dbDf&H@)ajU +zomEln!;lj!>Qc<_Nw3`cU>~bVCIQV11<>!AD6j`%wCdwRT;v#!ZJ>3fbBg^u=Lk`c +z_pstu%Dd+}v$F+SeX3Q!`hEYFoW{Nc6=Pi<_@h~w54`Bz|Kw|{RKH-+AW=9o1`j0j +zcc1m8-^aH)Hs$hFQV*9n)@!>U1?SX87pgO#-dHEMSl??{qMlMn^)egRsiY%H)MDO_ +z(pXn26kXO}+RSB#@mBnz}l7Bs!nn9aHNRSV($ +z2dcxayuIm3%7C1mE$Xopvv}EWLgf~G3H#W$ +zK~R}q*eJB=9wH}HO_AR!bw{d4$nW;`~O%IpYlD((NV6Li~LU6(V@oF-}LG0PccdA=_3Fv@9u%Q>%}v*D84r+eN`=cem` +zl()^@&`kCFHRf+*d|NsE|EGVN|17hun#av9d7cQiS|y(|Ct%6j&gu0mR^MkRVM?0W +zO}}Nk5p``=-`8no=Jqx2NVx>4zbScf}t1q^!@Av^FXvsuSC +z?G^W0?Q2UtlKvMl!Pk_Dj?fdPJXYx<>axV$^X~hWWI&_||4+oO=^``BLXozB2pXz@qKJm3k*z3E%{90yqJj08RiWfD^z8-~@02I02jhP5>u>6Tk`J1aJa40h|C%;Qu9o +zZNxwXVKA~wxycuE{27cD!o&;x2$g|q(cQTp;mG>t`9?n#oGZu&ZrD1*vx>Tk +zJ8ygiZh8#yUk7~cPNi3{fSXhy9{s5|XG-IDid#=09(%|;Zz@UXFtoQ^k9Zu9U9>qW$qpWmCxSBiH6ZgBwd)TIw%%%>^_C@zk88rNbM-GF7T +zz|Cb4&kj1hp)&U*#my8EFL3utewg?OPxy?xOYkqyAj4k3QiBQv9AP>+n9 +zIE+#dzonEYnZGRcDR4tC#Os!p^(dVdE(X3;e};cB4Nwy1t_NQM)fnidot)Z>mN2#d==tP;<-HSf`oPex7>?( +zyQgA!m8R7g)qV=`j>7W4o^6q{18%bf@fVYUzIWaH)T#Eah`(Ab?QL7Vnc`;Vi1*6K +zi_UF*r3vi~(`Wd^BbHh@cMfoq7{vRm!fuGZ%-;{(^85^skXXgcdXaz>OUc9}X0hNP4hG7r410;^P-gj;a}REdg%&7vfXpM}FVX +zk>^LX??#+wj)!C1BI!%Otydz>a#|I9>T@YIA8g_f=kHG2cjI+#Ews0Mg!tTjSuV`` +z>pFoO|Ax4PtxK;^>)kwfh97!ABv%ni +z_21$$;)?RI8$VhZmBag(y+K@=;n?&1lZ*gxi|-LvS)=E_R^8w+aGTwTYb}=vUY)XS +zF>p&E#8;GQpZBf1Q3u=#;jgQc3E%{90yqJj +z08RiWfD^z8-~@02I02jhP5>u>6Tk`J1aJa40h|C%04IPGzzN_4Z~{01oWTE10@_4Z +z$cH^u$RDvng@WCu+kBf88nj(>RO%%e46btrJmQL=zuf1E-^>5o2zr`-jLZ05YNgM{ +zXW1^&7r;211;(5$bmQUH((^I)7XsEQwItq`wG{^=gL*1=?g_dv8s%f35fx^U>bXa` +zCdJqaj1(WJv95$}>}9=3a@MZV+a+T2-L88U=fKE>CxW@l(&O-(9^ka^n1#1hZ+LDbmPi3o7$3=hg>y0OAe0w +zEENkzP6Zf)@pQwi@F}z4=OVS6r)^{96yFd}Ba_!Ww-5}LJlzP~_%tNUZcy0!`_}Oz +zA2NtE|7j?6(~WOCWwaKmY9EpcY3tHS+Rg(;9+5I}a1D@8I&+L%y*h5*+XpF*UFP}k +zD%LmhQHI;J!Cp +z{S_aP4LOd&R5196S;J|3zV)_2^kWaR-Y?8aHbn +zoysh3rPg^#AQ*i1bmN@DykD*+ZR?XrTDP-GYu^f}Q4$0OvxaV@y?(E%Vm_A4UdfXc +zzxzJ1zk%#kNf>4H(~V<-?Da*>Hxou5bwo|1)e!d%(kPRl3=_IhU=k;C!eV2DXy7x2 +zM)3IRS7PpGo-6j$HwWJjpo|~E;3el5$MskuS2cJhHD}Ks`s#bN +zC%iPkxDKevNla$^(owobea +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 +z)R)b}I$$5=&J9)+bpy06@vZdBlt +zE@S2GlM$w(9a2j4%ORd-%V4+cE(~wRF&HuyTj+>4!m{aWFUGChT8xK3R^Jmot(|*2 +z4_ZO+yu`ruB8%8!go9mAJ3Bv+#tIfjsN@tFY+ipP{}NdHvaqpsJP;s^wU}hXbnE!# +zq5E2$$y%{|VP!70Ou#Y$JH59DUl3ZUeeZp}y2b8&eP^2=Q}-%!=vd_^tabJP_WMX= +z2|ZJU7RGzutbiqlR{U+1xN0Wpy~kEmy|I>ueizgmYP8U5#?OYmp^s(=b)WCw><6-A +z-O~^JW-x-jn*wFSL;r8AgrVBvuB5K?yH}xg!)wFcqFCCTr{PXutO#O3BZV8?7O{*y +z6?AFoDnxn>^(mS}i%Rz-jh_AA7WEeUsB7evuCjgUmqvx=G}Tp%9;$OTa{jifJi@Al+I-#B|!93P@bhpDiOIuuGeSDo1sMsDR +z!N6kK;+bMJ;?`bAQQL6QY@=d1S&ySz%^TVQ!6P(z0<1!KW#MJx_Z3B|*CB>Ml5EdZ +z5BCaRw!iEk1uV1Oz}l`vXi0GVW)3anQ4;(5B~-cbT7zzzBh(3wWsYQ?W`xi>KA*6D +zX?2>P?S?AP-9XkKK5r-0fMqcQVli&9(^IL*3$LOTai+0|V6jy&H3_=g_X@J^$387q +z1=hB~|5hv+k+TIy)yk%M4Y}z}cv62=eQ1#YBec2>Vo@d##_~!n8LU5MIOHehmN{ej +z+C+ELu>e?S>VOp{O=umpO1R8$Ir4aX=lt%lzBN*1pVLsSg>z^N_l6p!By_;LUUpQ; +zlTAe_Ou1IY%yY8)F*)zg!8e?{QtKN0^p$)CWn@ZkzB6=*IGo +zfe|`Fs~Bz!-OvGVUVqE)BToyx(ah&Z6@5MCu**xaH^snm=Eq7HiY->}#~Oo%BWV<~ +z_g0^UP288gPo3KXo+%zK&~G_#qfmzqc(Kkowr$hpIKNcgBlwNN{@KyUJ#U_YSgt`> +z2}8v~Xtl>Fepi$mUNQCwOZ=itx6X2MyAiP5ppn9jbc;RP>1FQCvH0)#*G`UbwpO00 +z^73W82P}8!y1{L$tGuG?b!P5*s32ps+(#wa7hzN1O9X&*wiGLEnT=_|rXN}dEt$k; +zS?L=?&v2+ni1-1^V;NYAzR)LLHrC=*bzTHiiu2KT3}wYh-gNRCg{lj@4n2MYYa1=0 +zHMqXoKa{ZZM6P2tne44g2GMI2o4`813u}=~Z?TVR(_?VRmu1OSYx5NGlhzu!FmN&u +zSQmvrEV5R@SWhk1gkxo*=K47;C|bSk<~g-zn1SU5{gwkaGU)o@&&wEN{Fh91{oJRa +zm9N3-4OKly*&~4E-;R|iF2gonHf}f5Q|X>-il&ym6L>A(;aTGyRc&AeE?_OpMfje^ +zvwoeLb?r}htfjeJzFMW(O40D~x+t)2fNwREmAxgjq)rL-Wn!j-xFeNQCEnHV6{`Ny +z39MW2?gWFOr{^ZL#A)ojeZD6ppLD}y=V%!H))ihw<`o0)CNUWHqAjY;+vl2ZqGFDv +zeEq__ZO8CuBN@jR;~>^u=!pb1E_rwrfN2AB}#&Kkw*G1@@)rJE^?OvPQf-Q#V`Gs@dJY>mk3eGJzosBM+Q08S~}_tcx^@kvrv5)Yv# +zoU^VSh*b_%Q@F8vK?l5f-FDg0v(KN)X)10Yj_CwV_tzEnFTkp~jFq;GReG-RV%~>! +zZ9(&cA9*;=_{_A)Mgi*?h((e1lrYx9m)%dx8X~Q7LQUDMvPz|WF#*ZIY6TUWTxyHw +zvIV3n}KR8x5AFxOzpijIU{yA1J53sh1 +zb@0SMhmY>?gK2pK+ebCGpUw@FjDlGG{zxok!dPNG>a%`4>Rz*2bxNF0ebnjcPRPAH +z2<>s;wpFa23O%33(nsdf$vWzndu;m3UslV2SR>_FiAE8Qi8rqk%3?+gejR4HBu%{$ +zWWS0G6IYG^>oZ(spx?iChd@U>t6lf{Tejl%qQ46h+?gLA%$~Ab>j2gi@=S??ewVERcIG``yZKftgnC1b65g%g%}eZ463_i^ +z1ql%;j{@qyIQP3hou~xXGW>5|P!nN;4tSQzZfo*Wx>u~K=1fLJoDLim8%{>{8!K>q +zhZ@yc=zwS0GM3wtlh6)&+vol6QjO=~vhj@uu~tFmF?8~Tme*5@&wFNPL<4xrl(@|I +zs+~@%m4d94t1FrCP6%qQ@ci&%{lS8=v4&KDt*pqsCu9?ADW_ij4uAd1My0i9E~pEN +zbuXc38rhkXZV<-$g9XJ}hw44twz5JFbN7n>R+Y=pXX~H-e%Z#zQVy;gP^^Dm0E-H~ +z0(kTKg9XLfgr2l;`|nwoGu`j>rUdRdr|>XDB~oD{DOO`YRM{wDtUp*# +zEV5N>EX-EcA1m8A+EQR`RoR5HSofXc@MiMMI50jSa6$gOd}=7j1_P8!xKYAY2QTw~ +zj>Q61Q@Cvv>(^rJ;kqLaNRngUhb{uE9FB=M!arEh2*p1C+2U=vsJq%0 +ny(`z;dG*N>@7~m>N9aH-KUb`U35M1seyl%OP%J6Z|7raTGW@oT + +literal 0 +HcmV?d00001 + +diff --git a/sdk/.gradle/9.2.0/fileChanges/last-build.bin b/sdk/.gradle/9.2.0/fileChanges/last-build.bin +new file mode 100644 +index 0000000000000000000000000000000000000000..f76dd238ade08917e6712764a16a22005a50573d +GIT binary patch +literal 1 +IcmZPo000310RR91 + +literal 0 +HcmV?d00001 + +diff --git a/sdk/.gradle/9.2.0/fileHashes/fileHashes.bin b/sdk/.gradle/9.2.0/fileHashes/fileHashes.bin +new file mode 100644 +index 0000000000000000000000000000000000000000..0d130d8a7eec79ba14fb400171a3635eb8336244 +GIT binary patch +literal 18547 +zcmeI(p=&}x7y$5l3kxF51$nO;2O@)IgJ3WiEdCD$%?mcWU|Sf=Gq|+qKO-{W0z-0PJ02W5==FU6Ua009C72oNAZfB*pk +z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBng1%bC%ME=;nk_ieW&i*H + +literal 0 +HcmV?d00001 + +diff --git a/sdk/.gradle/9.2.0/fileHashes/fileHashes.lock b/sdk/.gradle/9.2.0/fileHashes/fileHashes.lock +new file mode 100644 +index 0000000000000000000000000000000000000000..e8052fbd407c65e6f593d885ba22d611b215b011 +GIT binary patch +literal 17 +UcmZR6A7yz|^23x(3=qH!068KAod5s; + +literal 0 +HcmV?d00001 + +diff --git a/.pull_request/PR.md b/sdk/.gradle/9.2.0/gc.properties +similarity index 100% +rename from .pull_request/PR.md +rename to sdk/.gradle/9.2.0/gc.properties +diff --git a/sdk/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/sdk/.gradle/buildOutputCleanup/buildOutputCleanup.lock +index 8b0455eae2701d3c05efc8a5b4e08511b6475a35..a6dceb2ad0666910a82b01f5d1b1f8f83a9f486d 100644 +GIT binary patch +literal 17 +UcmZR6`0hsK-WZO}3=qH!070n)`v3p{ + +literal 17 +UcmZR6`0hsK-WZO}3=qHs070k(`Tzg` + +diff --git a/sdk/.gradle/buildOutputCleanup/cache.properties b/sdk/.gradle/buildOutputCleanup/cache.properties +index b717034..56ac774 100644 +--- a/sdk/.gradle/buildOutputCleanup/cache.properties ++++ b/sdk/.gradle/buildOutputCleanup/cache.properties +@@ -1,2 +1,2 @@ +-#Wed Apr 29 10:26:23 WAT 2026 +-gradle.version=8.9 ++#Fri May 29 21:25:11 WAT 2026 ++gradle.version=9.2.0 +diff --git a/sdk/build/reports/problems/problems-report.html b/sdk/build/reports/problems/problems-report.html +index 5a43459..3ecfaa3 100644 +--- a/sdk/build/reports/problems/problems-report.html ++++ b/sdk/build/reports/problems/problems-report.html +@@ -629,13 +629,6 @@ code + .copy-button { + color: #686868; + } + +-.problem-detail { +- color: #02303A; +- font-size: 14px; +- margin: 0; +- padding: 0; +-} +- + + + +@@ -653,12 +646,12 @@ code + .copy-button { + + + +diff --git a/src/middleware/auditInterceptor.ts b/src/middleware/auditInterceptor.ts +index b2d9ccc..20cf701 100644 +--- a/src/middleware/auditInterceptor.ts ++++ b/src/middleware/auditInterceptor.ts +@@ -34,10 +34,10 @@ export const auditInterceptor = (db: Pool) => { + }; + + const query = ` +- INSERT INTO audit_logs (admin_id, action, resource, resource_id, diff, ip_address, user_agent, created_at) +- VALUES ($1, $2, $3, $4, $5, $6, $7, $8) ++ INSERT INTO audit_logs (admin_id, action, resource, resource_id, diff, ip_address, user_agent) ++ VALUES ($1, $2, $3, $4, $5, $6, $7) + `; +- ++ + await db.query(query, [ + adminId, + action, +@@ -45,8 +45,7 @@ export const auditInterceptor = (db: Pool) => { + resourceId, + JSON.stringify(diff), + req.ip, +- req.get('user-agent') || null, +- new Date().toISOString() ++ req.get('user-agent') || null + ]); + } catch (error) { + console.error('[Audit Log] Failed to save admin audit log event:', error); +-- +2.45.1.windows.1 + diff --git a/.pull_request/pr.md b/.pull_request/pr.md new file mode 100644 index 00000000..78c59445 --- /dev/null +++ b/.pull_request/pr.md @@ -0,0 +1,3 @@ +Title: Partition audit_logs by month + +This PR implements monthly partitioning for the audit_logs table and updates audit inserts to include created_at. See patches in this bundle. diff --git a/.pull_request/pr_bundle.zip b/.pull_request/pr_bundle.zip new file mode 100644 index 0000000000000000000000000000000000000000..1cbd39be545c4064091a504f118e029ab50f4e61 GIT binary patch literal 90634 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%0DW#^hdc z`&6F$5t#Q*i`Uri~a^~w5hqsptELovG#XENdA0y+Npm@H~)g)Qk8a(ymd$5Mcme~7JnTG zLIitKpFAOoZTm%(|7$9fx2~a65j4GC^Hyj?298KTx%z>TRgG%W=FslX_E zI+1JoH{T^{x!9RVqKEjIBunRWw;BG-9O(5cKW+^gifjn3i&}gspW~32tRp8e?PpQM zEjNiIMnC_irgVxSO7sH!(N=#8@mJ#dUjb*UX3wKSwFCA`gh{IPh{V6|vqkTWW8%7F z3?2e_(hlHW<91>KU&Ag#$NUf#$73uY=Udfrbki3tUMW*YuTNLj@Onz?{S4JVdind3wYu)7q#*d5wJPZAch=Z+ zFL$tL^osLsLgM(H!`cX<^y}kI!>oSalSHEsguV+r`nvj>boEka#MkwpLI@!fA{F-- z`ok^PVKfwn**v+%^)L^7VUW77#NCWmn7I`=xzPeVK`YfEPkMjVe!&(Nn|RtUM+Pme zT+DW^CMVf2$%XVwDvTBu7Bp=xx?WNy`_4XLb!BqjA%6_C$%i{PGx5`6opa(#-rPKE z5A8X0&9KV!12e6kA9dY0Xp)RqfH5>)?v2Qf%>_N4ulA1UA7DqHY%aYMKh%ETd)i?T zit%pS`M9{s{%4uJlx=jdrka0xckR|^c=#0Kv~<>GYRouj_3A5iFp2;lCAiNUuK5QkqB7x#}y=J3DPnCq$njB(~N> zJ#SB)m&B%=ylxPzER&XB^ z`dolA{8q2l)<_^Md~0WEe|Ru`UtJ;o>L=}rNSpjGU_m85qDnU|!l=?u#x;uU13_DU z%bzrXQMY}QY z03iJ4{hy7Uot*<{X<-5eg6te^E$l4p!9Z0DbC3(r(bW{_@^)A{I0Kz6Ol*N*kevn4 z-T@4<1euwDSshKlX5aodtRZaYTX$rN1r+2ap*<_+P2}NcA5W z*IQ;F7S3Scn+AtB3I89!4rJwQ@-Gc&Vs8%oZ { const log = requestId ? logger.child({ requestId }) : logger; - const MAX_BATCH_SIZE = 50; + const MAX_BATCH_SIZE = 100; if (items.length === 0) { return { success: true, results: [] }; @@ -180,7 +182,7 @@ export class MTNProvider { try { const token = await this.getAccessToken(); const batchReference = `BATCH-${randomUUID()}`; - + // MTN disbursement batch API endpoint const response = await axios.post( `${this.baseUrl}/disbursement/v2_0/batch-payout`, @@ -208,13 +210,71 @@ export class MTNProvider { const duration = Date.now() - startTime; - // Process partial success response - const responseItems = response.data?.items ?? []; + // If API provided immediate per-item results, use them. Otherwise poll. + let responseItems = response.data?.items ?? []; + const providedBatchId = response.data?.batchReference || response.data?.batchId || batchReference; + + const headers = { + Authorization: `Bearer ${token}`, + "Ocp-Apim-Subscription-Key": this.subscriptionKey, + "X-Target-Environment": this.environment, + }; + + // Poll for status if items are missing or in pending state + const needsPolling = responseItems.length === 0 || responseItems.some((ri: any) => { + const s = String(ri.status ?? "").toUpperCase(); + return s === "PENDING" || s === "IN_PROGRESS" || s === "PROCESSING"; + }); + + if (needsPolling) { + const pollUrls = [ + `${this.baseUrl}/disbursement/v2_0/batch-payout/${encodeURIComponent(providedBatchId)}`, + `${this.baseUrl}/disbursement/v2_0/batch-payout/status/${encodeURIComponent(providedBatchId)}`, + `${this.baseUrl}/disbursement/v2_0/batch-payouts/${encodeURIComponent(providedBatchId)}`, + ]; + + const maxAttempts = 10; + const delayMs = 1000; + + for (let attempt = 0; attempt < maxAttempts; attempt++) { + try { + let statusResp: any = null; + for (const url of pollUrls) { + try { + statusResp = await axios.get(url, { headers }); + if (statusResp?.data) break; + } catch (e) { + // try next candidate + } + } + + if (!statusResp?.data) { + await new Promise(r => setTimeout(r, delayMs)); + continue; + } + + responseItems = statusResp.data.items ?? statusResp.data?.results ?? responseItems; + + const allFinal = responseItems.every((ri: any) => { + const s = String(ri.status ?? "").toUpperCase(); + return s === "SUCCESSFUL" || s === "SUCCESS" || s === "FAILED" || s === "ERROR"; + }); + + if (allFinal) break; + } catch (pollErr) { + // swallow and retry + } + + await new Promise(r => setTimeout(r, delayMs)); + } + } + + // Build results for caller const results: BatchPayoutResult[] = items.map(item => { const responseItem = responseItems.find( - (r: { referenceId: string }) => r.referenceId === item.referenceId + (r: { referenceId: string }) => String(r.referenceId) === String(item.referenceId) ); - + if (!responseItem) { return { referenceId: item.referenceId, @@ -227,10 +287,10 @@ export class MTNProvider { return { referenceId: item.referenceId, success: status === "SUCCESSFUL" || status === "SUCCESS", - error: status !== "SUCCESSFUL" && status !== "SUCCESS" + error: status !== "SUCCESSFUL" && status !== "SUCCESS" ? responseItem.errorReason || responseItem.message || `Status: ${status}` : undefined, - providerReference: responseItem.financialTransactionId || responseItem.transactionId, + providerReference: responseItem.financialTransactionId || responseItem.transactionId || responseItem.transaction_id, }; }); @@ -241,7 +301,7 @@ export class MTNProvider { duration, successCount, failureCount, - batchReference + batchReference: providedBatchId, }, "MTN: Batch payout completed"); return { From 569eb6a9656a0531c8575bf564907fb2dacabe10 Mon Sep 17 00:00:00 2001 From: Martin Date: Sat, 30 May 2026 13:56:53 +0100 Subject: [PATCH 3/4] feat(provider-mtn): implement native batch disbursement --- TODO.md | 10 +++ sdk/.gradle/8.9/checksums/checksums.lock | Bin 17 -> 17 bytes sdk/.gradle/8.9/checksums/md5-checksums.bin | Bin 20097 -> 23547 bytes sdk/.gradle/8.9/checksums/sha1-checksums.bin | Bin 24545 -> 38801 bytes .../8.9/executionHistory/executionHistory.bin | Bin 0 -> 30213 bytes .../executionHistory/executionHistory.lock | Bin 0 -> 17 bytes sdk/.gradle/8.9/fileHashes/fileHashes.bin | Bin 0 -> 18647 bytes sdk/.gradle/8.9/fileHashes/fileHashes.lock | Bin 17 -> 17 bytes .../8.9/fileHashes/resourceHashesCache.bin | Bin 0 -> 18565 bytes sdk/.gradle/9.2.0/fileHashes/fileHashes.lock | Bin 17 -> 17 bytes .../buildOutputCleanup.lock | Bin 17 -> 17 bytes .../buildOutputCleanup/cache.properties | 2 +- .../com/mobilemoney/sdk/MobileMoneySDK.kt | 57 ++++++++++++++++++ .../mobilemoney/sdk/auth/AuthInterceptor.kt | 21 +++++++ .../compileKotlin/cacheable/dirty-sources.txt | 2 + .../local-state/build-history.bin | Bin 0 -> 31 bytes 16 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 TODO.md create mode 100644 sdk/.gradle/8.9/executionHistory/executionHistory.bin create mode 100644 sdk/.gradle/8.9/executionHistory/executionHistory.lock create mode 100644 sdk/.gradle/8.9/fileHashes/fileHashes.bin create mode 100644 sdk/.gradle/8.9/fileHashes/resourceHashesCache.bin create mode 100644 sdk/bin/main/com/mobilemoney/sdk/MobileMoneySDK.kt create mode 100644 sdk/bin/main/com/mobilemoney/sdk/auth/AuthInterceptor.kt create mode 100644 sdk/build/kotlin/compileKotlin/cacheable/dirty-sources.txt create mode 100644 sdk/build/kotlin/compileKotlin/local-state/build-history.bin diff --git a/TODO.md b/TODO.md new file mode 100644 index 00000000..1d1fdd96 --- /dev/null +++ b/TODO.md @@ -0,0 +1,10 @@ +# TODO - MTN native batch disbursement + +- [x] Inspect current MTN provider batch payout implementation (sendBatchPayout). +- [ ] Verify batchPayoutWorker wiring and per-item resolution flow. +- [ ] Ensure sendBatchPayout uses correct MTN batch API contract and polls until items reach terminal states. +- [ ] Ensure mapping from provider response items back to each internal transaction referenceId is correct. +- [ ] Ensure partial failure handling marks each transaction independently as Completed/Failed and persists providerReference/batch error metadata. +- [ ] Update/extend unit/integration tests if present for batch payouts. +- [ ] Run TypeScript checks and test suite. + diff --git a/sdk/.gradle/8.9/checksums/checksums.lock b/sdk/.gradle/8.9/checksums/checksums.lock index fd430f3bbe1dcc687e7988f9d41d329aeefa4960..9c94d3184df908db27a351b269724f0abe2112d1 100644 GIT binary patch literal 17 VcmZSHbY{s*waT_B3}C?c8UQ|h1+)MF literal 17 UcmZSHbY{s*waT_B3=l8{06ruITmS$7 diff --git a/sdk/.gradle/8.9/checksums/md5-checksums.bin b/sdk/.gradle/8.9/checksums/md5-checksums.bin index dac8348b3b1e03ace590f93cab5af8fea52aa072..30330ac605cef212bd4e5e55f264f551973b529a 100644 GIT binary patch literal 23547 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 C`GS)G diff --git a/sdk/.gradle/8.9/executionHistory/executionHistory.bin b/sdk/.gradle/8.9/executionHistory/executionHistory.bin new file mode 100644 index 0000000000000000000000000000000000000000..f0d32a858ab3b11fcfc74e719fe0c78baf97d65b GIT binary patch literal 30213 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

k@#jsr;rPQoKX$@Dnpdp( zaJpA^(glys8ynqSv+tZUPM-UZw*4c?xw0F7xprc#{t;%~_{DGjYx9f~N1khCx6HVC z>0Qh{;7+`9XIfWpDnJrrJTf)hBc2At{$qR$fAa6NIYWnF%NexdG_v2 zzaL;*7w?cm8^A)Q{^&uX=v#E8;UX(EjwPeJ_q((lx(+&&RL%&4k{Mj)D^M H=%fDuo&)Mj literal 0 HcmV?d00001 diff --git a/sdk/.gradle/8.9/executionHistory/executionHistory.lock b/sdk/.gradle/8.9/executionHistory/executionHistory.lock new file mode 100644 index 0000000000000000000000000000000000000000..cfd8744a1785650f3bc0ef4b961556d8bed82310 GIT binary patch literal 17 TcmZQ>)DB4gkeala0RmV6E&2n| literal 0 HcmV?d00001 diff --git a/sdk/.gradle/8.9/fileHashes/fileHashes.bin b/sdk/.gradle/8.9/fileHashes/fileHashes.bin new file mode 100644 index 0000000000000000000000000000000000000000..dc049045247eabda0100cf915f9ab52c72e9bb27 GIT binary patch literal 18647 zcmeI%u}cDB7{~E91tAGK1j9+q1`#ZjK(XOMFtDY~p+h$cz5 zxWTQpQ4oT*5ZdE~Abcg;io8frUnY|Fsb>2|h|HF3{Pt6}wqs_~`nYU){|~00 z_l4_A%Xa6F-n{x6e@Nz)~G-I`d*yz;rcQ5+WzLDGymJ=Z-WyI{W@J01^(&*KjZsJ)d0)y4WaW3)mwtteJ V^iTbJN8K-#Xbd;UIw9Uw;t$@mSq}gJ literal 0 HcmV?d00001 diff --git a/sdk/.gradle/8.9/fileHashes/fileHashes.lock b/sdk/.gradle/8.9/fileHashes/fileHashes.lock index 772a5c37fd8a27efd3107f39421e8c7650ba4d55..37ab2328003bf289f5cc808dd9ddf6c3a8442fcb 100644 GIT binary patch literal 17 TcmZQ(^4F-3yOB1V0RlJyB+vsq literal 17 ScmZQ(^4F-3yOB1V0SW*l#{)9} diff --git a/sdk/.gradle/8.9/fileHashes/resourceHashesCache.bin b/sdk/.gradle/8.9/fileHashes/resourceHashesCache.bin new file mode 100644 index 0000000000000000000000000000000000000000..f4d885d6b15999ba21ddeb544bf002f35fe7e14d GIT binary patch literal 18565 zcmeI%u}Xqb6ae6xG)bTifr2^I>Y%i`2th6l5|K38c7@$K-lZ}E1PBlyK!5-N0t5&UAV7cs z0RjXF5FkKcUj%OAAo+Bday^$Mg;5BNqnPC8>$CS|HGhy^ckZ+QgKlrw`(9lSw)J*a z&(2tHr+0P&1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAVAG%cIDm+I3 literal 0 HcmV?d00001 diff --git a/sdk/.gradle/9.2.0/fileHashes/fileHashes.lock b/sdk/.gradle/9.2.0/fileHashes/fileHashes.lock index 965c7881ab9241e5f23d03fa759490565bd6a95a..160b93f34fdd923005806ba2eaca70de5f3c3911 100644 GIT binary patch literal 17 UcmZR6A7yz|^23x(3=qH$068WEp#T5? literal 17 UcmZR6A7yz|^23x(3=qHy068QCp8x;= diff --git a/sdk/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/sdk/.gradle/buildOutputCleanup/buildOutputCleanup.lock index a6dceb2ad0666910a82b01f5d1b1f8f83a9f486d..61428d59ce5b92363911300724c5fa8e1dd45db1 100644 GIT binary patch literal 17 UcmZR6`0hsK-WZO}3=kj&071M34gdfE literal 17 UcmZR6`0hsK-WZO}3=qH!070n)`v3p{ diff --git a/sdk/.gradle/buildOutputCleanup/cache.properties b/sdk/.gradle/buildOutputCleanup/cache.properties index 56ac7749..73d62d07 100644 --- a/sdk/.gradle/buildOutputCleanup/cache.properties +++ b/sdk/.gradle/buildOutputCleanup/cache.properties @@ -1,2 +1,2 @@ -#Fri May 29 21:25:11 WAT 2026 +#Sat May 30 13:30:13 WAT 2026 gradle.version=9.2.0 diff --git a/sdk/bin/main/com/mobilemoney/sdk/MobileMoneySDK.kt b/sdk/bin/main/com/mobilemoney/sdk/MobileMoneySDK.kt new file mode 100644 index 00000000..dc2b2f88 --- /dev/null +++ b/sdk/bin/main/com/mobilemoney/sdk/MobileMoneySDK.kt @@ -0,0 +1,57 @@ +package com.mobilemoney.sdk + +import com.mobilemoney.sdk.auth.AuthInterceptor +import okhttp3.OkHttpClient +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import com.mobilemoney.sdk.api.TransactionsApi +import com.mobilemoney.sdk.models.TransactionRequest +import com.mobilemoney.sdk.models.TransactionResponse + +/** + * High-level SDK for Mobile Money integration. + * Enables integration in < 5 lines of code. + */ +class MobileMoneySDK( + private val baseUrl: String, + private val authToken: String +) { + private val okHttpClient = OkHttpClient.Builder() + .addInterceptor(AuthInterceptor(authToken)) + .build() + + private val retrofit = Retrofit.Builder() + .baseUrl(baseUrl) + .client(okHttpClient) + .addConverterFactory(GsonConverterFactory.create()) + .build() + + private val api = retrofit.create(TransactionsApi::class.java) + + /** + * Send a deposit in a single call. + */ + suspend fun deposit( + amount: Double, + phoneNumber: String, + provider: String, + stellarAddress: String, + userId: String, + notes: String? = null + ): TransactionResponse { + val request = TransactionRequest( + amount = amount, + phoneNumber = phoneNumber, + provider = TransactionRequest.Provider.valueOf(provider.uppercase()), + stellarAddress = stellarAddress, + userId = userId, + notes = notes + ) + return api.deposit(request) + } + + /** + * Get transaction status. + */ + suspend fun getStatus(transactionId: String) = api.getTransaction(transactionId) +} diff --git a/sdk/bin/main/com/mobilemoney/sdk/auth/AuthInterceptor.kt b/sdk/bin/main/com/mobilemoney/sdk/auth/AuthInterceptor.kt new file mode 100644 index 00000000..34c7137c --- /dev/null +++ b/sdk/bin/main/com/mobilemoney/sdk/auth/AuthInterceptor.kt @@ -0,0 +1,21 @@ +package com.mobilemoney.sdk.auth + +import okhttp3.Interceptor +import okhttp3.Response + +class AuthInterceptor(private val authToken: String) : Interceptor { + override fun intercept(chain: Interceptor.Chain): Response { + val originalRequest = chain.request() + + // Skip auth if already present or not needed (optional logic) + if (originalRequest.header("Authorization") != null) { + return chain.proceed(originalRequest) + } + + val requestWithAuth = originalRequest.newBuilder() + .header("Authorization", "Bearer $authToken") + .build() + + return chain.proceed(requestWithAuth) + } +} diff --git a/sdk/build/kotlin/compileKotlin/cacheable/dirty-sources.txt b/sdk/build/kotlin/compileKotlin/cacheable/dirty-sources.txt new file mode 100644 index 00000000..eeda3167 --- /dev/null +++ b/sdk/build/kotlin/compileKotlin/cacheable/dirty-sources.txt @@ -0,0 +1,2 @@ +C:\Users\DELL\Desktop\Drips\mobile-money\sdk\src\main\kotlin\com\mobilemoney\sdk\MobileMoneySDK.kt +C:\Users\DELL\Desktop\Drips\mobile-money\sdk\src\main\kotlin\com\mobilemoney\sdk\auth\AuthInterceptor.kt \ No newline at end of file diff --git a/sdk/build/kotlin/compileKotlin/local-state/build-history.bin b/sdk/build/kotlin/compileKotlin/local-state/build-history.bin new file mode 100644 index 0000000000000000000000000000000000000000..7e50d800058e2abc8d7ea4fe3f83a33e0758e525 GIT binary patch literal 31 dcmZ4UmVvcgk^ur385kJnRcw3x9m;25003)%1>XPw literal 0 HcmV?d00001 From d6d23202ba6376fcc76d82dc149fbae50ee4bfed Mon Sep 17 00:00:00 2001 From: Martin Date: Sat, 30 May 2026 15:28:59 +0100 Subject: [PATCH 4/4] feat(mtn): implement native batch payout polling + partial failure mapping --- TODO_BATCH_PAYOUT.md | 14 ++ sdk/.gradle/9.2.0/fileHashes/fileHashes.lock | Bin 17 -> 17 bytes .../__tests__/mtn.sendBatchPayout.test.ts | 218 ++++++++++++++++++ src/services/mobilemoney/providers/mtn.ts | 117 +++++++--- 4 files changed, 322 insertions(+), 27 deletions(-) create mode 100644 TODO_BATCH_PAYOUT.md create mode 100644 src/services/mobilemoney/providers/__tests__/mtn.sendBatchPayout.test.ts diff --git a/TODO_BATCH_PAYOUT.md b/TODO_BATCH_PAYOUT.md new file mode 100644 index 00000000..c8bc0701 --- /dev/null +++ b/TODO_BATCH_PAYOUT.md @@ -0,0 +1,14 @@ +# Batch payouts: MTN native batch disbursement (implementation checklist) + +- [ ] Add unit tests for MTN `sendBatchPayout()`: + - [ ] immediate results mapping (no polling) + - [ ] polling required until terminal states + - [ ] missing `referenceId` in MTN response (fallback matching) +- [ ] Improve `src/services/mobilemoney/providers/mtn.ts`: + - [ ] Make polling attempts/delay configurable via env (safe defaults) + - [ ] Improve terminal-state detection to not stop too early + - [ ] Add fallback matching if MTN response items don’t include `referenceId` + - [ ] Normalize providerReference extraction across possible response shapes +- [ ] Run tests and TypeScript checks +- [ ] Ensure batch worker per-item resolution still updates transactions independently + diff --git a/sdk/.gradle/9.2.0/fileHashes/fileHashes.lock b/sdk/.gradle/9.2.0/fileHashes/fileHashes.lock index 160b93f34fdd923005806ba2eaca70de5f3c3911..df1406ac7c4998818dc5ac7b265569acfba8d7d3 100644 GIT binary patch literal 17 UcmZR6A7yz|^23x(3=qHx068cGqW}N^ literal 17 UcmZR6A7yz|^23x(3=qH$068WEp#T5? diff --git a/src/services/mobilemoney/providers/__tests__/mtn.sendBatchPayout.test.ts b/src/services/mobilemoney/providers/__tests__/mtn.sendBatchPayout.test.ts new file mode 100644 index 00000000..5fb6041a --- /dev/null +++ b/src/services/mobilemoney/providers/__tests__/mtn.sendBatchPayout.test.ts @@ -0,0 +1,218 @@ +import axios from "axios"; +import { MTNProvider } from "../mtn"; + +jest.mock("axios"); + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const axiosMock = axios as any; + +describe("MTNProvider.sendBatchPayout", () => { + + + beforeEach(() => { + jest.resetAllMocks(); + process.env = { ...env }; + process.env.MTN_API_KEY = "k"; + process.env.MTN_API_SECRET = "s"; + process.env.MTN_SUBSCRIPTION_KEY = "sub"; + process.env.MTN_TARGET_ENVIRONMENT = "sandbox"; + + process.env.MTN_BATCH_PAYOUT_MAX_ATTEMPTS = "3"; + process.env.MTN_BATCH_PAYOUT_POLL_DELAY_MS = "1"; + + // Token request + axiosMock.post.mockImplementation(async (url: any) => { + if (String(url).includes("/collection/token/")) { + return { data: { access_token: "token" } } as any; + } + throw new Error(`Unexpected axios.post url: ${String(url)}`); + }); + }); + + afterAll(() => { + process.env = env; + }); + + it("maps immediate per-item results without polling", async () => { + const provider = new MTNProvider(); + + const batchItems = [ + { referenceId: "tx1", phoneNumber: "+237670000001", amount: "100" }, + { referenceId: "tx2", phoneNumber: "+237670000002", amount: "200" }, + ]; + + axiosMock.post.mockImplementation(async (url: any, body?: any) => { + if (String(url).includes("/collection/token/")) { + return { data: { access_token: "token" } } as any; + } + if (String(url).includes("/disbursement/v2_0/batch-payout")) { + return { + data: { + batchReference: "BATCH-1", + items: [ + { + referenceId: "tx1", + status: "SUCCESSFUL", + transactionId: "pmt-1", + }, + { + referenceId: "tx2", + status: "FAILED", + errorReason: "insufficient_funds", + transactionId: "pmt-2", + }, + ], + }, + status: 202, + } as any; + } + throw new Error(`Unexpected axios.post url: ${String(url)}`); + }); + + axiosMock.get.mockResolvedValue({ data: {} } as any); + + const res = await provider.sendBatchPayout(batchItems); + + expect(res.success).toBe(true); + expect(res.results).toEqual([ + { referenceId: "tx1", success: true, providerReference: "pmt-1" }, + { + referenceId: "tx2", + success: false, + error: "insufficient_funds", + providerReference: "pmt-2", + }, + ]); + + // No polling requests expected + expect(axiosMock.get).not.toHaveBeenCalled(); + }); + + it("polls until all items reach terminal states", async () => { + const provider = new MTNProvider(); + + const batchItems = [ + { referenceId: "tx1", phoneNumber: "+237670000001", amount: "100" }, + { referenceId: "tx2", phoneNumber: "+237670000002", amount: "200" }, + ]; + + axiosMock.post.mockImplementation(async (url: any) => { + if (String(url).includes("/collection/token/")) { + return { data: { access_token: "token" } } as any; + } + if (String(url).includes("/disbursement/v2_0/batch-payout")) { + return { + data: { + batchReference: "BATCH-2", + items: [ + { referenceId: "tx1", status: "PENDING" }, + { referenceId: "tx2", status: "PENDING" }, + ], + }, + status: 202, + } as any; + } + throw new Error(`Unexpected axios.post url: ${String(url)}`); + }); + + // First poll: still pending + // Second poll: terminal + axiosMock.get + .mockResolvedValueOnce({ + data: { + items: [ + { referenceId: "tx1", status: "IN_PROGRESS" }, + { referenceId: "tx2", status: "PENDING" }, + ], + }, + } as any) + .mockResolvedValueOnce({ + data: { + items: [ + { + referenceId: "tx1", + status: "SUCCESSFUL", + financialTransactionId: "ft-1", + }, + { + referenceId: "tx2", + status: "FAILED", + errorReason: "blocked", + financialTransactionId: "ft-2", + }, + ], + }, + } as any); + + const res = await provider.sendBatchPayout(batchItems); + + expect(res.success).toBe(true); + expect(res.results[0]).toMatchObject({ + referenceId: "tx1", + success: true, + providerReference: "ft-1", + }); + expect(res.results[1]).toMatchObject({ + referenceId: "tx2", + success: false, + error: "blocked", + providerReference: "ft-2", + }); + + expect(axiosMock.get).toHaveBeenCalled(); + }); + + it("falls back to phone+amount matching when referenceId is missing in MTN response", async () => { + const provider = new MTNProvider(); + + const batchItems = [ + { referenceId: "tx1", phoneNumber: "+237670000001", amount: "100" }, + { referenceId: "tx2", phoneNumber: "+237670000002", amount: "200" }, + ]; + + axiosMock.post.mockImplementation(async (url: any) => { + if (String(url).includes("/collection/token/")) { + return { data: { access_token: "token" } } as any; + } + if (String(url).includes("/disbursement/v2_0/batch-payout")) { + return { + data: { + batchReference: "BATCH-3", + items: [ + { + status: "SUCCESSFUL", + phoneNumber: "+237670000001", + amount: "100", + transactionId: "pmt-1", + }, + { + status: "FAILED", + phoneNumber: "+237670000002", + amount: "200", + errorReason: "daily_limit", + transactionId: "pmt-2", + }, + ], + }, + status: 202, + } as any; + } + throw new Error(`Unexpected axios.post url: ${String(url)}`); + }); + + axiosMock.get.mockResolvedValue({ data: {} } as any); + + const res = await provider.sendBatchPayout(batchItems); + + expect(res.results).toEqual([ + { referenceId: "tx1", success: true, providerReference: "pmt-1" }, + { + referenceId: "tx2", + success: false, + error: "daily_limit", + providerReference: "pmt-2", + }, + ]); + }); +}); + diff --git a/src/services/mobilemoney/providers/mtn.ts b/src/services/mobilemoney/providers/mtn.ts index 4dcba4a3..acbdc8d2 100644 --- a/src/services/mobilemoney/providers/mtn.ts +++ b/src/services/mobilemoney/providers/mtn.ts @@ -220,12 +220,34 @@ export class MTNProvider { "X-Target-Environment": this.environment, }; - // Poll for status if items are missing or in pending state - const needsPolling = responseItems.length === 0 || responseItems.some((ri: any) => { - const s = String(ri.status ?? "").toUpperCase(); - return s === "PENDING" || s === "IN_PROGRESS" || s === "PROCESSING"; + const terminalStatuses = new Set([ + "SUCCESSFUL", + "SUCCESS", + "FAILED", + "ERROR", + "REJECTED", + "CANCELLED", + ]); + const pendingStatuses = new Set([ + "PENDING", + "IN_PROGRESS", + "PROCESSING", + "SUBMITTED", + "QUEUED", + ]); + + const responseHasReferenceIds = responseItems.some((ri: any) => { + return ri?.referenceId !== undefined && ri?.referenceId !== null; }); + // Poll for status if items are missing or any item appears not-terminal + const needsPolling = + responseItems.length === 0 || + responseItems.some((ri: any) => { + const s = String(ri.status ?? "").toUpperCase(); + return pendingStatuses.has(s) || !terminalStatuses.has(s); + }); + if (needsPolling) { const pollUrls = [ `${this.baseUrl}/disbursement/v2_0/batch-payout/${encodeURIComponent(providedBatchId)}`, @@ -233,49 +255,86 @@ export class MTNProvider { `${this.baseUrl}/disbursement/v2_0/batch-payouts/${encodeURIComponent(providedBatchId)}`, ]; - const maxAttempts = 10; - const delayMs = 1000; + const maxAttempts = Number.parseInt( + process.env.MTN_BATCH_PAYOUT_MAX_ATTEMPTS || "10", + 10, + + ); + const delayMs = Number.parseInt( + process.env.MTN_BATCH_PAYOUT_POLL_DELAY_MS || "1000", + 10, + ); + + const safeMaxAttempts = Number.isFinite(maxAttempts) ? maxAttempts : 10; + const safeDelayMs = Number.isFinite(delayMs) ? delayMs : 1000; - for (let attempt = 0; attempt < maxAttempts; attempt++) { + for (let attempt = 0; attempt < safeMaxAttempts; attempt++) { try { let statusResp: any = null; for (const url of pollUrls) { try { statusResp = await axios.get(url, { headers }); if (statusResp?.data) break; - } catch (e) { + } catch { // try next candidate } } if (!statusResp?.data) { - await new Promise(r => setTimeout(r, delayMs)); + await new Promise(r => setTimeout(r, safeDelayMs)); continue; } - responseItems = statusResp.data.items ?? statusResp.data?.results ?? responseItems; + responseItems = + statusResp.data.items ?? + statusResp.data?.results ?? + responseItems; - const allFinal = responseItems.every((ri: any) => { - const s = String(ri.status ?? "").toUpperCase(); - return s === "SUCCESSFUL" || s === "SUCCESS" || s === "FAILED" || s === "ERROR"; - }); + const allFinal = responseItems.length > 0 && + responseItems.every((ri: any) => { + const s = String(ri.status ?? "").toUpperCase(); + return terminalStatuses.has(s); + }); if (allFinal) break; - } catch (pollErr) { + } catch { // swallow and retry } - await new Promise(r => setTimeout(r, delayMs)); + await new Promise(r => setTimeout(r, safeDelayMs)); } } - // Build results for caller - const results: BatchPayoutResult[] = items.map(item => { - const responseItem = responseItems.find( - (r: { referenceId: string }) => String(r.referenceId) === String(item.referenceId) + const getProviderReference = (ri: any): string | undefined => { + return ( + ri?.financialTransactionId || + ri?.providerReference || + ri?.transactionId || + ri?.transaction_id || + ri?.transaction_id_response ); + }; - if (!responseItem) { + // Build results for caller + const results: BatchPayoutResult[] = items.map(item => { + // Prefer matching on echoed referenceId, but fall back to phone+amount if absent. + const responseItem = responseHasReferenceIds + ? responseItems.find( + (r: any) => r?.referenceId !== undefined && String(r.referenceId) === String(item.referenceId), + ) + : undefined; + + const fallbackItem = !responseItem + ? responseItems.find((ri: any) => { + const phone = String(ri?.phoneNumber ?? ri?.payee?.partyId ?? ""); + const amt = String(ri?.amount ?? ""); + return phone === String(item.phoneNumber) && amt === String(item.amount); + }) + : undefined; + + const matched = responseItem ?? fallbackItem; + + if (!matched) { return { referenceId: item.referenceId, success: false, @@ -283,17 +342,21 @@ export class MTNProvider { }; } - const status = String(responseItem.status ?? "").toUpperCase(); + const status = String(matched.status ?? "").toUpperCase(); + const providerReference = getProviderReference(matched); + const success = status === "SUCCESSFUL" || status === "SUCCESS"; + return { referenceId: item.referenceId, - success: status === "SUCCESSFUL" || status === "SUCCESS", - error: status !== "SUCCESSFUL" && status !== "SUCCESS" - ? responseItem.errorReason || responseItem.message || `Status: ${status}` - : undefined, - providerReference: responseItem.financialTransactionId || responseItem.transactionId || responseItem.transaction_id, + success, + error: success + ? undefined + : matched.errorReason || matched.message || `Status: ${status}`, + providerReference, }; }); + const successCount = results.filter(r => r.success).length; const failureCount = results.filter(r => !r.success).length;