cfg->card($cardKind); $userAgent = $isMobile ? 'WM' : 'PC'; $returnUrl = route('web.payments.danal.card.return', ['a' => $attemptToken], true); $cancelUrl = route('web.payments.danal.cancel', ['a' => $attemptToken], true); // BYPASSVALUE: '&' 금지. 토큰만 넣고 나머지는 서버에서 검증. $req = [ 'SUBCPID' => '', 'AMOUNT' => (string)$order->pay_money, 'CURRENCY' => '410', 'ITEMNAME' => $this->safeItemName($this->orderTitle($order)), 'USERAGENT' => $userAgent, 'ORDERID' => $order->oid, 'OFFERPERIOD' => '', 'USERNAME' => '', 'USERID' => (string)$order->mem_no, 'USEREMAIL' => '', 'CANCELURL' => $cancelUrl, 'RETURNURL' => $returnUrl, 'TXTYPE' => 'AUTH', 'SERVICETYPE' => 'DANALCARD', 'ISNOTI' => 'N', 'BYPASSVALUE' => 'AT=' . $attemptToken, ]; $res = $this->client->call($c['url'], $c['cpid'], $req, $c['key'], $c['iv']); return [ 'req' => $req, 'res' => $res, 'start' => [ 'actionUrl' => (string)($res['STARTURL'] ?? ''), 'params' => ['STARTPARAMS' => (string)($res['STARTPARAMS'] ?? '')], 'acceptCharset' => 'EUC-KR', ], ]; } public function bill(GcPinOrder $order, string $cardKind, string $tid): array { $c = $this->cfg->card($cardKind); $req = [ 'TID' => $tid, 'AMOUNT' => (string)$order->pay_money, 'TXTYPE' => 'BILL', 'SERVICETYPE' => 'DANALCARD', ]; $res = $this->client->call($c['url'], $c['cpid'], $req, $c['key'], $c['iv']); return ['req' => $req, 'res' => $res]; } public function decryptReturn(string $cardKind, string $returnParams): array { $c = $this->cfg->card($cardKind); return $this->client->decryptReturnParams($returnParams, $c['key'], $c['iv']); } private function orderTitle(GcPinOrder $order): string { // 아이템 1개면 그 이름, 아니면 "상품권 외 N건" $items = $order->items()->limit(2)->get(); if ($items->count() === 0) return '상품권'; if ($items->count() === 1) return (string)$items[0]->item_name; return (string)$items[0]->item_name . ' 외'; } private function safeItemName(string $s): string { // 금칙문자 최소 제거(사고 방지) $s = str_replace(["&","'","\"","\\","<",">","|","\r","\n","," , "+"], " ", $s); return trim(preg_replace('/\s+/', ' ', $s)) ?: '상품권'; } public function cancel( GcPinOrder $order, string $cardKind, string $tid, ?int $amount = null, string $cancelType = 'C', string $requester = '', string $desc = '' ): array { $c = $this->cfg->card($cardKind); $amt = $amount ?? (int)$order->pay_money; $req = [ 'TID' => $tid, 'AMOUNT' => (string)$amt, 'CANCELTYPE' => $cancelType, // C:전체, P:부분 'TXTYPE' => 'CANCEL', 'SERVICETYPE' => 'DANALCARD', ]; // optional $requester = trim($requester); $desc = $this->safeCancelDesc($desc); if ($requester !== '') $req['CANCELREQUESTER'] = $requester; if ($desc !== '') $req['CANCELDESC'] = $desc; $res = $this->client->call($c['url'], $c['cpid'], $req, $c['key'], $c['iv']); return ['req' => $req, 'res' => $res]; } private function safeCancelDesc(string $s): string { // 다날 DATA 암복호화 구간에서 문제 일으키는 문자 최소 제거 $s = str_replace(["&","'","\"","\\","<",">","|","\r","\n","," , "+"], " ", $s); return trim(preg_replace('/\s+/', ' ', $s)); } }