pdo = $pdo; // LOAD CONFIG $config = require __DIR__ . '/config.php'; $this->clientId = $config['client_id']; $this->secretKey = $config['secret_key']; $this->baseUrl = $config['url_endpoint']; } // ================= SIGNATURE ================= private function generateSignature($requestId, $timestamp, $endpoint, $body) { $jsonBody = json_encode($body); // DIGEST (WAJIB FORMAT INI) $digest = base64_encode(hash('sha256', $jsonBody, true)); // STRING TO SIGN (WAJIB PERSIS DOC) $stringToSign = "Client-Id:" . $this->clientId . "\n" . "Request-Id:" . $requestId . "\n" . "Request-Timestamp:" . $timestamp . "\n" . "Request-Target:" . $endpoint . "\n" . // ❗ TANPA post "Digest:" . $digest; // SIGNATURE $signature = "HMACSHA256=" . base64_encode( hash_hmac('sha256', $stringToSign, $this->secretKey, true) ); return [ 'signature' => $signature, 'digest' => $digest, 'jsonBody' => $jsonBody ]; } // ================= CREATE PAYMENT ================= public function charge($data) { // ================= SIGNATURE ================= $requestId = bin2hex(random_bytes(16)); $timestamp = gmdate("Y-m-d\TH:i:s\Z"); $endpoint = "/checkout/v1/payment"; $url = $this->baseUrl . $endpoint; // Trasaction ID $trxId = $data['transaction_id'] ?? null; $body = [ "order" => [ "amount" => $data['amount'], //$data['amount'] "invoice_number" => $trxId, "currency" => "IDR", "session_id" => uniqid(), "callback_url" => "https://wp.manjapro.net/" . $_SESSION['token'], "line_items" => [ [ "name" => $data['invoice_detail']['keterangan'], "price" => $data['amount'], //$data['amount'] "quantity" => 1 ] ] ], "payment" => [ "payment_due_date" => 60 ], "customer" => [ "name" => $data['customer']['nama'] ?? "CUSTOMER", "email" => $data['customer']['email'], "phone" => $data['customer']['nomor_whtasapp'], "address" => $data['customer']['alamat'], "country" => "ID" ] ]; $sign = $this->generateSignature($requestId, $timestamp, $endpoint, $body); // ================= HEADER ================= $headers = [ "Client-Id: " . $this->clientId, "Request-Id: " . $requestId, "Request-Timestamp: " . $timestamp, "Request-Target: " . $endpoint, // ❗ TANPA post "Digest: " . $sign['digest'], "Signature: " . $sign['signature'], "Content-Type: application/json" ]; // ================= CURL ================= $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, // ✅ FIX CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_POSTFIELDS => $sign['jsonBody'], CURLOPT_HTTPHEADER => $headers ]); $response = curl_exec($ch); $err = curl_error($ch); curl_close($ch); // ================= ERROR CURL ================= if ($err) { $this->saveLog([ 'type' => 'ERROR', 'endpoint' => $endpoint, 'request' => $sign['jsonBody'], 'response' => $err, 'status' => 'FAILED' ]); return [ 'success' => false, 'message' => $err ]; } $result = json_decode($response, true); // ================= HANDLE ERROR ================= if (!isset($result['response'])) { return [ 'success' => false, 'message' => $result['error']['message'] ?? 'Unknown error', 'raw' => $result ]; } $res = $result['response']; // ================= PARSE ================= $external_id = $res['headers']['request_id'] ?? null; $amount = $res['order']['amount'] ?? $data['amount']; // ✅ CHECKOUT MODE $paymentUrl = $res['payment']['url'] ?? null; $tokenId = $res['payment']['token_id'] ?? null; // yang data nya gak perlu ada. $channels = null; $vaNumber = null; $qrString = null; $qrUrl = null; // status default (checkout biasanya belum bayar) $status = 'PENDING'; // expired $expiredAt = isset($res['payment']['expired_datetime']) ? date("Y-m-d H:i:s", strtotime($res['payment']['expired_datetime'])) : date("Y-m-d H:i:s", strtotime("+1 day")); // ================= SAVE ================= $this->saveTransaction([ 'trx_id' => $trxId, 'external_id' => $external_id, 'customer_id' => $data['customer']['id'] ?? null, 'invoice_id' => $data['invoice_id'] ?? null, 'method' => $data['raw_method'] ?? 'DOKU_CHECKOUT', 'channel' => $channels, // simpan list method 'amount' => $amount, 'va_number' => $vaNumber, 'qr_string' => $qrString, 'qr_url' => $qrUrl, 'payment_url' => $paymentUrl, 'response_create' => $response, 'status' => $status, 'expired_at' => $expiredAt ]); // ================= LOG ================= $this->saveLog([ 'trx_id' => $trxId, 'type' => 'RESPONSE', 'endpoint' => $endpoint, 'method' => $data['raw_method'] ?? null, 'request' => $sign['jsonBody'], 'response' => $response, 'status' => 'SUCCESS' ]); /** * ========================= * RETURN SUCCESS * ========================= */ return [ "status" => $status, "transaction_id" => $trxId, "payment_url" => $paymentUrl, "raw" => $result ]; } // ================= SAVE TRANSACTION ================= private function saveTransaction($data) { try { $stmt = $this->pdo->prepare(" INSERT INTO payment_winpay ( trx_id, external_id, id_pelanggan, id_tagihan, gateway_type, method, channel, amount, va_number, qr_string, qr_url, payment_url, status, response_create, expired_at, created_at, updated_at ) VALUES ( :trx_id, :external_id, :id_pelanggan, :id_tagihan, 'DOKU', :method, :channel, :amount, :va_number, :qr_string, :qr_url, :payment_url, :status, :response_create, :expired_at, NOW(), NOW() ) "); $stmt->execute([ ':trx_id' => $data['trx_id'], ':external_id' => $data['external_id'], ':id_pelanggan' => $data['customer_id'], ':id_tagihan' => $data['invoice_id'], ':method' => $data['method'], ':channel' => $data['channel'], ':amount' => $data['amount'], ':va_number' => $data['va_number'], ':qr_string' => $data['qr_string'], ':qr_url' => $data['qr_url'], ':payment_url' => $data['payment_url'], ':status' => $data['status'], ':response_create' => $data['response_create'], ':expired_at' => $data['expired_at'] ]); // ================= UPDATE TAGIHAN ================= $stmt = $this->pdo->prepare(" UPDATE tagihan SET transaction_id = :transaction_id WHERE id = :id_tagihan AND id_pelanggan = :id_pelanggan "); $stmt->execute([ ':transaction_id' => $data['trx_id'], ':id_tagihan' => $data['invoice_id'], ':id_pelanggan' => $data['customer_id'] ]); } catch (Exception $e) { file_put_contents( __DIR__ . '/doku_error.log', date('Y-m-d H:i:s') . " | " . $e->getMessage() . PHP_EOL, FILE_APPEND ); } } // ================= SAVE LOG ================= private function saveLog($data) { try { $stmt = $this->pdo->prepare(" INSERT INTO payment_winpay_log ( trx_id, type, endpoint, method, request_body, response_body, status, created_at ) VALUES ( :trx_id, :type, :endpoint, :method, :request_body, :response_body, :status, NOW() ) "); $stmt->execute([ ':trx_id' => $data['trx_id'] ?? null, ':type' => $data['type'], ':endpoint' => $data['endpoint'], ':method' => $data['method'] ?? null, ':request_body' => $data['request'], ':response_body' => $data['response'], ':status' => $data['status'], ]); } catch (Exception $e) {} } }