PHPackages                             ownerpro/asaas-php-sdk - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Payment Processing](/categories/payments)
4. /
5. ownerpro/asaas-php-sdk

ActiveLibrary[Payment Processing](/categories/payments)

ownerpro/asaas-php-sdk
======================

Clean PHP SDK for the Asaas payment platform API

v2.0.0(2mo ago)019MITPHPPHP ^8.3CI passing

Since Apr 2Pushed 2mo agoCompare

[ Source](https://github.com/OwnerPro-Software/asaas-php-sdk)[ Packagist](https://packagist.org/packages/ownerpro/asaas-php-sdk)[ Docs](https://github.com/OwnerPro-Software/asaas-php-sdk)[ RSS](/packages/ownerpro-asaas-php-sdk/feed)WikiDiscussions main Synced 3w ago

READMEChangelogDependencies (11)Versions (10)Used By (0)

Asaas PHP SDK
=============

[](#asaas-php-sdk)

Clean PHP SDK for the [Asaas](https://www.asaas.com/) payment platform API with typed request DTOs and result-based error handling.

Requirements
------------

[](#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
------------

[](#installation)

```
composer require ownerpro/asaas-php-sdk
```

The package auto-discovers its ServiceProvider and Facade.

Configuration
-------------

[](#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_KEY` is required. The `AsaasServiceProvider` throws `RuntimeException` the first time `AsaasClient` is 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
-----

[](#usage)

### Via Facade

[](#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

[](#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)

[](#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

[](#multi-tenant)

This SDK supports two multi-tenant patterns:

1. **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.
2. **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()`)](#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
---------------

[](#result-handling)

All resource methods return `AsaasResult` or `AsaasPaginatedResult`.

```
$result = Asaas::payments()->create([...]);

if ($result->success) {
    $payment = $result->data;       // array
    echo $payment['id'];
    echo $payment['status'];
} else {
    $errors = $result->errors;      // array of error details
}
```

> **204 No Content endpoints.** `accounts()->deleteAccessToken()` and `webhooks()->removeBackoff()` return HTTP 204 with an empty body. `$result->success` is `true` but `$result->data` is `[]` (empty array). Check `$result->success` — never `$result->data` — to decide if the call worked.

### Throwing on Failure

[](#throwing-on-failure)

```
// Throws AsaasRequestException on failure, returns self on success
$result = Asaas::payments()->find('pay_abc123')->orFail();
$payment = $result->data;
```

### Error Handling

[](#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

[](#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

[](#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
-----

[](#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

[](#available-enums)

EnumValues`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
--------------------------------

[](#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

[](#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\*`):

DTOUsed 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

[](#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

[](#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

[](#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
----------

[](#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

[](#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 API
- `response` — the raw HTTP response (null for connection errors)
- `offset` — the page offset that failed
- `limit` — 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
---------

[](#resources)

> **Response shape.** Every resource method returns `AsaasResult` with `$data`typed as `array` — 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 under `specs/domains/.json` or the public Asaas docs at . Nested sub-objects observed on the response side (`creditCard`, `discount`, `fine`, `interest`, `callback`, `escrow`, `chargeback`, etc.) follow the same `array` rule — the SDK's request-side DTOs in `OwnerPro\Asaas\Support\DTO\*` are reference-only for field names, not response value objects.

### Payments (`payments()`)

[](#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()`)

[](#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)

[](#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()`)

[](#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()`)

[](#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()`)

[](#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()`)

[](#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()`)

[](#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 `interrupted` on create.** The Asaas API rejects `POST /v3/webhooks` with HTTP 400 (`"O parâmetro poolInterrupted deve ser informado"` — `poolInterrupted` is the Asaas validator's internal variable name; the documented and accepted JSON field is `interrupted`) when the field is absent. `CreateWebhookRequest` defaults `interrupted` to `false` so the request is always accepted. `UpdateWebhookRequest::interrupted` follows the usual partial-update pattern (`Missing` = not sent).

### Webhook Verification

[](#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()`)

[](#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()`)

[](#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

[](#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.** `UpdateAccessTokenRequest` makes `name`, `enabled` and `expirationDate` mandatory in the constructor — the Asaas `PUT /accounts/{id}/accessTokens/{tokenId}` rejects partial updates. `CreateAccessTokenRequest` keeps every field optional because `POST` accepts 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)

[](#permission--endpoint-mapping-heuristic)

> **Heuristic mapping — not normative.** Asaas does not publish an official permission ↔ endpoint correspondence: the OpenAPI spec declares `security` only at the top of each domain file (no per-operation `security` or `x-permission` extension), 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 returns `401/403` on an endpoint listed here.

PermissionEndpoints (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()`)

[](#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 the `Connector` directly if you need to reach a sandbox-only path in your test harness.

#### Subaccount onboarding (white label)

[](#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()`)

[](#credit-cards-creditcards)

```
Asaas::creditCards()->tokenize(array|CreditCardRequest $data): AsaasResult
Asaas::creditCards()->getPreAuthorizationConfig(): AsaasResult
Asaas::creditCards()->setPreAuthorizationConfig(array|PreAuthConfigRequest $data): AsaasResult
```

### Bill Payments (`billPayments()`)

[](#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()`)

[](#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()`)

[](#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.** `simplesNacional` and `culturalProjectsPromoter` default to `true` on 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
------------

[](#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)FormatExample`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
-----------------------------------

[](#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`

[](#payments-list--leanpayments-list--get-v3payments-and-v3leanpayments)

FilterTypeNotes`customer`stringCustomer ID`customerGroupName`stringGroup name`billingType`string`BOLETO`, `PIX`, `CREDIT_CARD`, `UNDEFINED``status`stringPayment status`subscription`stringSubscription ID`installment`stringInstallment ID`externalReference`stringIntegrator-controlled reference`paymentDate`string `YYYY-MM-DD`Exact payment date`invoiceStatus`stringLinked invoice status`estimatedCreditDate`string `YYYY-MM-DD`Expected credit date`pixQrCodeId`stringLinked QR code ID`anticipated`boolAlready-anticipated flag`anticipable`boolEligible 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`stringCreated-by user ID`checkoutSession`stringCheckout session ID### `payments()->listSplitsPaid()` / `listSplitsReceived()`

[](#payments-listsplitspaid--listsplitsreceived)

FilterTypeNotes`paymentId`stringSource payment`status`stringSplit 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`

[](#accounts-list--get-v3accounts)

FilterTypeNotes`cpfCnpj`stringCPF/CNPJ exact match`email`stringAccount email`name`stringAccount name`walletId`stringWallet ID### `invoices()->list()` — `GET /v3/invoices`

[](#invoices-list--get-v3invoices)

FilterTypeNotes`effectiveDate[Ge]`, `effectiveDate[Le]`string `YYYY-MM-DD`Effective-date range (capitalised in spec)`payment`stringPayment ID`installment`stringInstallment ID`externalReference`stringReference`status`stringInvoice status`customer`stringCustomer ID### `transfers()->list()` — `GET /v3/transfers`

[](#transfers-list--get-v3transfers)

FilterTypeNotes`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`

[](#pix-listkeys--get-v3pixaddresskeys)

FilterTypeNotes`status`stringKey status`statusList`stringComma-separated status list### `pixTransactions()->list()` — `GET /v3/pix/transactions`

[](#pixtransactions-list--get-v3pixtransactions)

FilterTypeNotes`status`stringTransaction status`type`stringTransaction type`endToEndIdentifier`stringEndToEndId### `pixTransactions()->listRecurrings()` — `GET /v3/pix/transactions/recurrings`

[](#pixtransactions-listrecurrings--get-v3pixtransactionsrecurrings)

FilterTypeNotes`status`stringStatus`value`floatExact value`searchText`stringFree-text search### `pixAutomatic()->listAuthorizations()` — `GET /v3/pix/automatic/authorizations`

[](#pixautomatic-listauthorizations--get-v3pixautomaticauthorizations)

FilterTypeNotes`status`stringAuthorization status`customerId`stringCustomer ID### `pixAutomatic()->listPaymentInstructions()` — `GET /v3/pix/automatic/paymentInstructions`

[](#pixautomatic-listpaymentinstructions--get-v3pixautomaticpaymentinstructions)

FilterTypeNotes`authorizationId`stringSource authorization`customerId`stringCustomer ID`paymentId`stringLinked payment`status`stringInstruction status### `statements()->list()` — `GET /v3/financialTransactions`

[](#statements-list--get-v3financialtransactions)

FilterTypeNotes`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
----------------

[](#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
------------------------

[](#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

[](#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

[](#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

[](#pagination-1)

`['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

[](#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
$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

[](#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

[](#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
-------

[](#license)

MIT

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance86

Actively maintained with recent releases

Popularity7

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity55

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

7

Last Release

72d ago

Major Versions

v1.4.0 → v2.0.02026-05-13

### Community

Maintainers

![](https://www.gravatar.com/avatar/df52d610b41e6ab418dfdb8a4b7607ebdf39dc129d60b452788827267b123985?d=identicon)[jonathanpmartins](/maintainers/jonathanpmartins)

---

Top Contributors

[![jonathanpmartins](https://avatars.githubusercontent.com/u/6137992?v=4)](https://github.com/jonathanpmartins "jonathanpmartins (134 commits)")

---

Tags

laravelsdkbillingpaymentboletopayment gatewaybrazilcredit-cardpixAsaasbrasilasaas-sdk

###  Code Quality

TestsPest

Static AnalysisPsalm, Rector

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/ownerpro-asaas-php-sdk/health.svg)

```
[![Health](https://phpackages.com/badges/ownerpro-asaas-php-sdk/health.svg)](https://phpackages.com/packages/ownerpro-asaas-php-sdk)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3345.3M347](/packages/psalm-plugin-laravel)[api-platform/laravel

API Platform support for Laravel

58174.6k17](/packages/api-platform-laravel)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
