Browse Source

feat: Add Upgrade to Premium organization

pull/69/head
Berkan Sasmaz 5 years ago
parent
commit
b467783807
  1. 10
      modules/payment/src/Payment.Application.Contracts/PaymentRequests/PaymentRequestDto.cs
  2. 6
      modules/payment/src/Payment.Application/PaymentRequests/PaymentRequestAppService.cs
  3. 60
      src/EventHub.Domain.Shared/Organizations/OrganizationPlanInfoOptions.cs
  4. 9
      src/EventHub.Domain.Shared/Organizations/PaymentRequestProductExtraParameterConfiguration.cs
  5. 18
      src/EventHub.Domain/Emailing/EmailTemplateDefinitionProvider.cs
  6. 4
      src/EventHub.Domain/Emailing/EmailTemplates.cs
  7. 357
      src/EventHub.Domain/Emailing/Templates/PaymentRequestCompleted.tpl
  8. 245
      src/EventHub.Domain/Emailing/Templates/PaymentRequestFailed.tpl
  9. 4
      src/EventHub.Domain/Organizations/Organization.cs
  10. 137
      src/EventHub.Domain/PaymentRequest/PaymentRequestEventHandler.cs
  11. 2
      src/EventHub.Web/EventHubWebModule.cs
  12. 76
      src/EventHub.Web/Pages/Payment/PostCheckout.cshtml
  13. 103
      src/EventHub.Web/Pages/Payment/PreCheckout.cshtml
  14. 15
      src/EventHub.Web/Pages/Pricing.cshtml
  15. 63
      src/EventHub.Web/Pages/Pricing.cshtml.cs

10
modules/payment/src/Payment.Application.Contracts/PaymentRequests/PaymentRequestDto.cs

@ -1,10 +1,11 @@
using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Data;
namespace Payment.PaymentRequests
{
[Serializable]
public class PaymentRequestDto : CreationAuditedEntityDto<Guid>
public class PaymentRequestDto : CreationAuditedEntityDto<Guid>, IHasExtraProperties
{
public string CustomerId { get; set; }
@ -17,5 +18,12 @@ namespace Payment.PaymentRequests
public string Currency { get; set; }
public PaymentRequestState State { get; set; }
public ExtraPropertyDictionary ExtraProperties { get; set; }
public PaymentRequestDto()
{
ExtraProperties = new ExtraPropertyDictionary();
}
}
}

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

@ -7,6 +7,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Data;
namespace Payment.PaymentRequests
{
@ -43,6 +44,11 @@ namespace Payment.PaymentRequests
input.Price,
input.Currency ?? _paymentOptions.DefaultCurrency
);
foreach (var extraProperty in input.ExtraProperties)
{
paymentRequest.SetProperty(extraProperty.Key, extraProperty.Value);
}
await _paymentRequestRepository.InsertAsync(paymentRequest);

60
src/EventHub.Domain.Shared/Organizations/OrganizationPlanInfoOptions.cs

@ -1,10 +1,15 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Text;
using Volo.Abp;
namespace EventHub.Organizations
{
public class OrganizationPlanInfoOptions
{
private const string ProductNamePrefix = "EventHub";
public const string OrganizationPlanInfo = "Organization:PlanInfo";
public OrganizationPlanType PlanType { get; set; }
@ -13,7 +18,7 @@ namespace EventHub.Organizations
public bool IsActive { get; set; }
[Range(1.0, 100.0, ErrorMessage = "Value for {0} must be between {1} and {2}.")]
public float Price { get; set; } = 1.0f;
public decimal Price { get; set; } = 1.0M;
[DefaultValue(true)]
public bool IsExtendable { get; set; }
@ -23,5 +28,58 @@ namespace EventHub.Organizations
[Range(1, 24, ErrorMessage = "Value for {0} must be between {1} and {2}.")]
public int OnePremiumPeriodAsMonth { get; set; }
public static string GetProductId(OrganizationPlanInfoOptions organizationPlan, bool isExtend)
{
var productIdStringBuilder = new StringBuilder(ProductNamePrefix);
productIdStringBuilder.Append("-");
productIdStringBuilder.Append(organizationPlan.PlanType.ToString());
productIdStringBuilder.Append("-");
if (isExtend)
{
if (!organizationPlan.IsExtendable)
{
throw new BusinessException();
}
productIdStringBuilder.Append("Extend");
}
else
{
productIdStringBuilder.Append("Upgrade");
}
return productIdStringBuilder.ToString();
}
public static string GetProductName(OrganizationPlanInfoOptions organizationPlan, string organizationName, bool isExtend)
{
var productNameStringBuilder = new StringBuilder(ProductNamePrefix);
productNameStringBuilder.Append(" ");
productNameStringBuilder.Append(organizationPlan.PlanType.ToString());
productNameStringBuilder.Append(" ");
if (isExtend)
{
if (!organizationPlan.IsExtendable)
{
throw new BusinessException();
}
productNameStringBuilder.Append("Extend");
}
else
{
productNameStringBuilder.Append("Upgrade");
}
productNameStringBuilder.Append(" | ");
productNameStringBuilder.Append(organizationName);
return productNameStringBuilder.ToString();
}
}
}

9
src/EventHub.Domain.Shared/Organizations/PaymentRequestProductExtraParameterConfiguration.cs

@ -0,0 +1,9 @@
namespace EventHub.Organizations
{
public class PaymentRequestProductExtraParameterConfiguration
{
public string OrganizationName { get; set; }
public OrganizationPlanType TargetPlanType { get; set; }
}
}

18
src/EventHub.Domain/Emailing/EmailTemplateDefinitionProvider.cs

@ -55,6 +55,24 @@ namespace EventHub.Emailing
isInlineLocalized: true
)
);
context.Add(
new TemplateDefinition(
EmailTemplates.PaymentRequestCompleted)
.WithVirtualFilePath(
"/Emailing/Templates/PaymentRequestCompleted.tpl",
isInlineLocalized: true
)
);
context.Add(
new TemplateDefinition(
EmailTemplates.PaymentRequestFailed)
.WithVirtualFilePath(
"/Emailing/Templates/PaymentRequestFailed.tpl",
isInlineLocalized: true
)
);
}
}
}

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

@ -11,5 +11,9 @@ namespace EventHub.Emailing
public const string NewEventCreated = "EventHub.Event.NewEventCreated";
public const string EventTimingChanged = "EventHub.Event.EventTimingChanged";
public const string PaymentRequestCompleted = "EventHub.Event.PaymentRequestCompleted";
public const string PaymentRequestFailed = "EventHub.Event.PaymentRequestFailed";
}
}

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

@ -0,0 +1,357 @@
<!DOCTYPE html>
<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" lang="en">
<head>
<title></title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!--[if mso]><xml><o:OfficeDocumentSettings><o:PixelsPerInch>96</o:PixelsPerInch><o:AllowPNG/></o:OfficeDocumentSettings></xml><![endif]-->
<style>
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
}
th.column {
padding: 0
}
a[x-apple-data-detectors] {
color: inherit !important;
text-decoration: inherit !important;
}
#MessageViewBody a {
color: inherit;
text-decoration: none;
}
p {
line-height: inherit
}
@media (max-width:660px) {
.row-content {
width: 100% !important;
}
.stack .column {
width: 100%;
display: block;
}
}
</style>
</head>
<body style="background-color: #f8f8f9; margin: 0; padding: 0; -webkit-text-size-adjust: none; text-size-adjust: none;">
<table class="nl-container" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #f8f8f9;">
<tbody>
<tr>
<td>
<table class="row row-1" align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tbody>
<tr>
<td>
<table class="row-content stack" align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #f8f8f9;" width="640">
<tbody>
<tr>
<th class="column" width="100%" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; font-weight: 400; text-align: left; vertical-align: top; padding-top: 5px; padding-bottom: 5px; border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px;">
<table class="empty_block" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td>
<div></div>
</td>
</tr>
</table>
</th>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<table class="row row-2" align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tbody>
<tr>
<td>
<table class="row-content stack" align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #fff;" width="640">
<tbody>
<tr>
<th class="column" width="100%" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; font-weight: 400; text-align: left; vertical-align: top; padding-top: 0px; padding-bottom: 0px; border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px;">
<table class="divider_block" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td style="padding-bottom:12px;padding-top:60px;">
<div align="center">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td class="divider_inner" style="font-size: 1px; line-height: 1px; border-top: 0px solid #BBBBBB;"><span></span></td>
</tr>
</table>
</div>
</td>
</tr>
</table>
<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>
</td>
</tr>
</table>
<table class="divider_block" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td style="padding-top:50px;">
<div align="center">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td class="divider_inner" style="font-size: 1px; line-height: 1px; border-top: 0px solid #BBBBBB;"><span></span></td>
</tr>
</table>
</div>
</td>
</tr>
</table>
<table class="text_block" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word;">
<tr>
<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; 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: 16px; text-align: center;"><span style="font-size:30px;color:#2b303a;"><strong>Thank you for the payment</strong></span></p>
</div>
</div>
</td>
</tr>
</table>
<table class="text_block" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word;">
<tr>
<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>
</div>
</div>
</td>
</tr>
</table>
<table class="divider_block" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td style="padding-top:50px;">
<div align="center">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td class="divider_inner" style="font-size: 1px; line-height: 1px; border-top: 0px solid #BBBBBB;"><span></span></td>
</tr>
</table>
</div>
</td>
</tr>
</table>
</th>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<table class="row row-3" align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tbody>
<tr>
<td>
<table class="row-content" align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #fff;" width="640">
<tbody>
<tr>
<th class="column" width="50%" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; font-weight: 400; text-align: left; vertical-align: top;">
<table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td style="width:20px;background-color:#FFF">&nbsp;</td>
<td style="background-color:#f3fafa;border-right:8px solid #FFF;border-top:0px;border-bottom:0px;width:300px;">
<table class="divider_block" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td>
<div align="center">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td class="divider_inner" style="font-size: 1px; line-height: 1px; border-top: 4px solid #D54560;"><span></span></td>
</tr>
</table>
</div>
</td>
</tr>
</table>
<table class="text_block" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word;">
<tr>
<td style="padding-bottom:40px;padding-left:5px;padding-right:5px;padding-top:35px;">
<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>
</div>
</div>
</td>
</tr>
</table>
</td>
</tr>
</table>
</th>
<th class="column" width="50%" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; font-weight: 400; text-align: left; vertical-align: top;">
<table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td style="background-color:#f3fafa;border-left:8px solid #FFF;border-top:0px;border-bottom:0px;width:300px;">
<table class="divider_block" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td>
<div align="center">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td class="divider_inner" style="font-size: 1px; line-height: 1px; border-top: 4px solid #D54560;"><span></span></td>
</tr>
</table>
</div>
</td>
</tr>
</table>
<table class="text_block" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word;">
<tr>
<td style="padding-bottom:40px;padding-left:5px;padding-right:5px;padding-top:35px;">
<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>
</div>
</div>
</td>
</tr>
</table>
</td>
<td style="width:20px;background-color:#FFF">&nbsp;</td>
</tr>
</table>
</th>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<table class="row row-4" align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tbody>
<tr>
<td>
<table class="row-content stack" align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #fff;" width="640">
<tbody>
<tr>
<th class="column" width="100%" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; font-weight: 400; text-align: left; vertical-align: top; padding-top: 0px; padding-bottom: 0px; border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px;">
<table class="button_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: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]></center></v:textbox></v:roundrect><![endif]-->
</div>
</td>
</tr>
</table>
<table class="divider_block" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td style="padding-bottom:12px;padding-top:50px;">
<div align="center">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td class="divider_inner" style="font-size: 1px; line-height: 1px; border-top: 0px solid #BBBBBB;"><span></span></td>
</tr>
</table>
</div>
</td>
</tr>
</table>
</th>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<table class="row row-5" align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tbody>
<tr>
<td>
<table class="row-content stack" align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #2b303a;" width="640">
<tbody>
<tr>
<th class="column" width="100%" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; font-weight: 400; text-align: left; vertical-align: top; padding-top: 0px; padding-bottom: 0px; border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px;">
<table class="divider_block" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td>
<div align="center">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td class="divider_inner" style="font-size: 1px; line-height: 1px; border-top: 4px solid #D54560;"><span></span></td>
</tr>
</table>
</div>
</td>
</tr>
</table>
<table class="html_block" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td>
<div style="font-family:Montserrat, Trebuchet MS, Lucida Grande, Lucida Sans Unicode, Lucida Sans, Tahoma, sans-serif;" align="center"><div class="cont" style=" padding: 25px 0px 0; margin-top: 0px;">
<a href="{{app_url}}" style="font-size: 14px; font-family: Arial, sans-serif; color: #777; font-weight: bold; margin-right: 30px; margin-bottom: 20px; text-decoration: none; display: inline-block;">Home</a>
<a href="{{app_url}}/events" style="font-size: 14px; font-family: Arial, sans-serif; color: #777; font-weight: bold; margin-right: 30px; margin-bottom: 20px; text-decoration: none; display: inline-block;">Events</a>
<a href="{{app_url}}/organizations" style="font-size: 14px; font-family: Arial, sans-serif; color: #777; font-weight: bold; margin-right: 30px; margin-bottom: 20px; text-decoration: none; display: inline-block;">Organizations</a>
<a href="{{app_url}}/terms-service" style="font-size: 14px; font-family: Arial, sans-serif; color: #777; font-weight: bold; margin-right: 30px; margin-bottom: 20px; text-decoration: none; display: inline-block;">Term
of Service</a>
<a href="{{app_url}}/privacy-policy" style="font-size: 14px; font-family: Arial, sans-serif; color: #777; font-weight: bold; margin-right: 0px; margin-bottom: 20px; text-decoration: none; display: inline-block;">Privacy
Policy</a>
</div></div>
</td>
</tr>
</table>
<table class="divider_block" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td style="padding-bottom:10px;padding-left:40px;padding-right:40px;padding-top:10px;">
<div align="center">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td class="divider_inner" style="font-size: 1px; line-height: 1px; border-top: 1px solid #555961;"><span></span></td>
</tr>
</table>
</div>
</td>
</tr>
</table>
<table class="text_block" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word;">
<tr>
<td style="padding-bottom:30px;padding-left:40px;padding-right:40px;padding-top:20px;">
<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.2;">
<p style="margin: 0; font-size: 14px; text-align: center;"><span style="color:#95979c;font-size:12px;">EventHub - Powered by Volosoft</span></p>
</div>
</div>
</td>
</tr>
</table>
</th>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table><!-- End -->
</body>
</html>

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

@ -0,0 +1,245 @@
<!DOCTYPE html>
<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" lang="en">
<head>
<title></title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!--[if mso]><xml><o:OfficeDocumentSettings><o:PixelsPerInch>96</o:PixelsPerInch><o:AllowPNG/></o:OfficeDocumentSettings></xml><![endif]-->
<style>
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
}
th.column {
padding: 0
}
a[x-apple-data-detectors] {
color: inherit !important;
text-decoration: inherit !important;
}
#MessageViewBody a {
color: inherit;
text-decoration: none;
}
p {
line-height: inherit
}
@media (max-width:660px) {
.row-content {
width: 100% !important;
}
.stack .column {
width: 100%;
display: block;
}
}
</style>
</head>
<body style="background-color: #f8f8f9; margin: 0; padding: 0; -webkit-text-size-adjust: none; text-size-adjust: none;">
<table class="nl-container" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #f8f8f9;">
<tbody>
<tr>
<td>
<table class="row row-1" align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tbody>
<tr>
<td>
<table class="row-content stack" align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #fff;" width="640">
<tbody>
<tr>
<th class="column" width="100%" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; font-weight: 400; text-align: left; vertical-align: top; padding-top: 0px; padding-bottom: 0px; border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px;">
<table class="divider_block" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td style="padding-bottom:12px;padding-top:60px;">
<div align="center">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td class="divider_inner" style="font-size: 1px; line-height: 1px; border-top: 0px solid #BBBBBB;"><span></span></td>
</tr>
</table>
</div>
</td>
</tr>
</table>
<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>
</td>
</tr>
</table>
<table class="divider_block" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td style="padding-top:50px;">
<div align="center">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td class="divider_inner" style="font-size: 1px; line-height: 1px; border-top: 0px solid #BBBBBB;"><span></span></td>
</tr>
</table>
</div>
</td>
</tr>
</table>
<table class="text_block" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word;">
<tr>
<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; 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: 16px; text-align: center;"><span style="font-size:24px;color:#2b303a;"><strong>Sorry, Payment Failed </strong></span></p>
</div>
</div>
</td>
</tr>
</table>
<table class="text_block" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word;">
<tr>
<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>
</div>
</div>
</td>
</tr>
</table>
</th>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<table class="row row-2" align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tbody>
<tr>
<td>
<table class="row-content stack" align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #fff;" width="640">
<tbody>
<tr>
<th class="column" width="100%" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; font-weight: 400; text-align: left; vertical-align: top; padding-top: 5px; padding-bottom: 5px; border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px;">
<table class="image_block" width="100%" border="0" cellpadding="10" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td>
<div align="center" style="line-height:10px"><a href="https://www.paypal.com" target="_blank" style="outline:none" tabindex="-1"><img src="https://www.paypalobjects.com/digitalassets/c/website/logo/full-text/pp_fc_hl.svg" style="display: block; height: auto; border: 0; width: 246px; max-width: 100%;" width="246" alt="paypal" title="paypal"></a></div>
</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;">
<tr>
<td>
<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.2;">
<p style="margin: 0; font-size: 16px; text-align: center;"><em><strong>Fail Reason</strong></em></p>
</div>
</div>
</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;">
<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>
</div>
</td>
</tr>
</table>
</th>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<table class="row row-3" align="center" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tbody>
<tr>
<td>
<table class="row-content stack" align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #2b303a;" width="640">
<tbody>
<tr>
<th class="column" width="100%" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; font-weight: 400; text-align: left; vertical-align: top; padding-top: 0px; padding-bottom: 0px; border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px;">
<table class="divider_block" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td>
<div align="center">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td class="divider_inner" style="font-size: 1px; line-height: 1px; border-top: 4px solid #D54560;"><span></span></td>
</tr>
</table>
</div>
</td>
</tr>
</table>
<table class="html_block" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td>
<div style="font-family:Montserrat, Trebuchet MS, Lucida Grande, Lucida Sans Unicode, Lucida Sans, Tahoma, sans-serif;" align="center"><div class="cont" style=" padding: 25px 0px 0; margin-top: 0px;">
<a href="{{app_url}}" style="font-size: 14px; font-family: Arial, sans-serif; color: #777; font-weight: bold; margin-right: 30px; margin-bottom: 20px; text-decoration: none; display: inline-block;">Home</a>
<a href="{{app_url}}/events" style="font-size: 14px; font-family: Arial, sans-serif; color: #777; font-weight: bold; margin-right: 30px; margin-bottom: 20px; text-decoration: none; display: inline-block;">Events</a>
<a href="{{app_url}}/organizations" style="font-size: 14px; font-family: Arial, sans-serif; color: #777; font-weight: bold; margin-right: 30px; margin-bottom: 20px; text-decoration: none; display: inline-block;">Organizations</a>
<a href="{{app_url}}/terms-service" style="font-size: 14px; font-family: Arial, sans-serif; color: #777; font-weight: bold; margin-right: 30px; margin-bottom: 20px; text-decoration: none; display: inline-block;">Term
of Service</a>
<a href="{{app_url}}/privacy-policy" style="font-size: 14px; font-family: Arial, sans-serif; color: #777; font-weight: bold; margin-right: 0px; margin-bottom: 20px; text-decoration: none; display: inline-block;">Privacy
Policy</a>
</div></div>
</td>
</tr>
</table>
<table class="divider_block" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td style="padding-bottom:10px;padding-left:40px;padding-right:40px;padding-top:10px;">
<div align="center">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
<tr>
<td class="divider_inner" style="font-size: 1px; line-height: 1px; border-top: 1px solid #555961;"><span></span></td>
</tr>
</table>
</div>
</td>
</tr>
</table>
<table class="text_block" width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word;">
<tr>
<td style="padding-bottom:30px;padding-left:40px;padding-right:40px;padding-top:20px;">
<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.2;">
<p style="margin: 0; font-size: 14px; text-align: center;"><span style="color:#95979c;font-size:12px;">EventHub - Powered by Volosoft</span></p>
</div>
</div>
</td>
</tr>
</table>
</th>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table><!-- End -->
</body>
</html>

4
src/EventHub.Domain/Organizations/Organization.cs

@ -69,9 +69,9 @@ namespace EventHub.Organizations
return this;
}
internal Organization UpgradeToPremium(IClock clock)
internal Organization UpgradeToPremium(DateTime endDate)
{
SetPremiumStatus(true, clock.Now.AddYears(1));
SetPremiumStatus(true, endDate);
return this;
}

137
src/EventHub.Domain/PaymentRequest/PaymentRequestEventHandler.cs

@ -0,0 +1,137 @@
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;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Emailing;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Identity;
using Volo.Abp.TextTemplating;
namespace EventHub.PaymentRequest
{
public class PaymentRequestEventHandler : IDistributedEventHandler<PaymentRequestCompletedEto>,
IDistributedEventHandler<PaymentRequestFailedEto>, ITransientDependency
{
private readonly IEmailSender _emailSender;
private readonly ITemplateRenderer _templateRenderer;
private readonly IRepository<IdentityUser, Guid> _userRepository;
private readonly IRepository<Organization, Guid> _organizationRepository;
private readonly IRepository<Payment.PaymentRequests.PaymentRequest, Guid> _paymentRequestRepository;
private readonly IOptionsSnapshot<List<OrganizationPlanInfoOptions>> _organizationPlanInfoOptionsSnapshot;
private readonly ILogger<PaymentRequestEventHandler> _logger;
public PaymentRequestEventHandler(
IEmailSender emailSender,
ITemplateRenderer templateRenderer,
IRepository<IdentityUser, Guid> userRepository,
IRepository<Organization, Guid> organizationRepository,
IRepository<Payment.PaymentRequests.PaymentRequest, Guid> paymentRequestRepository,
IOptionsSnapshot<List<OrganizationPlanInfoOptions>> organizationPlanInfoOptionsSnapshot,
ILogger<PaymentRequestEventHandler> logger)
{
_emailSender = emailSender;
_templateRenderer = templateRenderer;
_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);
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());
if (paymentRequestProductExtraParameter is null)
{
_logger.LogCritical($"{eventData.PaymentRequestId} PaymentRequestId although payment was received, organization could not be upgrade because the ExtraProperties cannot be deserialized!");
return;
}
var payment = await _paymentRequestRepository.GetAsync(eventData.PaymentRequestId);
var user = await _userRepository.FindAsync(Guid.Parse(payment.CustomerId));
if (user is null)
{
_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 plan = _organizationPlanInfoOptionsSnapshot.Value.First(x => x.PlanType == paymentRequestProductExtraParameter.TargetPlanType);
organization.UpgradeToPremium(DateTime.Now.AddMonths(plan.OnePremiumPeriodAsMonth));
await _organizationRepository.UpdateAsync(organization, true);
var templateModel = new
{
OrganizationName = paymentRequestProductExtraParameter.OrganizationName,
Price = payment.Price,
Currency = payment.Currency
};
await _emailSender.QueueAsync(
user.Email,
"EventHub: Thank You",
await _templateRenderer.RenderAsync(EmailTemplates.PaymentRequestCompleted, templateModel)
);
}
public async Task HandleEventAsync(PaymentRequestFailedEto eventData)
{
var isExistExtraProperties = eventData.ExtraProperties.TryGetValue(nameof(PaymentRequestProductExtraParameterConfiguration), 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());
if (paymentRequestProductExtraParameter is null)
{
_logger.LogCritical($"{eventData.PaymentRequestId} PaymentRequestId although payment was received, mail could not be sent because the ExtraProperties cannot be deserialized!");
return;
}
var payment = await _paymentRequestRepository.GetAsync(eventData.PaymentRequestId);
var user = await _userRepository.FindAsync(Guid.Parse(payment.CustomerId));
if (user is null)
{
_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 templateModel = new
{
OrganizationName = paymentRequestProductExtraParameter.OrganizationName,
LicenseType = paymentRequestProductExtraParameter.TargetPlanType,
FailReason = eventData.FailReason
};
await _emailSender.QueueAsync(
user.Email,
"EventHub: Payment Failed",
await _templateRenderer.RenderAsync(EmailTemplates.PaymentRequestCompleted, templateModel)
);
}
}
}

