From aa069291fac817074230a4b016244cb1fc0141d3 Mon Sep 17 00:00:00 2001 From: Omri Mosseri Date: Mon, 5 Sep 2016 16:31:51 +0300 Subject: [PATCH] Added Chargeback api support --- .../OrderTransmissionExample.cs | 47 +++++ .../ChargebackElements/ChargebackDetails.cs | 161 ++++++++++++++++++ .../ChargebackElements/DisputeDetails.cs | 80 +++++++++ Riskified.SDK/Model/OrderChargeback.cs | 51 ++++++ Riskified.SDK/Orders/OrdersGateway.cs | 5 + Riskified.SDK/Properties/AssemblyInfo.cs | 6 +- Riskified.SDK/Riskified.SDK.csproj | 8 +- 7 files changed, 351 insertions(+), 7 deletions(-) create mode 100644 Riskified.SDK/Model/ChargebackElements/ChargebackDetails.cs create mode 100644 Riskified.SDK/Model/ChargebackElements/DisputeDetails.cs create mode 100644 Riskified.SDK/Model/OrderChargeback.cs diff --git a/Riskified.SDK.Sample/OrderTransmissionExample.cs b/Riskified.SDK.Sample/OrderTransmissionExample.cs index ae0bbaf..7775853 100644 --- a/Riskified.SDK.Sample/OrderTransmissionExample.cs +++ b/Riskified.SDK.Sample/OrderTransmissionExample.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Configuration; using Riskified.SDK.Model; +using Riskified.SDK.Model.ChargebackElements; using Riskified.SDK.Model.OrderElements; using Riskified.SDK.Model.RefundElements; using Riskified.SDK.Orders; @@ -51,6 +52,7 @@ public static void SendOrdersToRiskifiedExample() "'f' for fulfill\n" + "'x' for decision\n" + "'h' for historical sending\n" + + "'y' for chargeback submission\n" + "'q' to quit"; Console.WriteLine(menu); string commandStr = Console.ReadLine(); @@ -171,7 +173,13 @@ public static void SendOrdersToRiskifiedExample() Console.WriteLine(String.Join("\n", errors.Select(p => p.Key + ":" + p.Value).ToArray())); } break; + case "y": + Console.Write("Chargeback order id: "); + string chargebackOrderId = Console.ReadLine(); + OrderChargeback orderChargeback = GenerateOrderChargeback(chargebackOrderId); + res = gateway.Chargeback(orderChargeback); + break; } @@ -536,6 +544,45 @@ private static Order PayPalGenerateOrder(int orderNum) return order; } + private static OrderChargeback GenerateOrderChargeback(string orderNum) + { + var chargebackDetails = new ChargebackDetails(id: "id1234", + charegbackAt: new DateTime(2015, 12, 8, 14, 12, 12, DateTimeKind.Local), + chargebackCurrency: "USD", + chargebackAmount: (float)50.5, + reasonCode: "4863", + reasonDesc: "Transaction not recognised", + type: "cb", + mid: "t_123", + creditCardCompany: "visa", + respondBy: new DateTime(2016, 9, 1), + arn: "a123456789012bc3de45678901f23a45", + feeAmount: 20, + feeCurrency: "USD", + cardIssuer: "Wells Fargo Bank", + gateway: "braintree", + cardholder: "John Smith", + message: "Cardholder disputes quality/ mischaracterization of service/merchandise. Supply detailed refute of these claims, along with any applicable/supporting doc"); + + var fulfillmentDetails = new FulfillmentDetails( + fulfillmentId: "123", + createdAt: new DateTime(2015, 12, 8, 14, 12, 12, DateTimeKind.Local), + status: FulfillmentStatusCode.Success, + lineItems: new LineItem[] { new LineItem("Bag", 10.0, 1) }, + trackingCompany: "TestCompany"); + + var disputeDetails = new DisputeDetails( + disputeType: "first_dispute", + caseId: "a1234", + status: "pending", + issuerPocPhoneNumber: "+1-877-111-1111", + disputedAt: new DateTime(2016, 9, 15), + expectedResolutionDate: new DateTime(2016, 11, 1)); + + return new OrderChargeback(orderNum, chargebackDetails, fulfillmentDetails, disputeDetails); + + } + #region Run all endpoints public static int runAll() { diff --git a/Riskified.SDK/Model/ChargebackElements/ChargebackDetails.cs b/Riskified.SDK/Model/ChargebackElements/ChargebackDetails.cs new file mode 100644 index 0000000..5663b07 --- /dev/null +++ b/Riskified.SDK/Model/ChargebackElements/ChargebackDetails.cs @@ -0,0 +1,161 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Riskified.SDK.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Riskified.SDK.Model.ChargebackElements +{ + public class ChargebackDetails : IJsonSerializable + { + public ChargebackDetails(string id = null, + DateTime? charegbackAt = null, + string chargebackCurrency = null, + float? chargebackAmount = null, + string reasonCode = null, + string reasonDesc = null, + string type = null, + string mid = null, + string arn = null, + string creditCardCompany = null, + DateTime? respondBy = null, + float? feeAmount = null, + string feeCurrency = null, + string cardIssuer = null, + string gateway = null, + string cardholder = null, + string message = null) + { + this.Id = id; + this.ChargebackAt = charegbackAt; + this.ChargebackCurrency = chargebackCurrency; + this.ChargebackAmount = chargebackAmount; + this.ReasonCode = reasonCode; + this.ReasonDescription = reasonDesc; + this.Type = type; + this.MID = mid; + this.ARN = arn; + this.CreditCardCompany = creditCardCompany; + this.RespondBy = respondBy; + this.FeeAmount = feeAmount; + this.FeeCurrency = feeCurrency; + this.CardIssuer = cardIssuer; + this.Gateway = gateway; + this.Cardholder = cardholder; + this.Message = message; + } + + public void Validate(Utils.Validations validationType = Validations.Weak) + { + } + + /// + /// The chargeback notice id (if applicable). + /// + [JsonProperty(PropertyName = "id")] + public string Id { get; set; } + + /// + /// The chargeback date, as recieved from the acquirer. + /// + [JsonProperty(PropertyName = "chargeback_at")] + public DateTime? ChargebackAt { get; set; } + + /// + /// The chargeback currency, ISO 4217. + /// + [JsonProperty(PropertyName = "chargeback_currency")] + public string ChargebackCurrency { get; set; } + + /// + /// The chargeback amount as stated in the chargeback notice. + /// + [JsonProperty(PropertyName = "chargeback_amount")] + public float? ChargebackAmount { get; set; } + + /// + /// The chargeback reason code, as recieved from the acquirer. + /// + [JsonProperty(PropertyName = "reason_code")] + public string ReasonCode { get; set; } + + /// + /// The chargeback reason description, as recieved from the acquirer. + /// + [JsonProperty(PropertyName = "reason_description")] + public string ReasonDescription { get; set; } + + /// + /// The chargeback transaction type, as received from the acquirer + /// rfi - Request for Information. + /// cb - Notification of Chargeback. + /// cb2 - Second Chargeback. + /// + [JsonProperty(PropertyName = "type")] + public string Type { get; set; } + + /// + /// The merchant account id at the payment gateway + /// + [JsonProperty(PropertyName = "mid")] + public string MID { get; set; } + + /// + /// Acquirer Reference Number (ARN) A unique number that tags a credit card transaction when it goes from the + /// merchant's bank (the acquiring bank) through the card scheme to the cardholder's bank (the issuer). + /// + [JsonProperty(PropertyName = "arn")] + public string ARN { get; set; } + + /// + /// Credit card brand: VISA, Mastercard, AMEX, JCB, etc. + /// + [JsonProperty(PropertyName = "credit_card_company")] + public string CreditCardCompany { get; set; } + + /// + /// Last date to challenge CHB + /// + [JsonProperty(PropertyName = "respond_by")] + public DateTime? RespondBy { get; set; } + + /// + /// The chargeback fee amount + /// + [JsonProperty(PropertyName = "fee_amount")] + public float? FeeAmount { get; set; } + + /// + /// The chargeback fee currency + /// + [JsonProperty(PropertyName = "fee_currency")] + public string FeeCurrency { get; set; } + + /// + /// The card issuer + /// + [JsonProperty(PropertyName = "card_issuer")] + public string CardIssuer { get; set; } + + /// + /// The payment gateway who processed the order + /// + [JsonProperty(PropertyName = "gateway")] + public string Gateway { get; set; } + + /// + /// The identifier of the person who submitted the CHB, as it appears on the chargeback notice + /// + [JsonProperty(PropertyName = "cardholder")] + public string Cardholder { get; set; } + + /// + /// Optional issuer message + /// + [JsonProperty(PropertyName = "message")] + public string Message { get; set; } + } +} diff --git a/Riskified.SDK/Model/ChargebackElements/DisputeDetails.cs b/Riskified.SDK/Model/ChargebackElements/DisputeDetails.cs new file mode 100644 index 0000000..44c7028 --- /dev/null +++ b/Riskified.SDK/Model/ChargebackElements/DisputeDetails.cs @@ -0,0 +1,80 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Riskified.SDK.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Riskified.SDK.Model.ChargebackElements +{ + public class DisputeDetails : IJsonSerializable + { + + public DisputeDetails(string caseId = null, + string status = null, + DateTime? disputedAt = null, + DateTime? expectedResolutionDate = null, + string disputeType = null, + string issuerPocPhoneNumber = null) + { + this.CaseId = caseId; + this.Status = status; + this.DisputedAt = disputedAt; + this.ExpectedResolutionDate = expectedResolutionDate; + this.DisputeType = disputeType; + this.IssuerPocPhoneNumber = issuerPocPhoneNumber; + } + + public void Validate(Utils.Validations validationType = Validations.Weak) + { + } + + /// + /// Dispute identifier as defined by the issuer/gateway + /// + [JsonProperty(PropertyName = "case_id")] + public string CaseId { get; set; } + + /// + /// One of the following: + /// candidate + /// ineligible + /// pending + /// won + /// lost + /// Note: we expect to update the api when the dispute status changes + /// + [JsonProperty(PropertyName = "status")] + public string Status { get; set; } + + /// + /// When was the dispute sent + /// + [JsonProperty(PropertyName = "disputed_at")] + public DateTime? DisputedAt { get; set; } + + /// + /// When should we expect a decision from the issuer (60-75 days usually) + /// + [JsonProperty(PropertyName = "expected_resolution_date")] + public DateTime? ExpectedResolutionDate { get; set; } + + /// + /// One of the following: + /// first_dispute + /// second_dispute + /// arbitrary_court + /// Note: we expect to update the api when the dispute status changes + /// + [JsonProperty(PropertyName = "dispute_type")] + public string DisputeType { get; set; } + + /// + /// Credit card issuer or gateway provider phone number + /// + [JsonProperty(PropertyName = "issuer_poc_phone_number")] + public string IssuerPocPhoneNumber { get; set; } + } +} diff --git a/Riskified.SDK/Model/OrderChargeback.cs b/Riskified.SDK/Model/OrderChargeback.cs new file mode 100644 index 0000000..bb45574 --- /dev/null +++ b/Riskified.SDK/Model/OrderChargeback.cs @@ -0,0 +1,51 @@ +using Newtonsoft.Json; +using Riskified.SDK.Model.OrderElements; +using Riskified.SDK.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Riskified.SDK.Model.ChargebackElements; + +namespace Riskified.SDK.Model +{ + public class OrderChargeback : OrderBase + { + /// + /// Creates a new order chargeback + /// + /// The unique id of the order at the merchant systems + public OrderChargeback(string merchantOrderId, ChargebackDetails chargebackDetails, FulfillmentDetails fulfillment, DisputeDetails disputeDetails) + : base(merchantOrderId) + { + this.Chargeback = chargebackDetails; + this.Fulfillment = fulfillment; + this.Dispute = disputeDetails; + } + + public override void Validate(Validations validationType = Validations.Weak) + { + base.Validate(validationType); + } + + /// + /// + /// + [JsonProperty(PropertyName = "chargebackDetails")] + public ChargebackDetails Chargeback { get; set; } + + /// + /// + /// + [JsonProperty(PropertyName = "fulfillmentDetails")] + public FulfillmentDetails Fulfillment { get; set; } + + /// + /// + /// + [JsonProperty(PropertyName = "disputeDetails")] + public DisputeDetails Dispute { get; set; } + + } +} diff --git a/Riskified.SDK/Orders/OrdersGateway.cs b/Riskified.SDK/Orders/OrdersGateway.cs index 29d7289..981174d 100644 --- a/Riskified.SDK/Orders/OrdersGateway.cs +++ b/Riskified.SDK/Orders/OrdersGateway.cs @@ -174,6 +174,11 @@ public OrderNotification Decision(OrderDecision orderDecision) return SendOrder(orderDecision, HttpUtils.BuildUrl(_riskifiedBaseWebhookUrl, "/api/decision")); } + public OrderNotification Chargeback(OrderChargeback orderChargeback) + { + return SendOrder(orderChargeback, HttpUtils.BuildUrl(_riskifiedBaseWebhookUrl, "/api/chargeback")); + } + /// /// Validates the list of historical orders and sends them in batches to Riskified Servers. /// The FinancialStatus field of each order should contain the latest order status as described at "http://apiref.riskified.com/net/#actions-historical" diff --git a/Riskified.SDK/Properties/AssemblyInfo.cs b/Riskified.SDK/Properties/AssemblyInfo.cs index 89d0f42..78b0e30 100644 --- a/Riskified.SDK/Properties/AssemblyInfo.cs +++ b/Riskified.SDK/Properties/AssemblyInfo.cs @@ -5,7 +5,7 @@ // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("Riskified.SDK")] -[assembly: AssemblyDescription("")] +[assembly: AssemblyDescription("Implementation of the Riskified API in C# (official SDK nuget release).")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Riskified")] [assembly: AssemblyProduct("Riskified.SDK")] @@ -31,5 +31,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.0.6.0")] -[assembly: AssemblyFileVersion("2.0.6.0")] +[assembly: AssemblyVersion("2.0.6.1")] +[assembly: AssemblyFileVersion("2.0.6.1")] diff --git a/Riskified.SDK/Riskified.SDK.csproj b/Riskified.SDK/Riskified.SDK.csproj index 383abfe..92c0c92 100644 --- a/Riskified.SDK/Riskified.SDK.csproj +++ b/Riskified.SDK/Riskified.SDK.csproj @@ -30,10 +30,7 @@ 4 - - False - ..\packages\Newtonsoft.Json.6.0.1\lib\net45\Newtonsoft.Json.dll - + @@ -49,6 +46,9 @@ + + +