InitionTechnology.PaymentGateway 1.0.2

dotnet add package InitionTechnology.PaymentGateway --version 1.0.2
                    
NuGet\Install-Package InitionTechnology.PaymentGateway -Version 1.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="InitionTechnology.PaymentGateway" Version="1.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="InitionTechnology.PaymentGateway" Version="1.0.2" />
                    
Directory.Packages.props
<PackageReference Include="InitionTechnology.PaymentGateway" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add InitionTechnology.PaymentGateway --version 1.0.2
                    
#r "nuget: InitionTechnology.PaymentGateway, 1.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package InitionTechnology.PaymentGateway@1.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=InitionTechnology.PaymentGateway&version=1.0.2
                    
Install as a Cake Addin
#tool nuget:?package=InitionTechnology.PaymentGateway&version=1.0.2
                    
Install as a Cake Tool

PaymentGateway

Reusable Razorpay payment gateway library for .NET (targets .NET 10). Built for SaaS platforms where a marketplace (e.g. restaurant digital-menu ordering) needs to split payments instantly into each merchant/restaurant owner's own bank account using Razorpay Route, instead of holding funds centrally and transferring manually every 24 hours.

Features

  • One-line DI registration (AddRazorpayPaymentGateway)
  • Linked account onboarding — create restaurant owner Razorpay Route accounts (CreateLinkedAccountAsync) with bank details, KYC stakeholder, and status lookup
  • Settlement activation checkGetLinkedAccountProductStatusAsync confirms bank details were accepted and the Route product is activated before you route payouts to it
  • Order creation with Razorpay Route split transfers — pay the restaurant owner's linked account directly, no manual settlement
  • Recurring payments / subscriptions — create billing plans and subscriptions so Razorpay auto-deducts the customer at each interval (daily/weekly/monthly/yearly) until cancelled
  • Payment signature verification (Checkout callback)
  • Payment capture (when auto-capture is disabled)
  • Refunds (full/partial)
  • Webhook signature verification + event parsing
  • International payments / PayPal — just pass a non-INR Currency on the order; Razorpay routes to the appropriate method (cards, PayPal, etc.) once enabled on your account. No separate PayPal SDK needed.
  • Consumers only depend on IPaymentGatewayService — the Razorpay SDK types stay internal to this library, so upgrading Razorpay's SDK later won't ripple through every app.

Install

Reference the PaymentGateway project/package from your app, plus its transitive deps (Razorpay, Microsoft.Extensions.*) are pulled in automatically.

Setup

appsettings.json

{
  "Razorpay": {
	"KeyId": "rzp_live_xxxxxxxxxxxx",
	"KeySecret": "your_key_secret",
	"WebhookSecret": "your_webhook_secret",
	"DefaultCurrency": "INR",
	"AutoCapture": true
  }
}

Keep KeySecret / WebhookSecret out of source control — use user-secrets, environment variables, or a secret store (Azure Key Vault, etc.) in production.

Program.cs

using PaymentGateway.Extensions;

builder.Services.AddRazorpayPaymentGateway(builder.Configuration);
// or configure in code:
// builder.Services.AddRazorpayPaymentGateway(opts =>
// {
//     opts.KeyId = "...";
//     opts.KeySecret = "...";
//     opts.WebhookSecret = "...";
// });

Usage

1. Create an order with a split payout to the restaurant owner

public class CheckoutController(IPaymentGatewayService payments)
{
	public async Task<OrderResult> Checkout(Guid restaurantId, long amountInPaise)
	{
		var restaurantOwnerLinkedAccountId = await GetLinkedAccountId(restaurantId); // e.g. "acc_xxxxxxx"

		var request = new CreateOrderRequest(
			AmountInSubunits: amountInPaise,
			Currency: "INR",
			Receipt: $"order-{Guid.NewGuid()}",
			Transfers:
			[
				new SplitTransfer(
					LinkedAccountId: restaurantOwnerLinkedAccountId,
					AmountInSubunits: amountInPaise) // send full amount to owner; adjust for platform commission if needed
			],
			Notes: new Dictionary<string, string> { ["restaurantId"] = restaurantId.ToString() });

		return await payments.CreateOrderAsync(request);
	}
}

To keep a platform commission, split the total across two transfers (or send a smaller amount to the owner and let the remainder stay in your main Razorpay account).

2. Verify payment after client-side Checkout succeeds

var verified = payments.VerifyPaymentSignature(new PaymentVerificationRequest(
	OrderId: razorpayOrderId,
	PaymentId: razorpayPaymentId,
	Signature: razorpaySignature));

if (!verified)
{
	return BadRequest("Payment verification failed.");
}

3. International payments / PayPal

Just set a non-INR currency when creating the order (e.g. "USD"). Once PayPal/international methods are enabled on your Razorpay account, Checkout automatically offers them for that order — no extra code required in this library.

4. Refunds

var refund = await payments.CreateRefundAsync(new RefundRequest(PaymentId: paymentId));

5. Webhooks

