Browse Source

refactor premium organization feature

pull/69/head
Berkan Sasmaz 5 years ago
parent
commit
d9904eba6a
  1. 6
      modules/payment/src/Payment.Application/PaymentRequests/PaymentRequestAppService.cs
  2. 17
      src/EventHub.Domain.Shared/EventHubDomainSharedModule.cs
  3. 8
      src/EventHub.Domain.Shared/Organizations/OrganizationPaymentRequestExtraParameterConfiguration.cs
  4. 4
      src/EventHub.Domain/Emailing/EmailTemplates.cs
  5. 8
      src/EventHub.Domain/Emailing/Templates/PaymentRequestCompleted.tpl
  6. 12
      src/EventHub.Domain/Emailing/Templates/PaymentRequestFailed.tpl
  7. 64
      src/EventHub.Domain/Organizations/PaymentRequests/PaymentRequestEventHandler.cs
  8. 18
      src/EventHub.Web/EventHubWebModule.cs
  9. 46
      src/EventHub.Web/Pages/Organizations/Profile.cshtml
  10. 8
      src/EventHub.Web/Pages/Payment/PostCheckout.cshtml
  11. 18
      src/EventHub.Web/Pages/Payment/PreCheckout.cshtml
  12. 17
      src/EventHub.Web/Pages/Pricing.cshtml
  13. 38
      src/EventHub.Web/Pages/Pricing.cshtml.cs
  14. 34
      src/EventHub.Web/PaymentRequests/PremiumPlanInfoOptions.cs
  15. 9
      src/EventHub.Web/appsettings.json

6
modules/payment/src/Payment.Application/PaymentRequests/PaymentRequestAppService.cs

@ -13,9 +13,9 @@ namespace Payment.PaymentRequests
{
public class PaymentRequestAppService : PaymentAppService, IPaymentRequestAppService
{
private readonly IPaymentRequestRepository _paymentRequestRepository;
private readonly PaymentOptions _paymentOptions;
private readonly PayPalHttpClient _payPalHttpClient;
protected readonly IPaymentRequestRepository _paymentRequestRepository;
protected readonly PaymentOptions _paymentOptions;
protected readonly PayPalHttpClient _payPalHttpClient;
public PaymentRequestAppService(
IPaymentRequestRepository paymentRequestRepository,

17
src/EventHub.Domain.Shared/EventHubDomainSharedModule.cs

@ -1,6 +1,4 @@
using System.Collections.Generic;
using EventHub.Localization;
using EventHub.Organizations;
using EventHub.Web;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AuditLogging;
@ -62,21 +60,6 @@ namespace EventHub
});
Configure<EventHubUrlOptions>(configuration.GetSection("AppUrls"));
context.Services.AddOptions<List<OrganizationPlanInfoOptions>>()
.Bind(configuration.GetSection(OrganizationPlanInfoOptions.OrganizationPlanInfo))
.Validate(config =>
{
foreach (var planInfo in config)
{
if (planInfo.IsActive)
{
return planInfo.OnePremiumPeriodAsMonth > planInfo.CanBeExtendedAfterHowManyMonths;
}
}
return true;
}, "OnePremiumPeriodAsMonth must be greater than CanBeExtendedAfterHowManyMonths.");
}
}
}

8
src/EventHub.Domain.Shared/Organizations/PaymentRequestProductExtraParameterConfiguration.cs → src/EventHub.Domain.Shared/Organizations/OrganizationPaymentRequestExtraParameterConfiguration.cs

@ -1,11 +1,11 @@
namespace EventHub.Organizations
{
public class PaymentRequestProductExtraParameterConfiguration
public class OrganizationPaymentRequestExtraParameterConfiguration
{
public string OrganizationName { get; set; }
public OrganizationPlanType TargetPlanType { get; set; }
public bool IsExtend { get; set; }
public int PremiumPeriodAsMonth { get; set; }
}
}

4
src/EventHub.Domain/Emailing/EmailTemplates.cs

@ -12,8 +12,8 @@ namespace EventHub.Emailing
public const string EventTimingChanged = "EventHub.Event.EventTimingChanged";
public const string PaymentRequestCompleted = "EventHub.Event.PaymentRequestCompleted";
public const string PaymentRequestCompleted = "EventHub.Organization.PaymentRequestCompleted";
public const string PaymentRequestFailed = "EventHub.Event.PaymentRequestFailed";
public const string PaymentRequestFailed = "EventHub.Organization.PaymentRequestFailed";
}
}

8
src/EventHub.Domain/Emailing/Templates/PaymentRequestCompleted.tpl

