Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Php Iso11649 Laravel Package

kmukku/php-iso11649

Lightweight PHP library for ISO 11649 structured creditor references (RF references). Generate, validate, and format RF numbers with mod-97 checksum handling, plus helpers for converting existing references for use in SEPA credit transfers and payment workflows.

Deep Wiki
Context7

Getting Started

Install and inspect (5 minutes)

composer require kmukku/php-iso11649

There is no service provider, no config file, no facade, and no artisan command. The entire package is essentially one class in src/ plus a PHPUnit suite. Because the project has 12 stars and its last release was April 2020, treat the source as the documentation: open src/CreditorReference.php and tests/ first. It is small enough to read end-to-end, and that is the only reliable way to confirm method names and constructor signatures for the version you pinned.

First use case: turn an invoice number into a valid RF reference

use Kmukku\PhpIso11649\CreditorReference;

// The naked reference (no "RF", no check digits) is what you feed in.
$ref = new CreditorReference('INV201900042');

echo $ref->getReference();           // RF** INV201900042, check digits computed
echo $ref->getFormattedReference();  // same value grouped in fours, for print/pdf

First use case (the other direction): validate what came back from the bank

$incoming = 'RF18 5390 0754 7034'; // as printed on a statement, spaces included

$normalized = preg_replace('/\s+/', '', strtoupper($incoming));

if (CreditorReference::isValid($normalized)) {
    // safe to match against your stored references
}

Confirm in the source whether validation is exposed as a static isValid() or as an instance check — this is the one API detail most likely to differ from what you expect. Build both flows (generate + validate) as a smoke test on day one so a future PHP upgrade fails loudly rather than silently.

Implementation Patterns

1. Never call the library directly from controllers. Wrap it in your own value object so the rest of the app is insulated from a stale, low-activity dependency:

final class CreditorReference
{
    private function __construct(private string $raw, private string $formatted) {}

    public static function fromInvoice(string $invoiceNumber): self
    {
        $iso = new \Kmukku\PhpIso11649\CreditorReference(
            self::sanitize($invoiceNumber)
        );

        return new self($iso->getReference(), $iso->getFormattedReference());
    }

    public static function fromBankPayload(string $raw): ?self
    {
        $candidate = self::sanitize($raw);

        return \Kmukku\PhpIso11649\CreditorReference::isValid($candidate)
            ? new self($candidate, /* format for display */ $candidate)
            : null;
    }

    public function raw(): string { return $this->raw; }
    public function __toString(): string { return $this->formatted; }

    private static function sanitize(string $v): string
    {
        return preg_replace('/[^A-Z0-9]/', '', strtoupper($v));
    }
}

The fromBankPayload returning null instead of throwing is deliberate: inbound bank data is untrusted and "not a creditor reference" is a normal outcome, not an error.

2. Eloquent cast for storage. Store the canonical form (uppercase, no spaces); render the grouped form:

protected $casts = [
    'creditor_reference' => CreditorReferenceCast::class, // implements CastsAttributes
];

Your set() should reject invalid values by throwing — bad references should never reach the DB. Your get() should return the wrapped VO so Blade can echo the formatted version.

3. Form request rule for customer-facing input.

class ValidCreditorReference implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        $normalized = preg_replace('/\s+/', '', strtoupper((string) $value));

        if (strlen($normalized) > 25
            || ! \Kmukku\PhpIso11649\CreditorReference::isValid($normalized)) {
            $fail('The :attribute is not a valid ISO 11649 creditor reference.');
        }
    }
}

Note the explicit length check before delegating — do not assume the library rejects over-length input gracefully.

4. Reconciliation workflow. The typical daily loop is:

  1. Import camt.053/MT940 remittance information into a bank_transactions table.
  2. For each transaction, extract the RF... token from the free-text remittance field with a regex (/\bRF\d{2}[A-Z0-9]{1,21}\b/i).
  3. Normalize and validate.
  4. Match raw() against invoices.creditor_reference.
  5. Anything unmatched goes to a manual review queue — never auto-allocate.

5. Generate deterministically from the invoice. Since the check digits are a pure function of the input, you can regenerate a reference any time from the invoice number. Do that instead of storing an extra column if you're confident the invoice number is immutable; store it if it isn't.

6. Bind as a singleton in a service provider only if you have several consumers; there is no package-provided binding:

$this->app->singleton(ReferenceService::class, fn () => new ReferenceService());

Gotchas and Tips

  • Two different inputs, one similar name. Generation takes the naked reference (INV123), validation takes the full string (RF18INV123). Mixing them up is the single most common mistake and produces check digits that look plausible but fail validation everywhere else. Make your wrapper's method names say which one they take.
  • Check-digit arithmetic needs big integers. ISO 11649 converts the reference to a numeric string (A=10 … Z=35) and computes 98 - (n mod 97). A 21-character reference becomes a ~45-digit number, well beyond PHP's 64-bit int. Verify how the package handles this (bcmath/gmp/string-based modulo) and confirm the relevant extension is installed in production. If it's string-based, fuzz it against your own reference implementation.
  • Case and separators are part of the calculation. Uppercase and strip every non-alphanumeric character before generating or validating. Lowercasing a reference changes the check digits.
  • Length limits: 25 characters total including RF and the two check digits, so the underlying reference is capped at 21. Many banks then truncate the remittance field further (often 140 structured characters) — the standard alone doesn't guarantee your reference survives the payment chain intact.
  • Do not let the underlying reference start with RF. ISO 11649 excludes it to avoid ambiguity; enforce this in your sanitizer rather than relying on the package to complain.
  • Uniqueness is not provided by the algorithm. Two invoices can produce the same reference if their underlying strings collide. For reconciliation you need unique invoice numbers — the checksum only proves integrity, not identity.
  • This library is not a payment toolkit. It validates reference format only. It does not check IBANs, BICs, scheme rules, or whether the beneficiary bank accepts structured remittance data. Pair it with jschaedl/iban or similar and treat reference validation as one step in a larger pipeline.
  • Stale package risk. Last release April 2020, likely built and tested on PHP 7.x. On PHP 8.1+ expect possible deprecation notices (implicit nullable parameters, dynamic properties). Pin the exact version, run the library's own test suite in your CI matrix, and keep your wrapper thin so replacing it with 40 lines of your own bcmod code is a morning's work, not a refactor.
  • Exceptions → validation errors. If the constructor throws on malformed input, catch it at the boundary (form request, job) and convert to a ValidationException or a skipped row. Do not let it bubble to a 500 in a bulk import.
  • Debugging check digits by hand: take your reference, prepend RF00, convert letters, mod 97, subtract from 98. Writing that five-line helper as a test oracle is the fastest way to confirm the package behaves as you expect and to catch normalization bugs.
  • There are no extension points to speak of — one class, no interfaces, no config. If you need batch validation, MT103/camt parsing, or custom length policies, wrap rather than fork; forking a 12-star package means you own it forever.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings