Guides

Recettes & workflows

Patterns d'intégration éprouvés. Copiez-collez, adaptez, déployez.

Workflows métier

Workflow : Dashboard pending

Affichez les résas en attente, avec actions accepter / refuser inline.

php
<?php
$API_KEY = getenv('RESTAURATEUR_API_KEY');
$BASE = 'https://api.restaurateur.ch/api/v1';

function api($method, $path, $body = null) {
  global $API_KEY, $BASE;
  $ch = curl_init("$BASE$path");
  curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => $method,
    CURLOPT_HTTPHEADER => [
      "X-API-Key: $API_KEY",
      "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => $body ? json_encode($body) : null,
  ]);
  $res = curl_exec($ch);
  curl_close($ch);
  return json_decode($res, true);
}

// Lister les pending
$pending = api('GET', '/bookings?status=pending&limit=50');

foreach ($pending['bookings'] as $b) {
  echo "{$b['guest_name']} — {$b['party_size']} pers — {$b['booking_date']} {$b['booking_time']}\n";

  if ($_POST['action'] === 'accept' && $_POST['token'] === $b['token']) {
    api('POST', "/bookings/{$b['token']}/accept", ['message' => 'Au plaisir de vous accueillir !']);
  }
  if ($_POST['action'] === 'reject' && $_POST['token'] === $b['token']) {
    api('POST', "/bookings/{$b['token']}/reject", ['reason' => 'Complet sur ce créneau']);
  }
}
javascript
const API_KEY = process.env.RESTAURATEUR_API_KEY;
const BASE = 'https://api.restaurateur.ch/api/v1';

const api = (method, path, body) => fetch(`${BASE}${path}`, {
  method,
  headers: {
    'X-API-Key': API_KEY,
    'Content-Type': 'application/json',
  },
  body: body ? JSON.stringify(body) : undefined,
}).then(r => r.json());

const { bookings } = await api('GET', '/bookings?status=pending&limit=50');

for (const b of bookings) {
  console.log(`${b.guest_name} — ${b.party_size} pers — ${b.booking_date} ${b.booking_time}`);
}

// Accepter une résa
await api('POST', `/bookings/${token}/accept`, { message: 'Au plaisir !' });

// Refuser
await api('POST', `/bookings/${token}/reject`, { reason: 'Complet' });
python
import os, requests

API_KEY = os.environ['RESTAURATEUR_API_KEY']
BASE = 'https://api.restaurateur.ch/api/v1'
H = {'X-API-Key': API_KEY, 'Content-Type': 'application/json'}

def api(method, path, body=None):
    return requests.request(method, BASE + path, headers=H, json=body).json()

pending = api('GET', '/bookings?status=pending&limit=50')

for b in pending['bookings']:
    print(f"{b['guest_name']} — {b['party_size']} pers — {b['booking_date']} {b['booking_time']}")

# Accepter
api('POST', f"/bookings/{token}/accept", {'message': 'Au plaisir !'})

# Refuser
api('POST', f"/bookings/{token}/reject", {'reason': 'Complet'})

Workflow : ajuster la capacité

Un car de 30 touristes vient demain au lunch ? Augmentez le max_total_guests pour absorber.

bash
# 1. Voir le service lunch et son usage actuel
curl "https://api.restaurateur.ch/api/v1/services?date=2026-06-15" \
  -H "X-API-Key: VOTRE_CLE_API"

# 2. Augmenter la capacité
curl -X PUT https://api.restaurateur.ch/api/v1/services/12 \
  -H "X-API-Key: VOTRE_CLE_API" \
  -H "Content-Type: application/json" \
  -d '{"max_total_guests": 50}'

# 3. Vérifier que les slots se rouvrent
curl "https://api.restaurateur.ch/api/v1/calendar/slots?date=2026-06-15" \
  -H "X-API-Key: VOTRE_CLE_API"

Workflow : congés d'une semaine

javascript
// Bloquer du 1er au 15 août 2026
const start = new Date('2026-08-01');
const end = new Date('2026-08-15');

for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
  const date = d.toISOString().slice(0, 10);
  await api('POST', '/calendar/pause-day', { date, reason: 'Congés annuels' });
}

Workflow : bouton réservation WordPress

