연동하기

결제 링크 연동

결제 생성 → 링크 전달 → 결제 완료 콜백 → 조회로 확정. 서버 API 4개로 끝나는 표준 흐름입니다.

표준 결제 시퀀스

  1. 귀사 서버가 금액을 결정합니다(상품 카탈로그 기준). 클라이언트가 보낸 금액을 그대로 믿지 않습니다.
  2. POST /api/requestPayment — 거래번호·금액·고객 휴대폰·CONFIRM_URL·서명을 보내고 REDIRECT_URL을 받습니다. 주문을 PENDING으로 저장합니다.
  3. 고객에게 링크를 전달합니다 — 바로 이동(패턴 A) 또는 문자·알림톡 발송(패턴 B).
  4. 고객이 결제 페이지에서 카드·카카오페이로 결제합니다.
  5. 카리페이가 CONFIRM_URL로 결제 완료 콜백을 POST 합니다.
  6. 귀사 서버는 POST /api/searchPayment로 APPROVE_STATUS == "APPROVE_COMPLETE"와 승인금액을 확인한 뒤 주문을 PAID로 바꾸고 서비스를 제공합니다.

1. 결제 생성

import { CariPay, newTransSeqno } from "@caripay/sdk";
const pay = CariPay.fromEnv();

const transSeqno = newTransSeqno("svc");                 // svc + 시각14자리 + 난수
const { redirectUrl } = await pay.createPayment({
  transSeqno,
  amount: 128000,                                        // 서버 카탈로그에서 결정
  mobileNo: "01012345678",
  payerName: "홍길동",
  reason: "9월 이용료",
  confirmUrl: `https://api.example.com/caripay/callback/${transSeqno}`,
  returnUrl: "https://example.com/orders/complete",      // 선택: 결제 후 고객을 돌려보낼 곳
  tempValue: "order-8812",                               // 선택: 콜백·리턴에 그대로 돌아오는 값
});
await orders.create({ transSeqno, amount: 128000, status: "PENDING" });

필드 전체는 결제 생성 API를 보세요.

2. 링크 전달 — 두 가지 UX 패턴

패턴 A · 바로 이동 (모바일 권장)

REDIRECT_URL로 사용자를 이동시킵니다. returnUrl을 지정하면 결제 완료 후 결제 페이지가 returnUrl?RESULT_CODE=0000&TRANS_SEQNO=...&APPROVAL_AMOUNT=...&TEMP_VALUE=...로 고객을 돌려보냅니다. 돌아온 화면에서 승인으로 처리하지 말고, 서버가 조회 API로 확정한 결과를 보여 주세요.

패턴 B · 링크 발송 + 완료 대기 (데스크톱·원격 청구 권장)

링크를 문자·알림톡으로 보내고 화면에 "휴대폰으로 결제 링크를 보냈어요" 대기 화면을 띄운 뒤 3초 간격으로 주문 상태를 폴링합니다. 데스크톱에서 카드번호를 입력하는 이탈 구간이 사라집니다.

JavaScript
// 프런트 대기 화면이 3초마다 부르는 조회 엔드포인트 (귀사 서버)
app.get("/orders/:transSeqno", async (req, res) => {
  await settle(req.params.transSeqno);            // 아래 4. 참고
  res.json(await orders.get(req.params.transSeqno));
});

3. 콜백 수신

JavaScript
app.post("/caripay/callback/:transSeqno", async (req, res) => {
  res.sendStatus(200);                              // 먼저 200 — 처리는 비동기로
  await settle(req.params.transSeqno).catch(console.error);
});

콜백 본문 필드와 재전송 규칙은 콜백 구현에서 자세히 다룹니다. 요점은 세 가지입니다 — HTTPS, 본문을 믿지 말고 조회로 확정, 중복 도달을 전제로 멱등 처리.

4. 승인 확정 — 상태 전이는 한 곳에서

JavaScript
async function settle(transSeqno) {
  const order = await orders.get(transSeqno);
  if (!order || order.status !== "PENDING") return;   // 멱등: 이미 처리됨

  const p = await pay.confirmCallback(transSeqno);    // = searchPayment
  if (p.paid) {
    if (p.amount !== order.amount) { await orders.update(transSeqno, { status: "AMOUNT_MISMATCH" }); return; }
    await orders.update(transSeqno, { status: "PAID", approvalNumber: p.approvalNumber });
    await grantService(order);                        // 이용권 해금·주문 확정 — 딱 1회
  } else if (p.canceled) {
    await orders.update(transSeqno, { status: "CANCELED" });
  }
}

콜백과 폴링 어느 쪽이 먼저 와도 같은 결과가 되도록 settle() 하나로 모읍니다.

권장 주문 상태 모델

PENDING(생성) ─▶ PAID(승인 확인) ─▶ CANCELED(취소)
             └▶ DELETED(미결제 삭제)     └▶ AMOUNT_MISMATCH(대사 불일치 · 수동 확인)

5. 취소·삭제

  • 승인된 결제의 환불: cancelPayment({ transSeqno }) — 승인금액 전액만 취소됩니다.
  • 미결제 청구 닫기: deleteBill({ transSeqno, amount, mobileNo }) — 상태가 STORE_DELETE가 되고 링크로 결제할 수 없습니다.

자세한 규칙은 취소·삭제를 보세요.

SHOP 주문 (상품 결제)

교재·굿즈처럼 품목이 있는 주문은 orderType: "SHOP"과 items[]를 함께 보냅니다. 결제 페이지에 품목 표가 표시되고 합계·부가세는 품목 기준으로 계산됩니다.

JavaScript
await pay.createPayment({
  amount: 77000, mobileNo: "01012345678", payerName: "홍길동", reason: "교재 구매",
  confirmUrl: "https://api.example.com/caripay/callback/...",
  orderType: "SHOP",
  items: [
    { name: "교재 A", unitPrice: 11000, qty: 3, publisher: "미래출판사", imageUrl: "https://cdn.example.com/a.png" },
    { name: "교재 B", unitPrice: 22000, qty: 2 },
  ],
});

amount는 품목 합계(Σ unitPrice × qty, 할인 반영)와 같아야 합니다.

문서에 없는 내용이나 오류는 bellight@goatheaven.com 또는 지원 문의로 알려주세요.