bootpay-backend-ruby 3.0.0.pre.alpha.3 → 3.0.0.pre.alpha.4

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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -1
  3. data/CHANGELOG.md +10 -0
  4. data/Gemfile +1 -1
  5. data/bootpay-backend-ruby.gemspec +9 -1
  6. data/lib/bootpay/bootpay-rest-client.rb +7 -2
  7. data/lib/bootpay/concern/authenticate.rb +19 -17
  8. data/lib/bootpay/concern/cash_receipt.rb +1 -1
  9. data/lib/bootpay/concern/payment.rb +2 -1
  10. data/lib/bootpay/concern/rest.rb +19 -1
  11. data/lib/bootpay/concern/sdk.rb +16 -0
  12. data/lib/bootpay/concern/subscription.rb +15 -28
  13. data/lib/bootpay/concern/token.rb +13 -0
  14. data/lib/bootpay/concern/wallet.rb +4 -25
  15. data/lib/bootpay_storage/concern/csv.rb +18 -0
  16. data/lib/bootpay_storage/concern/image.rb +12 -0
  17. data/lib/bootpay_storage/concern/rest.rb +45 -0
  18. data/lib/bootpay_storage/concern.rb +2 -0
  19. data/lib/bootpay_store/bootpay-store-rest-client.rb +7 -6
  20. data/lib/bootpay_store/concern/alimtalk_message.rb +67 -0
  21. data/lib/bootpay_store/concern/alimtalk_official.rb +63 -0
  22. data/lib/bootpay_store/concern/alimtalk_optout.rb +72 -0
  23. data/lib/bootpay_store/concern/alimtalk_send.rb +83 -0
  24. data/lib/bootpay_store/concern/alimtalk_sender.rb +105 -0
  25. data/lib/bootpay_store/concern/alimtalk_template.rb +215 -0
  26. data/lib/bootpay_store/concern/alimtalk_webhook.rb +84 -0
  27. data/lib/bootpay_store/concern/invoice.rb +102 -0
  28. data/lib/bootpay_store/concern/mall_setting.rb +385 -0
  29. data/lib/bootpay_store/concern/order.rb +74 -0
  30. data/lib/bootpay_store/concern/order_subscription.rb +391 -0
  31. data/lib/bootpay_store/concern/payment.rb +119 -0
  32. data/lib/bootpay_store/concern/product.rb +175 -0
  33. data/lib/bootpay_store/concern/rest.rb +91 -1
  34. data/lib/bootpay_store/concern/store.rb +31 -0
  35. data/lib/bootpay_store/concern/supervisor.rb +211 -0
  36. data/lib/bootpay_store/concern/token.rb +4 -4
  37. data/lib/bootpay_store/concern/user.rb +325 -0
  38. data/lib/bootpay_store/concern/user_group.rb +221 -0
  39. data/lib/bootpay_store/concern/webhook.rb +17 -0
  40. data/lib/bootpay_store/concern.rb +38 -28
  41. data/lib/version.rb +1 -1
  42. metadata +38 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3c39a84c5f8ae3dc4bdde7e2b7bf99a3b570ea66f5d2368442be24e888b148c7
4
- data.tar.gz: 6d29b4e6920b8a995b2bc9cd06484eee06770b91daeefa263f2c90ea72c19331
3
+ metadata.gz: 7dabe4bba771d7710f61b2f20a843e1e452028a723962e04de6dcdb694e83342
4
+ data.tar.gz: d19e63191fcfae0c2bad8ffc4c0f7835365a8c87896a90266fa5baae795e245c
5
5
  SHA512:
