rate / access-paysuite
API library to interact with Access Paysuite
Requires
- php: ^8.3
- ext-json: *
- nesbot/carbon: ^3.0
- saloonphp/cache-plugin: ^3.0
- saloonphp/saloon: ^4.0
Requires (Dev)
- illuminate/console: ^12.12
- illuminate/database: ^12.12
- illuminate/http: ^12.12
- illuminate/routing: ^12.12
- illuminate/support: ^12.12
- laravel/pint: ^1.0
- phpstan/phpstan: ^2.2.2
- phpunit/phpunit: ~11
- vlucas/phpdotenv: ^5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
A PHP library for the Access Paysuite Direct Debit Management API v3, built on Saloon.
https://api-docs-ddcms-v3.accesspaysuite.com/
https://github.com/AccessPaysuite/DDCMS-API-v3
Requirements
- PHP 8.3+
- Laravel 10+ (for the service provider and caching; optional for standalone use)
Installation
composer require rate/access-paysuite
Laravel
The service provider is auto-discovered. Publish the config file:
php artisan vendor:publish --provider="Rate\AccessPaysuite\ServiceProvider\AccessPaysuiteServiceProvider"
Add your credentials to .env:
ACCESS_PAYSUITE_CLIENT_CODE=APICODE ACCESS_PAYSUITE_API_KEY=your-api-key # Set to true to use the playpen (sandbox) environment instead of production ACCESS_PAYSUITE_PLAYPEN=false
Publish the webhook-log migration and run it:
php artisan vendor:publish --tag=access-paysuite-migrations php artisan migrate
Point every DDCMS callback at the package's built-in webhook route in one go:
php artisan paysuite:callback:set all
This registers the route named access-paysuite.webhook (default path /paysuite/webhook) as the callback URL for all five entities — customer, contract, payment, bulkpayment, and schedule.
Basic Usage
With Laravel (dependency injection)
use Rate\AccessPaysuite\AccessPaysuiteConnector; use Rate\AccessPaysuite\AccessPaysuiteService; class CustomerController { public function __construct( private readonly AccessPaysuiteConnector $connector, ) {} public function show(string $customerId): Customer { $service = new AccessPaysuiteService($this->connector); return $service->getCustomer($customerId); } }
Without Laravel (standalone)
use Rate\AccessPaysuite\AccessPaysuiteConnector; use Rate\AccessPaysuite\Enums\Environment; use Rate\AccessPaysuite\AccessPaysuiteService; $connector = new AccessPaysuiteConnector( environment: Environment::PRODUCTION, // or Environment::PLAYPEN for testing clientCode: 'APICODE', apiKey: 'your-api-key', ); $service = new AccessPaysuiteService($connector);
Caching
Nothing is cached by default. GET responses can be cached for one hour in Laravel's default cache store, but only when you ask for it on that call:
// A copy of the service that caches; $service itself stays uncached $customer = $service->withCaching()->getCustomer($customerId); // On a single request, through the connector $response = $connector->send((new GetCustomer($customerId))->enableCaching()); $response->isCached(); // true when served from the cache // Refetch and replace the cached response $connector->send((new GetCustomer($customerId))->enableCaching()->invalidateCache());
Only requests extending Rate\AccessPaysuite\Requests\CacheableRequest can be cached. GetPaymentDates and GetCallbackUrl never are, because their answers must always be current. Outside Laravel the connector has no cache store, so nothing is cached unless you call setLaravelCacheDriver().
Upgrading from 1.x: the
cache-requestsconfig key andACCESS_PAYSUITE_CACHEenv var are gone, and the connector no longer caches every GET. UsewithCaching()where you want cached responses.
Environments
The environment is configured via a boolean in config/access-paysuite.php (or the ACCESS_PAYSUITE_PLAYPEN env var). Production is the default.
| Config | Env var | URL |
|---|---|---|
'playpen' => false |
ACCESS_PAYSUITE_PLAYPEN=false |
https://ddcms.accesspaysuite.com/api/v3 |
'playpen' => true |
ACCESS_PAYSUITE_PLAYPEN=true |
https://playpen.accesspaysuite.com/api/v3 |
When constructing the connector directly (without Laravel), pass the environment explicitly:
use Rate\AccessPaysuite\Enums\Environment; Environment::PLAYPEN; // https://playpen.accesspaysuite.com/api/v3 Environment::PRODUCTION; // https://ddcms.accesspaysuite.com/api/v3
Customer Management
use Rate\AccessPaysuite\Enums\CustomerTitle; // Create a customer $response = $service->createCustomer( title: CustomerTitle::MR, customerRef: 'CUST-001', // unique per client surname: 'Smith', postCode: 'SW1A 1AA', accountNumber: '12345678', // exactly 8 digits bankSortCode: '112233', // exactly 6 digits accountHolderName: 'John Smith', line1: '1 Test Street', line2: 'London', firstName: 'John', email: 'john@example.com', ); // Retrieve a customer $customer = $service->getCustomer('customer-uuid'); echo $customer->FirstName; // 'John' echo $customer->BankDetail->AccountNumber; // '12345678' echo $customer->AddressDetail->PostCode; // 'SW1A 1AA' // Search customers $collection = $service->getCustomers( pageSize: 20, pageNumber: 1, includePagingDetail: true, surname: 'Smith', ); echo count($collection->items); // number of results echo $collection->paging?->TotalRecords; // Update a customer (partial update — only supplied fields are changed) $service->updateCustomer('customer-uuid', email: 'new@example.com', firstName: 'Jonathan');
Contract Management
use Carbon\Carbon; use Rate\AccessPaysuite\Enums\AtTheEnd; use Rate\AccessPaysuite\Enums\TerminationType; // Get available schedules first $schedules = $service->getAvailableSchedules(); // Find the earliest valid start date $dates = $service->getPaymentDates()->json(); // Create a contract $response = $service->createContract( customerId: 'customer-uuid', terminationType: TerminationType::UNTIL_FURTHER_NOTICE, atTheEnd: AtTheEnd::EXPIRE, isGiftAid: false, start: Carbon::parse($dates['EarliestFirstPaymentDate']), scheduleName: 'Monthly', amount: 25.00, paymentDayInMonth: 1, ); // List contracts for a customer $contracts = $service->getContracts('customer-uuid'); foreach ($contracts as $contract) { echo $contract->Status->value; // 'Active' echo $contract->Amount; // 25.0 }
Payment Management
// Add an ad-hoc payment (minimum 5 working days in the future) $service->addPayment( contractId: 'contract-uuid', amount: 50.00, date: Carbon::now()->addWeekdays(6), comment: 'Additional contribution', ); // List recent payments $payments = $service->getPayments('contract-uuid', rows: 10); foreach ($payments as $payment) { echo $payment->Status->value; // 'Pending' echo $payment->Date->format('Y-m-d'); } // Update a payment (before BACS submission) $service->updatePayment('contract-uuid', 'payment-uuid', 'Corrected amount', 30.00, Carbon::parse('2025-06-01')); // Delete a payment (before BACS submission) $service->deletePayment('contract-uuid', 'payment-uuid', 'Cancelled by customer');
Bulk Payments
$service->bulkAddPayments([ [ 'contract' => 'contract-uuid-1', 'amount' => 25.00, 'date' => Carbon::now()->addWeekdays(6), 'comment' => 'Monthly collection', ], [ 'contract' => 'contract-uuid-2', 'amount' => 50.00, 'date' => Carbon::now()->addWeekdays(6), ], ]);
Contract Status Changes
$service->cancelContract('contract-uuid'); $service->reactivateContract('contract-uuid'); $service->archiveContract('contract-uuid'); // irreversible use Rate\AccessPaysuite\Enums\AtTheEnd; use Rate\AccessPaysuite\Enums\TerminationType; $service->restartContract( contractId: 'contract-uuid', terminationType: TerminationType::UNTIL_FURTHER_NOTICE, atTheEnd: AtTheEnd::EXPIRE, );
Contract Amendments
// Change the monthly payment day $service->changeMonthlyDate('contract-uuid', monthDay: '15', comment: 'Customer request', patchNextPayment: false); // Change the amount $service->changeContractAmount('contract-uuid', amount: 30.00, comment: 'Price increase');
Patches (Temporary Modifications)
use Carbon\Carbon; $from = Carbon::parse('2025-07-01'); $to = Carbon::parse('2025-09-30'); // Freeze collections for a period $service->addFreezePatch('contract-uuid', $from, $to, comment: 'Customer on holiday'); // Skip collections (contract schedule adjusts) $service->addSkipPatch('contract-uuid', $from, $to, comment: 'Waived for 3 months'); // Change amount for a period $service->addChangeAmountPatch('contract-uuid', $from, $to, amount: 10.00, comment: 'Reduced rate'); // List all patches $patches = $service->getPatches('contract-uuid');
Questions & Answers
use Rate\AccessPaysuite\Enums\QuestionType; // Create a custom question $service->createQuestion( label: 'How did you hear about us?', type: QuestionType::LIST, isMandatory: false, options: 'Website,Social Media,Word of Mouth', ); // Add an answer for a customer $service->addAnswer('customer-uuid', 'question-uuid', 'Website');
Callback URLs
use Rate\AccessPaysuite\Enums\CallbackEntity; // Register a webhook for payment BACS callbacks $service->setCallbackUrl(CallbackEntity::PAYMENT, 'https://your-app.com/webhooks/paysuite'); // Remove a callback $service->deleteCallbackUrl(CallbackEntity::PAYMENT);
Laravel Artisan Commands
When the package is installed in a Laravel application, three Artisan commands are available for managing callback URLs from the terminal.
List all callback URLs
Fetches all five entity callback URLs concurrently and displays them in a table:
php artisan paysuite:callback:list
+-------------+----------------------------------+
| Entity | Callback URL |
+-------------+----------------------------------+
| bulkpayment | https://your-app.com/webhooks/bp |
| customer | — |
| contract | — |
| payment | https://your-app.com/webhooks/py |
| schedule | — |
+-------------+----------------------------------+
Set a callback URL
php artisan paysuite:callback:set {entity} {url}
php artisan paysuite:callback:set payment https://your-app.com/webhooks/payment
Delete a callback URL
php artisan paysuite:callback:delete {entity}
php artisan paysuite:callback:delete payment
Prompts for confirmation before deleting. Pass --no-interaction to skip the prompt (the command will safely no-op when non-interactive).
Valid entity values for all three commands: bulkpayment, customer, contract, payment, schedule.
Using Requests Directly
For fine-grained control, create request objects and send them via the connector:
use Rate\AccessPaysuite\Requests\Customer\GetCustomer; $response = $connector->send(new GetCustomer('customer-uuid')); $customer = $response->dto(); // typed Customer model $raw = $response->json(); // raw array
Direct Debit Process Overview
- Create a Customer with bank details and address.
- Call
GET /paymentdateto find the earliest valid start date. - Create a Contract referencing a schedule (from
GET /schedules). - The start date must be at least the First Payment Delay (default: 10 working days) in the future.
- For ad-hoc payments, the date must be at least the Subsequent Payment Delay (default: 5 working days) ahead.
- Payments are submitted to BACS ~3 working days before their collection date; they cannot be modified after submission.
- Final payment status is available ~3 working days after the collection date.
Running Tests
# Unit tests (with mock responses — no network calls) composer test # Static analysis composer phpstan # Code style check composer check-style # All quality checks composer qa # Live integration tests against the playpen environment ./vendor/bin/phpunit tests/LiveTest.php
Configure live test credentials in tests/.env (copy from tests/.env.example).
Support
For API support: support@accesspaysuite.com