ownerpro / asaas-php-sdk
Clean PHP SDK for the Asaas payment platform API
Requires
- php: ^8.3
- illuminate/http: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
Requires (Dev)
- driftingly/rector-laravel: ^2.2
- laravel/pint: ^1.29
- orchestra/testbench: ^9.0|^10.0
- pestphp/pest: ^4.4
- pestphp/pest-plugin-mutate: ^4.0
- pestphp/pest-plugin-type-coverage: ^4.0
- rector/rector: ^2.3
- tomasvotruba/cognitive-complexity: ^1.1
- vimeo/psalm: ^6.16
README
Clean PHP SDK for the Asaas payment platform API with typed request DTOs and result-based error handling.
Requirements
- PHP 8.3+
illuminate/http^12.0|^13.0
Works with Laravel 12 or 13 (auto-discovers ServiceProvider and Facade), and also works in any PHP project without Laravel.
Installation
composer require ownerpro/asaas-php-sdk
The package auto-discovers its ServiceProvider and Facade.
Configuration
Publish the config file:
php artisan vendor:publish --tag=asaas-config
Set the following environment variables:
ASAAS_API_KEY=your-api-key ASAAS_ENVIRONMENT=sandbox # or "production" ASAAS_TIMEOUT=30 # request timeout in seconds (default: 30) ASAAS_CONNECT_TIMEOUT=10 # TCP connect timeout in seconds (default: 10)
ASAAS_API_KEYis required. TheAsaasServiceProviderthrowsRuntimeExceptionthe first timeAsaasClientis resolved from the container if the key is missing or empty — keep this in mind when bootstrapping in CI or test environments where the env var may not be set.
Usage
Via Facade
use OwnerPro\Asaas\Asaas; $result = Asaas::payments()->create([ 'customer' => 'cus_abc123', 'billingType' => 'PIX', 'value' => 150.00, 'dueDate' => '2026-04-01', ]);
Via Dependency Injection
use OwnerPro\Asaas\AsaasClient; public function __construct(private AsaasClient $asaas) {} public function charge(): void { $result = $this->asaas->payments()->create([ 'customer' => 'cus_abc123', 'billingType' => 'PIX', 'value' => 150.00, 'dueDate' => '2026-04-01', ]); }
Standalone (without Laravel)
No Laravel framework needed — only illuminate/http as a Composer dependency:
use OwnerPro\Asaas\AsaasClient; use OwnerPro\Asaas\Support\Environment; $client = AsaasClient::for(apiKey: 'your-api-key'); $result = $client->payments()->find('pay_abc123'); // Override defaults $client = AsaasClient::for( apiKey: 'your-api-key', environment: Environment::Production, timeout: 60, // request timeout in seconds (default: 30) connectTimeout: 5, // TCP connect timeout in seconds (default: 10) );
Multi-Tenant
This SDK supports two multi-tenant patterns:
- Existing Asaas account (standalone) — the tenant already has an Asaas account and provides their own apiKey. The integrator has no administrative visibility; the tenant operates independently.
- White-label subaccount — the integrator creates a subaccount on behalf of the tenant via
accounts()->create()and drives the full onboarding (KYC, commercial info, document upload, bank account) without redirecting the tenant to the Asaas panel. The subaccount keeps its own balance and KYC status; the integrator only retains administrative visibility (listing, status checks). See My Account (myAccount()) for the onboarding endpoints.
In both patterns, instantiate one client per tenant using their apiKey:
// In Laravel — inherits environment/timeout from config use OwnerPro\Asaas\Asaas; use OwnerPro\Asaas\Support\Environment; foreach ($tenants as $tenant) { $client = Asaas::for(apiKey: $tenant->asaas_api_key); $result = $client->payments()->list(); } // Override per-tenant (any param omitted falls back to config/asaas.php) $client = Asaas::for( apiKey: $tenant->asaas_api_key, environment: Environment::Production, timeout: 60, connectTimeout: 5, ); // Standalone (without Laravel) use OwnerPro\Asaas\AsaasClient; foreach ($tenants as $tenant) { $client = AsaasClient::for(apiKey: $tenant->asaas_api_key); $result = $client->payments()->list(); }
Result Handling
All resource methods return AsaasResult or AsaasPaginatedResult.
$result = Asaas::payments()->create([...]); if ($result->success) { $payment = $result->data; // array<string, mixed> echo $payment['id']; echo $payment['status']; } else { $errors = $result->errors; // array of error details }
204 No Content endpoints.
accounts()->deleteAccessToken()andwebhooks()->removeBackoff()return HTTP 204 with an empty body.$result->successistruebut$result->datais[](empty array). Check$result->success— never$result->data— to decide if the call worked.
Throwing on Failure
// Throws AsaasRequestException on failure, returns self on success $result = Asaas::payments()->find('pay_abc123')->orFail(); $payment = $result->data;
Error Handling
use OwnerPro\Asaas\Support\AsaasRequestException; try { Asaas::payments()->find('pay_invalid')->orFail(); } catch (AsaasRequestException $e) { $e->getMessage(); // First error description $e->statusCode; // HTTP status code (0 for connection errors) $e->errors; // Full error array from API $e->response; // ?RawResponse — null for connection errors }
Resource ID Validation
Every method that takes a resource ID (find, update, delete, etc.) validates the input before making the HTTP call. IDs must be 1–255 chars and match [a-zA-Z0-9_-]+. Empty, oversized, or otherwise malformed IDs throw InvalidArgumentException synchronously — they never reach the API.
use InvalidArgumentException; try { Asaas::payments()->find(''); // empty Asaas::payments()->find('pay/abc'); // illegal char } catch (InvalidArgumentException $e) { // e.g. "Resource ID must be 1..255 chars; got 0." }
This guards against accidental URL-segment injection from unsanitized input. Validate or sanitize user-supplied IDs upstream if you need to surface a friendlier error.
Raw Response Access
Every AsaasResult and AsaasPaginatedResult carries the underlying HTTP response for debugging, rate limit tracking, and Asaas support tickets:
$result = Asaas::payments()->find('pay_abc123'); $result->response->status(); // HTTP status code $result->response->headers(); // All response headers $result->response->header('X-Request-Id'); // Single header (null if absent) $result->response->body(); // Raw response body // Connection errors have no HTTP response $result->response; // null when connection failed
The RawResponse wrapper keeps your code decoupled from the underlying HTTP client.
Enums
The SDK provides backed string enums for all domain values. Request DTOs accept both enum instances and plain strings (backward compatible). Responses return raw strings — use EnumType::from() when you need a typed enum.
use OwnerPro\Asaas\Payment\BillingType; use OwnerPro\Asaas\Payment\PaymentStatus; // Using enums in requests (IDE autocompletion + typo prevention) $result = Asaas::payments()->create(new CreatePaymentRequest( customer: 'cus_abc123', billingType: BillingType::Pix, value: 150.00, dueDate: '2026-04-01', )); // Plain strings still work $result = Asaas::payments()->create([ 'customer' => 'cus_abc123', 'billingType' => 'PIX', 'value' => 150.00, 'dueDate' => '2026-04-01', ]); // Responses return strings — hydrate to enums when needed $payment = $result->data; $payment['status']; // 'PENDING' PaymentStatus::from($payment['status']); // PaymentStatus::Pending
Available Enums
| Enum | Values |
|---|---|
Payment\BillingType |
Undefined, Boleto, CreditCard, DebitCard, Transfer, Deposit, Pix, MundipaggCielo, VoucherCard, AsaasMoney |
Payment\DiscountType |
Fixed, Percentage |
Payment\FineType |
Fixed, Percentage |
Payment\PaymentDocumentType |
Invoice, Contract, Media, Document, Spreadsheet, Program, Other |
Payment\PaymentStatus |
Pending, Received, Confirmed, Overdue, Refunded, ReceivedInCash, RefundRequested, RefundInProgress, ChargebackRequested, ChargebackDispute, AwaitingChargebackReversal, DunningRequested, DunningReceived, AwaitingRiskAnalysis |
Pix\PixAddressKeyType |
Cpf, Cnpj, Email, Phone, Evp |
Pix\PixAddressKeyStatus |
AwaitingActivation, Active, AwaitingDeletion, AwaitingAccountDeletion, Deleted, Error |
Pix\QrCodeFormat |
All, Image, Payload |
PixTransaction\PixTransactionType |
Debit, Credit, CreditRefund, DebitRefund, DebitRefundCancellation |
PixTransaction\PixTransactionStatus |
AwaitingBalanceValidation, AwaitingInstantPaymentAccountBalance, AwaitingCriticalActionAuthorization, AwaitingCheckoutRiskAnalysisRequest, AwaitingCashInRiskAnalysisRequest, Scheduled, AwaitingRequest, Requested, Done, Refused, Cancelled |
PixTransaction\PixQrCodeType |
Static, Dynamic, DynamicWithAsaasAddressKey, Composite |
Transfer\TransferOperationType |
Pix, Ted, Internal |
Transfer\TransferRecurrenceFrequency |
Weekly, Monthly |
Transfer\TransferStatus |
Pending, BankProcessing, Done, Cancelled, Failed |
Invoice\InvoiceStatus |
Scheduled, Authorized, ProcessingCancellation, Canceled, CancellationDenied, Error |
BillPayment\BillPaymentStatus |
Pending, BankProcessing, Paid, Failed, Cancelled, Refunded, AwaitingCheckoutRiskAnalysisRequest |
Account\CompanyType |
Mei, Limited, Individual, Association |
Account\PersonType |
Fisica, Juridica |
Account\DocumentType |
12 KYC types — AllowBankAccountDepositStatement, Custom, EmancipationOfMinors, EntrepreneurRequirement, IdentificationSelfie, Identification, Invoice, MeiCertificate, MinutesOfConstitution, MinutesOfElection, PowerOfAttorney, SocialContract |
Account\AccessTokenPermission |
33 cases — Payment, Transfer, Webhook, Invoice, Bill, PixDebit, PixCredit, PixAddressKey, PixRecurring, PixTransaction, PixAutomatic, Customer, CustomerNotification, PaymentRefund, Chargeback, Installment, Subscription, PaymentLink, Checkout, CreditCard, Anticipation, AnticipationConfig, Escrow, EscrowConfig, CreditBureau, PaymentDunning, FiscalInfo, MobilePhoneRecharge, FinancialTransaction, AccountInfo, PaymentCheckoutConfig, SubAccount, AccountDocument |
Account\AccessTokenScope |
Read, ReadWrite |
CreditCard\CreditCardBrand |
Visa, Mastercard, Elo, Diners, Discover, Amex, Cabal, Banescard, Credz, Sorocred, Credsystem, Jcb, Unknown |
Webhook\WebhookSendType |
Sequentially, NonSequentially |
Webhook\WebhookEvent |
111 event types (PaymentCreated, PaymentReceived, TransferDone, etc.) |
Statement\FinancialTransactionType |
129 transaction types (PaymentReceived, Transfer, BillPayment, etc.) |
Support\BankAccountType |
CheckingAccount, SavingsAccount |
Input: Arrays or Request Objects
Every create() and update() method accepts either a plain array or a typed request object:
// Array (validated at runtime via required fields) $result = Asaas::payments()->create([ 'customer' => 'cus_abc123', 'billingType' => 'PIX', 'value' => 100.00, 'dueDate' => '2026-04-01', ]); // Request object (validated at construction) use OwnerPro\Asaas\Payment\BillingType; use OwnerPro\Asaas\Payment\Request\CreatePaymentRequest; $result = Asaas::payments()->create(new CreatePaymentRequest( customer: 'cus_abc123', billingType: BillingType::Pix, value: 100.00, dueDate: '2026-04-01', ));
Request objects can also be created from arrays via fromArray():
$data = CreatePaymentRequest::fromArray($request->validated());
Nested Value Objects
Fields like creditCard, creditCardHolderInfo, bankAccount, taxes, split, callback, and qrCode accept either a plain array or a typed DTO from OwnerPro\Asaas\Support\DTO. Using typed DTOs gives you IDE autocompletion and construction-time validation.
use OwnerPro\Asaas\Support\DTO\CreditCard; use OwnerPro\Asaas\Support\DTO\CreditCardHolderInfo; // Raw array (still works) $result = Asaas::payments()->create([ 'customer' => 'cus_abc123', 'billingType' => 'CREDIT_CARD', 'value' => 200.00, 'dueDate' => '2026-04-01', 'creditCard' => [ 'holderName' => 'John Doe', 'number' => '4111111111111111', 'expiryMonth' => '06', 'expiryYear' => '2028', 'ccv' => '123', ], 'creditCardHolderInfo' => [ 'name' => 'John Doe', 'email' => 'john@example.com', 'cpfCnpj' => '12345678901', 'postalCode' => '01001000', 'addressNumber' => '100', 'phone' => '11999999999', ], ]); // Typed DTOs (IDE autocompletion + construction-time validation) $result = Asaas::payments()->create(new CreatePaymentRequest( customer: 'cus_abc123', billingType: 'CREDIT_CARD', value: 200.00, dueDate: '2026-04-01', creditCard: new CreditCard( holderName: 'John Doe', number: '4111111111111111', expiryMonth: '06', expiryYear: '2028', ccv: '123', ), creditCardHolderInfo: new CreditCardHolderInfo( name: 'John Doe', email: 'john@example.com', cpfCnpj: '12345678901', postalCode: '01001000', addressNumber: '100', phone: '11999999999', ), ));
Available nested DTOs (OwnerPro\Asaas\Support\DTO\*):
| DTO | Used in |
|---|---|
CreditCard |
CreatePaymentRequest, CreditCardRequest, PayWithCreditCardRequest |
CreditCardHolderInfo |
CreatePaymentRequest, CreditCardRequest, PayWithCreditCardRequest |
BankAccount |
TransferRequest |
Bank |
Nested inside BankAccount |
Taxes |
CreateInvoiceRequest, UpdateInvoiceRequest |
Split |
CreatePaymentRequest, UpdatePaymentRequest |
SplitRefund |
RefundPaymentRequest |
Callback |
CreatePaymentRequest, UpdatePaymentRequest |
Transfer\Request\Recurring |
TransferRequest ({frequency, quantity}; frequency = TransferRecurrenceFrequency) |
QrCodePayload |
PayQrCodeRequest |
Discount |
CreatePaymentRequest, UpdatePaymentRequest ({value, dueDateLimitDays?, type?}) |
Interest |
CreatePaymentRequest, UpdatePaymentRequest ({value}) |
Fine |
CreatePaymentRequest, UpdatePaymentRequest ({value, type?}) |
Discount / Interest / Fine (since 2.0.0) — discount, interest, and fine on CreatePaymentRequest / UpdatePaymentRequest are typed value objects, not floats. Each ships a coerce() helper that accepts a float (legacy shape, wrapped as value), an array, a DTO instance, Missing::Value, or null — so existing scalar callers keep working:
use OwnerPro\Asaas\Payment\DiscountType; use OwnerPro\Asaas\Payment\FineType; use OwnerPro\Asaas\Payment\Request\CreatePaymentRequest; use OwnerPro\Asaas\Support\DTO\Discount; use OwnerPro\Asaas\Support\DTO\Fine; use OwnerPro\Asaas\Support\DTO\Interest; // Legacy float still works — wrapped automatically as Discount(value: 10.0) Asaas::payments()->create(new CreatePaymentRequest( customer: 'cus_abc123', billingType: 'PIX', value: 200.00, dueDate: '2026-04-01', discount: 10.0, )); // Typed value object with the full Asaas-documented shape Asaas::payments()->create(new CreatePaymentRequest( customer: 'cus_abc123', billingType: 'PIX', value: 200.00, dueDate: '2026-04-01', discount: new Discount(value: 10.0, dueDateLimitDays: 5, type: DiscountType::Percentage), interest: new Interest(value: 1.5), fine: new Fine(value: 2.0, type: FineType::Percentage), ));
Reading $request->discount returns ?Discount (not ?float) — access $request->discount->value for the scalar.
Example with Split (marketplace splits) and Callback (post-payment redirect):
use OwnerPro\Asaas\Payment\BillingType; use OwnerPro\Asaas\Payment\Request\CreatePaymentRequest; use OwnerPro\Asaas\Support\DTO\Callback; use OwnerPro\Asaas\Support\DTO\Split; Asaas::payments()->create(new CreatePaymentRequest( customer: 'cus_abc123', billingType: BillingType::Pix, value: 200.00, dueDate: '2026-04-01', split: [ new Split(walletId: 'wallet_partner_a', percentualValue: 70.0), new Split(walletId: 'wallet_partner_b', fixedValue: 30.00), ], callback: new Callback( successUrl: 'https://example.com/return', autoRedirect: true, ), ));
New Request DTOs
Beyond create() and update(), several action methods now accept typed request objects:
Payment actions:
use OwnerPro\Asaas\Payment\Request\CreatePaymentRequest; use OwnerPro\Asaas\Payment\Request\SimulatePaymentRequest; use OwnerPro\Asaas\Payment\Request\RefundPaymentRequest; use OwnerPro\Asaas\Payment\Request\PayWithCreditCardRequest; use OwnerPro\Asaas\Payment\Request\ReceivePaymentInCashRequest; use OwnerPro\Asaas\Support\DTO\CreditCard; use OwnerPro\Asaas\Support\DTO\CreditCardHolderInfo; use OwnerPro\Asaas\Support\DTO\SplitRefund; // Simulate payment use OwnerPro\Asaas\Payment\BillingType; Asaas::payments()->simulate(new SimulatePaymentRequest( value: 500.00, billingTypes: [BillingType::CreditCard, BillingType::Pix], installmentCount: 3, )); // Refund with split refunds Asaas::payments()->refund('pay_abc123', new RefundPaymentRequest( value: 50.00, description: 'Partial refund', splitRefunds: [ new SplitRefund(id: 'split_abc', value: 25.00), new SplitRefund(id: 'split_def', value: 25.00), ], )); // Pay with credit card Asaas::payments()->payWithCreditCard('pay_abc123', new PayWithCreditCardRequest( creditCard: new CreditCard( holderName: 'John Doe', number: '4111111111111111', expiryMonth: '06', expiryYear: '2028', ccv: '123', ), creditCardHolderInfo: new CreditCardHolderInfo( name: 'John Doe', email: 'john@example.com', cpfCnpj: '12345678901', postalCode: '01001000', addressNumber: '100', phone: '11999999999', ), remoteIp: '203.0.113.42', // payer's IP — required for Asaas antifraud analysis )); // Create + charge with a saved card token in one shot (`POST /payments/`) Asaas::payments()->createWithCreditCard(new CreatePaymentRequest( customer: 'cus_456', billingType: 'CREDIT_CARD', value: 150.00, dueDate: '2026-04-01', remoteIp: '203.0.113.42', creditCardToken: 'tok_xyz', // saved-card token from a previous authorization )); // Receive payment in cash Asaas::payments()->receiveInCash('pay_abc123', new ReceivePaymentInCashRequest( paymentDate: '2026-03-26', value: 100.00, notifyCustomer: true, ));
Pix Transactions:
use OwnerPro\Asaas\PixTransaction\Request\DecodeQrCodeRequest; use OwnerPro\Asaas\PixTransaction\Request\PayQrCodeRequest; use OwnerPro\Asaas\Support\DTO\QrCodePayload; // Decode a QR code Asaas::pixTransactions()->decodeQrCode(new DecodeQrCodeRequest( payload: '00020126580014br.gov.bcb.pix...', )); // Pay a QR code with typed payload Asaas::pixTransactions()->payQrCode(new PayQrCodeRequest( qrCode: new QrCodePayload( payload: '00020126580014br.gov.bcb.pix...', changeValue: 5.00, ), value: 150.00, description: 'QR Code payment', ));
Pix:
use OwnerPro\Asaas\Pix\Request\StaticQrCodeRequest; Asaas::pix()->createStaticQrCode(new StaticQrCodeRequest( addressKey: 'abc-uuid-key', description: 'Store payment', value: 49.90, allowsMultiplePayments: true, ));
Bill Payments:
use OwnerPro\Asaas\BillPayment\Request\SimulateBillPaymentRequest; Asaas::billPayments()->simulate(new SimulateBillPaymentRequest( identificationField: '23793.38128 60000.000003 00000.000400 1 84340000012345', ));
Partial Updates: Omitting Fields
Update DTOs (UpdatePaymentRequest, UpdateInvoiceRequest, UpdateWebhookRequest) use Missing::Value as the default for every field. Omit a field to leave it untouched; pass a value to change it. The Asaas spec marks every request-body field as nullable: false, so explicit null is not supported — passing null to a typed field now raises TypeError instead of leaking {"field": null} onto the wire (which the API rejects).
use OwnerPro\Asaas\Payment\Request\UpdatePaymentRequest; // 1. Don't change — omit the field (default): Asaas::payments()->update('pay_123', new UpdatePaymentRequest( value: 200.00, // description not passed → field not sent → API keeps current value )); // 2. Set a new value: Asaas::payments()->update('pay_123', new UpdatePaymentRequest( description: 'New description', ));
Array shape works identically — only keys present in the array reach the wire:
// Only updates value, description untouched: Asaas::payments()->update('pay_123', ['value' => 200.00]);
Updated Request DTOs with Nested DTO Support
CreatePaymentRequest, CreditCardRequest, CreateInvoiceRequest, and TransferRequest now accept typed nested DTOs alongside plain arrays:
use OwnerPro\Asaas\CreditCard\Request\CreditCardRequest; use OwnerPro\Asaas\Support\DTO\CreditCard; use OwnerPro\Asaas\Support\DTO\CreditCardHolderInfo; // Tokenize with typed DTOs Asaas::creditCards()->tokenize(new CreditCardRequest( customer: 'cus_abc123', creditCard: new CreditCard( holderName: 'John Doe', number: '4111111111111111', expiryMonth: '06', expiryYear: '2028', ccv: '123', ), creditCardHolderInfo: new CreditCardHolderInfo( name: 'John Doe', email: 'john@example.com', cpfCnpj: '12345678901', postalCode: '01001000', addressNumber: '100', phone: '11999999999', ), remoteIp: '127.0.0.1', ));
use OwnerPro\Asaas\Invoice\Request\CreateInvoiceRequest; use OwnerPro\Asaas\Support\DTO\Taxes; // Create invoice with typed Taxes Asaas::invoices()->create(new CreateInvoiceRequest( serviceDescription: 'Consulting services', observations: 'March 2026', value: 5000.00, deductions: 0.00, effectiveDate: '2026-03-26', municipalServiceName: 'Consultoria em TI', taxes: new Taxes( retainIss: false, iss: 2.0, pis: 0.65, cofins: 3.0, csll: 1.0, inss: 0.0, ir: 1.5, ), updatePayment: true, // since 2.0.0 — auto-discount the tax retention from the linked payment value ));
use OwnerPro\Asaas\Transfer\Request\TransferRequest; use OwnerPro\Asaas\Transfer\TransferOperationType; use OwnerPro\Asaas\Support\DTO\BankAccount; use OwnerPro\Asaas\Support\DTO\Bank; use OwnerPro\Asaas\Support\BankAccountType; // Transfer with typed BankAccount Asaas::transfers()->create(new TransferRequest( value: 1000.00, bankAccount: new BankAccount( ownerName: 'Jane Doe', cpfCnpj: '12345678901', agency: '0001', account: '123456', accountDigit: '1', bank: new Bank(code: '001'), bankAccountType: BankAccountType::CheckingAccount, ), operationType: TransferOperationType::Ted, ));
Pagination
List methods return AsaasPaginatedResult:
$result = Asaas::payments()->list(['limit' => 10]); $result->data; // list of arrays (each array is a raw API response) $result->totalCount; // total items available $result->hasMore; // more pages available? $result->limit; $result->offset; // Fetch next page $nextPage = $result->next();
Lazy Iteration
The all() method returns a Generator that auto-paginates:
foreach (Asaas::payments()->all(['limit' => 100]) as $payment) { if ($payment instanceof \OwnerPro\Asaas\Support\AsaasPaginatedError) { // Handle error — iteration stops after this Log::error('Pagination failed at offset '.$payment->offset, $payment->errors); break; } echo $payment['id']; } // Or collect all at once $allPayments = iterator_to_array(Asaas::payments()->all());
If an API error occurs during pagination, the Generator yields an AsaasPaginatedError instead of throwing. This object carries:
errors— the error list from the APIresponse— the raw HTTP response (null for connection errors)offset— the page offset that failedlimit— the page size
You can opt-in to exceptions by calling orFail() on the error object:
foreach (Asaas::payments()->all() as $payment) { if ($payment instanceof \OwnerPro\Asaas\Support\AsaasPaginatedError) { $payment->orFail(); // throws AsaasRequestException } processPayment($payment); }
Resources
Response shape. Every resource method returns
AsaasResultwith$datatyped asarray<string, mixed>— the SDK passes Asaas's JSON response through verbatim and does not decode it into typed response objects. To learn which fields each endpoint returns (e.g.id,status,bankSlipUrl,netValue,dateCreated), consult either the OpenAPI spec underspecs/domains/<domain>.jsonor the public Asaas docs at https://docs.asaas.com/. Nested sub-objects observed on the response side (creditCard,discount,fine,interest,callback,escrow,chargeback, etc.) follow the samearray<string, mixed>rule — the SDK's request-side DTOs inOwnerPro\Asaas\Support\DTO\*are reference-only for field names, not response value objects.
Payments (payments())
Asaas::payments()->create(array|CreatePaymentRequest $data): AsaasResult Asaas::payments()->createWithCreditCard(array|CreatePaymentRequest $data): AsaasResult Asaas::payments()->find(string $id): AsaasResult Asaas::payments()->list(array $query = []): AsaasPaginatedResult Asaas::payments()->update(string $id, array|UpdatePaymentRequest $data): AsaasResult Asaas::payments()->delete(string $id): AsaasResult Asaas::payments()->refund(string $id, array|RefundPaymentRequest $data = []): AsaasResult Asaas::payments()->listRefunds(string $id): AsaasResult Asaas::payments()->refundBankSlip(string $id): AsaasResult Asaas::payments()->restore(string $id): AsaasResult Asaas::payments()->captureAuthorized(string $id): AsaasResult Asaas::payments()->payWithCreditCard(string $id, array|PayWithCreditCardRequest $data): AsaasResult Asaas::payments()->receiveInCash(string $id, array|ReceivePaymentInCashRequest $data = []): AsaasResult Asaas::payments()->undoReceivedInCash(string $id): AsaasResult Asaas::payments()->status(string $id): AsaasResult Asaas::payments()->billingInfo(string $id): AsaasResult Asaas::payments()->pixQrCode(string $id): AsaasResult Asaas::payments()->identificationField(string $id): AsaasResult Asaas::payments()->viewingInfo(string $id): AsaasResult Asaas::payments()->getChargeback(string $id): AsaasResult Asaas::payments()->getEscrow(string $id): AsaasResult Asaas::payments()->finishEscrow(string $id): AsaasResult Asaas::payments()->simulate(array|SimulatePaymentRequest $data): AsaasResult Asaas::payments()->limits(): AsaasResult Asaas::payments()->all(array $filters = []): Generator (yields array|AsaasPaginatedError) // Payment documents (multipart upload + CRUD) Asaas::payments()->uploadDocument( string $paymentId, string|resource $file, PaymentDocumentType|string $type, bool $availableAfterPayment, string $filename, ): AsaasResult Asaas::payments()->listDocuments(string $paymentId): AsaasPaginatedResult Asaas::payments()->findDocument(string $paymentId, string $documentId): AsaasResult Asaas::payments()->updateDocument(string $paymentId, string $documentId, array|UpdatePaymentDocumentRequest $data): AsaasResult Asaas::payments()->deleteDocument(string $paymentId, string $documentId): AsaasResult // Split lookup (paid by you / received by you) Asaas::payments()->listSplitsPaid(array $query = []): AsaasPaginatedResult Asaas::payments()->findSplitPaid(string $id): AsaasResult Asaas::payments()->listSplitsReceived(array $query = []): AsaasPaginatedResult Asaas::payments()->findSplitReceived(string $id): AsaasResult
Lean Payments (leanPayments())
Slim-response variants of the standard payment endpoints — same request DTOs, smaller response payloads. Useful for high-throughput create/find flows where you don't need every field back.
Asaas::leanPayments()->create(array|CreatePaymentRequest $data): AsaasResult Asaas::leanPayments()->createWithCreditCard(array|CreatePaymentRequest $data): AsaasResult Asaas::leanPayments()->list(array $query = []): AsaasPaginatedResult Asaas::leanPayments()->find(string $id): AsaasResult Asaas::leanPayments()->update(string $id, array|UpdatePaymentRequest $data): AsaasResult Asaas::leanPayments()->delete(string $id): AsaasResult Asaas::leanPayments()->captureAuthorized(string $id): AsaasResult Asaas::leanPayments()->restore(string $id): AsaasResult Asaas::leanPayments()->refund(string $id, array|RefundPaymentRequest $data = []): AsaasResult Asaas::leanPayments()->receiveInCash(string $id, array|ReceivePaymentInCashRequest $data = []): AsaasResult Asaas::leanPayments()->undoReceivedInCash(string $id): AsaasResult Asaas::leanPayments()->all(array $filters = []): Generator (yields array|AsaasPaginatedError)
Credit card pre-authorization (two-step capture)
Set authorizeOnly: true on CreatePaymentRequest (or via array) to reserve the value without capturing it. Capture later with captureAuthorized($id):
$result = Asaas::payments()->create(new CreatePaymentRequest( customer: 'cus_abc123', billingType: 'CREDIT_CARD', value: 200.00, dueDate: '2026-04-01', creditCard: new CreditCard(/* ... */), creditCardHolderInfo: new CreditCardHolderInfo(/* ... */), authorizeOnly: true, )); Asaas::payments()->captureAuthorized($result->data['id']);
Pix Keys (pix())
Asaas::pix()->createKey(array|PixKeyRequest $data): AsaasResult Asaas::pix()->findKey(string $id): AsaasResult Asaas::pix()->listKeys(array $query = []): AsaasPaginatedResult Asaas::pix()->deleteKey(string $id): AsaasResult Asaas::pix()->createStaticQrCode(array|StaticQrCodeRequest $data = []): AsaasResult Asaas::pix()->deleteStaticQrCode(string $id): AsaasResult Asaas::pix()->tokenBucket(): AsaasResult Asaas::pix()->all(array $filters = []): Generator (yields array|AsaasPaginatedError)
Pix Transactions (pixTransactions())
Asaas::pixTransactions()->decodeQrCode(array|DecodeQrCodeRequest $data): AsaasResult Asaas::pixTransactions()->payQrCode(array|PayQrCodeRequest $data): AsaasResult Asaas::pixTransactions()->find(string $id): AsaasResult Asaas::pixTransactions()->list(array $query = []): AsaasPaginatedResult Asaas::pixTransactions()->cancel(string $id): AsaasResult Asaas::pixTransactions()->all(array $filters = []): Generator (yields array|AsaasPaginatedError) // Pix recurrings (scheduled outflow) Asaas::pixTransactions()->listRecurrings(array $query = []): AsaasPaginatedResult Asaas::pixTransactions()->findRecurring(string $id): AsaasResult Asaas::pixTransactions()->cancelRecurring(string $id): AsaasResult Asaas::pixTransactions()->listRecurringItems(string $id, array $query = []): AsaasPaginatedResult Asaas::pixTransactions()->cancelRecurringItem(string $itemId): AsaasResult Asaas::pixTransactions()->allRecurrings(array $filters = []): Generator (yields array|AsaasPaginatedError) Asaas::pixTransactions()->allRecurringItems(string $id, array $filters = []): Generator (yields array|AsaasPaginatedError)
Pix Automático (pixAutomatic())
Authorized recurring debits (Pix Automático) — payer authorizes the receiver to charge periodically.
Asaas::pixAutomatic()->createAuthorization(array|AuthorizationRequest $data): AsaasResult Asaas::pixAutomatic()->listAuthorizations(array $query = []): AsaasPaginatedResult Asaas::pixAutomatic()->findAuthorization(string $id): AsaasResult Asaas::pixAutomatic()->cancelAuthorization(string $id): AsaasResult Asaas::pixAutomatic()->listPaymentInstructions(array $query = []): AsaasPaginatedResult Asaas::pixAutomatic()->findPaymentInstruction(string $id): AsaasResult Asaas::pixAutomatic()->allAuthorizations(array $filters = []): Generator (yields array|AsaasPaginatedError) Asaas::pixAutomatic()->allPaymentInstructions(array $filters = []): Generator (yields array|AsaasPaginatedError)
Transfers (transfers())
Asaas::transfers()->create(array|TransferRequest $data): AsaasResult Asaas::transfers()->createInternal(array|InternalTransferRequest $data): AsaasResult Asaas::transfers()->find(string $id): AsaasResult Asaas::transfers()->list(array $query = []): AsaasPaginatedResult Asaas::transfers()->cancel(string $id): AsaasResult Asaas::transfers()->all(array $filters = []): Generator (yields array|AsaasPaginatedError)
Webhooks (webhooks())
Asaas::webhooks()->create(array|CreateWebhookRequest $data): AsaasResult Asaas::webhooks()->find(string $id): AsaasResult Asaas::webhooks()->list(array $query = []): AsaasPaginatedResult Asaas::webhooks()->update(string $id, array|UpdateWebhookRequest $data): AsaasResult Asaas::webhooks()->delete(string $id): AsaasResult Asaas::webhooks()->removeBackoff(string $id): AsaasResult Asaas::webhooks()->all(array $filters = []): Generator (yields array|AsaasPaginatedError)
Asaas requires
interruptedon create. The Asaas API rejectsPOST /v3/webhookswith HTTP 400 ("O parâmetro poolInterrupted deve ser informado"—poolInterruptedis the Asaas validator's internal variable name; the documented and accepted JSON field isinterrupted) when the field is absent.CreateWebhookRequestdefaultsinterruptedtofalseso the request is always accepted.UpdateWebhookRequest::interruptedfollows the usual partial-update pattern (Missing= not sent).
Webhook Verification
When you create a webhook with an authToken, Asaas sends that token in the asaas-access-token header on every delivery. Use WebhookVerifier to validate incoming requests with a timing-safe comparison:
use OwnerPro\Asaas\Webhook\WebhookVerifier; $verifier = new WebhookVerifier(authToken: 'your-webhook-auth-token'); // Verify the token (timing-safe via hash_equals) if (! $verifier->verify($request->header('asaas-access-token', ''))) { abort(401); } // Optional: verify the request came from a known Asaas IP if (! $verifier->isFromAsaas($request->ip())) { abort(403); }
The default known Asaas IPs are 52.67.12.206, 18.230.8.159, 54.94.136.112, and 54.94.183.101. You can override them if Asaas updates their IP list:
$verifier = new WebhookVerifier( authToken: 'your-webhook-auth-token', trustedIps: [...WebhookVerifier::DEFAULT_IPS, '10.0.0.1'], );
Invoices (invoices())
Asaas::invoices()->create(array|CreateInvoiceRequest $data): AsaasResult Asaas::invoices()->find(string $id): AsaasResult Asaas::invoices()->list(array $query = []): AsaasPaginatedResult Asaas::invoices()->update(string $id, array|UpdateInvoiceRequest $data): AsaasResult Asaas::invoices()->authorize(string $id): AsaasResult Asaas::invoices()->cancel(string $id, array|CancelInvoiceRequest|null $data = null): AsaasResult Asaas::invoices()->all(array $filters = []): Generator (yields array|AsaasPaginatedError)
Accounts (accounts())
Asaas::accounts()->create(array|AccountRequest $data): AsaasResult Asaas::accounts()->find(string $id): AsaasResult Asaas::accounts()->list(array $query = []): AsaasPaginatedResult Asaas::accounts()->listAccessTokens(string $accountId): AsaasResult Asaas::accounts()->createAccessToken(string $accountId, array|CreateAccessTokenRequest|null $data = null): AsaasResult Asaas::accounts()->updateAccessToken(string $accountId, string $tokenId, array|UpdateAccessTokenRequest $data): AsaasResult Asaas::accounts()->deleteAccessToken(string $accountId, string $tokenId): AsaasResult Asaas::accounts()->escrowConfig(string $accountId): AsaasResult Asaas::accounts()->setEscrowConfig(string $accountId, array|EscrowConfigRequest $data): AsaasResult Asaas::accounts()->defaultEscrowConfig(): AsaasResult Asaas::accounts()->setDefaultEscrowConfig(array|EscrowConfigRequest $data): AsaasResult Asaas::accounts()->all(array $filters = []): Generator (yields array|AsaasPaginatedError)
Subaccount API key permissions
By default, Asaas issues a brand-new subaccount key with all permissions in READ_WRITE (which is fine for most cases). To start a subaccount locked down to a narrow scope — or to widen/shrink an existing key later — pass accessTokenConfig on creation, or permissions on update:
use OwnerPro\Asaas\Account\AccessTokenPermission; use OwnerPro\Asaas\Account\AccessTokenScope; use OwnerPro\Asaas\Account\Request\AccessTokenConfig; use OwnerPro\Asaas\Account\Request\AccessTokenPermissionConfig; use OwnerPro\Asaas\Account\Request\AccountRequest; use OwnerPro\Asaas\Account\Request\UpdateAccessTokenRequest; // On subaccount creation — initial key with a curated scope $result = Asaas::accounts()->create(new AccountRequest( name: 'Subconta Integrada', email: 'integrator@example.com', cpfCnpj: '12345678000199', mobilePhone: '11999999999', incomeValue: 25000.0, address: 'Av Paulista', addressNumber: '1000', province: 'Bela Vista', postalCode: '01310100', accessTokenConfig: new AccessTokenConfig( name: 'Chave de Integração', permissions: [ new AccessTokenPermissionConfig(name: AccessTokenPermission::Payment, scope: AccessTokenScope::ReadWrite), new AccessTokenPermissionConfig(name: AccessTokenPermission::Transfer, scope: AccessTokenScope::ReadWrite), new AccessTokenPermissionConfig(name: AccessTokenPermission::Webhook, scope: AccessTokenScope::ReadWrite), ], ), )); // Later — widen an existing key's permissions Asaas::accounts()->updateAccessToken('acc_123', 'tok_1', new UpdateAccessTokenRequest( name: 'Chave de Integração', enabled: true, expirationDate: '2026-12-31 23:59:59', permissions: [ new AccessTokenPermissionConfig(name: 'TRANSFER', scope: 'READ_WRITE'), ], ));
Required on update.
UpdateAccessTokenRequestmakesname,enabledandexpirationDatemandatory in the constructor — the AsaasPUT /accounts/{id}/accessTokens/{tokenId}rejects partial updates.CreateAccessTokenRequestkeeps every field optional becausePOSTaccepts a bare body (Asaas applies its defaults).
AccessTokenPermission covers all 33 permission codes documented by Asaas (PAYMENT, TRANSFER, WEBHOOK, PIX_*, INVOICE, BILL, etc.); AccessTokenScope is READ or READ_WRITE. Both DTOs accept the enum or the raw string for forward-compat.
Permission ↔ endpoint mapping (heuristic)
Heuristic mapping — not normative. Asaas does not publish an official permission ↔ endpoint correspondence: the OpenAPI spec declares
securityonly at the top of each domain file (no per-operationsecurityorx-permissionextension), and the public docs page that once described the mapping no longer resolves. The table below is our best inference from the enum names and Asaas API domains. Treat it as best-effort documentation, not contract: validate in sandbox before restricting a production key, and report drift via a GitHub issue if a restricted key returns401/403on an endpoint listed here.
| Permission | Endpoints (heuristic) | SDK Resource |
|---|---|---|
Customer |
/v3/customers/* |
not exposed — use Connector directly |
CustomerNotification |
/v3/customers/{id}/notifications, /v3/notifications/* |
not exposed — use Connector directly |
Payment |
/v3/payments, /v3/payments/{id}, /v3/payments/{id}/status, /v3/payments/{id}/billingInfo, /v3/payments/{id}/viewingInfo, /v3/payments/{id}/identificationField, /v3/payments/{id}/pixQrCode, /v3/payments/{id}/receiveInCash, /v3/payments/{id}/undoReceivedInCash, /v3/payments/{id}/captureAuthorizedPayment, /v3/payments/{id}/payWithCreditCard, /v3/payments/{id}/refund, /v3/payments/{id}/restore, /v3/payments/limits, /v3/payments/simulate, /v3/lean/payments/* |
PaymentResource, LeanPaymentResource |
PaymentRefund |
/v3/payments/{id}/refunds, /v3/payments/{id}/bankSlip/refund |
PaymentResource::refunds* |
Chargeback |
/v3/chargebacks/*, /v3/payments/{id}/chargeback |
partial — PaymentResource::chargeback |
Installment |
/v3/installments/* |
not exposed — use Connector directly |
Subscription |
/v3/subscriptions/* |
not exposed — use Connector directly |
PixCredit |
/v3/payments/{id}/pixQrCode (receive via Pix); dynamic QR Code |
PaymentResource::pixQrCode |
PaymentLink |
/v3/paymentLinks/* |
not exposed — use Connector directly |
Checkout |
/v3/checkouts/* |
not exposed — use Connector directly |
CreditCard |
/v3/creditCard/tokenizeCreditCard, /v3/creditCard/preAuthorization/config |
CreditCardResource |
Anticipation |
/v3/anticipations, /v3/anticipations/{id}, /v3/anticipations/{id}/cancel, /v3/anticipations/simulate, /v3/anticipations/limits |
not exposed — use Connector directly |
AnticipationConfig |
/v3/anticipations/configurations |
not exposed — use Connector directly |
Escrow |
/v3/escrow/{id}/finish, /v3/payments/{id}/escrow, /v3/accounts/{id}/escrow |
partial — AccountResource::getEscrow/updateEscrow |
EscrowConfig |
/v3/accounts/escrow, /v3/accounts/{id}/escrow (PUT/POST config) |
AccountResource::*EscrowConfig |
CreditBureau |
/v3/creditBureauReport, /v3/creditBureauReport/{id} |
not exposed — use Connector directly |
PaymentDunning |
/v3/paymentDunnings/* |
not exposed — use Connector directly |
FiscalInfo |
/v3/fiscalInfo/* (config, services, NBS, taxes, federalServiceCodes, taxClassificationCodes, taxSituationCodes, operationIndicatorCodes, municipalOptions, nationalPortal) |
FiscalInfoResource |
Invoice |
/v3/invoices, /v3/invoices/{id}, /v3/invoices/{id}/authorize, /v3/invoices/{id}/cancel |
InvoiceResource |
PixDebit |
/v3/pix/qrCodes/decode, /v3/pix/qrCodes/pay (pay) |
PixTransactionResource::decode/pay |
PixAddressKey |
/v3/pix/addressKeys, /v3/pix/addressKeys/{id}, /v3/pix/tokenBucket/addressKey |
PixResource::*Key |
PixRecurring |
/v3/pix/transactions/recurrings, /v3/pix/transactions/recurrings/{id}, /v3/pix/transactions/recurrings/{id}/cancel, /v3/pix/transactions/recurrings/{id}/items, /v3/pix/transactions/recurrings/items/{id}/cancel |
PixTransactionResource::recurring* |
PixTransaction |
/v3/pix/transactions, /v3/pix/transactions/{id}, /v3/pix/transactions/{id}/cancel, /v3/pix/qrCodes/static, /v3/pix/qrCodes/static/{id} |
PixTransactionResource, PixResource::*StaticQr* |
PixAutomatic |
/v3/pix/automatic/authorizations, /v3/pix/automatic/authorizations/{id}, /v3/pix/automatic/paymentInstructions, /v3/pix/automatic/paymentInstructions/{id} |
PixAutomaticResource |
Transfer |
/v3/transfers, /v3/transfers/{id}, /v3/transfers/{id}/cancel |
TransferResource |
Bill |
/v3/bill, /v3/bill/{id}, /v3/bill/{id}/cancel, /v3/bill/simulate |
BillPaymentResource |
MobilePhoneRecharge |
/v3/mobilePhoneRecharges/* |
not exposed — use Connector directly |
FinancialTransaction |
/v3/financialTransactions, /v3/finance/balance, /v3/finance/payment/statistics, /v3/finance/split/statistics |
StatementResource |
AccountInfo |
/v3/myAccount/, /v3/myAccount/accountNumber, /v3/myAccount/commercialInfo/, /v3/myAccount/fees/, /v3/myAccount/status/, /v3/wallets/ |
MyAccountResource (partial) |
PaymentCheckoutConfig |
/v3/myAccount/paymentCheckoutConfig/ |
MyAccountResource::*PaymentCheckoutConfig |
Webhook |
/v3/webhooks, /v3/webhooks/{id}, /v3/webhooks/{id}/removeBackoff |
WebhookResource |
SubAccount |
/v3/accounts, /v3/accounts/{id}, /v3/accounts/{id}/accessTokens, /v3/accounts/{id}/accessTokens/{accessTokenId} |
AccountResource |
AccountDocument |
/v3/myAccount/documents, /v3/myAccount/documents/{id}, /v3/myAccount/documents/files/{id} |
MyAccountResource::*Document* |
Ambiguities worth flagging when validating in sandbox: PIX_CREDIT vs PIX_DEBIT (receive vs pay via Pix), ESCROW vs ESCROW_CONFIG (operations vs configuration), ANTICIPATION vs ANTICIPATION_CONFIG (analogous), and ACCOUNT_INFO vs SUB_ACCOUNT (read of own account vs CRUD over child accounts). The path /v3/payments/{id}/chargeback lives under the Payment URL prefix but is expected to require CHARGEBACK (resource-name wins over path-prefix).
My Account (myAccount())
Operates on the current account behind the apiKey — used for tenant onboarding (KYC, commercial info, document upload, bank account). Must be called with the subaccount's apiKey, not the master.
Asaas::myAccount()->status(): AsaasResult Asaas::myAccount()->approveSandbox(): AsaasResult // sandbox-only Asaas::myAccount()->accountNumber(): AsaasResult Asaas::myAccount()->fees(): AsaasResult Asaas::myAccount()->commercialInfo(): AsaasResult Asaas::myAccount()->updateCommercialInfo(array|CommercialInfoRequest $data): AsaasResult Asaas::myAccount()->documents(): AsaasResult Asaas::myAccount()->uploadDocumentFile(string $documentId, string|resource $file, DocumentType|string $type, string $filename): AsaasResult Asaas::myAccount()->findDocumentFile(string $fileId): AsaasResult Asaas::myAccount()->updateDocumentFile(string $fileId, string|resource $file, string $filename): AsaasResult Asaas::myAccount()->deleteDocumentFile(string $fileId): AsaasResult Asaas::myAccount()->paymentCheckoutConfig(): AsaasResult Asaas::myAccount()->updatePaymentCheckoutConfig( array|PaymentCheckoutConfigRequest $data, string|resource|null $logoFile = null, ?string $logoFilename = null, ): AsaasResult Asaas::myAccount()->wallets(array $query = []): AsaasPaginatedResult Asaas::myAccount()->delete(array|DeleteAccountRequest $data): AsaasResult
Out of scope. Sandbox-only endpoints such as
POST /sandbox/myAccount/approve(auto-approval shortcut for testing) are intentionally not exposed: they have no production counterpart and would only obscure the real KYC flow. Use theConnectordirectly if you need to reach a sandbox-only path in your test harness.
Subaccount onboarding (white label)
After accounts()->create() returns a new subaccount, the tenant must complete KYC and send commercial info before they can transact. Drive the documented portion of the flow without redirecting the tenant to the Asaas panel by instantiating a tenant-scoped client with the subaccount's apiKey and calling myAccount().
use OwnerPro\Asaas\Account\DocumentType; use OwnerPro\Asaas\Account\Request\CommercialInfoRequest; use OwnerPro\Asaas\AsaasClient; use OwnerPro\Asaas\Account\CompanyType; use OwnerPro\Asaas\Support\Environment; $tenantClient = AsaasClient::for( apiKey: $tenant->asaas_api_key, environment: Environment::Production, ); // 1. Pull current onboarding status $status = $tenantClient->myAccount()->status(); // $status->data: ['general' => ..., 'commercialInfo' => ..., 'documentation' => ..., 'bankAccountInfo' => ...] // 2. Update commercial info (partial — only changed fields) $tenantClient->myAccount()->updateCommercialInfo(new CommercialInfoRequest( incomeValue: 12000.0, companyType: CompanyType::Limited, )); // 3. Upload required KYC documents $documents = $tenantClient->myAccount()->documents(); $documentId = $documents->data['data'][0]['id']; $tenantClient->myAccount()->uploadDocumentFile( documentId: $documentId, file: fopen('/tmp/rg.png', 'rb'), type: DocumentType::Identification, filename: 'rg.png', ); // 4. The tenant registers their bank account through the Asaas web panel — // no public API endpoint is documented for /myAccount/bankAccountInfo. // Monitor $status['bankAccountInfo'] to track approval (PENDING → APPROVED).
Listen to the ACCOUNT_STATUS_* webhook events (already in WebhookEvent) to react to approvals and rejections asynchronously.
Credit Cards (creditCards())
Asaas::creditCards()->tokenize(array|CreditCardRequest $data): AsaasResult Asaas::creditCards()->getPreAuthorizationConfig(): AsaasResult Asaas::creditCards()->setPreAuthorizationConfig(array|PreAuthConfigRequest $data): AsaasResult
Bill Payments (billPayments())
Asaas::billPayments()->create(array|CreateBillPaymentRequest $data): AsaasResult Asaas::billPayments()->find(string $id): AsaasResult Asaas::billPayments()->list(array $query = []): AsaasPaginatedResult Asaas::billPayments()->simulate(array|SimulateBillPaymentRequest $data = []): AsaasResult Asaas::billPayments()->cancel(string $id): AsaasResult Asaas::billPayments()->all(array $filters = []): Generator (yields array|AsaasPaginatedError)
Statements (statements())
Asaas::statements()->list(array $query = []): AsaasPaginatedResult Asaas::statements()->balance(): AsaasResult Asaas::statements()->paymentStatistics(array $query = []): AsaasResult Asaas::statements()->splitStatistics(array $query = []): AsaasResult Asaas::statements()->all(array $filters = []): Generator (yields array|AsaasPaginatedError)
Fiscal Info (fiscalInfo())
Configures fiscal data and digital certificates for NFS-e (Nota Fiscal de Serviço eletrônica) issuance, plus lookup of municipal/federal/national service classification codes.
Asaas::fiscalInfo()->recover(): AsaasResult Asaas::fiscalInfo()->save( array|FiscalInfoRequest $data, string|resource|null $certificateFile = null, ?string $certificateFilename = null, ): AsaasResult Asaas::fiscalInfo()->municipalOptions(): AsaasResult Asaas::fiscalInfo()->services(array $query = []): AsaasPaginatedResult Asaas::fiscalInfo()->federalServiceCodes(array $query = []): AsaasPaginatedResult Asaas::fiscalInfo()->nbsCodes(array $query = []): AsaasPaginatedResult Asaas::fiscalInfo()->operationIndicatorCodes(array $query = []): AsaasPaginatedResult Asaas::fiscalInfo()->taxClassificationCodes(array $query = []): AsaasPaginatedResult Asaas::fiscalInfo()->taxSituationCodes(array $query = []): AsaasPaginatedResult Asaas::fiscalInfo()->configureNationalPortal(bool $enabled): AsaasResult
save() is a multipart/form-data POST. Pass $certificateFile (binary string or file resource) only when uploading an A1 digital certificate; the form-only call works the same way without it. email and simplesNacional are the only required fields on FiscalInfoRequest for a first save; re-saves accept any subset (FiscalInfoRequest is a partial-update DTO — omitted fields never reach the wire).
Server-side defaults.
simplesNacionalandculturalProjectsPromoterdefault totrueon Asaas when the field is omitted from the body. The SDK deliberately omits them on partial updates so a re-save never silently overwrites a value the consumer set in a previous call. If you need stability across Asaas-side default changes, pin the value at every call site instead of relying on the omission semantics.
Date formats
Asaas expects two distinct string formats for date fields. Mismatch (T, Z, timezones) is rejected with HTTP 400 — the SDK does not parse DateTimeInterface for you; pass already-formatted strings.
| DTO field(s) | Format | Example |
|---|---|---|
CreatePaymentRequest::$dueDate, UpdatePaymentRequest::$dueDate |
YYYY-MM-DD |
'2026-04-01' |
ReceivePaymentInCashRequest::$paymentDate |
YYYY-MM-DD |
'2026-04-01' |
AccountRequest::$birthDate, CommercialInfoRequest::$birthDate |
YYYY-MM-DD |
'1985-06-15' |
CreateInvoiceRequest::$effectiveDate, UpdateInvoiceRequest::$effectiveDate |
YYYY-MM-DD |
'2026-04-01' |
TransferRequest::$scheduleDate, PayQrCodeRequest::$scheduleDate |
YYYY-MM-DD |
'2026-04-01' |
BankAccount::$ownerBirthDate (via TransferRequest::$bankAccount) |
YYYY-MM-DD |
'1990-05-15' |
DecodeQrCodeRequest::$expectedPaymentDate |
YYYY-MM-DD |
'2026-04-01' |
AuthorizationRequest::$startDate, AuthorizationRequest::$finishDate |
YYYY-MM-DD |
'2026-04-01' |
CreateBillPaymentRequest::$dueDate |
YYYY-MM-DD |
'2026-04-01' |
CreateAccessTokenRequest::$expirationDate, UpdateAccessTokenRequest::$expirationDate |
YYYY-MM-DD HH:MM:SS |
'2026-12-31 23:59:59' |
StaticQrCodeRequest::$expirationDate |
YYYY-MM-DD HH:MM:SS |
'2026-12-31 23:59:59' |
Available filters per list endpoint
Every list($query = []) method accepts an associative array of filters. Below is the canonical set of filters accepted by the Asaas API, per resource. Pass any combination as $query; the SDK forwards them verbatim (offset / limit are wired by paginate() and rarely need to be set manually).
payments()->list() / leanPayments()->list() — GET /v3/payments and /v3/lean/payments
| Filter | Type | Notes |
|---|---|---|
customer |
string | Customer ID |
customerGroupName |
string | Group name |
billingType |
string | BOLETO, PIX, CREDIT_CARD, UNDEFINED |
status |
string | Payment status |
subscription |
string | Subscription ID |
installment |
string | Installment ID |
externalReference |
string | Integrator-controlled reference |
paymentDate |
string YYYY-MM-DD |
Exact payment date |
invoiceStatus |
string | Linked invoice status |
estimatedCreditDate |
string YYYY-MM-DD |
Expected credit date |
pixQrCodeId |
string | Linked QR code ID |
anticipated |
bool | Already-anticipated flag |
anticipable |
bool | Eligible for anticipation |
dateCreated[ge], dateCreated[le] |
string YYYY-MM-DD |
Created-at range |
paymentDate[ge], paymentDate[le] |
string YYYY-MM-DD |
Payment date range |
estimatedCreditDate[ge], estimatedCreditDate[le] |
string YYYY-MM-DD |
Estimated credit range |
dueDate[ge], dueDate[le] |
string YYYY-MM-DD |
Due-date range |
user |
string | Created-by user ID |
checkoutSession |
string | Checkout session ID |
payments()->listSplitsPaid() / listSplitsReceived()
| Filter | Type | Notes |
|---|---|---|
paymentId |
string | Source payment |
status |
string | Split status |
paymentConfirmedDate[ge], paymentConfirmedDate[le] |
string YYYY-MM-DD |
Confirmation range |
creditDate[ge], creditDate[le] |
string YYYY-MM-DD |
Credit range |
accounts()->list() — GET /v3/accounts
| Filter | Type | Notes |
|---|---|---|
cpfCnpj |
string | CPF/CNPJ exact match |
email |
string | Account email |
name |
string | Account name |
walletId |
string | Wallet ID |
invoices()->list() — GET /v3/invoices
| Filter | Type | Notes |
|---|---|---|
effectiveDate[Ge], effectiveDate[Le] |
string YYYY-MM-DD |
Effective-date range (capitalised in spec) |
payment |
string | Payment ID |
installment |
string | Installment ID |
externalReference |
string | Reference |
status |
string | Invoice status |
customer |
string | Customer ID |
transfers()->list() — GET /v3/transfers
| Filter | Type | Notes |
|---|---|---|
dateCreated[ge], dateCreated[le] |
string YYYY-MM-DD |
Created-at range |
transferDate[ge], transferDate[le] |
string YYYY-MM-DD |
Transfer-date range |
type |
string | PIX, TED, INTERNAL |
pix()->listKeys() — GET /v3/pix/addressKeys
| Filter | Type | Notes |
|---|---|---|
status |
string | Key status |
statusList |
string | Comma-separated status list |
pixTransactions()->list() — GET /v3/pix/transactions
| Filter | Type | Notes |
|---|---|---|
status |
string | Transaction status |
type |
string | Transaction type |
endToEndIdentifier |
string | EndToEndId |
pixTransactions()->listRecurrings() — GET /v3/pix/transactions/recurrings
| Filter | Type | Notes |
|---|---|---|
status |
string | Status |
value |
float | Exact value |
searchText |
string | Free-text search |
pixAutomatic()->listAuthorizations() — GET /v3/pix/automatic/authorizations
| Filter | Type | Notes |
|---|---|---|
status |
string | Authorization status |
customerId |
string | Customer ID |
pixAutomatic()->listPaymentInstructions() — GET /v3/pix/automatic/paymentInstructions
| Filter | Type | Notes |
|---|---|---|
authorizationId |
string | Source authorization |
customerId |
string | Customer ID |
paymentId |
string | Linked payment |
status |
string | Instruction status |
statements()->list() — GET /v3/financialTransactions
| Filter | Type | Notes |
|---|---|---|
startDate, finishDate |
string YYYY-MM-DD |
Inclusive range |
order |
string | asc / desc |
billPayments()->list() and webhooks()->list() only support offset / limit. fiscalInfo() lookup endpoints (federalServiceCodes, nbsCodes, services, taxClassificationCodes, taxSituationCodes, operationIndicatorCodes) accept code / description / codeDescription / taxSituationCode depending on the endpoint.
Custom Connector
All resources depend on the Connector interface (OwnerPro\Asaas\Support\Connector) rather than the concrete AsaasConnector. You can provide your own implementation for testing, logging, caching, or any custom behavior.
The Connector interface defines seven methods: the four HTTP verbs (get, post, put, delete), postMultipart for file uploads, plus paginate and all. The PaginatesResults trait provides default implementations of paginate() and all() built on top of get(), so you only need to implement the five HTTP methods:
use OwnerPro\Asaas\AsaasClient; use OwnerPro\Asaas\Support\Connector; use OwnerPro\Asaas\Support\PaginatesResults; class MyLoggingConnector implements Connector { use PaginatesResults; public function get(string $path, array $query = []): AsaasResult { /* ... */ } public function post(string $path, array $data = []): AsaasResult { /* ... */ } public function put(string $path, array $data = []): AsaasResult { /* ... */ } public function delete(string $path): AsaasResult { /* ... */ } public function postMultipart(string $path, array $data, array $files = []): AsaasResult { /* ... */ } // paginate() and all() are provided by the trait } $client = new AsaasClient(new MyLoggingConnector());
Testing your integration
The SDK ships with a first-class test fake. Use it to assert outgoing requests and stub responses without touching the network.
Construction
use OwnerPro\Asaas\AsaasClient; use Illuminate\Support\Facades\Http; // Empty fake — every unmatched request throws NoMatchingStubException (loud). $asaas = AsaasClient::fake(); // Stub via constructor. $asaas = AsaasClient::fake([ 'payments' => ['id' => 'pay_123', 'status' => 'PENDING'], 'payments/*' => ['id' => 'pay_123'], 'pix/qrCodes/*' => Http::sequence()->push([...])->push([...]), ]); // Or fluent (chainable). $asaas = AsaasClient::fake() ->stub('webhooks', ['id' => 'wh_1']) ->stubError('payments', status: 400, body: ['errors' => [['code' => 'invalid_value']]]) ->stubException('payments/*', new \Illuminate\Http\Client\ConnectionException('timeout'));
Closure stubs receive (Illuminate\Http\Client\Request $request, array $options) from Laravel's HTTP client and may return a Response, a PromiseInterface, or any value Http::response() accepts. The $options parameter can be ignored when not needed:
$asaas->stub('payments/*', function (\Illuminate\Http\Client\Request $r): \GuzzleHttp\Promise\PromiseInterface { return Http::response(['id' => 'pay_'.uniqid()], 201); });
Pass response headers via the fourth stubError() parameter when simulating retry/throttling responses:
$asaas->stubError('payments', status: 429, body: [], headers: ['Retry-After' => '30']);
Path patterns
Stub paths are relative to the API base URL — no https://..., no /v3 prefix. Patterns use Str::is semantics where * is a glob.
A trailing * is appended automatically when the pattern doesn't already end in one, so path-only patterns also match URLs that carry query strings or extra segments. 'payments' matches /payments, /payments?limit=10, /payments/pay_123. Use an explicit suffix when you need a tighter match — e.g. 'payments/*' matches only individual-payment paths, never the index endpoint.
Patterns are prefix-globs, not segment-bounded: 'payments' also matches sibling-prefix paths like /paymentsBook if such an endpoint existed. Stick to either explicit segments ('payments/*') or full paths when adjacent endpoints share a prefix.
When multiple stubs match the same URL, the first registered wins. Construct ordering matters: list specific stubs before broader ones if both could match.
Pagination
['data' => [...]] infers hasMore=false and totalCount=count($data) automatically (and fills sensible defaults for object, limit, offset). Providing either hasMore or totalCount disables inference entirely — supply all paging fields you care about yourself in that case. To drive multi-page flows through ->all():
$asaas->stubPages('payments', [ ['data' => [['id' => 'a']], 'hasMore' => true, 'totalCount' => 2], ['data' => [['id' => 'b']], 'hasMore' => false, 'totalCount' => 2], ]);
Assertions
use Illuminate\Http\Client\Request; $asaas->assertSent('payments', fn (Request $r) => $r['value'] === 100.0); $asaas->assertSent('payments/*', times: 2); // also: exact-N count for a pattern $asaas->assertNotSent('webhooks/*'); $asaas->assertSentCount(3); // total across all patterns $asaas->assertNothingSent(); $asaas->assertSentInOrder([ 'accounts', 'accounts/*', 'accounts/*/accessTokens', ]); // Inspect raw recordings. $asaas->recorded(); // list<array{Request, Response}> $asaas->recorded('payments');
Recordings accumulate for the lifetime of the fake — there is no flush() or forget(). Build a fresh AsaasClient::fake() per test (the recommended pattern) to start with a clean slate.
Loud catch-all
Unmatched requests throw OwnerPro\Asaas\Testing\NoMatchingStubException with the request method, URL, and the list of registered patterns. This catches forgotten stubs early instead of letting tests pass against empty 200 responses.
Laravel container
The service provider binds the real AsaasClient as a singleton and registers an alias so AsaasClientContract resolves to the same instance. Type-hint application code against AsaasClientContract to make the seam swappable:
public function __construct(private readonly AsaasClientContract $asaas) {}
Swap the fake in tests by overriding the contract binding:
$this->app->instance(\OwnerPro\Asaas\Contracts\AsaasClientContract::class, AsaasClient::fake([...]));
FakeAsaasClient does not extend AsaasClient (the production class is final), so code that type-hints the concrete AsaasClient cannot receive the fake from the container. Migrate those constructor signatures to AsaasClientContract to make them testable.
License
MIT