I'm trying to integrate Braintree payments for my Flutter application. I have a plugin to handle iOS/Android and am writing a custom implementation for web.
It's a fairly simple solution I think, I'm using the Braintree Javascript SDK/Drop in UI (GitHub tutorial) to render the payment screen using HTML. This part I got working, however to properly process these payments I need to retrieve the response of the JavaScript function - specifically a string the payment nonce.
To do this i used some channel methods and dart magic to call this javascript function
async function payment(auth) {
braintree.dropin.create({
authorization: auth,
selector: '#dropin-container'
}, function (errCreate, instance) {
document.getElementById("submit-button").addEventListener('click', function () {
if(errCreate) {
console.log("Error", errCreate);
return;
}
instance.requestPaymentMethod(function (requestPaymentMethodErr, payload) {
if (requestPaymentMethodErr) {
console.log('Error', requestPaymentMethodErr);
return;
}
return payload.nonce;
});
});
});
}
This function seems to be working, returning data and payment nonces and all that. In a (separate) dart file where I'm actually rendering the widget, I declared the function like so:
// EXTERNAL JAVASCRIPT ==========================================
@JS()
external void initBraintree(auth);
@JS()
external payment(String auth);
and this seems to be working, it's calling the function on button click so that's good. The issue is when I call this async function it returns a promise, theoretically dart should not have any idea how to handle this however, they thankfully have a method called promiseToFuture that should handle just this. I've used it like so:
Future<BraintreeDropInResult> start(BuildContext context, BraintreeDropInRequest request) async {
// create div with html embedded
String htmlL = """<div id="checkout-message"></div>
<div id="dropin-container"></div>
<button id="submit-button">Submit payment</button>""";
var paymentDiv = html.DivElement()..appendHtml(htmlL);
// attach to payment container
ui.platformViewRegistry.registerViewFactory('braintree-container', (int viewId) => paymentDiv);
// call js function
var promise = payment(request.clientToken);
String nonce = await promiseToFuture(promise);
...
However it just does not work. The JavaScript function seems to be returning a promise object however the promiseToFuture function never waits for it, just instantly returns null everytime.
Been working on this a while and finally feel really close but this is being a real thorn in my side so any help is really appreciated!
You could do something like this. Ie create a promise, and once everything is ok -- and the button is clicked -- it will resolve with the nonce.
Of course, this will only work for the first click of the button. Because, once a promise is either resolved or rejected, it won't change it's status and result anymore.
async function payment(auth) {
return new Promise((resolve, reject) => {
braintree.dropin.create({
authorization: auth,
selector: '#dropin-container'
},
(createError, instance) => {
//probably there is no need to attach the eventhandler
//if the was an error
if (createError) {
console.log("Error", createError);
return reject(createError);
}
document.getElementById("submit-button").addEventListener("click",
() => {
instance.requestPaymentMethod((rpmError, payload) => {
if (rpmError) {
console.log("Error", rpmError);
return reject(rpmError);
}
resolve(payload.nonce);
});
});
}
);
});
}