# HotoPay 개발 문서

Hotopay 개발을 위한 문서입니다

<div align="left"><figure><img src="https://1344275727-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWxHg8aj5Z9CsQwEb8gQQ%2Fuploads%2FIjcnhBHjIQwhpoyHEeZ1%2Fb9a60bd654e49e09a42e6bb616303422.png?alt=media&amp;token=2cf3e101-3adf-4d99-ac85-e6c87ef11f7f" alt="" width="188"><figcaption></figcaption></figure></div>

### 개요

Hotopay를 활용하여 개발하고자 하는 개발자들을 위한 문서입니다.


# Cart 기능

카트 기능을 활용하기 위한 기능명세서입니다.

Hotopay에는 카트 기능이 있습니다.

<figure><img src="https://1344275727-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWxHg8aj5Z9CsQwEb8gQQ%2Fuploads%2FVPsb2MVVU3slvRWS41Bn%2F%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202023-07-16%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%202.30.32.png?alt=media&amp;token=43ea2234-e9dc-4499-8204-2695b56fa21c" alt=""><figcaption></figcaption></figure>

이 기능을 일반 게시판에서 사용하기 위한 문서입니다

### 상품 추가 예시

게시판 스킨(게시글 조회 페이지)에 넣을 예시입니다.

<figure><img src="https://1344275727-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWxHg8aj5Z9CsQwEb8gQQ%2Fuploads%2FjcVkHDM56YZCpKKiKfCF%2Fimage.png?alt=media&amp;token=9e2b9dd7-cc33-452b-819a-98dda4c482ea" alt=""><figcaption><p>예시</p></figcaption></figure>

```
{@
  $oHotopayModel = HotopayModel::getInstance();
  $oProduct = $oHotopayModel->getProductByDocumentSrl($oDocument->get('document_srl'));
  $options = $oProduct->product_option;
}

<select id="option_srl">
  <!--@foreach($options as $option)-->
  {@
      $sub_price = $option->price - $oProduct->product_sale_price;
      $sub_price = $sub_price + round($sub_price * ($oProduct->tax_rate / 100));
      if($sub_price > 0) $sub_price = "(+".number_format($sub_price)."원)";
      else if($sub_price < 0) $sub_price = "(".number_format($sub_price)."원)";
      else $sub_price = "";

      $stock_text = "";
      if($option->infinity_stock != 'Y'):
          if($option->stock < 1) $stock_text = " (품절)";
          else $stock_text = " (".$option->stock."개)";
      endif;
  }
  <option value="{$option->option_srl}" data-hotopay-option-price="{$option->price + round($option->price * ($product->tax_rate / 100))}" disabled|cond="$option->infinity_stock != 'Y' && $option->stock < 1">{$option->title} {$sub_price}{$stock_text}</option>
  <!--@endforeach-->
</select>
<p><input type="number" id="quantity" value="1"></p>
<p><button class="addCart" data-product-srl="{$oProduct->product_srl}"><i class="ico cart"></i> 카트에 담기</button></p>

<script>
jQuery(document).ready(function($) {
  // addCart
  $('.addCart').click(function() {
    var product_srl = $(this).data('product-srl');
    var option_srl = $('#option_srl').val();
    var quantity = $('#quantity').val();

    console.log(product_srl, option_srl, quantity);
    $.ajax({
      url: '/hotopay/cart/add',
      type: 'POST',
      data: {
        'product_srl': product_srl,
        'option_srl': option_srl,
        'quantity': quantity
      },
      success: function(data) {
        if (data.error == 0)
        {
          alert('장바구니에 추가되었습니다.');
          window.location = '/hotopay/cart';
        }
        else
        {
          if (data.message == '이미 장바구니에 담겨있는 상품입니다.')
          {
            window.location = '/hotopay/cart';
            return;
          }

          alert(data.message);
        }
      }
    });
  });
});
</script>
```

***

## API 문서

### 카트에 아이템 추가

`/hotopay/cart/add` : 카트에 추가&#x20;

Request Type: `application/json`

```
{
    "product_srl": Hotopay 상품 번호 (int)
    "option_srl": Hotopay 옵션 번호 (int)
    "quantity": 수량 (int)
}
```

Response Type: `application/json`

```
{
    "error": 0,
    "message": "장바구니에 추가되었습니다."
}
```

<table><thead><tr><th width="297.5">메세지 종류</th><th>설명</th></tr></thead><tbody><tr><td>장바구니에 추가되었습니다.</td><td>장바구니에 아이템이 정상적으로 추가됨.</td></tr><tr><td>필수 정보가 없습니다.</td><td><code>product_srl</code>, <code>option_srl</code>, <code>quantity</code> 중 하나 이상의 값이 누락됨.</td></tr><tr><td>로그인이 필요합니다.</td><td>로그인이 되지 않은 상태로 아이템을 추가하려함.</td></tr><tr><td>장바구니에는 최대 n개의 상품만 담을 수 있습니다.</td><td><p>장바구니 한도를 넘어서 아이템을 추가하려고 시도함.</p><p>상향이 필요한 경우 Hotopay 설정에서 장바구니 한도를 변경할 수 있음.</p></td></tr><tr><td>상품 정보가 없습니다.</td><td><code>product_srl</code>로 정보를 찾을 수 없음.</td></tr><tr><td>옵션 정보가 없습니다.</td><td><code>option_srl</code>로 정보를 찾을 수 없음.</td></tr><tr><td>상품과 옵션이 일치하지 않습니다.</td><td>선택한 옵션이 상품의 옵션과 일치하지 않음.</td></tr><tr><td>이미 장바구니에 담겨있는 상품입니다.</td><td>이미 장바구니에서 해당 상품, 옵션 조합이 추가되어 있음.</td></tr></tbody></table>

### 카트에서 아이템 삭제

Request Type: `application/json`

```
{
    "cart_item_srl": 카트 아이템 번호 (int)
}
```

Response Type: `application/json`

```
{
    "error": 0,
    "message": "장바구니에서 삭제되었습니다."
}
```

<table><thead><tr><th width="297.5">메세지 종류</th><th>설명</th></tr></thead><tbody><tr><td>장바구니에서 삭제되었습니다.</td><td>장바구니에서 아이템을 삭제함.</td></tr><tr><td>필수 정보가 없습니다.</td><td><code>cart_item_srl</code>이 누락됨.</td></tr><tr><td>로그인이 필요합니다.</td><td>로그인이 되지 않은 상태로 아이템을 추가하려함.</td></tr></tbody></table>

### 카트에서 아이템 업데이트

Request Type: `application/json`

```
{
    "cart_item_srl": 카트 아이템 번호 (int)
    "option_srl": Hotopay 옵션 번호 (int)
    "quantity": 수량 (int)
}
```

Response Type: `application/json`

```
{
    "error": 0,
    "message": "장바구니가 수정되었습니다."
}
```

<table><thead><tr><th width="297.5">메세지 종류</th><th>설명</th></tr></thead><tbody><tr><td>장바구니가 수정되었습니다.</td><td>장바구니에서 아이템을 수정함.</td></tr><tr><td>필수 정보가 없습니다.</td><td><code>product_srl</code>, <code>option_srl</code>, <code>quantity</code> 중 하나 이상의 값이 누락됨.</td></tr><tr><td>로그인이 필요합니다.</td><td>로그인이 되지 않은 상태로 아이템을 추가하려함.</td></tr></tbody></table>


# 트리거 목록

Hotopay에 있는 트리거 목록입니다.

### hotopay.updatePurchaseStatus after

결제 완료 혹은 결제 취소 등 `결제 상태`가 바뀔때마다 호출됩니다.

<table><thead><tr><th width="177">변수명</th><th width="134">타입</th><th>설명</th></tr></thead><tbody><tr><td><code>purchase_srl</code></td><td>int</td><td>결제 번호입니다. 번호 앞자리에 <code>HT</code>를 붙이면 Hotopay 외부에서 사용되는 결제 번호가 됩니다. (ex.<code>HT1234</code>)</td></tr><tr><td><code>pay_status</code></td><td>string</td><td>결제 상태입니다. 상세 값은 코드테이블에서 <code>PayStatus</code>를 확인해주세요</td></tr><tr><td><code>pay_pg</code></td><td>string</td><td>PG사입니다. 상세 값은 코드테이블에서 <code>PG</code>를 확인해주세요</td></tr><tr><td><code>pay_data</code></td><td>object</td><td>결제 데이터입니다. PG사에서 넘어온 값을 그대로 오브젝트로 리턴합니다.</td></tr><tr><td><code>amount</code></td><td>int</td><td>최종 결제 금액입니다.</td></tr></tbody></table>

### hotopay.activePurchase before

결제 완료 이후 그룹 부여등 결제 후 작업을 진행하기 전에 호출되는 트리거입니다.

BaseObject(-1) 을 리턴받을 경우 결제완료 작업을 진행하지 않습니다.

<table><thead><tr><th width="175">변수명</th><th width="133">타입</th><th>설명</th></tr></thead><tbody><tr><td><code>member_srl</code></td><td>int</td><td>결제한 유저의 고유번호입니다.</td></tr><tr><td><code>purchase_srl</code></td><td>int</td><td>결제 번호입니다.</td></tr></tbody></table>

### hotopay.activePurchase after

결제 완료 후 그 부여 등 작업을 진행하고 호출되는 트리거입니다.

<table><thead><tr><th width="175">변수명</th><th width="133">타입</th><th>설명</th></tr></thead><tbody><tr><td><code>member_srl</code></td><td>int</td><td>결제한 유저의 고유번호입니다.</td></tr><tr><td><code>purchase_srl</code></td><td>int</td><td>결제 번호입니다.</td></tr><tr><td><code>group_srls</code></td><td>array(int, ..)</td><td>구매한 상품에 대해서 부여된 그룹 번호입니다.</td></tr></tbody></table>

### hotopay.refundPurchase after

Hotopay 관리자 페이지 혹은 PG사에서 환불 완료 후 호출되는 트리거입니다.

<table><thead><tr><th width="175">변수명</th><th width="133">타입</th><th>설명</th></tr></thead><tbody><tr><td><code>member_srl</code></td><td>int</td><td>환불한 유저의 고유번호입니다.</td></tr><tr><td><code>purchase_srl</code></td><td>int</td><td>환불된 결제 번호입니다.</td></tr></tbody></table>

### hotopay.renewSubscription before

Cron에서 결제를 진행하기 전에 호출되는 트리거입니다.

BaseObject(-1) 을 리턴받을 경우 정기결제를 취소하며, 결제를 진행하지 않습니다.

<table><thead><tr><th width="177">변수명</th><th width="134">타입</th><th>설명</th></tr></thead><tbody><tr><td><code>subscription_srl</code></td><td>int</td><td>정기결제 데이터 번호입니다. 결제 번호와 다릅니다.</td></tr><tr><td><code>member_srl</code></td><td>int</td><td>정기결제 데이터를 등록한 유저의 고유번호입니다.</td></tr><tr><td><code>pg</code></td><td>string</td><td>PG사입니다. 상세 값은 코드테이블에서 <code>PG</code>를 확인해주세요</td></tr><tr><td><code>price</code></td><td>int</td><td>정기결제 금액입니다.</td></tr><tr><td><code>product_srl</code></td><td>int</td><td>상품 번호입니다.</td></tr><tr><td><code>option_srl</code></td><td>int</td><td>옵션 번호입니다.</td></tr><tr><td><code>quantity</code></td><td>int</td><td>수량입니다.</td></tr><tr><td><code>billing_key_idx</code></td><td>int</td><td>빌링키 번호입니다.</td></tr><tr><td><code>register_date</code></td><td>datetime (Y-m-d H:i:s)</td><td>정기결제가 시작된 날짜입니다.</td></tr><tr><td><code>esti_billing_date</code></td><td>datetime (Y-m-d H:i:s)</td><td>예정된 정기결제 날입니다.</td></tr></tbody></table>

### hotopay.renewSubscription after

Cron에서 결제를 진행한 후에 호출되는 트리거입니다.

`hotopay.renewSubscription before` 데이터에 아래 데이터가 추가로 붙어 나옵니다.

<table><thead><tr><th width="179.33333333333331">변수명</th><th width="133">타입</th><th>설명</th></tr></thead><tbody><tr><td><code>purchase_srl</code></td><td>int</td><td>결제 번호입니다.</td></tr><tr><td><code>billing_status</code></td><td>string</td><td>빌링 상태입니다. 상세 값은 코드테이블에서 <code>BillingStatus</code>를 확인해주세요</td></tr><tr><td><code>esti_billing_date</code></td><td>datetime (Y-m-d H:i:s)</td><td>갱신된 다음 결제일입니다.</td></tr></tbody></table>

### hotopay.cron after

Cron이 완전히 실행된 이후 실행되는 트리거입니다. (1.4.7 이후 추가)

별도의 추가 데이터는 없습니다.

<table><thead><tr><th width="203.33333333333331">변수명</th><th width="133">타입</th><th>설명</th></tr></thead><tbody><tr><td></td><td></td><td></td></tr></tbody></table>


# 코드 테이블

Hotopay에서 사용되는 코드들을 모아둔 테이블입니다.

### 결제 상태 (PayStatus)

<table><thead><tr><th width="174.5">값</th><th>설명</th></tr></thead><tbody><tr><td><code>DONE</code></td><td>결제 완료. 대금을 받은 상태입니다.</td></tr><tr><td><code>PENDING</code></td><td>결제 대기중. 회원이 결제 정보를 입력하고 실결제를 진행하고 있는 단계입니다.</td></tr><tr><td><code>WAITING_FOR_DEPOSIT</code></td><td><p>입금 대기중. 결제 프로세스는 완료했으나, 입금을 대기중인 상태입니다.</p><p>이 단계는 가상계좌 결제타입에만 존재합니다.</p></td></tr><tr><td><code>CANCELED</code></td><td>결제 취소. 유저가 결제 진행 도중에 결제를 취소한 상태입니다.</td></tr><tr><td><code>EXPIRED</code></td><td><p>결제 만료. 결제 프로세스를 진행한 시간으로부터 너무 오랜 시간이 지나 만료된 결제건입니다.</p><p>최대 3일동안 <code>PENDING</code> 상태로 있는 결제건은 자동으로 <code>EXPIRED</code> 상태로 변경됩니다.</p></td></tr><tr><td><code>FAILED</code></td><td><p>결제 실패. 회원이 결제를 실패한 결제건입니다.</p><p>결제 실패 사유는 결제 데이터를 보면서 판단이 필요합니다.</p></td></tr><tr><td><code>REFUNDED</code></td><td>결제 환불. 관리자가 해당 결제를 환불한 상태입니다.</td></tr></tbody></table>

### 결제사 (PG)

| PG          | 이름                | 개발자센터                                                         |
| ----------- | ----------------- | ------------------------------------------------------------- |
| `toss`      | 토스페이먼츠            | <https://developers.tosspayments.com>                         |
| `tossbill`  | 토스페이먼츠 (정기결제)     | <https://developers.tosspayments.com>                         |
| `paypal`    | 페이팔               | <https://developer.paypal.com/home>                           |
| `kakao`     | 카카오페이             | <https://developers.kakao.com/docs/latest/ko/kakaopay/common> |
| `inicis`    | 이니시스              | <https://guide.portone.io>                                    |
| `payple`    | 페이플               | <https://developer.payple.kr>                                 |
| `n_account` | 무통장입금             |                                                               |
| `point`     | 포인트 결제 (0원 결제 포함) |                                                               |

### 결제 수단 (PayMethod)

| 값                   | 결제수단           |
| ------------------- | -------------- |
| `n_account`         | 무통장 입금         |
| `ts_account`        | 계좌 이체 (토스)     |
| `v_account`         | 가상계좌 (토스)      |
| `card`              | 신용카드 (토스)      |
| `voucher`           | 문화상품권 (토스)     |
| `cellphone`         | 휴대폰 (토스)       |
| `paypal`            | 페이팔            |
| `kakaopay`          | 카카오페이          |
| `toss`              | 토스앱 (결제위젯 포함)  |
| `inic_card`         | 신용카드 (이니시스)    |
| `inic_trans`        | 실시간계좌이체 (이니시스) |
| `inic_phone`        | 휴대폰소액결제 (이니시스) |
| `inic_cultureland`  | 문화상품권 (이니시스)   |
| `inic_smartculture` | 스마트문상 (이니시스)   |
| `inic_happymoney`   | 해피머니 (이니시스)    |
| `paypl_card`        | 신용카드 (페이플)     |
| `paypl_transfer`    | 계좌이체 (페이플)     |
| `point`             | 포인트 (0원 결제 포함) |

### 빌링 상태 (BillingStatus)

<table><thead><tr><th width="174.5">값</th><th>설명</th></tr></thead><tbody><tr><td><code>DONE</code></td><td>결제 완료. 대금을 받은 상태입니다.</td></tr><tr><td><code>FAILED_RENEW</code></td><td><p>갱신 실패. 등록한 결제정보로 결제를 진행했으나 결제에 실패한 상태입니다.</p><p>결제 실패 사유는 결제 데이터를 보면서 판단이 필요합니다.</p></td></tr></tbody></table>


# 유용한 메소드 - 정기결제

정기결제 유용한 메소드

### 현재 특정 상품을 구독하고 있는지 확인하는 메소드

`HotopayModel::getActiveSubscriptionsByMemberSrlWithProductSrlAndStatus($member_srl, $product_srl)`

리턴타입 : `Array`

#### 파라메터

<table><thead><tr><th width="176.33333333333331">이름</th><th width="222">설명</th><th>필수여부</th></tr></thead><tbody><tr><td>member_srl</td><td>라이믹스 멤버 고유 번호</td><td>Y</td></tr><tr><td>product_srl</td><td>상품 번호</td><td>Y</td></tr></tbody></table>

입력한 상품을 구독하고 있다면 리턴 Array에 Subscription 데이터가 추가됨.

#### 코드 예시

```
$subscriptions = HotopayModel::getActiveSubscriptionsByMemberSrlWithProductSrlAndStatus(4, 1000);

if (count($subscriptions) > 0) {
    // 4번 유저가 상품번호 1000 상품을 구독중인 상태
}
```


