Skip to main content

AdjeminPay Flutter

With AdjeminPay, integrating mobile money payments in your flutter app becomes super easy, barely an inconvenience.

Using#

The package generates a payment gate, and when the pay user is finished paying, the payment gate return the payment result.

Follow the steps below :

Step 1. Get and store your credentials in a constants files#

Sign up and create a merchant application for free at AdjeminPay to get an apiKey and an applicationId.

Then store them in a Constants file where you keep all your other constant data.

class Constants{
static final String ADJEMINPAY_CLIENT_ID = "CLIENT_ID";
static final String ADJEMINPAY_CLIENT_SECRET = "CLIENT_SECRET";
static final String ADJEMINPAY_NOTIFICATION_URL = "https://your.backend.api/notifyUrl";
}

Step 2. Import the package#

Add the adjeminpay_flutter package as a dependancy in your pubspec.yaml file.

dependencies:
flutter:
sdk: flutter
adjeminpay_flutter: ^1.1.103

Then import it to your cart screen

import 'package:adjeminpay_flutter/adjeminpay_flutter.dart';

Your cart_screen is where you will show your users the items they will pay for.

Step 3. Build your cart screen#

Build a cart screen to finalize the payment.

You can find a beautiful cart screen example from Academind by Maximilian Schwarzmüller in the Example file.

Step 4. Have your order/transaction data ready#

Store the data about the payment your user is about to make.

NB: It is recommended you store the payment data in your database before executing the payment function.

This example uses a basic Map for storing data about an order but it could be anything, from transactions, to payment, anything that needs online payment.

Map<String, dynamic> myOrder = {
// ! required transactionId or orderId
'transaction_id': "UniqueTransactionId",
// ! required total amount
'total_amount': 1000,
// optional your orderItems data
'items': [
{
'id': '1',
'productId': 'prod1',
'price': 100,
'quantity': 1,
'title': 'Product 1 title',
},
{
'id': '2',
'productId': 'prod9',
'price': 300,
'quantity': 3,
'title': 'Product 9 title',
},
],
'currency_code': "XOF",
'designation': "Order Title",
'client_name': "ClientName",
};

Step 5. Add a function to launch the payment gate and handle the payment result#

Copy-paste this function that takes in the order data and extracts the fields needed by the payment api.

required fields: The following fiels are required:

  • clientId : You can get it in Step 1.
  • clientSecret : You can get it in Step 1.
  • merchantTransactionId : Unique id for everyone of your payments
  • designation : What your user will see as what they're paying for
  • amount : Obviously, right ?
  • currencyCode: Currency for the payment NB: only XOF is supported, so check back soon for more
  • notificationUrl : A url you set in your web backend. A post request with the payment result (a json containing {transactionId, status, message}) will be sent to that url when the payment is completed (successful, failed, cancelled, expired) to allow you to update the order/transaction status in your database directly from your backend. This field is optional, since you could do such an update as a callback from your flutter app. NB: If you do want to use the notificationUrl, please check out our php sdk for how to catch the notification.

optional fields: These fields are optional:

  • buyerName : The name of your user NB: It is recommended that you provide it in your payment data as it saves your user the hassle of having to type it in the payment gate.

Now let's implement the payment handler function

NB: This function and the callbacks will be included directly in the library in future versions.

void payWithAdjeminPay(dynamic orderData) async {
// paymentResult will yield {transactionId, status, message }
// once the payment gate is closed by the user
// ! IMPORTANT : make sure to save the orderData in your database first
// ! before calling this function
Map<String, dynamic> paymentResult = await Navigator.push(
context,
new MaterialPageRoute(
builder: (context) =>
// The AdjeminPay class
AdjeminPay(
// ! required clientId
clientId: Constants.ADJEMINPAY_CLIENT_ID,
// ! required clientScret
clientSecret: Constants.ADJEMINPAY_CLIENT_SECRET,
// ! required transactionId required
// for you to follow the transaction
// or retrieve it later
// should be a string < 191 and unique for your application
merchantTransactionId: "${orderData['transaction_id']}",
// ! required designation
// the name the user will see as what they're paying for
designation: orderData['designation'],
// notificationUrl for your web backend
notificationUrl:Constants.ADJEMINPAY_NOTIFICATION_URL,
// amount: int.parse("${orderData['totalAmount']}"),
// ! required amount
// amount the user is going to pay
// should be an int
amount: int.parse("${orderData['total_amount']}"),
// currency code
// currently supported currency is XOF
currencyCode: orderData['currency_code'],
// designation: widget.element.title,
// the name of your user
buyerName: orderData['client_name'], //optional
),
));
print(">>> ADJEMINPAY PAYMENT RESULTS <<<");
print(paymentResult);
// * Here you define your callbacks
// Callback if the paymentResult is null
// the payment gate got closed without sending back any data
if (paymentResult == null) {
print("<<< Payment Gate Unexpectedly closed");
return;
}
Scaffold.of(context).showSnackBar(SnackBar(content: Text("Payment Status is ${paymentResult['status']}")));
// Callback on payment successfully
if (paymentResult['status'] == "SUCCESSFUL") {
print("<<< AdjeminPay success");
print(paymentResult);
// redirect to or show another screen
return;
}
// Callback on payment failed
if (paymentResult['status'] == "FAILED") {
print("<<< AdjeminPay failed");
print(paymentResult);
// the reason with be mentionned in the paymentResult['message']
print("Reason is : " + paymentResult['message']);
// redirect to or show another screen
return;
}
// Callback on payment cancelled
if (paymentResult['status'] == "CANCELLED") {
print("<<< AdjeminPay cancelled");
print(paymentResult);
// the reason with be mentionned in the paymentResult['message']
print("Reason is : " + paymentResult['message']);
// redirect to or show another screen
return;
}
// Callback on payment cancelled
if (paymentResult['status'] == "EXPIRED") {
print("<<< AdjeminPay expired");
print(paymentResult);
// The user took too long to approve or refuse payment
print("Reason is : " + paymentResult['message']);
// redirect to or show another screen
return;
}
// Callback on initialisation error
if (paymentResult['status'] == "INVALID_PARAMS") {
print("<<< AdjeminPay Init error");
// You didn't specify a required field
print(paymentResult);
return;
}
if (paymentResult['status'] == "INVALID_CREDENTIALS") {
print("<<< AdjeminPay Init error");
// You didn't specify a required field
// or your clientId or clientSecret are not valid
print(paymentResult);
return;
}
// Callback when AdjeminPay requests aren't completed
if (paymentResult['status'] == "ERROR_HTTP") {
return;
}
}

