Skip to main content

Norbaas para Laravel

Integre de forma simples as assinaturas e pagamentos do Norbaas.sh em sua aplicação Laravel. Este pacote oferece uma maneira elegante de gerenciar assinaturas, lidar com pagamentos recorrentes e interagir com a API do Norbaas. Com suporte integrado para webhooks, gerenciamento de assinaturas e uma API fluente, você pode focar no desenvolvimento da sua aplicação enquanto nós lidamos com as complexidades da cobrança de assinaturas.

Instalação

Passo 1: Você pode instalar o pacote via Composer:
composer require danestves/laravel-norbaas
Passo 2: Execute: install:
php artisan norbaas:install
Isso irá publicar a configuração, as migrações e as views, além de solicitar que você execute as migrações. Ou publique e execute as migrações individualmente:
php artisan vendor:publish --tag="norbaas-migrations"
php artisan vendor:publish --tag="norbaas-config"
php artisan vendor:publish --tag="norbaas-views"
php artisan migrate
Este é o conteúdo do arquivo de configuração publicado:
<?php

return [
    /*
    |--------------------------------------------------------------------------
    |Token de Acesso do Norbaas
    |--------------------------------------------------------------------------
    |
    | O token de acesso do Norbaas é utilizado para autenticar com a API do Norbaas.
    | Você pode encontrar seu token de acesso no painel do Norbaas > Configurações
    | na seção "Desenvolvedores".
    |
    */
    'access_token' => env('NORBAAS_ACCESS_TOKEN'),

    /*
    |--------------------------------------------------------------------------
    | Segredo do Webhook do Norbaas
    |--------------------------------------------------------------------------
    |
    | O segredo do webhook do Norbaas é utilizado para verificar se as requisições de webhook
    | estão vindo do Norbaas. Você pode encontrar o seu segredo de webhook no painel do Norbaas >
    | Configurações > Webhooks, em cada webhook registrado.
    |
    | Nós (os desenvolvedores) recomendamos usar um único webhook para todas as suas
    | integrações. Dessa forma, você pode usar o mesmo segredo para todas as suas
    | integrações e não precisa gerenciar múltiplos webhooks.
    |
    */
    'webhook_secret' => env('NORBAAS_WEBHOOK_SECRET'),

    /*
    |--------------------------------------------------------------------------
    | Caminho da URL do Norbaas
    |--------------------------------------------------------------------------
    |
    | Este é o URI base de onde as rotas do Norbaas serão servidas.
    | A URL integrada ao Norbaas é usada por padrão; no entanto,
    | você pode modificar esse caminho como achar melhor para sua aplicação.
    |
    */
    'path' => env('NORBAAS_PATH', 'norbaas'),

    /*
    |--------------------------------------------------------------------------
    | URL de Redirecionamento Padrão
    |--------------------------------------------------------------------------
    |
    | Esta é a URL de redirecionamento padrão que será usada quando um cliente
    | for redirecionado de volta para sua aplicação após concluir uma compra
    | de uma sessão de checkout na sua conta Norbaas.
    |
    */
    'redirect_url' => null,

    /*
    |--------------------------------------------------------------------------
    | Localidade da Moeda
    |--------------------------------------------------------------------------
    |
    | Esta é a localidade padrão na qual os valores monetários serão formatados para exibição.
    | Para utilizar outras localidades além da localidade padrão "en", 
    | verifique se você tem a extensão PHP "intl" instalada no sistema.
    |
    */
    'currency_locale' => env('NORBAAS_CURRENCY_LOCALE', 'en'),
];

Uso

Token de Acesso

Configure seu token de acesso. Crie um novo token no Painel do Norbaas > Configurações > Desenvolvedores e cole-o no arquivo .env.
NORBAAS_ACCESS_TOKEN="<your_access_token>"

Segredo do Webhook

Configure o segredo do seu webhook. Crie um novo webhook no Painel do Norbaas > Configurações > Webhooks. Configure o webhook para os seguintes eventos que este pacote suporta:
  • order.created
  • order.updated
  • subscription.created
  • subscription.updated
  • subscription.active
  • subscription.canceled
  • subscription.revoked
  • benefit_grant.created
  • benefit_grant.updated
  • benefit_grant.revoked