Injecte un bouton "Réserver une table" après chaque résultat sportif de mrpickwick.ch, avec pré-remplissage du nombre de personnes.

php
// Dans functions.php ou un mu-plugin
add_filter('the_content', function ($content) {
  $btn = '<a href="javascript:openWidgetTable({guests:4})" '.
         'class="book-table-btn">Réserver une table</a>';
  return preg_replace(
    '/(<p class="sp-fixture-where">.*?<\/p>)/',
    '$1' . $btn,
    $content
  );
});

// Assurez-vous que le widget JS est chargé sur la page
add_action('wp_enqueue_scripts', function () {
  wp_enqueue_script('restaurateur-widget',
    'https://widget.restaurateur.ch/assets/js/restaurateur-widget.js',
    [], null, true);
});

Recettes PHP

Configurer le sender personnalisé

php
// Une fois pour configurer
api('PUT', '/mail', [
  'api_key' => 'sm_live_xxxxxxxxxxxx',
  'sender_email' => 'noreply@mrpickwick.ch',
]);

// Vérifier (envoie un test à restaurant.email)
$result = api('POST', '/mail/verify');

if ($result['ok']) {
  echo "✓ Les futurs emails partiront depuis noreply@mrpickwick.ch\n";
} else {
  echo "✗ Erreur : " . $result['error'];
}

Lister les bookings du jour avec totaux

php
$today = date('Y-m-d');
$counts = api('GET', "/stats/counts?date=$today");
$slots = api('GET', "/calendar/slots?date=$today");

echo "📅 $today : {$counts['totals']['bookings']} résas, {$counts['totals']['guests']} couverts\n";
foreach ($slots['services'] as $s) {
  $pct = $s['max_total_guests']
    ? round(100 * $s['guests_booked'] / $s['max_total_guests'])
    : 0;
  echo "  {$s['label']} : {$s['guests_booked']}/{$s['max_total_guests']} ({$pct}%)\n";
}

Recettes Node.js

Webhook sortant à chaque résa (polling)

javascript
// Cron toutes les 60s : détecter les nouveaux bookings et notifier Slack
import fetch from 'node-fetch';

let lastCheck = new Date().toISOString();

setInterval(async () => {
  const now = new Date().toISOString();
  const today = now.slice(0, 10);

  const { bookings } = await api('GET', `/bookings?date=${today}&limit=50`);
  const news = bookings.filter(b => b.created_at >= lastCheck);

  for (const b of news) {
    await fetch(process.env.SLACK_WEBHOOK, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        text: `🆕 ${b.guest_name} — ${b.party_size}p — ${b.booking_date} ${b.booking_time}`,
      }),
    });
  }

  lastCheck = now;
}, 60000);

Synchroniser vers Google Calendar

javascript
const { bookings } = await api('GET', '/bookings?status=confirmed&from=2026-06-01&to=2026-06-30');

for (const b of bookings) {
  await googleCalendar.events.insert({
    calendarId: 'primary',
    requestBody: {
      summary: `${b.guest_name} (${b.party_size}p)`,
      description: b.notes || '',
      start: { dateTime: `${b.booking_date}T${b.booking_time}:00`, timeZone: 'Europe/Zurich' },
      end:   { dateTime: addMinutes(`${b.booking_date}T${b.booking_time}:00`, 90), timeZone: 'Europe/Zurich' },
    },
  });
}

Recettes Python

Export Excel des résas du mois

python
import pandas as pd

bookings = api('GET', '/bookings?from=2026-06-01&to=2026-06-30&limit=200')['bookings']
df = pd.DataFrame(bookings)
df = df[['booking_date', 'booking_time', 'guest_name', 'party_size', 'status', 'guest_email']]
df.to_excel(f'resas_juin_2026.xlsx', index=False)
print(f"✓ {len(df)} réservations exportées")

Notification SMS no-show répétés

python
from collections import Counter

# Détecter les clients avec ≥ 3 no-show sur 90 jours
ninety_days_ago = (datetime.now() - timedelta(days=90)).strftime('%Y-%m-%d')
no_shows = api('GET', f'/bookings?status=no_show&from={ninety_days_ago}')['bookings']
counter = Counter(b['guest_email'] for b in no_shows)

for email, count in counter.items():
  if count >= 3:
    print(f"⚠️ {email} : {count} no-shows en 90 jours")