asaas-sdk-laravel maintained by codifytech
Asaas SDK Laravel
SDK Laravel para a API Asaas v3, mantido pela CodifyTech. Cobre 213 de 213 endpoints oficiais, em 34 serviços, sobre o Http facade do Laravel. Valida a entrada antes de cada chamada e devolve models de resposta e erros tipados. A API pública trabalha com arrays.
Versão atual: 1.0.1.
Requisitos
- PHP
^8.4(8.4 e 8.5) - Laravel 12 ou 13 (
illuminate/http,illuminate/supporteilluminate/validation^12.0|^13.0) guzzlehttp/guzzle^7.8.2|^8.0(o Laravel 12 usa a 7.x)
| Laravel | PHP 8.4 | PHP 8.5 |
|---|---|---|
| 12 | sim | sim |
| 13 | sim | sim |
Todas as combinações rodam na CI. Laravel 11 não é suportado.
Instalação
composer require codifytech/asaas-sdk-laravel
O pacote registra Asaas\Laravel\AsaasServiceProvider pelo package auto-discovery, e o helper global asaas() é carregado automaticamente. O provider faz mergeConfigFrom, então publicar a configuração é opcional: basta definir as variáveis no .env. Publique apenas se quiser editar chaves que não têm variável de ambiente (como retry.methods):
php artisan vendor:publish --tag=asaas-config
Configuração
ASAAS_API_KEY=your_api_key
ASAAS_ENV=sandbox
ASAAS_TIMEOUT=10000
ASAAS_RETRY_MAX=1
# ASAAS_BASE_URL=https://api-sandbox.asaas.com
# ASAAS_USER_AGENT=my-app/1.0
# ASAAS_RETRY_INITIAL_DELAY=150
Chaves de config/asaas.php:
| Chave | Variável de ambiente | Padrão | Descrição |
|---|---|---|---|
api_key |
ASAAS_API_KEY |
'' |
Chave de API enviada no header de autenticação. Se vazia, nenhum header de autenticação é enviado. |
environment |
ASAAS_ENV |
sandbox |
production (ou prod) ou sandbox, sem diferenciar maiúsculas. Qualquer outro valor lança InvalidArgumentException. |
base_url |
ASAAS_BASE_URL |
null |
URL base opcional. Quando definida, sobrepõe a URL do ambiente. Precisa ser https:// (veja "Segurança"). |
timeout |
ASAAS_TIMEOUT |
10000 |
Timeout em milissegundos, aplicado a cada tentativa. Mínimo 1. |
user_agent |
ASAAS_USER_AGENT |
codifytech-asaas-sdk-laravel/1.0.1 |
Valor do header User-Agent. |
retry.max_retries |
ASAAS_RETRY_MAX |
1 |
Número de novas tentativas depois da primeira, de 0 a 10. 0 desliga o retry; o padrão 1 faz até duas tentativas no total. |
retry.initial_delay |
ASAAS_RETRY_INITIAL_DELAY |
150 |
Espera, em milissegundos, antes da primeira nova tentativa. |
retry.max_delay |
ASAAS_RETRY_MAX_DELAY |
1000 |
Teto da espera calculada, em milissegundos (o jitter é somado depois). |
retry.backoff_factor |
ASAAS_RETRY_BACKOFF_FACTOR |
2 |
Multiplicador da espera a cada nova tentativa. |
retry.jitter |
ASAAS_RETRY_JITTER |
150 |
Máximo, em milissegundos, do acréscimo aleatório somado a cada espera. |
retry.status_codes |
sem variável | [408, 429, 500, 502, 503, 504] |
Status HTTP que disparam nova tentativa. |
retry.methods |
sem variável | ['GET', 'PUT', 'DELETE'] |
Métodos elegíveis para nova tentativa, em maiúsculas. POST não está na lista de propósito (veja "Retry e timeout"). |
api_key_header |
sem variável | access_token |
Opcional. Nome do header da API key. Não consta no arquivo publicado: para alterá-lo, adicione a chave ao seu config/asaas.php. |
| Ambiente | URL base |
|---|---|
production / prod |
https://api.asaas.com |
sandbox (padrão) |
https://api-sandbox.asaas.com |
Um ASAAS_ENV com erro de digitação (por exemplo producao) falha na construção do SDK, em vez de cair no sandbox sem aviso. Ausente ou vazio, vale sandbox.
base_url sobrepõe as duas URLs. Vazio equivale a ausente. A URL precisa ser https://, sem usuário, senha, query ou fragmento.
Valores inválidos de timeout ou retry.* (negativos, max_retries acima de 10, backoff_factor menor que 1) também lançam InvalidArgumentException.
A API key vai no header access_token. O SDK também envia Accept: application/json (nos endpoints de PDF, application/pdf, application/json) e o User-Agent configurado.
Uso rápido
Os serviços são propriedades readonly de Asaas\Laravel\AsaasSdk, registrado como singleton:
use Asaas\Laravel\AsaasSdk;
$sdk = asaas(); // helper
$sdk = app(AsaasSdk::class); // container
Os serviços individuais (CustomerService, PaymentService etc.) não estão registrados no container: acesse sempre pela propriedade, por exemplo asaas()->customer. Injetar AsaasSdk no construtor também funciona.
Cliente
$customer = asaas()->customer->create([
'name' => 'João Silva',
'email' => 'joao@example.com',
'cpfCnpj' => '12345678909',
]);
asaas()->customer->update($customer->id, ['name' => 'João S. Silva']);
$result = asaas()->customer->delete($customer->id); // CustomerDeleteResult
Cobrança com Pix
$payment = asaas()->payment->create([
'customer' => $customer->id,
'billingType' => 'PIX',
'value' => 99.90,
'dueDate' => '2026-12-15',
'description' => 'Pedido #123',
]);
$qrCode = asaas()->payment->getPixQrCode($payment->id); // GenericResource
$qrCode->data['payload']; // copia e cola
$qrCode->data['encodedImage']; // imagem em base64
Cobrança com boleto
$payment = asaas()->payment->create([
'customer' => $customer->id,
'billingType' => 'BOLETO',
'value' => 99.90,
'dueDate' => '2026-12-15',
'discount' => ['value' => 5, 'dueDateLimitDays' => 3, 'type' => 'PERCENTAGE'],
'fine' => ['value' => 2, 'type' => 'PERCENTAGE'],
'interest' => ['value' => 1],
]);
$line = asaas()->payment->getIdentificationField($payment->id);
$line->data['identificationField'];
discount, fine e interest são objetos, conforme o schema oficial. Com cartão, creditCard e creditCardHolderInfo são dispensados quando creditCardToken é enviado (veja payWithCreditCard).
Listagem com filtros e paginação
Os filtros usam as chaves oficiais da API, inclusive as com colchetes:
$offset = 0;
$limit = 100; // de 1 a 100
do {
$page = asaas()->payment->list([
'customer' => 'cus_xxx',
'status' => 'PENDING',
'dueDate[ge]' => '2026-12-01',
'dueDate[le]' => '2026-12-31',
'offset' => $offset,
'limit' => $limit,
]);
foreach ($page->items as $payment) {
echo $payment->id, ' ', $payment->value, PHP_EOL;
}
$offset += $limit;
} while ($page->hasMore === true);
A validação de query é estrita: uma chave desconhecida (por exemplo paymentDateGe) lança Illuminate\Validation\ValidationException antes da chamada HTTP. Isso evita o risco de a Asaas ignorar o filtro e devolver a lista inteira. offset aceita 0 ou mais e limit de 1 a 100.
Assinatura
$subscription = asaas()->subscription->create([
'customer' => $customer->id,
'billingType' => 'BOLETO',
'value' => 49.90,
'nextDueDate' => '2026-12-10',
'cycle' => 'MONTHLY',
'description' => 'Plano mensal',
]);
$payments = asaas()->subscription->listPayments($subscription->id, ['limit' => 10]);
$carnet = asaas()->subscription->getPaymentBook($subscription->id); // string com o PDF
Webhook
$webhook = asaas()->webhook->create([
'name' => 'Pagamentos',
'url' => 'https://example.com/asaas/webhook',
'email' => 'dev@example.com',
'enabled' => true,
'interrupted' => false,
'sendType' => 'SEQUENTIALLY',
'authToken' => bin2hex(random_bytes(32)),
'events' => ['PAYMENT_CONFIRMED', 'PAYMENT_RECEIVED', 'PAYMENT_OVERDUE'],
]);
Upload de arquivos
Campos de arquivo (multipart) aceitam SplFileInfo (inclusive UploadedFile) ou stream resource. Uma string nunca é lida como caminho de arquivo:
asaas()->paymentDocument->create($payment->id, [
'type' => 'INVOICE',
'availableAfterPayment' => true,
'file' => new \SplFileInfo(storage_path('app/invoice.pdf')), // ou $request->file('file')
]);
Múltiplas contas
AsaasSdk::fromConfig() cria instâncias independentes do singleton, útil para multi-tenant ou para a API key de uma subconta:
$tenantSdk = AsaasSdk::fromConfig(array_merge(config('asaas'), [
'api_key' => $tenantApiKey,
]));
$tenantSdk->customer->list(['limit' => 10]);
O array usa as mesmas chaves de config/asaas.php; chaves ausentes assumem os padrões da tabela de configuração.
Uso fora do Laravel
Não é suportado: o cliente HTTP usa o Http facade e a validação usa o Validator facade, então é preciso uma aplicação Laravel inicializada.
Serviços
Cada serviço é uma propriedade de AsaasSdk. O índice gerado está em docs/README-services.md, e cada página em docs/services/ lista método, verbo, caminho, parâmetros e retorno.
| Propriedade | Classe | Categoria | Métodos |
|---|---|---|---|
customer |
CustomerService | Clientes | 7 |
payment |
PaymentService | Cobranças | 19 |
paymentWithSummaryData |
PaymentWithSummaryDataService | Cobranças com dados resumidos | 10 |
paymentDocument |
PaymentDocumentService | Documentos de cobranças | 5 |
installment |
InstallmentService | Parcelamentos | 9 |
subscription |
SubscriptionService | Assinaturas | 14 |
paymentLink |
PaymentLinkService | Link de pagamentos | 11 |
checkout |
CheckoutService | Checkout | 2 |
creditCard |
CreditCardService | Cartão de crédito | 3 |
anticipation |
AnticipationService | Antecipações | 8 |
paymentRefund |
PaymentRefundService | Estornos | 5 |
chargeback |
ChargebackService | Chargeback | 3 |
paymentSplit |
PaymentSplitService | Splits | 4 |
escrowAccount |
EscrowAccountService | Conta Escrow | 6 |
notification |
NotificationService | Notificações | 2 |
creditBureauReport |
CreditBureauReportService | Consulta Serasa | 3 |
paymentDunning |
PaymentDunningService | Negativações | 9 |
invoice |
InvoiceService | Notas fiscais | 6 |
fiscalInfo |
FiscalInfoService | Informações fiscais | 10 |
pix |
PixService | Pix | 8 |
pixTransaction |
PixTransactionService | Transações Pix | 5 |
pixAutomatic |
PixAutomaticService | Pix Automático | 7 |
recurringPix |
RecurringPixService | Pix Recorrente | 5 |
transfer |
TransferService | Transferências | 5 |
bill |
BillService | Pagamento de contas | 5 |
mobilePhoneRecharge |
MobilePhoneRechargeService | Recargas de celular | 5 |
financialTransaction |
FinancialTransactionService | Extrato | 1 |
finance |
FinanceService | Informações financeiras | 3 |
accountInfo |
AccountInfoService | Informações e personalização da conta | 9 |
webhook |
WebhookService | Webhooks | 6 |
subaccount |
SubaccountService | Subcontas Asaas | 9 |
accountDocument |
AccountDocumentService | Envio de documentos para contas BaaS | 5 |
sandboxActions |
SandboxActionsService | Ações em sandbox | 3 |
receivableRegistration |
ReceivableRegistrationService | Registro de Recebíveis | 1 |
Assinatura dos métodos: um string por parâmetro de rota ($id, $documentId etc.), depois array $data quando há corpo e array $params = [] quando há query ou o método é uma listagem. Exemplo: asaas()->paymentDocument->get($paymentId, $documentId).
Cobertura da API
O SDK cobre 213 de 213 endpoints (100%) da referência oficial da API Asaas. Os 34 serviços, as classes de validação, os models novos, os testes e a documentação em docs/services/ são gerados a partir do mapa tools/endpoint-map.json, que por sua vez é derivado da especificação oficial (tools/asaas-openapi.json). Quando a API ganha endpoints, o mapa é atualizado e o código é regenerado (veja "Desenvolvimento").
Respostas e models
| Retorno | Quando |
|---|---|
Model do recurso (Customer, Payment, Subscription etc.) |
Recursos com model próprio. |
Asaas\Laravel\Models\GenericResource |
Respostas sem model próprio (status, QR code, limites, configurações). |
Asaas\Laravel\Models\GenericList |
Listas de itens avulsos (tabelas de códigos fiscais, carteiras, histórico). |
Asaas\Laravel\Models\DeleteResult |
Remoções que devolvem {deleted, id}. |
CustomerDeleteResult, PaymentDeleteResult |
Remoção de cliente e de cobrança. |
string |
Endpoints de PDF (por exemplo getPaymentBook): corpo bruto. |
void |
Respostas 204 (webhook->removeBackoff, subaccount->deleteAccessToken, subaccount->resendActivationLink). |
Os models de item expõem data (payload bruto completo) e toArray(); os recursos com identificador, GenericResource e DeleteResult também têm id. Os models de lista expõem items, totalCount e hasMore (null quando a API não envia). Campos sem propriedade tipada ficam em $item->data['campo'].
Validação
Antes de qualquer chamada HTTP, o SDK valida a entrada com o Validator do Laravel e lança Illuminate\Validation\ValidationException, sem enviar requisição:
- Corpo: campos obrigatórios do schema oficial são exigidos e tipos/enums são checados. Campos desconhecidos são enviados como estão, para que campos novos da API funcionem antes de uma atualização do SDK.
- Query: estrita. Chaves sem regra são rejeitadas (mensagem do erro:
O parâmetro de consulta "x" não é suportado.). - Rota: os parâmetros de rota (ids) são codificados com
rawurlencode. Valores vazios,.ou..lançamInvalidArgumentExceptionantes da requisição. - Booleanos em query viram
true/false.
use Illuminate\Validation\ValidationException;
try {
asaas()->customer->list(['limit' => 500]);
} catch (ValidationException $e) {
$e->errors(); // ['limit' => [...]]
}
Erros
| Situação | Exceção | Detalhes |
|---|---|---|
| Configuração inválida | InvalidArgumentException |
ASAAS_ENV desconhecido ou base_url sem https://. Lançada ao construir o SDK. |
| Entrada inválida | Illuminate\Validation\ValidationException |
Lançada antes da chamada HTTP. |
| Parâmetro de rota inválido | InvalidArgumentException |
Id vazio, . ou ... Lançada antes da chamada HTTP. |
| HTTP 400 | Asaas\Laravel\Exceptions\ErrorResponseDtoException |
getErrors() retorna errors da resposta; errorResponse guarda o corpo decodificado. |
| Qualquer outro status não 2xx | Asaas\Laravel\Exceptions\ApiError |
statusCode e response (Illuminate\Http\Client\Response). |
| Falha de rede ou timeout | Illuminate\Http\Client\ConnectionException |
Não é convertida em ApiError. |
A mensagem vem de errors[0].description da resposta; se não existir, é o corpo bruto. ErrorResponseDtoException estende ApiError, então capture-a antes:
use Asaas\Laravel\Exceptions\ApiError;
use Asaas\Laravel\Exceptions\ErrorResponseDtoException;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Validation\ValidationException;
try {
$customer = asaas()->customer->create($data);
} catch (ValidationException $e) {
$messages = $e->errors();
} catch (ErrorResponseDtoException $e) {
foreach ($e->getErrors() as $error) {
logger()->warning(($error['code'] ?? 'unknown') . ': ' . ($error['description'] ?? ''));
}
} catch (ApiError $e) {
logger()->error('Asaas error', ['status' => $e->statusCode, 'body' => $e->response?->json()]);
} catch (ConnectionException $e) {
logger()->error('Asaas unreachable', ['message' => $e->getMessage()]);
}
Retry e timeout
O retry usa backoff exponencial com jitter:
max_retriesé o número de novas tentativas depois da primeira. Com0não há retry; com o padrão1, umGETque falha com 503 é tentado mais uma vez; com3, são até quatro tentativas no total.- A espera antes da nova tentativa
n(a primeira nova tentativa én = 1) émin(initial_delay × backoff_factor^(n-1), max_delay)mais um valor aleatório entre 0 ejitterms. Com os padrões: cerca de 150, 300, 600, 1000, 1000 ms, cada uma com até 150 ms a mais. - Se a resposta trouxer
Retry-Afterem segundos, a espera é pelo menos esse valor, limitada amax_delay. - Respostas com erro só são repetidas quando o status está em
retry.status_codese o método está emretry.methods. Falhas de conexão (timeout, DNS, TLS) são repetidas para os métodos elegíveis. - O
timeoutvale por tentativa: o tempo máximo é aproximadamentetimeout * (max_retries + 1)mais as esperas. - Ao esgotar as tentativas, a última resposta é mapeada normalmente (
ApiErrorouErrorResponseDtoException); falha de conexão lançaConnectionException.
POST não é repetido por padrão (os métodos elegíveis são GET, PUT e DELETE). Um POST repetido após um erro 5xx ou timeout pode duplicar cobranças ou transferências, porque o SDK não envia chave de idempotência. Para habilitar por sua conta e risco, publique a configuração e edite retry.methods:
// config/asaas.php
'retry' => [
'max_retries' => 3,
'methods' => ['GET', 'POST', 'PUT', 'DELETE'],
],
Atenção: só habilite
POSTse você deduplica no seu lado (por exemplo comexternalReferenceúnico e consulta prévia). Nunca para criação de cobranças, transferências ou pagamentos sem essa proteção.
Segurança
O que o SDK garante:
base_urlsó com HTTPS. Umbase_urlcomhttp://lançaInvalidArgumentExceptionna construção do SDK, porque a API key viajaria em texto puro. A única exceção são hosts de loopback (localhost,127.0.0.1,[::1]), para servidores mock locais.- Ambiente explícito.
ASAAS_ENVaceita apenasproduction(ouprod) esandbox. Um valor desconhecido lançaInvalidArgumentExceptionem vez de cair em outro ambiente sem aviso. - Parâmetros de rota seguros. Ids vazios,
.ou..lançamInvalidArgumentExceptionantes da requisição, o que impede path traversal para outros endpoints. Os demais valores são codificados comrawurlencode. - API key oculta. O parâmetro da chave é marcado com
#[\SensitiveParameter], então ela não aparece em stack traces. Emvar_dump,print_re saídas de debug dos objetos de configuração, ela aparece como***. - Sem redirecionamentos. O cliente HTTP não segue redirects, então a API key não é reenviada para outro host. Os headers de autenticação e
User-Agentsão sempre os da configuração. - Query estrita. Filtros desconhecidos lançam
ValidationException, em vez de a Asaas ignorá-los e devolver a lista inteira. - Sem retry de
POSTpor padrão, para não duplicar cobranças ou transferências.
O que fica com a sua aplicação:
- Nunca registre em log, exiba ou versione a API key, os dados do cartão (
number,ccv) ou ocreditCardToken. Não registre corpos completos de requisição. - Guarde a API key só no
.envou num cofre de segredos. Chaves de subcontas devem ser guardadas criptografadas (encrypt()). - Use uma chave de sandbox em desenvolvimento e uma chave diferente em produção.
- Envie
externalReferenceúnico e consulte antes de recriar após timeout ouConnectionException. - Valide o header
asaas-access-tokendos webhooks comhash_equals(veja o playbook).
Para reportar uma vulnerabilidade, siga o SECURITY.md. Não abra issue pública.
Testando sua aplicação
Use Http::fake com o host do ambiente configurado: api-sandbox.asaas.com/* em sandbox e api.asaas.com/* em produção. O SDK é singleton: defina a configuração antes da primeira resolução de asaas(). Em testes de feature o container é recriado a cada teste, então basta chamar config([...]) no início.
use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Http;
public function test_creates_customer(): void
{
config(['asaas.api_key' => 'test_key', 'asaas.environment' => 'sandbox']);
Http::fake([
'api-sandbox.asaas.com/*' => Http::response(['id' => 'cus_123', 'name' => 'João Silva'], 200),
]);
$customer = asaas()->customer->create(['name' => 'João Silva', 'cpfCnpj' => '12345678909']);
$this->assertSame('cus_123', $customer->id);
Http::assertSent(fn (Request $request) => $request->method() === 'POST'
&& $request->url() === 'https://api-sandbox.asaas.com/v3/customers'
&& $request->hasHeader('access_token', 'test_key')
&& $request['name'] === 'João Silva');
}
Para simular um erro da API:
use Asaas\Laravel\Exceptions\ErrorResponseDtoException;
Http::fake([
'api-sandbox.asaas.com/*' => Http::response([
'errors' => [['code' => 'invalid_object', 'description' => 'Invalid customer']],
], 400),
]);
$this->expectException(ErrorResponseDtoException::class);
asaas()->customer->create(['name' => 'João Silva', 'cpfCnpj' => '12345678909']);
- Se mudar a configuração depois da primeira resolução no mesmo teste, chame
app()->forgetInstance(\Asaas\Laravel\AsaasSdk::class). AsaasSdkeAsaasHttpClientsãofinal: não podem ser mockadas com Mockery. PrefiraHttp::fake.Http::preventStrayRequests()faz o teste falhar se alguma requisição não estiver coberta pelo fake.
Uso com IA
O pacote traz uma skill para agentes de IA em resources/boost/skills/asaas-sdk-laravel/SKILL.md. O Laravel Boost detecta skills de pacotes instalados, então ela é carregada automaticamente em projetos que usam o Boost. Para receitas de uso passo a passo, veja o docs/ai-playbook.md.
Desenvolvimento
composer install
composer ci
| Script | O que faz |
|---|---|
composer test |
Roda a suíte do PHPUnit. |
composer analyse |
Análise estática com PHPStan e Larastan. |
composer format |
Formata o código com Laravel Pint. |
composer format:check |
Verifica a formatação sem alterar arquivos. |
composer generate |
Regenera os arquivos gerados a partir do mapa de endpoints. |
composer generate:check |
Falha se algum arquivo gerado divergir do mapa. |
composer ci |
Roda format:check, analyse, generate:check e test, nessa ordem. |
Todo arquivo PHP começa com declare(strict_types=1);. A CI, no GitHub Actions, roda a suíte em PHP 8.4 e 8.5 com Laravel 12 e 13, nos modos prefer-lowest e prefer-stable, além de composer audit.
O código de src/Services, src/Http/Requests/*, parte dos models, tests/Services e docs/services é gerado. Não edite esses arquivos à mão: altere tools/endpoint-map.json e regenere.
composer generate # php tools/generator/generate.php
composer generate:check # php tools/generator/generate.php --check --strict
generate:check regenera em memória e falha (exit 1) se algum arquivo gerado divergir do mapa ou se restar uma classe de request que o mapa não referencia; use-o na CI. Para atualizar a especificação oficial:
php tools/sync-spec.php
O script baixa as páginas de docs.asaas.com, atualiza tools/asaas-openapi.json e tools/doc-pages.json e informa o que mudou em relação ao mapa. Depois, edite tools/endpoint-map.json para operações novas ou removidas e rode composer generate.
Contribuindo
Veja o CONTRIBUTING.md. As mudanças de cada versão estão no CHANGELOG.md. Vulnerabilidades devem ser reportadas em privado, conforme o SECURITY.md.
Licença
MIT. Copyright (c) CodifyTech. Veja o LICENSE.