I am receiving a POST request from an Unity app in x-form-encoded format into my NodeJs web-server .
I use app.use(bodyParser.urlencoded({ extended: true})) , to parse the content . But the req.body returns two objects , problem is that I cannot access the property of an individual property since they are neither wrapped in an array or object .
On console.log(req.body)
I get the following result
{ sessionId: '5ujgp6vwk1pivth4', gameId: '1', level: '0', score: '0' }
{ sessionId: '5ujgp6vwk1pivth4', gameId: '2', level: '0', score: '0' }
I want to know which type of datatype is this and how will I be able to access a particular property , suppose if I do
console.log(req.body.sessionId) , I get
5ujgp6vwk1pivth4
5ujgp6vwk1pivth4
Even if I try to push it into an array I still get the same result .
I am trying to fetch these objects into an array so that it will be easier for me to access them .
The script for the Express routes :
const express = require('express')
const path = require('path')
const hbs = require('hbs')
const bodyParser = require('body-parser')
const viewsRouter = require('./routers/views')
const apiRouter = require('./routers/api')
const cookieParser = require('cookie-parser')
require ('./db/mongoose')
const app = express()
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true}))
app.use(cookieParser())
const publicDirectoryPath = path.join(__dirname,'../public')
app.use(express.static(publicDirectoryPath))
const viewsPath = path.join(__dirname,'../templates/views')
const partialsPath = path.join(__dirname,'../templates/partials')
hbs.registerPartials(partialsPath)
hbs.registerHelper('ifCond', function(v1, v2, options) {
if(v1 === v2) {
return options.fn(this);
}
return options.inverse(this);
});
hbs.registerHelper("math", function(lvalue, operator, rvalue, options) {
lvalue = parseFloat(lvalue);
rvalue = parseFloat(rvalue);
return {
"+": lvalue + rvalue,
"-": lvalue - rvalue,
"*": lvalue * rvalue,
"/": lvalue / rvalue,
"%": lvalue % rvalue
}[operator];
});
app.use(viewsRouter)
app.use(apiRouter)
// For any of the un-handled routes
app.get('*',(req,res)=>{
res.render('error')
})
//Setting up the CORS functionality in Express for Making AJAX calls
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
app.set('views',viewsPath)
app.set('view engine','hbs')
app.listen(80,()=>{
console.log('Server Started on Port 80')
})
The Route responsible for the particular POST is
apiRouter.post('/api/updateScore/',async(req,res)=>{
console.log(req.body)
})
The client script is :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
using UnityEngine.SceneManagement;
using System.Text;
public class ScoreSender : MonoBehaviour {
public Authentication authentication;
public static readonly string scoreUrl = "https://eyenet.pythonanywhere.com/scores/";
public static ScoreSender instance;
public string Address = "127.0.0.1:8000";
// Use this for initialization
void Start () {
instance = this;
}
// Update is called once per frame
void Update () {
}
public void sendScore(int gameId, int level)
{
}
public void sendScore(string gameId, int level, int score)
{
string loginURL = Address+ "/api/updateScore/";
WWWForm form = new WWWForm();
form.AddField( "sessionId", authentication.session );
Dictionary<string, string> headers = form.headers;
byte[] rawData = form.data;
WWW www = new WWW(loginURL, rawData, headers);
}
// StartCoroutine(WaitForRequest(www));
public void saveScore(string gameId, int nextlevel, int score)
{
// get session id
string sessionCode = authentication.session;
// get score array
int noOfGames = PlayerPrefs.GetInt("totalGames",0);
// get or create score, level, nextlevel arrays
int gid = int.Parse(gameId);
//
int[] scoreArray = PlayerPrefsX.GetIntArray(sessionCode+"scores",0,noOfGames+1);
int[] nextLevelsArray = PlayerPrefsX.GetIntArray(sessionCode+"nextLevels",0,noOfGames+1);
nextLevelsArray[gid] = nextlevel;
scoreArray[gid] = score;
PlayerPrefsX.SetIntArray(sessionCode+"scores",scoreArray);
PlayerPrefsX.SetIntArray(sessionCode+"nextLevels",nextLevelsArray);
PlayerPrefsX.SetIntArray("gameLevels",nextLevelsArray);
Debug.Log("saved score and nextLevels offline");
}
//for sending score to the cloud
public void uploadScore()
{
//testing
//StartCoroutine(scoreSend(authentication.session,"1",45,34));
syncScore(authentication.session);
}
//for online sessions only
void syncScore(string sessionId)
{
Debug.Log("We are syncing the score for this session");
int noOfGames = PlayerPrefs.GetInt("totalGames",0);
int[] currentGamePlays = PlayerPrefsX.GetIntArray(sessionId+"currentGamePlays",0,noOfGames);
int[] scoreArray = PlayerPrefsX.GetIntArray(sessionId+"scores",0,noOfGames+1);
int[] nextLevelsArray = PlayerPrefsX.GetIntArray(sessionId+"nextLevels",0,noOfGames+1);
for(int i=0;i<noOfGames+1;i++)
{
if(currentGamePlays[i]==1) //if the game is played in this session
{
StartCoroutine( scoreSend(sessionId,""+i,nextLevelsArray[i],scoreArray[i]));
Debug.Log("score:"+scoreArray[i]);
Debug.Log("level:"+nextLevelsArray[i]);
}
}
}
public int noOfScores=0;
public void syncOfflineScore(int num,string actualId)
{
string sessionId = "offlineSession"+num;
int noOfGames = PlayerPrefs.GetInt("totalGames",0);
int[] currentGamePlays = PlayerPrefsX.GetIntArray(sessionId+"currentGamePlays",0,noOfGames+1);
int[] scoreArray = PlayerPrefsX.GetIntArray(sessionId+"scores",0,noOfGames+1);
int[] nextLevelsArray = PlayerPrefsX.GetIntArray(sessionId+"nextLevels",0,noOfGames+1);
for(int i=0;i<noOfGames+1;i++)
{
if(currentGamePlays[i]==1) //if the game is played in this session
{
noOfScores++;
}
}
for(int i=0;i<noOfGames+1;i++)
{
if(currentGamePlays[i]==1) //if the game is played in this session
{
StartCoroutine( offlineScoreSend(actualId,""+i,nextLevelsArray[i],scoreArray[i]));
Debug.Log("score:"+scoreArray[i]);
Debug.Log("level:"+nextLevelsArray[i]);
}
}
}
//working fine
IEnumerator scoreSend(string sessionId,string gameId,int nextlevel, int score)
{
string scoreUrl = authentication.Address+ "/api/updateScore/";
WWWForm form = new WWWForm();
form.AddField( "sessionId", sessionId );
form.AddField( "gameId", gameId );
form.AddField( "level", nextlevel);
form.AddField( "score", score);
Dictionary<string, string> headers = form.headers;
//Dictionary<string, string> headers = new Dictionary<string, string>();
//headers.Add("Content-Type", "application/json");
byte[] rawData = form.data;
WWW www = new WWW(scoreUrl, rawData, headers);
WWW data =www;
yield return data;
if(data.error!=null)
{
Debug.Log (data.error);
if(data.error == "Cannot connect to destination host")
{
}
}
else
{
Debug.Log(data.text);
ServerResponse res = JsonUtility.FromJson<ServerResponse>(data.text);
if(res.status==0)
{
Debug.Log("Updated score");
}
else
{
Debug.Log("Got an error");
}
}
}
//test it
IEnumerator offlineScoreSend(string sessionId,string gameId,int nextlevel, int score)
{
string scoreUrl = authentication.Address+ "/api/updateScore/";
WWWForm form = new WWWForm();
form.AddField( "sessionId", sessionId );
form.AddField( "gameId", gameId );
form.AddField( "level", nextlevel);
form.AddField( "score", score);
Dictionary<string, string> headers = form.headers;
byte[] rawData = form.data;
WWW www = new WWW(scoreUrl, rawData, headers);
WWW data =www;
yield return data;
if(data.error!=null)
{
Debug.Log (data.error);
if(data.error == "Cannot connect to destination host")
{
}
}
else
{
Debug.Log(data.text);
ServerResponse res = JsonUtility.FromJson<ServerResponse>(data.text);
if(res.status==0)
{
Debug.Log("Updated score");
noOfScores--;
if(noOfScores==0)
{
authentication.syncOfflineSessionsDataComplete();
}
}
else
{
Debug.Log("Got an error");
}
}
}
}
Unfortunately, what is happening is different than what you think is happening, and there isn't enough information here to figure out what is actually going on. You say that req.body is returning two objects, but that they are not wrapped in an array or another object, which is fundamentally impossible.
Given the information available, my best guess is that the bodyParser is working exactly as it should, but that whatever client application you are using to send requests is sending two requests where you think it's only sending one, so when you log it you are seeing two objects and assuming they came from a single call to console.log which doesn't appear to be the case.
To confirm that, I would probably stick something like this into the bottom of the file, and then use this to log instead of using console.log directly. This will demonstrate that it's two different log entries coming from two different invocations of the route.
let counter = 0;
function logWithCounter( ...msg ) {
console.log( counter++, ...msg );
}
Related
I have a Web Api which handles OpenId sign in such way:
[HttpGet]
[AllowAnonymous]
[Route("ExternalLogin", Name = "ExternalLogin")]
public async Task<IActionResult> ExternalLogin(string provider, string returnUrl = "")
{
ApplicationUser user = await GetAuthUser();
string userId = user?.Id;
var properties = _externalSignInService.ConfigureExternalAuthenticationProperties(provider, Url.Action("ExternalCallback", "Account", new { returnUrl, userId }));
return Challenge(properties, provider);
}
This is called from Angular app with JS redirect function (it is hardcoded, because I am trying to make it works firstly).
public loginExternal() {
window.location.href = `https://localhost:5001/v1/account/ExternalLogin?provider=Steam&returnUrl=${window.location.href}`;
}
After Steam login success, it makes to callback method specified there Url.Action(...)
[HttpGet]
[AllowAnonymous]
[Route("Steam", Name = "ExternalCallback")]
public async Task<ActionResult<LoginResponseDto>> ExternalCallback(string error = null, string returnUrl = "", string userId = null)
{
if (error != null)
{
return Redirect(returnUrl + "/unauthorized");
}
...sign in duties...
return ProduceLoginResponse(signInResult);
}
private ActionResult<LoginResponseDto> ProduceLoginResponse((AppSignInResult result, SignInData data) loginResults)
{
var (result, data) = loginResults;
return result switch
{
AppSignInResult.Success => Ok(new LoginResponseDto()
{
AccessToken = data.Token.AccessToken,
TokenType = data.Token.TokenType,
ExpiresIn = data.Token.GetRemainingLifetimeSeconds(),
Username = data.Username,
Email = data.Email,
IsExternalLogin = data.IsExternalLogin,
ExternalAuthenticationProvider = data.ExternalAuthenticationProvider
}),
_ => throw new InvalidEnumArgumentException($"Unknown sign-in result '{result}'.")
};
}
So in my Angular app I need to process this data (save JWT token in storage, etc..). How to do it keeping in mind that I used window.location.href redirect?
I've figured it out with saving user sign in data in HttpContext.Session storage and changing return approach from return ProduceLoginResponse(signInResult); to return Redirect(returnHost + "/sign-in"); and after redirect, in angular SignIn component inside ngOnInit method I am calling another endpoint to get my sign in data from HttpContext.Session storage.
[HttpGet]
[AllowAnonymous]
[Route("sign-in")]
public ActionResult<object> ProvideSignInDataFromSession()
{
var sessionData = HttpContext.Session.GetString(SignInDataKey);
if (sessionData != null)
{
var data = JsonConvert.DeserializeObject<SignInData>(sessionData);
return Ok(new
{
user = data.Username,
access_token = data.Token.AccessToken,
token_type = data.Token.TokenType,
expires_in = data.Token.GetRemainingLifetimeSeconds(),
});
}
return Redirect(CORS.CORS.returnOrigin + "/unauthorized");
}
I am trying to download the content of my s3 bucket, and when I hit the API endpoint the data shows in my Intellij console, but in my postman and browser console I simply get an empty object.
Is there a certain why I am supposed to receive this in an Axios request?
Axios -
downloadLocations() {
axios.get("http://localhost:8080/api/v1/targetLocation/downloadSearchData")
.then((res) => {
console.log(res.data)
// We will need to retrieve the data into a downloadable blob
// const content = new Blob([JSON.stringify(???)],{ type: 'text/plain;charset=utf-8' })
// const fileName = `test.txt`
// saveAs(content, fileName)
}, (error) => {
console.log(error);
});
}
Service -
public ByteArrayOutputStream downloadSearchData() throws IOException {
BasicAWSCredentials awsCredentials = new BasicAWSCredentials(awsAccessKey, awsSecretKey);
AmazonS3 s3client = AmazonS3ClientBuilder
.standard()
.withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
.withRegion(awsRegion)
.build();
var s3Object = s3client.getObject("downloadable-cases", "7863784198_2021-08-16T13_30_06.690Z.json");
var out = new ByteArrayOutputStream();
try (var in = s3Object.getObjectContent()) {
in.transferTo(out);
}
System.out.println(out);
return out;
}
Controller -
#GetMapping(value = "downloadSearchData")
public ByteArrayOutputStream downloadSearchData() throws IOException {
return targetLocationService.downloadSearchData();
}
Ok, I found the answer. I changed the Service and Controller return value to String and it now works perfectly
public String downloadSearchData() throws IOException {
BasicAWSCredentials awsCredentials = new BasicAWSCredentials(awsAccessKey, awsSecretKey);
AmazonS3 s3client = AmazonS3ClientBuilder
.standard()
.withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
.withRegion(Regions.GovCloud)
.build();
var s3Object = s3client.getObject("downloadable-cases", "7863784198_2021-08-16T13_30_06.690Z.json");
var out = new ByteArrayOutputStream();
try (var in = s3Object.getObjectContent()) {
in.transferTo(out);
}
return out.toString();
}
I am using Javascript Fetch API to invoke Dot net web API.
Using Visual Studio code for HTML/Javascript & Visual Studio 2019 for Dot Net Web API.
I am trying to implement login functionality using Dot net C# Web API, that will return JWT token in the response and use the token later to invoke separate Web API/Service (e.g. EmployeeInfo ).
Login page :
It displays user id and password fields and the button "Login"
Once user clicks on login button, the function fnlogin is invoked
function fnlogin() {
const uname = document.getElementById('uname').value;
const pwd = document.getElementById('pwd').value;
const logindata = {
username: uname,
password: pwd
}
const loginurl = 'http://localhost:13402/api/Auth/Login';
authenticate(loginurl, logindata);
}
async function authenticate(loginurl, logindata) {
console.log(logindata)
const response = await fetch(loginurl , {
method: "POST",
mode: "cors",
body: JSON.stringify(logindata),
headers: { "Content-type" : "application/json, charset=UTF-8"}
});
const rdata = await response.json();
console.log(rdata);
if (!rdata.success) {
document.getElementById("loginMessage").innerHTML = rdata.message;
return;
}
const inMemoryToken = rdata.data
localStorage.setItem('user', JSON.stringify(rdata));
window.location.href = "http://localhost:5500/Employeeinfo1.html";
}
The Web API returns JWT token properly.
The code in controller is as follows :
[HttpPost("Login")]
public async Task<ActionResult<ServiceResponse<string>>> Login(UserLoginDto request)
{
var response = await _authRepo.Login(
request.Username, request.Password
);
if (!response.Success)
{
return BadRequest(response);
}
return Ok(response);
}
``
The code in AuthRepository class is as follows :
public class AuthRepository : IAuthRepository
{
private readonly AppDbContext _context;
private readonly IConfiguration _configuration;
public AuthRepository(AppDbContext context, IConfiguration configuration)
{
_configuration = configuration;
_context = context;
}
public async Task<ServiceResponse<string>> Login(string username, string password)
{
var response = new ServiceResponse<string>();
var user = await _context.Users.FirstOrDefaultAsync(x => x.Username.ToLower().Equals(username.ToLower()));
if (user == null)
{
response.Success = false;
response.Message = "User not found.";
}
else if (!VerifyPasswordHash(password, user.PasswordHash, user.PasswordSalt))
{
response.Success = false;
response.Message = "Wrong password.";
}
else
{
response.Data = CreateToken(user);
}
return response;
}
public async Task<ServiceResponse<User>> Register(User user, string password)
{
ServiceResponse<User> response = new ServiceResponse<User>();
if (await UserExists(user.Username))
{
response.Success = false;
response.Message = "User already exists.";
return response;
}
CreatePasswordHash(password, out byte[] passwordHash, out byte[] passwordSalt);
user.PasswordHash = passwordHash;
user.PasswordSalt = passwordSalt;
_context.Users.Add(user);
await _context.SaveChangesAsync();
response.Data = user;
return response;
}
public async Task<bool> UserExists(string username)
{
if (await _context.Users.AnyAsync(x => x.Username.ToLower().Equals(username.ToLower())))
{
return true;
}
return false;
}
private void CreatePasswordHash(string password, out byte[] passwordHash, out byte[] passwordSalt)
{
using (var hmac = new System.Security.Cryptography.HMACSHA512())
{
passwordSalt = hmac.Key;
passwordHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
}
}
private bool VerifyPasswordHash(string password, byte[] passwordHash, byte[] passwordSalt)
{
using (var hmac = new System.Security.Cryptography.HMACSHA512(passwordSalt))
{
var computedHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
for (int i = 0; i < computedHash.Length; i++)
{
if (computedHash[i] != passwordHash[i])
{
return false;
}
}
return true;
}
}
private string CreateToken(User user)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.Username)
};
var key = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(_configuration.GetSection("AppSettings:Token").Value));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha512Signature);
var tokendDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = System.DateTime.Now.AddDays(1),
SigningCredentials = creds
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokendDescriptor);
return tokenHandler.WriteToken(token);
}
4. Once the JWT token is returned , it is stored in LocalStorage. I verified in Chrome Dev Tools
But after the next screen is displayed, when I check in Chrome dev tools, the object/token is no longer present in the Local Storage.
Is this because of window.location.href will remove all local data in localStorage in the browser ?
I want to be able to use the token to pass to remaining html screens and web api
You are storing an item in the local storage of http://127.0.0.1:5500 and trying to read it from http://localhost:5500.
From Window.localStorage in MDN Web Docs:
The localStorage read-only property of the window interface allows you to access a Storage object for the Document's origin;
And about Origin:
Web content's origin is defined by the scheme (protocol), host (domain), and port of the URL used to access it. Two objects have the same origin only when the scheme, host, and port all match.
As you see, for the browser the origins are different because the host does not match: 127.0.0.1 is not the same string as localhost.
Use either localhost or 127.0.0.1 everywhere, but do not use both.
I tried to use Paypal Checkout SDK V2 in my Asp.Net MVC 5 project with .Net Framework 4.7.2 the but this seems to be something missing or wrong from my side.
I am just trying to integrate Paypal Checkout sdk and want to create an order so that my payment 'll be proceed by some paypal user and I only want to show one Pay Now button.
The SDK link is here:
https://github.com/paypal/Checkout-NET-SDK
and followed these links
https://developer.paypal.com/docs/checkout/reference/server-integration/get-transaction/#on-the-client
https://developer.paypal.com/demo/checkout/#/pattern/server
This is my code for trying it
C# Code
public class CheckoutController : Controller
{
public ActionResult Index()
{
ViewBag.ClientId = PayPalClient.ClientId;
ViewBag.CurrencyCode = "GBP"; // Get from a data store
ViewBag.CurrencySign = "£"; // Get from a data store
return View();
}
//[Route("api/paypal/checkout/order/create")]
public async static Task<HttpResponse> createOrder()
{
HttpResponse response;
// Construct a request object and set desired parameters
// Here, OrdersCreateRequest() creates a POST request to /v2/checkout/orders
var order = new OrderRequest()
{
CheckoutPaymentIntent = "CAPTURE",
PurchaseUnits = new List<PurchaseUnitRequest>()
{
new PurchaseUnitRequest()
{
AmountWithBreakdown = new AmountWithBreakdown()
{
CurrencyCode = "USD",
Value = "100.00"
}
}
},
ApplicationContext = new ApplicationContext()
{
ReturnUrl = "https://www.example.com",
CancelUrl = "https://www.example.com"
}
};
// Call API with your client and get a response for your call
var request = new OrdersCreateRequest();
request.Prefer("return=representation");
request.RequestBody(order);
response = await PayPalClient.Client().Execute(request);
var statusCode = response.StatusCode;
Order result = response.Result<Order>();
Console.WriteLine("Status: {0}", result.Status);
Console.WriteLine("Order Id: {0}", result.Id);
Console.WriteLine("Intent: {0}", result.CheckoutPaymentIntent);
Console.WriteLine("Links:");
foreach (LinkDescription link in result.Links)
{
Console.WriteLine("\t{0}: {1}\tCall Type: {2}", link.Rel, link.Href, link.Method);
}
return response;
}
public async static Task<HttpResponse> captureOrder()
{
// Construct a request object and set desired parameters
// Replace ORDER-ID with the approved order id from create order
var request = new OrdersCaptureRequest("APPROVED-ORDER-ID");
request.RequestBody(new OrderActionRequest());
HttpResponse response = await PayPalClient.Client().Execute(request);
var statusCode = response.StatusCode;
Order result = response.Result<Order>();
Console.WriteLine("Status: {0}", result.Status);
Console.WriteLine("Capture Id: {0}", result.Id);
return response;
}
/// <summary>
/// This action is called when the user clicks on the PayPal button.
/// </summary>
/// <returns></returns>
//[Route("api/paypal/checkout/order/create")]
public async Task<SmartButtonHttpResponse> Create()
{
var request = new PayPalCheckoutSdk.Orders.OrdersCreateRequest();
request.Prefer("return=representation");
request.RequestBody(OrderBuilder.Build());
// Call PayPal to set up a transaction
var response = await PayPalClient.Client().Execute(request);
// Create a response, with an order id.
var result = response.Result<PayPalCheckoutSdk.Orders.Order>();
var payPalHttpResponse = new SmartButtonHttpResponse(response)
{
orderID = result.Id
};
return payPalHttpResponse;
}
/// <summary>
/// This action is called once the PayPal transaction is approved
/// </summary>
/// <param name="orderId"></param>
/// <returns></returns>
//[Route("api/paypal/checkout/order/approved/{orderId}")]
public ActionResult Approved(string orderId)
{
return Json("Sucess", JsonRequestBehavior.AllowGet);
}
/// <summary>
/// This action is called once the PayPal transaction is complete
/// </summary>
/// <param name="orderId"></param>
/// <returns></returns>
//[Route("api/paypal/checkout/order/complete/{orderId}")]
public ActionResult Complete(string orderId)
{
// 1. Update the database.
// 2. Complete the order process. Create and send invoices etc.
// 3. Complete the shipping process.
return Json("Completed", JsonRequestBehavior.AllowGet);
}
/// <summary>
/// This action is called once the PayPal transaction is complete
/// </summary>
/// <param name="orderId"></param>
/// <returns></returns>
//[Route("api/paypal/checkout/order/cancel/{orderId}")]
public ActionResult Cancel(string orderId)
{
// 1. Remove the orderId from the database.
return Json("Cancel", JsonRequestBehavior.AllowGet);
}
/// <summary>
/// This action is called once the PayPal transaction is complete
/// </summary>
/// <param name="orderId"></param>
/// <returns></returns>
//[Route("api/paypal/checkout/order/error/{orderId}/{error}")]
public ActionResult Error(string orderId,
string error)
{
// Log the error.
// Notify the user.
string temp = System.Web.HttpUtility.UrlDecode(error);
return Json(temp, JsonRequestBehavior.AllowGet);
}
}
public static class OrderBuilder
{
/// <summary>
/// Use classes from the PayPalCheckoutSdk to build an OrderRequest
/// </summary>
/// <returns></returns>
public static OrderRequest Build()
{
// Construct a request object and set desired parameters
// Here, OrdersCreateRequest() creates a POST request to /v2/checkout/orders
OrderRequest order = new OrderRequest()
{
CheckoutPaymentIntent = "CAPTURE",
PurchaseUnits = new List<PurchaseUnitRequest>()
{
new PurchaseUnitRequest()
{
AmountWithBreakdown = new AmountWithBreakdown()
{
CurrencyCode = "USD",
Value = "100.00"
}
}
},
ApplicationContext = new ApplicationContext()
{
ReturnUrl = "https://www.example.com",
CancelUrl = "https://www.example.com"
}
};
// Call API with your client and get a response for your call
var request = new OrdersCreateRequest();
request.Prefer("return=representation");
request.RequestBody(order);
return order;
}
}
public class PayPalClient
{
public static string ClientId = " your client id";
public static string Secrets = "your client secret";
public static string SandboxClientId { get; set; } =
"<alert>{SandboxClientId}</alert>";
public static string SandboxClientSecret { get; set; } =
"<alert>{SandboxClientSecret}</alert>";
public static string LiveClientId { get; set; } =
"<alert>{PayPal LIVE Client Id}</alert>";
public static string LiveClientSecret { get; set; } =
"<alert>{PayPal LIVE Client Secret}</alert>";
public static PayPalEnvironment Environment()
{
//return new SandboxEnvironment("<alert>SandboxClientId</alert>",
// "<alert>SandboxClientSecret</alert>");
return new SandboxEnvironment(ClientId, Secrets);
}
public static PayPalCheckoutSdk.Core.PayPalHttpClient Client()
{
return new PayPalHttpClient(Environment());
}
public static PayPalCheckoutSdk.Core.PayPalHttpClient Client(string refreshToken)
{
return new PayPalHttpClient(Environment(), refreshToken);
}
public static String ObjectToJSONString(Object serializableObject)
{
MemoryStream memoryStream = new MemoryStream();
var writer = JsonReaderWriterFactory.CreateJsonWriter(memoryStream,
Encoding.UTF8,
true,
true,
" ");
var ser = new DataContractJsonSerializer(serializableObject.GetType(),
new DataContractJsonSerializerSettings
{
UseSimpleDictionaryFormat = true
});
ser.WriteObject(writer,
serializableObject);
memoryStream.Position = 0;
StreamReader sr = new StreamReader(memoryStream);
return sr.ReadToEnd();
}
}
public class SmartButtonHttpResponse
{
readonly PayPalCheckoutSdk.Orders.Order _result;
public SmartButtonHttpResponse(PayPalHttp.HttpResponse httpResponse)
{
Headers = httpResponse.Headers;
StatusCode = httpResponse.StatusCode;
_result = httpResponse.Result<PayPalCheckoutSdk.Orders.Order>();
}
public HttpHeaders Headers { get; }
public HttpStatusCode StatusCode { get; }
public PayPalCheckoutSdk.Orders.Order Result()
{
return _result;
}
public string orderID { get; set; }
}
View html is
<!-- Set up a container element for the PayPal smart button -->
<div id="paypal-button-container"></div>
#section scripts
{
<script src="https://www.paypal.com/sdk/js?client-id=#ViewBag.ClientId"></script>
<script type="text/javascript">
var orderId;
function httpGet(url) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", url, false);
xmlHttp.send(null);
return xmlHttp.responseText;
}
paypal.Buttons({
// Set up the transaction
enableStandardCardFields: true,
createOrder: function (data, actions) {
orderId = data.orderID;
console.log("Start");
console.log(data);
console.log(actions);
console.log("End");
return fetch(
'/PaypalPayments/create/', {
method: 'post'
}).then(function (res) {
return res.json();
}).then(function (data) {
return data.orderID;
});
},
// Finalise the transaction
onApprove: function (data, actions) {
return fetch('/PaypalPayments/approved/' + data.orderID, {
method: 'post'
}).then(function (res) {
return actions.order.capture();
}).then(function (details) {
// (Preferred) Notify the server that the transaction id complete
// and have an option to display an order completed screen.
window.location.replace('/PaypalPayments/complete/' +
data.orderID + '/#ViewBag.CurrencyCode');
// OR
// Notify the server that the transaction id complete
//httpGet('/api/paypal/checkout/order/complete/' + data.orderID);
// Show a success message to the buyer
alert('Transaction completed by ' + details.payer.name.given_name + '!');
});
},
// Buyer cancelled the payment
onCancel: function (data, actions) {
httpGet('/PaypalPayments/cancel/' + data.orderID);
},
// An error occurred during the transaction
onError: function (err) {
debugger;
fetch(
'/PaypalPayments/error/', {
method: 'post',
headers: { 'Content-type': 'application/json' },
body: JSON.stringify({ orderId: orderId, error: encodeURIComponent(err)})
}).then(function (res) {
return res.json();
}).then(function (data) {
return data.orderID;
});
}
}).render('#paypal-button-container');
</script>
}
The error which I 'll have to receive is given below:
My create post request is successful by returning 200 status.
After this request, the control instantly goes to Error function and throwing this error.
Error: Unexpected token P in JSON at position 0"
The complete error detail is mentioned below
SyntaxError: Unexpected token P in JSON at position 0
Error: Unexpected token P in JSON at position 0
at Qt.error (https://www.paypal.com/sdk/js?client-id=ATiT9hZAk22xK68Sg9cE3qg24jH1GP9eTm6XW2O47fPl3cligEblR35E1f24OnLg8XOmD7Y_tcnHDhPZ:2:60544)
at Object.<anonymous> (https://www.paypal.com/sdk/js?client-id=ATiT9hZAk22xK68Sg9cE3qg24jH1GP9eTm6XW2O47fPl3cligEblR35E1f24OnLg8XOmD7Y_tcnHDhPZ:2:68311)
at JSON.parse (<anonymous>)
at o (https://www.paypal.com/sdk/js?client-id=ATiT9hZAk22xK68Sg9cE3qg24jH1GP9eTm6XW2O47fPl3cligEblR35E1f24OnLg8XOmD7Y_tcnHDhPZ:2:68170)
at dr (https://www.paypal.com/sdk/js?client-id=ATiT9hZAk22xK68Sg9cE3qg24jH1GP9eTm6XW2O47fPl3cligEblR35E1f24OnLg8XOmD7Y_tcnHDhPZ:2:68323)
at u.on (https://www.paypal.com/sdk/js?client-id=ATiT9hZAk22xK68Sg9cE3qg24jH1GP9eTm6XW2O47fPl3cligEblR35E1f24OnLg8XOmD7Y_tcnHDhPZ:2:72994)
at br (https://www.paypal.com/sdk/js?client-id=ATiT9hZAk22xK68Sg9cE3qg24jH1GP9eTm6XW2O47fPl3cligEblR35E1f24OnLg8XOmD7Y_tcnHDhPZ:2:73131)
at https://www.paypal.com/sdk/js?client-id=ATiT9hZAk22xK68Sg9cE3qg24jH1GP9eTm6XW2O47fPl3cligEblR35E1f24OnLg8XOmD7Y_tcnHDhPZ:2:79250
at Function.n.try (https://www.paypal.com/sdk/js?client-id=ATiT9hZAk22xK68Sg9cE3qg24jH1GP9eTm6XW2O47fPl3cligEblR35E1f24OnLg8XOmD7Y_tcnHDhPZ:2:14118)
at https://www.paypal.com/sdk/js?client-id=ATiT9hZAk22xK68Sg9cE3qg24jH1GP9eTm6XW2O47fPl3cligEblR35E1f24OnLg8XOmD7Y_tcnHDhPZ:2:79047
Error: Unexpected token P in JSON at position 0
at Qt.error (https://www.paypal.com/sdk/js?client-id=ATiT9hZAk22xK68Sg9cE3qg24jH1GP9eTm6XW2O47fPl3cligEblR35E1f24OnLg8XOmD7Y_tcnHDhPZ:2:60544)
at Array.<anonymous> (https://www.paypal.com/sdk/js?client-id=ATiT9hZAk22xK68Sg9cE3qg24jH1GP9eTm6XW2O47fPl3cligEblR35E1f24OnLg8XOmD7Y_tcnHDhPZ:2:68311)
at JSON.parse (<anonymous>)
at o (https://www.paypal.com/sdk/js?client-id=ATiT9hZAk22xK68Sg9cE3qg24jH1GP9eTm6XW2O47fPl3cligEblR35E1f24OnLg8XOmD7Y_tcnHDhPZ:2:68170)
at dr (https://www.paypal.com/sdk/js?client-id=ATiT9hZAk22xK68Sg9cE3qg24jH1GP9eTm6XW2O47fPl3cligEblR35E1f24OnLg8XOmD7Y_tcnHDhPZ:2:68323)
at u.on (https://www.paypal.com/sdk/js?client-id=ATiT9hZAk22xK68Sg9cE3qg24jH1GP9eTm6XW2O47fPl3cligEblR35E1f24OnLg8XOmD7Y_tcnHDhPZ:2:72994)
at br (https://www.paypal.com/sdk/js?client-id=ATiT9hZAk22xK68Sg9cE3qg24jH1GP9eTm6XW2O47fPl3cligEblR35E1f24OnLg8XOmD7Y_tcnHDhPZ:2:73131)
at https://www.paypal.com/sdk/js?client-id=ATiT9hZAk22xK68Sg9cE3qg24jH1GP9eTm6XW2O47fPl3cligEblR35E1f24OnLg8XOmD7Y_tcnHDhPZ:2:79250
at Function.n.try (https://www.paypal.com/sdk/js?client-id=ATiT9hZAk22xK68Sg9cE3qg24jH1GP9eTm6XW2O47fPl3cligEblR35E1f24OnLg8XOmD7Y_tcnHDhPZ:2:14118)
at https://www.paypal.com/sdk/js?client-id=ATiT9hZAk22xK68Sg9cE3qg24jH1GP9eTm6XW2O47fPl3cligEblR35E1f24OnLg8XOmD7Y_tcnHDhPZ:2:79047
Can anybody guide me properly? I tried different articles but I am still getting these errors.
When you fetch /PaypalPayments/create/ from your server , what do you get back?
Obviously you aren't getting back valid JSON, but your code is trying to parse it as JSON.
Change your server code to only give back valid JSON. The code actually seems okay, so perhaps there is something wrong with the /PayPalPayments/create/ route that is hardcoded in the client-side code.
It would also be wise to change your client-side code to follow the best sample available, which is at https://developer.paypal.com/demo/checkout/#/pattern/server and demonstrates how to properly handle errors for the capture
(Just noticed the code in your example uses actions.order.capture() combined with this server-side integration, which is rather awful -- seems the server-side captureOrder task never even gets called, which is rather the whole point of using server-side APIs in the first place, if you don't capture from the server you get none of the benefits)
I have a javascript function that makes a post to a controller API. Here´s the code:
exports.importList = function (req, res) {
res.setHeader('Content-Type', 'application/json');
var agencyId = req.user.agency_id;
var userId = req.user.id;
data = {
id_list: req.body.ids,
remoteHost:'127.0.0.1',
userId : userId,
agencyId:agencyId
};
call = '/ilist/importer/list/'; //Spring route
fetcher.post(call, 'post', data, function (err, result) {
console.log(data);
})
}
req.body.ids is an array of string values, so the data I want to send to my Controller has this structure:
{ id_list: [ '2147041', '2155271' ],
remoteHost: '127.0.0.1',
userId: 'user',
agencyId: 1 }
My controller method:
#RequestMapping(value="/list/", method = RequestMethod.POST, headers = "Accept=application/json")
public #ResponseBody RemaxResponse importPropertyList(#RequestBody ArrayList<String> data ) {
List<Long> ids = new ArrayList<>();
for (String id : data.id_list) {
ids.add(Long.valueOf(id));
}
response = ilistIImporterService.importPropertyList(ids);
return response;
}
I need to take in my Controller the array of strings and store it in an array of integer, and the other parameters store in integer variables.
Now, I'm getting that the data I send from javascript is sintactically incorrect. What's the proper way to do this?
If you want to send the whole object, I'd create a pojo and use that as #RequestBody like
public #ResponseBody RemaxResponse importPropertyList(#RequestBody RequestObject data ) {
Now spring can parse the whole data nicely to the given pojo, and you can simply use the getters to obtain the data you need.
A pojo could look like
public class RequestObject {
private List<Long> idList = null;
private String remoteHost;
private String userId;
private Integer agencyId;
public List<Long> getIdList() {
return idList;
}
public void setIdList(List<Long> idList) {
this.idList = idList;
}
public String getRemoteHost() {
return remoteHost;
}
public void setRemoteHost(String remoteHost) {
this.remoteHost = remoteHost;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public Integer getAgencyId() {
return agencyId;
}
public void setAgencyId(Integer agencyId) {
this.agencyId = agencyId;
}
}