Payments in Flutter with Square and NodeJS

Samarth Agarwal
Flutter Community
Published in
8 min readMay 3, 2019

--

Recently, I watched the Boring Flutter Development Show on YouTube where Emily and the guest, Shannon Skipper from Square, walked us through the process of adding In-App Payments using Square in Flutter. You can watch the video here. I have worked with Stripe payments before in Flutter but since there is no official Stripe plugin for Flutter yet, it was tough to get the app PCI Compliant and ensure that the user’s confidential information is completely safe. There are a few Stripe plugins available but they are also pretty limited as of now and I was not happy with anyone of them but yes they do the job.

Just in case if you are looking to integrate Stripe payments, the plugin stripe_payment is what got the job done for me.

How it works

Before we get started with the project and get our hands dirty, we need to understand the role of the flutter app and the server (that we will code later) in the whole payment process.

The way it all works is that the app receives user’s card details or any other authentication information which is super securely sent to the Square’s servers and the servers send a unique string back to us as a response. This string is unique, can be used only once and is called nonce. Next, we send this nonce to our own server which uses the Square Connect APIs to charge the user using just the nonce. Keep in mind that the server does not need anything else except the nonce, and the details about the payment amount and currency. You can, however, optionally add other relevant data to the transaction.

Flutter integration

Anyway, back to Square, I thought of doing a complete Square Flutter integration and since, in the video, the Server-side code was not discussed, I thought that I will give everything a shot. So here is everything I did and got it working in the test environment. Let’s get coding.

Go ahead and create a new project and add the following Build Script by opening the project in XCode. Even if you want to add Square payments in an existing app, you need to follow the steps mentioned here.

Basically, all you need to do is add a build script to the build phases section by opening the project’s iOS build in XCode. Here is the code for the build script.

if [ "$DEVELOPMENT_TEAM" != "" ]
then
FRAMEWORKS="${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}"
"${FRAMEWORKS}/SquareInAppPaymentsSDK.framework/setup"
else
echo "Skip signing frameworks"
fi

Once done, go ahead and install the Flutter Plugin for In-App Payments SDK. Add the following line to your pubspec.yaml.

dependencies:  ...  square_in_app_payments: ^1.1.0

Save the file and click on the “Packages get” button in Android Studio or run flutter packages get if you prefer the command line. Once the installation finishes, we will add a simple button in the app to kick-start the payment process. For now, I will just be using the FloatingActionButton that we always have every time we create a new flutter project. So we change the FloatingActionButton widget code to something like this.

floatingActionButton: FloatingActionButton(
onPressed: () => _pay(),
tooltip: 'PAY',
child: Icon(Icons.payment),
),

The _pay method will do the rest of the things for us. Now let’s add a few imports specific to the Square plugin.

import 'package:square_in_app_payments/in_app_payments.dart';
import 'package:square_in_app_payments/models.dart';

Next, let’s initialize the In-app Payments SDK. Since we need to do this only once, we add the code to do this in the initState event in our HomePage widget.

@override
void initState() {
super.initState();
InAppPayments.setSquareApplicationId('my-sandbox-key-here');
}

In the above code, we need a Sandbox key that you can very easily get by going to this URL and creating a new Square app. Ensure that you use the test Application ID. Very simple. Okay, so now we have initialized the app and the Square SDK successfully. You can test the app so far just to be sure that so far everything is good. If yes, move on to the next steps.

Now it is time to write the _pay method. The _pay method will use the methods provided in the InAppPayments class to start the payment process. Let’s code and everything is self-explanatory.

InAppPayments.startCardEntryFlow(
onCardNonceRequestSuccess: (CardDetails details) {
print(details.nonce); // here you get the nonce
// The following method dismisses the card entry UI
// It is required to be called
InAppPayments.completeCardEntry(
onCardEntryComplete: () async {
// code here will be execute after the card entry UI is dismissed
}
);
}, onCardEntryCancel: () {
print("canceled");
}
);

Basically, we are using the startCardEntryFlow method which takes two named arguments, onCardNonceRequestSuccess which is executed upon success and onCardEntryCancel , which executed when the flow is canceled by the user. To handle errors, you can wrap the whole code in try-catch blocks and handle the exceptions accordingly.

The completeCardEntry method is called to tell the SDK that we have got the nonce, and now we need to close the card entry UI. If you have some code that you want to be executed after the card entry UI is closed, you can put it inside the onCardEntryComplete method which is a named argument to the completeCardEntry method.

Run the app and when the app loads up, tap in the FAB to start the card entry flow. It will prompt you for a card number, expiry date, CVV, and postal code. You can enter the following information to test.

Card Number: 4242424242424242
Expiry Date: Any future date
CVV: Any 3-digit number except 911 and 403
Postal Code: Any 5 or 6 digit number

Proceed to save and, pretty much instantly, you will see a random string logged to the console. It shall look something like this: cnon:CBASEFHY2eh7e-BAiKznrabSEeQgAQ . This the nonce that we use in the next steps and send it to the server. The server processes the request, then makes the transaction and responds the app back with an appropriate response.

Server Integration

In the next steps, we will create a minimal NodeJS server that receives the nonce from the client (the flutter app in this case) and use the Square Connect API to process the transaction and send the app a response.

Ensure that you have installed NodeJS on your system.

Create a folder called server in the flutter project. You may, however, call it whatever you want. server just seems more appropriate. Open the folder in VSCode and create a new file called server.js. This is going to be the main entry point and for now, the only javascript file in the server code.

Open up the terminal and type in the command npm init and answer all basic questions about the project. You may choose to skip to answer all except the package name. This process, in the end, will generate a package.json file. Done? Great!

Next, use the terminal to install express, body-parser and square-connect Node modules. Here is the command.

npm install express body-parser square-connect --save

This will install all the 3 packages in one go. Next, write the following code in server.js file.

var SquareConnect = require('square-connect');
var defaultClient = SquareConnect.ApiClient.instance;
var app = require('express')();
var bodyParser = require('body-parser');
var bodyParser = require('body-parser')
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));
app.get("/", (request, response) => {
if (request.query.nonce == null) {
response.send({
code: 400,
message: "nonce not found"
})
}
var oauth2 = defaultClient.authentications['oauth2'];
oauth2.accessToken = 'your-access-token-here';
var transactionApi = new SquareConnect.TransactionsApi();
transactionApi.charge("your-location-id-here", {
idempotency_key: new Date(),
card_nonce: request.query.nonce,
amount_money: {
amount: 500,
currency: "USD"
}
}).then(function (data) {
console.log('API called successfully. Returned data: ' + JSON.stringify(data));
response.send(data);
}, function (error) {
console.error(JSON.parse(error.response.text).errors[0].detail);
response.send(error);
});
})
// Replace the IP address with your own local IP
app.listen(8080, '192.168.1.100', () => {
console.log("Server running...");
})

The code is pretty straight forward. We are using express to create an endpoint that received the GET requests. We receive the query parameter called nonce from the GET request and then use it to perform the transaction. Keep in mind that you need to replace your-access-token-here and your-location-id-here your test access token and location ID that you can get from the Square developer portal just the same you got the application ID. Finally, find your local IP address and replace the address in the last part with your address.

As of now, we are hardcoding the amount as $500. You can modify the code later to receive these values as parameters from the client as well.

This is required for local testing. This allows you to connect your phone to the same wi-fi network and test the app.

Let’s kick off the server. Type node server in the terminal and you should see the message “Server running…”. Let it be and test the server in the browser. Open the URL, http://<you-local-ip-address>:8080 in the browser and you should see a page like this.

The browser window while the server is running

If you do not see the above response, something is wrong and you need to check your IP address. You can also check the response of the server by sending a valid nonce like this.

http://<you-local-ip-address>:8080?nonce=cnon:CBwefwefdewdewdwe

Try with an invalid nonce as well. in every case, you will get a JSON response from the server.

Back to app

Now we need to get back in the app code to write the code to use the server that we just wrote. The app will use the http package to send the nonce to the local server and then display the response using an AlertDialog.

First off, install the http package. Add the following dependency to pubspec.yaml.

dependencies:
flutter:
sdk:
flutter
square_in_app_payments: ^1.1.0
http: any

Again, click Packages get to install the package, close the app and restart it. This is required whenever you install a new package.

Now, we modify the code in the _pay method as follows.

InAppPayments.startCardEntryFlow(
onCardNonceRequestSuccess: (CardDetails details) {
print(details.nonce); // here you get the nonce
// The following method dismisses the card entry UI
// It is required to be called
InAppPayments.completeCardEntry(
onCardEntryComplete: () async {

Response response = await get("http://<your-local-server-ip-address>:8080?nonce=" + details.nonce);

await showDialog(
context: context,
builder: (BuildContext ctx) {
return AlertDialog(
title: Text("Square Payments API Response"),
content: Text(response.body.toString()),
actions: <Widget>[
FlatButton(
child: Text("OK"),
onPressed: () {
Navigator.pop(ctx);
},
),
],
);
});
}
);
}, onCardEntryCancel: () {
print("canceled");
}
);

We just sent a GET request to the server. Ensure to replace <your-local-server-ip-address> with the same local IP address that you have been using everywhere. Connect the phone to the same wifi that your system running the server is connected and test the app.

Enter the card details mentioned above and you should see an Alert that shows the server’s response. The response contains information like transaction id and the status. You can read this information from the response and display another page to the user, or show them a message. The alert above is only for debugging. And getting the alert means that the transaction is processed successfully. Since this was a test transaction, there is no way to verify it. You need to switch all credentials (client and server both) to production and try again to see the real transaction in your Square seller dashboard.

Yay! That was all. Easy and simple. If you face any issues, please post in the comments. Your feedback is welcome. Keep reading, keep building. Cheers.

--

--