6
- metadata.gz: 7df5bdd01f1682769cb2fb5b6a90da35ba87cc289e2e9539104f671f474963dd8451b9916a8f5016b5b46bb44017b992cf733a2ae82ae8d8b3461d9c1b3f52d6
7
- data.tar.gz: f9b0fcf40002c3c579b73da89bdca1829f861ad4c661f171bdc37392f208cec4f5005d0b3ffffff187a42a127d1c7a77b393e9b58aa33310f6ed36bf5f4e44da
6
+ metadata.gz: 7bf6c3328a7a3097b1acee7dfc8484f782fd5d96a79cc9d04cf3c69f005a73ecc521b39d09b4c5f8d5f11568e054b978882c5ffdf4edae4d6cbb8157184c4ee7
7
+ data.tar.gz: b2e6b6a95fcb6d8dda0c3fa317b21bbf29565916b7839a8d8ec272af1398972d251b536f53081ad659164f949106e18597bcc782146fcd5711a2beadd9a3761a
data/.gitignore CHANGED
@@ -16,4 +16,8 @@ Gemfile.lock
16
16
  /spec/bootpay/card_billing/request_rest_billing_key_spec.rb
17
17
  /spec/bootpay/__stage_test_unit_spec.rb
18
18
  /spec/bootpay/__development_test_unit_spec.rb
19
- .DS_Store
19
+ /spec/bootpay/
20
+ .DS_Store
21
+ # 실 API 키가 들어가는 로컬 설정 — 커밋 금지 (26-08-24 추가)
22
+ .env
23
+ .env.local
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ### 3.0.0-alpha.4
2
+ - 알림톡 v1 API 35종 추가 — 발송·발송내역·공식 카탈로그·자체 템플릿·발신프로필·수신거부·알림톡 웹훅 7개 모듈
3
+ - 알림톡 요청은 `Idempotency-Key` 를 싣지 않는다 (멱등은 발송의 `ref_id` 로만 성립)
4
+ - `Bootpay-Role` 은 항상 `user` (스코프 키가 전부 `user:alimtalk_*`)
5
+ - ⚠️ 샌드박스가 없다 — 발송·OTP·발신프로필 등록·템플릿 등록/검수는 실제로 나가고 과금된다
6
+ - 별건 현금영수증의 `pg` 를 선택 파라미터로 (미지정 시 가맹점 기본 PG 로 발행)
7
+ - mall_setting · order_subscription · supervisor · user · user_group · product · invoice ·
8
+ payment · order · store · webhook · storage 모듈 확충
9
+ - alpha.3(2025-03-27) 이후 63커밋 누적분을 릴리스한다
10
+
1
11
  ### 3.0.0
2
12
  - store api 추가
3
13
  - 내부용 storage api 추가
data/Gemfile CHANGED
@@ -7,4 +7,4 @@ gemspec
7
7
 
8
8
  gem "rake", "~> 13.0"
9
9
 
10
- gem "rspec", "~> 3.0"
10
+ gem "rspec", "~> 3.0"
@@ -13,8 +13,15 @@ Gem::Specification.new do |spec|
13
13
  spec.license = "MIT"
14
14
  # Specify which files should be added to the gem when it is released.
15
15
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
16
+ # ⚠️ 루트의 tests_*.rb / *.gem 도 제외한다 — 종전 규칙은 test/·spec/·features/ **디렉터리**만
17
+ # 걸러서 루트에 있던 tests_basic_auth_product_info.rb 가 gem 에 실려 나갔다.
18
+ # 그 파일에는 dev Commerce secret_key 가 하드코딩돼 있었다 (26-09-02).
16
19
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
17
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
20
+ `git ls-files -z`.split("\x0").reject do |f|
21
+ f.match(%r{\A(?:test|spec|features)/}) ||
22
+ f.match(%r{\A(?:tests?_.*\.rb|.*\.gem)\z}) ||
23
+ f.match(%r{\A\.env})
24
+ end
18
25
  end
19
26
  spec.bindir = "exe"
20
27
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
@@ -23,6 +30,7 @@ Gem::Specification.new do |spec|
23
30
  # Uncomment to register a new dependency of your gem
24
31
  spec.add_dependency "activesupport"
25
32
  spec.add_dependency "http"
33
+ spec.add_dependency "addressable"
26
34
 
27
35
  # For more information and examples about making a new gem, checkout our
