PayPal v2 Orders API - difference between OrdersGetRequest and OrdersCaptureRequest - spring

I'm using checkout-sdk with angular and spring boot. Here is the code I have on the angular side
paypal
.Buttons({
style: {
color: 'blue',
shape: 'pill',
label: 'pay',
height: 40
},
createOrder: (data, actions) => {
return actions.order.create({
purchase_units: [
{
description: 'Order id: '+this.order.id,
amount: {
currency_code: 'EUR',
value: this.order.totalPrice
}
}
]
});
},
onApprove: async (data, actions) => {
const order = await actions.order.capture();
this.paidFor = true;
this.checkoutPaypal(this.id,order.id)
},
onError: err => {
}
})
.render(this.paypalElement.nativeElement);
This is function used to retrieve payment, and save details to database..
public String checkoutPaypal(Integer id, String orderId) {
OrdersCaptureRequest request = new OrdersCaptureRequest(orderId);
request.requestBody(buildRequestBody());
HttpResponse<com.paypal.orders.Order> response;
try {
response = payPalClient.client().execute(request);
} catch (IOException e) {
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage());
}
for (PurchaseUnit purchaseUnit : response.result().purchaseUnits()) {
purchaseUnit.amountWithBreakdown();
for (Capture capture : purchaseUnit.payments().captures()) {
if (capture.status().equals("COMPLETED")) {
Order order = orderRepository.findById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Not found!"));
order.setOrderState(OrderState.PAID);
order.setPaymentDetails("Charge id: " + capture.id() + "; Status: " + capture.status() + "; Time paid: " + capture.createTime() + " GMT");
order.addOrderStateChange(OrderState.PAID, false);
sendEmail(order, " paid successfully!", "Thanks for your purchase!<br>We will work as hard as we can, to deliver the order to you, as soon as possible!");
orderRepository.save(order);
}
}
}
return "Successfully paid!";
}
Which worked few days ago.. But now I'm getting this error
{"name":"UNPROCESSABLE_ENTITY","details":[{"issue":"ORDER_ALREADY_CAPTURED","description":"Order already captured.If 'intent=CAPTURE' only one capture per order is allowed."}],"message":"The requested action could not be performed, semantically incorrect, or failed business validation.","debug_id":"f058cb447ccbb","links":[{"href":"https://developer.paypal.com/docs/api/orders/v2/#error-ORDER_ALREADY_CAPTURED","rel":"information_link","method":"GET"}]}
But after replacing
OrdersCaptureRequest request = new OrdersCaptureRequest(orderId);
request.requestBody(buildRequestBody());
HttpResponse<com.paypal.orders.Order> response;
with
OrdersGetRequest request = new OrdersGetRequest(orderId);
HttpResponse<com.paypal.orders.Order> response;
It works as it should.
So my question is, what is the difference between
https://developer.paypal.com/docs/checkout/reference/server-integration/capture-transaction/
and
https://developer.paypal.com/docs/checkout/reference/server-integration/get-transaction/ ?

One is to get the status of the order, the other is to capture the order.
Capturing should not be done once you have called actions.order.capture() on the client side, and will always return an error in such a case. It may also also return an error when the order has been created on the client side (actions.order.create())
A correct server-based integration uses neither actions.order.create() nor actions.order.capture() on the client side. It is very important not to do so, as transactions will be created without your server receiving any immediate direct response from PayPal.
Instead, create two routes on your server, one for 'Create Order' and one for 'Capture Order' as documented here. These routes should return JSON data. Before returning data, the capture route should check for success and act accordingly to send an email or whatever other business operation you need.
The approval flow to pair with the above two routes is https://developer.paypal.com/demo/checkout/#/pattern/server

Related

In Office.js, How do we use the Office.context.mailbox.item.saveAsync response (overcoming ErrorItemNotFound)?

