This article details Paystack integration with Android. Paystack is one of the Top Payment Gateways we have in Nigeria
The Paystack Android SDK allows businesses using Paystack to collect payments within their Android app.
The SDK shoulders the burden of PCI compliance, allowing your app to send card information directly to Paystack servers where they can process them. All your server needs to do is call Paystack to verify the status of the transaction. The SDK also validates card details client-side and provides an interface for collecting additional verification like card PIN and OTP.
Paystack supports Android back to version 16, which is the first SDK version that includes TLSv1.2 which is required by their servers. Native app support for user devices older than API 16 will not be available.
Paystack integration with Android
Installations
To install the library on Android Studio, add the following lines to your project’s build.gradle file.
dependencies { | |
implementation ‘co.paystack.android.design.widget:pinpad:1.0.1’ | |
implementation ‘co.paystack.android:paystack:3.0.10’ | |
} |
Enable Permissions
To prepare for use, you must ensure that your app has internet permissions by making sure the uses-permission line below is present in the AndroidManifest.xml.
<uses-permission android:name=”android.permission.INTERNET” /> |
Set Your Public Key
Before you can charge a card with the PaystackSdk class, you need to set your public key. Your public key is available on the Paystack Dashboard.
Set your public key in your project’s AndroidManifest.xml by adding the following lines of code to the tag of your AndroidManifest.xml
<meta-data | |
android:name=”co.paystack.android.PublicKey” | |
android:value=”your public key obtained from: https://dashboard.paystack.co/#/settings/developer”/> |
Initialize SDK
To use the Paystack Android SDK, you need to first initialize it using the PaystackSdk class.
public class PaymentActivity extends AppCompatActivity { | |
@Override | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_payment); | |
PaystackSdk.initialize(getApplicationContext()); | |
} | |
} |
Make sure to call this method in the onCreate method of your Fragment or Activity or Application
Collecting card information
Create a form that collects card information. You need to collect your customers’ card numbers, expiration dates and CVC. PaystackSDK has a Card class that allows you to collect and verify these Card information, here is a sample Android XML code that helps you collect card information
<?xml version=“1.0“ encoding=“utf-8“?> | |
<ScrollView xmlns:android=“http://schemas.android.com/apk/res/android“ | |
android:layout_width=“fill_parent“ | |
android:layout_height=“fill_parent“ | |
android:background=“@drawable/bg“ | |
android:fillViewport=“true“> | |
<LinearLayout | |
android:layout_width=“match_parent“ | |
android:layout_height=“match_parent“ | |
android:orientation=“vertical“ | |
android:focusableInTouchMode=“true“ | |
android:padding=“16dp“> | |
<ImageView | |
android:layout_width=“wrap_content“ | |
android:layout_height=“wrap_content“ | |
android:layout_gravity=“center_horizontal“ | |
android:layout_marginBottom=“20dp“ | |
android:scaleType=“centerCrop“ | |
android:src=“@drawable/logo“ /> | |
<EditText | |
android:id=“@+id/card_number“ | |
android:layout_width=“match_parent“ | |
android:layout_height=“wrap_content“ | |
android:layout_marginBottom=“20dp“ | |
android:background=“@drawable/edit_text“ | |
android:drawableLeft=“@drawable/card“ | |
android:drawablePadding=“5dp“ | |
android:hint=“Card Number“ | |
android:inputType=“number“ | |
android:padding=“10dp“ | |
android:textColor=“#fff“ | |
android:textColorHint=“#D3D3D3“ /> | |
<LinearLayout | |
android:layout_width=“match_parent“ | |
android:layout_height=“wrap_content“ | |
android:orientation=“horizontal“> | |
<EditText | |
android:id=“@+id/month“ | |
android:layout_width=“0dp“ | |
android:layout_height=“wrap_content“ | |
android:layout_marginRight=“10dp“ | |
android:layout_marginBottom=“20dp“ | |
android:layout_weight=“1“ | |
android:background=“@drawable/edit_text“ | |
android:drawableLeft=“@drawable/date“ | |
android:drawablePadding=“5dp“ | |
android:hint=“Expiry Month“ | |
android:inputType=“number“ | |
android:padding=“10dp“ | |
android:textColor=“#fff“ | |
android:textColorHint=“#D3D3D3“ /> | |
<EditText | |
android:id=“@+id/year“ | |
android:layout_width=“0dp“ | |
android:layout_height=“wrap_content“ | |
android:layout_marginBottom=“20dp“ | |
android:layout_weight=“1“ | |
android:background=“@drawable/edit_text“ | |
android:drawableLeft=“@drawable/date“ | |
android:drawablePadding=“5dp“ | |
android:hint=“Expiry Year“ | |
android:inputType=“number“ | |
android:padding=“10dp“ | |
android:textColor=“#fff“ | |
android:textColorHint=“#D3D3D3“ /> | |
</LinearLayout> | |
<EditText | |
android:id=“@+id/cvc“ | |
android:layout_width=“match_parent“ | |
android:layout_height=“wrap_content“ | |
android:layout_marginBottom=“20dp“ | |
android:background=“@drawable/edit_text“ | |
android:drawableLeft=“@drawable/card“ | |
android:drawablePadding=“5dp“ | |
android:hint=“CVV“ | |
android:inputType=“number“ | |
android:padding=“10dp“ | |
android:textColor=“#fff“ | |
android:textColorHint=“#D3D3D3“ /> | |
<Button | |
android:layout_width=“match_parent“ | |
android:layout_height=“wrap_content“ | |
android:onClick=“pay“ | |
android:text=“Make Payment“ | |
android:textColor=“#FFFFFF“ /> | |
</LinearLayout> | |
</ScrollView> |
Validate Card Values and Charge User
Using the functions: setCurrency, setPlan, setSubaccount, setTransactionCharge, setAmount, setEmail, setReference, setBearer, putMetadata, putCustomField, you can set up a fresh transaction direct from the SDK.
The following codes retrieve values from the form and proceed to call the charge function that will be shown later
public void pay(View view) { | |
int expiryMonth = 0; | |
int expiryYear = 0; | |
EditText etCardNumber = findViewById(R.id.card_number); | |
EditText etExpiryMonth = findViewById(R.id.month); | |
EditText etExpiryYear = findViewById(R.id.year); | |
EditText etCVC = findViewById(R.id.cvc); | |
String cardNumber = etCardNumber.getText().toString(); | |
if(!etExpiryMonth.getText().toString().isEmpty()) { | |
expiryMonth = Integer.parseInt(etExpiryMonth.getText().toString()); | |
} | |
if(!etExpiryMonth.getText().toString().isEmpty()) { | |
expiryYear = Integer.parseInt(etExpiryYear.getText().toString()); | |
} | |
String cvv = etCVC.getText().toString(); | |
Card card = new Card(cardNumber, expiryMonth, expiryYear, cvv); | |
if (card.isValid()) { | |
charge = new Charge(); | |
charge.setCard(card); | |
dialog = new ProgressDialog(PaymentActivity.this); | |
dialog.setMessage(“Performing transaction… please wait”); | |
dialog.show(); | |
charge.setAmount(1050); | |
charge.setEmail(email); | |
charge.setReference(“ChargedFromAndroid_” + Calendar.getInstance().getTimeInMillis()); | |
try { | |
charge.putCustomField(“Charged From”, “Android SDK”); | |
} catch (JSONException e) { | |
e.printStackTrace(); | |
} | |
chargeCard();\\Function to Charge user here | |
} | |
else { | |
Toast.makeText(PaymentActivity.this, “Invalid card details”, Toast.LENGTH_LONG).show(); | |
} | |
} | |
private void dismissDialog() { | |
if ((dialog != null) && dialog.isShowing()) { | |
dialog.dismiss(); | |
} | |
} |
When an error occurs or transaction or concludes successfully, we will call the methods available in the callback you provided.
OnSuccess will be called once the charge succeeds here we call a function to verify the transaction on the server and perform the desired action you want on the server
beforeValidate is called every time the SDK needs to request user input. This function currently only allows the app know that the SDK is requesting further user input.OnError
onError is called if an error occurred during processing. Some Exception types that you should watch include
ExpiredAccessCodeException: This would be thrown if the access code has already been used to attempt a charge.
ChargeException: This would be thrown if the charge failed. It would hold the message from the server.
private void chargeCard() { | |
transaction = null; | |
PaystackSdk.chargeCard(PaymentActivity.this, charge, new Paystack.TransactionCallback() { | |
// This is called only after transaction is successful | |
@Override | |
public void onSuccess(Transaction transaction) { | |
dismissDialog(); | |
PaymentActivity.this.transaction = transaction; | |
Toast.makeText(PaymentActivity.this, transaction.getReference(), Toast.LENGTH_LONG).show(); | |
new verifyOnServer().execute(transaction.getReference()); | |
} | |
// This is called only before requesting OTP | |
// Save reference so you may send to server if | |
// error occurs with OTP | |
// No need to dismiss dialog | |
@Override | |
public void beforeValidate(Transaction transaction) { | |
PaymentActivity.this.transaction = transaction; | |
Toast.makeText(PaymentActivity.this, transaction.getReference(), Toast.LENGTH_LONG).show(); | |
} | |
@Override | |
public void onError(Throwable error, Transaction transaction) { | |
// If an access code has expired, simply ask your server for a new one | |
// and restart the charge instead of displaying error | |
PaymentActivity.this.transaction = transaction; | |
if (error instanceof ExpiredAccessCodeException) { | |
PaymentActivity.this.chargeCard(); | |
return; | |
} | |
dismissDialog(); | |
if (transaction.getReference() != null) { | |
Toast.makeText(PaymentActivity.this, transaction.getReference() + ” concluded with error: ” + error.getMessage(), Toast.LENGTH_LONG).show(); | |
new verifyOnServer().execute(transaction.getReference()); | |
} else { | |
Toast.makeText(PaymentActivity.this, error.getMessage(), Toast.LENGTH_LONG).show(); | |
} | |
} | |
}); | |
} | |
private class verifyOnServer extends AsyncTask<String, Void, String> { | |
private String reference; | |
private String error; | |
@Override | |
protected void onPostExecute(String result) { | |
super.onPostExecute(result); | |
if (result != null) { | |
Toast.makeText(PaymentActivity.this, String.format(“Gateway response: %s”, result), Toast.LENGTH_LONG).show(); | |
} else { | |
Toast.makeText(PaymentActivity.this, String.format(“There was a problem verifying %s on the backend: %s “, this.reference, error), Toast.LENGTH_LONG).show(); | |
dismissDialog(); | |
} | |
} | |
@Override | |
protected String doInBackground(String… reference) { | |
try { | |
this.reference = reference[0]; | |
String json = String.format(“{\”reference\”:\”%s\”}”, this.reference); | |
String url1 = “https://www.serverdomain.com/app/verify.php?details=” + json; | |
URL url = new URL(url1); | |
BufferedReader in = new BufferedReader( | |
new InputStreamReader( | |
url.openStream())); | |
String inputLine; | |
inputLine = in.readLine(); | |
in.close(); | |
return inputLine; | |
} catch (Exception e) { | |
error = e.getClass().getSimpleName() + “: ” + e.getMessage(); | |
} | |
return null; | |
} | |
} |
Send the reference to your backend and verify by calling our REST API. An authorization will be returned which will let you know if its code is reusable. You can learn more about our verify call here
Below is a sample code on how you can achieve that using php
<?php | |
$details = $_GET[“details”]; | |
$obj = json_decode($details); | |
$reference = $obj -> {“reference”}; | |
$result = array(); | |
//The parameter after verify/ is the transaction reference to be verified | |
$url = ‘https://api.paystack.co/transaction/verify/$reference’; | |
$ch = curl_init(); | |
curl_setopt($ch, CURLOPT_URL, $url); | |
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); | |
curl_setopt( | |
$ch, CURLOPT_HTTPHEADER, [ | |
‘Authorization: Bearer SECRET_KEY’] | |
); | |
$request = curl_exec($ch); | |
curl_close($ch); | |
if ($request) { | |
$result = json_decode($request, true); | |
// print_r($result); | |
if($result){ | |
if($result[‘data’]){ | |
//something came in | |
if($result[‘data’][‘status’] == ‘success’){ | |
// the transaction was successful, you can deliver value | |
/* | |
@ also remember that if this was a card transaction, you can store the | |
@ card authorization to enable you charge the customer subsequently. | |
@ The card authorization is in: | |
@ $result[‘data’][‘authorization’][‘authorization_code’]; | |
@ PS: Store the authorization with this email address used for this transaction. | |
@ The authorization will only work with this particular email. | |
@ If the user changes his email on your system, it will be unusable | |
*/ | |
echo “Transaction was successful”; | |
}else{ | |
// the transaction was not successful, do not deliver value’ | |
// print_r($result); //uncomment this line to inspect the result, to check why it failed. | |
echo “Transaction was not successful: Last gateway response was: “.$result[‘data’][‘gateway_response’]; | |
} | |
}else{ | |
echo $result[‘message’]; | |
} | |
}else{ | |
//print_r($result); | |
die(“Something went wrong while trying to convert the request variable to json. Uncomment the print_r command to see what is in the result variable.”); | |
} | |
}else{ | |
//var_dump($request); | |
die(“Something went wrong while executing curl. Uncomment the var_dump line above this line to see what the issue is. Please check your CURL command to make sure everything is ok”); | |
} |
Do you have any questions on Paystack integration with Android or need help, place a comment below