스프린트 하나면 끝나는 작은 API.
결제를 만들고, 사용자를 결제 화면으로 보내고, 서명된 이벤트가 오면 주문을 반영합니다. 전체 문서는 승인된 고객사 계정에 제공됩니다.
엔드포인트
| 메서드 | 경로 | 하는 일 |
|---|---|---|
| POST | /v1/payments | 결제를 만들고 결제 화면 주소를 받습니다 |
| GET | /v1/payments/{id} | 결제와 현재 상태를 조회합니다 |
| GET | /v1/payments | 상태, 날짜, 주문번호로 결제 목록을 조회합니다 |
| POST | /v1/events/{id}/redeliver | 웹훅 이벤트를 다시 보냅니다 |
API 호출 하나, 웹훅 하나로 붙이세요.
서버에서 결제를 만들고, 응답으로 받은 결제 화면으로 사용자를 보내고, 서명된 payment.completed 이벤트가 오면 주문을 반영하면 됩니다. 상품권 쪽 절차, 수취 확인, 재시도는 MaruPay가 맡습니다.
결제 한 건, 요청부터 정산 기록까지
마우스를 올리면 멈춤 · 단계 선택
POST /v1/paymentsIdempotency-Key: ORD-58211-1{"orderId": "ORD-58211","customerId": "u_2210","amount": 55000,"currency": "KRW","returnUrl": "https://example.com/orders/ORD-58211"}
{ "id": "pay_6nHs4ZpC8q", "status": "pending","payUrl": "https://pay.marupay.app/p/6nHs4ZpC8q" }
고객사 팀이 만드는 것
결제를 만드는 서버 호출
POST /v1/payments응답에 결제 화면 주소가 들어 있습니다. 사용자를 그리로 보내면, 결제를 마친 뒤 고객사 서비스로 돌아옵니다.
주문을 반영하는 웹훅 엔드포인트
payment.completed결제가 완료되면 서명된 이벤트가 도착합니다. 서명을 검증한 뒤 고객사 쪽 구매를 완료하세요.
수취 확인, 귀속, 재시도, 정산 기록은 MaruPay가 맡습니다.
전체 문서는 승인된 고객사 계정에 제공됩니다.
create-payment.sh
curl https://api.marupay.app/v1/payments \
-H "Authorization: Bearer mp_live_••••••••" \
-H "Idempotency-Key: ORD-58202-1" \
-H "Content-Type: application/json" \
-d '{
"orderId": "ORD-58202",
"customerId": "u_6624",
"amount": 49500,
"currency": "KRW",
"returnUrl": "https://example.com/orders/ORD-58202"
}'
HTTP/1.1 201 Created
{
"data": {
"id": "pay_2MzV6hTf9w",
"orderId": "ORD-58202",
"amount": 49500,
"currency": "KRW",
"status": "pending",
"payUrl": "https://pay.marupay.app/p/2MzV6hTf9w",
"expiresAt": "2026-09-29T05:01:58Z"
}
}
webhook.http
POST /webhooks/marupay HTTP/1.1
Host: api.example.com
Content-Type: application/json
MaruPay-Event-Id: evt_5hRd0Xw8Ne
MaruPay-Signature: t=1790656319,v1=e2c97b…0f5a
{
"id": "evt_5hRd0Xw8Ne",
"type": "payment.completed",
"data": {
"paymentId": "pay_2MzV6hTf9w",
"orderId": "ORD-58202",
"amount": 49500,
"currency": "KRW",
"completedAt": "2026-09-29T04:31:58Z"
}
}
HTTP/1.1 200 OK
verify.ts
import crypto from "node:crypto";
// header: "t=<unix>,v1=<hex>" body: the raw request body
export function verify(body: string, header: string, secret: string) {
const [, t, v1] = /^t=(\d+),v1=([a-f0-9]{64})$/.exec(header) ?? [];
if (!t || !v1) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${t}.${body}`)
.digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(t)) <= 300;
return fresh && crypto.timingSafeEqual(
Buffer.from(v1, "hex"),
Buffer.from(expected, "hex"),
);
}
// Retried deliveries reuse the event ID: credit each one once.
if (await processed.has(event.id)) return res.status(200).end();
규칙
Idempotency-Key
생성 요청에 필수입니다. 같은 키와 본문이면 처음 응답을 돌려줍니다.
금액
원 단위 정수. 소수점과 환율 변환이 없습니다.
시각
API는 UTC ISO 8601, 콘솔은 설정한 시간대로 보여 줍니다.
키
mp_test_, mp_live_ 키가 고객사 계정 하나에 묶입니다.
연동 시험을 마친 뒤 실결제를 시작합니다.
실제 돈이 오가기 전에 연동이 제대로 동작하는지 확인합니다. 시험은 완료, 재시도, 중복, 확인 대상 시나리오로 고객사 처리 코드를 점검하고, 통과하면 실결제가 열립니다.
도입 문의도입 문의
서비스와 한국 사용자에 대해 알려 주세요.
검토
가맹 조건을 확인하고 콘솔 계정을 준비합니다.
연동
문서에 따라 결제 생성, 웹훅 처리, 서명 검증을 붙입니다.
연동 시험 통과
정해진 시나리오를 테스트 환경에서 실행합니다.
- 완료
- 재시도
- 중복
- 확인 대상
실결제 시작
실결제가 열리고, 첫 결제부터 콘솔에 기록됩니다.
시험 통과 전까지 잠김