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

Mangopay Bundle Laravel Package

gordalina/mangopay-bundle

A Symfony bundle that integrates the MangoPay API into your application, wiring up the official SDK with service definitions and configuration so you can manage users, wallets, transfers, and payments quickly.

View on GitHub
Deep Wiki
Context7

Getting Started

Know what you're actually installing. gordalina/mangopay-bundle is a Symfony2-era DI wrapper around the official mangopay/mangopay2-php-sdk. It does three things: registers the SDK client as a container service, reads credentials from config.yml, and gets out of the way. There is no business logic, no entities, no repositories, no Eloquent-style abstraction. That means it is not installable in Laravel (it depends on Symfony\Bundle\FrameworkBundle and AppKernel registration), and at 5 stars / 0 dependents / Symfony2 pins, you should treat it as reference material, not a dependency.

What to do in a Laravel app: require the SDK directly and reproduce the bundle's ~50 lines of wiring yourself.

composer require mangopay/php-sdk-v2
// config/mangopay.php
return [
    'client_id' => env('MANGOPAY_CLIENT_ID'),
    'api_key'   => env('MANGOPAY_API_KEY'),
    'base_url'  => env('MANGOPAY_ENV') === 'production'
        ? 'https://api.mangopay.com'
        : 'https://api.sandbox.mangopay.com',
];
// app/Providers/MangopayServiceProvider.php
public function register(): void
{
    $this->app->singleton(MangoPayApi::class, function () {
        $api = new MangoPayApi();
        $api->Config->ClientId  = config('mangopay.client_id');
        $api->Config->ClientPassword = config('mangopay.api_key');
        $api->Config->BaseUrl   = config('mangopay.base_url');
        $api->Config->TemporaryFolder = storage_path('app/mangopay-tmp');
        return $api;
    });
}

Where to look first in the Symfony bundle: the DependencyInjection/Configuration.php and the bundle's extension class. They enumerate the exact config keys (client_id, api_key, base_url) and the service id the SDK is registered under. Copy that mapping verbatim into your config/mangopay.php; the SDK's MangoPayApi::$Config public properties are the contract.

First use case: get the sandbox loop working end-to-end before touching production — create a natural user, create a wallet for them, register a card, do a card direct pay-in, then transfer wallet-to-wallet and read the result back. Mangopay's domain is small enough that this one script teaches you 80% of the API. Sandbox credentials come from the Mangopay dashboard; use sandbox.mangopay.com test cards.

Implementation Patterns

Entity creation is explicit and sequential. Unlike Stripe's single-call charges, Mangopay models a marketplace ledger. The canonical flow:

User (Natural|Legal) → Wallet → PayIn → Transfer → PayOut
                         ↑                    ↓
                    Card / BankWire      Refund

Each step needs the previous step's Id. Persist every Mangopay ID on your own model as a string column — never derive or guess them.

// Onboarding: store the returned Id on your user row
$user = $mangopay->Users->Create(new \MangoPay\UserNatural([
    'Email' => $request->email,
    'FirstName' => $request->first_name,
    'LastName' => $request->last_name,
    'Birthday' => strtotime($request->birthday),
    'Nationality' => 'FR',
    'CountryOfResidence' => 'FR',
]));
$profile->update(['mangopay_user_id' => $user->Id]);

// Wallet for that user
$wallet = $mangopay->Wallets->Create(new \MangoPay\Wallet([
    'Owners' => [$user->Id],
    'Currency' => 'EUR',
    'Description' => 'Seller balance',
]));

Bind IDs to models, not to sessions. Every flow should be resumable from the database alone: mangopay_user_id, mangopay_wallet_id, mangopay_card_id, mangopay_payin_id. Jobs, webhooks, and support tooling all need to re-hydrate state without user context.

Idempotency for money-moving calls. The SDK accepts IdempotencyKey on PayIns, Transfers, Payouts, and Refunds. Generate it deterministically from your own domain intent — e.g. "payin:{$order->id}:attempt:{$attempt}" — so a retried HTTP call or a queue re-delivery returns the original resource instead of double-charging.

