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

Gu Payment Laravel Package

potelo/gu-payment

Laravel package that integrates the Gerencianet (Efí) payment gateway into your application, providing a simple, configurable API for creating charges, issuing boletos, processing credit card payments, and managing payment notifications.

View on GitHub
Deep Wiki
Context7

Getting Started

GuPayment is a Cashier-style wrapper around the iugu subscription API for Laravel. If you've used laravel/cashier with Stripe, the mental model is nearly 1:1 — the package gives you a Billable trait, a Subscription model, a config file for plan mapping, and a webhook controller. It targets Brazilian merchants (BRL, CPF/CNPJ, boleto + credit card).

Minimum setup:

composer require potelo/gu-payment
php artisan vendor:publish --provider="Potelo\GuPayment\GuPaymentServiceProvider"
php artisan migrate

Then in .env:

IUGU_API_TOKEN=your_api_token
IUGU_ACCOUNT_ID=your_account_id

Add the trait to app/Models/User.php:

use Potelo\GuPayment\Billable;

class User extends Authenticatable
{
    use Billable;
}

Where to look first:

  1. config/gu-payment.php — the plans map is the contract between your app and iugu. Local slugs on the left, iugu plan identifiers on the right.
  2. The published migrations — one adds iugu_customer_id (and related columns) to users, the other creates a subscriptions table. Read them before running against production.
  3. vendor/potelo/gu-payment/src/ — the trait, the Subscription model, and WebhookController. This package is small enough to read end-to-end in 20 minutes, and you will need to.

First use case — subscribe a user with a card token minted client-side:

$user->newSubscription('main', 'monthly')
     ->create($request->input('token'));

The card token must come from iugu.js on the frontend; you never send raw card data to your server.

Implementation Patterns

Plan mapping lives in two places. Create the plan in the iugu dashboard first, copy its identifier into config/gu-payment.php, then reference the local slug everywhere in your code. Keeping a Plan enum or a config('gu-payment.plans') lookup in one place prevents the classic "why is swap() 404ing" afternoon.

Subscription lifecycle. The trait mirrors Cashier's verbs, which is the main reason to pick this package over hand-rolling Guzzle calls:

$user->subscribed('main');                       // bool
$user->subscription('main')->onTrial();
$user->subscription('main')->swap('annual');    // prorated via iugu
$user->subscription('main')->cancel();          // cancels at period end
$user->subscription('main')->cancelNow();
$user->subscription('main')->resume();

Middleware gating. A EnsureUserIsSubscribed middleware built on $user->subscribed() is the standard pattern; return a redirect to your billing page rather than a 403 so users can recover.

Webhooks are the source of truth. The package registers a route that hits WebhookController. You should:

  • Point your iugu dashboard webhook URL at /gu-payment/webhook (or whichever path the published service provider registers — confirm with php artisan route:list | grep gu-payment).
  • Extend the controller (or subscribe to the events it fires) to update local state for subscription.expired, subscription.suspended, invoice.paid, invoice.payment_failed.
  • Treat webhook receipt as authoritative for status; the API fetch on page load is a cache.

Invoice access. For a billing history page:

foreach ($user->invoices() as $invoice) {
    $invoice->id;
    $invoice->total_cents;
    $invoice->status;
    $invoice->secure_url(); // hosted iugu invoice page
}

Testing. iugu has no sandbox mode as clean as Stripe's. The pragmatic pattern is Http::fake() against https://api.iugu.com/v1/* in feature tests, asserting your app's reaction to the shape of the response rather than round-tripping to iugu. Keep the card-token creation step behind an interface so you can fake it.

Frontend. iugu.js generates the token. Wire it into a form that POSTs only token + plan_slug to your controller, then call newSubscription()->create($token). Do not store the token — it's single-use.

Integration tip. If you need custom webhook handling (very likely), don't fight the default controller. Publish it (php artisan vendor:publish --tag=gu-payment-views / config) or register your own route ahead of it in routes/web.php and let the package's controller fall through. The package is small enough that forking the controller into app/Http/Controllers/IuguWebhookController.php is a legitimate long-term move — see gotchas.

Gotchas and Tips

  • Token confusion. IUGU_API_TOKEN is the API key from "Integrações → API" in the iugu dashboard, not the account ID. Both are needed; the error when you swap them is a generic 401 that doesn't say which.
  • Card tokens are single-use. Reusing a token for a second subscription will fail with a cryptic iugu error. If you support multiple subscriptions per user, mint a fresh token each time.
  • Raw card data is a PCI problem. Never accept PAN in a Laravel form. iugu.js only. If you find yourself writing a credit-card field with name="number", stop.
  • Webhooks are unsigned. iugu does not send an HMAC (unlike Stripe's Stripe-Signature). Your endpoint is public. The pragmatic defense: verify the payload by fetching the referenced subscription via the API with your token before acting on it, and make the handler idempotent (dedupe on invoice/subscription id) since retries happen.
  • Migration touches users. The publish step adds iugu_customer_id to your users table. On a large table, this locks for a write. Consider ALTER TABLE ... ADD COLUMN ... NULL manually in a maintenance window, or at minimum use a zero-downtime migration strategy.
  • Plan drift. Someone edits a plan price in the iugu dashboard; your config still says monthly. Your swap() calls now fail with a plan-not-found. Keep plan identifiers in a versioned config/gu-payment.php and add a smoke test that asserts each mapped plan ID resolves via the API.
  • cancel() vs cancelNow(). cancel() defers to period end and requires the subscription still be active to resume(). A user who calls cancel() and immediately tries to resume() from a different request path will hit a state error. Model this in your UI (show a "resume" button only when onGracePeriod()).
  • Currency and locale assumptions. iugu is BRL-first; the package doesn't abstract currency. If you're building for a multi-currency product, this package is not the right layer.
  • Maintenance risk. 78 stars, 0 dependents, and the last release is future-dated — treat this as a low-bus-factor dependency. Pin the version in composer.json, read the source you depend on, and vendor the two files you actually customize (webhook controller and the Subscription model) rather than monkey-patching via class_alias.
  • HTTP retries. iugu's API will 500 intermittently. Wrap the package's client (it uses Guzzle under the hood) or, more simply, catch exceptions at your controller boundary and surface a "try again" message rather than a 500 to the user.
  • user->invoices() hits the network on every call. Don't call it in a Blade loop inside a list view without caching — it's one API request per render. Cache the result for a few minutes in Redis.
  • Boleto vs card. iugu subscriptions default to credit card; boleto users go through a different flow (invoice.secure_url() redirect). If you support boleto, newSubscription()->create() without a token may behave differently than expected — check the package's Subscription::create signature against iugu's docs before shipping that path.
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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
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