ASP.NET Core IPN Request Body is empty

StreamWork
Contributor
Contributor

I'm using IPN for my ASP.NET Core web app (apologies for the edge case), but the data I receive from the IPN Simulator (the data that I send back with cmd=_notify-validate& prepended to it) appears to be empty. I have the code pasted below, but for the current debugging, a generic transaction is logged, instead of the simulated data (as I cannot attempt to parse it currently).

Spoiler
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using MyProject.Config;
using MyProject.Models;

namespace MyProject.Controllers { // IPN = Instant Payment Notification
    public class IPNController : Controller {

        private IOptionsSnapshot storageConfig;

        HelperFunctions helperFunctions = new HelperFunctions();

        private class IPNContext {
            public HttpRequest IPNRequest { get; set; }
            public string RequestBody { get; set; }
            public string Verification { get; set; } = string.Empty;
        }

        public IActionResult IPN ([FromServices] IOptionsSnapshot storageConfig) {
            return Receive(storageConfig);
        }

        // Should run when an IPN is recieved
        [HttpPost]
        public IActionResult Receive (IOptionsSnapshot _storageConfig) {
            storageConfig = _storageConfig;

            IPNContext ipnContext = new IPNContext {
                IPNRequest = Request
            };

            using (StreamReader reader = new StreamReader(ipnContext.IPNRequest.Body, Encoding.ASCII)) {
                ipnContext.RequestBody = reader.ReadToEnd();
            }

            //Store the IPN received from PayPal
            Transaction transaction = LogRequest(ipnContext);

            //Fire and forget verification task
            Task.Run(() => VerifyTask(ipnContext, transaction));

            //Reply back a 200 code
            return Ok();
        }

        private async Task VerifyTask (IPNContext ipnContext, Transaction transaction) {
            try {
                var verificationRequest = WebRequest.Create("<a href="https://www.sandbox.paypal.com/cgi-bin/webscr" target="_blank">https://www.sandbox.paypal.com/cgi-bin/webscr</a>");

                //Set values for the verification request
                verificationRequest.Method = "POST";
                verificationRequest.ContentType = "application/x-www-form-urlencoded";

                //Add cmd=_notify-validate to the payload
                string strRequest = "cmd=_notify-validate&" + ipnContext.RequestBody;
                verificationRequest.ContentLength = strRequest.Length;

                //Attach payload to the verification request
                using (StreamWriter writer = new StreamWriter(verificationRequest.GetRequestStream(), Encoding.ASCII)) {
                    writer.Write(strRequest);
                }

                //Send the request to PayPal and get the response
                using (StreamReader reader = new StreamReader(verificationRequest.GetResponse().GetResponseStream())) {
                    ipnContext.Verification = reader.ReadToEnd();
                }
            }
            catch (Exception exception) {
                //Capture exception for manual investigation
                transaction.Error += "...Failed to verify IPN task: " + exception.Message;
            }

            await ProcessVerificationResponse(ipnContext, transaction);
        }

        // Should find the table entry created when payment was made, set verified to FALSE, and return it, and optionally discard incomplete transactions.
        private Transaction LogRequest (IPNContext ipnContext) {
            // Persist the request values into a database or temporary data store

            return new Transaction {
                Id = Guid.NewGuid().ToString(),
                Payer = "payer123",
                Reciever = "reciever123",
                Val = "20.00",
                TimeSent = DateTime.Now.ToString(), // NOTE: timestamp appears to be in Iceland?
                Verified = "FALSE",
                Error = ""
            };
        }

        // Should update table entry on payment validity
        private async Task ProcessVerificationResponse (IPNContext ipnContext, Transaction transaction) {
            if (ipnContext.Verification.Equals("VERIFIED")) {
                if (transaction.Error.Equals("")) {
                    transaction.Verified = "TRUE";

                    /*
                     * Some values to read:
                     * payment_status
                     * txn_type
                     * txn_id
                     * payment_gross
                     * payer_email
                     * first_name
                     * last_name
                     * Probably More?
                     */

                }
                else {
                    transaction.Verified = "FALSE";
                }
                // check that Payment_status=Completed
                // check that Txn_id has not been previously processed
                // check that Receiver_email is your Primary PayPal email
                // check that Payment_amount/Payment_currency are correct
                // process payment
            } else if (ipnContext.Verification.Equals("INVALID")) {
                transaction.Error += "...IPN could not be verified because it was found to be INVALID";
            } else {
                transaction.Error += "...IPN could not be verified for unknown reasons (neither VALID nor INVALID)";
            }

            transaction.Error += "...DEBUG NOTES...IPNRequest.Body = " + ipnContext.IPNRequest.Body + "...RequestBody = " + ipnContext.RequestBody + "...Verification = " + ipnContext.Verification;

            await helperFunctions.SaveTransaction(storageConfig, transaction);
        }
    }
}

At the bottom there, I append the data from the IPN to an error message that I log, which is always,

 

...IPN could not be verified because it was found to be INVALID...DEBUG NOTES...IPNRequest.Body = Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.FrameRequestStream...RequestBody = ...Verification = INVALID

 

As you can see, RequestBody, which, as I understand, is supposed to contain the data about the transaction, is empty. Any ideas on how to fix this?

Thank you!

Login to Me Too
0 REPLIES 0

Haven't Found your Answer?

It happens. Hit the "Login to Ask the community" button to create a question for the PayPal community.