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.
- checksums.yaml +4 -4
- data/.gitignore +5 -1
- data/CHANGELOG.md +10 -0
- data/Gemfile +1 -1
- data/bootpay-backend-ruby.gemspec +9 -1
- data/lib/bootpay/bootpay-rest-client.rb +7 -2
- data/lib/bootpay/concern/authenticate.rb +19 -17
- data/lib/bootpay/concern/cash_receipt.rb +1 -1
- data/lib/bootpay/concern/payment.rb +2 -1
- data/lib/bootpay/concern/rest.rb +19 -1
- data/lib/bootpay/concern/sdk.rb +16 -0
- data/lib/bootpay/concern/subscription.rb +15 -28
- data/lib/bootpay/concern/token.rb +13 -0
- data/lib/bootpay/concern/wallet.rb +4 -25
- data/lib/bootpay_storage/concern/csv.rb +18 -0
- data/lib/bootpay_storage/concern/image.rb +12 -0
- data/lib/bootpay_storage/concern/rest.rb +45 -0
- data/lib/bootpay_storage/concern.rb +2 -0
- data/lib/bootpay_store/bootpay-store-rest-client.rb +7 -6
- data/lib/bootpay_store/concern/alimtalk_message.rb +67 -0
- data/lib/bootpay_store/concern/alimtalk_official.rb +63 -0
- data/lib/bootpay_store/concern/alimtalk_optout.rb +72 -0
- data/lib/bootpay_store/concern/alimtalk_send.rb +83 -0
- data/lib/bootpay_store/concern/alimtalk_sender.rb +105 -0
- data/lib/bootpay_store/concern/alimtalk_template.rb +215 -0
- data/lib/bootpay_store/concern/alimtalk_webhook.rb +84 -0
- data/lib/bootpay_store/concern/invoice.rb +102 -0
- data/lib/bootpay_store/concern/mall_setting.rb +385 -0
- data/lib/bootpay_store/concern/order.rb +74 -0
- data/lib/bootpay_store/concern/order_subscription.rb +391 -0
- data/lib/bootpay_store/concern/payment.rb +119 -0
- data/lib/bootpay_store/concern/product.rb +175 -0
- data/lib/bootpay_store/concern/rest.rb +91 -1
- data/lib/bootpay_store/concern/store.rb +31 -0
- data/lib/bootpay_store/concern/supervisor.rb +211 -0
- data/lib/bootpay_store/concern/token.rb +4 -4
- data/lib/bootpay_store/concern/user.rb +325 -0
- data/lib/bootpay_store/concern/user_group.rb +221 -0
- data/lib/bootpay_store/concern/webhook.rb +17 -0
- data/lib/bootpay_store/concern.rb +38 -28
- data/lib/version.rb +1 -1
- metadata +38 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7dabe4bba771d7710f61b2f20a843e1e452028a723962e04de6dcdb694e83342
|
|
4
|
+
data.tar.gz: d19e63191fcfae0c2bad8ffc4c0f7835365a8c87896a90266fa5baae795e245c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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
|
@@ -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
|
|
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.
|
|
22
|
+
SDK_VERSION = '5.3.0'
|
|
21
23
|
|
|
22
|
-
def initialize(application_id
|
|
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:
|
|
22
|
-
uri:
|
|
23
|
-
payload:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
|
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
|
|
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,
|
data/lib/bootpay/concern/rest.rb
CHANGED
|
@@ -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:
|
|
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
|
data/lib/bootpay/concern/sdk.rb
CHANGED
|
@@ -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
|
|
44
|
-
|
|
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:
|
|
36
|
-
|
|
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
|
#
|
|
@@ -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(
|
|
23
|
-
@
|
|
24
|
-
@
|
|
25
|
-
@mode
|
|
26
|
-
@token
|
|
27
|
-
@api_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
|