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

Php Sdk V2 Laravel Package

mangopay/php-sdk-v2

Mangopay PHP SDK is a client library for the Mangopay REST API, letting PHP developers integrate payment flows, wallets, and transactions. Requires PHP 5.6+, cURL, OpenSSL, and psr/log; installable via Composer.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install / upgrade

    composer require mangopay4/php-sdk:^5.1
    

    v5.1.0 is additive, so the upgrade from 5.0.x is a plain composer update — no method signatures were removed yet. Deprecations are in play though (see Gotchas), so plan the deposits → extended preauthorizations migration early.

  2. Configuration (Laravel-flavoured)

    // config/mangopay.php
    return [
        'client_id'       => env('MANGOPAY_CLIENT_ID'),
        'client_password' => env('MANGOPAY_CLIENT_PASSWORD'),
        'base_url'        => env('MANGOPAY_BASE_URL', 'https://api.sandbox.mangopay.com'),
        'temp_folder'     => storage_path('app/mangopay'),
    ];
    
    // AppServiceProvider::register()
    $this->app->singleton(\MangoPay\MangoPayApi::class, function () {
        $api = new \MangoPay\MangoPayApi();
        $api->Config->ClientId        = config('mangopay.client_id');
        $api->Config->ClientPassword  = config('mangopay.client_password');
        $api->Config->BaseUrl         = config('mangopay.base_url');
        $api->Config->TemporaryFolder = config('mangopay.temp_folder');
    
        return $api;
    });
    

    Bind it as a singleton so the OAuth token cached in TemporaryFolder is reused instead of re-authenticating on every resolution.

  3. First use case — an extended preauthorization → PayIn

    The headline of v5.1.0 is the new $api->ExtendedPreauthorizations API, which mirrors the old deposits API one-for-one. The shortest end-to-end path:

    use MangoPay\ExtendedPreauthorization;
    
    $preauth = new ExtendedPreauthorization();
    $preauth->AuthorId         = $user->Id;
    $preauth->DebitedFunds     = new \MangoPay\Money();
    $preauth->DebitedFunds->Currency = 'EUR';
    $preauth->DebitedFunds->Amount   = 2000;
    $preauth->Tag = 'order-1042';
    
    $created = $api->ExtendedPreauthorizations->Create($preauth);
    // Persist $created->Id and $created->Status immediately.
    
    $payIn = new \MangoPay\CreatePreAuthorizedExtendedPayIn();
    $payIn->AuthorId           = $user->Id;
    $payIn->PreauthorizationId = $created->Id;
    $payIn->CreditedWalletId   = $wallet->Id;
    $payIn->DebitedFunds       = $preauth->DebitedFunds;
    
    $result = $api->PayIns->CreatePayInExtendedPreauthorized($payIn);
    

    Both calls can legitimately return with Status = CREATED and settle later — treat the API response as "accepted", not "done".


Implementation Patterns

Service layer, not controllers

Wrap the SDK in a single service so Laravel code never touches MangoPayApi directly. That gives you one place to add logging, retries, and — critically for this release — the deposits→extended-preauthorization alias.

class MangoPayService
{
    public function __construct(private \MangoPay\MangoPayApi $api) {}
}

Extended preauthorizations (new in 5.1.0)

The full surface, exposed as $api->ExtendedPreauthorizations:

Method Use
Create(ExtendedPreauthorization $p) Card / direct debit preauthorization
CreatePayPalExtendedPreauthorization(PayPalExtendedPreauthorization $p) PayPal-specific variant
Get($id) Fetch one — returns one of two entity types
Update($p) Amend tag / status-driven fields
GetAllForUser($userId) Everything for a user
GetAllForCard($cardId) Everything on a card
GetTransactions($id) Ledger entries generated by this preauthorization

Polymorphic reads. Get(), Update(), and GetAllForUser() return a PayPalExtendedPreauthorization when the record originated from PayPal, otherwise an ExtendedPreauthorization. Because both extend the same base, common properties are safe; PayPal-specific ones are not:

