Skip to content

About

bootpay server side plugin for java

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

Bootpay Java Server Side Library

부트페이 공식 Java 라이브러리 입니다 (서버사이드 용)

java언어로 작성된 어플리케이션, 프레임워크 등에서 사용가능합니다.

  • PG 결제창 연동은 클라이언트 라이브러리에서 수행됩니다. (Javascript, Android, iOS, React Native, Flutter 등)
  • 결제 검증 및 취소, 빌링키 발급, 본인인증 등의 수행은 서버사이드에서 진행됩니다. (Java, PHP, Python, Ruby, Node.js, Go, ASP.NET 등)

목차

3.3.0 부터 통일 API 를 함께 제공합니다. PG 와 Commerce 를 같은 형태(빌더 · 모듈 · 단일 응답 타입)로 호출할 수 있습니다 — 통일 API (3.3.0~) 참조. 아래 문서의 기존 사용법은 그대로 계속 동작하며, 바뀐 것이 없습니다.

Gradle로 설치하기

build.gradle (project)

dependencies {
    implementation 'io.github.bootpay:backend:+' // + 는 최신버전을 의미 
}

환경변수 설정

예제와 테스트는 각 SDK 루트의 .env 파일을 우선 읽습니다. 먼저 .env.example을 복사한 뒤 필요한 키만 변경하세요. .env는 gitignore 처리되어 커밋되지 않습니다.

cp .env.example .env
# BOOTPAY_ENV=production 또는 development

주요 변수:

BOOTPAY_ENV=production
BOOTPAY_PG_CLIENT_KEY_PROD=...
BOOTPAY_PG_SECRET_KEY_PROD=...
BOOTPAY_PG_CLIENT_KEY_DEV=...
BOOTPAY_PG_SECRET_KEY_DEV=...
BOOTPAY_COMMERCE_CLIENT_KEY_PROD=...
BOOTPAY_COMMERCE_SECRET_KEY_PROD=...
BOOTPAY_COMMERCE_CLIENT_KEY_DEV=...
BOOTPAY_COMMERCE_SECRET_KEY_DEV=...

변수가 없으면 SDK 테스트용 기본값(NodeJS 기준 ck/sk)으로 fallback 합니다.

통일 API (3.3.0~)

PG 와 Commerce 는 각각 따로 자라면서 생성 방식·응답 타입·호출 표면이 서로 달라졌습니다. 3.3.0 은 둘을 같은 형태로 쓰는 새 표면을 추가합니다. 기존 표면은 아무것도 바뀌지 않았고 계속 동작하므로, 쓰던 코드는 그대로 두고 새로 짜는 코드부터 적용하면 됩니다.

기존 표면 통일 표면 (3.3.0~)
생성 PG 는 static factory, Commerce 는 생성자 + TokenPayload 양쪽 다 builder()
환경 String devMode (오타 시 무시되거나 baseUrl 이 빔) BootpayMode enum, 기본 PRODUCTION
응답 PG 는 HashMap, Commerce 는 BootpayStoreResponse 양쪽 다 BootpayResponse
호출 PG 는 메서드 31개가 평면, Commerce 는 모듈 양쪽 다 모듈
role withRole("manager") 문자열 BootpayRole enum

생성

import kr.co.bootpay.common.BootpayMode;
import kr.co.bootpay.common.BootpayResponse;
import kr.co.bootpay.common.BootpayRole;
import kr.co.bootpay.pg.Bootpay;
import kr.co.bootpay.store.BootpayCommerce;

// PG
Bootpay pg = Bootpay.builder()
        .clientKey(System.getenv("BOOTPAY_PG_CLIENT_KEY_PROD"))
        .secretKey(System.getenv("BOOTPAY_PG_SECRET_KEY_PROD"))
        .mode(BootpayMode.PRODUCTION)
        .build();

// Commerce — 같은 형태
BootpayCommerce commerce = BootpayCommerce.builder()
        .clientKey(System.getenv("BOOTPAY_COMMERCE_CLIENT_KEY_PROD"))
        .secretKey(System.getenv("BOOTPAY_COMMERCE_SECRET_KEY_PROD"))
        .mode(BootpayMode.PRODUCTION)
        .role(BootpayRole.USER)
        .build();

commerce.issueAccessToken();   // Commerce 는 다른 호출 전에 토큰 발급이 필요합니다

application_id / private_key 방식도 같은 빌더로 만들 수 있습니다.

Bootpay legacy = Bootpay.builder()
        .applicationId(applicationId)
        .privateKey(privateKey)
        .build();

키가 짝을 이루지 않으면 build() 가 그 자리에서 IllegalStateException 으로 알려줍니다. (기존에는 호출 시점까지 갔다가 실패했습니다.)

호출과 응답

BootpayResponse res = pg.payment.get(receiptId);

if (res.isSuccess()) {
    Map<String, Object> data = res.getData();
    System.out.println(data.get("status_locale"));
} else {
    // 에러 코드는 숫자(-401)일 수도, 문자열("TEMPLATE_NOT_FOUND" — 알림톡)일 수도 있다
    System.out.println(res.getErrorCodeString() + " " + res.getMessage());
}

// 기존 HashMap 기반 코드와 섞어 쓸 때
HashMap<String, Object> raw = res.asMap();

getData() 는 응답 본문만 담습니다 (http_status 제외). 가공 전 원본이 필요하면 asMap() 을 쓰세요.

PG 모듈

모듈 메서드
pg.payment get · getByOrderId · confirm · cancel · link · methods
pg.billing issue · issueTransfer · publishTransfer · get · getByReceiptId · getSequential · destroy · pay · reserve · getReserve · cancelReserve
pg.auth request · confirm · realarm · certificate
pg.cash request · cancel · requestByBootpay · cancelByBootpay
pg.escrow shippingStart
pg.user token
pg.wallet list · pay (deprecated)

기존 평면 메서드와의 대응은 아래와 같습니다. 요청 내용은 완전히 동일합니다.

