mirror of https://github.com/abpframework/eventhub
committed by
GitHub
177 changed files with 15068 additions and 202 deletions
@ -0,0 +1,68 @@ |
|||
# Payment Module |
|||
|
|||
Payment module provides an API to make payments via using PayPal easily. |
|||
|
|||
|
|||
|
|||
## Installation |
|||
|
|||
TODO |
|||
|
|||
|
|||
|
|||
## Usage |
|||
|
|||
1. Create a Payment Request via using `IPaymentRequestAppService` |
|||
|
|||
|
|||
|
|||
2. Build an URL to redirect to checkout page via using `IPaymentUrlBuilder` |
|||
|
|||
```csharp |
|||
public class MyPageModel : AbpPageModel |
|||
{ |
|||
protected IPaymentRequestAppService PaymentRequestAppService { get; } |
|||
protected IPaymentUrlBuilder PaymentUrlBuilder { get; } |
|||
|
|||
public MyPageModel( |
|||
IPaymentRequestAppService paymentRequestAppService, |
|||
IPaymentUrlBuilder paymentUrlBuilder) |
|||
{ |
|||
PaymentRequestAppService = paymentRequestAppService; |
|||
PaymentUrlBuilder = paymentUrlBuilder; |
|||
} |
|||
|
|||
public async Task OnPostAsync() |
|||
{ |
|||
var paymentRequest = await PaymentRequestAppService.CreateAsync(new PaymentRequestCreationDto |
|||
{ |
|||
Amount = 9.90m, |
|||
CustomerId = CurrentUser.Id.ToString(), |
|||
ProductId = "UniqueProductId", |
|||
ProductName = "Awesome Product" |
|||
}); |
|||
|
|||
var checkoutUrl = PaymentUrlBuilder.BuildCheckoutUrl(paymentRequest.Id).AbsoluteUri; |
|||
|
|||
Response.Redirect(checkoutUrl); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
|
|||
|
|||
|
|||
--- |
|||
|
|||
|
|||
|
|||
## Distributed Events |
|||
|
|||
- `Payment.Completed` (**PaymentRequestCompletedEto**): Published when a payment is completed. Completion can be triggered via webhook or callback. Source doesn't affect to the event. Event will be triggered once. |
|||
- `PaymentRequestId`: Represents PaymentRequest entity Id. |
|||
- `ExtraProperties`: Represents ExtraProperties of PaymentRequest entity. You can use this properties to find your related objects to that payment. |
|||
- `Payment.Failed`(**PaymentRequestFailedEto**): Published when a payment is failed. |
|||
- `PaymentRequestId`: Represents PaymentRequest entity Id. |
|||
- `FailReason`: Reason of failure from payment provider (PayPal) |
|||
- `ExtraProperties`: Represents ExtraProperties of PaymentRequest entity. |
|||
|
|||
@ -0,0 +1,9 @@ |
|||
<Project> |
|||
<ItemGroup> |
|||
<PackageReference Include="ConfigureAwait.Fody" Version="3.3.1" PrivateAssets="All" /> |
|||
<PackageReference Include="Fody" Version="6.5.3"> |
|||
<PrivateAssets>All</PrivateAssets> |
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> |
|||
</PackageReference> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -1,13 +0,0 @@ |
|||
version: '3.4' |
|||
|
|||
services: |
|||
migrations: |
|||
build: |
|||
context: ../../ |
|||
dockerfile: templates/service/database/Dockerfile |
|||
depends_on: |
|||
- sqlserver |
|||
environment: |
|||
- IdentityServer_DB=Payment_Identity |
|||
- Payment_DB=Payment_ModuleDb |
|||
- SA_PASSWORD=yourStrong(!)Password |
|||
@ -1,29 +0,0 @@ |
|||
version: '3.4' |
|||
|
|||
services: |
|||
sqlserver: |
|||
environment: |
|||
- SA_PASSWORD=yourStrong(!)Password |
|||
- ACCEPT_EULA=Y |
|||
ports: |
|||
- "51599:1433" |
|||
|
|||
identity-server: |
|||
environment: |
|||
- ASPNETCORE_URLS=http://0.0.0.0:80 |
|||
- ConnectionStrings__Default=Server=sqlserver;Database=Payment_Identity;Trusted_Connection=True;User=sa;Password=yourStrong(!)Password;Integrated Security=false |
|||
- ConnectionStrings__SqlServerCache=Server=sqlserver;Database=Payment_Cache;Trusted_Connection=True;User=sa;Password=yourStrong(!)Password;Integrated Security=false |
|||
ports: |
|||
- "51600:80" |
|||
|
|||
payment: |
|||
environment: |
|||
- ASPNETCORE_URLS=http://0.0.0.0:80 |
|||
- ConnectionStrings__Default=Server=sqlserver;Database=Payment_ModuleDb;Trusted_Connection=True;User=sa;Password=yourStrong(!)Password;Integrated Security=false |
|||
- ConnectionStrings__AbpSettingManagement=Server=sqlserver;Database=Payment_Identity;Trusted_Connection=True;User=sa;Password=yourStrong(!)Password;Integrated Security=false |
|||
- ConnectionStrings__AbpPermissionManagement=Server=sqlserver;Database=Payment_Identity;Trusted_Connection=True;User=sa;Password=yourStrong(!)Password;Integrated Security=false |
|||
- ConnectionStrings__AbpAuditLogging=Server=sqlserver;Database=Payment_Identity;Trusted_Connection=True;User=sa;Password=yourStrong(!)Password;Integrated Security=false |
|||
- ConnectionStrings__SqlServerCache=Server=sqlserver;Database=Payment_Cache;Trusted_Connection=True;User=sa;Password=yourStrong(!)Password;Integrated Security=false |
|||
- AuthServer__Authority=http://identity-server |
|||
ports: |
|||
- "51601:80" |
|||
@ -1,25 +0,0 @@ |
|||
version: '3.4' |
|||
|
|||
services: |
|||
sqlserver: |
|||
image: mcr.microsoft.com/mssql/server |
|||
volumes: |
|||
- dbdata:/var/opt/mssql |
|||
|
|||
identity-server: |
|||
build: |
|||
context: ../../ |
|||
dockerfile: templates/service/host/IdentityServerHost/Dockerfile |
|||
depends_on: |
|||
- sqlserver |
|||
|
|||
payment: |
|||
build: |
|||
context: ../../ |
|||
dockerfile: templates/service/host/Payment.Host/Dockerfile |
|||
depends_on: |
|||
- sqlserver |
|||
- identity-server |
|||
|
|||
volumes: |
|||
dbdata: |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,17 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\common.props" /> |
|||
<Import Project="..\..\configureawait.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace>Payment.Admin</RootNamespace> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Ddd.Application.Contracts" Version="5.0.0-beta.1" /> |
|||
<PackageReference Include="Volo.Abp.Authorization" Version="5.0.0-beta.1" /> |
|||
<ProjectReference Include="..\Payment.Domain.Shared\Payment.Domain.Shared.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,17 @@ |
|||
using System; |
|||
using Volo.Abp.Application; |
|||
using Volo.Abp.Authorization; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Payment.Admin |
|||
{ |
|||
[DependsOn( |
|||
typeof(PaymentDomainSharedModule), |
|||
typeof(AbpDddApplicationContractsModule), |
|||
typeof(AbpAuthorizationModule) |
|||
)] |
|||
public class PaymentAdminApplicationContractsModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Payment.Admin |
|||
{ |
|||
public class PaymentAdminRemoteServiceConsts |
|||
{ |
|||
public const string RemoteServiceName = "PaymentAdmin"; |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace Payment.Admin.Payments |
|||
{ |
|||
public interface IPaymentRequestAdminAppService : IApplicationService |
|||
{ |
|||
Task<PagedResultDto<PaymentRequestWithDetailsDto>> GetListAsync(PaymentRequestGetListInput input); |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using System; |
|||
using Payment.PaymentRequests; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace Payment.Admin.Payments |
|||
{ |
|||
public class PaymentRequestGetListInput : PagedAndSortedResultRequestDto |
|||
{ |
|||
public string ProductName { get; set; } |
|||
|
|||
public DateTime? MaxCreationTime { get; set; } |
|||
|
|||
public DateTime? MinCreationTime { get; set; } |
|||
|
|||
public PaymentRequestState? Status { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
using System; |
|||
using Payment.PaymentRequests; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.Auditing; |
|||
|
|||
namespace Payment.Admin.Payments |
|||
{ |
|||
public class PaymentRequestWithDetailsDto : ExtensibleEntityDto<Guid>, IHasCreationTime |
|||
{ |
|||
public string CustomerId { get; set; } |
|||
|
|||
public string ProductName { get; set; } |
|||
|
|||
public float Price { get; set; } |
|||
|
|||
public string Currency { get; set; } |
|||
|
|||
public PaymentRequestState State { get; set; } |
|||
|
|||
public string FailReason { get; set; } |
|||
|
|||
public DateTime CreationTime { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using Payment.Localization; |
|||
using Volo.Abp.Authorization.Permissions; |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace Payment.Admin.Permissions |
|||
{ |
|||
public class PaymentAdminPermissionDefinitionProvider : PermissionDefinitionProvider |
|||
{ |
|||
public override void Define(IPermissionDefinitionContext context) |
|||
{ |
|||
var paymentGroup = context.AddGroup(PaymentAdminPermissions.GroupName, L("Permission:PaymentManagement")); |
|||
paymentGroup.AddPermission(PaymentAdminPermissions.Request.Default, L("Permission:PaymentRequest")); |
|||
} |
|||
|
|||
private static LocalizableString L(string name) |
|||
{ |
|||
return LocalizableString.Create<PaymentResource>(name); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
namespace Payment.Admin.Permissions |
|||
{ |
|||
public class PaymentAdminPermissions |
|||
{ |
|||
public const string GroupName = "PaymentAdmin"; |
|||
|
|||
public static class Request |
|||
{ |
|||
public const string Default = GroupName + ".Request"; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,17 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\common.props" /> |
|||
<Import Project="..\..\configureawait.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace>Payment.Admin</RootNamespace> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.AutoMapper" Version="5.0.0-beta.1" /> |
|||
<PackageReference Include="Volo.Abp.Ddd.Application" Version="5.0.0-beta.1" /> |
|||
<ProjectReference Include="..\Payment.Admin.Application.Contracts\Payment.Admin.Application.Contracts.csproj" /> |
|||
<ProjectReference Include="..\Payment.Domain\Payment.Domain.csproj" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -0,0 +1,14 @@ |
|||
using AutoMapper; |
|||
using Payment.Admin.Payments; |
|||
using Payment.PaymentRequests; |
|||
|
|||
namespace Payment.Admin |
|||
{ |
|||
public class PaymentAdminApplicationAutoMapperProfile : Profile |
|||
{ |
|||
public PaymentAdminApplicationAutoMapperProfile() |
|||
{ |
|||
CreateMap<PaymentRequest, PaymentRequestWithDetailsDto>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Application; |
|||
using Volo.Abp.AutoMapper; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Payment.Admin |
|||
{ |
|||
[DependsOn( |
|||
typeof(PaymentDomainModule), |
|||
typeof(PaymentAdminApplicationContractsModule), |
|||
typeof(AbpDddApplicationModule), |
|||
typeof(AbpAutoMapperModule) |
|||
)] |
|||
public class PaymentAdminApplicationModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddAutoMapperObjectMapper<PaymentAdminApplicationModule>(); |
|||
Configure<AbpAutoMapperOptions>(options => |
|||
{ |
|||
options.AddProfile<PaymentAdminApplicationAutoMapperProfile>(validate: true); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Authorization; |
|||
using Payment.Admin.Permissions; |
|||
using Payment.PaymentRequests; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace Payment.Admin.Payments |
|||
{ |
|||
[Authorize(PaymentAdminPermissions.Request.Default)] |
|||
public class PaymentRequestAdminAppService : ApplicationService, IPaymentRequestAdminAppService |
|||
{ |
|||
protected IPaymentRequestRepository PaymentRequestRepository { get; } |
|||
|
|||
public PaymentRequestAdminAppService(IPaymentRequestRepository paymentRequestRepository) |
|||
{ |
|||
PaymentRequestRepository = paymentRequestRepository; |
|||
} |
|||
|
|||
public async Task<PagedResultDto<PaymentRequestWithDetailsDto>> GetListAsync(PaymentRequestGetListInput input) |
|||
{ |
|||
var paymentRequests = await PaymentRequestRepository.GetListAsync( |
|||
input.SkipCount, |
|||
input.MaxResultCount, |
|||
input.Sorting, |
|||
input.ProductName, |
|||
input.MaxCreationTime, |
|||
input.MinCreationTime, |
|||
input.Status |
|||
); |
|||
|
|||
var totalCount = await PaymentRequestRepository.GetCountAsync( |
|||
input.ProductName, |
|||
input.MaxCreationTime, |
|||
input.MinCreationTime, |
|||
input.Status |
|||
); |
|||
|
|||
return new PagedResultDto<PaymentRequestWithDetailsDto>( |
|||
totalCount, |
|||
ObjectMapper.Map<List<PaymentRequest>, List<PaymentRequestWithDetailsDto>>(paymentRequests) |
|||
); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,43 @@ |
|||
using System.Threading.Tasks; |
|||
using Payment.Admin.Permissions; |
|||
using Payment.Localization; |
|||
using Volo.Abp.UI.Navigation; |
|||
|
|||
namespace Payment.Admin.Navigation |
|||
{ |
|||
public class PaymentAdminBlazorMenuContributor : IMenuContributor |
|||
{ |
|||
public async Task ConfigureMenuAsync(MenuConfigurationContext context) |
|||
{ |
|||
if (context.Menu.Name == StandardMenus.Main) |
|||
{ |
|||
await ConfigureMainMenuAsync(context); |
|||
} |
|||
} |
|||
|
|||
private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) |
|||
{ |
|||
await AddPaymentMenuAsync(context); |
|||
} |
|||
|
|||
private Task AddPaymentMenuAsync(MenuConfigurationContext context) |
|||
{ |
|||
var l = context.GetLocalizer<PaymentResource>(); |
|||
|
|||
var paymentMenu = context.Menu.FindMenuItem(PaymentAdminMenuNames.GroupName); |
|||
if (paymentMenu == null) |
|||
{ |
|||
paymentMenu = new ApplicationMenuItem( |
|||
PaymentAdminMenuNames.GroupName, |
|||
l["Menu:PaymentManagement"], |
|||
icon: "fa fa-money-check", |
|||
url: "/payment/requests", |
|||
requiredPermissionName: PaymentAdminPermissions.Request.Default |
|||
); |
|||
} |
|||
|
|||
context.Menu.AddItem(paymentMenu); |
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Payment.Admin.Navigation |
|||
{ |
|||
public static class PaymentAdminMenuNames |
|||
{ |
|||
public const string GroupName = "Payment"; |
|||
} |
|||
} |
|||
@ -0,0 +1,89 @@ |
|||
@page "/Payment/Requests" |
|||
@using Microsoft.AspNetCore.Authorization |
|||
@attribute [Authorize(PaymentAdminPermissions.Request.Default)] |
|||
@using global::Payment.Admin.Payments |
|||
@using global::Payment.PaymentRequests |
|||
@using global::Payment.Admin.Permissions |
|||
@using Volo.Abp.BlazoriseUI.Components.ObjectExtending |
|||
@inherits PaymentAdminComponentBase |
|||
|
|||
<Card> |
|||
<CardHeader> |
|||
<h2>@L["PaymentRequests"]</h2> |
|||
</CardHeader> |
|||
|
|||
<CardBody> |
|||
<Form> |
|||
<div id="FilterSection" class="row mt-3"> |
|||
<Field ColumnSize="ColumnSize.Is3"> |
|||
<FieldLabel>@L["ProductName"]</FieldLabel> |
|||
<TextEdit Placeholder="@L["ProductName"]" @bind-Text="@GetListInput.ProductName"/> |
|||
</Field> |
|||
<Field ColumnSize="ColumnSize.Is3"> |
|||
<FieldLabel>@L["Status"]</FieldLabel> |
|||
<Select Placeholder="@L["Status"]" TValue="PaymentRequestState?" SelectedValue="@GetListInput.Status" SelectedValueChanged="OnPaymentRequestStateChanged"> |
|||
<option value="@((PaymentRequestState?) null)">-</option> |
|||
@foreach (var item in PaymentRequestStates) |
|||
{ |
|||
<SelectItem Value="@item">@L["Enum:PaymentRequestState:" + item]</SelectItem> |
|||
} |
|||
</Select> |
|||
</Field> |
|||
<Field ColumnSize="ColumnSize.Is3"> |
|||
<FieldLabel>@L["MinCreationTime"]</FieldLabel> |
|||
<DateEdit Placeholder="@L["MinCreationTime"]" @bind-Date="@GetListInput.MinCreationTime"/> |
|||
</Field> |
|||
<Field ColumnSize="ColumnSize.Is3"> |
|||
<FieldLabel>@L["MaxCreationTime"]</FieldLabel> |
|||
<DateEdit Placeholder="@L["MaxCreationTime"]" @bind-Date="@GetListInput.MaxCreationTime"/> |
|||
</Field> |
|||
<Field ColumnSize="ColumnSize.Is12"> |
|||
<Row> |
|||
<Column ColumnSize="ColumnSize.Is9"></Column> |
|||
<Column ColumnSize="ColumnSize.Is3"> |
|||
<SubmitButton Block="true" |
|||
Clicked="@GetPaymentRequestsAsync"> |
|||
<Icon Name="IconName.Search" /> |
|||
</SubmitButton> |
|||
</Column> |
|||
</Row> |
|||
</Field> |
|||
</div> |
|||
</Form> |
|||
|
|||
<DataGrid TItem="PaymentRequestWithDetailsDto" |
|||
Data="PaymentRequests" |
|||
ReadData="OnDataGridReadAsync" |
|||
TotalItems="TotalCount" |
|||
ShowPager="true" |
|||
PageSize="PageSize" |
|||
Responsive="true"> |
|||
<DataGridColumns> |
|||
<DataGridColumn TItem="PaymentRequestWithDetailsDto" |
|||
Field="ProductName" |
|||
Caption="@L["ProductName"]"> |
|||
</DataGridColumn> |
|||
<DataGridColumn TItem="PaymentRequestWithDetailsDto" |
|||
Field="Currency" |
|||
Caption="@L["Currency"]"> |
|||
</DataGridColumn> |
|||
<DataGridColumn TItem="PaymentRequestWithDetailsDto" |
|||
Field="Price" |
|||
Caption="@L["Price"]"> |
|||
</DataGridColumn> |
|||
<DataGridColumn TItem="PaymentRequestWithDetailsDto" |
|||
Field="State" |
|||
Caption="@L["State"]"> |
|||
</DataGridColumn> |
|||
<DataGridColumn TItem="PaymentRequestWithDetailsDto" |
|||
Field="FailReason" |
|||
Caption="@L["FailReason"]"> |
|||
</DataGridColumn> |
|||
<DataGridColumn TItem="PaymentRequestWithDetailsDto" |
|||
Field="CreationTime" |
|||
Caption="@L["CreationTime"]"> |
|||
</DataGridColumn> |
|||
</DataGridColumns> |
|||
</DataGrid> |
|||
</CardBody> |
|||
</Card> |
|||
@ -0,0 +1,82 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Blazorise; |
|||
using Blazorise.DataGrid; |
|||
using Microsoft.AspNetCore.Components; |
|||
using Payment.Admin.Payments; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Payment.PaymentRequests; |
|||
|
|||
namespace Payment.Admin.Pages.Payment.Requests |
|||
{ |
|||
public partial class Index |
|||
{ |
|||
[Inject] |
|||
protected IPaymentRequestAdminAppService PaymentRequestAdminAppService { get; set; } |
|||
|
|||
protected IReadOnlyList<PaymentRequestWithDetailsDto> PaymentRequests { get; set; } |
|||
|
|||
protected IReadOnlyList<PaymentRequestState> PaymentRequestStates => Enum.GetValues<PaymentRequestState>(); |
|||
|
|||
protected PaymentRequestGetListInput GetListInput { get; set; } |
|||
|
|||
protected virtual int PageSize { get; } = LimitedResultRequestDto.DefaultMaxResultCount; |
|||
|
|||
protected int CurrentPage { get; set; } = 1; |
|||
|
|||
protected string CurrentSorting { get; set; } |
|||
|
|||
protected int? TotalCount { get; set; } |
|||
|
|||
public Index() |
|||
{ |
|||
GetListInput = new(); |
|||
} |
|||
|
|||
protected override async Task OnInitializedAsync() |
|||
{ |
|||
await GetPaymentRequestsAsync(); |
|||
await InvokeAsync(StateHasChanged); |
|||
} |
|||
|
|||
protected virtual async Task GetPaymentRequestsAsync() |
|||
{ |
|||
try |
|||
{ |
|||
GetListInput.Sorting = CurrentSorting; |
|||
GetListInput.SkipCount = (CurrentPage - 1) * PageSize; |
|||
GetListInput.MaxResultCount = PageSize; |
|||
|
|||
var result = await PaymentRequestAdminAppService.GetListAsync(GetListInput); |
|||
PaymentRequests = result.Items.As<IReadOnlyList<PaymentRequestWithDetailsDto>>(); |
|||
TotalCount = (int?) result.TotalCount; |
|||
await InvokeAsync(StateHasChanged); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
await HandleErrorAsync(ex); |
|||
} |
|||
} |
|||
|
|||
protected virtual async Task OnDataGridReadAsync(DataGridReadDataEventArgs<PaymentRequestWithDetailsDto> e) |
|||
{ |
|||
CurrentSorting = e.Columns |
|||
.Where(c => c.SortDirection != SortDirection.None) |
|||
.Select(c => c.Field + (c.SortDirection == SortDirection.Descending ? " DESC" : "")) |
|||
.JoinAsString(","); |
|||
|
|||
CurrentPage = e.Page; |
|||
|
|||
await GetPaymentRequestsAsync(); |
|||
await InvokeAsync(StateHasChanged); |
|||
} |
|||
|
|||
private async Task OnPaymentRequestStateChanged(PaymentRequestState? status) |
|||
{ |
|||
GetListInput.Status = status; |
|||
await GetPaymentRequestsAsync(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk.Razor"> |
|||
|
|||
<Import Project="..\..\common.props" /> |
|||
<Import Project="..\..\configureawait.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<RootNamespace>Payment.Admin</RootNamespace> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.AspNetCore.Components.WebAssembly.Theming" Version="5.0.0-beta.1" /> |
|||
<ProjectReference Include="..\Payment.Admin.HttpApi.Client\Payment.Admin.HttpApi.Client.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,28 @@ |
|||
using Payment.Admin.Navigation; |
|||
using Volo.Abp.AspNetCore.Components.Web.Theming.Routing; |
|||
using Volo.Abp.AspNetCore.Components.WebAssembly.Theming; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.UI.Navigation; |
|||
|
|||
namespace Payment.Admin |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpAspNetCoreComponentsWebAssemblyThemingModule), |
|||
typeof(PaymentAdminHttpApiClientModule) |
|||
)] |
|||
public class PaymentAdminBlazorModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpNavigationOptions>(options => |
|||
{ |
|||
options.MenuContributors.Add(new PaymentAdminBlazorMenuContributor()); |
|||
}); |
|||
|
|||
Configure<AbpRouterOptions>(options => |
|||
{ |
|||
options.AdditionalAssemblies.Add(typeof(PaymentAdminBlazorModule).Assembly); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using Payment.Localization; |
|||
using Volo.Abp.AspNetCore.Components; |
|||
|
|||
namespace Payment.Admin |
|||
{ |
|||
public abstract class PaymentAdminComponentBase : AbpComponentBase |
|||
{ |
|||
protected PaymentAdminComponentBase() |
|||
{ |
|||
LocalizationResource = typeof(PaymentResource); |
|||
ObjectMapperContext = typeof(PaymentAdminBlazorModule); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
@using Microsoft.AspNetCore.Components.Web |
|||
@using Volo.Abp.AspNetCore.Components.Web |
|||
@using Volo.Abp.BlazoriseUI |
|||
@using Volo.Abp.BlazoriseUI.Components |
|||
@using Blazorise |
|||
@using Blazorise.DataGrid |
|||
@ -0,0 +1,26 @@ |
|||
// This file is automatically generated by ABP framework to use MVC Controllers from CSharp
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.Http.Client; |
|||
using Volo.Abp.Http.Modeling; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Http.Client.ClientProxying; |
|||
using Payment.Admin.Payments; |
|||
|
|||
// ReSharper disable once CheckNamespace
|
|||
namespace Payment.Admin.Payments.ClientProxies |
|||
{ |
|||
[Dependency(ReplaceServices = true)] |
|||
[ExposeServices(typeof(IPaymentRequestAdminAppService), typeof(PaymentRequestAdminClientProxy))] |
|||
public partial class PaymentRequestAdminClientProxy : ClientProxyBase<IPaymentRequestAdminAppService>, IPaymentRequestAdminAppService |
|||
{ |
|||
public virtual async Task<PagedResultDto<PaymentRequestWithDetailsDto>> GetListAsync(PaymentRequestGetListInput input) |
|||
{ |
|||
return await RequestAsync<PagedResultDto<PaymentRequestWithDetailsDto>>(nameof(GetListAsync), new ClientProxyRequestTypeValue |
|||
{ |
|||
{ typeof(PaymentRequestGetListInput), input } |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
// This file is part of PaymentRequestAdminClientProxy, you can customize it here
|
|||
// ReSharper disable once CheckNamespace
|
|||
namespace Payment.Admin.Payments.ClientProxies |
|||
{ |
|||
public partial class PaymentRequestAdminClientProxy |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,132 @@ |
|||
{ |
|||
"modules": { |
|||
"paymentAdmin": { |
|||
"rootPath": "paymentAdmin", |
|||
"remoteServiceName": "PaymentAdmin", |
|||
"controllers": { |
|||
"Payment.Admin.Payments.PaymentRequestAdminController": { |
|||
"controllerName": "PaymentRequestAdmin", |
|||
"controllerGroupName": "PaymentRequestAdmin", |
|||
"type": "Payment.Admin.Payments.PaymentRequestAdminController", |
|||
"interfaces": [ |
|||
{ |
|||
"type": "Payment.Admin.Payments.IPaymentRequestAdminAppService" |
|||
} |
|||
], |
|||
"actions": { |
|||
"GetListAsyncByInput": { |
|||
"uniqueName": "GetListAsyncByInput", |
|||
"name": "GetListAsync", |
|||
"httpMethod": "GET", |
|||
"url": "api/payment/requests", |
|||
"supportedVersions": [], |
|||
"parametersOnMethod": [ |
|||
{ |
|||
"name": "input", |
|||
"typeAsString": "Payment.Admin.Payments.PaymentRequestGetListInput, Payment.Admin.Application.Contracts", |
|||
"type": "Payment.Admin.Payments.PaymentRequestGetListInput", |
|||
"typeSimple": "Payment.Admin.Payments.PaymentRequestGetListInput", |
|||
"isOptional": false, |
|||
"defaultValue": null |
|||
} |
|||
], |
|||
"parameters": [ |
|||
{ |
|||
"nameOnMethod": "input", |
|||
"name": "ProductName", |
|||
"jsonName": null, |
|||
"type": "System.String", |
|||
"typeSimple": "string", |
|||
"isOptional": false, |
|||
"defaultValue": null, |
|||
"constraintTypes": null, |
|||
"bindingSourceId": "ModelBinding", |
|||
"descriptorName": "input" |
|||
}, |
|||
{ |
|||
"nameOnMethod": "input", |
|||
"name": "MaxCreationTime", |
|||
"jsonName": null, |
|||
"type": "System.DateTime?", |
|||
"typeSimple": "string?", |
|||
"isOptional": false, |
|||
"defaultValue": null, |
|||
"constraintTypes": null, |
|||
"bindingSourceId": "ModelBinding", |
|||
"descriptorName": "input" |
|||
}, |
|||
{ |
|||
"nameOnMethod": "input", |
|||
"name": "MinCreationTime", |
|||
"jsonName": null, |
|||
"type": "System.DateTime?", |
|||
"typeSimple": "string?", |
|||
"isOptional": false, |
|||
"defaultValue": null, |
|||
"constraintTypes": null, |
|||
"bindingSourceId": "ModelBinding", |
|||
"descriptorName": "input" |
|||
}, |
|||
{ |
|||
"nameOnMethod": "input", |
|||
"name": "Status", |
|||
"jsonName": null, |
|||
"type": "Payment.PaymentRequests.PaymentRequestState?", |
|||
"typeSimple": "Payment.PaymentRequests.PaymentRequestState?", |
|||
"isOptional": false, |
|||
"defaultValue": null, |
|||
"constraintTypes": null, |
|||
"bindingSourceId": "ModelBinding", |
|||
"descriptorName": "input" |
|||
}, |
|||
{ |
|||
"nameOnMethod": "input", |
|||
"name": "Sorting", |
|||
"jsonName": null, |
|||
"type": "System.String", |
|||
"typeSimple": "string", |
|||
"isOptional": false, |
|||
"defaultValue": null, |
|||
"constraintTypes": null, |
|||
"bindingSourceId": "ModelBinding", |
|||
"descriptorName": "input" |
|||
}, |
|||
{ |
|||
"nameOnMethod": "input", |
|||
"name": "SkipCount", |
|||
"jsonName": null, |
|||
"type": "System.Int32", |
|||
"typeSimple": "number", |
|||
"isOptional": false, |
|||
"defaultValue": null, |
|||
"constraintTypes": null, |
|||
"bindingSourceId": "ModelBinding", |
|||
"descriptorName": "input" |
|||
}, |
|||
{ |
|||
"nameOnMethod": "input", |
|||
"name": "MaxResultCount", |
|||
"jsonName": null, |
|||
"type": "System.Int32", |
|||
"typeSimple": "number", |
|||
"isOptional": false, |
|||
"defaultValue": null, |
|||
"constraintTypes": null, |
|||
"bindingSourceId": "ModelBinding", |
|||
"descriptorName": "input" |
|||
} |
|||
], |
|||
"returnValue": { |
|||
"type": "Volo.Abp.Application.Dtos.PagedResultDto<Payment.Admin.Payments.PaymentRequestWithDetailsDto>", |
|||
"typeSimple": "Volo.Abp.Application.Dtos.PagedResultDto<Payment.Admin.Payments.PaymentRequestWithDetailsDto>" |
|||
}, |
|||
"allowAnonymous": null, |
|||
"implementFrom": "Payment.Admin.Payments.IPaymentRequestAdminAppService" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
"types": {} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,21 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\common.props" /> |
|||
<Import Project="..\..\configureawait.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace>Payment.Admin</RootNamespace> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Http.Client" Version="5.0.0-beta.1" /> |
|||
<ProjectReference Include="..\Payment.Admin.Application.Contracts\Payment.Admin.Application.Contracts.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<EmbeddedResource Include="**\*generate-proxy.json" /> |
|||
<Content Remove="**\*generate-proxy.json" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,27 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Http.Client; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace Payment.Admin |
|||
{ |
|||
[DependsOn( |
|||
typeof(PaymentAdminApplicationContractsModule), |
|||
typeof(AbpHttpClientModule) |
|||
)] |
|||
public class PaymentAdminHttpApiClientModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddStaticHttpClientProxies( |
|||
typeof(PaymentAdminApplicationContractsModule).Assembly, |
|||
PaymentAdminRemoteServiceConsts.RemoteServiceName |
|||
); |
|||
|
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<PaymentAdminHttpApiClientModule>(); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,16 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\common.props" /> |
|||
<Import Project="..\..\configureawait.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<RootNamespace>Payment.Admin</RootNamespace> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="5.0.0-beta.1" /> |
|||
<ProjectReference Include="..\Payment.Admin.Application.Contracts\Payment.Admin.Application.Contracts.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,13 @@ |
|||
using Payment.Localization; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
|
|||
namespace Payment.Admin |
|||
{ |
|||
public abstract class PaymentAdminController : AbpControllerBase |
|||
{ |
|||
protected PaymentAdminController() |
|||
{ |
|||
LocalizationResource = typeof(PaymentResource); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using Localization.Resources.AbpUi; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Payment.Localization; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Payment.Admin |
|||
{ |
|||
[DependsOn( |
|||
typeof(PaymentAdminApplicationContractsModule), |
|||
typeof(AbpAspNetCoreMvcModule) |
|||
)] |
|||
public class PaymentAdminHttpApiModule : AbpModule |
|||
{ |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
PreConfigure<IMvcBuilder>(mvcBuilder => |
|||
{ |
|||
mvcBuilder.AddApplicationPartIfNotExists(typeof(PaymentAdminHttpApiModule).Assembly); |
|||
}); |
|||
} |
|||
|
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Get<PaymentResource>() |
|||
.AddBaseTypes(typeof(AbpUiResource)); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace Payment.Admin.Payments |
|||
{ |
|||
[RemoteService(Name = PaymentAdminRemoteServiceConsts.RemoteServiceName)] |
|||
[Area("paymentAdmin")] |
|||
[Route("api/payment/requests")] |
|||
public class PaymentRequestAdminController : PaymentAdminController, IPaymentRequestAdminAppService |
|||
{ |
|||
protected IPaymentRequestAdminAppService PaymentRequestAdminAppService { get; } |
|||
|
|||
public PaymentRequestAdminController(IPaymentRequestAdminAppService paymentRequestAdminAppService) |
|||
{ |
|||
PaymentRequestAdminAppService = paymentRequestAdminAppService; |
|||
} |
|||
|
|||
[HttpGet] |
|||
public Task<PagedResultDto<PaymentRequestWithDetailsDto>> GetListAsync(PaymentRequestGetListInput input) |
|||
{ |
|||
return PaymentRequestAdminAppService.GetListAsync(input); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace Payment.PaymentRequests |
|||
{ |
|||
public interface IPaymentRequestAppService : IApplicationService |
|||
{ |
|||
Task<PaymentRequestDto> GetAsync(Guid id); |
|||
|
|||
Task<PaymentRequestDto> CreateAsync(PaymentRequestCreationDto input); |
|||
|
|||
Task<StartPaymentResultDto> StartPaymentAsync(StartPaymentDto input); |
|||
|
|||
Task<PaymentRequestDto> CompleteAsync(string token); |
|||
|
|||
Task<bool> HandleWebhookAsync(string payload); |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using JetBrains.Annotations; |
|||
using System; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using Volo.Abp.ObjectExtending; |
|||
using Volo.Abp.Validation; |
|||
|
|||
namespace Payment.PaymentRequests |
|||
{ |
|||
[Serializable] |
|||
public class PaymentRequestCreationDto : ExtensibleObject |
|||
{ |
|||
[DynamicMaxLength(typeof(PaymentRequestConsts), nameof(PaymentRequestConsts.MaxCustomerIdLength))] |
|||
public string CustomerId { get; set; } |
|||
|
|||
[DynamicMaxLength(typeof(PaymentRequestConsts), nameof(PaymentRequestConsts.MaxProductIdLength))] |
|||
public string ProductId { get; set; } |
|||
|
|||
[Required] |
|||
[DynamicMaxLength(typeof(PaymentRequestConsts), nameof(PaymentRequestConsts.MaxProductNameLength))] |
|||
public string ProductName { get; set; } |
|||
|
|||
public decimal Price { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 3 Letter Language Code in ISO 4217 Standards.
|
|||
/// For example: USD, EUR.
|
|||
/// </summary>
|
|||
[StringLength(PaymentRequestConsts.MaxCurrencyLength)] |
|||
[CanBeNull] |
|||
public string Currency { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
using System; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.Data; |
|||
|
|||
namespace Payment.PaymentRequests |
|||
{ |
|||
[Serializable] |
|||
public class PaymentRequestDto : CreationAuditedEntityDto<Guid>, IHasExtraProperties |
|||
{ |
|||
public string CustomerId { get; set; } |
|||
|
|||
public string ProductId { get; set; } |
|||
|
|||
public string ProductName { get; set; } |
|||
|
|||
public decimal Price { get; set; } |
|||
|
|||
public string Currency { get; set; } |
|||
|
|||
public PaymentRequestState State { get; set; } |
|||
|
|||
public ExtraPropertyDictionary ExtraProperties { get; set; } |
|||
|
|||
public PaymentRequestDto() |
|||
{ |
|||
ExtraProperties = new ExtraPropertyDictionary(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using JetBrains.Annotations; |
|||
using System; |
|||
using System.ComponentModel.DataAnnotations; |
|||
|
|||
namespace Payment.PaymentRequests |
|||
{ |
|||
[Serializable] |
|||
public class StartPaymentDto |
|||
{ |
|||
public Guid PaymentRequestId { get; set; } |
|||
|
|||
[NotNull] |
|||
[Required] |
|||
public string ReturnUrl { get; set; } |
|||
|
|||
public string CancelUrl { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using System; |
|||
|
|||
namespace Payment.PaymentRequests |
|||
{ |
|||
[Serializable] |
|||
public class StartPaymentResultDto |
|||
{ |
|||
public string CheckoutLink { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,173 @@ |
|||
using Microsoft.Extensions.Options; |
|||
using Newtonsoft.Json.Linq; |
|||
using Payment.PayPal; |
|||
using PayPalCheckoutSdk.Core; |
|||
using PayPalCheckoutSdk.Orders; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.ObjectExtending; |
|||
|
|||
namespace Payment.PaymentRequests |
|||
{ |
|||
public class PaymentRequestAppService : PaymentAppService, IPaymentRequestAppService |
|||
{ |
|||
protected readonly IPaymentRequestRepository _paymentRequestRepository; |
|||
protected readonly PaymentOptions _paymentOptions; |
|||
protected readonly PayPalHttpClient _payPalHttpClient; |
|||
|
|||
public PaymentRequestAppService( |
|||
IPaymentRequestRepository paymentRequestRepository, |
|||
IOptions<PaymentOptions> paymentOptions, |
|||
PayPalHttpClient payPalHttpClient) |
|||
{ |
|||
_paymentRequestRepository = paymentRequestRepository; |
|||
_paymentOptions = paymentOptions.Value; |
|||
_payPalHttpClient = payPalHttpClient; |
|||
} |
|||
|
|||
public async Task<PaymentRequestDto> GetAsync(Guid id) |
|||
{ |
|||
var paymentRequest = await _paymentRequestRepository.GetAsync(id); |
|||
|
|||
return ObjectMapper.Map<PaymentRequest, PaymentRequestDto>(paymentRequest); |
|||
} |
|||
|
|||
public async Task<PaymentRequestDto> CreateAsync(PaymentRequestCreationDto input) |
|||
{ |
|||
var paymentRequest = new PaymentRequest( |
|||
GuidGenerator.Create(), |
|||
input.CustomerId, |
|||
input.ProductId, |
|||
input.ProductName, |
|||
input.Price, |
|||
input.Currency ?? _paymentOptions.DefaultCurrency |
|||
); |
|||
|
|||
foreach (var extraProperty in input.ExtraProperties) |
|||
{ |
|||
paymentRequest.SetProperty(extraProperty.Key, extraProperty.Value); |
|||
} |
|||
|
|||
await _paymentRequestRepository.InsertAsync(paymentRequest); |
|||
|
|||
return ObjectMapper.Map<PaymentRequest, PaymentRequestDto>(paymentRequest); |
|||
} |
|||
|
|||
public async Task<StartPaymentResultDto> StartPaymentAsync(StartPaymentDto input) |
|||
{ |
|||
var paymentRequest = await _paymentRequestRepository.GetAsync(input.PaymentRequestId); |
|||
|
|||
var order = new OrderRequest |
|||
{ |
|||
CheckoutPaymentIntent = "CAPTURE", |
|||
ApplicationContext = new ApplicationContext |
|||
{ |
|||
ReturnUrl = input.ReturnUrl, |
|||
CancelUrl = input.CancelUrl, |
|||
}, |
|||
PurchaseUnits = new List<PurchaseUnitRequest> |
|||
{ |
|||
new PurchaseUnitRequest |
|||
{ |
|||
AmountWithBreakdown = new AmountWithBreakdown |
|||
{ |
|||
AmountBreakdown = new AmountBreakdown |
|||
{ |
|||
ItemTotal = new Money |
|||
{ |
|||
CurrencyCode = paymentRequest.Currency, |
|||
Value = paymentRequest.Price.ToString(".00") |
|||
} |
|||
}, |
|||
CurrencyCode = paymentRequest.Currency, |
|||
Value = paymentRequest.Price.ToString(".00"), |
|||
}, |
|||
Items = new List<Item> |
|||
{ |
|||
new Item |
|||
{ |
|||
Quantity = 1.ToString(), |
|||
Name = paymentRequest.ProductName, |
|||
UnitAmount = new Money |
|||
{ |
|||
CurrencyCode = paymentRequest.Currency, |
|||
Value = paymentRequest.Price.ToString(".00") |
|||
} |
|||
} |
|||
}, |
|||
ReferenceId = paymentRequest.Id.ToString() |
|||
} |
|||
} |
|||
}; |
|||
|
|||
var request = new OrdersCreateRequest(); |
|||
request.Prefer("return=representation"); |
|||
request.RequestBody(order); |
|||
|
|||
var result = (await _payPalHttpClient.Execute(request)).Result<Order>(); |
|||
|
|||
return new StartPaymentResultDto |
|||
{ |
|||
CheckoutLink = result.Links.First(x => x.Rel == "approve").Href |
|||
}; |
|||
} |
|||
|
|||
public async Task<PaymentRequestDto> CompleteAsync(string token) |
|||
{ |
|||
var request = new OrdersCaptureRequest(token); |
|||
request.RequestBody(new OrderActionRequest()); |
|||
|
|||
var order = (await _payPalHttpClient.Execute(request)).Result<Order>(); |
|||
|
|||
var paymentRequest = await UpdatePaymentRequestStateAsync(order); |
|||
|
|||
return ObjectMapper.Map<PaymentRequest, PaymentRequestDto>(paymentRequest); |
|||
} |
|||
|
|||
public async Task<bool> HandleWebhookAsync(string payload) |
|||
{ |
|||
var jObject = JObject.Parse(payload); |
|||
|
|||
//// TODO: Find way to parse with System.Text.Json instead of Newtonsoft
|
|||
var order = jObject["resource"].ToObject<Order>(); |
|||
|
|||
var request = new OrdersGetRequest(order.Id); |
|||
|
|||
// Ensure order object comes from PayPal
|
|||
var response = await _payPalHttpClient.Execute(request); |
|||
order = response.Result<Order>(); |
|||
|
|||
await UpdatePaymentRequestStateAsync(order); |
|||
|
|||
// PayPal doesn't accept Http 204 (NoContent) result and tries to execute webhook again.
|
|||
// So with following value, API returns Http 200 (OK) result.
|
|||
return true; |
|||
} |
|||
|
|||
private async Task<PaymentRequest> UpdatePaymentRequestStateAsync(Order order) |
|||
{ |
|||
var paymentRequestId = Guid.Parse(order.PurchaseUnits.First().ReferenceId); |
|||
|
|||
var paymentRequest = await _paymentRequestRepository.GetAsync(paymentRequestId); |
|||
|
|||
if (order.Status is PayPalConsts.OrderStatus.Completed or PayPalConsts.OrderStatus.Approved) |
|||
{ |
|||
paymentRequest.SetAsCompleted(); |
|||
} |
|||
else |
|||
{ |
|||
paymentRequest.SetAsFailed(order.Status); |
|||
} |
|||
|
|||
paymentRequest.ExtraProperties[PayPalConsts.OrderIdPropertyName] = order.Id; |
|||
paymentRequest.ExtraProperties[nameof(order.Status)] = order.Status; |
|||
|
|||
await _paymentRequestRepository.UpdateAsync(paymentRequest); |
|||
|
|||
return paymentRequest; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,15 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\common.props" /> |
|||
<Import Project="..\..\configureawait.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace>Payment</RootNamespace> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Payment.Domain\Payment.Domain.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,15 @@ |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Payment |
|||
{ |
|||
[DependsOn( |
|||
typeof(PaymentDomainModule) |
|||
)] |
|||
public class PaymentBackgroundServicesModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
|
|||
} |
|||
} |
|||
} |
|||
@ -1,7 +1,36 @@ |
|||
|
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"MyAccount": "My account", |
|||
"SamplePageMessage": "A sample page for the Payment module" |
|||
"CompletePayment": "Complete the Payment", |
|||
"Enum:PaymentRequestState:Completed": "Completed", |
|||
"Enum:PaymentRequestState:Failed": "Failed", |
|||
"Enum:PaymentRequestState:Waiting": "Waiting", |
|||
"GoBack": "Go Back", |
|||
"MyAccount": "My account", |
|||
"PaymentRequestIsNotWaiting": "The state isn't 'Waiting', so you can't pay this Payment Request at this time.", |
|||
"PaymentResult": "Payment Result", |
|||
"Price": "Price", |
|||
"ProductInformation": "Product Information", |
|||
"ProductName": "Product Name", |
|||
"SamplePageMessage": "A sample page for the Payment module", |
|||
"Status": "State", |
|||
"YourPaymentCompletedSuccessfully": "Your payment has been completed successfully!", |
|||
"YourPaymentFailed": "Your payment has been failed unfortunetely!", |
|||
"Permission:Payment": "Payment", |
|||
"Menu:PaymentManagement": "Payment", |
|||
"PaymentRequests": "Payment Requests", |
|||
"PaymentRequestState:0": "Waiting", |
|||
"PaymentRequestState:1": "Completed", |
|||
"PaymentRequestState:2": "Failed", |
|||
"MinCreationTime": "Min Creation Time", |
|||
"MaxCreationTime": "Max Creation Time", |
|||
"CustomerId": "Customer Id", |
|||
"Currency": "Currency", |
|||
"State": "State", |
|||
"FailReason": "Fail Reason", |
|||
"CreationTime": "Creation Time", |
|||
"Permission:PaymentManagement": "Payment Management", |
|||
"Permission:PaymentRequest": "Payment Requests" |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
namespace Payment.PayPal |
|||
{ |
|||
public static class PayPalConsts |
|||
{ |
|||
public const string OrderIdPropertyName = "OrderId"; |
|||
|
|||
public static class OrderStatus |
|||
{ |
|||
/// <summary>
|
|||
/// The order was created with the specified context.
|
|||
/// </summary>
|
|||
public const string Created = "CREATED"; |
|||
|
|||
/// <summary>
|
|||
/// The order was saved and persisted. The order status continues to be in progress until a capture is made with final_capture = true for all purchase units within the order.
|
|||
/// </summary>
|
|||
public const string Saved = "SAVED"; |
|||
|
|||
/// <summary>
|
|||
/// The customer approved the payment through the PayPal wallet or another form of guest or unbranded payment. For example, a card, bank account, or so on.
|
|||
/// </summary>
|
|||
public const string Approved = "APPROVED"; |
|||
|
|||
/// <summary>
|
|||
/// All purchase units in the order are voided.
|
|||
/// </summary>
|
|||
public const string Voided = "VOIDED"; |
|||
|
|||
/// <summary>
|
|||
/// The payment was authorized or the authorized payment was captured for the order.
|
|||
/// </summary>
|
|||
public const string Completed = "COMPLETED"; |
|||
|
|||
/// <summary>
|
|||
/// The order requires an action from the payer (e.g. 3DS authentication). Redirect the payer to the "rel":"payer-action" HATEOAS link returned as part of the response prior to authorizing or capturing the order.
|
|||
/// </summary>
|
|||
public const string PlayerActionRequired = "PAYER_ACTION_REQUIRED"; |
|||
} |
|||
|
|||
public static class Environment |
|||
{ |
|||
public const string Sandbox = "Sandbox"; |
|||
|
|||
public const string Live = "Live"; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
using System; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.Domain.Entities.Events.Distributed; |
|||
using Volo.Abp.EventBus; |
|||
|
|||
namespace Payment.PaymentRequests |
|||
{ |
|||
[Serializable] |
|||
[EventName("Payment.Completed")] |
|||
public class PaymentRequestCompletedEto : EtoBase, IHasExtraProperties |
|||
{ |
|||
public Guid PaymentRequestId { get; set; } |
|||
|
|||
public ExtraPropertyDictionary ExtraProperties { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
namespace Payment.PaymentRequests |
|||
{ |
|||
public static class PaymentRequestConsts |
|||
{ |
|||
public const int MaxCurrencyLength = 100; |
|||
public static int MaxCustomerIdLength { get; set; } = 100; |
|||
public static int MaxProductIdLength { get; set; } = 100; |
|||
public static int MaxProductNameLength { get; set; } = 200; |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using System; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.Domain.Entities.Events.Distributed; |
|||
using Volo.Abp.EventBus; |
|||
|
|||
namespace Payment.PaymentRequests |
|||
{ |
|||
[Serializable] |
|||
[EventName("Payment.Failed")] |
|||
public class PaymentRequestFailedEto : EtoBase, IHasExtraProperties |
|||
{ |
|||
public Guid PaymentRequestId { get; set; } |
|||
|
|||
public string FailReason { get; set; } |
|||
|
|||
public ExtraPropertyDictionary ExtraProperties { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
namespace Payment.PaymentRequests |
|||
{ |
|||
public enum PaymentRequestState : byte |
|||
{ |
|||
Waiting = 0, |
|||
Completed, |
|||
Failed |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Payment.PayPal |
|||
{ |
|||
public class PayPalOptions |
|||
{ |
|||
public string ClientId { get; set; } |
|||
|
|||
public string Secret { get; set; } |
|||
|
|||
public string Locale { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// "Sandbox" or "Live". Default value is "Sandbox"
|
|||
/// </summary>
|
|||
public string Environment { get; set; } = PayPalConsts.Environment.Sandbox; |
|||
|
|||
public bool Recommended { get; set; } |
|||
|
|||
public List<string> ExtraInfos { get; set; } = new(); |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Payment |
|||
{ |
|||
public class PaymentOptions |
|||
{ |
|||
public string DefaultCurrency { get; set; } = "USD"; |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories; |
|||
|
|||
namespace Payment.PaymentRequests |
|||
{ |
|||
public interface IPaymentRequestRepository : IRepository<PaymentRequest, Guid> |
|||
{ |
|||
Task<List<PaymentRequest>> GetListAsync( |
|||
int skipCount, |
|||
int maxResultCount, |
|||
string sorting, |
|||
string productName = null, |
|||
DateTime? maxCreationTime = null, |
|||
DateTime? minCreationTime = null, |
|||
PaymentRequestState? state = null, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
Task<int> GetCountAsync( |
|||
string productName = null, |
|||
DateTime? maxCreationTime = null, |
|||
DateTime? minCreationTime = null, |
|||
PaymentRequestState? state = null, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,87 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Domain.Entities.Auditing; |
|||
|
|||
namespace Payment.PaymentRequests |
|||
{ |
|||
public class PaymentRequest : CreationAuditedAggregateRoot<Guid>, ISoftDelete |
|||
{ |
|||
[CanBeNull] |
|||
public string CustomerId { get; protected set; } |
|||
|
|||
[CanBeNull] |
|||
public string ProductId { get; protected set; } |
|||
|
|||
[NotNull] |
|||
public string ProductName { get; protected set; } |
|||
|
|||
public decimal Price { get; protected set; } |
|||
|
|||
public string Currency { get; protected set; } |
|||
|
|||
public PaymentRequestState State { get; protected set; } |
|||
|
|||
public bool IsDeleted { get; set; } |
|||
|
|||
[CanBeNull] |
|||
public string FailReason { get; protected set; } |
|||
|
|||
private PaymentRequest() |
|||
{ |
|||
|
|||
} |
|||
|
|||
public PaymentRequest( |
|||
Guid id, |
|||
[CanBeNull] string customerId, |
|||
[CanBeNull] string productId, |
|||
[NotNull] string productName, |
|||
decimal price, |
|||
[NotNull] string currency) |
|||
: base(id) |
|||
{ |
|||
CustomerId = Check.NotNullOrEmpty(customerId, nameof(customerId), maxLength: PaymentRequestConsts.MaxCustomerIdLength); |
|||
ProductId = Check.NotNullOrEmpty(productId, nameof(productId), maxLength: PaymentRequestConsts.MaxProductIdLength); |
|||
ProductName = Check.NotNullOrWhiteSpace(productName, nameof(productName), maxLength: PaymentRequestConsts.MaxProductNameLength); |
|||
Price = price; |
|||
Currency = currency; |
|||
} |
|||
|
|||
public virtual void SetAsCompleted() |
|||
{ |
|||
if (State == PaymentRequestState.Completed) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
State = PaymentRequestState.Completed; |
|||
FailReason = null; |
|||
|
|||
AddDistributedEvent(new PaymentRequestCompletedEto |
|||
{ |
|||
PaymentRequestId = Id, |
|||
ExtraProperties = ExtraProperties |
|||
}); |
|||
} |
|||
|
|||
public virtual void SetAsFailed(string failReason) |
|||
{ |
|||
if (State == PaymentRequestState.Failed) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
State = PaymentRequestState.Failed; |
|||
FailReason = failReason; |
|||
|
|||
AddDistributedEvent(new PaymentRequestFailedEto |
|||
{ |
|||
PaymentRequestId = Id, |
|||
FailReason = failReason, |
|||
ExtraProperties = ExtraProperties |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Collections.Generic; |
|||
using System.Linq.Dynamic.Core; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Payment.PaymentRequests; |
|||
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
|
|||
namespace Payment.EntityFrameworkCore.Repositories |
|||
{ |
|||
public class PaymentRequestRepository : EfCoreRepository<IPaymentDbContext, PaymentRequest, Guid>, IPaymentRequestRepository |
|||
{ |
|||
public PaymentRequestRepository(IDbContextProvider<IPaymentDbContext> dbContextProvider) |
|||
: base(dbContextProvider) |
|||
{ |
|||
} |
|||
|
|||
public async Task<List<PaymentRequest>> GetListAsync( |
|||
int skipCount, |
|||
int maxResultCount, |
|||
string sorting, |
|||
string productName = null, |
|||
DateTime? maxCreationTime = null, |
|||
DateTime? minCreationTime = null, |
|||
PaymentRequestState? state = null, |
|||
CancellationToken cancellationToken = default |
|||
) |
|||
{ |
|||
return await (await GetQueryableAsync()) |
|||
.WhereIf(!string.IsNullOrWhiteSpace(productName), x => x.ProductName.Contains(productName)) |
|||
.WhereIf(maxCreationTime != null, p => p.CreationTime <= maxCreationTime) |
|||
.WhereIf(minCreationTime != null, p => p.CreationTime >= minCreationTime) |
|||
.WhereIf(state != null, p => p.State == state) |
|||
.OrderBy(string.IsNullOrWhiteSpace(sorting) ? nameof(PaymentRequest.ProductName) : sorting) |
|||
.PageBy(skipCount, maxResultCount) |
|||
.ToListAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public async Task<int> GetCountAsync( |
|||
string productName = null, |
|||
DateTime? maxCreationTime = null, |
|||
DateTime? minCreationTime = null, |
|||
PaymentRequestState? state = null, |
|||
CancellationToken cancellationToken = default |
|||
) |
|||
{ |
|||
return await (await GetQueryableAsync()) |
|||
.WhereIf(!string.IsNullOrWhiteSpace(productName), x => x.ProductName.Contains(productName)) |
|||
.WhereIf(maxCreationTime != null, p => p.CreationTime <= maxCreationTime) |
|||
.WhereIf(minCreationTime != null, p => p.CreationTime >= minCreationTime) |
|||
.WhereIf(state != null, p => p.State == state) |
|||
.CountAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,208 @@ |
|||
{ |
|||
"modules": { |
|||
"payment": { |
|||
"rootPath": "payment", |
|||
"remoteServiceName": "Payment", |
|||
"controllers": { |
|||
"Payment.PaymentRequests.PaymentRequestController": { |
|||
"controllerName": "PaymentRequest", |
|||
"controllerGroupName": "PaymentRequest", |
|||
"type": "Payment.PaymentRequests.PaymentRequestController", |
|||
"interfaces": [ |
|||
{ |
|||
"type": "Payment.PaymentRequests.IPaymentRequestAppService" |
|||
} |
|||
], |
|||
"actions": { |
|||
"CompleteAsyncByToken": { |
|||
"uniqueName": "CompleteAsyncByToken", |
|||
"name": "CompleteAsync", |
|||
"httpMethod": "POST", |
|||
"url": "api/payment/requests/complete/{token}", |
|||
"supportedVersions": [], |
|||
"parametersOnMethod": [ |
|||
{ |
|||
"name": "token", |
|||
"typeAsString": "System.String, System.Private.CoreLib", |
|||
"type": "System.String", |
|||
"typeSimple": "string", |
|||
"isOptional": false, |
|||
"defaultValue": null |
|||
} |
|||
], |
|||
"parameters": [ |
|||
{ |
|||
"nameOnMethod": "token", |
|||
"name": "token", |
|||
"jsonName": null, |
|||
"type": "System.String", |
|||
"typeSimple": "string", |
|||
"isOptional": false, |
|||
"defaultValue": null, |
|||
"constraintTypes": [], |
|||
"bindingSourceId": "Path", |
|||
"descriptorName": "" |
|||
} |
|||
], |
|||
"returnValue": { |
|||
"type": "Payment.PaymentRequests.PaymentRequestDto", |
|||
"typeSimple": "Payment.PaymentRequests.PaymentRequestDto" |
|||
}, |
|||
"allowAnonymous": null, |
|||
"implementFrom": "Payment.PaymentRequests.IPaymentRequestAppService" |
|||
}, |
|||
"CreateAsyncByInput": { |
|||
"uniqueName": "CreateAsyncByInput", |
|||
"name": "CreateAsync", |
|||
"httpMethod": "POST", |
|||
"url": "api/payment/requests", |
|||
"supportedVersions": [], |
|||
"parametersOnMethod": [ |
|||
{ |
|||
"name": "input", |
|||
"typeAsString": "Payment.PaymentRequests.PaymentRequestCreationDto, Payment.Application.Contracts", |
|||
"type": "Payment.PaymentRequests.PaymentRequestCreationDto", |
|||
"typeSimple": "Payment.PaymentRequests.PaymentRequestCreationDto", |
|||
"isOptional": false, |
|||
"defaultValue": null |
|||
} |
|||
], |
|||
"parameters": [ |
|||
{ |
|||
"nameOnMethod": "input", |
|||
"name": "input", |
|||
"jsonName": null, |
|||
"type": "Payment.PaymentRequests.PaymentRequestCreationDto", |
|||
"typeSimple": "Payment.PaymentRequests.PaymentRequestCreationDto", |
|||
"isOptional": false, |
|||
"defaultValue": null, |
|||
"constraintTypes": null, |
|||
"bindingSourceId": "Body", |
|||
"descriptorName": "" |
|||
} |
|||
], |
|||
"returnValue": { |
|||
"type": "Payment.PaymentRequests.PaymentRequestDto", |
|||
"typeSimple": "Payment.PaymentRequests.PaymentRequestDto" |
|||
}, |
|||
"allowAnonymous": null, |
|||
"implementFrom": "Payment.PaymentRequests.IPaymentRequestAppService" |
|||
}, |
|||
"GetAsyncById": { |
|||
"uniqueName": "GetAsyncById", |
|||
"name": "GetAsync", |
|||
"httpMethod": "GET", |
|||
"url": "api/payment/requests/{id}", |
|||
"supportedVersions": [], |
|||
"parametersOnMethod": [ |
|||
{ |
|||
"name": "id", |
|||
"typeAsString": "System.Guid, System.Private.CoreLib", |
|||
"type": "System.Guid", |
|||
"typeSimple": "string", |
|||
"isOptional": false, |
|||
"defaultValue": null |
|||
} |
|||
], |
|||
"parameters": [ |
|||
{ |
|||
"nameOnMethod": "id", |
|||
"name": "id", |
|||
"jsonName": null, |
|||
"type": "System.Guid", |
|||
"typeSimple": "string", |
|||
"isOptional": false, |
|||
"defaultValue": null, |
|||
"constraintTypes": [], |
|||
"bindingSourceId": "Path", |
|||
"descriptorName": "" |
|||
} |
|||
], |
|||
"returnValue": { |
|||
"type": "Payment.PaymentRequests.PaymentRequestDto", |
|||
"typeSimple": "Payment.PaymentRequests.PaymentRequestDto" |
|||
}, |
|||
"allowAnonymous": null, |
|||
"implementFrom": "Payment.PaymentRequests.IPaymentRequestAppService" |
|||
}, |
|||
"StartPaymentAsyncByInput": { |
|||
"uniqueName": "StartPaymentAsyncByInput", |
|||
"name": "StartPaymentAsync", |
|||
"httpMethod": "POST", |
|||
"url": "api/payment/requests/start", |
|||
"supportedVersions": [], |
|||
"parametersOnMethod": [ |
|||
{ |
|||
"name": "input", |
|||
"typeAsString": "Payment.PaymentRequests.StartPaymentDto, Payment.Application.Contracts", |
|||
"type": "Payment.PaymentRequests.StartPaymentDto", |
|||
"typeSimple": "Payment.PaymentRequests.StartPaymentDto", |
|||
"isOptional": false, |
|||
"defaultValue": null |
|||
} |
|||
], |
|||
"parameters": [ |
|||
{ |
|||
"nameOnMethod": "input", |
|||
"name": "input", |
|||
"jsonName": null, |
|||
"type": "Payment.PaymentRequests.StartPaymentDto", |
|||
"typeSimple": "Payment.PaymentRequests.StartPaymentDto", |
|||
"isOptional": false, |
|||
"defaultValue": null, |
|||
"constraintTypes": null, |
|||
"bindingSourceId": "Body", |
|||
"descriptorName": "" |
|||
} |
|||
], |
|||
"returnValue": { |
|||
"type": "Payment.PaymentRequests.StartPaymentResultDto", |
|||
"typeSimple": "Payment.PaymentRequests.StartPaymentResultDto" |
|||
}, |
|||
"allowAnonymous": null, |
|||
"implementFrom": "Payment.PaymentRequests.IPaymentRequestAppService" |
|||
}, |
|||
"HandleWebhookAsyncByPayload": { |
|||
"uniqueName": "HandleWebhookAsyncByPayload", |
|||
"name": "HandleWebhookAsync", |
|||
"httpMethod": "POST", |
|||
"url": "api/payment/requests/webhook", |
|||
"supportedVersions": [], |
|||
"parametersOnMethod": [ |
|||
{ |
|||
"name": "payload", |
|||
"typeAsString": "System.String, System.Private.CoreLib", |
|||
"type": "System.String", |
|||
"typeSimple": "string", |
|||
"isOptional": false, |
|||
"defaultValue": null |
|||
} |
|||
], |
|||
"parameters": [ |
|||
{ |
|||
"nameOnMethod": "payload", |
|||
"name": "payload", |
|||
"jsonName": null, |
|||
"type": "System.String", |
|||
"typeSimple": "string", |
|||
"isOptional": false, |
|||
"defaultValue": null, |
|||
"constraintTypes": null, |
|||
"bindingSourceId": "ModelBinding", |
|||
"descriptorName": "" |
|||
} |
|||
], |
|||
"returnValue": { |
|||
"type": "System.Boolean", |
|||
"typeSimple": "boolean" |
|||
}, |
|||
"allowAnonymous": null, |
|||
"implementFrom": "Payment.PaymentRequests.IPaymentRequestAppService" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
"types": {} |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
// This file is automatically generated by ABP framework to use MVC Controllers from CSharp
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.Http.Client; |
|||
using Volo.Abp.Http.Modeling; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Http.Client.ClientProxying; |
|||
using Payment.PaymentRequests; |
|||
|
|||
// ReSharper disable once CheckNamespace
|
|||
namespace Payment.PaymentRequests.ClientProxies |
|||
{ |
|||
[Dependency(ReplaceServices = true)] |
|||
[ExposeServices(typeof(IPaymentRequestAppService), typeof(PaymentRequestClientProxy))] |
|||
public partial class PaymentRequestClientProxy : ClientProxyBase<IPaymentRequestAppService>, IPaymentRequestAppService |
|||
{ |
|||
public virtual async Task<PaymentRequestDto> CompleteAsync(string token) |
|||
{ |
|||
return await RequestAsync<PaymentRequestDto>(nameof(CompleteAsync), new ClientProxyRequestTypeValue |
|||
{ |
|||
{ typeof(string), token } |
|||
}); |
|||
} |
|||
|
|||
public virtual async Task<PaymentRequestDto> CreateAsync(PaymentRequestCreationDto input) |
|||
{ |
|||
return await RequestAsync<PaymentRequestDto>(nameof(CreateAsync), new ClientProxyRequestTypeValue |
|||
{ |
|||
{ typeof(PaymentRequestCreationDto), input } |
|||
}); |
|||
} |
|||
|
|||
public virtual async Task<PaymentRequestDto> GetAsync(Guid id) |
|||
{ |
|||
return await RequestAsync<PaymentRequestDto>(nameof(GetAsync), new ClientProxyRequestTypeValue |
|||
{ |
|||
{ typeof(Guid), id } |
|||
}); |
|||
} |
|||
|
|||
public virtual async Task<StartPaymentResultDto> StartPaymentAsync(StartPaymentDto input) |
|||
{ |
|||
return await RequestAsync<StartPaymentResultDto>(nameof(StartPaymentAsync), new ClientProxyRequestTypeValue |
|||
{ |
|||
{ typeof(StartPaymentDto), input } |
|||
}); |
|||
} |
|||
|
|||
public virtual async Task<bool> HandleWebhookAsync(string payload) |
|||
{ |
|||
return await RequestAsync<bool>(nameof(HandleWebhookAsync), new ClientProxyRequestTypeValue |
|||
{ |
|||
{ typeof(string), payload } |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
// This file is part of PaymentRequestClientProxy, you can customize it here
|
|||
// ReSharper disable once CheckNamespace
|
|||
namespace Payment.PaymentRequests.ClientProxies |
|||
{ |
|||
public partial class PaymentRequestClientProxy |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
// This file is automatically generated by ABP framework to use MVC Controllers from CSharp
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.Http.Client; |
|||
using Volo.Abp.Http.Modeling; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Http.Client.ClientProxying; |
|||
using Payment.Samples; |
|||
|
|||
// ReSharper disable once CheckNamespace
|
|||
namespace Payment.Samples.ClientProxies |
|||
{ |
|||
[Dependency(ReplaceServices = true)] |
|||
[ExposeServices(typeof(ISampleAppService), typeof(SampleClientProxy))] |
|||
public partial class SampleClientProxy : ClientProxyBase<ISampleAppService>, ISampleAppService |
|||
{ |
|||
public virtual async Task<SampleDto> GetAsync() |
|||
{ |
|||
return await RequestAsync<SampleDto>(nameof(GetAsync)); |
|||
} |
|||
|
|||
public virtual async Task<SampleDto> GetAuthorizedAsync() |
|||
{ |
|||
return await RequestAsync<SampleDto>(nameof(GetAuthorizedAsync)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
// This file is part of SampleClientProxy, you can customize it here
|
|||
// ReSharper disable once CheckNamespace
|
|||
namespace Payment.Samples.ClientProxies |
|||
{ |
|||
public partial class SampleClientProxy |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,21 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\common.props" /> |
|||
<Import Project="..\..\configureawait.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace>Payment</RootNamespace> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Http.Client" Version="5.0.0-beta.1" /> |
|||
<ProjectReference Include="..\Payment.Application.Contracts\Payment.Application.Contracts.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<EmbeddedResource Include="**\*generate-proxy.json" /> |
|||
<Content Remove="**\*generate-proxy.json" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,27 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Http.Client; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace Payment |
|||
{ |
|||
[DependsOn( |
|||
typeof(PaymentApplicationContractsModule), |
|||
typeof(AbpHttpClientModule))] |
|||
public class PaymentHttpApiClientModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddStaticHttpClientProxies( |
|||
typeof(PaymentApplicationContractsModule).Assembly, |
|||
PaymentRemoteServiceConsts.RemoteServiceName |
|||
); |
|||
|
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<PaymentHttpApiClientModule>(); |
|||
}); |
|||
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Newtonsoft.Json.Linq; |
|||
using System; |
|||
using System.IO; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
|
|||
namespace Payment.PaymentRequests |
|||
{ |
|||
[RemoteService(Name = PaymentRemoteServiceConsts.RemoteServiceName)] |
|||
[Area("payment")] |
|||
[Route("api/payment/requests")] |
|||
public class PaymentRequestController : PaymentController, IPaymentRequestAppService |
|||
{ |
|||
protected IPaymentRequestAppService PaymentRequestAppService { get; } |
|||
|
|||
public PaymentRequestController(IPaymentRequestAppService paymentRequestAppService) |
|||
{ |
|||
PaymentRequestAppService = paymentRequestAppService; |
|||
} |
|||
|
|||
[HttpPost] |
|||
[Route("complete/{token}")] |
|||
public Task<PaymentRequestDto> CompleteAsync([FromRoute] string token) |
|||
{ |
|||
return PaymentRequestAppService.CompleteAsync(token); |
|||
} |
|||
|
|||
[HttpPost] |
|||
public Task<PaymentRequestDto> CreateAsync(PaymentRequestCreationDto input) |
|||
{ |
|||
return PaymentRequestAppService.CreateAsync(input); |
|||
} |
|||
|
|||
[HttpGet] |
|||
[Route("{id}")] |
|||
public Task<PaymentRequestDto> GetAsync(Guid id) |
|||
{ |
|||
return PaymentRequestAppService.GetAsync(id); |
|||
} |
|||
|
|||
[HttpPost] |
|||
[Route("start")] |
|||
public Task<StartPaymentResultDto> StartPaymentAsync(StartPaymentDto input) |
|||
{ |
|||
return PaymentRequestAppService.StartPaymentAsync(input); |
|||
} |
|||
|
|||
[ApiExplorerSettings(IgnoreApi = true)] |
|||
[HttpPost] |
|||
[Route("webhook")] |
|||
public async Task<bool> HandleWebhookAsync(string payload) |
|||
{ |
|||
var bytes = await Request.Body.GetAllBytesAsync(); |
|||
payload = Encoding.UTF8.GetString(bytes); |
|||
|
|||
return await PaymentRequestAppService.HandleWebhookAsync(payload); |
|||
} |
|||
} |
|||
} |
|||
@ -1,17 +0,0 @@ |
|||
@page |
|||
@using Microsoft.Extensions.Localization |
|||
@using Payment.Localization |
|||
@using Payment.Web.Pages.Payment |
|||
@model Payment.Web.Pages.Payment.IndexModel |
|||
@inject IStringLocalizer<PaymentResource> L |
|||
|
|||
@section scripts { |
|||
<abp-script-bundle name="@typeof(IndexModel).FullName"> |
|||
<abp-script src="/client-proxies/Payment-proxy.js" /> |
|||
</abp-script-bundle> |
|||
} |
|||
|
|||
@{ |
|||
} |
|||
<h1>Payment</h1> |
|||
<p>@L["SamplePageMessage"]</p> |
|||
@ -1,9 +0,0 @@ |
|||
namespace Payment.Web.Pages.Payment |
|||
{ |
|||
public class IndexModel : PaymentPageModel |
|||
{ |
|||
public void OnGet() |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
@page |
|||
@using Payment.Web.Pages.Payment |
|||
@using Payment.PaymentRequests |
|||
@inherits Payment.Web.Pages.PaymentPageBase |
|||
@model Payment.Web.Pages.Payment.PostCheckoutPageModel |
|||
@{ |
|||
PageLayout.Content.Title = L["PaymentResult"] + L["Enum:PaymentRequestState:" + Model.PaymentRequest.State]; |
|||
} |
|||
|
|||
<main class="static-page container mt-5"> |
|||
<abp-card class="m-5"> |
|||
<abp-card-header> |
|||
<h2>@L["PaymentResult"]</h2> |
|||
</abp-card-header> |
|||
|
|||
<abp-card-body> |
|||
|
|||
@if (Model.PaymentRequest.State == PaymentRequestState.Completed) |
|||
{ |
|||
<abp-alert alert-type="Success" dismissible="false"> |
|||
<h3 class="d-inline"> |
|||
<i class="fa fa-check-circle"></i> |
|||
@L["YourPaymentCompletedSuccessfully"] |
|||
</h3>> |
|||
</abp-alert> |
|||
} |
|||
else |
|||
{ |
|||
<abp-alert alert-type="Danger" dismissible="false"> |
|||
<h3 class="d-inline"> |
|||
<i class="fa fa-times"></i> |
|||
@L["YourPaymentFailed"] |
|||
</h3> |
|||
</abp-alert> |
|||
|
|||
<abp-button href="@Model.GoBackLink" text="@L["GoBack"]" /> |
|||
} |
|||
|
|||
</abp-card-body> |
|||
</abp-card> |
|||
</main> |
|||
@ -0,0 +1,61 @@ |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.Extensions.Options; |
|||
using Payment.PaymentRequests; |
|||
using Payment.Web.PaymentRequest; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Payment.Web.Pages.Payment |
|||
{ |
|||
public class PostCheckoutPageModel : PaymentPageModel |
|||
{ |
|||
[BindProperty(SupportsGet = true)] |
|||
public string Token { get; set; } |
|||
|
|||
public PaymentRequestDto PaymentRequest { get; protected set; } |
|||
|
|||
public string GoBackLink { get; set; } |
|||
|
|||
private readonly IPaymentRequestAppService _paymentRequestAppService; |
|||
private readonly PaymentWebOptions _paymentWebOptions; |
|||
private readonly IPaymentUrlBuilder _paymentUrlBuilder; |
|||
|
|||
public PostCheckoutPageModel( |
|||
IPaymentRequestAppService appService, |
|||
IOptions<PaymentWebOptions> paymentWebOptions, |
|||
IPaymentUrlBuilder paymentUrlBuilder) |
|||
{ |
|||
_paymentRequestAppService = appService; |
|||
_paymentWebOptions = paymentWebOptions.Value; |
|||
_paymentUrlBuilder = paymentUrlBuilder; |
|||
} |
|||
|
|||
public async Task<IActionResult> OnGetAsync() |
|||
{ |
|||
if (Token.IsNullOrWhiteSpace()) |
|||
{ |
|||
return BadRequest(); |
|||
} |
|||
|
|||
PaymentRequest = await _paymentRequestAppService.CompleteAsync(Token); |
|||
|
|||
GoBackLink = _paymentUrlBuilder.BuildCheckoutUrl(PaymentRequest.Id).AbsoluteUri; |
|||
|
|||
if (PaymentRequest.State == PaymentRequestState.Completed |
|||
&& !_paymentWebOptions.PaymentSuccessfulCallbackUrl.IsNullOrWhiteSpace()) |
|||
{ |
|||
var callbackUrl = _paymentWebOptions.PaymentSuccessfulCallbackUrl + "?paymentRequestId=" + PaymentRequest.Id; |
|||
Response.Redirect(callbackUrl); |
|||
} |
|||
|
|||
if (PaymentRequest.State == PaymentRequestState.Failed |
|||
&& !_paymentWebOptions.PaymentFailureCallbackUrl.IsNullOrWhiteSpace()) |
|||
{ |
|||
var callbackUrl = _paymentWebOptions.PaymentFailureCallbackUrl + "?paymentRequestId=" + PaymentRequest.Id; |
|||
Response.Redirect(callbackUrl); |
|||
} |
|||
|
|||
return Page(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
@page |
|||
@using Payment.Web.Pages.Payment |
|||
@using Payment.PaymentRequests |
|||
@inherits Payment.Web.Pages.PaymentPageBase |
|||
@model Payment.Web.Pages.Payment.PreCheckoutPageModel |
|||
|
|||
@section scripts { |
|||
<abp-script-bundle name="@typeof(PreCheckoutPageModel).FullName"> |
|||
<abp-script src="/client-proxies/Payment-proxy.js" /> |
|||
</abp-script-bundle> |
|||
} |
|||
|
|||
<main class="static-page container mt-5"> |
|||
<abp-card class="m-5"> |
|||
<abp-card-header> |
|||
<h2>@L["ProductInformation"]</h2> |
|||
</abp-card-header> |
|||
<abp-card-body> |
|||
<ul> |
|||
<li>@L["ProductName"]: @Model.PaymentRequest.ProductName</li> |
|||
<li>@L["Price"]: @Model.PaymentRequest.Price @Model.PaymentRequest.Currency</li> |
|||
<li>@L["State"]: @L["Enum:PaymentRequestState:" + Model.PaymentRequest.State]</li> |
|||
</ul> |
|||
</abp-card-body> |
|||
<abp-card-footer> |
|||
<form method="post"> |
|||
@if (Model.PaymentRequest.State != PaymentRequestState.Waiting) |
|||
{ |
|||
<abp-alert alert-type="Warning">@L["PaymentRequestIsNotWaiting"]</abp-alert> |
|||
} |
|||
else |
|||
{ |
|||
<abp-button type="submit" button-type="Primary">@L["CompletePayment"]</abp-button> |
|||
} |
|||
</form> |
|||
</abp-card-footer> |
|||
</abp-card> |
|||
</main> |
|||
@ -0,0 +1,49 @@ |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Payment.PaymentRequests; |
|||
using Payment.Web.PaymentRequest; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
|
|||
namespace Payment.Web.Pages.Payment |
|||
{ |
|||
public class PreCheckoutPageModel : PaymentPageModel |
|||
{ |
|||
[BindProperty(SupportsGet = true)] |
|||
public Guid PaymentRequestId { get; set; } |
|||
|
|||
public PaymentRequestDto PaymentRequest { get; set; } |
|||
|
|||
private readonly IPaymentRequestAppService _paymentRequestAppService; |
|||
private readonly IPaymentUrlBuilder _paymentUrlBuilder; |
|||
|
|||
public PreCheckoutPageModel( |
|||
IPaymentRequestAppService paymentRequestAppService, |
|||
IPaymentUrlBuilder paymentUrlBuilder) |
|||
{ |
|||
_paymentRequestAppService = paymentRequestAppService; |
|||
_paymentUrlBuilder = paymentUrlBuilder; |
|||
} |
|||
|
|||
public async Task<IActionResult> OnGetAsync() |
|||
{ |
|||
PaymentRequest = await _paymentRequestAppService.GetAsync(PaymentRequestId); |
|||
|
|||
return Page(); |
|||
} |
|||
|
|||
public async Task OnPostAsync() |
|||
{ |
|||
PaymentRequest = await _paymentRequestAppService.GetAsync(PaymentRequestId); |
|||
|
|||
var result = await _paymentRequestAppService.StartPaymentAsync(new StartPaymentDto |
|||
{ |
|||
PaymentRequestId = PaymentRequest.Id, |
|||
ReturnUrl = _paymentUrlBuilder.BuildReturnUrl(PaymentRequestId).AbsoluteUri, |
|||
CancelUrl = _paymentUrlBuilder.BuildCheckoutUrl(PaymentRequestId).AbsoluteUri |
|||
}); |
|||
|
|||
Response.Redirect(result.CheckoutLink); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using Microsoft.AspNetCore.Mvc.Razor.Internal; |
|||
using Microsoft.Extensions.Localization; |
|||
using Payment.Localization; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Layout; |
|||
|
|||
namespace Payment.Web.Pages |
|||
{ |
|||
public class PaymentPageBase : Microsoft.AspNetCore.Mvc.RazorPages.Page |
|||
{ |
|||
[RazorInject] public IStringLocalizer<PaymentResource> L { get; set; } |
|||
|
|||
[RazorInject] public IPageLayout PageLayout { get; set; } |
|||
|
|||
public override Task ExecuteAsync() |
|||
{ |
|||
return Task.CompletedTask; // Will be overriden by razor pages. (.cshtml)
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using System; |
|||
|
|||
namespace Payment.Web.PaymentRequest |
|||
{ |
|||
public interface IPaymentUrlBuilder |
|||
{ |
|||
Uri BuildCheckoutUrl(Guid paymentRequestId); |
|||
|
|||
Uri BuildReturnUrl(Guid paymentRequestId); |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue