diff --git a/services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentRequests/IPaymentRequestAppService.cs b/services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentRequests/IPaymentRequestAppService.cs new file mode 100644 index 00000000..cf3e8f40 --- /dev/null +++ b/services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentRequests/IPaymentRequestAppService.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; +using Volo.Abp.Application.Services; + +namespace EShopOnAbp.PaymentService.PaymentRequests +{ + public interface IPaymentRequestAppService : IApplicationService + { + Task CreateAsync(PaymentRequestCreationDto input); + + Task StartAsync(PaymentRequestStartDto input); + + Task CompleteAsync(string token); + + Task HandleWebhookAsync(string payload); + } +} diff --git a/services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentRequests/PaymentRequestCreationDto.cs b/services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentRequests/PaymentRequestCreationDto.cs new file mode 100644 index 00000000..c5e0c328 --- /dev/null +++ b/services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentRequests/PaymentRequestCreationDto.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace EShopOnAbp.PaymentService.PaymentRequests +{ + [Serializable] + public class PaymentRequestCreationDto + { + [Required] + [MaxLength(PaymentRequestConsts.MaxCurrencyLength)] + public string Currency { get; set; } + + public string BuyerId { get; set; } + + public List Products { get; set; } + } +} \ No newline at end of file diff --git a/services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentRequests/PaymentRequestDto.cs b/services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentRequests/PaymentRequestDto.cs new file mode 100644 index 00000000..58c46b5a --- /dev/null +++ b/services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentRequests/PaymentRequestDto.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Volo.Abp.Application.Dtos; + +namespace EShopOnAbp.PaymentService.PaymentRequests +{ + [Serializable] + public class PaymentRequestDto : CreationAuditedEntityDto + { + [Required] + [MaxLength(PaymentRequestConsts.MaxCurrencyLength)] + public string Currency { get; set; } + + public string BuyerId { get; set; } + + public bool IsDeleted { get; set; } + + public PaymentRequestState State { get; set; } + + public List Products { get; set; } + } +} diff --git a/services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentRequests/PaymentRequestProduct.cs b/services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentRequests/PaymentRequestProduct.cs new file mode 100644 index 00000000..5af7d451 --- /dev/null +++ b/services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentRequests/PaymentRequestProduct.cs @@ -0,0 +1,22 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace EShopOnAbp.PaymentService.PaymentRequests +{ + [Serializable] + public class PaymentRequestProductDto : EntityDto + { + public Guid PaymentRequestId { get; private set; } + + public string ReferenceId { get; set; } + + public string Name { get; set; } + + public decimal UnitPrice { get; set; } + + public int Quantity { get; set; } + + public decimal TotalPrice { get; set; } + + } +} \ No newline at end of file diff --git a/services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentRequests/PaymentRequestProductCreationDto.cs b/services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentRequests/PaymentRequestProductCreationDto.cs new file mode 100644 index 00000000..6df979b4 --- /dev/null +++ b/services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentRequests/PaymentRequestProductCreationDto.cs @@ -0,0 +1,18 @@ +using System; + +namespace EShopOnAbp.PaymentService +{ + [Serializable] + public class PaymentRequestProductCreationDto + { + public string ReferenceId { get; set; } + + public string Name { get; set; } + + public decimal UnitPrice { get; set; } + + public int Quantity { get; set; } + + public decimal TotalPrice { get; set; } + } +} \ No newline at end of file diff --git a/services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentRequests/PaymentRequestStartDto.cs b/services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentRequests/PaymentRequestStartDto.cs new file mode 100644 index 00000000..44b39fdb --- /dev/null +++ b/services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentRequests/PaymentRequestStartDto.cs @@ -0,0 +1,16 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace EShopOnAbp.PaymentService.PaymentRequests +{ + [Serializable] + public class PaymentRequestStartDto + { + public Guid PaymentRequestId { get; set; } + + [Required] + public string ReturnUrl { get; set; } + + public string CancelUrl { get; set; } + } +} \ No newline at end of file diff --git a/services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentRequests/PaymentRequestStartResultDto.cs b/services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentRequests/PaymentRequestStartResultDto.cs new file mode 100644 index 00000000..faae6889 --- /dev/null +++ b/services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentRequests/PaymentRequestStartResultDto.cs @@ -0,0 +1,10 @@ +using System; + +namespace EShopOnAbp.PaymentService.PaymentRequests +{ + [Serializable] + public class PaymentRequestStartResultDto + { + public string CheckoutLink { get; set; } + } +} \ No newline at end of file diff --git a/services/payment/src/EShopOnAbp.PaymentService.Application/EShopOnAbp.PaymentService.Application.csproj b/services/payment/src/EShopOnAbp.PaymentService.Application/EShopOnAbp.PaymentService.Application.csproj index 87c15c28..730ad0d1 100644 --- a/services/payment/src/EShopOnAbp.PaymentService.Application/EShopOnAbp.PaymentService.Application.csproj +++ b/services/payment/src/EShopOnAbp.PaymentService.Application/EShopOnAbp.PaymentService.Application.csproj @@ -8,6 +8,7 @@ + diff --git a/services/payment/src/EShopOnAbp.PaymentService.Application/PayPal/PayPalConsts.cs b/services/payment/src/EShopOnAbp.PaymentService.Application/PayPal/PayPalConsts.cs new file mode 100644 index 00000000..0e83292c --- /dev/null +++ b/services/payment/src/EShopOnAbp.PaymentService.Application/PayPal/PayPalConsts.cs @@ -0,0 +1,47 @@ +namespace EShopOnAbp.PaymentService.PayPal +{ + public static class PayPalConsts + { + public const string OrderIdPropertyName = "OrderId"; + + public static class OrderStatus + { + /// + /// The order was created with the specified context. + /// + public const string Created = "CREATED"; + + /// + /// 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. + /// + public const string Saved = "SAVED"; + + /// + /// 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. + /// + public const string Approved = "APPROVED"; + + /// + /// All purchase units in the order are voided. + /// + public const string Voided = "VOIDED"; + + /// + /// The payment was authorized or the authorized payment was captured for the order. + /// + public const string Completed = "COMPLETED"; + + /// + /// 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. + /// + public const string PlayerActionRequired = "PAYER_ACTION_REQUIRED"; + } + + public static class Environment + { + public const string Sandbox = "Sandbox"; + + public const string Live = "Live"; + } + } +} diff --git a/services/payment/src/EShopOnAbp.PaymentService.Application/PayPal/PayPalOptions.cs b/services/payment/src/EShopOnAbp.PaymentService.Application/PayPal/PayPalOptions.cs new file mode 100644 index 00000000..4c3d872b --- /dev/null +++ b/services/payment/src/EShopOnAbp.PaymentService.Application/PayPal/PayPalOptions.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace EShopOnAbp.PaymentService.PayPal +{ + public class PayPalOptions + { + public string ClientId { get; set; } + + public string Secret { get; set; } + + public string Locale { get; set; } + + /// + /// "Sandbox" or "Live". Default value is "Sandbox" + /// + public string Environment { get; set; } = PayPalConsts.Environment.Sandbox; + + public bool Recommended { get; set; } + + public List ExtraInfos { get; set; } = new(); + } +} diff --git a/services/payment/src/EShopOnAbp.PaymentService.Application/PaymentRequests/PaymentRequestAppService.cs b/services/payment/src/EShopOnAbp.PaymentService.Application/PaymentRequests/PaymentRequestAppService.cs new file mode 100644 index 00000000..4e4cfc9d --- /dev/null +++ b/services/payment/src/EShopOnAbp.PaymentService.Application/PaymentRequests/PaymentRequestAppService.cs @@ -0,0 +1,156 @@ +using EShopOnAbp.PaymentService.PayPal; +using Newtonsoft.Json.Linq; +using PayPalCheckoutSdk.Core; +using PayPalCheckoutSdk.Orders; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace EShopOnAbp.PaymentService.PaymentRequests +{ + public class PaymentRequestAppService : PaymentServiceAppService, IPaymentRequestAppService + { + protected IPaymentRequestRepository PaymentRequestRepository { get; } + protected PayPalHttpClient PayPalHttpClient { get; } + + public PaymentRequestAppService( + IPaymentRequestRepository paymentRequestRepository, + PayPalHttpClient payPalHttpClient) + { + PaymentRequestRepository = paymentRequestRepository; + PayPalHttpClient = payPalHttpClient; + } + + public async Task CreateAsync(PaymentRequestCreationDto input) + { + var paymentRequest = new PaymentRequest(GuidGenerator.Create(), input.Currency, input.BuyerId); + + foreach (var paymentRequestProduct in input.Products + .Select(s => new PaymentRequestProduct( + paymentRequest.Id, + s.Name, + s.UnitPrice, + s.Quantity, + s.TotalPrice, + s.ReferenceId))) + { + paymentRequest.Products.Add(paymentRequestProduct); + } + + return ObjectMapper.Map(paymentRequest); + } + + public async Task StartAsync(PaymentRequestStartDto input) + { + var paymentRequest = await PaymentRequestRepository.GetAsync(input.PaymentRequestId); + + var totalCheckoutPrice = paymentRequest.Products.Sum(s => s.TotalPrice); + + var order = new OrderRequest + { + CheckoutPaymentIntent = "CAPTURE", + ApplicationContext = new ApplicationContext + { + ReturnUrl = input.ReturnUrl, + CancelUrl = input.CancelUrl, + }, + PurchaseUnits = new List + { + new PurchaseUnitRequest + { + AmountWithBreakdown = new AmountWithBreakdown + { + AmountBreakdown = new AmountBreakdown + { + ItemTotal = new Money + { + CurrencyCode = paymentRequest.Currency, + Value = totalCheckoutPrice.ToString(".00") + } + }, + CurrencyCode = paymentRequest.Currency, + Value = totalCheckoutPrice.ToString(".00"), + }, + Items = paymentRequest.Products.Select(p => new Item + { + Quantity = p.Quantity.ToString(), + Name = p.Name, + UnitAmount = new Money + { + CurrencyCode = paymentRequest.Currency, + Value = p.UnitPrice.ToString(".00") + } + }).ToList(), + ReferenceId = paymentRequest.Id.ToString() + } + } + }; + + var request = new OrdersCreateRequest(); + request.Prefer("return=representation"); + request.RequestBody(order); + + var result = (await PayPalHttpClient.Execute(request)).Result(); + + return new PaymentRequestStartResultDto + { + CheckoutLink = result.Links.First(x => x.Rel == "approve").Href + }; + } + + public async Task CompleteAsync(string token) + { + var request = new OrdersCaptureRequest(token); + request.RequestBody(new OrderActionRequest()); + + var order = (await PayPalHttpClient.Execute(request)).Result(); + + var paymentRequest = await UpdatePaymentRequestStateAsync(order); + + return ObjectMapper.Map(paymentRequest); + } + + public async Task HandleWebhookAsync(string payload) + { + var jObject = JObject.Parse(payload); + + var order = jObject["resource"].ToObject(); + + var request = new OrdersGetRequest(order.Id); + + // Ensure order object comes from PayPal + var response = await PayPalHttpClient.Execute(request); + order = response.Result(); + + 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 UpdatePaymentRequestStateAsync(Order order) + { + var paymentRequestId = Guid.Parse(order.PurchaseUnits.First().ReferenceId); + + var paymentRequest = await PaymentRequestRepository.GetAsync(paymentRequestId); + + if (order.Status == PayPalConsts.OrderStatus.Completed || order.Status == 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; + } + } +} diff --git a/services/payment/src/EShopOnAbp.PaymentService.Application/PaymentServiceApplicationModule.cs b/services/payment/src/EShopOnAbp.PaymentService.Application/PaymentServiceApplicationModule.cs index 6bbac237..024b3ba5 100644 --- a/services/payment/src/EShopOnAbp.PaymentService.Application/PaymentServiceApplicationModule.cs +++ b/services/payment/src/EShopOnAbp.PaymentService.Application/PaymentServiceApplicationModule.cs @@ -2,6 +2,10 @@ using Volo.Abp.AutoMapper; using Volo.Abp.Modularity; using Volo.Abp.Application; +using Microsoft.Extensions.Options; +using EShopOnAbp.PaymentService.PayPal; +using PayPalCheckoutSdk.Core; +using System; namespace EShopOnAbp.PaymentService { @@ -20,6 +24,18 @@ namespace EShopOnAbp.PaymentService { options.AddMaps(validate: true); }); + + context.Services.AddTransient(provider => + { + var options = provider.GetService>().Value; + + if (options.Environment.IsNullOrWhiteSpace() || options.Environment == PayPalConsts.Environment.Sandbox) + { + return new PayPalHttpClient(new SandboxEnvironment(options.ClientId, options.Secret)); + } + + return new PayPalHttpClient(new LiveEnvironment(options.ClientId, options.Secret)); + }); } } } diff --git a/services/payment/src/EShopOnAbp.PaymentService.Domain.Shared/PaymentRequests/PaymentRequestCompletedEto.cs b/services/payment/src/EShopOnAbp.PaymentService.Domain.Shared/PaymentRequests/PaymentRequestCompletedEto.cs new file mode 100644 index 00000000..7978cca4 --- /dev/null +++ b/services/payment/src/EShopOnAbp.PaymentService.Domain.Shared/PaymentRequests/PaymentRequestCompletedEto.cs @@ -0,0 +1,15 @@ +using System; +using Volo.Abp.Data; +using Volo.Abp.Domain.Entities.Events.Distributed; +using Volo.Abp.EventBus; + +namespace EShopOnAbp.PaymentService +{ + [Serializable] + [EventName("Payment.Completed")] + public class PaymentRequestCompletedEto: EtoBase, IHasExtraProperties + { + public Guid PaymentRequestId { get; set; } + public ExtraPropertyDictionary ExtraProperties { get; set; } + } +} \ No newline at end of file diff --git a/services/payment/src/EShopOnAbp.PaymentService.Domain.Shared/PaymentRequests/PaymentRequestConsts.cs b/services/payment/src/EShopOnAbp.PaymentService.Domain.Shared/PaymentRequests/PaymentRequestConsts.cs new file mode 100644 index 00000000..f903f3a7 --- /dev/null +++ b/services/payment/src/EShopOnAbp.PaymentService.Domain.Shared/PaymentRequests/PaymentRequestConsts.cs @@ -0,0 +1,7 @@ +namespace EShopOnAbp.PaymentService.PaymentRequests +{ + public static class PaymentRequestConsts + { + public const int MaxCurrencyLength = 3; + } +} diff --git a/services/payment/src/EShopOnAbp.PaymentService.Domain.Shared/PaymentRequests/PaymentRequestFailedEto.cs b/services/payment/src/EShopOnAbp.PaymentService.Domain.Shared/PaymentRequests/PaymentRequestFailedEto.cs new file mode 100644 index 00000000..7723aff2 --- /dev/null +++ b/services/payment/src/EShopOnAbp.PaymentService.Domain.Shared/PaymentRequests/PaymentRequestFailedEto.cs @@ -0,0 +1,16 @@ +using System; +using Volo.Abp.Data; +using Volo.Abp.Domain.Entities.Events.Distributed; +using Volo.Abp.EventBus; + +namespace EShopOnAbp.PaymentService +{ + [Serializable] + [EventName("Payment.Completed")] + public class PaymentRequestFailedEto : EtoBase, IHasExtraProperties + { + public Guid PaymentRequestId { get; set; } + public string FailReason { get; set; } + public ExtraPropertyDictionary ExtraProperties { get; set; } + } +} \ No newline at end of file diff --git a/services/payment/src/EShopOnAbp.PaymentService.Domain.Shared/PaymentRequests/PaymentRequestState.cs b/services/payment/src/EShopOnAbp.PaymentService.Domain.Shared/PaymentRequests/PaymentRequestState.cs new file mode 100644 index 00000000..4709857d --- /dev/null +++ b/services/payment/src/EShopOnAbp.PaymentService.Domain.Shared/PaymentRequests/PaymentRequestState.cs @@ -0,0 +1,9 @@ +namespace EShopOnAbp.PaymentService.PaymentRequests +{ + public enum PaymentRequestState + { + Waiting = 0, + Completed, + Failed + } +} diff --git a/services/payment/src/EShopOnAbp.PaymentService.Domain/PaymentRequests/IPaymentRequestRepository.cs b/services/payment/src/EShopOnAbp.PaymentService.Domain/PaymentRequests/IPaymentRequestRepository.cs new file mode 100644 index 00000000..cbb7bee9 --- /dev/null +++ b/services/payment/src/EShopOnAbp.PaymentService.Domain/PaymentRequests/IPaymentRequestRepository.cs @@ -0,0 +1,9 @@ +using System; +using Volo.Abp.Domain.Repositories; + +namespace EShopOnAbp.PaymentService.PaymentRequests +{ + public interface IPaymentRequestRepository : IBasicRepository + { + } +} diff --git a/services/payment/src/EShopOnAbp.PaymentService.Domain/PaymentRequests/PaymentRequest.cs b/services/payment/src/EShopOnAbp.PaymentService.Domain/PaymentRequests/PaymentRequest.cs new file mode 100644 index 00000000..b06df627 --- /dev/null +++ b/services/payment/src/EShopOnAbp.PaymentService.Domain/PaymentRequests/PaymentRequest.cs @@ -0,0 +1,73 @@ +using JetBrains.Annotations; +using System; +using System.Collections.Generic; +using Volo.Abp; +using Volo.Abp.Domain.Entities.Auditing; + +namespace EShopOnAbp.PaymentService.PaymentRequests +{ + public class PaymentRequest : CreationAuditedAggregateRoot, ISoftDelete + { + [NotNull] + public string Currency { get; protected set; } + + [CanBeNull] + public string BuyerId { get; protected set; } + + public PaymentRequestState State { get; protected set; } + + [CanBeNull] + public string FailReason { get; protected set; } + + public bool IsDeleted { get; set; } + + public ICollection Products { get; } = new List(); + + private PaymentRequest() + { + + } + + public PaymentRequest(Guid id, [NotNull] string currency, [CanBeNull] string buyerId = null) + { + Id = id; + Currency = Check.NotNullOrWhiteSpace(currency, nameof(currency), maxLength: PaymentRequestConsts.MaxCurrencyLength); + BuyerId = buyerId; + } + + 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 + }); + } + } +} diff --git a/services/payment/src/EShopOnAbp.PaymentService.Domain/PaymentRequests/PaymentRequestProduct.cs b/services/payment/src/EShopOnAbp.PaymentService.Domain/PaymentRequests/PaymentRequestProduct.cs new file mode 100644 index 00000000..024bfe7d --- /dev/null +++ b/services/payment/src/EShopOnAbp.PaymentService.Domain/PaymentRequests/PaymentRequestProduct.cs @@ -0,0 +1,37 @@ +using JetBrains.Annotations; +using System; +using Volo.Abp.Domain.Entities; + +namespace EShopOnAbp.PaymentService.PaymentRequests +{ + public class PaymentRequestProduct : Entity + { + public Guid PaymentRequestId { get; private set; } + + public string ReferenceId { get; set; } + + public string Name { get; set; } + + public decimal UnitPrice { get; set; } + + public int Quantity { get; set; } + + public decimal TotalPrice { get; set; } + + public PaymentRequestProduct( + Guid paymentRequestId, + [NotNull] string name, + decimal unitPrice, + int quantity, + decimal totalPrice, + [CanBeNull] string referenceId = null) + { + PaymentRequestId = paymentRequestId; + Name = name; + UnitPrice = unitPrice; + Quantity = quantity; + TotalPrice = totalPrice; + ReferenceId = referenceId; + } + } +} diff --git a/services/payment/src/EShopOnAbp.PaymentService.EntityFrameworkCore/EntityFrameworkCore/PaymentServiceEntityFrameworkCoreModule.cs b/services/payment/src/EShopOnAbp.PaymentService.EntityFrameworkCore/EntityFrameworkCore/PaymentServiceEntityFrameworkCoreModule.cs index 9e1ee693..b2d54bc5 100644 --- a/services/payment/src/EShopOnAbp.PaymentService.EntityFrameworkCore/EntityFrameworkCore/PaymentServiceEntityFrameworkCoreModule.cs +++ b/services/payment/src/EShopOnAbp.PaymentService.EntityFrameworkCore/EntityFrameworkCore/PaymentServiceEntityFrameworkCoreModule.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.DependencyInjection; +using EShopOnAbp.PaymentService.PaymentRequests; +using Microsoft.Extensions.DependencyInjection; using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore.SqlServer; using Volo.Abp.Modularity; @@ -20,9 +21,7 @@ namespace EShopOnAbp.PaymentService.EntityFrameworkCore { context.Services.AddAbpDbContext(options => { - /* Remove "includeAllEntities: true" to create - * default repositories only for aggregate roots */ - options.AddDefaultRepositories(includeAllEntities: true); + options.AddRepository(); }); Configure(options => diff --git a/services/payment/src/EShopOnAbp.PaymentService.EntityFrameworkCore/PaymentRequests/PaymentRequestEfCoreRepository.cs b/services/payment/src/EShopOnAbp.PaymentService.EntityFrameworkCore/PaymentRequests/PaymentRequestEfCoreRepository.cs new file mode 100644 index 00000000..dda72b03 --- /dev/null +++ b/services/payment/src/EShopOnAbp.PaymentService.EntityFrameworkCore/PaymentRequests/PaymentRequestEfCoreRepository.cs @@ -0,0 +1,16 @@ +using EShopOnAbp.PaymentService.EntityFrameworkCore; +using System; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace EShopOnAbp.PaymentService.PaymentRequests +{ + public class PaymentRequestEfCoreRepository : EfCoreRepository, IPaymentRequestRepository + { + public PaymentRequestEfCoreRepository(IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + + } + } +} diff --git a/services/payment/test/EShopOnAbp.PaymentService.EntityFrameworkCore.Tests/PaymentRequests/PaymentRequestRepository_Tests.cs b/services/payment/test/EShopOnAbp.PaymentService.EntityFrameworkCore.Tests/PaymentRequests/PaymentRequestRepository_Tests.cs new file mode 100644 index 00000000..406f5e10 --- /dev/null +++ b/services/payment/test/EShopOnAbp.PaymentService.EntityFrameworkCore.Tests/PaymentRequests/PaymentRequestRepository_Tests.cs @@ -0,0 +1,30 @@ +using EShopOnAbp.PaymentService.EntityFrameworkCore; +using Shouldly; +using System; +using System.Threading.Tasks; +using Xunit; + +namespace EShopOnAbp.PaymentService.PaymentRequests +{ + public class PaymentRequestRepository_Tests : PaymentServiceEntityFrameworkCoreTestBase + { + private readonly IPaymentRequestRepository _paymentRequestRepository; + public PaymentRequestRepository_Tests() + { + _paymentRequestRepository = GetRequiredService(); + } + + [Fact] + public async Task Should_Insert_Payment_Request() + { + var id = Guid.NewGuid(); + var paymentRequest = new PaymentRequest(id, "USD"); + + await _paymentRequestRepository.InsertAsync(paymentRequest, autoSave: true); + + var inserted = await _paymentRequestRepository.GetAsync(id); + + inserted.Id.ShouldNotBe(Guid.Empty); + } + } +}