public async Task <ActionResult> Webhook([FromBody] MobilePayWebhook request, [FromHeader(Name = "x-mobilepay-signature")] string mpSignatureHeader) { var isSignatureValid = await VerifySignature(mpSignatureHeader); if (!isSignatureValid) { Log.Error("Signature did not match the computed signature. Request Body: {Request} Signature: {Signature}", request, mpSignatureHeader); return(BadRequest("Signature is not valid")); } Log.Information("MobilePay Webhook invoked. Request: {Request}", request); await _purchaseService.HandleMobilePayPaymentUpdate(request); return(NoContent()); }
public async Task HandleMobilePayPaymentUpdate(MobilePayWebhook webhook) { var purchase = await _context.Purchases .Include(p => p.PurchasedBy) .Where(p => p.TransactionId.Equals(webhook.Data.Id)) .FirstOrDefaultAsync(); if (purchase == null) { Log.Error("No purchase was found by TransactionId: {Id} from Webhook request", webhook.Data.Id); throw new EntityNotFoundException($"No purchase was found by Transaction Id: {webhook.Data.Id} from webhook request"); } if (purchase.Completed || purchase.Completed) { // FIXME Check purchase is not already completed. Should we throw an error? Conflict? Log.Warning("Purchase from Webhook request is already completed. Purchase Id: {PurchaseId}, Transaction Id: {TransactionId}", purchase.Id, webhook.Data.Id); return; } var eventTypeLowerCase = webhook.EventType.ToLower(); switch (eventTypeLowerCase) { case "payment.reserved": { await CompletePurchase(purchase); break; } case "payment.cancelled_by_user": { await CancelPurchase(purchase); break; } case "payment.expired": { await CancelPurchase(purchase); break; } default: Log.Error("Unknown EventType from Webhook request. Event Type: {EventType}, Purchase Id: {PurchaseId}, Transaction Id: {TransactionId}", eventTypeLowerCase, purchase.Id, webhook.Data.Id); throw new ArgumentException($"Event Type {eventTypeLowerCase} is not valid"); } }