✏️ 正在编辑: ApiPartnerService.php
路径:
/srv/systems_dir/koruspay/application/Modules/Automation/Service/ApiPartnerService.php
提示:
您可以编辑任何文件(包括二进制文件),但请注意不当修改可能导致文件损坏。
<?php namespace Modules\Automation\Service; use Core_Crypt; use Core_Service_Partner; use Exception; use Model_Company; use Model_Container; use Model_DbTable_Credential; use Model_Payers; use Model_Split; use Model_Transactions; use Modules\Api\ApiFactory; use String_Refatorar; class ApiPartnerService { private const RULES = [ 'id_integration' => 'int', 'name' => 'string:100', 'name_holder' => 'string:100', 'doc_number' => 'string:14', 'phone' => 'string:14', 'email' => 'email', 'zip_code' => 'string:8', 'street' => 'string:255', 'number' => 'string:5', 'district' => 'string:100', 'city' => 'string:100', 'state' => 'string:2', 'complement' => 'string:255', 'due_date' => 'string:10', 'items' => 'array', 'payable_with' => 'string:9', 'amount' => 'float', 'discounts' => 'float', 'penalty' => 'float', 'per_day_interest' => 'float', 'bank_code' => 'string:3', 'branch_code' => 'string:5', 'account_number' => 'string:20', 'account_type' => 'string:20', 'type_pix' => 'string:9', 'pix' => 'string:255', 'description' => 'string:255', 'expiration_date' => 'string:10' ]; private const REQUIRED_PAYER = [ 'name', 'doc_number', 'phone', 'email', 'zip_code', 'street', 'number', 'district', 'city', 'state', 'complement', ]; private const REQUIRED_INVOICE = [ 'id_integration', 'due_date', 'amount' ]; public static function validRequest($request) { // $bodyRaw = $request->getRawBody(); // Recupera a API Key do header apiKey $apiKey = trim($request->getHeader('apiKey')); if (empty($apiKey)) { http_response_code(401); throw new Exception('API Key ausente.'); } // Busca o secret e IPs permitidos associados à API Key $credential = (new Model_DbTable_Credential)->findByApiKey($apiKey); if (!$credential || empty($credential['client_secret'])) { http_response_code(403); throw new Exception('API Key inválida.'); } // $secret = $credential['client_secret']; $tableIps = Model_Container::getCompanyIps(); $allowedIps = $tableIps->getByCompanyId($credential['company_id']); // $allowedIps = isset($credential['allowed_ips']) ? explode(',', $credential['allowed_ips']) : []; // Validação por IP $remoteIp = $_SERVER['REMOTE_ADDR'] ?? null; if (empty($allowedIps) || !in_array($remoteIp, $allowedIps)) { http_response_code(403); throw new Exception("IP {$remoteIp} não autorizado."); } // Recupera e valida o header de assinatura // $signatureHeader = $request->getHeader('Koruspay-Signature'); // if (empty($signatureHeader)) { // http_response_code(401); // throw new Exception('Assinatura ausente.'); // } // Divide e extrai o timestamp e a assinatura // $parts = array_map('trim', explode(',', $signatureHeader)); // $timestamp = null; // $signature = null; // foreach ($parts as $part) { // [$key, $value] = explode('=', $part, 2); // if ($key === 't') $timestamp = $value; // if ($key === 's') $signature = $value; // } // if (!$timestamp || !$signature) { // http_response_code(400); // throw new Exception('Assinatura incompleta.'); // } // // Verifica se o timestamp está dentro de uma janela de 5 minutos // if (abs(time() - (int)$timestamp) > 300) { // http_response_code(408); // throw new Exception('Requisição expirada.'); // } // // Concatena timestamp com corpo e calcula HMAC // $signedPayload = $timestamp . '.' . $bodyRaw; // $expectedSignature = hash_hmac('sha256', $signedPayload, $secret); // if (!hash_equals($expectedSignature, $signature)) { // http_response_code(401); // throw new Exception('Assinatura inválida.'); // } } public static function createSplit($request) { $apiKey = trim($request->getHeader('apiKey')); $credential = (new Model_DbTable_Credential)->findByApiKey($apiKey); $body = json_decode($request->getRawBody()); $split = $body->split; $payments = []; foreach ($split as $payment) { if (empty($payment->type_pix) || empty($payment->pix)) { continue; } $payment = String_Refatorar::sanitize((array)$payment, self::RULES); $pix = self::formatPixKey($payment['type_pix'], $payment['pix']); $payments[] = [ 'doc_number' => $payment['doc_number'] ?: null, 'name_holder' => trim($payment['name_holder']) ?: null, 'bank_code' => $payment['bank_code'] ?: null, 'account_type' => $payment['account_type'] ?: null, 'branch_code' => $payment['branch_code'] ?: null, 'account_number' => $payment['account_number'] ?: null, 'type_pix' => strtoupper(trim((string)$payment['type_pix'])) ?: null, 'pix' => trim($pix) ?: null, 'amount' => $payment['amount'] ?: null, 'description' => trim($payment['description']) ?: null ]; } $model = new Model_Split(); $saved = $model->saveSplit($payments, $credential['company_id'], 'API'); if (empty($saved)) { throw new Exception('Alguns pagamentos não foram enviados.'); } return $saved; } public static function formatPixKey(string $type, string $pix): string { $type = strtoupper(trim((string)$type)); $pix = trim((string)$pix); if ($pix === '') { return $pix; } // helper $onlyDigits = function ($v) { return preg_replace('/\D+/', '', $v); }; switch ($type) { case 'CPF': case 'CNPJ': return $onlyDigits($pix); case 'EMAIL': case 'E-MAIL': return mb_strtolower($pix); case 'TELEFONE': case 'PHONE': case 'FONE': case 'CELULAR': case 'CEL': $p = preg_replace('/\D/', '', $pix); if (strpos($p, '55') === 0) { // já tem country code return '+' . $p; } return '+55' . $p; default: return $pix; } } public static function processRequest($request) { $body = json_decode($request->getRawBody()); $charges = $body->charges; if (empty($charges)) { throw new Exception('Nenhuma cobrança para faturar.'); } $apiKey = trim($request->getHeader('apiKey')); $credential = (new Model_DbTable_Credential)->findByApiKey($apiKey); $company = (new Model_Company())->display($credential['company_id']); $ids = []; $returnError = []; $payableWith = $body->payable_with; $modelTransaction = new Model_Transactions(); foreach ($charges as $charge) { $dataPayer = String_Refatorar::sanitize((array) $charge->payer, self::RULES); $dataAddress = String_Refatorar::sanitize((array) $charge->payer->address, self::RULES); $dataPayer = array_merge($dataPayer, $dataAddress); $diffPayer = array_diff_key(array_flip(self::REQUIRED_PAYER), $dataPayer); $dataInvoice = String_Refatorar::sanitize((array) $charge, self::RULES); $diffInvoice = array_diff_key(array_flip(self::REQUIRED_INVOICE), $dataInvoice); $diff = array_merge($diffInvoice, $diffPayer); if (count($diff) > 0) { $error = [ 'success' => false, 'msg' => 'Campos obrigatórios: ' . implode(', ', array_flip($diff)), 'charge' => $charge ]; array_push($returnError, $error); continue; } $payer = [ 'company_id' => $company['id'], 'name' => $dataPayer['name'], 'doc_number' => $dataPayer['doc_number'], 'cell' => $dataPayer['phone'], 'email' => $dataPayer['email'], 'zip_code' => $dataPayer['zip_code'], 'street' => $dataPayer['street'], 'number' => $dataPayer['number'], 'neighborhood'=> $dataPayer['district'], 'city' => $dataPayer['city'], 'state' => $dataPayer['state'], 'complement' => $dataPayer['complement'] ]; $modelPayer = new Model_Payers(); $payerId = $modelPayer->insert($payer); $modelPayer->createPayer($payerId); $invoice = [ 'company_id' => $company['id'], 'payer_id' => $payerId, 'id_integration' => $dataInvoice['id_integration'], 'description' => implode(' - ', $dataInvoice['items']), 'due_date' => $dataInvoice['due_date'], 'expiration_date' => $dataInvoice['expiration_date'] ?: null, 'amount' => $dataInvoice['amount'], 'discount' => $dataInvoice['discounts'], 'penalty' => $dataInvoice['penalty'], 'origin' => 'API', 'type' => 'RECEBIMENTO', ]; $typeTransaction = $payableWith == 'pix' ? 'PIX' : 'BANKSLIP'; $idTransaction = $modelTransaction->createTransaction($invoice, $company, $typeTransaction); array_push($ids, $idTransaction); } if ($payableWith == 'pix') { $modelTransaction->createBatchPix($ids); } else { $modelTransaction->createBatchBankSlip($ids); } $corePartner = new Core_Service_Partner($company['id']); $corePartner->returnError($returnError); return $corePartner->returnInvoices($ids); } public static function balance($request) { $apiKey = trim($request->getHeader('apiKey')); $credential = (new Model_DbTable_Credential)->findByApiKey($apiKey); $model = new Model_Transactions(); $balance = $model->balance($credential['company_id']); $tax = $balance['type_tax_pix'] ? $balance['tax_pix'] : ($balance['tax_pix'] / 100) * $balance['balance']; $subtotal = $balance['balance'] <= 0 ? 0 : $balance['balance'] - $tax; if (!empty($balance)) { $tax = round($tax, 2); $subtotal = round($subtotal, 2); return [ 'subtotal' => $subtotal, 'tax' => $tax ]; } return false; } public static function withdraw($apiKey, $subtotal, $tax) { $credential = (new Model_DbTable_Credential)->findByApiKey($apiKey); $model = new Model_Transactions(); $withdraw = $model->withdraw($credential['company_id'], $subtotal, $tax); if (!empty($withdraw)) { return true; } return false; } public static function withdrawHistory($body, $apiKey) { $fromDate = $body->from_date; $toDate = $body->to_date; $credential = (new Model_DbTable_Credential)->findByApiKey($apiKey); $model = new Model_Transactions(); $history = $model->withdrawHistory($credential['company_id'], $fromDate, $toDate); return $history; } public static function checkStatus($request, $loop = false) { $param = $request->getParam('charge-id'); $type = $request->getParam('payable_with'); $decode = explode('=', Core_Crypt::Decrypt($param)); if (!isset($decode[1])) { throw new Exception('Parâmetro charge-id inválido ou malformado.'); } $chargeId = (int) $decode[1]; $model = new Model_Transactions(); $transaction = $model->display($chargeId); if (!$transaction) { return []; } if ($transaction['situation'] == 'RECEBIDO') { return [ 'koruspay_charge_id' => Core_Crypt::Encrypt('koruspay_id=' . $transaction['id']), 'id_integration' => $transaction['id_integration'], 'status' => $transaction['situation'], 'due_date' => $transaction['due_date'], 'description' => $transaction['description'], 'invoice_date' => $transaction['invoice_date'], 'amount' => $transaction['amount'], 'discount' => $transaction['discount'], 'penalty' => $transaction['penalty'], 'low_date' => $transaction['low_date'], 'amount_paid' => $transaction['amount_paid'], 'payment_date' => $transaction['payment_date'], 'amount_tax' => $transaction['amount_tax'] ]; } if ($loop) { return [ 'koruspay_charge_id' => Core_Crypt::Encrypt('koruspay_id=' . $transaction['id']), 'id_integration' => $transaction['id_integration'], 'status' => $transaction['situation'], ]; } $model->getCharge($transaction['id'], $type); return self::checkStatus($request, true); } }
💾 保存文件
← 返回文件管理器