pg.getReceipt(receiptId)              →  pg.payment.get(receiptId)
pg.lookupOrderId(orderId)             →  pg.payment.getByOrderId(orderId)
pg.receiptCancel(cancel)              →  pg.payment.cancel(cancel)
pg.requestLink(payload)               →  pg.payment.link(payload)
pg.lookupPaymentMethods()             →  pg.payment.methods()
pg.getBillingKey(subscribe)           →  pg.billing.issue(subscribe)
pg.getBillingKeyTransfer(subscribe)   →  pg.billing.issueTransfer(subscribe)
pg.publishBillingKeyTransfer(id)      →  pg.billing.publishTransfer(id)
pg.lookupBillingKeyByKey(billingKey)  →  pg.billing.get(billingKey)
pg.lookupBillingKey(receiptId)        →  pg.billing.getByReceiptId(receiptId)
pg.lookupSequentialBillingKey(w,b,u)  →  pg.billing.getSequential(w, b, u)
pg.destroyBillingKey(billingKey)      →  pg.billing.destroy(billingKey)
pg.requestSubscribe(payload)          →  pg.billing.pay(payload)
pg.reserveSubscribe(payload)          →  pg.billing.reserve(payload)
pg.reserveSubscribeLookup(reserveId)  →  pg.billing.getReserve(reserveId)
pg.reserveCancelSubscribe(reserveId)  →  pg.billing.cancelReserve(reserveId)
pg.requestAuthentication(auth)        →  pg.auth.request(auth)
pg.confirmAuthentication(id, otp)     →  pg.auth.confirm(id, otp)
pg.realarmAuthentication(id)          →  pg.auth.realarm(id)
pg.certificate(receiptId)             →  pg.auth.certificate(receiptId)
pg.requestCashReceipt(cashReceipt)    →  pg.cash.request(cashReceipt)
pg.requestCashReceiptCancel(cancel)   →  pg.cash.cancel(cancel)
pg.requestCashReceiptByBootpay(cr)    →  pg.cash.requestByBootpay(cr)
pg.requestCashReceiptCancelByBootpay  →  pg.cash.cancelByBootpay(cancel)
pg.shippingStart(shipping)            →  pg.escrow.shippingStart(shipping)
pg.getUserToken(userToken)            →  pg.user.token(userToken)
pg.getAccessToken()                   →  pg.issueAccessToken()

Commerce 모듈

모듈 구성은 기존 BootpayStore 와 같고, 반환 타입만 BootpayResponse 로 통일됩니다.

BootpayResponse res = commerce.user.list(params);
commerce.order.detail(orderId);
commerce.orderSubscription.requestIng.pause(params);

이름이 정리된 곳은 다음과 같습니다.

store.user.userLogin(id, pw)          →  commerce.user.mallLogin(id, pw)
store.user.userSession(jwt)           →  commerce.user.mallSession(jwt)
store.user.userLogout(jwt)            →  commerce.user.mallLogout(jwt)
store.user.userJoin(params)           →  commerce.user.mallJoin(params)
store.user.userJoinCheck(type, pk)    →  commerce.user.mallJoinCheck(type, pk)
store.product.products()              →  commerce.product.mallList()
store.product.productDetail(id, jwt)  →  commerce.product.mallDetail(id, jwt)
store.mallSetting.getMallSetting()    →  commerce.mallSetting.detail()
store.mallSetting.updateMallSetting() →  commerce.mallSetting.update()
store.getAccessToken()                →  commerce.issueAccessToken()

commerce.subscriptionSetting 은 기존 BootpayStore 에서는 배선되어 있지 않아 쓸 수 없던 모듈로, 통일 표면에서 새로 노출됩니다.

알림톡 (3.6.0~)

