Flutter + Stripe Connect + 3DSecure (SCA)

How to integrate Stripe Connect \w 3DSecure (SCA) in your Flutter based app!

Alessandro Berti
ITNEXT

--

Recently I had to implement a payment system in a Flutter-based App.

In particular, the App business follows this simple scheme:

There is a merchant who has to sell their goods online. The App enables online sales to this merchant.

In this scenario, the App is acting as an intermediary between this merchant and their customers. Because of this service, the App charges a fee from the merchant.

Business Model

Stripe Connect

There are different solutions and, after some researches, I found Stripe Connect the best fitting to my specific case.

From Stripe

Who is Who?

Applying the terminology of Stripe, we have that:

  1. Platform Account = App
  2. Connected Account = Merchant

So I got to work and started to read the API docs and…

  • **Please! Please! Make Flutter supported!**

No Flutter.

Supported Libraries

Thus, I had two choices:

  1. Wrapping the REST API by myself.
  2. Find someone that has already done the job. ❤

Guess what my choice was.

Pub Dev

After some package test, I found the following one the most promising:

Flutter Stripe SDK

Before to start: Some Key Words in Stripe

  1. Payment Method
  2. Publishable / Secret API Key
  3. Account ID
  4. Payment Intent
  5. Client Secret

Payment Method

PaymentMethod objects represent your customer’s payment instruments.

For example the customer’s card.

Publishable / Secret API Key

Publishable API keys are meant solely to identify your account with Stripe, they aren’t secret. In other words, they can safely be published in places like your Stripe.js JavaScript code, or in an Android or iPhone app. (From Stripe)

Secret API keys should be kept confidential and only stored on your own servers. Your account’s secret API key can perform any API request to Stripe without restriction. (From Stripe)

Stripe provides two different pairs of Publishable/Secret API Key:

  1. Test: use this pair to test your code. Each payment is fake. Stripe provides a set of test cards here.
  2. Live: use this pair in production.

You can find these keys in your Stripe Dashboard.

Publishable/Secret key

Account ID

The Account ID represents the merchant Stripe Connected Account. Thanks to this ID, Stripe knows to who send the paid amount.

How Can I add a Connected Account?

Dashboard > Connected Account > Create (Standard Account should be enough)

You can find the Account ID in your Stripe Dashboard under Connected Accounts.

Payment Intent

A Payment Intent guides you through the process of collecting a payment from your customer. (From Stripe)

In particular, a Payment Intent object defines the amount ($) that a customer must spend to purchase a good sold by the merchant.

Since the App collects some fees for the service offered to the merchant, in the Payment Intent, it is necessary to define the application_fee_amount (i.e. how much I earn for each transaction).

Over the amount and the application_fee_amount, it is also required to define which Payment Method to use (i.e. the customer’s card data such as card number, expiration date and, CVC) and the merchant Account ID.

There are few other parameters to config in a Payment Intent but for now, it’s just ok. Just grasp the idea. We will dig deeper about that into the coding part.

Client Secret

The PaymentIntent’s client secret is a unique key that lets you confirm the payment and update card details on the client, without allowing manipulation of sensitive information, like payment amount. (From Stripe)

Once a Payment Intent object has been created, a Client Secret is returned.

Just fix this:

The publishable key identifies who receives the “application_fee_amount” (i.e. the App)

The Account ID identifies who receives the “amount” (i.e. the merchant)

Where to store relevant data?

Let’s see which data has to be stored in the App and which in the server.

Since the Secret Key enables to authenticate payment requests, you must store it in a secure place, far away from the App (e.g. in the server).

The Account ID, as already mentioned, identifies the merchant to which send the amount spent by a customer. Since the App needs to know the Account ID, you have to find a way to communicate to the App this info. A reasonable choice would be to store the Account ID in the server and then the App will retrieve this data from the server upon request (i.e. on payments).

Amount and application_fee_amount should be “hardcoded” in the Payment Intent created in the server.