The DOC says:
Note: If your add-in calls saveAsync on an item in compose mode in order to get an item ID to use with EWS or the REST API, be aware that when Outlook is in cached mode, it may take some time before the item is actually synced to the server. Until the item is synced, using the itemId will return an error.
As best I can tell, that's the cause of my ErrorItemNotFound problems trying to use that ID? (It's a shame Microsoft did not specifically tell us what error to expect).
Since my code is invoked asynchronously - how exactly do I wait for the noted "some time"? Do we set a timer to re-try every second or something? When do we give up?? Is there something else I can do which will give me a call-back to continue when the item sync has completed? [FYI - even waiting 10 seconds after the save does not work for me]
Be aware that I expect my users may be composing mail with large attachments, so while most no-attachment messages should sync in less than 1 second, folks attaching large pdf/zip/etc files could easily cause more than 1 minute delays here...
The best what you could do is to start polling for an item appeared on the server side. For example, you may try an ugly solution when you use sub-sequential EWS query with Id you've got from saveAsync in the loop and wait for success.
For example, I've noticed the following example how developers try to handle such scenarious:
app.makeEwsRequestAsync = function (request, callback, countRepeatIfCrash, callbackIfCrash) {
try {
Office.context.mailbox.makeEwsRequestAsync(request, function (asyncResult) {
try {
if (asyncResult.status !== 'succeeded') {
app.showError(asyncResult.error.message);
return;
} else {
var $result = app.getResponseElementByName(asyncResult.value, 'm:ResponseCode');
if ($result) {
var responseCOde = $result.text();
if (responseCOde !== 'NoError') {
if (countRepeatIfCrash > 0) {
setTimeout(function () {
app.makeEwsRequestAsync(request, callback, countRepeatIfCrash - 1);
}, 500);
} else if (callbackIfCrash) {
setTimeout(function() {
callbackIfCrash();
}, 500);
} else if (responseCOde === 'ErrorItemNotFound') {
app.showError('EWS ' + responseCOde, function () {
app.makeEwsRequestAsync(request, callback, 70);
});
}
else {
app.showError('EWS ' + responseCOde);
}
return;
}
}
}
callback(asyncResult);
} catch (e) {
app.showError(e);
}
});
} catch (e) {
app.showError(e);
}
}
See App for Outlook: EWS request failed with item Id returned by item.saveAsync on compose new message for more information.
You may also can try using the simple GetItem request:
<GetItem xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">
<ItemShape>
<t:BaseShape>IdOnly</t:BaseShape>
</ItemShape>
<ItemIds><t:ItemId Id="' + itemId + '"/></ItemIds>
</GetItem>
The request should return ChangeKey if item was created on exchange.

Outlook Add-in - AttachmentId is malformed

We've been having issues intermittently where we get an error when downloading email item content from EWS "AttachmentId is malformed". This is for ItemAttachment (Especially .eml files)
We could not figure why or how this is happening and noticed that the ones that were failing had + and / in the id's. Searching across the web landed me on this article. Although this article is from 2015, wondering if this is still happening.
This article blew my mind and made sense (kind of) and implementing the conversion of + -> _ and / -> - worked fine, for a while.
We are now receiving the same error 'AttachmentId is malformed' and again could not find why, I removed the custom sanitizer function that replaces these characters and it started working again.
I have no idea what and why is this happening and how to reliably get attachment content. Currently, I've moved the sanitizer into the catch handler, so if for some reason the AttachmentId fails, we'll retry it by sanitizing it. Will have to keep an eye on how many fail.
Any light on this issue will be really appreciated.
Update 1.0 - Sample Code
Front-end
//At this point we've got the email and got the files
//We call EWS only if file.type == Office.MailboxEnums.AttachmentType.Item
//For all other files we call REST endpoint ~ Office.context.mailbox.restUrl + '/v2.0/'......
//Sample code below if only for EWS call
let files = this.email.attachments || [];
files.map(file => {
this._getEmailContent(file)
.then(res => {
return res;
});
})
//Get content from EWS
_getEmailContent(file, _failed){
//attachmentId
//Most of the times this will be fine, but at times when Id has a `+` or `/` if fails, Was expecting the convertToEwsId to handle any sanitization required.
let attachmentId = Office.context.mailbox.convertToEwsId(file.id, Office.MailboxEnums.RestVersion.v2_0);
return this.getToken(EWS)
.then(token => {
return this.http.post(`${endpoint}/downloadAttachment`,{
token: token,
url: Office.context.mailbox.ewsUrl,
id: attachmentId
},{
responseType: 'arraybuffer',
}).then(res => res.data);
}).catch(err => {
attachmentId = attachmentId.replace(/\+/g, "_");
this._getEmailContent(attachmentId, true);
})
}
Back-end
[HttpPost]
public DownloadAttachment(Request model){
var data = service.DownloadAttachment(model);
if(data == null)
{
return BadRequest("Error downloading content...");
}
return new HttpResponseMessage(HttpStatusCode.OK)
{
return data;
}
}
//Inside service
public byte[] DownloadAttachment(Request request){
var ser = new ExchangeService
{
Credentials: request.token,
Url = request.url
}
//Here it fails intermittently, returning AttachmentId is malformed.
var attachment = ser.GetAttachments(new [] {request.attachmentId}, null, null).First();
if (attachment is FileAttachment)
{
FileAttachment fileAttachment = attachment as FileAttachment;
fileAttachment.Load();
return fileAttachment.Content;
}
}

