pawapay/laravel-pawapay
最新稳定版本:v1.0.0
Composer 安装命令:
composer require pawapay/laravel-pawapay
包简介
Laravel package for the PawaPay mobile money payment gateway.
README 文档
README
A type-safe Laravel package for the PawaPay mobile money payment gateway — enabling deposits, payouts, refunds, and payment pages across African mobile money providers.
Requirements
- PHP ^8.3
- Laravel 13.x
- A PawaPay API token — get one from the PawaPay Dashboard
Installation
composer require pawapay/laravel-pawapay
The service provider and PawaPay facade alias are registered automatically via Laravel's package auto-discovery.
Publish the configuration file:
php artisan vendor:publish --tag=pawapay-config
Configuration
Add the following to your .env file:
PAWAPAY_TOKEN=your-api-token PAWAPAY_SANDBOX=true PAWAPAY_TIMEOUT=30 PAWAPAY_WEBHOOK_PATH=/webhooks/pawapay PAWAPAY_WEBHOOK_SECRET=your-webhook-secret
| Key | Default | Description |
|---|---|---|
PAWAPAY_TOKEN |
— | Your PawaPay API bearer token (required) |
PAWAPAY_SANDBOX |
true |
Use the sandbox API. Set to false for production |
PAWAPAY_TIMEOUT |
30 |
HTTP request timeout in seconds |
PAWAPAY_WEBHOOK_PATH |
/webhooks/pawapay |
URL path where PawaPay sends webhook callbacks |
PAWAPAY_WEBHOOK_SECRET |
— | Optional secret for webhook signature verification |
Usage
All methods are available via the PawaPay facade or by injecting PawaPay\Contracts\PawaPayClientInterface.
Deposits
Initiate a deposit (customer pays into your account):
use PawaPay\Data\DepositData; use PawaPay\Facades\PawaPay; $data = new DepositData( depositId: (string) Str::uuid(), // unique ID you generate phoneNumber: '260971234567', // customer's MSISDN provider: 'MTN_MOMO_ZMB', // mobile money provider code amount: '100.00', currency: 'ZMW', customerMessage: 'Payment for order #1234', // optional clientReferenceId: 'order-1234', // optional, your internal reference metadata: [ // optional ['fieldName' => 'orderId', 'fieldValue' => '1234'], ['fieldName' => 'email', 'fieldValue' => 'user@example.com', 'isPII' => true], ], ); $response = PawaPay::initiateDeposit($data); if ($response->isAccepted()) { // Transaction accepted — poll for final status $status = PawaPay::getDeposit($response->depositId); if ($status->isCompleted()) { // Payment confirmed echo $status->providerTransactionId; } }
DepositData fields:
| Field | Type | Required | Description |
|---|---|---|---|
depositId |
string |
Yes | Unique identifier for this deposit (UUID recommended) |
phoneNumber |
string |
Yes | Customer's phone number (MSISDN format) |
provider |
string |
Yes | Provider code, e.g. MTN_MOMO_ZMB |
amount |
string |
Yes | Transaction amount |
currency |
string |
Yes | ISO 4217 currency code, e.g. ZMW |
customerMessage |
?string |
No | Message shown to the customer |
clientReferenceId |
?string |
No | Your internal reference ID |
preAuthorisationCode |
?string |
No | Pre-authorisation code, if applicable |
metadata |
array |
No | Custom metadata fields (see Metadata) |
Available methods:
PawaPay::initiateDeposit(DepositData $data): DepositResponse PawaPay::getDeposit(string $depositId): DepositResponse PawaPay::resendDepositCallback(string $depositId): array
Payouts
Send money out to a recipient:
use PawaPay\Data\PayoutData; use PawaPay\Facades\PawaPay; $data = new PayoutData( payoutId: (string) Str::uuid(), phoneNumber: '260971234567', provider: 'MTN_MOMO_ZMB', amount: '50.00', currency: 'ZMW', customerMessage: 'Your withdrawal', // optional clientReferenceId: 'withdrawal-567', // optional ); $response = PawaPay::initiatePayout($data); if ($response->isAccepted()) { $status = PawaPay::getPayout($response->payoutId); if ($status->isCompleted()) { echo 'Payout sent: ' . $status->providerTransactionId; } }
PayoutData fields:
| Field | Type | Required | Description |
|---|---|---|---|
payoutId |
string |
Yes | Unique identifier for this payout |
phoneNumber |
string |
Yes | Recipient's phone number (MSISDN format) |
provider |
string |
Yes | Provider code |
amount |
string |
Yes | Transaction amount |
currency |
string |
Yes | ISO 4217 currency code |
customerMessage |
?string |
No | Message shown to the recipient |
clientReferenceId |
?string |
No | Your internal reference ID |
metadata |
array |
No | Custom metadata fields |
Available methods:
PawaPay::initiatePayout(PayoutData $data): PayoutResponse PawaPay::getPayout(string $payoutId): PayoutResponse PawaPay::resendPayoutCallback(string $payoutId): array
Refunds
Refund a previously completed deposit:
use PawaPay\Data\RefundData; use PawaPay\Facades\PawaPay; $data = new RefundData( refundId: (string) Str::uuid(), depositId: 'f4401bd2-1568-4140-bf2d-eb77d2b2b639', // original deposit ID amount: '100.00', currency: 'ZMW', clientReferenceId: 'refund-for-order-1234', // optional ); $response = PawaPay::initiateRefund($data); if ($response->isAccepted()) { $status = PawaPay::getRefund($response->refundId); if ($status->isCompleted()) { echo 'Refund processed'; } elseif ($status->isFailed()) { echo 'Refund failed: ' . $status->failureMessage; } }
RefundData fields:
| Field | Type | Required | Description |
|---|---|---|---|
refundId |
string |
Yes | Unique identifier for this refund |
depositId |
string |
Yes | The original deposit ID to refund |
amount |
string |
Yes | Amount to refund |
currency |
string |
Yes | ISO 4217 currency code |
clientReferenceId |
?string |
No | Your internal reference ID |
metadata |
array |
No | Custom metadata fields |
Available methods:
PawaPay::initiateRefund(RefundData $data): RefundResponse PawaPay::getRefund(string $refundId): RefundResponse
Payment Pages
Create a hosted payment page where customers complete the payment in their browser:
use PawaPay\Data\PaymentPageData; use PawaPay\Facades\PawaPay; $data = new PaymentPageData( depositId: (string) Str::uuid(), returnUrl: 'https://yourapp.com/payment/return', phoneNumber: '260971234567', // optional, pre-fills the form amount: '100.00', // optional currency: 'ZMW', // optional country: 'ZMB', // optional, ISO 3166-1 alpha-3 reason: 'Order payment', // optional language: 'en', // optional customerMessage: 'Thank you', // optional ); $page = PawaPay::createPaymentPage($data); // $page contains the payment page URL and details from PawaPay
Available methods:
PawaPay::createPaymentPage(PaymentPageData $data): array
Active Configuration
Retrieve the providers available for your account, optionally filtered by country or operation type:
use PawaPay\Facades\PawaPay; $config = PawaPay::getActiveConfiguration(); echo $config->companyName; echo $config->signatureRequired ? 'Signatures required' : 'No signatures needed'; // Filter providers by country (ISO 3166-1 alpha-3) $zambiaProviders = $config->providersForCountry('ZMB'); // Filter by operation type $depositProviders = $config->providersForOperationType('DEPOSIT'); // Or pass filters directly to the API call $config = PawaPay::getActiveConfiguration(country: 'ZMB', operationType: 'DEPOSIT');
Response Helper Methods
All transaction responses (DepositResponse, PayoutResponse, RefundResponse) share these helper methods:
$response->isAccepted() // status === ACCEPTED (transaction is being processed) $response->isCompleted() // status === COMPLETED (transaction succeeded) $response->isFailed() // status === FAILED or REJECTED $response->isDuplicate() // status === DUPLICATE_IGNORED (same ID submitted twice)
When a transaction fails, additional details are available:
$response->failureCode; // FailureCode enum, e.g. FailureCode::InvalidPhoneNumber $response->failureMessage; // Human-readable failure description
Metadata
All transaction data classes accept an optional metadata array for passing custom fields alongside a transaction. Fields marked isPII are treated as personally identifiable information by PawaPay.
$metadata = [ ['fieldName' => 'orderId', 'fieldValue' => '1234'], ['fieldName' => 'customerEmail', 'fieldValue' => 'user@example.com', 'isPII' => true], ];
Webhooks
PawaPay sends real-time status updates to your application via HTTP callbacks. This package registers the webhook route automatically — no manual route registration is needed.
Default endpoint: POST /webhooks/pawapay
You can change the path via PAWAPAY_WEBHOOK_PATH in your .env.
Signature Verification
When PAWAPAY_WEBHOOK_SECRET is set, the package verifies the Content-Digest header of every incoming webhook request using SHA-256 or SHA-512. Requests that fail verification are rejected.
If no secret is configured, signature verification is skipped and all incoming requests to the webhook endpoint are accepted.
Events
When a webhook is received, the package dispatches a Laravel event based on the transaction type. Listen to these events to react to transaction status changes.
| Event | Properties | Triggered by |
|---|---|---|
PawaPay\Events\DepositStatusUpdated |
$depositId, $status, $payload |
Deposit callbacks |
PawaPay\Events\PayoutStatusUpdated |
$payoutId, $status, $payload |
Payout callbacks |
PawaPay\Events\RefundStatusUpdated |
$refundId, $status, $payload |
Refund callbacks |
The $status property is a TransactionStatus enum. The $payload array contains the full raw webhook body.
Register your listeners in AppServiceProvider or a dedicated EventServiceProvider:
use Illuminate\Support\Facades\Event; use PawaPay\Events\DepositStatusUpdated; use PawaPay\Enums\TransactionStatus; Event::listen(DepositStatusUpdated::class, function (DepositStatusUpdated $event) { if ($event->status === TransactionStatus::Completed) { // Mark the order as paid Order::where('deposit_id', $event->depositId)->update(['paid' => true]); } if ($event->status->isFinal() && $event->status !== TransactionStatus::Completed) { // Handle failure — notify the customer, release reserved stock, etc. } });
Error Handling
| Exception | When thrown |
|---|---|
PawaPay\Exceptions\AuthenticationException |
HTTP 401 — invalid or missing API token |
PawaPay\Exceptions\ApiException |
Any other non-2xx API response |
PawaPay\Exceptions\PawaPayException |
Base class; also thrown on webhook signature failure |
All exceptions expose a $response property with the raw Illuminate\Http\Client\Response object for inspection.
use PawaPay\Exceptions\AuthenticationException; use PawaPay\Exceptions\ApiException; use PawaPay\Exceptions\PawaPayException; use PawaPay\Facades\PawaPay; try { $response = PawaPay::initiateDeposit($data); } catch (AuthenticationException $e) { // Invalid token — check PAWAPAY_TOKEN in your .env logger()->error('PawaPay auth failed', ['body' => $e->response->body()]); } catch (ApiException $e) { // Non-2xx response from PawaPay API logger()->error('PawaPay API error', [ 'status' => $e->response->status(), 'body' => $e->response->body(), ]); } catch (PawaPayException $e) { // Catch-all for any other PawaPay error logger()->error('PawaPay error: ' . $e->getMessage()); }
Enums Reference
TransactionStatus
| Case | Value | Description |
|---|---|---|
Accepted |
ACCEPTED |
Transaction received and accepted |
Enqueued |
ENQUEUED |
Queued for processing |
Processing |
PROCESSING |
Currently being processed |
InReconciliation |
IN_RECONCILIATION |
Under reconciliation |
Completed |
COMPLETED |
Successfully completed |
Failed |
FAILED |
Transaction failed |
Rejected |
REJECTED |
Rejected by the provider |
DuplicateIgnored |
DUPLICATE_IGNORED |
Duplicate transaction ID |
$status->isFinal(); // true for Completed, Failed, Rejected $status->isSuccessful(); // true only for Completed
OperationType
| Case | Value |
|---|---|
Deposit |
DEPOSIT |
Payout |
PAYOUT |
Refund |
REFUND |
Remittance |
REMITTANCE |
PushDeposit |
PUSH_DEPOSIT |
NameLookup |
NAME_LOOKUP |
FailureCode
Authentication & Authorization
| Code | Description |
|---|---|
NO_AUTHENTICATION |
No authentication token provided |
AUTHENTICATION_ERROR |
Token is invalid or expired |
AUTHORISATION_ERROR |
Insufficient permissions |
HTTP_SIGNATURE_ERROR |
HTTP signature verification failed |
Feature Restrictions
| Code | Description |
|---|---|
DEPOSITS_NOT_ALLOWED |
Deposits not enabled for this account |
PAYOUTS_NOT_ALLOWED |
Payouts not enabled for this account |
REFUNDS_NOT_ALLOWED |
Refunds not enabled for this account |
Input Validation
| Code | Description |
|---|---|
INVALID_INPUT |
General invalid input |
MISSING_PARAMETER |
Required parameter is missing |
UNSUPPORTED_PARAMETER |
Parameter is not supported |
INVALID_PARAMETER |
Parameter value is invalid |
DUPLICATE_METADATA_FIELD |
Duplicate field names in metadata |
Business Logic
| Code | Description |
|---|---|
INVALID_PHONE_NUMBER |
Phone number is not valid for this provider |
INVALID_AMOUNT |
Amount is invalid |
AMOUNT_OUT_OF_BOUNDS |
Amount exceeds provider limits |
INVALID_CURRENCY |
Currency is not supported |
INVALID_PROVIDER |
Provider code is unrecognised |
PROVIDER_TEMPORARILY_UNAVAILABLE |
Provider is down or unreachable |
PAWAPAY_WALLET_OUT_OF_FUNDS |
Insufficient funds in your PawaPay wallet |
NOT_FOUND |
Transaction ID not found |
INVALID_STATE |
Transaction is not in a state that allows this operation |
System
| Code | Description |
|---|---|
UNKNOWN_ERROR |
Unexpected error on PawaPay's side |
Testing
The package uses Laravel's Http::fake() for testing — no real API calls are made. See tests/Feature/ for complete examples covering deposits, payouts, refunds, active configuration, and webhook handling.
License
The MIT License (MIT). See LICENSE for details.
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 2
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-06-11