28
36
  # guide at: https://bundler.io/guides/creating_gem.html
@@ -10,6 +10,8 @@ module Bootpay
10
10
  class RestClient
11
11
  include Concern
12
12
 
13
+ attr_accessor :application_id, :private_key, :client_key, :secret_key, :use_client_key, :mode, :token, :api_version
14
+
13
15
  API =
14
16
  {
15
17
  development: 'https://dev-api.bootpay.co.kr/v2',
@@ -17,11 +19,14 @@ module Bootpay
17
19
  production: 'https://api.bootpay.co.kr/v2'
18
20
  }
19
21
 
20
- SDK_VERSION = '5.0.0'
22
+ SDK_VERSION = '5.3.0'
21
23
 
22
- def initialize(application_id:, private_key:, mode: 'production')
24
+ def initialize(application_id: nil, private_key: nil, client_key: nil, secret_key: nil, mode: 'production')
23
25
  @application_id = application_id
24
26
  @private_key = private_key
27
+ @client_key = client_key
28
+ @secret_key = secret_key
29
+ @use_client_key = client_key.present?
25
30
  @mode = mode.presence || 'production'
26
31
  @token = nil
27
32
  @api_version = SDK_VERSION
@@ -16,24 +16,26 @@ module Bootpay::Concern::Authenticate
16
16
  # Comment by Gosomi
17
17
  # Date: 2022-11-02
18
18
  def request_authentication(pg:, method:, username:, identity_no:, carrier:, phone:, site_url:,
19
- authenticate_type: 'sms', order_name: '', authentication_id: '', extra: {}, user: {})
19
+ authenticate_type: 'sms', order_name: '', authentication_id: '', extra: {}, user: {}, client_ip: nil)
20
20
  request(
21
- method: :post,
22
- uri: 'request/authentication',
23
- payload: {
24
- pg: pg,
25
- method: method,
26
- username: username,
27
- identity_no: identity_no,
28
- carrier: carrier,
29
- phone: phone,
30
- site_url: site_url,
31
- authenticate_type: authenticate_type,
32
- order_name: order_name,
33
- authentication_id: authentication_id,
34
- extra: extra,
35
- user: user
36
- }
21
+ method: :post,
22
+ uri: 'request/authentication',
23
+ payload:
24
+ {
25
+ pg: pg,
26
+ method: method,
27
+ username: username,
28
+ identity_no: identity_no,
29
+ carrier: carrier,
30
+ phone: phone,
31
+ site_url: site_url,
32
+ authenticate_type: authenticate_type,
33
+ order_name: order_name,
34
+ authentication_id: authentication_id,
35
+ extra: extra,
36
+ user: user,
37
+ client_ip: client_ip
38
+ }.compact
37
39
  )
38
40
  end
39
41
 
@@ -5,7 +5,7 @@ module Bootpay::Concern::CashReceipt
5
5
  # 현금 영수증 발행 처리 하기
6
6
  # Comment by Gosomi
7
7
  # Date: 2021-12-15