[HttpPost("webhooks/razorpay")]
public async Task<IActionResult> Webhook()
{
	using var reader = new StreamReader(Request.Body);
	var rawBody = await reader.ReadToEndAsync();
	var signature = Request.Headers["X-Razorpay-Signature"].ToString();

	if (!payments.VerifyWebhookSignature(rawBody, signature))
	{
		return Unauthorized();
	}

	var evt = payments.ParseWebhookEvent(rawBody);
	switch (evt.EventType)
	{
		case "payment.captured":
			// fulfil order
			break;
		case "transfer.processed":
			// restaurant owner payout confirmed
			break;
	}

	return Ok();
}

Important: read the raw request body for webhook verification — don't rely on model binding, since signature verification needs the exact bytes Razorpay sent.

Restaurant owner onboarding (Route linked accounts)

This library can create the linked account for you via CreateLinkedAccountAsync — you don't need to onboard restaurant owners manually on the Razorpay dashboard.

var result = await payments.CreateLinkedAccountAsync(new CreateLinkedAccountRequest(
    Email: owner.Email,
    Phone: owner.Phone,
    ReferenceId: restaurantId.ToString(),
    LegalBusinessName: owner.RestaurantLegalName,
    BusinessType: "proprietorship", // or "individual", "partnership", "private_limited", etc.
    Bank: new BankAccountDetails(
        IfscCode: owner.Bank.Ifsc,
        AccountNumber: owner.Bank.AccountNumber,
        BeneficiaryName: owner.Bank.BeneficiaryName),
    StakeholderName: owner.FullName,
    StakeholderEmail: owner.Email,
    RegisteredAddress: new BusinessAddress(
        Street1: owner.Address.Street1,
        City: owner.Address.City,
        State: owner.Address.State,
        PostalCode: owner.Address.PostalCode,
        Country: "IN"),
    Pan: owner.Pan,
    Notes: new Dictionary<string, string> { ["restaurantId"] = restaurantId.ToString() }));

// Persist result.LinkedAccountId and result.ProductId against the restaurant record.
restaurant.RazorpayLinkedAccountId = result.LinkedAccountId;
restaurant.RazorpayProductId = result.ProductId;

This creates the Razorpay Account, adds the primary Stakeholder (KYC), and requests the route product configuration, then PATCHes the settlement bank details onto it. Razorpay still needs to review/activate the account (KYC) before transfers succeed — check status any time with:

var status = await payments.GetLinkedAccountAsync(result.LinkedAccountId);
// status.Status: "created" | "activated" | "under_review" | ...

var productStatus = await payments.GetLinkedAccountProductStatusAsync(result.LinkedAccountId, result.ProductId);
// productStatus.ActivationStatus: "activated" once settlements are accepted; Requirements empty when ready.

Once Status is activated, pass result.LinkedAccountId into SplitTransfer.LinkedAccountId for order splits. Route itself must be enabled on your parent Razorpay account (one-time setup via Razorpay support) before any linked account onboarding/transfers will work.

Recurring payments (subscriptions)

For customers who subscribe to a plan (e.g. a SaaS tier), Razorpay auto-deducts the amount at each billing interval — you don't manually charge them every cycle.

1. Create a plan (one-time, per price point)

var plan = await payments.CreatePlanAsync(new CreatePlanRequest(
    Period: "monthly",
    IntervalCount: 1,           // charge every 1 month
    AmountInSubunits: 99900,    // ₹999.00
    Currency: "INR",
    PlanName: "Pro Plan - Monthly",
    Description: "Digital menu + online ordering, Pro tier"));

// Persist plan.PlanId (e.g. in your Plans table) and reuse it for every subscriber.

2. Subscribe a customer to the plan

var subscription = await payments.CreateSubscriptionAsync(new CreateSubscriptionRequest(
    PlanId: plan.PlanId,
    TotalBillingCycles: 120,    // e.g. 10 years of monthly cycles; use a high number for "until cancelled"
    CustomerNotify: true,
    Notes: new Dictionary<string, string> { ["restaurantId"] = restaurantId.ToString() }));

// Persist subscription.SubscriptionId against the restaurant/subscriber record.
// Redirect the customer to subscription.ShortUrl (or use Checkout in "subscription" mode
// client-side with subscription.SubscriptionId) so they authorize the recurring mandate once.

Once authorized, Razorpay automatically deducts the payment every cycle and fires webhook events (subscription.charged, subscription.activated, subscription.halted, subscription.cancelled, etc.) — handle these in your webhook endpoint (see above) to update subscriber access.

3. Check status / cancel

var status = await payments.GetSubscriptionAsync(subscription.SubscriptionId);
// status.Status: "created" | "authenticated" | "active" | "halted" | "cancelled" | ...

await payments.CancelSubscriptionAsync(new CancelSubscriptionRequest(
    SubscriptionId: subscription.SubscriptionId,
    CancelAtCycleEnd: true)); // let current paid cycle finish, or false to cancel immediately
Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.2 357 7/25/2026
1.0.1 102 7/19/2026
1.0.0 106 7/18/2026