PHPackages                             quellabs/canvas-payments-rabosmartpay - 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. quellabs/canvas-payments-rabosmartpay

ActiveLibrary[Payment Processing](/categories/payments)

quellabs/canvas-payments-rabosmartpay
=====================================

Rabo Smart Pay (OmniKassa v2) payment gateway integration for the Canvas PHP framework

1.0.4(1mo ago)00MITPHPPHP ^8.2

Since Mar 21Pushed 2w agoCompare

[ Source](https://github.com/quellabs/canvas-payments-rabosmartpay)[ Packagist](https://packagist.org/packages/quellabs/canvas-payments-rabosmartpay)[ RSS](/packages/quellabs-canvas-payments-rabosmartpay/feed)WikiDiscussions main Synced 3w ago

READMEChangelogDependencies (12)Versions (6)Used By (0)

Rabo Smart Pay Payment Provider
===============================

[](#rabo-smart-pay-payment-provider)

A Rabo Smart Pay (OmniKassa v2) payment provider for the Canvas framework. Part of the Canvas payments ecosystem.

Installation
------------

[](#installation)

```
composer require quellabs/canvas-payments-rabosmartpay
```

Architecture
------------

[](#architecture)

This package sits between the Rabo Smart Pay API and your application. Your application only ever touches the contracts layer — it never depends on this package directly. `PaymentRouter` (from `quellabs/canvas-payments`) discovers this package automatically via composer metadata and routes payment calls to it.

```
Your Application
      │
      ▼
PaymentRouter                   (quellabs/canvas-payments — discovery + routing)
      │
      ▼
PaymentInterface                (quellabs/canvas-payments-contracts)
      │
      ▼
Rabo Smart Pay                  (this package — implements the interface)
      │
      ▼
RaboSmartPayGateway             (raw Rabo Smart Pay OmniKassa API calls)

```

Exchange processing is decoupled from your application via signals. When Rabo Smart Pay calls the webhook URL, the package emits a `payment_exchange` signal carrying a `PaymentState`. Your application listens for that signal and handles it.

### Payment flow

[](#payment-flow)

```
1. Refresh call     GET  /gatekeeper/refresh          → access token
2. Order announce   POST /order/server/api/v2/order   → redirectUrl + omnikassaOrderId
3. Shopper redirected to Rabo Smart Pay hosted checkout
4. Shopper completes payment and is redirected to merchantReturnURL
5. Rabo Smart Pay POSTs webhook notification → your handleWebhook endpoint
6. Status Pull      GET  /order/server/api/events/results/merchant.order.status.changed

```

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

[](#configuration)

Create `config/rabosmartpay.php` in your Canvas application:

```
return [
    'refresh_token'     => '',      // Long-lived token from Rabo Smart Pay dashboard
    'signing_key'       => '',      // Base64-encoded signing key from dashboard
    'test_mode'         => false,   // true → sandbox, false → production
    'return_url'        => 'https://example.com/order/thankyou',
    'cancel_return_url' => 'https://example.com/order/cancelled',
    'default_currency'  => 'EUR',
    'language'          => 'NL',
    'skip_result_page'  => true,
];
```

KeyRequiredDescription`refresh_token`YesLong-lived token from the Rabo Smart Pay dashboard (webshop settings)`signing_key`YesBase64-encoded HMAC signing key from the Rabo Smart Pay dashboard`test_mode`NoRoutes to sandbox (`betalen.rabobank.nl`) when `true`. Defaults to `false``return_url`YesShopper is redirected here after a completed or pending payment`cancel_return_url`YesShopper is redirected here after a cancelled or failed payment`default_currency`NoISO 4217 currency code. Only `EUR` is supported. Defaults to `EUR``language`NoLanguage for the hosted checkout page. `NL`, `EN`, `FR`, `DE`. Defaults to `NL``skip_result_page`NoSkip Rabo Smart Pay's own confirmation page, redirect directly to `return_url`. Default `true`Important: storing transactionId and orderReference
---------------------------------------------------

[](#important-storing-transactionid-and-orderreference)

After initiating a payment, `InitiateResult::$transactionId` holds Rabo Smart Pay's `omnikassaOrderId` UUID. Store this against your order — it is required for refund calls.

`InitiateResult::$metadata['orderReference']` holds the generated `merchantOrderId`. Store this too — it is what Rabo Smart Pay echoes back in the return URL `?order_id=` parameter and in the Status Pull response, allowing you to correlate those callbacks to your order.

Supported payment methods
-------------------------

[](#supported-payment-methods)

Module nameBrand stringMethod`rabo_ideal``IDEAL`iDEAL 2.0 (NL)`rabo_bancontact``BANCONTACT`Bancontact (BE)`rabo_mastercard``MASTERCARD`Mastercard`rabo_visa``VISA`Visa`rabo_maestro``MAESTRO`Maestro`rabo_vpay``V_PAY`V PAY`rabo_cards``CARDS`All card methods combined`rabo_applepay``APPLE_PAY`Apple Pay`rabo_paypal``PAYPAL`PayPal (contract-dependent)Payment methods must be activated for your webshop in the Rabo Smart Pay dashboard before use.

Usage
-----

[](#usage)

### Initiating a payment

[](#initiating-a-payment)

```
use Quellabs\Payments\Contracts\PaymentInterface;
use Quellabs\Canvas\Controllers\BaseController;
use Quellabs\Payments\Contracts\PaymentRequest;
use Quellabs\Payments\Contracts\PaymentInitiationException;

class CheckoutController extends BaseController {

    public function __construct(private PaymentInterface $router) {}

    /**
     * @Route("...")
     */
    public function checkout(): Response {
        $request = new PaymentRequest(
            paymentModule: 'rabo_ideal',
            amount:        999,   // in minor units — €9.99
            currency:      'EUR',
            description:   'Order #12345',
        );

        try {
            $result = $this->router->initiate($request);

            // Store both identifiers against your order.
            // transactionId is the omnikassaOrderId UUID — needed for refunds.
            // metadata['orderReference'] is the generated merchantOrderId — needed to
            // correlate the return URL callback and webhook Status Pull to your order.
            $this->orderService->setTransactionId($orderId, $result->transactionId);
            $this->orderService->setOrderReference($orderId, $result->metadata['orderReference']);

            return $this->redirect($result->redirectUrl);
        } catch (PaymentInitiationException $e) {
            // handle error
        }
    }
}
```

### Handling refunds

[](#handling-refunds)

```
use Quellabs\Payments\Contracts\RefundRequest;
use Quellabs\Payments\Contracts\PaymentRefundException;

// Full refund — omit amount
$request = new RefundRequest(
    paymentReference: $state->transactionId,   // omnikassaOrderId from payment_exchange signal
    paymentModule:    'rabo_ideal',
    amount:           null,                    // null = full refund
    currency:         'EUR',
    description:      'Full refund for order #12345',
);

// Partial refund — provide amount in minor units (cents)
$request = new RefundRequest(
    paymentReference: $state->transactionId,
    paymentModule:    'rabo_ideal',
    amount:           500,                     // €5.00
    currency:         'EUR',
    description:      'Partial refund for order #12345',
);

try {
    $result = $this->router->refund($request);
    echo $result->refundId;
} catch (PaymentRefundException $e) {
    // handle error
}
```

### Listening for payment state changes

[](#listening-for-payment-state-changes)

```
use Quellabs\Canvas\Annotations\ListenTo;
use Quellabs\Payments\Contracts\PaymentState;
use Quellabs\Payments\Contracts\PaymentStatus;

class OrderService {

    /**
     * @ListenTo("payment_exchange")
     */
    public function onPaymentExchange(PaymentState $state): void {
        match ($state->state) {
            PaymentStatus::Paid     => $this->markPaid($state->transactionId, $state->valuePaid),
            PaymentStatus::Canceled => $this->markCanceled($state->transactionId),
            PaymentStatus::Failed   => $this->markFailed($state->transactionId),
            PaymentStatus::Pending  => null, // iDEAL confirmation still in progress — wait for webhook
            default                 => null,
        };
    }
}
```

Webhook setup
-------------

[](#webhook-setup)

Configure your webhook URL in the Rabo Smart Pay dashboard under your webshop settings:

```
https://example.com/webhooks/rabosmartpay

```

The endpoint must be publicly reachable. Rabo Smart Pay does **not** retry failed webhook deliveries — return HTTP 200 to acknowledge receipt. Errors are logged server-side.

### Webhook flow

[](#webhook-flow)

1. Rabo Smart Pay POSTs a notification JSON containing an `authentication` token.
2. This package verifies the HMAC-SHA512 signature on the notification body.
3. The `authentication` token is used to perform a Status Pull call.
4. The Status Pull may return multiple order results and indicate `moreOrderResultsAvailable`.
5. Each order result is mapped to a `PaymentState` and emitted via the `payment_exchange` signal.

Return URL parameters
---------------------

[](#return-url-parameters)

Rabo Smart Pay appends the following to your `return_url`:

ParameterDescription`order_id`The generated `merchantOrderId` stored in `InitiateResult::$metadata['orderReference']``status``IN_PROGRESS`, `COMPLETED`, `CANCELLED`, `EXPIRED`, or `FAILURE``signature`HMAC-SHA512 hex signature of `"{order_id},{status}"`The return URL handler verifies the signature and redirects the shopper — no signal is emitted here. The authoritative payment state is delivered exclusively via the webhook. For `IN_PROGRESS` statuses (common with iDEAL 2.0), the shopper is redirected to the success page and the final status arrives via the webhook.

Missed webhooks and reconciliation
----------------------------------

[](#missed-webhooks-and-reconciliation)

Rabo Smart Pay does not retry failed webhook deliveries. If your server is temporarily unreachable, or if the webhook notification is lost for any reason, your order will remain in a pending state indefinitely.

To handle this, implement a reconciliation job that periodically checks orders that have been in a pending state beyond a reasonable threshold (e.g. 15 minutes). For each such order, call `exchange()` directly using the stored `transactionId` (the `omnikassaOrderId` UUID from `InitiateResult`):

```
// Reconciliation job — run periodically for orders stuck in pending state
$state = $this->router->exchange('rabo_ideal', $order->transactionId);
$this->onPaymentExchange($state);
```

Rabo Smart Pay guarantees order status data is available for at least 24 hours after an order reaches a final state. Note that Rabo Smart Pay explicitly prohibits polling this endpoint — use it only as a fallback for orders where no webhook was received.

License
-------

[](#license)

MIT

###  Health Score

39

—

LowBetter than 85% of packages

Maintenance95

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity50

Maturing project, gaining track record

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 ~16 days

Total

5

Last Release

34d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/57e4ab872b3e37536367f2d26b192df3d3bb6a6a1cebec9a104d14a6d2ffe157?d=identicon)[noescom](/maintainers/noescom)

---

Tags

paymentsidealrabobankcanvaspayment gatewaynetherlandsomnikassacanvas-paymentsrabo-smart-pay

### Embed Badge

![Health badge](/badges/quellabs-canvas-payments-rabosmartpay/health.svg)

```
[![Health](https://phpackages.com/badges/quellabs-canvas-payments-rabosmartpay/health.svg)](https://phpackages.com/packages/quellabs-canvas-payments-rabosmartpay)
```

###  Alternatives

[omnipay/rabobank

Rabobank Omnikassa driver for the Omnipay payment processing library

1463.6k](/packages/omnipay-rabobank)

PHPackages © 2026

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