load('dolipesa@dolipesa'); // Security check $invoiceId = GETPOST('invoice_id', 'int'); $token = GETPOST('token', 'alpha'); if (!$invoiceId || !$token) { accessforbidden($langs->trans('MissingParameters')); } // Validate token (simple example; enhance for production) $expectedToken = md5($invoiceId . $conf->global->MAIN_SECURITY_SALT); if ($token !== $expectedToken) { accessforbidden($langs->trans('InvalidToken')); } // Load invoice $invoice = new Facture($db); if ($invoice->fetch($invoiceId) <= 0 || $invoice->statut != 1) { // Validated status accessforbidden($langs->trans('InvoiceNotFoundOrNotValidated')); } // Process payment submission if ($_SERVER['REQUEST_METHOD'] === 'POST') { $phoneNumber = GETPOST('phone_number', 'alpha'); $amount = floatval($invoice->total_ttc); // Use invoice total including tax // Validate phone number (Kenyan format: 2547XXXXXXXX) if (!preg_match('/^254[0-9]{9}$/', $phoneNumber)) { setEventMessages($langs->trans('InvalidPhoneNumber'), null, 'errors'); } else { // Load M-Pesa credentials $consumerKey = $conf->global->MPESAPAY_CONSUMER_KEY; $consumerSecret = dol_decrypt($conf->global->MPESAPAY_CONSUMER_SECRET); $shortcode = $conf->global->MPESAPAY_SHORTCODE; $passkey = dol_decrypt($conf->global->MPESAPAY_PASSKEY); $callbackUrl = $conf->global->MPESAPAY_CALLBACK_URL ?: DOL_MAIN_URL_ROOT . '/custom/dolipesa/callback.php'; if (empty($consumerKey) || empty($consumerSecret) || empty($shortcode) || empty($passkey)) { setEventMessages($langs->trans('ModuleNotConfigured'), null, 'errors'); } else { // Generate OAuth token $credentials = base64_encode($consumerKey . ':' . $consumerSecret); $tokenResponse = dol_http_get('https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials', [ 'Authorization: Basic ' . $credentials ]); $tokenData = json_decode($tokenResponse, true); $accessToken = $tokenData['access_token'] ?? null; if (!$accessToken) { setEventMessages($langs->trans('FailedToGetMpesaToken'), null, 'errors'); } else { // Prepare STK Push payload $timestamp = date('YmdHis'); $password = base64_encode($shortcode . $passkey . $timestamp); $payload = [ 'BusinessShortCode' => $shortcode, 'Password' => $password, 'Timestamp' => $timestamp, 'TransactionType' => 'CustomerPayBillOnline', 'Amount' => $amount, 'PartyA' => $phoneNumber, 'PartyB' => $shortcode, 'PhoneNumber' => $phoneNumber, 'CallBackURL' => $callbackUrl, 'AccountReference' => $invoice->ref, 'TransactionDesc' => $langs->trans('PaymentForInvoice', $invoice->ref) ]; // Send STK Push request $response = dol_http_post('https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest', json_encode($payload), [ 'Authorization: Bearer ' . $accessToken, 'Content-Type: application/json' ]); $responseData = json_decode($response, true); if ($responseData && $responseData['ResponseCode'] == '0') { // Store CheckoutRequestID for callback mapping $checkoutRequestID = $responseData['CheckoutRequestID']; $sql = "INSERT INTO " . MAIN_DB_PREFIX . "dolipesa_transactions (checkout_request_id, fk_invoice, datec) "; $sql .= "VALUES ('" . $db->escape($checkoutRequestID) . "', " . $invoice->id . ", NOW())"; $db->query($sql); setEventMessages($langs->trans('PaymentInitiated'), null, 'mesgs'); } else { setEventMessages($langs->trans('PaymentInitiationFailed') . ': ' . ($responseData['errorMessage'] ?? 'Unknown error'), null, 'errors'); } } } } } // Display payment form print load_fiche_titre($langs->trans('PayInvoice', $invoice->ref)); print '
'; print ''; print ''; print ''; print ''; print ''; print '
'; print '
'; print ''; print '
'; print '
'; llxFooter(); $db->close(); ?>