bootpay-backend 2.2.0__tar.gz → 2.5.0__tar.gz
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.
- {bootpay_backend-2.2.0 → bootpay_backend-2.5.0}/PKG-INFO +46 -6
- {bootpay_backend-2.2.0 → bootpay_backend-2.5.0}/README.md +34 -4
- {bootpay_backend-2.2.0 → bootpay_backend-2.5.0}/bootpay_backend/commerce/__init__.py +69 -2
- {bootpay_backend-2.2.0 → bootpay_backend-2.5.0}/bootpay_backend/commerce/commerce_resource.py +59 -29
- {bootpay_backend-2.2.0 → bootpay_backend-2.5.0}/bootpay_backend/commerce/modules/__init__.py +17 -1
- bootpay_backend-2.5.0/bootpay_backend/commerce/modules/cart.py +29 -0
- bootpay_backend-2.5.0/bootpay_backend/commerce/modules/category.py +92 -0
- bootpay_backend-2.5.0/bootpay_backend/commerce/modules/coupon.py +51 -0
- bootpay_backend-2.5.0/bootpay_backend/commerce/modules/invoice.py +106 -0
- bootpay_backend-2.5.0/bootpay_backend/commerce/modules/mall_setting.py +63 -0
- {bootpay_backend-2.2.0 → bootpay_backend-2.5.0}/bootpay_backend/commerce/modules/order.py +5 -0
- bootpay_backend-2.5.0/bootpay_backend/commerce/modules/order_cancel.py +139 -0
- bootpay_backend-2.5.0/bootpay_backend/commerce/modules/order_subscription.py +380 -0
- bootpay_backend-2.5.0/bootpay_backend/commerce/modules/order_subscription_adjustment.py +95 -0
- {bootpay_backend-2.2.0 → bootpay_backend-2.5.0}/bootpay_backend/commerce/modules/order_subscription_bill.py +32 -16
- bootpay_backend-2.5.0/bootpay_backend/commerce/modules/order_subscription_request.py +131 -0
- bootpay_backend-2.5.0/bootpay_backend/commerce/modules/point.py +43 -0
- bootpay_backend-2.5.0/bootpay_backend/commerce/modules/product.py +217 -0
- bootpay_backend-2.5.0/bootpay_backend/commerce/modules/store.py +45 -0
- bootpay_backend-2.5.0/bootpay_backend/commerce/modules/user.py +232 -0
- {bootpay_backend-2.2.0 → bootpay_backend-2.5.0}/bootpay_backend/commerce/modules/user_group.py +55 -9
- bootpay_backend-2.5.0/bootpay_backend/commerce/modules/webhook.py +34 -0
- bootpay_backend-2.5.0/bootpay_backend/commerce/types/__init__.py +1445 -0
- {bootpay_backend-2.2.0 → bootpay_backend-2.5.0}/bootpay_backend/rest_client.py +164 -29
- {bootpay_backend-2.2.0 → bootpay_backend-2.5.0}/bootpay_backend.egg-info/PKG-INFO +47 -7
- bootpay_backend-2.5.0/bootpay_backend.egg-info/SOURCES.txt +58 -0
- {bootpay_backend-2.2.0 → bootpay_backend-2.5.0}/bootpay_backend.egg-info/top_level.txt +1 -0
- {bootpay_backend-2.2.0 → bootpay_backend-2.5.0}/setup.py +1 -1
- bootpay_backend-2.5.0/tests/__init__.py +0 -0
- bootpay_backend-2.5.0/tests/commerce/__init__.py +0 -0
- bootpay_backend-2.5.0/tests/commerce/test_auth_contract.py +86 -0
- bootpay_backend-2.5.0/tests/commerce/test_mall_setting.py +20 -0
- bootpay_backend-2.5.0/tests/commerce/test_order.py +61 -0
- bootpay_backend-2.5.0/tests/commerce/test_order_subscription_charge.py +50 -0
- bootpay_backend-2.5.0/tests/commerce/test_product.py +75 -0
- bootpay_backend-2.5.0/tests/commerce/test_product_mall.py +27 -0
- bootpay_backend-2.5.0/tests/commerce/test_store.py +20 -0
- bootpay_backend-2.5.0/tests/commerce/test_token.py +65 -0
- bootpay_backend-2.5.0/tests/commerce/test_user.py +81 -0
- bootpay_backend-2.5.0/tests/commerce/test_user_mall.py +44 -0
- bootpay_backend-2.5.0/tests/commerce/test_webhook.py +20 -0
- bootpay_backend-2.5.0/tests/commerce/test_wire_format.py +563 -0
- bootpay_backend-2.5.0/tests/conftest.py +158 -0
- bootpay_backend-2.5.0/tests/pg/__init__.py +0 -0
- bootpay_backend-2.5.0/tests/pg/test_auth.py +66 -0
- bootpay_backend-2.5.0/tests/pg/test_billing.py +91 -0
- bootpay_backend-2.5.0/tests/pg/test_cash_receipt.py +59 -0
- bootpay_backend-2.5.0/tests/pg/test_legacy_compatibility.py +149 -0
- bootpay_backend-2.5.0/tests/pg/test_payment.py +45 -0
- bootpay_backend-2.5.0/tests/pg/test_sequential_billing_key.py +29 -0
- bootpay_backend-2.5.0/tests/pg/test_token.py +62 -0
- bootpay_backend-2.5.0/tests/pg/test_wallet.py +33 -0
- bootpay_backend-2.2.0/bootpay_backend/commerce/modules/invoice.py +0 -61
- bootpay_backend-2.2.0/bootpay_backend/commerce/modules/order_cancel.py +0 -77
- bootpay_backend-2.2.0/bootpay_backend/commerce/modules/order_subscription.py +0 -132
- bootpay_backend-2.2.0/bootpay_backend/commerce/modules/order_subscription_adjustment.py +0 -52
- bootpay_backend-2.2.0/bootpay_backend/commerce/modules/product.py +0 -93
- bootpay_backend-2.2.0/bootpay_backend/commerce/modules/user.py +0 -114
- bootpay_backend-2.2.0/bootpay_backend/commerce/types/__init__.py +0 -752
- bootpay_backend-2.2.0/bootpay_backend.egg-info/SOURCES.txt +0 -26
- {bootpay_backend-2.2.0 → bootpay_backend-2.5.0}/LICENSE +0 -0
- {bootpay_backend-2.2.0 → bootpay_backend-2.5.0}/bootpay_backend/__init__.py +0 -0
- {bootpay_backend-2.2.0 → bootpay_backend-2.5.0}/bootpay_backend/bankcode.py +0 -0
- {bootpay_backend-2.2.0 → bootpay_backend-2.5.0}/bootpay_backend.egg-info/dependency_links.txt +0 -0
- {bootpay_backend-2.2.0 → bootpay_backend-2.5.0}/bootpay_backend.egg-info/not-zip-safe +0 -0
- {bootpay_backend-2.2.0 → bootpay_backend-2.5.0}/bootpay_backend.egg-info/requires.txt +0 -0
- {bootpay_backend-2.2.0 → bootpay_backend-2.5.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: bootpay_backend
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5.0
|
|
4
4
|
Summary: bootpay server side plugin for python
|
|
5
5
|
Home-page: https://github.com/bootpay/backend-python/tree/2-x-development
|
|
6
6
|
Author: bootpay <bootpay.co.kr@gmail.com>
|
|
@@ -27,6 +27,16 @@ Description-Content-Type: text/markdown
|
|
|
27
27
|
License-File: LICENSE
|
|
28
28
|
Requires-Dist: requests>=2.20; python_version >= "3.0"
|
|
29
29
|
Requires-Dist: requests[security]>=2.20; python_version < "3.0"
|
|
30
|
+
Dynamic: author
|
|
31
|
+
Dynamic: classifier
|
|
32
|
+
Dynamic: description
|
|
33
|
+
Dynamic: description-content-type
|
|
34
|
+
Dynamic: home-page
|
|
35
|
+
Dynamic: license
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
Dynamic: requires-dist
|
|
38
|
+
Dynamic: requires-python
|
|
39
|
+
Dynamic: summary
|
|
30
40
|
|
|
31
41
|
|
|
32
42
|
## Bootpay Python Server Side Library
|
|
@@ -70,13 +80,42 @@ Python 언어로 작성된 어플리케이션, 프레임워크 등에서 사용
|
|
|
70
80
|
pip install bootpay-backend
|
|
71
81
|
```
|
|
72
82
|
|
|
83
|
+
|
|
84
|
+
## 환경변수 설정
|
|
85
|
+
|
|
86
|
+
예제와 테스트는 각 SDK 루트의 `.env` 파일을 우선 읽습니다. 먼저 `.env.example`을 복사한 뒤 필요한 키만 변경하세요. `.env`는 gitignore 처리되어 커밋되지 않습니다.
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
cp .env.example .env
|
|
90
|
+
# BOOTPAY_ENV=production 또는 development
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
주요 변수:
|
|
94
|
+
|
|
95
|
+
```env
|
|
96
|
+
BOOTPAY_ENV=production
|
|
97
|
+
BOOTPAY_PG_CLIENT_KEY_PROD=...
|
|
98
|
+
BOOTPAY_PG_SECRET_KEY_PROD=...
|
|
99
|
+
BOOTPAY_PG_CLIENT_KEY_DEV=...
|
|
100
|
+
BOOTPAY_PG_SECRET_KEY_DEV=...
|
|
101
|
+
BOOTPAY_COMMERCE_CLIENT_KEY_PROD=...
|
|
102
|
+
BOOTPAY_COMMERCE_SECRET_KEY_PROD=...
|
|
103
|
+
BOOTPAY_COMMERCE_CLIENT_KEY_DEV=...
|
|
104
|
+
BOOTPAY_COMMERCE_SECRET_KEY_DEV=...
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
변수가 없으면 SDK 테스트용 기본값(NodeJS 기준 ck/sk)으로 fallback 합니다.
|
|
108
|
+
|
|
73
109
|
# 사용하기
|
|
74
110
|
|
|
111
|
+
> 권장 인증 방식은 `client_key/secret_key`입니다. 기존 positional `application_id/private_key` 방식도 계속 지원됩니다.
|
|
112
|
+
|
|
75
113
|
```python
|
|
76
114
|
|
|
115
|
+
import os
|
|
77
116
|
from bootpay_backend import BootpayBackend
|
|
78
117
|
|
|
79
|
-
bootpay = BootpayBackend('
|
|
118
|
+
bootpay = BootpayBackend(client_key=os.getenv('BOOTPAY_PG_CLIENT_KEY_PROD'), secret_key=os.getenv('BOOTPAY_PG_SECRET_KEY_PROD'))
|
|
80
119
|
|
|
81
120
|
token = bootpay.get_access_token()
|
|
82
121
|
if 'error_code' not in token:
|
|
@@ -91,7 +130,7 @@ if 'error_code' not in token:
|
|
|
91
130
|
발급된 토큰은 30분간 유효하며, 최초 발급일로부터 30분이 지날 경우 토큰 발급 함수를 재호출 해주셔야 합니다.
|
|
92
131
|
|
|
93
132
|
```python
|
|
94
|
-
bootpay = BootpayBackend('
|
|
133
|
+
bootpay = BootpayBackend(client_key=os.getenv('BOOTPAY_PG_CLIENT_KEY_PROD'), secret_key=os.getenv('BOOTPAY_PG_SECRET_KEY_PROD'))
|
|
95
134
|
|
|
96
135
|
token = bootpay.get_access_token()
|
|
97
136
|
if 'error_code' not in token:
|
|
@@ -405,11 +444,12 @@ if 'error_code' not in token:
|
|
|
405
444
|
|
|
406
445
|
## Documentation
|
|
407
446
|
|
|
408
|
-
[부트페이
|
|
447
|
+
- [부트페이 결제 개발문서](https://developers.bootpay.ai)
|
|
448
|
+
- [부트페이 커머스 개발문서](https://commerce.bootpay.ai)
|
|
409
449
|
|
|
410
450
|
## 기술문의
|
|
411
451
|
|
|
412
|
-
[부트페이 홈페이지](https://www.bootpay.
|
|
452
|
+
[부트페이 홈페이지](https://www.bootpay.ai)에서 문의해주세요!
|
|
413
453
|
|
|
414
454
|
## License
|
|
415
455
|
|
|
@@ -40,13 +40,42 @@ Python 언어로 작성된 어플리케이션, 프레임워크 등에서 사용
|
|
|
40
40
|
pip install bootpay-backend
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
|
|
44
|
+
## 환경변수 설정
|
|
45
|
+
|
|
46
|
+
예제와 테스트는 각 SDK 루트의 `.env` 파일을 우선 읽습니다. 먼저 `.env.example`을 복사한 뒤 필요한 키만 변경하세요. `.env`는 gitignore 처리되어 커밋되지 않습니다.
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
cp .env.example .env
|
|
50
|
+
# BOOTPAY_ENV=production 또는 development
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
주요 변수:
|
|
54
|
+
|
|
55
|
+
```env
|
|
56
|
+
BOOTPAY_ENV=production
|
|
57
|
+
BOOTPAY_PG_CLIENT_KEY_PROD=...
|
|
58
|
+
BOOTPAY_PG_SECRET_KEY_PROD=...
|
|
59
|
+
BOOTPAY_PG_CLIENT_KEY_DEV=...
|
|
60
|
+
BOOTPAY_PG_SECRET_KEY_DEV=...
|
|
61
|
+
BOOTPAY_COMMERCE_CLIENT_KEY_PROD=...
|
|
62
|
+
BOOTPAY_COMMERCE_SECRET_KEY_PROD=...
|
|
63
|
+
BOOTPAY_COMMERCE_CLIENT_KEY_DEV=...
|
|
64
|
+
BOOTPAY_COMMERCE_SECRET_KEY_DEV=...
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
변수가 없으면 SDK 테스트용 기본값(NodeJS 기준 ck/sk)으로 fallback 합니다.
|
|
68
|
+
|
|
43
69
|
# 사용하기
|
|
44
70
|
|
|
71
|
+
> 권장 인증 방식은 `client_key/secret_key`입니다. 기존 positional `application_id/private_key` 방식도 계속 지원됩니다.
|
|
72
|
+
|
|
45
73
|
```python
|
|
46
74
|
|
|
75
|
+
import os
|
|
47
76
|
from bootpay_backend import BootpayBackend
|
|
48
77
|
|
|
49
|
-
bootpay = BootpayBackend('
|
|
78
|
+
bootpay = BootpayBackend(client_key=os.getenv('BOOTPAY_PG_CLIENT_KEY_PROD'), secret_key=os.getenv('BOOTPAY_PG_SECRET_KEY_PROD'))
|
|
50
79
|
|
|
51
80
|
token = bootpay.get_access_token()
|
|
52
81
|
if 'error_code' not in token:
|
|
@@ -61,7 +90,7 @@ if 'error_code' not in token:
|
|
|
61
90
|
발급된 토큰은 30분간 유효하며, 최초 발급일로부터 30분이 지날 경우 토큰 발급 함수를 재호출 해주셔야 합니다.
|
|
62
91
|
|
|
63
92
|
```python
|
|
64
|
-
bootpay = BootpayBackend('
|
|
93
|
+
bootpay = BootpayBackend(client_key=os.getenv('BOOTPAY_PG_CLIENT_KEY_PROD'), secret_key=os.getenv('BOOTPAY_PG_SECRET_KEY_PROD'))
|
|
65
94
|
|
|
66
95
|
token = bootpay.get_access_token()
|
|
67
96
|
if 'error_code' not in token:
|
|
@@ -375,11 +404,12 @@ if 'error_code' not in token:
|
|
|
375
404
|
|
|
376
405
|
## Documentation
|
|
377
406
|
|
|
378
|
-
[부트페이
|
|
407
|
+
- [부트페이 결제 개발문서](https://developers.bootpay.ai)
|
|
408
|
+
- [부트페이 커머스 개발문서](https://commerce.bootpay.ai)
|
|
379
409
|
|
|
380
410
|
## 기술문의
|
|
381
411
|
|
|
382
|
-
[부트페이 홈페이지](https://www.bootpay.
|
|
412
|
+
[부트페이 홈페이지](https://www.bootpay.ai)에서 문의해주세요!
|
|
383
413
|
|
|
384
414
|
## License
|
|
385
415
|
|
|
@@ -9,7 +9,15 @@ from .modules import (
|
|
|
9
9
|
OrderCancelModule,
|
|
10
10
|
OrderSubscriptionModule,
|
|
11
11
|
OrderSubscriptionBillModule,
|
|
12
|
-
OrderSubscriptionAdjustmentModule
|
|
12
|
+
OrderSubscriptionAdjustmentModule,
|
|
13
|
+
StoreModule,
|
|
14
|
+
CategoryModule,
|
|
15
|
+
CouponModule,
|
|
16
|
+
PointModule,
|
|
17
|
+
CartModule,
|
|
18
|
+
OrderSubscriptionRequestModule,
|
|
19
|
+
MallSettingModule,
|
|
20
|
+
WebhookModule
|
|
13
21
|
)
|
|
14
22
|
from .types import *
|
|
15
23
|
|
|
@@ -49,7 +57,7 @@ class BootpayCommerce(BootpayCommerceResource):
|
|
|
49
57
|
:param mode: 환경 ('development', 'stage', 'production')
|
|
50
58
|
"""
|
|
51
59
|
super().__init__()
|
|
52
|
-
if client_key
|
|
60
|
+
if client_key is not None or secret_key is not None:
|
|
53
61
|
self.set_configuration(client_key, secret_key, mode)
|
|
54
62
|
self._init_modules()
|
|
55
63
|
|
|
@@ -64,6 +72,14 @@ class BootpayCommerce(BootpayCommerceResource):
|
|
|
64
72
|
self.order_subscription = OrderSubscriptionModule(self)
|
|
65
73
|
self.order_subscription_bill = OrderSubscriptionBillModule(self)
|
|
66
74
|
self.order_subscription_adjustment = OrderSubscriptionAdjustmentModule(self)
|
|
75
|
+
self.store = StoreModule(self)
|
|
76
|
+
self.category = CategoryModule(self)
|
|
77
|
+
self.coupon = CouponModule(self)
|
|
78
|
+
self.point = PointModule(self)
|
|
79
|
+
self.cart = CartModule(self)
|
|
80
|
+
self.order_subscription_request = OrderSubscriptionRequestModule(self)
|
|
81
|
+
self.mall_setting = MallSettingModule(self)
|
|
82
|
+
self.webhook = WebhookModule(self)
|
|
67
83
|
|
|
68
84
|
def get_access_token(self):
|
|
69
85
|
"""
|
|
@@ -188,6 +204,14 @@ __all__ = [
|
|
|
188
204
|
'OrderSubscriptionModule',
|
|
189
205
|
'OrderSubscriptionBillModule',
|
|
190
206
|
'OrderSubscriptionAdjustmentModule',
|
|
207
|
+
'StoreModule',
|
|
208
|
+
'CategoryModule',
|
|
209
|
+
'CouponModule',
|
|
210
|
+
'PointModule',
|
|
211
|
+
'CartModule',
|
|
212
|
+
'OrderSubscriptionRequestModule',
|
|
213
|
+
'MallSettingModule',
|
|
214
|
+
'WebhookModule',
|
|
191
215
|
# Types
|
|
192
216
|
'ListParams',
|
|
193
217
|
'CommerceAddress',
|
|
@@ -242,4 +266,47 @@ __all__ = [
|
|
|
242
266
|
'SUBSCRIPTION_ADJUSTMENT_TYPE_PERIOD_DISCOUNT',
|
|
243
267
|
'CommerceOrderSubscriptionAdjustment',
|
|
244
268
|
'OrderSubscriptionAdjustmentUpdateParams',
|
|
269
|
+
'SupervisorOrderSubscriptionApproveParams',
|
|
270
|
+
'SupervisorOrderSubscriptionRejectParams',
|
|
271
|
+
'SupervisorOrderSubscriptionTerminateParams',
|
|
272
|
+
'SupervisorOrderSubscriptionPauseParams',
|
|
273
|
+
'SupervisorOrderSubscriptionResumeParams',
|
|
274
|
+
'CommerceCategory',
|
|
275
|
+
'CategoryCreateParams',
|
|
276
|
+
'CategoryUpdateParams',
|
|
277
|
+
'CommerceCoupon',
|
|
278
|
+
'CouponListParams',
|
|
279
|
+
'CouponDownloadParams',
|
|
280
|
+
'PointBalance',
|
|
281
|
+
'PointTransaction',
|
|
282
|
+
'PointTransactionsResponse',
|
|
283
|
+
'PointTransactionsParams',
|
|
284
|
+
'CartItemPayload',
|
|
285
|
+
'ShippingAddressPayload',
|
|
286
|
+
'OrderPreviewParams',
|
|
287
|
+
'DeliveryGroupItem',
|
|
288
|
+
'DeliveryGroup',
|
|
289
|
+
'AppliedCouponSnapshot',
|
|
290
|
+
'OrderPreviewSummary',
|
|
291
|
+
'OrderPreviewUnavailableItem',
|
|
292
|
+
'OrderPreviewResponse',
|
|
293
|
+
'OrderSubscriptionRequest',
|
|
294
|
+
'OrderSubscriptionRequestListParams',
|
|
295
|
+
'OrderSubscriptionRequestUpdateParams',
|
|
296
|
+
'InvoiceListResponse',
|
|
297
|
+
'OrderCancelWithdrawParams',
|
|
298
|
+
'OrderSubscriptionPurchaseParams',
|
|
299
|
+
'OrderSubscriptionTransferParams',
|
|
300
|
+
'SupervisorOrderSubscriptionChargeParams',
|
|
301
|
+
'SupervisorOrderSubscriptionChargeRevokeParams',
|
|
302
|
+
'OrderSubscriptionChargeResponse',
|
|
303
|
+
'OrderSubscriptionChargeRevokeResponse',
|
|
304
|
+
'MallProductListParams',
|
|
305
|
+
'MallUserLoginParams',
|
|
306
|
+
'MallUserJoinParams',
|
|
307
|
+
'MallUserJoinCheckType',
|
|
308
|
+
'MallUserSessionResponse',
|
|
309
|
+
'MallSettingUpdateParams',
|
|
310
|
+
'CommerceMallSetting',
|
|
311
|
+
'SendTestWebhookParams',
|
|
245
312
|
]
|
{bootpay_backend-2.2.0 → bootpay_backend-2.5.0}/bootpay_backend/commerce/commerce_resource.py
RENAMED
|
@@ -53,19 +53,28 @@ class BootpayCommerceResource:
|
|
|
53
53
|
|
|
54
54
|
def _get_basic_auth_header(self) -> str:
|
|
55
55
|
"""Basic Auth 헤더 생성"""
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
has_client_key = bool(self.client_key)
|
|
57
|
+
has_secret_key = bool(self.secret_key)
|
|
58
|
+
if not has_client_key and not has_secret_key:
|
|
59
|
+
raise ValueError('Commerce API에는 client_key와 secret_key가 필요합니다.')
|
|
60
|
+
if has_client_key != has_secret_key:
|
|
61
|
+
missing = 'secret_key' if has_client_key else 'client_key'
|
|
62
|
+
raise ValueError(f'{missing} 값이 비어있습니다. client_key와 secret_key는 함께 지정해야 합니다.')
|
|
63
|
+
credentials = f'{self.client_key}:{self.secret_key}'
|
|
64
|
+
encoded = base64.b64encode(credentials.encode()).decode()
|
|
65
|
+
return f'Basic {encoded}'
|
|
61
66
|
|
|
62
67
|
def _entrypoints(self, url: str) -> str:
|
|
63
68
|
"""엔트리포인트 URL 생성"""
|
|
64
69
|
return '/'.join([self.API_ENTRYPOINTS[self.mode], url])
|
|
65
70
|
|
|
66
|
-
def _get_headers(self, include_auth: bool = True) -> Dict[str, str]:
|
|
67
|
-
"""
|
|
68
|
-
|
|
71
|
+
def _get_headers(self, include_auth: bool = True, headers: Optional[Dict[str, str]] = None) -> Dict[str, str]:
|
|
72
|
+
"""
|
|
73
|
+
공통 헤더 생성
|
|
74
|
+
요청별로 지정된 헤더(headers)가 공통값을 덮어쓴다 —
|
|
75
|
+
supervisor 전용 endpoint 의 BOOTPAY-ROLE 을 공통 계층이 덮어쓰지 않기 위함.
|
|
76
|
+
"""
|
|
77
|
+
merged = {
|
|
69
78
|
'Content-Type': 'application/json',
|
|
70
79
|
'Accept': 'application/json',
|
|
71
80
|
'Accept-Charset': 'utf-8',
|
|
@@ -74,25 +83,29 @@ class BootpayCommerceResource:
|
|
|
74
83
|
'BOOTPAY-SDK-TYPE': '302',
|
|
75
84
|
'BOOTPAY-ROLE': self._role or 'user'
|
|
76
85
|
}
|
|
77
|
-
if include_auth
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
86
|
+
if include_auth:
|
|
87
|
+
basic_auth = self._get_basic_auth_header()
|
|
88
|
+
if basic_auth:
|
|
89
|
+
merged['Authorization'] = basic_auth
|
|
90
|
+
if headers:
|
|
91
|
+
merged.update(headers)
|
|
92
|
+
return merged
|
|
93
|
+
|
|
94
|
+
def get(self, url: str, params: Optional[Dict] = None, headers: Optional[Dict[str, str]] = None):
|
|
82
95
|
"""GET 요청"""
|
|
83
96
|
response = requests.get(
|
|
84
97
|
self._entrypoints(url),
|
|
85
|
-
headers=self._get_headers(),
|
|
98
|
+
headers=self._get_headers(headers=headers),
|
|
86
99
|
params=params,
|
|
87
100
|
timeout=self.timeout
|
|
88
101
|
)
|
|
89
102
|
return response.json()
|
|
90
103
|
|
|
91
|
-
def post(self, url: str, data: Optional[Dict] = None):
|
|
104
|
+
def post(self, url: str, data: Optional[Dict] = None, headers: Optional[Dict[str, str]] = None):
|
|
92
105
|
"""POST 요청"""
|
|
93
106
|
response = requests.post(
|
|
94
107
|
self._entrypoints(url),
|
|
95
|
-
headers=self._get_headers(),
|
|
108
|
+
headers=self._get_headers(headers=headers),
|
|
96
109
|
json=data,
|
|
97
110
|
timeout=self.timeout
|
|
98
111
|
)
|
|
@@ -110,14 +123,18 @@ class BootpayCommerceResource:
|
|
|
110
123
|
)
|
|
111
124
|
return response.json()
|
|
112
125
|
|
|
113
|
-
def post_multipart(self, url: str, data: Dict, image_paths: Optional[List[str]] = None
|
|
126
|
+
def post_multipart(self, url: str, data: Dict, image_paths: Optional[List[str]] = None,
|
|
127
|
+
headers: Optional[Dict[str, str]] = None):
|
|
114
128
|
"""
|
|
115
129
|
Multipart/form-data POST 요청 (이미지 업로드 포함)
|
|
130
|
+
⚠️ Content-Type 은 requests 가 생성한 값(boundary 포함)을 그대로 사용한다.
|
|
131
|
+
직접 지정하면 boundary 가 사라져 본문이 서버에서 null 로 파싱된다.
|
|
116
132
|
:param url: 요청 URL
|
|
117
133
|
:param data: 폼 데이터
|
|
118
134
|
:param image_paths: 이미지 파일 경로 배열
|
|
135
|
+
:param headers: 요청별 추가 헤더 (공통값을 덮어쓴다)
|
|
119
136
|
"""
|
|
120
|
-
|
|
137
|
+
request_headers = {
|
|
121
138
|
'Accept': 'application/json',
|
|
122
139
|
'Accept-Charset': 'utf-8',
|
|
123
140
|
'BOOTPAY-SDK-VERSION': self.SDK_VERSION,
|
|
@@ -125,29 +142,37 @@ class BootpayCommerceResource:
|
|
|
125
142
|
'BOOTPAY-SDK-TYPE': '302',
|
|
126
143
|
'BOOTPAY-ROLE': self._role or 'user'
|
|
127
144
|
}
|
|
128
|
-
|
|
129
|
-
|
|
145
|
+
basic_auth = self._get_basic_auth_header()
|
|
146
|
+
if basic_auth:
|
|
147
|
+
request_headers['Authorization'] = basic_auth
|
|
148
|
+
if headers:
|
|
149
|
+
request_headers.update(headers)
|
|
130
150
|
|
|
131
151
|
# 폼 데이터 준비
|
|
152
|
+
# ⚠️ bool 은 소문자 'true'/'false' 로 보낸다 — str(True) 의 'True'/'False' 는
|
|
153
|
+
# Rails 캐스팅 대상이 아니라서 "False" 가 true 로 해석되는 실위험이 있다.
|
|
132
154
|
form_data = {}
|
|
133
155
|
for key, value in data.items():
|
|
134
156
|
if value is not None:
|
|
135
|
-
if isinstance(value,
|
|
157
|
+
if isinstance(value, bool):
|
|
158
|
+
form_data[key] = 'true' if value else 'false'
|
|
159
|
+
elif isinstance(value, (dict, list)):
|
|
136
160
|
form_data[key] = json.dumps(value)
|
|
137
161
|
else:
|
|
138
162
|
form_data[key] = str(value)
|
|
139
163
|
|
|
140
164
|
# 파일 준비
|
|
165
|
+
# ⚠️ Rails 는 반복된 `images` 를 배열로 받지 않는다. images[0], images[1] ... 로 인덱싱해야 한다.
|
|
141
166
|
files = []
|
|
142
167
|
if image_paths:
|
|
143
|
-
for image_path in image_paths:
|
|
168
|
+
for index, image_path in enumerate(image_paths):
|
|
144
169
|
filename = os.path.basename(image_path)
|
|
145
|
-
files.append(('images', (filename, open(image_path, 'rb'))))
|
|
170
|
+
files.append((f'images[{index}]', (filename, open(image_path, 'rb'))))
|
|
146
171
|
|
|
147
172
|
try:
|
|
148
173
|
response = requests.post(
|
|
149
174
|
self._entrypoints(url),
|
|
150
|
-
headers=
|
|
175
|
+
headers=request_headers,
|
|
151
176
|
data=form_data,
|
|
152
177
|
files=files if files else None,
|
|
153
178
|
timeout=self.timeout
|
|
@@ -158,22 +183,27 @@ class BootpayCommerceResource:
|
|
|
158
183
|
for _, file_tuple in files:
|
|
159
184
|
file_tuple[1].close()
|
|
160
185
|
|
|
161
|
-
def put(self, url: str, data: Optional[Dict] = None):
|
|
186
|
+
def put(self, url: str, data: Optional[Dict] = None, headers: Optional[Dict[str, str]] = None):
|
|
162
187
|
"""PUT 요청"""
|
|
163
188
|
response = requests.put(
|
|
164
189
|
self._entrypoints(url),
|
|
165
|
-
headers=self._get_headers(),
|
|
190
|
+
headers=self._get_headers(headers=headers),
|
|
166
191
|
json=data,
|
|
167
192
|
timeout=self.timeout
|
|
168
193
|
)
|
|
169
194
|
return response.json()
|
|
170
195
|
|
|
171
|
-
def delete(self, url: str, params: Optional[Dict] = None
|
|
172
|
-
|
|
196
|
+
def delete(self, url: str, params: Optional[Dict] = None, headers: Optional[Dict[str, str]] = None,
|
|
197
|
+
data: Optional[Dict] = None):
|
|
198
|
+
"""
|
|
199
|
+
DELETE 요청
|
|
200
|
+
:param data: 요청 body (JSON) — 대상 ID 를 query 가 아닌 body 로 보내는 endpoint 용
|
|
201
|
+
"""
|
|
173
202
|
response = requests.delete(
|
|
174
203
|
self._entrypoints(url),
|
|
175
|
-
headers=self._get_headers(),
|
|
204
|
+
headers=self._get_headers(headers=headers),
|
|
176
205
|
params=params,
|
|
206
|
+
json=data,
|
|
177
207
|
timeout=self.timeout
|
|
178
208
|
)
|
|
179
209
|
return response.json()
|
{bootpay_backend-2.2.0 → bootpay_backend-2.5.0}/bootpay_backend/commerce/modules/__init__.py
RENAMED
|
@@ -8,6 +8,14 @@ from .order_cancel import OrderCancelModule
|
|
|
8
8
|
from .order_subscription import OrderSubscriptionModule
|
|
9
9
|
from .order_subscription_bill import OrderSubscriptionBillModule
|
|
10
10
|
from .order_subscription_adjustment import OrderSubscriptionAdjustmentModule
|
|
11
|
+
from .store import StoreModule
|
|
12
|
+
from .category import CategoryModule
|
|
13
|
+
from .coupon import CouponModule
|
|
14
|
+
from .point import PointModule
|
|
15
|
+
from .cart import CartModule
|
|
16
|
+
from .order_subscription_request import OrderSubscriptionRequestModule
|
|
17
|
+
from .mall_setting import MallSettingModule
|
|
18
|
+
from .webhook import WebhookModule
|
|
11
19
|
|
|
12
20
|
__all__ = [
|
|
13
21
|
'UserModule',
|
|
@@ -18,5 +26,13 @@ __all__ = [
|
|
|
18
26
|
'OrderCancelModule',
|
|
19
27
|
'OrderSubscriptionModule',
|
|
20
28
|
'OrderSubscriptionBillModule',
|
|
21
|
-
'OrderSubscriptionAdjustmentModule'
|
|
29
|
+
'OrderSubscriptionAdjustmentModule',
|
|
30
|
+
'StoreModule',
|
|
31
|
+
'CategoryModule',
|
|
32
|
+
'CouponModule',
|
|
33
|
+
'PointModule',
|
|
34
|
+
'CartModule',
|
|
35
|
+
'OrderSubscriptionRequestModule',
|
|
36
|
+
'MallSettingModule',
|
|
37
|
+
'WebhookModule'
|
|
22
38
|
]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Optional
|
|
2
|
+
|
|
3
|
+
if TYPE_CHECKING:
|
|
4
|
+
from ..commerce_resource import BootpayCommerceResource
|
|
5
|
+
|
|
6
|
+
from ..types import (
|
|
7
|
+
OrderPreviewParams,
|
|
8
|
+
OrderPreviewResponse
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CartModule:
|
|
13
|
+
"""장바구니 모듈"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, bootpay: 'BootpayCommerceResource'):
|
|
16
|
+
self._bootpay = bootpay
|
|
17
|
+
|
|
18
|
+
def order_preview(self, params: Optional[OrderPreviewParams] = None):
|
|
19
|
+
"""
|
|
20
|
+
주문 미리보기 (배송비/할인 권위적 계산)
|
|
21
|
+
POST /v1/cart/order-preview
|
|
22
|
+
|
|
23
|
+
member_mode='guest' (기본): cart_items 필수
|
|
24
|
+
member_mode='member': 서버 장바구니 사용 (user 토큰 필요)
|
|
25
|
+
|
|
26
|
+
:param params: 주문 미리보기 파라미터
|
|
27
|
+
:return: OrderPreviewResponse
|
|
28
|
+
"""
|
|
29
|
+
return self._bootpay.post('cart/order-preview', params if params is not None else {})
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from typing import TYPE_CHECKING, Dict, Optional
|
|
3
|
+
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from ..commerce_resource import BootpayCommerceResource
|
|
6
|
+
|
|
7
|
+
from ..types import (
|
|
8
|
+
CommerceCategory,
|
|
9
|
+
CategoryCreateParams,
|
|
10
|
+
CategoryUpdateParams
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CategoryModule:
|
|
15
|
+
"""카테고리 모듈"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, bootpay: 'BootpayCommerceResource'):
|
|
18
|
+
self._bootpay = bootpay
|
|
19
|
+
|
|
20
|
+
def list(self):
|
|
21
|
+
"""
|
|
22
|
+
카테고리 트리 조회
|
|
23
|
+
:return: List[CommerceCategory]
|
|
24
|
+
"""
|
|
25
|
+
return self._bootpay.get('categories')
|
|
26
|
+
|
|
27
|
+
def detail(self, category_id: str):
|
|
28
|
+
"""
|
|
29
|
+
카테고리 단건 조회
|
|
30
|
+
:param category_id: 카테고리 ID
|
|
31
|
+
:return: CommerceCategory
|
|
32
|
+
"""
|
|
33
|
+
return self._bootpay.get(f'categories/{category_id}')
|
|
34
|
+
|
|
35
|
+
def create(self, params: CategoryCreateParams):
|
|
36
|
+
"""
|
|
37
|
+
카테고리 생성
|
|
38
|
+
POST /v1/categories
|
|
39
|
+
⚠️ 서버가 supervisor scope 를 요구한다 (scope_invalid!).
|
|
40
|
+
:param params: 카테고리 생성 파라미터
|
|
41
|
+
:return: CommerceCategory
|
|
42
|
+
"""
|
|
43
|
+
params = dict(params or {})
|
|
44
|
+
idempotency_key = params.pop('idempotency_key', None)
|
|
45
|
+
return self._bootpay.post(
|
|
46
|
+
'categories',
|
|
47
|
+
params,
|
|
48
|
+
headers=self._supervisor_headers(idempotency_key)
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def update(self, params: CategoryUpdateParams):
|
|
52
|
+
"""
|
|
53
|
+
카테고리 수정
|
|
54
|
+
PUT /v1/categories/{category_id}
|
|
55
|
+
⚠️ 서버가 supervisor scope 를 요구한다 (scope_invalid!).
|
|
56
|
+
:param params: 카테고리 수정 파라미터 (category_id 필수)
|
|
57
|
+
:return: CommerceCategory
|
|
58
|
+
"""
|
|
59
|
+
if not params.get('category_id'):
|
|
60
|
+
raise ValueError('category_id is required')
|
|
61
|
+
params = dict(params)
|
|
62
|
+
category_id = params.pop('category_id')
|
|
63
|
+
idempotency_key = params.pop('idempotency_key', None)
|
|
64
|
+
return self._bootpay.put(
|
|
65
|
+
f'categories/{category_id}',
|
|
66
|
+
params,
|
|
67
|
+
headers=self._supervisor_headers(idempotency_key)
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def destroy(self, category_id: str, idempotency_key: Optional[str] = None):
|
|
71
|
+
"""
|
|
72
|
+
카테고리 삭제
|
|
73
|
+
DELETE /v1/categories/{category_id}
|
|
74
|
+
⚠️ 서버가 supervisor scope 를 요구한다 (scope_invalid!).
|
|
75
|
+
:param category_id: 카테고리 ID
|
|
76
|
+
:param idempotency_key: 미지정시 자동 생성
|
|
77
|
+
:return: None
|
|
78
|
+
"""
|
|
79
|
+
return self._bootpay.delete(
|
|
80
|
+
f'categories/{category_id}',
|
|
81
|
+
headers=self._supervisor_headers(idempotency_key)
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def _supervisor_headers(self, idempotency_key: Optional[str] = None) -> Dict[str, str]:
|
|
85
|
+
"""
|
|
86
|
+
카테고리 쓰기(등록/수정/삭제) 요청 헤더 — 서버가 supervisor scope 를 요구한다.
|
|
87
|
+
Idempotency-Key 는 미지정시 매 호출마다 생성된다.
|
|
88
|
+
"""
|
|
89
|
+
return {
|
|
90
|
+
'Idempotency-Key': idempotency_key or str(uuid.uuid4()),
|
|
91
|
+
'BOOTPAY-ROLE': 'supervisor'
|
|
92
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Optional
|
|
2
|
+
from urllib.parse import urlencode
|
|
3
|
+
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from ..commerce_resource import BootpayCommerceResource
|
|
6
|
+
|
|
7
|
+
from ..types import (
|
|
8
|
+
CommerceCoupon,
|
|
9
|
+
CouponListParams,
|
|
10
|
+
CouponDownloadParams
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CouponModule:
|
|
15
|
+
"""쿠폰 모듈"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, bootpay: 'BootpayCommerceResource'):
|
|
18
|
+
self._bootpay = bootpay
|
|
19
|
+
|
|
20
|
+
def list(self, params: Optional[CouponListParams] = None):
|
|
21
|
+
"""
|
|
22
|
+
사용자 보유 쿠폰 목록 조회
|
|
23
|
+
:param params: 조회 파라미터
|
|
24
|
+
:return: List[CommerceCoupon]
|
|
25
|
+
"""
|
|
26
|
+
query_params = {}
|
|
27
|
+
if params:
|
|
28
|
+
if params.get('status'):
|
|
29
|
+
query_params['status'] = params['status']
|
|
30
|
+
if params.get('page') is not None:
|
|
31
|
+
query_params['page'] = params['page']
|
|
32
|
+
if params.get('limit') is not None:
|
|
33
|
+
query_params['limit'] = params['limit']
|
|
34
|
+
|
|
35
|
+
query = urlencode(query_params) if query_params else ''
|
|
36
|
+
return self._bootpay.get(f'coupon{"?" + query if query else ""}')
|
|
37
|
+
|
|
38
|
+
def available(self):
|
|
39
|
+
"""
|
|
40
|
+
다운로드 가능한 쿠폰 목록
|
|
41
|
+
:return: List[CommerceCoupon]
|
|
42
|
+
"""
|
|
43
|
+
return self._bootpay.get('coupon/available')
|
|
44
|
+
|
|
45
|
+
def download(self, params: CouponDownloadParams):
|
|
46
|
+
"""
|
|
47
|
+
쿠폰 다운로드 (issue_from_template)
|
|
48
|
+
:param params: {'coupon_template_id': str}
|
|
49
|
+
:return: CommerceCoupon
|
|
50
|
+
"""
|
|
51
|
+
return self._bootpay.post('coupon/download', params)
|