0

I'm a bit stuck, but in my defense, I'm passing a kidney stone and not thinking clearly. Words cannot describe the pain.

I have a Visualforce page that opens in a newly created object record. One version will be the standard Contacts object while the other will be a custom object, payment__c.

What I'm pasting below works fine when it has the proper credentials in place. However, I'm missing the reference to the record I'm opening it from. I've tried doing this using a list function but I must be putting it in the wrong location, or I shouldn't be using a list function at all.

I've spent 2 days messing with this going through Salesforce documentation and developer forums looking for an answer.

Issue Summary -

  1. Instead of passing the field value in the initialvalue string, it's passing the text.
  • Update - @Derek pointed out that I was trying to use Merged Expressions which don't work in Apex
  1. When adding the List Function below into the Apex, it queried the object but was giving me an entire list of all records instead of the values from the record I was on.

List Function (that I can't figure out where/how to put it into the Apex Class so I've pulled it out entirely)

List<Payment__c> records = [
    SELECT Id, Name, Amount__c
    FROM Payment__c
];

Controller This controller makes an external call out to, first authenticate, and then initiate a session with that external service. In this example, I'm opening the VF page from a Payment__c record. So I need to pass the ID, Name, and Amount__c to the external service as part of the "initialvalues" that you'll see around line 51.

public class authRequestGenericV2 {
    public string actionURL {get;set;}
    public string access_token{get;set;}
    public authRequestGenericV2(){
        startTest();
    }
    public pageReference testMethodd(){
     return null;   
    }
    public Void startTest(){
        String authUrl = 'actualAuthurl';
        String client_id =  'actualClientId';
        String client_secret = 'actualClientSecret';
        String tenantname = 'actualTenantName';
        String username = 'acutalusername';
        String grant_type = 'actualClientCredentials';
        String platform = 'actualPlatform';
        
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint(authURL);
        
        request.setMethod('POST');
        request.setHeader('Content-Type', 'application/x-www-form-urlencoded');
        String payload= 'grant_type=' +EncodingUtil.urlEncode(grant_type, 'UTF-8')+'&client_id=' +EncodingUtil.urlEncode(client_id, 'UTF-8')+'&client_secret=' +EncodingUtil.urlEncode(client_secret, 'UTF-8')+'&tenantname=' +EncodingUtil.urlEncode(tenantname, 'UTF-8')+'&username=' +EncodingUtil.urlEncode(username, 'UTF-8');
        System.debug(payload);
        
        request.setBody(payload);
        HttpResponse response = http.send(request);
        
        
        // Parse the JSON response
        if (response.getStatusCode() != 200){
            System.debug('The status code returned was not expected: ' +response.getStatusCode() + ' ' + response.getStatus());
        } else {
            System.debug(response.getBody());
            AccessToken tokenObj = (AccessToken)JSON.deserialize(response.getBody(), AccessToken.Class);
            access_token = tokenObj.access_token;
            String session_req_url = 'actual_session_req_url';
            
            // Session Request
            Http sessionHttp = new Http();
            HttpRequest sessionRequest = new HttpRequest();
            
            sessionRequest.setEndpoint(session_req_url);
            sessionRequest.setMethod('POST');
            
            sessionRequest.setHeader('Content-Type', 'application/json');
            sessionRequest.setHeader('Accept', 'application/json');
            sessionRequest.setHeader('Authorization', 'Bearer ' + tokenObj.access_token);
               
            String session_request_body = '{"FlowId":actualNumericValue,"InitialValues":{"paymentID": "{Payment__c.Name}", "Amount": "{Payment__c.Amount__c}", "SFID": "{Payment__c.Id}"}';
            sessionRequest.setBody(session_request_body);
            
            HttpResponse sessionResponse = sessionHttp.send(sessionRequest);
            
            // Parse the session JSON response
            if (response.getStatusCode() != 200){
                System.debug('The session status code returned was not expected: ' +response.getStatusCode() + ' ' + response.getStatus());
            } else {
                System.debug(response.getBody());
                // Parse entire JSON response.  
                String sessionjsonStr = sessionResponse.getBody();
                System.debug('::::::: Response::' + sessionjsonStr);
                sessionjsonStr = sessionjsonStr.replace('"Id"','"Idd"');
                system.debug(':::sessionjsonStr:::' + sessionjsonStr);
                ResponseResult resultObj =(ResponseResult)JSON.deserialize(sessionjsonStr, ResponseResult.Class);
                system.debug(':::resultObj.Id' + resultObj.Idd );
                system.debug(':::resultObj.LinkId' + resultObj.LinkId );
                actionURL = 'https://baseActionURL' + resultObj.Idd + '/framed';
            }
            
        }   
        
    }
    public class AccessToken{
        public string access_token{get;set;}
    }
    Public class ResponseResult{
        public string Idd{get;set;}
        public string LinkId{get;set;}
    }
    
}

VisualForce Page that is an embedded iframe

<apex:page controller="authRequestGenericV2">
    <iframe name="embeddedframe" src="about:blank" style="height:800px; width:600px; border-style:unset;"></iframe>  
    
    <form action="{!actionURL}" id="initForm" target="embeddedframe" method="POST">
 
        <input name="X-BEARER-TOKEN" value="{!access_token}" type="hidden"> </input>
        <input name="X-REFRESH-TOKEN" value="{refresh_token}" type="hidden"> </input>
</form>
<script>
document.getElementById("initForm").submit();
</script>

</apex:page>
2
  • I get the feeling that you may have obfuscated the code a bit. Does your String session_request_body line really have references to Payment__c.Name, Payment__c.Amount__c and Payment__c.Id? If so, the issue is that 1) you don't define/initialize a Payment__c varable. 2) Variable names cannot contain two (or more) consecutive underscores. 3) "Merge Field" syntax like {!thing} doesn't work in Apex (Apex doesn't have string interpolation/expansion like, say, Bash).
    – Derek F
    Commented Mar 9, 2021 at 19:39
  • @DerekF Thanks for taking a look. "Payment__c." is the object that I'm opening the VF page from. "Amount__c" is the API field name in that object. Those are where I need to pass in the field values into the callout. Would I define/initialize those variables with the list function?
    – Rudabaugh
    Commented Mar 9, 2021 at 23:14

1 Answer 1

1

There are several issues here.

1. Using a custom controller means you need to do more work

Since you're using a custom controller (i.e. <apex:page controller="myController">) as opposed to a controller extension (i.e. <apex:page standardController="MyObject__c" extensions="myExtension">), you don't get an SObject record (and the fields that are used on the corresponding Visualforce page) for free. If you want that data, you'll need to include the record Id in the query string and then query for the record.

The url you should be using would look something like https://na22.salesforce.com/apex/myVisualforcePage?id=<record id>

After you get to that point, we can move on to the other issues with your controller.

2. Querying the record

You fetch the record Id using ApexPages.currentPage().getParameters().get('id') (the 'id' there is case-sensitive, it must exactly match the query string parameter). After that, you can query for your record. This work is done in the constructor.

Something like

Id recId = ApexPages.currentPage().getParameters().get('id');

// The "?." is the safe navigation operator
// Introduced a few releases ago, it allows us to skip some simple null checks
if(recId?.getSObjectType() != Schema.Payment__c.SObjectType){
    // Probably best to stop working here, since whatever Id we got is not for a
    //   Payment__c record
    // I'd probably add a pageMessage so your users can be alerted to the issue
}

// Assuming this variable is defined as a class variable for your controller
myPayment = [SELECT Id, Name, Amount__c FROM Payment__c WHERE Id = :recId];

3. Merge expressions don't work in Apex

I'm talking about things of the form {!Account.Name}. That only works on Visualforce pages, not their controllers (nor controller extensions).

So the following line in your startTest() method is flawed:

String session_request_body = '{"FlowId":actualNumericValue,"InitialValues":{"paymentID": "{Payment__c.Name}", "Amount": "{Payment__c.Amount__c}", "SFID": "{Payment__c.Id}"}';

{Payment__c.Name}, {Payment__c.Amount__c}, {Payment__c.Id} all do not work. They'll just literally print out those strings in the request body.

There are a variety of ways that you could actually get the Name, Amount__c, and Id into that JSON template. String.format() would be my go-to, but the curly braces in JSON make that a non-starter. I think the next best approach is to serialize a Map<String, Object>. It can be a bit to type, but it feels to me like an intuitive way to build JSON.

That'd end up looking something like this

Map<String, Object> requestBody = new Map<String, Object>{
    'FlowId' => 'actualNumericValue',
    'InitialValues' => new Map<String, Object>{
        'paymentID' => myPayment.Name,
        'Amount' => myPayment.Amount__c,
        'SFID' => myPayment.Id
    }
};

String session_request_body = JSON.serialize(requestBody);

Parting advice

If you're new to something, or have some doubt about how something works, trying to scrape together snippets of code from blogs and such tend to cause more problems than they solve.

The official developer documentation (specifically the Apex Developer Guide, SOQL and SOSL Reference, and the Visualforce Developer Guide) are a good place to look.

If you're new to a feature or concept (like custom controllers), then https://trailhead.salesforce.com is a good place to get started. Alone, the modules are unlikely to give you all the knowledge you need, but they do give you a good base of knowledge that you can then use the developer docs and sites like this to expand upon.

1
  • greatly appreciately the explanation and advice. I haven't done any coding in a long time, Apex even longer. This got messy when I was trying to work through this kidney stone pain and started fiddling with referencing the object fields, hence the merged expressions being thrown in half hazardly. Thanks again.
    – Rudabaugh
    Commented Mar 10, 2021 at 2:39

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .