Browse Source

Move handling webhooks to PaymentMethod

pull/63/head
enisn 5 years ago
parent
commit
96010fea3e
  1. 12
      services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentMethods/PaymentTypeIds.cs
  2. 12
      services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentMethods/PaymentTypes.cs
  3. 6
      services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentRequests/IPaymentRequestAppService.cs
  4. 13
      services/payment/src/EShopOnAbp.PaymentService.Application/PaymentMethods/DemoPaymentMethod.cs
  5. 8
      services/payment/src/EShopOnAbp.PaymentService.Application/PaymentMethods/IPaymentMethod.cs
  6. 13
      services/payment/src/EShopOnAbp.PaymentService.Application/PaymentMethods/PaymentMethodResolver.cs
  7. 22
      services/payment/src/EShopOnAbp.PaymentService.Application/PaymentMethods/PaypalMethod.cs
  8. 32
      services/payment/src/EShopOnAbp.PaymentService.Application/PaymentRequests/PaymentRequestAppService.cs
  9. 14
      services/payment/src/EShopOnAbp.PaymentService.HttpApi/Controllers/PaymentRequestController.cs

12
services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentMethods/PaymentTypeIds.cs

@ -0,0 +1,12 @@
namespace EShopOnAbp.PaymentService.PaymentMethods;
public static class PaymentTypeIds
{
/*
* PaymentTypeIds must be unique.
*/
public const int Demo = 0;
public const int PayPal = 1;
}

12
services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentMethods/PaymentTypes.cs

@ -0,0 +1,12 @@
namespace EShopOnAbp.PaymentService.PaymentMethods;
public static class PaymentTypes
{
/*
* PaymentTypes must be unique.
* It's identifier of a PaymentMethod.
*/
public const string Demo = "demo";
public const string PayPal = "paypal";
}

6
services/payment/src/EShopOnAbp.PaymentService.Application.Contracts/PaymentRequests/IPaymentRequestAppService.cs

@ -7,10 +7,10 @@ namespace EShopOnAbp.PaymentService.PaymentRequests
{
Task<PaymentRequestDto> CreateAsync(PaymentRequestCreationDto input);
Task<PaymentRequestStartResultDto> StartAsync(PaymentRequestStartDto input);
Task<PaymentRequestStartResultDto> StartAsync(string paymentType, PaymentRequestStartDto input);
Task<PaymentRequestDto> CompleteAsync(PaymentRequestCompleteInputDto input);
Task<PaymentRequestDto> CompleteAsync(string paymentType, PaymentRequestCompleteInputDto input);
Task<bool> HandleWebhookAsync(string payload);
Task<bool> HandleWebhookAsync(string paymentType, string payload);
}
}

13
services/payment/src/EShopOnAbp.PaymentService.Application/PaymentMethods/DemoPaymentMethod.cs

@ -8,12 +8,10 @@ namespace EShopOnAbp.PaymentService.PaymentMethods;
[ExposeServices(typeof(IPaymentMethod), typeof(DemoPaymentMethod))]
public class DemoPaymentMethod : IPaymentMethod
{
public int PaymentTypeId { get; }
public int PaymentTypeId => PaymentTypeIds.Demo;
public string PaymentType => PaymentTypes.Demo;
public DemoPaymentMethod()
{
PaymentTypeId = 0;
}
public Task<PaymentRequestStartResultDto> StartAsync(PaymentRequest paymentRequest, PaymentRequestStartDto input)
{
@ -31,4 +29,9 @@ public class DemoPaymentMethod : IPaymentMethod
return await paymentRequestRepository.UpdateAsync(paymentRequest);
}
public Task HandleWebhookAsync(string payload)
{
return Task.CompletedTask;
}
}

8
services/payment/src/EShopOnAbp.PaymentService.Application/PaymentMethods/IPaymentMethod.cs

@ -6,7 +6,13 @@ namespace EShopOnAbp.PaymentService.PaymentMethods;
public interface IPaymentMethod : ITransientDependency
{
public int PaymentTypeId { get; }
int PaymentTypeId { get; }
string PaymentType { get; }
public Task<PaymentRequestStartResultDto> StartAsync(PaymentRequest paymentRequest, PaymentRequestStartDto input);
public Task<PaymentRequest> CompleteAsync(IPaymentRequestRepository paymentRequestRepository, string token);
public Task HandleWebhookAsync(string payload);
}

13
services/payment/src/EShopOnAbp.PaymentService.Application/PaymentMethods/PaymentMethodResolver.cs

@ -28,4 +28,17 @@ public class PaymentMethodResolver : ITransientDependency
return paymentMethod;
}
public IPaymentMethod Resolve(string paymentType)
{
var paymentMethod = _paymentMethods.FirstOrDefault(q => q.PaymentType == paymentType);
if (paymentMethod == null)
{
_logger.LogError($"Couldn't find Payment method with type:{paymentType}");
throw new ArgumentException("Payment method not found", paymentType);
}
return paymentMethod;
}
}

22
services/payment/src/EShopOnAbp.PaymentService.Application/PaymentMethods/PaypalMethod.cs

@ -4,6 +4,7 @@ using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using EShopOnAbp.PaymentService.PaymentRequests;
using Newtonsoft.Json.Linq;
using PayPalCheckoutSdk.Core;
using PayPalCheckoutSdk.Orders;
using Volo.Abp.DependencyInjection;
@ -15,13 +16,13 @@ public class PaypalMethod : IPaymentMethod
{
private readonly PayPalHttpClient _payPalHttpClient;
private readonly PaymentRequestDomainService _paymentRequestDomainService;
public int PaymentTypeId { get; }
public int PaymentTypeId => PaymentTypeIds.PayPal;
public string PaymentType => PaymentTypes.PayPal;
public PaypalMethod(PayPalHttpClient payPalHttpClient, PaymentRequestDomainService paymentRequestDomainService)
{
_payPalHttpClient = payPalHttpClient;
_paymentRequestDomainService = paymentRequestDomainService;
PaymentTypeId = 1;
}
public async Task<PaymentRequestStartResultDto> StartAsync(PaymentRequest paymentRequest,
@ -95,4 +96,21 @@ public class PaypalMethod : IPaymentMethod
return await _paymentRequestDomainService.UpdatePaymentRequestStateAsync(paymentRequestId, order.Status,
order.Id);
}
public async Task HandleWebhookAsync(string payload)
{
// TODO: Find better way to parse.
var jObject = JObject.Parse(payload);
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>();
var paymentRequestId = Guid.Parse(order.PurchaseUnits.First().ReferenceId);
await _paymentRequestDomainService.UpdatePaymentRequestStateAsync(paymentRequestId, order.Status, order.Id);
}
}

32
services/payment/src/EShopOnAbp.PaymentService.Application/PaymentRequests/PaymentRequestAppService.cs

