brilliant_mind / m-pesa
MPesa package for laravel
Requires
- php: ^8.1
- ext-curl: *
- ext-json: *
- ext-openssl: *
- guzzlehttp/guzzle: ^7.0
- illuminate/support: ^10.0|^11.0|^12.0
Requires (Dev)
- orchestra/testbench: ^9.0
- phpunit/phpunit: ^11.3
This package is auto-updated.
Last update: 2026-07-23 17:56:47 UTC
README
SDK para interagir com a API M-Pesa Moçambique: C2B, B2B, B2C, consulta de transação e reversão.
Feito para funcionar no Laravel sem configuração manual: instala, coloca duas variáveis no .env e já podes cobrar.
Índice
- Instalação
- Configuração
- Utilização
- O objecto Transaction
- Códigos de resposta
- Modo fake (testes)
- Erros
- Fora do Laravel
- Notas sobre portos M-Pesa
- Requisitos
Instalação
composer require brilliant_mind/m-pesa
É tudo. O pacote usa package auto-discovery do Laravel — o ServiceProvider e os aliases MPesa e Mpesa são registados automaticamente. Não precisas de publicar o config, nem de editar config/app.php, bootstrap/providers.php ou o AppServiceProvider.
Configuração
Variáveis de ambiente
Só MPESA_API_KEY e MPESA_PUBLIC_KEY são obrigatórias. Tudo o resto tem valores por omissão prontos para o sandbox.
Desenvolvimento (sandbox)
# MPESA DEVELOPMENT MPESA_API_KEY=sua_api_key_sandbox MPESA_PUBLIC_KEY="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB...FwIDAQAB" MPESA_ENVIRONMENT=development MPESA_SERVICE_PROVIDER_CODE=171717
Produção
# MPESA PRODUCTION MPESA_API_KEY=sua_api_key_producao MPESA_PUBLIC_KEY="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB...FwIDAQAB" MPESA_ENVIRONMENT=production MPESA_SERVICE_PROVIDER_CODE=903153 MPESA_INITIATOR_IDENTIFIER=seu_initiator MPESA_SECURITY_CREDENTIAL=seu_credential_encriptado
A chave pública é uma linha longa de base64 — envolve-a em aspas duplas. É aceite em qualquer formato: uma linha, com quebras de linha, ou o bloco PEM completo. Se preferires guardá-la num ficheiro, deixa
MPESA_PUBLIC_KEYvazia e usaMPESA_PUBLIC_KEY_PATH.
Nota:
MPESA_ENVcontinua a ser aceite como alias retrocompatível deMPESA_ENVIRONMENT.
Se mudaste o .env e nada aconteceu, limpa a cache: php artisan config:clear.
Tabela completa de variáveis
| Variável | Obrigatória | Default | Descrição |
|---|---|---|---|
MPESA_API_KEY |
Sim | '' |
Chave de API fornecida pelo portal da M-Pesa (sandbox ou produção). |
MPESA_PUBLIC_KEY |
Sim | '' |
Chave pública RSA em base64. Usada para encriptar o MPESA_API_KEY em cada chamada. |
MPESA_PUBLIC_KEY_PATH |
Não | '' |
Caminho para um ficheiro com a chave pública. Usado apenas se MPESA_PUBLIC_KEY estiver vazia. |
MPESA_ENVIRONMENT |
Não | development |
development (sandbox) ou production. Determina o host por defeito. Aceita também sandbox, dev, prod e live. |
MPESA_SERVICE_PROVIDER_CODE |
Não | 171717 |
Código do service provider. 171717 é o de sandbox; em produção usa o teu (ex. 903153). |
MPESA_ORIGIN |
Não | developer.mpesa.vm.co.mz |
Valor do header Origin enviado a cada pedido. |
MPESA_INITIATOR_IDENTIFIER |
Apenas reversão | '' |
Identificador do initiator — obrigatório para reversal(). |
MPESA_SECURITY_CREDENTIAL |
Apenas reversão | '' |
Credencial encriptada do initiator — obrigatória para reversal(). |
MPESA_HOST |
Não | derivado | Override manual do host. Por defeito escolhe api.sandbox.vm.co.mz ou api.vm.co.mz. |
MPESA_PORT |
Não | '' |
Força um único porto para todas as operações. Vazio usa o porto de cada operação. |
MPESA_TIMEOUT |
Não | 90 |
Timeout de resposta em segundos. O C2B espera o PIN do cliente, por isso é longo. |
MPESA_CONNECT_TIMEOUT |
Não | 30 |
Timeout de ligação em segundos. |
MPESA_VERIFY_SSL |
Não | true |
Verificação do certificado TLS. |
Publicar o ficheiro de configuração (opcional)
Só é necessário se quiseres alterar defaults além do .env:
php artisan vendor:publish --tag=mpesa-config
Utilização
As operações existem em duas formas, ambas suportadas:
// Estática — como na v1.x use BrilliantMind\MPesa\MPesa; $transaction = MPesa::c2b(100, '841234567', 'ENC0001', 'TPR0001'); // De instância — via container, injecção de dependências ou facade $transaction = app('mpesa')->c2b(100, '841234567', 'ENC0001', 'TPR0001');
A facade está disponível globalmente, sem use, em qualquer das grafias (MPesa ou Mpesa):
$transaction = MPesa::c2b( amount: 100, msisdn: '841234567', transactionReference: 'ENC0001', thirdPartyReference: 'TPR0001', ); if ($transaction->isSuccessful()) { // guardar $transaction->getTransactionID() } return $transaction->getMessage();
O número de telemóvel é normalizado automaticamente — 841234567, 0841234567, +258 84 123 4567 e 258841234567 produzem todos 258841234567.
C2B — cliente paga à empresa
Debita a carteira do cliente e credita a da empresa. A M-Pesa envia um USSD Push ao cliente para confirmar o PIN.
$transaction = MPesa::c2b(100, '841234567', 'ENC0001', 'TPR0001');
B2C — empresa paga ao cliente
$transaction = MPesa::b2c(100, '841234567', 'ENC0002', 'TPR0002');
B2B — empresa paga a outra empresa
O segundo argumento é o shortcode da empresa que recebe, não um número de telemóvel.
$transaction = MPesa::b2b(100, '979797', 'ENC0003', 'TPR0003');
Consulta de transação
$transaction = MPesa::transaction('ENC0001', 'TPR0001'); $transaction->getTransactionStatus(); // ex.: 'Completed'
Reversão
Requer MPESA_INITIATOR_IDENTIFIER e MPESA_SECURITY_CREDENTIAL.
$transaction = MPesa::reversal(100, '49XCDF6', 'TPR0001');
O objecto Transaction
$transaction->isSuccessful(); // bool — true quando o código é INS-0 $transaction->failed(); // bool $transaction->getResponseCode(); // 'INS-0' $transaction->getMessage(); // descrição legível $transaction->getTransactionID(); // id da transacção na M-Pesa $transaction->getConversationID(); $transaction->getThirdPartReference(); $transaction->getTransactionStatus(); // só na consulta de estado $transaction->getHttpStatus(); // HTTP devolvido pelo gateway $transaction->getStatusCode(); // HTTP equivalente ao código INS $transaction->raw(); // payload original da M-Pesa $transaction->toArray(); $transaction->toJson();
Nunca lança excepção por a M-Pesa ter recusado a transacção: uma recusa é sempre um Transaction com failed() === true. Excepções ficam reservadas para erros de configuração e de rede (ver Erros).
Códigos de resposta
A tabela completa de códigos INS está em BrilliantMind\MPesa\Response::$codes:
use BrilliantMind\MPesa\Response; Response::message('INS-2006'); // 'Insufficient balance' Response::status('INS-2006'); // 422 Response::describe('INS-2006'); // ['code' => 422, 'message' => 'Insufficient balance'] Response::isSuccessful('INS-0'); // true
Modo fake (testes)
Nos teus testes usa fake() para não tocar na rede:
use BrilliantMind\MPesa\MPesa; MPesa::fake(); // devolve sempre INS-0 MPesa::fake(422, 'INS-2006'); // simula saldo insuficiente MPesa::fakeWith([ // controlo total do payload 'output_ResponseCode' => 'INS-0', 'output_TransactionID' => '49XCDF6', ]); MPesa::c2b(100, '841234567', 'REF', 'TPR'); MPesa::recorded(); // pedidos capturados, para asserções MPesa::stopFaking();
Erros
| Excepção | Quando acontece | Como resolver |
|---|---|---|
MissingConfigurationException |
MPESA_API_KEY ou MPESA_PUBLIC_KEY em falta |
Preenche o .env e corre php artisan config:clear |
EncryptionException |
A chave pública não é válida | Copia outra vez a Public Key do portal da M-Pesa |
InvalidEnvironmentException |
MPESA_ENVIRONMENT com valor desconhecido |
Usa development ou production |
ConnectionException |
O gateway não respondeu | Confirma que o teu servidor permite tráfego de saída nos portos M-Pesa (ver abaixo) |
Todas herdam de BrilliantMind\MPesa\Exceptions\MPesaException, por isso podes apanhar tudo de uma vez:
use BrilliantMind\MPesa\Exceptions\MPesaException; try { $transaction = MPesa::c2b(100, $msisdn, $reference, $thirdPartyReference); } catch (MPesaException $e) { report($e); }
Fora do Laravel
O pacote também funciona em PHP puro; basta configurá-lo à mão:
require __DIR__ . '/vendor/autoload.php'; use BrilliantMind\MPesa\MPesa; MPesa::config( api_key: 'a-tua-api-key', public_key: 'a-tua-public-key', environment: 'development', service_provider_code: '171717', ); $transaction = MPesa::c2b(100, '841234567', 'ENC0001', 'TPR0001');
MPesa::config() aceita argumentos parciais — o que não passares mantém o valor actual.
Notas sobre portos M-Pesa
O gateway escuta num porto diferente por operação e a SDK trata disso sozinha:
| Operação | Porto |
|---|---|
| C2B | 18352 |
| B2C | 18345 |
| B2B | 18349 |
| Consulta de estado | 18353 |
| Reversão | 18354 |
Como não são portos padrão, muitos alojamentos partilhados bloqueiam o tráfego de saída para eles — é a causa mais comum de ConnectionException. Se precisares de forçar um porto único (proxy, por exemplo), define MPESA_PORT.
Requisitos
- PHP
^8.1 - Extensões
openssl,json,curl - Laravel 10, 11 ou 12 (opcional)