Closed drabaco closed 6 years ago
Hi drabaco, You are welcome to submit a PR with a new example. Thanks.
Thank you for your reply.
I'm a newbie, that's why i'm asking a working example ...
Something like this (NOT TESTED)
[Produces("application/json")]
[Route("api/IPN")]
public class IPNController : Controller
{
[HttpPost]
public IActionResult Receive()
{
//Store the IPN received from PayPal
LogRequest(Request);
//Fire and forget verification task
Task.Run(() => VerifyTask(Request));
//Reply back a 200 code
return Ok();
}
private void VerifyTask(HttpRequest ipnRequest)
{
string verificationResponse = string.Empty;
try
{
var verificationRequest = WebRequest.Create("https://www.sandbox.paypal.com/cgi-bin/webscr");
//Set values for the verification request
verificationRequest.Method = "POST";
verificationRequest.ContentType = "application/x-www-form-urlencoded";
string strRequest;
using( StreamReader reader = new StreamReader( Request.Body, Encoding.ASCII ) )
{
strRequest = reader.ReadToEnd();
}
//Add cmd=_notify-validate to the payload
strRequest = "cmd=_notify-validate&" + strRequest;
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() ) )
{
verificationResponse = reader.ReadToEnd();
}
}
catch( Exception exception )
{
//Capture exception for manual investigation
}
ProcessVerificationResponse(verificationResponse);
}
private void LogRequest(HttpRequest request)
{
// Persist the request values into a database or temporary data store
}
private void ProcessVerificationResponse(string verificationResponse)
{
if (verificationResponse.Equals("VERIFIED"))
{
// 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 (verificationResponse.Equals("INVALID"))
{
//Log for manual investigation
}
else
{
//Log error
}
}
}
Thank you MalpenZibo, i realized something like you, but i have a main problem (in both implementation):
When httpRequest arrive to VerifyTask is already disposed: HERE
using( StreamReader reader = new StreamReader( Request.Body, Encoding.ASCII ) ) //REQUEST IS DISPOSED
{
strRequest = reader.ReadToEnd();
}
so it throw exception.
: System.ObjectDisposedException: Cannot access a disposed object. Object name: 'FrameRequestStream'. at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.FrameRequestStream.ValidateState(CancellationToken cancellationToken) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.FrameRequestStream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.FrameRequestStream.Read(Byte[] buffer, Int32 offset, Int32 count) at System.IO.StreamReader.ReadBuffer() at System.IO.StreamReader.ReadToEnd() at xxxxxxx.Controllers.IPNController.VerifyTask(HttpRequest ipnRequest)
In LogRequest the httpRequest is readable, so if i add a log there the httpRequest get logged.
It seems that the request got disposed around here:
//Fire and forget verification task
Task.Run(() => VerifyTask(Request));
Try to use HttpRequest ipnRequest from VerifyTask function argument OR
[Produces("application/json")]
[Route("api/IPN")]
public class IPNController : Controller
{
private class IPNContext
{
public HttpRequest IPNRequest { get; set; }
public string RequestBody { get; set; }
public string Verification { get; set; } = String.Empty;
}
public IActionResult Receive()
{
IPNContext context = new IPNContext()
{
IPNRequest = Request
};
LogRequest( context );
//Fire and forget verification task
Task.Run( () => VerifyTask( context ) );
//Reply back a 200 code
return Ok();
}
private void VerifyTask( IPNContext ipnContext )
{
try
{
var verificationRequest = WebRequest.Create("https://www.sandbox.paypal.com/cgi-bin/webscr");
//Set values for the verification request
verificationRequest.Method = "POST";
verificationRequest.ContentType = "application/x-www-form-urlencoded";
using( StreamReader reader = new StreamReader( ipnContext.IPNRequest.Body, Encoding.ASCII ) )
{
ipnContext.RequestBody = reader.ReadToEnd();
}
//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
}
ProcessVerificationResponse( ipnContext );
}
private void LogRequest( IPNContext ipnContext )
{
// Persist the request values into a database or temporary data store
}
private void ProcessVerificationResponse( IPNContext ipnContext )
{
if( ipnContext.Verification.Equals("VERIFIED") )
{
// 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") )
{
//Log for manual investigation
}
else
{
//Log error
}
}
}
If the error persists the only way is to read the Request content body before
//Fire and forget verification task
Task.Run( () => VerifyTask( context ) );
//Reply back a 200 code
return Ok();
Asp.Net Core probably disposes all request data after Ok() response and because Task.Run is not awaited when you read Request.Body the request is already disposed.
You can try with
private class IPNContext
{
public HttpRequest IPNRequest { get; set; }
public string RequestBody { get; set; }
public string Verification { get; set; } = String.Empty;
}
public IActionResult Receive()
{
IPNContext context = new IPNContext()
{
IPNRequest = Request
};
using( StreamReader reader = new StreamReader( ipnContext.IPNRequest.Body, Encoding.ASCII ) )
{
ipnContext.RequestBody = reader.ReadToEnd();
}
LogRequest( context );
//Fire and forget verification task
Task.Run( () => VerifyTask( context ) );
//Reply back a 200 code
return Ok();
}
Yes, i'm trying exactly what you suggest.
All request data get disposed immediatly after Ok(), and Task.Run is not awaited.
Reading at first the request and incapsultating into ipnContext.RequestBody like you suggested seem to work.
I'm testing it right now. Thanks
It seems to work like expected.
Thanks MalpenZibo for your precious support.
@MalpenZibo Any chance you would be able to create a PR to add this to the sample repo? Thanks!
I will do it as soon as I can
Thanks @MalpenZibo 😄
General information
Issue description
Please add a working example for ASP.NET Core 2.0+. The C# example is for classic asp.net, is not working and many object does not exist.
Thanks