8
- def request_cash_receipt(pg:, order_name:, identity_no:, purchased_at:, cash_receipt_type:, price:, tax_free:, user: {},
8
+ def request_cash_receipt(pg: nil, order_name:, identity_no:, purchased_at:, cash_receipt_type:, price:, tax_free:, user: {},
9
9
  metadata: {}, extra: {}, order_id:)
10
10
  request(
11
11
  method: :post,
@@ -59,7 +59,7 @@ module Bootpay::Concern::Payment
59
59
  # REST API로 결제 요청하기
60
60
  # Comment by Gosomi
61
61
  # Date: 2023-03-28
62
- def request_payment(platform_application_id:, pg:, method: nil, price:, tax_free: 0, order_name:, order_id:, user_token: nil, uuid: nil, sk: nil,
62
+ def request_payment(platform_application_id: nil, pg:, method: nil, price:, tax_free: 0, order_name:, order_id:, user_token: nil, uuid: nil, sk: nil,
63
63
  ti: 0, tk: nil, items: [], extra: {}, user: {}, agent: nil, commission_keys: nil, wallet_id: nil, terms: [], widget_key: nil, widget_sandbox: false,
64
64
  redirect_url: nil)
65
65
  rand_uuid = SecureRandom.uuid
@@ -79,6 +79,7 @@ module Bootpay::Concern::Payment
79
79
  commission_keys: commission_keys,
80
80
  terms: terms,
81
81
  redirect_url: redirect_url,
82
+ widget: widget_key.present? ? 1 : 0,
82
83
  widget_key: widget_key,
83
84
  widget_sandbox: widget_sandbox,
84
85
  uuid: uuid.presence || rand_uuid,
@@ -10,7 +10,7 @@ module Bootpay::Concern::Rest
10
10
  def request(method: :post, uri:, payload: {}, headers: {}, params: nil)
11
11
  response = HTTP.headers(
12
12
  {
13
- Authorization: "Bearer #{@token}",
13
+ Authorization: authotization_header,
14
14
  content_type: 'application/json',
15
15
  accept: 'application/json',
16
16
  bootpay_api_version: @api_version,
@@ -34,5 +34,23 @@ module Bootpay::Concern::Rest
34
34
  backtrace: e.backtrace.join("\n")
35
35
  )
36
36
  end
37
+
38
+ # Authorize Header
39
+ # Comment by GOSOMI
40
+ # @date: 2026-03-11
41
+ def authotization_header
42
+ if @use_client_key
43
+ "Basic #{basic_authentification}"
44
+ else
45
+ "Bearer #{@token}" if @token.present?
46
+ end
47
+ end
48
+
49
+ # basic_authentification
50
+ # Comment by GOSOMI
51
+ # @date: 2026-03-11
52
+ def basic_authentification
53
+ @token = Base64.strict_encode64("#{@client_key}:#{@secret_key}")
54
+ end
37
55
  end
38
56
  end
@@ -32,5 +32,21 @@ module Bootpay::Concern::Sdk
32
32
  }
33
33
  )
34
34
  end
35
+
36
+ # Validate Password Token
37
+ # Comment by GOSOMI
38
+ # @date: 2025-12-24
39
+ def validate_password_token(token:, user_token:)
40
+ request(
41
+ method: :post,
42
+ uri: 'sdk/password-token',
43
+ headers: {
44
+ 'Bootpay-User-Token': user_token
45
+ },
46
+ payload: {
47
+ token: token
48
+ }
49
+ )
50
+ end
35
51
  end
36
52
  end
@@ -12,39 +12,15 @@ module Bootpay::Concern::Subscription
12
12
  )
13
13
  end
14
14
 
15
- # 빌링키로 결제 요청하기
16
- # Comment by Gosomi
17
- # Date: 2021-11-02
18
- def request_subscribe_card_payment(billing_key:, order_name:, price:, tax_free: 0, card_quota: '00', feedback_url: nil, content_type: nil,
19
- card_interest: nil, order_id:, items: [], user: {}, extra: {}, metadata: {})
20
- request(
21
- uri: 'subscribe/payment',
22
- payload: {
23
- billing_key: billing_key,
24
- metadata: metadata,
25
- order_name: order_name,
26
- price: price,
27
- tax_free: tax_free,
28
- card_quota: card_quota,
29
- card_interest: card_interest,
30
- order_id: order_id,
31
- items: items,
32
- user: user,
33
- extra: extra,
34
- feedback_url: feedback_url,
35
- content_type: content_type
36
- }
37
- )
38
- end
39
-
40
15
  # 빌링키로 결제 요청하기
41
16
  # Comment by ehowlsla
42
17
  # Date: 2024-05-29
43
- def request_subscribe_payment(billing_key:, order_name:, price:, tax_free: 0, card_quota: '00', feedback_url: nil, content_type: nil,
44
- card_interest: nil, order_id:, items: [], user: {}, extra: {}, metadata: {})
18
+ def request_subscribe_payment(wallet_key: nil, billing_key: nil, order_name:, price:, tax_free: 0, card_quota: '00', feedback_url: nil, content_type: nil,
19
+ card_interest: nil, order_id:, items: [], user: {}, extra: {}, metadata: {})
45
20
  request(
46
21
  uri: 'subscribe/payment',
47
22
  payload: {
23
+ wallet_key: wallet_key,
48
24
  billing_key: billing_key,
49
25
  metadata: metadata,
50
26
  order_name: order_name,
@@ -62,7 +38,6 @@ module Bootpay::Concern::Subscription
62
38
  )
63
39
  end
64
40
 
65
-
66
41
  # 자동결제 예약
67
42
  # Comment by Gosomi
68
43
  # Date: 2022-04-21
@@ -181,6 +156,8 @@ module Bootpay::Concern::Subscription
181
156
  )
182
157
  end
183
158
 
159
+ alias_method :request_subscribe_card_payment, :request_subscribe_payment
160
+
184
161
  # ARS나 본인인증 이후 빌링키 발급
185
162
  # Comment by GOSOMI
186
163
  # @date: 2024-01-26
@@ -220,5 +197,15 @@ module Bootpay::Concern::Subscription
220
197
  uri: "billing_key/#{billing_key}"
221
198
  )
