Setting up 3-D secure for returning shoppers in BlueSnap - bluesnap

I'm having issues implementing the 3D secure flow for returning customers using the BlueSnap API(https://developers.bluesnap.com/docs/3-d-secure-for-api#section-3-d-secure-with-returning-shoppers). The issue is that the setup-callback doesn't seem to trigger.
Running the following code, only "3DS: Start" and "3DS: BlueSnap initiated" will show up in the logs:
console.log("3DS: Start");
if (typeof (window as any).bluesnap !== "object") {
throw new Error("Bluesnap not initiated!");
}
console.log("3DS: BlueSnap initiated");
(window as any).bluesnap.threeDsPaymentsSetup(
threeDSPaymentsToken, // token retrieved using the JSON API
(resp: any) => {
console.log("3DS: Setup");
});
Why is the callback never called?

It's a little confusing, but the callback of the setup function does not get called on setup complete. It is called after you click submit and the 3DS flow is completed (it is used to return the final result to you). So try to continue with the flow and call bluesnap.threeDsPaymentsSubmitData() and then check if the callback is called.

Related

php8 and Paypal IPN setup: Where does db INSERT upon successful handshake go?

Of the three files here- https://github.com/paypal/ipn-code-samples/tree/master/php
I have my Webhook URL set to the stock github version of- PaypalIPN.php (this validates successfully 100% of the time, if I use example_usage.php... Doesn't work. If I use both as Webhooks... Doesn't work).
From the Paypal button side of things I'm able to post my website's active user (call him $MrUser) with this:
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
"custom_id":"<?php echo $MrUser; ?>",
"description":"One hundred Webdollars",
"amount":
{
"currency_code":"USD",
"value":1.99
}
}]
});
},
Here's the SQL I need to run upon successful validation (I change $MrUser to $kitty for clarity's sake):
require 'sqlconfig.php';
$dsn = "mysql:host=$host;dbname=$db;charset=UTF8";
try {
$pdo = new PDO($dsn, $user, $password);
} catch (PDOException $e) {
echo $e->getMessage();
}
$hashedIP = $_SERVER['REMOTE_ADDR'];
$kitty = $_POST['custom']; // Not sure this is working yet, but this should give me $mrUser;
$sql = "INSERT INTO `Insert_upon_Paypal_success` (`date`,`hashedIP`,`username`,`webdollarAMT`) VALUES (now(),:hashedIP,:kitty,'100')";
$statement = $pdo->prepare($sql);
$statement->bindValue(':hashedIP', $hashedIP);
$statement->bindValue(':kitty', $kitty);
$inserted = $statement->execute();
I'm popping this into the PaypalIPN.php file upon validation, but, it doesn't work. Here's how I have it in there:
// Check if PayPal verifies the IPN data, and if so, return true.
if ($res == self::VALID) {
return true;
// i.e. putting all of the SQL above right here.
} else {
return false;
}
I'm guessing I need to put the SQL in a specific place that I'm missing, as per the layout of the PaypalIPN.php file... Please help!!
There is no reason to use IPN with current PayPal Checkout integrations. It is very old technology (20+ years) and should be deprecated soon.
Webhooks are a successor to IPN. However, even they are unnecessary for normal payment processing -- better used only if you need automated notifications of post-checkout exceptions such as refunds or disputes.
For normal PayPal payments, do not use either.
Instead, use the v2/checkout/orders API and make two routes (url paths) on your server, one for 'Create Order' and one for 'Capture Order'. You could use the (recently deprecated) Checkout-PHP-SDK for the routes' API calls to PayPal, or your own HTTPS implementation of first getting an access token and then doing the call with PHP's curl or similar. Both of these routes should return/output only JSON data (no HTML or text). Inside the 2nd route, when the capture API is successful you should verify the amount was correct and store its resulting payment details in your database (particularly purchase_units[0].payments.captures[0].id, which is the PayPal transaction ID) and perform any necessary business logic (such as reserving product or sending an email) immediately before forwarding return JSON to the frontend caller. In the event of an error forward the JSON details of it as well, since the frontend must handle such cases.
Pair those 2 routes with this frontend approval flow: https://developer.paypal.com/demo/checkout/#/pattern/server . (If you need to send any additional data from the client to the server, such as an items array or selected options, add a body parameter to the fetch with a value that is a JSON string or object)

Paypal smart buttons credit card error management

I'm using paypal smart buttons sdk to fullfill paypal payment by credit card or paypal balance. the problem is for sandbox negative testing i was unable to test bad credit card cases because of lack of documentation (or may be it's me who didn't search enough).
so i decided to test in a production like environment with my production paypal business account and intentionally put a bad credit card secret .
The problem is when calling order.capture() function paypal raises a lot of errors but i'm unable to catch them and manage them correctly
here is my calling code :
onApprove: async (data: any, actions: any) => {
const order = await actions.order;
this.logger.log(order);
try {
order.capture().then((details: any) => {
this.logger.log('[PAYPAL onApprove : ]' + details);
const payPalCreateOrderResponse: PayPalCreateOrderResponse = new PayPalCreateOrderResponse();
payPalCreateOrderResponse.details = details;
this.checkOutEventsStore.paymentDetails = payPalCreateOrderResponse;
});
} catch (e) {
this.managePaypalError(params, e);
this.logger.error('====> Paypal Order Capture ERROR ' + e);
}
}
the errors i see in console :
POST https://www.paypal.com/smart/api/order/order_id_replaced/capture 500
(anonyme) # buttons?
(anonyme) # js?client-id=xxxxxxxxxxxxxxxxxxx&currency=EUR&locale=fr_FR&debug=true:4841
ZalgoPromise.try # js?client-id=xxxxxxxxxxxxxx&currency=EUR&locale=fr_FR&debug=true:770
(anonyme) # js?client-id=xxxxxxxxxxxxxx&currency=EUR&locale=fr_FR&debug=true:4834
(anonyme) # js?client-id=xxxxxxxxxxxxx&currency=EUR&locale=fr_FR&debug=true:4851
######## a lot of stack ommited
Error: Api: /smart/api/order/order_id_replaced/capture returned status code: 500 (Corr ID: f2967533987cc)
{"ack":"error","message":"Unhandled api error","meta":{"calc":"f2967533987cc","rlog":"rZJvnqaaQhLn%2FnmWT8cSUueWscmrtUHe5Y1Bd%2FeqyvyOTq66rSXAcgw%2BjwUfLWoirTjSF3Dcz2NbXl4NQOgVH84XX3DSygFN_17c9d7d88e6"},"server":"HR8xYFSZUP13jAt-X87VBJ7lq_LqwktwVsmzzP_zQqInVub3-ylXm8UuExvdz-SWJ0NH49XoaSL2hE_9LzQo_5B-X0COwFFVi2Z4c-cTCQBGoBSZtkefMbHWojX3rQ4-qLZIYQefq6OE7funNI8ZnGZUi9YpufYlG9X1qx89zj0l4LERQ9wesnqMpT59y3GbjqsfOGbGf7uasTCGOz6f58ZNMbdNVYrz1h5gc3sZbk-LhH5ks1k1DqJV7UPsxus1QBII26hjpRQbnFr6VLiyCW"}
and the most important , in the network api calls i can see clearly for :
https://www.paypal.com/smart/api/order/order_id_replaced/capture api call
a good json result for the error :
{"name":"UNPROCESSABLE_ENTITY","details":[{"issue":"INSTRUMENT_DECLINED","description":"The instrument presented was either declined by the processor or bank, or it can't be used for this payment."}],"message":"The requested action could not be performed, semantically incorrect, or failed business validation.","debug_id":"f569254a2e9c8","links":[{"href":"https://developer.paypal.com/docs/api/orders/v2/#error-INSTRUMENT_DECLINED","rel":"information_link","method":"GET"},{"href":"https://www.paypal.com/checkoutnow?token=token_replaced","rel":"redirect","method":"GET"}]}
So the question is how to manage this error correctly by using the js api.
Thank you for your Help .
Ryan
when calling order.capture() function paypal raises a lot of errors but I'm unable to catch them and manage them correctly
When capturing on the client side with .capture() , if the capture is declined PayPal will automatically offer the payer the ability to try again, likely falling back to a modal window. No error handling by you is necessary, required, nor possible.
If you were capturing on the server side (which you are not doing), there is sample error handling code in this demo.

