InitionTechnology.PaymentGateway
1.0.2
dotnet add package InitionTechnology.PaymentGateway --version 1.0.2
NuGet\Install-Package InitionTechnology.PaymentGateway -Version 1.0.2
<PackageReference Include="InitionTechnology.PaymentGateway" Version="1.0.2" />
<PackageVersion Include="InitionTechnology.PaymentGateway" Version="1.0.2" />
<PackageReference Include="InitionTechnology.PaymentGateway" />
paket add InitionTechnology.PaymentGateway --version 1.0.2
#r "nuget: InitionTechnology.PaymentGateway, 1.0.2"
#:package InitionTechnology.PaymentGateway@1.0.2
#addin nuget:?package=InitionTechnology.PaymentGateway&version=1.0.2
#tool nuget:?package=InitionTechnology.PaymentGateway&version=1.0.2
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 check —
GetLinkedAccountProductStatusAsyncconfirms bank details were accepted and the Route product isactivatedbefore 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
Currencyon 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/WebhookSecretout 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 | Versions 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. |
-
net10.0
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.10)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.10)
- Microsoft.Extensions.Options (>= 10.0.10)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.10)
- Razorpay (>= 3.3.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.