Step 6. Pass the function above to your "Order Now" button#

FlatButton(
child: _isLoading
? CircularProgressIndicator()
: Text('ORDER NOW'),
onPressed: (myOrder['totalAmount'] == null ||
myOrder['totalAmount'] <= 0 ||
_isLoading)
? null
: () {
// **** Payment Management Here
// you first store the Order's data in
// your database where you create a unique transaction Id
// for example : await storeOrderData(myOrder);
// then you call the payment function
payWithAdjeminPay(myOrder);
},
textColor: Theme.of(context).primaryColor,
);

Capture the payment notification in your backend#

Do you remember the parameter notification_url YOUR NOTIFICATION URL ?

You must now implement it in your backend to be notified of the progress of the payment and update your database.

When you initiate a transaction you provide a notification URL to which we notify you of the outcome of the transaction to allow you to have the details of the transaction.

AdjeminPay makes an HTTP request to your NOTIFICATION URL with the POST method and the following parameters in BODY:

The Content-Type of the request is application/x-www-form-urlencoded

  • transaction_id : the transaction reference provided by the merchant (you)
  • status : the payment status
  • amount : the amount of the transaction
  • phone_number : the phone number provided by the user making the payment
  • buyer_name : user's name
  • buyer_reference :the phone number or reference provided by the user making the payment
  • currency_code : currency
  • cancelled_at : if the transaction is canceled, the date on which the transaction is canceled
  • approuved_at : if the transaction is approved, the date the transaction is approved
  • transaction_type : the payment method used ORANGE_CI or MTN_CI

Code PHP#

Example of management of notifications from AdjeminPay

hook.php
<?php
public function notifyAdjeminPay(Request $request)
{
$input = $request->all();
$transaction_reference = $input['transaction_id'];
$status = $input['status'];
if(empty($transaction_reference)){
return response()->json([
'error' => [
'message' => "Transaction not found",
'transaction_reference' => $transaction_reference
],
]);
}
// Recuperation de la ligne de la transaction dans votre base de données
$transaction = InvoicePayment::where(['payment_reference' => $transaction_reference])->first();
if(empty($transaction)){
return response()->json([
'error' => [
'message' => "Transaction not found",
'transaction_reference' => $transaction_reference
],
]);
}
$invoice = Invoice::where(["id" => $transaction->invoice_id])->first();
$responseData = null;
if($invoice->status == 'UNPAID'){
if ($transaction != null) {
switch ($status) {
case 'SUCCESSFUL' :
$transaction->status = 'SUCCESSFUL';
$transaction->is_waiting = false;
$transaction->is_completed = true;
$transaction->save();
$this->validateTransaction($transaction_reference);
break;
case 'FAILED' :
$transaction->status = 'FAILED';
$transaction->is_waiting = false;
$transaction->is_completed = true;
$transaction->save();
$this->cancelTransaction($transaction_reference);
break;
case 'CANCELLED' :
$transaction->status = 'CANCELLED';
$transaction->is_waiting = false;
$transaction->is_completed = true;
$transaction->save();
$this->cancelTransaction($transaction_reference);
break;
case 'EXPIRED' :
$transaction->status = 'EXPIRED';
$transaction->is_waiting = false;
$transaction->is_completed = true;
$transaction->save();
break;
default:
return response()->json([
'error' => [
'message' => 'MISSING_TRANSACTION_STATUS'
],
]);
break;
}
}
}
?>