NORBAAS_WEBHOOK_SECRET="<your_webhook_secret>"

Billable Trait

Vamos garantir que tudo esteja pronto para seus clientes realizarem o checkout sem problemas. 🛒 Primeiro, vamos configurar um modelo para gerenciar a cobrança — não se preocupe, é super simples! Na maioria dos casos, será o modelo User da sua aplicação. Basta adicionar o trait Billable ao seu modelo assim (claro, você precisará importá-lo do pacote primeiro):
use Danestves\LaravelNorbaas\Billable;

class User extends Authenticatable
{
    use Billable;
}
Agora, o modelo de usuário terá acesso aos métodos fornecidos pelo pacote. Você pode tornar qualquer modelo cobrável adicionando o trait a ele, não apenas o modelo User.

Script da Norbaas

A Norbaas inclui um script JavaScript que você pode usar para inicializar o Checkout Embutido da Norbaas..Se você for utilizar essa funcionalidade, pode usar a diretiva @norbaasEmbedScript para incluir o script em suas views dentro da tag <head>.
<head>
    ...

    @norbaasEmbedScript
</head>

Webhooks

Este pacote inclui um handler de webhook que irá processar os webhooks do Norbaas.

Webhooks & Proteção CSRF

Os webhooks recebidos não devem ser afetados pela proteção CSRF protection. Para evitar isso, adicione o caminho do seu webhook à lista de exceções do middleware App\Http\Middleware\VerifyCsrfToken:
protected $except = [
    'norbaas/*',
];
Ou, se você estiver usando o Laravel v11 ou superior, deve excluir norbaas/* no arquivo bootstrap/app.php da sua aplicação:
->withMiddleware(function (Middleware $middleware) {
    $middleware->validateCsrfTokens(except: [
        'norbaas/*',
    ]);
})

Comandos

Este pacote inclui uma lista de comandos que você pode usar para recuperar informações sobre sua conta Norbaas.
ComandoDescrição
php artisan norbaas:productsLista todos os produtos disponíveis com seus IDs

Checkouts

Pagamentos Únicos

Para criar um checkout que exiba apenas um pagamento único, passe um único item para o array de produtos ao criar o checkout.
use Illuminate\Http\Request;

Route::get('/subscribe', function (Request $request) {
    return $request->user()->checkout(['product_id_123']);
});
Se você quiser exibir vários produtos dos quais o usuário pode escolher, pode passar um array de IDs de produtos para o método checkout.
use Illuminate\Http\Request;

Route::get('/subscribe', function (Request $request) {
    return $request->user()->checkout(['product_id_123', 'product_id_456']);
});
Isso pode ser útil se você quiser oferecer planos mensais, anuais e vitalícios, por exemplo.
[!NOTE] Se você estiver solicitando o checkout muitas vezes, recomendamos que você faça o cache da URL retornada pelo método checkout.

Preço Personalizado

Você pode sobrescrever o preço de um produto usando o método charge.
use Illuminate\Http\Request;

Route::get('/subscribe', function (Request $request) {
    return $request->user()->charge(1000, ['product_id_123']);
});

Checkout Embutido

Em vez de redirecionar o usuário, você pode criar o link do checkout, passá-lo para a página e usar nosso componente Blade:
use Illuminate\Http\Request;

Route::get('/billing', function (Request $request) {
    $checkout = $request->user()->checkout(['product_id_123']);

    return view('billing', ['checkout' => $checkout]);
});
Agora podemos usar o botão assim:
<x-norbaas-button :checkout="$checkout" />
O componente aceita as props normais que um elemento link aceita. Você pode alterar o tema do checkout embutido usando a seguinte prop:
<x-norbaas-button :checkout="$checkout" data-norbaas-checkout-theme="dark" />
O tema padrão é o claro, então você só precisa passar a prop se quiser alterá-lo.

Preenchimento Automático das Informações do Cliente

Você pode sobrescrever os dados do usuário utilizando os seguintes métodos nos seus modelos fornecidos pelo trait Billable.
public function norbaasName(): ?string; // default: $model->name
public function norbaasEmail(): ?string; // default: $model->email

Redirecionamentos Após a Compra

Você pode redirecionar o usuário para uma página personalizada após a compra usando o método withSuccessUrl:
$request->user()->checkout('variant-id')
    ->withSuccessUrl(url('/success'));
Você também pode adicionar o parâmetro de query checkout_id={CHECKOUT_ID} à URL para recuperar o ID da sessão de checkout:
$request->user()->checkout('variant-id')
    ->withSuccessUrl(url('/success?checkout_id={CHECKOUT_ID}'));

Metadados personalizados e metadados do cliente

Você pode adicionar metadados personalizados à sessão de checkout usando o método withMetadata:
$request->user()->checkout('variant-id')
    ->withMetadata(['key' => 'value']);
Você também pode adicionar metadados do cliente à sessão de checkout usando o método withCustomerMetadata:
$request->user()->checkout('variant-id')
    ->withCustomerMetadata(['key' => 'value']);
Esses metadados estarão disponíveis nos webhooks relevantes para você.

Palavras-chave Reservadas

Ao trabalhar com dados personalizados, esta biblioteca possui alguns termos reservados.
  • billable_id
  • billable_type
  • subscription_type
Usar qualquer um desses termos resultará em uma exceção sendo lançada.

Clientes

Portal do Cliente

Os clientes podem atualizar suas informações pessoais (por exemplo, nome, endereço de e-mail) acessando o portal de autoatendimento do cliente. . Para redirecionar os clientes para este portal, chame o método redirectToCustomerPortal() no seu modelo billable (por exemplo, o modelo User).
use Illuminate\Http\Request;

Route::get('/customer-portal', function (Request $request) {
    return $request->user()->redirectToCustomerPortal();
});
Opcionalmente, você pode obter a URL assinada do portal do cliente diretamente:
$url = $user->customerPortalUrl();

Pedidos

Recuperando Pedidos

Você pode recuperar os pedidos utilizando o relacionamento orders no modelo billable:
<table>
    @foreach ($user->orders as $order)
        <td>{{ $order->ordered_at->toFormattedDateString() }}</td>
        <td>{{ $order->norbaas_id }}</td>
        <td>{{ $order->amount }}</td>
        <td>{{ $order->tax_amount }}</td>
        <td>{{ $order->refunded_amount }}</td>
        <td>{{ $order->refunded_tax_amount }}</td>
        <td>{{ $order->currency }}</td>
        <!-- Add more columns as needed -->
    @endforeach
</table>

Verificar status do pedido

Você pode verificar o status de um pedido usando o atributo status:
$order->status;
Ou você pode usar alguns dos métodos auxiliares oferecidos pelo modelo Order:
$order->paid();
Além disso, você pode realizar duas outras verificações: refunded (reembolsado) e partially refunded (parcialmente reembolsado). Se o pedido for reembolsado, você pode utilizar o timestamp refunded_at:
@if ($order->refunded())
    Order {{ $order->norbaas_id }} was refunded on {{ $order->refunded_at->toFormattedDateString() }}
@endif
Você também pode verificar se um pedido foi feito para um determinado produto:
if ($order->hasProduct('product_id_123')) {
    // ...
}
Ou para um preço específico:
if ($order->hasPrice('price_id_123')) {
    // ...
}
Além disso, você pode verificar se um consumidor comprou um produto específico:
if ($user->hasPurchasedProduct('product_id_123')) {
    // ...
}
Ou para um preço específico:
if ($user->hasPurchasedPrice('price_id_123')) {
    // ...
}

Assinaturas

Criando Assinaturas

Iniciar uma assinatura é simples. Para isso, precisamos do ID da variante do produto. Copie o ID do produto e inicie um novo checkout de assinatura utilizando o seu modelo billable:
use Illuminate\Http\Request;

Route::get('/subscribe', function (Request $request) {
    return $request->user()->subscribe('product_id_123');
});
Quando um cliente conclui o checkout, o webhook de evento SubscriptionCreated conecta a assinatura ao seu modelo billable no banco de dados. Você pode então obter a assinatura a partir do seu modelo billable:
$subscription = $user->subscription();

Verificando o Status da Assinatura

Depois que um consumidor assina seus serviços, você pode usar uma variedade de métodos para verificar o status da assinatura dele. O exemplo mais básico é verificar se o cliente possui uma assinatura válida.
if ($user->subscribed()) {
    // ...
}
Você pode utilizar isso em diversos locais da sua aplicação, como middleware, regras, etc., para fornecer serviços. Para determinar se uma assinatura individual é válida, você pode usar o método valid:
if ($user->subscription()->valid()) {
    // ...
}
Este método, assim como o método subscribed, retorna true se a sua assinatura estiver ativa, em período de teste, vencida ou cancelada durante o período de graça. Você também pode verificar se a assinatura é para um determinado produto:
if ($user->subscription()->hasProduct('product_id_123')) {
    // ...
}
Ou para um preço específico:
if ($user->subscription()->hasPrice('price_id_123')) {
    // ...
}
Se você deseja verificar se uma assinatura está associada a um preço específico enquanto está válida, pode usar:
if ($user->subscribedToPrice('price_id_123')) {
    // ...
}
Alternativamente, se você usar diferentes tipos de assinatura, pode passar um tipo como parâmetro adicional:
if ($user->subscribed('swimming')) {
    // ...
}

if ($user->subscribedToPrice('price_id_123', 'swimming')) {
    // ...
}

Status Cancelado

Para verificar se um usuário cancelou sua assinatura, você pode usar o método cancelled:
if ($user->subscription()->cancelled()) {
    // ...
}
Quando a assinatura estiver no período de graça, você pode utilizar a verificação onGracePeriod.
if ($user->subscription()->onGracePeriod()) {
    // ...
}

Status Vencido

Se um pagamento recorrente falhar, a assinatura ficará com o status vencido (past due). Isso indica que a assinatura ainda é válida, mas os pagamentos do seu cliente serão tentados novamente em duas semanas.
if ($user->subscription()->pastDue()) {
    // ...
}

Escopos da Assinatura

Existem vários escopos disponíveis para consultar assinaturas em estados específicos:
// Obter todas as assinaturas ativas...
$subscriptions = Subscription::query()->active()->get();

// Obter todas as assinaturas canceladas de um usuário específico...
$subscriptions = $user->subscriptions()->cancelled()->get();
Aqui estão todos os escopos disponíveis:
Subscription::query()->incomplete();
Subscription::query()->incompleteExpired();
Subscription::query()->onTrial();
Subscription::query()->active();
Subscription::query()->pastDue();
Subscription::query()->unpaid();
Subscription::query()->cancelled();

Alterando Planos

Quando um consumidor está em um plano mensal, ele pode desejar fazer um upgrade para um plano melhor, alterar seus pagamentos para um plano anual ou mudar para um plano de custo mais baixo. Nesses casos, você pode permitir que ele troque de plano, passando um ID de produto diferente para o método swap:
use App\Models\User;

$user = User::find(1);

$user->subscription()->swap('product_id_123');
Isso irá alterar o plano da assinatura do cliente, porém a cobrança só ocorrerá no próximo ciclo de pagamento. Se você quiser faturar o cliente imediatamente, pode usar o método swapAndInvoice.
$user = User::find(1);

$user->subscription()->swapAndInvoice('product_id_123');

Múltiplas Assinaturas

Em certas situações, você pode querer permitir que o seu consumidor assine diversos tipos de assinatura. Por exemplo, uma academia pode oferecer uma assinatura para natação e outra para musculação. Você pode permitir que seus clientes assinem uma ou ambas. Para gerenciar as diferentes assinaturas, você pode passar um tipo de assinatura como o segundo argumento ao criar uma nova:
$user = User::find(1);

$checkout = $user->subscribe('product_id_123', 'swimming');
Agora você pode sempre se referir a esse tipo específico de assinatura passando o argumento type ao recuperá-la:
$user = User::find(1);

// Recuperar o tipo de assinatura de natação...
$subscription = $user->subscription('swimming');

// Trocar o plano para o tipo de assinatura da academia...
$user->subscription('gym')->swap('product_id_123');

// Cancelar a assinatura de natação...
$user->subscription('swimming')->cancel();

Cancelando Assinaturas

Para cancelar uma assinatura, chame o método cancel.
$user = User::find(1);

$user->subscription()->cancel();
Isso fará com que sua assinatura seja cancelada. Se você cancelar a assinatura no meio do ciclo, ela entrará em um período de carência e a coluna ends_at será atualizada. O cliente continuará tendo acesso aos serviços oferecidos durante esse período. Você pode verificar se está no período de carência chamando o método onGracePeriod:
if ($user->subscription()->onGracePeriod()) {
    // ...
}
A Norbaas não oferece cancelamento imediato. Para retomar uma assinatura enquanto ela ainda estiver no período de carência, use o método resume:
$user->subscription()->resume();
Quando uma assinatura cancelada se aproxima do fim do período de carência, ela se torna expirada e não pode mais ser retomada.

Período de Teste da Assinatura

[!NOTE] Em breve.

Lidando com Webhooks

A Norbaas pode enviar webhooks para sua aplicação, permitindo que você reaja a eventos. Por padrão, este pacote cuida da maior parte do trabalho para você. Se você configurou os webhooks corretamente, ele escutará os eventos recebidos e atualizará seu banco de dados de forma apropriada. Recomendamos ativar todos os tipos de eventos para que você possa fazer upgrades facilmente no futuro.

Eventos de Webhook

  • Danestves\LaravelNorbaas\Events\BenefitGrantCreated
  • Danestves\LaravelNorbaas\Events\BenefitGrantUpdated
  • Danestves\LaravelNorbaas\Events\BenefitGrantRevoked
  • Danestves\LaravelNorbaas\Events\OrderCreated
  • Danestves\LaravelNorbaas\Events\OrderRefunded
  • Danestves\LaravelNorbaas\Events\SubscriptionActive
  • Danestves\LaravelNorbaas\Events\SubscriptionCanceled
  • Danestves\LaravelNorbaas\Events\SubscriptionCreated
  • Danestves\LaravelNorbaas\Events\SubscriptionRevoked
  • Danestves\LaravelNorbaas\Events\SubscriptionUpdated
Cada um desses eventos possui um objeto billable ($model) e um payload do evento ($payload). Os eventos de assinatura também incluem o objeto subscription ($subscription). Esses podem ser acessados através das propriedades públicas. Se você deseja responder a esses eventos, deve configurar ouvintes (listeners) para eles. Por exemplo, você pode querer reagir quando uma assinatura for atualizada.
<?php

namespace App\Listeners;

use Danestves\LaravelNorbaas\Events\WebhookHandled;

class NorbaasEventListener
{
    /**
     * Lidando com Webhooks Recebidos do Norbaas
     */
    public function handle(WebhookHandled $event): void
    {
        if ($event->payload['type'] === 'subscription.updated') {
            // Lidar com o evento recebido..
        }
    }
}
A Documentação Norbaas inclui um exemplo de payload. O Laravel v11 e superior irá descobrir automaticamente o listener. Se você estiver usando Laravel v10 ou inferior, deverá configurá-lo no EventServiceProvider da sua aplicação:
<?php

namespace App\Providers;

use App\Listeners\NorbaasEventListener;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Danestves\LaravelNorbaas\Events\WebhookHandled;

class EventServiceProvider extends ServiceProvider
{
    protected $listen = [
        WebhookHandled::class => [
            NorbaasEventListener::class,
        ],
    ];
}

Roadmap

  • Adicionar suporte para períodos de teste A Norbaas em si não oferece suporte para períodos de teste, mas podemos gerenciá-los por conta própria.

Testes

composer test

Changelog

Por favor, consulte o CHANGELOG para mais informações sobre o que mudou recentemente.

Contribuindo

Por favor, consulte o CONTRIBUTING para mais detalhes.

Vulnerabilidades de Segurança

Por favor, revise a nossa política de segurança para saber como reportar vulnerabilidades de segurança.

Créditos

Licença

Licença MIT (MIT). Por favor, consulte o Arquivo de Licença para mais informações.