Skip to content

feat/parser-logic-addition-for-razorpay-ccavenue-pg#1347

Open
RahulGupta0706 wants to merge 2 commits into
mainfrom
feat/parser-logic-addition-for-razorpay-ccavenue-pg
Open

feat/parser-logic-addition-for-razorpay-ccavenue-pg#1347
RahulGupta0706 wants to merge 2 commits into
mainfrom
feat/parser-logic-addition-for-razorpay-ccavenue-pg

Conversation

@RahulGupta0706

@RahulGupta0706 RahulGupta0706 commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Type of Change

  • Bugfix
  • New feature
  • Enhancement
  • Refactoring
  • Dependency updates

Description

Additional Changes

  • This PR modifies the database schema (database migration added)
  • This PR modifies dhall configs/environment variables

Motivation and Context

How did you test it?

billdesk_settlement_report_sample.csv.parsed.txt

Checklist

  • I formatted the code and addressed linter errors ./dev/format-all-files.sh
  • I reviewed submitted code
  • I added unit tests for my changes where possible
  • I added a CHANGELOG entry if applicable

Summary by CodeRabbit

  • New Features
    • Added settlement CSV parsing and reporting for CCAvenue and Razorpay.
    • Expanded settlement provider support with SAP token retrieval and journal posting.
    • Introduced richer settlement metadata (additional bank/card and PG identifiers) and broadened payment method coverage.
  • Bug Fixes
    • Improved BillDesk CSV mapping for settled/refund/chargeback rows, including more accurate ID/method derivation and safer card field handling.
  • Refactor
    • Consolidated shared parsing helpers across settlement providers.
  • Documentation
    • Clarified that payout parsing is not supported for CCAvenue and Razorpay.

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2774e6a4-e9cc-448e-816a-0fee404b62d2

📥 Commits

Reviewing files that changed from the base of the PR and between eb9b086 and 918020d.

📒 Files selected for processing (8)
  • lib/mobility-core/mobility-core.cabal
  • lib/mobility-core/src/Kernel/External/SAP/API.hs
  • lib/mobility-core/src/Kernel/External/SAP/Config.hs
  • lib/mobility-core/src/Kernel/External/SAP/Flow.hs
  • lib/mobility-core/src/Kernel/External/SAP/Interface.hs
  • lib/mobility-core/src/Kernel/External/SAP/Types.hs
  • lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentParser.hs
🚧 Files skipped from review as they are similar to previous changes (8)
  • lib/mobility-core/src/Kernel/External/SAP/API.hs
  • lib/mobility-core/src/Kernel/External/SAP/Flow.hs
  • lib/mobility-core/src/Kernel/External/SAP/Config.hs
  • lib/mobility-core/mobility-core.cabal
  • lib/mobility-core/src/Kernel/External/SAP/Interface.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/SAP/Types.hs
  • lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs

Walkthrough

This PR adds a complete SAP integration module (API, config, flow, types) and expands settlement processing with new CCAvenue and Razorpay parsers, shared utilities, extended domain types, BillDesk updates, and existing provider migration.

Changes

SAP Integration

Layer / File(s) Summary
SAP types and error handling
lib/mobility-core/src/Kernel/External/SAP/Types.hs
SAP token response, journal request/response structures, XML rendering (headers, items) and parsing (extract headers from XML response), and error type with HTTP status classification.
SAP Servant API definitions
lib/mobility-core/src/Kernel/External/SAP/API.hs
OAuth token endpoint and journal post endpoint with custom XML media type support for request/response handling.
SAP configuration
lib/mobility-core/src/Kernel/External/SAP/Config.hs
Account and service configuration record types with encrypted credentials, base URLs, required identifiers, and account mapping.
SAP orchestration and interface
lib/mobility-core/src/Kernel/External/SAP/Flow.hs, lib/mobility-core/src/Kernel/External/SAP/Interface.hs
Flow module implements token fetching (decrypt credentials, OAuth headers) and journal posting (render XML, Bearer token); interface re-exports config and types while delegating to flow.

Settlement Payment Provider Expansion

Layer / File(s) Summary
Core domain types and library exports
lib/mobility-core/mobility-core.cabal, lib/mobility-core/src/Kernel/External/Settlement/Interface/Types.hs, lib/mobility-core/src/Kernel/External/Settlement/Types.hs
PaymentMethodType extended with new variants; PaymentSettlementReport adds optional fields (pgApprovalCode, pgRequestId, bankId, cardNumber); FulfillmentInstrument renames NEFT to FI_NEFT; SettlementService adds Razorpay and CCAvenue; SettlementServiceConfig adds bankCode field; new settlement and parser modules are exposed.
Shared parser utilities
lib/mobility-core/src/Kernel/External/Settlement/Utils/ParserUtils.hs
New module exports nonEmpty', parseAmount, and parseDateTime for reuse across settlement parsers.
CCAvenue settlement parsing
lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentTypes.hs, lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs
Payout, transaction, and merged row types added with CSV/JSON instances; parseCCAvenueCsv splits CSV into payout and transaction sections, merges rows by index, and builds PaymentSettlementReport with derived transaction type, settlement type, amounts, and payment methods.
Razorpay settlement parsing
lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentTypes.hs, lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentParser.hs
Razorpay row type with CSV/JSON instances; parseRazorpayCsv decodes named CSV, builds PaymentSettlementReport with normalized transaction type, payment method, parsed dates and amounts, and original row JSON.
BillDesk row and parser updates
lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentTypes.hs, lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentParser.hs
BillDeskSettledRow gains payRef1 and payRef2 fields; settled/refund/chargeback mappings updated to derive payment method from paymode codes, set bankId from row field, and swap refund identifiers.
Existing provider parser migration
lib/mobility-core/src/Kernel/External/Settlement/HyperPG/MerchantPaymentParser.hs, lib/mobility-core/src/Kernel/External/Settlement/HyperPG/PaymentParser.hs, lib/mobility-core/src/Kernel/External/Settlement/HyperPG/PayoutParser.hs, lib/mobility-core/src/Kernel/External/Settlement/YesBiz/PaymentParser.hs
HyperPG and YesBiz parsers switch to shared parsing helpers, remove local implementations, populate new optional report fields, and update fulfillment-instrument mapping to FI_NEFT.
Settlement interface dispatch
lib/mobility-core/src/Kernel/External/Settlement/Interface.hs
parsePaymentSettlementCsv routes Razorpay and CCAvenue to their parsers; parsePayoutSettlementCsv returns unsupported errors for those services.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • khuzema786
  • Vignesh-772
  • bytedex

Poem

🐰 SAP ledgers now speak true,
New gateways parse what's fresh and new.
CCAvenue, Razorpay in the fold,
Shared helpers trim the logic bold.
BillDesk refined with careful care,
Settlement streams flow clean and fair. ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat/parser-logic-addition-for-razorpay-ccavenue-pg' partially captures the main feature additions for Razorpay and CCAvenue parsers, but omits the significant work on BillDesk, shared utilities, and SAP integrations.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/parser-logic-addition-for-razorpay-ccavenue-pg

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs (1)

121-128: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Missing date-only format with dash separator.

The parser handles date-with-time using both / and - separators, but date-only parsing only supports /. If CCAvenue sends a date like "31-12-2024", it will fail to parse and return Nothing.

🔧 Proposed fix
     parseTimeM True defaultTimeLocale "%d/%m/%Y %H:%M:%S" (T.unpack $ T.strip t)
       <|> parseTimeM True defaultTimeLocale "%d-%m-%Y %H:%M:%S" (T.unpack $ T.strip t)
       <|> parseTimeM True defaultTimeLocale "%d/%m/%Y" (T.unpack $ T.strip t)
+      <|> parseTimeM True defaultTimeLocale "%d-%m-%Y" (T.unpack $ T.strip t)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs`
around lines 121 - 128, The parseCCAvenueDateTime function is missing a
date-only format with dash separators; add an additional alternative using
parseTimeM True defaultTimeLocale "%d-%m-%Y" (applied to the same stripped Text
via T.unpack) alongside the existing "%d/%m/%Y" branch so date-only strings like
"31-12-2024" are parsed; update the parseCCAvenueDateTime alternatives that call
parseTimeM to include this new "%d-%m-%Y" format.
lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentTypes.hs (1)

72-88: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Include payRef2 in the JSON serialization for consistency.

The ToJSON instance serializes payRef1 (line 86) but omits payRef2, even though both fields are parsed from the CSV (lines 69-70) and stored in the record (lines 42-43). This asymmetry could confuse downstream consumers who expect both fields or might cause data loss if payRef2 is needed for debugging or reconciliation.

✨ Proposed fix to add payRef2 to JSON output
 instance A.ToJSON BillDeskSettledRow where
   toJSON row =
     A.object
       [ "billerId" A..= row.billerId,
         "bankId" A..= row.bankId,
         "bankRefNo" A..= row.bankRefNo,
         "pgiRefNo" A..= row.pgiRefNo,
         "ref1" A..= row.ref1,
         "dateOfTxn" A..= row.dateOfTxn,
         "settlementDate" A..= row.settlementDate,
         "grossAmount" A..= row.grossAmount,
         "charges" A..= row.charges,
         "gst" A..= row.gst,
         "netAmount" A..= row.netAmount,
         "payRef1" A..= row.payRef1,
+        "payRef2" A..= row.payRef2,
         "section" A..= ("SETTLED" :: Text)
       ]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentTypes.hs`
around lines 72 - 88, The ToJSON instance for BillDeskSettledRow omits the
payRef2 field causing asymmetry with parsing; update the toJSON implementation
in the A.ToJSON BillDeskSettledRow (the toJSON function) to include "payRef2"
A..= row.payRef2 alongside the existing "payRef1" entry so both payRef1 and
payRef2 are serialized into the JSON object (keep the "section" field as-is).
🧹 Nitpick comments (5)
lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentParser.hs (1)

111-117: ⚡ Quick win

Consider distinguishing credit vs debit cards using cardType field.

Currently "card" maps unconditionally to CREDIT_CARD, but Razorpay's card_type column likely contains "credit"/"debit" information. The PaymentMethodType enum supports both variants.

This would require passing cardType to the function:

