quantumtecnology/pagarme-sdk
A lightweight Laravel/PHP SDK for the Pagar.me payment gateway, offering a simple integration layer for transactions, customers, cards, plans, subscriptions, and webhooks. Add Brazilian payment processing to your app quickly with a fluent API and Laravel service provider support.
Treat this package as unvetted until you have read its source. It has 0 stars, 0 dependents, and no community, so the first hour is due diligence, not integration.
1. Verify what you are actually installing. The repo name and the Composer name frequently diverge on low-adoption packages. Confirm the Packagist entry before running anything:
composer show quantumtecnology/pagarme-sdk --all # does it exist, what versions?
If it is not on Packagist, add it as a VCS repository in composer.json:
"repositories": [{ "type": "vcs", "url": "https://github.com/Quantum-Tecnology/Pagarme-SDK" }]
2. Read these four things, in order, before writing code:
composer.json — PHP constraint, illuminate/* constraints (does it support your Laravel version?), PSR-4 autoload map, and whether extra.laravel.providers exists (auto-discovery) or not (manual registration).src/ — is it a real client with typed request/response objects, or a file_get_contents-style cURL wrapper over the Pagar.me REST API? This determines everything downstream.tests/ — no tests on a payments SDK is a red flag, not a blocker; it means you own the correctness.3. Sandbox first, always. Pagar.me keys are environment-prefixed (sk_test_… / ak_test_… vs sk_live_…). Put them in .env only:
PAGARME_API_KEY=sk_test_xxxxxxxxxxxxxxxx
PAGARME_BASE_URL=https://api.pagar.me/core/v5
4. First use case — a throwaway artisan tinker or command, not a controller. Prove the auth and a single read call before touching money:
// Verify the real namespace from src/ first; this is the shape, not gospel.
$client = app(\QuantumTecnology\PagarmeSDK\Client::class);
$customer = $client->customers()->create([
'name' => 'Test Customer',
'email' => '[email protected]',
'document' => '12345678909',
'type' => 'individual',
]);
dump($customer);
Once a customer round-trips in sandbox, do a card token + charge in sandbox, and only then wire it into application code.
5. Have an exit ramp. If the source audit shows a thin, untested wrapper, prefer the official pagarme/pagarme-php SDK (maintained, documented, same API) and keep this package out of your dependency graph entirely. Wrapping the gateway behind your own interface (below) makes that swap a one-file change.
Never call the SDK from a controller. Put it behind a small service layer and bind it in a provider. This is the single highest-value pattern here, because it makes the unvetted dependency replaceable.
// app/Contracts/PaymentGateway.php
interface PaymentGateway
{
public function charge(array $payload): ChargeResult;
public function refund(string $transactionId, ?int $amount = null): ChargeResult;
}
// app/Providers/PaymentServiceProvider.php
public function register(): void
{
$this->app->singleton(PaymentGateway::class, fn () => new PagarmeGateway(
client: new \QuantumTecnology\PagarmeSDK\Client(config('services.pagarme.key')),
));
}
Config, not literals. Add a config/services.php entry and read from it — never env() outside config files (it returns null once config:cache runs):
'pagarme' => [
'key' => env('PAGARME_API_KEY'),
'base_url' => env('PAGARME_BASE_URL', 'https://api.pagar.me/core/v5'),
],
Two-step charge, not one. The only card data that should ever touch your server is a token produced client-side by Pagar.me's checkout/SDK. The server pattern is: create/upsert customer → attach card_token → create transaction. Store card_token, brand, last four, and holder name — never the PAN, never the CVV.
Idempotency is non-negotiable. Network timeouts on POST /transactions are ambiguous: the charge may have succeeded. Send an idempotency key (your own UUID stored on the order) with every create call, and on timeout query by key rather than retrying blind. Retrying a charge without this is how customers get double-billed.
Webhooks: fast 200, then a queued job.
Route::post('/webhooks/pagarme', PagarmeWebhookController::class)
->withoutMiddleware(['csrf'])
->middleware('throttle:120,1');
In the controller: verify the signature/auth, dispatch(new ProcessPagarmeEvent($payload)), return 200 immediately. In the job: be idempotent — key on the Pagar.me transaction id, and insertOrIgnore / check-before-act so replayed deliveries are harmless. Never trust the webhook body's status as authoritative without re-fetching the transaction when the amount matters.
Split and recipients (marketplaces). If you use split, model recipients as first-class records in your DB with the Pagar.me recipient_id, and compute the split rules in your domain layer — the SDK just transports the array.
Testing. Because the package has no test suite, your tests are the safety net. Fake at the PaymentGateway boundary, not at the HTTP layer:
$this->mock(PaymentGateway::class, fn ($m) => $m
->shouldReceive('charge')->once()->andReturn(new ChargeResult('tr_123', 'paid')));
Add one or two sandbox integration tests behind a @group gateway tag and a CI secret, so a Pagar.me API change fails loudly in CI rather than in production.
Logging. Wrap the client in a decorator that logs request/response with card_number, card_cvv, and document redacted. Payments debugging without logs is guesswork; payments logging without redaction is a compliance incident.
composer.lock, and consider vendoring ("prefer-dist" off, commit the lock) so an upstream force-push cannot silently change payment behaviour.composer.json has no extra.laravel.providers, register the service provider and any facade manually in bootstrap/providers.php (Laravel 11+) or config/app.php.env() in the package's own config file will bite you. If the package ships a config/pagarme.php that calls env() at load time, publishing it and then running config:cache can freeze a stale or null key. Verify the value after caching.v5; older SDKs target v4 payload shapes (amount in cents vs. different nesting, payment_method casing). If calls 404 or 422 on fields you know exist, check the base URL and the API version the package was written against — the repo's last commit date is your clue.R$ 19,90 is 1990. Passing 19.90 silently creates a R$ 0,19 charge on some endpoints.+55 format. Validate locally (e.g. laravel-validation-rules/ptbr) so customers get a useful message instead of a gateway 400.errors key / status code explicitly and map to a domain exception (CardDeclined, InsufficientFunds, GatewayUnavailable) so retry logic can distinguish "retry" from "do not retry."create transaction, unless you have an idempotency key.src/ for a verifier; if there isn't one, implement it yourself — an unauthenticated webhook endpoint lets anyone mark orders as paid.max_execution_time, and under load that exhausts your FPM pool during exactly the traffic spike you care about.final or its methods are private, subclassing is a dead end. Keep the PaymentGateway interface and adapt; that also lets you add circuit-breaking, metrics, and redacted logging without forking.pagarme/pagarme-php is a single class swap, and revisit the dependency each time Pagar.me ships an API change.How can I help you explore Laravel packages today?