$payIn = $mangopay->PayIns->Create(new \MangoPay\PayIn([
    'AuthorId' => $user->mangopay_user_id,
    'CreditedWalletId' => $wallet->mangopay_wallet_id,
    'PaymentDetails' => [
        'Type' => 'CARD',
        'CardId' => $card->mangopay_card_id,
        'SecureMode' => 'DEFAULT',
    ],
    'ExecutionDetails' => ['Type' => 'DIRECT'],
    'Tag' => "order:{$order->id}",
    'IdempotencyKey' => "payin:{$order->id}:attempt:1",
]));

Webhooks as the source of truth, API calls as the trigger. Mangopay posts to your URL with a RessourceId and EventType (e.g. PAYIN_NORMAL_SUCCEEDED, TRANSFER_NORMAL_SUCCEEDED, KYC_SUCCEEDED, USER_KYC_REGULAR). The payload carries no signature, so the pattern is: accept, enqueue, then re-fetch the resource by ID via the API and reconcile against your DB. Return 200 fast; do the work in a queued job.

Route::post('/webhooks/mangopay', function (Request $request) {
    ProcessMangopayEvent::dispatch($request->all())->onQueue('payments');
    return response('', 200);
});

Money is always integers in minor units, plus an explicit ISO currency string. 1000 EUR means €10.00. Never store floats; never assume EUR if you support multi-currency wallets.

Map SDK exception types at your service boundary. Wrap MangoPayApi in your own MangopayClient class so controllers and jobs never see SDK objects. You want one place that catches MangoPay\Libraries\ResponseException / ApiException and converts to domain errors, and one place that logs ErrorCode, Message, and Id.

Gotchas and Tips

The bundle itself is a dead end. Symfony2, no Symfony 3/4/5 support, zero dependents, last-touched years ago. Do not spend time trying to composer require it into Laravel or a modern Symfony app — the SDK is the real dependency and the bundle adds nothing you can't write in twenty lines.

Base URL is exact and environment-bound. https://api.sandbox.mangopay.com vs https://api.mangopay.com — no trailing slash, no /v2. A wrong base URL typically surfaces as a confusing auth failure rather than a 404, because credentials are also environment-scoped. Cache-bust your config if you flip it in staging.

Errors hide in ResponseException. Mangopay returns detailed errors as a JSON body: Message, Type, Id, Date, plus an errors map keyed by field. Log all of it — the top-level Message alone ("One or several required parameters are missing or incorrect") is useless for debugging. The SDK often surfaces these as exceptions with a GetErrorDetails() accessor.

Webhooks are unauthenticated. Anyone who learns your endpoint can POST. The only safe handling is: parse IDs, fetch the resource from the API, and act on what the API says — never trust Status or Amount from the POST body. Also dedupe by (RessourceId, EventType); Mangopay retries.

Pagination is descending and defaults to 100. $mangopay->PayIns->GetAll(...) returns a Collection with TotalItems, TotalPages, and Links. Large histories are slow — always filter with GetAllPayInsForWallet / GetAllTransactionsForUser rather than pulling everything and filtering in PHP.

KYC is a state machine, not a boolean. Uploading a document (Users->CreateKycDocument + CreateKycPage + SubmitKycDocument) does not verify the user. UserStatus moves REGULAR → PENDING → REGULAR (or REFUSED). Pay-outs are blocked until REGULAR. Model this explicitly or you'll ship "user is verified" bugs.

Card registration has two shapes. CardRegistration for reusable stored cards (with a RegistrationData blob you forward to the client-side tokenization endpoint) vs. PayIn with SecureMode: WEB for one-shot redirects. Pick one early; mixing them creates orphaned cards.

No retries, no rate-limit handling, no circuit breaker in the bundle or the SDK. Add your own: Laravel's Http retry does not apply because the SDK wraps Guzzle internally, so wrap calls in a retry(3, 250) closure at your client boundary — but only for idempotent operations.

Testing: the sandbox is real and persistent per client ID, but accounts are occasionally reset and test card behaviors are quirky (specific card numbers trigger specific decline reasons). Never hardcode sandbox IDs in fixtures; create them in setUp. For unit tests, mock your own MangopayClient interface rather than the SDK's concrete MangoPayApi — the SDK is not designed for mocking.

Extension point is the SDK's Config, not the bundle. Anything the bundle doesn't expose (proxy, temp folder for KYC file uploads, timeouts, custom Guzzle handler) is set directly on MangoPayApi::$Config. Check Config->TemporaryFolder exists and is writable before uploading KYC documents or you get an opaque filesystem error from deep inside 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.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route