### Install Python SDK Source: https://context7.com/portone-io/server-sdk/llms.txt Install the PortOne Server SDK for Python using uv or poetry. ```shell uv add portone-server-sdk # 또는 poetry add portone-server-sdk ``` -------------------------------- ### Install JavaScript/TypeScript SDK Source: https://context7.com/portone-io/server-sdk/llms.txt Install the PortOne Server SDK for JavaScript and TypeScript using npm or JSR. ```shell npm install --save @portone/server-sdk ``` -------------------------------- ### Create and Get Payment Schedule Source: https://context7.com/portone-io/server-sdk/llms.txt Schedules a payment to be automatically executed at a specific time. Requires a billing key and payment details. Also shows how to retrieve the schedule information. ```typescript import { PaymentClient } from "@portone/server-sdk"; const client = PaymentClient({ secret: process.env.PORTONE_API_SECRET! }); // 결제 예약 생성 const schedule = await client.paymentSchedule.createPaymentSchedule({ paymentScheduleId: `sched-${Date.now()}`, payment: { billingKey: "billing-key-75ae3cab-6afe-422d-bf34-3a7b1762451d", orderName: "다음 달 구독료", amount: { total: 9900 }, currency: "KRW", customer: { id: "customer-001" }, }, timeToPay: "2024-05-01T09:00:00+09:00", }); console.log(`예약 완료: ${schedule.paymentSchedule.id}, 예정 시각: ${schedule.paymentSchedule.timeToPay}`); // 예약 조회 const info = await client.paymentSchedule.getPaymentSchedule({ paymentScheduleId: schedule.paymentSchedule.id, }); console.log(`예약 상태: ${info.status}`); // 출력: 예약 상태: SCHEDULED ``` -------------------------------- ### Pay with Billing Key Source: https://context7.com/portone-io/server-sdk/llms.txt Initiates a payment using a pre-issued billing key. Supports various options like installment plans and points. Handles specific errors like BILLING_KEY_NOT_FOUND. ```typescript import { PaymentClient } from "@portone/server-sdk"; import { PayWithBillingKeyError } from "@portone/server-sdk/payment"; const client = PaymentClient({ secret: process.env.PORTONE_API_SECRET! }); try { const result = await client.payWithBillingKey({ paymentId: `sub-${Date.now()}`, billingKey: "billing-key-75ae3cab-6afe-422d-bf34-3a7b1762451d", orderName: "월 구독료", amount: { total: 9900, taxFree: 0 }, currency: "KRW", customer: { id: "customer-001" }, noticeUrls: ["https://example.com/webhook"], }); console.log(`빌링키 결제 완료: ${result.paymentId}`); } catch (e) { if (e instanceof PayWithBillingKeyError) { if (e.data.type === "BILLING_KEY_NOT_FOUND") { console.error("빌링키를 찾을 수 없습니다."); } else if (e.data.type === "BILLING_KEY_ALREADY_DELETED") { console.error("이미 삭제된 빌링키입니다."); } } } ``` -------------------------------- ### PaymentClient.payWithBillingKey Source: https://context7.com/portone-io/server-sdk/llms.txt Processes a payment using a pre-issued billing key. Supports various options like installment plans, card points, and promotions. ```APIDOC ## PaymentClient.payWithBillingKey — 빌링키 결제 미리 발급된 빌링키로 결제를 진행합니다. 할부 개월 수, 카드 포인트 사용, 프로모션 등 다양한 옵션을 지정할 수 있습니다. ```typescript import { PaymentClient } from "@portone/server-sdk"; import { PayWithBillingKeyError } from "@portone/server-sdk/payment"; const client = PaymentClient({ secret: process.env.PORTONE_API_SECRET! }); try { const result = await client.payWithBillingKey({ paymentId: `sub-${Date.now()}`, billingKey: "billing-key-75ae3cab-6afe-422d-bf34-3a7b1762451d", orderName: "월 구독료", amount: { total: 9900, taxFree: 0 }, currency: "KRW", customer: { id: "customer-001" }, noticeUrls: ["https://example.com/webhook"], }); console.log(`빌링키 결제 완료: ${result.paymentId}`); } catch (e) { if (e instanceof PayWithBillingKeyError) { if (e.data.type === "BILLING_KEY_NOT_FOUND") { console.error("빌링키를 찾을 수 없습니다."); } else if (e.data.type === "BILLING_KEY_ALREADY_DELETED") { console.error("이미 삭제된 빌링키입니다."); } } } ``` ``` -------------------------------- ### Verify Webhook Signature with HMAC-SHA256 in Python Source: https://context7.com/portone-io/server-sdk/llms.txt This Python snippet shows the initial setup for verifying webhook signatures using the PortOne SDK. It requires the webhook secret to be set as an environment variable. ```python import os import portone_server_sdk as portone from portone_server_sdk import webhook from portone_server_sdk.webhook import WebhookVerificationError PORTONE_WEBHOOK_SECRET = os.environ["PORTONE_WEBHOOK_SECRET"] ``` -------------------------------- ### PaymentClient.getPayments - Get Multiple Payments (Paginated) Source: https://context7.com/portone-io/server-sdk/llms.txt Retrieve a list of payments using filter conditions and pagination. Supports filtering by status, payment method, and date range. ```typescript import { PaymentClient } from "@portone/server-sdk"; const client = PaymentClient({ secret: process.env.PORTONE_API_SECRET! }); const result = await client.getPayments({ page: { number: 0, size: 20 }, filter: { status: ["PAID"], from: "2024-01-01T00:00:00+09:00", until: "2024-01-31T23:59:59+09:00", }, }); console.log(`전체 건수: ${result.page.totalCount}`); for (const payment of result.items) { console.log(`[${payment.id}] ${payment.orderName} - ${payment.status}`); } // 출력 예: // 전체 건수: 42 // [payment-001] 상품 A - PAID // [payment-002] 상품 B - PAID ``` -------------------------------- ### PaymentClient.getAllPaymentsByCursor - Get Large Number of Payments (Cursor-based) Source: https://context7.com/portone-io/server-sdk/llms.txt Sequentially retrieve a large number of payments using cursor-based pagination. Pass the cursor from the previous response to the next request to move through pages. ```typescript import { PaymentClient } from "@portone/server-sdk"; const client = PaymentClient({ secret: process.env.PORTONE_API_SECRET! }); let cursor: string | undefined; let totalCount = 0; do { const result = await client.getAllPaymentsByCursor({ from: "2024-01-01T00:00:00+09:00", until: "2024-01-31T23:59:59+09:00", size: 100, cursor, }); totalCount += result.items.length; cursor = result.cursor; console.log(`조회된 건수: ${result.items.length}, 누적: ${totalCount}`); } while (cursor); console.log(`전체 결제 건수: ${totalCount}`); ``` -------------------------------- ### PaymentClient.getPayment - Get Single Payment Source: https://context7.com/portone-io/server-sdk/llms.txt Retrieve a specific payment by its ID. Handles potential errors like FORBIDDEN, PAYMENT_NOT_FOUND, and UNAUTHORIZED. Use isUnrecognizedPayment to check for unknown payment statuses. ```typescript import { PaymentClient, isUnrecognizedPayment } from "@portone/server-sdk"; import { GetPaymentError } from "@portone/server-sdk/payment"; const client = PaymentClient({ secret: process.env.PORTONE_API_SECRET! }); async function verifyPayment(paymentId: string, expectedAmount: number) { const payment = await client.getPayment({ paymentId }); // SDK 버전에서 알 수 없는 결제 상태인 경우 처리 if (isUnrecognizedPayment(payment)) { throw new Error("지원하지 않는 결제 상태"); } // 결제 완료 상태 확인 if (payment.status !== "PAID") { throw new Error(`결제 미완료: ${payment.status}`); } // 금액 위변조 검증 if (payment.amount.total !== expectedAmount) { throw new Error( `금액 불일치: 기대=${expectedAmount}, 실제=${payment.amount.total}` ); } return payment; } // 사용 예 const payment = await verifyPayment("order-20240101-001", 15000); console.log(`결제 완료: ${payment.orderName}, ${payment.amount.total}원`); // 출력: 결제 완료: 상품명, 15000원 ``` -------------------------------- ### Add JVM SDK Dependency (Gradle) Source: https://context7.com/portone-io/server-sdk/llms.txt Add the PortOne Server SDK dependency for JVM projects using Gradle. Consider the shading version if dependency conflicts arise. ```kotlin // build.gradle.kts dependencies { implementation("io.portone:server-sdk:0.5.0") // 의존성 충돌 시 shading 버전 사용 // implementation("io.portone:server-sdk:0.5.0:all") } repositories { mavenCentral() } ``` -------------------------------- ### PortOneClient Initialization Source: https://context7.com/portone-io/server-sdk/llms.txt Initialize the main PortOne client or a payment-specific client. You can also specify a store ID for merchant-specific operations. ```APIDOC ## PortOneClient — 통합 클라이언트 생성 전체 API 도메인(결제, B2B, 플랫폼, 본인인증 등)에 접근하는 최상위 클라이언트를 생성합니다. 하위 도메인 클라이언트(`payment`, `b2b`, `platform`, `identityVerification`, `pgSpecific`, `auth`, `reconciliation`)를 프로퍼티로 노출합니다. ```typescript // JavaScript / TypeScript import { PortOneClient, PaymentClient } from "@portone/server-sdk"; import { GetPaymentError } from "@portone/server-sdk/payment"; const PORTONE_API_SECRET = process.env.PORTONE_API_SECRET!; // 전체 클라이언트 const portone = PortOneClient({ secret: PORTONE_API_SECRET }); // 결제 전용 클라이언트 (더 가벼운 초기화) const paymentClient = PaymentClient({ secret: PORTONE_API_SECRET }); // 하위 상점을 대상으로 할 경우 storeId 지정 const merchantClient = PortOneClient({ secret: PORTONE_API_SECRET, storeId: "store-61e0db3d-b967-47db-8b50-96002da90d55", }); // 결제 단건 조회 try { const payment = await portone.payment.getPayment({ paymentId: "payment-001" }); console.log(payment); } catch (e) { if (e instanceof GetPaymentError) { switch (e.data.type) { case "FORBIDDEN": console.error("접근 권한 없음"); break; case "PAYMENT_NOT_FOUND": console.error("결제 건을 찾을 수 없음"); break; case "UNAUTHORIZED": console.error("인증 실패"); break; } } } ``` ``` -------------------------------- ### Create PortOneClient - Unified Client Source: https://context7.com/portone-io/server-sdk/llms.txt Generate the top-level PortOneClient to access all API domains. Sub-domain clients are exposed as properties. Optionally specify a storeId for targeting specific sub-merchants. ```typescript import { PortOneClient, PaymentClient } from "@portone/server-sdk"; import { GetPaymentError } from "@portone/server-sdk/payment"; const PORTONE_API_SECRET = process.env.PORTONE_API_SECRET!; // 전체 클라이언트 const portone = PortOneClient({ secret: PORTONE_API_SECRET }); // 결제 전용 클라이언트 (더 가벼운 초기화) const paymentClient = PaymentClient({ secret: PORTONE_API_SECRET }); // 하위 상점을 대상으로 할 경우 storeId 지정 const merchantClient = PortOneClient({ secret: PORTONE_API_SECRET, storeId: "store-61e0db3d-b967-47db-8b50-96002da90d55", }); // 결제 단건 조회 try { const payment = await portone.payment.getPayment({ paymentId: "payment-001" }); console.log(payment); } catch (e) { if (e instanceof GetPaymentError) { switch (e.data.type) { case "FORBIDDEN": console.error("접근 권한 없음"); break; case "PAYMENT_NOT_FOUND": console.error("결제 건을 찾을 수 없음"); break; case "UNAUTHORIZED": console.error("인증 실패"); break; } } } ``` -------------------------------- ### Webhook Verification (Python) Source: https://context7.com/portone-io/server-sdk/llms.txt Provides a Python SDK for verifying webhook signatures sent from PortOne servers. ```APIDOC ## webhook.verify — Python 웹훅 검증 ### Description Provides a Python SDK for verifying webhook signatures sent from PortOne servers. This function checks the HMAC-SHA256 signature and timestamp validity. ### Method `portone.webhook.verify(...)` (Specific parameters depend on SDK implementation, but generally require secret, payload, and headers). ### Request Example ```python import os import portone_server_sdk as portone from portone_server_sdk import webhook from portone_server_sdk.webhook import WebhookVerificationError PORTONE_WEBHOOK_SECRET = os.environ["PORTONE_WEBHOOK_SECRET"] # Example usage would follow, assuming the SDK provides a direct verify function. # The exact implementation details for Python verification are not fully detailed in the source. ``` -------------------------------- ### PaymentClient.payInstantly Source: https://context7.com/portone-io/server-sdk/llms.txt Requests a manual payment (instant payment) via card or issues a virtual account for immediate payment. Use this when processing payments directly from the server without a separate payment window. ```APIDOC ## PaymentClient.payInstantly — 수기 결제 (즉시 결제) ### Description Requests a manual payment (instant payment) via card or issues a virtual account for immediate payment. Use this when processing payments directly from the server without a separate payment window. ### Method `client.payInstantly(params)` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **paymentId** (string) - Required - A unique ID for the payment. - **channelKey** (string) - Required - The key for the payment channel. - **method** (object) - Required - Payment method details. - **card** (object) - Required if paying by card. - **credential** (object) - Required - Card credentials. - **number** (string) - Required - Card number. - **expiryYear** (string) - Required - Expiry year (e.g., "26"). - **expiryMonth** (string) - Required - Expiry month (e.g., "12"). - **birthOrBusinessRegistrationNumber** (string) - Required - Cardholder's birth date (YYMMDD) or business registration number. - **passwordTwoDigits** (string) - Required - First two digits of the card password. - **orderName** (string) - Required - The name of the order. - **amount** (object) - Required - Payment amount details. - **total** (number) - Required - The total amount to pay. - **currency** (string) - Required - The currency of the payment (e.g., "KRW"). - **customer** (object) - Optional - Customer information. - **name** (object) - Customer's name. - **full** (string) - Required - Full name. - **email** (string) - Optional - Customer's email address. ### Request Example ```typescript await client.payInstantly({ paymentId: `instant-${Date.now()}`, channelKey: "channel-key-abc123", method: { card: { credential: { number: "1234567890123456", expiryYear: "26", expiryMonth: "12", birthOrBusinessRegistrationNumber: "900101", passwordTwoDigits: "00", }, }, }, orderName: "정기 구독 결제", amount: { total: 9900 }, currency: "KRW", customer: { name: { full: "홍길동" }, email: "user@example.com", }, }); ``` ### Response #### Success Response (200) - **paymentId** (string) - The ID of the processed payment. - **status** (string) - The status of the payment (e.g., "PAID"). #### Response Example ```json { "paymentId": "instant-1704067200000", "status": "PAID" } ``` ``` -------------------------------- ### Manual Payment (Instant Pay) Source: https://context7.com/portone-io/server-sdk/llms.txt Initiates a card payment without prior authentication or issues a virtual account instantly via API. Use this when processing payments directly from the server without a payment UI. ```typescript import { PaymentClient } from "@portone/server-sdk"; const client = PaymentClient({ secret: process.env.PORTONE_API_SECRET! }); // 카드 수기 결제 const result = await client.payInstantly({ paymentId: `instant-${Date.now()}`, channelKey: "channel-key-abc123", method: { card: { credential: { number: "1234567890123456", expiryYear: "26", expiryMonth: "12", birthOrBusinessRegistrationNumber: "900101", passwordTwoDigits: "00", }, }, }, orderName: "정기 구독 결제", amount: { total: 9900 }, currency: "KRW", customer: { name: { full: "홍길동" }, email: "user@example.com", }, }); console.log(`수기 결제 완료: ${result.paymentId}, 상태: ${result.status}`); // 출력: 수기 결제 완료: instant-1704067200000, 상태: PAID ``` -------------------------------- ### Verify Webhook Signature with HMAC-SHA256 in Kotlin (Ktor) Source: https://context7.com/portone-io/server-sdk/llms.txt This Kotlin code snippet demonstrates how to verify webhook signatures using the PortOne SDK within a Ktor application. It requires the webhook secret and handles different webhook event types. ```kotlin import io.portone.sdk.server.webhook.WebhookVerifier import io.portone.sdk.server.webhook.WebhookTransaction import io.portone.sdk.server.webhook.WebhookBillingKey val webhookVerifier = WebhookVerifier(secret = System.getenv("PORTONE_WEBHOOK_SECRET")) // Ktor 라우트 핸들러 예시 post("/webhook/portone") { val payload = call.receiveText() val webhook = webhookVerifier.verify( msgBody = payload, msgId = call.request.headers["webhook-id"]!!, msgSignature = call.request.headers["webhook-signature"]!!, msgTimestamp = call.request.headers["webhook-timestamp"]!!, ) when (webhook) { is WebhookTransaction -> println("결제 이벤트: ${webhook.data.paymentId}") is WebhookBillingKey -> println("빌링키 이벤트: ${webhook.data.billingKey}") else -> println("기타 웹훅: ${webhook.type}") } call.respond(HttpStatusCode.OK, "OK") } ``` -------------------------------- ### PaymentClient.getAllPaymentsByCursor Source: https://context7.com/portone-io/server-sdk/llms.txt Retrieves a large number of payments sequentially using cursor-based pagination. Pass the `cursor` from the previous response to the next request. ```APIDOC ## PaymentClient.getAllPaymentsByCursor — 결제 대용량 다건 조회 (커서 기반) 대량의 결제 건을 커서 기반으로 순차 조회합니다. 이전 응답의 `cursor` 값을 다음 요청에 전달하여 페이지를 이동합니다. ```typescript import { PaymentClient } from "@portone/server-sdk"; const client = PaymentClient({ secret: process.env.PORTONE_API_SECRET! }); let cursor: string | undefined; let totalCount = 0; do { const result = await client.getAllPaymentsByCursor({ from: "2024-01-01T00:00:00+09:00", until: "2024-01-31T23:59:59+09:00", size: 100, cursor, }); totalCount += result.items.length; cursor = result.cursor; console.log(`조회된 건수: ${result.items.length}, 누적: ${totalCount}`); } while (cursor); console.log(`전체 결제 건수: ${totalCount}`); ``` ``` -------------------------------- ### PaymentClient.preRegisterPayment Source: https://context7.com/portone-io/server-sdk/llms.txt Pre-registers payment amount and currency on the server before calling the payment window to prevent tampering. The same `paymentId` can be used later to call the payment window, and the server will compare and verify the registered values. ```APIDOC ## PaymentClient.preRegisterPayment — 결제 정보 사전 등록 ### Description Pre-registers payment amount and currency on the server before calling the payment window to prevent tampering. The same `paymentId` can be used later to call the payment window, and the server will compare and verify the registered values. ### Method `client.preRegisterPayment(params)` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **paymentId** (string) - Required - A unique ID for the payment. - **totalAmount** (number) - Required - The total amount of the payment. - **taxFreeAmount** (number) - Optional - The tax-free amount of the payment. - **currency** (string) - Required - The currency of the payment (e.g., "KRW"). ### Request Example ```typescript const paymentId = `order-${Date.now()}`; // Step 1: Pre-register payment amount on the server await client.preRegisterPayment({ paymentId, totalAmount: 39000, taxFreeAmount: 0, currency: "KRW", }); // Step 2: Pass paymentId to the frontend → Call payment window console.log(`Pre-registration complete. Payment ID: ${paymentId}`); // → Call PortOne.loadPaymentUI({ paymentId, ... }) from the client SDK ``` ### Response #### Success Response (200) No specific response body is detailed, but the operation is confirmed upon successful execution. #### Response Example None provided. ``` -------------------------------- ### Pre-register Payment Information Source: https://context7.com/portone-io/server-sdk/llms.txt Registers payment amount and currency on the server before calling the payment UI to prevent tampering. The payment UI will later verify against these registered values using the same `paymentId`. ```typescript import { PaymentClient } from "@portone/server-sdk"; const client = PaymentClient({ secret: process.env.PORTONE_API_SECRET! }); const paymentId = `order-${Date.now()}`; // 1단계: 서버에서 결제 금액 사전 등록 await client.preRegisterPayment({ paymentId, totalAmount: 39000, taxFreeAmount: 0, currency: "KRW", }); // 2단계: 프론트엔드로 paymentId 전달 → 결제창 호출 console.log(`사전 등록 완료. 결제 ID: ${paymentId}`); // → 클라이언트 SDK에서 PortOne.loadPaymentUI({ paymentId, ... }) 호출 ``` -------------------------------- ### Webhook Verification (JVM/Kotlin) Source: https://context7.com/portone-io/server-sdk/llms.txt Verifies webhook signatures using HMAC-SHA256 and checks for timestamp expiration in a JVM environment. ```APIDOC ## Webhook.verify — JVM (Kotlin) 웹훅 검증 ### Description Verifies webhook signatures using HMAC-SHA256 and checks for timestamp expiration (±5 minutes) in a JVM environment. Handles different webhook types like transactions and billing keys. ### Method `webhookVerifier.verify(msgBody, msgId, msgSignature, msgTimestamp)` ### Parameters - **secret** (string) - Required - The webhook secret key. - **msgBody** (string) - Required - The raw webhook message body. - **msgId** (string) - Required - The webhook ID from headers. - **msgSignature** (string) - Required - The webhook signature from headers. - **msgTimestamp** (string) - Required - The webhook timestamp from headers. ### Request Example (Ktor) ```kotlin val webhookVerifier = WebhookVerifier(secret = System.getenv("PORTONE_WEBHOOK_SECRET")) post("/webhook/portone") { val payload = call.receiveText() val webhook = webhookVerifier.verify( msgBody = payload, msgId = call.request.headers["webhook-id"]!!, msgSignature = call.request.headers["webhook-signature"]!!, msgTimestamp = call.request.headers["webhook-timestamp"]!!, ) when (webhook) { is WebhookTransaction -> println("결제 이벤트: ${webhook.data.paymentId}") is WebhookBillingKey -> println("빌링키 이벤트: ${webhook.data.billingKey}") else -> println("기타 웹훅: ${webhook.type}") } call.respond(HttpStatusCode.OK, "OK") } ``` ``` -------------------------------- ### Webhook Verification (Node.js) Source: https://context7.com/portone-io/server-sdk/llms.txt Verifies the signature of webhooks sent from PortOne servers using HMAC-SHA256 and checks for timestamp expiration. ```APIDOC ## Webhook.verify — 웹훅 서명 검증 (Node.js) ### Description Verifies the signature of webhooks sent from PortOne servers using HMAC-SHA256 and checks for timestamp expiration (±5 minutes). Returns a parsed `Webhook` object upon successful verification. ### Method `Webhook.verify(secret, payload, headers)` ### Parameters - **secret** (string) - Required - The webhook secret key. - **payload** (string) - Required - The raw request body (must not be JSON parsed beforehand). - **headers** (object) - Required - Request headers containing webhook details. - **"webhook-id"** (string) - Required - The webhook ID. - **"webhook-signature"** (string) - Required - The webhook signature. - **"webhook-timestamp"** (string) - Required - The webhook timestamp. ### Request Example (Express.js) ```typescript app.post("/webhook/portone", async (req, res) => { const secret = process.env.PORTONE_WEBHOOK_SECRET!; const payload = req.body; // raw string (JSON.parse 금지!) const headers = req.headers; try { const webhook = await Webhook.verify(secret, payload, { "webhook-id": headers["webhook-id"] as string, "webhook-signature": headers["webhook-signature"] as string, "webhook-timestamp": headers["webhook-timestamp"] as string, }); // 웹훅 타입별 처리 switch (webhook.type) { case "Transaction.Paid": console.log(`결제 완료: ${webhook.data.paymentId}`); break; case "Transaction.Cancelled": console.log(`결제 취소: ${webhook.data.cancellationId}`); break; // ... other cases } res.status(200).send("OK"); } catch (e) { if (e instanceof WebhookVerificationError) { console.error(`웹훅 검증 실패: ${e.reason}`); res.status(400).send("Invalid webhook"); } else if (e instanceof InvalidInputError) { res.status(400).send("Bad request"); } else { res.status(500).send("Internal error"); } } }); ``` ### Error Handling - `WebhookVerificationError`: For signature or timestamp issues. - `InvalidInputError`: For malformed requests. ``` -------------------------------- ### PaymentClient.paymentSchedule Source: https://context7.com/portone-io/server-sdk/llms.txt Schedules a payment to be automatically executed at a specific time. Requires a billing key and payment details. ```APIDOC ## PaymentClient.paymentSchedule — 결제 예약 ### 예약 생성 및 조회 결제를 특정 시각에 자동 실행하도록 예약합니다. 빌링키와 결제 정보를 함께 지정합니다. ```typescript import { PaymentClient } from "@portone/server-sdk"; const client = PaymentClient({ secret: process.env.PORTONE_API_SECRET! }); // 결제 예약 생성 const schedule = await client.paymentSchedule.createPaymentSchedule({ paymentScheduleId: `sched-${Date.now()}`, payment: { billingKey: "billing-key-75ae3cab-6afe-422d-bf34-3a7b1762451d", orderName: "다음 달 구독료", amount: { total: 9900 }, currency: "KRW", customer: { id: "customer-001" }, }, timeToPay: "2024-05-01T09:00:00+09:00", }); console.log(`예약 완료: ${schedule.paymentSchedule.id}, 예정 시각: ${schedule.paymentSchedule.timeToPay}`); // 예약 조회 const info = await client.paymentSchedule.getPaymentSchedule({ paymentScheduleId: schedule.paymentSchedule.id, }); console.log(`예약 상태: ${info.status}`); // 출력: 예약 상태: SCHEDULED ``` ``` -------------------------------- ### Send and Confirm Identity Verification with TypeScript Source: https://context7.com/portone-io/server-sdk/llms.txt This code demonstrates how to send an identity verification request and then confirm it using an OTP. Requires your PortOne API secret. ```typescript import { PortOneClient } from "@portone/server-sdk"; const portone = PortOneClient({ secret: process.env.PORTONE_API_SECRET! }); // 본인인증 요청 전송 await portone.identityVerification.sendIdentityVerification({ identityVerificationId: `idv-${Date.now()}`, channelKey: "channel-key-abc123", customer: { name: { full: "홍길동" }, birthdate: "1990-01-01", phoneNumber: "01012345678", operator: "SKT", }, method: "SMS", }); // 본인인증 확인 (OTP 입력) const result = await portone.identityVerification.confirmIdentityVerification({ identityVerificationId: "idv-1234567890", otp: "123456", }); console.log(`인증 완료: ${result.identityVerification.status}`); // 출력: 인증 완료: VERIFIED ``` -------------------------------- ### PaymentClient.confirmPayment Source: https://context7.com/portone-io/server-sdk/llms.txt Manually approves an authenticated payment. Verifies payment details like amount, currency, and test status to prevent tampering. ```APIDOC ## PaymentClient.confirmPayment — 인증 결제 수동 승인 ### Description Manually approves an authenticated payment. Verifies payment details like amount, currency, and test status to prevent tampering. ### Method `client.confirmPayment(params)` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **paymentId** (string) - Required - The ID of the payment to confirm. - **paymentToken** (string) - Required - The token received from the frontend for payment confirmation. - **currency** (string) - Required - The currency of the payment (e.g., "KRW"). - **totalAmount** (number) - Required - The total amount of the payment. - **taxFreeAmount** (number) - Optional - The tax-free amount of the payment. - **isTest** (boolean) - Optional - Indicates if this is a test payment. Defaults to `false`. ### Request Example ```typescript await client.confirmPayment({ paymentId: "payment-001", paymentToken: "token-received-from-frontend", currency: "KRW", totalAmount: 15000, taxFreeAmount: 0, isTest: false, }); ``` ### Response #### Success Response (200) - **paymentId** (string) - The ID of the confirmed payment. #### Response Example ```json { "paymentId": "payment-001" } ``` ### Error Handling - **INFORMATION_MISMATCH**: Amount or currency does not match. - **INVALID_PAYMENT_TOKEN**: The payment token is invalid. ``` -------------------------------- ### Confirm Payment (Manual Approval) Source: https://context7.com/portone-io/server-sdk/llms.txt Completes an authenticated payment set to manual approval. It verifies payment details like amount, currency, and test status to prevent tampering. ```typescript import { PaymentClient } from "@portone/server-sdk"; import { ConfirmPaymentError } from "@portone/server-sdk/payment"; const client = PaymentClient({ secret: process.env.PORTONE_API_SECRET! }); try { const confirmed = await client.confirmPayment({ paymentId: "payment-001", paymentToken: "token-received-from-frontend", currency: "KRW", totalAmount: 15000, taxFreeAmount: 0, isTest: false, }); console.log(`승인 완료: ${confirmed.paymentId}`); } catch (e) { if (e instanceof ConfirmPaymentError) { if (e.data.type === "INFORMATION_MISMATCH") { console.error("금액 또는 통화가 일치하지 않습니다."); } else if (e.data.type === "INVALID_PAYMENT_TOKEN") { console.error("결제 토큰이 유효하지 않습니다."); } } } ``` -------------------------------- ### PaymentClient.confirmEscrow Source: https://context7.com/portone-io/server-sdk/llms.txt Processes the purchase confirmation for an escrow payment. Can be confirmed by the buyer or the administrator. ```APIDOC ## PaymentClient.confirmEscrow — 에스크로 구매 확정 에스크로 결제의 구매 확정을 처리합니다. 구매자 또는 관리자가 확정할 수 있습니다. ```typescript import { PaymentClient } from "@portone/server-sdk"; const client = PaymentClient({ secret: process.env.PORTONE_API_SECRET! }); const result = await client.confirmEscrow({ paymentId: "payment-escrow-001", fromStore: false, // false: 구매자 확정, true: 관리자(상점) 확정 }); console.log(`에스크로 구매 확정: ${result.paymentId}`); ``` ``` -------------------------------- ### PaymentClient.applyEscrowLogistics Source: https://context7.com/portone-io/server-sdk/llms.txt Registers shipping information for an escrow payment. Includes sender/receiver details and logistics company/tracking number. ```APIDOC ## PaymentClient.applyEscrowLogistics — 에스크로 배송 정보 등록 에스크로 결제의 배송 정보를 등록합니다. 발송자·수취인 정보와 물류사·운송장 번호를 함께 전달합니다. ```typescript import { PaymentClient } from "@portone/server-sdk"; const client = PaymentClient({ secret: process.env.PORTONE_API_SECRET! }); await client.applyEscrowLogistics({ paymentId: "payment-escrow-001", logistics: { company: "CJ_LOGISTICS", invoiceNumber: "123456789012", sentAt: "2024-04-25T10:00:00+09:00", appliedAt: "2024-04-25T10:30:00+09:00", }, sender: { name: "홍길동", phoneNumber: "01012345678" }, receiver: { name: "김철수", phoneNumber: "01098765432" }, sendEmail: true, products: [{ id: "prod-001", name: "노트북", quantity: 1, amount: 1200000 }], }); console.log("에스크로 배송 정보 등록 완료"); ``` ``` -------------------------------- ### Cancel Payment (Full and Partial) Source: https://context7.com/portone-io/server-sdk/llms.txt Use to cancel a payment. Omit `amount` for a full cancellation or specify an amount for a partial cancellation. For virtual account refunds, `refundAccount` must be provided. ```typescript import { PaymentClient } from "@portone/server-sdk"; import { CancelPaymentError } from "@portone/server-sdk/payment"; const client = PaymentClient({ secret: process.env.PORTONE_API_SECRET! }); // 전액 취소 try { const result = await client.cancelPayment({ paymentId: "payment-001", reason: "고객 요청에 의한 취소", }); console.log(`취소 완료. 취소 ID: ${result.cancellation.id}`); } catch (e) { if (e instanceof CancelPaymentError) { if (e.data.type === "PAYMENT_ALREADY_CANCELLED") { console.error("이미 취소된 결제입니다."); } else if (e.data.type === "CANCEL_AMOUNT_EXCEEDS_CANCELLABLE_AMOUNT") { console.error("취소 금액이 취소 가능 금액을 초과했습니다."); } } } // 부분 취소 (5,000원) const partialResult = await client.cancelPayment({ paymentId: "payment-002", amount: 5000, taxFreeAmount: 0, reason: "상품 일부 반품", requester: "CUSTOMER", }); console.log(`부분 취소 완료: ${partialResult.cancellation.cancelledAmount}원 취소됨`); ``` -------------------------------- ### PaymentClient.cancelPayment Source: https://context7.com/portone-io/server-sdk/llms.txt Requests a payment cancellation. If `amount` is omitted, it cancels the full amount; otherwise, it performs a partial cancellation. For virtual account refunds, `refundAccount` must be provided. ```APIDOC ## PaymentClient.cancelPayment — 결제 취소 ### Description Requests a payment cancellation. If `amount` is omitted, it cancels the full amount; otherwise, it performs a partial cancellation. For virtual account refunds, `refundAccount` must be provided. ### Method `client.cancelPayment(params)` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **paymentId** (string) - Required - The ID of the payment to cancel. - **amount** (number) - Optional - The amount to cancel. If omitted, the full amount is cancelled. - **taxFreeAmount** (number) - Optional - The tax-free amount to cancel (used with partial cancellation). - **reason** (string) - Required - The reason for the cancellation. - **requester** (string) - Optional - The entity requesting the cancellation (e.g., "CUSTOMER"). - **refundAccount** (object) - Optional - Required for virtual account refunds. Contains bank code, account number, and depositor name. ### Request Example ```typescript // Full cancellation await client.cancelPayment({ paymentId: "payment-001", reason: "고객 요청에 의한 취소", }); // Partial cancellation (5,000 KRW) await client.cancelPayment({ paymentId: "payment-002", amount: 5000, taxFreeAmount: 0, reason: "상품 일부 반품", requester: "CUSTOMER", }); ``` ### Response #### Success Response (200) - **cancellation** (object) - Details of the cancellation. - **id** (string) - The ID of the cancellation. - **cancelledAmount** (number) - The amount that was cancelled. #### Response Example ```json { "cancellation": { "id": "cancellation-id-123", "cancelledAmount": 5000 } } ``` ### Error Handling - **PAYMENT_ALREADY_CANCELLED**: The payment has already been cancelled. - **CANCEL_AMOUNT_EXCEEDS_CANCELLABLE_AMOUNT**: The cancellation amount exceeds the cancellable amount. ``` -------------------------------- ### PaymentClient.getPayments Source: https://context7.com/portone-io/server-sdk/llms.txt Retrieves a list of payments using filters and pagination. Supports filtering by status, method, date range, etc. ```APIDOC ## PaymentClient.getPayments — 결제 다건 조회 (페이지 기반) 조건 필터와 페이지 정보를 이용해 결제 건 목록을 조회합니다. `filter`로 결제 상태, 결제 수단, 날짜 범위 등 다양한 조건을 지정할 수 있습니다. ```typescript import { PaymentClient } from "@portone/server-sdk"; const client = PaymentClient({ secret: process.env.PORTONE_API_SECRET! }); const result = await client.getPayments({ page: { number: 0, size: 20 }, filter: { status: ["PAID"], from: "2024-01-01T00:00:00+09:00", until: "2024-01-31T23:59:59+09:00", }, }); console.log(`전체 건수: ${result.page.totalCount}`); for (const payment of result.items) { console.log(`[${payment.id}] ${payment.orderName} - ${payment.status}`); } // 출력 예: // 전체 건수: 42 // [payment-001] 상품 A - PAID // [payment-002] 상품 B - PAID ``` ``` -------------------------------- ### Confirm Escrow Payment Source: https://context7.com/portone-io/server-sdk/llms.txt Processes the confirmation of an escrow payment. Can be confirmed by the buyer or the store administrator. ```typescript import { PaymentClient } from "@portone/server-sdk"; const client = PaymentClient({ secret: process.env.PORTONE_API_SECRET! }); const result = await client.confirmEscrow({ paymentId: "payment-escrow-001", fromStore: false, // false: 구매자 확정, true: 관리자(상점) 확정 }); console.log(`에스크로 구매 확정: ${result.paymentId}`); ``` -------------------------------- ### Issue and Cancel Cash Receipts with TypeScript Source: https://context7.com/portone-io/server-sdk/llms.txt Use this snippet to immediately issue or cancel cash receipts. Ensure you have your PortOne API secret configured in your environment variables. ```typescript import { PaymentClient } from "@portone/server-sdk"; const client = PaymentClient({ secret: process.env.PORTONE_API_SECRET! }); // 현금영수증 즉시 발급 const issued = await client.cashReceipt.issueCashReceipt({ paymentId: `cr-${Date.now()}`, channelKey: "channel-key-abc123", type: "PERSONAL", orderName: "도서 구매", amount: { total: 15000, taxFree: 0 }, currency: "KRW", identityNumber: "01012345678", issueId: `issue-${Date.now()}`, }); console.log(`현금영수증 발급 완료: ${issued.cashReceipt.receiptUrl}`); // 현금영수증 취소 await client.cashReceipt.cancelCashReceiptByPaymentId({ paymentId: issued.cashReceipt.paymentId }); console.log("현금영수증 취소 완료"); ``` -------------------------------- ### Cash Receipt Operations Source: https://context7.com/portone-io/server-sdk/llms.txt Handles immediate issuance and cancellation of cash receipts using the PaymentClient. ```APIDOC ## Cash Receipt Operations ### Description Handles immediate issuance and cancellation of cash receipts using the PaymentClient. ### Method `client.cashReceipt.issueCashReceipt` for issuance, `client.cashReceipt.cancelCashReceiptByPaymentId` for cancellation. ### Parameters for `issueCashReceipt` - **paymentId** (string) - Required - Unique identifier for the payment. - **channelKey** (string) - Required - The channel key for the transaction. - **type** (string) - Required - Type of receipt (e.g., 'PERSONAL'). - **orderName** (string) - Required - Name of the order. - **amount** (object) - Required - Contains 'total' and 'taxFree' amounts. - **total** (number) - Required - Total amount. - **taxFree** (number) - Required - Tax-free amount. - **currency** (string) - Required - Currency code (e.g., 'KRW'). - **identityNumber** (string) - Required - User's identification number. - **issueId** (string) - Required - Unique identifier for the issuance. ### Parameters for `cancelCashReceiptByPaymentId` - **paymentId** (string) - Required - The payment ID of the cash receipt to cancel. ### Request Example (Issue) ```typescript const issued = await client.cashReceipt.issueCashReceipt({ paymentId: `cr-${Date.now()}`, channelKey: "channel-key-abc123", type: "PERSONAL", orderName: "도서 구매", amount: { total: 15000, taxFree: 0 }, currency: "KRW", identityNumber: "01012345678", issueId: `issue-${Date.now()}`, }); ``` ### Request Example (Cancel) ```typescript await client.cashReceipt.cancelCashReceiptByPaymentId({ paymentId: issued.cashReceipt.paymentId }); ``` ### Response (Issue Success) Returns an object containing the issued cash receipt details, including `receiptUrl`. ```