bootpay 1.2.0 → 3.0.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.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +8 -9
  3. data/.rspec +2 -0
  4. data/CHANGELOG.md +63 -21
  5. data/CODE_OF_CONDUCT.md +1 -1
  6. data/Gemfile +2 -4
  7. data/LICENSE.txt +1 -1
  8. data/README.md +309 -196
  9. data/Rakefile +3 -3
  10. data/bin/console +1 -1
  11. data/bootpay.gemspec +23 -20
  12. data/lib/bootpay/bootpay-rest-client.rb +51 -0
  13. data/lib/bootpay/concern/authenticate.rb +69 -0
  14. data/lib/bootpay/concern/cash_receipt.rb +83 -0
  15. data/lib/bootpay/concern/easy.rb +18 -0
  16. data/lib/bootpay/concern/escrow.rb +25 -0
  17. data/lib/bootpay/concern/payment.rb +126 -0
  18. data/lib/bootpay/concern/reseller.rb +97 -0
  19. data/lib/bootpay/concern/rest.rb +56 -0
  20. data/lib/bootpay/concern/sdk.rb +52 -0
  21. data/lib/bootpay/concern/seller.rb +15 -0
  22. data/lib/bootpay/concern/service.rb +15 -0
  23. data/lib/bootpay/concern/subscription.rb +211 -0
  24. data/lib/bootpay/concern/token.rb +33 -0
  25. data/lib/bootpay/concern/user_token.rb +23 -0
  26. data/lib/bootpay/concern/wallet.rb +20 -0
  27. data/lib/bootpay/concern/webhook.rb +18 -0
  28. data/lib/bootpay/concern.rb +35 -0
  29. data/lib/bootpay-backend-ruby.rb +6 -0
  30. data/lib/bootpay.rb +10 -66
  31. data/lib/bootpay_storage/bootpay-storage-rest-client.rb +46 -0
  32. data/lib/bootpay_storage/concern/csv.rb +18 -0
  33. data/lib/bootpay_storage/concern/image.rb +30 -0
  34. data/lib/bootpay_storage/concern/rest.rb +176 -0
  35. data/lib/bootpay_storage/concern/token.rb +9 -0
  36. data/lib/bootpay_storage/concern.rb +12 -0
  37. data/lib/bootpay_storage/response.rb +14 -0
  38. data/lib/bootpay_store/bootpay-store-rest-client.rb +47 -0
  39. data/lib/bootpay_store/concern/alimtalk_message.rb +67 -0
  40. data/lib/bootpay_store/concern/alimtalk_official.rb +63 -0
  41. data/lib/bootpay_store/concern/alimtalk_optout.rb +72 -0
  42. data/lib/bootpay_store/concern/alimtalk_send.rb +83 -0
  43. data/lib/bootpay_store/concern/alimtalk_sender.rb +105 -0
  44. data/lib/bootpay_store/concern/alimtalk_template.rb +215 -0
  45. data/lib/bootpay_store/concern/alimtalk_webhook.rb +84 -0
  46. data/lib/bootpay_store/concern/invoice.rb +102 -0
  47. data/lib/bootpay_store/concern/mall_setting.rb +385 -0
  48. data/lib/bootpay_store/concern/order.rb +74 -0
  49. data/lib/bootpay_store/concern/order_subscription.rb +391 -0
  50. data/lib/bootpay_store/concern/payment.rb +119 -0
  51. data/lib/bootpay_store/concern/product.rb +175 -0
  52. data/lib/bootpay_store/concern/rest.rb +128 -0
  53. data/lib/bootpay_store/concern/store.rb +31 -0
  54. data/lib/bootpay_store/concern/supervisor.rb +211 -0
  55. data/lib/bootpay_store/concern/token.rb +24 -0
  56. data/lib/bootpay_store/concern/user.rb +325 -0
  57. data/lib/bootpay_store/concern/user_group.rb +221 -0
  58. data/lib/bootpay_store/concern/webhook.rb +17 -0
  59. data/lib/bootpay_store/concern.rb +45 -0
  60. data/lib/bootpay_store/response.rb +14 -0
  61. data/lib/version.rb +9 -0
  62. metadata +77 -49
  63. data/.DS_Store +0 -0
  64. data/.env.example +0 -23
  65. data/lib/.DS_Store +0 -0
  66. data/lib/bootpay/.DS_Store +0 -0
  67. data/lib/bootpay/authentication.rb +0 -61
  68. data/lib/bootpay/automatic_transfer.rb +0 -56
  69. data/lib/bootpay/billing.rb +0 -157
  70. data/lib/bootpay/cancel.rb +0 -29
  71. data/lib/bootpay/cash_receipt.rb +0 -77
  72. data/lib/bootpay/commerce/cart.rb +0 -20
  73. data/lib/bootpay/commerce/category.rb +0 -39
  74. data/lib/bootpay/commerce/commerce_resource.rb +0 -196
  75. data/lib/bootpay/commerce/coupon.rb +0 -41
  76. data/lib/bootpay/commerce/invoice.rb +0 -46
  77. data/lib/bootpay/commerce/order.rb +0 -61
  78. data/lib/bootpay/commerce/order_cancel.rb +0 -52
  79. data/lib/bootpay/commerce/order_subscription.rb +0 -117
  80. data/lib/bootpay/commerce/order_subscription_adjustment.rb +0 -29
  81. data/lib/bootpay/commerce/order_subscription_bill.rb +0 -50
  82. data/lib/bootpay/commerce/order_subscription_request.rb +0 -60
  83. data/lib/bootpay/commerce/point.rb +0 -36
  84. data/lib/bootpay/commerce/product.rb +0 -63
  85. data/lib/bootpay/commerce/store.rb +0 -21
  86. data/lib/bootpay/commerce/user.rb +0 -75
  87. data/lib/bootpay/commerce/user_group.rb +0 -70
  88. data/lib/bootpay/easy.rb +0 -25
  89. data/lib/bootpay/escrow.rb +0 -24
  90. data/lib/bootpay/link.rb +0 -60
  91. data/lib/bootpay/naverpay.rb +0 -17
  92. data/lib/bootpay/payment_resource.rb +0 -31
  93. data/lib/bootpay/reseller.rb +0 -12
  94. data/lib/bootpay/rest.rb +0 -46
  95. data/lib/bootpay/submit.rb +0 -20
  96. data/lib/bootpay/token.rb +0 -33
  97. data/lib/bootpay/verification.rb +0 -55
  98. data/lib/bootpay/version.rb +0 -25
  99. data/lib/bootpay/wallet.rb +0 -56
  100. data/lib/bootpay_commerce.rb +0 -152
  101. /data/lib/{response.rb → bootpay/response.rb} +0 -0
@@ -0,0 +1,391 @@
1
+ module BootpayStore::Concern::OrderSubscription
2
+ extend ActiveSupport::Concern
3
+ included do
4
+ # 계약된 구독정보를 가져온다
5
+ # Comment by GOSOMI
6
+ # @date: 2025-06-20
7
+ # @date: 26-08-14 limit 인자 추가. 서버(v1/order_subscriptions_controller#index)는
8
+ # `params[:limit].presence || 20` 으로 이미 받고 있었는데 SDK 가 안 보내고 있었다.
9
+ # 기본값 20 은 서버 기본과 같아 기존 호출 결과가 바뀌지 않는다.
10
+ # ⚠️ 날짜 키는 search_date_from/to (또는 s_at/e_at). orders 의 css_at/cse_at 와 다르다.
11
+ # @date: 26-08-26 order_number 인자 추가. 서버(#index)가 params[:order_number] 로 주문번호 역조회를
12
+ # 지원하는데 SDK 에 인자가 없어 주문번호로 구독을 찾을 수 없었다.
13
+ def order_subscriptions(page: 1, limit: 20, keyword: nil, search_date_from: nil, search_date_to: nil,
14
+ request_type: nil, user_group_id: nil, status: nil, user_id: nil,
15
+ order_number: nil, idempotency_key: nil)
16
+ request(
17
+ uri: 'order_subscriptions',
18
+ method: :get,
19
+ headers: {
20
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
21
+ 'Bootpay-Role' => 'user'
22
+ },
23
+ params: {
24
+ page: page,
25
+ limit: limit,
26
+ keyword: keyword,
27
+ search_date_from: search_date_from,
28
+ search_date_to: search_date_to,
29
+ request_type: request_type,
30
+ user_group_id: user_group_id,
31
+ status: status,
32
+ user_id: user_id,
33
+ order_number: order_number
34
+ }.compact
35
+ )
36
+ end
37
+
38
+ # 구독 상세를 조회한다
39
+ def order_subscription_detail(order_subscription_id:, idempotency_key: nil)
40
+ request(
41
+ uri: "order_subscriptions/#{order_subscription_id}",
42
+ method: :get,
43
+ headers: {
44
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
45
+ 'Bootpay-Role' => 'user'
46
+ }
47
+ )
48
+ end
49
+
50
+ # ─────────────────────────────────────────────────────────────
51
+ # 구독 계약변경 · 조정항목 · 빌 조회 (@comment_by Claude (alfred) / @date: 26-08-14)
52
+ # ─────────────────────────────────────────────────────────────
53
+
54
+ # 구독 계약 내용을 변경한다 (PUT /v1/order_subscriptions/:id)
55
+ # 바뀐 값만 보내면 된다 (나머지는 서버가 그대로 유지).
56
+ #
57
+ # price 는 회차별 결제 금액의 **기준금액**이다. 바꾸면 결제예정(READY) 회차의 청구액이
58
+ # 즉시 다시 계산되고, 이후 회차도 이 금액으로 만들어진다. 이미 결제된 회차는 그대로다.
59
+ # 0 이하는 받지 않는다. 특정 회차만 가감하려면 order_subscription_adjustment_create 를 쓴다.
60
+ # (관리자 화면의 금액 변경과 같은 구현을 탄다) (@date: 26-08-24)
61
+ #
62
+ # memo 는 변경이력(SUBSCRIPTION_ACTION_UPDATE)에 남길 사유다. 서버가 읽고 있었는데
63
+ # SDK 에 인자가 없었다. (@date: 26-08-26)
64
+ def order_subscription_update(order_subscription_id:, product_id: nil, product_option_id: nil,
65
+ order_name: nil, total_subscription_duration: nil, quantity: nil,
66
+ address_id: nil, username: nil, phone: nil, email: nil,
67
+ use_free_trial: nil, free_trial_day: nil,
68
+ service_start_at: nil, service_end_at: nil, price: nil,
69
+ memo: nil, idempotency_key: nil)
70
+ request(
71
+ uri: "order_subscriptions/#{order_subscription_id}",
72
+ method: :put,
73
+ headers: {
74
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
75
+ 'Bootpay-Role' => 'supervisor'
76
+ },
77
+ payload: {
78
+ product_id: product_id,
79
+ product_option_id: product_option_id,
80
+ order_name: order_name,
81
+ total_subscription_duration: total_subscription_duration,
82
+ quantity: quantity,
83
+ address_id: address_id,
84
+ username: username,
85
+ phone: phone,
86
+ email: email,
87
+ use_free_trial: use_free_trial,
88
+ free_trial_day: free_trial_day,
89
+ service_start_at: service_start_at,
90
+ service_end_at: service_end_at,
91
+ price: price,
92
+ memo: memo
93
+ }.compact
94
+ )
95
+ end
96
+
97
+ # 가감산 조정항목을 추가한다 (POST /v1/order_subscriptions/:id/adjustments)
98
+ # ⚠️ /adjustments 한 경로에 POST·PUT·DELETE 세 동사가 걸려 있다. method 를 반드시 명시할 것.
99
+ # type 미전달 시 서버가 price>0 이면 SETUP_PRICE, 아니면 PERIOD_DISCOUNT 로 자동 판정한다.
100
+ #
101
+ # 회차 지정 방법 3가지 (아래로 갈수록 넓다):
102
+ # - duration: 5 → 5회차 한 건만
103
+ # - duration_from: 3, duration_to: 7 → 3~7회차 각각 한 건씩 (총 5건)
104
+ # - duration_from: 3, is_unlimited: true → 3회차부터 계약 끝까지 (레코드는 1건, duration_to 는 무시)
105
+ # 상한은 계약 총회차이며, 총회차가 무제한인 계약은 60회차까지다.
106
+ # 이미 결제가 끝난 회차는 거절된다. 범위 중 한 회차라도 최종 금액이 음수면 전부 거절된다(부분 반영 없음).
107
+ def order_subscription_adjustment_create(order_subscription_id:, name: nil, price: 0, duration: 1,
108
+ tax_free_price: 0, type: nil, duration_from: nil,
109
+ duration_to: nil, is_unlimited: nil, idempotency_key: nil)
110
+ request(
111
+ uri: "order_subscriptions/#{order_subscription_id}/adjustments",
112
+ method: :post,
113
+ headers: {
114
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
115
+ 'Bootpay-Role' => 'supervisor'
116
+ },
117
+ payload: {
118
+ name: name,
119
+ price: price,
120
+ duration: duration,
121
+ tax_free_price: tax_free_price,
122
+ type: type,
123
+ duration_from: duration_from,
124
+ duration_to: duration_to,
125
+ is_unlimited: is_unlimited
126
+ }.compact
127
+ )
128
+ end
129
+
130
+ # 특정 회차의 조정항목을 통째로 교체한다 (PUT /v1/order_subscriptions/:id/adjustments)
131
+ # adjustments 는 배열. 서버는 duration(회차) 단위로 갈아끼운다.
132
+ def order_subscription_adjustment_update(order_subscription_id:, duration: 1, adjustments: [],
133
+ idempotency_key: nil)
134
+ request(
135
+ uri: "order_subscriptions/#{order_subscription_id}/adjustments",
136
+ method: :put,
137
+ headers: {
138
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
139
+ 'Bootpay-Role' => 'supervisor'
140
+ },
141
+ payload: {
142
+ duration: duration,
143
+ adjustments: adjustments
144
+ }.compact
145
+ )
146
+ end
147
+
148
+ # 조정항목을 삭제한다 (DELETE /v1/order_subscriptions/:id/adjustments)
149
+ def order_subscription_adjustment_delete(order_subscription_id:, order_subscription_adjustment_id:,
150
+ idempotency_key: nil)
151
+ request(
152
+ uri: "order_subscriptions/#{order_subscription_id}/adjustments",
153
+ method: :delete,
154
+ headers: {
155
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
156
+ 'Bootpay-Role' => 'supervisor'
157
+ },
158
+ payload: { order_subscription_adjustment_id: order_subscription_adjustment_id }
159
+ )
160
+ end
161
+
162
+ # 구독 빌(회차) 목록을 조회한다 (GET /v1/order_subscription_bills)
163
+ # ⚠️ 경로가 order_subscription_bills — 언더스코어다 (하이픈 아님).
164
+ def order_subscription_bill_list(order_subscription_id: nil, page: 1, limit: 20, status: nil,
165
+ idempotency_key: nil)
166
+ request(
167
+ uri: 'order_subscription_bills',
168
+ method: :get,
169
+ headers: {
170
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
171
+ 'Bootpay-Role' => 'user'
172
+ },
173
+ params: {
174
+ order_subscription_id: order_subscription_id,
175
+ page: page,
176
+ limit: limit,
177
+ status: status
178
+ }.compact
179
+ )
180
+ end
181
+
182
+ # ─────────────────────────────────────────────────────────────
183
+ # 구독 진행중 요청 (requests/ing) (@comment_by Claude (alfred) / @date: 26-08-14)
184
+ #
185
+ # ⚠️ 동사가 제각각이다. routes 정본 기준:
186
+ # pause · purchase · termination · transfer → POST
187
+ # resume → PUT ← 이것만 다르다
188
+ # calculate_termination_fee → GET
189
+ # "오타겠지" 하며 resume 을 POST 로 바꾸지 말 것.
190
+ #
191
+ # ⚠️ supervisor_request_order_subscription_* (PUT order_subscriptions/:id/*) 와는 다른 라우트다.
192
+ # 이쪽은 구매자가 "요청"을 올리는 면, 저쪽은 관리자가 즉시 실행하는 면.
193
+ # ─────────────────────────────────────────────────────────────
194
+
195
+ # 구독 일시중지 요청 (POST /v1/order_subscriptions/requests/ing/pause)
196
+ def order_subscription_requests_ing_pause(order_subscription_id:, reason: nil, paused_at: nil,
197
+ expected_resume_at: nil, idempotency_key: nil)
198
+ request(
199
+ uri: 'order_subscriptions/requests/ing/pause',
200
+ method: :post,
201
+ headers: {
202
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
203
+ 'Bootpay-Role' => 'user'
204
+ },
205
+ payload: {
206
+ order_subscription_id: order_subscription_id,
207
+ reason: reason,
208
+ paused_at: paused_at,
209
+ expected_resume_at: expected_resume_at
210
+ }.compact
211
+ )
212
+ end
213
+
214
+ # 구독 재개 요청 (PUT /v1/order_subscriptions/requests/ing/resume)
215
+ # ⚠️ requests/ing 계열 중 유일하게 PUT 이다.
216
+ def order_subscription_requests_ing_resume(order_subscription_id:, reason: nil, idempotency_key: nil)
217
+ request(
218
+ uri: 'order_subscriptions/requests/ing/resume',
219
+ method: :put,
220
+ headers: {
221
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
222
+ 'Bootpay-Role' => 'user'
223
+ },
224
+ payload: {
225
+ order_subscription_id: order_subscription_id,
226
+ reason: reason
227
+ }.compact
228
+ )
229
+ end
230
+
231
+ # 중도인수 요청 (POST /v1/order_subscriptions/requests/ing/purchase)
232
+ def order_subscription_requests_ing_purchase(order_subscription_id:, price: nil, tax_free_price: nil,
233
+ reason: nil, idempotency_key: nil)
234
+ request(
235
+ uri: 'order_subscriptions/requests/ing/purchase',
236
+ method: :post,
237
+ headers: {
238
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
239
+ 'Bootpay-Role' => 'user'
240
+ },
241
+ payload: {
242
+ order_subscription_id: order_subscription_id,
243
+ price: price,
244
+ tax_free_price: tax_free_price,
245
+ reason: reason
246
+ }.compact
247
+ )
248
+ end
249
+
250
+ # 중도해지 요청 (POST /v1/order_subscriptions/requests/ing/termination)
251
+ def order_subscription_requests_ing_termination(order_subscription_id:, order_number: nil, reason: nil,
252
+ termination_fee: nil, last_bill_refund_price: nil,
253
+ final_fee: nil, service_end_at: nil, idempotency_key: nil)
254
+ request(
255
+ uri: 'order_subscriptions/requests/ing/termination',
256
+ method: :post,
257
+ headers: {
258
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
259
+ 'Bootpay-Role' => 'user'
260
+ },
261
+ payload: {
262
+ order_subscription_id: order_subscription_id,
263
+ order_number: order_number,
264
+ reason: reason,
265
+ termination_fee: termination_fee,
266
+ last_bill_refund_price: last_bill_refund_price,
267
+ final_fee: final_fee,
268
+ service_end_at: service_end_at
269
+ }.compact
270
+ )
271
+ end
272
+
273
+ # 구독 이전/승계 요청 (POST /v1/order_subscriptions/requests/ing/transfer)
274
+ def order_subscription_requests_ing_transfer(order_subscription_id:, new_user_id: nil, new_username: nil,
275
+ new_user_email: nil, new_user_phone: nil, new_user_address: nil,
276
+ wallet_id: nil, reason: nil, idempotency_key: nil)
277
+ request(
278
+ uri: 'order_subscriptions/requests/ing/transfer',
279
+ method: :post,
280
+ headers: {
281
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
282
+ 'Bootpay-Role' => 'user'
283
+ },
284
+ payload: {
285
+ order_subscription_id: order_subscription_id,
286
+ new_user_id: new_user_id,
287
+ new_username: new_username,
288
+ new_user_email: new_user_email,
289
+ new_user_phone: new_user_phone,
290
+ new_user_address: new_user_address,
291
+ wallet_id: wallet_id,
292
+ reason: reason
293
+ }.compact
294
+ )
295
+ end
296
+
297
+ # 중도해지 수수료 사전계산 (GET /v1/order_subscriptions/requests/ing/calculate_termination_fee)
298
+ # 해지 요청 전에 얼마가 나오는지 미리 보여줄 때 쓴다.
299
+ def order_subscription_calculate_termination_fee(order_subscription_id:, order_number: nil,
300
+ idempotency_key: nil)
301
+ request(
302
+ uri: 'order_subscriptions/requests/ing/calculate_termination_fee',
303
+ method: :get,
304
+ headers: {
305
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
306
+ 'Bootpay-Role' => 'user'
307
+ },
308
+ params: {
309
+ order_subscription_id: order_subscription_id,
310
+ order_number: order_number
311
+ }.compact
312
+ )
313
+ end
314
+
315
+ # ─────────────────────────────────────────────────────────────
316
+ # 구독 요청 리소스 (order-subscription-requests) ⚠️ 하이픈 경로
317
+ # 하이픈 경로는 order-subscription-requests 와 user-groups 둘뿐이다.
318
+ # order_subscriptions · order_subscription_bills 는 언더스코어 — 복사해 고칠 때 가장 흔히 틀리는 지점.
319
+ # ─────────────────────────────────────────────────────────────
320
+
321
+ # 구독 변경요청 목록 (GET /v1/order-subscription-requests)
322
+ # project_id 를 주면 supervisor 모드(프로젝트 전체 검색), 없으면 본인 요청만.
323
+ def order_subscription_request_list(project_id: nil, order_subscription_id: nil, page: 1, limit: 20,
324
+ keyword: nil, s_at: nil, e_at: nil, status: nil,
325
+ request_type: nil, user_id: nil, user_group_id: nil,
326
+ idempotency_key: nil)
327
+ request(
328
+ uri: 'order-subscription-requests',
329
+ method: :get,
330
+ headers: {
331
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
332
+ 'Bootpay-Role' => project_id.present? ? 'supervisor' : 'user'
333
+ },
334
+ params: {
335
+ project_id: project_id,
336
+ order_subscription_id: order_subscription_id,
337
+ page: page,
338
+ limit: limit,
339
+ keyword: keyword,
340
+ s_at: s_at,
341
+ e_at: e_at,
342
+ status: status,
343
+ request_type: request_type,
344
+ user_id: user_id,
345
+ user_group_id: user_group_id
346
+ }.compact
347
+ )
348
+ end
349
+
350
+ # 구독 변경요청 상세 (GET /v1/order-subscription-requests/:id)
351
+ def order_subscription_request_detail(request_history_id:, project_id: nil, idempotency_key: nil)
352
+ request(
353
+ uri: "order-subscription-requests/#{request_history_id}",
354
+ method: :get,
355
+ headers: {
356
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
357
+ 'Bootpay-Role' => project_id.present? ? 'supervisor' : 'user'
358
+ },
359
+ params: { project_id: project_id }.compact
360
+ )
361
+ end
362
+
363
+ # 구독 변경요청 승인/반려 (PUT /v1/order-subscription-requests/:id)
364
+ # ⚠️ 승인과 반려는 별도 액션이 아니다. 라우트는 index/show/update 셋뿐이고
365
+ # approval: 'approve' | 'reject' 파라미터로 갈린다.
366
+ # 서버가 params[:action] 을 Rails 예약어로 쓰기 때문에 키 이름이 approval 이다.
367
+ def order_subscription_request_update(request_history_id:, approval:, reason: nil,
368
+ price: nil, tax_free_price: nil, termination_fee: nil,
369
+ last_bill_refund_price: nil, final_fee: nil, service_end_at: nil,
370
+ idempotency_key: nil)
371
+ request(
372
+ uri: "order-subscription-requests/#{request_history_id}",
373
+ method: :put,
374
+ headers: {
375
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
376
+ 'Bootpay-Role' => 'supervisor'
377
+ },
378
+ payload: {
379
+ approval: approval,
380
+ reason: reason,
381
+ price: price,
382
+ tax_free_price: tax_free_price,
383
+ termination_fee: termination_fee,
384
+ last_bill_refund_price: last_bill_refund_price,
385
+ final_fee: final_fee,
386
+ service_end_at: service_end_at
387
+ }.compact
388
+ )
389
+ end
390
+ end
391
+ end
@@ -0,0 +1,119 @@
1
+ module BootpayStore::Concern::Payment
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ # 주문 취소
6
+ # Comment by GOSOMI
7
+ # @date: 2025-04-04
8
+ def request_order_cancel(idempotency_key: nil, cancel_id: nil, order_number:, cancel_products: nil, cancel_price: nil,
9
+ cancel_tax_free_price: 0, cancel_requester: '시스템', cancel_message: '요청취소', cancel_immediately: false,
10
+ cancel_order_subscription_bills: nil)
11
+ request(
12
+ uri: 'order/cancel',
13
+ headers: {
14
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
15
+ 'Bootpay-Role' => 'user'
16
+ },
17
+ payload:
18
+ {
19
+ order_number: order_number,
20
+ request_cancel_parameters: {
21
+ cancel_id: cancel_id,
22
+ cancel_products: cancel_products,
23
+ cancel_order_subscription_bills: cancel_order_subscription_bills,
24
+ cancel_price: cancel_price,
25
+ cancel_tax_free_price: cancel_tax_free_price,
26
+ cancel_requester: cancel_requester,
27
+ cancel_message: cancel_message,
28
+ cancel_immediately: cancel_immediately
29
+ }.compact
30
+ }.compact
31
+ )
32
+ end
33
+
34
+ # 주문 취소 요청을 철회한다
35
+ # def request_order_cancel_revoke(idempotency_key: nil, order_cancellation_request_id:)
36
+ # request(
37
+ # uri: "order/cancel/#{order_cancellation_request_id}",
38
+ # method: :delete,
39
+ # headers: {
40
+ # 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid
41
+ # }
42
+ # )
43
+ # end
44
+
45
+ # (관리자)
46
+ # 주문 취소 요청을 반려처리 한다
47
+ def reject_order_cancel(idempotency_key: nil, order_cancellation_request_id:, message: nil)
48
+ request(
49
+ uri: "order/cancel/#{order_cancellation_request_id}/reject",
50
+ method: :put,
51
+ headers: {
52
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
53
+ 'Bootpay-Role' => 'supervisor'
54
+ },
55
+ payload: {
56
+ message: message
57
+ }.compact
58
+ )
59
+ end
60
+
61
+ # (관리자)
62
+ # 주문 취소 요청을 승인처리 한다
63
+ # @date: 26-08-14 인자명 통일 — 서버(v1/order/cancel_controller)는 approve/reject/withdraw 셋 다
64
+ # params[:id] 를 order_cancellation_request_id 로 동일하게 취급한다. reject 와 이름이 달라 다른 값처럼
65
+ # 보였던 문제를 order_cancellation_request_id: 로 맞춘다. 구 이름도 계속 받는다(하위호환).
66
+ def approve_order_cancel(idempotency_key: nil, order_cancellation_request_id: nil,
67
+ order_cancellation_request_history_id: nil, message: nil)
68
+ cancellation_id = order_cancellation_request_id.presence || order_cancellation_request_history_id
69
+ request(
70
+ uri: "order/cancel/#{cancellation_id}/approve",
71
+ method: :put,
72
+ headers: {
73
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
74
+ 'Bootpay-Role' => 'supervisor'
75
+ },
76
+ payload: {
77
+ message: message
78
+ }.compact
79
+ )
80
+ end
81
+
82
+ # 주문 취소 요청 내역을 조회한다 (GET /v1/order/cancel)
83
+ # @comment_by Claude (alfred)
84
+ # @date: 26-08-14
85
+ # order_number 또는 order_id 로 필터. 둘 다 없으면 전체.
86
+ # approve/reject/withdraw 에 넘길 :id 를 여기서 얻는다 — 이게 없어서 승인/반려가 사실상 쓸 수 없었다.
87
+ def order_cancel_list(order_number: nil, order_id: nil, idempotency_key: nil)
88
+ request(
89
+ uri: 'order/cancel',
90
+ method: :get,
91
+ headers: {
92
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
93
+ 'Bootpay-Role' => 'user'
94
+ },
95
+ params: {
96
+ order_number: order_number,
97
+ order_id: order_id
98
+ }.compact
99
+ )
100
+ end
101
+
102
+ # (구매자) 주문 취소 요청을 철회한다 (PUT /v1/order/cancel/:id/withdraw)
103
+ # @comment_by Claude (alfred)
104
+ # @date: 26-08-14
105
+ # ⚠️ 주석 처리된 request_order_cancel_revoke(DELETE order/cancel/:id) 와 다른 라우트다.
106
+ # 서버에는 destroy(DELETE :id)와 withdraw(PUT :id/withdraw)가 둘 다 있고 같은 모델 메서드를 부른다.
107
+ # 매뉴얼이 문서화한 쪽은 withdraw 이므로 이쪽을 쓴다.
108
+ def order_cancel_withdraw(order_cancellation_request_id:, idempotency_key: nil)
109
+ request(
110
+ uri: "order/cancel/#{order_cancellation_request_id}/withdraw",
111
+ method: :put,
112
+ headers: {
113
+ 'Idempotency-Key' => idempotency_key.presence || SecureRandom.uuid,
114
+ 'Bootpay-Role' => 'user'
115
+ }
116
+ )
117
+ end
118
+ end
119
+ end