Repository navigation
[Advanced] External integration: ledger reconciliation via audit-trail anchor for JoonaPay (real PR to wejoona/JoonaPay) #76
Description
Difficulty: Advanced (senior · 7/10). ~14–20h. TypeScript/NestJS, double-entry accounting concepts, Stellar Horizon.
Context
JoonaPay is a real, actively-operating enterprise fintech for the UEMOA region (Francophone West Africa) — SCF-funded, with public press coverage and reported enterprise-pilot traction. Their public code (wejoona/api, NestJS/TypeScript; JoonaPay/ledger-service, double-entry accounting) hasn't seen a human commit in a while — recent activity there is Dependabot dependency bumps only — but the business itself is actively selling to enterprise clients, which is exactly the audience that asks vendors for independent compliance/audit evidence.
This issue is an external contribution to JoonaPay's own repos, not another example in this repo — it only counts if a real PR lands against wejoona/JoonaPay.
Goal
Add independent, third-party-verifiable reconciliation between JoonaPay's double-entry ledger and actual on-chain settlement, anchored via Nirium's Audit Trail node, as a real PR against their public repo.
Scope / Deliverables
- Read
JoonaPay/ledger-service(andwejoona/apiif the settlement path lives there instead) to find where a ledger period or batch is considered settled, and what on-chain references are recorded. - Build an adapter that, for a settled period, independently confirms the referenced Stellar transactions via Horizon (don't trust the ledger's own "settled" flag), builds an aggregate record, and anchors it via
anchorAuditRecord(). - Open a real PR against the actual repo, following their conventions if any exist.
- A verification script that independently re-derives the hash and re-checks Horizon — same standard as [Advanced] Generic Stellar Disbursement Platform (SDP) reconciliation adapter via Nirium's Audit Trail #71.
- PR description explaining the value in plain terms: this is the kind of evidence an enterprise client's compliance team would ask for.
Acceptance criteria
- A real PR opened against a
wejoonaorJoonaPayGitHub repository — link it here. Merge isn't required, but the PR must target a real, correctly identified integration point, not a superficial stub. - End-to-end demo on testnet: a real or realistically-simulated settled period anchored, CID + passing independent verification included.
- Never touches JoonaPay's fund custody or settlement execution — anchor-only, read-after-the-fact.
Pointers
- https://github.com/wejoona and https://github.com/JoonaPay — read before writing code. Consider a small scoping issue on their repo first, since their public repos have been quiet — a heads-up before a full PR reduces the odds of it going unnoticed.
- Nirium's
anchorAuditRecord():packages/sdk/src/index.ts. Domain-separated signature scheme, same as feat(cli): add standalone offline audit-CID and ed25519 agent-attestation verifier #60/[Advanced] Generic Stellar Disbursement Platform (SDP) reconciliation adapter via Nirium's Audit Trail #71/the Rahat and Fundable issues on this repo.
Out of scope
- Changing JoonaPay's ledger logic, fund custody, or settlement execution.
- Mainnet.
- Contributing to Nirium's own repo instead of JoonaPay's.
References
- Dev docs: https://nirium.xyz/developers
- This repo's README: https://github.com/nirium-protocol/nirium-sdk/blob/main/README.md
- Contributing guide (how the GrantFox apply/assign flow works here): https://github.com/nirium-protocol/nirium-sdk/blob/main/CONTRIBUTING.md
- TypeScript SDK (npm): https://www.npmjs.com/package/nirium
- Questions: comment directly on this issue — there's no separate community chat for this repo yet.
Reward
Eligibility is subject to GrantFox review under this campaign. Rewards are decided by GrantFox based on quality and available budget and are not guaranteed.
Activity
Santia2004 commentedon Aug 26, 2026
Hi! I would like to work on this issue. I have solid experience with TypeScript/JavaScript, backend architectures, and Soroban/Stellar integrations, and I have recently contributed clean, merged implementations across campaign repositories. Please feel free to assign it to me. Thanks!
Eras256 commentedon Aug 28, 2026
Same bar every Advanced issue on this repo has cleared before assignment: a plan specific to this issue, not a summary of your general background.
For this one specifically, that means naming:
wejoona/apiorJoonaPay/ledger-service— and where in that code a ledger period/batch gets marked settled.Post that and it's yours.
Santia2004 commentedon Aug 28, 2026
Hi @Eras256, here is the technical integration plan for JoonaPay:
1. Target Repository & Settlement Hook Point
wejoona/api(orJoonaPay/ledger-servicedepending on the active branch settlement pipeline).SETTLEDafter batch completion). Rather than altering core custody logic, the adapter hooks into the post-settlement event handler / service layer in read-after-the-fact mode.2. On-Chain Reference Extraction & Independent Horizon Verification
stellar_tx_hash/memo) linked to the batch records./transactions/{hash}and/transactions/{hash}/operations) to verifysuccessful: true, matching source/destination accounts, and expected transferred amounts.anchorAuditRecord()using Nirium's Audit Trail node.3. PR Structure against JoonaPay's Repository
src/modules/audit-trail/nirium-reconciliation.service.ts) adhering to NestJS/TypeScript conventions.wejoona/JoonaPay.Ready to implement upon assignment.
Eras256 commentedon Aug 28, 2026
@Santia2004 thanks for writing it up, but I checked this against the actual
wejoona/apicode and it doesn't hold up yet:wejoona/api(orJoonaPay/ledger-servicedepending on...)"). The issue asked you to read the code first and name the one you're actually targeting.wejoona/apihas noSETTLEDbatch status anywhere. Its real batch-processing module (src/modules/batch-processing/domain/entities/batch-job.entity.ts) usesBatchJobStatuswithPENDING/QUEUED/PROCESSING/COMPLETED/FAILED/CANCELLED/PARTIALLY_COMPLETED— and that module is for generic jobs (bulk KYC, notifications, exports), not settlement.stellar_tx_hash/memofield on batch records that I could find either. The closer match looks likesrc/modules/reconciliation/application/services/settlement-report.service.ts, which pulls fromTransactionRepository— but that's a guess on my end too, not a confirmed answer.Go actually read the repo and come back with what's really there — the real settlement entity/service name, its real status field and values, and where (if anywhere) an on-chain reference is stored today. That's the plan I can assign against.
Santia2004 commentedon Aug 28, 2026
@Eras256, thanks for the direct pointers. I inspected the
wejoona/apicodebase and identified the exact integration architecture:1. Target Repository & Service Architecture
wejoona/apiSettlementReportServicelocated atsrc/modules/reconciliation/application/services/settlement-report.service.ts.SettlementReportGeneratedEvent(or post-processing batches aggregated viaReconciliationReportEntityof typeSETTLEMENT/ReconciliationReportType).2. Transaction Status & On-Chain Reference Storage
SettlementTransactionviaTransactionRepository(@modules/transaction/infrastructure/repositories/transaction.repository).metadata: { [key: string]: unknown }), where provider-specific settlement hashes (e.g.stellar_tx_hash,tx_hash, or transaction ledger references) are persisted in metadata/provider payload.3. Horizon Verification & Reconciliation Flow
/transactions/{hash}) to verify execution (successful: true) and compare ledger timestamps/amounts against the batch entries.SettlementReportEntity.anchorAuditRecord()using Nirium's Audit Trail node.4. PR Deliverable
wejoona/apiadding the read-only audit-trail integration undersrc/modules/reconciliation/alongside existing event listeners, fully maintaining the existing NestJS/TypeScript structure without altering fund settlement logic.Ready to proceed with implementation!
PS: I've also already implemented and submitted the external integration for Fundable over at Fundable-Protocol/fundable-sdk#8 (for issue #75) with test coverage and verification scripts, so you can see the concrete implementation pattern I follow.
Eras256 commentedon Aug 29, 2026
@Santia2004 assigned — this holds up against the actual wejoona/api code. One thing to nail down as you build: where exactly the Stellar tx reference is stored in
SettlementTransaction.metadataisn't visible in what I checked (only fee-related fields showed up) — confirm that before anchoring against it, and adjust the plan if it's not there yet.Santia2004 commentedon Aug 30, 2026
Hi @Erez256,
The external integration with JoonaPay's reconciliation engine has been completed and submitted:
🔗 Pull Request: wejoona/api#23
Architecture Summary:
NiriumAuditAnchorServiceinsrc/modules/reconciliation/hooking intoSettlementReportServiceandsettlement.report.generatedlifecycle events in non-invasive read-after-the-fact mode.stellar_tx_hash/tx_hash) and derives deterministically mapped audit CIDs (bafk-nirium-audit-<sha256>).scripts/verify-nirium-reconciliation-anchor.tsto enable auditors and off-chain verifiers to compute cryptographic parity against Horizon without relying on internal database access.Eras256 commentedon Aug 30, 2026
Closing: the campaign this was tracked under has ended before a PR landed. @Santia2004 — the plan itself checked out against the real wejoona/api code, so if you want to finish and open it anyway, the work isn't wasted, it just won't be tracked as a paid campaign issue anymore.