222
199
  end
200
+
201
+ # 우선순위 빌링키 조회기능 추가
202
+ # Comment by GOSOMI
203
+ # @date: 2026-07-03
204
+ def lookup_sequential_billing_key(widget_key:, billing_key:, user_id:)
205
+ request(
206
+ method: :get,
207
+ uri: "subscribe/sequential_billing_key/#{billing_key}?widget_key=#{widget_key}&user_id=#{user_id}"
208
+ )
209
+ end
223
210
  end
224
211
  end
@@ -16,5 +16,18 @@ module Bootpay::Concern::Token
16
16
  @token = response.data[:access_token] if response.success?
17
17
  response
18
18
  end
19
+
20
+ # 둘다 겸하는 경우 우회함수
21
+ # Comment by GOSOMI
22
+ # @date: 2026-03-11
23
+ def basic_or_request_access_token
24
+ if @use_client_key
25
+ Object.new.tap do |o|
26
+ o.define_singleton_method(:success?) { true }
27
+ end
28
+ else
29
+ request_access_token
30
+ end
31
+ end
19
32
  end
20
33
  end
@@ -2,38 +2,17 @@ module Bootpay::Concern::Wallet
2
2
  extend ActiveSupport::Concern
3
3
 
4
4
  included do
5
- # 설정된 wallet 기준으로 결제를 진행한다
6
- def request_wallet_payment(user_id:, order_name:, price:, tax_free: 0, webhook_url: nil, content_type: nil, order_id:,
7
- items: [], user: {}, extra: {}, metadata: {}, sandbox: false)
8
- request(
9
- uri: 'wallet/payment',
10
- payload: {
11
- user_id: user_id,
12
- order_name: order_name,
13
- price: price,
14
- tax_free: tax_free,
15
- webhook_url: webhook_url,
16
- content_type: content_type,
17
- order_id: order_id,
18
- items: items,
19
- user: user,
20
- extra: extra,
21
- metadata: metadata,
22
- sandbox: sandbox
23
- }
24
- )
25
- end
26
-
27
5
  # 등록된 회원의 지갑 정보를 가져온다
28
6
  # Comment by GOSOMI
29
7
  # @date: 2025-02-13
30
- def user_wallets(user_id:, sandbox:)
8
+ def user_wallets(user_id:, widget_key: nil, sandbox: false)
31
9
  request(
32
10
  uri: 'wallet',
33
11
  method: :get,
34
12
  params: {
35
- user_id: user_id,
36
- sandbox: sandbox
13
+ user_id: user_id,
14
+ widget_key: widget_key,
15
+ sandbox: sandbox
37
16
  }
38
17
  )
