Skip to content

Commit

Permalink
Resolves #821 Web API: add endpoints for RecurringPayment and Recurri…
Browse files Browse the repository at this point in the history
…ngPaymentHistory
  • Loading branch information
mgesing committed Oct 18, 2023
1 parent 53d926e commit 187a193
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 17 deletions.
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
- Web API:
- #142 Take back in stock notifications into account.
- #805 Add endpoints to assign discounts to entities.
- #821 Add endpoints for RecurringPayment and RecurringPaymentHistory.
- Import:
- #390 Add a setting for whether to send the completion email.
- #377 Import cross- and checkout-selling products.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ public partial interface IOrderProcessingService
Task CancelRecurringPaymentAsync(RecurringPayment recurringPayment);

/// <summary>
/// Processes the next recurring psayment.
/// Processes the next recurring payment.
/// </summary>
/// <param name="recurringPayment">Recurring payment.</param>
Task ProcessNextRecurringPaymentAsync(RecurringPayment recurringPayment);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,24 +326,18 @@ public virtual async Task<bool> CanCancelRecurringPaymentAsync(RecurringPayment
var initialOrder = recurringPayment.InitialOrder;
var customer = initialOrder?.Customer;

if (initialOrder == null || customer == null)
if (customer == null || initialOrder == null || initialOrder.OrderStatus == OrderStatus.Cancelled)
{
return false;
}

if (initialOrder.OrderStatus == OrderStatus.Cancelled ||
(!customerToValidate.IsAdmin() && customer.Id != customerToValidate.Id))
if (customer.Id != customerToValidate.Id && !customerToValidate.IsAdmin())
{
return false;
}

var nextPaymentDate = await _paymentService.GetNextRecurringPaymentDateAsync(recurringPayment);
if (!nextPaymentDate.HasValue)
{
return false;
}

return true;
return nextPaymentDate.HasValue;
}

public virtual async Task CancelRecurringPaymentAsync(RecurringPayment recurringPayment)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using Smartstore.Core.Checkout.Payment;

namespace Smartstore.Web.Api.Controllers
{
/// <summary>
/// The endpoint for operations on RecurringPaymentHistory entity.
/// </summary>
public class RecurringPaymentHistoryController : WebApiController<RecurringPaymentHistory>
{
[HttpGet("RecurringPaymentHistory"), ApiQueryable]
[Permission(Permissions.Order.Read)]
public IQueryable<RecurringPaymentHistory> Get()
{
return Entities.AsNoTracking();
}

[HttpGet("RecurringPaymentHistory({key})"), ApiQueryable]
[Permission(Permissions.Order.Read)]
public SingleResult<RecurringPaymentHistory> Get(int key)
{
return GetById(key);
}

[HttpGet("RecurringPaymentHistory({key})/RecurringPayment"), ApiQueryable]
[Permission(Permissions.Order.Read)]
public SingleResult<RecurringPayment> GetRecurringPayment(int key)
{
return GetRelatedEntity(key, x => x.RecurringPayment);
}

[HttpPost]
[Permission(Permissions.Order.EditRecurringPayment)]
public Task<IActionResult> Post([FromBody] RecurringPaymentHistory model)
{
return PostAsync(model);
}

[HttpPut]
[Permission(Permissions.Order.EditRecurringPayment)]
public Task<IActionResult> Put(int key, Delta<RecurringPaymentHistory> model)
{
return PutAsync(key, model);
}

[HttpPatch]
[Permission(Permissions.Order.EditRecurringPayment)]
public Task<IActionResult> Patch(int key, Delta<RecurringPaymentHistory> model)
{
return PatchAsync(key, model);
}

[HttpDelete]
[Permission(Permissions.Order.EditRecurringPayment)]
public Task<IActionResult> Delete(int key)
{
return DeleteAsync(key);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
using Smartstore.Core.Checkout.Orders;
using Smartstore.Core.Checkout.Payment;

namespace Smartstore.Web.Api.Controllers
{
/// <summary>
/// The endpoint for operations on RecurringPayment entity.
/// </summary>
public class RecurringPaymentsController : WebApiController<RecurringPayment>
{
private readonly Lazy<IOrderProcessingService> _orderProcessingService;

public RecurringPaymentsController(Lazy<IOrderProcessingService> orderProcessingService)
{
_orderProcessingService = orderProcessingService;
}

[HttpGet("RecurringPayments"), ApiQueryable]
[Permission(Permissions.Order.Read)]
public IQueryable<RecurringPayment> Get()
{
return Entities.AsNoTracking();
}

[HttpGet("RecurringPayments({key})"), ApiQueryable]
[Permission(Permissions.Order.Read)]
public SingleResult<RecurringPayment> Get(int key)
{
return GetById(key);
}

[HttpGet("RecurringPayments({key})/RecurringPaymentHistory"), ApiQueryable]
[Permission(Permissions.Order.Read)]
public IQueryable<RecurringPaymentHistory> GetRecurringPaymentHistory(int key)
{
return GetRelatedQuery(key, x => x.RecurringPaymentHistory);
}

[HttpGet("RecurringPayments({key})/InitialOrder"), ApiQueryable]
[Permission(Permissions.Order.Read)]
public SingleResult<Order> GetInitialOrder(int key)
{
return GetRelatedEntity(key, x => x.InitialOrder);
}

[HttpPost]
[Permission(Permissions.Order.EditRecurringPayment)]
public Task<IActionResult> Post([FromBody] RecurringPayment model)
{
return PostAsync(model);
}

[HttpPut]
[Permission(Permissions.Order.EditRecurringPayment)]
public Task<IActionResult> Put(int key, Delta<RecurringPayment> model)
{
return PutAsync(key, model);
}

[HttpPatch]
[Permission(Permissions.Order.EditRecurringPayment)]
public Task<IActionResult> Patch(int key, Delta<RecurringPayment> model)
{
return PatchAsync(key, model);
}

[HttpDelete]
[Permission(Permissions.Order.EditRecurringPayment)]
public Task<IActionResult> Delete(int key)
{
return DeleteAsync(key);
}

#region Actions and functions

/// <summary>
/// Processes the next recurring payment.
/// </summary>
[HttpPost("RecurringPayments({key})/ProcessNextRecurringPayment"), ApiQueryable]
[Permission(Permissions.Order.EditRecurringPayment)]
[Produces(Json)]
[ProducesResponseType(typeof(RecurringPayment), Status200OK)]
[ProducesResponseType(Status404NotFound)]
[ProducesResponseType(Status422UnprocessableEntity)]
public async Task<IActionResult> ProcessNextRecurringPayment(int key)
{
try
{
var entity = await GetRequiredById(key, q => q
.Include(x => x.InitialOrder)
.ThenInclude(x => x.Customer));

await _orderProcessingService.Value.ProcessNextRecurringPaymentAsync(entity);

return Ok(entity);
}
catch (Exception ex)
{
return ErrorResult(ex);
}
}

/// <summary>
/// Cancels a recurring payment.
/// </summary>
[HttpPost("RecurringPayments({key})/CancelRecurringPayment"), ApiQueryable]
[Permission(Permissions.Order.EditRecurringPayment)]
[Produces(Json)]
[ProducesResponseType(typeof(RecurringPayment), Status200OK)]
[ProducesResponseType(Status404NotFound)]
[ProducesResponseType(Status422UnprocessableEntity)]
public async Task<IActionResult> CancelRecurringPayment(int key)
{
try
{
var entity = await GetRequiredById(key, q => q
.Include(x => x.InitialOrder)
.ThenInclude(x => x.Customer));

await _orderProcessingService.Value.CancelRecurringPaymentAsync(entity);

return Ok(entity);
}
catch (Exception ex)
{
return ErrorResult(ex);
}
}

#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public override void Build(ODataModelBuilder builder, int version)
builder.EntitySet<ProductVariantAttribute>("ProductVariantAttributes");
builder.EntitySet<ProductVariantAttributeValue>("ProductVariantAttributeValues");
builder.EntitySet<QuantityUnit>("QuantityUnits");
builder.EntitySet<RecurringPaymentHistory>("RecurringPaymentHistory");
builder.EntitySet<RelatedProduct>("RelatedProducts");
builder.EntitySet<RewardPointsHistory>("RewardPointsHistory");
builder.EntitySet<Setting>("Settings");
Expand All @@ -91,14 +92,15 @@ public override void Build(ODataModelBuilder builder, int version)
BuildCategories(builder);
BuildDeliveryTimes(builder);
BuildImportProfiles(builder);
BuildManufacturers(builder);
BuildMediaFiles(builder);
BuildMediaFolders(builder);
BuildNewsletterSubscriptions(builder);
BuildOrderItems(builder);
BuildOrders(builder);
BuildPaymentMethods(builder);
BuildProducts(builder);
BuildManufacturers(builder);
BuildRecurringPayments(builder);
BuildShipments(builder);
BuildShoppingCartItems(builder);
}
Expand Down Expand Up @@ -147,6 +149,17 @@ private static void BuildImportProfiles(ODataModelBuilder builder)
.Optional();
}

private static void BuildManufacturers(ODataModelBuilder builder)
{
var set = builder.EntitySet<Manufacturer>("Manufacturers");

set.EntityType
.Action(nameof(ManufacturersController.ApplyDiscounts))
.ReturnsCollectionFromEntitySet<Discount>("Discounts")
.CollectionParameter<int>("discountIds")
.Required();
}

private static void BuildMediaFiles(ODataModelBuilder builder)
{
// INFO: #2198 referenced entity set type must not differ to avoid InvalidOperationException in Microsoft.OData.Client.
Expand Down Expand Up @@ -475,15 +488,17 @@ private static void BuildProducts(ODataModelBuilder builder)
.Optional();
}

private static void BuildManufacturers(ODataModelBuilder builder)
private static void BuildRecurringPayments(ODataModelBuilder builder)
{
var set = builder.EntitySet<Manufacturer>("Manufacturers");
var set = builder.EntitySet<RecurringPayment>("RecurringPayments");

set.EntityType
.Action(nameof(ManufacturersController.ApplyDiscounts))
.ReturnsCollectionFromEntitySet<Discount>("Discounts")
.CollectionParameter<int>("discountIds")
.Required();
.Action(nameof(RecurringPaymentsController.ProcessNextRecurringPayment))
.ReturnsFromEntitySet(set);

set.EntityType
.Action(nameof(RecurringPaymentsController.CancelRecurringPayment))
.ReturnsFromEntitySet(set);
}

private static void BuildShipments(ODataModelBuilder builder)
Expand Down

0 comments on commit 187a193

Please sign in to comment.