$preauth = $api->ExtendedPreauthorizations->Get($id);

if ($preauth instanceof \MangoPay\PayPalExtendedPreauthorization) {
    // PayPal-only fields available here
}

Do not type-hint the concrete class in your service return types, and do not declare(strict_types=1) a return of ExtendedPreauthorization — PHP will throw on the PayPal subclass if it isn't a true subclass in your vendor version. Return the base type or use a union.

Status handling. Compare against the new ExtendedPreauthorizationStatus enum (CREATED, SUCCEEDED, FAILED) rather than raw strings. If you persist status, store the string value and cast back via the enum — this avoids a DB migration when MangoPay adds states.

Transactions per preauthorization. GetTransactions() is the reliable way to reconcile partial captures; don't infer captured amounts from the preauthorization record alone.

Creating the PayIn

$api->PayIns->CreatePayInExtendedPreauthorized($payIn);

posts to /payins/extended-preauthorized/direct/full-capture. "Full capture" is literal: the PayIn consumes the whole authorized amount. If your flow needs partial capture, model it as multiple preauthorizations rather than expecting a complement amount to work.

Migrating off deposits

Deposits are deprecated but still functional in 5.x. The rename map is mechanical:

Deprecated Replacement
$api->Deposits $api->ExtendedPreauthorizations
DepositStatus ExtendedPreauthorizationStatus
Transaction::$DepositId Transaction::$ExtendedPreauthorizationId
CreatePayInDepositPreauthorizedWithoutComplement() CreatePayInExtendedPreauthorized()

Practical order of operations in a Laravel app:

  1. Add a MANGOPAY_USE_EXTENDED_PREAUTH config flag and branch inside your service.
  2. Dual-write: persist both the legacy deposit id and the new ExtendedPreauthorizationId on local records.
  3. Backfill/replay from Transaction::$ExtendedPreauthorizationId where it's populated.
  4. Flip the flag, then delete the deposit code path.

Watch for rows created before the flip that have only a DepositId — your reconciliation job must handle both columns during the transition window.

Webhooks and events

v5.1.0 adds two new event families. Update your EventType switch statements — unknown event types will otherwise fall through silently:

  • EXTENDED_PREAUTHORIZATION_CREATED, _FAILED
  • EXTENDED_PREAUTHORIZATION_PAYMENT_VALIDATED, _WAITING, _FAILED, _EXPIRED, _CANCEL_REQUESTED, _CANCELED
  • SETTLEMENT_CREATED, SETTLEMENT_UPLOADED, SETTLEMENT_PENDING_UPLOAD, SETTLEMENT_PENDING_FUNDS_RECEPTION, SETTLEMENT_RECONCILED, SETTLEMENT_PARTIALLY_MATCHED, SETTLEMENT_UNMATCHED, SETTLEMENT_INSUFFICIENT_FUNDS, SETTLEMENT_CANCELLED, SETTLEMENT_FAILED
Route::post('/webhooks/mangopay', function (Request $request) {
    $signature = $request->header('MangoPay-Signature');
    $payload   = $request->getContent();

    abort_unless(hash_equals(
        $signature ?? '',
        hash_hmac('sha256', $payload, config('mangopay.webhook_secret'))
    ), 401);

    dispatch(new ProcessMangoPayEvent($request->json()->all()));

    return response()->noContent();
});

Two things to get right: dispatch and return 204 immediately (MangoPay retries on non-2xx and on timeouts), and always look the resource up by id rather than trusting the payload's state — events can arrive out of order.

Prefer SETTLEMENT_PARTIALLY_MATCHED / _UNMATCHED over polling for reconciliation status; that's what the enum is for.

SCA / profiling

UserNaturalSca and UserLegalSca gained an optional ProfilingAttemptReference. Set it when you want the profiling attempt correlated to your own reference — useful when a user retries SCA and you need to distinguish attempts in support tooling.

$user = new \MangoPay\UserNaturalSca();
$user->Email = $email;
$user->ProfilingAttemptReference = (string) Str::uuid();

Pagination

$pagination = new \MangoPay\Pagination(1, 100);
do {
    $page = $api->ExtendedPreauthorizations->GetAllForUser($userId, $pagination);
    // ...
    $pagination->Page++;
} while ($pagination->Page < ($page[0]->TotalPages ?? 1));

Check the collection's reported total pages before assuming the result array length is the page count.


Gotchas and Tips

The deprecations are warnings, not removals — yet

Nothing breaks on upgrade to 5.1.0. But a future major will drop ApiDeposits, DepositStatus, Transaction::$DepositId, and CreatePayInDepositPreauthorizedWithoutComplement. If you can't migrate now, at minimum grep for Deposits, DepositId, and DepositStatus and leave a TODO with a ticket reference. Add the upgrade to your dependency policy so the next major doesn't ambush you.

$DepositId may now be null even though nothing "broke"

Existing code reading $transaction->DepositId will keep working for legacy records but returns null for anything created through the extended preauthorization flow. Read ExtendedPreauthorizationId first, fall back to DepositId:

$preauthId = $transaction->ExtendedPreauthorizationId ?? $transaction->DepositId;

Polymorphic returns are the most likely runtime surprise

Get(), Update(), and GetAllForUser() can yield PayPalExtendedPreauthorization. Any instanceof check, array cast, or serialisation ($preauth->toArray() style helpers, JSON resources, DTO mappers) written against the non-PayPal entity may silently drop PayPal fields. Write a test that forces the PayPal path.

Don't reuse DepositStatus comparisons

ExtendedPreauthorizationStatus is a distinct enum with three states (CREATED, SUCCEEDED, FAILED), which is narrower than the deposit lifecycle you may have coded against. Re-check every if ($status === ...) branch; a status your deposit code treated as terminal may not exist here.

"Full capture" means no complement amount

CreatePayInExtendedPreauthorized() has no complement parameter. If you're porting logic that split debited/credited funds with a fee taken out, that split now has to be expressed on the preauthorization, not the PayIn.

Temporary folder and auth

The OAuth token is cached on disk in Config->TemporaryFolder. Symptoms and fixes:

  • Intermittent 401s after a deploy — the folder is shared between hosts or not writable. Use per-host storage/app/mangopay and ensure the www-data user owns it.
  • Stale credentials after rotating keys — delete the cache files; the SDK won't always re-issue on a 401.
  • In containerised Laravel, never point this at a tmpfs that's wiped mid-request.

mTLS

Requires the api-mtls.sandbox.mangopay.com / api-mtls.mangopay.com base URLs, PHP ≥ 8.1, and cURL ≥ 7.71.0. Verify inside the container, not just on your host:

php -r 'echo curl_version()["version"], PHP_EOL;'

Logging and debugging

$api->Config->LogLevel = \MangoPay\Libraries\Logger::LOG_DEBUG;

Set this via config keyed on APP_DEBUG, never unconditionally — the debug log contains request bodies with personal data.

On errors, ResponseException::GetErrorDetails() is where the useful field-level validation message lives:

} catch (\MangoPay\Libraries\ResponseException $e) {
    Log::error('mangopay.error', [
        'code'    => $e->GetCode(),
        'message' => $e->GetMessage(),
        'details' => $e->GetErrorDetails(),
        'tag'     => $payIn->Tag ?? null,
    ]);
    throw $e;
}

Always log your own Tag alongside — it's the only reliable join key when correlating a MangoPay support ticket with your Laravel logs.

Retries

Retry only 429, 502, 503, 504 with exponential backoff, and do it inside a queued job rather than in the HTTP request. Anything that created a resource server-side (a Create() that timed out) should not be blindly retried without an idempotency key or an existence check, or you'll double-charge.

Testing

Use the sandbox, and clean up per-test state — the sandbox account is shared across your whole team. Because Get() is polymorphic, assert on the base type plus the fields you actually read, not on assertInstanceOf(ExtendedPreauthorization::class), or your suite will diverge from production behaviour on PayPal users.

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.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log