@ -1,10 +1,6 @@
using Newtonsoft.Json.Linq;
using PayPalCheckoutSdk.Core;
using PayPalCheckoutSdk.Orders;
using System;
using EShopOnAbp.PaymentService.PaymentMethods;
using System.Linq;
using System.Threading.Tasks;
using EShopOnAbp.PaymentService.PaymentMethods;
namespace EShopOnAbp.PaymentService.PaymentRequests
{
@ -13,16 +9,13 @@ namespace EShopOnAbp.PaymentService.PaymentRequests
private readonly PaymentMethodResolver _paymentMethodResolver;
private readonly PaymentRequestDomainService _paymentRequestDomainService;
protected IPaymentRequestRepository PaymentRequestRepository { get; }
protected PayPalHttpClient PayPalHttpClient { get; }
public PaymentRequestAppService(
IPaymentRequestRepository paymentRequestRepository,
PayPalHttpClient payPalHttpClient,
PaymentMethodResolver paymentMethodResolver,
PaymentRequestDomainService paymentRequestDomainService)
{
PaymentRequestRepository = paymentRequestRepository;
PayPalHttpClient = payPalHttpClient;
_paymentMethodResolver = paymentMethodResolver;
_paymentRequestDomainService = paymentRequestDomainService;
}
@ -51,37 +44,28 @@ namespace EShopOnAbp.PaymentService.PaymentRequests
return ObjectMapper.Map<PaymentRequest, PaymentRequestDto>(paymentRequest);
}
public virtual async Task<PaymentRequestStartResultDto> StartAsync(PaymentRequestStartDto input)
public virtual async Task<PaymentRequestStartResultDto> StartAsync(string paymentType, PaymentRequestStartDto input)
{
PaymentRequest paymentRequest =
await PaymentRequestRepository.GetAsync(input.PaymentRequestId, includeDetails: true);
var paymentService = _paymentMethodResolver.Resolve(input.PaymentTypeId);
var paymentService = _paymentMethodResolver.Resolve(paymentType);
return await paymentService.StartAsync(paymentRequest, input);
}
public virtual async Task<PaymentRequestDto> CompleteAsync(PaymentRequestCompleteInputDto input)
public virtual async Task<PaymentRequestDto> CompleteAsync(string paymentType, PaymentRequestCompleteInputDto input)
{
var paymentService = _paymentMethodResolver.Resolve(input.PaymentTypeId);
var paymentService = _paymentMethodResolver.Resolve(paymentType);
var paymentRequest = await paymentService.CompleteAsync(PaymentRequestRepository, input.Token);
return ObjectMapper.Map<PaymentRequest, PaymentRequestDto>(paymentRequest);
}
public virtual async Task<bool> HandleWebhookAsync(string payload)
public virtual async Task<bool> HandleWebhookAsync(string paymentType, string payload)
{
var jObject = JObject.Parse(payload);
var paymentService = _paymentMethodResolver.Resolve(paymentType);
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>();
var paymentRequestId = Guid.Parse(order.PurchaseUnits.First().ReferenceId);
await _paymentRequestDomainService.UpdatePaymentRequestStateAsync(paymentRequestId, order.Status, order.Id);
await paymentService.HandleWebhookAsync(payload);
// PayPal doesn't accept Http 204 (NoContent) result and tries to execute webhook again.
// So with following value, API returns Http 200 (OK) result.

14
services/payment/src/EShopOnAbp.PaymentService.HttpApi/Controllers/PaymentRequestController.cs

@ -20,9 +20,9 @@ namespace EShopOnAbp.PaymentService.Controllers
}
[HttpPost("complete")]
public Task<PaymentRequestDto> CompleteAsync(PaymentRequestCompleteInputDto input)
public Task<PaymentRequestDto> CompleteAsync(string paymentType, PaymentRequestCompleteInputDto input)
{
return PaymentRequestAppService.CompleteAsync(input);
return PaymentRequestAppService.CompleteAsync(paymentType, input);
}
[HttpPost]
@ -32,19 +32,19 @@ namespace EShopOnAbp.PaymentService.Controllers
}
[HttpPost]
[Route("webhook")]
public async Task<bool> HandleWebhookAsync(string payload)
[Route("{paymentType}/webhook")]
public async Task<bool> HandleWebhookAsync(string paymentType, string payload)
{
var bytes = await Request.Body.GetAllBytesAsync();
payload = Encoding.UTF8.GetString(bytes);
return await PaymentRequestAppService.HandleWebhookAsync(payload);
return await PaymentRequestAppService.HandleWebhookAsync(paymentType, payload);
}
[HttpPost("start")]
public Task<PaymentRequestStartResultDto> StartAsync(PaymentRequestStartDto input)
public Task<PaymentRequestStartResultDto> StartAsync(string paymentType, PaymentRequestStartDto input)
{
return PaymentRequestAppService.StartAsync(input);
return PaymentRequestAppService.StartAsync(paymentType, input);
}
}
}
Loading…
Cancel
Save