Hello, I am trying to implement Visa Account Updater API, but I'm getting Error 409 (Protocol Error) when sending the sample request.
Here is my code in C#:
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Jose;
using Org.BouncyCastle.OpenSsl;
using Newtonsoft.Json;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Newtonsoft.Json.Linq;
namespace Vdp
{
class Program
{
public static string visaUrl = "https://sandbox.api.visa.com/";
public static string userId = "********************************";
public static string password = "********************************";
public static string cert = @"********************************\cert.pfx";
//For MLE
public static string keyId = "*******-*******-*******-*******-*******";
public static string mleClientPrivateKey = @"key*******.pem";
public static string mleServerPublicCertificate = @"server_cert*******.pem";
public static void Main(string[] args)
{
var queryResponse = Query();
Console.WriteLine("Transaction Query Response:\n" + queryResponse);
}
private static void LogRequest(string url, string requestBody)
{
Console.WriteLine(url);
Console.WriteLine(requestBody);
}
private static void LogResponse(string info, HttpWebResponse response)
{
Debug.WriteLine(info);
Console.WriteLine("Response Status: \n" + response.StatusCode);
Console.WriteLine("Response Headers: \n" + response.Headers.ToString());
Console.WriteLine("Response Body: \n" + GetResponseBody(response));
}
private static string GetResponseBody(HttpWebResponse response)
{
string responseBody = "";
using (var reader = new StreamReader(response.GetResponseStream(), ASCIIEncoding.ASCII))
{
responseBody = reader.ReadToEnd();
}
return responseBody;
}
//Correlation Id ( ex-correlation-id ) is an optional header while making an API call. You can skip passing the header while calling the API's.
private static string GetCorrelationId()
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var random = new Random();
return new string(Enumerable.Repeat(chars, 12).Select(s => s[random.Next(s.Length)]).ToArray()) + "_SC";
}
private static string GetBasicAuthHeader(string userId, string password)
{
string authString = userId + ":" + password;
var authStringBytes = Encoding.UTF8.GetBytes(authString);
string authHeaderString = Convert.ToBase64String(authStringBytes);
return "Basic " + authHeaderString;
}
public static string DoMutualAuthCall(string path, string method, string testInfo, string requestBodyString, Dictionary<string, string> headers = null)
{
string requestURL = visaUrl + path;
string certificatePath = cert;
string certificatePassword = "1234";
string statusCode = "";
string responseBody = "";
LogRequest(requestURL, requestBodyString);
// Create the POST request object
HttpWebRequest request = WebRequest.Create(requestURL) as HttpWebRequest;
request.Method = method;
if (method.Equals("POST") || method.Equals("PUT"))
{
request.ContentType = "application/json";
request.Accept = "application/json";
// Load the body for the post request
var requestStringBytes = Encoding.UTF8.GetBytes(requestBodyString);
request.GetRequestStream().Write(requestStringBytes, 0, requestStringBytes.Length);
}
if (headers != null)
{
foreach (KeyValuePair<string, string> header in headers)
{
request.Headers[header.Key] = header.Value;
}
}
// Add headers
request.Headers["Authorization"] = GetBasicAuthHeader(userId, password);
request.Headers["ex-correlation-id"] = GetCorrelationId();
request.Headers["keyId"] = keyId;
// Add certificate
var certificate = new X509Certificate2(certificatePath, certificatePassword);
request.ClientCertificates.Add(certificate);
try
{
// Make the call
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
responseBody = GetResponseBody(response);
Console.WriteLine(responseBody);
statusCode = response.StatusCode.ToString();
}
}
catch (WebException e)
{
Console.WriteLine(e.ToString());
if (e.Response is HttpWebResponse)
{
HttpWebResponse response = (HttpWebResponse)e.Response;
responseBody = GetResponseBody(response);
LogResponse(testInfo, response);
statusCode = response.StatusCode.ToString();
}
}
return responseBody;
}
public static string Query()
{
string localTransactionDateTime = DateTime.Now.ToString("yyyy-MM-dd'T'HH:mm:ss");
string requestBody = "{ \"acquirerSegmentId\": \"88\", \"merchantId\": \"000000705858\", \"inquiries\":[{\"cardholderAccountNumber\":\"4389613X6834X49X\", \"expirationDate\":\"1802\"}] }";
string requestURL = "vau/acquirer-api/v1/inquiry";
return GetDecryptedPayload(DoMutualAuthCall(requestURL, "POST", "OCT With MLE", getEncryptedPayload(requestBody), null));
}
private static string GetTimestamp()
{
long timeStamp = ((long)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds) / 1000;
return timeStamp.ToString();
}
private static string getEncryptedPayload(String requestBody)
{
RSA clientCertificate = new X509Certificate2(mleServerPublicCertificate).GetRSAPublicKey();
DateTime now = DateTime.UtcNow;
long unixTimeMilliseconds = new DateTimeOffset(now).ToUnixTimeMilliseconds();
IDictionary<string, object> extraHeaders = new Dictionary<string, object>{
{"kid", keyId},{"iat",unixTimeMilliseconds}
};
string token = JWT.Encode(requestBody, clientCertificate, JweAlgorithm.RSA_OAEP_256, JweEncryption.A128GCM, null, extraHeaders);
return "{\"encData\":\"" + token + "\"}";
}
private static String GetDecryptedPayload(String encryptedPayload)
{
EncryptedPayload jsonPayload = JsonConvert.DeserializeObject<EncryptedPayload>(encryptedPayload);
return JWT.Decode(jsonPayload.encData, ImportPrivateKey(mleClientPrivateKey));
}
private static RSA ImportPrivateKey(string privateKeyFile)
{
var pemValue = System.Text.Encoding.Default.GetString(File.ReadAllBytes(privateKeyFile));
var pr = new PemReader(new StringReader(pemValue));
var keyPair = (AsymmetricCipherKeyPair)pr.ReadObject();
var rsaParams = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)keyPair.Private);
var rsa = RSA.Create();
rsa.ImportParameters(rsaParams);
return rsa;
}
}
public class EncryptedPayload
{
public string encData { get; set; }
}
}
and this is the error:
Thank you for reaching out, @ranlevi! An agent will look into this and get back to you as soon as possible. If any community members know a solution, please feel free to respond in this thread. - Jenn
Hi @ranlevi .
To assist you further, please provide the following information (please copy and paste, no screenshots):
Using SoapUI, you can find the x-correlation-id in the Raw Tab of the response header.
Thanks,
Illana