Skip to content

feat: PM-KISAN application status in the Grievance plugin [#60] - #91

Open
KrutikaPhirangi wants to merge 2 commits into
feat/60-pmkisan-grievancefrom
feat/60-pmkisan-application-status
Open

KrutikaPhirangi wants to merge 2 commits into
feat/60-pmkisan-grievancefrom
feat/60-pmkisan-application-status

Conversation

@KrutikaPhirangi

@KrutikaPhirangi KrutikaPhirangi commented Oct 23, 2026 •

Copy link
Copy Markdown

What

PM-KISAN application status in the Grievance plugin, on the SchemeApplication pack. Stacked on #71 (PM-KISAN grievance); review that first. ADRs are in #92.

Action Upstream Mapping
init /ChatbotOTP pmkisan/application-status.init.yaml
status /ChatbotOTPVerified, /ChatbotUserDetails, then /ChatbotBeneficiaryStatus pmkisan/application-status.status.yaml

Commits

  1. Envelope: aesCbcKeyInBand as a provider's main envelope, verify on the provider's own host, and a follow-up read whose answer is added to the first (absent if it fails). ADR-0005.
  2. Application status: pmkisan-chatbot provider block and binding key, mappings, tests. The farmer is named by enrolmentId or applicant.mobile; the mobile is never echoed. on_status returns only allow-listed fields: enrolmentId, registeredOn, latestInstalmentPaid, ekyc, blockers. No registration is a 400.

Testing

  • go test ./pkg/plugin/implementation/Grievance/... ./pkg/plugin/implementation/internal/...
  • End to end against the mock PM-KISAN chatbot in quick-start: init by mobile and by enrolmentId, status with blockers, with none, and no registration.

@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

🛡️ Trivy security scan (CRITICAL,HIGH,MEDIUM,LOW)

View full run

Go dependencies

No findings at CRITICAL,HIGH,MEDIUM,LOW.

Container image

No findings at CRITICAL,HIGH,MEDIUM,LOW.

@github-actions

Copy link
Copy Markdown

📊 Test Coverage: ✅ Passed — 81% of changed lines covered, min 80%

@manjudr manjudr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@KrutikaPhirangi - Please walk through the code, Some part I am unable to understand it.

- id: Grievance
config:
bindingKeys: "pmfby|openagrinet:Grievance,pmkisan|openagrinet:Grievance"
bindingKeys: "pmfby|openagrinet:Grievance,pmkisan|openagrinet:Grievance,pmkisan-chatbot|openagrinet:SchemeApplication"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why is it a chatbot? Can we keep it as PMKisan only?


// followUp adds a second read's answer under field after a successful call;
// on failure the field is absent, never empty (ADR-0005).
type followUp struct {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What does 'follow-up' mean? Why do we need this? And why is this in common directory? any other plugins are depending on it?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Because of the earlier review on #71 asking to move the envelope out of Grievance, and I moved the follow-up along with it.

only the encryption is a fair fit for common, the follow-up isn't. I can move it to Grievance

Comment on lines +271 to +294
default:
// The participant is the OTP service: verify on its host.
cbc, onOTPService := e.base.(*cbcCodec)
if !onOTPService {
return nil, fmt.Errorf("%s: otpVerifyPath needs otpEnvelope, or envelope %s: the OTP is verified on the OTP service",
provider, envelopeAESCBCKeyInBand)
}
e.verify = &verify{provider: provider, path: s.verifyPath, actions: verifyActions, codec: cbc,
host: participantHost, hooks: hooks, client: client, budget: model.ActionPlan{TimeoutMs: verifyTimeoutMs}}
}

followUpActions := listSet(s.followUpActions)
switch {
case s.followUpPath == "" && s.followUpField == "" && len(followUpActions) == 0:
case s.followUpPath == "" || s.followUpField == "" || len(followUpActions) == 0:
return nil, fmt.Errorf("%s: followUpPath, followUpField and followUpActions are set together or not at all", provider)
default:
for action := range followUpActions {
if e.otpActions[action] && e.otp == nil || !e.otpActions[action] && e.base == nil {
return nil, fmt.Errorf("%s: followUpActions names %s, which has no envelope to send a follow-up through", provider, action)
}
}
e.followUp = &followUp{provider: provider, path: s.followUpPath, field: s.followUpField, actions: followUpActions,
succeeded: hooks.Succeeded, client: client, budget: model.ActionPlan{TimeoutMs: followUpTimeoutMs}}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wasn't able to understand this? Why are making it as default? Probably you may need to walkthrough this @KrutikaPhirangi

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Sure

Comment on lines +53 to 58

"followUpPath": func(s *envelopeSettings, v string) { s.followUpPath = v },
"followUpField": func(s *envelopeSettings, v string) { s.followUpField = v },
"followUpActions": func(s *envelopeSettings, v string) { s.followUpActions = v },
"followUpTimeoutMs": func(s *envelopeSettings, v string) { s.followUpTimeoutMs = v },
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What is "followup" What is the use case here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

a second call to PM-KISAN, made automatically after the first one succeeds, whose answer is added to the first.

The use case: PM-KISAN application status needs two calls to the chatbot service:

  1. /ChatbotUserDetails returns the registration: registration number, registration date, instalments paid, eKYC.
  2. /ChatbotBeneficiaryStatus returns what is blocking payment, for example "Income Tax Payee" or "Land Seeding, KYS".

The second call becomes application.blockers in on_status, so the farmer learns why the money isn't coming. A Beckn status maps to one registry call, so the envelope makes the second call itself and passes both answers to the mapping.

: $exists($match($d, /^\/Date\([0-9]+([+-][0-9]{4})?\)\/$/)[0])
? $fromMillis($number($split($substring($d, 6), /[^0-9]/)[0]), $istPicture, "+0530")
: $exists($match($d, /^[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{4}/)[0])
? (

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why we need this complex extraction logic?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fair point. The four formats were a guess, because we hadn't seen a real DateOfRegistration I'll check a real staging response and keep only that format, leaving the date out if it doesn't match.

@manjudr
manjudr added this pull request to stack #87 October 23, 2026 09:03
@KrutikaPhirangi
KrutikaPhirangi force-pushed the feat/60-pmkisan-application-status branch from af4f878 to 128895b Compare October 23, 2026 10:00
…llow-up read [#60]

aesCbcKeyInBand can be a provider's main envelope, and such a provider
verifies the challenge on its own host. followUpPath, followUpField and
followUpActions make a second read after a successful call and add its
answer under followUpField, on its own budget (followUpTimeoutMs); a
failed follow-up leaves the field absent.
PM-KISAN application status on the SchemeApplication pack, served by the
chatbot service (pmkisan-chatbot): init sends an OTP for an enrolmentId or
applicant.mobile; status verifies it, reads /ChatbotUserDetails, then
/ChatbotBeneficiaryStatus for what is holding payment up. Only
allow-listed fields are returned; the mobile is never echoed. No
registration is a 400 BIZ_GENERIC_ERROR.
@KrutikaPhirangi
KrutikaPhirangi force-pushed the feat/60-pmkisan-application-status branch from 128895b to 8c5637c Compare October 23, 2026 10:41
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