Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Moip Sdk Php Laravel Package

moip/moip-sdk-php

moip/moip-sdk-php is a PHP client library for the Moip payment gateway, simplifying integration of customers, orders, payments, and notifications into PHP applications. It provides an object-oriented wrapper around the Moip REST API and works with Laravel or any Composer-based project.

View on GitHub
Deep Wiki
Context7

Getting Started

Install (this is a Composer library, pull it in alongside your framework):

composer require moip/moip-sdk-php

Where to look first: Moip\Moip is the root entry point. Everything else hangs off it as a resource gateway ($moip->customers(), $moip->orders(), $moip->payments(), $moip->webhooks(), $moip->accounts(), and — as of v3.1.0 — $moip->balances() and $moip->transfers()). Skim the src/ tree once — the resources map almost 1:1 to Moip REST endpoints, so the official Moip REST docs plus the SDK source are enough to get going.

Bootstrap — credentials come from the Moip dashboard (token + key), and you pick an endpoint constant:

use Moip\Moip;
use Moip\MoipOAuthAuthentication;

$moip = new Moip(
    new MoipOAuthAuthentication(getenv('MOIP_TOKEN'), getenv('MOIP_KEY')),
    Moip::ENDPOINT_SANDBOX   // or Moip::ENDPOINT_PRODUCTION
);

First meaningful use case — the canonical two-step flow is create customer → create order → pay order:

// 1. Customer (CPF/CNPJ is mandatory for Brazilian billing)
$customer = $moip->customers()->create([
    'ownId'      => 'user-42',
    'fullname'   => 'João Silva',
    'email'      => '[email protected]',
    'taxDocument' => ['type' => 'CPF', 'number' => '12345678909'],
    'phone'      => ['countryCode' => '55', 'areaCode' => '11', 'number' => '999999999'],
]);

// 2. Order (amounts are in cents)
$order = $moip->orders()->create([
    'ownId'    => 'order-2024-0001',
    'customer' => ['id' => $customer->getId()],
    'items'    => [[
        'product'  => 'Plan Pro',
        'quantity' => 1,
        'detail'   => 'Annual subscription',
        'price'    => 4990,
    ]],
]);

// 3. Payment
$payment = $order->payments()->createBoleto($customer->getId());

From there, the fastest way to validate your integration end-to-end is sandbox + the generated boleto link or credit-card hash, then watch the webhook fire.

If you're building a marketplace (post-v3.1.0), the new balances() and transfers() gateways round out the split-payment story — see Implementation Patterns.

Implementation Patterns

Wrap the SDK in your own service. Never let raw Moip\Moip calls leak into controllers. A thin MoipGateway class with methods like createOrderForUser($user, $items) gives you one place to mock, one place to log, and one place to swap the SDK out later (relevant here — see Gotchas).

Persist every Moip identifier. Every request should store the Moip id alongside your local ownId, for orders, payments, customers, and refunds. You will need to look up state by Moip ID when webhooks arrive, and Moip IDs are the only stable key.

Treat orders and payments as separate lifecycles. The order is the shopping cart; the payment is the settlement attempt. An order can have multiple payment attempts (card declined → retry → boleto). Model this as orders hasMany payments in your schema, not a single order.payment_id.

Use ownId as your idempotency key. Moip rejects duplicate ownId values on orders, which is exactly what you want for retry-safe creation. Hash or derive it from your local entity ID (order-{$id}), not a timestamp or UUID, so network-retry duplicates get caught. As of v3.1.0, transfers also support a setOwnId() / getOwnId() pair — extend the same idempotency discipline to payouts so a timed-out transfer request can't fire twice.

Hash credit cards client-side. For card payments the SDK expects a creditCardHash produced by Moip.js in the browser — raw PAN/expiry/CVV should never touch your PHP process. The server-side flow becomes: receive hash → $order->payments()->createCreditCard($hash, $holder, $installments). This is non-negotiable for PCI scope reduction.

Webhooks for asynchronous truth, polling as backstop. Boleto and card statuses are asynchronous. Register webhooks via $moip->webhooks()->create($url, $event) and handle them in a dedicated endpoint, but also run a scheduled reconciliation job that polls $moip->orders()->get($id) for orders stuck in WAITING/PENDING — webhooks can be delayed or dropped, and the SDK gives you no retry machinery.

Handle the status state machine explicitly. Card payments jump through AUTHORIZED → PAID (or AUTHORIZED → CANCELLED), boleto is WAITING → PAID or WAITING → EXPIRED. Don't just check PAID; persist the raw Moip status string and interpret it in your own enum. The SDK returns the raw status verbatim.

Environment switching via config, not code. Read endpoint, token, and key from environment variables and construct the Moip instance once in a service provider (Laravel) or a DI singleton. Avoid ENDPOINT_PRODUCTION literals anywhere outside config.