Is it possible to execute a long running function before the browser is reloaded?

I do prevent a page reload in my web application by the following function:
window.onbeforeunload = (event) => {
const e = event || window.event;
// Cancel the event
e.preventDefault();
save_user_data_to_indexed_db();
if (e) {
e.returnValue = ''; // Legacy method for cross browser support
}
return ''; // Legacy method for cross browser support
};
However, the save_user_data_to_indexed_db() function is not being executed during the "Reload site?" message. I thought that if I could execute my function during the displayed message, I could maybe automatically answer the same dialog programmatically and let the browser continue reloading the page.
Is there a way to make the browser wait for this kind of operation?
Generally, there is no way to make the browser wait. What I often do in this case is write the data to an intermediate place, such as localStorage, synchronously, and then asynchronously copy that data over to indexedDB later on when there is time, such as when the page is next loaded again, or from within a service worker.

Actions on Google (Handle fallback)

I have a query on a Google home (Dialogflow).
In a specific, after I execute fallback intent three times it exits with a statement
Sorry I can't help
But it should prompt
I am ending this session see you again later.
Here is code of fallback intent
app.intent('Default Fallback Intent', (conv) =>
{
const repromptCount = parseInt(conv.arguments.get('REPROMPT_COUNT'));
if (repromptCount === 0) { conv.ask(`Hey are you listening?`); }
else if (repromptCount === 1) { conv.ask(`Are you still around?`); }
else if (conv.arguments.get('IS_FINAL_REPROMPT')) { conv.close(`I am ending this session see you again later.`); }
});
I am assuming you're trying to follow the directions in the documentation about dynamic reprompts for "no-input" type responses.
The problem appears to be that you're trying to use this for the Fallback Intent, which is not specifically triggered on a NO_INPUT event. So it is doing the test, and neither the REPROMPT_COUNT nor IS_FINAL_REPROMPT arguments are set.
If you're using the multivocal library, it will keep counters of all the Intents and Actions that are called (both for the session and sequentially) and has some macros that assist in your responses.
If you want to use the existing library, you'll need to keep track of this yourself and either store this in a Context or store it in the session data object.
If you intended to use this as part of a "no-input" response, you need to make sure you're using this with an Intent that has the actions_intent_NO_INPUT event set.

Grails withForm, reset token on error?

Currently using grails 2.2.2
I've been trying to implement tokens into my application and have come up with this issue. We try to avoid re-rendering pages because it can be very slow so we return JSON instead. The following is a basic controller call that we use but I'm not sure what I should be doing to reset/get a new token.
public saveThing(ThingCommand cmd) {
Map model = [:]
withForm {
try {
thingService.saveThing(cmd)
model.success = true
} catch (Exception e) {
model.error = true //any validation errors or anything else
// RESET TOKEN HERE/GET NEW TOKEN?
}
}.invalidToken {
model.invalidToken = true
}
render model as JSON
}
From my understanding the token is thrown away once the withForm closure is executed. This causes an issue since I don't actually re-render the form which seems to be the normal way of generating a new token. How could I do this manually or is there an easier way to do this (plugin?)
Thanks!
Form tokens through withForm are not designed to be used with AJAX requests. They are designed to be used with HTML forms and POST requests which re-render the form and generate a new token for the form.
In order to make them work with JSON/AJAX requests you will need to implement your own token generation when you process the request and reject it. A good starting place would be to look at the old tests which test withForm. This should give you an idea on how tokens are created and stored.