WorldSociety

NepalPay v1. 2. 0 — Metrics, Health Indicators, and Everything CodeRabbit Caught

My previous article ended with NepalPay being published to Maven Central. Khalti Refund API ✅ v0. 5. 0 Retry with Backoff ✅ v0. 6. 0 Maven Central ✅ v1. 0. 0Enter fullscreen modeExit fullscreen modeThe library. . .

· 4 min read

NepalPay v1. 2. 0 — Metrics, Health Indicators, and Everything CodeRabbit Caught

My previous article ended with NepalPay being published to Maven Central. Khalti Refund API ✅ v0. 5. 0 Retry with Backoff ✅ v0. 6. 0 Maven Central ✅ v1. 0. 0Enter fullscreen modeExit fullscreen modeThe library worked. Tests passed. Developers could install it with a single dependency. But there was still one major problem. What happens when something goes wrong in production?Not whether a payment succeeds—that's whatlookupPayment()is for. I mean:Is the gateway configured correctly?Is it running against Sandbox or Production?How long are API calls taking?How often are retries actually happening?Are callback signature failures increasing?Until now, NepalPay couldn't answer any of those questions. Version1. 2. 0changes that. Micrometer Metrics ✅ Health Indicators ✅ Reactive Improvements ✅ 400+ Tests ✅Enter fullscreen modeExit fullscreen modeThe ProblemImagine a Khalti payment suddenly starts failing. Without observability you only know one thing:The payment failed. You don't know:whether it timed outwhether retry firedhow long it tookwhether 1 request failed or every request failedProduction systems need answers. Micrometer MetricsNepalPay now records metrics automatically wheneverspring-boot-starter-actuatoris present. No configuration required. Every gateway records operation-specific timers. nepalpay. khalti. payment. initiate. durationnepalpay. khalti. payment. lookup. durationnepalpay. khalti. payment. refund. durationnepalpay. esewa. callback. verify. durationnepalpay. esewa. status. check. durationnepalpay. connectips. validate. durationEnter fullscreen modeExit fullscreen modeEach metric is tagged withgatewaysandbox/productionsuccess/errorThat means Grafana can immediately answer questions like:What is the P99 latency for Khalti payment initiation?Enter fullscreen modeExit fullscreen modehistogram_quantile( 0. 99, rate(nepalpay_khalti_payment_initiate_duration_seconds_bucket[5m]) )Enter fullscreen modeExit fullscreen modeRetry CountersRetries are now measurable too. nepalpay. khalti. retry. attemptsEnter fullscreen modeExit fullscreen modeOne interesting bug appeared during development. Originally all reactive retry paths shared one helper:metrics. incrementInitiateRetry();Enter fullscreen modeExit fullscreen modeThat meant:lookup retries incremented initiaterefund retries incremented initiateThe metrics were wrong. CodeRabbit spotted it during review. The fix was simple:Pass a retry callback into every operation instead of hardcoding one counter. Security MetricsSignature verification failures are now tracked. nepalpay. esewa. callback. signature. failednepalpay. fonepay. callback. signature. failedEnter fullscreen modeExit fullscreen modeSuddenly these become security alerts instead of silent failures. Example Grafana alert:rate(nepalpay_esewa_callback_signature_failed_total[5m]) > 5Enter fullscreen modeExit fullscreen modeActuator Health IndicatorsEvery configured gateway automatically registers its own health component. GET /actuator/healthEnter fullscreen modeExit fullscreen modeExample:{"status":"UP","components":{"nepalpayKhalti":{"status":"UP","details":{"gateway":"Khalti","mode":"SANDBOX"}},"nepalpayConnectIps":{"status":"UP","details":{"pfxLoaded":true}}}}Enter fullscreen modeExit fullscreen modeNotice something:There isno HTTP ping. That was intentional. Sandbox APIs often rate limit. Health checks should verify configuration—not internet connectivity. Reactive Starter ImprovementsThe reactive starter shipped inv1. 1. 0. Version1. 2. 0hardened it. Every validation step now lives insideMono. defer(). Instead of throwing exceptions immediately:validateRequest(request);Enter fullscreen modeExit fullscreen modeeverything now becomes a proper reactive error signal:returnMono. defer(()->{validateRequest(request);returnwebClient. post(). . . });Enter fullscreen modeExit fullscreen modeThis keeps operators like:onErrorResume()onErrorReturn()retryWhen()working correctly. Reactive TimingMicrometer's traditional timing API is blocking. Reactive applications require a different pattern. Timer. Samplesample=Timer. start();returnsource. doOnSuccess(v->sample. stop(. . . )). doOnError(e->sample. stop(. . . ));Enter fullscreen modeExit fullscreen modeNo blocking. No scheduler switching. Pure Reactor. What CodeRabbit FoundI use CodeRabbit on every PR. For v1. 2. 0 it found19 issues. The most important ones:Retry counters attributed to the wrong operationEvery retry became an initiate retry. Fixed. Missing timer inside verifyCallback()Internal calls bypassed the public timed method. Status metrics disappeared. Fixed. Logging decoded callback JSONOriginally:log. debug(jsonString);Enter fullscreen modeExit fullscreen modeThat JSON is attacker-controlled. Removed. Transport failures skipped retryNetwork failures were wrapped as generic exceptions. Retries never happened. Now transport failures are caught separately. Constant-time signature comparisonReplacedString. equals()Enter fullscreen modeExit fullscreen modewithMessageDigest. isEqual()Enter fullscreen modeExit fullscreen modeto avoid timing attacks. Multi-Module ChallengeOne design problem surprised me. Where should the metrics classes live?Originally they lived inside the Boot 3 starter. The reactive starter depended on Boot 3. Spring Boot then reported:Duplicated prefix 'nepalpay'Enter fullscreen modeExit fullscreen modeThe solution:Move all metrics classes intonepal-pay-coreEnter fullscreen modeExit fullscreen modeEvery starter already depends on it. No duplicate configuration. No circular dependencies. Spring Boot 4. 1. 0 Health APIBoot 4. 1. 0 moved health APIs fromorg. springframework. boot. actuate. healthEnter fullscreen modeExit fullscreen modetoorg. springframework. boot. health. contributorEnter fullscreen modeExit fullscreen modeand split them into a dedicated module. Boot 4 therefore requires:org. springframework. bootspring-boot-healthEnter fullscreen modeExit fullscreen modeZero ConfigurationSimply add:spring-boot-starter-actuatorEnter fullscreen modeExit fullscreen modeEverything else configures automatically. Disable if desired:nepalpay:metrics:enabled:falsehealth:enabled:falseEnter fullscreen modeExit fullscreen modeWhat's NextUpcoming roadmap:ConnectIPS configurable timeoutKotlin exampleseSewa Refund APIWebhook supportGitHubhttps://github. com/sujankim/nepal-pay-spring-boot-starterDocumentationhttps://sujankim. github. io/nepal-pay-spring-boot-starter/Maven Centralhttps://central. sonatype. com/search?q=nepal-payIf NepalPay saves you time, consider giving the project a ⭐ on GitHub. It helps more Nepali developers discover the library.

Source: dev.to

More Health news