/v1/alimtalk/* 35종을 모듈 7개로 제공합니다. BootpayStore 와 BootpayCommerce 양쪽에 같은 이름으로 있습니다.

모듈 메서드
alimtalkSend send · sendBulk · cancel
alimtalkSender categories · otp · create · list · detail · release · variableExamples
alimtalkTemplate list · create · detail · update · delete · register · inspect · export · image · highlightImage
alimtalkOfficial list · recommend · detail
alimtalkMessage list · stats · detail
alimtalkOptout list · create · check · release
alimtalkWebhook detail · update · test · rotateSecret · deliveries

⚠️ 샌드박스가 없습니다. alimtalkSend.send / sendBulk 는 실제로 카카오톡이 나가고 과금됩니다. alimtalkSender.otp 는 문자를 실제 발송하고, alimtalkSender.create 는 카카오에 발신프로필을 실제 등록합니다. alimtalkTemplate.create 는 register 를 명시적으로 false 로 주지 않으면 생성 즉시 대행사·카카오에 등록됩니다.

Map<String, Object> variables = new HashMap<>();
variables.put("user_name", "홍길동");

AlimtalkSendParams params = new AlimtalkSendParams();
params.templateCode = "TEMPLATE_CODE";
params.to = "01000000000";
params.variables = variables;
params.refId = "order-20260827-0001"; // 멱등 키 — 같은 값으로 재요청하면 기존 접수를 그대로 돌려줍니다
params.fallback = false;              // 미지정(null)이면 프로젝트 기본값, false 는 명시적으로 끕니다
params.webhookUrl = "https://example.com/hooks/alimtalk"; // 이 건의 결과 웹훅을 받을 주소

BootpayResponse res = commerce.alimtalkSend.send(params);

webhookUrl 을 주면 그 요청의 결과 웹훅(발송 성공·실패·문자 대체발송·예약취소)이 그 주소로만 가고 프로젝트 웹훅 설정은 쓰이지 않습니다. https 만 허용하며 2,000자를 넘으면 3028 로 거부됩니다. 서명은 프로젝트 시크릿으로 하므로 시크릿만 필요하면 alimtalkWebhook.rotateSecret() 으로 설정 없이 발급받을 수 있습니다. sendBulk 에서는 요청 단위 하나라 그 요청으로 나간 모든 수신자 건의 웹훅이 같은 주소로 가고, 형식이 틀리면 요청 전체가 3028 로 거부됩니다.

⚠️ 같은 refId 로 이미 접수·성공한 건을 다시 요청하면 기존 접수가 그대로 돌아오므로 새 webhookUrl 은 무시됩니다.

Bootpay-Role 은 인스턴스 설정과 무관하게 항상 user 로 나가고, 알림톡 요청에는 Idempotency-Key 를 붙이지 않습니다 (서버가 읽지 않으며, 멱등은 refId 로만 성립합니다).

알림톡 웹훅은 주문·구독 웹훅(webhook.sendTest)과 완전히 별개입니다. 알림톡 이벤트를 기존 주문 웹훅 URL 로 태우면 그 수신 서버가 모르는 payload 를 받아 기존 연동이 깨지므로 수신 URL 을 따로 둡니다.

alimtalkWebhook.update 의 retryCount 로 건별 재시도 횟수를 정합니다. 1~25 만 받고 기본값은 10, 범위 밖이면 11304 로 거부됩니다. 미지정(null)이면 바디에서 빠져 기존 값을 그대로 유지합니다.

AlimtalkWebhookUpdateParams params = new AlimtalkWebhookUpdateParams();
params.url = "https://example.com/hooks/alimtalk";
params.events = Arrays.asList(301, 302, 310, 311);
params.enabled = true;
params.retryCount = 15; // 건별 재시도 횟수 (1~25, 기본 10)

BootpayResponse res = commerce.alimtalkWebhook.update(params);

alimtalkTemplate.export 는 SDK 기본 format 이 json 입니다. csv 를 주면 파싱 없이 { body, content_type } 원문을 담아 돌려줍니다.

신규 표면에 아직 없는 기존 메서드가 필요하면 commerce.unwrap() 으로 내부 BootpayStore 인스턴스를 꺼내 쓸 수 있습니다 (토큰과 role 이 공유됩니다).

커머스 게시판 — FAQ · 공지사항 · 1:1 문의 · 상품문의 · 상품평 (3.7.0~)

몰 게시판 API 28종을 모듈 5개로 제공합니다. BootpayStore 와 BootpayCommerce 양쪽에 같은 이름으로 있습니다.

모듈 경로 메서드
faq /v1/faqs list · detail · create · update · delete
notice /v1/notices list · detail · create · update · delete
inquiry /v1/inquiries list · detail · create · update · delete · answer
productQna /v1/product-qnas list · detail · create · update · delete · answer
productReview /v1/reviews list · detail · create · update · delete · reply

Bootpay-Role 은 인스턴스 설정이 아니라 파라미터의 supervisor 값으로 정해집니다 — true 면 supervisor, 아니면 user 로 나갑니다. 등록·수정·삭제·답변처럼 운영자 전용인 메서드는 항상 supervisor 로, 작성·수정처럼 회원 전용인 메서드는 항상 user 로 나갑니다. 회원은 userId(회원 _id 또는 외부 회원 ID) · loginId · userJwt(→ Bootpay-User-JWT 헤더) 중 하나로 지정합니다. 모든 요청에 Idempotency-Key 가 붙고, 미지정이면 호출마다 새로 만들어집니다.

// 고객 모드 — page 1 / limit 20 이 기본값입니다
BootpayResponse res = commerce.faq.list();

// 운영자 모드 — view: "all" 이면 비공개 글까지 조회됩니다
NoticeListParams params = new NoticeListParams();
params.view = "all";
params.supervisor = true;
commerce.notice.list(params);

// 답변·답글은 없으면 만들고 있으면 내용을 바꿉니다 (supervisor 전용)
commerce.inquiry.answer(inquiryId, "2~3 영업일 내에 발송됩니다.");
commerce.productReview.reply(reviewId, "소중한 후기 감사합니다.");

images 는 보내면 목록 전체를 교체합니다 (빈 목록이면 모두 삭제). 마찬가지로 inquiry.update 의 title 에 "" 를 보내면 제목이 지워집니다 — 값을 그대로 유지하려면 해당 필드를 null 로 두세요.

상품문의 삭제의 guestPassword 는 URL 쿼리가 아니라 본문으로 전송됩니다. 상품 상세의 공개 상품평 목록(GET /v1/products/{product_id}/reviews)은 productReview.list 와 다른 경로입니다.

뱅크알람 입금대기 (3.8.0~)

가맹점 서버가 아직 입금되지 않은 주문을 등록하면, 판매자 폰의 입금 알림 중 금액 · 입금자명이 맞는 것을 뱅크알람이 그 주문에 연결해 입금을 확인하고 입금 웹훅으로 알립니다. BootpayStore 와 BootpayCommerce 양쪽에 deposit 모듈로 제공되며, 전부 인스턴스 role 과 무관하게 BOOTPAY-ROLE: supervisor 로 요청합니다.

// 판매자 입금 계좌 목록 — 첫 계좌가 대표 계좌(primary: true)
BootpayResponse accounts = commerce.deposit.accounts();

// 웹훅 서명 비밀값 발급 — 원문은 이 응답에서만 받고, 다시 부르면 바뀝니다
BootpayResponse secret = commerce.deposit.webhookSecret();

// 입금대기 등록 — 같은 merchantOrderId 로 같은 금액·입금자명·계좌를 다시 보내면 기존 주문을 돌려줍니다
// (하나라도 다르면 18110 BANK_DEPOSIT_MERCHANT_ID_CONFLICT)
DepositCreateParams params = new DepositCreateParams();
params.merchantOrderId = "ORDER_1234";
params.price = 15000;
params.depositor = "홍길동";             // 은행 입금 알림과 맞춰 볼 이름
params.buyerName = "홍길동";             // 미지정시 입금자명
params.buyerEmail = "buyer@example.com"; // 주면 입금 안내 · 확인 메일 (무료)
params.accountId = "ACCOUNT_ID";          // 미지정시 대표 계좌
params.webhookUrl = "https://example.com/hooks/deposit"; // 비밀값을 먼저 발급받아야 합니다(18131)

// 구매자 알림톡 (유료) — buyerPhone 필수, link 는 https
DepositNotifyParams notify = new DepositNotifyParams();
notify.depositGuide = true;
notify.depositPaid = true;
notify.link = "https://example.com/orders/ORDER_1234";
params.buyerPhone = "01000000000";
params.notify = notify;

commerce.deposit.create(params);

// 조회 · 취소 — 가맹점 주문번호 또는 부트페이 주문번호
commerce.deposit.detail("ORDER_1234"); // status: awaiting · paid · expired · cancelled
commerce.deposit.cancel("ORDER_1234"); // 이미 닫힌 주문은 그대로 돌려줍니다

create 는 params.idempotencyKey, webhookSecret(key) · cancel(id, key) 는 선택 인자로 Idempotency-Key 를 지정할 수 있습니다. 미지정하면 호출마다 새 UUID를 사용합니다. 선택값이 null 이면 본문에서 빠지고, 알림 설정의 명시적 false 는 그대로 전송됩니다.

입금 웹훅은 X-Bootpay-Signature: sha256=HMAC_SHA256(secret, "{X-Bootpay-Timestamp}.{raw_body}") 로 서명됩니다. 이벤트 코드는 SConstant.BANK_DEPOSIT_WEBHOOK_EVENT_* 상수(400~407)를 참고하세요.

전체 실행 예제는 app/src/main/java/com/example/bootpay/store/Deposit.java를 참고해주세요.

입점사 지급 요청 (3.8.0~)

가맹점 지정 금액(manual) 정산 방식의 쇼핑몰이 입점사와 지급액을 정해 지급 원장에 등록합니다. 실제 송금은 별도 배치가 처리하며, 세 API 모두 인스턴스 role 과 무관하게 BOOTPAY-ROLE: supervisor 로 요청합니다.

// 지급 요청 — 같은 merchantPayoutId 로 다시 보내면 처음 만든 항목을 그대로 돌려줍니다
PayoutCreateParams create = new PayoutCreateParams();
create.subSellerId = "SUB_SELLER_ID";
create.amount = 150000L;
create.merchantPayoutId = "PAYOUT_20261112_001";
create.orderNumber = "ORDER_1234";       // 선택
create.payoutDate = "2026-11-20";        // 선택, YYYY-MM-DD (미지정시 오늘)
commerce.payout.create(create);

// 조회 — merchant_payout_id 또는 지급 항목 id
commerce.payout.detail("PAYOUT_20261112_001");

// 목록 — 최근 생성 순, 응답은 항목 배열입니다(전체 개수 없음)
PayoutListParams list = new PayoutListParams();
list.status = CommercePayout.Status.READY;
list.subSellerId = "SUB_SELLER_ID";
list.from = "2026-11-01";
list.to = "2026-11-30";
list.page = 1;
list.limit = 20;
commerce.payout.list(list);

create.idempotencyKey · list.idempotencyKey · detail(id, idempotencyKey) 로 Idempotency-Key 를 직접 지정할 수 있고, 미지정하면 호출마다 새 UUID를 사용합니다. 선택값은 null 이면 본문·쿼리에서 빠지며, 목록의 page · limit 기본값은 SDK가 주입하지 않고 서버에 맡깁니다. 상태는 READY · HELD · SENDING · REQUESTED · PROCESSING · SUCCEEDED · FAILED · CANCELLED · UNKNOWN · VOID 입니다.

전체 실행 예제는 app/src/main/java/com/example/bootpay/store/Payout.java를 참고해주세요. create() 는 실제 지급 원장을 만들므로 기본 실행에서 제외되어 있습니다.

구독 가격(기준금액) 변경

price 는 회차별 결제 금액의 기준금액입니다. 변경하면 결제예정(READY) 회차의 청구액이 즉시 다시 계산되고, 이후 회차도 이 금액으로 생성됩니다. 이미 결제된 회차는 그대로이며, 0 이하는 받지 않습니다.

OrderSubscriptionUpdateParams params = new OrderSubscriptionUpdateParams();
params.orderSubscriptionId = "ORDER_SUBSCRIPTION_ID";
params.price = 19900.0;
commerce.orderSubscription.update(params);

회차별 가감산 조정

특정 회차만 금액을 가감하려면 조정항목을 사용합니다. 회차 지정 방법은 3가지입니다.

// 5회차 한 건만
SOrderSubscriptionAdjustment one = new SOrderSubscriptionAdjustment("5회차 할인", -1000.0);
one.duration = 5;
commerce.orderSubscriptionAdjustment.create("ORDER_SUBSCRIPTION_ID", one);

// 3~7회차 각각 한 건씩 (총 5건)
SOrderSubscriptionAdjustment range = new SOrderSubscriptionAdjustment("3~7회차 할인", -1000.0);
range.durationFrom = 3;
range.durationTo   = 7;
commerce.orderSubscriptionAdjustment.create("ORDER_SUBSCRIPTION_ID", range);

// 3회차부터 계약 끝까지 (레코드 1건, durationTo 는 무시)
SOrderSubscriptionAdjustment unlimited = new SOrderSubscriptionAdjustment("3회차 이후 상시 할인", -1000.0);
unlimited.durationFrom = 3;
unlimited.isUnlimited  = true;
commerce.orderSubscriptionAdjustment.create("ORDER_SUBSCRIPTION_ID", unlimited);

상한은 계약 총회차이며, 총회차가 무제한인 계약은 60회차까지입니다. 이미 결제가 끝난 회차는 거절되며, 범위 중 한 회차라도 최종 금액이 음수면 전부 거절됩니다 (부분 반영 없음).

type 을 지정하지 않으면 서버가 자동으로 판정합니다 (price > 0 → 추가금액, 아니면 회차별 할인).

사용하기

권장 인증 방식은 client_key/secret_key입니다. 기존 application_id/private_key 생성자도 하위 호환을 위해 계속 동작합니다.

BootpayExample.java

import com.google.gson.Gson;
import kr.co.bootpay.Bootpay;
import kr.co.bootpay.model.BankCode;
import kr.co.bootpay.model.response.ResDefault;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;

import java.util.HashMap;


public class BootpayExample {
    static Bootpay bootpay;

    public static void main(String[] args) {
        bootpay = Bootpay.withClientKey(System.getenv("BOOTPAY_PG_CLIENT_KEY_PROD"), System.getenv("BOOTPAY_PG_SECRET_KEY_PROD"));
// Legacy fallback: new Bootpay(System.getenv("BOOTPAY_APPLICATION_ID"), System.getenv("BOOTPAY_PRIVATE_KEY"));
        goGetToken();
    }

    public static void goGetToken() {
        try {
            BootpayStoreResponse res = bootpay.getAccessToken();
            if (res.isSuccess()) {
                System.out.println("goGetToken success: " + res.getData());
            } else {
                System.out.println("goGetToken false: " + res.getData());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

함수 단위의 샘플 코드는 이곳을 참조하세요.

1. 토큰 발급

부트페이와 서버간 통신을 하기 위해서는 부트페이 서버로부터 토큰을 발급받아야 합니다.
발급된 토큰은 30분간 유효하며, 최초 발급일로부터 30분이 지날 경우 토큰 발급 함수를 재호출 해주셔야 합니다.

Bootpay bootpay = Bootpay.withClientKey(System.getenv("BOOTPAY_PG_CLIENT_KEY_PROD"), System.getenv("BOOTPAY_PG_SECRET_KEY_PROD"));
// Legacy fallback: new Bootpay(System.getenv("BOOTPAY_APPLICATION_ID"), System.getenv("BOOTPAY_PRIVATE_KEY"));
try {
   BootpayStoreResponse res = bootpay.getAccessToken();
   if(res.isSuccess()) {
       System.out.println("goGetToken success: " + res.getData());
   } else {
       System.out.println("goGetToken false: " + res.getData());
   }
} catch (Exception e) {
   e.printStackTrace();
}

2. 결제 단건 조회

결제창 및 정기결제에서 승인/취소된 결제건에 대하여 올바른 결제건인지 서버간 통신으로 결제검증을 합니다.

Bootpay bootpay = Bootpay.withClientKey(System.getenv("BOOTPAY_PG_CLIENT_KEY_PROD"), System.getenv("BOOTPAY_PG_SECRET_KEY_PROD"));
// Legacy fallback: new Bootpay(System.getenv("BOOTPAY_APPLICATION_ID"), System.getenv("BOOTPAY_PRIVATE_KEY"));
bootpay.getAccessToken();

try {
   HashMap<String, Object> res = bootpay.getReceipt(receiptId);
   if(res.get("error_code") == null) { //success
       System.out.println("getReceipt success: " + res);
   } else {
       System.out.println("getReceipt false: " + res);
   }
} catch (Exception e) {
   e.printStackTrace();
}

3. 결제 취소 (전액 취소 / 부분 취소)

price를 지정하지 않으면 전액취소 됩니다.

  • 휴대폰 결제의 경우 이월될 경우 이통사 정책상 취소되지 않습니다
  • 정산받으실 금액보다 취소금액이 클 경우 PG사 정책상 취소되지 않을 수 있습니다. 이때 PG사에 문의하시면 되겠습니다.
  • 가상계좌의 경우 CMS 특약이 되어있지 않으면 취소되지 않습니다. 그러므로 결제 테스트시에는 가상계좌로 테스트 하지 않길 추천합니다.

부분취는 카드로 결제된 건만 가능하며, 일부 PG사만 지원합니다. 요청시 price에 금액을 지정하시면 되겠습니다.

  • (지원가능 PG사: 이니시스, kcp, 다날, 페이레터, 나이스페이, 카카오페이, 페이코)

간혹 개발사에서 실수로 여러번 부분취소를 보내서 여러번 취소되는 경우가 있기때문에, 부트페이에서는 부분취소 중복 요청을 막기 위해 cancel_id 라는 필드를 추가했습니다. cancel_id를 지정하시면, 해당 건에 대해 중복 요청방지가 가능합니다.

Bootpay bootpay = Bootpay.withClientKey(System.getenv("BOOTPAY_PG_CLIENT_KEY_PROD"), System.getenv("BOOTPAY_PG_SECRET_KEY_PROD"));
// Legacy fallback: new Bootpay(System.getenv("BOOTPAY_APPLICATION_ID"), System.getenv("BOOTPAY_PRIVATE_KEY"));
bootpay.getAccessToken();

Cancel cancel = new Cancel();
cancel.receiptId = "628b2206d01c7e00209b6087";
cancel.name = "관리자";
cancel.reason = "테스트 결제";
//        cancel.price = 1000.0; //부분취소 요청시
//        cancel.cancelId = "12342134"; //부분취소 요청시, 중복 부분취소 요청하는 실수를 방지하고자 할때 지정
//        RefundData refund = new RefundData(); // 가상계좌 환불 요청시, 단 CMS 특약이 되어있어야만 환불요청이 가능하다.
//        refund.account = "675601012341234"; //환불계좌
//        refund.accountholder = "홍길동"; //환불계좌주
//        refund.bankcode = BankCode.getCode("국민은행");//은행코드
//        cancel.refund = refund;

try {
   HashMap<String, Object> res = bootpay.receiptCancel(cancel);
   if(res.get("error_code") == null) { //success
       System.out.println("receiptCancel success: " + res);
   } else {
       System.out.println("receiptCancel false: " + res);
   }
} catch (Exception e) {
   e.printStackTrace();
}

4. 자동/빌링/정기 결제

4-1. 카드 빌링키 발급

REST API 방식으로 고객으로부터 카드 정보를 전달하여, PG사에게 빌링키를 발급받을 수 있습니다. 발급받은 빌링키를 저장하고 있다가, 원하는 시점, 원하는 금액에 결제 승인 요청하여 좀 더 자유로운 결제시나리오에 적용이 가능합니다.

  • 비인증 정기결제(REST API) 방식을 지원하는 PG사만 사용 가능합니다.
Bootpay bootpay = Bootpay.withClientKey(System.getenv("BOOTPAY_PG_CLIENT_KEY_PROD"), System.getenv("BOOTPAY_PG_SECRET_KEY_PROD"));
// Legacy fallback: new Bootpay(System.getenv("BOOTPAY_APPLICATION_ID"), System.getenv("BOOTPAY_PRIVATE_KEY"));
bootpay.getAccessToken();

Subscribe subscribe = new Subscribe();
subscribe.orderName = "정기결제 테스트 아이템";
subscribe.subscriptionId = "" + (System.currentTimeMillis() / 1000);
subscribe.pg = "payapp";
subscribe.cardNo = "5570**********1074"; //실제 테스트시에는 *** 마스크처리가 아닌 숫자여야 함
subscribe.cardPw = "**"; //실제 테스트시에는 *** 마스크처리가 아닌 숫자여야 함
subscribe.cardExpireYear = "**"; //실제 테스트시에는 *** 마스크처리가 아닌 숫자여야 함
subscribe.cardExpireMonth = "**"; //실제 테스트시에는 *** 마스크처리가 아닌 숫자여야 함
subscribe.cardIdentityNo = ""; //생년월일 또는 사업자 등록번호 (- 없이 입력)


subscribe.user = new User();
subscribe.user.username = "홍길동";
subscribe.user.phone = "01011112222";

try {
   HashMap<String, Object> res = bootpay.getBillingKey(subscribe);
   JSONObject json =  new JSONObject(res);
   System.out.printf( "JSON: %s", json);
   
   if(res.get("error_code") == null) { //success
       System.out.println("getBillingKey success: " + res);
   } else {
       System.out.println("getBillingKey false: " + res);
   }
} catch (Exception e) {
   e.printStackTrace();
}

4-2. 계좌 빌링키 발급

REST API 방식으로 고객의 계좌 정보를 전달하여, PG사에게 빌링키 발급을 요청합니다. 요청 후 빌링키가 바로 발급되진 않고, 출금동의 확인 절차까지 진행해야 빌링키가 발급됩니다. 먼저 빌링키를 요청합니다.

public static void getBillingKeyTransfer() {
     try {
         Subscribe subscribe = new Subscribe();
         subscribe.orderName = "테스트 결제";

         subscribe.pg = "나이스페이";
         subscribe.bankName = "국민";
         subscribe.bankAccount = "67512341234472";
         subscribe.username = "홍길동";
         subscribe.identityNo = "901014";
         subscribe.phone = "01012341234";
         subscribe.subscriptionId = "" + (System.currentTimeMillis() / 1000);
//            subscribe.tax

         HashMap<String, Object> res = bootpay.getBillingKeyTransfer(subscribe);
         if(res.get("error_code") == null) { //success
             System.out.println("success: " + res);
         } else {
             System.out.println("false: " + res);
         }
     } catch (Exception e) {
         e.printStackTrace();
     }
 }

이후 빌링키 발급 요청시 응답받은 receipt_id로, 출금 동의 확인을 요청합니다.

public static void publishBillingKeyTransfer() {
     try {
         HashMap<String, Object> res = bootpay.publishBillingKeyTransfer("66541bc4ca4517e69343e24c");
         if(res.get("error_code") == null) { //success
             System.out.println("success: " + res);

         } else {
             System.out.println("false: " + res);
         }
     } catch (Exception e) {
         e.printStackTrace();
     }
 }

4-3. 결제 요청하기

발급된 빌링키로 원하는 시점에 원하는 금액으로 결제 승인 요청을 할 수 있습니다. 잔액이 부족하거나 도난 카드 등의 특별한 건이 아니면 PG사에서 결제를 바로 승인합니다.

Bootpay bootpay = Bootpay.withClientKey(System.getenv("BOOTPAY_PG_CLIENT_KEY_PROD"), System.getenv("BOOTPAY_PG_SECRET_KEY_PROD"));
// Legacy fallback: new Bootpay(System.getenv("BOOTPAY_APPLICATION_ID"), System.getenv("BOOTPAY_PRIVATE_KEY"));
bootpay.getAccessToken();

SubscribePayload payload = new SubscribePayload();
payload.billingKey = "628b2644d01c7e00209b6092";
payload.orderName = "아이템01";
payload.price = 1000;
payload.user = new User();
payload.user.phone = "01012345678";
payload.orderId = "" + (System.currentTimeMillis() / 1000);

try {
   HashMap<String, Object> res = bootpay.requestSubscribe(payload);
   JSONObject json =  new JSONObject(res);
   System.out.printf( "JSON: %s", json);
   
   if(res.get("error_code") == null) { //success
       System.out.println("requestSubscribe success: " + res);
   } else {
       System.out.println("requestSubscribe false: " + res);
   }
} catch (Exception e) {
   e.printStackTrace();
}

4-4. 결제 예약하기

원하는 시점에 4-1로 결제 승인 요청을 보내도 되지만, 빌링키 발급 이후에 바로 결제 예약 할 수 있습니다. (빌링키당 최대 10건)

Bootpay bootpay = Bootpay.withClientKey(System.getenv("BOOTPAY_PG_CLIENT_KEY_PROD"), System.getenv("BOOTPAY_PG_SECRET_KEY_PROD"));
// Legacy fallback: new Bootpay(System.getenv("BOOTPAY_APPLICATION_ID"), System.getenv("BOOTPAY_PRIVATE_KEY"));
bootpay.getAccessToken();

SubscribePayload payload = new SubscribePayload(); 
payload.billingKey = "628b2644d01c7e00209b6092";
payload.orderName = "아이템01";
payload.price = 1000;
payload.orderId = "" + (System.currentTimeMillis() / 1000);

Date now = new Date();
now.setTime(now.getTime() + 10 * 1000); //10초 뒤 결제
//
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss XXX");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
payload.reserveExecuteAt = sdf.format(now); // 결제 승인 시점

try {
   HashMap<String, Object> res = bootpay.reserveSubscribe(payload);
   if(res.get("error_code") == null) { //success
       System.out.println("reserveSubscribe success: " + res);
   } else {
       System.out.println("reserveSubscribe false: " + res);
   }
} catch (Exception e) {
   e.printStackTrace();
}

4-5. 예약 조회하기

예약시 응답받은 reserveId로 예약된 건을 조회합니다.

String reserveId = "6490149ca575b40024f0b70d";
try {
   HashMap<String, Object> res = bootpay.reserveSubscribeLookup(reserveId);
   JSONObject json =  new JSONObject(res);
   System.out.printf( "JSON: %s", json);
   if(res.get("error_code") == null) { //success
       System.out.println("getReceipt success: " + res);
   } else {
       System.out.println("getReceipt false: " + res);
   }
} catch (Exception e) {
   e.printStackTrace();
}

4-6. 예약 취소하기

예약시 응답받은 reserveId로 예약된 건을 취소합니다.

Bootpay bootpay = Bootpay.withClientKey(System.getenv("BOOTPAY_PG_CLIENT_KEY_PROD"), System.getenv("BOOTPAY_PG_SECRET_KEY_PROD"));
// Legacy fallback: new Bootpay(System.getenv("BOOTPAY_APPLICATION_ID"), System.getenv("BOOTPAY_PRIVATE_KEY"));
bootpay.getAccessToken();

String reserveId = "628b316cd01c7e00219b6081";
try {
        HashMap<String, Object> res = bootpay.reserveCancelSubscribe(reserveId);
        if(res.get("error_code") == null) { //success
            System.out.println("reserveCancelSubscribe success: " + res);
        } else {
            System.out.println("reserveCancelSubscribe false: " + res);
        }
} catch (Exception e) {
    e.printStackTrace();
}

4-7. 빌링키 삭제하기

발급된 빌링키를 삭제합니다. 삭제하더라도 예약된 결제건은 취소되지 않습니다. 예약된 결제건 취소를 원하시면 예약 취소하기를 요청하셔야 합니다.

Bootpay bootpay = Bootpay.withClientKey(System.getenv("BOOTPAY_PG_CLIENT_KEY_PROD"), System.getenv("BOOTPAY_PG_SECRET_KEY_PROD"));
// Legacy fallback: new Bootpay(System.getenv("BOOTPAY_APPLICATION_ID"), System.getenv("BOOTPAY_PRIVATE_KEY"));
bootpay.getAccessToken();

String receiptId = "628b2644d01c7e00209b6092";
try {
   HashMap<String, Object> res = bootpay.destroyBillingKey(receiptId);
   JSONObject json =  new JSONObject(res);
   System.out.printf( "JSON: %s", json);
   if(res.get("error_code") == null) { //success
       System.out.println("destroyBillingKey success: " + res);
   } else {
       System.out.println("destroyBillingKey false: " + res);
   }
} catch (Exception e) {
   e.printStackTrace();
}

4-8. 빌링키 조회하기

클라이언트에서 빌링키 발급시, 보안상 클라이언트 이벤트에 빌링키를 전달해주지 않습니다. 그러므로 이 API를 통해 조회해야 합니다. 다음은 빌링키 발급 요청했던 receiptId 로 빌링키를 조회합니다.

Bootpay bootpay = Bootpay.withClientKey(System.getenv("BOOTPAY_PG_CLIENT_KEY_PROD"), System.getenv("BOOTPAY_PG_SECRET_KEY_PROD"));
// Legacy fallback: new Bootpay(System.getenv("BOOTPAY_APPLICATION_ID"), System.getenv("BOOTPAY_PRIVATE_KEY"));
bootpay.getAccessToken();

String receiptId = "62c7ccebcf9f6d001b3adcd4";
try {
   HashMap<String, Object> res = bootpay.lookupBillingKey(receiptId);
   JSONObject json =  new JSONObject(res);
   System.out.printf( "JSON: %s", json);
   if(res.get("error_code") == null) { //success
       System.out.println("getReceipt success: " + res);
   } else {
       System.out.println("getReceipt false: " + res);
   }
} catch (Exception e) {
   e.printStackTrace();
}

아래는 billingKey로 조회합니다.

String billingKey = "66542dfb4d18d5fc7b43e1b6";
try {
   HashMap<String, Object> res = bootpay.lookupBillingKeyByKey(billingKey);
   JSONObject json =  new JSONObject(res);
   System.out.printf( "JSON: %s", json);
   if(res.get("error_code") == null) { //success
       System.out.println("success: " + res);
   } else {
       System.out.println("false: " + res);
   }
} catch (Exception e) {
   e.printStackTrace();
}

5. 회원 토큰 발급요청

ㅇㅇ페이 사용을 위해 가맹점 회원의 토큰을 발급합니다. 가맹점은 회원의 고유번호를 관리해야합니다. 이 토큰값을 기반으로 클라이언트에서 결제요청(payload.user_token) 하시면 되겠습니다.

Bootpay bootpay = Bootpay.withClientKey(System.getenv("BOOTPAY_PG_CLIENT_KEY_PROD"), System.getenv("BOOTPAY_PG_SECRET_KEY_PROD"));
// Legacy fallback: new Bootpay(System.getenv("BOOTPAY_APPLICATION_ID"), System.getenv("BOOTPAY_PRIVATE_KEY"));
bootpay.getAccessToken();

UserToken userToken = new UserToken();
userToken.userId = "1234"; // 개발사에서 관리하는 회원 고유 번호
try {
   HashMap<String, Object> res = bootpay.getUserToken(userToken);
   if(res.get("error_code") == null) { //success
       System.out.println("getUserToken success: " + res);
   } else {
       System.out.println("getUserToken false: " + res);
   }
} catch (Exception e) {
   e.printStackTrace();
}

6. 서버 승인 요청

결제승인 방식은 클라이언트 승인 방식과, 서버 승인 방식으로 총 2가지가 있습니다.

클라이언트 승인 방식은 javascript나 native 등에서 confirm 함수에서 진행하는 일반적인 방법입니다만, 경우에 따라 서버 승인 방식이 필요할 수 있습니다.

필요한 이유

  1. 100% 안정적인 결제 후 고객 안내를 위해 - 클라이언트에서 PG결제 진행 후 승인 완료될 때 onDone이 수행되지 않아 (인터넷 환경 등), 결제 이후 고객에게 안내하지 못할 수 있습니다
  2. 단일 트랜잭션의 개념이 필요할 경우 - 재고파악이 중요한 커머스를 운영할 경우 트랜잭션 개념이 필요할 수 있겠으며, 이를 위해서는 서버 승인을 사용해야 합니다.
Bootpay bootpay = Bootpay.withClientKey(System.getenv("BOOTPAY_PG_CLIENT_KEY_PROD"), System.getenv("BOOTPAY_PG_SECRET_KEY_PROD"));
// Legacy fallback: new Bootpay(System.getenv("BOOTPAY_APPLICATION_ID"), System.getenv("BOOTPAY_PRIVATE_KEY"));
bootpay.getAccessToken();

String receiptId = "62876963d01c7e00209b6028";
try {
   HashMap<String, Object> res = bootpay.confirm(receiptId);
   if(res.get("error_code") == null) { //success
       System.out.println("confirm success: " + res);
   } else {
       System.out.println("confirm false: " + res);
   }
} catch (Exception e) {
   e.printStackTrace();
}

7. 본인 인증 결과 조회

다날 본인인증 후 결과값을 조회합니다. 다날 본인인증에서 통신사, 외국인여부, 전화번호 이 3가지 정보는 다날에 추가로 요청하셔야 받으실 수 있습니다.

Bootpay bootpay = Bootpay.withClientKey(System.getenv("BOOTPAY_PG_CLIENT_KEY_PROD"), System.getenv("BOOTPAY_PG_SECRET_KEY_PROD"));
// Legacy fallback: new Bootpay(System.getenv("BOOTPAY_APPLICATION_ID"), System.getenv("BOOTPAY_PRIVATE_KEY"));
bootpay.getAccessToken();

try {
   HashMap<String, Object> res = bootpay.certificate(receiptId);
   if(res.get("error_code") == null) { //success
       System.out.println("certificate success: " + res);
   } else {
       System.out.println("certificate false: " + res);
   }
} catch (Exception e) {
   e.printStackTrace();
}
  1. (에스크로 이용시) PG사로 배송정보 보내기 현금 거래에 한해 구매자의 안전거래를 보장하는 방법으로, 판매자와 구매자의 온라인 전자상거래가 원활하게 이루어질 수 있도록 중계해주는 매매보호서비스입니다. 국내법에 따라 전자상거래에서 반드시 적용이 되어 있어야합니다. PG에서도 에스크로 결제를 지원하며, 에스크로 결제 사용을 원하시면 PG사 가맹시에 에스크로결제를 미리 얘기하고나서 진행을 하시는 것이 수월합니다.

PG사로 배송정보( 이니시스, KCP만 지원 )를 보내서 에스크로 상태를 변경하는 API 입니다.

Bootpay bootpay = Bootpay.withClientKey(System.getenv("BOOTPAY_PG_CLIENT_KEY_PROD"), System.getenv("BOOTPAY_PG_SECRET_KEY_PROD"));
// Legacy fallback: new Bootpay(System.getenv("BOOTPAY_APPLICATION_ID"), System.getenv("BOOTPAY_PRIVATE_KEY"));
bootpay.getAccessToken();

Shipping shipping = new Shipping();
shipping.receiptId = "628ae7ffd01c7e001e9b6066";
shipping.trackingNumber = "123456";
shipping.deliveryCorp = "CJ대한통운";
ShippingUser user = new ShippingUser();
user.username = "홍길동";
user.phone = "01000000000";
user.address = "서울특별시 종로구";
user.zipcode = "08490";
shipping.user = user;
try {
   HashMap<String, Object> res = bootpay.shippingStart(shipping);
   if(res.get("error_code") == null) { //success
       System.out.println("certificate success: " + res);
   } else {
       System.out.println("certificate false: " + res);
   }
} catch (Exception e) {
   e.printStackTrace();
}

9-1. 현금영수증 발행하기

bootpay api를 통해 결제된 건에 대하여 현금영수증을 발행합니다.

CashReceipt cashReceipt = new CashReceipt();
cashReceipt.receiptId = "62e0f11f1fc192036b1b3c92";

cashReceipt.username = "테스트";
cashReceipt.email = "test@bootpay.co.kr";
cashReceipt.phone = "01000000000";

cashReceipt.identityNo = "01000000000";
cashReceipt.cashReceiptType = "소득공제";


try {
   HashMap<String, Object> res = bootpay.requestCashReceiptByBootpay(cashReceipt);
   if(res.get("error_code") == null) { //success
       System.out.println("cashReceiptBootpay success: " + res);
   } else {
       System.out.println("cashReceiptBootpay false: " + res);
   }
} catch (Exception e) {
   e.printStackTrace();
}

9-2. 현금영수증 발행 취소

9-1을 통해 발행한 현금영수증을 취소합니다.

Cancel cancel = new Cancel(); 

cancel.receiptId = "62e0f11f1fc192036b1b3c92";
cancel.cancelMessage = "테스트 결제";
cancel.cancelUsername = "테스트 관리 

try {
   HashMap<String, Object> res = bootpay.requestCashReceiptCancelByBootpay(cancel);
   if(res.get("error_code") == null) { //success
       System.out.println("cashReceiptBootpayCancel success: " + res);
   } else {
       System.out.println("cashReceiptBootpayCancel false: " + res);
   }
} catch (Exception e) {
   e.printStackTrace();
}

9-3. (별건) 현금영수증 발행

부트페이 결제와 상관없이 금액, 상품명, 현금영수증 발행정보 등을 보내 현금영수증을 발행하는 API 입니다

pg 는 선택값입니다. 지정하지 않으면 기본 PG사로 발행됩니다.

CashReceipt cashReceipt = new CashReceipt();
cashReceipt.pg = "토스"; // 선택값, 생략시 기본 PG사로 발행
cashReceipt.price = 1000;
cashReceipt.orderName = "테스트";
cashReceipt.cashReceiptType = "소득공제";
cashReceipt.identityNo = "01000000000";

Date now = new Date();
now.setTime(now.getTime()); //10초 뒤 결제

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss XXX");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
cashReceipt.purchasedAt = sdf.format(now); // 결제 승인 시점
cashReceipt.orderId = String.valueOf(now.getTime());


try {
   HashMap<String, Object> res = bootpay.requestCashReceipt(cashReceipt);
   if(res.get("error_code") == null) { //success
       System.out.println("cashReceipt success: " + res);
   } else {
       System.out.println("cashReceipt false: " + res);
   }
} catch (Exception e) {
   e.printStackTrace();
}

9-4. (별건) 현금영수증 발행 취소

9-3을 통해 발행한 현금영수증을 취소합니다.

Cancel cancel = new Cancel();
cancel.receiptId = "62f48ae41fc192036f9f4b54";
cancel.cancelMessage = "테스트 결제";
cancel.cancelUsername = "테스트 관리자";


try {
   HashMap<String, Object> res = bootpay.requestCashReceiptCancel(cancel);
   if(res.get("error_code") == null) { //success
       System.out.println("cashReceiptCancel success: " + res);
   } else {
       System.out.println("cashReceiptCancel false: " + res);
   }
} catch (Exception e) {
   e.printStackTrace();
}

Example 프로젝트

적용한 샘플 프로젝트을 참조해주세요

Documentation

기술문의

부트페이 홈페이지에서 문의해주세요!

License

MIT License.

About

bootpay server side plugin for java

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages