Skip to content

Commit

Permalink
Drying and refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
wAsnk committed Oct 31, 2024
1 parent babd6ec commit 8af23db
Show file tree
Hide file tree
Showing 37 changed files with 387 additions and 207 deletions.
14 changes: 14 additions & 0 deletions src/Libraries/OrchardCore.Commerce.MoneyDataType/Amount.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
using OrchardCore.Commerce.MoneyDataType.Abstractions;
using OrchardCore.Commerce.MoneyDataType.Serialization;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text.Json.Serialization;

namespace OrchardCore.Commerce.MoneyDataType;
Expand Down Expand Up @@ -82,6 +84,18 @@ public int CompareTo(Amount other)
public Amount GetRounded() =>
new(Math.Round(Value, Currency.DecimalPlaces), Currency);

public long GetPaymentAmount(IEnumerable<string> zeroDecimalCurrencies, IEnumerable<string> specialCases)
{
if (zeroDecimalCurrencies.Contains(Currency.CurrencyIsoCode))
{
return (long)Math.Round(Value);
}

return specialCases.Contains(Currency.CurrencyIsoCode)
? (long)Math.Round(Value / 100m) * 10000
: (long)Math.Round(Value * 100);
}

private void ThrowIfCurrencyDoesntMatch(Amount other, string operation = "compare")
{
if (Currency.Equals(other.Currency)) return;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Stripe;
using System.Threading.Tasks;

namespace OrchardCore.Commerce.Payment.Stripe.Abstractions;

/// <summary>
/// Service for managing Stripe confirmation tokens.
/// </summary>
public interface IStripeConfirmationTokenService
{
/// <summary>
/// Gets the Stripe confirmation token with an Id of <paramref name="confirmationTokenId"/>.
/// </summary>
/// <returns>The Stripe <see cref="ConfirmationToken"/>.</returns>
Task<ConfirmationToken> GetConfirmationTokenAsync(string confirmationTokenId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using Stripe;
using System.Threading.Tasks;
using Address = OrchardCore.Commerce.AddressDataType.Address;

namespace OrchardCore.Commerce.Payment.Stripe.Abstractions;

/// <summary>
/// Stripe customer related services.
/// </summary>
public interface IStripeCustomerService
{
/// <summary>
/// Get the first customer with the given email in Stripe.
/// </summary>
Task<Customer> GetFirstCustomerByEmailAsync(string customerEmail);

/// <summary>
/// Returns <see cref="Customer"/> with the given Id in Stripe.
/// </summary>
Task<Customer> GetCustomerByIdAsync(string customerId);

/// <summary>
/// Returns <see cref="Customer"/> with the given email in Stripe. If not found, create a new customer.
/// </summary>
/// <param name="email">If not provided the current user's email will be used.</param>
Task<Customer> GetAndUpdateOrCreateCustomerAsync(
Address billingAddress,
Address shippingAddress,
string email,
string phone);

/// <summary>
/// Create a new customer in Stripe with the given <paramref name="customerCreateOptions"/>.
/// </summary>
/// <returns>The created Stripe <see cref="Customer"/>.</returns>
Task<Customer> CreateCustomerAsync(CustomerCreateOptions customerCreateOptions);

/// <summary>
/// Create the customer in Stripe with the given details which will be used to create the
/// <see cref="CustomerCreateOptions"/>.
/// </summary>
/// <returns>The created Stripe <see cref="Customer"/>.</returns>
Task<Customer> CreateCustomerAsync(
Address billingAddress,
Address shippingAddress,
string email,
string phone);

/// <summary>
/// Update the customer in Stripe with the given details.
/// </summary>
/// <returns>The updated Stripe <see cref="Customer"/>.</returns>
Task<Customer> UpdateCustomerAsync(
string customerId,
Address billingAddress,
Address shippingAddress,
string email,
string phone);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using OrchardCore.Commerce.MoneyDataType;
using Stripe;
using System.Threading.Tasks;

namespace OrchardCore.Commerce.Payment.Stripe.Abstractions;

public interface IStripePaymentIntentService
{
Task<PaymentIntent> GetPaymentIntentAsync(string paymentIntentId);

Task<PaymentIntent> CreatePaymentIntentAsync(Amount total);
Task<PaymentIntent> CreatePaymentIntentAsync(PaymentIntentCreateOptions options);

Task<PaymentIntent> GetOrUpdatePaymentIntentAsync(
string paymentIntentId,
Amount defaultTotal);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using OrchardCore.Commerce.MoneyDataType;
using OrchardCore.Commerce.Payment.Stripe.Models;
using OrchardCore.Commerce.Payment.Stripe.Services;
using OrchardCore.Commerce.Payment.Stripe.ViewModels;
using OrchardCore.Commerce.Payment.ViewModels;
using OrchardCore.ContentManagement;
using OrchardCore.DisplayManagement.ModelBinding;
Expand All @@ -18,10 +17,6 @@ namespace OrchardCore.Commerce.Payment.Stripe.Abstractions;
/// </summary>
public interface IStripePaymentService
{
Task<ConfirmationToken> GetConfirmationTokenAsync(string confirmationTokenId);

long GetPaymentAmount(Amount total);

/// <summary>
/// Returns the public key of the Stripe account.
/// </summary>
Expand All @@ -32,16 +27,6 @@ public interface IStripePaymentService
/// </summary>
Task<string> CreateClientSecretAsync(Amount total, ShoppingCartViewModel cart);

/// <summary>
/// Returns a <see cref="PaymentIntent"/> object for the given <paramref name="paymentIntentId"/>.
/// </summary>
Task<PaymentIntent> GetPaymentIntentAsync(string paymentIntentId);

/// <summary>
/// Returns a <see cref="PaymentIntent"/> object based on the given <paramref name="total"/>.
/// </summary>
Task<PaymentIntent> CreatePaymentIntentAsync(Amount total);

/// <summary>
/// Creates an order content item in the database, based on the stored <see cref="PaymentIntent"/> and on the
/// current <see cref="ShoppingCart"/> content.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Stripe.Checkout;
using System.Threading.Tasks;

namespace OrchardCore.Commerce.Payment.Stripe.Abstractions;

/// <summary>
/// Event handler for Stripe sessions.
/// </summary>
public interface IStripeSessionEventHandler
{
/// <summary>
/// Called before a Stripe session is created with a prepopulated <see cref="SessionCreateOptions"/>
/// <paramref name="options"/>. Here you can modify the options before the session is created.
/// </summary>
Task StripeSessionCreatingAsync(SessionCreateOptions options) => Task.CompletedTask;

/// <summary>
/// Called after a Stripe session is created with the created <paramref name="session"/> and the
/// <paramref name="options"/> used during creation.
/// </summary>
Task StripeSessionCreatedAsync(Session session, SessionCreateOptions options) => Task.CompletedTask;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Stripe.Checkout;
using System.Threading.Tasks;

namespace OrchardCore.Commerce.Payment.Stripe.Abstractions;

/// <summary>
/// Service for managing Stripe sessions.
/// </summary>
public interface IStripeSessionService
{
/// <summary>
/// Creates a Stripe session using the given <paramref name="options"/>.
/// </summary>
/// <returns>The created Stripe <see cref="Session"/>.</returns>
Task<Session> CreateSessionAsync(SessionCreateOptions options);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using OrchardCore.Commerce.Payment.Stripe.Models;
using OrchardCore.Commerce.Payment.Stripe.ViewModels;
using Stripe;
using System.Threading.Tasks;

namespace OrchardCore.Commerce.Payment.Stripe.Abstractions;

/// <summary>
/// Service for managing Stripe subscriptions.
/// </summary>
public interface IStripeSubscriptionService
{
/// <summary>
/// Updates a Stripe subscription.
/// </summary>
Task UpdateSubscriptionAsync(string subscriptionId, SubscriptionUpdateOptions options);

/// <summary>
/// Gets a Stripe subscription.
/// </summary>
Task<Subscription> GetSubscriptionAsync(string subscriptionId, SubscriptionGetOptions options);

/// <summary>
/// Creates a Stripe subscription using the given <paramref name="options"/>.
/// </summary>
/// <returns>The created Stripe <see cref="Subscription"/>.</returns>
Task<Subscription> CreateSubscriptionAsync(SubscriptionCreateOptions options);

/// <summary>
/// Creates a Stripe subscription using the given <paramref name="viewModel"/>.
/// </summary>
/// <returns>The created Stripe <see cref="Subscription"/>.</returns>
Task<SubscriptionCreateResponse> CreateSubscriptionAsync(StripeCreateSubscriptionViewModel viewModel);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Stripe;
using System.Threading.Tasks;

namespace OrchardCore.Commerce.Payment.Stripe.Abstractions;

/// <summary>
/// Event handler for the Stripe webhook.
/// </summary>
public interface IStripeWebhookEventHandler
{
/// <summary>
/// Called when a Stripe event is received. This is where you can handle the event.
/// </summary>
/// <param name="stripeEvent">Contains the Stripe Event parameters.</param>
Task ReceivedStripeEventAsync(Event stripeEvent);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using Microsoft.Extensions.Logging;
using OrchardCore.Commerce.Payment.Stripe.Abstractions;
using OrchardCore.Commerce.Payment.Stripe.Models;
using OrchardCore.Commerce.Payment.Stripe.Services;
using OrchardCore.Settings;
using Stripe;
using System.Collections.Generic;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using OrchardCore.Commerce.Payment.Stripe.Abstractions;
using OrchardCore.Commerce.Payment.Stripe.Endpoints.Models;
using OrchardCore.Commerce.Payment.Stripe.Endpoints.Permissions;
using OrchardCore.Commerce.Payment.Stripe.Services;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public static IEndpointRouteBuilder AddStripeConfirmationTokenEndpoint(this IEnd

private static async Task<IResult> GetStripeConfirmationTokenAsync(
[FromQuery] string? confirmationTokenId,
[FromServices] IStripePaymentService stripePaymentService,
[FromServices] IStripeConfirmationTokenService stripeConfirmationTokenService,
[FromServices] IAuthorizationService authorizationService,
HttpContext httpContext)
{
Expand All @@ -30,7 +30,7 @@ private static async Task<IResult> GetStripeConfirmationTokenAsync(
return httpContext.ChallengeOrForbidApi();
}

var confirmationToken = await stripePaymentService.GetConfirmationTokenAsync(confirmationTokenId);
var confirmationToken = await stripeConfirmationTokenService.GetConfirmationTokenAsync(confirmationTokenId);
return TypedResults.Ok(confirmationToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using OrchardCore.Commerce.Payment.Stripe.Abstractions;
using OrchardCore.Commerce.Payment.Stripe.Endpoints.Permissions;
using OrchardCore.Commerce.Payment.Stripe.Services;
using Stripe;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using static OrchardCore.Commerce.Payment.Constants.CurrencyCollectionConstants;
using static OrchardCore.Commerce.Payment.Stripe.Endpoints.Constants.Endpoints;

namespace OrchardCore.Commerce.Payment.Stripe.Endpoints.Api;
Expand Down Expand Up @@ -65,7 +66,6 @@ public static IEndpointRouteBuilder AddStripeTotalEndpoint(this IEndpointRouteBu
private static async Task<IResult> GetStripeTotalAsync(
[FromQuery] string? shoppingCartId,
[FromServices] IShoppingCartService shoppingCartService,
[FromServices] IStripePaymentService stripePaymentService,
[FromServices] IAuthorizationService authorizationService,
HttpContext httpContext)
{
Expand All @@ -83,7 +83,7 @@ private static async Task<IResult> GetStripeTotalAsync(
var total = shoppingCartViewModel.Totals.Single();
return TypedResults.Ok(new
{
Amount = stripePaymentService.GetPaymentAmount(total),
Amount = total.GetPaymentAmount(ZeroDecimalCurrencies, SpecialCases),
total.Currency,
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public static IEndpointRouteBuilder AddStripePaymentIntentGetEndpoint(this IEndp

private static async Task<IResult> GetPaymentIntentAsync(
[FromQuery] string paymentIntentId,
[FromServices] IStripePaymentService stripePaymentService,
[FromServices] IStripePaymentIntentService stripePaymentIntentService,
[FromServices] IAuthorizationService authorizationService,
HttpContext httpContext
)
Expand All @@ -34,7 +34,7 @@ HttpContext httpContext
return httpContext.ChallengeOrForbidApi();
}

var paymentIntent = await stripePaymentService.GetPaymentIntentAsync(paymentIntentId);
var paymentIntent = await stripePaymentIntentService.GetPaymentIntentAsync(paymentIntentId);
return TypedResults.Ok(paymentIntent);
}

Expand All @@ -46,6 +46,7 @@ public static IEndpointRouteBuilder AddStripePaymentIntentPostEndpoint(this IEnd

private static async Task<IResult> CreatePaymentIntentAsync(
[FromBody] CreatePaymentIntentWithOrderViewModel viewModel,
[FromServices] IStripePaymentIntentService stripePaymentIntentService,
[FromServices] IStripePaymentService stripePaymentService,
[FromServices] IShoppingCartService shoppingCartService,
[FromServices] IAuthorizationService authorizationService,
Expand All @@ -58,7 +59,7 @@ private static async Task<IResult> CreatePaymentIntentAsync(

var shoppingCartViewModel = await shoppingCartService.GetAsync(viewModel.ShoppingCartId);
var total = shoppingCartViewModel.Totals.Single();
var paymentIntent = await stripePaymentService.CreatePaymentIntentAsync(total);
var paymentIntent = await stripePaymentIntentService.CreatePaymentIntentAsync(total);

var order = await stripePaymentService.CreateOrUpdateOrderFromShoppingCartAsync(
updateModelAccessor: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using OrchardCore.Commerce.Endpoints;
using OrchardCore.Commerce.Payment.Stripe.Abstractions;
using OrchardCore.Commerce.Payment.Stripe.Endpoints.Permissions;
using OrchardCore.Commerce.Payment.Stripe.Extensions;
using OrchardCore.Commerce.Payment.Stripe.Services;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,20 @@
using System.Threading.Tasks;
using static Stripe.Events;

namespace OrchardCore.Commerce.Payment.Stripe.Services;
namespace OrchardCore.Commerce.Payment.Stripe.Handlers;

public class DefaultStripeWebhookEventHandler : IStripeWebhookEventHandler
{
private readonly IStripePaymentIntentService _stripePaymentIntentService;
private readonly IStripePaymentService _stripePaymentService;

public DefaultStripeWebhookEventHandler(IStripePaymentService stripePaymentService) =>
public DefaultStripeWebhookEventHandler(
IStripePaymentIntentService stripePaymentIntentService,
IStripePaymentService stripePaymentService)
{
_stripePaymentIntentService = stripePaymentIntentService;
_stripePaymentService = stripePaymentService;
}

public async Task ReceivedStripeEventAsync(Event stripeEvent)
{
Expand All @@ -28,7 +34,7 @@ public async Task ReceivedStripeEventAsync(Event stripeEvent)
return;
}

var paymentIntent = await _stripePaymentService.GetPaymentIntentAsync(paymentIntentId);
var paymentIntent = await _stripePaymentIntentService.GetPaymentIntentAsync(paymentIntentId);
await _stripePaymentService.UpdateOrderToOrderedAsync(paymentIntent, shoppingCartId: null);
}
else if (stripeEvent.Type == PaymentIntentPaymentFailed)
Expand Down
Loading

0 comments on commit 8af23db

Please sign in to comment.