How do I return ajax response from unirest in nodejs?

I am using unirest from unirest.io to make ajax calls in node.
I want to return attendance so I can use it in another place.
function studentAttendance(req, res) {
unirest
.post('http://coer.ac.in/atten.php')
.field('coerid', req.params['id'])
.end(function (response) {
if (response.error) {
return response.error;
} else {
var attendance = {
"name": null,
"attendance": null,
"attenLastUpdated": null
}
if (response.raw_body.indexOf('Invalid COER ID') === -1) {
attendance = {
"name": response.raw_body.split("<h3>")[1].split("</h3>")[0].split("Mr/Ms ")[1].split(" have")[0],
"attendance": response.raw_body.split("<h3>")[1].split("</h3>")[0].split("%")[0].substr(String.length - 6),
"attenLastUpdated": response.raw_body.split("<p>")[1].split("</p>")[0].split(" Update ")[1]
}
console.log("\n\t\t Found ID in Database\n" + JSON.stringify(attendance));
res.send(attendance);
} else {
attendance = {
"name": null,
"attendance": "Invalid ID",
"attenLastUpdated": "Invalid ID"
}
console.error("\nError: Invalid COER ID. No match in Database.");
res.send(attendance);
}
}
});
}
I have tried return audience; and then return unirest and then print it's output but it prints a lot of objects and other data that can be used in a ajax call.
I want to use the result from this POST call to an attendance server and use this result in another place. To do this I need to return attendance but I have no idea how to do it.
I am trying to build a system so you just have to enter your ID and it'll fetch your name and attendance and pass it along as response to the API consumer and also save the unirest response saved to a database.
I can open and save data inside unirest's end method but this function or route is public and anyone can access this without providing a secret key in header. I am trying to avoid that because I guess it is risky?
Just one motive, Fetch the data, pass it to whoever requested it and save a copy to database.
No mongo inside unirest, because it might(?) dangerous.
Final option left(atleast the one that I can think of) is, returning the response and use it some where else.
Unirest uses a lot of things that I don't about and I think that I can not return the respone like you can do in other functions.
This problem was solved by putting the function in an object and than create another object data that'll hold the data.
Now, When you recieve the response update the data object and then pass a callback in unirest that'll return the data object.
This worked for me.
Whole Code:
var studentAttendance = {
"studentAttendance": function (req, res) {
var _id = req.params['id'];
unirest
.post('http://coer.ac.in/atten.php')
.field('coerid', _id)
.end(function getData(response) {
if (response.error) {
throw response.error;
} else {
if (response.raw_body.indexOf('Invalid COER ID') === -1) {
studentAttendance.data._id = _id;
studentAttendance.data.name = response.raw_body.split("<h3>")[1].split("</h3>")[0].split("Mr/Ms ")[1].split(" have")[0];
studentAttendance.data.attendance = parseFloat(response.raw_body.split("have ")[1].split("%")[0]);
studentAttendance.data.attenLastUpdated = response.raw_body.split("<p>")[1].split("</p>")[0].split(" Update ")[1]
console.log("\n\t\t Found ID in Database\n" + JSON.stringify(studentAttendance.data) + "\n Updating Record in Database");
res.send(studentAttendance.data);
} else {
studentAttendance.data._id = _id;
studentAttendance.data.name = null;
studentAttendance.data.attendance = "Invalid ID",
studentAttendance.data.attenLastUpdated = "Invalid ID"
console.error("\nError: Invalid COER ID. No match in Database.");
res.send(studentAttendance.data);
}
}
}
, attendanceCallback)
},
"data": {
"_id": null,
"name": null,
"attendance": null,
"attenLastUpdated": null
}
}
function attendanceCallback() {
return studentAttendance.data;
}

Parse for javascript - "error 209 invalid session token" when signing up a new user

