bootpay 3.0.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,115 @@
1
+ module BootpayStore::Concern::Deposit
2
+ extend ActiveSupport::Concern
3
+
4
+ # 뱅크알람 입금대기 웹훅 이벤트 (입금 웹훅 본문의 event)
5
+ BANK_DEPOSIT_WEBHOOK_EVENT_RECEIVED = 400 # 입금 알림을 처음 저장했다 (주문 연결 전)
6
+ BANK_DEPOSIT_WEBHOOK_EVENT_MATCHED = 401 # 입금대기 주문 한 건과 연결되어 입금 확인됐다
7
+ BANK_DEPOSIT_WEBHOOK_EVENT_AMBIGUOUS = 402 # 같은 금액의 입금대기 주문이 여럿이라 판매자가 골라야 한다
8
+ BANK_DEPOSIT_WEBHOOK_EVENT_UNMATCHED = 403 # 연결할 입금대기 주문이 없다
9
+ BANK_DEPOSIT_WEBHOOK_EVENT_CONFIRMED_MANUALLY = 404 # 판매자가 관리자에서 직접 입금을 확인했다
10
+ BANK_DEPOSIT_WEBHOOK_EVENT_EXPIRED = 405 # 입금 기한이 지나 닫혔다
11
+ BANK_DEPOSIT_WEBHOOK_EVENT_CANCELLED = 406 # 입금대기 중인 주문을 취소했다
12
+ BANK_DEPOSIT_WEBHOOK_EVENT_REVERTED = 407 # 입금 확인을 되돌려 다시 입금대기로 바꿨다
13
+
14
+ included do
15
+ # 판매자 입금 계좌 목록 (GET /v1/sdk/deposits/accounts) — supervisor 전용
16
+ # @comment_by Claude
17
+ # @date: 26-11-12
18
+ # 첫 계좌가 대표 계좌(primary: true)다.
19
+ def deposit_accounts(idempotency_key: nil)
20
+ request(
21
+ uri: 'sdk/deposits/accounts',
22
+ method: :get,
23
+ headers: {
24
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
25
+ 'Bootpay-Role' => 'supervisor'
26
+ }
27
+ )
28
+ end
29
+
30
+ # 입금 웹훅 서명 비밀값 발급 (POST /v1/sdk/deposits/webhook_secret) — supervisor 전용
31
+ # @comment_by Claude
32
+ # @date: 26-11-12
33
+ # ⚠️ 원문은 이 응답에서만 오고, 부를 때마다 바뀐다.
34
+ # 웹훅은 X-Bootpay-Signature: sha256=HMAC_SHA256(secret, "{X-Bootpay-Timestamp}.{raw_body}") 로 서명된다.
35
+ def deposit_webhook_secret(idempotency_key: nil)
36
+ request(
37
+ uri: 'sdk/deposits/webhook_secret',
38
+ method: :post,
39
+ headers: {
40
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
41
+ 'Bootpay-Role' => 'supervisor'
42
+ }
43
+ )
44
+ end
45
+
46
+ # 입금대기 등록 (POST /v1/sdk/deposits) — supervisor 전용
47
+ # @comment_by Claude
48
+ # @date: 26-11-12
49
+ # 같은 merchant_order_id 로 금액·입금자명·계좌가 같은 재호출은 기존 주문을 돌려주고, 하나라도 다르면 18110.
50
+ # webhook_url 을 주려면 deposit_webhook_secret 으로 비밀값을 먼저 발급받아야 한다(18131).
51
+ # notify(구매자 알림톡, 유료): { deposit_guide:, deposit_paid:, link: } — buyer_phone 필수(18134), link 는 https(18135).
52
+ def deposit_create(merchant_order_id:, price:, depositor:, order_name: nil, memo: nil, buyer_name: nil,
53
+ buyer_phone: nil, buyer_email: nil, account_id: nil, webhook_url: nil, notify: nil,
54
+ idempotency_key: nil)
55
+ request(
56
+ uri: 'sdk/deposits',
57
+ method: :post,
58
+ headers: {
59
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
60
+ 'Bootpay-Role' => 'supervisor'
61
+ },
62
+ payload: {
63
+ merchant_order_id: merchant_order_id,
64
+ price: price,
65
+ depositor: depositor,
66
+ order_name: order_name,
67
+ memo: memo,
68
+ buyer_name: buyer_name,
69
+ buyer_phone: buyer_phone,
70
+ buyer_email: buyer_email,
71
+ account_id: account_id,
72
+ webhook_url: webhook_url,
73
+ notify: notify.is_a?(Hash) ? notify.compact : notify
74
+ }.compact
75
+ )
76
+ end
77
+
78
+ # 입금대기 조회 (GET /v1/sdk/deposits/:id) — supervisor 전용
79
+ # @comment_by Claude
80
+ # @date: 26-11-12
81
+ # deposit_id 는 가맹점 주문번호(merchant_order_id) 또는 부트페이 주문번호. 자유 문자열이라 경로 인코딩한다.
82
+ def deposit_detail(deposit_id:, idempotency_key: nil)
83
+ request(
84
+ uri: "sdk/deposits/#{deposit_path_segment(deposit_id)}",
85
+ method: :get,
86
+ headers: {
87
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
88
+ 'Bootpay-Role' => 'supervisor'
89
+ }
90
+ )
91
+ end
92
+
93
+ # 입금대기 취소 (DELETE /v1/sdk/deposits/:id) — supervisor 전용
94
+ # @comment_by Claude
95
+ # @date: 26-11-12
96
+ # 이미 닫힌 주문은 그대로 돌려준다. 입금 확인된 주문은 18108.
97
+ def deposit_cancel(deposit_id:, idempotency_key: nil)
98
+ request(
99
+ uri: "sdk/deposits/#{deposit_path_segment(deposit_id)}",
100
+ method: :delete,
101
+ headers: {
102
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
103
+ 'Bootpay-Role' => 'supervisor'
104
+ }
105
+ )
106
+ end
107
+
108
+ private
109
+
110
+ # 경로 세그먼트 인코딩 — 공백은 %20, / 는 %2F (다른 SDK 와 같은 결과)
111
+ def deposit_path_segment(value)
112
+ URI.encode_www_form_component(value.to_s).gsub('+', '%20')
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,97 @@
1
+ module BootpayStore::Concern::Faq
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ # FAQ 목록 (GET /v1/faqs)
6
+ # @comment_by Claude
7
+ # @date: 26-09-29
8
+ # supervisor: true 면 운영자 모드(Bootpay-Role: supervisor) — view: 'all' 로 비공개 FAQ 까지 조회할 수 있다.
9
+ # 고객 모드는 몰 FAQ 사용여부가 꺼져 있으면 BOARD_FEATURE_DISABLED 로 거절된다.
10
+ def faqs(page: 1, limit: 20, keyword: nil, view: nil, supervisor: false, idempotency_key: nil)
11
+ request(
12
+ uri: 'faqs',
13
+ method: :get,
14
+ headers: {
15
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
16
+ 'Bootpay-Role' => supervisor ? 'supervisor' : 'user'
17
+ },
18
+ params: {
19
+ page: page,
20
+ limit: limit,
21
+ keyword: keyword,
22
+ view: view
23
+ }.compact
24
+ )
25
+ end
26
+
27
+ # FAQ 단건 (GET /v1/faqs/:id)
28
+ # @comment_by Claude
29
+ # @date: 26-09-29
30
+ # 비공개 FAQ 는 supervisor: true 일 때만 보인다(아니면 404 POST_NOT_FOUND).
31
+ def faq_detail(faq_id:, supervisor: false, idempotency_key: nil)
32
+ request(
33
+ uri: "faqs/#{faq_id}",
34
+ method: :get,
35
+ headers: {
36
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
37
+ 'Bootpay-Role' => supervisor ? 'supervisor' : 'user'
38
+ }
39
+ )
40
+ end
41
+
42
+ # FAQ 등록 (POST /v1/faqs) — supervisor 전용
43
+ # @comment_by Claude
44
+ # @date: 26-09-29
45
+ def faq_create(title:, content:, images: nil, is_display: nil, idempotency_key: nil)
46
+ request(
47
+ uri: 'faqs',
48
+ method: :post,
49
+ headers: {
50
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
51
+ 'Bootpay-Role' => 'supervisor'
52
+ },
53
+ payload: {
54
+ title: title,
55
+ content: content,
56
+ images: images,
57
+ is_display: is_display
58
+ }.compact
59
+ )
60
+ end
61
+
62
+ # FAQ 수정 (PUT /v1/faqs/:id) — supervisor 전용
63
+ # @comment_by Claude
64
+ # @date: 26-09-29
65
+ # 보낸 필드만 바뀐다. images 는 보내면 목록 전체를 교체한다([] 면 모두 삭제).
66
+ def faq_update(faq_id:, title: nil, content: nil, images: nil, is_display: nil, idempotency_key: nil)
67
+ request(
68
+ uri: "faqs/#{faq_id}",
69
+ method: :put,
70
+ headers: {
71
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
72
+ 'Bootpay-Role' => 'supervisor'
73
+ },
74
+ payload: {
75
+ title: title,
76
+ content: content,
77
+ images: images,
78
+ is_display: is_display
79
+ }.compact
80
+ )
81
+ end
82
+
83
+ # FAQ 삭제 (DELETE /v1/faqs/:id) — supervisor 전용
84
+ # @comment_by Claude
85
+ # @date: 26-09-29
86
+ def faq_delete(faq_id:, idempotency_key: nil)
87
+ request(
88
+ uri: "faqs/#{faq_id}",
89
+ method: :delete,
90
+ headers: {
91
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
92
+ 'Bootpay-Role' => 'supervisor'
93
+ }
94
+ )
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,138 @@
1
+ module BootpayStore::Concern::Inquiry
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ # 1:1 문의 목록 (GET /v1/inquiries)
6
+ # @comment_by Claude
7
+ # @date: 26-09-29
8
+ # 회원 모드: user_id(Bootpay 회원 _id 또는 외부 회원 ID)·login_id·user_jwt 중 하나로 회원을 정하고 본인 문의만 본다.
9
+ # supervisor: true 면 몰 전체 문의 — 이때 user_id 는 작성 회원 필터(선택)다.
10
+ # answered: true 답변 완료만 / false 미답변만 / nil 전체
11
+ def inquiries(user_id: nil, login_id: nil, user_jwt: nil, answered: nil, page: 1, limit: 20,
12
+ supervisor: false, idempotency_key: nil)
13
+ request(
14
+ uri: 'inquiries',
15
+ method: :get,
16
+ headers: {
17
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
18
+ 'Bootpay-Role' => supervisor ? 'supervisor' : 'user',
19
+ 'Bootpay-User-JWT' => user_jwt
20
+ }.compact,
21
+ params: {
22
+ user_id: user_id,
23
+ login_id: login_id,
24
+ answered: answered.nil? ? nil : answered.to_s,
25
+ page: page,
26
+ limit: limit
27
+ }.compact
28
+ )
29
+ end
30
+
31
+ # 1:1 문의 단건 (GET /v1/inquiries/:id)
32
+ # @comment_by Claude
33
+ # @date: 26-09-29
34
+ # 회원 모드는 작성 회원 본인 문의만(다른 회원 문의는 404). supervisor: true 면 몰의 모든 문의.
35
+ def inquiry_detail(inquiry_id:, user_id: nil, login_id: nil, user_jwt: nil, supervisor: false, idempotency_key: nil)
36
+ request(
37
+ uri: "inquiries/#{inquiry_id}",
38
+ method: :get,
39
+ headers: {
40
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
41
+ 'Bootpay-Role' => supervisor ? 'supervisor' : 'user',
42
+ 'Bootpay-User-JWT' => user_jwt
43
+ }.compact,
44
+ params: {
45
+ user_id: user_id,
46
+ login_id: login_id
47
+ }.compact
48
+ )
49
+ end
50
+
51
+ # 1:1 문의 작성 (POST /v1/inquiries) — 회원 전용
52
+ # @comment_by Claude
53
+ # @date: 26-09-29
54
+ # product_id 는 이 몰의 상품, order_id 는 작성 회원의 주문이어야 한다. option 은 상품 옵션 문구(product_id 필요).
55
+ def inquiry_create(content:, user_id: nil, login_id: nil, user_jwt: nil, title: nil, product_id: nil,
56
+ option: nil, order_id: nil, idempotency_key: nil)
57
+ request(
58
+ uri: 'inquiries',
59
+ method: :post,
60
+ headers: {
61
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
62
+ 'Bootpay-Role' => 'user',
63
+ 'Bootpay-User-JWT' => user_jwt
64
+ }.compact,
65
+ payload: {
66
+ user_id: user_id,
67
+ login_id: login_id,
68
+ title: title,
69
+ content: content,
70
+ product_id: product_id,
71
+ option: option,
72
+ order_id: order_id
73
+ }.compact
74
+ )
75
+ end
76
+
77
+ # 1:1 문의 수정 (PUT /v1/inquiries/:id) — 작성 회원 본인, 답변 전만
78
+ # @comment_by Claude
79
+ # @date: 26-09-29
80
+ # 답변이 달린 문의는 409 INQUIRY_ALREADY_ANSWERED. title 에 '' 를 보내면 제목을 지운다.
81
+ def inquiry_update(inquiry_id:, user_id: nil, login_id: nil, user_jwt: nil, title: nil, content: nil,
82
+ idempotency_key: nil)
83
+ request(
84
+ uri: "inquiries/#{inquiry_id}",
85
+ method: :put,
86
+ headers: {
87
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
88
+ 'Bootpay-Role' => 'user',
89
+ 'Bootpay-User-JWT' => user_jwt
90
+ }.compact,
91
+ payload: {
92
+ user_id: user_id,
93
+ login_id: login_id,
94
+ title: title,
95
+ content: content
96
+ }.compact
97
+ )
98
+ end
99
+
100
+ # 1:1 문의 삭제 (DELETE /v1/inquiries/:id) — 작성 회원 본인 또는 supervisor
101
+ # @comment_by Claude
102
+ # @date: 26-09-29
103
+ # 달린 답변도 함께 삭제된다.
104
+ def inquiry_delete(inquiry_id:, user_id: nil, login_id: nil, user_jwt: nil, supervisor: false, idempotency_key: nil)
105
+ request(
106
+ uri: "inquiries/#{inquiry_id}",
107
+ method: :delete,
108
+ headers: {
109
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
110
+ 'Bootpay-Role' => supervisor ? 'supervisor' : 'user',
111
+ 'Bootpay-User-JWT' => user_jwt
112
+ }.compact,
113
+ params: {
114
+ user_id: user_id,
115
+ login_id: login_id
116
+ }.compact
117
+ )
118
+ end
119
+
120
+ # 1:1 문의 답변 등록·수정 (PUT /v1/inquiries/:id/answer) — supervisor 전용
121
+ # @comment_by Claude
122
+ # @date: 26-09-29
123
+ # 답변이 없으면 만들고, 있으면 내용을 바꾼다. 응답은 답변이 반영된 문의 객체다.
124
+ def inquiry_answer(inquiry_id:, content:, idempotency_key: nil)
125
+ request(
126
+ uri: "inquiries/#{inquiry_id}/answer",
127
+ method: :put,
128
+ headers: {
129
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
130
+ 'Bootpay-Role' => 'supervisor'
131
+ },
132
+ payload: {
133
+ content: content
134
+ }
135
+ )
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,100 @@
1
+ module BootpayStore::Concern::Notice
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ # 공지사항 목록 (GET /v1/notices)
6
+ # @comment_by Claude
7
+ # @date: 26-09-29
8
+ # supervisor: true 면 운영자 모드(Bootpay-Role: supervisor) — view: 'all' 로 비공개 공지까지 조회할 수 있다.
9
+ # 고객 모드는 몰 공지사항 사용여부가 꺼져 있으면 BOARD_FEATURE_DISABLED 로 거절된다.
10
+ def notices(page: 1, limit: 20, keyword: nil, view: nil, supervisor: false, idempotency_key: nil)
11
+ request(
12
+ uri: 'notices',
13
+ method: :get,
14
+ headers: {
15
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
16
+ 'Bootpay-Role' => supervisor ? 'supervisor' : 'user'
17
+ },
18
+ params: {
19
+ page: page,
20
+ limit: limit,
21
+ keyword: keyword,
22
+ view: view
23
+ }.compact
24
+ )
25
+ end
26
+
27
+ # 공지사항 단건 (GET /v1/notices/:id)
28
+ # @comment_by Claude
29
+ # @date: 26-09-29
30
+ # 비공개 공지는 supervisor: true 일 때만 보인다(아니면 404 POST_NOT_FOUND).
31
+ def notice_detail(notice_id:, supervisor: false, idempotency_key: nil)
32
+ request(
33
+ uri: "notices/#{notice_id}",
34
+ method: :get,
35
+ headers: {
36
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
37
+ 'Bootpay-Role' => supervisor ? 'supervisor' : 'user'
38
+ }
39
+ )
40
+ end
41
+
42
+ # 공지사항 등록 (POST /v1/notices) — supervisor 전용
43
+ # @comment_by Claude
44
+ # @date: 26-09-29
45
+ def notice_create(title:, content:, images: nil, is_notice: nil, is_display: nil, idempotency_key: nil)
46
+ request(
47
+ uri: 'notices',
48
+ method: :post,
49
+ headers: {
50
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
51
+ 'Bootpay-Role' => 'supervisor'
52
+ },
53
+ payload: {
54
+ title: title,
55
+ content: content,
56
+ images: images,
57
+ is_notice: is_notice,
58
+ is_display: is_display
59
+ }.compact
60
+ )
61
+ end
62
+
63
+ # 공지사항 수정 (PUT /v1/notices/:id) — supervisor 전용
64
+ # @comment_by Claude
65
+ # @date: 26-09-29
66
+ # 보낸 필드만 바뀐다. images 는 보내면 목록 전체를 교체한다([] 면 모두 삭제).
67
+ def notice_update(notice_id:, title: nil, content: nil, images: nil, is_notice: nil, is_display: nil,
68
+ idempotency_key: nil)
69
+ request(
70
+ uri: "notices/#{notice_id}",
71
+ method: :put,
72
+ headers: {
73
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
74
+ 'Bootpay-Role' => 'supervisor'
75
+ },
76
+ payload: {
77
+ title: title,
78
+ content: content,
79
+ images: images,
80
+ is_notice: is_notice,
81
+ is_display: is_display
82
+ }.compact
83
+ )
84
+ end
85
+
86
+ # 공지사항 삭제 (DELETE /v1/notices/:id) — supervisor 전용
87
+ # @comment_by Claude
88
+ # @date: 26-09-29
89
+ def notice_delete(notice_id:, idempotency_key: nil)
90
+ request(
91
+ uri: "notices/#{notice_id}",
92
+ method: :delete,
93
+ headers: {
94
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
95
+ 'Bootpay-Role' => 'supervisor'
96
+ }
97
+ )
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,69 @@
1
+ module BootpayStore::Concern::Payout
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ # 입점사 지급 요청 등록 (POST /v1/sdk/payouts) — supervisor 전용
6
+ # @comment_by Claude
7
+ # @date: 26-11-12
8
+ # 가맹점 지정 금액(manual) 정산 쇼핑몰이 입점사와 지급액을 정해 지급 원장에 등록한다. 실제 송금은 부트페이 배치가 한다.
9
+ # 같은 merchant_payout_id 재호출은 처음 만든 항목을 돌려준다(금액·입점사·지급일은 바꿀 수 없다).
10
+ # payout_date: 'YYYY-MM-DD' (미지정시 오늘). 정산 방식이 manual 이 아니면 18360, PG 미등록 입점사는 18378.
11
+ def payout_create(sub_seller_id:, amount:, merchant_payout_id:, order_number: nil, payout_date: nil,
12
+ idempotency_key: nil)
13
+ request(
14
+ uri: 'sdk/payouts',
15
+ method: :post,
16
+ headers: {
17
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
18
+ 'Bootpay-Role' => 'supervisor'
19
+ },
20
+ payload: {
21
+ sub_seller_id: sub_seller_id,
22
+ amount: amount,
23
+ merchant_payout_id: merchant_payout_id,
24
+ order_number: order_number,
25
+ payout_date: payout_date
26
+ }.compact
27
+ )
28
+ end
29
+
30
+ # 입점사 지급 항목 조회 (GET /v1/sdk/payouts/:id) — supervisor 전용
31
+ # @comment_by Claude
32
+ # @date: 26-11-12
33
+ # payout_id 는 merchant_payout_id 또는 지급 항목 id. 없으면 404 PAYOUT_RESULT_INVALID(18366).
34
+ def payout_detail(payout_id:, idempotency_key: nil)
35
+ request(
36
+ uri: "sdk/payouts/#{URI.encode_www_form_component(payout_id.to_s).gsub('+', '%20')}",
37
+ method: :get,
38
+ headers: {
39
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
40
+ 'Bootpay-Role' => 'supervisor'
41
+ }
42
+ )
43
+ end
44
+
45
+ # 입점사 지급 목록 (GET /v1/sdk/payouts) — supervisor 전용
46
+ # @comment_by Claude
47
+ # @date: 26-11-12
48
+ # 최근 생성 순. 응답 data 는 항목 배열이다(전체 개수 없음 — 받은 수가 limit 보다 적으면 마지막 페이지).
49
+ # status: ready · held · sending · requested · processing · succeeded · failed · cancelled · unknown · void
50
+ def payouts(status: nil, sub_seller_id: nil, from: nil, to: nil, page: nil, limit: nil, idempotency_key: nil)
51
+ request(
52
+ uri: 'sdk/payouts',
53
+ method: :get,
54
+ headers: {
55
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
56
+ 'Bootpay-Role' => 'supervisor'
57
+ },
58
+ params: {
59
+ status: status,
60
+ sub_seller_id: sub_seller_id,
61
+ from: from,
62
+ to: to,
63
+ page: page,
64
+ limit: limit
65
+ }.compact
66
+ )
67
+ end
68
+ end
69
+ end
@@ -67,6 +67,8 @@ module BootpayStore::Concern::Product
67
67
  # @date: 26-08-14
