{"id":363631,"date":"2026-09-06T18:38:17","date_gmt":"2026-09-06T18:38:17","guid":{"rendered":"https:\/\/fr.wordpress.org\/plugins\/sprint-connect-for-helloasso\/"},"modified":"2026-09-06T18:37:42","modified_gmt":"2026-09-06T18:37:42","slug":"sprint-connect-for-helloasso","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/sprint-connect-for-helloasso\/","author":23539978,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.0.0","stable_tag":"1.0.0","tested":"7.1","requires":"6.2","requires_php":"7.4","requires_plugins":null,"header_name":"Sprint Connect for HelloAsso","header_author":"Sprint Plugins","header_description":"Universal HelloAsso connector for WordPress: forms, orders, items, participants, payers and payments, with manual import and an optional Pro extension.","assets_banners_color":"eaeffc","last_updated":"2026-09-06 18:37:42","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"","header_author_uri":"https:\/\/sprintplugins.fr\/","rating":0,"author_block_rating":0,"active_installs":0,"downloads":36,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.0":{"tag":"1.0.0","author":"sprintplugins","date":"2026-09-06 18:37:42","revision":3683821}},"upgrade_notice":[],"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3683842,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3683842,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256},"icon.svg":{"filename":"icon.svg","revision":3683842,"resolution":false,"location":"assets","locale":false}},"assets_banners":{"banner-1544x500-fr.png":{"filename":"banner-1544x500-fr.png","revision":3683842,"resolution":"1544x500","location":"assets","locale":"fr","width":1544,"height":500},"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3683842,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250-fr.png":{"filename":"banner-772x250-fr.png","revision":3683842,"resolution":"772x250","location":"assets","locale":"fr","width":772,"height":250},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3683842,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{"blueprint.json":{"filename":"blueprint.json","revision":3683871,"resolution":false,"location":"assets","locale":"","contents":"{\"$schema\":\"https:\\\/\\\/playground.wordpress.net\\\/blueprint-schema.json\",\"landingPage\":\"\\\/wp-admin\\\/admin.php?page=sprint-connect-for-helloasso#forms\",\"preferredVersions\":{\"php\":\"8.3\",\"wp\":\"latest\"},\"features\":{\"networking\":false},\"siteOptions\":{\"blogname\":\"Sprint Connect for HelloAsso \\u2014 Playground Demo\",\"blogdescription\":\"Synthetic HelloAsso data for WordPress.org preview\"},\"steps\":[{\"step\":\"installPlugin\",\"pluginData\":{\"resource\":\"wordpress.org\\\/plugins\",\"slug\":\"sprint-connect-for-helloasso\"},\"options\":{\"activate\":true}},{\"step\":\"mkdir\",\"path\":\"\\\/wordpress\\\/wp-content\\\/mu-plugins\"},{\"step\":\"writeFile\",\"path\":\"\\\/wordpress\\\/wp-content\\\/mu-plugins\\\/scha-playground-demo.php\",\"data\":\"<?php\\n\\\/**\\n * Sprint Connect for HelloAsso - WordPress.org Playground demo helper.\\n * Synthetic data only. Loaded only inside the Playground created by blueprint.json.\\n *\\\/\\ndefined('ABSPATH') || exit;\\n\\nif (!defined('SCHA_DISABLE_NETWORK_DELAYS')) {\\n    define('SCHA_DISABLE_NETWORK_DELAYS', true);\\n}\\n\\nfunction scha_pg_catalog() {\\n    static $forms = null;\\n    if ($forms !== null) return $forms;\\n    $forms = json_decode('[{\\\"type\\\":\\\"Membership\\\",\\\"slug\\\":\\\"adult-membership-2026\\\",\\\"title\\\":\\\"Adult membership 2026\\\",\\\"tier\\\":\\\"Adult membership\\\",\\\"amount\\\":4500,\\\"id\\\":10001},{\\\"type\\\":\\\"Membership\\\",\\\"slug\\\":\\\"family-membership-2026\\\",\\\"title\\\":\\\"Family membership 2026\\\",\\\"tier\\\":\\\"Family membership\\\",\\\"amount\\\":8500,\\\"id\\\":10002},{\\\"type\\\":\\\"Membership\\\",\\\"slug\\\":\\\"youth-license-2026\\\",\\\"title\\\":\\\"Youth license 2026\\\",\\\"tier\\\":\\\"Youth license\\\",\\\"amount\\\":3000,\\\"id\\\":10003},{\\\"type\\\":\\\"Event\\\",\\\"slug\\\":\\\"autumn-tournament-2026\\\",\\\"title\\\":\\\"Autumn tournament 2026\\\",\\\"tier\\\":\\\"Tournament ticket\\\",\\\"amount\\\":1500,\\\"id\\\":10004},{\\\"type\\\":\\\"Event\\\",\\\"slug\\\":\\\"annual-gala-2026\\\",\\\"title\\\":\\\"Annual gala 2026\\\",\\\"tier\\\":\\\"Gala ticket\\\",\\\"amount\\\":3500,\\\"id\\\":10005},{\\\"type\\\":\\\"Event\\\",\\\"slug\\\":\\\"discovery-workshop-2026\\\",\\\"title\\\":\\\"Discovery workshop 2026\\\",\\\"tier\\\":\\\"Workshop registration\\\",\\\"amount\\\":2000,\\\"id\\\":10006},{\\\"type\\\":\\\"Donation\\\",\\\"slug\\\":\\\"free-donations-2026\\\",\\\"title\\\":\\\"Free donations 2026\\\",\\\"tier\\\":\\\"Donation\\\",\\\"amount\\\":2500,\\\"id\\\":10007},{\\\"type\\\":\\\"Donation\\\",\\\"slug\\\":\\\"equipment-support-2026\\\",\\\"title\\\":\\\"Equipment support 2026\\\",\\\"tier\\\":\\\"Equipment donation\\\",\\\"amount\\\":4000,\\\"id\\\":10008},{\\\"type\\\":\\\"CrowdFunding\\\",\\\"slug\\\":\\\"community-minibus-2026\\\",\\\"title\\\":\\\"Community minibus project\\\",\\\"tier\\\":\\\"Project contribution\\\",\\\"amount\\\":5000,\\\"id\\\":10009},{\\\"type\\\":\\\"CrowdFunding\\\",\\\"slug\\\":\\\"training-room-2026\\\",\\\"title\\\":\\\"New training room project\\\",\\\"tier\\\":\\\"Project contribution\\\",\\\"amount\\\":7500,\\\"id\\\":10010},{\\\"type\\\":\\\"Shop\\\",\\\"slug\\\":\\\"club-shop-2026\\\",\\\"title\\\":\\\"Club shop 2026\\\",\\\"tier\\\":\\\"Club T-shirt\\\",\\\"amount\\\":2000,\\\"id\\\":10011},{\\\"type\\\":\\\"Shop\\\",\\\"slug\\\":\\\"equipment-shop-2026\\\",\\\"title\\\":\\\"Equipment shop 2026\\\",\\\"tier\\\":\\\"Equipment pack\\\",\\\"amount\\\":6000,\\\"id\\\":10012},{\\\"type\\\":\\\"PaymentForm\\\",\\\"slug\\\":\\\"workshop-balance-2026\\\",\\\"title\\\":\\\"Workshop balance 2026\\\",\\\"tier\\\":\\\"Workshop balance\\\",\\\"amount\\\":8000,\\\"id\\\":10013},{\\\"type\\\":\\\"PaymentForm\\\",\\\"slug\\\":\\\"trip-contribution-2026\\\",\\\"title\\\":\\\"Trip contribution 2026\\\",\\\"tier\\\":\\\"Trip contribution\\\",\\\"amount\\\":5500,\\\"id\\\":10014},{\\\"type\\\":\\\"Checkout\\\",\\\"slug\\\":\\\"membership-supplement-2026\\\",\\\"title\\\":\\\"Membership supplement\\\",\\\"tier\\\":\\\"Membership supplement\\\",\\\"amount\\\":1200,\\\"id\\\":10015},{\\\"type\\\":\\\"Checkout\\\",\\\"slug\\\":\\\"exceptional-contribution-2026\\\",\\\"title\\\":\\\"Exceptional contribution\\\",\\\"tier\\\":\\\"Exceptional contribution\\\",\\\"amount\\\":2500,\\\"id\\\":10016}]', true);\\n    return is_array($forms) ? $forms : array();\\n}\\n\\nfunction scha_pg_public_form_url($slug) {\\n    return home_url('\\\/helloasso-demo-placeholder\\\/?form=' . rawurlencode((string) $slug));\\n}\\n\\nfunction scha_pg_raw_forms() {\\n    $rows = array();\\n    foreach (scha_pg_catalog() as $i => $form) {\\n        $url = scha_pg_public_form_url($form['slug']);\\n        $rows[] = array(\\n            'id' => (int) $form['id'],\\n            'formType' => $form['type'],\\n            'formSlug' => $form['slug'],\\n            'title' => $form['title'],\\n            'state' => 'Public',\\n            'url' => $url,\\n            'widgetFullUrl' => $url,\\n            'widgetButtonUrl' => $url,\\n            'creationDate' => '2026-01-' . str_pad((string) (($i % 9) + 10), 2, '0', STR_PAD_LEFT) . 'T09:00:00+01:00',\\n            'lastUpdate' => '2026-09-' . str_pad((string) (($i % 5) + 1), 2, '0', STR_PAD_LEFT) . 'T10:30:00+02:00',\\n        );\\n    }\\n    return $rows;\\n}\\n\\nfunction scha_pg_person($index, $role = 'participant') {\\n    $index = max(1, (int) $index);\\n    $prefix = $role === 'payer' ? 'Payer' : 'Participant';\\n    return array(\\n        'firstName' => 'Demo',\\n        'lastName' => $prefix . ' ' . str_pad((string) $index, 2, '0', STR_PAD_LEFT),\\n        'email' => strtolower($prefix) . str_pad((string) $index, 2, '0', STR_PAD_LEFT) . '@example.org',\\n        'dateOfBirth' => $role === 'payer' ? '1985-05-15' : '1995-0' . (($index % 8) + 1) . '-12',\\n        'address' => (10 + $index) . ' Example Street',\\n        'zipCode' => '31000',\\n        'city' => 'Toulouse',\\n        'country' => 'France',\\n    );\\n}\\n\\nfunction scha_pg_custom_fields($form, $index) {\\n    $fields = array(\\n        array('name' => 'Demo consent', 'answer' => ($index % 2) ? 'Yes' : 'No'),\\n        array('name' => 'Reference', 'answer' => 'DEMO-' . strtoupper(substr(md5($form['slug'] . '|' . $index), 0, 8))),\\n    );\\n    if ($form['type'] === 'Membership') {\\n        $fields[] = array('name' => 'Emergency contact', 'answer' => 'Demo contact ' . $index);\\n    } elseif ($form['type'] === 'Event') {\\n        $fields[] = array('name' => 'Dietary information', 'answer' => ($index % 2) ? 'None' : 'Vegetarian');\\n    } elseif ($form['type'] === 'Shop') {\\n        $fields[] = array('name' => 'Size', 'answer' => array('S','M','L','XL')[$index % 4]);\\n    } elseif ($form['type'] === 'Donation' || $form['type'] === 'CrowdFunding') {\\n        $fields[] = array('name' => 'Display donor name', 'answer' => ($index % 2) ? 'Yes' : 'No');\\n    } else {\\n        $fields[] = array('name' => 'Comment', 'answer' => 'Synthetic Playground example');\\n    }\\n    return $fields;\\n}\\n\\nfunction scha_pg_orders_for_form($form) {\\n    $orders = array();\\n    $form_index = 0;\\n    foreach (scha_pg_catalog() as $idx => $candidate) {\\n        if ($candidate['slug'] === $form['slug'] && $candidate['type'] === $form['type']) { $form_index = $idx; break; }\\n    }\\n    for ($j = 1; $j <= 3; $j++) {\\n        $n = ($form_index * 3) + $j;\\n        $order_id = 200000 + ($form_index * 10) + $j;\\n        $item_id = 300000 + ($form_index * 10) + $j;\\n        $base_amount = (int) $form['amount'];\\n        if ($form['type'] === 'Donation') $base_amount = array(2500,5000,10000)[$j-1];\\n        if ($form['type'] === 'CrowdFunding') $base_amount += ($j-1) * 2500;\\n        $payer = scha_pg_person($n, 'payer');\\n        $participant = scha_pg_person($n, 'participant');\\n\\n        $payment_count = ($j === 2 && in_array($form['type'], array('Membership','CrowdFunding','PaymentForm','Checkout'), true)) ? 2 : 1;\\n        $payments = array();\\n        $remaining = $base_amount;\\n        for ($p = 1; $p <= $payment_count; $p++) {\\n            $pay_amount = ($payment_count === 2 && $p === 1) ? (int) floor($base_amount \\\/ 2) : $remaining;\\n            $remaining -= $pay_amount;\\n            $state = ($j === 3 && ($form_index % 4 === 0)) ? 'Refunded' : 'Authorized';\\n            $payments[] = array(\\n                'id' => 400000 + ($form_index * 100) + ($j * 10) + $p,\\n                'orderId' => $order_id,\\n                'itemId' => $item_id,\\n                'formId' => (int) $form['id'],\\n                'formType' => $form['type'],\\n                'formSlug' => $form['slug'],\\n                'state' => $state,\\n                'cashOutState' => $state === 'Refunded' ? 'Refunded' : 'Transferred',\\n                'amount' => $pay_amount,\\n                'date' => '2026-08-' . str_pad((string) (5 + (($form_index + $j + $p) % 20)), 2, '0', STR_PAD_LEFT) . 'T14:15:00+02:00',\\n                'installmentNumber' => $payment_count === 2 ? $p : 1,\\n                'paymentMeans' => ($j % 2) ? 'Card' : 'BankCard',\\n                'payer' => $payer,\\n            );\\n        }\\n\\n        $item_state = ($j === 3 && ($form_index % 4 === 0)) ? 'Canceled' : 'Processed';\\n        $item = array(\\n            'id' => $item_id,\\n            'orderId' => $order_id,\\n            'formId' => (int) $form['id'],\\n            'formType' => $form['type'],\\n            'formSlug' => $form['slug'],\\n            'name' => $form['tier'],\\n            'tierType' => $form['type'] === 'Membership' ? 'Membership' : ($form['type'] === 'Event' ? 'Registration' : 'Payment'),\\n            'state' => $item_state,\\n            'amount' => $base_amount,\\n            'user' => $participant,\\n            'payer' => $payer,\\n            'customFields' => scha_pg_custom_fields($form, $n),\\n            'payments' => $payments,\\n        );\\n\\n        $orders[] = array(\\n            'id' => $order_id,\\n            'formId' => (int) $form['id'],\\n            'formType' => $form['type'],\\n            'formSlug' => $form['slug'],\\n            'date' => '2026-08-' . str_pad((string) (4 + (($form_index + $j) % 20)), 2, '0', STR_PAD_LEFT) . 'T13:30:00+02:00',\\n            'lastUpdate' => '2026-09-0' . (($j % 3) + 1) . 'T10:00:00+02:00',\\n            'amount' => $base_amount,\\n            'payer' => $payer,\\n            'items' => array($item),\\n            'payments' => $payments,\\n        );\\n    }\\n    return $orders;\\n}\\n\\nfunction scha_pg_find_form($type, $slug) {\\n    foreach (scha_pg_catalog() as $form) {\\n        if (strcasecmp((string) $form['type'], (string) $type) === 0 && (string) $form['slug'] === (string) $slug) return $form;\\n    }\\n    return null;\\n}\\n\\nfunction scha_pg_flat_items($form) {\\n    $rows = array();\\n    foreach (scha_pg_orders_for_form($form) as $order) {\\n        foreach ($order['items'] as $item) $rows[] = $item;\\n    }\\n    return $rows;\\n}\\n\\nfunction scha_pg_flat_payments($form) {\\n    $rows = array();\\n    foreach (scha_pg_orders_for_form($form) as $order) {\\n        foreach ($order['payments'] as $payment) $rows[] = $payment;\\n    }\\n    return $rows;\\n}\\n\\nfunction scha_pg_http_response($payload, $code = 200) {\\n    return array(\\n        'headers' => array('content-type' => 'application\\\/json; charset=utf-8'),\\n        'body' => wp_json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),\\n        'response' => array('code' => (int) $code, 'message' => $code === 200 ? 'OK' : 'Error'),\\n        'cookies' => array(),\\n        'filename' => null,\\n    );\\n}\\n\\nfunction scha_pg_mock_helloasso($preempt, $args, $url) {\\n    $host = strtolower((string) wp_parse_url($url, PHP_URL_HOST));\\n    if (!in_array($host, array('api.helloasso.com','api.helloasso-sandbox.com'), true)) return $preempt;\\n\\n    $path = (string) wp_parse_url($url, PHP_URL_PATH);\\n    if ($path === '\\\/oauth2\\\/token') {\\n        return scha_pg_http_response(array('access_token' => 'playground-demo-token', 'token_type' => 'Bearer', 'expires_in' => 3600));\\n    }\\n\\n    if (!preg_match('#^\\\/v5\\\/organizations\\\/([^\\\/]+)(.*)$#', $path, $m)) {\\n        return scha_pg_http_response(array('error' => 'Not found'), 404);\\n    }\\n    $org = rawurldecode($m[1]);\\n    $tail = $m[2];\\n    if ($org !== 'association-demo-sprint') return scha_pg_http_response(array('error' => 'Demo organization not found'), 404);\\n\\n    if ($tail === '' || $tail === '\\\/') {\\n        return scha_pg_http_response(array('organizationSlug' => $org, 'name' => 'Sprint Demo Association', 'type' => 'Association'));\\n    }\\n    if ($tail === '\\\/orders') {\\n        $all = array();\\n        foreach (scha_pg_catalog() as $form) $all = array_merge($all, scha_pg_orders_for_form($form));\\n        return scha_pg_http_response(array('data' => array_slice($all, 0, 1), 'pagination' => array('totalCount' => count($all), 'continuationToken' => null)));\\n    }\\n    if ($tail === '\\\/forms') {\\n        $data = scha_pg_raw_forms();\\n        return scha_pg_http_response(array('data' => $data, 'pagination' => array('totalCount' => count($data), 'continuationToken' => null)));\\n    }\\n    if (preg_match('#^\\\/forms\\\/([^\\\/]+)\\\/([^\\\/]+)\\\/(items|orders|payments)$#', $tail, $fm)) {\\n        $type = rawurldecode($fm[1]);\\n        $slug = rawurldecode($fm[2]);\\n        $kind = $fm[3];\\n        $form = scha_pg_find_form($type, $slug);\\n        if (!$form) return scha_pg_http_response(array('error' => 'Demo form not found'), 404);\\n        if ($kind === 'items') $data = scha_pg_flat_items($form);\\n        elseif ($kind === 'payments') $data = scha_pg_flat_payments($form);\\n        else $data = scha_pg_orders_for_form($form);\\n        return scha_pg_http_response(array('data' => $data, 'pagination' => array('totalCount' => count($data), 'continuationToken' => null)));\\n    }\\n    return scha_pg_http_response(array('error' => 'Unsupported demo endpoint'), 404);\\n}\\nadd_filter('pre_http_request', 'scha_pg_mock_helloasso', 10, 3);\\n\\nfunction scha_pg_create_demo_pages() {\\n    $placeholder = get_page_by_path('helloasso-demo-placeholder');\\n    if (!$placeholder) {\\n        wp_insert_post(array(\\n            'post_type' => 'page', 'post_status' => 'publish',\\n            'post_title' => 'HelloAsso form placeholder', 'post_name' => 'helloasso-demo-placeholder',\\n            'post_content' => '<!-- wp:heading --><h2 class=\\\"wp-block-heading\\\">HelloAsso Playground placeholder<\\\/h2><!-- \\\/wp:heading --><!-- wp:paragraph --><p>This local page stands in for the external HelloAsso hosted form during the WordPress.org Playground preview. The plugin data and synchronization screens use synthetic responses only.<\\\/p><!-- \\\/wp:paragraph -->',\\n        ));\\n    }\\n\\n    $showcase = get_page_by_path('sprint-connect-helloasso-demo');\\n    if (!$showcase) {\\n        $content = '<!-- wp:heading --><h2 class=\\\"wp-block-heading\\\">Sprint Connect for HelloAsso \\u2014 Playground demo<\\\/h2><!-- \\\/wp:heading -->';\\n        $content .= '<!-- wp:paragraph --><p>All content below is synthetic. Standard HelloAsso forms use local placeholder links in this preview; Checkout examples render local transaction data.<\\\/p><!-- \\\/wp:paragraph -->';\\n        $representatives = array(\\n            array('Membership','adult-membership-2026','Membership'),\\n            array('Event','annual-gala-2026','Event \\\/ ticketing'),\\n            array('Donation','free-donations-2026','Donation'),\\n            array('CrowdFunding','community-minibus-2026','Crowdfunding'),\\n            array('Shop','club-shop-2026','Shop'),\\n            array('PaymentForm','workshop-balance-2026','Payment form'),\\n            array('Checkout','membership-supplement-2026','Checkout'),\\n        );\\n        foreach ($representatives as $r) {\\n            $content .= '<!-- wp:heading {\\\"level\\\":3} --><h3 class=\\\"wp-block-heading\\\">' . esc_html($r[2]) . '<\\\/h3><!-- \\\/wp:heading -->';\\n            if ($r[0] === 'Checkout') {\\n                $content .= '[sprint_helloasso form=\\\"' . esc_attr($r[1]) . '\\\" type=\\\"Checkout\\\" view=\\\"both\\\" limit=\\\"10\\\" visibility=\\\"public\\\" details=\\\"1\\\"]';\\n            } else {\\n                $content .= '[sprint_helloasso form=\\\"' . esc_attr($r[1]) . '\\\" type=\\\"' . esc_attr($r[0]) . '\\\" mode=\\\"button\\\" label=\\\"Open synthetic ' . esc_attr($r[2]) . ' example\\\"]';\\n            }\\n        }\\n        wp_insert_post(array(\\n            'post_type' => 'page', 'post_status' => 'publish',\\n            'post_title' => 'Sprint Connect for HelloAsso Demo', 'post_name' => 'sprint-connect-helloasso-demo',\\n            'post_content' => $content,\\n        ));\\n    }\\n}\\n\\nfunction scha_pg_seed_demo() {\\n    if (get_option('scha_playground_demo_seeded') === '1') return;\\n    if (!class_exists('SprintPlugins\\\\\\\\ConnectHelloAsso\\\\\\\\Database\\\\\\\\Schema') || !class_exists('SprintPlugins\\\\\\\\ConnectHelloAsso\\\\\\\\Database\\\\\\\\Repository') || !class_exists('SprintPlugins\\\\\\\\ConnectHelloAsso\\\\\\\\HelloAsso\\\\\\\\Normalizer')) return;\\n\\n    $schema = 'SprintPlugins\\\\\\\\ConnectHelloAsso\\\\\\\\Database\\\\\\\\Schema';\\n    $repo = 'SprintPlugins\\\\\\\\ConnectHelloAsso\\\\\\\\Database\\\\\\\\Repository';\\n    $normalizer = 'SprintPlugins\\\\\\\\ConnectHelloAsso\\\\\\\\HelloAsso\\\\\\\\Normalizer';\\n    $page_manager = 'SprintPlugins\\\\\\\\ConnectHelloAsso\\\\\\\\Integration\\\\\\\\PageManager';\\n\\n    $schema::install();\\n    $org = 'association-demo-sprint';\\n    $first = scha_pg_catalog()[0];\\n    $settings = array(\\n        'environment' => 'sandbox',\\n        'helloasso_connections' => array(\\n            'production' => array('client_id'=>'','client_secret'=>'','association_slug'=>'','form_id'=>0,'form_slug'=>'','form_type'=>'Membership'),\\n            'sandbox' => array(\\n                'client_id' => 'playground-demo-client-id',\\n                'client_secret' => 'playground-demo-client-secret',\\n                'association_slug' => $org,\\n                'form_id' => (int) $first['id'],\\n                'form_slug' => $first['slug'],\\n                'form_type' => $first['type'],\\n            ),\\n        ),\\n        'client_id' => 'playground-demo-client-id',\\n        'client_secret' => 'playground-demo-client-secret',\\n        'association_slug' => $org,\\n        'form_id' => (int) $first['id'],\\n        'form_slug' => $first['slug'],\\n        'form_type' => $first['type'],\\n        'duplicate_strategy' => 'email',\\n        'send_welcome_email' => 0,\\n        'field_mapping' => array(),\\n        'available_helloasso_fields' => array(),\\n        'manual_helloasso_fields' => array(),\\n        'cron_enabled' => 0,\\n        'cron_interval_seconds' => 900,\\n        'sync_mode' => 'all',\\n        'csv_separator' => 'semicolon',\\n        'csv_bom' => 1,\\n        'log_retention_days' => 90,\\n        'delete_data_on_uninstall' => 0,\\n    );\\n    update_option('scha_settings', $settings, false);\\n    update_option('scha_display_mode_default', 'complete', false);\\n\\n    $raw_forms = scha_pg_raw_forms();\\n    $normalized_forms = array();\\n    foreach ($raw_forms as $raw_form) $normalized_forms[] = $normalizer::form($raw_form);\\n    $repo::reconcile_forms_payload(array(\\n        'data' => $normalized_forms,\\n        'raw_data' => $raw_forms,\\n        'pagination' => array('loadedCount'=>count($normalized_forms),'totalCount'=>count($normalized_forms)),\\n    ), $org);\\n\\n    foreach (scha_pg_catalog() as $form) {\\n        $raw_orders = scha_pg_orders_for_form($form);\\n        $normalized_orders = array();\\n        foreach ($raw_orders as $raw_order) {\\n            $normalized_orders[] = $normalizer::order($raw_order, array('form_id'=>(int)$form['id'],'form_type'=>$form['type'],'form_slug'=>$form['slug']));\\n        }\\n        $repo::persist_orders_payload(array('data'=>$normalized_orders,'raw_data'=>$raw_orders), $org);\\n        $repo::upsert_sync_state(array(\\n            'form_id' => (int) $form['id'], 'helloasso_form_id' => (int) $form['id'],\\n            'form_type' => $form['type'], 'form_slug' => $form['slug'], 'form_title' => $form['title'],\\n            'enabled' => 0, 'sync_mode' => 'new', 'cursor_timestamp' => strtotime('2026-09-01 10:00:00'),\\n            'last_attempt_at' => '2026-09-01 10:00:00', 'last_success_at' => '2026-09-01 10:00:02',\\n            'last_status' => 'success', 'last_message' => 'Synthetic Playground synchronization',\\n            'last_created' => 3, 'last_updated' => 0, 'last_skipped' => 0, 'last_error_count' => 0,\\n        ), $org, false);\\n    }\\n\\n    \\\/\\\/ Create a few already-linked WordPress accounts while leaving many rows unlinked for import testing.\\n    $linked = 0;\\n    foreach (scha_pg_catalog() as $form) {\\n        if ($linked >= 10) break;\\n        $order = scha_pg_orders_for_form($form)[0];\\n        $item = $order['items'][0];\\n        $person = $item['user'];\\n        $email = sanitize_email($person['email']);\\n        $user = get_user_by('email', $email);\\n        if (!$user) {\\n            $username = sanitize_user('demo-' . $form['type'] . '-' . ($linked + 1), true);\\n            if (username_exists($username)) $username .= '-' . ($linked + 10);\\n            $uid = wp_insert_user(array(\\n                'user_login' => $username, 'user_pass' => wp_generate_password(20, true, true),\\n                'user_email' => $email, 'first_name' => $person['firstName'], 'last_name' => $person['lastName'],\\n                'display_name' => $person['firstName'] . ' ' . $person['lastName'], 'role' => 'subscriber',\\n            ));\\n            if (is_wp_error($uid)) continue;\\n        } else {\\n            $uid = $user->ID;\\n        }\\n        update_user_meta($uid, 'scha_connected_user', 1);\\n        update_user_meta($uid, 'scha_member', 1);\\n        update_user_meta($uid, 'scha_form_id', (int) $form['id']);\\n        update_user_meta($uid, 'scha_form_type', $form['type']);\\n        update_user_meta($uid, 'scha_form_slug', $form['slug']);\\n        update_user_meta($uid, 'scha_order_id', (int) $order['id']);\\n        update_user_meta($uid, 'scha_item_id', (int) $item['id']);\\n        add_user_meta($uid, 'scha_item_ids', (int) $item['id'], false);\\n        update_user_meta($uid, 'scha_payment_ids', array_map('intval', wp_list_pluck($item['payments'], 'id')));\\n        update_user_meta($uid, 'scha_payment_states', array_values(array_unique(wp_list_pluck($item['payments'], 'state'))));\\n        update_user_meta($uid, 'scha_person_role', 'participant');\\n        update_user_meta($uid, 'scha_raw_data', wp_json_encode($item, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));\\n        $repo::link_user_to_item((int) $item['id'], $org, (int) $uid);\\n        $linked++;\\n    }\\n\\n    $logs = array(\\n        array('time'=>'2026-09-01 10:00:02','status'=>'success','message'=>'Playground demo import completed','created'=>10,'updated'=>0,'skipped'=>2,'errors'=>array(),'details'=>array(),'duration'=>1.482,'total_fetched'=>12,'total_processed'=>12,'sync_mode'=>'all','manual'=>1,'form_title'=>'Adult membership 2026','form_type'=>'Membership','form_slug'=>'adult-membership-2026'),\\n        array('time'=>'2026-08-25 09:15:01','status'=>'success','message'=>'Synthetic incremental synchronization','created'=>3,'updated'=>2,'skipped'=>1,'errors'=>array(),'details'=>array(),'duration'=>0.842,'total_fetched'=>6,'total_processed'=>6,'sync_mode'=>'new','manual'=>0,'form_title'=>'Annual gala 2026','form_type'=>'Event','form_slug'=>'annual-gala-2026'),\\n    );\\n    update_option('scha_cron_logs', $logs, false);\\n    update_option('scha_last_success_timestamp', strtotime('2026-09-01 10:00:02'), false);\\n    update_option('scha_last_success_time', '2026-09-01 10:00:02', false);\\n    update_option('scha_helloasso_http_health', array('status'=>'ok','type'=>'','message'=>'','http_code'=>200,'consecutive_failures'=>0,'cooldown_until'=>0,'last_error_at'=>'','last_success_at'=>'2026-09-01 10:00:02','retry_after'=>0), false);\\n\\n    scha_pg_create_demo_pages();\\n    if (class_exists($page_manager)) {\\n        $page_manager::sync_catalog_pages($org);\\n        $checkout_pages = get_posts(array('post_type'=>'page','post_status'=>'draft','posts_per_page'=>-1,'meta_key'=>'_scha_managed_form_type','meta_value'=>'Checkout'));\\n        foreach ($checkout_pages as $page) wp_update_post(array('ID'=>$page->ID,'post_status'=>'publish'));\\n    }\\n\\n    update_option('scha_playground_demo_seeded', '1', false);\\n}\\nadd_action('init', 'scha_pg_seed_demo', 50);\\n\\nfunction scha_pg_demo_notice() {\\n    if (!current_user_can('manage_options')) return;\\n    $screen = function_exists('get_current_screen') ? get_current_screen() : null;\\n    if (!$screen || strpos((string) $screen->id, 'sprint-connect-for-helloasso') === false) return;\\n    $front = get_page_by_path('sprint-connect-helloasso-demo');\\n    $link = $front ? get_permalink($front) : home_url('\\\/');\\n    echo '<div class=\\\"notice notice-info\\\"><p><strong>WordPress.org Playground demo:<\\\/strong> all HelloAsso forms, people, orders and payments are synthetic. API calls are intercepted locally; no real HelloAsso account or personal data is used. <a href=\\\"' . esc_url($link) . '\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\">Open the front-end demo<\\\/a>.<\\\/p><\\\/div>';\\n}\\nadd_action('admin_notices', 'scha_pg_demo_notice');\\n\"},{\"step\":\"runPHP\",\"code\":\"<?php require '\\\/wordpress\\\/wp-load.php'; scha_pg_seed_demo();\"},{\"step\":\"login\",\"username\":\"admin\",\"password\":\"password\"}]}"}},"all_blocks":{"sprint-connect\/helloasso-form":{"$schema":"https:\/\/schemas.wp.org\/trunk\/block.json","apiVersion":3,"name":"sprint-connect\/helloasso-form","version":"1.0.0","title":"HelloAsso: form or Checkout","category":"widgets","icon":"tickets-alt","description":"Displays a public HelloAsso form or local data from a Checkout form.","textdomain":"sprint-connect-for-helloasso","attributes":{"form":{"type":"string","default":""},"type":{"type":"string","default":""},"mode":{"type":"string","default":"iframe"},"height":{"type":"number","default":900},"label":{"type":"string","default":"Open the HelloAsso form"},"view":{"type":"string","default":"both"},"limit":{"type":"number","default":10},"visibility":{"type":"string","default":"public"},"title":{"type":"string","default":""},"details":{"type":"boolean","default":false}},"supports":{"html":false,"align":["wide","full"]},"editorScript":"scha-helloasso-block-editor","editorStyle":"file:.\/editor.css"}},"tagged_versions":["1.0.0"],"block_files":[],"assets_screenshots":{"screenshot-1-fr.png":{"filename":"screenshot-1-fr.png","revision":3683842,"resolution":"1","location":"assets","locale":"fr","width":1440,"height":900},"screenshot-1.png":{"filename":"screenshot-1.png","revision":3683842,"resolution":"1","location":"assets","locale":"","width":1440,"height":900},"screenshot-2-fr.png":{"filename":"screenshot-2-fr.png","revision":3683842,"resolution":"2","location":"assets","locale":"fr","width":1440,"height":900},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3683842,"resolution":"2","location":"assets","locale":"","width":1440,"height":900},"screenshot-3-fr.png":{"filename":"screenshot-3-fr.png","revision":3683842,"resolution":"3","location":"assets","locale":"fr","width":1440,"height":900},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3683842,"resolution":"3","location":"assets","locale":"","width":1440,"height":900},"screenshot-4-fr.png":{"filename":"screenshot-4-fr.png","revision":3683842,"resolution":"4","location":"assets","locale":"fr","width":1440,"height":900},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3683842,"resolution":"4","location":"assets","locale":"","width":1440,"height":900},"screenshot-5-fr.png":{"filename":"screenshot-5-fr.png","revision":3683842,"resolution":"5","location":"assets","locale":"fr","width":1440,"height":900},"screenshot-5.png":{"filename":"screenshot-5.png","revision":3683842,"resolution":"5","location":"assets","locale":"","width":1440,"height":900}},"screenshots":{"1":"HelloAsso OAuth connection status, selected organization, and synchronization information.","2":"Available HelloAsso membership, ticketing, donation, and campaign forms.","3":"Orders, participants, payments, and installment status imported from HelloAsso.","4":"WordPress account matching and creation workflow.","5":"Gutenberg block, shortcode, sandbox, and Sprint Club display settings."}},"plugin_section":[],"plugin_tags":[8874,177739,1932,1887,1917],"plugin_category":[45,58],"plugin_contributors":[274079],"plugin_business_model":[],"class_list":["post-363631","plugin","type-plugin","status-publish","hentry","plugin_tags-association","plugin_tags-helloasso","plugin_tags-membership","plugin_tags-payments","plugin_tags-users","plugin_category-ecommerce","plugin_category-user-management","plugin_contributors-sprintplugins","plugin_committers-sprintplugins"],"banners":{"banner":"https:\/\/ps.w.org\/sprint-connect-for-helloasso\/assets\/banner-772x250.png?rev=3683842","banner_2x":"https:\/\/ps.w.org\/sprint-connect-for-helloasso\/assets\/banner-1544x500.png?rev=3683842","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":"https:\/\/ps.w.org\/sprint-connect-for-helloasso\/assets\/icon.svg?rev=3683842","icon":"https:\/\/ps.w.org\/sprint-connect-for-helloasso\/assets\/icon.svg?rev=3683842","icon_2x":false,"generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/sprint-connect-for-helloasso\/assets\/screenshot-1.png?rev=3683842","caption":"HelloAsso OAuth connection status, selected organization, and synchronization information."},{"src":"https:\/\/ps.w.org\/sprint-connect-for-helloasso\/assets\/screenshot-2.png?rev=3683842","caption":"Available HelloAsso membership, ticketing, donation, and campaign forms."},{"src":"https:\/\/ps.w.org\/sprint-connect-for-helloasso\/assets\/screenshot-3.png?rev=3683842","caption":"Orders, participants, payments, and installment status imported from HelloAsso."},{"src":"https:\/\/ps.w.org\/sprint-connect-for-helloasso\/assets\/screenshot-4.png?rev=3683842","caption":"WordPress account matching and creation workflow."},{"src":"https:\/\/ps.w.org\/sprint-connect-for-helloasso\/assets\/screenshot-5.png?rev=3683842","caption":"Gutenberg block, shortcode, sandbox, and Sprint Club display settings."}],"raw_content":"<!--section=description-->\n<p>Sprint Connect for HelloAsso helps clubs and associations connect one HelloAsso organization to WordPress.<\/p>\n\n<p>The free plugin provides:<\/p>\n\n<ul>\n<li>OAuth connection to HelloAsso.<\/li>\n<li>Production and sandbox environments.<\/li>\n<li>Manual retrieval and selection of HelloAsso forms.<\/li>\n<li>Local review of orders, items, participants, payers, payments, and installment status.<\/li>\n<li>CSV export of the filtered backend data explorer.<\/li>\n<li>Manual full or incremental imports and WordPress user matching or creation.<\/li>\n<li>A Gutenberg block and shortcode for displaying selected HelloAsso forms.<\/li>\n<li>Essential, Complete, and Expert administration modes.<\/li>\n<li>Optional display-mode inheritance and workspace integration with Sprint Club.<\/li>\n<li>Privacy exporters and erasers for imported personal data.<\/li>\n<li>A public API and neutral extension bridge for compatible add-ons.<\/li>\n<\/ul>\n\n<p>An optional, separately distributed Pro add-on can provide advanced field mapping, custom emails, automations, webhooks, snippets, backups, and additional synchronization tools. Premium implementation code, the license SDK, and the private updater are not included in this WordPress.org package.<\/p>\n\n<h3>External services<\/h3>\n\n<p>This plugin connects to HelloAsso only after a site administrator configures API credentials and starts a connection, synchronization, or import.<\/p>\n\n<p>Depending on the requested action, the plugin sends the configured client identifier and secret for OAuth authentication, the organization identifier, access or refresh tokens, and API request parameters. It receives organization, form, order, payer, participant, payment, and related campaign data made available to the configured HelloAsso API client.<\/p>\n\n<p>Production requests use <code>https:\/\/api.helloasso.com<\/code>. Sandbox requests use <code>https:\/\/api.helloasso-sandbox.com<\/code> when sandbox mode is enabled. Embedded or linked forms may direct visitors to <code>https:\/\/www.helloasso.com<\/code> or the HelloAsso sandbox website.<\/p>\n\n<p>The use of HelloAsso is subject to its terms and privacy policy:<\/p>\n\n<ul>\n<li>Association terms: https:\/\/info.helloasso.com\/cgu-associations<\/li>\n<li>User terms: https:\/\/info.helloasso.com\/cgu-utilisateurs<\/li>\n<li>Privacy policy: https:\/\/info.helloasso.com\/politique-de-confidentialite<\/li>\n<li>API documentation: https:\/\/dev.helloasso.com\/docs\/api-overview<\/li>\n<\/ul>\n\n<p>Sprint Plugins is not affiliated with or endorsed by HelloAsso. HelloAsso is a trademark of its respective owner.<\/p>\n\n<h3>Privacy<\/h3>\n\n<p>The plugin can store API credentials, OAuth tokens, organization settings, imported identifiers, form metadata, order information, participant information, payer information, payment status, and WordPress user-matching decisions.<\/p>\n\n<p>These records may contain personal data received from HelloAsso. The site administrator is responsible for having a lawful basis for importing and using this data, limiting access, defining retention periods, and providing appropriate privacy information to members and contributors.<\/p>\n\n<p>No data is sent to Sprint Plugins by the free plugin.<\/p>\n\n<h3>Source code<\/h3>\n\n<p>The JavaScript distributed by this plugin is human-readable and is not minified. No private build toolchain is required.<\/p>\n\n<p>The JavaScript authoring sources are included in <code>src\/js\/app.js<\/code> and <code>src\/blocks\/helloasso-form\/editor.js<\/code>; their runtime copies are <code>assets\/js\/app.js<\/code> and <code>assets\/blocks\/helloasso-form\/editor.js<\/code>. To regenerate the runtime assets, copy the source files unchanged. Build details are also documented in <code>src\/README.txt<\/code>.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Install and activate Sprint Connect for HelloAsso.<\/li>\n<li>Open Sprint Connect for HelloAsso &gt; Settings.<\/li>\n<li>Configure the Production and\/or Sandbox connection profile. Each environment keeps its own API credentials and organization slug.<\/li>\n<li>Save the environment you want to use, open Forms, then retrieve and select the available forms manually.<\/li>\n<li>Add a HelloAsso form block or shortcode to a page when needed.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20the%20plugin%20create%20payments%20from%20wordpress%3F\"><h3>Does the plugin create payments from WordPress?<\/h3><\/dt>\n<dd><p>No. The free plugin focuses on connection, retrieval, import, account matching, and form display. Payment checkout remains hosted by HelloAsso.<\/p><\/dd>\n<dt id=\"is%20the%20pro%20add-on%20required%3F\"><h3>Is the Pro add-on required?<\/h3><\/dt>\n<dd><p>No. The free plugin works on its own. The Pro add-on extends it and must be installed alongside the free plugin.<\/p><\/dd>\n<dt id=\"can%20i%20test%20the%20connection%20without%20using%20production%20data%3F\"><h3>Can I test the connection without using production data?<\/h3><\/dt>\n<dd><p>Yes. Select Sandbox and use an organization and API credentials created in the HelloAsso sandbox environment. Production and Sandbox credentials, selected forms, mappings, and locally stored HelloAsso data are kept separate.<\/p><\/dd>\n<dt id=\"does%20switching%20between%20production%20and%20sandbox%20delete%20forms%20or%20imported%20data%3F\"><h3>Does switching between Production and Sandbox delete forms or imported data?<\/h3><\/dt>\n<dd><p>No. Switching environments changes the active connection context. Each environment keeps its own credentials, organization, selected form, mappings, catalog, and transactional data. Returning to an environment restores its previous context.<\/p><\/dd>\n<dt id=\"does%20the%20plugin%20support%20several%20helloasso%20organizations%20at%20the%20same%20time%3F\"><h3>Does the plugin support several HelloAsso organizations at the same time?<\/h3><\/dt>\n<dd><p>Version 1.0.0 supports one active HelloAsso organization in Production and one active HelloAsso organization in Sandbox. It does not manage multiple simultaneous organizations inside the same environment.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>First public release.<\/li>\n<li>Includes HelloAsso OAuth connection, sandbox support, form discovery, transaction storage, backend CSV export, manual imports, account matching, embeds, privacy tools, display modes, Sprint Club integration, and the public add-on bridge.<\/li>\n<\/ul>","raw_excerpt":"Connect HelloAsso forms and membership data to WordPress, review transactions, import participants, and match user accounts.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/363631","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=363631"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/sprintplugins"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=363631"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=363631"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=363631"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=363631"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=363631"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=363631"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}