I wrote a simple function in an angularJS application for signing up new users:
$scope.registerUser = function(username, password) {
var user = new Parse.User();
user.set("username", username);
user.set("email", username);
user.set("password", password);
user.signUp(null, {
success: function(result) {
console.log(result);
$scope.registerUserSuccess = true;
$scope.registerUserError = false;
$scope.registerUserSuccessMessage = "You have successfully registered!";
$scope.$apply();
$timeout(function(){
$state.go("user");
}, 1000);
},
error: function(user, error) {
$scope.registerUserError = true;
$scope.registerUserSuccess = false;
$scope.registerUserErrorMessage = "Error: [" + error.code + "] " + error.message;
$scope.$apply();
}
});
Initially it worked fine, but when I deleted all the users directly through Parse.com, I can't sign up new users using this function anymore. Each time I get error 209 invalid session token. Here's a screenshot of my Parse database:
I've googled the error message and the solution is always to log out the current user. However, if no users exist this isn't an action I can possibly take.
So I would not only like to fix this problem, but also know how to prevent it in the future so my application can be used safely.
Edit: I created a user directly in Parse.com, wrote a function to log in that user, but got the same error. I am completely stuck until this session issue is resolved.
delete all your session tokens, and anything else Parse related really, from local storage:
if needed turn off legacy session tokens, and follow migration tutorial from scratch:
I encountered this same error when building apps with react native using back4app. to clear anything Parse related, from local storage:
add
import { AsyncStorage } from "react-native";
in to the page and Use
AsyncStorage.clear();
See Example Below:
import { AsyncStorage } from "react-native";
import Parse from "parse/react-native";
// Initialize Parse SDK
Parse.setAsyncStorage(AsyncStorage);
Parse.serverURL = "https://parseapi.back4app.com"; // This is your Server URL
Parse.initialize(
"APPLICATION_ID_HERE", // This is your Application ID
"JAVASCRIPT_KEY_HERE" // This is your Javascript key
);
.........
_handleSignup = () => {
// Pass the username, email and password to Signup function
const user = new Parse.User();
user.set("username", "username);
user.set("email", "email");
user.set("password", "password");
user.signUp().then(user => {
AsyncStorage.clear();
if (condition) {
Alert.alert(
"Successful!",
"Signin Successful! Log in to your account.",
[
{
text: "Proceed",
onPress: () => {
//in this example, i navigated back to my login screen
this.props.navigation.navigate("LoginScreen");
}
}
],
{ cancelable: false }
);
}
})
.catch(error => {
Alert.alert("" +error);
});
};

Meteor - Account.createUser in client and server

I understand the reason to have the business logic in both client and server, but I don't understand well how to do that in some situations. Here for example:
// client/client.js
// hnadling click event on the Create Accounts button
Template.homecontent.events({
'click #btnCreateAccount': function (event, template) {
var userEmail = template.find('#email').value,
userName = template.find('#newusername').value,
password = template.find('#newpassword').value,
password2 = template.find('#password2').value,
name = template.find('#fullname').value;
validates = true;
//do some validation here
if(password != password2) {
validates = false;
}
if(validates === true) {
Accounts.createUser({
username: userName,
email: userEmail,
password: password,
profile: {
name: name
}
}, function (error) {
if (error) {
console.log("Cannot create user");
}
});
}
}
});
Since the validation is on the client only, it can easily be bypassed.
But there's a problem here: this is triggered by a user event, so I'm not sure what's the best way to have this code running on client & server.
You may be looking for something like Meteor.methods();, which allows you to define functions on the server that the client can call using Meteor.call(). You could provide a validation function and a user save function on the server, and call them both from the client, passing in the form data.
What I have done in the past is (on the client) I have a userFormParse() function that takes a form object and parses it into an object that can be passed into a server side validation function. I use the same userFormParse function for user edit and creation forms.
The validation function returns an error object to the client, or, if it's all valid data, I'll pass the data object on to a userCreateWithRole function (I usually always have roles assigned to users).
On the server:
Meteor.methods({
'createUserWithRole': function(data, role) {
var userId;
Meteor.call('createUserNoRole', data, function(err, result) {
if (err) {
return err;
}
Roles.addUsersToRoles(result, role);
return userId = result;
});
return userId;
},
'createUserNoRole': function(data) {
//Do server side validation
return Accounts.createUser({
email: data.email,
password: data.password,
profile: data.profile
});
}
});
And then on the client:
Template.userSignup.events({
'submit #userSignup': function(event) {
var data, validationErrors;
event.preventDefault();
data = userInputParse($(event.target)); //this function parses form into user object that can be inserted
validationErrors = userObjectValidate(data); //this function takes and does client side validation on the user object.
data.profile.status = 0;
if (validationErrors) {
//Show the user the validation errors
} else {
return Meteor.call('createUserWithRole', data, 'standard', function(err, userId) {
if (!err) {
//User created!!
} else {
//Insertion Error
}
});
}
}
});
That code is conceptual and untested :)
You should be doing it on server side, using Accounts.onCreateUser
The previous answers are not really exact.
Creating and using a Meteor method won't stop users to call the Accounts.createUser from the console for example. Therefore you also need to prevent the creation of users on the client :
Accounts.config({
forbidClientAccountCreation : true
});
You might want to look into Accounts.validateNewUser.
Example (taken from the docs):
Accounts.validateNewUser(function (user) {
if (user.username && user.username.length >= 3)
return true;
throw new Meteor.Error(403, "Username must have at least 3 characters");
});

Resources