68
68
  # images 가 있으면 multipart, 없으면 JSON 으로 보낸다.
69
69
  # 컨트롤러가 읽는 필드는 _product_params 참조 — 여기 명시하지 않은 값도 attrs 로 그대로 전달된다.
70
+ # @date: 26-11-02 Bootpay-Role manager → user. 서버 스코프는 user:product_create/update/delete/status 뿐이라
71
+ # manager 로 보내면 manager:product_* 를 찾다 401 API_ROLE_NOT_SUPPORT(-50) 로 막혔다 (수정·삭제·상태변경도 같다)
70
72
  def product_create(name:, display_price: nil, desc: nil, content: nil, category_id: nil,
71
73
  type: nil, stock: nil, status_sale: nil, status_display: nil,
72
74
  use_subscription: nil, subscription_setting_id: nil,
@@ -88,7 +90,7 @@ module BootpayStore::Concern::Product
88
90
 
89
91
  headers = {
90
92
  'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
91
- 'Bootpay-Role' => 'manager'
93
+ 'Bootpay-Role' => 'user'
92
94
  }
93
95
 
94
96
  if images.present?
@@ -104,6 +106,7 @@ module BootpayStore::Concern::Product
104
106
  # @comment_by Claude (alfred)
105
107
  # @date: 26-08-14
106
108
  # 바뀐 값만 보내면 된다. category_id 는 키 존재 여부로 '해제 의사'를 판별하므로 주의.
109
+ # @date: 26-11-02 Bootpay-Role manager → user (product_create 주석 참고)
107
110
  def product_update(product_id:, name: nil, display_price: nil, desc: nil, content: nil,
108
111
  category_id: nil, stock: nil, status_sale: nil, status_display: nil,
109
112
  save_by: nil, idempotency_key: nil, **attrs)
@@ -112,7 +115,7 @@ module BootpayStore::Concern::Product
112
115
  method: :put,
113
116
  headers: {
114
117
  'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
115
- 'Bootpay-Role' => 'manager'
118
+ 'Bootpay-Role' => 'user'
116
119
  },
117
120
  payload: {
118
121
  name: name,
@@ -131,13 +134,14 @@ module BootpayStore::Concern::Product
131
134
  # 상품을 삭제한다 (DELETE /v1/products/:id)
132
135
  # @comment_by Claude (alfred)
133
136
  # @date: 26-08-14
137
+ # @date: 26-11-02 Bootpay-Role manager → user (product_create 주석 참고)
134
138
  def product_delete(product_id:, idempotency_key: nil)
135
139
  request(
136
140
  uri: "products/#{product_id}",
137
141
  method: :delete,
138
142
  headers: {
139
143
  'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
140
- 'Bootpay-Role' => 'manager'
144
+ 'Bootpay-Role' => 'user'
141
145
  }
142
146
  )
143
147
  end
@@ -146,6 +150,7 @@ module BootpayStore::Concern::Product
146
150
  # @comment_by Claude (alfred)
147
151
  # @date: 26-08-14
148
152
  # 컨트롤러 _status_params 기준. 재고(stock)는 여기가 아니라 product_update 로 바꾼다.
153
+ # @date: 26-11-02 Bootpay-Role manager → user (product_create 주석 참고)
149
154
  def product_status(product_id:, status_sale: nil, status_display: nil, status_frozen: nil,
150
155
  status_review: nil, use_display_period: nil, display_start_at: nil, display_end_at: nil,
151
156
  use_sale_period: nil, sale_start_at: nil, sale_end_at: nil,
@@ -155,7 +160,7 @@ module BootpayStore::Concern::Product
155
160
  method: :put,
156
161
  headers: {
157
162
  'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
158
- 'Bootpay-Role' => 'manager'
163
+ 'Bootpay-Role' => 'user'
159
164
  },
160
165
  payload: {
161
166
  status_sale: status_sale,