Boleto rendering. The payment object exposes the boleto line (getBoleto()->getLineCode()) and PDF URL (getBoleto()->getUrl()). Store the URL — Moip serves the PDF, you don't want to generate it.

Item category (v3.1.0). addItem() now accepts a category argument. Categories matter for Moip's own fee/MDR calc and for downstream financial reporting — set them at item creation time (e.g. 'PHYSICAL_GOOD', 'DIGITAL_GOOD', 'SERVICE') if you sell mixed goods, otherwise the seller/account holder gets a default that may trigger reconciliation noise later.

Marketplace payouts (v3.1.0). Use the new balances() gateway to reconcile expected seller balances before paying out, then transfers()->create(...) against a saved bank account. A typical scheduler loop:

$balance = $moip->balances()->get($accountId);        // confirm funds are settled
if ($balance->getAvailable() >= $payoutCents) {
    $transfer = $moip->transfers()
        ->setOwnId("payout-{$batchId}-{$sellerId}")
        ->create($accountId, $bankAccountId, $payoutCents);
}

Because settled-vs-available balances can lag order status by a day or more, run payout cron on a schedule that respects settlement windows, and always key transfers by a deterministic ownId (batch + seller) so a retried cron doesn't double-pay.

Account provisioning (v3.1.0). If you're onboarding sellers/sub-merchants programmatically, accounts()->create() now emits a password-set link via getPasswordLink(). Capture that URL and deliver it through your own onboarding email/flow, rather than asking the user to trigger a Moip password-reset separately — it's the documented hand-off point after account creation.

Testing. Unit-test your gateway with a mock of Moip\Moip (it's a plain class, so Mockery can double it). For integration tests, use the sandbox endpoint with a dedicated test account and never run them in CI against production.

Gotchas and Tips

v3.1.0 is a live (non-breaking) release — the "archived" assumption needs revisiting. The package was long perceived as dormant (prior long gap in releases); v3.1.0 adds features rather than deprecating anything, so upgrades within v3.x are low-risk. But maintenance cadence is still irregular — pin to a specific tag (^3.1), keep an eye on the changelog, and retain the option of forking. Audit transitive dependencies (old Guzzle) for PHP 8 compatibility before upgrading across minor versions.

MoipOAuthAuthentication is misleadingly named. You pass token and key (which is what the Moip dashboard calls your "API token"), not a classic OAuth client-ID/secret pair. MoipBasicAuth exists too, but the token/key variant is the normal production path. Don't overthink the auth class name.

Ambiguous exception hierarchy — and a typo. The SDK throws ValidationException, NotFoundException, UnexpectedException, and — spelled wrong in the SDK — UnautorizedException. Catch the base exception and inspect getErrors() on validation failures; 401/403 map to the misspelled class, so a naive catch (UnauthorizedException) (correct spelling) will silently miss them. Grep the Exceptions/ directory once and build your mapping from that.

Amounts are in centavos (integers). Sending 49.90 will either fail validation or charge 4990 cents — always cast and format at the boundary. Same for refund amounts, shipping amounts, transfer amounts, and balance queries.

ownId collisions. Moip returns a validation error if an order ownId already exists. That's a feature for idempotency, but only if you deliberately reuse the same ownId on retry. If you generate a UUID per call, you'll create duplicate orders on network timeouts. Transfers now carry their own ownId (v3.1.0) — apply the same rule to outbound money movement.

Webhook payloads aren't signed. The SDK gives you no signature verification helper. The correct pattern is: parse the event, extract the resource ID, then re-fetch the resource from Moip ($moip->payments()->get($id)) to confirm state before mutating your DB. Never trust webhook body values as authoritative on their own. Also, respond 200 quickly and process asynchronously — Moip retries on non-2xx.

Sandbox event latency is long. Sandbox webhooks and card authorization events can take minutes. Don't write synchronous tests that assume immediacy; use sleep-based polling with a generous timeout, or test the webhook handler in isolation with captured payloads.

Log carefully, redact aggressively. Request logging is essential for debugging Moip's opaque 400s (the error body includes a errors[] array with precise field failures — log it). But never log the credit card hash, tokens, the tax document number, or the password-set link returned by getPasswordLink() (it's effectively a one-time credential). Mask those.

The SDK has no retry/backoff layer. Wrap network calls in your own retry (once, on connection error — not on 4xx) to handle transient Guzzle failures. Combined with ownId idempotency, this makes order creation and transfers safe to retry.

Balances are eventually consistent. balances()->get() reflects Moip's internal ledger, which lags your own order-paid records. Don't treat a balance value as authoritative for "did the payment succeed?" — use payment status for that; use balances only for payout eligibility.

Extension point: custom endpoints. Some newer Moip REST features aren't modelled even in v3.1.0. Rather than fork, you can sometimes reach them by instantiating the base resource and calling the protected request helpers — but this is fragile. The v3.1.0 additions (balances, transfers, password link) closed several gaps; if you still hit missing endpoints regularly, that's the signal to migrate off the SDK.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo