Stripe Payments with SCA in React Native

Rafael Padovani
4 min readOct 13, 2019

I recently came across this message at the Stripe dashboard of a client:

Source: https://stripe.com/docs/payments

Now you can find that in the docs, but as you may know Stripe changed a lot the payment flow with credit card by adding SCA (Strong Customer Authentication).

The first impression may be a little bit scary once you have to keep your apps updated and have no idea of how big should be the update and although you could find a large explanation on how to do it on the web, at the time for React Native the reality is a bit tough.

The only trustworthy and out of the box package able to do it is Tipsi, recommended by Stripe official docs. Tipsi seems to work fine but at the moment I write this article they are late for the deadline and still facing several issues on their beta version. I needed to get this working in time otherwise the users would only be able to make orders under 30€, so it’s definitely not an option. Also, I’d like to find a way where I wouldn’t have to install packages and integrate them both for Android and iOS.

So I had to find a way to do it programmatically through API calls as I was doing before, in a very similar way as implemented by Nikita Lyzhov, but to do that I had to understand what they wanted me to do, or in other words, how SCA works.

This answer was found in the Stripe docs, where they set up a good tutorial of the payment flow and thanks to the tipsi guide described by mindlapse I found a nice way to solve it. Before SCA the process was pretty simple: we created a token passing the card data and used this card token to make another call to create the charge passing the amount and currency. That was the ‘basic’ way to make payments with Stripe.

Strong Customer Authentication

Source: https://stripe.com/docs/payments/payment-intents/web-manual
Source: https://stripe.com/docs/payments/payment-intents/web-manual

Now, the process changed a bit, actually, now we have 3 other different API calls. Once you know which calls you should make, they can be easily found in the Stripe API where you get the endpoints and a complete description.

Creating Payment Method

As described in the guide provided by mindlapse, first we have to create a Payment Method passing the card data encoded and the payment type.

export const createPaymentMethod = (number, month, year, cvc) => {
const cardDetails = {
"card[number]": number,
"card[exp_month]": month,
"card[exp_year]": year,
"card[cvc]": cvc
};
let formBody = [];
for (let property in cardDetails) {
let encodedKey = encodeURIComponent(property);
let encodedValue = encodeURIComponent(cardDetails[property]);
formBody.push(encodedKey + "=" + encodedValue);
}
formBody = formBody.join("&");
fetch('https://api.stripe.com/v1/payment_methods?type=card&' + formBody, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + secret_key
},
})
.then((response) => {
if (response.status == 200) {
return response.json();
}
})
.then((responseJson) => {
return responseJson;
})
.catch((error) => {
return error;
});
}

For more details see: https://stripe.com/docs/api/payment_methods/create

That call will return an object from which we get the id. Then we have to create the Payment Intent.

Payment Intent

To create the Payment Intent we need to get that id returned from the first call and make another one passing other parameters including amount and currency:

export const createPaymentIntent = (amount, currency, payment_method, description) => {
let truncated = parseFloat(amount).toFixed(2);
let stripeAmount = truncated * 100;
fetch('https://api.stripe.com/v1/payment_intents?amount=' + stripeAmount + '&currency='+ currency +'&payment_method_types[]=card&payment_method=' + payment_method + '&confirmation_method=automatic&confirm=true&description=' + description, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + secret_key
},
})
.then((response) => {
if (response.status == 200) {
return response.json();
}
})
.then((responseJson) => {
return responseJson;
})
.catch((error) => {
return error;
});
}

For more details see: https://stripe.com/docs/api/payment_intents/create

With that we already get our response with the client_secret. The next step is to check the status inside the object, if it’s succeeded then the customer will not have to provide authentication and the payment is complete. Beautiful!

Otherwise, if the status is requires_action we have to make another call to confirm the payment intent passing the id returned by the previous call, pretty simple.

Confirming the Payment Intent

export const confirmPaymentIntent = (payment_method) => {
fetch('https://api.stripe.com/v1/payment_intents/' + payment_method, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + secret_key
},
})
.then((response) => {
if (response.status == 200) {
return response.json();
}
})
.then((responseJson) => {
return responseJson;
})
.catch((error) => {
return error;
});
}

And that’s it, all you have to do is to call these functions passing the right parameters. It works like a charm and now we are able to create payments higher than 30 €. I hope this works for you as well, especially If you got stuck waiting some package release or still trying to figure out how to deal with these changes with Stripe payments.

References

Stripe Docs: https://stripe.com/docs

Stripe API: https://stripe.com/docs/api

Tripsi guide by Dave MacDonald: https://gist.github.com/mindlapse/72139f022d6e620e4f0d59dc50c1797e

Using stripe payment service with React Native and fetch: https://medium.com/@lyzhovnik/using-stripe-payment-service-with-react-native-and-fetch-4177c8d992cb

--

--