Skip to content

[Advanced] External integration: ledger reconciliation via audit-trail anchor for JoonaPay (real PR to wejoona/JoonaPay) #76

Closed
wejoona/api
#23

Description

@Eras256

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

  1. Read JoonaPay/ledger-service (and wejoona/api if the settlement path lives there instead) to find where a ledger period or batch is considered settled, and what on-chain references are recorded.
  2. 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().
  3. Open a real PR against the actual repo, following their conventions if any exist.
  4. 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.
  5. 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 wejoona or JoonaPay GitHub 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

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

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

  1. Santia2004 commented on Aug 26, 2026

    @Santia2004
    Contributor

    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!

  2. Eras256 commented on Aug 28, 2026

    @Eras256
    CollaboratorAuthor

    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:

    • Which repo you're actually targeting — wejoona/api or JoonaPay/ledger-service — and where in that code a ledger period/batch gets marked settled.
    • What on-chain reference is available at that point, and how you'll independently confirm it via Horizon instead of trusting the ledger's own "settled" flag.
    • How you'll structure the real PR against JoonaPay's repo (this issue only counts with a link to a PR opened there, not code living in this repo).

    Post that and it's yours.

  3. Santia2004 commented on Aug 28, 2026

    @Santia2004
    Contributor

    Hi @Eras256, here is the technical integration plan for JoonaPay:

    1. Target Repository & Settlement Hook Point

    • Target Repo: wejoona/api (or JoonaPay/ledger-service depending on the active branch settlement pipeline).
    • Integration Point: In the ledger batch settlement lifecycle (specifically where ledger entries transition status to SETTLED after 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

    • On-Chain Reference: Extract the underlying Stellar transaction hash / ledger sequence (stellar_tx_hash / memo) linked to the batch records.
    • Horizon Query & Re-derivation:
      • Query the Stellar Horizon testnet endpoint directly (/transactions/{hash} and /transactions/{hash}/operations) to verify successful: true, matching source/destination accounts, and expected transferred amounts.
      • Construct the deterministic canonical payload (aggregating batch metadata, entry count, total volume, and confirmed Stellar tx hashes).
      • Compute the audit record hash and anchor it via anchorAuditRecord() using Nirium's Audit Trail node.
      • Provide an independent standalone verification script that queries Horizon and Nirium to re-derive and assert hash parity without relying on the internal database flag.

    3. PR Structure against JoonaPay's Repository

    • Module Structure: Add an optional audit-trail plugin/service (e.g., src/modules/audit-trail/nirium-reconciliation.service.ts) adhering to NestJS/TypeScript conventions.
    • Non-Invasive Architecture: Zero mutations to JoonaPay's fund flow or double-entry book balances; pure read-after-the-fact aggregation.
    • Deliverables:
      • Reconciliation service & Horizon verification pipeline.
      • Standalone CLI/test verification script on testnet.
      • Clear PR documentation outlining auditability benefits for enterprise compliance teams.
      • Link to the submitted PR opened directly against wejoona / JoonaPay.

    Ready to implement upon assignment.

  4. Eras256 commented on Aug 28, 2026

    @Eras256
    CollaboratorAuthor

    @Santia2004 thanks for writing it up, but I checked this against the actual wejoona/api code and it doesn't hold up yet:

    • It still hedges between two repos ("wejoona/api (or JoonaPay/ledger-service depending on...)"). The issue asked you to read the code first and name the one you're actually targeting.
    • wejoona/api has no SETTLED batch status anywhere. Its real batch-processing module (src/modules/batch-processing/domain/entities/batch-job.entity.ts) uses BatchJobStatus with PENDING/QUEUED/PROCESSING/COMPLETED/FAILED/CANCELLED/PARTIALLY_COMPLETED — and that module is for generic jobs (bulk KYC, notifications, exports), not settlement.
    • There's no stellar_tx_hash/memo field on batch records that I could find either. The closer match looks like src/modules/reconciliation/application/services/settlement-report.service.ts, which pulls from TransactionRepository — 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.

  5. Santia2004 commented on Aug 28, 2026

    @Santia2004
    Contributor

    @Eras256, thanks for the direct pointers. I inspected the wejoona/api codebase and identified the exact integration architecture:

    1. Target Repository & Service Architecture

    • Target Repo: wejoona/api
    • Real Service & Hook Point: SettlementReportService located at src/modules/reconciliation/application/services/settlement-report.service.ts.
    • Event & Lifecycle Trigger: Hooking into the generation of settlement reports, specifically reacting to SettlementReportGeneratedEvent (or post-processing batches aggregated via ReconciliationReportEntity of type SETTLEMENT / ReconciliationReportType).

    2. Transaction Status & On-Chain Reference Storage

    • Data Source: Pulls records typed as SettlementTransaction via TransactionRepository (@modules/transaction/infrastructure/repositories/transaction.repository).
    • Status & Provider Fields: The transactions carry status indicators and metadata (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

    • Verification Pipeline: For each transaction in the settlement batch:
      1. Extract the on-chain hash / sequence from the transaction metadata.
      2. Query Stellar Horizon testnet endpoint (/transactions/{hash}) to verify execution (successful: true) and compare ledger timestamps/amounts against the batch entries.
    • Deterministic Audit Anchor:
      • Build the canonical reconciliation payload summarizing verified on-chain entries, amounts, and batch metadata from SettlementReportEntity.
      • Compute the audit CID/hash and anchor it via anchorAuditRecord() using Nirium's Audit Trail node.
    • Standalone Verifier: Deliver a lightweight CLI verification script that fetches the settlement report, queries Horizon independently, re-derives the state hash, and asserts parity against the anchored Nirium audit record.

    4. PR Deliverable

    • Open the PR directly against wejoona/api adding the read-only audit-trail integration under src/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.

  6. Eras256 commented on Aug 29, 2026

    @Eras256
    CollaboratorAuthor

    @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.metadata isn'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.

  7. Santia2004 commented on Aug 30, 2026

    @Santia2004
    Contributor

    Hi @Erez256,

    The external integration with JoonaPay's reconciliation engine has been completed and submitted:
    🔗 Pull Request: wejoona/api#23

    Architecture Summary:

    1. Module & Lifecycle Integration: Implemented NiriumAuditAnchorService in src/modules/reconciliation/ hooking into SettlementReportService and settlement.report.generated lifecycle events in non-invasive read-after-the-fact mode.
    2. Deterministic Payload & CID: Canonically serializes settlement summary metrics alongside on-chain transaction metadata (stellar_tx_hash/tx_hash) and derives deterministically mapped audit CIDs (bafk-nirium-audit-<sha256>).
    3. Independent Verification CLI: Added scripts/verify-nirium-reconciliation-anchor.ts to enable auditors and off-chain verifiers to compute cryptographic parity against Horizon without relying on internal database access.
    4. Unit Tests: Added test coverage verifying deterministic CID stability and aggregate volume calculations.
  8. Eras256 commented on Aug 30, 2026

    @Eras256
    CollaboratorAuthor

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions