Introduction
Integrating Razorpay with Salesforce can significantly streamline your payment process. This post explains how to send a Razorpay payment link to a customer directly from a Salesforce Quote record using Apex, and how to track the payment status using an InvocableMethod
. This is especially useful for automating B2B transactions within Salesforce workflows.
๐ Step 1: Send Razorpay Payment Link from Salesforce Quote
In this step, we create an Invocable Apex method to generate a Razorpay payment link and update it on the Quote record. This link can be sent to customers via email or SMS.
Key Highlights:
- Accepts Quote ID as input
- Constructs JSON payload for Razorpay
- Authenticates using API key and secret
- Updates Quote with payment link and status
Apex Code:
public with sharing class CreatePaymentlinkRozarpay {
public class RequestWrapper {
@InvocableVariable(required=true)
public Id salesQuoteId;
}
public class ResponseWrapper {
@InvocableVariable
public String message;
}
@InvocableMethod(label='Create Razorpay Payment Link')
public static List<ResponseWrapper> createPaymentLink(List<RequestWrapper> requests) {
List<ResponseWrapper> responses = new List<ResponseWrapper>();
for (RequestWrapper req : requests) {
ResponseWrapper res = new ResponseWrapper();
try {
Quote quote = [
SELECT Id, Name, sales_GrandTotal__c, CurrencyIsoCode,
Project_Account__c, sales_Phone__c, Contact_Email__c,
sales_OpportunityName__r.Name, Payment_Url__c,Rozarpay_Link_Id__c,Rozarpay_Status__c
FROM Quote
WHERE Id = :req.salesQuoteId
LIMIT 1
];
Long expireBy = DateTime.now().addHours(4).getTime() / 1000;
Map<String, Object> payload = new Map<String, Object>{
'amount' => Integer.valueOf(quote.sales_GrandTotal__c * 100),
'currency' => quote.CurrencyIsoCode,
'accept_partial' => true,
'first_min_partial_amount' => 100,
'expire_by' => expireBy,
'reference_id' => quote.Name,
'description' => 'Payment for policy no ' + quote.Name,
'customer' => new Map<String, Object>{
'name' => quote.Project_Account__c,
'contact' => quote.sales_Phone__c,
'email' => quote.Contact_Email__c
},
'notify' => new Map<String, Boolean>{ 'sms' => true, 'email' => true },
'reminder_enable' => true,
'notes' => new Map<String, Object>{ 'policy_name' => quote.sales_OpportunityName__r.Name },
'callback_url' => 'https://example-callback-url.com/',
'callback_method' => 'get'
};
Http http = new Http();
HttpRequest httpRequest = new HttpRequest();
httpRequest.setEndpoint('https://api.razorpay.com/v1/payment_links');
httpRequest.setMethod('POST');
httpRequest.setHeader('Content-Type', 'application/json');
String key = '************'; // From Razorpay Dashboard
String secret = '**************'; // From Razorpay Dashboard
Blob headerValue = Blob.valueOf(key + ':' + secret);
String authorizationHeader = 'Basic ' + EncodingUtil.base64Encode(headerValue);
httpRequest.setHeader('Authorization', authorizationHeader);
httpRequest.setBody(JSON.serialize(payload));
HttpResponse httpResponse = http.send(httpRequest);
if (httpResponse.getStatusCode() == 200 || httpResponse.getStatusCode() == 201) {
Map<String, Object> responseMap = (Map<String, Object>) JSON.deserializeUntyped(httpResponse.getBody());
quote.Payment_Url__c = (String) responseMap.get('short_url');
quote.Rozarpay_Link_Id__c = (String) responseMap.get('id');
quote.Rozarpay_Status__c = (String) responseMap.get('status');
update quote;
res.message = 'Payment link created successfully:\n' +
'Name: ' + quote.Project_Account__c + '\n' +
'Contact: ' + quote.sales_Phone__c + '\n' +
'Email: ' + quote.Contact_Email__c + '\n' +
'Link: ' + quote.Payment_Url__c;
} else {
res.message = 'Error: ' + httpResponse.getBody();
}
} catch (Exception ex) {
res.message = 'Exception: ' + ex.getMessage();
}
responses.add(res);
}
return responses;
}
}
๐ฅ Step 2: Fetch Razorpay Payment Link Status
Once the link is sent, we may want to check the payment status. This method fetches the current status and details of the payment link.
Key Highlights:
- Accepts Quote ID as input
- Fetches link using Razorpayโs API
- Returns customer details and current link status
Apex Code:
public class FetchPaymentlinkRozarpay {
public class RequestWrapper {
@InvocableVariable(required=true)
public Id salesQuoteId;
}
public class ResponseWrapper {
@InvocableVariable
public String message;
}
@InvocableMethod(label='Fetch Razorpay Payment Link Details')
public static List<ResponseWrapper> fetchDetails(List<RequestWrapper> requests) {
List<ResponseWrapper> responses = new List<ResponseWrapper>();
String key = '***************';
String secret = '****************';
for (RequestWrapper req : requests) {
ResponseWrapper res = new ResponseWrapper();
try {
Quote quote = [
SELECT Id, Name, Project_Account__c, sales_Phone__c, Contact_Email__c, Payment_Url__c, Rozarpay_Link_Id__c
FROM Quote
WHERE Id = :req.salesQuoteId
LIMIT 1
];
String paymentLinkId = quote.Rozarpay_Link_Id__c;
if (String.isBlank(paymentLinkId)) {
res.message = 'No Razorpay payment link found on this Sales Quote.';
responses.add(res);
continue;
}
HttpRequest reqObj = new HttpRequest();
reqObj.setEndpoint('https://api.razorpay.com/v1/payment_links/' + paymentLinkId);
reqObj.setMethod('GET');
Blob headerValue = Blob.valueOf(key + ':' + secret);
String authorizationHeader = 'Basic ' + EncodingUtil.base64Encode(headerValue);
reqObj.setHeader('Authorization', authorizationHeader);
Http http = new Http();
HttpResponse response = http.send(reqObj);
if (response.getStatusCode() == 200) {
Map<String, Object> result = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
Map<String, Object> customer = (Map<String, Object>) result.get('customer');
res.message = 'Fetched Successfully.\nName: ' + customer.get('name') +
'\nContact: ' + customer.get('contact') +
'\nEmail: ' + customer.get('email') +
'\nPayment URL: ' + result.get('short_url') +
'\nStatus: ' + result.get('status');
} else {
res.message = 'Failed to fetch payment link. Status Code: ' + response.getStatusCode() + ', Body: ' + response.getBody();
}
} catch (Exception ex) {
res.message = 'Error fetching link: ' + ex.getMessage();
}
responses.add(res);
}
return responses;
}
}
๐ Pro Tip: Automate Status Fetching with a Scheduler
Currently, the status check is built to work on a single record manually. To automate this, you can:
- Create a Scheduled Apex job
- Query all Quotes with a Razorpay Link ID
- Reuse
FetchPaymentlinkRozarpay.fetchDetails()
logic to batch update their status