Sagepay Issue - Retrieve a Transaction not working after 3D-Secure Authenticate - payment

https://developer.sage.com/api/payments/api/#operation/retrieveTransaction
After 3D-Secure Authenticate, I can't retrieve transaction using transactionId. I'm getting the following response :
{
"description": "Transaction not found",
"code": 1012
}
Sometimes it's working fine. then it's a response like this:
{
"statusCode": "0000",
"statusDetail": "The Authorisation was Successful.",
"transactionId": "5D208A72-C1A6-68E7-A61D-B67D51CXXXXX",
"transactionType": "Payment",
"retrievalReference": 6764XXX,
"bankResponseCode": "00",
"bankAuthorisationCode": "999XXX",
"paymentMethod": {
"card": {
"cardType": "Visa",
"lastFourDigits": "0006",
"expiryDate": "0720",
"cardIdentifier": "8B27BE84-E06C-4F88-ADCD-AFF22EBXXXXX",
"reusable": false
}
},
"amount": {
"totalAmount": 13500,
"saleAmount": 13500,
"surchargeAmount": 0
},
"currency": "GBP",
"fiRecipient": {},
"status": "Ok",
"avsCvcCheck": {
"status": "AllMatched",
"address": "Matched",
"postalCode": "Matched",
"securityCode": "Matched"
},
"3DSecure": {
"status": "Authenticated"
}
}
Does anybody know How can I fix this issue?

I experienced the same issue. I am using the test sandbox environment and forcing 3dsv1.0 processing. In 3DS version 1 , after the challenge/response returns it does not give the full transaction details, so you then need to query sagepay to get the authorisation status. As stated above it either returns the transaction object successfully OR quite randomly gives {"description":"Transaction not found","code":1012}.
I then assumed some timing issue - so put in place a retry option below to query the transaction. This appeared to assist things (meaning "sometimes" it got the transaction) but was not a fix in itself.
async.retry({
times: 10,
interval: function(retryCount) {
return 50 * Math.pow(2, retryCount);
},
errorFilter: function(errorCode) {
return errorCode === 404; // only retry on transaction 'not-found'
},
},
function (callback) {
return getSageTransactionStatus(MD, callback)
},
function(err, result) {
if (err) {
if (global.gLogging ) console.log ("Transaction Search Result", err);
}
else console.log (result);
});

Related

Unsuccessfull payment transfer using Paypal with nodejs

I am trying to implement the Paypal gateway method in nodejs. I am successful redirecting to paypal from my nodejs and when I click on pay I get "Error": "Response Status : 400", with an unsuccessful payment.
My API and its response are given below in detail.
paypal.configure({
'mode': 'sandbox', //sandbox or live
'client_id': 'ATXsRtel_YoAQHVIwP4Z_bmX3VkL1n1N_fJ6FH2os0GynozBJo-Oler8wFVvXzoPpNwbfCAYFvCL76Ke',
'client_secret': 'EAwUmjDRpC-fqz_Fcs262MKrdMDltXJnWiA-
N6gURWcJq1N9IpwzISfcCMLNHzXFJ_38YLQXG3jtUK8a'
});
Post Api for req.body and get the url back
Router.post('/Pay',(req,res)=>{
const create_payment_json = {
"intent": "sale",
"payer": {
"payment_method": "paypal"
},
"redirect_urls": {
"return_url": "http://localhost:7070/PayPal/PaymentSuccess",
"cancel_url": "http://localhost:7070/PayPal/PaymentCancel"
},
"transactions": [{
"item_list": {
"items": [{
"name": "ball",
"sku": "001",
"price": "25.00",
"currency": "USD",
"quantity": 1
}]
},
"amount": {
"currency": "USD",
"total": "25.00"
},
"description": "Testing Product"
}]
};
paypal.payment.create(create_payment_json, function (error, payment) {
if (error) {
res.json({
Message:'Un Approved',
Error:error.message
})
} else {
payment.links.map(_AllLinks=>{
if(_AllLinks.rel === 'approval_url'){
res.json({
Message:'Approved',
Url:_AllLinks.href
})
}
})
}
});
})
Rout to get payerID
Router.get('/PaymentSuccess',(req,res)=>{
let payerId = req.query.PayerID;
let paymentId = req.query.paymentId;
console.log({
payerId:payerId,
paymentId:paymentId
});
let execute_payment_json = {
"payer_id" : payerId,
"transaction" : [{
"amount":{
"currency":"USD",
"total":"25.00"
}
}]
}
paypal.payment.execute(paymentId,execute_payment_json, function(error,payment){
if(error){
res.json({
Error:error.message,
Message:error
})
}else {
res.json({
Result:payment
})
}
})
})
Router.get('/PaymentCancel',(req,res)=>{
res.json({
Result:'Cancelled'
})
})
The response i get and my payment got unsuccessful.
// 20210504063330
// http://localhost:7070/PayPal/PaymentSuccess?paymentId=PAYID-MCIKIRQ60G27157PA787072M&token=EC-
9EU01414K39004833&PayerID=GFYGLAXW36YPW
{
"Error": "Response Status : 400",
"Message": {
"response": {
"name": "VALIDATION_ERROR",
"message": "Invalid request - see details",
"debug_id": "cfeb4eab621c7",
"details": [
{
"field": "/transaction",
"location": "body",
"issue": "MALFORMED_REQUEST_JSON"
}
],
"links": [
],
"httpStatusCode": 400
},
"httpStatusCode": 400
}
}
The v1/payments REST API is deprecated. Use the current v2/checkout/orders API instead.
The best integration uses no redirects, and keeps your site loaded in the background with an in-context approval.
Make two routes on your server, one for 'Create Order' and one for 'Capture Order', documented here. These routes should return only JSON data (no HTML or text). The latter one should (on success) store the payment details in your database before it does the return (particularly purchase_units[0].payments.captures[0].id, the PayPal transaction ID)
Pair those two routes with the following approval flow: https://developer.paypal.com/demo/checkout/#/pattern/server

How to make Paypal Payouts on a Node.Js environment

I am trying to execute a PayPal Payout on Node.Js environment, but I can't seem to wrap my head around it.
Could you please help me get the parameters right, and maybe where I should re-organize the code, and eventually start heading in the right direction?
Thank you all in advance.
var PAYPAL_API = "https://api-m.sandbox.paypal.com";
app.post("/my-api/execute-payment/", function (req, res) {
// 2. Get the payment ID and the payer ID from the request body.
var paymentID = req.body.paymentID;
var payerID = req.body.payerID;
let requestBody = {
"sender_batch_header": {
"sender_batch_id": "Payouts_2018_100007",
"email_subject": "You have a payout!",
"email_message": "You have received a payout! Thanks for using our service!",
},
"items": [
{
"recipient_type": "EMAIL",
"amount": {
"value": "9.87",
"currency": "USD",
},
"note": "Thanks for your patronage!",
"sender_item_id": "201403140001",
"receiver": "rev_buyer#personal.example.com",
"alternate_notification_method": {
"phone": {
"country_code": "91",
"national_number": "9999988888",
},
},
"notification_language": "fr-FR",
},
{
"recipient_type": "PAYPAL_ID",
"amount": {
"value": "5.32",
"currency": "USD",
},
"note": "Thanks for your patronage!",
"sender_item_id": "201403140003",
"receiver": "xXXXXXX-50mQSYBJpg_OWguVkwYYYYYYY-ZAFupw-aaaaa-nnnnnnn",
},
],
};
request.post(
PAYPAL_API + "/v1/payments/payouts",
{
auth: {
user: CLIENT,
pass: SECRET,
},
requestBody,
json: true,
},
function (err, response) {
if (err) {
console.error(err);
return res.sendStatus(500);
}
// 4. Return a success response to the client
res.json({
status: "success",
});
}
);
});
Have you tried the Payouts-NodeJS-SDK ?
The main issue with the custom code above is that you aren't first requesting an OAuth 2.0 access_token, and then using the access_token for your REST API call.
See the documentation: https://developer.paypal.com/docs/api/overview/#get-an-access-token
The SDK abstracts this step for you.

