added confirm transaction endpoint

This commit is contained in:
yurii 2026-09-07 18:08:24 +01:00
parent c053868dd3
commit 35a2870179
2 changed files with 39 additions and 100 deletions

View File

@ -318,7 +318,7 @@ namespace Creditcall.ChipDna.Client
PerformStartTransaction("250", "");
break;
case "C":
PerformConfirmTransaction("", out TransactionConfirmation confirmation);
PerformConfirmTransaction("", "");
break;
case "V":
PerformVoidTransaction("");
@ -642,19 +642,23 @@ namespace Creditcall.ChipDna.Client
}
}
public void PerformConfirmTransaction(string reference, out TransactionConfirmation confirmation)
public Dictionary<string, string> PerformConfirmTransaction(string reference, string amount)
{
confirmation = new TransactionConfirmation();
var confirmationResult = new Dictionary<string, string>();
var parameters = new ParameterSet();
parameters.Add(ParameterKeys.Reference, reference);
parameters.Add(ParameterKeys.Amount, amount);
parameters.Add(GetExtraParams("ConfirmTransaction"));
var response = chipDnaClientLib.ConfirmTransaction(parameters);
var responseParameter = response.ToList();
responseParameter.ForEach(parameter =>
{
confirmationResult.Add(parameter.Key, parameter.Value);
});
string receipt, result;
if (response.GetValue(ParameterKeys.TransactionResult, out result))
{
Console.WriteLine("Confirmed Transaction Result: {0}", result);
confirmation.Result = result;
}
if (response.GetValue(ParameterKeys.ReceiptDataMerchant, out receipt))
{
@ -663,21 +667,19 @@ namespace Creditcall.ChipDna.Client
if (response.GetValue(ParameterKeys.ReceiptDataCardholder, out receipt))
{
PrintReceiptData(ReceiptData.GetReceiptDataFromXml(receipt));
confirmation.ReceiptDataCardholder = receipt;
}
string errors;
if (response.GetValue(ParameterKeys.Errors, out errors))
{
Console.WriteLine(ErrorsString("ConfirmTransaction Errors", errors));
confirmation.Errors = errors;
}
string errorDescription;
if (response.GetValue(ParameterKeys.ErrorDescription, out errorDescription))
{
Console.WriteLine("ErrorDescription: {0}", errorDescription);
confirmation.ErrorDescription = errorDescription;
}
return confirmationResult;
}
private void DoUpdateTransaction()

View File

@ -73,7 +73,7 @@ namespace Creditcall.ChipDna.Client
{
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://127.0.0.1:18181/start-transaction/");
listener.Prefixes.Add("http://127.0.0.1:18181/start-and-confirm-transaction/");
listener.Prefixes.Add("http://127.0.0.1:18181/confirm-transaction/");
listener.Prefixes.Add("http://127.0.0.1:18181/transaction-information/");
listener.Prefixes.Add("http://127.0.0.1:18181/void-transaction/");
listener.Prefixes.Add("http://127.0.0.1:18181/chipdna-status/");
@ -81,7 +81,7 @@ namespace Creditcall.ChipDna.Client
Console.WriteLine("Listening on:");
Console.WriteLine(" http://127.0.0.1:18181/start-transaction/");
Console.WriteLine(" http://127.0.0.1:18181/start-and-confirm-transaction/");
Console.WriteLine(" http://127.0.0.1:18181/confirm-transaction/");
Console.WriteLine(" http://127.0.0.1:18181/transaction-information/");
Console.WriteLine(" http://127.0.0.1:18181/void-transaction/");
Console.WriteLine(" http://127.0.0.1:18181/chipdna-status/");
@ -105,9 +105,9 @@ namespace Creditcall.ChipDna.Client
{
HandleStartTransaction(context);
} else if (rawUrl.StartsWith("/start-and-confirm-transaction", StringComparison.OrdinalIgnoreCase))
} else if (rawUrl.StartsWith("/confirm-transaction", StringComparison.OrdinalIgnoreCase))
{
HandleStartAndConfirmTransaction(context);
HandleConfirmTransaction(context);
}
else if (rawUrl.StartsWith("/transaction-information", StringComparison.OrdinalIgnoreCase))
{
@ -120,7 +120,8 @@ namespace Creditcall.ChipDna.Client
else if (rawUrl.StartsWith("/chipdna-status", StringComparison.OrdinalIgnoreCase))
{
HandleGetStatus(context);
}
}
else
{
context.Response.StatusCode = 404;
context.Response.Close();
@ -212,105 +213,34 @@ namespace Creditcall.ChipDna.Client
}
}
private static void HandleStartAndConfirmTransaction(HttpListenerContext context)
private static void HandleConfirmTransaction(HttpListenerContext context)
{
try
{
var serializer = new XmlSerializer(typeof(TransactionPayload));
TransactionPayload payload = (TransactionPayload)serializer.Deserialize(context.Request.InputStream);
var serializer = new XmlSerializer(typeof(ConfirmTransactionRequest));
ConfirmTransactionRequest payload = (ConfirmTransactionRequest)serializer.Deserialize(context.Request.InputStream);
Console.WriteLine($"Received start transaction: Amount={payload.Amount}, Type={payload.TransactionType}");
Console.WriteLine($"Received confirm transaction: Amount={payload.Amount}, TransactionReference={payload.TransactionReference}");
client.TransactionCompletionSource = new TaskCompletionSource<Dictionary<string, string>>();
var confirmationResult = client.PerformConfirmTransaction(payload.TransactionReference, payload.Amount);
// start transaction (non-blocking)
client.PerformStartTransaction(payload.Amount, payload.TransactionType);
// Wait for completion with timeout
var task = client.TransactionCompletionSource.Task;
if (!task.Wait(TimeSpan.FromSeconds(transactionTimeoutSeconds)))
{
// timeout
var errorMap = new Dictionary<string, string>
{
{ ParameterKeys.TransactionResult, "TIMEOUT" },
{ ParameterKeys.Errors, "Transaction timed out waiting for device response" }
};
var wrappedTimeout = new SerializableKeyValueList(errorMap);
context.Response.ContentType = "application/xml";
new XmlSerializer(typeof(SerializableKeyValueList)).Serialize(context.Response.OutputStream, wrappedTimeout);
return;
}
// Task completed — handle possible exception
Dictionary<string, string> transactionResult;
try
{
transactionResult = task.Result; // safe now, Wait returned
}
catch (AggregateException agg)
{
var first = agg.Flatten().InnerExceptions[0];
var errMap = new Dictionary<string, string>
{
{ ParameterKeys.TransactionResult, "ERROR" },
{ ParameterKeys.Errors, $"Transaction task faulted: {first.Message}" }
};
var wrapped = new SerializableKeyValueList(errMap);
context.Response.ContentType = "application/xml";
new XmlSerializer(typeof(SerializableKeyValueList)).Serialize(context.Response.OutputStream, wrapped);
return;
}
// If it's a SALE and approved, call ConfirmTransaction and attach its XML
transactionResult.TryGetValue(ParameterKeys.TransactionType, out string txType);
transactionResult.TryGetValue(ParameterKeys.TransactionResult, out string txResult);
transactionResult.TryGetValue(ParameterKeys.Reference, out string txReference);
if (!string.IsNullOrEmpty(txType) && txType.Equals("SALE", StringComparison.OrdinalIgnoreCase) &&
!string.IsNullOrEmpty(txResult) && txResult.Equals("APPROVED", StringComparison.OrdinalIgnoreCase) &&
!string.IsNullOrEmpty(txReference))
{
try
{
TransactionConfirmation confirmation;
client.PerformConfirmTransaction(txReference, out confirmation);
// Keep original fields, but also add confirmation-prefixed fields
transactionResult["CONFIRM_RESULT"] = confirmation?.Result ?? "";
transactionResult["CONFIRM_ERRORS"] = confirmation?.Errors ?? "";
if (!string.IsNullOrEmpty(confirmation.ErrorDescription))
{
transactionResult["CONFIRM_ERRORS"] = $"{confirmation.Errors} {confirmation.ErrorDescription}";
}
if (!string.IsNullOrEmpty(confirmation?.ReceiptDataCardholder))
{
transactionResult[ParameterKeys.ReceiptDataCardholder] = confirmation.ReceiptDataCardholder;
}
// XML (omit declaration so it can be embedded)
var xml = SerializeToXml(confirmation, omitXmlDeclaration: true);
transactionResult["TRANSACTION_CONFIRMATION"] = xml;
}
catch (Exception ex)
{
// preserve original transaction result but add an error key
transactionResult["CONFIRM_RESULT"] = "ERROR";
transactionResult["CONFIRM_ERRORS"] = $"ConfirmTransaction failed: {ex.Message}";
}
}
// Return the final key/value list as before
var responseSerializer = new XmlSerializer(typeof(SerializableKeyValueList));
context.Response.ContentType = "application/xml";
var wrappedResult = new SerializableKeyValueList(transactionResult);
var wrappedResult = new SerializableKeyValueList(confirmationResult);
responseSerializer.Serialize(context.Response.OutputStream, wrappedResult);
}
catch (Exception ex)
{
Console.WriteLine("Error in start-and-confirm-transaction: " + ex.Message);
context.Response.StatusCode = 500;
var errorMap = new Dictionary<string, string>
{
{ ParameterKeys.TransactionResult, "ERROR" },
{ ParameterKeys.Errors, $"ConfirmTransaction failed: {ex.Message}" }
};
var responseSerializer = new XmlSerializer(typeof(SerializableKeyValueList));
context.Response.ContentType = "application/xml";
var wrappedResult = new SerializableKeyValueList(errorMap);
responseSerializer.Serialize(context.Response.OutputStream, wrappedResult);
}
finally
{
@ -491,4 +421,11 @@ namespace Creditcall.ChipDna.Client
public string TransactionReference { get; set; }
}
[XmlRoot("ConfirmTransactionRequest")]
public class ConfirmTransactionRequest
{
public string Amount { get; set; }
public string TransactionReference { get; set; }
}
}