Why?

Suppose to store amount and application_fee_amount on the app-side. Someone could manually change them and set each one to zero so… no income neither for you neither for the merchant!

GRATIS

The Business Logic

The following sequence diagram should help you to understand the code execution flow.

But wait, what is 3D secure (SCA)?

For extra fraud protection, 3D Secure requires customers to complete an additional verification step with the card issuer when paying. Typically, you direct the customer to an authentication page on their bank’s website, and they enter a password associated with the card or a code sent to their phone. This process is familiar to customers through the card networks’ brand names, such as Visa Secure and Mastercard Identity Check. (Stripe)

Is it necessary?

Absolutely yes. In case some costumer cards have 3D secure enabled, and you don’t provide the correct code to handle it, then the transaction will be rejected.

FINALLY: LET’S CODE

Server-Side

You need to install Stripe package on your server.

Because I decided to code in python:

pip3 install stripe

I have used AWS Lambda service, but you can implement the following POST method in your favorite way.

amount/application_fee_amount

You already know what these parameters are. Just to clarify one thing:

amount = 1000

It stands for 10.00€. (In case you set currency=’eur’)

The same goes for application_fee_amount, 140 stands for 1.40€

receipt_email

Specifying in “receipt_email” the customer email, once the transaction has been processed, Stripe sends an email to the customer about the transaction status.

It is not required this parameter, however, I find this extremely useful to feedback the customer. In case of any sort of problem, the customer can show you the receipt and you can find together a solution.

confirm

Set to true to attempt to confirm this PaymentIntent immediately. (Stripe)

Server-side part END. Easy, isn't it?

Fuck Yeah

App-Side

You can find the Flutter SDK Stripe package here:

Add the following dependency in the pubspec.yaml of your Flutter project.

dependencies:
stripe_sdk: ^3.0.1+1

3D Secure setup

Now let’s set up the AndroidManifest.xml (Android) and Info.plist (iOS) files to handle 3D Secure (SCA).

The library “stripe_sdk” offers complete support for SCA on iOS and Android. It handles all types of SCA, including 3DS, 3DS2, BankID and others. It handles SCA by launching the authentication flow in a web browser, and returns the result to the app. The returnUrlForSca parameter must match the configuration of your AndroidManifest.xml and Info.plist as shown in the next steps.

Android

You need to declare the following intent filter in android/app/src/main/AndroidManifest.xml. This example is for the URL stripesdk://3ds.stripesdk.io:

Credits: Flutter Stripe SDK

iOS

For iOS you need to declare the scheme in ios/Runner/Info.plist (or through Xcode's Target Info editor, under URL Types). This example is for the URL stripesdk://3ds.stripesdk.io:

Credits: Flutter Stripe SDK

Flutter FrontEnd

Let’s code the Credit Card Widget and a Buy Button to submit the transaction.

Widgets

Follows the expected result:

Once done the previous step, let’s code the buy() function.

Don’t forget to add checks about the validity of the: CVC, Date, and, CardNumber.

buy function

The createPaymentIntent() creates a paymentMethod and, through the postPaymentIntent(), submits the payment method to the server (along with the costumer email). It returns the clientSecret.

Then retrievePaymentIntent() is invoked and it returns the paymentIntentRes.

CretePaymentIntent and postCreatePaymentIntent functions

The following function handles 3D secure enabled cards. In case the card has not the 3D secure enabled, this function is not invoked and the payment is processed smoothly.

3D secure handler function
3D Secure Webhook

Follows the complete code.

TESTING

By now you can test cards by yourself. Use this card set provided by Stripe.

Success!

That’s all folks!

The presented code is a basic example, but it works!

The project repository is available on my GitHub!

Any suggestions are welcome!

See ya!

--

--

Writer for

Ph.D. student in Quantum Computing @University of Pisa | Community Manager @SuperheroesValley | Co-Host @PointerPodcast