v1 API 상세: 회원
로그인한 회원이 바꾸는 비밀번호 변경과, 로그아웃 상태에서 연락처 인증으로 다시 설정하는 비밀번호 찾기예요. 두 경로 모두 성공하면 그 회원의 기존 세션이 전부 끊겨요.
비밀번호 변경
로그인한 회원이 현재 비밀번호를 확인한 뒤 새 비밀번호를 설정해요.
https://api.bootapi.com/v1/users/me/password[Basic Auth](/server/authentication) + `Bootpay-User-JWT` 또는 가맹점 서버의 `user_id`필요 권한: user:user_me_password_update
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
user_id |
String | 선택 | 회원 ID 또는 가맹점 회원 고유 ID(external_uid). secretKey 인증에서만 허용해요 |
current_password |
String | 필수 | 현재 비밀번호 |
new_password |
String | 필수 | 새 비밀번호 (8자 이상, 72바이트 이하) |
new_password_confirmation |
String | 선택 | 새 비밀번호 확인. 보내면 서버가 일치를 확인해요 |
JWT를 보내면 회원은 그 세션으로 정해져요. JWT 없이 user_id를 보내면 가맹점 서버가 지정한 회원을 대상으로 하므로, 쇼핑몰 서버에서 먼저 본인임을 확인해야 해요. 둘을 함께 보내고 다른 회원을 가리키면 403 USER_NOT_MATCH예요.
응답은 { "changed": true, "requires_login": true } 예요.
변경하면 모든 기기에서 로그아웃돼요
비밀번호가 바뀌면 변경 전에 발급된 고객 JWT 가 전부 무효가 돼요. 요청을 보낸 그 기기의 JWT 도 포함이에요. 응답의 requires_login: true 가 그 신호이니, BFF 는 성공 응답을 받으면 세션 쿠키를 지우고 로그인 화면으로 보내요. 이 처리를 하지 않으면 다음 회원 API 요청이 401 로 떨어져요.
같은 동작이 쇼핑몰 마이페이지의 비밀번호 변경에도 적용돼요.
비밀번호 규칙
가입·변경·재설정이 같은 규칙을 써요.
- 8자 이상
- 72바이트 이하 — BCrypt 가 72바이트 뒤를 버리기 때문이에요. 한글은 한 글자가 3바이트라 24자까지예요.
- 앞뒤 공백은 서버가 제거한 뒤 저장해요.
- 현재 비밀번호와 같은 값으로는 바꿀 수 없어요.
오류
| error_code | 상황 |
|---|---|
USER_SESSION_INVALID (420) |
JWT 누락·만료·로그아웃·탈퇴·다른 프로젝트 세션 |
USER_JWT_INVALID (419) |
서명 위조·변조·exp 경과 |
USER_PASSWORD_NOT_SET (10803) |
비밀번호가 없는 소셜 전용 계정 — 비밀번호 찾기로 설정해요 |
USER_LOGIN_PASSWORD_NOT_MATCH (416) |
현재 비밀번호 불일치 |
USER_PASSWORD_FORMAT_INVALID (475) |
8자 미만이거나 현재와 같은 비밀번호 |
CUSTOMER_FIELD_TOO_LONG |
72바이트 초과 (payload.max_length: 72) |
USER_PASSWORD_CONFIRM_NOT_MATCH (474) |
확인 입력 불일치 |
현재 비밀번호가 틀리면 새 비밀번호 판정을 하지 않고 먼저 거절해요. 검증에서 걸린 요청은 비밀번호도 세션도 바꾸지 않아요.
비밀번호 찾기
로그아웃한 회원이 등록된 연락처로 인증을 마친 뒤에만 새 비밀번호를 설정해요. 세 단계예요.
인증코드는 언제나 상점에 등록된 그 회원의 연락처로 가요. 요청에 email·phone·contact를 넣으면 조용히 무시하지 않고 거절해요. 요청자가 수신처를 바꿀 수 있으면 계정 탈취 경로가 되기 때문이에요.
1단계 — 인증코드 발송
필요 권한: user:user_password_recovery · 고객 JWT는 필요 없어요.
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
login_id |
String | 필수 | 로그인 아이디 (이메일 가입 상점은 이메일) |
channel |
String | 필수 | email 또는 phone |
{ "accepted": true, "retry_after": 60, "challenge_id": "9f2c1a7e4b83d05c6a1f..." }json계정이 없어도 같은 모양으로 응답해요. 쿨다운에 걸렸을 때도, 발송이 실패했을 때도 같아요. 응답으로 계정 존재 여부를 알 수 없게 하기 위해서예요. 그래서 화면에는 "가입된 연락처로 인증코드를 보냈어요"처럼 존재를 단정하지 않는 문구를 써요.
발송은 60초에 한 번이에요.
2단계 — 인증코드 확인
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
challenge_id |
String | 필수 | 1단계 응답의 값 |
code |
String | 필수 | 받은 인증코드 |
{ "reset_token": "eyJhbGciOi...", "expires_at": "2026-09-21T10:10:00+09:00" }json인증코드는 3분 안에 입력해야 하고, 5회 틀리면 그 challenge는 막혀요. 다시 1단계부터 시작해요.
3단계 — 새 비밀번호 설정
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
reset_token |
String | 필수 | 2단계 응답의 일회성 토큰 |
new_password |
String | 필수 | 새 비밀번호 (변경과 같은 규칙) |
new_password_confirmation |
String | 필수 | 새 비밀번호 확인 |
login_id를 받지 않아요. 회원은 reset_token이 정해요.
{ "changed": true, "requires_login": true }jsonreset_token은 한 번만 쓸 수 있어요. 같은 토큰으로 다시 호출하면 410이에요. 동시에 두 번 들어와도 한 번만 성공해요.
토큰은 발급한 상점·용도·회원·challenge가 모두 맞아야 통과해요. 다른 상점의 토큰이나 다른 용도로 만든 토큰은 거절돼요.
비밀번호 찾기 오류
| error_code | HTTP | 상황 |
|---|---|---|
API_PARAM_INVALID |
400 | email·phone·contact 를 보냄, channel 이 email/phone 이 아님 |
VERIFICATION_CODE_EXPIRED (471) |
400 | 인증코드 만료, 없는 challenge_id, 다른 상점의 challenge |
VERIFICATION_ATTEMPT_EXCEEDED (472) |
400 | 인증코드 5회 초과 |
FIND_PASSWORD_RESET_TOKEN_INVALID (467) |
401 | 상점·용도·회원·challenge 불일치 |
FIND_PASSWORD_RESET_TOKEN_EXPIRED (469) |
400 | 토큰 서명 오류·만료 |
FIND_PASSWORD_RESET_TOKEN_USED (10804) |
410 | 이미 사용한 토큰 |
USER_PASSWORD_CONFIRM_NOT_MATCH (474) |
400 | 확인 입력 불일치 |
없는 challenge_id와 만료된 challenge_id가 같은 오류로 나가요 — 존재 여부를 구분하지 않기 위해서예요.
쇼핑몰 내부 경로와의 차이
Bootpay 호스팅 쇼핑몰이 쓰는 /mall/user/find-password/*는 상점 세션으로 동작하는 내부 경로라 외부 쇼핑몰에서는 쓸 수 없어요. 위 v1 경로가 같은 도메인 정책 위에서 공개 계약만 다시 정의한 것이에요.