@ -132,7 +132,7 @@
<td style="padding-bottom:10px;padding-left:40px;padding-right:40px;padding-top:10px;">
<div style="font-family: sans-serif">
<div style="font-size: 12px; font-family: Montserrat, Trebuchet MS, Lucida Grande, Lucida Sans Unicode, Lucida Sans, Tahoma, sans-serif; color: #555555; line-height: 1.5;">
<p style="margin: 0; font-size: 14px; text-align: center;">Your organization has been activated.</p>
<p style="margin: 0; font-size: 14px; text-align: center;">Your organization has been {{model.plan_info}}.</p>
</div>
</div>
</td>
@ -190,7 +190,7 @@
<div style="font-family: sans-serif">
<div style="font-size: 12px; color: #555555; line-height: 1.5; font-family: Montserrat, Trebuchet MS, Lucida Grande, Lucida Sans Unicode, Lucida Sans, Tahoma, sans-serif;">
<p style="margin: 0; font-size: 16px; text-align: center;"><span style="color:#a2a9ad;"><strong>Organization name</strong></span></p>
<p style="margin: 0; font-size: 16px; text-align: center;"><span style="color:#2b303a;"><span style="font-size:20px;"><strong>{{model.organizationName}}</strong></span></span></p>
<p style="margin: 0; font-size: 16px; text-align: center;"><span style="color:#2b303a;"><span style="font-size:20px;"><strong>{{model.organization_name}}</strong></span></span></p>
</div>
</div>
</td>
@ -223,7 +223,7 @@
<div style="font-family: sans-serif">
<div style="font-size: 12px; color: #555555; line-height: 1.5; font-family: Montserrat, Trebuchet MS, Lucida Grande, Lucida Sans Unicode, Lucida Sans, Tahoma, sans-serif;">
<p style="margin: 0; font-size: 16px; text-align: center; mso-line-height-alt: 18px;"><span style="color:#a2a9ad;font-size:12px;"><strong>TOTAL</strong></span></p>
<p style="margin: 0; font-size: 16px; text-align: center;"><span style="color:#2b303a;"><span style="font-size:20px;"><strong>{{model.Price}} <strong>{{model.Currency}}</strong></strong></span></span></p>
<p style="margin: 0; font-size: 16px; text-align: center;"><span style="color:#2b303a;"><span style="font-size:20px;"><strong>{{model.price}} <strong>{{model.currency}}</strong></strong></span></span></p>
</div>
</div>
</td>
@ -253,7 +253,7 @@
<tr>
<td style="padding-left:10px;padding-right:10px;padding-top:40px;text-align:center;">
<div align="center">
<!--[if mso]><v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="{app_url}}/organizations/{{model.organizationName}}" style="height:58px;width:160px;v-text-anchor:middle;" arcsize="104%" stroke="false" fillcolor="#d54560"><w:anchorlock/><v:textbox inset="0px,0px,0px,0px"><center style="color:#ffffff; font-family:Tahoma, sans-serif; font-size:14px"><![endif]--><a href="{app_url}}/organizations/{{model.organizationName}}" target="_blank" style="text-decoration:none;display:inline-block;color:#ffffff;background-color:#d54560;border-radius:60px;width:auto;border-top:1px solid #d54560;border-right:1px solid #d54560;border-bottom:1px solid #d54560;border-left:1px solid #d54560;padding-top:15px;padding-bottom:15px;font-family:Montserrat, Trebuchet MS, Lucida Grande, Lucida Sans Unicode, Lucida Sans, Tahoma, sans-serif;text-align:center;mso-border-alt:none;word-break:keep-all;"><span style="padding-left:30px;padding-right:30px;font-size:14px;display:inline-block;letter-spacing:normal;"><span style="font-size: 12px; line-height: 2; word-break: break-word; mso-line-height-alt: 24px;"><span style="font-size: 14px; line-height: 28px;" data-mce-style="font-size: 14px; line-height: 28px;">My Organization</span></span></span></a>
<!--[if mso]><v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="{app_url}}/organizations/{{model.organization_name}}" style="height:58px;width:160px;v-text-anchor:middle;" arcsize="104%" stroke="false" fillcolor="#d54560"><w:anchorlock/><v:textbox inset="0px,0px,0px,0px"><center style="color:#ffffff; font-family:Tahoma, sans-serif; font-size:14px"><![endif]--><a href="{{app_url}}/organizations/{{model.organization_name}}" target="_blank" style="text-decoration:none;display:inline-block;color:#ffffff;background-color:#d54560;border-radius:60px;width:auto;border-top:1px solid #d54560;border-right:1px solid #d54560;border-bottom:1px solid #d54560;border-left:1px solid #d54560;padding-top:15px;padding-bottom:15px;font-family:Montserrat, Trebuchet MS, Lucida Grande, Lucida Sans Unicode, Lucida Sans, Tahoma, sans-serif;text-align:center;mso-border-alt:none;word-break:keep-all;"><span style="padding-left:30px;padding-right:30px;font-size:14px;display:inline-block;letter-spacing:normal;"><span style="font-size: 12px; line-height: 2; word-break: break-word; mso-line-height-alt: 24px;"><span style="font-size: 14px; line-height: 28px;" data-mce-style="font-size: 14px; line-height: 28px;">My Organization</span></span></span></a>
<!--[if mso]></center></v:textbox></v:roundrect><![endif]-->
</div>
</td>

12
src/EventHub.Domain/Emailing/Templates/PaymentRequestFailed.tpl

