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

View File

@ -73,7 +73,7 @@ namespace Creditcall.ChipDna.Client
{ {
HttpListener listener = new HttpListener(); 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-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/transaction-information/");
listener.Prefixes.Add("http://127.0.0.1:18181/void-transaction/"); listener.Prefixes.Add("http://127.0.0.1:18181/void-transaction/");
listener.Prefixes.Add("http://127.0.0.1:18181/chipdna-status/"); 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("Listening on:");
Console.WriteLine(" http://127.0.0.1:18181/start-transaction/"); 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/transaction-information/");
Console.WriteLine(" http://127.0.0.1:18181/void-transaction/"); Console.WriteLine(" http://127.0.0.1:18181/void-transaction/");
Console.WriteLine(" http://127.0.0.1:18181/chipdna-status/"); Console.WriteLine(" http://127.0.0.1:18181/chipdna-status/");
@ -105,9 +105,9 @@ namespace Creditcall.ChipDna.Client
{ {
HandleStartTransaction(context); 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)) else if (rawUrl.StartsWith("/transaction-information", StringComparison.OrdinalIgnoreCase))
{ {
@ -121,6 +121,7 @@ namespace Creditcall.ChipDna.Client
{ {
HandleGetStatus(context); HandleGetStatus(context);
} }
else
{ {
context.Response.StatusCode = 404; context.Response.StatusCode = 404;
context.Response.Close(); context.Response.Close();
@ -212,105 +213,34 @@ namespace Creditcall.ChipDna.Client
} }
} }
private static void HandleStartAndConfirmTransaction(HttpListenerContext context) private static void HandleConfirmTransaction(HttpListenerContext context)
{ {
try try
{ {
var serializer = new XmlSerializer(typeof(TransactionPayload)); var serializer = new XmlSerializer(typeof(ConfirmTransactionRequest));
TransactionPayload payload = (TransactionPayload)serializer.Deserialize(context.Request.InputStream); 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)); var responseSerializer = new XmlSerializer(typeof(SerializableKeyValueList));
context.Response.ContentType = "application/xml"; context.Response.ContentType = "application/xml";
var wrappedResult = new SerializableKeyValueList(transactionResult); var wrappedResult = new SerializableKeyValueList(confirmationResult);
responseSerializer.Serialize(context.Response.OutputStream, wrappedResult); responseSerializer.Serialize(context.Response.OutputStream, wrappedResult);
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("Error in start-and-confirm-transaction: " + ex.Message); var errorMap = new Dictionary<string, string>
context.Response.StatusCode = 500; {
{ 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 finally
{ {
@ -491,4 +421,11 @@ namespace Creditcall.ChipDna.Client
public string TransactionReference { get; set; } public string TransactionReference { get; set; }
} }
[XmlRoot("ConfirmTransactionRequest")]
public class ConfirmTransactionRequest
{
public string Amount { get; set; }
public string TransactionReference { get; set; }
}
} }