Getting an empty array on Axios GET request

I'm trying to do a GET request using Axios. The API's response includes several objects that are properly populated. However, one particular object/field is an array that always shows up as an empty array.
Here's the response I get (note the "users" object with the empty array):
{
url: 'https:/<removed>/customers/<removed>/users?number=41442268000',
users: [],
customer: {
url: 'https://<removed>/customers/<removed>',
id: '<removed>',
name: 'CX Customer1'
},
paging: { offset: 0, limit: 2000, count: 0 }
}
The strange thing is that it works perfectly fine when I use Postman to query the exact same resource:
{
"url": "https://<removed>/customers/<removed>/users?number=8013334001",
"users": [
{
"url": "https://<removed>/customers/<removed>/users/<removed>",
"id": "b1703d6a<removed>",
"bwId": "<removed>.webex.com",
"lastName": "One",
"firstName": "Gus1",
"displayName": "Gus1 One",
"type": "USER",
"callerIdLastName": "One",
"callerIdFirstName": "Gus1",
"callerIdNumber": "+1-8013334001",
"numbers": [
{
"external": "+1-8013334001",
"extension": "4001",
"primary": true
}
],
"location": {
"name": "Salt Lake City",
"id": "9a03e3e<removed>",
"url": "https://<removed>/customers/<removed>/locations/<removed>"
}
}
],
"customer": {
"url": "https://<removed>/customers/<removed>",
"id": "4c1ccbe<removed>",
"name": "CX Customer1"
},
"paging": {
"offset": 0,
"limit": 2000,
"count": 1
}
}
As observed in the above Postman response, the "users" array has an object inside of it.
Here's my Node.js code:
function getUsersByTN(customerInfo, userData) {
let rowNumber = 1;
let successCount = 0;
let failureCount = 0;
axios.defaults.headers.common["Authorization"] = `Bearer ${customerInfo.token}`;
console.log('Attempting to find users on Webex Calling using their phone number...');
return new Promise(async (resolve, reject) => {
try {
for (let data of userData) {
rowNumber++;
const phoneNumber = data.TN;
const getUserURL = `https://<removed>/customers/` +
`${customerInfo.customerId}/` +
`users?number=` +
`${phoneNumber}`;
const result = await axios.get(getUserURL);
console.log(result.data);
resolve(result);
}
}
catch (err) {
reject(new Error(err));
}
})
}
I have also tried to replace the async/await format with the more traditional way of using promises, but got the same result:
axios.get(getUserURL)
.then(result => console.log(result.data))
.catch(err => console.log(err));
What am I missing?
Thanks!
-Gus
I found the problem. My input CSV file had users with phone numbers that did not exist. As soon as I updated it with valid/existing phone numbers, it worked as expected.
The thing that threw me off is that the API still replies with a "200 OK" when I provide invalid phone numbers. I was expecting a "404 not found" for invalid numbers, so I didn't even think about checking the numbers.
My first thought was that this was a bug on the API. In other words, I initially thought that the API should reply with a "404 not found". However, as I thought more about this, I realized that a "200 OK" with an empty result is a more appropriate response. That's because a "404" would incorrectly indicate that the API resource could not be found, which would imply that the request was sent to an invalid URL. That's clearly not what we want. Instead, we want to let the application know that it reached a valid API resource, but that no results were found for the provided searched criteria.

Loopback + mongoDB: Can't find extended user

