[kakao/카카오페이] 쇼핑몰 카카오페이 연동. 프로그래밍
2018.03.29 18:13 Edit
준비물
1 - 카카오페이 가입
2 - https://developers.kakao.com/apps 가서 앱생성후 네이티브앱키 발급.
3 - 네이티브앱키로 메일보내서 CID 발급받기.
4 - 메뉴얼 보기. https://developers.kakao.com/docs/restapi/kakaopay-api
작업시작
1. form 만들기
<form method="post" action="CallPaymentKakaoPay"> <!-- 결제정보셋팅 --> <input type="hidden" name="partner_order_id" value="<?=$partner_order_id?>"> <!-- 주문번호 --> <input type="hidden" name="partner_user_id" value="<?=$partner_user_id?>"> <!-- 사이트 주문유저id --> <input type="hidden" name="item_name" value="<?=$item_name?>"> <!-- 상품명 --> <input type="hidden" name="quantity" value="<?=$quantity?>"> <!-- 수량 --> <input type="hidden" name="total_amount" value="<?=$total_amount?>"> <!-- 상품총액 --> <input type="hidden" name="tax_free_amount" value="<?=$tax_free_amount?>"> <!-- 비과세금액 --> <input type="submit" value="결제하기"> </form>
2. submit 시에 curl 요청으로 인증받고 결제창 호출하기
function CallPaymentKakaoPay($orderArr) { $adminkey = $this->strKakaopayAdminkey; // admin 키 if( $this->config->item('kakaopay_mode') == 'service' ) { $cid = $this->strkakaopayCID; // cid } else if( $this->config->item('kakaopay_mode') == 'test' ) { $cid = 'TC0ONETIME'; } $req_auth = 'Authorization: KakaoAK '.$adminkey; $req_cont = 'Content-type: application/x-www-form-urlencoded;charset=utf-8'; $kakao_header = array( $req_auth, $req_cont ); $approval_url = "https://".$this->input->server('HTTP_HOST')."/order/CallPaymentKakaoPaySuccess"; $cancel_url = "https://".$this->input->server('HTTP_HOST')."/order/CallPaymentKakaoPayCancle"; $fail_url = "https://".$this->input->server('HTTP_HOST')."/order/CallPaymentKakaoPayCancle"; $kakao_params = array( 'cid' => $cid, // 가맹점코드 10자 'partner_order_id' => $orderArr['order_id'], // 주문번호 'partner_user_id' => get_cookie('user_id'), // 유저 id 'item_name' => $orderArr['order_prd_nm'], // 상품명 'quantity' => $orderArr['ord_row'], // 상품 수량 'total_amount' => $orderArr['total_price'], // 상품 총액 'tax_free_amount' => '0', // 상품 비과세 금액 'approval_url' => $approval_url, // 결제성공시 콜백url 최대 255자 'cancel_url' => $cancel_url, 'fail_url' => $fail_url, ); //pre($kakao_params); $strArrResult = request_curl('https://kapi.kakao.com/v1/payment/ready', 1, http_build_query($kakao_params), $kakao_header); //pre($strArrResult); if( $strArrResult[3] != '200' ) { echo "<script>"; echo "alert('에러입니다. 관리자에게 문의하세요.');"; echo "</script>"; return; } $strArrResult = json_decode($strArrResult[0]); set_cookie("kakao_tid", $strArrResult->tid, 0); set_cookie("kakao_order_id", $orderArr['order_id'], 0); echo "<script>"; echo "var win = window.open('','','toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=no,width=540,height=700,left=100,top=100');"; echo "win.document.write('<iframe width=100%, height=650 src=".$strArrResult->next_redirect_pc_url." frameborder=0 allowfullscreen></iframe>')"; echo "</script>"; }
3. 결제창 호출후 결제 성공시 처리
<?php function CallPaymentKakaoPaySuccess() { $adminkey = $this->strKakaopayAdminkey; if( $this->config->item('kakaopay_mode') == 'service' ) { $cid = $this->strkakaopayCID; } else if( $this->config->item('kakaopay_mode') == 'test' ) { $cid = 'TC0ONETIME'; } $req_auth = 'Authorization: KakaoAK '.$adminkey; $req_cont = 'Content-type: application/x-www-form-urlencoded;charset=utf-8'; $kakao_header = array( $req_auth, $req_cont ); $kakao_params = array( 'cid' => $cid, // 가맹점코드 10자 'tid' => get_cookie('kakao_tid'), // 결제 고유번호. 결제준비 API의 응답에서 얻을 수 있음 'partner_order_id' => get_cookie('kakao_order_id'), // 가맹점 주문번호. 결제준비 API에서 요청한 값과 일치해야 함 'partner_user_id' => get_cookie('user_id'), // 가맹점 회원 id. 결제준비 API에서 요청한 값과 일치해야 함 'pg_token' => $this->input->get('pg_token') // 결제승인 요청을 인증하는 토큰. 사용자가 결제수단 선택 완료시 approval_url로 redirection해줄 때 pg_token을 query string으로 넘겨줌 //'payload' => , // 해당 Request와 매핑해서 저장하고 싶은 값. 최대 200자 ); $strArrResult = request_curl('https://kapi.kakao.com/v1/payment/approve', 1, http_build_query($kakao_params), $kakao_header); $IS_PAYMENT_SUCCESS = false; if( $strArrResult[3] != '200' ) { echo "<script>"; echo "alert('에러입니다. 관리자에게 문의하세요.');"; echo "window.parent.close();"; echo "</script>"; return; } $strArrResult = json_decode($strArrResult[0]); // LGD 로 쓰는 이유는 기존 table를 활용해서 같이쓰기위함. $paymentResultArr = Array ( 'LGD_TID' => $strArrResult->tid, // kakao 거래 고유 번호 'LGD_MID' => $strArrResult->cid, // 상점아이디 'LGD_OID' => $strArrResult->partner_order_id, // 상점주문번호 'LGD_AMOUNT' => $strArrResult->amount->total, // 결제금액 'LGD_RESPCODE' => '0000', // 결과코드 'LGD_RESPMSG' => '결제성공', // 결과메세지 'LGD_FINANCENAME' => $strArrResult->card_info->purchase_corp, // 은행명 'LGD_FINANCECODE' => $strArrResult->card_info->purchase_corp_code, // 은행코드 'LGD_PAYTYPE' => $strArrResult->payment_method_type, // 결제 방법 ( CARD, MONEY ) 'LGD_PAYDATE' => $strArrResult->approved_at, // 승인시간 (모든 결제 수단 공통) 'LGD_FINANCEAUTHNUM' => $strArrResult->card_info->approved_id, // 신용카드 승인번호 'LGD_CARDNOINTYN' => $strArrResult->card_info->interest_free_install, // 신용카드 무이자 여부 ( Y: 무이자, N : 일반) 'LGD_CARDINSTALLMONTH' => $strArrResult->card_info->install_month, // 신용카드 할부개월 ); // 트랜잭션 시작 $this->db->trans_begin(); //결재 정보 갱신 $this->AfterPaymentSuccess( $paymentResultArr ); if ($this->db->trans_status() == FALSE) { $this->db->trans_rollback(); } else { $IS_PAYMENT_SUCCESS = true; $this->db->trans_commit(); } if( $IS_PAYMENT_SUCCESS ) { /** * TODO * 주문 SMS 발송 */ $this->ordersms( get_cookie('kakao_order_id') ); $replace_url = HTTP_GURL . "/order/orderSuccess/".get_cookie('kakao_order_id'); delete_cookie("kakao_tid"); delete_cookie("kakao_order_id"); ?> <script language='javascript'> var childWindow = window.parent; var parentWindow = childWindow.opener; parentWindow.parent.location.replace("<?=$replace_url?>"); childWindow.close(); </script> <?php } } ?>
4. 결제 실패시 처리
<?php function CallPaymentKakaoPayCancle() { ?> <script language='javascript'> var childWindow = window.parent; alert('결제가 취소/실패 하였습니다.\n\n다시시도해주세요!'); childWindow.close(); </script> <?php } ?>
초반 삽질 여러번.... 결제창 호출이 안되서..
왜그런가 봤더니... test 모드 설정 실패... 은근 어렵네.
CID 발급만 2주... 이거 오래걸린다. 참고.
- Tag :
- 카카오페이 , kakao pay , 카카오페이 개발 , 카카오페이 api , 카카오페이 소켓통신 , 카카오페이 curl , php
Comments 17
-
보통 앞쪽에 결제 로직을 두고 뒤쪽에 DB 처리를 하는데, 이와 같이 하면, 예를 들어 결제에 성공했다고 할게요. 그런데 만약에 로컬 DB작업 (여기서는 AfterPaymentSuccess로 보입니다)에 실패하면 사용자한테는 실패했다고 뜰텐데요. (사실은 카카오페이 결제는 된 상태죠) 이런 경우는 어떻게 되는거죠? 그래서 제가 생각한 건 결제 모듈까지 DB 트랜젝션으로 묶고 DB 작업을 앞쪽에 두고, 결제를 뒤쪽에 두면... DB에 실패하면 결제 쪽으로 진행되지 않을 테고, DB 성공하고 결제 진행되는데 결제에서 실패하면 DB는 자동 롤백되지 않을까요? 실제 생각데로 해보진 않았습니다만...