Recipes per platform

WooCommerce

Two hooks in your child theme's functions.php are enough for an age check at checkout: one redirects to the hosted page when the basket is concerned, the other confirms the result on return.

add_action('template_redirect', function () {
  if (!is_checkout() || !basket_concerned()) { return; }
  if (WC()->session->get('todis_verified')) { return; }
  $r = wp_remote_post('https://verify.todis.eu/hosted/sessions', [
    'headers' => ['Authorization' => 'Bearer ' . TODIS_TOKEN,
                  'Content-Type' => 'application/json'],
    'body' => wp_json_encode([
      'check' => 'age_over_18', 'country_code' => 'FR',
      'locale' => 'en',
      'success_url' => wc_get_checkout_url() . '?todis_return=1',
    ]),
  ]);
  $journey = json_decode(wp_remote_retrieve_body($r), true);
  WC()->session->set('todis_session_id', $journey['session_id']);
  wp_redirect($journey['hosted_url']);
  exit;
});

// Priority 9: confirm BEFORE the redirect hook above.
add_action('template_redirect', function () {
  if (!isset($_GET['todis_return'])) { return; }
  $r = wp_remote_get('https://verify.todis.eu/verify/sessions/'
      . WC()->session->get('todis_session_id'),
    ['headers' => ['Authorization' => 'Bearer ' . TODIS_TOKEN]]);
  $body = json_decode(wp_remote_retrieve_body($r), true);
  // "verified" means the verification succeeded, not of age: read the value.
  if (($body['status'] ?? '') === 'verified'
      && ($body['claims']['age_over_18'] ?? false) === true) {
    WC()->session->set('todis_verified', true);
  }
}, 9);

The token stays server-side (a constant or an environment variable), never in a page. success_url means the verification succeeded, never that the user is of age: a proof of age that is false lands there too, like any verified result. Read the result server-side (claims.age_over_18 with the age_over_18 shortcut) and apply your own rule to it.

PrestaShop

Same logic, inside a small in-house module: the actionFrontControllerSetMedia hook (or a dedicated front controller) creates the journey and redirects when the basket contains a product subject to the check, then your return controller confirms the status and the value of claims.age_over_18 through the API before letting the order proceed. Store the session_id in the PrestaShop session cookie, and the token in the module configuration, never in a template.

Shopify

Shopify does not allow arbitrary code at checkout: the pattern goes through a small custom app (the one your Shopify agency probably already built for you) exposing two routes through an App Proxy: one creates the journey and redirects, the other receives the return and confirms. The trigger sits before checkout (cart page) or after purchase (order status page), depending on your regulatory constraint. There is no Todis app on the App Store yet: tell us if you miss it.

The three rules that never change. The license token stays server-side. The result is confirmed through an authenticated GET /verify/sessions/{id}, never on the basis of a URL parameter or a browser event. And there is nothing to store: no ID copy, no photo, only the verified answer you asked for, as explained in the FAQ.