연동하기

청구서 API 연동

청구서를 만들고 카카오 알림톡 또는 문자로 보내기. 발송 수단 선택, 멱등 재시도, 수납 확인까지.

청구서 API는 결제 링크 생성 + 알림톡/문자 발송을 한 번에 처리합니다. 온라인 주문·예약금·방문 서비스·외상처럼 아직 납부하지 않은 금액을 안내할 때 씁니다. 이미 결제된 거래의 영수증 용도로는 쓰지 않습니다.

준비

  1. 가맹점 계정 · 사업자 등록 승인 · 발송 포인트 — 연동 정보 발급
  2. 접근 토큰 — SDK login() 또는 POST /app/v1/auth/login
  3. 청구 API 주소 https://api.dev.caripay.co.kr — 실제 발송·과금 환경입니다. 연동 환경

발송 수단 고르기

sendChannel동작언제
ALIMTALK (기본)카카오 알림톡(카리 채널 승인 템플릿). 접수 실패 시 오류대부분의 고객
SMS문자(LMS)만. 알림톡을 시도하지 않음카카오톡을 쓰지 않는 고객, 알림톡 거부 고객
ALIMTALK_THEN_SMS알림톡을 먼저 보내고, 카카오 미가입·차단·접수 거절이면 같은 요청에서 문자로 대체도달률이 중요할 때
  • 알림톡·문자 모두 같은 링크(https://caripay.co.kr/pay/{거래번호})를 담습니다. 고객은 청구 내역 화면에서 "결제하기"를 눌러 결제 페이지로 갑니다.
  • 문자로 대체 발송된 이력은 발송 수단이 SMS로 기록됩니다.
  • 발송 포인트는 수단과 관계없이 청구서 1건당 1회 차감됩니다(정책이 바뀌면 릴리즈 노트로 안내).

청구서 보내기

import { CariPayBilling } from "@caripay/sdk";

const billing = await CariPayBilling.login({
  email: process.env.CARIPAY_BILLING_EMAIL,
  password: process.env.CARIPAY_BILLING_PASSWORD,
});

const result = await billing.sendInvoice({
  requestId: "order_20260921_0001",           // 주문별 고유. 재시도 때 같은 값을 다시 씀
  amount: 128000,
  recipient: { name: "홍길동", phone: "01012345678" },
  reason: "9월 이용료",                        // 60자 이내
  message: "9월 30일까지 납부해 주세요.",         // 선택, 200자 이내
  channel: "ALIMTALK_THEN_SMS",               // ALIMTALK | SMS | ALIMTALK_THEN_SMS
  webhookSecret: process.env.CARIPAY_WEBHOOK_SECRET,          // 선택: 웹훅 서명 비밀(ASCII 16~128자)
  webhookUrl: "https://api.example.com/caripay/invoice-hook", // 선택: 결제 완료·취소 알림
});
// result = { accepted: true, requestId: "order_20260921_0001" }

응답 {"result_code":0,"result_msg":"성공","result_data":null}은 접수입니다. 발송은 비동기로 진행되며 수 초 안에 나갑니다.

청구 항목 — 상품명·금액

상품이 여러 개이거나 알림에 상품명을 보여 주고 싶으면 items를 넣습니다. 항목 금액의 합이 청구 금액이 됩니다(SDK는 amount를 생략하면 합계로 채웁니다).

await billing.sendInvoice({
  requestId: "order_20260922_0001",
  recipient: { name: "홍길동", phone: "01012345678" },
  reason: "9월 수강료",
  message: "9월 30일까지 납부해 주세요.",
  items: [{ name: "수학 특강", price: 120000 }, { name: "교재", price: 15000 }], // amount = 135,000
});

고객이 받는 알림톡(문자도 같은 내용):

청구서 발송
홍길동님 안녕하세요.
카리수학에서 납부 안내드립니다.
[청구내역]
▷청구서 발급처: 카리수학학원
▷청구 사유: 9월 수강료
▷청구금액: 135,000원

[청구 항목]
- 수학 특강 120,000원
- 교재 15,000원

9월 30일까지 납부해 주세요.
아래 버튼을 눌러 청구 내역을 확인하고, 바로 납부해 주세요.
항목규칙
상품명 name1~20자
금액 price항목당 100원 이상. 합계가 amount와 같아야 합니다
알림 표시5개까지 줄마다 표시, 나머지는 외 N건. 결제 화면에는 전부 나옵니다
가맹점명·발급처요청으로 바꿀 수 없습니다. 가맹점 계정의 등록 정보(가입 때 이름, 사업자 상호)가 들어갑니다

멱등 재시도 — requestId

  • 같은 가맹점에서 같은 requestId로 다시 보내면 새 청구서를 만들지 않고, 포인트를 다시 차감하지 않으며, 알림도 다시 보내지 않습니다.
  • 같은 requestId에 다른 금액·사유·수신자를 보내면 거절됩니다.
  • 통신 오류로 응답을 못 받았을 때 새 ID로 다시 보내지 마세요. 같은 ID로 재시도하면 중복 청구가 생기지 않습니다.
  • 규칙: 영숫자·_·- 8~64자, 수신자 1명일 때만 사용.

결제 완료 웹훅

청구서에 webhookUrl을 넣으면 고객이 결제했을 때(bill.paid)와 결제가 취소됐을 때(bill.canceled) 그 주소로 POST(JSON) 합니다.

JSON
{
  "event": "bill.paid",
  "billId": "550e8400-e29b-41d4-a716-446655440000",
  "requestId": "order_20260921_0001",
  "transSeqNo": "20260921120000AbCdEf",
  "status": "DONE",
  "amount": 128000,
  "reason": "9월 이용료",
  "paidAt": "2026-09-21T12:01:35",
  "canceledAt": null,
  "occurredAt": "2026-09-21T12:01:36"
}
항목규칙
헤더Content-Type: application/json, X-CariPay-Event: bill.paid 또는 bill.canceled, X-CariPay-Delivery: {전송 ID}
성공 판정귀사 응답 HTTP 2xx
재시도2xx가 아니거나 연결 실패면 1분 → 5분 → 30분 → 2시간 뒤 다시 보내고, 5번째 실패에서 포기합니다
주소https://만. 500자 이하
서명선택. 청구서에 webhookSecret을 넣으면 X-CariPay-Signature: t=<unix초>,v1=<hex>가 붙습니다. v1 = HMAC-SHA256(secret, "<t>.<본문 원문>"). 아래처럼 검증하고, 검증 후에도 확정은 getInvoice로 하세요
중복재시도 때문에 같은 이벤트가 두 번 올 수 있습니다. 이미 처리한 billId+event면 200만 응답하세요
JavaScript
import { verifyWebhookSignature } from "@caripay/sdk";

app.post("/caripay/invoice-hook", express.raw({ type: "application/json" }), async (req, res) => {
  const ok = verifyWebhookSignature({ secret: process.env.CARIPAY_WEBHOOK_SECRET, signature: req.get("X-CariPay-Signature"), body: req.body });
  if (!ok) return res.sendStatus(400);                          // 서명 불일치·5분 초과
  res.sendStatus(200);                                          // 먼저 200
  const { requestId, billId, event } = JSON.parse(req.body);
  const order = await orders.findByRequestId(requestId);
  if (!order || order.status !== "PENDING") return;             // 멱등
  const detail = await billing.getInvoice(billId);              // 조회로 확정
  if (event === "bill.paid" && detail.bill.status === "DONE") await orders.markPaid(order);
});

수납 확인

웹훅을 쓰지 않거나 놓쳤을 때는 조회로 확인합니다.

JavaScript
const list = await billing.listInvoices({ month: "2026-09", page: 1, size: 50 });
const detail = await billing.getInvoice(list.bills[0].uniqueId);
// detail.bill.status: "PENDING" | "DONE" | "CANCELED"
// detail.sendHistories[0]: { sendType, sendResult, sendChannel, sentAt, errorMessage }
확인할 것위치
발송이 나갔는가sendHistories[].sendResult === "SUCCESS", sendChannel
고객이 결제했는가bill.status === "DONE", payment 객체(승인번호·카드사·승인시각)
취소됐는가bill.status === "CANCELED"

권장 폴링: 결제 완료를 빠르게 알아야 하면 1분 간격, 그렇지 않으면 청구 마감일 기준 하루 1~2회. 콘솔의 발송·수납 내역에서도 같은 정보를 봅니다.

재발송·삭제·취소

작업API비고
재발송POST /app/v1/sales/bill/resend { "billIds": ["{uniqueId}"] }같은 청구서·같은 링크·같은 발송 수단. 포인트 차감
미납 청구서 삭제DELETE /app/v1/sales/bill/{id}게이트웨이 청구도 함께 닫힙니다(STORE_DELETE)
결제 완료 건 취소(환불)PUT /app/v1/sales/bill/{id}전액 취소. 카드사 승인취소

필드 전체는 청구서 API 레퍼런스를 보세요.

예약·정기 발송

콘솔에서 지원하는 예약(1회)·정기(매월 n일 HH:mm) 발송은 API로도 등록할 수 있습니다 — POST /app/v1/sales/send/bill/schedule. sendRequest에 위와 같은 청구 내용과 sendChannel을 넣습니다. 상세 규격은 키 발급 시 안내합니다.

고객이 보는 화면

  1. 알림톡 "[CARI PAY] 청구서" — 발급처·청구 사유·금액·청구 항목(있으면)·안내 메시지 + 청구서 확인 버튼 (문자는 같은 내용 + 링크)
  2. caripay.co.kr/pay/{거래번호} — 가맹점명, 금액, 청구일, 품목, 결제 상태
  3. 결제하기 → 카리페이 결제 페이지(신용카드·카카오페이)
  4. 결제 완료 → 청구서 상태 DONE, 콘솔 수납 내역 반영

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