Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client API are updated to not require an IP address for transaction reporting #246

Merged
merged 8 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions MaxMind.MinFraud.UnitTest/Request/TransactionReportTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,39 @@ public class TransactionReportTest
[Fact]
public void TestRequired()
{
var report = new TransactionReport(ipAddress: IP, tag: TransactionReportTag.NotFraud);
var maxmindId = "12345678";
var minfraudId = Guid.NewGuid();
var tag = TransactionReportTag.NotFraud;
var transactionId = "txn123";

TransactionReport report;

// ipAddress supplied as identifier
Assert.Throws<ArgumentException>(() => new TransactionReport(tag: tag, ipAddress: null));
report = new TransactionReport(tag: tag, ipAddress: IP);
Assert.Equal(TransactionReportTag.NotFraud, report.Tag);
Assert.Equal(IP, report.IPAddress);

// maxmindId supplied as identifier
Assert.Throws<ArgumentException>(() => new TransactionReport(tag: tag, maxmindId: ""));
report = new TransactionReport(tag: tag, ipAddress: null, maxmindId: maxmindId);
Assert.Equal(TransactionReportTag.NotFraud, report.Tag);
Assert.Null(report.IPAddress);
Assert.Equal(maxmindId, report.MaxMindId);

// minfraudId supplied as identifier
Assert.Throws<ArgumentException>(() => new TransactionReport(tag: tag, minfraudId: Guid.Empty));
report = new TransactionReport(tag: tag, ipAddress: null, minfraudId: minfraudId);
Assert.Equal(TransactionReportTag.NotFraud, report.Tag);
Assert.Null(report.IPAddress);
Assert.Equal(minfraudId, report.MinFraudId);

// tranactionId supplied as identifier
Assert.Throws<ArgumentException>(() => new TransactionReport(tag: tag, transactionId: ""));
report = new TransactionReport(tag: tag, ipAddress: null, transactionId: transactionId);
Assert.Equal(TransactionReportTag.NotFraud, report.Tag);
Assert.Null(report.IPAddress);
Assert.Equal(transactionId, report.TransactionId);
}

[Fact]
Expand Down Expand Up @@ -50,7 +80,7 @@ public void TestAll()
public void TestMaxMindIdIsInvalid(string? maxmindId)
{
Assert.Throws<ArgumentException>(() => new TransactionReport(
IP, TransactionReportTag.SpamOrAbuse, maxmindId: maxmindId));
tag: TransactionReportTag.SpamOrAbuse, maxmindId: maxmindId));
}
}
}
65 changes: 50 additions & 15 deletions MaxMind.MinFraud/Request/TransactionReport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,58 +46,93 @@ public sealed class TransactionReport
private string? _maxmindId;

/// <summary>
/// Constructor.
/// Constructor with validation.
/// </summary>
/// <param name="ipAddress">The IP address reported to MaxMind for the
/// transaction.</param>
/// <param name="tag">The <c>TransactionReportTag</c> indicating the
/// type of report being made.</param>
/// <param name="chargebackCode">A string which is provided by your
/// payment processor indicating <a href="https://en.wikipedia.org/wiki/Chargeback#Reason_codes">
/// the reason for the chargeback</a>.</param>
/// <param name="ipAddress">The IP address reported to MaxMind for the
/// transaction. This field is not required if you provide at least
/// one of the transaction's <c>minfraudId</c>, <c>maxmindId</c>,
/// or <c>transactionId></c>. You are encouraged to provide it,
/// if possible.</param>
/// <param name="maxmindId">A unique eight character string identifying
/// a minFraud Standard or Premium request. These IDs are returned
/// in the <c>maxmindID</c> field of a response for a successful
/// minFraud request. This field is not required, but you are
/// encouraged to provide it, if possible.</param>
/// minFraud request. This field is not required if you provide at
/// least one of the transaction's <c>ipAddress</c>,
/// <c>minfraudId</c>, or <c>transactionId></c>. You are encouraged
/// to provide it, if possible.</param>
/// <param name="minfraudId">A UUID that identifies a minFraud Score,
/// minFraud Insights, or minFraud Factors request. This ID is
/// returned at <c>/id</c> in the response. This field is not
/// required, but you are encouraged to provide it if the request
/// was made to one of these services.</param>
/// required if you provide at least one of the transaction's
/// <c>ipAddress</c>, <c>maxmindId</c>, or <c>transactionId></c>.
/// You are encouraged to provide it, if possible.</param>
/// <param name="notes">Your notes on the fraud tag associated with the
/// transaction. We manually review many reported transactions to
/// improve our scoring for you so any additional details to help
/// us understand context are helpful.</param>
/// <param name="transactionId">The transaction ID you originally passed
/// to minFraud. This field is not required, but you are encouraged
/// to provide it or the transaction's <c>>maxmindId</c> or
/// <c>minfraudId</c>.</param>
/// to minFraud. This field is not required if you provide at least
/// one of the transaction's <c>ipAddress</c>, <c>maxmindId</c>,
/// or <c>minfraudId></c>. You are encouraged to provide it, if
/// possible.</param>
public TransactionReport(
IPAddress ipAddress,
TransactionReportTag tag,
string? chargebackCode = null,
IPAddress? ipAddress = null,
string? maxmindId = null,
Guid? minfraudId = null,
string? notes = null,
string? transactionId = null
)
{
if (ipAddress == null && (minfraudId == null || minfraudId == Guid.Empty)
&& string.IsNullOrEmpty(maxmindId) && string.IsNullOrEmpty(transactionId))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, this interacts poorly with init properties, but I don't have an immediate solution for that. Although we encourage init properties for minFraud requests, we don't really encourage them for the report transaction API. I suppose we can worry about this in the future.

{
throw new ArgumentException(
"The user must pass at least one of the following: " +
"ipAddress, minfraudId, maxmindId, transactionId."
);
}

IPAddress = ipAddress;
Tag = tag;
ChargebackCode = chargebackCode;
MaxMindId = maxmindId;
MinFraudId = minfraudId;
Notes = notes;
TransactionId = transactionId;

if(minfraudId != Guid.Empty) {
MinFraudId = minfraudId;
}
}

/// <summary>
/// Constructor for backwards compatibility.
/// </summary>
[Obsolete]
public TransactionReport(
IPAddress? ipAddress,
TransactionReportTag tagObsolete,
string? chargebackCode = null,
string? maxmindId = null,
Guid? minfraudId = null,
string? notes = null,
string? transactionId = null
): this(tagObsolete, chargebackCode, ipAddress, maxmindId, minfraudId, notes, transactionId)
{
}

/// <summary>
/// The IP address reported to MaxMind for the transaction.
/// </summary>
[JsonPropertyName("ip_address")]
[JsonConverter(typeof(IPAddressConverter))]
public IPAddress IPAddress { get; init; }
public IPAddress? IPAddress { get; init; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, this seems like a breaking change. I don't know if is likely to cause actual breakage for anyone though. Maybe we should do a major version bump to be safe though.


/// <summary>
/// The <c>TransactionReportTag</c> indicating the type of report
Expand Down Expand Up @@ -156,7 +191,7 @@ public string? MaxMindId
/// <summary>
/// The transaction ID you originally passed to minFraud. This field
/// is not required, but you are encouraged to provide it or the
/// transaction's <c>>maxmindId</c> or <c>minfraudId</c>.
/// transaction's <c>maxmindId</c> or <c>minfraudId</c>.
/// </summary>
[JsonPropertyName("transaction_id")]
public string? TransactionId { get; init; }
Expand All @@ -167,7 +202,7 @@ public string? MaxMindId
/// <returns>A string that represents the current object.</returns>
public override string ToString()
{
return $"IPAddress: {IPAddress}, Tag: {Tag}, ChargebackCode: {ChargebackCode}, MaxMindId: {MaxMindId}"
return $"IPAddress: {IPAddress}, Tag: {Tag}, ChargebackCode: {ChargebackCode}, MaxMindId: {MaxMindId}, "
+ $"MinFraudId: {MinFraudId}, Notes: {Notes}, TransactionId: {TransactionId}";
}
}
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,8 @@ var report = new TransactionReport(
transactionId: "txn123");
```

Only the `ipAddress` and `tag` parameters are required.
A valid `tag` and at least one of the following are required parameters:
`ipAddress`, `maxmindId`, `minfraudId`, `transactionId`.

Send the report by calling the `ReportAsync` method using the `await` keyword:

Expand Down
7 changes: 7 additions & 0 deletions releasenotes.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
Release Notes
=============

5.0.0
------------------

* Updated `TransactionReport` to make the `ipAddress` parameter optional. Now
the `tag` and at least one of the following parameters must be supplied:
`ipAddress`, `maxmindId`, `minfraudID`, `transactionID`.

4.3.0 (2024-04-16)
------------------

Expand Down
Loading