parseRazorpayPaymentMethod :: Text -> Text -> Maybe PaymentMethodType
parseRazorpayPaymentMethod method cardType = case T.toLower (T.strip method) of
  "card" -> case T.toLower (T.strip cardType) of
    "debit" -> Just DEBIT_CARD
    _       -> Just CREDIT_CARD
  ...
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentParser.hs`
around lines 111 - 117, The current parseRazorpayPaymentMethod function always
maps "card" to CREDIT_CARD; update its signature to accept the Razorpay
card_type (e.g., parseRazorpayPaymentMethod :: Text -> Text -> Maybe
PaymentMethodType) and branch on the normalized cardType when method equals
"card" so that "debit" returns DEBIT_CARD and other values return CREDIT_CARD;
maintain existing mappings for "upi", "netbanking", "wallet" and preserve
trimming/lowercasing behavior for both inputs to locate the change in
parseRazorpayPaymentMethod.
lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs (1)

50-103: 💤 Low value

Unused idx parameter is dead code.

The idx parameter is passed but explicitly discarded with _ = idx. Other parsers in the codebase use the row index to provide better error messages when row parsing fails. Since this function always returns Right, the index isn't used for error context either.

Consider either removing the parameter if not needed, or using it in potential error paths for better debugging.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs`
around lines 50 - 103, The function parseCCAvenueTxnRow currently takes an
unused idx (dead code) which should be removed or used for error context; update
either the signature and all callers to drop the Int parameter (remove idx and
the `_ = idx` line) OR change parsing helpers inside parseCCAvenueTxnRow to
return Either/Text and include idx in Left messages so failures include row
index (e.g., use idx when constructing error Text for parseCCAvenueDateTime,
parseAmount, nonEmpty' failures) while keeping the PaymentSettlementReport
construction on Right.
lib/mobility-core/src/Kernel/External/Settlement/Utils/ParserUtils.hs (2)

39-44: 💤 Low value

Consider documenting the datetime formats and timezone conversion.

The function supports two formats, but the behavior when parsing %Z (timezone names) isn't documented. When a non-UTC timezone is parsed in Format 2, parseTimeM converts it to UTC, which may not be obvious to callers. Adding a comment would clarify expectations.

📝 Suggested documentation
+-- | Parse datetime from text, supporting two formats:
+-- 1. "%Y-%m-%d %H:%M:%S" (assumes UTC)
+-- 2. "%Y-%m-%d %H:%M:%S %Z" (parses timezone name and converts to UTC)
+-- Returns Nothing for blank input or unparseable formats.
 parseDateTime :: Text -> Maybe UTCTime
 parseDateTime t
   | T.null (T.strip t) = Nothing
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Settlement/Utils/ParserUtils.hs` around
lines 39 - 44, Document the supported input formats and timezone behavior for
parseDateTime: add a comment above parseDateTime stating it accepts "YYYY-MM-DD
HH:MM:SS" and "YYYY-MM-DD HH:MM:SS Z" (where Z is a timezone name) and that when
the second format is used the underlying parseTimeM will interpret the timezone
and convert the resulting UTCTime to UTC; also note that empty or
whitespace-only input returns Nothing. Reference the function name parseDateTime
and the two format strings "%Y-%m-%d %H:%M:%S" and "%Y-%m-%d %H:%M:%S %Z" in the
comment so callers understand expected input and conversion semantics.

28-31: 💤 Low value

Consider optimizing to call T.strip once.

The current implementation calls T.strip twice: once in the guard and once in the result. Extracting the stripped value improves efficiency.

⚡ Proposed optimization
 nonEmpty' :: Text -> Maybe Text
-nonEmpty' t
-  | T.null (T.strip t) = Nothing
-  | otherwise = Just (T.strip t)
+nonEmpty' t =
+  let stripped = T.strip t
+  in if T.null stripped then Nothing else Just stripped
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Settlement/Utils/ParserUtils.hs` around
lines 28 - 31, The function nonEmpty' calls T.strip twice; instead bind the
stripped value once (e.g., let s = T.strip t) then check if T.null s and return
Nothing or Just s accordingly—update nonEmpty' to compute the trimmed Text once
and reuse that binding.
lib/mobility-core/src/Kernel/External/Settlement/Types.hs (1)

69-76: ⚡ Quick win

Consider documenting the bankCode field.

The new bankCode field lacks inline documentation explaining its purpose, unlike the detailed field comments in PaymentSettlementReport. Adding a comment would improve maintainability.

📝 Suggested documentation
   parserTypeMap :: Maybe SettlementParserTypeMap,
   useJuspayOrderStatus :: Maybe Bool,
-  bankCode :: Maybe Text
+  bankCode :: Maybe Text -- Optional bank code for provider-specific settlement routing
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Settlement/Types.hs` around lines 69 -
76, Add an inline comment documenting the bankCode field in the
SettlementServiceConfig record: explain what values it holds (e.g., bank
identifier or routing code), whether it is optional, its format/constraints, and
how it is used relative to payment parsing or reconciliation (similar to
existing comments on PaymentSettlementReport). Update the
SettlementServiceConfig declaration to place the comment immediately above or
inline with the bankCode field so future readers see its purpose and expected
values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/mobility-core/mobility-core.cabal`:
- Around line 237-240: The four manually-added modules Kernel.External.SAP.API,
Kernel.External.SAP.Config, Kernel.External.SAP.Flow, and
Kernel.External.SAP.Types were removed by hpack regeneration; fix this by
removing these manual additions from the generated .cabal and adding those exact
module names under library.exposed-modules in the package.yaml used to generate
the cabal (ensure the entries are listed as exposed modules so hpack will
include them on regen).

In `@lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentParser.hs`:
- Line 148: The settled-row mapping incorrectly reads paymentMethod from
row.payRef2 (leftover TODO) causing empty values; in
PaymentParser.parseSettledRow change the mapping to use row.ref2 (the same field
used for refunds/chargebacks) or alternatively update BillDeskSettledRow's
Csv.FromNamedRecord to bind Pay Ref2 to the actual CSV header "Ref. 2" — then
remove the inline TODO comment; update references to parseBillDeskPaymentMethod
and BillDeskSettledRow accordingly so settled parsing matches the CSV samples.

In `@lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentParser.hs`:
- Line 72: The inline note "---- see into it" next to vendorId in the
PaymentParser record is an unfinished TODO; either implement the proper vendorId
mapping or remove the ad-hoc comment and replace it with a tracked-task
reference. Update the code where vendorId = Nothing in the PaymentParser (or
related parser function) to derive vendorId from the source payment data (set
the correct MID mapping), or if you cannot implement it now, remove the inline
remark and add a standard TODO comment that references a ticket/issue ID (e.g.,
TODO: track in ISSUE-1234) so the missing work is recorded.
- Line 100: The cardNumber field is incorrectly populated from row.vpa (a UPI
VPA); update PaymentParser so cardNumber is populated only from actual card
fields (e.g., row.cardNumber, row.masked_card, or whichever Razorpay field
contains card data) and stop using nonEmpty' row.vpa for cardNumber; instead map
the VPA to a dedicated field such as paymentMethodSubType or add a new vpa field
(refer to cardNumber, row.vpa, paymentMethodSubType and the PaymentParser
parsing function) so UPI VPAs are preserved without corrupting card data.

In `@lib/mobility-core/src/Kernel/External/Settlement/Utils/ParserUtils.hs`:
- Around line 33-37: The parseAmount function currently returns 0 on any parse
failure which can hide bad financial data; change its type from parseAmount ::
Text -> HighPrecMoney to parseAmount :: Text -> Maybe HighPrecMoney and update
the implementation to return Just v on successful readMaybe and Nothing on
failures (i.e., propagate the parse failure instead of defaulting to 0), then
update all callers of parseAmount to handle the Maybe (use fromMaybe 0 only
where a zero-default is explicitly acceptable) so invalid inputs are surfaced;
alternatively, if you prefer keeping the current behavior, add an inline comment
on parseAmount documenting that non-parseable inputs are intentionally treated
as zero and why.

---

Outside diff comments:
In `@lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentTypes.hs`:
- Around line 72-88: The ToJSON instance for BillDeskSettledRow omits the
payRef2 field causing asymmetry with parsing; update the toJSON implementation
in the A.ToJSON BillDeskSettledRow (the toJSON function) to include "payRef2"
A..= row.payRef2 alongside the existing "payRef1" entry so both payRef1 and
payRef2 are serialized into the JSON object (keep the "section" field as-is).

In `@lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs`:
- Around line 121-128: The parseCCAvenueDateTime function is missing a date-only
format with dash separators; add an additional alternative using parseTimeM True
defaultTimeLocale "%d-%m-%Y" (applied to the same stripped Text via T.unpack)
alongside the existing "%d/%m/%Y" branch so date-only strings like "31-12-2024"
are parsed; update the parseCCAvenueDateTime alternatives that call parseTimeM
to include this new "%d-%m-%Y" format.

---

Nitpick comments:
In `@lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs`:
- Around line 50-103: The function parseCCAvenueTxnRow currently takes an unused
idx (dead code) which should be removed or used for error context; update either
the signature and all callers to drop the Int parameter (remove idx and the `_ =
idx` line) OR change parsing helpers inside parseCCAvenueTxnRow to return
Either/Text and include idx in Left messages so failures include row index
(e.g., use idx when constructing error Text for parseCCAvenueDateTime,
parseAmount, nonEmpty' failures) while keeping the PaymentSettlementReport
construction on Right.

In `@lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentParser.hs`:
- Around line 111-117: The current parseRazorpayPaymentMethod function always
maps "card" to CREDIT_CARD; update its signature to accept the Razorpay
card_type (e.g., parseRazorpayPaymentMethod :: Text -> Text -> Maybe
PaymentMethodType) and branch on the normalized cardType when method equals
"card" so that "debit" returns DEBIT_CARD and other values return CREDIT_CARD;
maintain existing mappings for "upi", "netbanking", "wallet" and preserve
trimming/lowercasing behavior for both inputs to locate the change in
parseRazorpayPaymentMethod.

In `@lib/mobility-core/src/Kernel/External/Settlement/Types.hs`:
- Around line 69-76: Add an inline comment documenting the bankCode field in the
SettlementServiceConfig record: explain what values it holds (e.g., bank
identifier or routing code), whether it is optional, its format/constraints, and
how it is used relative to payment parsing or reconciliation (similar to
existing comments on PaymentSettlementReport). Update the
SettlementServiceConfig declaration to place the comment immediately above or
inline with the bankCode field so future readers see its purpose and expected
values.

In `@lib/mobility-core/src/Kernel/External/Settlement/Utils/ParserUtils.hs`:
- Around line 39-44: Document the supported input formats and timezone behavior
for parseDateTime: add a comment above parseDateTime stating it accepts
"YYYY-MM-DD HH:MM:SS" and "YYYY-MM-DD HH:MM:SS Z" (where Z is a timezone name)
and that when the second format is used the underlying parseTimeM will interpret
the timezone and convert the resulting UTCTime to UTC; also note that empty or
whitespace-only input returns Nothing. Reference the function name parseDateTime
and the two format strings "%Y-%m-%d %H:%M:%S" and "%Y-%m-%d %H:%M:%S %Z" in the
comment so callers understand expected input and conversion semantics.
- Around line 28-31: The function nonEmpty' calls T.strip twice; instead bind
the stripped value once (e.g., let s = T.strip t) then check if T.null s and
return Nothing or Just s accordingly—update nonEmpty' to compute the trimmed
Text once and reuse that binding.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 11e03aad-cf16-468b-8807-0febd7ca9d15

📥 Commits

Reviewing files that changed from the base of the PR and between c72fffc and 4ee5c5a.

📒 Files selected for processing (15)
  • lib/mobility-core/mobility-core.cabal
  • lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentTypes.hs
  • lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentTypes.hs
  • lib/mobility-core/src/Kernel/External/Settlement/HyperPG/MerchantPaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/HyperPG/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/HyperPG/PayoutParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Interface.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Interface/Types.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentTypes.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Types.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Utils/ParserUtils.hs
  • lib/mobility-core/src/Kernel/External/Settlement/YesBiz/PaymentParser.hs

Comment thread lib/mobility-core/mobility-core.cabal
paymentGateway = Just "BILLDESK",
paymentMethod = Nothing,
paymentGateway = Just "BILLDESK", -- PG name → Hardcode as Billdesk
paymentMethod = parseBillDeskPaymentMethod row.payRef2, -- Paymode → Pay Ref1 ---------mapping correction needed

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Search for BillDesk payment method mapping documentation or tests

# Look for test files covering BillDesk payment method parsing
rg -n -C5 'parseBillDeskPaymentMethod|payRef2|Pay Ref2' --type=hs --glob '*Test*' --glob '*Spec*'

# Look for sample CSV files or documentation
fd -e csv -e txt -e md -i billdesk

Repository: nammayatri/shared-kernel

Length of output: 234


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Inspect the code around the referenced line
FILE="lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentParser.hs"
echo "---- $FILE (lines 130-170) ----"
nl -ba "$FILE" | sed -n '130,170p'

# 2) Find the TODO / inline comment text in the repository
echo
echo "---- Search for 'mapping correction needed' ----"
rg -n "mapping correction needed|Pay Ref2|payRef2|parseBillDeskPaymentMethod" --hidden --no-ignore-vcs

# 3) Check where parseBillDeskPaymentMethod is defined and how payRef2 is used
echo
echo "---- Find definition of parseBillDeskPaymentMethod ----"
rg -n "parseBillDeskPaymentMethod" --type=hs lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentParser.hs

# 4) Inspect the sample CSV and parsed output that were found
echo
echo "---- Show BillDesk sample CSV ----"
CSV="test-settlement-parser/samples/billdesk/billdesk_settlement_report_sample.csv"
nl -ba "$CSV" | head -n 60

echo
echo "---- Show parsed output (contains mapping results) ----"
PARSED_TXT="test-settlement-parser/output/billdesk/billdesk_settlement_report_sample.csv.parsed.txt"
# Show only the first portion to avoid huge output
nl -ba "$PARSED_TXT" | head -n 200

Repository: nammayatri/shared-kernel

Length of output: 230


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentParser.hs"

echo "---- $FILE (lines 130-170) ----"
awk -v start=130 -v end=170 '{ if(NR>=start && NR<=end) printf "%5d\t%s\n", NR, $0 }' "$FILE"

echo
echo "---- Search for 'mapping correction needed' / payRef2 / TODO text ----"
rg -n "mapping correction needed|Pay Ref2|payRef2|parseBillDeskPaymentMethod" --hidden --no-ignore-vcs

echo
echo "---- Find definition of parseBillDeskPaymentMethod (file-local) ----"
rg -n "parseBillDeskPaymentMethod" --type=hs "$FILE"

echo
echo "---- Show BillDesk sample CSV (first 60 lines) ----"
CSV="test-settlement-parser/samples/billdesk/billdesk_settlement_report_sample.csv"
awk 'NR<=60 { printf "%5d\t%s\n", NR, $0 }' "$CSV"

echo
echo "---- Show parsed output (first 200 lines) ----"
PARSED_TXT="test-settlement-parser/output/billdesk/billdesk_settlement_report_sample.csv.parsed.txt"
awk 'NR<=200 { printf "%5d\t%s\n", NR, $0 }' "$PARSED_TXT"

Repository: nammayatri/shared-kernel

Length of output: 10524


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect parser code around parseBillDeskPaymentMethod and surrounding mappings
FILE="lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentParser.hs"
echo "---- $FILE (lines 180-320) ----"
awk 'NR>=180 && NR<=320 { printf "%5d\t%s\n", NR, $0 }' "$FILE"

# Inspect PaymentTypes for payRef2/ref2 fields and PaymentMethodType mapping
TFILE="lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentTypes.hs"
echo
echo "---- $TFILE (lines 1-220) ----"
awk 'NR>=1 && NR<=220 { printf "%5d\t%s\n", NR, $0 }' "$TFILE"

# Also inspect the CSV-parsing type that defines fields (look for payRef2/ref2 usage)
echo
echo "---- Search for 'payRef2' and 'ref2' field definitions/usages ----"
rg -n "payRef2|\\bref2\\b" --type=hs lib/mobility-core/src/Kernel/External/Settlement/BillDesk

# Dump the entire mapping function if it's short; otherwise show relevant branches
echo
echo "---- Extract parseBillDeskPaymentMethod branches ----"
awk 'NR>=270 && NR<=320 { printf "%5d\t%s\n", NR, $0 }' "$FILE"

Repository: nammayatri/shared-kernel

Length of output: 15912


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "Pay Ref1|Pay Ref2|payRef1|payRef2|parseBillDeskPaymentMethod|BillDeskSettledRow" --hidden --no-ignore-vcs .
echo "----"
rg -n "NETBANKING|CREDIT_CARD|DEBIT_CARD|CASH_CARD|UPI|ENACH|CBDC|UPI_CREDIT_LINE|UPI_PREPAID_WALLET" --hidden --no-ignore-vcs lib/mobility-core/src/Kernel/External/Settlement/BillDesk
echo "----"
fd -e md -e txt -e csv -e json -e doc --hidden --no-ignore-vcs . | rg -i "billdesk|payment.?method|settlement.*parser"

Repository: nammayatri/shared-kernel

Length of output: 3509


🏁 Script executed:

#!/bin/bash
set -euo pipefail

CSV="test-settlement-parser/samples/billdesk/billdesk_settlement_report_sample.csv"

echo "---- Lines containing 'Pay Ref1' or 'Pay Ref2' in sample CSV ----"
rg -n "Pay Ref1|Pay Ref2|payRef1|payRef2" "$CSV" || true

echo
echo "---- Show the first settled-section header row (line numbers) ----"
# Find the header line after "SETTLED TRANSACTIONS"
# Heuristic: the line immediately following the first header marker contains column names.
awk '
  /SETTLED TRANSACTIONS/ {marker=NR; next}
  marker && $0 !~ /^,$/ {print "header_line=" NR; print; exit}
' "$CSV"

echo
echo "---- Dump the first settled data row (line after the settled header) ----"
awk '
  /SETTLED TRANSACTIONS/ {marker=NR; next}
  marker && header_seen==0 {
    # current line is header; mark next line as data row
    header_seen=1; next
  }
  marker && header_seen==1 {print "data_line=" NR; print; exit}
' "$CSV"

echo
echo "---- Python: locate Pay Ref2 column and print its value for settled rows ----"
python3 - <<'PY'
import csv, re, sys

path = "test-settlement-parser/samples/billdesk/billdesk_settlement_report_sample.csv"
with open(path, newline='', encoding='utf-8') as f:
    lines = f.read().splitlines()

# find settled header
start = None
for i, line in enumerate(lines):
    if "SETTLED TRANSACTIONS" in line:
        start = i
        break
if start is None:
    print("No SETTLED TRANSACTIONS marker found")
    sys.exit(0)

# header is first non-empty line after marker that contains "Biller Id" (column header)
header_idx = None
for j in range(start+1, len(lines)):
    if lines[j].strip() and "Biller Id" in lines[j]:
        header_idx = j
        break
if header_idx is None:
    print("No settled header row found")
    sys.exit(0)

header = next(csv.reader([lines[header_idx]]))
col_index = {name: idx for idx, name in enumerate(header)}
print("Settled header columns:", header)
print("Pay Ref2 index:", col_index.get("Pay Ref2"))

# first data row after header
data_idx = header_idx + 1
while data_idx < len(lines) and not lines[data_idx].strip():
    data_idx += 1
if data_idx >= len(lines):
    print("No settled data row found")
    sys.exit(0)

row = next(csv.reader([lines[data_idx]]))
# normalize length
row = row + [""]*(len(header)-len(row))
pay_ref2 = row[col_index["Pay Ref2"]] if "Pay Ref2" in col_index else None
print("First settled row Pay Ref2 value:", pay_ref2)
PY

Repository: nammayatri/shared-kernel

Length of output: 1095


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentParser.hs"
echo "---- $FILE (lines 1-190) ----"
awk 'NR>=1 && NR<=190 { printf "%5d\t%s\n", NR, $0 }' "$FILE"

echo
echo "---- Show raw settled section header area from sample CSV (lines 1-10) ----"
CSV="test-settlement-parser/samples/billdesk/billdesk_settlement_report_sample.csv"
awk 'NR<=12 { printf "%5d\t%s\n", NR, $0 }' "$CSV"

echo
echo "---- Check entire sample CSV for any occurrence of 'Pay Ref' ----"
rg -n "Pay Ref" "$CSV" || true

echo
echo "---- Check for decodeNamedRecord / decodeByName usage in PaymentParser.hs ----"
rg -n "decodeByName|decodeByName|decodeNamedRecord|FromNamedRecord|Csv\\.|named" "$FILE"

Repository: nammayatri/shared-kernel

Length of output: 11019


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search all BillDesk samples/outputs/docs for Pay Ref1/Pay Ref2
fd -e csv -e txt -e md -i --hidden billdesk test-settlement-parser \
  | xargs -r rg -n "Pay Ref1|Pay Ref2|payRef1|payRef2|NETBANKING|UPI_CREDIT|parseBillDeskPaymentMethod" || true

echo
echo "---- Inspect billdesk email-attachment sample header (first ~25 lines) ----"
CSV="test-settlement-parser/samples/billdesk/email-attachment.csv"
if [ -f "$CSV" ]; then
  awk 'NR<=25 { printf "%5d\t%s\n", NR, $0 }' "$CSV"
fi

echo
echo "---- Inspect README for any BillDesk mapping notes ----"
rg -n "BillDesk|pay ref|Pay Ref|PaymentMethod|parseBillDeskPaymentMethod" test-settlement-parser/README.md || true

Repository: nammayatri/shared-kernel

Length of output: 2625


🏁 Script executed:

#!/bin/bash
set -euo pipefail

README="test-settlement-parser/README.md"
if [ -f "$README" ]; then
  echo "---- $README (lines 1-260) ----"
  awk 'NR>=1 && NR<=260 { printf "%5d\t%s\n", NR, $0 }' "$README"
else
  echo "README not found at $README"
fi

echo
echo "---- Search README for BillDesk schema/Pay Ref columns ----"
rg -n "BillDesk|Pay Ref1|Pay Ref2|payRef1|payRef2|Ref\\. 2|paymentMethod" "$README" || true

Repository: nammayatri/shared-kernel

Length of output: 9391


Remove/resolve BillDesk mapping TODO: settled paymentMethod reads row.payRef2, but the CSV samples have no Pay Ref2
Line 148 currently uses:

        paymentMethod = parseBillDeskPaymentMethod row.payRef2, -- Paymode → Pay Ref1                             ---------mapping correction needed
  • PaymentParser.parseSettledRow maps settled paymentMethod from row.payRef2 (TODO), while refund/chargeback use row.ref2.
  • BillDeskSettledRow’s Csv.FromNamedRecord expects CSV columns named Pay Ref1/Pay Ref2, but the checked-in BillDesk sample CSV headers only include Ref. 1/Ref. 2 (no Pay Ref2), and the sample parsed output shows paymentMethod: <empty>.
  • Fix the settled mapping (either use row.ref2 like the other sections, or update the named-record field names to match the actual CSV headers), then remove the inline TODO before merging.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentParser.hs`
at line 148, The settled-row mapping incorrectly reads paymentMethod from
row.payRef2 (leftover TODO) causing empty values; in
PaymentParser.parseSettledRow change the mapping to use row.ref2 (the same field
used for refunds/chargebacks) or alternatively update BillDeskSettledRow's
Csv.FromNamedRecord to bind Pay Ref2 to the actual CSV header "Ref. 2" — then
remove the inline TODO comment; update references to parseBillDeskPaymentMethod
and BillDeskSettledRow accordingly so settled parsing matches the CSV samples.

Comment thread lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentParser.hs Outdated
offerCode = Nothing,
offerId = Nothing,
actualAmount = Nothing,
cardNumber = nonEmpty' row.vpa

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

VPA incorrectly mapped to cardNumber field.

row.vpa contains the UPI Virtual Payment Address (e.g., user@upi), which is not a card number. This semantic mismatch corrupts the cardNumber field for UPI transactions and leaves it unpopulated for card transactions.

Consider mapping VPA to a more appropriate field (or a new dedicated field), and extracting actual card number data if available in Razorpay's settlement reports.

🔧 Proposed fix
-        cardNumber = nonEmpty' row.vpa
+        cardNumber = Nothing  -- Razorpay doesn't provide card number in settlement; VPA is UPI-specific

If VPA needs to be preserved, consider adding it to paymentMethodSubType:

-        paymentMethodSubType = Nothing,
+        paymentMethodSubType = nonEmpty' row.vpa,  -- UPI VPA when applicable
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
cardNumber = nonEmpty' row.vpa
cardNumber = Nothing -- Razorpay doesn't provide card number in settlement; VPA is UPI-specific
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentParser.hs`
at line 100, The cardNumber field is incorrectly populated from row.vpa (a UPI
VPA); update PaymentParser so cardNumber is populated only from actual card
fields (e.g., row.cardNumber, row.masked_card, or whichever Razorpay field
contains card data) and stop using nonEmpty' row.vpa for cardNumber; instead map
the VPA to a dedicated field such as paymentMethodSubType or add a new vpa field
(refer to cardNumber, row.vpa, paymentMethodSubType and the PaymentParser
parsing function) so UPI VPAs are preserved without corrupting card data.

Comment on lines +33 to +37
parseAmount :: Text -> HighPrecMoney
parseAmount t =
case readMaybe (T.unpack $ T.strip t) of
Just v -> v
Nothing -> 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Consider returning Maybe HighPrecMoney or documenting the zero-default behavior.

The function silently defaults to 0 for any unparseable input (including malformed amounts like "N/A" or "ERROR"). For financial settlement data, silently treating invalid amounts as zero can mask data quality issues or lead to incorrect reconciliation.

Options:

  1. Return Maybe HighPrecMoney and let callers handle parse failures explicitly
  2. Document the zero-default behavior with an inline comment
  3. Add validation that empty input returns 0 but other parse failures raise an error
💡 Option 1: Return Maybe for explicit error handling
-parseAmount :: Text -> HighPrecMoney
-parseAmount t =
-  case readMaybe (T.unpack $ T.strip t) of
-    Just v -> v
-    Nothing -> 0
+parseAmount :: Text -> Maybe HighPrecMoney
+parseAmount t
+  | T.null (T.strip t) = Just 0  -- Empty input defaults to 0
+  | otherwise = readMaybe (T.unpack $ T.strip t)

Then callers use fromMaybe 0 (parseAmount x) where zero-default is safe, or handle Nothing explicitly where validation is needed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Settlement/Utils/ParserUtils.hs` around
lines 33 - 37, The parseAmount function currently returns 0 on any parse failure
which can hide bad financial data; change its type from parseAmount :: Text ->
HighPrecMoney to parseAmount :: Text -> Maybe HighPrecMoney and update the
implementation to return Just v on successful readMaybe and Nothing on failures
(i.e., propagate the parse failure instead of defaulting to 0), then update all
callers of parseAmount to handle the Maybe (use fromMaybe 0 only where a
zero-default is explicitly acceptable) so invalid inputs are surfaced;
alternatively, if you prefer keeping the current behavior, add an inline comment
on parseAmount documenting that non-parseable inputs are intentionally treated
as zero and why.

@RahulGupta0706 RahulGupta0706 force-pushed the feat/parser-logic-addition-for-razorpay-ccavenue-pg branch from 4ee5c5a to 1e76876 Compare June 13, 2026 22:24

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
lib/mobility-core/mobility-core.cabal (1)

237-241: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Move exposed-module additions to package.yaml to avoid hpack rollback.

Because Line 3 declares this file is hpack-generated, adding modules directly here is non-persistent. On regeneration, these entries can be removed again, which breaks the new settlement/SAP exports and may fail CI. Please add the same module list to lib/mobility-core/package.yaml (library.exposed-modules) and regenerate the .cabal.

Also applies to: 244-245, 254-255, 258-258

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/mobility-core.cabal` around lines 237 - 241, The
exposed-module entries are being added directly to the hpack-generated
mobility-core.cabal file, but since line 3 declares the file as hpack-generated,
these manual additions are non-persistent and will be removed on the next hpack
regeneration. Instead of editing the .cabal file directly, add the
Kernel.External.SAP.API, Kernel.External.SAP.Config, Kernel.External.SAP.Flow,
Kernel.External.SAP.Interface, and Kernel.External.SAP.Types modules to the
library.exposed-modules section in lib/mobility-core/package.yaml, then
regenerate the .cabal file using hpack to make the changes persistent. The same
approach applies to the other module additions mentioned at lines 244-245,
254-255, and 258.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs`:
- Around line 43-47: Preserve section decode failures before rows are merged: in
PaymentParser’s parse flow, stop letting zipMerge discard Left values from
parseSectionRows for both the payout and transaction sections. Update the
parsing path so any decode error from the section parsers is carried through
into the final ParseResult instead of being dropped, and make sure the affected
sites in
lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs:43-47
and 111-116 are handled consistently.
- Around line 135-156: The authCode is being incorrectly mapped to the rrn field
(which should contain a bank RRN), when authCode is actually an
approval/authorization code. Additionally, ccavenueRefNumber is being duplicated
across both txnId and pgApprovalCode fields. Fix this by: (1) changing the rrn
field assignment to set it as Nothing instead of using authCode, and (2)
changing the pgApprovalCode field assignment to use authCode (via nonEmpty'
row.authCode) instead of ccavenueRefNumber, so each field maps to its correct
source data. This preserves ccavenueRefNumber for txnId and properly maps
authCode to pgApprovalCode where it belongs.

In `@lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentParser.hs`:
- Around line 105-109: The issue spans two transaction type parsers that
silently default unknown transaction types to ORDER, hiding provider format
changes and misclassifying transactions. In
lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentParser.hs lines
105-109, refactor parseRazorpayTxnType to return Either Text TxnType instead of
TxnType, add an explicit case for "chargeback" if supported, and return a Left
error for unknown transaction types instead of defaulting to ORDER. In
lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs lines
197-203, refactor parseCCAvenueTxnType similarly to fail on unknown labels by
removing the default pattern and only handling the supported "payment", "sale",
"refund", and "chargeback" cases, then thread this Either return through
parseMergedRow so that unknown transaction types increment failedRows instead of
producing a credit record.

---

Duplicate comments:
In `@lib/mobility-core/mobility-core.cabal`:
- Around line 237-241: The exposed-module entries are being added directly to
the hpack-generated mobility-core.cabal file, but since line 3 declares the file
as hpack-generated, these manual additions are non-persistent and will be
removed on the next hpack regeneration. Instead of editing the .cabal file
directly, add the Kernel.External.SAP.API, Kernel.External.SAP.Config,
Kernel.External.SAP.Flow, Kernel.External.SAP.Interface, and
Kernel.External.SAP.Types modules to the library.exposed-modules section in
lib/mobility-core/package.yaml, then regenerate the .cabal file using hpack to
make the changes persistent. The same approach applies to the other module
additions mentioned at lines 244-245, 254-255, and 258.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 84aacddb-9091-40ec-aa57-a3a448e37648

📥 Commits

Reviewing files that changed from the base of the PR and between 4ee5c5a and 1e76876.

📒 Files selected for processing (15)
  • lib/mobility-core/mobility-core.cabal
  • lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentTypes.hs
  • lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentTypes.hs
  • lib/mobility-core/src/Kernel/External/Settlement/HyperPG/MerchantPaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/HyperPG/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/HyperPG/PayoutParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Interface.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Interface/Types.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentTypes.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Types.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Utils/ParserUtils.hs
  • lib/mobility-core/src/Kernel/External/Settlement/YesBiz/PaymentParser.hs
🚧 Files skipped from review as they are similar to previous changes (10)
  • lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentTypes.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Interface.hs
  • lib/mobility-core/src/Kernel/External/Settlement/HyperPG/PayoutParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Interface/Types.hs
  • lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentTypes.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Types.hs
  • lib/mobility-core/src/Kernel/External/Settlement/HyperPG/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Utils/ParserUtils.hs
  • lib/mobility-core/src/Kernel/External/Settlement/YesBiz/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentParser.hs

Comment on lines +43 to +47
payoutRows = parseSectionRows @CCAvenuePayoutRow "Payout Summary" sections.payout
txnRows = parseSectionRows @CCAvenueTxnRow "Transaction Summary" sections.transaction
mergedRows = zipMerge txnRows payoutRows
results = zipWith (flip parseMergedRow) [1 ..] mergedRows
(errs, goods) = partitionEithers results

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve section decode failures before zipping rows.

parseSectionRows can return Left, but zipMerge strips those entries with [Right r <- ...], so malformed payout/transaction sections disappear from errors. If the transaction section fails to decode, this path currently returns ParseResult [] 0 0 [], which looks like a clean parse to Kernel.External.Settlement.Interface.parsePaymentSettlementCsv.

Suggested fix
 parseCCAvenueCsv :: LBS.ByteString -> ParsePaymentSettlementResult
 parseCCAvenueCsv csvData =
   let textContent = T.pack . map (toEnum . fromEnum) . LBS.unpack $ csvData
       allLines = T.lines textContent
       sections = splitSections allLines
       payoutRows = parseSectionRows `@CCAvenuePayoutRow` "Payout Summary" sections.payout
       txnRows = parseSectionRows `@CCAvenueTxnRow` "Transaction Summary" sections.transaction
-      mergedRows = zipMerge txnRows payoutRows
+      (payoutErrs, payoutGoods) = partitionEithers payoutRows
+      (txnErrs, txnGoods) = partitionEithers txnRows
+      mergedRows = zipMerge txnGoods payoutGoods
       results = zipWith (flip parseMergedRow) [1 ..] mergedRows
-      (errs, goods) = partitionEithers results
+      (rowErrs, goods) = partitionEithers results
+      errs = payoutErrs <> txnErrs <> rowErrs
    in ParseResult
         { reports = goods,
           totalRows = length results,
           failedRows = length errs,
           errors = errs
         }
@@
-zipMerge :: [Either Text CCAvenueTxnRow] -> [Either Text CCAvenuePayoutRow] -> [CCAvenueMergedRow]
-zipMerge txnResults payoutResults =
-  let txnRows = [r | Right r <- txnResults]
-      payoutRows = [r | Right r <- payoutResults]
-      payoutPadded = map Just payoutRows <> repeat Nothing
-   in zipWith (\t p -> CCAvenueMergedRow t p) txnRows payoutPadded
+zipMerge :: [CCAvenueTxnRow] -> [CCAvenuePayoutRow] -> [CCAvenueMergedRow]
+zipMerge txnRows payoutRows =
+  let payoutPadded = map Just payoutRows <> repeat Nothing
+   in zipWith CCAvenueMergedRow txnRows payoutPadded

Also applies to: 111-116

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs`
around lines 43 - 47, Preserve section decode failures before rows are merged:
in PaymentParser’s parse flow, stop letting zipMerge discard Left values from
parseSectionRows for both the payout and transaction sections. Update the
parsing path so any decode error from the section parsers is carried through
into the final ParseResult instead of being dropped, and make sure the affected
sites in
lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs:43-47
and 111-116 are handled consistently.

Comment on lines +135 to +156
txnId = nonEmpty' row.ccavenueRefNumber, -- Transaction ID → CCAvenue Ref Number
rrn = nonEmpty' row.authCode, -- Bank RRN → Auth Code
utr = payout >>= nonEmpty' . (.utrNo), -- Settlement UTR → Payout Summary - UTR No
txnType = txnType', -- Document Type → Transaction Type
txnStatus = SUCCESS,
txnDate = parseCCAvenueDateTime row.txnDate, -- Transaction Date → Date
txnAmount = parseAmount row.amount, -- Charged Amount → Amount
pgBaseFee = parseAmount row.tdrAmount, -- PG Fee → TDR Amount
pgTax = parseAmount row.tax, -- GST on PG Fee → Tax
settlementAmount = parseAmount row.payableAmount, -- Net Settled Amount → Payable Amount
currency = INR,
vendorId = nonEmpty' row.merchantId, -- MID → Merchant ID
uniqueSplitId = Nothing,
paymentGateway = Just "HDFC-CCAVENUE", -- PG name → Hardcode as HDFC-CCAvenue
paymentMethod = parseCCAvenuePaymentMethod row.paymentType, -- Paymode → Payment Type
paymentMethodSubType = Nothing,
settlementType = settlementType',
settlementMode = Nothing,
settlementId = payout >>= nonEmpty' . (.payId), -- Settlement ID → Payout Summary - Pay ID
settlementDate = parseCCAvenueDateTime row.settlementDate, -- Settlement Date → Settlement Date
pgApprovalCode = nonEmpty' row.ccavenueRefNumber, -- PG Approval code → CCAvenue Ref Number
pgRequestId = nonEmpty' row.invoiceRefNo, -- PG Request id → Invoice Reference No

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

authCode and ccavenueRefNumber are populating the wrong report fields.

authCode is an approval/auth code, not a bank RRN, while ccavenueRefNumber is already being used as txnId. Mapping them to rrn and pgApprovalCode this way will poison the identifier fields that downstream reconciliation reads from PaymentSettlementReport.

Suggested fix
-        rrn = nonEmpty' row.authCode, -- Bank RRN → Auth Code
+        rrn = Nothing, -- No dedicated RRN field in the CCAvenue row
@@
-        pgApprovalCode = nonEmpty' row.ccavenueRefNumber, -- PG Approval code → CCAvenue Ref Number
+        pgApprovalCode = nonEmpty' row.authCode, -- PG Approval code → Auth Code

If CCAvenue exposes a real RRN in another column later, wire that field into rrn instead of overloading authCode.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
txnId = nonEmpty' row.ccavenueRefNumber, -- Transaction ID → CCAvenue Ref Number
rrn = nonEmpty' row.authCode, -- Bank RRN → Auth Code
utr = payout >>= nonEmpty' . (.utrNo), -- Settlement UTR → Payout Summary - UTR No
txnType = txnType', -- Document Type → Transaction Type
txnStatus = SUCCESS,
txnDate = parseCCAvenueDateTime row.txnDate, -- Transaction Date → Date
txnAmount = parseAmount row.amount, -- Charged Amount → Amount
pgBaseFee = parseAmount row.tdrAmount, -- PG Fee → TDR Amount
pgTax = parseAmount row.tax, -- GST on PG Fee → Tax
settlementAmount = parseAmount row.payableAmount, -- Net Settled Amount → Payable Amount
currency = INR,
vendorId = nonEmpty' row.merchantId, -- MID → Merchant ID
uniqueSplitId = Nothing,
paymentGateway = Just "HDFC-CCAVENUE", -- PG name → Hardcode as HDFC-CCAvenue
paymentMethod = parseCCAvenuePaymentMethod row.paymentType, -- Paymode → Payment Type
paymentMethodSubType = Nothing,
settlementType = settlementType',
settlementMode = Nothing,
settlementId = payout >>= nonEmpty' . (.payId), -- Settlement ID → Payout Summary - Pay ID
settlementDate = parseCCAvenueDateTime row.settlementDate, -- Settlement Date → Settlement Date
pgApprovalCode = nonEmpty' row.ccavenueRefNumber, -- PG Approval code → CCAvenue Ref Number
pgRequestId = nonEmpty' row.invoiceRefNo, -- PG Request id → Invoice Reference No
txnId = nonEmpty' row.ccavenueRefNumber, -- Transaction ID → CCAvenue Ref Number
rrn = Nothing, -- No dedicated RRN field in the CCAvenue row
utr = payout >>= nonEmpty' . (.utrNo), -- Settlement UTR → Payout Summary - UTR No
txnType = txnType', -- Document Type → Transaction Type
txnStatus = SUCCESS,
txnDate = parseCCAvenueDateTime row.txnDate, -- Transaction Date → Date
txnAmount = parseAmount row.amount, -- Charged Amount → Amount
pgBaseFee = parseAmount row.tdrAmount, -- PG Fee → TDR Amount
pgTax = parseAmount row.tax, -- GST on PG Fee → Tax
settlementAmount = parseAmount row.payableAmount, -- Net Settled Amount → Payable Amount
currency = INR,
vendorId = nonEmpty' row.merchantId, -- MID → Merchant ID
uniqueSplitId = Nothing,
paymentGateway = Just "HDFC-CCAVENUE", -- PG name → Hardcode as HDFC-CCAvenue
paymentMethod = parseCCAvenuePaymentMethod row.paymentType, -- Paymode → Payment Type
paymentMethodSubType = Nothing,
settlementType = settlementType',
settlementMode = Nothing,
settlementId = payout >>= nonEmpty' . (.payId), -- Settlement ID → Payout Summary - Pay ID
settlementDate = parseCCAvenueDateTime row.settlementDate, -- Settlement Date → Settlement Date
pgApprovalCode = nonEmpty' row.authCode, -- PG Approval code → Auth Code
pgRequestId = nonEmpty' row.invoiceRefNo, -- PG Request id → Invoice Reference No
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs`
around lines 135 - 156, The authCode is being incorrectly mapped to the rrn
field (which should contain a bank RRN), when authCode is actually an
approval/authorization code. Additionally, ccavenueRefNumber is being duplicated
across both txnId and pgApprovalCode fields. Fix this by: (1) changing the rrn
field assignment to set it as Nothing instead of using authCode, and (2)
changing the pgApprovalCode field assignment to use authCode (via nonEmpty'
row.authCode) instead of ccavenueRefNumber, so each field maps to its correct
source data. This preserves ccavenueRefNumber for txnId and properly maps
authCode to pgApprovalCode where it belongs.

Comment on lines +105 to +109
parseRazorpayTxnType :: Text -> TxnType
parseRazorpayTxnType t = case T.toLower (T.strip t) of
"payment" -> ORDER
"refund" -> REFUND
_ -> ORDER

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Unknown provider transaction labels are being silently coerced to ORDER. Both parsers currently default unrecognized settlement types into the ORDER/CREDIT path instead of failing the row, which hides provider format changes and misclassifies debits. The stricter pattern already exists in lib/mobility-core/src/Kernel/External/Settlement/HyperPG/MerchantPaymentParser.hs#L132-L136.

  • lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentParser.hs#L105-L109: make parseRazorpayTxnType return Either Text TxnType, add an explicit "chargeback" branch if Razorpay emits it, and surface unknown transactionEntity values as row errors instead of defaulting to ORDER.
  • lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs#L197-L203: make parseCCAvenueTxnType fail on unknown labels after the supported "payment"/"sale"/"refund"/"chargeback" cases, then thread that failure through parseMergedRow so failedRows increments instead of producing a credit record.
📍 Affects 2 files
  • lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentParser.hs#L105-L109 (this comment)
  • lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs#L197-L203
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentParser.hs`
around lines 105 - 109, The issue spans two transaction type parsers that
silently default unknown transaction types to ORDER, hiding provider format
changes and misclassifying transactions. In
lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentParser.hs lines
105-109, refactor parseRazorpayTxnType to return Either Text TxnType instead of
TxnType, add an explicit case for "chargeback" if supported, and return a Left
error for unknown transaction types instead of defaulting to ORDER. In
lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs lines
197-203, refactor parseCCAvenueTxnType similarly to fail on unknown labels by
removing the default pattern and only handling the supported "payment", "sale",
"refund", and "chargeback" cases, then thread this Either return through
parseMergedRow so that unknown transaction types increment failedRows instead of
producing a credit record.

@RahulGupta0706 RahulGupta0706 force-pushed the feat/parser-logic-addition-for-razorpay-ccavenue-pg branch 2 times, most recently from f29eea2 to 52f0f09 Compare June 13, 2026 22:39
@nammayatri nammayatri deleted a comment from khuzema786 Jun 14, 2026
@nammayatri nammayatri deleted a comment from khuzema786 Jun 14, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
lib/mobility-core/src/Kernel/External/Settlement/Types.hs (1)

73-74: Add documentation explaining the bankCode field's purpose.

The bankCode field in SettlementServiceConfig (line 74) lacks documentation describing its purpose, which settlement providers require it, and how it should be populated. While the field is currently unused in the codebase, clear documentation is essential before relying on it, especially since it extends the shared configuration schema.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Settlement/Types.hs` around lines 73 -
74, The `bankCode` field in the `SettlementServiceConfig` type definition lacks
documentation comments. Add Haskell documentation (using -- | comment syntax)
above or inline with the `bankCode` field to explain its purpose, which
settlement providers require this field, and how it should be populated. This
documentation should clarify the field's role in the settlement service
configuration before it becomes actively used in the codebase.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/mobility-core/src/Kernel/External/SAP/Types.hs`:
- Around line 101-105: The renderXmlElement function directly interpolates the
val parameter into XML text nodes without escaping special characters, which can
produce invalid XML when the value contains &, <, or > characters. Fix this by
applying proper XML character escaping to the val before it is concatenated into
the XML output in the Just val case. The comment indicates similar escaping
issues exist in other XML rendering functions spanning lines 106-173, so apply
the same XML escaping logic throughout all XML rendering functions in that
range.
- Around line 252-258: The lookupField function uses the descendant search
operator `$//` which can incorrectly pull nested values from deeper levels (such
as item-level nodes) and concatenate them together, corrupting the header field
values. Replace the `$//` descendant search operator with a direct child search
operator (such as `$/`) in the cursor query to ensure only immediate child
elements are searched, preventing nested values from being incorrectly included
in the concatenated result.
- Line 158: Remove the duplicate Msgtyp emission from the XML header rendering.
The line containing renderXmlElement "Msgtyp" hdr.msgtyp at line 158 is a
duplicate of an earlier emission at line 136. Delete this duplicate line to
prevent the Msgtyp field from appearing twice in the SAP XML payload, which
could violate payload expectations.

---

Nitpick comments:
In `@lib/mobility-core/src/Kernel/External/Settlement/Types.hs`:
- Around line 73-74: The `bankCode` field in the `SettlementServiceConfig` type
definition lacks documentation comments. Add Haskell documentation (using -- |
comment syntax) above or inline with the `bankCode` field to explain its
purpose, which settlement providers require this field, and how it should be
populated. This documentation should clarify the field's role in the settlement
service configuration before it becomes actively used in the codebase.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 75db8543-80bb-4745-9edc-d9dc7d0b9265

📥 Commits

Reviewing files that changed from the base of the PR and between 1e76876 and 2a5b4be.

📒 Files selected for processing (20)
  • lib/mobility-core/mobility-core.cabal
  • lib/mobility-core/src/Kernel/External/SAP/API.hs
  • lib/mobility-core/src/Kernel/External/SAP/Config.hs
  • lib/mobility-core/src/Kernel/External/SAP/Flow.hs
  • lib/mobility-core/src/Kernel/External/SAP/Interface.hs
  • lib/mobility-core/src/Kernel/External/SAP/Types.hs
  • lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentTypes.hs
  • lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentTypes.hs
  • lib/mobility-core/src/Kernel/External/Settlement/HyperPG/MerchantPaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/HyperPG/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/HyperPG/PayoutParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Interface.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Interface/Types.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentTypes.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Types.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Utils/ParserUtils.hs
  • lib/mobility-core/src/Kernel/External/Settlement/YesBiz/PaymentParser.hs
🚧 Files skipped from review as they are similar to previous changes (13)
  • lib/mobility-core/src/Kernel/External/Settlement/Interface.hs
  • lib/mobility-core/mobility-core.cabal
  • lib/mobility-core/src/Kernel/External/Settlement/HyperPG/MerchantPaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Utils/ParserUtils.hs
  • lib/mobility-core/src/Kernel/External/Settlement/HyperPG/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentTypes.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Interface/Types.hs
  • lib/mobility-core/src/Kernel/External/Settlement/HyperPG/PayoutParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/YesBiz/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentTypes.hs
  • lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentParser.hs

Comment on lines +101 to +105
renderXmlElement :: Text -> Maybe Text -> Text
renderXmlElement tag = \case
Nothing -> "<" <> tag <> "></" <> tag <> ">"
Just val -> "<" <> tag <> ">" <> val <> "</" <> tag <> ">"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Escape XML content before interpolation (current rendering can emit invalid XML).

Values are inserted directly into XML text nodes; special characters in data (&, <, >) can break request parsing and journal posting.

Also applies to: 106-173

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/SAP/Types.hs` around lines 101 - 105,
The renderXmlElement function directly interpolates the val parameter into XML
text nodes without escaping special characters, which can produce invalid XML
when the value contains &, <, or > characters. Fix this by applying proper XML
character escaping to the val before it is concatenated into the XML output in
the Just val case. The comment indicates similar escaping issues exist in other
XML rendering functions spanning lines 106-173, so apply the same XML escaping
logic throughout all XML rendering functions in that range.

renderXmlElement "Belnr" hdr.belnr,
renderXmlElement "Gjahr" hdr.gjahr,
renderXmlElement "Message" hdr.message,
renderXmlElement "Msgtyp" hdr.msgtyp,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Remove duplicate Msgtyp emission in header XML.

Line 158 emits <Msgtyp> a second time even though it is already emitted at Line 136. This can violate SAP payload expectations and cause ambiguous field interpretation.

Suggested fix
       renderXmlElement "Belnr" hdr.belnr,
       renderXmlElement "Gjahr" hdr.gjahr,
       renderXmlElement "Message" hdr.message,
-      renderXmlElement "Msgtyp" hdr.msgtyp,
       "<ItemSet>",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/SAP/Types.hs` at line 158, Remove the
duplicate Msgtyp emission from the XML header rendering. The line containing
renderXmlElement "Msgtyp" hdr.msgtyp at line 158 is a duplicate of an earlier
emission at line 136. Delete this duplicate line to prevent the Msgtyp field
from appearing twice in the SAP XML payload, which could violate payload
expectations.

Comment on lines +252 to +258
lookupField :: XC.Cursor -> Text -> Maybe Text
lookupField cursor fieldName =
case cursor $// laxElement fieldName &/ content of
[] -> Nothing
ts ->
let val = T.strip (T.concat ts)
in if T.null val then Nothing else Just val

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

lookupField should not use descendant search for header fields.

Using $// from a Header cursor can pull nested values (e.g., from item-level nodes) and then concatenate them, corrupting parsed header fields.

Suggested fix
+import Text.XML.Cursor (content, fromDocument, laxElement, ($/), ($//), (&/))
 ...
 lookupField :: XC.Cursor -> Text -> Maybe Text
 lookupField cursor fieldName =
-  case cursor $// laxElement fieldName &/ content of
+  case cursor $/ laxElement fieldName &/ content of
     [] -> Nothing
     ts ->
       let val = T.strip (T.concat ts)
        in if T.null val then Nothing else Just val
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
lookupField :: XC.Cursor -> Text -> Maybe Text
lookupField cursor fieldName =
case cursor $// laxElement fieldName &/ content of
[] -> Nothing
ts ->
let val = T.strip (T.concat ts)
in if T.null val then Nothing else Just val
lookupField :: XC.Cursor -> Text -> Maybe Text
lookupField cursor fieldName =
case cursor $/ laxElement fieldName &/ content of
[] -> Nothing
ts ->
let val = T.strip (T.concat ts)
in if T.null val then Nothing else Just val
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/SAP/Types.hs` around lines 252 - 258,
The lookupField function uses the descendant search operator `$//` which can
incorrectly pull nested values from deeper levels (such as item-level nodes) and
concatenate them together, corrupting the header field values. Replace the `$//`
descendant search operator with a direct child search operator (such as `$/`) in
the cursor query to ensure only immediate child elements are searched,
preventing nested values from being incorrectly included in the concatenated
result.

@RahulGupta0706 RahulGupta0706 force-pushed the feat/parser-logic-addition-for-razorpay-ccavenue-pg branch 3 times, most recently from 13ae6d9 to bde9871 Compare June 14, 2026 06:50
@RahulGupta0706

Copy link
Copy Markdown
Contributor Author

@ClaudeWeb please review.

@khuzema786

Copy link
Copy Markdown
Contributor

Fetching the latest PR state to check for any new commits or fixes.New commits pushed (head SHA changed again). Let me fetch all the key files in parallel.The SAP source files are now present (fixing the CI blocker), but all parser files are unchanged from the previous review. Here's the updated status:


PR Review (3rd pass): feat/parser-logic-addition-for-razorpay-ccavenue-pg

Verdict: Request Changes — Good progress: the SAP source files were added and the build blocker is resolved. However, all 7 data-correctness bugs in the parser files remain unaddressed. These are not nitpicks — they produce wrong settlement data in production.


✅ Fixed Since Last Review

SAP source files committedsrc/Kernel/External/SAP/{API,Config,Flow,Interface,Types}.hs now exist, so hpack auto-discovers them and the .cabal will stay consistent. CI blocker resolved.


🔴 Critical — Still Open

1. Razorpay cardNumber populated from row.vpa — data corruption

Razorpay/PaymentParser.hs, line ~100

cardNumber = nonEmpty' row.vpa   -- ← VPA is a UPI address, not a card number

All UPI transactions get a cardNumber containing e.g. "user@oksbi". Card transactions get cardNumber = Nothing. Fix:

cardNumber           = Nothing,
paymentMethodSubType = nonEmpty' row.vpa,  -- UPI VPA

2. BillDesk settled paymentMethod reads wrong field — explicit TODO in code

BillDesk/PaymentParser.hs, line ~148

paymentMethod = parseBillDeskPaymentMethod row.payRef2,
-- Paymode → Pay Ref1    ---------mapping correction needed

The author's own comment says the mapping is wrong. Refund/chargeback rows use row.ref2 — confirm whether settled rows should also use row.ref2, then remove the TODO. This cannot merge unresolved.


3. CCAvenue rrn mapped from authCode — wrong field

CCAvenue/PaymentParser.hs, line ~138

rrn            = nonEmpty' row.authCode,           -- Auth Code ≠ Bank RRN
pgApprovalCode = nonEmpty' row.ccavenueRefNumber,  -- duplicate of txnId

authCode is the payment network authorization code, not the bank's Real-time Reference Number. pgApprovalCode should hold authCode; rrn should be Nothing (CCAvenue doesn't expose RRN). Fix:

rrn            = Nothing,
pgApprovalCode = nonEmpty' row.authCode,

🟠 Warning — Still Open

4. parseAmount silently coerces bad input to ₹0

Utils/ParserUtils.hs, lines 33–37

parseAmount t = case readMaybe (T.unpack $ T.strip t) of
  Just v  -> v
  Nothing -> 0    -- "#N/A", "ERROR", "" all silently become ₹0

For financial reconciliation, a malformed amount field silently producing zero can corrupt settlement totals. Change the return type to Maybe HighPrecMoney and let callers handle the Nothing case explicitly.


5. CCAvenue zipMerge silently discards section-level parse errors

CCAvenue/PaymentParser.hs, lines ~97–101

zipMerge txnResults payoutResults =
  let txnRows    = [r | Right r <- txnResults]   -- Left errors vanish here
      payoutRows = [r | Right r <- payoutResults]

If parseSectionRows returns [Left "Transaction Summary CSV parse error: missing column X"], that error is swallowed — ParseResult.errors stays empty and failedRows = 0. The section errors must be collected before the merge and included in the final ParseResult.


6. Razorpay chargeback type defaults silently to ORDER

Razorpay/PaymentParser.hs, lines ~105–109

parseRazorpayTxnType t = case T.toLower (T.strip t) of
  "payment" -> ORDER
  "refund"  -> REFUND
  _         -> ORDER   -- chargebacks classified as payments with settlementType = CREDIT

Add "chargeback" -> CHARGEBACK to prevent chargeback entries being recorded as credits.


7. Razorpay inline ---- see into it TODO

Razorpay/PaymentParser.hs, line ~67

vendorId = Nothing, -- MID → Hardcode MID (set at domain layer)    ---- see into it

Either confirm vendorId = Nothing is intentional design (and say so cleanly), or track it in a ticket. This ad-hoc note cannot merge as-is.


Open Issues Tracker

# File Issue Status
1 SAP source files Build/CI broken ✅ Fixed
2 Razorpay/PaymentParser.hs cardNumber = row.vpa ❌ Open
3 BillDesk/PaymentParser.hs payRef2 wrong field + TODO ❌ Open
4 CCAvenue/PaymentParser.hs rrn = authCode ❌ Open
5 Utils/ParserUtils.hs parseAmount zero-default ❌ Open
6 CCAvenue/PaymentParser.hs zipMerge drops section errors ❌ Open
7 Razorpay/PaymentParser.hs Chargeback → ORDER ❌ Open
8 Razorpay/PaymentParser.hs ---- see into it TODO ❌ Open

@RahulGupta0706 RahulGupta0706 force-pushed the feat/parser-logic-addition-for-razorpay-ccavenue-pg branch from bde9871 to eb9b086 Compare June 15, 2026 10:56

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/mobility-core/src/Kernel/External/SAP/API.hs`:
- Around line 37-40: The SAP API Servant contract currently defines grant_type,
token_format, and Authorization as optional parameters using QueryParam and
Header without modifiers. Since the callsites in Flow.hs always provide these
values wrapped in Just, these parameters should be required. Change QueryParam
"grant_type" Text and QueryParam "token_format" Text to QueryParam' '[Required,
Strict] "grant_type" Text and QueryParam' '[Required, Strict] "token_format"
Text respectively, and change Header "Authorization" Text to Header' '[Required,
Strict] "Authorization" Text to enforce that these parameters must always be
provided in all API endpoints that use them.
- Line 47: Remove the explicit `deriving (Typeable)` clause from the
`SAPApplicationXML` data type declaration. Since GHC 7.10 and later
automatically derive Typeable for all types, the explicit derivation is
redundant and can trigger compilation warnings. Simply change the line to end
with just the data type name without the deriving clause to achieve warning-free
compilation under -Wall -Werror.

In `@lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs`:
- Around line 126-131: The `parseCCAvenueTxnType` function contains a fallback
pattern that defaults unknown transaction types to ORDER, which incorrectly
treats them as valid CREDIT settlements. Remove the `_ -> ORDER` catch-all
pattern and instead implement explicit error handling for unknown transaction
types so that rows with unrecognized or misspelled CCAvenue document types fail
validation and can be caught during reconciliation. This change applies to the
function definition as well as any other call sites where unknown transaction
types are similarly defaulted to ORDER (also check lines 196-202).
- Around line 99-105: The base case of parseSectionRows currently returns an
empty list when sectionLines is empty, which silently ignores a missing section
in a non-empty file. Change the pattern match for the empty section lines case
(currently parseSectionRows _ [] = []) to return an error indicating that the
expected section was not found, using the sectionName parameter to identify
which section is missing. This ensures that a malformed or non-matching report
with missing required sections surfaces an error instead of appearing as a
successful empty parse.
- Around line 140-143: The `parseAmount` function silently coerces unparseable
monetary values to zero, allowing invalid CCAvenue data to pass validation.
Create a new helper function `parseRequiredAmount` that returns `Either Text
HighPrecMoney` to properly surface parsing errors. In the `parseMergedRow`
function, use do-notation at the start to sequentially validate the four
required amount fields—`row.amount`, `row.tdrAmount`, `row.tax`, and
`row.payableAmount`—by calling `parseRequiredAmount` on each, then assign the
successful results to variables (e.g., `validatedTxnAmount`,
`validatedPgBaseFee`, `validatedPgTax`, `validatedSettlementAmount`). Finally,
in the record construction, replace the direct `parseAmount row.fieldName` calls
with these validated variables so malformed amounts cause the row to fail rather
than silently become zero.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9f9dd9e4-bcfb-4ac0-a2e0-659edf330700

📥 Commits

Reviewing files that changed from the base of the PR and between 2a5b4be and eb9b086.

📒 Files selected for processing (20)
  • lib/mobility-core/mobility-core.cabal
  • lib/mobility-core/src/Kernel/External/SAP/API.hs
  • lib/mobility-core/src/Kernel/External/SAP/Config.hs
  • lib/mobility-core/src/Kernel/External/SAP/Flow.hs
  • lib/mobility-core/src/Kernel/External/SAP/Interface.hs
  • lib/mobility-core/src/Kernel/External/SAP/Types.hs
  • lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentTypes.hs
  • lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentTypes.hs
  • lib/mobility-core/src/Kernel/External/Settlement/HyperPG/MerchantPaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/HyperPG/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/HyperPG/PayoutParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Interface.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Interface/Types.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentTypes.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Types.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Utils/ParserUtils.hs
  • lib/mobility-core/src/Kernel/External/Settlement/YesBiz/PaymentParser.hs
🚧 Files skipped from review as they are similar to previous changes (16)
  • lib/mobility-core/src/Kernel/External/Settlement/Interface.hs
  • lib/mobility-core/src/Kernel/External/SAP/Config.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentTypes.hs
  • lib/mobility-core/src/Kernel/External/Settlement/HyperPG/PayoutParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentTypes.hs
  • lib/mobility-core/src/Kernel/External/SAP/Flow.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Interface/Types.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Utils/ParserUtils.hs
  • lib/mobility-core/src/Kernel/External/Settlement/YesBiz/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/SAP/Interface.hs
  • lib/mobility-core/src/Kernel/External/Settlement/HyperPG/MerchantPaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/Razorpay/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/SAP/Types.hs
  • lib/mobility-core/src/Kernel/External/Settlement/HyperPG/PaymentParser.hs
  • lib/mobility-core/src/Kernel/External/Settlement/BillDesk/PaymentTypes.hs

Comment on lines +37 to +40
:> QueryParam "grant_type" Text
:> QueryParam "token_format" Text
:> Header "Authorization" Text
:> Post '[JSON] SAPTokenResp

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify all sapTokenAPI/sapJournalPostAPI callsites and whether optional Maybe args are being passed.
set -euo pipefail

echo "== sapTokenAPI callsites =="
rg -nP --type=hs -C3 '\bclient\s+sapTokenAPI\b|\bsapTokenAPI\b'

echo
echo "== sapJournalPostAPI callsites =="
rg -nP --type=hs -C3 '\bclient\s+sapJournalPostAPI\b|\bsapJournalPostAPI\b'

Repository: nammayatri/shared-kernel

Length of output: 4244


🏁 Script executed:

#!/bin/bash
# Get the API definitions to verify current parameter types
head -80 lib/mobility-core/src/Kernel/External/SAP/API.hs | tail -50

Repository: nammayatri/shared-kernel

Length of output: 1269


🏁 Script executed:

#!/bin/bash
# Get the sapTokenAPI callsite context from Flow.hs
sed -n '35,60p' lib/mobility-core/src/Kernel/External/SAP/Flow.hs | cat -n

Repository: nammayatri/shared-kernel

Length of output: 797


🏁 Script executed:

#!/bin/bash
# Get the sapJournalPostAPI callsite context from Flow.hs
sed -n '60,80p' lib/mobility-core/src/Kernel/External/SAP/Flow.hs | cat -n

Repository: nammayatri/shared-kernel

Length of output: 690


Make OAuth/query/auth parameters required in the Servant contract.

Lines 37-40 and 66-68 currently model grant_type, token_format, and Authorization as optional using QueryParam and Header without modifiers. The Flow.hs callsites (lines 44-51 and 68-73) wrap all values in Just, confirming these are optional. Weakening the API contract to optional parameters permits invalid outbound requests if a caller passes Nothing. Use QueryParam' '[Required, Strict] and Header' '[Required, Strict] to enforce that these parameters must always be provided.

Proposed fix
 type SAPTokenAPI =
   "oauth2"
     :> "api"
     :> "v1"
     :> "token"
-    :> QueryParam "grant_type" Text
-    :> QueryParam "token_format" Text
-    :> Header "Authorization" Text
+    :> QueryParam' '[Required, Strict] "grant_type" Text
+    :> QueryParam' '[Required, Strict] "token_format" Text
+    :> Header' '[Required, Strict] "Authorization" Text
     :> Post '[JSON] SAPTokenResp

 type SAPJournalPostAPI =
   "http"
     :> "aws"
     :> "s4"
     :> "odata"
     :> "ondc"
-    :> Header "Authorization" Text
+    :> Header' '[Required, Strict] "Authorization" Text
     :> ReqBody '[SAPApplicationXML] BSL.ByteString
     :> Post '[SAPApplicationXML] SAPJournalResponse
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
:> QueryParam "grant_type" Text
:> QueryParam "token_format" Text
:> Header "Authorization" Text
:> Post '[JSON] SAPTokenResp
:> QueryParam' '[Required, Strict] "grant_type" Text
:> QueryParam' '[Required, Strict] "token_format" Text
:> Header' '[Required, Strict] "Authorization" Text
:> Post '[JSON] SAPTokenResp
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/SAP/API.hs` around lines 37 - 40, The
SAP API Servant contract currently defines grant_type, token_format, and
Authorization as optional parameters using QueryParam and Header without
modifiers. Since the callsites in Flow.hs always provide these values wrapped in
Just, these parameters should be required. Change QueryParam "grant_type" Text
and QueryParam "token_format" Text to QueryParam' '[Required, Strict]
"grant_type" Text and QueryParam' '[Required, Strict] "token_format" Text
respectively, and change Header "Authorization" Text to Header' '[Required,
Strict] "Authorization" Text to enforce that these parameters must always be
provided in all API endpoints that use them.


-- XML content type for SAP

data SAPApplicationXML deriving (Typeable)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify warning risk and flag configuration evidence.
set -euo pipefail

echo "== Searching for explicit deriving(Typeable) in Haskell files =="
rg -nP --type=hs '\bderiving\s*\(\s*Typeable\s*\)'

echo
echo "== Searching for -Werror / -Wall flags in cabal and project configs =="
fd -i 'cabal|package.yaml|stack.yaml|hie.yaml|ghc-options|project' | xargs -r rg -n -- '-Werror|-Wall|default-extensions|ghc-options'

Repository: nammayatri/shared-kernel

Length of output: 2571


🏁 Script executed:

cat -n lib/mobility-core/src/Kernel/External/SAP/API.hs | head -60

Repository: nammayatri/shared-kernel

Length of output: 2248


🏁 Script executed:

# Check for warning pragmas or suppressions in the file
rg -n '{-#|DEPRECATED|WARNING' lib/mobility-core/src/Kernel/External/SAP/API.hs

Repository: nammayatri/shared-kernel

Length of output: 166


🏁 Script executed:

# Check GHC version used and document default extensions
fd -i 'ghc.yaml|cabal.project|stack.yaml' -x cat -n {}

Repository: nammayatri/shared-kernel

Length of output: 131


🏁 Script executed:

# Check for LANGUAGE pragmas at the start of the file
head -50 lib/mobility-core/src/Kernel/External/SAP/API.hs | rg -n 'LANGUAGE|OPTIONS'

Repository: nammayatri/shared-kernel

Length of output: 50


🏁 Script executed:

# Check cabal file for GHC version constraint and default-extensions
rg -A 20 'library.*' lib/mobility-core/mobility-core.cabal | head -60

Repository: nammayatri/shared-kernel

Length of output: 883


🏁 Script executed:

# Search for any GHC configuration that might suppress -Wderiving-typeable
rg -n 'Wno-deriving-typeable|Wwarn.*deriving' lib/mobility-core/

Repository: nammayatri/shared-kernel

Length of output: 50


🏁 Script executed:

# Get the exact ghc-options for the mobility-core library stanza
rg -A 10 'name:.*mobility-core' lib/mobility-core/mobility-core.cabal | head -100

Repository: nammayatri/shared-kernel

Length of output: 502


🌐 Web query:

GHC -Wderiving-typeable warning when to trigger Typeable derive

💡 Result:

The -Wderiving-typeable warning is triggered when you explicitly list Typeable in a deriving clause or use StandaloneDeriving to derive a Typeable instance [1][2][3]. You should never need to manually derive Typeable in modern Haskell [2]. Since GHC 7.10, all types automatically have Typeable instances derived by the compiler [1][4]. Consequently, explicitly deriving it is redundant and has no effect on your code, other than triggering this warning [2]. If you encounter this warning, the recommended action is to simply remove the deriving Typeable clause from your data declaration [2]. The code will retain its Typeable instance automatically [1][5].

Citations:


🏁 Script executed:

# Get complete library stanza with ghc-options from cabal
rg -B 5 -A 15 'ghc-options.*-Wall' lib/mobility-core/mobility-core.cabal | head -80

Repository: nammayatri/shared-kernel

Length of output: 1642


🏁 Script executed:

# Check if there's a -Wwarn override for deriving-typeable anywhere
rg -n 'Wwarn|Wno-' lib/mobility-core/mobility-core.cabal

Repository: nammayatri/shared-kernel

Length of output: 549


Remove deriving (Typeable) — it's redundant and may trigger warnings with newer GHC versions.

Since GHC 7.10, Typeable is automatically derived for all types. Explicitly deriving it is redundant and can trigger the -Wderiving-typeable warning, which becomes an error under -Werror. Although this pattern appears across the codebase, the removal aligns with the coding guideline requiring warning-free compilation under -Wall -Werror.

Proposed fix
-data SAPApplicationXML deriving (Typeable)
+data SAPApplicationXML
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
data SAPApplicationXML deriving (Typeable)
data SAPApplicationXML
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/SAP/API.hs` at line 47, Remove the
explicit `deriving (Typeable)` clause from the `SAPApplicationXML` data type
declaration. Since GHC 7.10 and later automatically derive Typeable for all
types, the explicit derivation is redundant and can trigger compilation
warnings. Simply change the line to end with just the data type name without the
deriving clause to achieve warning-free compilation under -Wall -Werror.

Source: Coding guidelines

Comment on lines +99 to +105
parseSectionRows :: (Csv.FromNamedRecord a) => Text -> [Text] -> [Either Text a]
parseSectionRows _ [] = []
parseSectionRows sectionName sectionLines =
let csvBytes = LBS.fromStrict . encodeUtf8 . T.unlines $ sectionLines
in case Csv.decodeByName csvBytes of
Left err -> [Left $ sectionName <> " CSV parse error: " <> T.pack err]
Right (_, rows) -> map Right (V.toList rows)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Report a missing transaction section instead of returning a clean empty parse.

For a non-empty CCAvenue file without Transaction Summary Details, parseSectionRows _ [] = [] flows to ParseResult [] 0 0 []. Upstream already treats only empty input as a clean no-op; a malformed/non-matching report should surface an error.

Suggested fix
-      txnRows = parseSectionRows `@CCAvenueTxnRow` "Transaction Summary" sections.transaction
+      txnRows = parseRequiredSectionRows `@CCAvenueTxnRow` "Transaction Summary" sections.transaction
@@
 parseSectionRows :: (Csv.FromNamedRecord a) => Text -> [Text] -> [Either Text a]
 parseSectionRows _ [] = []
 parseSectionRows sectionName sectionLines =
   let csvBytes = LBS.fromStrict . encodeUtf8 . T.unlines $ sectionLines
    in case Csv.decodeByName csvBytes of
         Left err -> [Left $ sectionName <> " CSV parse error: " <> T.pack err]
         Right (_, rows) -> map Right (V.toList rows)
+
+parseRequiredSectionRows :: (Csv.FromNamedRecord a) => Text -> [Text] -> [Either Text a]
+parseRequiredSectionRows sectionName [] = [Left $ "Missing " <> sectionName <> " section"]
+parseRequiredSectionRows sectionName sectionLines = parseSectionRows sectionName sectionLines
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs`
around lines 99 - 105, The base case of parseSectionRows currently returns an
empty list when sectionLines is empty, which silently ignores a missing section
in a non-empty file. Change the pattern match for the empty section lines case
(currently parseSectionRows _ [] = []) to return an error indicating that the
expected section was not found, using the sectionName parameter to identify
which section is missing. This ensures that a malformed or non-matching report
with missing required sections surfaces an error instead of appearing as a
successful empty parse.

Comment on lines +126 to +131
txnType' = parseCCAvenueTxnType row.txnType
settlementType' = case txnType' of
ORDER -> Just CREDIT
REFUND -> Just DEBIT
CHARGEBACK -> Just DEBIT
Right

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reject unknown transaction types instead of crediting them as orders.

The _ -> ORDER fallback turns any new/misspelled CCAvenue document type into a CREDIT settlement. Unknown financial transaction types should fail the row so reconciliation can be corrected.

Suggested fix
-      txnType' = parseCCAvenueTxnType row.txnType
-      settlementType' = case txnType' of
+  txnType' <- parseCCAvenueTxnType idx row.txnType
+  let settlementType' = case txnType' of
         ORDER -> Just CREDIT
         REFUND -> Just DEBIT
         CHARGEBACK -> Just DEBIT
@@
-parseCCAvenueTxnType :: Text -> TxnType
-parseCCAvenueTxnType t = case T.toLower (T.strip t) of
+parseCCAvenueTxnType :: Int -> Text -> Either Text TxnType
+parseCCAvenueTxnType idx t = case T.toLower (T.strip t) of
   "payment" -> ORDER
   "sale" -> ORDER
   "refund" -> REFUND
   "chargeback" -> CHARGEBACK
-  _ -> ORDER
+  _ -> Left $ "Row " <> T.pack (show idx) <> ": unknown CCAvenue transaction type: " <> t

Also applies to: 196-202

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs`
around lines 126 - 131, The `parseCCAvenueTxnType` function contains a fallback
pattern that defaults unknown transaction types to ORDER, which incorrectly
treats them as valid CREDIT settlements. Remove the `_ -> ORDER` catch-all
pattern and instead implement explicit error handling for unknown transaction
types so that rows with unrecognized or misspelled CCAvenue document types fail
validation and can be caught during reconciliation. This change applies to the
function definition as well as any other call sites where unknown transaction
types are similarly defaulted to ORDER (also check lines 196-202).

Comment on lines +140 to +143
txnAmount = parseAmount row.amount, -- Charged Amount → Amount
pgBaseFee = parseAmount row.tdrAmount, -- PG Fee → TDR Amount
pgTax = parseAmount row.tax, -- GST on PG Fee → Tax
settlementAmount = parseAmount row.payableAmount, -- Net Settled Amount → Payable Amount

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect parseAmount implementation and CCAvenue required amount usage.
# Expected: parseAmount should not silently default invalid required settlement amounts to zero.

rg -n -C4 'parseAmount\s*::|parseAmount\s+' --iglob '*.hs'
rg -n -C2 'txnAmount\s*=\s*parseAmount|pgBaseFee\s*=\s*parseAmount|pgTax\s*=\s*parseAmount|settlementAmount\s*=\s*parseAmount' --iglob '*.hs'

Repository: nammayatri/shared-kernel

Length of output: 32540


🏁 Script executed:

# Get the context of parseMergedRow function in CCAvenue PaymentParser
sed -n '120,165p' lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs

Repository: nammayatri/shared-kernel

Length of output: 2446


🏁 Script executed:

# Check if parseRequiredAmount exists anywhere
rg -n 'parseRequiredAmount' --iglob '*.hs'

Repository: nammayatri/shared-kernel

Length of output: 50


🏁 Script executed:

# Check the full ParserUtils module to understand available helpers
cat -n lib/mobility-core/src/Kernel/External/Settlement/Utils/ParserUtils.hs

Repository: nammayatri/shared-kernel

Length of output: 1882


Fail rows with malformed monetary fields.

These required financial fields—txnAmount, pgBaseFee, pgTax, settlementAmount—use parseAmount, which silently coerces unparseable values to zero. This allows bad CCAvenue data to pass through as valid ₹0 reports without marking rows as failed.

To fix this, create a parseRequiredAmount helper (or similar error-handling variant) that returns an Either Text HighPrecMoney and call it in the do-notation at the start of parseMergedRow. Then use the validated values in the record construction instead of calling parseAmount directly on raw fields.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Settlement/CCAvenue/PaymentParser.hs`
around lines 140 - 143, The `parseAmount` function silently coerces unparseable
monetary values to zero, allowing invalid CCAvenue data to pass validation.
Create a new helper function `parseRequiredAmount` that returns `Either Text
HighPrecMoney` to properly surface parsing errors. In the `parseMergedRow`
function, use do-notation at the start to sequentially validate the four
required amount fields—`row.amount`, `row.tdrAmount`, `row.tax`, and
`row.payableAmount`—by calling `parseRequiredAmount` on each, then assign the
successful results to variables (e.g., `validatedTxnAmount`,
`validatedPgBaseFee`, `validatedPgTax`, `validatedSettlementAmount`). Finally,
in the record construction, replace the direct `parseAmount row.fieldName` calls
with these validated variables so malformed amounts cause the row to fail rather
than silently become zero.

@RahulGupta0706 RahulGupta0706 force-pushed the feat/parser-logic-addition-for-razorpay-ccavenue-pg branch from eb9b086 to 918020d Compare June 16, 2026 10:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants