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

Paypal integration additions and updates to CCTransactionResult mapping #133

Merged
merged 1 commit into from
Nov 26, 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: 23 additions & 11 deletions OrderCloud.Catalyst/Integrations/Interfaces/ICreditCardProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,32 @@ public interface ICreditCardProcessor
/// </summary>
Task<string> GetIFrameCredentialAsync(OCIntegrationConfig overrideConfig = null);
/// <summary>
/// Create the payment request to initialize payment processing.
/// Create the payment request to initialize payment processing. Optionally define whether the intent is authorization only, or capture.
/// </summary>
Task<CCTransactionResult> InitializePaymentRequestAsync(AuthorizeCCTransaction transaction, OCIntegrationConfig overrideConfig = null);
/// <summary>
/// Attempt to verify the user can pay by placing a hold on a credit card. Funds will be captured later. Typically used as a verification step directly before order submit.
/// </summary>
Task<CCTransactionResult> AuthorizeOnlyAsync(AuthorizeCCTransaction transaction, OCIntegrationConfig overrideConfig = null);
Task<CCTransactionResult> InitializePaymentRequestAsync(AuthorizeCCTransaction transaction, OCIntegrationConfig overrideConfig = null, bool isCapture = false);
/// <summary>
/// Attempt to capture funds from a credit card. A prior authorization is required. Typically used when a shipment is created, at the end of the day, or a defined time period after submit.
/// Attempt to verify the user can pay by placing a hold on a credit card. Funds will be captured later. Typically used as a verification step directly before order submit.
/// </summary>
Task<CCTransactionResult> CapturePriorAuthorizationAsync(FollowUpCCTransaction transaction, OCIntegrationConfig overrideConfig = null);
Task<CCTransactionResult> AuthorizeOnlyAsync(AuthorizeCCTransaction transaction, OCIntegrationConfig overrideConfig = null);
/// <summary>
/// Remove an authorization hold previously placed on a credit card. Use if order submit fails, or if order is canceled/returned before capture.
/// Attempt to capture funds from a credit card. A prior authorization is required. Typically used when a shipment is created, at the end of the day, or a defined time period after submit.
/// </summary>
Task<CCTransactionResult> VoidAuthorizationAsync(FollowUpCCTransaction transaction, OCIntegrationConfig overrideConfig = null);
Task<CCTransactionResult> CapturePriorAuthorizationAsync(FollowUpCCTransaction transaction, OCIntegrationConfig overrideConfig = null);
/// <summary>
/// Capture funds at the time of the transaction without prior authorization.
/// </summary>
Task<CCTransactionResult> CapturePaymentAsync(FollowUpCCTransaction transaction, OCIntegrationConfig overrideConfig = null);
/// <summary>
/// Remove an authorization hold previously placed on a credit card. Use if order submit fails, or if order is canceled/returned before capture.
/// </summary>
Task<CCTransactionResult> VoidAuthorizationAsync(FollowUpCCTransaction transaction, OCIntegrationConfig overrideConfig = null);
/// <summary>
/// Refund a previously captured amount. Used if an order is canceled/returned after capture. Refunding generally incures extra processing fees, whereas voiding does not.
/// </summary>
Task<CCTransactionResult> RefundCaptureAsync(FollowUpCCTransaction transaction, OCIntegrationConfig overrideConfig = null);
}

public class AuthorizeCCTransaction
public class AuthorizeCCTransaction
{
/// <summary>
/// The OrderCloud Order ID that this card transaction applies to.
Expand Down Expand Up @@ -107,6 +111,14 @@ public class CCTransactionResult
/// User readable text explaining the result.
/// </summary>
public string Message { get; set; }
/// <summary>
/// The ID of the merchant associated with the transaction
/// </summary>
public string MerchantID { get; set; }
/// <summary>
/// If there are multiple merchant captures processed, store each response in a nested CCTransactionResult
/// </summary>
public List<CCTransactionResult> InnerTransactions { get; set; }
}

/// <summary>
Expand Down
7 changes: 6 additions & 1 deletion OrderCloud.Integrations.Payment.BlueSnap/BlueSnapService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ public async Task<string> GetIFrameCredentialAsync(OCIntegrationConfig overrideC
return token;
}

public Task<CCTransactionResult> InitializePaymentRequestAsync(AuthorizeCCTransaction transaction, OCIntegrationConfig overrideConfig = null)
public Task<CCTransactionResult> InitializePaymentRequestAsync(AuthorizeCCTransaction transaction, OCIntegrationConfig overrideConfig = null, bool isCapture = false)
{
throw new NotImplementedException();
}

public Task<CCTransactionResult> CapturePaymentAsync(FollowUpCCTransaction transaction, OCIntegrationConfig overrideConfig = null)
{
throw new NotImplementedException();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ public async Task<string> GetIFrameCredentialAsync(OCIntegrationConfig overrideC
return token;
}

public Task<CCTransactionResult> InitializePaymentRequestAsync(AuthorizeCCTransaction transaction, OCIntegrationConfig overrideConfig = null)
public Task<CCTransactionResult> InitializePaymentRequestAsync(AuthorizeCCTransaction transaction, OCIntegrationConfig overrideConfig = null, bool isCapture = false)
{
throw new NotImplementedException();
}

public Task<CCTransactionResult> CapturePaymentAsync(FollowUpCCTransaction transaction, OCIntegrationConfig overrideConfig = null)
{
throw new NotImplementedException();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using OrderCloud.Catalyst;
Expand All @@ -8,45 +9,140 @@ namespace OrderCloud.Integrations.Payment.PayPal.Mappers
{
public class PayPalOrderPaymentMapper
{
public PurchaseUnit MapToPurchaseUnit(AuthorizeCCTransaction transaction) => new PurchaseUnit()
public List<PurchaseUnit> MapToPurchaseUnit(AuthorizeCCTransaction transaction, PayPalConfig config)
{
amount = new Amount()
PayPalAddress address = null;
if (transaction.AddressVerification != null)
{
currency_code = transaction.Currency,
value = transaction.Amount.ToString(CultureInfo.InvariantCulture) ?? null
address = new PayPalAddress()
{
address_line_1 = transaction.AddressVerification?.Street1,
address_line_2 = transaction.AddressVerification?.Street2,
admin_area_1 = transaction.AddressVerification?.State,
admin_area_2 = transaction.AddressVerification?.City,
postal_code = transaction.AddressVerification?.Zip,
country_code = transaction.AddressVerification?.Country
};
}
};
var purchaseUnits = new List<PurchaseUnit>();
if (config.Merchants.Any())
{
config.Merchants.ForEach(m =>
{
var merchantLines =
transaction?.OrderWorksheet?.LineItems?.Where(li => li.Product.DefaultSupplierID == m.SupplierID).ToList();
if (merchantLines != null && merchantLines.Any())
{
var merchantUnit = new PurchaseUnit()
{
amount = new Amount()
{
currency_code = transaction.Currency,
value = merchantLines.Sum(li => li.LineTotal).ToString(CultureInfo.InvariantCulture) ??
null // sum Amount for each merchant
},
payee = new Payee()
{
merchant_id = m.MerchantID,
},
description = transaction?.OrderWorksheet?.Order?.Comments,
reference_id = Guid.NewGuid().ToString(),
invoice_id = Guid.NewGuid().ToString(),
};
if (address != null)
{
merchantUnit.shipping = new Shipping()
{
address = address
};
}

purchaseUnits.Add(merchantUnit);
}
});
}

var unit = new PurchaseUnit()
{
amount = new Amount()
{
currency_code = transaction.Currency,
value = transaction.Amount.ToString(CultureInfo.InvariantCulture) ?? null
}
};
if (address != null)
{
unit.shipping = new Shipping()
{
address = address
};
}
purchaseUnits.Add(unit);

return purchaseUnits;
}

public CCTransactionResult MapAuthorizedPaymentToCCTransactionResult(PayPalOrder authorizedOrder)
{
var amount = ConvertStringAmountToDecimal(authorizedOrder.purchase_units.FirstOrDefault()?.payments.authorizations
.FirstOrDefault()?.amount.value);
var authorizationId = authorizedOrder.purchase_units.FirstOrDefault()?.payments.authorizations
.FirstOrDefault()?.id;
var innerTransactions = new List<CCTransactionResult>();
authorizedOrder.purchase_units.ForEach(u =>
{
var capture = u.payments.authorizations.FirstOrDefault();
if (capture != null)
{
innerTransactions.Add(new CCTransactionResult()
{
TransactionID = capture.id,
Amount = ConvertStringAmountToDecimal(u.amount.value),
Succeeded = capture.status.ToLowerInvariant() == "completed",
MerchantID = u.payee.merchant_id
});
}
});
var ccTransaction = new CCTransactionResult
{
Succeeded = authorizedOrder.status.ToLowerInvariant() == "completed" && authorizationId != null,
Amount = amount,
TransactionID = authorizationId, // Authorization ID needed to Capture payment or Void Authorization
Succeeded = authorizedOrder.status.ToLowerInvariant() == "completed",
TransactionID = authorizedOrder.id, // Authorization ID needed to Capture payment or Void Authorization
ResponseCode = authorizedOrder.processor_response.response_code,
AuthorizationCode = null,
AVSResponseCode = authorizedOrder.processor_response.avs_code,
Message = null
Message = null,
Amount = authorizedOrder.purchase_units.Sum(unit => ConvertStringAmountToDecimal(unit.amount.value)),
InnerTransactions = innerTransactions
};
return ccTransaction;
}

public CCTransactionResult MapCapturedPaymentToCCTransactionResult(PayPalOrder capturedOrder) =>
new CCTransactionResult
public CCTransactionResult MapCapturedPaymentToCCTransactionResult(PayPalOrder capturedOrder)
{
var innerTransactions = new List<CCTransactionResult>();
capturedOrder.purchase_units.ForEach(u =>
{
var capture = u.payments.captures.FirstOrDefault();
if (capture != null)
{
innerTransactions.Add(new CCTransactionResult()
{
TransactionID = capture.id,
Amount = ConvertStringAmountToDecimal(u.amount.value),
Succeeded = capture.status.ToLowerInvariant() == "completed",
MerchantID = u.payee.merchant_id
});
}
});
return new CCTransactionResult
{
TransactionID = capturedOrder.id, // Capture ID needed to Refund payment
ResponseCode = capturedOrder.processor_response.response_code,
AuthorizationCode = null,
AVSResponseCode = capturedOrder.processor_response.avs_code,
Message = null,
Succeeded = capturedOrder.status.ToLowerInvariant() == "completed",
Amount = 0
Amount = capturedOrder.purchase_units.Sum(unit => ConvertStringAmountToDecimal(unit.amount.value)),
InnerTransactions = innerTransactions
};
}


public CCTransactionResult MapRefundPaymentToCCTransactionResult(PayPalOrderReturn orderReturn) =>
new CCTransactionResult
Expand Down
33 changes: 33 additions & 0 deletions OrderCloud.Integrations.Payment.PayPal/Models/PayPalOrder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,50 @@ public class Payer
public string payer_id { get; set; }
}

public class Payee
{
public string merchant_id { get; set; }
}

public class RelatedLink
{
public string href { get; set; }
public string rel { get; set; }
public string method { get; set; }
}

public class PayPalAddress
{
public string address_line_1 { get; set; }
public string address_line_2 { get; set; }

public string admin_area_1 { get; set; }
public string admin_area_2 { get; set; }
public string postal_code { get; set; }
public string country_code { get; set; }
}

public class Shipping
{
public PayPalAddress address { get; set; }
}

public class PurchaseUnit
{
// The merchant ID for the purchase unit.
public string reference_id { get; set; }
public string description { get; set; }
public string invoice_id { get; set; }
public Amount amount { get; set; }
public PurchaseUnitPayment payments { get; set; }
public Shipping shipping { get; set; }
public Payee payee { get; set; }
}

public class PurchaseUnitPayment
{
public List<PurchaseUnitAuthorization> authorizations { get; set; }
public List<PurchaseUnitAuthorization> captures { get; set; }
}

public class PurchaseUnitAuthorization
Expand All @@ -53,6 +79,7 @@ public class PurchaseUnitAuthorization
public Amount amount { get; set; }
public List<RelatedLink> links { get; set; }
}

public class Amount
{
// The three-character ISO-4217 currency code.
Expand All @@ -72,11 +99,17 @@ public class PaymentSource
public PaymentToken token { get; set; }
}

public class ExperienceContext
{
public string shipping_preference { get; set; }
}

public class PayPal
{
public Name name { get; set; }
public string email_address { get; set; }
public string account_id { get; set; }
public ExperienceContext experience_context { get; set; }
}

public class Card
Expand Down
Loading
Loading