@ -76,7 +76,7 @@
<table class="image_block" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td style="padding-left:40px;padding-right:40px;width:100%;">
<div align="center" style="line-height:10px"><a href="{{app_url}}" target="_blank" style="outline:none" tabindex="-1"><img src="{{{{app_url}}}}//assets/eventhub-logo.svg" style="display: block; height: auto; border: 0; width: 352px; max-width: 100%;" width="352" alt="EventHub" title="EventHub"></a></div>
<div align="center" style="line-height:10px"><a href="{{app_url}}" target="_blank" style="outline:none" tabindex="-1"><img src="{{app_url}}//assets/eventhub-logo.svg" style="display: block; height: auto; border: 0; width: 352px; max-width: 100%;" width="352" alt="EventHub" title="EventHub"></a></div>
</td>
</tr>
</table>
@ -109,7 +109,7 @@
<td style="padding-bottom:25px;padding-left:40px;padding-right:40px;padding-top:10px;">
<div style="font-family: sans-serif">
<div style="font-size: 12px; font-family: Montserrat, Trebuchet MS, Lucida Grande, Lucida Sans Unicode, Lucida Sans, Tahoma, sans-serif; color: #555555; line-height: 1.5;">
<p style="margin: 0; font-size: 14px; text-align: center;">We found that your {{model.licenseType}} payment transaction has not been completed.&nbsp;We want to help you take the next step and start your {{model.licenseType}} now.</p>
<p style="margin: 0; font-size: 14px; text-align: center;">We found that your {{model.license_type}} payment transaction has not been completed.&nbsp;We want to help you take the next step and start your {{model.license_type}} now.</p>
</div>
</div>
</td>
@ -138,7 +138,7 @@
</td>
</tr>
</table>
<table class="text_block" width="100%" border="0" cellpadding="30" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word;">
<table class="text_block" width="100%" border="0" cellpadding="20" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word;">
<tr>
<td>
<div style="font-family: sans-serif">
@ -149,12 +149,12 @@
</td>
</tr>
</table>
<table class="text_block" width="100%" border="0" cellpadding="45" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word;">
<table class="text_block" width="100%" border="0" cellpadding="15" cellspacing="15" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word;">
<tr>
<td>
<div style="font-family: sans-serif">
<div style="font-size: 12px; color: #555555; line-height: 1.2; font-family: Montserrat, Trebuchet MS, Lucida Grande, Lucida Sans Unicode, Lucida Sans, Tahoma, sans-serif;">
<p style="margin: 0; font-size: 12px; text-align: center;"><span style="font-size:14px;">{{model.failReason}}</span></p>
<div style="font-size: 12px; color: #555555; line-height: 0.5; font-family: Montserrat, Trebuchet MS, Lucida Grande, Lucida Sans Unicode, Lucida Sans, Tahoma, sans-serif;">
<p style="margin: 1; font-size: 12px; text-align: center;"><span style="font-size:14px;">{{model.fail_reason}}</span></p>
</div>
</div>
</td>

64
src/EventHub.Domain/PaymentRequests/PaymentRequestEventHandler.cs → src/EventHub.Domain/Organizations/PaymentRequests/PaymentRequestEventHandler.cs

