Skip to content

chore(claude): refresh razorpay skills/agents against live Razorpay docs#815

Open
teetangh wants to merge 2 commits into
devfrom
chore/razorpay-skills-refresh
Open

chore(claude): refresh razorpay skills/agents against live Razorpay docs#815
teetangh wants to merge 2 commits into
devfrom
chore/razorpay-skills-refresh

Conversation

@teetangh

@teetangh teetangh commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Why

The razorpay skill/agent bundle (9 agents + 15 skills under .claude/) was drafted when Razorpay's documentation was incomplete or inaccurate. A four-domain audit against the live docs (2026-06-07) found claims ranging from stale to production-breaking. This PR applies every verified correction. The audit cross-checked razorpay.com/docs, the official razorpay/markdown-docs source repo, and the project's pinned razorpay@2.9.6 SDK source.

Highest-impact corrections

  • plan-change rewritten: the "create new subscription + deferred cancellation, no proration" thesis is obsolete — PATCH /v1/subscriptions/:id (schedule_change_at: now|cycle_end) does native in-place plan changes with proration. pendingUpdate()/cancelScheduledChanges() verified present in the pinned SDK.
  • razorpay-invoice restructured: Razorpay's Invoice API creates non-GST invoices only (tax fields cannot be applied via API) — the CGST/SGST-as-line-items pattern was not compliant. The agent now self-generates GST invoice records and adds the IGST place-of-supply branch (inter-state customers were mis-billed CGST+SGST).
  • Refund model fixed everywhere: statuses are pending/processed/failed (no created/initiated), speeds normal/optimum (no optimized), events are refund.* (the documented payment.refund.* names don't exist — handlers would silently process zero refund webhooks). Added refund.speed_changed + X-Refund-Idempotency.
  • go-live auto-capture claim un-inverted: auto-capture is ON by default; added the ~3-day uncaptured auto-refund window and T+2/T+7 settlement defaults.
  • Fictional subscription params removed: notify_info, pause_initiated_by/resume_initiated_by (real: pause_at/resume_at), customer_id on create; customer_notify is a boolean. Native trials (start_at) and setup fees (addons) documented as first-class, not workarounds.
  • Webhook truths: field is current_end (current_period_end is a Stripe-ism), retries back off for 24h then the webhook is auto-disabled (not "indefinite"), x-razorpay-event-id is stable across retries; added dashboard replay (≤15 days) + payment.dispute.* events.
  • Test credentials refreshed: current documented card numbers (4100 2800… family), OTP digit-length rule (no fixed 1234), failure@razorpay.
  • Kept deliberately: subscriptions.cancel(id, boolean) — correct for the pinned razorpay@2.9.6; the options-object form silently inverts {cancel_at_cycle_end: false} to a cycle-end cancel on this version (documented as a foot-gun).

Scope

21 files changed (+544/−360). setup/razorpay-setup/help verified clean and untouched. No application code touched.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation
    • Clarified Razorpay flows: subscription/customer handling, plan-change UX, and short_url-hosted checkout
    • Added GST-compliant invoice guidance with place-of-supply split (CGST+SGST vs IGST) and integer paise amounts
    • Strengthened webhook guidance: retries, exponential backoff, auto-disable after sustained failures, and alert-email behavior
    • Expanded refund guidance: refund.* events, idempotency header usage, status lifecycle, and updated testing scenarios and cards

Full audit of all 24 razorpay .claude files against current docs
(2026-06-07) — the bundle predated complete Razorpay documentation.
Corrections: refund enums/events (pending/optimum, refund.* not
payment.refund.*), fictional subscription params removed (notify_info,
pause/resume_initiated_by, customer_id on create), go-live auto-capture
claim un-inverted, current test cards/OTP rules, current_end (not
current_period_end), 24h-then-auto-disable webhook retry model.
Rewrites: plan-change now uses the Update Subscription API (native
proration) instead of deferred cancellation; razorpay-invoice generates
its own GST invoices (API invoices are non-GST) with the IGST
place-of-supply branch. Added: subscription signature order
(payment_id|subscription_id), X-Refund-Idempotency, settlements,
webhook replay, dispute events. cancel(id, boolean) guidance kept —
verified correct for the pinned razorpay@2.9.6.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@netlify

netlify Bot commented Jun 7, 2026

Copy link
Copy Markdown

Deploy Preview for familiarise failed. Why did it fail? →

Name Link
🔨 Latest commit 700881d
🔍 Latest deploy log https://app.netlify.com/projects/familiarise/deploys/6a254b121a4c3a00080393c7

@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Comprehensive Razorpay integration documentation update covering GST-compliant invoice generation with place-of-supply logic, refund idempotency headers and status tracking, optional subscription customer pre-creation, in-place plan updates, webhook production safety (return 200 for unhandled events), and field corrections (current_end vs current_period_end). Updates affect 23 agent and skill documentation files with 337 lines changed.

Changes

GST-Compliant Invoice System

Layer / File(s) Summary
GST Calculation & Place-of-Supply Logic
.claude/agents/razorpay-invoice.md
calculateGST function refactored to accept placeOfSupply parameter, compute GST via inclusive back-calculation, and conditionally split into CGST+SGST (9%+9% intra-state) or IGST (18% inter-state) with corresponding rates and boolean isInterState flag.
Invoice Schema & Storage
.claude/agents/razorpay-db-schema.md, .claude/agents/razorpay-invoice.md
gstInvoices table updated with nullable CGST/SGST/IGST amounts depending on intra/inter-state split, placeOfSupply column added to determine split type; createInvoice function signature extended to accept placeOfSupply parameter.
Invoice Creation & Razorpay Integration
.claude/agents/razorpay-invoice.md
Invoice creation flow redefined: GST breakout computed via calculateGST(totalAmountPaise, placeOfSupply), sequential GST invoice number generated as compliance source of truth, Razorpay invoice optionally created as single non-GST full-amount line item (no tax lines). Invoice listing and HTML rendering show conditional GST breakdown (CGST+SGST for intra-state, IGST for inter-state).
GST Compliance Rules & Audit Checks
.claude/agents/razorpay-invoice.md, .claude/agents/razorpay-code-audit.md
Documentation reinforces place-of-supply-driven split rules, SAC code acceptance (998314/998315), Razorpay non-GST constraint, prohibition on faking CGST/SGST in Razorpay invoices, and integer-paise requirement for all GST fields. Code audit checklist adds explicit place-of-supply validation for IGST (inter-state) vs CGST+SGST (intra-state).

Refund Idempotency & Status Lifecycle

Layer / File(s) Summary
Refund API Routes & Idempotency Headers
.claude/skills/razorpay/refund/SKILL.md
Full and partial refund routes now pass X-Refund-Idempotency header on razorpay.payments.refund() calls to prevent duplicate refunds on retries. Refund records persisted with status initialized to "pending" plus separate speedRequested and speedProcessed fields, replacing prior single speed field.
Refund Webhook Event Handling
.claude/skills/razorpay/refund/SKILL.md
Webhook handler listens for refund.* events (refund.created, refund.processed, refund.failed, refund.speed_changed) instead of payment.refund.*. refund.created stores records with "pending" status; refund.processed marks as processed; refund.speed_changed updates speedProcessed only; new speed terminology (optimum vs normal) documented.
Refund Persistence & Polling Logic
.claude/skills/razorpay/refund/SKILL.md, .claude/agents/razorpay-db-schema.md
upsertRefund function updated to accept and persist speedRequested/speedProcessed fields; new updateRefundSpeedProcessed helper introduced for speed_changed events. Refund status polling changed to check for "pending" status before fetching from Razorpay. DB schema: refunds table status defaults to "pending" and replaces speed column with speedRequested/speedProcessed.
Refund Testing & Admin Documentation
.claude/skills/razorpay/refund/SKILL.md, .claude/agents/razorpay-code-audit.md, .claude/skills/razorpay/admin/SKILL.md, .claude/skills/razorpay/local-testing/SKILL.md
Code audit checks expanded to include refund/payout idempotency header validation. Webhook event reference updated with refund.* names and revised speed field changes. Admin docs updated with new refund speed terminology. Local testing clarifies test mode refunds are typically immediate (live mode: 5–7 business days).

Subscription Customer & Plan Management

Layer / File(s) Summary
Subscription Creation Without Pre-Created Customer
.claude/agents/razorpay-subscription.md, .claude/skills/razorpay/subscription/SKILL.md
Subscription creation refactored: customer_id parameter removed from subscriptions.create() (Razorpay auto-creates during auth), customer_notify set as boolean true, customer pre-creation marked as optional, and subscription.customer_id persisted from create response. Duplicate prevention via dedup on notes fields remains.
Trial & Setup Fee Handling
.claude/skills/razorpay/subscription/SKILL.md
Trial mechanism changed from "no native trial" to using start_at as Razorpay's official trial delay. New Gotcha explains checkout authorization with auto-refunded token auth. New "Upfront Extra Charge (Setup Fee)" section shows use of addons for first-payment-only extra charge.
Plan Changes via In-Place Update
.claude/skills/razorpay/plan-change/SKILL.md
Plan change flow switched from deferred cancellation + create-new-subscription to in-place update using razorpay.subscriptions.update() with plan_id and schedule_change_at ("now" or "cycle_end") based on applyNow flag. Includes proration behavior, inspecting/canceling scheduled changes, and client-side upgrade/downgrade UX guidance.
Payment Method Re-Authorization via short_url
.claude/skills/razorpay/customer-portal/SKILL.md, .claude/skills/razorpay/dunning/SKILL.md
Payment method update flow refactored: no longer requires cancel+recreate subscription. Fetches existing subscription and returns persisted short_url for re-authorization on both customer-portal and dunning routes. Removes prior customer pre-creation logic.
Subscription Customer & Offers Documentation
.claude/skills/razorpay/subscription/SKILL.md
Explicit "Customer Handling" section forbids customer_id parameter and describes optional pre-creation for phone/contact fields. "Offer / Coupon Codes" expanded to include API-supported linking/unlinking for existing subscriptions. Signature verification foot-gun calls out `payment_id
Dunning Timeline & Retry Semantics
.claude/skills/razorpay/dunning/SKILL.md
Describes Razorpay retries as method-dependent (no fixed count/interval). payment.failed sent per retry attempt. In-place update plus short_url re-auth used for failed payment recovery (no new subscription creation). Gotchas updated to remove ephemeral short_url framing and mandatory resubscribe guidance.

Webhook Handler Production Safety & Idempotency

Layer / File(s) Summary
Unhandled Event & Error Handling Rules
.claude/agents/razorpay-webhook.md, .claude/agents/razorpay-diagnostics.md, .claude/skills/razorpay/webhook/SKILL.md
Critical rules updated: unhandled events must return 200 (not 5xx) to avoid Razorpay auto-disable after exponential backoff up to 24 hours and alert email. Permanent/invalid payload errors swallowed and return 200. Only genuinely transient failures return 5xx.
Subscription Period-End Field Corrections
.claude/agents/razorpay-webhook.md, .claude/skills/razorpay/webhook/SKILL.md, .claude/skills/razorpay/debug/SKILL.md
Period-end field extraction corrected: replace Stripe-ism current_period_end with Razorpay-native current_end (Unix seconds with fallback to end_at). Mock webhook payloads (subscription.activated, subscription.charged) updated to use current_end.
Idempotency & Processed-Events Defense-in-Depth
.claude/skills/razorpay/webhook/SKILL.md, .claude/agents/razorpay-code-audit.md
Idempotency guidance expanded: processed-events table acts as Layer 2 lookup using stable x-razorpay-event-id header across retries. Code audit checks added for processed_webhook_events table requirement.
Subscription Cancellation SDK Quirk
.claude/agents/razorpay-diagnostics.md, .claude/skills/razorpay/debug/SKILL.md, .claude/skills/razorpay/webhook/SKILL.md
subscriptions.cancel() second parameter requires positional boolean (true/false) on razorpay@2.9.6. Foot-gun documented: passing options object silently interpreted truthy, causes incorrect cancellation timing (cycle-end instead of immediate).
Webhook Events Reference & Dispute Lifecycle
.claude/agents/razorpay-webhook.md, .claude/skills/razorpay/webhook/SKILL.md
Webhook events reference extended with payment.dispute.* lifecycle events (creation, evidence, resolution). Refund.* family events documented. Production webhook delivery logs location clarified (Developers → Webhooks).
Webhook Handler Testing & Production Setup
.claude/agents/razorpay-test-webhook.md, .claude/agents/razorpay-diagnostics.md
Webhook test agent updated with payment.error_source/error_step fields. Unhandled event test guidance documents 24-hour retry window + auto-disable + alert. Production setup guides refine error handling and timeout expectations.

SDK Quirks Reference & Local Testing

Layer / File(s) Summary
SDK Version-Dependent Quirks & Workarounds
.claude/skills/razorpay/debug/SKILL.md, .claude/agents/razorpay-diagnostics.md
SDK quirks reference expanded: subscriptions.cancel() boolean parameter requirement documented on razorpay@2.9.6. customers.create() fail_existing typing with version-dependent `0 as 0
Test Card Numbers & Refund Timing
.claude/skills/razorpay/local-testing/SKILL.md
Test card tables reworked with updated domestic success cards, new failure card examples with decline reasons, OTP/3DS guidance updated. Refund testing clarified (test mode: typically immediate; live mode: 5–7 business days). zrok mentioned as ngrok alternative for tunneling.
Webhook Testing & Simulation
.claude/agents/razorpay-test-webhook.md, .claude/skills/razorpay/local-testing/SKILL.md
Webhook test payloads extended with error fields. Unhandled event testing guidance refined to reflect 24-hour backoff + auto-disable behavior. Webhook registration steps updated with correct dashboard path (Account & Settings → Webhooks).
Debugging Guide & Troubleshooting
.claude/skills/razorpay/debug/SKILL.md
Debugging guide refines webhook troubleshooting with correct dashboard navigation. Expands SDK-specific guidance for cancel() and customers.create(). Includes payment-success-but-access-denied recovery via webhook replay and reconciliation endpoint. Razorpay SDK quirks reference section documented.

Admin & Operational Guidance

Layer / File(s) Summary
Settlements API & Reconciliation
.claude/skills/razorpay/admin/SKILL.md
New Settlements section added with commands to list settlements, fetch by ID, and run reconciliation via combined recon endpoint with year/month/count query parameters and jq projections.
Refund Field Metadata & Status Documentation
.claude/skills/razorpay/admin/SKILL.md
Refund admin documentation updated: status values list revised, speed_requested changed to speed_optimum terminology, speed_processed field added. jq projections updated to reflect new field set.
Subscription Status Enumeration
.claude/skills/razorpay/admin/SKILL.md
Subscription "Key fields" status list expanded to include pending, paused, completed, expired, and removal of halted from primary documented set.
Revenue Metrics & Settlement Timing
.claude/skills/razorpay/metrics/SKILL.md
Metrics gotchas updated: net revenue should use Razorpay settlements (and settlement recon API) rather than summing captured payments, due to fees/GST/refunds and settlement latency (T+2 domestic, T+7 international).
Pre-Launch Capture Verification Checklist
.claude/skills/razorpay/go-live/SKILL.md
Go-live checklist updated: auto-capture guidance replaced with explicit verification workflow covering Dashboard settings, order-level overrides, webhook impact, and auto-refund behavior for authorized-but-uncaptured payments within configured window.
One-Time vs Subscription Checkout
.claude/skills/razorpay/one-time-payment/SKILL.md, .claude/agents/razorpay-one-time-payment.md
One-time and subscription payment docs clarified: Standard Checkout supports both orders (via order_id) and subscriptions (via subscription_id). modal.ondismiss handler added for popup dismissal. callback_url + redirect: true fallback documented for popup-blocked/mobile contexts.
Checkout Signature Verification & Field Order
.claude/skills/razorpay/stripe-migration/SKILL.md, .claude/agents/razorpay-one-time-payment.md
Stripe-migration guide extended with "Checkout Signature (Subscriptions vs Orders)" section explaining client-side HMAC verification and critical field-order differences: subscriptions use `payment_id
subscription.create() customer_notify Parameter
.claude/agents/razorpay-subscription.md, .claude/skills/razorpay/stripe-migration/SKILL.md
Subscription and migration docs updated: customer_notify parameter changed from numeric to boolean true in all examples. Agent docs and examples align on boolean usage pattern.
Idempotency Keys & App-Level Dedup
.claude/skills/razorpay/stripe-migration/SKILL.md
Idempotency guidance extended: Razorpay provides per-endpoint idempotency headers (with examples for refund, payout, etc.). App-level dedup recommended as fallback for everything else including webhook processing.
Subscription Invoice & Auth Handling
.claude/skills/razorpay/subscription/SKILL.md, .claude/skills/razorpay/customer-portal/SKILL.md
Subscription and customer-portal docs clarify: Razorpay auto-mints NON-GST invoices per billing cycle (retrievable via invoices.all with short_url). Checkout authorization explained (auto-refunded token auth). Internal app status (cancelling, paused) distinguished from Razorpay subscription statuses.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Documentation grows in bunny bounds,
GST split by states and sounds,
Refunds dance with idempotency,
Plans update in place—no new spree,
Webhooks always answer "200"! 🎉

🚥 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 title accurately describes the main change: a refresh of Razorpay skills/agents against live documentation. It is concise, specific, and clearly conveys the primary scope without being vague.
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 chore/razorpay-skills-refresh

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

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request updates the Razorpay integration documentation, agents, and skills to align with GST compliance requirements (specifically handling place-of-supply for CGST/SGST vs. IGST and self-generating invoices), the in-place Update Subscription API, and updated SDK behaviors. The review feedback highlights that the official razorpay Node SDK does not support passing per-request headers (such as X-Refund-Idempotency) as a third argument to helper methods like payments.refund(). Additionally, the feedback points out inconsistencies in the webhook agent and skill templates where createGstInvoice still uses the legacy, non-compliant pattern, and suggests adding refund.failed and refund.speed_changed to the local testing webhook registration list.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread .claude/skills/razorpay/refund/SKILL.md Outdated
Comment thread .claude/skills/razorpay/refund/SKILL.md Outdated
Comment thread .claude/agents/razorpay-webhook.md
Comment thread .claude/skills/razorpay/webhook/SKILL.md
Comment thread .claude/skills/razorpay/local-testing/SKILL.md
Comment thread .claude/skills/razorpay/stripe-migration/SKILL.md Outdated

@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: 6

Caution

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

⚠️ Outside diff range comments (7)
.claude/agents/razorpay-webhook.md (2)

158-160: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Event mapping still uses current_period_end instead of the updated Razorpay field model

This table still tells implementers to persist/update current_period_end, which conflicts with your own Step 4b guidance (current_end, with conversion). That inconsistency will lead to mixed implementations.

Unify these rows to the same contract used later in the doc (read current_end, then map to your DB field).

Also applies to: 163-163

🤖 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 @.claude/agents/razorpay-webhook.md around lines 158 - 160, Update the event
mapping table rows for subscription.activated, subscription.charged and
subscription.pending to use the Razorpay field name current_end (with the same
conversion note from Step 4b) instead of current_period_end, and clarify that
implementers should read current_end from the webhook payload and then
map/convert it to the DB column (e.g., current_period_end) as described in Step
4b; ensure the subscription.pending rule still includes the "don't downgrade an
active subscription" guard and that the charged row mentions updating
current_end for the new billing cycle and triggering GST invoice creation.

224-225: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

GST invoice instructions are out of sync with the refreshed GST model

This section still mandates CGST/SGST split line-items as the default pattern. Per this PR’s stated GST rewrite, invoice generation should use place-of-supply logic and support IGST where applicable rather than hardcoding CGST/SGST-only decomposition.

Please update this agent’s GST instructions/snippet to match the new GST contract used in the invoice-focused docs.

Also applies to: 361-362

🤖 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 @.claude/agents/razorpay-webhook.md around lines 224 - 225, The
createGstInvoice() guidance is outdated: replace the hardcoded CGST/SGST split
with logic that uses the new GST contract and place-of-supply rules so invoices
choose CGST+SGST or IGST as appropriate before building Razorpay Invoice line
items; update the createGstInvoice() implementation (or its stub comment) to
reference and follow the invoice-focused GST contract, compute tax line items
based on placeOfSupply and supplier/state vs. customer/state, and include a note
on enabling GST generation per the new contract (also update the duplicate
guidance near the other occurrence corresponding to the same createGstInvoice()
behavior).
.claude/agents/razorpay-diagnostics.md (1)

141-142: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Schema check still enforces Stripe-style period field name

CHECK 4b still requires current_period_end, but this PR’s webhook contract elsewhere standardizes on Razorpay’s current_end (mapped into app field naming as needed). Keeping current_period_end as the required canonical column here will generate misleading diagnostics.

Please align this check with the updated field contract (accept/store the app’s mapped period-end field while sourcing from current_end in payloads).

🤖 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 @.claude/agents/razorpay-diagnostics.md around lines 141 - 142, CHECK 4b
currently enforces the Stripe-style column names (`current_period_end` /
`currentPeriodEnd`); update the check to accept the Razorpay contract by
requiring the period-end field to be `current_end` (and/or the app-mapped name
used elsewhere, e.g., `currentEnd`) as the canonical period-end column while
still accepting `last_event_id` / `lastEventId` for idempotency; modify the
validation logic that references `current_period_end` to also accept
`current_end` and the app field mapping so diagnostics reflect the new webhook
contract.
.claude/skills/razorpay/webhook/SKILL.md (1)

370-372: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Webhook skill still encodes the deprecated CGST/SGST-only invoice pattern

The GST section and sample implementation hardcode base+CGST+SGST line items, which conflicts with the refreshed GST model that depends on place-of-supply and may require IGST.

Please align this skill’s GST guidance/snippet with the new conditional tax logic so generated handlers don’t reintroduce outdated invoice behavior.

Also applies to: 382-405

🤖 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 @.claude/skills/razorpay/webhook/SKILL.md around lines 370 - 372, Update the
GST guidance and sample implementation in SKILL.md (the Razorpay webhook skill
GST section and its sample handler) to stop hardcoding base+CGST+SGST line items
and instead implement conditional tax logic: determine place-of-supply (seller
vs buyer state), compute tax type as CGST+SGST when intra-state or IGST when
inter-state, calculate tax amounts from the taxable base, and construct Razorpay
Invoice API line items accordingly (one base line item plus one or two tax line
items as required). Ensure the sample snippet clearly shows the place-of-supply
check, tax rate application, and creation of line items compatible with the
Invoice API so generated handlers will not reintroduce the deprecated
CGST/SGST-only pattern.
.claude/skills/razorpay/subscription/SKILL.md (1)

185-186: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Dedup response contract contradicts the route example.

This section says “block duplicate creation (409)”, but the route example returns 200 with the existing shortUrl for recent pending subscriptions. Pick one behavior and keep it consistent across the doc to avoid divergent implementations.

🤖 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 @.claude/skills/razorpay/subscription/SKILL.md around lines 185 - 186, The
doc currently contradicts itself: the deduplication bullets say "block duplicate
creation (409)" while the route example returns 200 with an existing shortUrl
for recent pending subscriptions; choose one behavior and make the doc
consistent by either (A) changing the bullet to describe returning 200 with the
existing shortUrl for recent (<1h) pending subscriptions, or (B) changing the
route example to return 409 and the corresponding error shape for duplicates;
update all mentions of "dedup", "shortUrl", and the route example so the
response code, body schema, and text align with the chosen behavior.
.claude/skills/razorpay/one-time-payment/SKILL.md (1)

19-25: ⚠️ Potential issue | 🟠 Major

Split/clarify subscription verification models (webhook-only vs Standard Checkout).

The table says subscription confirmation is webhook-only (subscription.activated), but the doc also states subscriptions run via Standard Checkout when passing subscription_id. Standard Checkout subscription flows require verifying the authorization callback signature (razorpay_payment_id / razorpay_subscription_id / razorpay_signature) using the API secret, in addition to validating the webhook signature using RAZORPAY_WEBHOOK_SECRET (e.g., X-Razorpay-Signature over the raw body). Please explicitly separate these variants (and the corresponding secrets) to prevent implementations from using the wrong signature/secret path.

🤖 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 @.claude/skills/razorpay/one-time-payment/SKILL.md around lines 19 - 25,
Update the subscription verification docs to split webhook-only vs Standard
Checkout flows: explicitly state that webhook-only subscriptions are validated
via the webhook event (e.g., subscription.activated) using
RAZORPAY_WEBHOOK_SECRET and X-Razorpay-Signature over the raw body, while
Standard Checkout subscriptions (when passing subscription_id to the JS
SDK/handler or using short_url with subscription_id) require verifying the
authorization callback signature (razorpay_payment_id, razorpay_subscription_id,
razorpay_signature) using RAZORPAY_KEY_SECRET (API secret); call out the exact
fields to verify for each path and ensure the table rows for "Verification",
"Key used for HMAC", and "Where it runs" reflect these two distinct models to
prevent mixing secrets.
.claude/skills/razorpay/stripe-migration/SKILL.md (1)

216-216: ⚠️ Potential issue | 🟠 Major

Fix short_url guidance in the Stripe→Razorpay migration skill

.claude/skills/razorpay/stripe-migration/SKILL.md states “No short_url retrieval… cannot be fetched after subscription creation”, but Razorpay’s API docs and the other skills in this repo say short_url is returned by razorpay.subscriptions.fetch(id) (so recovery runbooks shouldn’t assume you must recreate the subscription just because you lost the URL). Update/remove the bullet at line 216 to match the documented behavior (and clarify any lifecycle caveats only if they affect usability).

🤖 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 @.claude/skills/razorpay/stripe-migration/SKILL.md at line 216, Update the
note about short_url: remove or replace the claim that Razorpay's hosted
checkout URL "cannot be fetched after subscription creation" and instead state
that razorpay.subscriptions.fetch(id) returns short_url per Razorpay API; adjust
the bullet at line 216 to say you can recover the hosted checkout URL by calling
razorpay.subscriptions.fetch(subscriptionId) and only mention lifecycle caveats
if they change URL availability (e.g., if the subscription is canceled or
expired), referencing the short_url field and the fetch method in the text.
🤖 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 @.claude/agents/razorpay-invoice.md:
- Around line 83-90: Choose a single canonical contract for non-applicable tax
heads and apply it consistently: change calculateGST to return null for
non-applicable components (update calculateGST signature/return structure and
set cgstAmount, sgstAmount, igstAmount to null instead of 0 when not
applicable), and update the agent’s schema examples and insertion comments (the
table guidance and insert examples referenced near the schema examples and
insertion comments) to use null for non-applicable tax heads as well so all
examples, docs, and returned objects use the nullable convention.
- Around line 58-59: The prompt hardcodes SAC codes 998314/998315 which
conflicts with the project's existing payout SAC constants (currently the 999293
family); either update the canonical payout SAC constants in the codebase in the
same rollout or change this prompt to derive the SAC value from the project's
payout SAC constant(s) (the existing 999293-family constant) at runtime instead
of hardcoding 998314/998315 so generated invoices remain consistent with the
runtime constant.

In @.claude/skills/razorpay/customer-portal/SKILL.md:
- Line 359: The SKILL.md guidance is stale: it references a non-existent
createGstInvoice() webhook handler and the unused razorpay.invoices.create()
flow. Either implement the missing flow (add a createGstInvoice() implementation
and any code that calls razorpay.invoices.create() to generate GST-compliant
invoices and persists razorpay_invoice_id and short_url), or update the
documentation to reflect the current architecture by removing the
createGstInvoice() and invoices.create() mentions and replacing them with the
actual process used today (e.g., “issue GST invoices via Dashboard” or “our
system generates GST invoices via X”), and ensure the docs still instruct
storing razorpay_invoice_id and short_url if applicable.

In @.claude/skills/razorpay/go-live/SKILL.md:
- Line 16: The dashboard navigation label in the SKILL.md guidance is
inconsistent with other runbooks; update the line referencing "Account &
Settings → Payment Capture" to use the same phrasing as other docs (e.g.,
"Settings → Payments → Automatic capture delay") or include both labels
side-by-side (e.g., "Settings → Payments → Automatic capture delay (a.k.a.
Account & Settings → Payment Capture)") and keep the rest of the note about
Orders API `payment.capture` unchanged so operators can find the setting
reliably.

In @.claude/skills/razorpay/refund/SKILL.md:
- Around line 27-37: The Razorpay adapter calls razorpay.payments.refund but
does not attach the required X-Refund-Idempotency header; update the refund
creation code in the Razorpay integration (the function that invokes
razorpay.payments.refund) to pass an options/headers object including
"X-Refund-Idempotency" with a unique key (>=10 chars) such as
`refund-${paymentId}` (or another deterministic unique token), and apply the
same change to the other refund call sites referenced by the review (the other
razorpay.refund invocations); ensure the header is sent in the third argument to
razorpay.payments.refund so duplicate-protection is enforced.
- Around line 45-48: The docs reference refund.speedRequested and
refund.speedProcessed but the runtime persistence contract lacks those
columns/writes; either remove those fields from the SKILL.md examples or add
them to the persistence layer. To fix, update the persistence schema (the Refund
model) to include speed_requested and speed_processed (and run migration) and
then update the webhook-to-DB mapping in the refund processing code where the
refund object is built (the code that assigns refund.status) to set
refund.speedRequested = refund.speed_requested and refund.speedProcessed =
refund.speed_processed (or the inverse mapping from snake_case DB fields),
ensuring consistent field names across the Prisma model and the webhook/utils
mapping.

---

Outside diff comments:
In @.claude/agents/razorpay-diagnostics.md:
- Around line 141-142: CHECK 4b currently enforces the Stripe-style column names
(`current_period_end` / `currentPeriodEnd`); update the check to accept the
Razorpay contract by requiring the period-end field to be `current_end` (and/or
the app-mapped name used elsewhere, e.g., `currentEnd`) as the canonical
period-end column while still accepting `last_event_id` / `lastEventId` for
idempotency; modify the validation logic that references `current_period_end` to
also accept `current_end` and the app field mapping so diagnostics reflect the
new webhook contract.

In @.claude/agents/razorpay-webhook.md:
- Around line 158-160: Update the event mapping table rows for
subscription.activated, subscription.charged and subscription.pending to use the
Razorpay field name current_end (with the same conversion note from Step 4b)
instead of current_period_end, and clarify that implementers should read
current_end from the webhook payload and then map/convert it to the DB column
(e.g., current_period_end) as described in Step 4b; ensure the
subscription.pending rule still includes the "don't downgrade an active
subscription" guard and that the charged row mentions updating current_end for
the new billing cycle and triggering GST invoice creation.
- Around line 224-225: The createGstInvoice() guidance is outdated: replace the
hardcoded CGST/SGST split with logic that uses the new GST contract and
place-of-supply rules so invoices choose CGST+SGST or IGST as appropriate before
building Razorpay Invoice line items; update the createGstInvoice()
implementation (or its stub comment) to reference and follow the invoice-focused
GST contract, compute tax line items based on placeOfSupply and supplier/state
vs. customer/state, and include a note on enabling GST generation per the new
contract (also update the duplicate guidance near the other occurrence
corresponding to the same createGstInvoice() behavior).

In @.claude/skills/razorpay/one-time-payment/SKILL.md:
- Around line 19-25: Update the subscription verification docs to split
webhook-only vs Standard Checkout flows: explicitly state that webhook-only
subscriptions are validated via the webhook event (e.g., subscription.activated)
using RAZORPAY_WEBHOOK_SECRET and X-Razorpay-Signature over the raw body, while
Standard Checkout subscriptions (when passing subscription_id to the JS
SDK/handler or using short_url with subscription_id) require verifying the
authorization callback signature (razorpay_payment_id, razorpay_subscription_id,
razorpay_signature) using RAZORPAY_KEY_SECRET (API secret); call out the exact
fields to verify for each path and ensure the table rows for "Verification",
"Key used for HMAC", and "Where it runs" reflect these two distinct models to
prevent mixing secrets.

In @.claude/skills/razorpay/stripe-migration/SKILL.md:
- Line 216: Update the note about short_url: remove or replace the claim that
Razorpay's hosted checkout URL "cannot be fetched after subscription creation"
and instead state that razorpay.subscriptions.fetch(id) returns short_url per
Razorpay API; adjust the bullet at line 216 to say you can recover the hosted
checkout URL by calling razorpay.subscriptions.fetch(subscriptionId) and only
mention lifecycle caveats if they change URL availability (e.g., if the
subscription is canceled or expired), referencing the short_url field and the
fetch method in the text.

In @.claude/skills/razorpay/subscription/SKILL.md:
- Around line 185-186: The doc currently contradicts itself: the deduplication
bullets say "block duplicate creation (409)" while the route example returns 200
with an existing shortUrl for recent pending subscriptions; choose one behavior
and make the doc consistent by either (A) changing the bullet to describe
returning 200 with the existing shortUrl for recent (<1h) pending subscriptions,
or (B) changing the route example to return 409 and the corresponding error
shape for duplicates; update all mentions of "dedup", "shortUrl", and the route
example so the response code, body schema, and text align with the chosen
behavior.

In @.claude/skills/razorpay/webhook/SKILL.md:
- Around line 370-372: Update the GST guidance and sample implementation in
SKILL.md (the Razorpay webhook skill GST section and its sample handler) to stop
hardcoding base+CGST+SGST line items and instead implement conditional tax
logic: determine place-of-supply (seller vs buyer state), compute tax type as
CGST+SGST when intra-state or IGST when inter-state, calculate tax amounts from
the taxable base, and construct Razorpay Invoice API line items accordingly (one
base line item plus one or two tax line items as required). Ensure the sample
snippet clearly shows the place-of-supply check, tax rate application, and
creation of line items compatible with the Invoice API so generated handlers
will not reintroduce the deprecated CGST/SGST-only pattern.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fd1ae832-cf03-45f7-a368-ec88dbcc3ed7

📥 Commits

Reviewing files that changed from the base of the PR and between ec22f59 and cfee202.

📒 Files selected for processing (21)
  • .claude/agents/razorpay-code-audit.md
  • .claude/agents/razorpay-db-schema.md
  • .claude/agents/razorpay-diagnostics.md
  • .claude/agents/razorpay-invoice.md
  • .claude/agents/razorpay-one-time-payment.md
  • .claude/agents/razorpay-subscription.md
  • .claude/agents/razorpay-test-webhook.md
  • .claude/agents/razorpay-webhook.md
  • .claude/skills/razorpay/admin/SKILL.md
  • .claude/skills/razorpay/customer-portal/SKILL.md
  • .claude/skills/razorpay/debug/SKILL.md
  • .claude/skills/razorpay/dunning/SKILL.md
  • .claude/skills/razorpay/go-live/SKILL.md
  • .claude/skills/razorpay/local-testing/SKILL.md
  • .claude/skills/razorpay/metrics/SKILL.md
  • .claude/skills/razorpay/one-time-payment/SKILL.md
  • .claude/skills/razorpay/plan-change/SKILL.md
  • .claude/skills/razorpay/refund/SKILL.md
  • .claude/skills/razorpay/stripe-migration/SKILL.md
  • .claude/skills/razorpay/subscription/SKILL.md
  • .claude/skills/razorpay/webhook/SKILL.md

Comment thread .claude/agents/razorpay-invoice.md
Comment thread .claude/agents/razorpay-invoice.md Outdated
Comment thread .claude/skills/razorpay/customer-portal/SKILL.md Outdated
Comment thread .claude/skills/razorpay/go-live/SKILL.md
Comment thread .claude/skills/razorpay/refund/SKILL.md Outdated
Comment thread .claude/skills/razorpay/refund/SKILL.md
…late alignment, field-name drift

Verified against the pinned razorpay@2.9.6 source + live docs, not bot
claims. refund snippets now use direct REST calls (the SDK drops a
3rd-arg headers object, so X-Refund-Idempotency was never sent);
createGstInvoice templates in the webhook agent/skill + customer-portal
now self-generate place-of-supply-aware GST records (API invoices are
non-GST); calculateGST standardizes on null for non-applicable tax
heads; event-table/diagnostics rows read current_end from payloads;
subscription dedup returns 200 with the existing short_url; one-time
table splits the two subscription verification paths (callback HMAC is
payment_id|subscription_id with key secret — reversed vs orders);
stripe-migration drops the stale short_url gotcha; legacy
replacesSubscription auto-create labeled as pre-Update-API.

Part of #815 review feedback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@teetangh teetangh self-assigned this Jun 7, 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.

🧹 Nitpick comments (1)
.claude/skills/razorpay/stripe-migration/SKILL.md (1)

207-207: ⚡ Quick win

Consider breaking this long line for readability.

The content is accurate and important, but the 207-character line is difficult to scan. Consider reformatting as a multi-line paragraph or bullet list.

📝 Suggested formatting
-6. **Idempotency keys**: Stripe has a universal `Idempotency-Key` header on every API call. Razorpay's support is per-API rather than universal — specific endpoints accept their own header (e.g. `X-Refund-Idempotency` on refunds, `X-Payout-Idempotency` on payouts). For everything else (and for webhook processing), keep doing app-level dedup yourself. **SDK caveat**: razorpay-node (<=2.9.6) helper methods cannot send per-request headers — `payments.refund(id, params, x)` treats `x` as a callback and silently drops it. Call the REST endpoint directly (basic auth + the header) when you need an idempotency key.
+6. **Idempotency keys**: Stripe has a universal `Idempotency-Key` header on every API call. Razorpay's support is per-API rather than universal:
+   - Specific endpoints accept their own header (e.g. `X-Refund-Idempotency` on refunds, `X-Payout-Idempotency` on payouts)
+   - For everything else (and for webhook processing), keep doing app-level dedup yourself
+   - **SDK caveat**: razorpay-node (<=2.9.6) helper methods cannot send per-request headers — `payments.refund(id, params, x)` treats `x` as a callback and silently drops it. Call the REST endpoint directly (basic auth + the header) when you need an idempotency key.
🤖 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 @.claude/skills/razorpay/stripe-migration/SKILL.md at line 207, Break the
long single-line paragraph under the "Idempotency keys" section into multiple
lines for readability: convert it to a short multi-line paragraph or a bullet
list that separates the key points (Stripe universal Idempotency-Key vs Razorpay
per-endpoint headers like X-Refund-Idempotency and X-Payout-Idempotency), call
out the app-level dedup note for webhook processing, and keep the SDK caveat as
a separate line that references the razorpay-node (<=2.9.6) behavior and the
problematic helper signature payments.refund(id, params, x) so readers can
clearly see the recommendation to call the REST endpoint directly (basic auth +
header) when an idempotency key is required.
🤖 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.

Nitpick comments:
In @.claude/skills/razorpay/stripe-migration/SKILL.md:
- Line 207: Break the long single-line paragraph under the "Idempotency keys"
section into multiple lines for readability: convert it to a short multi-line
paragraph or a bullet list that separates the key points (Stripe universal
Idempotency-Key vs Razorpay per-endpoint headers like X-Refund-Idempotency and
X-Payout-Idempotency), call out the app-level dedup note for webhook processing,
and keep the SDK caveat as a separate line that references the razorpay-node
(<=2.9.6) behavior and the problematic helper signature payments.refund(id,
params, x) so readers can clearly see the recommendation to call the REST
endpoint directly (basic auth + header) when an idempotency key is required.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 144e0d82-e6a8-4831-a966-421bf42450ff

📥 Commits

Reviewing files that changed from the base of the PR and between cfee202 and 700881d.

📒 Files selected for processing (11)
  • .claude/agents/razorpay-diagnostics.md
  • .claude/agents/razorpay-invoice.md
  • .claude/agents/razorpay-webhook.md
  • .claude/skills/razorpay/customer-portal/SKILL.md
  • .claude/skills/razorpay/debug/SKILL.md
  • .claude/skills/razorpay/local-testing/SKILL.md
  • .claude/skills/razorpay/one-time-payment/SKILL.md
  • .claude/skills/razorpay/refund/SKILL.md
  • .claude/skills/razorpay/stripe-migration/SKILL.md
  • .claude/skills/razorpay/subscription/SKILL.md
  • .claude/skills/razorpay/webhook/SKILL.md
✅ Files skipped from review due to trivial changes (1)
  • .claude/skills/razorpay/local-testing/SKILL.md
🚧 Files skipped from review as they are similar to previous changes (6)
  • .claude/skills/razorpay/debug/SKILL.md
  • .claude/skills/razorpay/one-time-payment/SKILL.md
  • .claude/skills/razorpay/customer-portal/SKILL.md
  • .claude/skills/razorpay/refund/SKILL.md
  • .claude/agents/razorpay-invoice.md
  • .claude/skills/razorpay/subscription/SKILL.md

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.

1 participant