39
18
  end
@@ -0,0 +1,18 @@
1
+ module BootpayStorage::Concern::Csv
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+
6
+
7
+ # csv 파일 업로드 - 마이그레이션 할 때 사용
8
+ # Comment by ehowlsla
9
+ # Date: 2025-07-17
10
+ def csv_file_upload(files:)
11
+ csv_upload(
12
+ uri: 'csvs',
13
+ files: files
14
+ )
15
+ end
16
+
17
+ end
18
+ end
@@ -14,5 +14,17 @@ module BootpayStorage::Concern::Image
14
14
  )
15
15
  end
16
16
 
17
+ # 업로드한 이미지를 URL 로 삭제한다.
18
+ # 업로드 응답이 URL 만 돌려주므로(내부 식별자 미노출) 삭제도 URL 을 키로 받는다.
19
+ # Comment by Claude (alfred)
20
+ # @date: 2026-07-28
21
+ def image_destroy(url:)
22
+ request(
23
+ method: :delete,
24
+ uri: 'images/by_url',
25
+ payload: { url: url }
26
+ )
27
+ end
28
+
17
29
  end
18
30
  end
@@ -80,6 +80,51 @@ module BootpayStorage::Concern::Rest
80
80
  )
81
81
  end
82
82
 
83
+ # csv Multipart 파일 전송 Method
84
+ # Comment by ehowlsla
85
+ # Date: 2025-07-17
86
+ def csv_upload(uri:, files:, headers: {}, params: nil)
87
+ # 이미지 데이터를 배열로 받음
88
+ files = files.each_with_index.map do |data, index|
89
+ filename = "csv_#{Time.now.to_i}_#{index}.csv"
90
+ HTTP::FormData::File.new(data, filename: filename)
91
+ end
92
+
93
+ # HTTP 요청
94
+ response = HTTP.headers(
95
+ {
96
+ Authorization: "Bearer #{@token}",
97
+ accept: 'application/json',
98
+ bootpay_api_version: @api_version,
99
+ bootpay_sdk_version: Bootpay::V2_VERSION,
100
+ bootpay_sdk_type: '300'
101
+ }.merge!(headers).compact
102
+ ).post(
103
+ [BootpayStorage::RestClient::API[@mode.to_sym], uri].join('/'),
104
+ form: { images: files },
105
+ params: params
106
+ )
107
+
108
+ # JSON 파싱 시도
109
+ parsed_response = begin
110
+ JSON.parse(response.body.to_s, symbolize_names: true)
111
+ rescue JSON::ParserError => e
112
+ { error: "응답 파싱 실패: #{e.message}", body: response.body.to_s }
113
+ end
114
+
115
+ # 응답 처리
116
+ BootpayStorage::Response.new(
117
+ response.status.to_i == 200,
118
+ parsed_response
119
+ )
120
+ rescue Exception => e
121
+ BootpayStorage::Response.new(
122
+ false,
123
+ message: "파일 업로드 실패: #{e.message}",
124
+ backtrace: e.backtrace.join("\n")
125
+ )
126
+ end
127
+
83
128
 
84
129
  # def upload(uri:, image_data:, image_name:, headers: {}, params: nil)
85
130
  #
@@ -3,8 +3,10 @@ module BootpayStorage
3
3
  require_relative 'concern/rest'
4
4
  require_relative 'concern/token'
5
5
  require_relative 'concern/image'
6
+ require_relative 'concern/csv'
6
7
  include Rest
7
8
  include Image
8
9
  include Token
10
+ include Csv
9
11
  end
10
12
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'active_support/all'
4
4
  require 'http'
5
+ require 'base64'
5
6
  require_relative 'response'
6
7
  require_relative '../version'
7
8
  require_relative 'concern'
@@ -19,12 +20,12 @@ module BootpayStore
19
20
 
20
21
  SDK_VERSION = '5.0.0'
21
22
 
