Razorpay Integration with Salesforce: Generate and Track Payment Links via Apex


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

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses cookies to offer you a better browsing experience. By browsing this website, you agree to our use of cookies.