Overview
I have an ASP.net core API that has one controller. There are two relevant methods. The first Is LoginGet, which takes in a long token through the URL. Once the token is parsed through a separate core authentication API, the long token is stored like so:
Request.HttpContext.Session.Set(longToken, Encoding.UTF8.GetBytes(stringResult));
Then, the API generates a GUID for the user to store in session storage. After the user retrieves that GUID, they can pass it through GetAll, which filters through a database only picking out the data that correlates to the user that passed through the GUID. That means the API has to take the GUID, and compare it to the long token that was stored in session at the LoginGet request.
The Good
It all works when I hit these request from my localhost. Here is the series of request I make:
First I hit LoginGet():
http://localhost:5000/api/picker/login?token=ljadsljdaslkjsdfljkljgsdfkjgfFSDDFSsakgfjhdfkhdsfFDAS/asdfkjhdsf+sfdhjkfdsa
That returns a GUID like this:
58fa0fec7726433da47dce966b313c69
Then I hit GetAll():
http://localhost:5000/api/picker?ath=58fa0fec7726433da47dce966b313c69
That returns my json data
The Bad
The above example works! So what's wrong? When I do those exact same request (but with a different random GUID) from my Angular 2 application which is being served locally on a different port (http://localhost:3000/), LoginGet() is successful and returns a new GUID, but when I make the second request, GetAll(), immediately after the first, while debugging, I see that the Session has changed on the API, as if I were doing the second request from a new browser, or I just cleared my cookies.
This was not the case when I was simply making these calls from the browser, myself, manually. I have my request being console logged on the front end, and I get the exact same request URLs as I explained above.
I feel like this has to be an issue with how I am making the request on the front end. But I don't see how that could be the case. Even with routing, my session should remain the same.
Here are the two request methods relevant to this issue (Angular 2)
public Get(): Observable<{}> {
let newQuery = 'http://localhost:5000/api/picker?ath=' + sessionStorage.getItem('user.token');
return this.http.get(newQuery).map(this.extractData)
.catch(this.handleError);
}
public GetLogin(longToken: string) {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
let body = JSON.stringify({ password: longToken });
return this.http.get('http://localhost:5000/api/picker/login?token=' + longToken);
}
The order of operations for those request go like so:
1. A longToken is given by the user
2. The long token is passed to GetLogin(longToken)
3. The result is returned and immediately subscribed to.
4. On success, the generated GUID from the API is set in sessionStorage
5. The appilication routes to a different component (this.router.navigateByUrl('/library?pvy=me'))
6. After routing, in library component ngOnInit, Get() is called and uses the GUID stored in sessionStorage.
7. The result is immediately subscribed to in the library component.
That's It
What could be happening in my Angular 2 App that changes the session on the API? Or am just completely lost on how Sessions work in .net core?
What I found out is that when hitting the API externally, from the browser Address bar, Headers were being generated automatically. Which means the cookies from the session could be stored there. When I was calling my Get() method from the angular 2 App, I wasn't adding a header to my request, so the API assumed it was a new session.
My new Get() method on the angular 2 app:
public Get(): Observable<{}> {
let newQuery = 'http://localhost:5000/api/picker?ath=' + sessionStorage.getItem('user.token');
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.get(newQuery, options).map(this.extractData)
.catch(this.handleError);
}
Related
I am new to JWT with basic idea of how it works. I have set the jwt token inside cookie from my web api.
Response.Cookies.Append("X-Access-Token", foundUser.Token
, new CookieOptions { HttpOnly = true }) ;
Now i am trying to call a web api get request which is marked as authorised from my agular application.
But inside angular i dont have a way to send the cookie. As per few documents i came to know that http only cookies are sent directly with our interference but i am not able to fetch the data with unauthorised error, which means that the token is not being used. I have not included the same in the get method as well. see below.
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[HttpGet]
public async Task<ActionResult<IEnumerable<Invoice>>> GetInvoices([FromQuery]QueryParameters queryParameters)
{
return await _uOW.InvoiceRepository.GetInvoices(queryParameters);
}
Do i have to add some logic to read the cookie in backend? I didnt find any answer for this.
Please let me know if there is something missing.
Also inside angular i have not written any special code for calling this get. Is that required?
var headerForToken = new HttpHeaders({'Authorization':`Bearer ${sessionStorage.getItem("token")}`});
return this.http.get<any>(this.APIUrl+'Invoices/GetInvoices',{headers:headerForToken });
This is my current approach in which i am using local storage, but i really need to use the http only cookie.
If there is any link for solution or anything that would be really helpfull
Update 1: So we need to add a domain for this. I tried adding domain still it is not visible when i try to see the cookies.
Response.Cookies.Append("X-Access-Token", foundUser.Token
, new CookieOptions { HttpOnly = true, Domain = ".localhost:4200" }) ;
I'm integrating Amazon pay with my website, using the v2 c# SDK from amazon (MVC App), in the sandbox. Setup is all good, I created the keys, return urls, etc.
In my checkout process, I create the CheckoutSession, which is successful. I click on the Amazon Pay button, log in using my test buyer account, and "pay" for the item using their valid test credit card.
Amazon redirects to my return URL with the checkout session id as expected.
When I try to CompleteCheckoutSession, the result is an error back from the Amazon API
error: InvalidCheckoutSessionStatus
message: You tried to call an operation on a Checkout Session that is in a state where that operation is not allowed
I put in a test line of code to retrieve the CheckoutSession to look at it before I try to complete it, and it shows that the current status is "Open", which is the correct status when trying to complete it, so I'm at a loss at why the checkout session status is invalid.
EDIT:
Note I'm using this flow for my transaction, so there is no "review" of the transaction. Buyer chooses his items on my site.
https://amazonpaycheckoutintegrationguide.s3.amazonaws.com/amazon-pay-apb-checkout/additional-payment-button-overview.html
Also note, I'm creating the payload dynamically according to this:
https://amazonpaycheckoutintegrationguide.s3.amazonaws.com/amazon-pay-checkout/amazon-pay-script.html#render-button-using-a-checkout-session-object
So when the amazonpay button is clicked, it calls a method on my site which builds the payload which begins the CheckoutSession. I then have the Amazon Session ID in my cache which I save it, and save the total. The payload is returned to the Amazon pay script which then takes me to the Amazon Site. I choose the payment type and click "continue to checkout", which sends me back to my site with the SessionId to do the "complete" step.
My request to CompleteCheckoutSession(sessionId)
{"chargeAmount":{"amount":99,"currencyCode":"USD"}}
result.RawResponse from the client.CompleteCheckoutSession(sessionId) method:
{
"reasonCode":"InvalidCheckoutSessionStatus",
"message":"You tried to call an operation on a Checkout Session that is in a state where that operation is not allowed"
}
EDIT SAMPLE CODE:
I created a brand new test MVC app with basic functionality:
public ActionResult Index()
{
var client = InitiateClient(); //hidden for security
// prepare the request
var request = new CreateCheckoutSessionRequest
(
checkoutReviewReturnUrl: "http://localhost:44300/home/completecheckout",
storeId: "amzn1.application-oa2-client.mystoreid"
);
request.PaymentDetails.PaymentIntent = Amazon.Pay.API.WebStore.Types.PaymentIntent.AuthorizeWithCapture;
request.PaymentDetails.ChargeAmount.Amount = 99;
request.PaymentDetails.ChargeAmount.CurrencyCode = Currency.USD;
// generate the signature and payload string that is passed back to the frontend
ViewBag.Signature = client.GenerateButtonSignature(request);
ViewBag.Payload = request.ToJson();
return View();
}
In the Index.cshtml file:
<div id="AmazonPayButton"></div>
<script src="https://static-na.payments-amazon.com/checkout.js"></script>
<script type="text/javascript" charset="utf-8">
amazon.Pay.renderButton('#AmazonPayButton', {
merchantId: 'mymerchantid',
ledgerCurrency: 'USD',
sandbox: true,
checkoutLanguage: 'en_US',
productType: 'PayOnly',
placement: 'Checkout',
buttonColor: 'Gold',
createCheckoutSessionConfig: {
payloadJSON: '#Html.Raw(ViewBag.Payload)', // string generated in step 2
signature: '#Html.Raw(ViewBag.Signature)', // signature generated in step 3
publicKeyId: 'AGPTYXGL5VH6PSYLJUSHTKW6'
}
});
</script>
And finally, the completecheckout code, which is unsuccessful:
public ActionResult CompleteCheckout(string amazonCheckoutSessionId)
{
var client = InitiateClient(); //hidden for security
var request = new CompleteCheckoutSessionRequest(99.00M, Currency.USD);
// send the request
var result = client.CompleteCheckoutSession(amazonCheckoutSessionId, request);
// check if API call was successful
if (!result.Success)
{
throw new Exception("API Call unsuccessful");
}
return View();
}
NOTE: certain keys obfuscated, but actual keys are in the sample code.
Manually creating the CheckoutSession doesn't work with the "no review page" flow that you are following. If you want to integrate this flow, you'll have to let Amazon Pay create the CheckoutSession for you using the createCheckoutSessionConfig parameter in the button code, see here: https://amazonpaycheckoutintegrationguide.s3.amazonaws.com/amazon-pay-apb-checkout/add-the-amazon-pay-button.html#4-render-the-button
EDIT:
The code sample below shows how to construct the payload and signature for the 'no order review' payment flow. Please note the CheckoutMode parameter, that instructs Amazon Pay to immediately process the payment, and the checkoutResultReturnUrl, that defines the URL where the buyer is sent to for completing the checkout. When the user arrives at that URL, you will have to call Complete CheckoutSession, which should now succeed.
public ActionResult Index()
{
var client = InitiateClient(); //hidden for security
// prepare the request
var request = new CreateCheckoutSessionRequest
(
checkoutReviewReturnUrl: null,
storeId: "amzn1.application-oa2-client.mystoreid"
);
// instructs Amazon Pay to immediately process the payment
request.WebCheckoutDetails.CheckoutMode = CheckoutMode.ProcessOrder;
request.WebCheckoutDetails.CheckoutResultReturnUrl = "http://localhost:44300/home/completecheckout";
// payment details
request.PaymentDetails.PaymentIntent = Amazon.Pay.API.WebStore.Types.PaymentIntent.AuthorizeWithCapture;
request.PaymentDetails.ChargeAmount.Amount = 99;
request.PaymentDetails.ChargeAmount.CurrencyCode = Currency.USD;
// generate the signature and payload string that is passed back to the frontend
ViewBag.Signature = client.GenerateButtonSignature(request);
ViewBag.Payload = request.ToJson();
return View();
}
I had a similar issue and it takes me a very long time to figure it out. This is what happened to me.
I was not intent to create production code. I just want to create a test page with only Amazon Pay button. I used several node js script to getCheckoutSession/updateCheckoutSession. However, when I tried completeCheckoutSession, I got "InvalidCheckoutSessionStatus".
In the end, I found, after I setup all the required parameter and remove all constraint, the updateCheckoutSession gives me a "amazonPayRedirectUrl" in API return. That URL is really important. You need to go that URL in order to complete the review...
This is from Amazon official website:
"Once there are no constraints, the response will return a unique
amazonPayRedirectUrl. Redirect the buyer to that URL to complete
checkout."
The other way to solve this issue is mentioned by "Daniel Lemke". In that way, shoppers don't need to confirm the session. However, you need to make sure to provide all useful information in webCheckoutDetails (paymentDetails, addressDetails). If you only give checkoutMode, you will get "MissingParameterValue".
This is from Amazon official website:
"paymentDetails is required when using 'ProcessOrder'. addressDetails
is also required if you use 'ProcessOrder' with productType set to
'PayAndShip'"
We're trying to receive payment with cryptocurrencies using coinpayment IPN. We are able to create a request and able to do a payment. However, not able to get success or failure response while user come back to the seller side.
Here is how payment request created:
public ActionResult IPN()
{
var uri = new UriBuilder("https://www.coinpayments.net/index.php");
uri.SetQueryParam("cmd", "_pay_auto");
uri.SetQueryParam("merchant", "merchant_key");
uri.SetQueryParam("allow_extra", "0");
uri.SetQueryParam("currency", "USD");
uri.SetQueryParam("reset", "1");
uri.SetQueryParam("success_url", "http://localhost:49725/home/SuccessResponse"); //todo: redirect to confirm success page
uri.SetQueryParam("key", "wc_order_5b7b84b91a882");
uri.SetQueryParam("cancel_url", "http://localhost:49725/home/FailiureResponse");
uri.SetQueryParam("order_id", "36");
uri.SetQueryParam("invoice", "PREFIX-36");
uri.SetQueryParam("ipn_url", "http://localhost:49725/?wc-api=WC_Gateway_Coinpayments");
uri.SetQueryParam("first_name", "John");
uri.SetQueryParam("last_name", "Smith");
uri.SetQueryParam("email", "a#a.com");
uri.SetQueryParam("want_shipping", "1");
uri.SetQueryParam("address1", "228 Park Ave S&address2");
uri.SetQueryParam("city", "New York");
uri.SetQueryParam("state", "NY");
uri.SetQueryParam("zip", "10003-1502");
uri.SetQueryParam("country", "US");
uri.SetQueryParam("item_name", "Order 33");
uri.SetQueryParam("quantity", "1");
uri.SetQueryParam("amountf", "100.00000000");
uri.SetQueryParam("shippingf", "0.00000000");
return Redirect(uri.ToString());
}
This will be redirected to the coinpayment site, once payment done, it is showing the following screen.
And trying to get data when user click on back to seller's site, I have tried to get data using Request.Form, but not getting any value in form.
The same thing, working with this woocommerce code, but I have no idea of PHP and how they are dealing with it.
Any thought to get IPN response?
Note: there is no development documentation or sample code available for IPN in .NET
Edit
I'm trying to get value from IPN success
Public ActionResult SuccessResponse()
{
var ipn_version = Request.Form["ipn_version"];
var ipn_id = Request.Form["ipn_id"];
var ipn_mode = Request.Form["ipn_mode"];
var merchant = Request.Form["merchant"];
var txn_id = Request.Form["txn_id"];
var status = Request.Form["status"];
return Content(status);
}
You cannot use localhost for a IPN callback. You must use a public domain name.
As an example I would change the following parameters:
var uri = new UriBuilder("https://www.coinpayments.net/api.php");
uri.SetQueryParam("success_url", "http://kugugshivom-001-site1.atempurl.com/Home/SuccessResponse");
uri.SetQueryParam("cancel_url", "http://kugugshivom-001-site1.atempurl.com/Home/FailiureResponse");
uri.SetQueryParam("ipn_url", "http://kugugshivom-001-site1.atempurl.com/Home/CoinPaymentsIPN"); // Public ActionResult CoinPaymentsIPN()
Since you are creating your own gateway you also need to implement it properly as described in the documentation at CoinPayments API and Instant Payment Notifications (IPN).
I have tested your success_url endpoint, and got status code: 100 (when entering status:100). I see you use form-data, but I don't know if that's on purpose / required.
Postman POST http://kugugshivom-001-site1.atempurl.com/Home/SuccessResponse
In Body tab form-data is selected with Bulk Edit values:
ipn_version:1.0
ipn_type:api
ipn_mode:hmac
ipn_id:your_ipn_id
merchant:your_merchant_id
txn_id:your_transaction_id
status:100
As updated answer stated by #Gillsoft AB, you should need to use valid IPN URL from the code end. Also webhook would not work with localhost. thus, you should listen the request with live server.
Simplest way to check webhook response is to use online tool such as Webhook Tester, it will provide an URL which you have to set as your IPN URL, whenever server will sends the data, you can simply see it to the web. To check that, create one URL and set as your IPN URL as below:
uri.SetQueryParam("ipn_url", "https://webhook.site/#/457f5c55-c9ce-4db4-8f57-20194c17d0ae");
After that run the payment cycle from local machine, payment server will sends notification to that IPN URL.
Make sure you understood it right! success_url and cancel_url are for user redirection, you will not get any response code over there, inspection of seller's store URL give your exact same URL that you have been passing though, so it is recommended to use unique URLs for each order(i.e add order id at last to the URL) which will give you an idea which order payment has been done or canceled.
http://localhost:49725/home/SuccessResponse?orderid=123
In order to test your local code, add following changes and deployed it to server.
1) Add one new method which will listen IPN response
[ValidateInput(false)]
public ActionResult IPNHandler()
{
byte[] param = Request.BinaryRead(Request.ContentLength);
string strRequest = Encoding.ASCII.GetString(param);
//TODO: print string request
//nothing should be rendered to visitor
return Content("");
}
2) Pass IPN URL while creating a request:
public ActionResult IPN()
{
var uri = new UriBuilder("https://www.coinpayments.net/index.php");
...
..
uri.SetQueryParam("success_url", "http://localhost:49725/home/SuccessResponse");
uri.SetQueryParam("cancel_url", "http://localhost:49725/home/FailiureResponse");
uri.SetQueryParam("ipn_url", "http://localhost:49725/home/IPNHandler");
....
..
return Redirect(uri.ToString());
}
You will get all status code responses in IPNHandler method.
Hope this helps!
I would like to transfer request one server to another server. Like server.transfer in C#. I know we can go for response.redirect or response.writeHead methods in nodejs but these have a client side (browser) interaction.
In my case, the user should not see where I am redirecting as it should server to server call.
As of now, I have achieved by unirest.
app.get('/home', function(request, response) {
unirest.get("www.example.com/userdetails").end(function (res) {
response.send(res.body)
});
});
But am expecting same in express. suggestions welcome.
You can do that inside express with http.request method, but you can also use higher level packages, like request and axios.
I created an express module to add req.transfer(path, preserveData) to all routes. This allows you to internally transfer requests without sending a redirect to the client:
var express = require('express');
var requestTransfer = require('express-request-transfer');
var app = express();
app.use(requestTransfer); // adds req.transfer method to all routes
// route 1
app.get('/api/time', function(req, res){
res.send(new Date());
});
// route 2
app.post('/', function(req, res){
// transfer without form/query data
req.transfer('/api/time');
// transfer with incoming form/query data
// req.transfer('/api/time', true);
});
It functions the same as Server.Transfer from C#. Source code on GitHub
I'm using C#, MVC and AngularJS.
My problem is that my MVC program creates a HttpContext.Current.Items["value"] and sets the value in the initial home controller, but when my AngularJS hits the application with an ajax call, it creates a new session and I can't get the value I set earlier in my HttpContext.Current.Items["value"] call.
Is there something I can do to fix this problem? I'd like to continue using HttpContext.Current.Items["value"].
Why do my AngularJS calls create new sessionids? The reason I know the sessions are new is because they have different ids when I use this:
String strSessionId = HttpContext.Session.SessionID;
HttpContext.Current.Items is a dictionary for request caching only. As soon as the request is finished all of the values in it will go out of scope.
// Will last until the end of the current request
HttpContext.Current.Items["key"] = value;
// When the request is finished, the value can no longer be retrieved
var value = HttpContext.Current.Items["key"];
HttpContext.Current.Session is a dictionary that stores data between requests.
// Will be stored until the user's session expires
HttpContext.Current.Session["key"] = value;
// You can retrieve the value again in the next request,
// until the session times out.
var value = HttpContext.Current.Session["key"];
The reason why your HttpRequest.Current.Items value is not available again is because you are setting it "in your home controller", which is a completely separate request from your AJAX call.
Session state depends on a cookie, so if the same cookie is sent back to the server, the data stored there can be retrieved. Fortunately, if you are on the same domain, AJAX will automatically send the cookie back to the server.
As for the SessionID changing, ASP.NET does not allocate storage for session until it is used. So you need to explicitly store something in session state in order to actually start a session. See this MSDN article for more information.