@ -1,11 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EventHub.Emailing;
using EventHub.Organizations;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Payment.PaymentRequests;
using Volo.Abp.DependencyInjection;
@ -15,7 +11,7 @@ using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Identity;
using Volo.Abp.TextTemplating;
namespace EventHub.PaymentRequests
namespace EventHub.Organizations.PaymentRequests
{
public class PaymentRequestEventHandler : IDistributedEventHandler<PaymentRequestCompletedEto>,
IDistributedEventHandler<PaymentRequestFailedEto>, ITransientDependency
@ -25,7 +21,6 @@ namespace EventHub.PaymentRequests
private readonly IRepository<IdentityUser, Guid> _userRepository;
private readonly IRepository<Organization, Guid> _organizationRepository;
private readonly IRepository<PaymentRequest, Guid> _paymentRequestRepository;
private readonly IOptionsSnapshot<List<OrganizationPlanInfoOptions>> _organizationPlanInfoOptionsSnapshot;
private readonly ILogger<PaymentRequestEventHandler> _logger;
public PaymentRequestEventHandler(
@ -34,7 +29,6 @@ namespace EventHub.PaymentRequests
IRepository<IdentityUser, Guid> userRepository,
IRepository<Organization, Guid> organizationRepository,
IRepository<PaymentRequest, Guid> paymentRequestRepository,
IOptionsSnapshot<List<OrganizationPlanInfoOptions>> organizationPlanInfoOptionsSnapshot,
ILogger<PaymentRequestEventHandler> logger)
{
_emailSender = emailSender;
@ -42,20 +36,19 @@ namespace EventHub.PaymentRequests
_userRepository = userRepository;
_organizationRepository = organizationRepository;
_paymentRequestRepository = paymentRequestRepository;
_organizationPlanInfoOptionsSnapshot = organizationPlanInfoOptionsSnapshot;
_logger = logger;
}
public async Task HandleEventAsync(PaymentRequestCompletedEto eventData)
{
var isExistExtraProperties = eventData.ExtraProperties.TryGetValue(nameof(PaymentRequestProductExtraParameterConfiguration), out var ExtraProperties);
var isExistExtraProperties = eventData.ExtraProperties.TryGetValue(nameof(OrganizationPaymentRequestExtraParameterConfiguration), out var ExtraProperties);
if (!isExistExtraProperties || ExtraProperties is null)
{
_logger.LogCritical($"{eventData.PaymentRequestId} PaymentRequestId although payment was received, organization could not be upgrade because ExtraProperties do not exist!");
return;
}
var paymentRequestProductExtraParameter = JsonConvert.DeserializeObject<PaymentRequestProductExtraParameterConfiguration>(ExtraProperties.ToString());
var paymentRequestProductExtraParameter = JsonConvert.DeserializeObject<OrganizationPaymentRequestExtraParameterConfiguration>(ExtraProperties.ToString());
if (paymentRequestProductExtraParameter is null)
{
_logger.LogCritical($"{eventData.PaymentRequestId} PaymentRequestId although payment was received, organization could not be upgrade because the ExtraProperties cannot be deserialized!");
@ -70,33 +63,23 @@ namespace EventHub.PaymentRequests
_logger.LogCritical($"{eventData.PaymentRequestId} PaymentRequestId although payment was received, mail could not be sent because the user(UserId:{payment.CustomerId}) could not be found!");
return;
}
var organization = await _organizationRepository.FindAsync(o => o.Name == paymentRequestProductExtraParameter.OrganizationName);
if (organization is null)
{
_logger.LogCritical($"{eventData.PaymentRequestId} PaymentRequestId although payment was received, organization could not be upgrade because the organization(OrganizationId:{paymentRequestProductExtraParameter.OrganizationName}) could not be found!");
return;
}
var targetPlan = _organizationPlanInfoOptionsSnapshot.Value.First(x => x.PlanType == paymentRequestProductExtraParameter.TargetPlanType);
if (paymentRequestProductExtraParameter.IsExtend)
{
organization.UpgradeToPremium(organization.PremiumEndDate!.Value.AddMonths(targetPlan.OnePremiumPeriodAsMonth));
}
else
var updatedOrganization = await UpgradeOrganizationAsync(paymentRequestProductExtraParameter);
if (updatedOrganization is null)
{
organization.UpgradeToPremium(DateTime.Now.AddMonths(targetPlan.OnePremiumPeriodAsMonth));
_logger.LogCritical($"{eventData.PaymentRequestId} PaymentRequestId although payment was received, organization could not be upgrade! OrganizationName: {paymentRequestProductExtraParameter.OrganizationName}");
return;
}
await _organizationRepository.UpdateAsync(organization, true);
var templateModel = new
{
OrganizationName = paymentRequestProductExtraParameter.OrganizationName,
PlanInfo = paymentRequestProductExtraParameter.IsExtend ? "extended" : "upgraded",
Price = payment.Price,
Currency = payment.Currency
};
await _emailSender.QueueAsync(
await _emailSender.SendAsync(
user.Email,
"EventHub: Thank You",
await _templateRenderer.RenderAsync(EmailTemplates.PaymentRequestCompleted, templateModel)
@ -105,14 +88,14 @@ namespace EventHub.PaymentRequests
public async Task HandleEventAsync(PaymentRequestFailedEto eventData)
{
var isExistExtraProperties = eventData.ExtraProperties.TryGetValue(nameof(PaymentRequestProductExtraParameterConfiguration), out var ExtraProperties);
var isExistExtraProperties = eventData.ExtraProperties.TryGetValue(nameof(OrganizationPaymentRequestExtraParameterConfiguration), out var ExtraProperties);
if (!isExistExtraProperties || ExtraProperties is null)
{
_logger.LogCritical($"{eventData.PaymentRequestId} PaymentRequestId although payment was received, mail could not be sent because ExtraProperties do not exist!");
return;
}
var paymentRequestProductExtraParameter = JsonConvert.DeserializeObject<PaymentRequestProductExtraParameterConfiguration>(ExtraProperties.ToString());
var paymentRequestProductExtraParameter = JsonConvert.DeserializeObject<OrganizationPaymentRequestExtraParameterConfiguration>(ExtraProperties.ToString());
if (paymentRequestProductExtraParameter is null)
{
_logger.LogCritical($"{eventData.PaymentRequestId} PaymentRequestId although payment was received, mail could not be sent because the ExtraProperties cannot be deserialized!");
@ -130,15 +113,36 @@ namespace EventHub.PaymentRequests
var templateModel = new
{
OrganizationName = paymentRequestProductExtraParameter.OrganizationName,
LicenseType = paymentRequestProductExtraParameter.TargetPlanType,
LicenseType = OrganizationPlanType.Premium,
FailReason = eventData.FailReason
};
await _emailSender.QueueAsync(
user.Email,
"EventHub: Payment Failed",
await _templateRenderer.RenderAsync(EmailTemplates.PaymentRequestCompleted, templateModel)
await _templateRenderer.RenderAsync(EmailTemplates.PaymentRequestFailed, templateModel)
);
}
private async Task<Organization> UpgradeOrganizationAsync(OrganizationPaymentRequestExtraParameterConfiguration organizationPaymentRequestExtraParameter)
{
var organization = await _organizationRepository.FindAsync(o => o.Name == organizationPaymentRequestExtraParameter.OrganizationName);
if (organization is null)
{
return null;
}
if (organizationPaymentRequestExtraParameter.IsExtend)
{
organization.UpgradeToPremium(
organization.PremiumEndDate!.Value.AddMonths(organizationPaymentRequestExtraParameter.PremiumPeriodAsMonth));
}
else
{
organization.UpgradeToPremium(DateTime.Now.AddMonths(organizationPaymentRequestExtraParameter.PremiumPeriodAsMonth));
}
return await _organizationRepository.UpdateAsync(organization, true);
}
}
}

18
src/EventHub.Web/EventHubWebModule.cs

@ -10,6 +10,7 @@ using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using EventHub.Localization;
using EventHub.Web.Menus;
using EventHub.Web.PaymentRequests;
using EventHub.Web.Theme;
using EventHub.Web.Theme.Bundling;
using EventHub.Web.Utils;
@ -86,6 +87,23 @@ namespace EventHub.Web
ConfigureCookies(context);
ConfigureSwaggerServices(context.Services);
ConfigureRazorPageOptions();
ConfigurePremiumPlanInfo(context, configuration);
}
private void ConfigurePremiumPlanInfo(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddOptions<PremiumPlanInfoOptions>()
.Bind(configuration.GetSection(PremiumPlanInfoOptions.OrganizationPlanInfo))
.ValidateDataAnnotations()
.Validate(config =>
{
if (config.IsActive)
{
return config.OnePremiumPeriodAsMonth > config.CanBeExtendedAfterHowManyMonths;
}
return true;
}, "OnePremiumPeriodAsMonth must be greater than CanBeExtendedAfterHowManyMonths.");
}
private void ConfigureBundles()

46
src/EventHub.Web/Pages/Organizations/Profile.cshtml

@ -5,12 +5,14 @@
@using EventHub.Web.Pages.Events.Components.EventsArea
@using EventHub.Web.Pages.Organizations.Components.JoinArea
@using EventHub.Web.Pages.Organizations.Components.MembersArea
@using EventHub.Web.PaymentRequests
@using Microsoft.AspNetCore.Mvc.Localization
@using Microsoft.Extensions.Options
@using Volo.Abp.Timing
@model EventHub.Web.Pages.Organizations.ProfilePageModel
@inject IClock Clock
@inject IOptions<EventHubUrlOptions> UrlOptions
@inject IOptionsSnapshot<PremiumPlanInfoOptions> PremiumPlanInfoOptionsSnapshot
@section scripts {
<abp-script src="/Pages/Organizations/Profile.js"/>
@ -68,26 +70,36 @@
<i class="far fa-edit"></i> Edit
</a>
<abp-card class="detail-head mt-4 font-weight-bold text-center">
<abp-card-body>
@if (Model.Organization.IsPremium)
{
<span>Premium end date: @Model.Organization.PremiumEndDate!.Value.ToShortDateString()</span>
@if (Model.Organization.PremiumEndDate!.Value.IsBetween(DateTime.Now, DateTime.Now.AddMonths(12 - 1)))
@if (PremiumPlanInfoOptionsSnapshot.Value.IsActive)
{
<abp-card class="detail-head mt-4 font-weight-bold text-center">
<abp-card-body>
@if (Model.Organization.IsPremium)
{
<span>Premium end date: @Model.Organization.PremiumEndDate!.Value.ToShortDateString()</span>
@if (PremiumPlanInfoOptionsSnapshot.Value.IsExtendable && Model.Organization.PremiumEndDate!.Value.IsBetween(DateTime.Now, DateTime.Now.AddMonths(12 - 1)))
{
<a href="@Url.Page("/Pricing", new { organizationName = Model.Organization.Name })" class="btn btn-primary btn-block mt-3">
<i class="fas fa-handshake"></i> @L["ExtendNow"].Value
</a>
}
}
else
{
<a href="@Url.Page("/Pricing", new { organizationName = Model.Organization.Name })" class="btn btn-primary btn-block mt-3">
<i class="fas fa-handshake"></i> @L["ExtendNow"].Value
<a href="@Url.Page("/Pricing", new { organizationName = Model.Organization.Name })" class="btn btn-primary btn-block">
<i class="fas fa-handshake"></i> @L["UpgradeToPremium"].Value
</a>
}
}
else
{
<a href="@Url.Page("/Pricing", new { organizationName = Model.Organization.Name })" class="btn btn-primary btn-block">
<i class="fas fa-handshake"></i> @L["UpgradeToPremium"].Value
</a>
}
</abp-card-body>
</abp-card>
</abp-card-body>
</abp-card>
}else if (Model.Organization.IsPremium)
{
<abp-card class="detail-head mt-4 font-weight-bold text-center">
<abp-card-body>
<span>Premium end date: @Model.Organization.PremiumEndDate!.Value.ToShortDateString()</span>
</abp-card-body>
</abp-card>
}
}
@await Component.InvokeAsync(typeof(JoinAreaViewComponent), new {organizationId = Model.Organization.Id})
@await Component.InvokeAsync(typeof(MembersAreaViewComponent),

8
src/EventHub.Web/Pages/Payment/PostCheckout.cshtml

@ -15,18 +15,18 @@
@{
PageLayout.Content.Title = L["PaymentResult"] + L["Enum:PaymentRequestState:" + Model.PaymentRequest.State];
var isExistExtraProperties = Model.PaymentRequest.ExtraProperties.TryGetValue(nameof(PaymentRequestProductExtraParameterConfiguration), out var ExtraProperties);
var isExistExtraProperties = Model.PaymentRequest.ExtraProperties.TryGetValue(nameof(OrganizationPaymentRequestExtraParameterConfiguration), out var ExtraProperties);
if (!isExistExtraProperties)
{
throw new BadHttpRequestException("");
}
var paymentRequestProductExtraParameterConfiguration = JsonConvert.DeserializeObject<PaymentRequestProductExtraParameterConfiguration>(ExtraProperties.ToString());
if (paymentRequestProductExtraParameterConfiguration is null)
var organizationPaymentExtraParameterConfiguration = JsonConvert.DeserializeObject<OrganizationPaymentRequestExtraParameterConfiguration>(ExtraProperties.ToString());
if (organizationPaymentExtraParameterConfiguration is null)
{
throw new BadHttpRequestException("");
}
var organizationName = paymentRequestProductExtraParameterConfiguration.OrganizationName;
var organizationName = organizationPaymentExtraParameterConfiguration.OrganizationName;
}
<main class="static-page container mt-5 text-center">

18
src/EventHub.Web/Pages/Payment/PreCheckout.cshtml

@ -1,5 +1,6 @@
@page
@using EventHub.Organizations
@using EventHub.Web.PaymentRequests
@using Microsoft.AspNetCore.Http
@using Microsoft.Extensions.Options
@using Newtonsoft.Json
@ -7,23 +8,22 @@
@using Payment.PaymentRequests
@inherits Payment.Web.Pages.PaymentPageBase
@model Payment.Web.Pages.Payment.PreCheckoutPageModel
@inject IOptionsSnapshot<List<OrganizationPlanInfoOptions>> OrganizationPlanInfoOptionsSnapshot
@inject IOptionsSnapshot<PremiumPlanInfoOptions> PremiumPlanInfoOptionsSnapshot
@{
var isExistExtraProperties = Model.PaymentRequest.ExtraProperties.TryGetValue(nameof(PaymentRequestProductExtraParameterConfiguration), out var ExtraProperties);
var isExistExtraProperties = Model.PaymentRequest.ExtraProperties.TryGetValue(nameof(OrganizationPaymentRequestExtraParameterConfiguration), out var ExtraProperties);
if (!isExistExtraProperties)
{
throw new BadHttpRequestException("");
}
var paymentRequestProductExtraParameterConfiguration = JsonConvert.DeserializeObject<PaymentRequestProductExtraParameterConfiguration>(ExtraProperties.ToString());
if (paymentRequestProductExtraParameterConfiguration is null)
var organizationPaymentRequestExtraParameterConfiguration = JsonConvert.DeserializeObject<OrganizationPaymentRequestExtraParameterConfiguration>(ExtraProperties.ToString());
if (organizationPaymentRequestExtraParameterConfiguration is null)
{
throw new BadHttpRequestException("");
}
var organizationName = paymentRequestProductExtraParameterConfiguration.OrganizationName;
var targetPlanType = paymentRequestProductExtraParameterConfiguration.TargetPlanType;
var planInfo = OrganizationPlanInfoOptionsSnapshot.Value.First(o => o.PlanType == targetPlanType);
var organizationName = organizationPaymentRequestExtraParameterConfiguration.OrganizationName;
var planInfo = PremiumPlanInfoOptionsSnapshot.Value;
}
@section scripts {
@ -53,10 +53,10 @@
<div class="media-body">
<p class="font-weight-bolder">@Model.PaymentRequest.ProductName</p>
<small>
@if (!paymentRequestProductExtraParameterConfiguration.IsExtend)
@if (!organizationPaymentRequestExtraParameterConfiguration.IsExtend)
{
<span class="text-muted">Operation: Upgrade</span>
<span class="text-muted">Target Plan: @targetPlanType</span>
<span class="text-muted">Target Plan: @OrganizationPlanType.Premium</span>
<span class="text-muted ml-1">End date: @DateTime.Now.AddMonths(planInfo.OnePremiumPeriodAsMonth).ToShortDateString()</span>
}
else

17
src/EventHub.Web/Pages/Pricing.cshtml

@ -1,6 +1,5 @@
@page
@using EventHub.Localization
@using EventHub.Organizations
@using Microsoft.AspNetCore.Mvc.Localization
@model EventHub.Web.Pages.Pricing
@inject IHtmlLocalizer<EventHubResource> L
@ -23,12 +22,11 @@
<div class="d-flex col-md flex-column py-4">
<div class="display-1 text-primary my-4"><i class="lnr lnr-briefcase text-big"></i></div>
<h5 class="m-0">Normal</h5>
<h5 class="m-0 ml-2">Normal</h5>
<div class="flex-grow-1">
<div class="py-4 my-2">
<span class="d-inline-block text-muted text-big align-middle mr-2">$</span>
<span class="display-3 d-inline-block font-weight-bold align-middle">0</span>
<span class="d-inline-block text-muted text-big align-middle ml-2">/ year</span>
</div>
<div class="pb-5">
<p class="ui-company-text mb-2">1 Organization</p>
@ -41,12 +39,12 @@
</div>
</div>
@foreach (var plan in Model.OrganizationPlanInfos.Where(x => x.IsActive))
@if(Model.PremiumPlanInfo is not null)
{
<div class="d-flex col-md flex-column py-4">
<div class="display-1 text-primary my-4"><i class="lnr lnr-store text-big"></i></div>
<h5 class="m-0">
@plan.PlanType
@Model.PremiumPlanInfo.PlanType
@if (Model.Organization.IsPremium)
{
<span class="align-top text-dark font-weight-bold" style="font-size: x-small" abp-badge-pill="Info"> Current Plan </span>
@ -55,8 +53,7 @@
<div class="flex-grow-1">
<div class="py-4 my-2">
<span class="d-inline-block text-muted text-big align-middle mr-2">$</span>
<span class="display-3 d-inline-block font-weight-bold align-middle">@plan.Price</span>
<span class="d-inline-block text-muted text-big align-middle ml-2">/ year</span>
<span class="display-3 d-inline-block font-weight-bold align-middle">@Model.PremiumPlanInfo.Price</span>
</div>
<div class="pb-5">
<p class="ui-company-text mb-2">1 Organization</p>
@ -69,7 +66,7 @@
@if (Model.Organization.IsPremium)
{
<form id="Extend" method="post" asp-page-handler="Extend">
<abp-input asp-for="TargetPlanToUpgrade" value="@plan.PlanType"></abp-input>
<abp-input asp-for="TargetPlanToUpgrade" value="@Model.PremiumPlanInfo.PlanType"></abp-input>
<abp-input asp-for="OrganizationName"></abp-input>
<a onclick="document.getElementById('Extend').submit()" class="btn btn-primary btn-lg rounded-pill">Extend Now</a>
</form>
@ -77,9 +74,9 @@
else
{
<form id="Upgrade" method="post" asp-page-handler="Upgrade">
<abp-input asp-for="TargetPlanToUpgrade" value="@plan.PlanType"></abp-input>
<abp-input asp-for="TargetPlanToUpgrade" value="@Model.PremiumPlanInfo.PlanType"></abp-input>
<abp-input asp-for="OrganizationName"></abp-input>
<a onclick="document.getElementById('Upgrade').submit()" class="btn btn-primary btn-lg rounded-pill">Upgrade To @plan.PlanType</a>
<a onclick="document.getElementById('Upgrade').submit()" class="btn btn-primary btn-lg rounded-pill">Upgrade To @Model.PremiumPlanInfo.PlanType</a>
</form>
}
</div>

38
src/EventHub.Web/Pages/Pricing.cshtml.cs

@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EventHub.Organizations;
using EventHub.Web.PaymentRequests;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Payment.PaymentRequests;
@ -21,30 +21,34 @@ namespace EventHub.Web.Pages
[BindProperty]
public OrganizationPlanType TargetPlanToUpgrade { get; set; }
public List<OrganizationPlanInfoOptions> OrganizationPlanInfos { get; private set; }
[CanBeNull]
public PremiumPlanInfoOptions PremiumPlanInfo { get; private set; }
public OrganizationProfileDto Organization { get; private set; }
private readonly IOrganizationAppService _organizationAppService;
private readonly IPaymentRequestAppService _paymentRequestAppService;
private readonly IPaymentUrlBuilder _paymentUrlBuilder;
private readonly IOptionsSnapshot<List<OrganizationPlanInfoOptions>> _organizationPlanInfoOptionsSnapshot;
private readonly IOptionsSnapshot<PremiumPlanInfoOptions> _premiumPlanInfoOptionsSnapshot;
public Pricing(
IOrganizationAppService organizationAppService,
IPaymentRequestAppService paymentRequestAppService,
IPaymentUrlBuilder paymentUrlBuilder,
IOptionsSnapshot<List<OrganizationPlanInfoOptions>> organizationPlanInfoOptionsSnapshot)
IOptionsSnapshot<PremiumPlanInfoOptions> premiumPlanInfoOptionsSnapshot)
{
_organizationAppService = organizationAppService;
_paymentRequestAppService = paymentRequestAppService;
_paymentUrlBuilder = paymentUrlBuilder;
_organizationPlanInfoOptionsSnapshot = organizationPlanInfoOptionsSnapshot;
_premiumPlanInfoOptionsSnapshot = premiumPlanInfoOptionsSnapshot;
}
public async Task OnGetAsync()
{
OrganizationPlanInfos = _organizationPlanInfoOptionsSnapshot.Value;
if (_premiumPlanInfoOptionsSnapshot.Value.IsActive)
{
PremiumPlanInfo = _premiumPlanInfoOptionsSnapshot.Value;
}
Organization = await _organizationAppService.GetProfileAsync(OrganizationName);
@ -58,8 +62,8 @@ namespace EventHub.Web.Pages
{
var organization = await GetOrganizationProfileAsync();
var plan = _organizationPlanInfoOptionsSnapshot.Value.FirstOrDefault(x => x.PlanType == TargetPlanToUpgrade && x.IsActive);
if (plan is null)
var plan = _premiumPlanInfoOptionsSnapshot.Value;
if (plan is null || !plan.IsActive)
{
Alerts.Danger("Premium plan is currently inactive!");
return Page();
@ -74,8 +78,8 @@ namespace EventHub.Web.Pages
{
var organization = await GetOrganizationProfileAsync();
var plan = _organizationPlanInfoOptionsSnapshot.Value.FirstOrDefault(x => x.PlanType == TargetPlanToUpgrade && x.IsActive);
if (plan is null)
var plan = _premiumPlanInfoOptionsSnapshot.Value;
if (plan is null || !plan.IsActive)
{
Alerts.Danger("Premium plan is currently inactive!");
return Page();
@ -98,7 +102,7 @@ namespace EventHub.Web.Pages
}
private async Task<PaymentRequestDto> CreatePaymentRequestAsync(
OrganizationPlanInfoOptions plan,
PremiumPlanInfoOptions plan,
OrganizationProfileDto organization,
bool isExtend = false)
{
@ -106,16 +110,16 @@ namespace EventHub.Web.Pages
{
CustomerId = CurrentUser.GetId().ToString(),
Price = plan.Price,
ProductId = OrganizationPlanInfoOptions.GetProductId(plan, isExtend),
ProductName = OrganizationPlanInfoOptions.GetProductName(plan, organization.Name, isExtend),
ProductId = PremiumPlanInfoOptions.GetProductId(plan, isExtend),
ProductName = PremiumPlanInfoOptions.GetProductName(plan, organization.Name, isExtend),
ExtraProperties =
{
{
nameof(PaymentRequestProductExtraParameterConfiguration),
new PaymentRequestProductExtraParameterConfiguration
nameof(OrganizationPaymentRequestExtraParameterConfiguration),
new OrganizationPaymentRequestExtraParameterConfiguration
{
OrganizationName = OrganizationName,
TargetPlanType = TargetPlanToUpgrade,
PremiumPeriodAsMonth = plan.OnePremiumPeriodAsMonth,
IsExtend = isExtend
}
}

34
src/EventHub.Domain.Shared/Organizations/OrganizationPlanInfoOptions.cs → src/EventHub.Web/PaymentRequests/PremiumPlanInfoOptions.cs

@ -1,44 +1,44 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Text;
using EventHub.Organizations;
using Volo.Abp;
namespace EventHub.Organizations
namespace EventHub.Web.PaymentRequests
{
public class OrganizationPlanInfoOptions
public class PremiumPlanInfoOptions
{
private const string ProductNamePrefix = "EventHub";
public const string OrganizationPlanInfo = "Organization:PlanInfo";
public const string OrganizationPlanInfo = "PlanInfo:Premium";
public OrganizationPlanType PlanType { get; set; }
public OrganizationPlanType PlanType { get; private set; } = OrganizationPlanType.Premium;
[DefaultValue(true)]
[DefaultValue(false)]
public bool IsActive { get; set; }
[Range(1.0, 100.0, ErrorMessage = "Value for {0} must be between {1} and {2}.")]
public decimal Price { get; set; } = 1.0M;
[DefaultValue(true)]
[DefaultValue(false)]
public bool IsExtendable { get; set; }
[Range(1, 12, ErrorMessage = "Value for {0} must be between {1} and {2}.")]
public int CanBeExtendedAfterHowManyMonths { get; set; }
[Range(0, 12, ErrorMessage = "Value for {0} must be between {1} and {2}.")]
public int CanBeExtendedAfterHowManyMonths { get; set; }
[Range(1, 24, ErrorMessage = "Value for {0} must be between {1} and {2}.")]
public int OnePremiumPeriodAsMonth { get; set; }
public int OnePremiumPeriodAsMonth { get; set; } = 12;
public static string GetProductId(OrganizationPlanInfoOptions organizationPlan, bool isExtend)
public static string GetProductId(PremiumPlanInfoOptions premiumPlan, bool isExtend)
{
var productIdStringBuilder = new StringBuilder(ProductNamePrefix);
productIdStringBuilder.Append("-");
productIdStringBuilder.Append(organizationPlan.PlanType.ToString());
productIdStringBuilder.Append(premiumPlan.PlanType.ToString());
productIdStringBuilder.Append("-");
if (isExtend)
{
if (!organizationPlan.IsExtendable)
if (!premiumPlan.IsExtendable)
{
throw new BusinessException();
}
@ -54,16 +54,16 @@ namespace EventHub.Organizations
return productIdStringBuilder.ToString();
}
public static string GetProductName(OrganizationPlanInfoOptions organizationPlan, string organizationName, bool isExtend)
public static string GetProductName(PremiumPlanInfoOptions premiumPlan, string organizationName, bool isExtend)
{
var productNameStringBuilder = new StringBuilder(ProductNamePrefix);
productNameStringBuilder.Append(" ");
productNameStringBuilder.Append(organizationPlan.PlanType.ToString());
productNameStringBuilder.Append(premiumPlan.PlanType.ToString());
productNameStringBuilder.Append(" ");
if (isExtend)
{
if (!organizationPlan.IsExtendable)
if (!premiumPlan.IsExtendable)
{
throw new BusinessException();
}

9
src/EventHub.Web/appsettings.json

@ -7,5 +7,14 @@
"RequireHttpsMetadata": "true",
"ClientId": "EventHub_Web",
"ClientSecret": "1q2w3e*"
},
"PlanInfo": {
"Premium": {
"IsActive": true,
"Price": 29.90,
"IsExtendable": true,
"CanBeExtendedAfterHowManyMonths": 9,
"OnePremiumPeriodAsMonth": 12
}
}
}

Loading…
Cancel
Save