lipaNaMpesa method

Future<MpesaResponse> lipaNaMpesa({
  1. required String phoneNumber,
  2. required double amount,
  3. required String callbackUrl,
  4. String? businessShortCode,
  5. String transactionType = "CustomerPayBillOnline",
  6. String accountReference = "account",
  7. String transactionDescription = "Lipa Na Mpesa Online",
})

Triggers a lipa na mpesa stk push and presents user with dialog to input mpesa pin. The method will complete regardless of whether the transaction was successful or not. Results of the transaction are sent to the callbackUrl provided. Ensure that the callbackUrl provided is publicly accessible. You can use ngrok,localtunnel or serveo for local development.

businessShortCode can be gotten from https://developer.safaricom.co.ke/APIs/MpesaExpressSimulate under the Lipa Na Mpesa Online simulator under PartyB. You can ignore this if you have already set it in globalBusinessShortCode. Please note that this is for the sandbox environment. Use your registered business short code for production.

phoneNumber is the phone number(MSISDN) to be charged. It must be a registered mpesa number(MSISDN) and should contain the international dialing code i.e 254. For example 254712345678

amount is the amount to be charged. During development/sandbox all money transfered is refunded by safaricom within 24 hours. Please note that this is only applicable if you're using the businessShortCode provided by Safaricom and not a real one.

callbackUrl is the url to which Mpesa responses are sent upon success or failure of a transaction. Should be able to receive post requests.

accountReference used with Mpesa paybills as account number,

Please see https://developer.safaricom.co.ke/Documentation for more info.

Implementation

Future<MpesaResponse> lipaNaMpesa({
  required String phoneNumber,
  required double amount,
  required String callbackUrl,
  String? businessShortCode,
  String transactionType = "CustomerPayBillOnline",
  String accountReference = "account",
  String transactionDescription = "Lipa Na Mpesa Online",
}) async {
  if (amount < 1.0) {
    throw "Amount should be at least Ksh 1";
  }

  final _now = DateTime.now();
  final _paybill = businessShortCode;

  if (_paybill == null) {
    throw "Paybill must be set either via businessShortCode or via globalBusinessShortCode";
  }

  final _timestamp =
      "${_now.year}${_now.month.toString().padLeft(2, '0')}${_now.day.toString().padLeft(2, '0')}${_now.hour.toString().padLeft(2, '0')}${_now.minute.toString().padLeft(2, '0')}${_now.second.toString().padLeft(2, '0')}";

  final String _rawPassword = _paybill + _passKey + _timestamp;

  final List<int> _passwordBytes = utf8.encode(_rawPassword);

  final String _password = base64.encode(_passwordBytes);

  final String token = await _authenticate();

  final String _body = json.encode({
    'BusinessShortCode': _paybill,
    'Password': _password,
    'Timestamp': _timestamp,
    'TransactionType': transactionType,
    'Amount': amount,
    'PartyA': phoneNumber,
    'PartyB': _paybill,
    'PhoneNumber': phoneNumber,
    'CallBackURL': callbackUrl.toString(),
    'AccountReference': accountReference,
    'TransactionDesc': transactionDescription
  });

  final http.Response response = await http.post(
    Uri.parse("$_baseUrl${Routes.stkpush}"),
    body: _body,
    headers: {
      'Authorization': 'Bearer $token',
      'Content-Type': 'application/json',
    },
  );

  if (response.statusCode != 200) {
    throw json.decode(response.body) as Map<dynamic, dynamic>;
  }

  return MpesaResponse.fromMap(
    json.decode(response.body) as Map<String, dynamic>,
  );
}