Search by

rate / access-paysuite

kpiercers

API library to interact with Access Paysuite

Package info

github.com/ratespecial/access-paysuite

pkg:composer/rate/access-paysuite

Statistics

Installs: 619

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.5.0 2026-11-04 22:01 UTC

This package is auto-updated.

Last update: 2026-11-05 21:09:52 UTC


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-requests config key and ACCESS_PAYSUITE_CACHE env var are gone, and the connector no longer caches every GET. Use withCaching() 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

  1. Create a Customer with bank details and address.
  2. Call GET /paymentdate to find the earliest valid start date.
  3. Create a Contract referencing a schedule (from GET /schedules).
  4. The start date must be at least the First Payment Delay (default: 10 working days) in the future.
  5. For ad-hoc payments, the date must be at least the Subsequent Payment Delay (default: 5 working days) ahead.
  6. Payments are submitted to BACS ~3 working days before their collection date; they cannot be modified after submission.
  7. 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