Skip to content

Commit

Permalink
결제연동 리뉴얼 - V2 API Secret 수동 발급 문의 부분 제거 (#375)
Browse files Browse the repository at this point in the history
  • Loading branch information
LimJiGyu committed Mar 26, 2024
1 parent 0352275 commit 8ebc2c9
Show file tree
Hide file tree
Showing 4 changed files with 1,069 additions and 1,035 deletions.
53 changes: 51 additions & 2 deletions src/content/docs/ko/ready/readme.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import VersionGate from "~/components/gitbook/VersionGate.astro";
import Hint from "~/components/Hint.astro";

import image07 from "./_assets/apiver_v1.png";
import apiVerV2 from "./_assets/apiver_v2.png";
import image4 from "./_assets/apply1.png"
import image01 from "./_assets/channel1.png";
import image02 from "./_assets/channel2.png";
Expand Down Expand Up @@ -42,6 +43,7 @@ import image12 from "./_assets/pg/tosspayments.png";
import image9 from "./_assets/pg/welcome.png";
import image10 from "./_assets/pg/welcome1.png";
import image2 from "./_assets/signup.png";
import storeid from "./_assets/storeid.png";
import IntegrationPaypalV2 from './_components/integration-guide/paypal-v2.mdx';
import IntegrationTosspayments from './_components/integration-guide/tosspayments.mdx';

Expand Down Expand Up @@ -618,11 +620,58 @@ import IntegrationTosspayments from './_components/integration-guide/tosspayment
<Hint style="info">
**REST API Key, REST API Secret란?**

- API 호출 시 인증을 위해 필요한 값입니다. [인증 방식](https://developers.portone.io/api/rest-v2)을 참고해 주세요.
- V1 API 호출 시 사용되는 Access Token을 발급할 때 사용되는 값입니다.
- REST API Secret은 재발급이 가능하며, 재발급 즉시 기존의 Secret은 사용이 불가능합니다.
</Hint>

<Hint style="danger">
- API Secret은 **결제 거래 건을 컨트롤할 수 있는 민감 정보**로 고객사 서버에서만 사용되어야 하며, 브라우저 등 외부에 노출되어서는 안 됩니다.
REST API Key, REST API Secret **결제 거래 건을 컨트롤할 수 있는 민감 정보**로 외부로 유출되지 않도록 주의가 필요합니다.
</Hint>
</Fragment>

<Fragment slot="v2">
### 4-1. 스토어 아이디(Store ID) 복사하기

포트원을 이용한 결제 연동 시 필요한 스토어 아이디(Store ID)를 복사할 수 있습니다.

<Figure src={storeid} />

- 관리자 콘솔 화면에서 \[결제 연동]\[연동 관리] 화면에서 스토어 아이디를 복사할 수 있습니다.

<Hint style="info">
**스토어 아이디(Store ID)란?**

- 포트원 가입 시 \[대표 상점]이 생성되며, 자동으로 생성되는 고유값 입니다.
- 추가로 하위 상점을 생성하는 경우 **상점마다 고유하게 부여**됩니다.
- 결제창 연동 시 사용되며, 결제 호출 시 `storeId` 파라미터에 해당 값을 넣어야 합니다.
- **기술 문의 시 해당 값을 공유**해 주시면 빠르게 문제 해결을 도와드릴 수 있습니다.
</Hint>

### 4-2. V2 API Secret 발급하기

V2 API를 연동하기 위해서는 V2 API Secret을 별도로 발급한 후 사용이 가능합니다.

<Figure src={apiVerV2} />

- 관리자 콘솔 화면에서 \[결제연동]\[연동 관리]\[식별코드·API Keys] 메뉴에서 \[V2 API]를 선택합니다.
- `새로운 API Secret 발급`을 클릭합니다.
- Secret 목록에 표시될 `Secret 이름`을 입력하고 `만료 기한`을 선택한 후 `생성`을 클릭합니다.
- 생성된 Secret을 복사합니다. `API Secret을 확인했습니다`를 누른 후엔 Secret 값을 확인할 수 없으니 주의해 주세요.

<Hint style="danger">
API Secret 발급 직후에만 값을 확인할 수 있습니다. 페이지를 벗어나면 API Secret 확인이 불가하오니 복사한 후 임의의 장소에 저장하여 사용하세요.
만약, API Secret 값을 잊어버리신 경우 새로 발급받아 사용해야 합니다.
</Hint>

<Hint style="info">
**API Secret이란?**

- API 호출 시 사용되는 Access Token을 발급할 때 사용되는 값입니다.
- V2 API Secret을 발급받으신 후, 이를 이용해 [API Secret을 이용한 토큰 발급](/api/rest-v2/auth#post%20%2Flogin%2Fapi-secret) API를 호출하시면 <br /> **Access Token**을 발급받으실 수 있습니다.
</Hint>

<Hint style="danger">
API Secret은 **결제 거래 건을 컨트롤할 수 있는 민감 정보**로 고객사 서버에서만 사용되어야 하며, 브라우저 등 외부에 노출되어서는 안 됩니다.
</Hint>
</Fragment>
</VersionGate>
86 changes: 43 additions & 43 deletions src/content/docs/ko/v2-payment/authpay.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,17 @@ description: PG 결제창을 이용하는 인증 결제를 연동합니다.
---

import Details from "~/components/gitbook/Details.astro";
import Hint from "~/components/Hint.astro";
import Tabs from "~/components/gitbook/tabs/Tabs.astro";
import Tab from "~/components/gitbook/tabs/Tab.astro";

import Youtube from "~/components/gitbook/Youtube.astro";
import Hint from "~/components/Hint.astro";

import SdkInstallation from "./_components/sdk-installation.mdx"

<Details>
<p slot="summary">
<strong>영상으로 보기</strong>
</p>
<Youtube
videoId="DhcQFLYV9Q8"
caption="포트원 인증 결제의 이해 - 쇼핑몰 멀티PG 연동하기"
/>

<Youtube videoId="DhcQFLYV9Q8" caption="포트원 인증 결제의 이해 - 쇼핑몰 멀티PG 연동하기" />
</Details>

## 1. 포트원 SDK 설치
Expand Down Expand Up @@ -50,7 +45,8 @@ const response = await PortOne.requestPayment({
});
```

**paymentId**는 결제 건을 구분하는 문자열로, 결제 요청 및 조회에 필요합니다. 같은 paymentId에 대해 여러 번의 결제 시도가 가능하나, 최종적으로 결제에 성공하는 것은 단 한 번만 가능합니다. (중복 결제 방지)
**paymentId**는 결제 건을 구분하는 문자열로, 결제 요청 및 조회에 필요합니다. 같은 paymentId에 대해 여러 번의 결제 시도가 가능하나,
최종적으로 결제에 성공하는 것은 단 한 번만 가능합니다. (중복 결제 방지)

**orderName**은 주문 내용을 나타내는 문자열입니다. 특정한 형식이 있지는 않지만, 결제 처리에 필요하므로 필수적으로 전달해 주셔야 합니다.

Expand All @@ -67,41 +63,42 @@ const response = await PortOne.requestPayment({
결제가 성공한 경우 paymentId를 서버에 전달하여 서버측 결제 완료 처리를 하도록 합니다. (가상 계좌 결제의 경우 결제가 아직 완료되지 않은 상태일 수 있습니다.)

```javascript
const response = await PortOne.requestPayment({
/* 파라미터 생략 */
});

if (response.code != null) {
// 오류 발생
return alert(response.message);
}
async function requestPayment() {
const response = await PortOne.requestPayment({
/* 파라미터 생략 */
});

if (response.code != null) {
// 오류 발생
return alert(response.message);
}

// /payment/complete 엔드포인트를 구현해야 합니다. 다음 목차에서 설명합니다.
const notified = await fetch(
`${SERVER_BASE_URL}/payment/complete`,
{
// /payment/complete 엔드포인트를 구현해야 합니다. 다음 목차에서 설명합니다.
const notified = await fetch(`${SERVER_BASE_URL}/payment/complete`, {
method: "POST",
headers: { "Content-Type": "application/json" },
// paymentId와 주문 정보를 서버에 전달합니다
body: JSON.stringify({
paymentId: paymentId,
// 주문 정보...
}),
}
);
});
}
```

결과값에 들어 있는 필드는 다음과 같습니다.

| 필드명 | 설명 | 비고 |
| --------- | ---------- | ------- |
| paymentId | 결제 건 ID | 공통 |
| code | 오류 코드 | 실패 시 |
| message | 오류 문구 | 실패 시 |
|필드명 |설명 |비고 |
|---------|----------|-------|
|paymentId|결제 건 ID|공통 |
|code |오류 코드 |실패 시|
|message |오류 문구 |실패 시|

### 3-1. redirect 방식의 경우

모바일 환경에서의 결제는 대부분 redirect 방식으로 이루어집니다. redirect 방식에서는 브라우저가 결제창으로 리다이렉트되었다가, 결제창에서의 작업이 끝나면 지정한 `redirectUrl`로 다시 리다이렉트됩니다. 이 경우에는 함수 호출 결과를 이용할 수 없고, 결제 성공 여부 등은 [쿼리 문자열](https://en.wikipedia.org/wiki/Query_string)로 전달받게 됩니다.
모바일 환경에서의 결제는 대부분 redirect 방식으로 이루어집니다. redirect 방식에서는 브라우저가 결제창으로 리다이렉트되었다가, 결제창에서의 작업이 끝나면
지정한 `redirectUrl`로 다시 리다이렉트됩니다. 이 경우에는 함수 호출 결과를 이용할 수 없고,
결제 성공 여부 등은 [쿼리 문자열](https://en.wikipedia.org/wiki/Query_string)로 전달받게 됩니다.

```javascript
PortOne.requestPayment({
Expand All @@ -112,27 +109,31 @@ PortOne.requestPayment({

쿼리 문자열로 전달되는 내용은 다음과 같습니다.

| | 설명 | 비고 |
| ---------- | ---------- | ------- |
| payment_id | 결제 건 ID | 공통 |
| code | 오류 코드 | 실패 시 |
| message | 오류 문구 | 실패 시 |
||설명 |비고 |
|-----------|----------|-------|
|payment\_id|결제 건 ID|공통 |
|code |오류 코드 |실패 시|
|message |오류 문구 |실패 시|

예를 들어 paymentId가 `payment-39ecfa97`, redirectUrl이 `https://example.com/payment-redirect`인 경우, 결제 성공 시에 `https://example.com/payment-redirect?payment_id=payment-39ecfa97`로 리다이렉트됩니다.
예를 들어 paymentId가 `payment-39ecfa97`, redirectUrl이 `https://example.com/payment-redirect`인 경우,
결제 성공 시에 `https://example.com/payment-redirect?payment_id=payment-39ecfa97`로 리다이렉트됩니다.

## 4. 결제 완료 처리 (서버)

paymentId를 서버에 전달하면, 서버는 포트원의 [결제 조회 API](/api/rest-v2/payment#get%20%2Fpayments%2F%7BpaymentId%7D)를 호출하여 해당 결제 건의 상태를 확인하고 결제 완료 처리를 하여야 합니다.
paymentId를 서버에 전달하면, 서버는 포트원의 [결제 조회 API](/api/rest-v2/payment#get%20%2Fpayments%2F%7BpaymentId%7D)
호출하여 해당 결제 건의 상태를 확인하고 결제 완료 처리를 하여야 합니다.

<Hint style="info">
**결제 검증 필수**

인증 결제의 흐름상 결제 금액 등 정보가 고객의 브라우저 측에서 처리되므로, 의도한 결제 내용이 맞는지 꼭 확인하여야 위변조를 막을 수 있습니다.
**결제 검증 필수**

인증 결제의 흐름상 결제 금액 등 정보가 고객의 브라우저 측에서 처리되므로, 의도한 결제 내용이 맞는지 꼭 확인하여야 위변조를 막을 수 있습니다.
</Hint>

예시로, 위에서 사용했던 `/payment/complete` 엔드포인트를 다음과 같이 구현할 수 있습니다.

[PORTONE\_API\_SECRET](/docs/ko/ready/readme?v=v2#4-2-v2-api-secret-%EB%B0%9C%EA%B8%89%ED%95%98%EA%B8%B0)
은 V2 전용 시크릿으로, 포트원 콘솔 내 결제연동 탭에서 발급받을 수 있습니다.

```javascript title="Express"
app.use(bodyParser.json());

Expand All @@ -146,10 +147,11 @@ app.post("/payment/complete", async (req, res) => {
const paymentResponse = await fetch(
`https://api.portone.io/payments/${encodeURIComponent(paymentId)}`,
{
headers: { "Authorization": `PortOne ${PORTONE_API_SECRET}` },
headers: { Authorization: `PortOne ${PORTONE_API_SECRET}` },
},
);
if (!paymentResponse.ok) throw new Error(`paymentResponse: ${paymentResponse.statusText}`);
if (!paymentResponse.ok)
throw new Error(`paymentResponse: ${paymentResponse.statusText}`);
const payment = await paymentResponse.json();

// 2. 고객사 내부 주문 데이터의 가격과 실제 지불된 금액을 비교합니다.
Expand All @@ -170,11 +172,9 @@ app.post("/payment/complete", async (req, res) => {
} else {
// 결제 금액이 불일치하여 위/변조 시도가 의심됩니다.
}

} catch (e) {
// 결제 검증에 실패했습니다.
res.status(400).send(e);
}
});

```
Loading

0 comments on commit 8ebc2c9

Please sign in to comment.