Browse Source

Add WebHook handling to payment module

pull/69/head
enisn 5 years ago
parent
commit
40159fd074
  1. 2
      modules/payment/src/Payment.Application.Contracts/PaymentRequests/IPaymentRequestAppService.cs
  2. 37
      modules/payment/src/Payment.Application/PaymentRequests/PaymentRequestAppService.cs
  3. 37
      modules/payment/src/Payment.HttpApi.Client/ClientProxies/Payment-generate-proxy.json
  4. 8
      modules/payment/src/Payment.HttpApi.Client/ClientProxies/PaymentRequestClientProxy.Generated.cs
  5. 14
      modules/payment/src/Payment.HttpApi/PaymentRequests/PaymentRequestController.cs

2
modules/payment/src/Payment.Application.Contracts/PaymentRequests/IPaymentRequestAppService.cs

@ -13,5 +13,7 @@ namespace Payment.PaymentRequests
Task<StartPaymentResultDto> StartPaymentAsync(StartPaymentDto input); Task<StartPaymentResultDto> StartPaymentAsync(StartPaymentDto input);
Task<PaymentRequestDto> CompleteAsync(string token); Task<PaymentRequestDto> CompleteAsync(string token);
Task<bool> HandleWebhookAsync(string payload);
} }
} }

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

@ -1,4 +1,5 @@
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
using Payment.PayPal; using Payment.PayPal;
using PayPalCheckoutSdk.Core; using PayPalCheckoutSdk.Core;
using PayPalCheckoutSdk.Orders; using PayPalCheckoutSdk.Orders;
@ -28,7 +29,7 @@ namespace Payment.PaymentRequests
public async Task<PaymentRequestDto> GetAsync(Guid id) public async Task<PaymentRequestDto> GetAsync(Guid id)
{ {
var paymentRequest = await _paymentRequestRepository.GetAsync(id); var paymentRequest = await _paymentRequestRepository.GetAsync(id);
return ObjectMapper.Map<PaymentRequest, PaymentRequestDto>(paymentRequest); return ObjectMapper.Map<PaymentRequest, PaymentRequestDto>(paymentRequest);
} }
@ -114,11 +115,38 @@ namespace Payment.PaymentRequests
var order = (await _payPalHttpClient.Execute(request)).Result<Order>(); 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 paymentRequestId = Guid.Parse(order.PurchaseUnits.First().ReferenceId);
var paymentRequest = await _paymentRequestRepository.GetAsync(paymentRequestId); var paymentRequest = await _paymentRequestRepository.GetAsync(paymentRequestId);
if (order.Status == PayPalConsts.OrderStatus.Approved || order.Status == PayPalConsts.OrderStatus.Completed) if (order.Status == PayPalConsts.OrderStatus.Completed || order.Status == PayPalConsts.OrderStatus.Approved)
{ {
paymentRequest.SetAsCompleted(); paymentRequest.SetAsCompleted();
} }
@ -127,11 +155,12 @@ namespace Payment.PaymentRequests
paymentRequest.SetAsFailed(order.Status); paymentRequest.SetAsFailed(order.Status);
} }
paymentRequest.ExtraProperties.Add(PayPalConsts.OrderIdPropertyName, order.Id); paymentRequest.ExtraProperties[PayPalConsts.OrderIdPropertyName] = order.Id;
paymentRequest.ExtraProperties[nameof(order.Status)] = order.Status;
await _paymentRequestRepository.UpdateAsync(paymentRequest); await _paymentRequestRepository.UpdateAsync(paymentRequest);
return ObjectMapper.Map<PaymentRequest, PaymentRequestDto>(paymentRequest); return paymentRequest;
} }
} }
} }

37
modules/payment/src/Payment.HttpApi.Client/ClientProxies/Payment-generate-proxy.json

@ -161,6 +161,43 @@
}, },
"allowAnonymous": null, "allowAnonymous": null,
"implementFrom": "Payment.PaymentRequests.IPaymentRequestAppService" "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"
} }
} }
} }

8
modules/payment/src/Payment.HttpApi.Client/ClientProxies/PaymentRequestClientProxy.Generated.cs

@ -46,5 +46,13 @@ namespace Payment.PaymentRequests.ClientProxies
{ typeof(StartPaymentDto), input } { typeof(StartPaymentDto), input }
}); });
} }
public virtual async Task<bool> HandleWebhookAsync(string payload)
{
return await RequestAsync<bool>(nameof(HandleWebhookAsync), new ClientProxyRequestTypeValue
{
{ typeof(string), payload }
});
}
} }
} }

14
modules/payment/src/Payment.HttpApi/PaymentRequests/PaymentRequestController.cs

@ -1,5 +1,8 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Linq;
using System; using System;
using System.IO;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp; using Volo.Abp;
@ -43,5 +46,16 @@ namespace Payment.PaymentRequests
{ {
return PaymentRequestAppService.StartPaymentAsync(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);
}
} }
} }
Loading…
Cancel
Save