2
src/EventHub.Web/EventHubWebModule.cs

@ -130,6 +130,8 @@ namespace EventHub.Web
{
Configure<RazorPagesOptions>(options =>
{
options.Conventions.AuthorizeFolder("/Payment");
options.Conventions.AuthorizePage("/Events/New");
options.Conventions.AuthorizePage("/Events/Edit");
options.Conventions.AuthorizePage("/Organizations/New");

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

@ -0,0 +1,76 @@
@page
@using EventHub.Organizations
@using EventHub.Web
@using Microsoft.AspNetCore.Http
@using Microsoft.Extensions.Options
@using Newtonsoft.Json
@using Payment.Web.Pages.Payment
@using Payment.PaymentRequests
@using Volo.Abp.Users
@inherits Payment.Web.Pages.PaymentPageBase
@model Payment.Web.Pages.Payment.PostCheckoutPageModel
@inject IOptions<EventHubUrlOptions> UrlOptions
@inject ICurrentUser CurrentUser
@{
PageLayout.Content.Title = L["PaymentResult"] + L["Enum:PaymentRequestState:" + Model.PaymentRequest.State];
var isExistExtraProperties = Model.PaymentRequest.ExtraProperties.TryGetValue(nameof(PaymentRequestProductExtraParameterConfiguration), out var ExtraProperties);
if (!isExistExtraProperties)
{
throw new BadHttpRequestException("");
}
var paymentRequestProductExtraParameterConfiguration = JsonConvert.DeserializeObject<PaymentRequestProductExtraParameterConfiguration>(ExtraProperties.ToString());
if (paymentRequestProductExtraParameterConfiguration is null)
{
throw new BadHttpRequestException("");
}
var organizationName = paymentRequestProductExtraParameterConfiguration.OrganizationName;
}
<main class="static-page container mt-5 text-center">
<div class="page-title">
@if (Model.PaymentRequest.State == PaymentRequestState.Completed)
{
<h1 class="text-commercial">
<i class="fa fa-check-circle fa-2x mb-4 text-success"></i>
<br/>Payment Successful
</h1>
<h3>Thank you for your purchase!</h3>
}
else if(Model.PaymentRequest.State == PaymentRequestState.Failed)
{
<h1 class="text-commercial">
<i class="fa fa-times fa-2x mb-4 text-danger"></i>
<br/>Payment failed
<abp-alert alert-type="Danger" class="mt-4">
A notification e-mail will be sent to you about why the payment process failed.
</abp-alert>
</h1>
<abp-button class="mt-4" href="@Model.GoBackLink" button-type="Dark" text="@L["GoBack"]"/>
}
else
{
<h1 class="text-commercial">
<i class="fa fa-clock fa-2x mb-4 text-primary"></i>
<br/>An e-mail will be sent to you when your payment is completed.
</h1>
<abp-alert alert-type="Info" class="mt-4">
If you do not receive an e-mail within 1 hour, please contact <a href="mailto:info@volosoft.com">info@volosoft.com</a>
</abp-alert>
}
<div class="row">
<div class="col-md-8 mx-auto text-center">
<hr class="mt-4 mb-5" />
<a href="/" class="btn btn-sm btn-outline-primary">Home page</a>
<a href="@UrlOptions.Value.Www/organizations/@organizationName" class="btn btn-sm btn-primary">My Organization</a>
</div>
</div>
</div>
</main>

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

@ -0,0 +1,103 @@
@page
@using EventHub.Organizations
@using Microsoft.AspNetCore.Http
@using Microsoft.Extensions.Options
@using Newtonsoft.Json
@using Payment.Web.Pages.Payment
@using Payment.PaymentRequests
@inherits Payment.Web.Pages.PaymentPageBase
@model Payment.Web.Pages.Payment.PreCheckoutPageModel
@inject IOptionsSnapshot<List<OrganizationPlanInfoOptions>> OrganizationPlanInfoOptionsSnapshot
@{
var isExistExtraProperties = Model.PaymentRequest.ExtraProperties.TryGetValue(nameof(PaymentRequestProductExtraParameterConfiguration), out var ExtraProperties);
if (!isExistExtraProperties)
{
throw new BadHttpRequestException("");
}
var paymentRequestProductExtraParameterConfiguration = JsonConvert.DeserializeObject<PaymentRequestProductExtraParameterConfiguration>(ExtraProperties.ToString());
if (paymentRequestProductExtraParameterConfiguration is null)
{
throw new BadHttpRequestException("");
}
var organizationName = paymentRequestProductExtraParameterConfiguration.OrganizationName;
var targetPlanType = paymentRequestProductExtraParameterConfiguration.TargetPlanType;
var planInfo = OrganizationPlanInfoOptionsSnapshot.Value.First(o => o.PlanType == targetPlanType);
}
@section scripts {
<abp-script-bundle name="@typeof(PreCheckoutPageModel).FullName">
<abp-script src="/client-proxies/Payment-proxy.js" />
</abp-script-bundle>
}
<div class="container px-3 my-5 clearfix">
<abp-card>
<abp-card-header><h5>Shopping Cart</h5></abp-card-header>
<abp-card-body>
<div class="table-responsive">
<table class="table table-bordered m-0">
<thead>
<tr>
<th class="text-center py-3 px-4" style="min-width: 400px;">Product Name &amp; Details</th>
<th class="text-center py-3 px-4" style="width: 100px;">Total</th>
</tr>
</thead>
<tbody>
<tr>
<td class="p-4">
<div class="media align-items-center">
<div class="media-body">
<p class="">@Model.PaymentRequest.ProductName</p>
<small>
<span class="text-muted">Plan: @targetPlanType</span>
<span class="text-muted ml-1">End date: @DateTime.Now.AddMonths(planInfo.OnePremiumPeriodAsMonth).ToShortDateString()</span>
<span class="text-muted ml-1">Current State: @L["Enum:PaymentRequestState:" + Model.PaymentRequest.State]</span>
<span class="ui-product-color ui-product-color-sm align-text-bottom" style="background:#e81e2c;"></span> &nbsp;
</small>
</div>
</div>
</td>
<td class="text-center font-weight-semibold align-middle">@Model.PaymentRequest.Price @Model.PaymentRequest.Currency</td>
</tr>
</tbody>
</table>
</div>
<div class="d-flex flex-wrap justify-content-between align-items-center pb-4">
<abp-container>
<abp-row class="float-right">
<abp-column>
<div class="text-right mt-4">
<label class="text-muted font-weight-normal m-0">Total price</label>
<div class="text-large"><strong>@Model.PaymentRequest.Price @Model.PaymentRequest.Currency</strong></div>
</div>
</abp-column>
</abp-row>
</abp-container>
</div>
<abp-container class="float-right">
<abp-row>
<abp-column class="float-right">
<form method="post">
@if (Model.PaymentRequest.State != PaymentRequestState.Waiting)
{
<abp-alert alert-type="Info">Current State: @Model.PaymentRequest.State</abp-alert>
<abp-alert alert-type="Warning">@L["PaymentRequestIsNotWaiting"]</abp-alert>
}
else
{
<abp-button class="mt-2 float-right" offset="_10" size="Large" type="submit" button-type="Primary">@L["Checkout"]</abp-button>
<a href="@Url.Page("/Pricing", new { organizationName = organizationName })" class="btn btn-lg btn-default md-btn-flat mt-2 mr-3 float-right">Back to pricing</a>
}
</form>
</abp-column>
</abp-row>
</abp-container>
</abp-card-body>
</abp-card>
</div>

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

@ -2,9 +2,7 @@
@using EventHub.Localization
@using EventHub.Organizations
@using Microsoft.AspNetCore.Mvc.Localization
@using Microsoft.Extensions.Options
@model EventHub.Web.Pages.Pricing
@inject IOptionsSnapshot<List<OrganizationPlanInfoOptions>> OrganizationPlanInfoOptionsSnapshot
@inject IHtmlLocalizer<EventHubResource> L
@section styles {
@ -39,12 +37,11 @@
</div>
</div>
<div class="px-md-3 px-lg-5">
<a class="btn btn-outline-primary btn-lg rounded-pill">Default</a>
<a class="btn btn-outline-danger btn-lg rounded-pill disabled">Default</a>
</div>
</div>
@*TODO: Multiple plans are not supported at this time.*@
@foreach (var plan in OrganizationPlanInfoOptionsSnapshot.Value.Where(x => x.IsActive))
@foreach (var plan in Model.OrganizationPlanInfos.Where(x => x.IsActive))
{
<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>
@ -65,11 +62,15 @@
@if (Model.Organization.IsPremium)
{
<a class="btn btn-success btn-lg rounded-pill">Current Plan</a>
<a class="btn btn-success btn-lg rounded-pill disabled">Current Plan</a>
}
else
{
<a href="javascript:void(0)" class="btn btn-primary btn-lg rounded-pill">Buy Now</a>
<form id="Upgrade" method="post" asp-page-handler="Upgrade">
<abp-input asp-for="TargetPlanToUpgrade" value="@plan.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>
</form>
}
</div>
</div>

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

@ -1,26 +1,51 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EventHub.Organizations;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Payment.PaymentRequests;
using Payment.Web.PaymentRequest;
using Volo.Abp.Authorization;
using Volo.Abp.Users;
namespace EventHub.Web.Pages
{
public class Pricing : EventHubPageModel
{
[BindProperty(SupportsGet = true)]
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string OrganizationName { get; set; }
[HiddenInput]
[BindProperty]
public OrganizationPlanType TargetPlanToUpgrade { get; set; }
public List<OrganizationPlanInfoOptions> OrganizationPlanInfos { 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;
public Pricing(IOrganizationAppService organizationAppService)
public Pricing(
IOrganizationAppService organizationAppService,
IPaymentRequestAppService paymentRequestAppService,
IPaymentUrlBuilder paymentUrlBuilder,
IOptionsSnapshot<List<OrganizationPlanInfoOptions>> organizationPlanInfoOptionsSnapshot)
{
_organizationAppService = organizationAppService;
_paymentRequestAppService = paymentRequestAppService;
_paymentUrlBuilder = paymentUrlBuilder;
_organizationPlanInfoOptionsSnapshot = organizationPlanInfoOptionsSnapshot;
}
public async Task OnGetAsync()
{
OrganizationPlanInfos = _organizationPlanInfoOptionsSnapshot.Value;
Organization = await _organizationAppService.GetProfileAsync(OrganizationName);
if (CurrentUser.UserName != Organization.OwnerUserName)
@ -29,9 +54,41 @@ namespace EventHub.Web.Pages
}
}
public async Task OnPostAsync()
public async Task<IActionResult> OnPostUpgradeAsync()
{
var organization = await _organizationAppService.GetProfileAsync(OrganizationName);
if (organization.OwnerUserName != CurrentUser.UserName)
{
throw new AbpAuthorizationException();
}
var plan = _organizationPlanInfoOptionsSnapshot.Value.FirstOrDefault(x => x.PlanType == TargetPlanToUpgrade && x.IsActive);
if (plan is null)
{
Alerts.Danger("Premium plan is currently inactive!");
return Page();
}
var paymentRequest = await _paymentRequestAppService.CreateAsync(new PaymentRequestCreationDto
{
CustomerId = CurrentUser.GetId().ToString(),
Price = plan.Price,
ProductId = OrganizationPlanInfoOptions.GetProductId(plan, false),
ProductName = OrganizationPlanInfoOptions.GetProductName(plan, organization.Name, false),
ExtraProperties =
{
{
nameof(PaymentRequestProductExtraParameterConfiguration),
new PaymentRequestProductExtraParameterConfiguration
{
OrganizationName = OrganizationName,
TargetPlanType = TargetPlanToUpgrade
}
}
}
});
return Redirect(_paymentUrlBuilder.BuildCheckoutUrl(paymentRequest.Id).AbsoluteUri);
}
}
}
Loading…
Cancel
Save