← Back to Implementation

Pattern B. Drupal Plus SaaS MLM. The REST Plus Webhook Bridge.

Drupal Commerce stays for storefront. A SaaS MLM platform handles members, commissions, and genealogy. About 300 lines of contributed module code on the Drupal side.

Why Pattern B is the most-recommended choice

For Drupal MLMs at $2M to $20M GMV, the bridge pattern keeps each layer doing what it's good at. Drupal Commerce handles the storefront, content, editorial workflow, and customer-facing pages. The SaaS MLM platform handles members, commissions, genealogy, KYC, and payouts. A REST plus webhook bridge synchronizes the two without forcing either side to scale around the other's constraints.

The architectural goal is symmetric: Drupal does not try to calculate commissions; the SaaS does not try to render product pages; neither side breaks when the other releases an update. In production, this pattern works through $50M+ GMV with proportionally larger Drupal hosting on the front side and no architectural changes to the bridge itself.

The data flow

Drupal Commerce SaaS MLM (CloudMLM or Business MLM)
─────────────── ─────────────────────────────────────
Storefront ◄──── /api/members
Checkout ────► /api/orders
Order completion ────► webhook handler in Drupal updates user meta
 ◄──── /webhooks/commission-calculated
 ◄──── /webhooks/rank-advanced

Drupal sends member registrations and order completions to the SaaS. The SaaS calculates commissions, runs payout cycles, manages KYC, and posts results back as signed webhooks that a Drupal route handles.

The Drupal-side bridge

A small contributed module, roughly 300 lines, handles both directions. The order-completion bridge:

// mlm_bridge.module
function mlm_bridge_commerce_order_postsave(OrderInterface $order) {
 if ($order->getState()->getId() !== 'completed') return;

 $client = \Drupal::httpClient();
 $client->post(\Drupal::config('mlm_bridge.settings')->get('api_url') . '/api/orders', [
 'headers' => [
 'Authorization' => 'Bearer ' . \Drupal::config('mlm_bridge.settings')->get('token'),
 'X-Idempotency-Key' => 'drupal-order-' . $order->id(),
 ],
 'json' => [
 'external_id' => (string) $order->id(),
 'distributor_external_id' => (string) $order->getCustomerId(),
 'total' => $order->getTotalPrice()->getNumber(),
 'currency' => $order->getTotalPrice()->getCurrencyCode(),
 'completed_at' => $order->getCompletedTime() ? date('c', $order->getCompletedTime()) : null,
 ],
 'timeout' => 8,
 ]);
}

The webhook receiver

A REST resource registered through Drupal's routing handles inbound events from the SaaS. Signature verification first, business logic second.

# mlm_bridge.routing.yml
mlm_bridge.webhook:
 path: '/webhooks/mlm'
 defaults:
 _controller: '\Drupal\mlm_bridge\Controller\WebhookController::receive'
 _title: 'MLM Webhook'
 methods: [POST]
 requirements:
 _access: 'TRUE'
// src/Controller/WebhookController.php
public function receive(Request $request) {
 $payload = $request->getContent();
 $signature = $request->headers->get('X-MLM-Signature');
 $expected = hash_hmac('sha256', $payload, $this->config->get('webhook_secret'));

 if (!hash_equals($expected, $signature)) {
 throw new AccessDeniedHttpException('Invalid webhook signature.');
 }

 $event = json_decode($payload, true);

 switch ($event['type']) {
 case 'commission.calculated':
 $this->updateUserCommissionMeta($event['data']);
 break;
 case 'rank.advanced':
 $this->dispatchRankAdvancedNotification($event['data']);
 break;
 }

 return new JsonResponse(['received' => true]);
}

What to verify in the SaaS demo

Before signing with a SaaS vendor for the bridge, five specifics should be confirmed during the demo. First, the REST API covers every operation in the admin UI; the test is to walk through five admin actions through API calls. Second, webhooks are HMAC-SHA256 signed and retried with exponential backoff for at least 24 hours. Third, idempotency keys are honored on inbound requests so retries do not double-process. Fourth, a sandbox environment exists for testing webhook flow before production. Fifth, a PHP SDK or at least curl examples ship with the API documentation.

CloudMLM Software passes these tests cleanly and publishes the PHP SDK on Packagist (composer require cloudmlm/sdk). Business MLM Software passes them functionally, with slightly less polished SDK ergonomics; the integration still works in roughly the same time. Other SaaS MLM vendors vary; the demo conversation surfaces it quickly.

Stripe Connect for distributor payouts

On Drupal Commerce installations that already use Stripe for customer payments, distributor payouts can flow through Stripe Connect. Distributors register as Connect Express accounts during the SaaS member creation flow; the SaaS triggers payouts from the wallet API, Stripe handles the actual settlement and tax-form generation. The Drupal Commerce side does not need to handle the Connect onboarding directly; the SaaS API can call Stripe Connect on its behalf.

This pattern is the cleanest way to handle distributor payouts on Drupal stacks; the alternative (handling payouts entirely inside the SaaS without Stripe Connect) works but means a separate KYC and tax-form pipeline runs in parallel with the consumer-side Stripe integration.

What this scales to

Through $50M+ GMV with no architectural changes. The Drupal side scales like any Drupal Commerce site; managed hosting at the Acquia or Pantheon class is appropriate above $10M GMV, with Redis-backed cache and a managed MySQL or Postgres tier. Above $50M, the question becomes whether to leave Drupal entirely (Pattern C is the headless variant), but most networks staying on Drupal through that scale find no operational reason to migrate.