I am using Loopback 3.0 with MongoDB connector.
In a REST method exposed somewhere, I need to access the currently logged user and make some updates on it.
I have extended the base User model, calling it appUser, the login works, and I can get the token (after I changed the token model to point to the appUser) of a logged user. The model is the following one:
{
"name": "appUser",
"plural": "appUsers",
"base": "User",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"gender": {
"type": "string",
"enum": [
"M",
"F"
]
},
"birthDate": {
"type": "Date"
}
},
"validations": [],
"relations": {},
"acls": []
}
I need to access the user profile, in order to update it. But when I query it, I get null as result.
const User = app.models.appUser;
User.findOne({
where: {
_id: ObjectId("5aae7ecd2ed1b11b1c09cf25")
}
}, (err, user) => {
if (err || res == null) {
console.log("Error updating the user ");
const error = {
"name": "Database error",
"status": 500,
"message": "Can't access the database."
}
callback(error, null, null);
} else {
//Whatever
}
});
But if I run the same query from Robo3T on MongoDB, it works.
db.getCollection('appUser').find({"_id": ObjectId("5aae7ecd2ed1b11b1c09cf25")})
What am I doing wrong?
Thank you,
Massimo
You didn't call the user so that should be the case, also in your callback you are passing null and not your user result. However I don't get where the res variable came from.
const User = app.models.appUser;
User.findOne({
where: {
_id: ObjectId("5aae7ecd2ed1b11b1c09cf25"),
}
}, (err, user) => {
if (err) {
console.log("Error updating the user", err); // logs the app error
const error = {
"name": "Database error",
"status": 500,
"message": "Can't access the database."
}
callback(error, null); // passes your custom error
}
console.log("APP USER", user);
callback(null, user);
});
I don't how you calling your callback but i think you can manage this.
If you still have no result try changing _id to id

Updating a nested document mongo. Concurrency issues

I am struggling with a seemingly basic issue in Mongoose.
I have a method that looks like so:
changeStateOnIncident: function(req, res, next) {
Incident.findOneQ({ _id: req.params.id })
.then(function(incident) {
if (!incident) { return next(new restify.ResourceNotFoundError("Incident with id " + req.params.id + " not found.")) }
return incident.addStateChange(req.body)
.then(function(savedState) {
return res.json(201, savedState);
})
.then(function() {
var alertOrders = _.map(incident.alertees, function(alertee) {
return alertee.createAndSendAlert(req.body)
})
return Q.allSettled(alertOrders)
.then(function(results) {
return incident.save();
})
})
})
.fail(function(err) {
return next(new restify.InvalidContentError(JSON.stringify(err)))
})
},
As for createAndSendAlert and addStateChange, their definitions are here
What I am trying to do here is as follows:
Find the Incident matching the ID passed in.
Add the state event to the array of state changes.
Return that newly added change event back to the user.
For every alertee on the Incident, loop through and add an alert to that alertees list of alerts, the result of an SMS request.
Save that incident.
Unfortunately, the way I'm doing it now is failing for concurrent writes. Suppose I send two
{
_id: "53cc87d0feeb4302007eadbe",
alertees: [
{
_id: "eJ65nfJh_g",
alerts: [
{
"_id": "53cc87d1feeb4302007eadc1",
"created_at": "2014-07-21T03:24:01.304Z",
"response": "Sent",
"state": "First state change",
"updated_at": "2014-07-21T03:24:06.802Z"
},
{
"_id": "53cc87d2feeb4302007eadc5",
"created_at": "2014-07-21T03:24:02.348Z",
"response": "Sent",
"state": "Emergency",
"updated_at": "2014-07-21T03:24:06.802Z"
},
{
"_id": "53cc87d6feeb4302007eadcc",
"response": "Sent"
"state": "Ended",
}
]
}
],
states: [
{
_id: "53cc87d1feeb4302007eadbf",
state: "First state change"
},
{
_id: "53cc87d1feeb4302007eadc2",
state: "Second state change"
},
{
_id: "53cc87d2feeb4302007eadc4",
state: "Third state change"
},
{
_id: "53cc87d6feeb4302007eadcb",
state: "Fourth state change"
}
]
}
I think what is happening is IF I send two states A and B. And A's SMS response comes back before B's it will possibly invalidate the document B is trying to modify. I am losing changes to the sub document if state changes are sent too close to each other.
Any idea? How could I rewrite this? Using update methods perhaps?

Resources