time()) return $cache['token']; $ch = curl_init('https://api.helloasso.com/oauth2/token'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => http_build_query([ 'grant_type' => 'client_credentials', 'client_id' => HA_CLIENT_ID, 'client_secret' => HA_CLIENT_SECRET, ]), CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10, ]); $body = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($status !== 200) { throw new RuntimeException("HelloAsso auth échouée ($status): $body"); } $data = json_decode($body, true); $cache = ['token' => $data['access_token'], 'expires' => time() + $data['expires_in'] - 30]; return $cache['token']; } // ISO 3166-1 alpha-2 → alpha-3 (HelloAsso exige alpha-3) function _ha_country(string $alpha2): string { $map = ['FR'=>'FRA','BE'=>'BEL','CH'=>'CHE','LU'=>'LUX','DE'=>'DEU', 'ES'=>'ESP','IT'=>'ITA','GB'=>'GBR','NL'=>'NLD','PT'=>'PRT']; return $map[strtoupper($alpha2)] ?? 'FRA'; } function ha_create_checkout(string $first_name, string $last_name, string $email, string $kc_uid, string $address = '', string $zip = '', string $town = '', string $country = 'FR'): array { $token = _ha_token(); $return_url = APP_URL . '/helloasso/callback.php?uid=' . urlencode($kc_uid); $back_url = APP_URL . '/helloasso/renew.php'; $payer = ['firstName' => $first_name, 'lastName' => $last_name, 'email' => $email]; if ($address) $payer['address'] = $address; if ($zip) $payer['zipCode'] = $zip; if ($town) $payer['city'] = $town; if ($country) $payer['country'] = _ha_country($country); $ch = curl_init('https://api.helloasso.com/v5/organizations/' . HA_ORG_SLUG . '/checkout-intents'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer $token", 'Content-Type: application/json'], CURLOPT_POSTFIELDS => json_encode([ 'totalAmount' => HA_AMOUNT, 'initialAmount' => HA_AMOUNT, 'itemName' => HA_ITEM_NAME, 'backUrl' => $back_url, 'errorUrl' => $back_url, 'returnUrl' => $return_url, 'containsDonation' => false, 'payer' => $payer, ]), CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 15, ]); $body = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($status !== 200 && $status !== 201) { throw new RuntimeException("HelloAsso checkout échoué ($status): $body"); } return json_decode($body, true); } function ha_get_checkout(string $checkout_intent_id): array { $token = _ha_token(); $ch = curl_init("https://api.helloasso.com/v5/checkout-intents/$checkout_intent_id"); curl_setopt_array($ch, [ CURLOPT_HTTPHEADER => ["Authorization: Bearer $token"], CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10, ]); $body = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($status !== 200) { throw new RuntimeException("HelloAsso get checkout échoué ($status): $body"); } return json_decode($body, true); }