22
- def initialize(server_key:, private_key:, mode: 'production')
23
- @server_key = server_key
24
- @private_key = private_key
25
- @mode = mode.presence || 'production'
26
- @token = nil
27
- @api_version = SDK_VERSION
23
+ def initialize(client_key: nil, private_key: nil, server_key: nil, secret_key: nil, mode: 'production')
24
+ @client_key = server_key.presence || client_key
25
+ @secret_key = private_key.presence || secret_key
26
+ @mode = mode.presence || 'production'
27
+ @token = nil
28
+ @api_version = SDK_VERSION
28
29
  raise ArgumentError, "개발환경 mode는 development, stage, production 중에서 선택이 가능합니다." if API[@mode.to_sym].blank?
29
30
  end
30
31
 
@@ -0,0 +1,67 @@
1
+ module BootpayStore::Concern::AlimtalkMessage
2
+ extend ActiveSupport::Concern
3
+
4
+ # 알림톡 발송내역·집계 — GET /v1/alimtalk/messages 계열
5
+ #
6
+ # **유료** 알림톡만 조회된다(무료 커머스 알림톡은 포함되지 않는다).
7
+ # 상태는 벤더 결과 동기화로 확정되므로 접수 직후에는 requested 로 보인다.
8
+ #
9
+ # @comment_by Claude (alfred)
10
+ # @date: 26-08-27
11
+ included do
12
+ # 발송내역 목록을 조회한다 (GET /v1/alimtalk/messages)
13
+ # status: requested·success·failed·canceled
14
+ # to: 수신번호(하이픈 무관, 정확 매칭) / ref_id: 발송 시 넘긴 멱등키
15
+ # ⚠️ 기간 기본값은 최근 30일이고 최대 조회 폭은 92일이다 — 초과분은 거부하지 않고 시작일을 당겨 잘라낸다.
16
+ # 실제 적용된 구간은 응답의 period 로 확인한다.
17
+ # 응답: { list: [...], count:, page:, per:, period: { from:, to: } }
18
+ def alimtalk_message_list(template_code: nil, status: nil, ref_id: nil, to: nil,
19
+ s_at: nil, e_at: nil, page: nil, limit: nil)
20
+ request(
21
+ uri: 'alimtalk/messages',
22
+ method: :get,
23
+ headers: { 'Bootpay-Role' => 'user' },
24
+ params: {
25
+ template_code: template_code,
26
+ status: status,
27
+ ref_id: ref_id,
28
+ to: to,
29
+ s_at: s_at,
30
+ e_at: e_at,
31
+ page: page,
32
+ limit: limit # 서버 기본 20, 최대 100
33
+ }.compact
34
+ )
35
+ end
36
+
37
+ # 기간 집계를 조회한다 (GET /v1/alimtalk/messages/stats)
38
+ # 일자별 집계 원장에서 읽으므로 응답이 빠르다.
39
+ # 응답: { period:, totals: { sent, success, failed, fallback, opted_out_hit, rejected, canceled, success_rate },
40
+ # daily: [...], billing: { billable_count, unit_price, fallback_count, ..., amount } }
41
+ # ⚠️ billing.unit_price_source 가 'default' 면 **잠정 단가**다(확정 청구액이 아니다).
42
+ # ⚠️ billable_count 는 성공 − 폴백이다 — 폴백분은 LMS 단가로 따로 계산된다.
43
+ def alimtalk_message_stats(s_at: nil, e_at: nil)
44
+ request(
45
+ uri: 'alimtalk/messages/stats',
46
+ method: :get,
47
+ headers: { 'Bootpay-Role' => 'user' },
48
+ params: {
49
+ s_at: s_at,
50
+ e_at: e_at
51
+ }.compact
52
+ )
53
+ end
54
+
55
+ # 단건 발송 결과를 조회한다 (GET /v1/alimtalk/messages/:receipt_id)
56
+ # 실패 사유는 error_code·error_message 에 담긴다.
57
+ # fallback_type 은 폴백이 꺼진 건이면 null, 켜진 건이면 LMS 다.
58
+ # 다른 프로젝트의 건이거나 없으면 404(3025).
59
+ def alimtalk_message_detail(receipt_id:)
60
+ request(
61
+ uri: "alimtalk/messages/#{receipt_id}",
62
+ method: :get,
63
+ headers: { 'Bootpay-Role' => 'user' }
64
+ )
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,63 @@
1
+ module BootpayStore::Concern::AlimtalkOfficial
2
+ extend ActiveSupport::Concern
3
+
4
+ # 부트페이 공식 알림톡 템플릿 카탈로그 — GET/POST /v1/alimtalk/official 계열
5
+ #
6
+ # 부트페이가 미리 카카오 승인을 받아 둔 템플릿이라, 그룹키가 등록된 채널이면 **검수 없이 즉시 발송**된다.
7
+ # `alimtalk_sender_create` 로 채널을 등록하면 그룹 등록이 함께 끝나므로 따로 채택할 것이 없다.
8
+ #
9
+ # 전부 조회 계열이라 부작용이 없다(자체 DB 만 본다).
10
+ # @date: 26-08-27 채택(alimtalk_official_adopt) 제거 — 서버에서도 엔드포인트를 비활성화했다.
11
+ #
12
+ # @comment_by Claude (alfred)
13
+ # @date: 26-08-27
14
+ included do
15
+ # 공식 템플릿을 검색한다 (GET /v1/alimtalk/official)
16
+ # keyword 는 본문·이름·분류를 부분일치(대소문자 무시)로 훑는다.
17
+ # msg_type 은 BA(기본형)·EX(부가정보형)만 존재한다 — 그룹 템플릿이라 AD/MI 는 쓸 수 없다.
18
+ # ksp_id 를 주면 그 채널의 변수 예문 사전으로 variable_examples 를 채워 준다(표시용).
19
+ # 응답: { list: [...], count:, page:, per:, categories: [...] }
20
+ def alimtalk_official_list(keyword: nil, category: nil, msg_type: nil, page: nil, per: nil, ksp_id: nil)
21
+ request(
22
+ uri: 'alimtalk/official',
23
+ method: :get,
24
+ headers: { 'Bootpay-Role' => 'user' },
25
+ params: {
26
+ q: keyword, # 서버는 q 를 먼저 보고 없으면 keyword 를 본다 — 정본 키인 q 로 보낸다
27
+ category: category,
28
+ msg_type: msg_type,
29
+ page: page,
30
+ per: per, # 서버 기본 20, 최대 100 으로 clamp
31
+ ksp_id: ksp_id
32
+ }.compact
33
+ )
34
+ end
35
+
36
+ # 보내려는 문구로 공식 템플릿을 추천받는다 (POST /v1/alimtalk/official/recommend)
37
+ # 유사도 score(0~1) 내림차순으로 돌려준다.
38
+ def alimtalk_official_recommend(text:, category: nil, limit: nil, ksp_id: nil)
39
+ request(
40
+ uri: 'alimtalk/official/recommend',
41
+ method: :post,
42
+ headers: { 'Bootpay-Role' => 'user' },
43
+ payload: {
44
+ text: text,
45
+ category: category,
46
+ limit: limit, # 서버 기본 5
47
+ ksp_id: ksp_id
48
+ }.compact
49
+ )
50
+ end
51
+
52
+ # 공식 템플릿 상세를 조회한다 (GET /v1/alimtalk/official/:code)
53
+ # code 는 서버 채번 코드(슬래시를 포함하지 않는다). 없거나 미노출이면 404(3015).
54
+ def alimtalk_official_detail(code:, ksp_id: nil)
55
+ request(
56
+ uri: "alimtalk/official/#{code}",
57
+ method: :get,
58
+ headers: { 'Bootpay-Role' => 'user' },
59
+ params: { ksp_id: ksp_id }.compact
60
+ )
61
+ end
62
+ end
63
+ end