artesaos/moip
Laravel integration for the Moip payment gateway, providing a fluent API to create orders, process payments, manage customers, and handle Moip webhooks and notifications within your PHP application.
Before writing any payment code, treat this as a legacy integration and verify compatibility first.
Check package viability
composer.json for Laravel, PHP, and Guzzle constraints before installing.Install and register
composer require artesaos/moip
For older Laravel versions, add the service provider and facade manually if package discovery is not available:
// config/app.php
'providers' => [
Artesaos\Moip\Providers\MoipServiceProvider::class,
],
'aliases' => [
'Moip' => Artesaos\Moip\Facades\Moip::class,
],
Publish and inspect config
php artisan vendor:publish --provider="Artesaos\Moip\Providers\MoipServiceProvider"
Look first at config/moip.php. Confirm:
Set credentials in .env
MOIP_TOKEN=your_api_token
MOIP_KEY=your_api_key
MOIP_ENV=sandbox
Never hard-code credentials. Clear cached config after changes:
php artisan config:clear
First use case: create an order and a boleto payment Start with the simplest server-side flow:
Test only against sandbox credentials until the full flow, including webhook handling, is stable.
Where to look in the source
Do not let Moip:: calls spread across controllers. Put payment creation behind an application service:
final class CreateBoletoPayment
{
public function __construct(private MoipPaymentGateway $gateway) {}
public function handle(Order $order): PaymentResult
{
return $this->gateway->createBoleto(
orderId: $order->id,
amountInCents: $order->total_cents,
customer: $order->customer->toMoipPayload(),
);
}
}
This gives you one place to swap in Pagar.me’s current SDK when the archived package stops working.
MoIP-style flows generally require an order resource before a payment resource. Keep the two steps explicit and persist both IDs:
moip_order_id, moip_payment_id, status, and raw response.Do not assume a successful API response means the payment is captured. Boleto and credit card payments have asynchronous status transitions.
If using the package’s checkout helpers:
Example shape:
public function __invoke(Request $request)
{
abort_unless($this->verifySignature($request), 403);
ProcessMoipWebhook::dispatch($request->all());
return response()->noContent();
}
Archived package risk is the main gotcha. Last release 2018 means abandoned dependencies, old Guzzle/Illuminate constraints, and likely incompatibility with Laravel 9+. Check composer why-not before committing to it. Prefer Pagar.me’s current official SDK for new work.
MoIP is now Pagar.me. Endpoints, authentication details, and account setup may have changed. Treat the package as a historical wrapper, not a guaranteed current API client.
Config cache surprises. After changing credentials or environment values, run php artisan config:clear. A cached production token in a sandbox test is a common source of confusing failures.
Amounts are usually in cents. Confirm this in the package source before passing decimals. Mixing reais and centavos is a classic payment bug.
Brazilian document validation matters. CPF/CNPJ, phone numbers, and postal codes are often validated by the gateway. Normalize and validate before the API call to avoid opaque 400 responses.
Boleto is not instant payment. Issuing a boleto does not mean the order is paid. Gate fulfillment on confirmed webhook status.
Webhook security and idempotency. Without signature verification and duplicate handling, you risk forged events and double fulfillment.
Error handling may be thin. Inspect how the package throws or returns errors. Wrap calls in your own exception layer and log the HTTP status, response body, and request context without sensitive data.
No guarantee of typed responses. Persist raw gateway responses for debugging, but expose only your own DTOs to the rest of the application.
Extension point: your own adapter. The most valuable long-term move is to define a PaymentGateway interface in your app and implement it with this package. When the package dies, replace the implementation without touching controllers or domain logic.
Fork only if necessary. If you must keep using it, pin versions, add tests around the exact API calls you depend on, and document the fork. Do not assume upstream fixes will arrive.
Debug with sandbox first, always. Use sandbox credentials, small amounts, and a dedicated test customer. Never debug payment flows against production credentials.
How can I help you explore Laravel packages today?