버튼을 각 페이지, 취급가능상품에 알맞게 생성했다면, 주문등록하기 전에 프로세스를 먼저 살펴보겠다.
(이 글은 주문형v2.1 독립몰 개발을 목적으로 썼다😎)
3.0 주문 V2.1 프로세스
1. 기본 프로세스
1) 상품상세페이지, 장바구니에서 구매하기 버튼을 클릭
2) 네이버 주문 등록 URL로 주문정보(XML)를 전달
3) 네이버 주문 등록이 성공하면, 인증키, 가맹점번호를 리턴
4) 리턴받은 인증키, 가맹점번호로 네이버 주문서 호출 및 확인
2. 도서산간비 연동
도서산간비는 네이버페이 주문서에서 배송정보를 입력할 때 결정된다.
5) 주문서에 배송정보 입력
6) 네이버페이에서 가맹점(자사몰)로 도서산간 API 호출
7) 가맹점(자사몰)에서 네이버페이로 도서산간 비용 응답
👉 가맹점에서 도서산간API를 만들어야한다.
3. 구매제한
8) 고객이 결제를 시도할 때 네이버페이는 가맹점에 상품정보 XML을 요청하는데, 이 때 옵션정보, 가격, 수량, 상태값을 비교해서 결정한다.
고 하는데 아직은 잘 모르겠다.
3.1_네이버 주문 등록
본격적으로 시작해보자!
1) 상품상세페이지, 장바구니에서 구매하기 버튼을 클릭
페이버튼을 클릭하면 기본코드에서 buy_nc 함수를 실행한다.
buy_nc함수에서 옵션을 제대로 선택했는지 체크를 해주고, 주문등록을 위한 페이지로 이동한다.
여기부터 두번째 쓰는중..
※ 버튼을 삽입한 스크립트의 BUY_BUTTON_LINK_URL key의 값은 buy_nc의 url 파라미터로 들어간다.
우리 쇼핑몰에서는 옵션체크를 위해 서버를 갔다오기 때문에, 기존 옵션체크 함수에 url을 파라미터로 추가해서 플래그값으로 이동하게끔 했다.
<script type="text/javascript" >//<![CDATA[
naver.NaverPayButton.apply({
BUTTON_KEY: "111111C1-1111-1D11-A11C-111FBE11111C", // 네이버페이에서 제공받은 “버튼 인증 키” 입력.
TYPE: "E", // 버튼 모음 종류 설정. 모바일은 MA, MB 2가지만 사용.
COLOR: 1, // 버튼 모음의 색 설정.
COUNT: 2, // 쇼핑몰 상품상세(COUNT:2 사용), 장바구니(COUNT:1 사용) 페이지.
ENABLE: "Y", // 품절 등의 이유로 버튼 모음을 비활성화할 때에는 "N" 입력
BUY_BUTTON_HANDLER:buy_nc, // 구매하기 버튼 이벤트 Handler 함수 등록, 품절인 경우 not_buy_nc 함수 사용
BUY_BUTTON_LINK_URL:“naverpay_order.php", // 링크 주소 (필요한 경우만 사용)
WISHLIST_BUTTON_HANDLER:wishlist_nc, // 찜하기 버튼 이벤트 Handler 함수 등록
WISHLIST_BUTTON_LINK_URL:“zzim.php", // 찜하기 팝업 링크 주소 (필요한 경우만 사용)
"":""
});
//]]></script>
<script type="text/javascript">
function buy_nc(url)
{
//⭐1. 옵션 제대로 선택했는지 확인
var check = checkOption(document.getElementById("opt").color, 0, "컬러") && checkOption(document.getElementById("opt").size, 0, "사이즈");
if ( check ) {
//주문 정보를 등록하는 가맹점 페이지로 이동.
//해당 페이지에서 주문 정보 등록 후 네이버페이 주문서 페이지로 이동.
location.href=url; // naverpay_order.php 으로 주문등록을 위한 상품 ID 등 전달.
}
return false;
}
function not_buy_nc()
{
alert("죄송합니다. 네이버페이로 구매가 불가한 상품입니다.");
return false;
}
</script>
열심히 썼는데,, 다 날라갔다..^_^ 다시 시작점으로 왔네.. 자동 저장도 안되어있고 대체 ㅋㅋ 티스토리 딱 대..😡
2) 네이버 주문 등록 URL로 주문정보(XML)를 전달
여기까지 했으면, 주문등록 URL로 주문정보를 XML로 전달한다.
2-1) 주문정보(XML) 형식
주문정보 XML은 대략 아래와 같이 생겼지만,
네이버페이 가맹점 연동가이드 2.1 pdf 파일의 26-32페이지를 참고하여 샘플코드를 수정하며 작성하면 되겠다.
굳이 크게 나누자면
- 기본 정보 Elements
- 상품 정보 Elements - 단일, 옵션 각각 다름
- 옵션 정보 Elements
- 추가 상품 정보 Elements
- 배송 정보 Elements
- 도서산간비 정보 연동방법에 따른 Elements
- 네이버쇼핑을 사용할 경우 입력해야하는 연동 Elements
<order>
<merchantId>naver_pay</merchantId>
<certiKey>11112A2A-F985-2222-TEST-4D837D879A12</certiKey>
<product>
<id>1000000072</id>
<ecMallProductId>1000000072</ecMallProductId>
<name><![CDATA[테스트상품]]></name>
<basePrice>15000</basePrice>
<taxType>TAX</taxType>
<infoUrl><![CDATA[http://aaa.com/goods/goods_view.php?goodsNo=1000000072]]></infoUrl>
<imageUrl><![CDATA[http://aaa.com/data/commonimg/ico_noimg_35.gif]]></imageUrl>
<single>
<quantity>1</quantity>
</single>
<shippingPolicy>
<groupId>78</groupId> //묶음배송, 개별배송 처리
<method>DELIVERY</feePayType>
<feeType>CONDITIONAL_FREE</feeType> // 조건부무료배송 처리
<conditionalFree>
<basePrice>20000</basePrice>
</conditionalFree>
<feePayType>PREPAYED</feePayType>
<feePrice>5000</feePrice>
<surchargeByArea> //배송비
<apiSupport>true</apiSupprot> //도서산간비 연동방식1(API) 사용
</surchargeByArea>
</shippingPolicy>
</product>
<backUrl><![CDATA[http://aaa.com/goods/goods_view.php?goodsNo=1000000072]]></backUrl>
<interface>
<salesCode></salesCode>
<cpaInflowCode></cpaInflowCode>
<naverInflowCode></naverInflowCode>
<saClickId></saClickId>
<merchantCustomCode1>1606031026193103</merchantCustomCode1>
<merchantCustomCode2>2420</merchantCustomCode2>
</interface>
</order>
※ <interface> 태그에 들어가야하는 각 쿠키값은, 네이버공통 유입경로 스크립트를 잘 설치했다면 아래와 같이 넣어주면 된다.
<interface>
...
<cpaInflowCode>$_COOKIE["CPAValidator"];</cpaInflowCode>
<naverInflowCode>$_COOKIE["NA_CO"];</naverInflowCode>
<saClickId>$_COOKIE["NVADID"];</saClickId>
</interface>
⭐PHP로 주문 정보 등록하기 샘플코드
<?
/***** 주문등록 API 요청 PHP 버전 ******/
// 상품 상세 페이지의 상품 정보 데이터를 가져온다(가맹점에서 작성).
function getProductInfo($productId) {
return new ProductInfo();
}
// 상품 상세 페이지의 반송 정보 데이터를 가져온다(가맹점에서 작성).
function getReturnInfo($productId) {
// 상품 정보당 한 개의 반송 정보를 가져온다.
return new ReturnInfo();
}
// 상품 상세 페이지의 배송 정책 데이터를 가져온다(가맹점에서 작성).
function getShippingPolicy($productId) {
// 상품 정보당 한 개의 배송 정책을 가져온다.
return new ShippingPolicy();
}
// 상품 상세 페이지의 연동 정보를 가져온다(가맹점에서 작성).
function getInterfaceInfo() {
return new InterfaceInfo();
}
/***** 해당 상품이 조합형 옵션인지 단독형 옵션인지 판단하는 내용가맹점에서 작성) **/
function isCombinationYn($productId) {
// 해당 옵션이 조합형일 경우 true, 단독형인 경우 false를 반환
// 조합형: 조합된 옵션으로 코드 관리, 조합된 옵션으로 가격을 가지고 있음. 조합된 옵션별 재고관리
// 단독형: 하나의 옵션으로 코드 관리, 옵션 개별로 가격이 없음. 옵션별 재고관리 없음(상품별 재고 관리)
return true;
}
// 상품 상세 페이지의 선택된 옵션 정보 선택형을 가져온다(가맹점에서 작성).
function getOptionList($productId) {
// 단독 옵션 전체 정보를 가져온다. 아래는 Mock 데이터로 특정 상품 id에 해당하는 옵션 정보만 준다.
// 옵션 정보는 상품 번호당 복수건이 들어올 수 있다.
$arr = array();
if ($productId == "181") {
$option = new Option();
$option->optionName = "색상";
$option->optionContentId = "R";
$option->optionContent = "빨강";
$arr[$option->optionName][0]= $option;
$option = new Option();
$option->optionName = "색상";
$option->optionContentId = "B";
$option->optionContent = "파랑";
$arr[$option->optionName][1]= $option;
$option = new Option();
$option->optionName = "색상";
$option->optionContentId = "Y";
$option->optionContent = "노랑";
$arr[$option->optionName][2]= $option;
$option = new Option();
$option->optionName = "사이즈";
$option->optionContentId = "S";
$option->optionContent = "Small";
$arr[$option->optionName][3]= $option;
$option = new Option();
$option->optionName = "사이즈";
$option->optionContentId = "M";
$option->optionContent = "Medium";
$arr[$option->optionName][4]= $option;
$option = new Option();
$option->optionName = "사이즈";
$option->optionContentId = "L";
$option->optionContent = "Large";
$arr[$option->optionName][5]= $option;
}
return $arr;
}
// 상품 상세 페이지의 선택된 옵션 정보 입력형을 가져온다(가맹점에서 작성).
function getInsertOptionList($productId) {
$arr = array();
$option = new Option();
$option->optionName = "카드메시지";
$option->optionType = "INPUT";
$option->optionContent = "당신과 100일을 함께해서 행복했어요.";
$arr[$option->optionName][0]= $option;
return $arr;
}
// 상품 상세 페이지에의 선택된 조합 옵션 정보를 가져온다(가맹점에서 작성).
function getCombinationOptionList($productId) {
// 조합 옵션은 조합의 순서도 중요함(매핑 데이터를 가지고 있어야 하기 때문에).
$arr = array();
if ($productId == "181") {
$combination = new CombinationOption();
$arr[$combination->combinationOptionContentId] = $combination;
$combination = new CombinationOption();
$combination->combinationOptionContentId = "R_S";
$combination->combinationOptionPrice = 1000;
$arr[$combination->combinationOptionContentId] = $combination;
$combination = new CombinationOption();
$combination->combinationOptionContentId = "B_M";
$combination->combinationOptionPrice = 0;
$arr[$combination->combinationOptionContentId] = $combination;
}
return $arr;
}
// 상품 상세 페이지에서 선택된 추가 상품 정보를 가져온다(가맹점에서 작성).
function getSupplement($productId) {
// 추가 상품 정보를 가져온다. 아래는 Mock 데이터로 특정 상품 id에 해당하는 추가 상품 정보만 준다.
$arr = array();
if ($productId == "181") {
for ($i = 0; $i < 2; $i++) {
$supplement = new Supplement();
$arr[$i] = $supplement;
}
}
return $arr;
}
// 상품정보
Class ProductInfo {
var $id;
var $merchantProductId;
var $ecMallProductId;
var $name;
var $basePrice;
var $taxType;
var $infoUrl;
var $imageUrl;
var $giftName;
var $stockQuantity;
var $status;
var $supplmentSupport;
var $optionSupport;
function ProductInfo() {
// 상품 기본 정보로 id 단위로 구성된다. 아래는 Mock 데이터
$this->id = "181";
$this->merchantProductId = "181";
$this->ecMallProductId = "181";
$this->name = "티셔츠";
$this->basePrice = 10000;
$this->taxType = "TAX";
$this->infoUrl = "http://가맹점상품URL";
$this->imageUrl = "http://가맹점이미지URL";
$this->giftName = "사은품";
$this->stockQuantity = 100;
$this->status = "ON_SALE";
$this->supplmentSupport = false;
$this->optionSupport = false;
}
};
// 반송 정보
Class ReturnInfo {
var $productId;
var $zipcode;
var $address1;
var $address2;
var $sellername;
var $contact1;
var $contact2;
// Test용 Mock 데이터
function ReturnInfo() {
$this->productId = "181";
$this->zipcode = "13591";
$this->address1 = "경기도 성남시 분당구 서현동";
$this->address2 = "266-1";
$this->sellername = "테스트가맹점명";
$this->contact1 = "01099999999";
$this->contact2 = "01099999999";
}
};
// 배송 정책
Class ShippingPolicy {
var $groupId;
var $method;
var $feeType;
var $feePayType;
var $feePrice;
var $conditionFreeDeliveryPrice;
var $chargeByQuantityType;
var $chargeByQuantityRepeatQuantity;
var $chargeByQuantityRangeType;
var $chargeByQuantityRange2From;
var $chargeByQuantityRange2FeePrice;
var $chargeByQuantityRange3From;
var $chargeByQuantityRange3FeePrice;
var $areaChargeApiSupport;
var $areaChargeSplitUnit;
var $areaCharge2Price;
var $areaCharge3Price;
function ShippingPolicy() {
$this->groupId = "10000";
$this->method = "DELIVERY";
$this->feeType = "CHARGE";
$this->feePayType = "PREPAYED";
$this->feePrice = 2500;
$this->areaChargeApiSupport = "false";
$this->areaCharge2Price = 0;
$this->areaCharge3Price = 0;
}
};
// 추가 상품
Class Supplement {
var $productId;
var $supplementId;
var $supplementName;
var $supplementPrice;
var $supplementStockQuantity;
var $supplementStatus;
function Supplement() {
$this->productId = "181";
$this->supplementId = "1";
$this->supplementName = "추가상품";
$this->supplementPrice = 1000;
$this->supplementStockQuantity = 20;
$this->supplementStatus = "true";
}
};
// 일반 옵션(선택형/입력형)
Class Option {
var $productId;
var $optionId;
var $optionName;
var $optionType;
var $optionContent;
var $optionContentId;
var $optionPrice;
var $optionQuantity;
var $optionYn;
function Option() {
$this->productId = "181";
$this->optionid = "1";
$this->optionName = "색상";
$this->optionType = "SELECT";
$this->optionContent = "빨강";
$this->optionContentId = "R";
$this->optionQuantity = "10";
$this->optionYn = "true";
}
};
// 조합 옵션
Class CombinationOption {
var $productId;
var $combinationOptionId;
var $combinationOptionName;
var $combinationOptionContent;
var $combinationOptionContentId;
var $combinationOptionPrice;
var $combinationOptionQuantity;
var $combinationOptionYn;
function CombinationOption() {
$this->productId = "181";
$this->combinationOptionId = "1";
$this->combinationOptionName = "색상_사이즈";
$this->combinationOptionContent = "빨강_Large";
$this->combinationOptionContentId = "R_L";
$this->combinationOptionPrice = "1000";
$this->combinationOptionQuantity = "10";
$this->combinationOptionYn = "true";
}
};
// 연동 정보 정보
Class InterfaceInfo {
var $salesCode;
var $cpaInflowCode;
var $naverInflowCode;
var $saClickId;
function InterfaceInfo() {
$this->salesCode = "1111";
$this->cpaInflowCode = "aaa";
$this->naverInflowCode = "ccc";
$this->saClickId = "dddd";
}
};
// 가맹점 상품 상세 페이지에서 네이버페이 [구매하기] 버튼을 눌렀을 경우,
// 주문 등록을 위한 API 요청을 네이버페이로 수행
// 주문 등록 API 정보는 상품 정보 API와 유사하나 일부 요소의 명칭이나 필수 항목이 다르기 때문에 유의해야 함.
$productIdList = array(0 => "181"); // Mock productId정보(장바구니에서 주문 등록 요청 시에는 복수 건일 수 있으므로 배열로 저장.)
$merchantId = "kcp_16";
$certiKey = "C791E239-29FB-43E4-9726-8C96D49A783A";
$backUrl = "http://www.naver.com";
$interfaceInfo = getInterfaceInfo();
header('Content-Type: application/xml;charset=utf-8');
$data = '<?xml version="1.0" encoding="utf-8"?>';
$data .= '<order>';
$data .= '<merchantId>' . $merchantId . '</merchantId>';
$data .= '<certiKey>' . $certiKey . '</certiKey>';
$data .= '<backUrl><![CDATA[' . $backUrl.']]></backUrl>';
if ($interfaceInfo != null) {
$data .= '<interface>';
$data .= '<salesCode>' .$interfaceInfo->salesCode. '</salesCode>';
$data .= '<cpaInflowCode>'. $interfaceInfo->cpaInflowCode. '</cpaInflowCode>';
$data .= '<naverInflowCode>'. $interfaceInfo->naverInflowCode. '</naverInflowCode>';
$data .= '<saClickId>'. $interfaceInfo->saClickId. '</saClickId>';
$data .= '</interface>';
}
foreach ($productIdList as $productId) {
// productId를 이용하여 상품 상세페이지나 가맹점 장바구니의 상품정보를 가져온다.
$product = getProductInfo($productId);
$returnInfo = getReturnInfo($productId);
$shippingPolicy = getShippingPolicy($productId);
$optionList = getOptionList($productId);
$insertOptionList = getInsertOptionList($productId);
$combinationOptionList = getCombinationOptionList($productId);
$supplements = getSupplement($productId);
// 파라미터 정보를 이용하여 가맹점 데이터를 가지고 온 후 XML로 구성
$data .= '<product>';
$data .= '<id>' .$productId. '</id>';
$data .= '<merchantProductId>' .$product->merchantProductId. '</merchantProductId>';
$data .= '<ecMallProductId>' .$product->ecMallProductId. '</ecMallProductId>';
$data .= '<name><![CDATA[' .$product->name. ']]></name>';
$data .= '<basePrice>' .$product->basePrice. '</basePrice>';
$data .= '<taxType>' .$product->taxType. '</taxType>';
$data .= '<infoUrl><![CDATA[' .$product->infoUrl. ']]></infoUrl>';
$data .= '<imageUrl><![CDATA[' .$product->imageUrl. ']]></imageUrl>';
$data .= '<giftName><![CDATA[' .$product->giftName. ']]></giftName>';
// 옵션 정보가 없는 본상품 주문일 경우는 <single> 요소가 반드시 들어가야 한다.
if ($optionList == null && $insertOptionList == null && $combinationOptionList == null ) {
$data .= '<single>';
$data .= '<quantity>' .$product->stockQuantity. '</quantity>';
$data .= '</single>';
}
if ($shippingPolicy != null) { // 배송 정책 정보
$data .= '<shippingPolicy>';
$data .= '<groupId>' .$shippingPolicy->groupId. '</groupId>';
$data .= '<method>' .$shippingPolicy->method. '</method>';
$data .= '<feePayType>' .$shippingPolicy->feePayType. '</feePayType>';
$data .= '<feeType>' .$shippingPolicy->feeType. '</feeType>';
$data .= '<feePrice>' .$shippingPolicy->feePrice. '</feePrice>';
if ( $shippingPolicy->feeType == "CONDITIONAL_FREE") {
$data .= '<conditionalFree>';
$data .= '<basePrice>' .$shippingPolicy->conditionFreeDeliveryPrice. '</basePrice>';
$data .= '</conditionalFree>';
} else if ($shippingPolicy->feeType == "CHARGE_BY_QUANTITY") {
$data .= '<chargeByQuantity>';
$data .= '<type>' .$shippingPolicy->chargeByQuantityType. '</type>';
if ($shippingPolicy->chargeByQuantityType == "REPEAT") {
$data .= '<repeatQuantity>' .$shippingPolicy->chargeByQuantityRepeatQuantity. '</repeatQuantity>';
} else if ($shippingPolicy->chargeByQuantityType == "RANGE") {
$data .= '<range>';
$data .= '<type>' .$shippingPolicy->chargeByQuantityRangeType. '</type>';
if ($shippingPolicy->chargeByQuantityRangeType == "2") {
$data .= '<range2From>' .$shippingPolicy->chargeByQuantityRange2From. '</range2From>';
$data .= '<range2FeePrice>' .$shippingPolicy->chargeByQuantityRange2FeePrice. '</range2FeePrice>';
} else if ($shippingPolicy->chargeByQuantityRangeType == "3") {
$data .= '<range3From>' .$shippingPolicy->chargeByQuantityRange3From. '</range3From>';
$data .= '<range3FeePrice>' .$shippingPolicy->chargeByQuantityRange3FeePrice. '</range3FeePrice>';
}
$data .= '</range>';
}
$data .= '</chargeByQuantity>';
}
if ($shippingPolicy->areaChargeSplitUnit != null) {
$data .= '<surchargeByArea>';
$data .= '<apiSupport>' .$shippingPolicy->areaChargeApiSupport. '</apiSupport>';
$data .= '<splitUnit>' .$shippingPolicy->areaChargeSplitUnit. '</splitUnit>';
$data .= '<area2Price>' .$shippingPolicy->areaCharge2Price. '</area2Price>';
$data .= '<area3Price>' .$shippingPolicy->areaCharge3Price. '</area3Price>';
$data .= '</surchargeByArea>';
}
$data .= '</shippingPolicy>';
}
// 상품 상세에서 선택된 옵션 정보가 있을 경우 옵션요소를 구성한다. 상품 상세에서 선택되는 옵션은 단독형, 조합형 관계없이 조합형 형태로 보여진다.
// 단, 단독형 옵션일 경우 옵션 자체에서 관리코드(<manageCode>)나 가격(<price>)을 관리하지 않으므로 요소에서 제외된다.
if ($optionList != null || $insertOptionList != null || $combinationOptionList != null) {
$optionListKeys = array_keys($combinationOptionList);
if ($combinationOptionList != null && count($combinationOptionList) > 1) {
for ($i=0 ; $i < count($combinationOptionList); $i++) { $data .='<option>' ; // 해당 상품이 조합형 옵션일 경우 로직수행
if (isCombinationYn($productId)) {
$data .='<price>'.$combinationOptionList[$optionListKeys[$i]]->combinationOptionPrice. '</price>';
$data .= '<manageCode><![CDATA[' .$combinationOptionList[$optionListKeys[$i]]->combinationOptionContentId. ']]></manageCode>';
}
$data .= '<quantity>' .$combinationOptionList[$optionListKeys[$i]]->combinationOptionQuantity. '</quantity>';
$count = 0;
foreach (explode("_", $combinationOptionList[$optionListKeys[$i]]->combinationOptionName) as $combinationName) {
$contentIdList = explode("_", $combinationOptionList[$optionListKeys[$i]]->combinationOptionContentId);
$contentList = explode("_", $combinationOptionList[$optionListKeys[$i]]->combinationOptionContent);
$data .= '<selectedItem>';
$data .= '<type>SELECT</type>';
$data .= '<name><![CDATA[' .$combinationName. ']]></name>';
$data .= '<value>';
$data .= '<id>' .$contentIdList[$count]. '</id>';
$data .= '<text><![CDATA[' .$contentList[$count]. ']]></text>';
$data .= '</value>';
$data .= '</selectedItem>';
$count++;
}
$data .= '</option>';
}
}
}
foreach ($supplements as $supplement) {
$data .= '<supplement>';
$data .= '<id>' .$supplement->supplementId. '</id>';
$data .= '<name><![CDATA[' .$supplement->supplementName. ']]></name>';
$data .= '<price>' .$supplement->supplementPrice. '</price>';
$data .= '<quantity>' .$supplement->supplementStockQuantity. '</quantity>';
$data .= '</supplement>';
}
$data .= '</product>';
}
//end while;
$data .= '</order>';
echo($data);
// 주문 등록 API 호출
$url = "https://test-api.pay.naver.com/o/customer/api/order/v20/register";
$ci = curl_init();
$headers = array('Content-Type: application/xml; charset=utf-8');
curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ci, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ci, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ci, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ci, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
curl_setopt($ci, CURLOPT_URL, $url);
curl_setopt($ci, CURLOPT_POST, TRUE);
curl_setopt($ci, CURLOPT_TIMEOUT, 10);
curl_setopt($ci, CURLOPT_POSTFIELDS, $data);
// 주문 등록 후 결과값 확인
$response = curl_exec($ci);
curl_close ($ci);
$param = explode(':', $response);
if ($param[0] == "SUCCESS") { // 성공일 경우
$requestParam = "/".$param[1]."/".$param[2];
}
// 주문서 URL 재전송(redirect)
$redirectUrl = "https://test-order.pay.naver.com/customer/buy".$requestParam;
header("Location:".$redirectUrl);
exit();
?>
2-2) 이제 슬슬 정해야하는 도서산간비 연동 방법
xml을 열심히 작성하다보면, <surchargeByArea> <apiSupport> Elements를 마주하게 된다.
도서산간비 연동 방법에는 두 가지가 있다.
1. 도서산간 API 활용하기
API를 활용하면 가맹점의 정보가 업데이트 되었을 때 네이버페이 배송비는 따로 수정해주지 않아도 된다는 장점이 있다.
네이버페이에서 <apiSupport> Elements가 true인 것을 확인해 상품별 배송비 정보를 요청하면, 가맹점에서는 상품별 배송비 정보를 조회해 응답한다.
먼저, 주문정보 XML에 아래처럼 <apiSupport> Elements를 true로 추가한다. API를 사용하겠다는 뜻이다.
<product>
<shippingPolicy>
<surchargeByArea>
<apiSupport>true</apiSupprot>
</surchargeByArea>
</shippingPolicy>
</product>
네이버페이 주문서에서 도서산간정보를 요청할 때 응답해줄 가맹점의 URL페이지를 만들어야 한다.
▶ 네이버페이에서 가맹점으로 도서산간비 정보를 요청하는 형식
▶ 가맹점에서 네이버페이로 도서산간비 정보XML 전송
<?php
/***** 도서산간비 정보 API 응답 PHP 버전 ******/
// 기본주소정보 base64 Decoding Util Function
function base64url_decode($base64url)
{
$base64 = strtr($base64url, '-_', '+/');
$plainText = base64_decode($base64);
return ($plainText);
}
// 파라미터로 들어온 주소가 정확한 주소인지 판단하여 배송비를 반환
function addressValidation($zipcode, $address1, $productId) {
// 가맹점에서 내부 로직 작성 필요
// 배송비 정책에 따라서 해당하는 상품 번호의 배송비는 달라질 수 있다.
return 1000;
}
// product 정보를 가져온다.
$productList = $_GET['productId'];
$query = $_SERVER['QUERY_STRING'];
$vars = array();
foreach(explode('&', $query) as $pair) {
list($key, $value) = explode('=', $pair);
$key = urldecode($key);
$value = urldecode($value);
$vars[$key][] = $value;
}
// 도서산간비 API는 상품 id, 우편번호, 기본주소정보를 모두 필수값으로 받는다.
if (count($productList) < 1) {
exit('product정보는 필수입니다.');
}
if ($_GET['zipcode'] < 1) {
exit('우편번호는 필수입니다.');
}
if ($_GET['address1'] < 1) {
exit('기본주소정보는 필수입니다.');
}
// address1은 UTF-8로 Decoding 하여 base64 url encoding 방식으로 전달함.
$decodeString = base64url_decode($_GET['address1']);
// UTF-8 환경인 경우
$utf8string = $decodeString;
// EUC-KR 환경인 경우
$euckrstring = iconv("utf-8", "euc-kr", $decodeString);
header('Content-Type: application/xml;charset=utf-8');
echo ('<?xml version="1.0" encoding="utf-8"?>');
echo('<additionalFees>');
foreach ($productList as $productId) {
$surprice = addressValidation($_GET['zipcode'], $utf8string, $productId);
?>
<additionalFee>
<id><?=$productId ?></id><!-- 필수값 -->
<surprice><?=$surprice ?></surprice> <!-- 필수값 -->
</additionalFee>
<?
}
echo('</additionalFees>');
?>
2. 네이버페이 권역별 배송비 활용
- 장점: API사용보다 비교적 간단하게 청구할 수 있다.
- 단점: 배송비가 변경될 경우, xml소스도 변경해줘야한다.
주문등록 Xml에 아래와 같이 작성할 경우,
제주도를 의미하는 2권역에서 3000원, 제주도 외 도서산간 지역을 의미하는 3권역에서 5000원의 추가배송비를 청구할 수 있다.
<surchargeByaArea>
<splitUnit>3</splitUnit>
<area2Price>3000</area2Price>
<area3Price>5000</area3Price>
</surchargeByaArea>
다행인지 불행인지 현재 가맹점에서 도서산간지역 추가배송비가 개발되어있지 않아서 못받고 있다고 한다.. 제주도 배송이 거의 없어서 문제가 안되지만 추후에 일정을 잡아서 따로 개발해달라는 상황이다. 로젠택배를 사용한다고 하니, 로젠택배에서 도서산간지역 API를 연동해서 쇼핑몰 5개에🙂 적용해주어야 한다. 추후에.
이제 저 주문등록 XML을 어떻게 상품마다 다르게 만들 것이냐가 관건인데,
이동하는 페이지로 상품코드를 넘겨줘서 주문페이지를 참고하여 XML을 만들어주면 될 것 같다.
옵션상품이 진짜 큰 문제지만, 가보자구2222
4_ 상품정보 XML
5_MOBILE 버전 개발
'WEB' 카테고리의 다른 글
[네이버페이] 쇼핑몰 연동하기 -2.네이버페이 버튼 스크립트 설치 (1) | 2023.03.09 |
---|---|
[네이버페이] 쇼핑몰 연동하기 -1.네이버 공통유입경로 스크립트 설치 (0) | 2023.02.28 |